diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1a68d7db..bdc6f4db 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,9 +9,28 @@ updates: patterns: - "react" - "react-*" + - "next" + - "react-dom" + - "@types/react" + - "@types/react-dom" + next-typescript-eslint: + patterns: + - "eslint" + - "eslint-config-next" + - "typescript" + - "typescript-eslint" + - "@typescript-eslint/*" everything-else: patterns: - "*" exclude-patterns: - "react" - "react-*" + - "react-dom" + - "@types/react" + - "@types/react-dom" + - "eslint" + - "eslint-config-next" + - "typescript" + - "typescript-eslint" + - "@typescript-eslint/*" diff --git a/LICENSE b/LICENSE index 95e3fa3c..266586c6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Shape ApS +Copyright (c) 2026 Framna Denmark ApS Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/SECURITY.md b/SECURITY.md index 041850bb..694cb7fd 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,4 +2,4 @@ ## Reporting a Vulnerability -Please contact us at security@shape.dk if you find a security vulnerability in the software. Do note that we do not offer a bug bounty program. +Please contact us at security@dk.framna.com if you find a security vulnerability in the software. Do note that we do not offer a bug bounty program. diff --git a/__test__/projects/CachingProjectDataSource.test.ts b/__test__/projects/CachingProjectDataSource.test.ts deleted file mode 100644 index 9365f53a..00000000 --- a/__test__/projects/CachingProjectDataSource.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Project, CachingProjectDataSource } from "@/features/projects/domain" - -test("It caches projects read from the data source", async () => { - const projects: Project[] = [{ - id: "foo", - name: "foo", - displayName: "foo", - versions: [{ - id: "bar", - name: "bar", - isDefault: false, - specifications: [{ - id: "baz.yml", - name: "baz.yml", - url: "https://example.com/baz.yml" - }] - }, { - id: "hello", - name: "hello", - isDefault: false, - specifications: [{ - id: "world.yml", - name: "world.yml", - url: "https://example.com/world.yml" - }] - }], - owner: "acme", - ownerUrl: "https://example.com/acme" - }] - let cachedProjects: Project[] | undefined - const sut = new CachingProjectDataSource({ - dataSource: { - async getProjects() { - return projects - } - }, - repository: { - async get() { - return [] - }, - async set(projects) { - cachedProjects = projects - }, - async delete() {} - } - }) - await sut.getProjects() - expect(cachedProjects).toEqual(projects) -}) diff --git a/__test__/projects/CachingProjectListDataSource.test.ts b/__test__/projects/CachingProjectListDataSource.test.ts new file mode 100644 index 00000000..a3620249 --- /dev/null +++ b/__test__/projects/CachingProjectListDataSource.test.ts @@ -0,0 +1,102 @@ +import { ProjectSummary, CachingProjectListDataSource } from "@/features/projects/domain" + +const projects: ProjectSummary[] = [{ + id: "acme-foo", + name: "foo", + displayName: "Foo", + owner: "acme", + ownerUrl: "https://github.com/acme" +}] + +test("It returns cached projects when cache is populated", async () => { + let didCallDataSource = false + const sut = new CachingProjectListDataSource({ + dataSource: { + async getProjectList() { + didCallDataSource = true + return projects + } + }, + repository: { + async get() { + return projects + }, + async set() {}, + async delete() {} + } + }) + const result = await sut.getProjectList() + expect(result).toEqual(projects) + expect(didCallDataSource).toBe(false) +}) + +test("It fetches and caches projects when cache is empty", async () => { + let cachedProjects: ProjectSummary[] | undefined + const sut = new CachingProjectListDataSource({ + dataSource: { + async getProjectList() { + return projects + } + }, + repository: { + async get() { + return undefined + }, + async set(value) { + cachedProjects = value + }, + async delete() {} + } + }) + const result = await sut.getProjectList() + expect(result).toEqual(projects) + expect(cachedProjects).toEqual(projects) +}) + +test("It treats an empty cached list as a valid cache hit and does not call the data source", async () => { + let didCallDataSource = false + const sut = new CachingProjectListDataSource({ + dataSource: { + async getProjectList() { + didCallDataSource = true + return [] + } + }, + repository: { + async get() { + return [] + }, + async set() {}, + async delete() {} + } + }) + const result = await sut.getProjectList() + expect(result).toEqual([]) + expect(didCallDataSource).toBe(false) +}) + +test("It bypasses cache and refreshes when refresh=true", async () => { + let didCallDataSource = false + let cachedProjects: ProjectSummary[] | undefined + const sut = new CachingProjectListDataSource({ + dataSource: { + async getProjectList() { + didCallDataSource = true + return projects + } + }, + repository: { + async get() { + return projects + }, + async set(value) { + cachedProjects = value + }, + async delete() {} + } + }) + const result = await sut.getProjectList({ refresh: true }) + expect(result).toEqual(projects) + expect(didCallDataSource).toBe(true) + expect(cachedProjects).toEqual(projects) +}) diff --git a/__test__/projects/FilteringGitHubRepositoryDataSource.test.ts b/__test__/projects/FilteringGitHubRepositoryDataSource.test.ts deleted file mode 100644 index e834d7b0..00000000 --- a/__test__/projects/FilteringGitHubRepositoryDataSource.test.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { FilteringGitHubRepositoryDataSource } from "@/features/projects/domain" - -test("It returns all repositories when no hidden repositories are provided", async () => { - const sut = new FilteringGitHubRepositoryDataSource({ - hiddenRepositories: [], - dataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - branches: [], - tags: [] - }, { - owner: "acme", - name: "bar-openapi", - defaultBranchRef: { - id: "12345678", - name: "bar" - }, - branches: [], - tags: [] - }] - } - } - }) - const repositories = await sut.getRepositories() - expect(repositories.length).toEqual(2) -}) - -test("It removes hidden repository", async () => { - const sut = new FilteringGitHubRepositoryDataSource({ - hiddenRepositories: ["acme/foo-openapi"], - dataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - branches: [], - tags: [] - }, { - owner: "acme", - name: "bar-openapi", - defaultBranchRef: { - id: "12345678", - name: "bar" - }, - branches: [], - tags: [] - }] - } - } - }) - const repositories = await sut.getRepositories() - expect(repositories.length).toEqual(1) -}) - -test("It returns unmodified list when hidden repository was not found", async () => { - const sut = new FilteringGitHubRepositoryDataSource({ - hiddenRepositories: ["acme/baz-openapi"], - dataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - branches: [], - tags: [] - }, { - owner: "acme", - name: "bar-openapi", - defaultBranchRef: { - id: "12345678", - name: "bar" - }, - branches: [], - tags: [] - }] - } - } - }) - const repositories = await sut.getRepositories() - expect(repositories.length).toEqual(2) -}) - -test("It removes multiple hidden repositories", async () => { - const sut = new FilteringGitHubRepositoryDataSource({ - hiddenRepositories: ["acme/foo-openapi", "acme/bar-openapi"], - dataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - branches: [], - tags: [] - }, { - owner: "acme", - name: "bar-openapi", - defaultBranchRef: { - id: "12345678", - name: "bar" - }, - branches: [], - tags: [] - }] - } - } - }) - const repositories = await sut.getRepositories() - expect(repositories.length).toEqual(0) -}) diff --git a/__test__/projects/GitHubProjectDataSource.test.ts b/__test__/projects/GitHubProjectDataSource.test.ts deleted file mode 100644 index 586375bc..00000000 --- a/__test__/projects/GitHubProjectDataSource.test.ts +++ /dev/null @@ -1,1196 +0,0 @@ -import { GitHubProjectDataSource } from "@/features/projects/data" -import RemoteConfig from "@/features/projects/domain/RemoteConfig" - -/** - * Simple encryption service for testing. Does nothing. - */ -const noopEncryptionService = { - encrypt: function (data: string): string { - return data - }, - decrypt: function (encryptedDataBase64: string): string { - return encryptedDataBase64 - } -} - -/** - * Simple encoder for testing - */ -const base64RemoteConfigEncoder = { - encode: function (remoteConfig: RemoteConfig): string { - return Buffer.from(JSON.stringify(remoteConfig)).toString("base64") - }, - decode: function (encodedString: string): RemoteConfig { - return JSON.parse(Buffer.from(encodedString, "base64").toString()) - } -} - -test("It loads repositories from data source", async () => { - let didLoadRepositories = false - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - didLoadRepositories = true - return [] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - await sut.getProjects() - expect(didLoadRepositories).toBeTruthy() -}) - -test("It maps projects including branches and tags", async () => { - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - branches: [{ - id: "12345678", - name: "main", - files: [{ - name: "openapi.yml" - }] - }], - tags: [{ - id: "12345678", - name: "1.0", - files: [{ - name: "openapi.yml" - }] - }] - }] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - const projects = await sut.getProjects() - expect(projects).toEqual([{ - id: "acme-foo", - name: "foo", - displayName: "foo", - url: "https://github.com/acme/foo-openapi", - versions: [{ - id: "main", - name: "main", - specifications: [{ - id: "openapi.yml", - name: "openapi.yml", - url: "/api/blob/acme/foo-openapi/openapi.yml?ref=12345678", - editURL: "https://github.com/acme/foo-openapi/edit/main/openapi.yml", - isDefault: false - }], - url: "https://github.com/acme/foo-openapi/tree/main", - isDefault: true - }, { - id: "1.0", - name: "1.0", - specifications: [{ - id: "openapi.yml", - name: "openapi.yml", - url: "/api/blob/acme/foo-openapi/openapi.yml?ref=12345678", - editURL: "https://github.com/acme/foo-openapi/edit/1.0/openapi.yml", - isDefault: false - }], - url: "https://github.com/acme/foo-openapi/tree/1.0", - isDefault: false - }], - owner: "acme", - ownerUrl: "https://github.com/acme" - }]) -}) - -test("It removes suffix from project name", async () => { - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - branches: [{ - id: "12345678", - name: "main", - files: [{ - name: "openapi.yml" - }] - }], - tags: [{ - id: "12345678", - name: "1.0", - files: [{ - name: "openapi.yml" - }] - }] - }] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - const projects = await sut.getProjects() - expect(projects[0].id).toEqual("acme-foo") - expect(projects[0].name).toEqual("foo") - expect(projects[0].displayName).toEqual("foo") -}) - -test("It supports multiple OpenAPI specifications on a branch", async () => { - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - branches: [{ - id: "12345678", - name: "main", - files: [{ - name: "foo-service.yml", - }, { - name: "bar-service.yml", - }, { - name: "baz-service.yml", - }] - }], - tags: [{ - id: "12345678", - name: "1.0", - files: [{ - name: "openapi.yml" - }] - }] - }] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - const projects = await sut.getProjects() - expect(projects).toEqual([{ - id: "acme-foo", - name: "foo", - displayName: "foo", - url: "https://github.com/acme/foo-openapi", - versions: [{ - id: "main", - name: "main", - specifications: [{ - id: "bar-service.yml", - name: "bar-service.yml", - url: "/api/blob/acme/foo-openapi/bar-service.yml?ref=12345678", - editURL: "https://github.com/acme/foo-openapi/edit/main/bar-service.yml", - isDefault: false - }, { - id: "baz-service.yml", - name: "baz-service.yml", - url: "/api/blob/acme/foo-openapi/baz-service.yml?ref=12345678", - editURL: "https://github.com/acme/foo-openapi/edit/main/baz-service.yml", - isDefault: false - }, - { - id: "foo-service.yml", - name: "foo-service.yml", - url: "/api/blob/acme/foo-openapi/foo-service.yml?ref=12345678", - editURL: "https://github.com/acme/foo-openapi/edit/main/foo-service.yml", - isDefault: false - }], - url: "https://github.com/acme/foo-openapi/tree/main", - isDefault: true - }, { - id: "1.0", - name: "1.0", - specifications: [{ - id: "openapi.yml", - name: "openapi.yml", - url: "/api/blob/acme/foo-openapi/openapi.yml?ref=12345678", - editURL: "https://github.com/acme/foo-openapi/edit/1.0/openapi.yml", - isDefault: false - }], - url: "https://github.com/acme/foo-openapi/tree/1.0", - isDefault: false - }], - owner: "acme", - ownerUrl: "https://github.com/acme" - }]) -}) - -test("It filters away projects with no versions", async () => { - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - branches: [], - tags: [] - }] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - const projects = await sut.getProjects() - expect(projects.length).toEqual(0) -}) - -test("It filters away branches with no specifications", async () => { - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - branches: [{ - id: "12345678", - name: "main", - files: [{ - name: "openapi.yml", - }] - }, { - id: "12345678", - name: "bugfix", - files: [{ - name: "README.md", - }] - }], - tags: [] - }] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - const projects = await sut.getProjects() - expect(projects[0].versions.length).toEqual(1) -}) - -test("It filters away tags with no specifications", async () => { - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - branches: [{ - id: "12345678", - name: "main", - files: [{ - name: "foo-service.yml", - }] - }], - tags: [{ - id: "12345678", - name: "1.0", - files: [{ - name: "openapi.yml" - }] - }, { - id: "12345678", - name: "0.1", - files: [{ - name: "README.md" - }] - }] - }] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - const projects = await sut.getProjects() - expect(projects[0].versions.length).toEqual(2) -}) - -test("It reads image from configuration file with .yml extension", async () => { - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - configYml: { - text: "image: icon.png" - }, - branches: [{ - id: "12345678", - name: "main", - files: [{ - name: "openapi.yml", - }] - }], - tags: [] - }] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - const projects = await sut.getProjects() - expect(projects[0].imageURL).toEqual("/api/blob/acme/foo-openapi/icon.png?ref=12345678") -}) - -test("It reads display name from configuration file with .yml extension", async () => { - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - configYml: { - text: "name: Hello World" - }, - branches: [{ - id: "12345678", - name: "main", - files: [{ - name: "openapi.yml", - }] - }], - tags: [] - }] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - const projects = await sut.getProjects() - expect(projects[0].id).toEqual("acme-foo") - expect(projects[0].name).toEqual("foo") - expect(projects[0].displayName).toEqual("Hello World") -}) - -test("It reads image from configuration file with .yaml extension", async () => { - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - configYaml: { - text: "image: icon.png" - }, - branches: [{ - id: "12345678", - name: "main", - files: [{ - name: "openapi.yml", - }] - }], - tags: [] - }] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - const projects = await sut.getProjects() - expect(projects[0].imageURL).toEqual("/api/blob/acme/foo-openapi/icon.png?ref=12345678") -}) - -test("It reads display name from configuration file with .yaml extension", async () => { - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - configYaml: { - text: "name: Hello World" - }, - branches: [{ - id: "12345678", - name: "main", - files: [{ - name: "openapi.yml", - }] - }], - tags: [] - }] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - const projects = await sut.getProjects() - expect(projects[0].id).toEqual("acme-foo") - expect(projects[0].name).toEqual("foo") - expect(projects[0].displayName).toEqual("Hello World") -}) - -test("It sorts projects alphabetically", async () => { - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "cathrine-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - configYaml: { - text: "name: Hello World" - }, - branches: [{ - id: "12345678", - name: "main", - files: [{ - name: "openapi.yml", - }] - }], - tags: [] - }, { - owner: "acme", - name: "bobby-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - configYaml: { - text: "name: Hello World" - }, - branches: [{ - id: "12345678", - name: "main", - files: [{ - name: "openapi.yml", - }] - }], - tags: [] - }, { - owner: "acme", - name: "anne-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - configYaml: { - text: "name: Hello World" - }, - branches: [{ - id: "12345678", - name: "main", - files: [{ - name: "openapi.yml", - }] - }], - tags: [] - }] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - const projects = await sut.getProjects() - expect(projects[0].name).toEqual("anne") - expect(projects[1].name).toEqual("bobby") - expect(projects[2].name).toEqual("cathrine") -}) - -test("It sorts versions alphabetically", async () => { - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - configYaml: { - text: "name: Hello World" - }, - branches: [{ - id: "12345678", - name: "anne", - files: [{ - name: "openapi.yml", - }] - }, { - id: "12345678", - name: "bobby", - files: [{ - name: "openapi.yml", - }] - }], - tags: [{ - id: "12345678", - name: "cathrine", - files: [{ - name: "openapi.yml", - }] - }, { - id: "12345678", - name: "1.0", - files: [{ - name: "openapi.yml", - }] - }] - }] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - const projects = await sut.getProjects() - expect(projects[0].versions[0].name).toEqual("1.0") - expect(projects[0].versions[1].name).toEqual("anne") - expect(projects[0].versions[2].name).toEqual("bobby") - expect(projects[0].versions[3].name).toEqual("cathrine") -}) - -test("It prioritizes main, master, develop, and development branch names when sorting verisons", async () => { - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - configYaml: { - text: "name: Hello World" - }, - branches: [{ - id: "12345678", - name: "anne", - files: [{ - name: "openapi.yml", - }] - }, { - id: "12345678", - name: "develop", - files: [{ - name: "openapi.yml", - }] - }, { - id: "12345678", - name: "main", - files: [{ - name: "openapi.yml", - }] - }, { - id: "12345678", - name: "development", - files: [{ - name: "openapi.yml", - }] - }, { - id: "12345678", - name: "master", - files: [{ - name: "openapi.yml", - }] - }], - tags: [{ - id: "12345678", - name: "1.0", - files: [{ - name: "openapi.yml", - }] - }] - }] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - const projects = await sut.getProjects() - expect(projects[0].versions[0].name).toEqual("main") - expect(projects[0].versions[1].name).toEqual("master") - expect(projects[0].versions[2].name).toEqual("develop") - expect(projects[0].versions[3].name).toEqual("development") - expect(projects[0].versions[4].name).toEqual("1.0") - expect(projects[0].versions[5].name).toEqual("anne") -}) - -test("It sorts file specifications alphabetically", async () => { - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - configYaml: { - text: "name: Hello World" - }, - branches: [{ - id: "12345678", - name: "anne", - files: [{ - name: "z-openapi.yml", - }, { - name: "a-openapi.yml", - }, { - name: "1-openapi.yml", - }] - }], - tags: [{ - id: "12345678", - name: "cathrine", - files: [{ - name: "o-openapi.yml", - }, { - name: "2-openapi.yml", - }] - }] - }] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - const projects = await sut.getProjects() - expect(projects[0].versions[0].specifications[0].name).toEqual("1-openapi.yml") - expect(projects[0].versions[0].specifications[1].name).toEqual("a-openapi.yml") - expect(projects[0].versions[0].specifications[2].name).toEqual("z-openapi.yml") - expect(projects[0].versions[1].specifications[0].name).toEqual("2-openapi.yml") - expect(projects[0].versions[1].specifications[1].name).toEqual("o-openapi.yml") -}) - -test("It maintains remote version specification ordering from config", async () => { - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - configYaml: { - text: ` - name: Hello World - remoteVersions: - - name: Bar - specifications: - - id: some-spec - name: Zac - url: https://example.com/zac.yml - - id: another-spec - name: Bob - url: https://example.com/bob.yml - ` - }, - branches: [], - tags: [] - }] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - const projects = await sut.getProjects() - expect(projects[0].versions[0].specifications[0].name).toEqual("Zac") - expect(projects[0].versions[0].specifications[1].name).toEqual("Bob") -}) - -test("It identifies the default branch in returned versions", async () => { - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "development" - }, - configYaml: { - text: "name: Hello World" - }, - branches: [{ - id: "12345678", - name: "anne", - files: [{ - name: "openapi.yml", - }] - }, { - id: "12345678", - name: "main", - files: [{ - name: "openapi.yml", - }] - }, { - id: "12345678", - name: "development", - files: [{ - name: "openapi.yml", - }] - }], - tags: [] - }] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - const projects = await sut.getProjects() - const defaultVersionNames = projects[0] - .versions - .filter(e => e.isDefault) - .map(e => e.name) - expect(defaultVersionNames).toEqual(["development"]) -}) - -test("It adds remote versions from the project configuration", async () => { - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - configYaml: { - text: ` - remoteVersions: - - name: Anne - specifications: - - name: Huey - url: https://example.com/huey.yml - - name: Dewey - url: https://example.com/dewey.yml - - name: Bobby - specifications: - - name: Louie - url: https://example.com/louie.yml - ` - }, - branches: [], - tags: [] - }] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - const projects = await sut.getProjects() - expect(projects[0].versions).toEqual([{ - id: "anne", - name: "Anne", - isDefault: false, - specifications: [{ - id: "huey", - name: "Huey", - url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/huey.yml" })}`, - urlHash: "89ba381286214eec", - isDefault: false - }, { - id: "dewey", - name: "Dewey", - url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/dewey.yml" })}`, - urlHash: "8f810fff152505f6", - isDefault: false - }] - }, { - id: "bobby", - name: "Bobby", - isDefault: false, - specifications: [{ - id: "louie", - name: "Louie", - url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/louie.yml" })}`, - urlHash: "b83ebf43ceede6bc", - isDefault: false - }] - }]) -}) - -test("It modifies ID of remote version if the ID already exists", async () => { - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "bar" - }, - configYaml: { - text: ` - remoteVersions: - - name: Bar - specifications: - - name: Baz - url: https://example.com/baz.yml - - name: Bar - specifications: - - name: Hello - url: https://example.com/hello.yml - ` - }, - branches: [{ - id: "12345678", - name: "bar", - files: [{ - name: "openapi.yml" - }] - }], - tags: [] - }] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - const projects = await sut.getProjects() - expect(projects[0].versions).toEqual([{ - id: "bar", - name: "bar", - url: "https://github.com/acme/foo-openapi/tree/bar", - isDefault: true, - specifications: [{ - id: "openapi.yml", - name: "openapi.yml", - url: "/api/blob/acme/foo-openapi/openapi.yml?ref=12345678", - editURL: "https://github.com/acme/foo-openapi/edit/bar/openapi.yml", - isDefault: false - }] - }, { - id: "bar1", - name: "Bar", - isDefault: false, - specifications: [{ - id: "baz", - name: "Baz", - url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/baz.yml" })}`, - urlHash: "25cb42ff63570cb5", - isDefault: false - }] - }, { - id: "bar2", - name: "Bar", - isDefault: false, - specifications: [{ - id: "hello", - name: "Hello", - url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/hello.yml" })}`, - urlHash: "d078bd689699d1f0", - isDefault: false - }] - }]) -}) - -test("It lets users specify the ID of a remote version", async () => { - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "bar" - }, - configYaml: { - text: ` - remoteVersions: - - id: some-version - name: Bar - specifications: - - name: Baz - url: https://example.com/baz.yml - ` - }, - branches: [], - tags: [] - }] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - const projects = await sut.getProjects() - expect(projects[0].versions).toEqual([{ - id: "some-version", - name: "Bar", - isDefault: false, - specifications: [{ - id: "baz", - name: "Baz", - url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/baz.yml" })}`, - urlHash: "25cb42ff63570cb5", - isDefault: false - }] - }]) -}) - -test("It lets users specify the ID of a remote specification", async () => { - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "bar" - }, - configYaml: { - text: ` - remoteVersions: - - name: Bar - specifications: - - id: some-spec - name: Baz - url: https://example.com/baz.yml - ` - }, - branches: [], - tags: [] - }] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - const projects = await sut.getProjects() - expect(projects[0].versions).toEqual([{ - id: "bar", - name: "Bar", - isDefault: false, - specifications: [{ - id: "some-spec", - name: "Baz", - url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/baz.yml" })}`, - urlHash: "25cb42ff63570cb5", - isDefault: false - }] - }]) -}) - -test("It sets isDefault on the correct specification based on defaultSpecificationName in config", async () => { - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - configYml: { - text: ` - defaultSpecificationName: bar-service.yml - remoteVersions: - - name: Bar - specifications: - - id: some-spec - name: Baz - url: https://example.com/baz.yml - ` - }, - branches: [{ - id: "12345678", - name: "main", - files: [ - { name: "foo-service.yml" }, - { name: "bar-service.yml" }, - { name: "baz-service.yml" } - ] - }], - tags: [] - }] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - const projects = await sut.getProjects() - const specs = projects[0].versions[0].specifications - expect(specs.find(s => s.name === "bar-service.yml")!.isDefault).toBe(true) - expect(specs.find(s => s.name === "foo-service.yml")!.isDefault).toBe(false) - expect(specs.find(s => s.name === "baz-service.yml")!.isDefault).toBe(false) - expect(projects[0].versions[1].specifications.find(s => s.name === "Baz")!.isDefault).toBe(false) -}) - -test("It sets a remote specification as the default if specified", async () => { - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - configYaml: { - text: ` - defaultSpecificationName: Baz - remoteVersions: - - name: Bar - specifications: - - id: some-spec - name: Baz - url: https://example.com/baz.yml - - id: another-spec - name: Qux - url: https://example.com/qux.yml - ` - }, - branches: [], - tags: [] - }] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - const projects = await sut.getProjects() - const remoteSpecs = projects[0].versions[0].specifications - expect(remoteSpecs.find(s => s.id === "some-spec")!.isDefault).toBe(true) - expect(remoteSpecs.find(s => s.id === "another-spec")!.isDefault).toBe(false) -}) - - -test("It sets isDefault to false for all specifications if defaultSpecificationName is not set", async () => { - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - configYml: { - text: `` - }, - branches: [{ - id: "12345678", - name: "main", - files: [ - { name: "foo-service.yml" }, - { name: "bar-service.yml" }, - { name: "baz-service.yml" } - ] - }], - tags: [] - }] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - const projects = await sut.getProjects() - const specs = projects[0].versions[0].specifications - expect(specs.every(s => s.isDefault === false)).toBe(true) -}) - -test("It silently ignores defaultSpecificationName if no matching spec is found", async () => { - const sut = new GitHubProjectDataSource({ - repositoryNameSuffix: "-openapi", - repositoryDataSource: { - async getRepositories() { - return [{ - owner: "acme", - name: "foo-openapi", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - configYml: { - text: `defaultSpecificationName: non-existent.yml` - }, - branches: [{ - id: "12345678", - name: "main", - files: [ - { name: "foo-service.yml" }, - { name: "bar-service.yml" }, - { name: "baz-service.yml" } - ] - }], - tags: [] - }] - } - }, - encryptionService: noopEncryptionService, - remoteConfigEncoder: base64RemoteConfigEncoder - }) - const projects = await sut.getProjects() - const specs = projects[0].versions[0].specifications - expect(specs.every(s => s.isDefault === false)).toBe(true) -}) diff --git a/__test__/projects/GitHubProjectDetailsDataSource.test.ts b/__test__/projects/GitHubProjectDetailsDataSource.test.ts new file mode 100644 index 00000000..7135d9c2 --- /dev/null +++ b/__test__/projects/GitHubProjectDetailsDataSource.test.ts @@ -0,0 +1,501 @@ +import { jest } from "@jest/globals" +import { GitHubProjectDetailsDataSource } from "@/features/projects/data" +import { IGitHubGraphQLClient } from "@/features/projects/domain" +import { IEncryptionService } from "@/features/encrypt/EncryptionService" +import { IRemoteConfigEncoder } from "@/features/projects/domain/RemoteConfigEncoder" + +const createMockGraphQLClient = (response: unknown = null): IGitHubGraphQLClient => ({ + graphql: jest.fn<() => Promise>().mockResolvedValue(response) +}) + +const createMockEncryptionService = (): IEncryptionService => ({ + encrypt: jest.fn<(data: string) => string>().mockImplementation(data => `encrypted:${data}`), + decrypt: jest.fn<(data: string) => string>().mockImplementation(data => data.replace("encrypted:", "")) +}) + +const createMockRemoteConfigEncoder = (): IRemoteConfigEncoder => ({ + encode: jest.fn<() => string>().mockReturnValue("encoded-config"), + decode: jest.fn() +}) + +const createSut = (overrides: { + graphQlClient?: IGitHubGraphQLClient + repositoryNameSuffix?: string + projectConfigurationFilename?: string + encryptionService?: IEncryptionService + remoteConfigEncoder?: IRemoteConfigEncoder +} = {}) => { + return new GitHubProjectDetailsDataSource({ + graphQlClient: overrides.graphQlClient || createMockGraphQLClient(), + repositoryNameSuffix: overrides.repositoryNameSuffix || "-openapi", + projectConfigurationFilename: overrides.projectConfigurationFilename || ".framna-docs.yml", + encryptionService: overrides.encryptionService || createMockEncryptionService(), + remoteConfigEncoder: overrides.remoteConfigEncoder || createMockRemoteConfigEncoder() + }) +} + +const createRepositoryResponse = (overrides: { + name?: string + defaultBranchRef?: { name: string; target: { oid: string } } + configYml?: { text: string } | null + branches?: { name: string; target: { oid: string; tree: { entries: { name: string }[] } } }[] + tags?: { name: string; target: { oid: string; tree: { entries: { name: string }[] } } }[] + pullRequests?: { number: number; headRefName: string; baseRefName: string; baseRefOid: string; files?: { nodes: { path: string }[] } }[] +} = {}) => ({ + repository: { + name: overrides.name || "my-project-openapi", + defaultBranchRef: overrides.defaultBranchRef || { name: "main", target: { oid: "abc123" } }, + configYml: overrides.configYml === null ? undefined : (overrides.configYml || undefined), + configYaml: undefined, + branches: { + edges: (overrides.branches || [{ name: "main", target: { oid: "abc123", tree: { entries: [{ name: "api.yml" }] } } }]) + .map(node => ({ node })) + }, + tags: { + edges: (overrides.tags || []).map(node => ({ node })) + }, + pullRequests: { + edges: (overrides.pullRequests || []).map(node => ({ node })) + } + } +}) + +describe("GitHubProjectDetailsDataSource", () => { + describe("getProjectDetails", () => { + test("It returns null when repository is not found", async () => { + const graphQlClient = createMockGraphQLClient({ repository: null }) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectDetails("acme", "my-project") + + expect(result).toBeNull() + }) + + test("It returns null when the GraphQL client throws a not-found error", async () => { + const graphQlClient: IGitHubGraphQLClient = { + graphql: jest.fn<() => Promise>().mockRejectedValue( + Object.assign(new Error("Could not resolve to a Repository"), { type: "NOT_FOUND" }) + ) + } + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectDetails("acme", "nonexistent") + + expect(result).toBeNull() + }) + + test("It throws when the GraphQL client throws a non-not-found error", async () => { + const networkError = new Error("Network failure") + const graphQlClient: IGitHubGraphQLClient = { + graphql: jest.fn<() => Promise>().mockRejectedValue(networkError) + } + const sut = createSut({ graphQlClient }) + + await expect(sut.getProjectDetails("acme", "my-project")).rejects.toThrow("Network failure") + }) + + test("It returns project with basic info", async () => { + const graphQlClient = createMockGraphQLClient(createRepositoryResponse()) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectDetails("acme", "my-project") + + expect(result).not.toBeNull() + expect(result!.id).toBe("acme-my-project") + expect(result!.name).toBe("my-project") + expect(result!.owner).toBe("acme") + expect(result!.ownerUrl).toBe("https://github.com/acme") + expect(result!.url).toBe("https://github.com/acme/my-project-openapi") + }) + + test("It uses display name from config", async () => { + const graphQlClient = createMockGraphQLClient(createRepositoryResponse({ + configYml: { text: "name: My Awesome API" } + })) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectDetails("acme", "my-project") + + expect(result!.displayName).toBe("My Awesome API") + }) + + test("It uses project name when config has no name", async () => { + const graphQlClient = createMockGraphQLClient(createRepositoryResponse({ + configYml: null + })) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectDetails("acme", "my-project") + + expect(result!.displayName).toBe("my-project") + }) + + test("It generates image URL from config", async () => { + const graphQlClient = createMockGraphQLClient(createRepositoryResponse({ + configYml: { text: "image: logo.png" }, + defaultBranchRef: { name: "main", target: { oid: "abc123" } } + })) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectDetails("acme", "my-project") + + expect(result!.imageURL).toBe("/api/blob/acme/my-project-openapi/logo.png?ref=abc123") + }) + + test("It appends suffix to repo name if not present", async () => { + const graphQlClient = createMockGraphQLClient(createRepositoryResponse()) + const sut = createSut({ graphQlClient }) + + await sut.getProjectDetails("acme", "my-project") + + expect(graphQlClient.graphql).toHaveBeenCalledWith( + expect.objectContaining({ + variables: { owner: "acme", name: "my-project-openapi" } + }) + ) + }) + + test("It does not double-append suffix if already present", async () => { + const graphQlClient = createMockGraphQLClient(createRepositoryResponse()) + const sut = createSut({ graphQlClient }) + + await sut.getProjectDetails("acme", "my-project-openapi") + + expect(graphQlClient.graphql).toHaveBeenCalledWith( + expect.objectContaining({ + variables: { owner: "acme", name: "my-project-openapi" } + }) + ) + }) + }) + + describe("versions", () => { + test("It creates versions from branches", async () => { + const graphQlClient = createMockGraphQLClient(createRepositoryResponse({ + branches: [ + { name: "main", target: { oid: "abc123", tree: { entries: [{ name: "api.yml" }] } } }, + { name: "develop", target: { oid: "def456", tree: { entries: [{ name: "api.yml" }] } } } + ] + })) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectDetails("acme", "my-project") + + expect(result!.versions).toHaveLength(2) + expect(result!.versions.map(v => v.name)).toContain("main") + expect(result!.versions.map(v => v.name)).toContain("develop") + }) + + test("It creates versions from tags", async () => { + const graphQlClient = createMockGraphQLClient(createRepositoryResponse({ + branches: [{ name: "main", target: { oid: "abc123", tree: { entries: [{ name: "api.yml" }] } } }], + tags: [{ name: "v1.0.0", target: { oid: "tag123", tree: { entries: [{ name: "api.yml" }] } } }] + })) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectDetails("acme", "my-project") + + expect(result!.versions.map(v => v.name)).toContain("v1.0.0") + }) + + test("It marks default branch as default", async () => { + const graphQlClient = createMockGraphQLClient(createRepositoryResponse({ + defaultBranchRef: { name: "main", target: { oid: "abc123" } }, + branches: [ + { name: "main", target: { oid: "abc123", tree: { entries: [{ name: "api.yml" }] } } }, + { name: "develop", target: { oid: "def456", tree: { entries: [{ name: "api.yml" }] } } } + ] + })) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectDetails("acme", "my-project") + + const mainVersion = result!.versions.find(v => v.name === "main") + const developVersion = result!.versions.find(v => v.name === "develop") + expect(mainVersion!.isDefault).toBe(true) + expect(developVersion!.isDefault).toBe(false) + }) + + test("It sorts versions with default branch first", async () => { + const graphQlClient = createMockGraphQLClient(createRepositoryResponse({ + defaultBranchRef: { name: "main", target: { oid: "abc123" } }, + branches: [ + { name: "zebra", target: { oid: "z123", tree: { entries: [{ name: "api.yml" }] } } }, + { name: "main", target: { oid: "abc123", tree: { entries: [{ name: "api.yml" }] } } }, + { name: "alpha", target: { oid: "a123", tree: { entries: [{ name: "api.yml" }] } } } + ] + })) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectDetails("acme", "my-project") + + expect(result!.versions[0].name).toBe("main") + }) + + test("It filters out versions without specifications", async () => { + const graphQlClient = createMockGraphQLClient(createRepositoryResponse({ + branches: [ + { name: "main", target: { oid: "abc123", tree: { entries: [{ name: "api.yml" }] } } }, + { name: "empty", target: { oid: "def456", tree: { entries: [{ name: "README.md" }] } } } + ] + })) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectDetails("acme", "my-project") + + expect(result!.versions).toHaveLength(1) + expect(result!.versions[0].name).toBe("main") + }) + }) + + describe("specifications", () => { + test("It creates specifications from YAML files", async () => { + const graphQlClient = createMockGraphQLClient(createRepositoryResponse({ + branches: [{ + name: "main", + target: { + oid: "abc123", + tree: { entries: [{ name: "api.yml" }, { name: "other.yaml" }] } + } + }] + })) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectDetails("acme", "my-project") + + expect(result!.versions[0].specifications).toHaveLength(2) + expect(result!.versions[0].specifications.map(s => s.name)).toContain("api.yml") + expect(result!.versions[0].specifications.map(s => s.name)).toContain("other.yaml") + }) + + test("It ignores non-YAML files", async () => { + const graphQlClient = createMockGraphQLClient(createRepositoryResponse({ + branches: [{ + name: "main", + target: { + oid: "abc123", + tree: { entries: [{ name: "api.yml" }, { name: "README.md" }, { name: "script.js" }] } + } + }] + })) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectDetails("acme", "my-project") + + expect(result!.versions[0].specifications).toHaveLength(1) + expect(result!.versions[0].specifications[0].name).toBe("api.yml") + }) + + test("It ignores hidden files (starting with dot)", async () => { + const graphQlClient = createMockGraphQLClient(createRepositoryResponse({ + branches: [{ + name: "main", + target: { + oid: "abc123", + tree: { entries: [{ name: "api.yml" }, { name: ".framna-docs.yml" }] } + } + }] + })) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectDetails("acme", "my-project") + + expect(result!.versions[0].specifications).toHaveLength(1) + expect(result!.versions[0].specifications[0].name).toBe("api.yml") + }) + + test("It generates correct URLs for specifications", async () => { + const graphQlClient = createMockGraphQLClient(createRepositoryResponse({ + branches: [{ + name: "feature/test", + target: { oid: "abc123", tree: { entries: [{ name: "api.yml" }] } } + }] + })) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectDetails("acme", "my-project") + + const spec = result!.versions[0].specifications[0] + expect(spec.url).toBe("/api/blob/acme/my-project-openapi/api.yml?ref=abc123") + expect(spec.editURL).toBe("https://github.com/acme/my-project-openapi/edit/feature/test/api.yml") + }) + + test("It sorts specifications alphabetically", async () => { + const graphQlClient = createMockGraphQLClient(createRepositoryResponse({ + branches: [{ + name: "main", + target: { + oid: "abc123", + tree: { entries: [{ name: "zebra.yml" }, { name: "alpha.yml" }, { name: "middle.yml" }] } + } + }] + })) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectDetails("acme", "my-project") + + expect(result!.versions[0].specifications.map(s => s.name)).toEqual(["alpha.yml", "middle.yml", "zebra.yml"]) + }) + }) + + describe("pull requests", () => { + test("It adds diff URL for changed files in PRs", async () => { + const graphQlClient = createMockGraphQLClient(createRepositoryResponse({ + branches: [{ + name: "feature-branch", + target: { oid: "feature123", tree: { entries: [{ name: "api.yml" }] } } + }], + pullRequests: [{ + number: 42, + headRefName: "feature-branch", + baseRefName: "main", + baseRefOid: "base123", + files: { nodes: [{ path: "api.yml" }] } + }] + })) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectDetails("acme", "my-project") + + const spec = result!.versions.find(v => v.name === "feature-branch")!.specifications[0] + expect(spec.diffURL).toBe("/api/diff/acme/my-project-openapi/api.yml?baseRefOid=base123&to=feature123") + expect(spec.diffBaseBranch).toBe("main") + expect(spec.diffPrUrl).toBe("https://github.com/acme/my-project-openapi/pull/42") + }) + + test("It does not add diff URL for unchanged files", async () => { + const graphQlClient = createMockGraphQLClient(createRepositoryResponse({ + branches: [{ + name: "feature-branch", + target: { oid: "feature123", tree: { entries: [{ name: "api.yml" }, { name: "other.yml" }] } } + }], + pullRequests: [{ + number: 42, + headRefName: "feature-branch", + baseRefName: "main", + baseRefOid: "base123", + files: { nodes: [{ path: "api.yml" }] } + }] + })) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectDetails("acme", "my-project") + + const specs = result!.versions.find(v => v.name === "feature-branch")!.specifications + const changedSpec = specs.find(s => s.name === "api.yml") + const unchangedSpec = specs.find(s => s.name === "other.yml") + + expect(changedSpec!.diffURL).toBeDefined() + expect(unchangedSpec!.diffURL).toBeUndefined() + }) + }) + + describe("remote versions", () => { + test("It creates versions from remote config", async () => { + const graphQlClient = createMockGraphQLClient(createRepositoryResponse({ + configYml: { + text: ` +remoteVersions: + - name: External API + specifications: + - name: External Spec + url: https://example.com/spec.yml +` + }, + branches: [] + })) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectDetails("acme", "my-project") + + expect(result!.versions).toHaveLength(1) + expect(result!.versions[0].name).toBe("External API") + expect(result!.versions[0].specifications).toHaveLength(1) + expect(result!.versions[0].specifications[0].name).toBe("External Spec") + }) + + test("It generates remote spec URLs through encoder", async () => { + const remoteConfigEncoder = createMockRemoteConfigEncoder() + const graphQlClient = createMockGraphQLClient(createRepositoryResponse({ + configYml: { + text: ` +remoteVersions: + - name: External + specifications: + - name: Spec + url: https://example.com/spec.yml +` + }, + branches: [] + })) + const sut = createSut({ graphQlClient, remoteConfigEncoder }) + + const result = await sut.getProjectDetails("acme", "my-project") + + expect(result!.versions[0].specifications[0].url).toBe("/api/remotes/encoded-config") + }) + }) + + describe("default specification", () => { + test("It sets isDefault on the correct specification based on config", async () => { + const graphQlClient = createMockGraphQLClient(createRepositoryResponse({ + configYml: { text: "defaultSpecificationName: bar-service.yml" }, + branches: [{ + name: "main", + target: { + oid: "abc123", + tree: { entries: [ + { name: "foo-service.yml" }, + { name: "bar-service.yml" }, + { name: "baz-service.yml" } + ]} + } + }] + })) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectDetails("acme", "my-project") + + const specs = result!.versions[0].specifications + expect(specs.find(s => s.name === "bar-service.yml")!.isDefault).toBe(true) + expect(specs.find(s => s.name === "foo-service.yml")!.isDefault).toBe(false) + expect(specs.find(s => s.name === "baz-service.yml")!.isDefault).toBe(false) + }) + + test("It sets isDefault to false for all specifications when no default is configured", async () => { + const graphQlClient = createMockGraphQLClient(createRepositoryResponse({ + configYml: null, + branches: [{ + name: "main", + target: { + oid: "abc123", + tree: { entries: [{ name: "api.yml" }, { name: "other.yml" }] } + } + }] + })) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectDetails("acme", "my-project") + + expect(result!.versions[0].specifications.every(s => s.isDefault === false)).toBe(true) + }) + + test("It silently ignores defaultSpecificationName if no matching spec is found", async () => { + const graphQlClient = createMockGraphQLClient(createRepositoryResponse({ + configYml: { text: "defaultSpecificationName: non-existent.yml" }, + branches: [{ + name: "main", + target: { + oid: "abc123", + tree: { entries: [{ name: "api.yml" }] } + } + }] + })) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectDetails("acme", "my-project") + + expect(result!.versions[0].specifications.every(s => s.isDefault === false)).toBe(true) + }) + }) +}) diff --git a/__test__/projects/GitHubProjectListDataSource.test.ts b/__test__/projects/GitHubProjectListDataSource.test.ts new file mode 100644 index 00000000..6567f251 --- /dev/null +++ b/__test__/projects/GitHubProjectListDataSource.test.ts @@ -0,0 +1,338 @@ +import { jest } from "@jest/globals" +import { GitHubProjectListDataSource } from "@/features/projects/data" +import { IGitHubLoginDataSource, IGitHubGraphQLClient } from "@/features/projects/domain" + +const createMockLoginsDataSource = (logins: string[] = []): IGitHubLoginDataSource => ({ + getLogins: jest.fn<() => Promise>().mockResolvedValue(logins) +}) + +const createMockGraphQLClient = (responses: Record[] = []): IGitHubGraphQLClient => { + let callIndex = 0 + return { + graphql: jest.fn<() => Promise>().mockImplementation(() => { + const response = responses[callIndex] || { search: { results: [], pageInfo: { hasNextPage: false } } } + callIndex++ + return Promise.resolve(response) + }) + } +} + +const createSut = (overrides: { + loginsDataSource?: IGitHubLoginDataSource + graphQlClient?: IGitHubGraphQLClient + repositoryNameSuffix?: string + projectConfigurationFilename?: string + hiddenRepositories?: string[] +} = {}) => { + return new GitHubProjectListDataSource({ + loginsDataSource: overrides.loginsDataSource || createMockLoginsDataSource(), + graphQlClient: overrides.graphQlClient || createMockGraphQLClient(), + repositoryNameSuffix: overrides.repositoryNameSuffix || "-openapi", + projectConfigurationFilename: overrides.projectConfigurationFilename || ".framna-docs.yml", + hiddenRepositories: overrides.hiddenRepositories || [] + }) +} + +describe("GitHubProjectListDataSource", () => { + test("It returns an empty list when no repositories are found", async () => { + const graphQlClient = createMockGraphQLClient([ + { search: { results: [], pageInfo: { hasNextPage: false } } } + ]) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectList() + + expect(result).toEqual([]) + }) + + test("It returns project summaries for repositories with matching suffix", async () => { + const graphQlClient = createMockGraphQLClient([ + { + search: { + results: [ + { name: "my-project-openapi", owner: { login: "acme" } } + ], + pageInfo: { hasNextPage: false } + } + } + ]) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectList() + + expect(result).toHaveLength(1) + expect(result[0]).toMatchObject({ + id: "acme-my-project", + name: "my-project", + displayName: "my-project", + owner: "acme", + url: "https://github.com/acme/my-project-openapi", + ownerUrl: "https://github.com/acme" + }) + }) + + test("It filters out repositories without matching suffix", async () => { + const graphQlClient = createMockGraphQLClient([ + { + search: { + results: [ + { name: "my-project-openapi", owner: { login: "acme" } }, + { name: "other-repo", owner: { login: "acme" } } + ], + pageInfo: { hasNextPage: false } + } + } + ]) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectList() + + expect(result).toHaveLength(1) + expect(result[0].name).toBe("my-project") + }) + + test("It uses display name from config when available", async () => { + const graphQlClient = createMockGraphQLClient([ + { + search: { + results: [ + { + name: "my-project-openapi", + owner: { login: "acme" }, + configYml: { text: "name: My Awesome Project" } + } + ], + pageInfo: { hasNextPage: false } + } + } + ]) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectList() + + expect(result[0].displayName).toBe("My Awesome Project") + }) + + test("It uses configYaml when configYml is not present", async () => { + const graphQlClient = createMockGraphQLClient([ + { + search: { + results: [ + { + name: "my-project-openapi", + owner: { login: "acme" }, + configYaml: { text: "name: YAML Config Name" } + } + ], + pageInfo: { hasNextPage: false } + } + } + ]) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectList() + + expect(result[0].displayName).toBe("YAML Config Name") + }) + + test("It generates image URL from config", async () => { + const graphQlClient = createMockGraphQLClient([ + { + search: { + results: [ + { + name: "my-project-openapi", + owner: { login: "acme" }, + configYml: { text: "image: logo.png" } + } + ], + pageInfo: { hasNextPage: false } + } + } + ]) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectList() + + expect(result[0].imageURL).toBe("/api/blob/acme/my-project-openapi/logo.png?ref=HEAD") + }) + + test("It encodes special characters in image path", async () => { + const graphQlClient = createMockGraphQLClient([ + { + search: { + results: [ + { + name: "my-project-openapi", + owner: { login: "acme" }, + configYml: { text: "image: images/my logo.png" } + } + ], + pageInfo: { hasNextPage: false } + } + } + ]) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectList() + + expect(result[0].imageURL).toBe("/api/blob/acme/my-project-openapi/images%2Fmy%20logo.png?ref=HEAD") + }) + + test("It deduplicates repositories from multiple search queries", async () => { + const loginsDataSource = createMockLoginsDataSource(["user1"]) + const graphQlClient = createMockGraphQLClient([ + // First query (private repos) + { + search: { + results: [{ name: "shared-openapi", owner: { login: "acme" } }], + pageInfo: { hasNextPage: false } + } + }, + // Second query (user1's public repos) + { + search: { + results: [{ name: "shared-openapi", owner: { login: "acme" } }], + pageInfo: { hasNextPage: false } + } + } + ]) + const sut = createSut({ loginsDataSource, graphQlClient }) + + const result = await sut.getProjectList() + + expect(result).toHaveLength(1) + }) + + test("It sorts projects alphabetically by name", async () => { + const graphQlClient = createMockGraphQLClient([ + { + search: { + results: [ + { name: "zebra-openapi", owner: { login: "acme" } }, + { name: "alpha-openapi", owner: { login: "acme" } }, + { name: "middle-openapi", owner: { login: "acme" } } + ], + pageInfo: { hasNextPage: false } + } + } + ]) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectList() + + expect(result.map(p => p.name)).toEqual(["alpha", "middle", "zebra"]) + }) + + test("It handles pagination", async () => { + const graphQlClient = createMockGraphQLClient([ + { + search: { + results: [{ name: "project-a-openapi", owner: { login: "acme" } }], + pageInfo: { hasNextPage: true, endCursor: "cursor1" } + } + }, + { + search: { + results: [{ name: "project-b-openapi", owner: { login: "acme" } }], + pageInfo: { hasNextPage: false } + } + } + ]) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectList() + + expect(result).toHaveLength(2) + expect(result.map(p => p.name)).toEqual(["project-a", "project-b"]) + }) + + test("It handles empty search results gracefully", async () => { + const graphQlClient = createMockGraphQLClient([ + { search: { results: null, pageInfo: { hasNextPage: false } } } + ]) + const sut = createSut({ graphQlClient }) + + const result = await sut.getProjectList() + + expect(result).toEqual([]) + }) + + test("It strips .yml extension from config filename", async () => { + const graphQlClient = createMockGraphQLClient([]) + const sut = createSut({ + graphQlClient, + projectConfigurationFilename: ".framna-docs.yml" + }) + + await sut.getProjectList() + + expect(graphQlClient.graphql).toHaveBeenCalledWith( + expect.objectContaining({ + query: expect.stringContaining("HEAD:.framna-docs.yml") + }) + ) + }) + + test("It strips .yaml extension from config filename", async () => { + const graphQlClient = createMockGraphQLClient([]) + const sut = createSut({ + graphQlClient, + projectConfigurationFilename: ".framna-docs.yaml" + }) + + await sut.getProjectList() + + expect(graphQlClient.graphql).toHaveBeenCalledWith( + expect.objectContaining({ + query: expect.stringContaining("HEAD:.framna-docs.yml") + }) + ) + }) + + test("It filters out hidden repositories", async () => { + const graphQlClient = createMockGraphQLClient([ + { + search: { + results: [ + { name: "visible-openapi", owner: { login: "acme" } }, + { name: "hidden-openapi", owner: { login: "acme" } }, + { name: "also-visible-openapi", owner: { login: "other" } } + ], + pageInfo: { hasNextPage: false } + } + } + ]) + const sut = createSut({ + graphQlClient, + hiddenRepositories: ["acme/hidden-openapi"] + }) + + const result = await sut.getProjectList() + + expect(result).toHaveLength(2) + expect(result.map(p => p.name)).toEqual(["also-visible", "visible"]) + }) + + test("It ignores invalid hidden repository entries", async () => { + const graphQlClient = createMockGraphQLClient([ + { + search: { + results: [ + { name: "project-openapi", owner: { login: "acme" } } + ], + pageInfo: { hasNextPage: false } + } + } + ]) + const sut = createSut({ + graphQlClient, + hiddenRepositories: ["invalid-entry", "", "also-invalid"] + }) + + const result = await sut.getProjectList() + + expect(result).toHaveLength(1) + expect(result[0].name).toBe("project") + }) +}) diff --git a/__test__/projects/GitHubRepositoryDataSource.test.ts b/__test__/projects/GitHubRepositoryDataSource.test.ts deleted file mode 100644 index 59148fc0..00000000 --- a/__test__/projects/GitHubRepositoryDataSource.test.ts +++ /dev/null @@ -1,220 +0,0 @@ -import { GitHubRepositoryDataSource } from "@/features/projects/data" - -test("It loads repositories from data source", async () => { - let didLoadRepositories = false - const sut = new GitHubRepositoryDataSource({ - repositoryNameSuffix: "-openapi", - projectConfigurationFilename: ".demo-docs.yml", - loginsDataSource: { - async getLogins() { - return ["acme"] - } - }, - graphQlClient: { - async graphql() { - didLoadRepositories = true - return { - search: { - results: [] - } - } - } - } - }) - await sut.getRepositories() - expect(didLoadRepositories).toBeTruthy() -}) - -test("It maps repositories from GraphQL to the GitHubRepository model", async () => { - const sut = new GitHubRepositoryDataSource({ - repositoryNameSuffix: "-openapi", - projectConfigurationFilename: ".demo-docs.yml", - loginsDataSource: { - async getLogins() { - return ["acme"] - } - }, - graphQlClient: { - async graphql() { - return { - search: { - results: [{ - name: "foo-openapi", - owner: { - login: "acme" - }, - defaultBranchRef: { - name: "main", - target: { - oid: "12345678" - } - }, - branches: { - edges: [{ - node: { - name: "main", - target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] - } - } - } - }] - }, - tags: { - edges: [{ - node: { - name: "1.0", - target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] - } - } - } - }] - } - }] - } - } - } - } - }) - const repositories = await sut.getRepositories() - expect(repositories).toEqual([{ - name: "foo-openapi", - owner: "acme", - defaultBranchRef: { - id: "12345678", - name: "main" - }, - branches: [{ - id: "12345678", - name: "main", - files: [{ - name: "openapi.yml" - }] - }], - tags: [{ - id: "12345678", - name: "1.0", - files: [{ - name: "openapi.yml" - }] - }] - }]) -}) - -test("It queries for both .yml and .yaml file extension with specifying .yml extension", async () => { - let query: string | undefined - const sut = new GitHubRepositoryDataSource({ - repositoryNameSuffix: "-openapi", - projectConfigurationFilename: ".demo-docs.yml", - loginsDataSource: { - async getLogins() { - return ["acme"] - } - }, - graphQlClient: { - async graphql(request) { - query = request.query - return { - search: { - results: [] - } - } - } - } - }) - await sut.getRepositories() - expect(query).toContain(".demo-docs.yml") - expect(query).toContain(".demo-docs.yaml") -}) - -test("It queries for both .yml and .yaml file extension with specifying .yaml extension", async () => { - let query: string | undefined - const sut = new GitHubRepositoryDataSource({ - repositoryNameSuffix: "-openapi", - projectConfigurationFilename: ".demo-docs.yml", - loginsDataSource: { - async getLogins() { - return ["acme"] - } - }, - graphQlClient: { - async graphql(request) { - query = request.query - return { - search: { - results: [] - } - } - } - } - }) - await sut.getRepositories() - expect(query).toContain(".demo-docs.yml") - expect(query).toContain(".demo-docs.yaml") -}) - -test("It queries for both .yml and .yaml file extension with no extension", async () => { - let query: string | undefined - const sut = new GitHubRepositoryDataSource({ - repositoryNameSuffix: "-openapi", - projectConfigurationFilename: ".demo-docs", - loginsDataSource: { - async getLogins() { - return ["acme"] - } - }, - graphQlClient: { - async graphql(request) { - query = request.query - return { - search: { - results: [] - } - } - } - } - }) - await sut.getRepositories() - expect(query).toContain(".demo-docs.yml") - expect(query).toContain(".demo-docs.yaml") -}) - -test("It loads repositories for all logins", async () => { - const searchQueries: string[] = [] - const sut = new GitHubRepositoryDataSource({ - repositoryNameSuffix: "-openapi", - projectConfigurationFilename: ".demo-docs", - loginsDataSource: { - async getLogins() { - return ["acme", "somecorp", "techsystems"] - } - }, - graphQlClient: { - async graphql(request) { - if (request.variables?.searchQuery) { - searchQueries.push(request.variables.searchQuery) - } - return { - search: { - results: [] - } - } - } - } - }) - await sut.getRepositories() - expect(searchQueries.length).toEqual(4) - expect(searchQueries).toContain("\"-openapi\" in:name is:private") - expect(searchQueries).toContain("\"-openapi\" in:name user:acme is:public") - expect(searchQueries).toContain("\"-openapi\" in:name user:somecorp is:public") - expect(searchQueries).toContain("\"-openapi\" in:name user:techsystems is:public") -}) diff --git a/jest.config.js b/jest.config.js index 67d061c6..ada31bd3 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,6 +1,6 @@ module.exports = { clearMocks: true, - moduleFileExtensions: ["js", "ts"], + moduleFileExtensions: ["js", "ts", "tsx"], testEnvironment: "node", testMatch: ["**/*.test.ts"], extensionsToTreatAsEsm: [".ts"], diff --git a/package-lock.json b/package-lock.json index 5db706a0..d6f8b8a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,57 +11,57 @@ "@emotion/react": "^11.13.3", "@emotion/styled": "^11.14.1", "@fontsource/poppins": "^5.2.7", - "@fortawesome/fontawesome-svg-core": "^7.1.0", - "@fortawesome/free-brands-svg-icons": "^7.1.0", - "@fortawesome/free-regular-svg-icons": "^7.1.0", - "@fortawesome/free-solid-svg-icons": "^7.1.0", - "@fortawesome/react-fontawesome": "^3.1.1", - "@mui/icons-material": "^7.3.6", + "@fortawesome/fontawesome-svg-core": "^7.2.0", + "@fortawesome/free-brands-svg-icons": "^7.2.0", + "@fortawesome/free-regular-svg-icons": "^7.2.0", + "@fortawesome/free-solid-svg-icons": "^7.2.0", + "@fortawesome/react-fontawesome": "^3.3.0", + "@mui/icons-material": "^7.3.9", "@mui/material": "^7.0.1", - "@octokit/auth-app": "^8.1.2", + "@octokit/auth-app": "^8.2.0", "@octokit/core": "^7.0.6", "@octokit/webhooks": "~14.2.0", - "core-js": "^3.47.0", + "core-js": "^3.49.0", "encoding": "^0.1.13", "figma-squircle": "^1.1.0", "install": "^0.13.0", - "ioredis": "^5.8.2", + "ioredis": "^5.10.1", "mobx": "^6.15.0", - "next": "16.1.1", + "next": "16.2.3", "next-auth": "^5.0.0-beta.30", - "npm": "^11.7.0", + "npm": "^11.12.1", "nprogress": "^0.2.0", "octokit": "^5.0.5", - "react": "^19.2.3", - "react-dom": "^19.2.3", - "redis-semaphore": "^5.6.2", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "redis-semaphore": "^5.7.0", "redoc": "^2.5.2", "sharp": "^0.34.2", - "styled-components": "^6.1.19", - "swr": "^2.3.8", + "styled-components": "^6.3.12", + "swr": "^2.4.1", "usehooks-ts": "^3.1.1", - "yaml": "^2.8.2", - "zod": "^4.3.4" + "yaml": "^2.8.3", + "zod": "^4.3.6" }, "devDependencies": { "@auth/pg-adapter": "^1.11.1", - "@tailwindcss/postcss": "^4.1.18", + "@tailwindcss/postcss": "^4.2.2", "@types/jest": "^30.0.0", - "@types/node": "^25.0.3", + "@types/node": "^25.5.0", "@types/nprogress": "^0.2.3", - "@types/pg": "^8.16.0", - "@types/react": "^19.2.7", + "@types/pg": "^8.20.0", + "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", - "@typescript-eslint/eslint-plugin": "^8.51.0", - "@typescript-eslint/parser": "^8.51.0", - "eslint": "^9.39.2", - "eslint-config-next": "^16.1.1", - "pg": "^8.16.3", - "postcss": "^8.5.6", + "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/parser": "^8.57.2", + "eslint": "^9.39.4", + "eslint-config-next": "^16.2.1", + "pg": "^8.20.0", + "postcss": "^8.5.10", "tailwindcss": "^4.1.4", "ts-jest": "^29.4.6", "typescript": "^5.9.3", - "typescript-eslint": "^8.51.0" + "typescript-eslint": "^8.57.2" }, "engines": { "node": ">=24.0.0 <25.0.0", @@ -154,7 +154,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -286,6 +285,7 @@ "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6.9.0" } @@ -353,6 +353,7 @@ "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -366,6 +367,7 @@ "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -379,6 +381,7 @@ "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.12.13" }, @@ -392,6 +395,7 @@ "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -408,6 +412,7 @@ "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -424,6 +429,7 @@ "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -437,6 +443,7 @@ "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -450,6 +457,7 @@ "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -466,6 +474,7 @@ "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -479,6 +488,7 @@ "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -492,6 +502,7 @@ "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -505,6 +516,7 @@ "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -518,6 +530,7 @@ "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -531,6 +544,7 @@ "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -544,6 +558,7 @@ "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -560,6 +575,7 @@ "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -576,6 +592,7 @@ "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -587,9 +604,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", - "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -645,7 +662,8 @@ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@emnapi/core": { "version": "1.7.1", @@ -738,7 +756,6 @@ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -782,7 +799,6 @@ "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -829,9 +845,9 @@ "license": "MIT" }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", - "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -858,24 +874,24 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", "dev": true, "license": "Apache-2.0", "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", - "minimatch": "^3.1.2" + "minimatch": "^3.1.5" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { @@ -884,9 +900,9 @@ } }, "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -923,20 +939,20 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "^6.12.4", + "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, "engines": { @@ -947,9 +963,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { @@ -968,9 +984,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -981,9 +997,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", - "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", "dev": true, "license": "MIT", "engines": { @@ -1033,67 +1049,66 @@ } }, "node_modules/@fortawesome/fontawesome-common-types": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-7.1.0.tgz", - "integrity": "sha512-l/BQM7fYntsCI//du+6sEnHOP6a74UixFyOYUyz2DLMXKx+6DEhfR3F2NYGE45XH1JJuIamacb4IZs9S0ZOWLA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-7.2.0.tgz", + "integrity": "sha512-IpR0bER9FY25p+e7BmFH25MZKEwFHTfRAfhOyJubgiDnoJNsSvJ7nigLraHtp4VOG/cy8D7uiV0dLkHOne5Fhw==", "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/@fortawesome/fontawesome-svg-core": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-7.1.0.tgz", - "integrity": "sha512-fNxRUk1KhjSbnbuBxlWSnBLKLBNun52ZBTcs22H/xEEzM6Ap81ZFTQ4bZBxVQGQgVY0xugKGoRcCbaKjLQ3XZA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-7.2.0.tgz", + "integrity": "sha512-6639htZMjEkwskf3J+e6/iar+4cTNM9qhoWuRfj9F3eJD6r7iCzV1SWnQr2Mdv0QT0suuqU8BoJCZUyCtP9R4Q==", "license": "MIT", - "peer": true, "dependencies": { - "@fortawesome/fontawesome-common-types": "7.1.0" + "@fortawesome/fontawesome-common-types": "7.2.0" }, "engines": { "node": ">=6" } }, "node_modules/@fortawesome/free-brands-svg-icons": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-7.1.0.tgz", - "integrity": "sha512-9byUd9bgNfthsZAjBl6GxOu1VPHgBuRUP9juI7ZoM98h8xNPTCTagfwUFyYscdZq4Hr7gD1azMfM9s5tIWKZZA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-7.2.0.tgz", + "integrity": "sha512-VNG8xqOip1JuJcC3zsVsKRQ60oXG9+oYNDCosjoU/H9pgYmLTEwWw8pE0jhPz/JWdHeUuK6+NQ3qsM4gIbdbYQ==", "license": "(CC-BY-4.0 AND MIT)", "dependencies": { - "@fortawesome/fontawesome-common-types": "7.1.0" + "@fortawesome/fontawesome-common-types": "7.2.0" }, "engines": { "node": ">=6" } }, "node_modules/@fortawesome/free-regular-svg-icons": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-7.1.0.tgz", - "integrity": "sha512-0e2fdEyB4AR+e6kU4yxwA/MonnYcw/CsMEP9lH82ORFi9svA6/RhDyhxIv5mlJaldmaHLLYVTb+3iEr+PDSZuQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-7.2.0.tgz", + "integrity": "sha512-iycmlN51EULlQ4D/UU9WZnHiN0CvjJ2TuuCrAh+1MVdzD+4ViKYH2deNAll4XAAYlZa8WAefHR5taSK8hYmSMw==", "license": "(CC-BY-4.0 AND MIT)", "dependencies": { - "@fortawesome/fontawesome-common-types": "7.1.0" + "@fortawesome/fontawesome-common-types": "7.2.0" }, "engines": { "node": ">=6" } }, "node_modules/@fortawesome/free-solid-svg-icons": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-7.1.0.tgz", - "integrity": "sha512-Udu3K7SzAo9N013qt7qmm22/wo2hADdheXtBfxFTecp+ogsc0caQNRKEb7pkvvagUGOpG9wJC1ViH6WXs8oXIA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-7.2.0.tgz", + "integrity": "sha512-YTVITFGN0/24PxzXrwqCgnyd7njDuzp5ZvaCx5nq/jg55kUYd94Nj8UTchBdBofi/L0nwRfjGOg0E41d2u9T1w==", "license": "(CC-BY-4.0 AND MIT)", "dependencies": { - "@fortawesome/fontawesome-common-types": "7.1.0" + "@fortawesome/fontawesome-common-types": "7.2.0" }, "engines": { "node": ">=6" } }, "node_modules/@fortawesome/react-fontawesome": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-3.1.1.tgz", - "integrity": "sha512-EDllr9hpodc21odmUywHS1alXNiCd4E8sp5GJ5s7wYINz8vSmMiNWpALTiuYODb865YyQ/NlyiN4mbXp7HCNqg==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-3.3.0.tgz", + "integrity": "sha512-EHmHeTf8WgO29sdY3iX/7ekE3gNUdlc2RW6mm/FzELlHFKfTrA9S4MlyquRR+RRCRCn8+jXfLFpLGB2l7wCWyw==", "license": "MIT", "engines": { "node": ">=20" @@ -1621,9 +1636,9 @@ } }, "node_modules/@ioredis/commands": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.4.0.tgz", - "integrity": "sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.5.1.tgz", + "integrity": "sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==", "license": "MIT" }, "node_modules/@isaacs/cliui": { @@ -1632,6 +1647,7 @@ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -1650,6 +1666,7 @@ "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "camelcase": "^5.3.1", "find-up": "^4.1.0", @@ -1667,6 +1684,7 @@ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "sprintf-js": "~1.0.2" } @@ -1677,6 +1695,7 @@ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -1691,6 +1710,7 @@ "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -1705,6 +1725,7 @@ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "p-locate": "^4.1.0" }, @@ -1718,6 +1739,7 @@ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "p-try": "^2.0.0" }, @@ -1734,6 +1756,7 @@ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "p-limit": "^2.2.0" }, @@ -1747,6 +1770,7 @@ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -1757,6 +1781,7 @@ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -1767,6 +1792,7 @@ "integrity": "sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", @@ -1785,6 +1811,7 @@ "integrity": "sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/console": "30.2.0", "@jest/pattern": "30.0.1", @@ -1843,6 +1870,7 @@ "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/fake-timers": "30.2.0", "@jest/types": "30.2.0", @@ -1859,6 +1887,7 @@ "integrity": "sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "expect": "30.2.0", "jest-snapshot": "30.2.0" @@ -1886,6 +1915,7 @@ "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/types": "30.2.0", "@sinonjs/fake-timers": "^13.0.0", @@ -1914,6 +1944,7 @@ "integrity": "sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/environment": "30.2.0", "@jest/expect": "30.2.0", @@ -1944,6 +1975,7 @@ "integrity": "sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", "@jest/console": "30.2.0", @@ -2000,6 +2032,7 @@ "integrity": "sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/types": "30.2.0", "chalk": "^4.1.2", @@ -2016,6 +2049,7 @@ "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "callsites": "^3.1.0", @@ -2031,6 +2065,7 @@ "integrity": "sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/console": "30.2.0", "@jest/types": "30.2.0", @@ -2047,6 +2082,7 @@ "integrity": "sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/test-result": "30.2.0", "graceful-fs": "^4.2.11", @@ -2063,6 +2099,7 @@ "integrity": "sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/core": "^7.27.4", "@jest/types": "30.2.0", @@ -2089,7 +2126,8 @@ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@jest/types": { "version": "30.2.0", @@ -2157,9 +2195,9 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.6.tgz", - "integrity": "sha512-QaYtTHlr8kDFN5mE1wbvVARRKH7Fdw1ZuOjBJcFdVpfNfRYKF3QLT4rt+WaB6CKJvpqxRsmEo0kpYinhH5GeHg==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.9.tgz", + "integrity": "sha512-MOkOCTfbMJwLshlBCKJ59V2F/uaLYfmKnN76kksj6jlGUVdI25A9Hzs08m+zjBRdLv+sK7Rqdsefe8X7h/6PCw==", "license": "MIT", "funding": { "type": "opencollective", @@ -2167,12 +2205,12 @@ } }, "node_modules/@mui/icons-material": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.3.6.tgz", - "integrity": "sha512-0FfkXEj22ysIq5pa41A2NbcAhJSvmcZQ/vcTIbjDsd6hlslG82k5BEBqqS0ZJprxwIL3B45qpJ+bPHwJPlF7uQ==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.3.9.tgz", + "integrity": "sha512-BT+zPJXss8Hg/oEMRmHl17Q97bPACG4ufFSfGEdhiE96jOyR5Dz1ty7ZWt1fVGR0y1p+sSgEwQT/MNZQmoWDCw==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.4" + "@babel/runtime": "^7.28.6" }, "engines": { "node": ">=14.0.0" @@ -2182,7 +2220,7 @@ "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@mui/material": "^7.3.6", + "@mui/material": "^7.3.9", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -2193,23 +2231,22 @@ } }, "node_modules/@mui/material": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.6.tgz", - "integrity": "sha512-R4DaYF3dgCQCUAkr4wW1w26GHXcf5rCmBRHVBuuvJvaGLmZdD8EjatP80Nz5JCw0KxORAzwftnHzXVnjR8HnFw==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.9.tgz", + "integrity": "sha512-I8yO3t4T0y7bvDiR1qhIN6iBWZOTBfVOnmLlM7K6h3dx5YX2a7rnkuXzc2UkZaqhxY9NgTnEbdPlokR1RxCNRQ==", "license": "MIT", - "peer": true, "dependencies": { - "@babel/runtime": "^7.28.4", - "@mui/core-downloads-tracker": "^7.3.6", - "@mui/system": "^7.3.6", - "@mui/types": "^7.4.9", - "@mui/utils": "^7.3.6", + "@babel/runtime": "^7.28.6", + "@mui/core-downloads-tracker": "^7.3.9", + "@mui/system": "^7.3.9", + "@mui/types": "^7.4.12", + "@mui/utils": "^7.3.9", "@popperjs/core": "^2.11.8", "@types/react-transition-group": "^4.4.12", "clsx": "^2.1.1", - "csstype": "^3.1.3", + "csstype": "^3.2.3", "prop-types": "^15.8.1", - "react-is": "^19.2.0", + "react-is": "^19.2.3", "react-transition-group": "^4.4.5" }, "engines": { @@ -2222,7 +2259,7 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material-pigment-css": "^7.3.6", + "@mui/material-pigment-css": "^7.3.9", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -2243,13 +2280,13 @@ } }, "node_modules/@mui/private-theming": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.3.6.tgz", - "integrity": "sha512-Ws9wZpqM+FlnbZXaY/7yvyvWQo1+02Tbx50mVdNmzWEi51C51y56KAbaDCYyulOOBL6BJxuaqG8rNNuj7ivVyw==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.3.9.tgz", + "integrity": "sha512-ErIyRQvsiQEq7Yvcvfw9UDHngaqjMy9P3JDPnRAaKG5qhpl2C4tX/W1S4zJvpu+feihmZJStjIyvnv6KDbIrlw==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.4", - "@mui/utils": "^7.3.6", + "@babel/runtime": "^7.28.6", + "@mui/utils": "^7.3.9", "prop-types": "^15.8.1" }, "engines": { @@ -2270,16 +2307,16 @@ } }, "node_modules/@mui/styled-engine": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.3.6.tgz", - "integrity": "sha512-+wiYbtvj+zyUkmDB+ysH6zRjuQIJ+CM56w0fEXV+VDNdvOuSywG+/8kpjddvvlfMLsaWdQe5oTuYGBcodmqGzQ==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.3.9.tgz", + "integrity": "sha512-JqujWt5bX4okjUPGpVof/7pvgClqh7HvIbsIBIOOlCh2u3wG/Bwp4+E1bc1dXSwkrkp9WUAoNdI5HEC+5HKvMw==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.4", + "@babel/runtime": "^7.28.6", "@emotion/cache": "^11.14.0", "@emotion/serialize": "^1.3.3", "@emotion/sheet": "^1.4.0", - "csstype": "^3.1.3", + "csstype": "^3.2.3", "prop-types": "^15.8.1" }, "engines": { @@ -2304,18 +2341,18 @@ } }, "node_modules/@mui/system": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.3.6.tgz", - "integrity": "sha512-8fehAazkHNP1imMrdD2m2hbA9sl7Ur6jfuNweh5o4l9YPty4iaZzRXqYvBCWQNwFaSHmMEj2KPbyXGp7Bt73Rg==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.3.9.tgz", + "integrity": "sha512-aL1q9am8XpRrSabv9qWf5RHhJICJql34wnrc1nz0MuOglPRYF/liN+c8VqZdTvUn9qg+ZjRVbKf4sJVFfIDtmg==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.4", - "@mui/private-theming": "^7.3.6", - "@mui/styled-engine": "^7.3.6", - "@mui/types": "^7.4.9", - "@mui/utils": "^7.3.6", + "@babel/runtime": "^7.28.6", + "@mui/private-theming": "^7.3.9", + "@mui/styled-engine": "^7.3.9", + "@mui/types": "^7.4.12", + "@mui/utils": "^7.3.9", "clsx": "^2.1.1", - "csstype": "^3.1.3", + "csstype": "^3.2.3", "prop-types": "^15.8.1" }, "engines": { @@ -2344,12 +2381,12 @@ } }, "node_modules/@mui/types": { - "version": "7.4.9", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.9.tgz", - "integrity": "sha512-dNO8Z9T2cujkSIaCnWwprfeKmTWh97cnjkgmpFJ2sbfXLx8SMZijCYHOtP/y5nnUb/Rm2omxbDMmtUoSaUtKaw==", + "version": "7.4.12", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.12.tgz", + "integrity": "sha512-iKNAF2u9PzSIj40CjvKJWxFXJo122jXVdrmdh0hMYd+FR+NuJMkr/L88XwWLCRiJ5P1j+uyac25+Kp6YC4hu6w==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.4" + "@babel/runtime": "^7.28.6" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -2361,17 +2398,17 @@ } }, "node_modules/@mui/utils": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.6.tgz", - "integrity": "sha512-jn+Ba02O6PiFs7nKva8R2aJJ9kJC+3kQ2R0BbKNY3KQQ36Qng98GnPRFTlbwYTdMD6hLEBKaMLUktyg/rTfd2w==", + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.9.tgz", + "integrity": "sha512-U6SdZaGbfb65fqTsH3V5oJdFj9uYwyLE2WVuNvmbggTSDBb8QHrFsqY8BN3taK9t3yJ8/BPHD/kNvLNyjwM7Yw==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.4", - "@mui/types": "^7.4.9", + "@babel/runtime": "^7.28.6", + "@mui/types": "^7.4.12", "@types/prop-types": "^15.7.15", "clsx": "^2.1.1", "prop-types": "^15.8.1", - "react-is": "^19.2.0" + "react-is": "^19.2.3" }, "engines": { "node": ">=14.0.0" @@ -2404,15 +2441,15 @@ } }, "node_modules/@next/env": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.1.tgz", - "integrity": "sha512-3oxyM97Sr2PqiVyMyrZUtrtM3jqqFxOQJVuKclDsgj/L728iZt/GyslkN4NwarledZATCenbk4Offjk1hQmaAA==", + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.3.tgz", + "integrity": "sha512-ZWXyj4uNu4GCWQw9cjRxWlbD+33mcDszIo9iQxFnBX3Wmgq9ulaSJcl6VhuWx5pCWqqD+9W6Wfz7N0lM5lYPMA==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.1.1.tgz", - "integrity": "sha512-Ovb/6TuLKbE1UiPcg0p39Ke3puyTCIKN9hGbNItmpQsp+WX3qrjO3WaMVSi6JHr9X1NrmthqIguVHodMJbh/dw==", + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.1.tgz", + "integrity": "sha512-r0epZGo24eT4g08jJlg2OEryBphXqO8aL18oajoTKLzHJ6jVr6P6FI58DLMug04MwD3j8Fj0YK0slyzneKVyzA==", "dev": true, "license": "MIT", "dependencies": { @@ -2420,9 +2457,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.1.tgz", - "integrity": "sha512-JS3m42ifsVSJjSTzh27nW+Igfha3NdBOFScr9C80hHGrWx55pTrVL23RJbqir7k7/15SKlrLHhh/MQzqBBYrQA==", + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.3.tgz", + "integrity": "sha512-u37KDKTKQ+OQLvY+z7SNXixwo4Q2/IAJFDzU1fYe66IbCE51aDSAzkNDkWmLN0yjTUh4BKBd+hb69jYn6qqqSg==", "cpu": [ "arm64" ], @@ -2436,9 +2473,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.1.tgz", - "integrity": "sha512-hbyKtrDGUkgkyQi1m1IyD3q4I/3m9ngr+V93z4oKHrPcmxwNL5iMWORvLSGAf2YujL+6HxgVvZuCYZfLfb4bGw==", + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.3.tgz", + "integrity": "sha512-gHjL/qy6Q6CG3176FWbAKyKh9IfntKZTB3RY/YOJdDFpHGsUDXVH38U4mMNpHVGXmeYW4wj22dMp1lTfmu/bTQ==", "cpu": [ "x64" ], @@ -2452,9 +2489,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.1.tgz", - "integrity": "sha512-/fvHet+EYckFvRLQ0jPHJCUI5/B56+2DpI1xDSvi80r/3Ez+Eaa2Yq4tJcRTaB1kqj/HrYKn8Yplm9bNoMJpwQ==", + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.3.tgz", + "integrity": "sha512-U6vtblPtU/P14Y/b/n9ZY0GOxbbIhTFuaFR7F4/uMBidCi2nSdaOFhA0Go81L61Zd6527+yvuX44T4ksnf8T+Q==", "cpu": [ "arm64" ], @@ -2468,9 +2505,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.1.tgz", - "integrity": "sha512-MFHrgL4TXNQbBPzkKKur4Fb5ICEJa87HM7fczFs2+HWblM7mMLdco3dvyTI+QmLBU9xgns/EeeINSZD6Ar+oLg==", + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.3.tgz", + "integrity": "sha512-/YV0LgjHUmfhQpn9bVoGc4x4nan64pkhWR5wyEV8yCOfwwrH630KpvRg86olQHTwHIn1z59uh6JwKvHq1h4QEw==", "cpu": [ "arm64" ], @@ -2484,9 +2521,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.1.tgz", - "integrity": "sha512-20bYDfgOQAPUkkKBnyP9PTuHiJGM7HzNBbuqmD0jiFVZ0aOldz+VnJhbxzjcSabYsnNjMPsE0cyzEudpYxsrUQ==", + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.3.tgz", + "integrity": "sha512-/HiWEcp+WMZ7VajuiMEFGZ6cg0+aYZPqCJD3YJEfpVWQsKYSjXQG06vJP6F1rdA03COD9Fef4aODs3YxKx+RDQ==", "cpu": [ "x64" ], @@ -2500,9 +2537,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.1.tgz", - "integrity": "sha512-9pRbK3M4asAHQRkwaXwu601oPZHghuSC8IXNENgbBSyImHv/zY4K5udBusgdHkvJ/Tcr96jJwQYOll0qU8+fPA==", + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.3.tgz", + "integrity": "sha512-Kt44hGJfZSefebhk/7nIdivoDr3Ugp5+oNz9VvF3GUtfxutucUIHfIO0ZYO8QlOPDQloUVQn4NVC/9JvHRk9hw==", "cpu": [ "x64" ], @@ -2516,9 +2553,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.1.tgz", - "integrity": "sha512-bdfQkggaLgnmYrFkSQfsHfOhk/mCYmjnrbRCGgkMcoOBZ4n+TRRSLmT/CU5SATzlBJ9TpioUyBW/vWFXTqQRiA==", + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.3.tgz", + "integrity": "sha512-O2NZ9ie3Tq6xj5Z5CSwBT3+aWAMW2PIZ4egUi9MaWLkwaehgtB7YZjPm+UpcNpKOme0IQuqDcor7BsW6QBiQBw==", "cpu": [ "arm64" ], @@ -2532,9 +2569,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.1.tgz", - "integrity": "sha512-Ncwbw2WJ57Al5OX0k4chM68DKhEPlrXBaSXDCi2kPi5f4d8b3ejr3RRJGfKBLrn2YJL5ezNS7w2TZLHSti8CMw==", + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.3.tgz", + "integrity": "sha512-Ibm29/GgB/ab5n7XKqlStkm54qqZE8v2FnijUPBgrd67FWrac45o/RsNlaOWjme/B5UqeWt/8KM4aWBwA1D2Kw==", "cpu": [ "x64" ], @@ -2614,9 +2651,9 @@ } }, "node_modules/@octokit/auth-app": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-8.1.2.tgz", - "integrity": "sha512-db8VO0PqXxfzI6GdjtgEFHY9tzqUql5xMFXYA12juq8TeTgPAuiiP3zid4h50lwlIP457p5+56PnJOgd2GGBuw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-8.2.0.tgz", + "integrity": "sha512-vVjdtQQwomrZ4V46B9LaCsxsySxGoHsyw6IYBov/TqJVROrlYdyNgw5q6tQbB7KZt53v1l1W53RiqTvpzL907g==", "license": "MIT", "dependencies": { "@octokit/auth-oauth-app": "^9.0.3", @@ -2706,7 +2743,6 @@ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz", "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", "license": "MIT", - "peer": true, "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", @@ -2953,6 +2989,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=14" } @@ -2963,6 +3000,7 @@ "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": "^12.20.0 || ^14.18.0 || >=16.0.0" }, @@ -3030,9 +3068,9 @@ } }, "node_modules/@redocly/openapi-core/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.8.tgz", + "integrity": "sha512-7RN35vit8DeBclkofOVmBY0eDAZZQd1HzmukRdSyz95CRh8FT54eqnbj0krQr3mrHR6sfRyYkyhwBWjoV5uqlQ==", "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -3061,6 +3099,7 @@ "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "dependencies": { "type-detect": "4.0.8" } @@ -3071,6 +3110,7 @@ "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "dependencies": { "@sinonjs/commons": "^3.0.1" } @@ -3085,49 +3125,49 @@ } }, "node_modules/@tailwindcss/node": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz", - "integrity": "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz", + "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/remapping": "^2.3.4", - "enhanced-resolve": "^5.18.3", + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", - "lightningcss": "1.30.2", + "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", - "tailwindcss": "4.1.18" + "tailwindcss": "4.2.2" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.18.tgz", - "integrity": "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.2.tgz", + "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==", "dev": true, "license": "MIT", "engines": { - "node": ">= 10" + "node": ">= 20" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.18", - "@tailwindcss/oxide-darwin-arm64": "4.1.18", - "@tailwindcss/oxide-darwin-x64": "4.1.18", - "@tailwindcss/oxide-freebsd-x64": "4.1.18", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", - "@tailwindcss/oxide-linux-x64-musl": "4.1.18", - "@tailwindcss/oxide-wasm32-wasi": "4.1.18", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" + "@tailwindcss/oxide-android-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-x64": "4.2.2", + "@tailwindcss/oxide-freebsd-x64": "4.2.2", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-x64-musl": "4.2.2", + "@tailwindcss/oxide-wasm32-wasi": "4.2.2", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.18.tgz", - "integrity": "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz", + "integrity": "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==", "cpu": [ "arm64" ], @@ -3138,13 +3178,13 @@ "android" ], "engines": { - "node": ">= 10" + "node": ">= 20" } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.18.tgz", - "integrity": "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz", + "integrity": "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==", "cpu": [ "arm64" ], @@ -3155,13 +3195,13 @@ "darwin" ], "engines": { - "node": ">= 10" + "node": ">= 20" } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.18.tgz", - "integrity": "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz", + "integrity": "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==", "cpu": [ "x64" ], @@ -3172,13 +3212,13 @@ "darwin" ], "engines": { - "node": ">= 10" + "node": ">= 20" } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.18.tgz", - "integrity": "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz", + "integrity": "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==", "cpu": [ "x64" ], @@ -3189,13 +3229,13 @@ "freebsd" ], "engines": { - "node": ">= 10" + "node": ">= 20" } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.18.tgz", - "integrity": "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz", + "integrity": "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==", "cpu": [ "arm" ], @@ -3206,13 +3246,13 @@ "linux" ], "engines": { - "node": ">= 10" + "node": ">= 20" } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.18.tgz", - "integrity": "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz", + "integrity": "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==", "cpu": [ "arm64" ], @@ -3223,13 +3263,13 @@ "linux" ], "engines": { - "node": ">= 10" + "node": ">= 20" } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.18.tgz", - "integrity": "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz", + "integrity": "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==", "cpu": [ "arm64" ], @@ -3240,13 +3280,13 @@ "linux" ], "engines": { - "node": ">= 10" + "node": ">= 20" } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz", - "integrity": "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz", + "integrity": "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==", "cpu": [ "x64" ], @@ -3257,13 +3297,13 @@ "linux" ], "engines": { - "node": ">= 10" + "node": ">= 20" } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz", - "integrity": "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz", + "integrity": "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==", "cpu": [ "x64" ], @@ -3274,13 +3314,13 @@ "linux" ], "engines": { - "node": ">= 10" + "node": ">= 20" } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.18.tgz", - "integrity": "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz", + "integrity": "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -3296,19 +3336,19 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1", + "@emnapi/core": "^1.8.1", + "@emnapi/runtime": "^1.8.1", "@emnapi/wasi-threads": "^1.1.0", - "@napi-rs/wasm-runtime": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.1", "@tybys/wasm-util": "^0.10.1", - "tslib": "^2.4.0" + "tslib": "^2.8.1" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.7.1", + "version": "1.8.1", "dev": true, "inBundle": true, "license": "MIT", @@ -3319,7 +3359,7 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.7.1", + "version": "1.8.1", "dev": true, "inBundle": true, "license": "MIT", @@ -3339,7 +3379,7 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.0", + "version": "1.1.1", "dev": true, "inBundle": true, "license": "MIT", @@ -3348,6 +3388,10 @@ "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" } }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { @@ -3368,9 +3412,9 @@ "optional": true }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz", - "integrity": "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz", + "integrity": "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==", "cpu": [ "arm64" ], @@ -3381,13 +3425,13 @@ "win32" ], "engines": { - "node": ">= 10" + "node": ">= 20" } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.18.tgz", - "integrity": "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz", + "integrity": "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==", "cpu": [ "x64" ], @@ -3398,21 +3442,21 @@ "win32" ], "engines": { - "node": ">= 10" + "node": ">= 20" } }, "node_modules/@tailwindcss/postcss": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.18.tgz", - "integrity": "sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.2.2.tgz", + "integrity": "sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==", "dev": true, "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.1.18", - "@tailwindcss/oxide": "4.1.18", - "postcss": "^8.4.41", - "tailwindcss": "4.1.18" + "@tailwindcss/node": "4.2.2", + "@tailwindcss/oxide": "4.2.2", + "postcss": "^8.5.6", + "tailwindcss": "4.2.2" } }, "node_modules/@tybys/wasm-util": { @@ -3438,6 +3482,7 @@ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", @@ -3452,6 +3497,7 @@ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/types": "^7.0.0" } @@ -3462,6 +3508,7 @@ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" @@ -3473,6 +3520,7 @@ "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/types": "^7.28.2" } @@ -3536,13 +3584,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.0.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz", - "integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==", + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", + "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.16.0" + "undici-types": "~7.18.0" } }, "node_modules/@types/nprogress": { @@ -3559,9 +3607,9 @@ "license": "MIT" }, "node_modules/@types/pg": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.16.0.tgz", - "integrity": "sha512-RmhMd/wD+CF8Dfo+cVIy3RR5cl8CyfXQ0tGgW6XBL8L4LM/UTEbNXYRbLwU6w+CgrKBNbrQWt4FUtTfaU5jSYQ==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.20.0.tgz", + "integrity": "sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==", "dev": true, "license": "MIT", "dependencies": { @@ -3577,11 +3625,10 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.2.7", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", - "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -3613,9 +3660,9 @@ "license": "MIT" }, "node_modules/@types/stylis": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", - "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==", + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.7.tgz", + "integrity": "sha512-VgDNokpBoKF+wrdvhAAfS55OMQpL6QRglwTwNC3kIgBrzZxA4WsFj+2eLfEA/uMUDzBcEhYmjSbwQakn/i3ajA==", "license": "MIT" }, "node_modules/@types/trusted-types": { @@ -3643,20 +3690,20 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.51.0.tgz", - "integrity": "sha512-XtssGWJvypyM2ytBnSnKtHYOGT+4ZwTnBVl36TA4nRO2f4PRNGz5/1OszHzcZCvcBMh+qb7I06uoCmLTRdR9og==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.2.tgz", + "integrity": "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.51.0", - "@typescript-eslint/type-utils": "8.51.0", - "@typescript-eslint/utils": "8.51.0", - "@typescript-eslint/visitor-keys": "8.51.0", - "ignore": "^7.0.0", + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/type-utils": "8.57.2", + "@typescript-eslint/utils": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", + "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.2.0" + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3666,24 +3713,23 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.51.0", - "eslint": "^8.57.0 || ^9.0.0", + "@typescript-eslint/parser": "^8.57.2", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/parser": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.51.0.tgz", - "integrity": "sha512-3xP4XzzDNQOIqBMWogftkwxhg5oMKApqY0BAflmLZiFYHqyhSOxv/cd/zPQLTcCXr4AkaKb25joocY0BD1WC6A==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz", + "integrity": "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.51.0", - "@typescript-eslint/types": "8.51.0", - "@typescript-eslint/typescript-estree": "8.51.0", - "@typescript-eslint/visitor-keys": "8.51.0", - "debug": "^4.3.4" + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2", + "@typescript-eslint/utils": "8.57.2", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3693,20 +3739,26 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.51.0.tgz", - "integrity": "sha512-Luv/GafO07Z7HpiI7qeEW5NW8HUtZI/fo/kE0YbtQEFpJRUuR0ajcWfCE5bnMvL7QQFrmT/odMe8QZww8X2nfQ==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", + "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.51.0", - "@typescript-eslint/types": "^8.51.0", - "debug": "^4.3.4" + "@typescript-eslint/project-service": "8.57.2", + "@typescript-eslint/tsconfig-utils": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3719,15 +3771,16 @@ "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.51.0.tgz", - "integrity": "sha512-JhhJDVwsSx4hiOEQPeajGhCWgBMBwVkxC/Pet53EpBVs7zHHtayKefw1jtPaNRXpI9RA2uocdmpdfE7T+NrizA==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/project-service": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz", + "integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.51.0", - "@typescript-eslint/visitor-keys": "8.51.0" + "@typescript-eslint/tsconfig-utils": "^8.57.2", + "@typescript-eslint/types": "^8.57.2", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3735,12 +3788,15 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.51.0.tgz", - "integrity": "sha512-Qi5bSy/vuHeWyir2C8u/uqGMIlIDu8fuiYWv48ZGlZ/k+PRPHtaAu7erpc7p5bzw2WNNSniuxoMSO4Ar6V9OXw==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz", + "integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==", "dev": true, "license": "MIT", "engines": { @@ -3754,18 +3810,17 @@ "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.51.0.tgz", - "integrity": "sha512-0XVtYzxnobc9K0VU7wRWg1yiUrw4oQzexCG2V2IDxxCxhqBMSMbjB+6o91A+Uc0GWtgjCa3Y8bi7hwI0Tu4n5Q==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz", + "integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.51.0", - "@typescript-eslint/typescript-estree": "8.51.0", - "@typescript-eslint/utils": "8.51.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.2.0" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3775,40 +3830,48 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/types": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.51.0.tgz", - "integrity": "sha512-TizAvWYFM6sSscmEakjY3sPqGwxZRSywSsPEiuZF6d5GmGD9Gvlsv0f6N8FvAAA0CD06l3rIcWNbsN1e5F/9Ag==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", + "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", "dev": true, "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.57.2", + "@typescript-eslint/tsconfig-utils": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.51.0.tgz", - "integrity": "sha512-1qNjGqFRmlq0VW5iVlcyHBbCjPB7y6SxpBkrbhNWMy/65ZoncXCEPJxkRZL8McrseNH6lFhaxCIaX+vBuFnRng==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/project-service": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz", + "integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.51.0", - "@typescript-eslint/tsconfig-utils": "8.51.0", - "@typescript-eslint/types": "8.51.0", - "@typescript-eslint/visitor-keys": "8.51.0", - "debug": "^4.3.4", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.2.0" + "@typescript-eslint/tsconfig-utils": "^8.57.2", + "@typescript-eslint/types": "^8.57.2", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3821,18 +3884,12 @@ "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/utils": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.51.0.tgz", - "integrity": "sha512-11rZYxSe0zabiKaCP2QAwRf/dnmgFgvTmeDTtZvUvXG3UuAdg/GU02NExmmIXzz3vLGgMdtrIosI84jITQOxUA==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz", + "integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==", "dev": true, "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.51.0", - "@typescript-eslint/types": "8.51.0", - "@typescript-eslint/typescript-estree": "8.51.0" - }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -3841,19 +3898,60 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.51.0.tgz", - "integrity": "sha512-mM/JRQOzhVN1ykejrvwnBRV3+7yTKK8tVANVN3o1O0t0v7o+jqdVu9crPy5Y9dov15TJk/FTIgoUGHrTOVL3Zg==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.51.0", - "eslint-visitor-keys": "^4.2.1" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.2.tgz", + "integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3861,69 +3959,230 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", + "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.57.2", + "@typescript-eslint/tsconfig-utils": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/project-service": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz", + "integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==", "dev": true, - "license": "ISC" + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.57.2", + "@typescript-eslint/types": "^8.57.2", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } }, - "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", - "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", - "cpu": [ - "arm" - ], + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz", + "integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ] + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } }, - "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", - "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", - "cpu": [ - "arm64" - ], + "node_modules/@typescript-eslint/parser/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ] + "engines": { + "node": "18 || 20 || >=22" + } }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", - "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", - "cpu": [ - "arm64" - ], + "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz", + "integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", + "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz", + "integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.57.2", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] }, "node_modules/@unrs/resolver-binding-darwin-x64": { "version": "1.11.1", @@ -4153,12 +4412,11 @@ ] }, "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4186,9 +4444,9 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "license": "MIT", "dependencies": { @@ -4208,6 +4466,7 @@ "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "type-fest": "^0.21.3" }, @@ -4224,6 +4483,7 @@ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -4252,6 +4512,7 @@ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -4470,9 +4731,9 @@ } }, "node_modules/axe-core": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.0.tgz", - "integrity": "sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ==", + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.1.tgz", + "integrity": "sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==", "dev": true, "license": "MPL-2.0", "engines": { @@ -4495,6 +4756,7 @@ "integrity": "sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/transform": "30.2.0", "@types/babel__core": "^7.20.5", @@ -4517,6 +4779,7 @@ "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "workspaces": [ "test/babel-8" ], @@ -4537,6 +4800,7 @@ "integrity": "sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/babel__core": "^7.20.5" }, @@ -4565,6 +4829,7 @@ "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", @@ -4592,6 +4857,7 @@ "integrity": "sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "babel-plugin-jest-hoist": "30.2.0", "babel-preset-current-node-syntax": "^1.2.0" @@ -4610,12 +4876,15 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.28", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.28.tgz", - "integrity": "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ==", + "version": "2.10.8", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.8.tgz", + "integrity": "sha512-PCLz/LXGBsNTErbtB6i5u4eLpHeMfi93aUv5duMmj6caNu6IphS4q6UevDnL36sZQv9lrP11dbPKGMaXPwMKfQ==", "license": "Apache-2.0", "bin": { - "baseline-browser-mapping": "dist/cli.js" + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/before-after-hook": { @@ -4631,9 +4900,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -4672,7 +4941,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", @@ -4706,6 +4974,7 @@ "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "node-int64": "^0.4.0" } @@ -4715,7 +4984,8 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/call-bind": { "version": "1.0.8", @@ -4788,6 +5058,7 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -4844,6 +5115,7 @@ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" } @@ -4869,7 +5141,8 @@ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.1.tgz", "integrity": "sha512-+CmxIZ/L2vNcEfvNtLdU0ZQ6mbq3FZnwAP2PPTiKP+1QOoKwlKlPgb8UKV0Dds7QVaMnHm+FwSft2VB0s/SLjQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/classnames": { "version": "2.5.1", @@ -4979,6 +5252,7 @@ "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "iojs": ">= 1.0.0", "node": ">= 0.12.0" @@ -4989,7 +5263,8 @@ "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/color-convert": { "version": "2.0.1", @@ -5029,12 +5304,11 @@ "license": "MIT" }, "node_modules/core-js": { - "version": "3.47.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz", - "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==", + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz", + "integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==", "hasInstallScript": true, "license": "MIT", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" @@ -5057,9 +5331,9 @@ } }, "node_modules/cosmiconfig/node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", "license": "ISC", "engines": { "node": ">= 6" @@ -5101,9 +5375,9 @@ } }, "node_modules/csstype": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.2.tgz", - "integrity": "sha512-D80T+tiqkd/8B0xNlbstWDG4x6aqVfO52+OlSUNIdkTvmNw0uQpJLeos2J/2XvpyidAFuTPmpad+tUxLndwj6g==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, "node_modules/damerau-levenshtein": { @@ -5195,6 +5469,7 @@ "integrity": "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, @@ -5217,6 +5492,7 @@ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -5290,6 +5566,7 @@ "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -5318,9 +5595,9 @@ } }, "node_modules/dompurify": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.0.tgz", - "integrity": "sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.0.tgz", + "integrity": "sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" @@ -5346,7 +5623,8 @@ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/electron-to-chromium": { "version": "1.5.254", @@ -5361,6 +5639,7 @@ "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -5380,20 +5659,19 @@ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "license": "MIT", - "peer": true, "dependencies": { "iconv-lite": "^0.6.2" } }, "node_modules/enhanced-resolve": { - "version": "5.18.4", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz", - "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==", + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", + "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "tapable": "^2.3.0" }, "engines": { "node": ">=10.13.0" @@ -5409,9 +5687,9 @@ } }, "node_modules/es-abstract": { - "version": "1.24.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", - "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", + "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", "dev": true, "license": "MIT", "dependencies": { @@ -5498,27 +5776,28 @@ } }, "node_modules/es-iterator-helpers": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", - "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.1.tgz", + "integrity": "sha512-zWwRvqWiuBPr0muUG/78cW3aHROFCNIQ3zpmYDpwdbnt2m+xlNyRWpHBpa2lJjSBit7BQ+RXA1iwbSmu5yJ/EQ==", "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.8", - "call-bound": "^1.0.3", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.6", + "es-abstract": "^1.24.1", "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.3", + "es-set-tostringtag": "^2.1.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.6", + "get-intrinsic": "^1.3.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", - "iterator.prototype": "^1.1.4", + "iterator.prototype": "^1.1.5", + "math-intrinsics": "^1.1.0", "safe-array-concat": "^1.1.3" }, "engines": { @@ -5613,26 +5892,25 @@ } }, "node_modules/eslint": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", - "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.1", + "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.2", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "ajv": "^6.12.4", + "ajv": "^6.14.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", @@ -5651,7 +5929,7 @@ "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", + "minimatch": "^3.1.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -5674,13 +5952,13 @@ } }, "node_modules/eslint-config-next": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.1.1.tgz", - "integrity": "sha512-55nTpVWm3qeuxoQKLOjQVciKZJUphKrNM0fCcQHAIOGl6VFXgaqeMfv0aKJhs7QtcnlAPhNVqsqRfRjeKBPIUA==", + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.2.1.tgz", + "integrity": "sha512-qhabwjQZ1Mk53XzXvmogf8KQ0tG0CQXF0CZ56+2/lVhmObgmaqj7x5A1DSrWdZd3kwI7GTPGUjFne+krRxYmFg==", "dev": true, "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "16.1.1", + "@next/eslint-plugin-next": "16.2.1", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", @@ -5700,42 +5978,18 @@ } } }, - "node_modules/eslint-config-next/node_modules/globals": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", - "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/eslint-config-next/node_modules/brace-expansion": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.1" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/eslint-import-resolver-typescript": { + "node_modules/eslint-config-next/node_modules/eslint-import-resolver-typescript": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", @@ -5770,41 +6024,12 @@ } } }, - "node_modules/eslint-module-utils": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", - "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import": { + "node_modules/eslint-config-next/node_modules/eslint-plugin-import": { "version": "2.32.0", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -5833,18 +6058,7 @@ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, - "node_modules/eslint-plugin-import/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { + "node_modules/eslint-config-next/node_modules/eslint-plugin-import/node_modules/debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", @@ -5854,30 +6068,7 @@ "ms": "^2.1.1" } }, - "node_modules/eslint-plugin-import/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-jsx-a11y": { + "node_modules/eslint-config-next/node_modules/eslint-plugin-jsx-a11y": { "version": "6.10.2", "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", @@ -5907,34 +6098,10 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint-plugin-react": { - "version": "7.37.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", - "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "node_modules/eslint-config-next/node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "dev": true, "license": "MIT", "dependencies": { @@ -5964,7 +6131,7 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, - "node_modules/eslint-plugin-react-hooks": { + "node_modules/eslint-config-next/node_modules/eslint-plugin-react-hooks": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", @@ -5984,21 +6151,23 @@ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, - "node_modules/eslint-plugin-react/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "node_modules/eslint-config-next/node_modules/globals": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", "dev": true, "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-react/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/eslint-config-next/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -6008,25 +6177,31 @@ "node": "*" } }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.5", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "node_modules/eslint-config-next/node_modules/resolve": { + "version": "2.0.0-next.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz", + "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "node-exports-info": "^1.6.0", + "object-keys": "^1.1.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-plugin-react/node_modules/semver": { + "node_modules/eslint-config-next/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", @@ -6036,6 +6211,56 @@ "semver": "bin/semver.js" } }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/eslint-scope": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", @@ -6067,9 +6292,9 @@ } }, "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { @@ -6101,9 +6326,9 @@ } }, "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -6150,6 +6375,7 @@ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -6159,9 +6385,9 @@ } }, "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -6216,6 +6442,7 @@ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -6239,7 +6466,8 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/exit-x": { "version": "0.2.2", @@ -6247,6 +6475,7 @@ "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8.0" } @@ -6358,9 +6587,9 @@ "license": "BSD-3-Clause" }, "node_modules/fast-xml-parser": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", - "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.6.tgz", + "integrity": "sha512-Yd4vkROfJf8AuJrDIVMVmYfULKmIJszVsMv7Vo71aocsKgFxpdlpSHXSaInvyYfgw2PRuObQSW2GFpVMUjxu9A==", "funding": [ { "type": "github", @@ -6369,7 +6598,7 @@ ], "license": "MIT", "dependencies": { - "strnum": "^1.1.1" + "strnum": "^1.0.5" }, "bin": { "fxparser": "src/cli/cli.js" @@ -6391,6 +6620,7 @@ "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "bser": "2.1.1" } @@ -6465,9 +6695,9 @@ } }, "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" }, @@ -6499,6 +6729,7 @@ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" @@ -6515,7 +6746,8 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/fsevents": { "version": "2.3.3", @@ -6528,6 +6760,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } @@ -6632,6 +6865,7 @@ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8.0.0" } @@ -6656,6 +6890,7 @@ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -6682,9 +6917,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", - "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "version": "4.13.7", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz", + "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==", "dev": true, "license": "MIT", "dependencies": { @@ -6700,6 +6935,7 @@ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -6779,9 +7015,9 @@ "license": "ISC" }, "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6940,7 +7176,8 @@ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/http2-client": { "version": "1.3.5", @@ -6967,6 +7204,7 @@ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=10.17.0" } @@ -7015,6 +7253,7 @@ "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" @@ -7046,6 +7285,7 @@ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -7056,7 +7296,8 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/install": { "version": "0.13.0", @@ -7083,13 +7324,12 @@ } }, "node_modules/ioredis": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.8.2.tgz", - "integrity": "sha512-C6uC+kleiIMmjViJINWk80sOQw5lEzse1ZmvD+S/s8p8CWapftSaC+kocGTx6xrbrJ4WmYQGC08ffHLr6ToR6Q==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.10.1.tgz", + "integrity": "sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA==", "license": "MIT", - "peer": true, "dependencies": { - "@ioredis/commands": "1.4.0", + "@ioredis/commands": "1.5.1", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", @@ -7298,6 +7538,7 @@ "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -7442,6 +7683,7 @@ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" }, @@ -7566,6 +7808,7 @@ "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "engines": { "node": ">=8" } @@ -7576,6 +7819,7 @@ "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "dependencies": { "@babel/core": "^7.23.9", "@babel/parser": "^7.23.9", @@ -7593,6 +7837,7 @@ "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^4.0.0", @@ -7608,6 +7853,7 @@ "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.23", "debug": "^4.1.1", @@ -7623,6 +7869,7 @@ "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" @@ -7655,6 +7902,7 @@ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "license": "BlueOak-1.0.0", + "peer": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -7699,6 +7947,7 @@ "integrity": "sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "execa": "^5.1.1", "jest-util": "30.2.0", @@ -7714,6 +7963,7 @@ "integrity": "sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/environment": "30.2.0", "@jest/expect": "30.2.0", @@ -7746,6 +7996,7 @@ "integrity": "sha512-Os9ukIvADX/A9sLt6Zse3+nmHtHaE6hqOsjQtNiugFTbKRHYIYtZXNGNK9NChseXy7djFPjndX1tL0sCTlfpAA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "30.2.0", "@jest/test-result": "30.2.0", @@ -7779,6 +8030,7 @@ "integrity": "sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/core": "^7.27.4", "@jest/get-type": "30.1.0", @@ -7847,6 +8099,7 @@ "integrity": "sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "detect-newline": "^3.1.0" }, @@ -7860,6 +8113,7 @@ "integrity": "sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/get-type": "30.1.0", "@jest/types": "30.2.0", @@ -7877,6 +8131,7 @@ "integrity": "sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/environment": "30.2.0", "@jest/fake-timers": "30.2.0", @@ -7896,6 +8151,7 @@ "integrity": "sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", @@ -7921,6 +8177,7 @@ "integrity": "sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/get-type": "30.1.0", "pretty-format": "30.2.0" @@ -7987,6 +8244,7 @@ "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6" }, @@ -8015,6 +8273,7 @@ "integrity": "sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "chalk": "^4.1.2", "graceful-fs": "^4.2.11", @@ -8035,6 +8294,7 @@ "integrity": "sha512-xTOIGug/0RmIe3mmCqCT95yO0vj6JURrn1TKWlNbhiAefJRWINNPgwVkrVgt/YaerPzY3iItufd80v3lOrFJ2w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "jest-regex-util": "30.0.1", "jest-snapshot": "30.2.0" @@ -8049,6 +8309,7 @@ "integrity": "sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/console": "30.2.0", "@jest/environment": "30.2.0", @@ -8083,6 +8344,7 @@ "integrity": "sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/environment": "30.2.0", "@jest/fake-timers": "30.2.0", @@ -8117,6 +8379,7 @@ "integrity": "sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/core": "^7.27.4", "@babel/generator": "^7.27.5", @@ -8163,9 +8426,9 @@ } }, "node_modules/jest-util/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -8181,6 +8444,7 @@ "integrity": "sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/get-type": "30.1.0", "@jest/types": "30.2.0", @@ -8199,6 +8463,7 @@ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -8212,6 +8477,7 @@ "integrity": "sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/test-result": "30.2.0", "@jest/types": "30.2.0", @@ -8232,6 +8498,7 @@ "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*", "@ungap/structured-clone": "^1.3.0", @@ -8249,6 +8516,7 @@ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -8418,6 +8686,7 @@ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -8437,9 +8706,9 @@ } }, "node_modules/lightningcss": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", - "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", "dev": true, "license": "MPL-2.0", "dependencies": { @@ -8453,23 +8722,23 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-android-arm64": "1.30.2", - "lightningcss-darwin-arm64": "1.30.2", - "lightningcss-darwin-x64": "1.30.2", - "lightningcss-freebsd-x64": "1.30.2", - "lightningcss-linux-arm-gnueabihf": "1.30.2", - "lightningcss-linux-arm64-gnu": "1.30.2", - "lightningcss-linux-arm64-musl": "1.30.2", - "lightningcss-linux-x64-gnu": "1.30.2", - "lightningcss-linux-x64-musl": "1.30.2", - "lightningcss-win32-arm64-msvc": "1.30.2", - "lightningcss-win32-x64-msvc": "1.30.2" + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" } }, "node_modules/lightningcss-android-arm64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", - "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", "cpu": [ "arm64" ], @@ -8488,9 +8757,9 @@ } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", - "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", "cpu": [ "arm64" ], @@ -8509,9 +8778,9 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", - "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", "cpu": [ "x64" ], @@ -8530,9 +8799,9 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", - "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", "cpu": [ "x64" ], @@ -8551,9 +8820,9 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", - "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", "cpu": [ "arm" ], @@ -8572,9 +8841,9 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", - "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", "cpu": [ "arm64" ], @@ -8593,9 +8862,9 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", - "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", "cpu": [ "arm64" ], @@ -8614,9 +8883,9 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", - "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", "cpu": [ "x64" ], @@ -8635,9 +8904,9 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", - "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", "cpu": [ "x64" ], @@ -8656,9 +8925,9 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", - "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", "cpu": [ "arm64" ], @@ -8677,9 +8946,9 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", - "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", "cpu": [ "x64" ], @@ -8795,6 +9064,7 @@ "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "semver": "^7.5.3" }, @@ -8818,6 +9088,7 @@ "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "dependencies": { "tmpl": "1.0.5" } @@ -8855,7 +9126,8 @@ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/merge2": { "version": "1.4.1", @@ -8887,18 +9159,20 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6" } }, "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.7.tgz", + "integrity": "sha512-MOwgjc8tfrpn5QQEvjijjmDVtMw2oL88ugTevzxQnzRLm6l3fVEF2gzU0kYeYYKD8C66+IdGX6peJ4MyUlUnPg==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -8907,6 +9181,31 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minimatch/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/minimatch/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", @@ -8923,6 +9222,7 @@ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "license": "ISC", + "peer": true, "engines": { "node": ">=16 || 14 >=14.17" } @@ -8932,7 +9232,6 @@ "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.15.0.tgz", "integrity": "sha512-UczzB+0nnwGotYSgllfARAqWCJ5e/skuV2K/l+Zyck/H6pJIhLXuBnz+6vn2i211o7DtbE78HQtsYEKICHGI+g==", "license": "MIT", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/mobx" @@ -9043,14 +9342,14 @@ "license": "MIT" }, "node_modules/next": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/next/-/next-16.1.1.tgz", - "integrity": "sha512-QI+T7xrxt1pF6SQ/JYFz95ro/mg/1Znk5vBebsWwbpejj1T0A23hO7GYEaVac9QUOT2BIMiuzm0L99ooq7k0/w==", + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/next/-/next-16.2.3.tgz", + "integrity": "sha512-9V3zV4oZFza3PVev5/poB9g0dEafVcgNyQ8eTRop8GvxZjV2G15FC5ARuG1eFD42QgeYkzJBJzHghNP8Ad9xtA==", "license": "MIT", "dependencies": { - "@next/env": "16.1.1", + "@next/env": "16.2.3", "@swc/helpers": "0.5.15", - "baseline-browser-mapping": "^2.8.3", + "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" @@ -9062,15 +9361,15 @@ "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "16.1.1", - "@next/swc-darwin-x64": "16.1.1", - "@next/swc-linux-arm64-gnu": "16.1.1", - "@next/swc-linux-arm64-musl": "16.1.1", - "@next/swc-linux-x64-gnu": "16.1.1", - "@next/swc-linux-x64-musl": "16.1.1", - "@next/swc-win32-arm64-msvc": "16.1.1", - "@next/swc-win32-x64-msvc": "16.1.1", - "sharp": "^0.34.4" + "@next/swc-darwin-arm64": "16.2.3", + "@next/swc-darwin-x64": "16.2.3", + "@next/swc-linux-arm64-gnu": "16.2.3", + "@next/swc-linux-arm64-musl": "16.2.3", + "@next/swc-linux-x64-gnu": "16.2.3", + "@next/swc-linux-x64-musl": "16.2.3", + "@next/swc-win32-arm64-msvc": "16.2.3", + "@next/swc-win32-x64-msvc": "16.2.3", + "sharp": "^0.34.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -9179,6 +9478,35 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/node-exports-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", + "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array.prototype.flatmap": "^1.3.3", + "es-errors": "^1.3.0", + "object.entries": "^1.1.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/node-exports-info/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -9216,7 +9544,8 @@ "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/node-readfiles": { "version": "0.2.0", @@ -9240,14 +9569,15 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } }, "node_modules/npm": { - "version": "11.7.0", - "resolved": "https://registry.npmjs.org/npm/-/npm-11.7.0.tgz", - "integrity": "sha512-wiCZpv/41bIobCoJ31NStIWKfAxxYyD1iYnWCtiyns8s5v3+l8y0HCP/sScuH6B5+GhIfda4HQKiqeGZwJWhFw==", + "version": "11.12.1", + "resolved": "https://registry.npmjs.org/npm/-/npm-11.12.1.tgz", + "integrity": "sha512-zcoUuF1kezGSAo0CqtvoLXX3mkRqzuqYdL6Y5tdo8g69NVV3CkjQ6ZBhBgB4d7vGkPcV6TcvLi3GRKPDFX+xTA==", "bundleDependencies": [ "@isaacs/string-locale-compare", "@npmcli/arborist", @@ -9265,7 +9595,6 @@ "cacache", "chalk", "ci-info", - "cli-columns", "fastest-levenshtein", "fs-minipass", "glob", @@ -9326,47 +9655,46 @@ ], "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^9.1.9", - "@npmcli/config": "^10.4.5", + "@npmcli/arborist": "^9.4.2", + "@npmcli/config": "^10.8.1", "@npmcli/fs": "^5.0.0", "@npmcli/map-workspaces": "^5.0.3", "@npmcli/metavuln-calculator": "^9.0.3", - "@npmcli/package-json": "^7.0.4", + "@npmcli/package-json": "^7.0.5", "@npmcli/promise-spawn": "^9.0.1", "@npmcli/redact": "^4.0.0", - "@npmcli/run-script": "^10.0.3", - "@sigstore/tuf": "^4.0.0", + "@npmcli/run-script": "^10.0.4", + "@sigstore/tuf": "^4.0.2", "abbrev": "^4.0.0", "archy": "~1.0.0", - "cacache": "^20.0.3", + "cacache": "^20.0.4", "chalk": "^5.6.2", - "ci-info": "^4.3.1", - "cli-columns": "^4.0.0", + "ci-info": "^4.4.0", "fastest-levenshtein": "^1.0.16", "fs-minipass": "^3.0.3", - "glob": "^13.0.0", + "glob": "^13.0.6", "graceful-fs": "^4.2.11", "hosted-git-info": "^9.0.2", "ini": "^6.0.0", - "init-package-json": "^8.2.4", - "is-cidr": "^6.0.1", + "init-package-json": "^8.2.5", + "is-cidr": "^6.0.3", "json-parse-even-better-errors": "^5.0.0", "libnpmaccess": "^10.0.3", - "libnpmdiff": "^8.0.12", - "libnpmexec": "^10.1.11", - "libnpmfund": "^7.0.12", + "libnpmdiff": "^8.1.5", + "libnpmexec": "^10.2.5", + "libnpmfund": "^7.0.19", "libnpmorg": "^8.0.1", - "libnpmpack": "^9.0.12", + "libnpmpack": "^9.1.5", "libnpmpublish": "^11.1.3", "libnpmsearch": "^9.0.1", "libnpmteam": "^8.0.2", "libnpmversion": "^8.0.3", - "make-fetch-happen": "^15.0.3", - "minimatch": "^10.1.1", - "minipass": "^7.1.1", + "make-fetch-happen": "^15.0.5", + "minimatch": "^10.2.4", + "minipass": "^7.1.3", "minipass-pipeline": "^1.2.4", "ms": "^2.1.2", - "node-gyp": "^12.1.0", + "node-gyp": "^12.2.0", "nopt": "^9.0.0", "npm-audit-report": "^7.0.0", "npm-install-checks": "^8.0.0", @@ -9376,21 +9704,21 @@ "npm-registry-fetch": "^19.1.1", "npm-user-validate": "^4.0.0", "p-map": "^7.0.4", - "pacote": "^21.0.4", + "pacote": "^21.5.0", "parse-conflict-json": "^5.0.1", "proc-log": "^6.1.0", "qrcode-terminal": "^0.12.0", "read": "^5.0.1", - "semver": "^7.7.3", + "semver": "^7.7.4", "spdx-expression-parse": "^4.0.0", - "ssri": "^13.0.0", + "ssri": "^13.0.1", "supports-color": "^10.2.2", - "tar": "^7.5.2", + "tar": "^7.5.11", "text-table": "~0.2.0", "tiny-relative-date": "^2.0.2", "treeverse": "^3.0.0", - "validate-npm-package-name": "^7.0.0", - "which": "^6.0.0" + "validate-npm-package-name": "^7.0.2", + "which": "^6.0.1" }, "bin": { "npm": "bin/npm-cli.js", @@ -9406,6 +9734,7 @@ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "path-key": "^3.0.0" }, @@ -9413,23 +9742,12 @@ "node": ">=8" } }, - "node_modules/npm/node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/npm/node_modules/@isaacs/brace-expansion": { - "version": "5.0.0", + "node_modules/npm/node_modules/@gar/promise-retry": { + "version": "1.0.3", "inBundle": true, "license": "MIT", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" - }, "engines": { - "node": "20 || >=22" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/@isaacs/fs-minipass": { @@ -9464,10 +9782,11 @@ } }, "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "9.1.9", + "version": "9.4.2", "inBundle": true, "license": "ISC", "dependencies": { + "@gar/promise-retry": "^1.0.0", "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/fs": "^5.0.0", "@npmcli/installed-package-contents": "^4.0.0", @@ -9481,7 +9800,7 @@ "@npmcli/run-script": "^10.0.0", "bin-links": "^6.0.0", "cacache": "^20.0.1", - "common-ancestor-path": "^1.0.1", + "common-ancestor-path": "^2.0.0", "hosted-git-info": "^9.0.0", "json-stringify-nice": "^1.1.4", "lru-cache": "^11.2.1", @@ -9510,7 +9829,7 @@ } }, "node_modules/npm/node_modules/@npmcli/config": { - "version": "10.4.5", + "version": "10.8.1", "inBundle": true, "license": "ISC", "dependencies": { @@ -9539,16 +9858,16 @@ } }, "node_modules/npm/node_modules/@npmcli/git": { - "version": "7.0.1", + "version": "7.0.2", "inBundle": true, "license": "ISC", "dependencies": { + "@gar/promise-retry": "^1.0.0", "@npmcli/promise-spawn": "^9.0.0", "ini": "^6.0.0", "lru-cache": "^11.2.1", "npm-pick-manifest": "^11.0.1", "proc-log": "^6.0.0", - "promise-retry": "^2.0.1", "semver": "^7.3.5", "which": "^6.0.0" }, @@ -9617,7 +9936,7 @@ } }, "node_modules/npm/node_modules/@npmcli/package-json": { - "version": "7.0.4", + "version": "7.0.5", "inBundle": true, "license": "ISC", "dependencies": { @@ -9627,7 +9946,7 @@ "json-parse-even-better-errors": "^5.0.0", "proc-log": "^6.0.0", "semver": "^7.5.3", - "validate-npm-package-license": "^3.0.4" + "spdx-expression-parse": "^4.0.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" @@ -9664,7 +9983,7 @@ } }, "node_modules/npm/node_modules/@npmcli/run-script": { - "version": "10.0.3", + "version": "10.0.4", "inBundle": true, "license": "ISC", "dependencies": { @@ -9672,8 +9991,7 @@ "@npmcli/package-json": "^7.0.0", "@npmcli/promise-spawn": "^9.0.0", "node-gyp": "^12.1.0", - "proc-log": "^6.0.0", - "which": "^6.0.0" + "proc-log": "^6.0.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" @@ -9691,7 +10009,7 @@ } }, "node_modules/npm/node_modules/@sigstore/core": { - "version": "3.0.0", + "version": "3.2.0", "inBundle": true, "license": "Apache-2.0", "engines": { @@ -9707,48 +10025,40 @@ } }, "node_modules/npm/node_modules/@sigstore/sign": { - "version": "4.0.1", + "version": "4.1.1", "inBundle": true, "license": "Apache-2.0", "dependencies": { + "@gar/promise-retry": "^1.0.2", "@sigstore/bundle": "^4.0.0", - "@sigstore/core": "^3.0.0", + "@sigstore/core": "^3.2.0", "@sigstore/protobuf-specs": "^0.5.0", - "make-fetch-happen": "^15.0.2", - "proc-log": "^5.0.0", - "promise-retry": "^2.0.1" + "make-fetch-happen": "^15.0.4", + "proc-log": "^6.1.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm/node_modules/@sigstore/sign/node_modules/proc-log": { - "version": "5.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, "node_modules/npm/node_modules/@sigstore/tuf": { - "version": "4.0.0", + "version": "4.0.2", "inBundle": true, "license": "Apache-2.0", "dependencies": { "@sigstore/protobuf-specs": "^0.5.0", - "tuf-js": "^4.0.0" + "tuf-js": "^4.1.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/@sigstore/verify": { - "version": "3.0.0", + "version": "3.1.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { "@sigstore/bundle": "^4.0.0", - "@sigstore/core": "^3.0.0", + "@sigstore/core": "^3.1.0", "@sigstore/protobuf-specs": "^0.5.0" }, "engines": { @@ -9764,31 +10074,17 @@ } }, "node_modules/npm/node_modules/@tufjs/models": { - "version": "4.0.0", + "version": "4.1.0", "inBundle": true, "license": "MIT", "dependencies": { "@tufjs/canonical-json": "2.0.0", - "minimatch": "^9.0.5" + "minimatch": "^10.1.1" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm/node_modules/@tufjs/models/node_modules/minimatch": { - "version": "9.0.5", - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/npm/node_modules/abbrev": { "version": "4.0.0", "inBundle": true, @@ -9805,14 +10101,6 @@ "node": ">= 14" } }, - "node_modules/npm/node_modules/ansi-regex": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/npm/node_modules/aproba": { "version": "2.1.0", "inBundle": true, @@ -9824,9 +10112,12 @@ "license": "MIT" }, "node_modules/npm/node_modules/balanced-match": { - "version": "1.0.2", + "version": "4.0.4", "inBundle": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } }, "node_modules/npm/node_modules/bin-links": { "version": "6.0.0", @@ -9855,15 +10146,18 @@ } }, "node_modules/npm/node_modules/brace-expansion": { - "version": "2.0.2", + "version": "5.0.4", "inBundle": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/npm/node_modules/cacache": { - "version": "20.0.3", + "version": "20.0.4", "inBundle": true, "license": "ISC", "dependencies": { @@ -9876,8 +10170,7 @@ "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^7.0.2", - "ssri": "^13.0.0", - "unique-filename": "^5.0.0" + "ssri": "^13.0.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" @@ -9903,7 +10196,7 @@ } }, "node_modules/npm/node_modules/ci-info": { - "version": "4.3.1", + "version": "4.4.0", "funding": [ { "type": "github", @@ -9917,28 +10210,13 @@ } }, "node_modules/npm/node_modules/cidr-regex": { - "version": "5.0.1", + "version": "5.0.3", "inBundle": true, "license": "BSD-2-Clause", - "dependencies": { - "ip-regex": "5.0.0" - }, "engines": { "node": ">=20" } }, - "node_modules/npm/node_modules/cli-columns": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">= 10" - } - }, "node_modules/npm/node_modules/cmd-shim": { "version": "8.0.0", "inBundle": true, @@ -9948,9 +10226,12 @@ } }, "node_modules/npm/node_modules/common-ancestor-path": { - "version": "1.0.1", + "version": "2.0.0", "inBundle": true, - "license": "ISC" + "license": "BlueOak-1.0.0", + "engines": { + "node": ">= 18" + } }, "node_modules/npm/node_modules/cssesc": { "version": "3.0.0", @@ -9980,27 +10261,13 @@ } }, "node_modules/npm/node_modules/diff": { - "version": "8.0.2", + "version": "8.0.3", "inBundle": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } }, - "node_modules/npm/node_modules/emoji-regex": { - "version": "8.0.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/encoding": { - "version": "0.1.13", - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, "node_modules/npm/node_modules/env-paths": { "version": "2.2.1", "inBundle": true, @@ -10009,11 +10276,6 @@ "node": ">=6" } }, - "node_modules/npm/node_modules/err-code": { - "version": "2.0.3", - "inBundle": true, - "license": "MIT" - }, "node_modules/npm/node_modules/exponential-backoff": { "version": "3.1.3", "inBundle": true, @@ -10039,16 +10301,16 @@ } }, "node_modules/npm/node_modules/glob": { - "version": "13.0.0", + "version": "13.0.6", "inBundle": true, "license": "BlueOak-1.0.0", "dependencies": { - "minimatch": "^10.1.1", - "minipass": "^7.1.2", - "path-scurry": "^2.0.0" + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -10100,7 +10362,7 @@ } }, "node_modules/npm/node_modules/iconv-lite": { - "version": "0.6.3", + "version": "0.7.2", "inBundle": true, "license": "MIT", "optional": true, @@ -10109,6 +10371,10 @@ }, "engines": { "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/npm/node_modules/ignore-walk": { @@ -10122,14 +10388,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm/node_modules/imurmurhash": { - "version": "0.1.4", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, "node_modules/npm/node_modules/ini": { "version": "6.0.0", "inBundle": true, @@ -10139,7 +10397,7 @@ } }, "node_modules/npm/node_modules/init-package-json": { - "version": "8.2.4", + "version": "8.2.5", "inBundle": true, "license": "ISC", "dependencies": { @@ -10148,7 +10406,6 @@ "promzard": "^3.0.1", "read": "^5.0.1", "semver": "^7.7.2", - "validate-npm-package-license": "^3.0.4", "validate-npm-package-name": "^7.0.0" }, "engines": { @@ -10156,49 +10413,30 @@ } }, "node_modules/npm/node_modules/ip-address": { - "version": "10.0.1", + "version": "10.1.0", "inBundle": true, "license": "MIT", "engines": { "node": ">= 12" } }, - "node_modules/npm/node_modules/ip-regex": { - "version": "5.0.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/npm/node_modules/is-cidr": { - "version": "6.0.1", + "version": "6.0.3", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { - "cidr-regex": "5.0.1" + "cidr-regex": "^5.0.1" }, "engines": { "node": ">=20" } }, - "node_modules/npm/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/npm/node_modules/isexe": { - "version": "3.1.1", + "version": "4.0.0", "inBundle": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { - "node": ">=16" + "node": ">=20" } }, "node_modules/npm/node_modules/json-parse-even-better-errors": { @@ -10248,11 +10486,11 @@ } }, "node_modules/npm/node_modules/libnpmdiff": { - "version": "8.0.12", + "version": "8.1.5", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.1.9", + "@npmcli/arborist": "^9.4.2", "@npmcli/installed-package-contents": "^4.0.0", "binary-extensions": "^3.0.0", "diff": "^8.0.2", @@ -10266,18 +10504,18 @@ } }, "node_modules/npm/node_modules/libnpmexec": { - "version": "10.1.11", + "version": "10.2.5", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.1.9", + "@gar/promise-retry": "^1.0.0", + "@npmcli/arborist": "^9.4.2", "@npmcli/package-json": "^7.0.0", "@npmcli/run-script": "^10.0.0", "ci-info": "^4.0.0", "npm-package-arg": "^13.0.0", "pacote": "^21.0.2", "proc-log": "^6.0.0", - "promise-retry": "^2.0.1", "read": "^5.0.1", "semver": "^7.3.7", "signal-exit": "^4.1.0", @@ -10288,11 +10526,11 @@ } }, "node_modules/npm/node_modules/libnpmfund": { - "version": "7.0.12", + "version": "7.0.19", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.1.9" + "@npmcli/arborist": "^9.4.2" }, "engines": { "node": "^20.17.0 || >=22.9.0" @@ -10311,11 +10549,11 @@ } }, "node_modules/npm/node_modules/libnpmpack": { - "version": "9.0.12", + "version": "9.1.5", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.1.9", + "@npmcli/arborist": "^9.4.2", "@npmcli/run-script": "^10.0.0", "npm-package-arg": "^13.0.0", "pacote": "^21.0.2" @@ -10381,19 +10619,21 @@ } }, "node_modules/npm/node_modules/lru-cache": { - "version": "11.2.2", + "version": "11.2.7", "inBundle": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } }, "node_modules/npm/node_modules/make-fetch-happen": { - "version": "15.0.3", + "version": "15.0.5", "inBundle": true, "license": "ISC", "dependencies": { + "@gar/promise-retry": "^1.0.0", "@npmcli/agent": "^4.0.0", + "@npmcli/redact": "^4.0.0", "cacache": "^20.0.1", "http-cache-semantics": "^4.1.1", "minipass": "^7.0.2", @@ -10402,7 +10642,6 @@ "minipass-pipeline": "^1.2.4", "negotiator": "^1.0.0", "proc-log": "^6.0.0", - "promise-retry": "^2.0.1", "ssri": "^13.0.0" }, "engines": { @@ -10410,23 +10649,23 @@ } }, "node_modules/npm/node_modules/minimatch": { - "version": "10.1.1", + "version": "10.2.4", "inBundle": true, "license": "BlueOak-1.0.0", "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" + "brace-expansion": "^5.0.2" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/npm/node_modules/minipass": { - "version": "7.1.2", + "version": "7.1.3", "inBundle": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" } @@ -10443,19 +10682,19 @@ } }, "node_modules/npm/node_modules/minipass-fetch": { - "version": "5.0.0", + "version": "5.0.2", "inBundle": true, "license": "MIT", "dependencies": { "minipass": "^7.0.3", - "minipass-sized": "^1.0.3", + "minipass-sized": "^2.0.0", "minizlib": "^3.0.1" }, "engines": { "node": "^20.17.0 || >=22.9.0" }, "optionalDependencies": { - "encoding": "^0.1.13" + "iconv-lite": "^0.7.2" } }, "node_modules/npm/node_modules/minipass-flush": { @@ -10480,6 +10719,11 @@ "node": ">=8" } }, + "node_modules/npm/node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC" + }, "node_modules/npm/node_modules/minipass-pipeline": { "version": "1.2.4", "inBundle": true, @@ -10502,23 +10746,17 @@ "node": ">=8" } }, - "node_modules/npm/node_modules/minipass-sized": { - "version": "1.0.3", + "node_modules/npm/node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } + "license": "ISC" }, - "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { - "version": "3.3.6", + "node_modules/npm/node_modules/minipass-sized": { + "version": "2.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "yallist": "^4.0.0" + "minipass": "^7.1.2" }, "engines": { "node": ">=8" @@ -10557,7 +10795,7 @@ } }, "node_modules/npm/node_modules/node-gyp": { - "version": "12.1.0", + "version": "12.2.0", "inBundle": true, "license": "MIT", "dependencies": { @@ -10568,7 +10806,7 @@ "nopt": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5", - "tar": "^7.5.2", + "tar": "^7.5.4", "tinyglobby": "^0.2.12", "which": "^6.0.0" }, @@ -10646,7 +10884,7 @@ } }, "node_modules/npm/node_modules/npm-packlist": { - "version": "10.0.3", + "version": "10.0.4", "inBundle": true, "license": "ISC", "dependencies": { @@ -10721,10 +10959,11 @@ } }, "node_modules/npm/node_modules/pacote": { - "version": "21.0.4", + "version": "21.5.0", "inBundle": true, "license": "ISC", "dependencies": { + "@gar/promise-retry": "^1.0.0", "@npmcli/git": "^7.0.0", "@npmcli/installed-package-contents": "^4.0.0", "@npmcli/package-json": "^7.0.0", @@ -10738,7 +10977,6 @@ "npm-pick-manifest": "^11.0.1", "npm-registry-fetch": "^19.0.0", "proc-log": "^6.0.0", - "promise-retry": "^2.0.1", "sigstore": "^4.0.0", "ssri": "^13.0.0", "tar": "^7.4.3" @@ -10764,7 +11002,7 @@ } }, "node_modules/npm/node_modules/path-scurry": { - "version": "2.0.0", + "version": "2.0.2", "inBundle": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -10772,14 +11010,14 @@ "minipass": "^7.1.2" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/npm/node_modules/postcss-selector-parser": { - "version": "7.1.0", + "version": "7.1.1", "inBundle": true, "license": "MIT", "dependencies": { @@ -10822,18 +11060,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/npm/node_modules/promise-retry": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/npm/node_modules/promzard": { "version": "3.0.1", "inBundle": true, @@ -10871,14 +11097,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm/node_modules/retry": { - "version": "0.12.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, "node_modules/npm/node_modules/safer-buffer": { "version": "2.1.2", "inBundle": true, @@ -10886,7 +11104,7 @@ "optional": true }, "node_modules/npm/node_modules/semver": { - "version": "7.7.3", + "version": "7.7.4", "inBundle": true, "license": "ISC", "bin": { @@ -10908,16 +11126,16 @@ } }, "node_modules/npm/node_modules/sigstore": { - "version": "4.0.0", + "version": "4.1.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { "@sigstore/bundle": "^4.0.0", - "@sigstore/core": "^3.0.0", + "@sigstore/core": "^3.1.0", "@sigstore/protobuf-specs": "^0.5.0", - "@sigstore/sign": "^4.0.0", - "@sigstore/tuf": "^4.0.0", - "@sigstore/verify": "^3.0.0" + "@sigstore/sign": "^4.1.0", + "@sigstore/tuf": "^4.0.1", + "@sigstore/verify": "^3.1.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" @@ -10958,24 +11176,6 @@ "node": ">= 14" } }, - "node_modules/npm/node_modules/spdx-correct": { - "version": "3.2.0", - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { - "version": "3.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, "node_modules/npm/node_modules/spdx-exceptions": { "version": "2.5.0", "inBundle": true, @@ -10991,43 +11191,19 @@ } }, "node_modules/npm/node_modules/spdx-license-ids": { - "version": "3.0.22", - "inBundle": true, - "license": "CC0-1.0" - }, - "node_modules/npm/node_modules/ssri": { - "version": "13.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/string-width": { - "version": "4.2.3", - "inBundle": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } + "version": "3.0.23", + "inBundle": true, + "license": "CC0-1.0" }, - "node_modules/npm/node_modules/strip-ansi": { - "version": "6.0.1", + "node_modules/npm/node_modules/ssri": { + "version": "13.0.1", "inBundle": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "ansi-regex": "^5.0.1" + "minipass": "^7.0.3" }, "engines": { - "node": ">=8" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm/node_modules/supports-color": { @@ -11042,7 +11218,7 @@ } }, "node_modules/npm/node_modules/tar": { - "version": "7.5.2", + "version": "7.5.11", "inBundle": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -11056,14 +11232,6 @@ "node": ">=18" } }, - "node_modules/npm/node_modules/tar/node_modules/yallist": { - "version": "5.0.0", - "inBundle": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, "node_modules/npm/node_modules/text-table": { "version": "0.2.0", "inBundle": true, @@ -11109,7 +11277,6 @@ "version": "4.0.3", "inBundle": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -11126,35 +11293,13 @@ } }, "node_modules/npm/node_modules/tuf-js": { - "version": "4.0.0", + "version": "4.1.0", "inBundle": true, "license": "MIT", "dependencies": { - "@tufjs/models": "4.0.0", - "debug": "^4.4.1", - "make-fetch-happen": "^15.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/unique-filename": { - "version": "5.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "unique-slug": "^6.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm/node_modules/unique-slug": { - "version": "6.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4" + "@tufjs/models": "4.1.0", + "debug": "^4.4.3", + "make-fetch-happen": "^15.0.1" }, "engines": { "node": "^20.17.0 || >=22.9.0" @@ -11165,26 +11310,8 @@ "inBundle": true, "license": "MIT" }, - "node_modules/npm/node_modules/validate-npm-package-license": { - "version": "3.0.4", - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { - "version": "3.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, "node_modules/npm/node_modules/validate-npm-package-name": { - "version": "7.0.0", + "version": "7.0.2", "inBundle": true, "license": "ISC", "engines": { @@ -11200,11 +11327,11 @@ } }, "node_modules/npm/node_modules/which": { - "version": "6.0.0", + "version": "6.0.1", "inBundle": true, "license": "ISC", "dependencies": { - "isexe": "^3.1.1" + "isexe": "^4.0.0" }, "bin": { "node-which": "bin/which.js" @@ -11214,11 +11341,10 @@ } }, "node_modules/npm/node_modules/write-file-atomic": { - "version": "7.0.0", + "version": "7.0.1", "inBundle": true, "license": "ISC", "dependencies": { - "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" }, "engines": { @@ -11226,9 +11352,12 @@ } }, "node_modules/npm/node_modules/yallist": { - "version": "4.0.0", + "version": "5.0.0", "inBundle": true, - "license": "ISC" + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } }, "node_modules/nprogress": { "version": "0.2.0", @@ -11260,9 +11389,9 @@ } }, "node_modules/oas-linter/node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", "license": "ISC", "engines": { "node": ">= 6" @@ -11288,9 +11417,9 @@ } }, "node_modules/oas-resolver/node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", "license": "ISC", "engines": { "node": ">= 6" @@ -11325,9 +11454,9 @@ } }, "node_modules/oas-validator/node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", "license": "ISC", "engines": { "node": ">= 6" @@ -11492,6 +11621,7 @@ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "wrappy": "1" } @@ -11502,6 +11632,7 @@ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "mimic-fn": "^2.1.0" }, @@ -11597,6 +11728,7 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -11606,7 +11738,8 @@ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true, - "license": "BlueOak-1.0.0" + "license": "BlueOak-1.0.0", + "peer": true }, "node_modules/parent-module": { "version": "1.0.1", @@ -11660,6 +11793,7 @@ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -11686,6 +11820,7 @@ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "license": "BlueOak-1.0.0", + "peer": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -11702,7 +11837,8 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/path-type": { "version": "4.0.0", @@ -11720,16 +11856,15 @@ "license": "MIT" }, "node_modules/pg": { - "version": "8.16.3", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", - "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.20.0.tgz", + "integrity": "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "pg-connection-string": "^2.9.1", - "pg-pool": "^3.10.1", - "pg-protocol": "^1.10.3", + "pg-connection-string": "^2.12.0", + "pg-pool": "^3.13.0", + "pg-protocol": "^1.13.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, @@ -11737,7 +11872,7 @@ "node": ">= 16.0.0" }, "optionalDependencies": { - "pg-cloudflare": "^1.2.7" + "pg-cloudflare": "^1.3.0" }, "peerDependencies": { "pg-native": ">=3.0.1" @@ -11749,17 +11884,17 @@ } }, "node_modules/pg-cloudflare": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", - "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz", + "integrity": "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==", "dev": true, "license": "MIT", "optional": true }, "node_modules/pg-connection-string": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", - "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.12.0.tgz", + "integrity": "sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==", "dev": true, "license": "MIT" }, @@ -11774,9 +11909,9 @@ } }, "node_modules/pg-pool": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", - "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.13.0.tgz", + "integrity": "sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==", "dev": true, "license": "MIT", "peerDependencies": { @@ -11784,9 +11919,9 @@ } }, "node_modules/pg-protocol": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", - "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.13.0.tgz", + "integrity": "sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==", "dev": true, "license": "MIT" }, @@ -11824,9 +11959,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -11842,6 +11977,7 @@ "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 6" } @@ -11852,6 +11988,7 @@ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "find-up": "^4.0.0" }, @@ -11865,6 +12002,7 @@ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -11879,6 +12017,7 @@ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "p-locate": "^4.1.0" }, @@ -11892,6 +12031,7 @@ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "p-try": "^2.0.0" }, @@ -11908,6 +12048,7 @@ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "p-limit": "^2.2.0" }, @@ -11947,9 +12088,9 @@ } }, "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", + "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", "dev": true, "funding": [ { @@ -12029,7 +12170,6 @@ "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.3.tgz", "integrity": "sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==", "license": "MIT", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" @@ -12140,7 +12280,8 @@ "url": "https://opencollective.com/fast-check" } ], - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/queue-microtask": { "version": "1.2.3", @@ -12164,32 +12305,30 @@ "license": "MIT" }, "node_modules/react": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", - "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.2.3" + "react": "^19.2.4" } }, "node_modules/react-is": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.3.tgz", - "integrity": "sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA==", + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.4.tgz", + "integrity": "sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==", "license": "MIT" }, "node_modules/react-tabs": { @@ -12243,9 +12382,9 @@ } }, "node_modules/redis-semaphore": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/redis-semaphore/-/redis-semaphore-5.6.2.tgz", - "integrity": "sha512-Oh1zOqNa51VC14mwYcmdOyjHpb+y8N1ieqpGxITjkrqPiO8IoCYiXGrSyKEmXH5+UEsl/7OAnju2e0x1TY5Jhg==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/redis-semaphore/-/redis-semaphore-5.7.0.tgz", + "integrity": "sha512-2xjl8eYVVTKX8O8C/H70iWEGx8BSOOzI5P1tQgXEO8A8dR2/BPgU7QctCfcXOHsQ7/IIkebv1ulEdo2iC2SwvA==", "license": "MIT", "dependencies": { "debug": "^4.4.0" @@ -12399,6 +12538,7 @@ "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "resolve-from": "^5.0.0" }, @@ -12412,6 +12552,7 @@ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -12807,6 +12948,7 @@ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, "license": "ISC", + "peer": true, "engines": { "node": ">=14" }, @@ -12857,6 +12999,7 @@ "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -12868,6 +13011,7 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -12887,7 +13031,8 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true, - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/stable-hash": { "version": "0.0.5", @@ -12950,6 +13095,7 @@ "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "char-regex": "^1.0.2", "strip-ansi": "^6.0.0" @@ -12964,6 +13110,7 @@ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -12974,6 +13121,7 @@ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -12987,6 +13135,7 @@ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -13006,6 +13155,7 @@ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -13021,6 +13171,7 @@ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -13030,7 +13181,8 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/string-width-cjs/node_modules/strip-ansi": { "version": "6.0.1", @@ -13038,6 +13190,7 @@ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -13164,6 +13317,7 @@ "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -13181,6 +13335,7 @@ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -13194,6 +13349,7 @@ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -13204,6 +13360,7 @@ "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -13214,6 +13371,7 @@ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -13244,21 +13402,20 @@ "license": "MIT" }, "node_modules/styled-components": { - "version": "6.1.19", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.19.tgz", - "integrity": "sha512-1v/e3Dl1BknC37cXMhwGomhO8AkYmN41CqyX9xhUDxry1ns3BFQy2lLDRQXJRdVVWB9OHemv/53xaStimvWyuA==", + "version": "6.3.12", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.3.12.tgz", + "integrity": "sha512-hFR6xsVkVYbsdcUlzPYFvFfoc6o2KlV0VvgRIQwSYMtdThM7SCxnjX9efh/cWce2kTq16I/Kl3xM98xiLptsXA==", "license": "MIT", - "peer": true, "dependencies": { - "@emotion/is-prop-valid": "1.2.2", - "@emotion/unitless": "0.8.1", - "@types/stylis": "4.2.5", + "@emotion/is-prop-valid": "1.4.0", + "@emotion/unitless": "0.10.0", + "@types/stylis": "4.2.7", "css-to-react-native": "3.2.0", - "csstype": "3.1.3", + "csstype": "3.2.3", "postcss": "8.4.49", "shallowequal": "1.1.0", - "stylis": "4.3.2", - "tslib": "2.6.2" + "stylis": "4.3.6", + "tslib": "2.8.1" }, "engines": { "node": ">= 16" @@ -13270,35 +13427,13 @@ "peerDependencies": { "react": ">= 16.8.0", "react-dom": ">= 16.8.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } } }, - "node_modules/styled-components/node_modules/@emotion/is-prop-valid": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", - "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", - "license": "MIT", - "dependencies": { - "@emotion/memoize": "^0.8.1" - } - }, - "node_modules/styled-components/node_modules/@emotion/memoize": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", - "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==", - "license": "MIT" - }, - "node_modules/styled-components/node_modules/@emotion/unitless": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", - "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==", - "license": "MIT" - }, - "node_modules/styled-components/node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "license": "MIT" - }, "node_modules/styled-components/node_modules/postcss": { "version": "8.4.49", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", @@ -13328,17 +13463,11 @@ } }, "node_modules/styled-components/node_modules/stylis": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", - "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", "license": "MIT" }, - "node_modules/styled-components/node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "license": "0BSD" - }, "node_modules/styled-jsx": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", @@ -13421,18 +13550,18 @@ } }, "node_modules/swagger2openapi/node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", "license": "ISC", "engines": { "node": ">= 6" } }, "node_modules/swr": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.8.tgz", - "integrity": "sha512-gaCPRVoMq8WGDcWj9p4YWzCMPHzE0WNl6W8ADIx9c3JBEIdMkJGMzW+uzXvxHMltwcYACr9jP+32H8/hgwMR7w==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.4.1.tgz", + "integrity": "sha512-2CC6CiKQtEwaEeNiqWTAw9PGykW8SR5zZX8MZk6TeAvEAnVS7Visz8WzphqgtQ8v2xz/4Q5K+j+SeMaKXeeQIA==", "license": "MIT", "dependencies": { "dequal": "^2.0.3", @@ -13448,6 +13577,7 @@ "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@pkgr/core": "^0.2.9" }, @@ -13459,16 +13589,16 @@ } }, "node_modules/tailwindcss": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", - "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz", + "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", "dev": true, "license": "MIT" }, "node_modules/tapable": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", + "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", "dev": true, "license": "MIT", "engines": { @@ -13485,6 +13615,7 @@ "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", @@ -13495,11 +13626,12 @@ } }, "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -13512,6 +13644,7 @@ "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -13528,11 +13661,12 @@ } }, "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.4.tgz", + "integrity": "sha512-twmL+S8+7yIsE9wsqgzU3E8/LumN3M3QELrBZ20OdmQ9jB2JvW5oZtBEmft84k/Gs5CG9mqtWc6Y9vW+JEzGxw==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -13576,12 +13710,11 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -13594,7 +13727,8 @@ "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true, - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/to-regex-range": { "version": "5.0.1", @@ -13625,9 +13759,9 @@ "license": "MIT" }, "node_modules/ts-api-utils": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.3.0.tgz", - "integrity": "sha512-6eg3Y9SF7SsAvGzRHQvvc1skDAhwI4YQ32ui1scxD1Ccr0G5qIIbUBT3pFTKX8kmWIQClHobtUdNuaBgwdfdWg==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", "dev": true, "license": "MIT", "engines": { @@ -13764,6 +13898,7 @@ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=4" } @@ -13774,6 +13909,7 @@ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, "license": "(MIT OR CC0-1.0)", + "peer": true, "engines": { "node": ">=10" }, @@ -13865,7 +14001,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -13875,16 +14010,107 @@ } }, "node_modules/typescript-eslint": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.51.0.tgz", - "integrity": "sha512-jh8ZuM5oEh2PSdyQG9YAEM1TCGuWenLSuSUhf/irbVUNW9O5FhbFVONviN2TgMTBnUmyHv7E56rYnfLZK6TkiA==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.2.tgz", + "integrity": "sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.57.2", + "@typescript-eslint/parser": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2", + "@typescript-eslint/utils": "8.57.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", + "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.57.2", + "@typescript-eslint/tsconfig-utils": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/project-service": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz", + "integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.57.2", + "@typescript-eslint/types": "^8.57.2", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz", + "integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz", + "integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.51.0", - "@typescript-eslint/parser": "8.51.0", - "@typescript-eslint/typescript-estree": "8.51.0", - "@typescript-eslint/utils": "8.51.0" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -13894,10 +14120,49 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, + "node_modules/typescript-eslint/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/typescript-eslint/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/typescript-eslint/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/uglify-js": { "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", @@ -13932,9 +14197,9 @@ } }, "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", "dev": true, "license": "MIT" }, @@ -14062,6 +14327,7 @@ "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", @@ -14076,7 +14342,8 @@ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/walker": { "version": "1.0.8", @@ -14084,6 +14351,7 @@ "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "makeerror": "1.0.12" } @@ -14232,6 +14500,7 @@ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -14251,6 +14520,7 @@ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -14269,6 +14539,7 @@ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -14278,7 +14549,8 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/wrap-ansi-cjs/node_modules/string-width": { "version": "4.2.3", @@ -14286,6 +14558,7 @@ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -14301,6 +14574,7 @@ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -14314,6 +14588,7 @@ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -14326,7 +14601,8 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/write-file-atomic": { "version": "5.0.1", @@ -14334,6 +14610,7 @@ "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" @@ -14369,9 +14646,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", - "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", "license": "ISC", "bin": { "yaml": "bin.mjs" @@ -14471,11 +14748,10 @@ } }, "node_modules/zod": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.4.tgz", - "integrity": "sha512-Zw/uYiiyF6pUT1qmKbZziChgNPRu+ZRneAsMUDU6IwmXdWt5JwcUfy2bvLOCUtz5UniaN/Zx5aFttZYbYc7O/A==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index 3a04e48a..92f045ff 100644 --- a/package.json +++ b/package.json @@ -18,54 +18,54 @@ "@emotion/react": "^11.13.3", "@emotion/styled": "^11.14.1", "@fontsource/poppins": "^5.2.7", - "@fortawesome/fontawesome-svg-core": "^7.1.0", - "@fortawesome/free-brands-svg-icons": "^7.1.0", - "@fortawesome/free-regular-svg-icons": "^7.1.0", - "@fortawesome/free-solid-svg-icons": "^7.1.0", - "@fortawesome/react-fontawesome": "^3.1.1", - "@mui/icons-material": "^7.3.6", + "@fortawesome/fontawesome-svg-core": "^7.2.0", + "@fortawesome/free-brands-svg-icons": "^7.2.0", + "@fortawesome/free-regular-svg-icons": "^7.2.0", + "@fortawesome/free-solid-svg-icons": "^7.2.0", + "@fortawesome/react-fontawesome": "^3.3.0", + "@mui/icons-material": "^7.3.9", "@mui/material": "^7.0.1", - "@octokit/auth-app": "^8.1.2", + "@octokit/auth-app": "^8.2.0", "@octokit/core": "^7.0.6", "@octokit/webhooks": "~14.2.0", - "core-js": "^3.47.0", + "core-js": "^3.49.0", "encoding": "^0.1.13", "figma-squircle": "^1.1.0", "install": "^0.13.0", - "ioredis": "^5.8.2", + "ioredis": "^5.10.1", "mobx": "^6.15.0", - "next": "16.1.1", + "next": "16.2.3", "next-auth": "^5.0.0-beta.30", - "npm": "^11.7.0", + "npm": "^11.12.1", "nprogress": "^0.2.0", "octokit": "^5.0.5", - "react": "^19.2.3", - "react-dom": "^19.2.3", - "redis-semaphore": "^5.6.2", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "redis-semaphore": "^5.7.0", "redoc": "^2.5.2", "sharp": "^0.34.2", - "styled-components": "^6.1.19", - "swr": "^2.3.8", + "styled-components": "^6.3.12", + "swr": "^2.4.1", "usehooks-ts": "^3.1.1", - "yaml": "^2.8.2", - "zod": "^4.3.4" + "yaml": "^2.8.3", + "zod": "^4.3.6" }, "devDependencies": { "@auth/pg-adapter": "^1.11.1", - "@tailwindcss/postcss": "^4.1.18", + "@tailwindcss/postcss": "^4.2.2", "@types/jest": "^30.0.0", - "@types/node": "^25.0.3", + "@types/node": "^25.5.0", "@types/nprogress": "^0.2.3", - "@types/pg": "^8.16.0", - "@types/react": "^19.2.7", + "@types/pg": "^8.20.0", + "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", - "@typescript-eslint/eslint-plugin": "^8.51.0", - "@typescript-eslint/parser": "^8.51.0", - "typescript-eslint": "^8.51.0", - "eslint": "^9.39.2", - "eslint-config-next": "^16.1.1", - "pg": "^8.16.3", - "postcss": "^8.5.6", + "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/parser": "^8.57.2", + "typescript-eslint": "^8.57.2", + "eslint": "^9.39.4", + "eslint-config-next": "^16.2.1", + "pg": "^8.20.0", + "postcss": "^8.5.10", "tailwindcss": "^4.1.4", "ts-jest": "^29.4.6", "typescript": "^5.9.3" diff --git a/src/app/(authed)/(project-doc)/[...slug]/page.tsx b/src/app/(authed)/(project-doc)/[...slug]/page.tsx index 02473315..4a2e7743 100644 --- a/src/app/(authed)/(project-doc)/[...slug]/page.tsx +++ b/src/app/(authed)/(project-doc)/[...slug]/page.tsx @@ -1,21 +1,45 @@ "use client" -import { useContext, useEffect } from "react" +import { useEffect } from "react" +import { useParams } from "next/navigation" import ErrorMessage from "@/common/ui/ErrorMessage" import { updateWindowTitle } from "@/features/projects/domain" import { useProjectSelection } from "@/features/projects/data" import Documentation from "@/features/projects/view/Documentation" import NotFound from "@/features/projects/view/NotFound" -import { ProjectsContext } from "@/common/context/ProjectsContext" +import { useProjectDetails } from "@/features/projects/view/ProjectDetailsContext" import LoadingIndicator from "@/common/ui/LoadingIndicator" export default function Page() { + const params = useParams() + const slug = params.slug as string[] | undefined + const owner = slug?.[0] + const name = slug?.[1] + + const { fetchProject, isLoading, getError, getProject } = useProjectDetails() const { project, version, specification, navigateToSelectionIfNeeded } = useProjectSelection() - const { refreshing } = useContext(ProjectsContext) + + const loading = owner && name ? isLoading(owner, name) : false + const error = owner && name ? getError(owner, name) : null + + // cachedProject distinguishes not-yet-fetched (undefined) from not-found (null). + // useProjectSelection converts null→undefined, so we read the cache directly here. + const cachedProject = owner && name ? getProject(owner, name) : undefined + + // Fetch project details when the page loads. + // Only fetch when the project is undefined (not yet requested). + // A null value means it was fetched and not found — don't retry. + useEffect(() => { + if (owner && name && cachedProject === undefined && !loading) { + fetchProject(owner, name) + } + }, [owner, name, loading, cachedProject, fetchProject]) + // Ensure the URL reflects the current selection of project, version, and specification. useEffect(() => { navigateToSelectionIfNeeded() }, [project, version, specification, navigateToSelectionIfNeeded]) + useEffect(() => { if (!project) { return @@ -28,18 +52,23 @@ export default function Page() { }) }, [project, version, specification]) + if (loading || (cachedProject === undefined && !error)) { + return + } + + if (error) { + return + } + return ( <> {project && version && specification && } - {project && (!version || !specification) && !refreshing && + {project && (!version || !specification) && } - {!project && !version && !specification && refreshing && - - } - {!project && !refreshing && } + {cachedProject === null && } ) } diff --git a/src/app/(authed)/layout.tsx b/src/app/(authed)/layout.tsx index 798d2fe5..f6699754 100644 --- a/src/app/(authed)/layout.tsx +++ b/src/app/(authed)/layout.tsx @@ -1,9 +1,10 @@ import { redirect } from "next/navigation"; import { SessionProvider } from "next-auth/react"; -import { session, projectDataSource } from "@/composition"; +import { session } from "@/composition"; import ErrorHandler from "@/common/ui/ErrorHandler"; import SessionBarrier from "@/features/auth/view/SessionBarrier"; -import ProjectsContextProvider from "@/features/projects/view/ProjectsContextProvider"; +import ProjectListContextProvider from "@/features/projects/view/ProjectListContextProvider"; +import ProjectDetailsContextProvider from "@/features/projects/view/ProjectDetailsContextProvider"; import { SidebarTogglableContextProvider, SplitView, @@ -19,18 +20,17 @@ export default async function Layout({ return redirect("/api/auth/signin"); } - const projects = await projectDataSource.getProjects(); - - return ( - - - {children} - - + + + + {children} + + + diff --git a/src/app/api/projects/[owner]/[repo]/route.ts b/src/app/api/projects/[owner]/[repo]/route.ts new file mode 100644 index 00000000..ec264ccc --- /dev/null +++ b/src/app/api/projects/[owner]/[repo]/route.ts @@ -0,0 +1,33 @@ +import { NextResponse } from "next/server" +import { session, projectDetailsDataSource } from "@/composition" +import { makeUnauthenticatedAPIErrorResponse } from "@/common" + +export async function GET( + _request: Request, + { params }: { params: Promise<{ owner: string; repo: string }> } +) { + const isAuthenticated = await session.getIsAuthenticated() + if (!isAuthenticated) { + return makeUnauthenticatedAPIErrorResponse() + } + const { owner, repo } = await params + + try { + const project = await projectDetailsDataSource.getProjectDetails(owner, repo) + + if (!project) { + return NextResponse.json( + { error: "Project not found" }, + { status: 404 } + ) + } + + return NextResponse.json({ project }) + } catch (error) { + console.error(`Failed to fetch project details for ${owner}/${repo}:`, error) + return NextResponse.json( + { error: "Failed to fetch project details" }, + { status: 500 } + ) + } +} diff --git a/src/app/api/projects/route.ts b/src/app/api/projects/route.ts new file mode 100644 index 00000000..bb00bc89 --- /dev/null +++ b/src/app/api/projects/route.ts @@ -0,0 +1,21 @@ +import { NextRequest, NextResponse } from "next/server" +import { session, projectListDataSource } from "@/composition" +import { makeUnauthenticatedAPIErrorResponse } from "@/common" + +export async function GET(request: NextRequest) { + const isAuthenticated = await session.getIsAuthenticated() + if (!isAuthenticated) { + return makeUnauthenticatedAPIErrorResponse() + } + const refresh = request.nextUrl.searchParams.get("refresh") === "true" + try { + const projects = await projectListDataSource.getProjectList({ refresh }) + return NextResponse.json({ projects }) + } catch (error) { + console.error("Failed to fetch project list:", error) + return NextResponse.json( + { error: "Failed to fetch project list" }, + { status: 500 } + ) + } +} diff --git a/src/app/api/refresh-projects/route.ts b/src/app/api/refresh-projects/route.ts deleted file mode 100644 index fbaedd4c..00000000 --- a/src/app/api/refresh-projects/route.ts +++ /dev/null @@ -1,9 +0,0 @@ - -import { NextResponse } from "next/server" -import { projectDataSource } from "@/composition"; - - -export async function POST() { - const projects = await projectDataSource.refreshProjects() - return NextResponse.json({ projects }) -} \ No newline at end of file diff --git a/src/common/context/ProjectsContext.ts b/src/common/context/ProjectsContext.ts index 4666e695..9ea9b620 100644 --- a/src/common/context/ProjectsContext.ts +++ b/src/common/context/ProjectsContext.ts @@ -1,18 +1,5 @@ "use client" import { createContext } from "react" -import { Project } from "@/features/projects/domain" export const SidebarTogglableContext = createContext(true) - -type ProjectsContextValue = { - refreshing: boolean, - projects: Project[], - refreshProjects: () => void, -} - -export const ProjectsContext = createContext({ - refreshing: false, - projects: [], - refreshProjects: () => {}, -}) diff --git a/src/composition.ts b/src/composition.ts index 8187b966..e4297013 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -17,13 +17,12 @@ import { } from "@/common" import { GitHubLoginDataSource, - GitHubProjectDataSource, - GitHubRepositoryDataSource + GitHubProjectListDataSource, + GitHubProjectDetailsDataSource } from "@/features/projects/data" import { - CachingProjectDataSource, - FilteringGitHubRepositoryDataSource, - ProjectRepository + CachingProjectListDataSource, + ProjectListRepository } from "@/features/projects/domain" import { GitHubOAuthTokenRefresher @@ -175,11 +174,6 @@ const projectUserDataRepository = new KeyValueUserDataRepository({ baseKey: "projects" }) -export const projectRepository = new ProjectRepository({ - userIDReader: session, - repository: projectUserDataRepository -}) - export const encryptionService = new RsaEncryptionService({ publicKey: Buffer.from(env.getOrThrow("ENCRYPTION_PUBLIC_KEY_BASE_64"), "base64").toString("utf-8"), privateKey: Buffer.from(env.getOrThrow("ENCRYPTION_PRIVATE_KEY_BASE_64"), "base64").toString("utf-8") @@ -187,29 +181,43 @@ export const encryptionService = new RsaEncryptionService({ export const remoteConfigEncoder = new RemoteConfigEncoder(encryptionService) -export const projectDataSource = new CachingProjectDataSource({ - dataSource: new GitHubProjectDataSource({ - repositoryDataSource: new FilteringGitHubRepositoryDataSource({ - hiddenRepositories: listFromCommaSeparatedString(env.get("HIDDEN_REPOSITORIES")), - dataSource: new GitHubRepositoryDataSource({ - loginsDataSource: new GitHubLoginDataSource({ - graphQlClient: userGitHubClient - }), - graphQlClient: userGitHubClient, - repositoryNameSuffix: env.getOrThrow("REPOSITORY_NAME_SUFFIX"), - projectConfigurationFilename: env.getOrThrow("FRAMNA_DOCS_PROJECT_CONFIGURATION_FILENAME") - }) - }), - repositoryNameSuffix: env.getOrThrow("REPOSITORY_NAME_SUFFIX"), - encryptionService: encryptionService, - remoteConfigEncoder: remoteConfigEncoder +const gitHubProjectListDataSource = new GitHubProjectListDataSource({ + loginsDataSource: new GitHubLoginDataSource({ + graphQlClient: userGitHubClient }), - repository: projectRepository + graphQlClient: userGitHubClient, + repositoryNameSuffix: env.getOrThrow("REPOSITORY_NAME_SUFFIX"), + projectConfigurationFilename: env.getOrThrow("FRAMNA_DOCS_PROJECT_CONFIGURATION_FILENAME"), + hiddenRepositories: listFromCommaSeparatedString(env.get("HIDDEN_REPOSITORIES")) +}) + +const projectListUserDataRepository = new KeyValueUserDataRepository({ + store: new RedisKeyValueStore(env.getOrThrow("REDIS_URL")), + baseKey: "projectList" +}) + +const projectListRepository = new ProjectListRepository({ + userIDReader: session, + repository: projectListUserDataRepository +}) + +export const projectListDataSource = new CachingProjectListDataSource({ + dataSource: gitHubProjectListDataSource, + repository: projectListRepository +}) + +export const projectDetailsDataSource = new GitHubProjectDetailsDataSource({ + graphQlClient: userGitHubClient, + repositoryNameSuffix: env.getOrThrow("REPOSITORY_NAME_SUFFIX"), + projectConfigurationFilename: env.getOrThrow("FRAMNA_DOCS_PROJECT_CONFIGURATION_FILENAME"), + encryptionService: encryptionService, + remoteConfigEncoder: remoteConfigEncoder }) export const logOutHandler = new ErrorIgnoringLogOutHandler( new CompositeLogOutHandler([ - new UserDataCleanUpLogOutHandler(session, projectUserDataRepository) + new UserDataCleanUpLogOutHandler(session, projectUserDataRepository), + new UserDataCleanUpLogOutHandler(session, projectListUserDataRepository) ]) ) diff --git a/src/features/projects/data/GitHubProjectDataSource.ts b/src/features/projects/data/GitHubProjectDataSource.ts deleted file mode 100644 index 9976ac45..00000000 --- a/src/features/projects/data/GitHubProjectDataSource.ts +++ /dev/null @@ -1,299 +0,0 @@ -import { createHash } from "crypto" -import { IEncryptionService } from "@/features/encrypt/EncryptionService" -import { - Project, - Version, - IProjectConfig, - IProjectDataSource, - ProjectConfigParser, - ProjectConfigRemoteVersion, - IGitHubRepositoryDataSource, - GitHubRepository, - GitHubRepositoryRef, - ProjectConfigRemoteSpecification -} from "../domain" -import RemoteConfig from "../domain/RemoteConfig" -import { IRemoteConfigEncoder } from "../domain/RemoteConfigEncoder" - -export default class GitHubProjectDataSource implements IProjectDataSource { - private readonly repositoryDataSource: IGitHubRepositoryDataSource - private readonly repositoryNameSuffix: string - private readonly encryptionService: IEncryptionService - private readonly remoteConfigEncoder: IRemoteConfigEncoder - - constructor(config: { - repositoryDataSource: IGitHubRepositoryDataSource - repositoryNameSuffix: string - encryptionService: IEncryptionService - remoteConfigEncoder: IRemoteConfigEncoder - }) { - this.repositoryDataSource = config.repositoryDataSource - this.repositoryNameSuffix = config.repositoryNameSuffix - this.encryptionService = config.encryptionService - this.remoteConfigEncoder = config.remoteConfigEncoder - } - - async getProjects(): Promise { - const repositories = await this.repositoryDataSource.getRepositories() - return repositories.map(repository => { - return this.mapProject(repository) - }) - .filter((project: Project) => { - return project.versions.length > 0 - }) - .sort((a: Project, b: Project) => { - return a.name.localeCompare(b.name) - }) - } - - private mapProject(repository: GitHubRepository): Project { - const config = this.getConfig(repository) - let imageURL: string | undefined - if (config && config.image) { - imageURL = this.getGitHubBlobURL({ - ownerName: repository.owner, - repositoryName: repository.name, - path: config.image, - ref: repository.defaultBranchRef.id - }) - } - const versions = this.sortVersions( - this.addRemoteVersions( - this.getVersions(repository), - config?.remoteVersions || [] - ), - repository.defaultBranchRef.name - ).filter(version => { - return version.specifications.length > 0 - }) - .map(version => this.setDefaultSpecification(version, config?.defaultSpecificationName)) - const defaultName = repository.name.replace(new RegExp(this.repositoryNameSuffix + "$"), "") - return { - id: `${repository.owner}-${defaultName}`, - owner: repository.owner, - name: defaultName, - displayName: config?.name || defaultName, - versions, - imageURL: imageURL, - ownerUrl: `https://github.com/${repository.owner}`, - url: `https://github.com/${repository.owner}/${repository.name}` - } - } - - private getConfig(repository: GitHubRepository): IProjectConfig | null { - const yml = repository.configYml || repository.configYaml - if (!yml || !yml.text || yml.text.length == 0) { - return null - } - const parser = new ProjectConfigParser() - return parser.parse(yml.text) - } - - private getVersions(repository: GitHubRepository): Version[] { - const branchVersions = repository.branches.map(branch => { - const isDefaultRef = branch.name == repository.defaultBranchRef.name - return this.mapVersionFromRef({ - ownerName: repository.owner, - repositoryName: repository.name, - ref: branch, - isDefaultRef - }) - }) - const tagVersions = repository.tags.map(tag => { - return this.mapVersionFromRef({ - ownerName: repository.owner, - repositoryName: repository.name, - ref: tag - }) - }) - return branchVersions.concat(tagVersions) - } - - private mapVersionFromRef({ - ownerName, - repositoryName, - ref, - isDefaultRef - }: { - ownerName: string - repositoryName: string - ref: GitHubRepositoryRef - isDefaultRef?: boolean - }): Version { - const specifications = ref.files.filter(file => { - return this.isOpenAPISpecification(file.name) - }).map(file => { - const isFileChanged = ref.changedFiles?.includes(file.name) ?? false - return { - id: file.name, - name: file.name, - url: this.getGitHubBlobURL({ - ownerName, - repositoryName, - path: file.name, - ref: ref.id - }), - editURL: `https://github.com/${ownerName}/${repositoryName}/edit/${ref.name}/${encodeURIComponent(file.name)}`, - diffURL: isFileChanged ? this.getGitHubDiffURL({ - ownerName, - repositoryName, - path: file.name, - baseRefOid: ref.baseRefOid, - headRefOid: ref.id - }) : undefined, - diffBaseBranch: isFileChanged ? ref.baseRef : undefined, - diffBaseOid: isFileChanged ? ref.baseRefOid : undefined, - diffPrUrl: isFileChanged && ref.prNumber ? `https://github.com/${ownerName}/${repositoryName}/pull/${ref.prNumber}` : undefined, - isDefault: false // initial value - } - }).sort((a, b) => a.name.localeCompare(b.name)) - return { - id: ref.name, - name: ref.name, - specifications: specifications, - url: `https://github.com/${ownerName}/${repositoryName}/tree/${ref.name}`, - isDefault: isDefaultRef || false, - } - } - - private isOpenAPISpecification(filename: string) { - return !filename.startsWith(".") && ( - filename.endsWith(".yml") || filename.endsWith(".yaml") - ) - } - - private getGitHubBlobURL({ - ownerName, - repositoryName, - path, - ref - }: { - ownerName: string - repositoryName: string - path: string - ref: string - }): string { - const encodedPath = path.split('/').map(segment => encodeURIComponent(segment)).join('/') - return `/api/blob/${ownerName}/${repositoryName}/${encodedPath}?ref=${ref}` - } - - private getGitHubDiffURL({ - ownerName, - repositoryName, - path, - baseRefOid, - headRefOid - }: { - ownerName: string; - repositoryName: string; - path: string; - baseRefOid: string | undefined; - headRefOid: string } - ): string | undefined { - if (!baseRefOid) { - return undefined - } else { - const encodedPath = path.split('/').map(segment => encodeURIComponent(segment)).join('/') - return `/api/diff/${ownerName}/${repositoryName}/${encodedPath}?baseRefOid=${baseRefOid}&to=${headRefOid}` - } - } - - private addRemoteVersions( - existingVersions: Version[], - remoteVersions: ProjectConfigRemoteVersion[] - ): Version[] { - const versions = [...existingVersions] - const versionIds = versions.map(e => e.id) - for (const remoteVersion of remoteVersions) { - const baseVersionId = this.makeURLSafeID( - (remoteVersion.id || remoteVersion.name).toLowerCase() - ) - // If the version ID exists then we suffix it with a number to ensure unique versions. - // E.g. if "foo" already exists, we make it "foo1". - const existingVersionIdCount = versionIds.filter(e => e == baseVersionId).length - const versionId = baseVersionId + (existingVersionIdCount > 0 ? existingVersionIdCount : "") - const specifications = remoteVersion.specifications.map(e => { - const remoteConfig: RemoteConfig = { - url: e.url, - auth: this.tryDecryptAuth(e) - }; - - const encodedRemoteConfig = this.remoteConfigEncoder.encode(remoteConfig); - // 16 hex chars (64 bits) - sufficient for change detection, not cryptographic security - const configHash = createHash("sha256").update(JSON.stringify(remoteConfig)).digest("hex").slice(0, 16); - - return { - id: this.makeURLSafeID((e.id || e.name).toLowerCase()), - name: e.name, - url: `/api/remotes/${encodedRemoteConfig}`, - urlHash: configHash, - isDefault: false // initial value - }; - }) - versions.push({ - id: versionId, - name: remoteVersion.name, - specifications, - isDefault: false - }) - versionIds.push(baseVersionId) - } - return versions - } - - private sortVersions(versions: Version[], defaultBranchName: string): Version[] { - const candidateDefaultBranches = [ - defaultBranchName, "main", "master", "develop", "development", "trunk" - ] - // Reverse them so the top-priority branches end up at the top of the list. - .reverse() - const copiedVersions = [...versions].sort((a, b) => { - return a.name.localeCompare(b.name) - }) - // Move the top-priority branches to the top of the list. - for (const candidateDefaultBranch of candidateDefaultBranches) { - const defaultBranchIndex = copiedVersions.findIndex(version => { - return version.name === candidateDefaultBranch - }) - if (defaultBranchIndex !== -1) { - const branchVersion = copiedVersions[defaultBranchIndex] - copiedVersions.splice(defaultBranchIndex, 1) - copiedVersions.splice(0, 0, branchVersion) - } - } - return copiedVersions - } - - private makeURLSafeID(str: string): string { - return str - .replace(/ /g, "-") - .replace(/[^A-Za-z0-9-]/g, "") - } - - private tryDecryptAuth(projectConfigRemoteSpec: ProjectConfigRemoteSpecification): { type: string, username: string, password: string } | undefined { - if (!projectConfigRemoteSpec.auth) { - return undefined - } - - try { - return { - type: projectConfigRemoteSpec.auth.type, - username: this.encryptionService.decrypt(projectConfigRemoteSpec.auth.encryptedUsername), - password: this.encryptionService.decrypt(projectConfigRemoteSpec.auth.encryptedPassword) - } - } catch (error) { - console.info(`Failed to decrypt remote specification auth for ${projectConfigRemoteSpec.name} (${projectConfigRemoteSpec.url}). Perhaps a different public key was used?:`, error); - return undefined - } - } - - private setDefaultSpecification(version: Version, defaultSpecificationName?: string): Version { - return { - ...version, - specifications: version.specifications.map(spec => ({ - ...spec, - isDefault: spec.name === defaultSpecificationName - })) - } - } -} diff --git a/src/features/projects/data/GitHubProjectDetailsDataSource.ts b/src/features/projects/data/GitHubProjectDetailsDataSource.ts new file mode 100644 index 00000000..5bcaf8ac --- /dev/null +++ b/src/features/projects/data/GitHubProjectDetailsDataSource.ts @@ -0,0 +1,358 @@ +import { createHash } from "crypto" +import { IEncryptionService } from "@/features/encrypt/EncryptionService" +import { + Project, + Version, + IProjectDetailsDataSource, + IGitHubGraphQLClient, + ProjectConfigParser, + IProjectConfig, + ProjectConfigRemoteVersion, + ProjectConfigRemoteSpecification +} from "../domain" +import RemoteConfig from "../domain/RemoteConfig" +import { IRemoteConfigEncoder } from "../domain/RemoteConfigEncoder" + +type GraphQLRef = { + name: string + target: { + oid: string + tree: { + entries: { name: string }[] + } + } +} + +type GraphQLPullRequest = { + number: number + headRefName: string + baseRefName: string + baseRefOid: string + files?: { + nodes?: { path: string }[] + } +} + +export default class GitHubProjectDetailsDataSource implements IProjectDetailsDataSource { + private readonly graphQlClient: IGitHubGraphQLClient + private readonly repositoryNameSuffix: string + private readonly projectConfigurationFilename: string + private readonly encryptionService: IEncryptionService + private readonly remoteConfigEncoder: IRemoteConfigEncoder + + constructor(config: { + graphQlClient: IGitHubGraphQLClient + repositoryNameSuffix: string + projectConfigurationFilename: string + encryptionService: IEncryptionService + remoteConfigEncoder: IRemoteConfigEncoder + }) { + this.graphQlClient = config.graphQlClient + this.repositoryNameSuffix = config.repositoryNameSuffix + this.projectConfigurationFilename = config.projectConfigurationFilename.replace(/\.ya?ml$/, "") + this.encryptionService = config.encryptionService + this.remoteConfigEncoder = config.remoteConfigEncoder + } + + async getProjectDetails(owner: string, repo: string): Promise { + const repoName = repo.endsWith(this.repositoryNameSuffix) + ? repo + : `${repo}${this.repositoryNameSuffix}` + + let response: Awaited> + try { + response = await this.fetchRepository(owner, repoName) + } catch (error) { + if (this.isNotFoundError(error)) { + return null + } + throw error + } + if (!response.repository) { + return null + } + + const repository = response.repository + const pullRequests = this.mapPullRequests(repository.pullRequests?.edges || []) + + return this.mapToProject({ + owner, + name: repository.name, + defaultBranchRef: repository.defaultBranchRef, + configYml: repository.configYml, + configYaml: repository.configYaml, + branches: repository.branches?.edges?.map((e: { node: GraphQLRef }) => e.node) || [], + tags: repository.tags?.edges?.map((e: { node: GraphQLRef }) => e.node) || [], + pullRequests + }) + } + + private async fetchRepository(owner: string, name: string) { + const request = { + query: ` + query ProjectDetails($owner: String!, $name: String!) { + repository(owner: $owner, name: $name) { + name + defaultBranchRef { + name + target { + ... on Commit { oid } + } + } + configYml: object(expression: "HEAD:${this.projectConfigurationFilename}.yml") { + ... on Blob { text } + } + configYaml: object(expression: "HEAD:${this.projectConfigurationFilename}.yaml") { + ... on Blob { text } + } + branches: refs(refPrefix: "refs/heads/", first: 100) { + edges { + node { + name + target { + ... on Commit { + oid + tree { entries { name } } + } + } + } + } + } + tags: refs(refPrefix: "refs/tags/", first: 100) { + edges { + node { + name + target { + ... on Commit { + oid + tree { entries { name } } + } + } + } + } + } + pullRequests(first: 100, states: [OPEN]) { + edges { + node { + number + headRefName + baseRefName + baseRefOid + files(first: 100) { + nodes { path } + } + } + } + } + } + } + `, + variables: { owner, name } + } + + return await this.graphQlClient.graphql(request) + } + + private mapPullRequests(edges: { node: GraphQLPullRequest }[]): Map { + const map = new Map() + for (const edge of edges) { + const pr = edge.node + map.set(pr.headRefName, { + number: pr.number, + baseRefName: pr.baseRefName, + baseRefOid: pr.baseRefOid, + changedFiles: pr.files?.nodes?.map(f => f.path) || [] + }) + } + return map + } + + private mapToProject(data: { + owner: string + name: string + defaultBranchRef: { name: string; target: { oid: string } } + configYml?: { text: string } + configYaml?: { text: string } + branches: GraphQLRef[] + tags: GraphQLRef[] + pullRequests: Map + }): Project { + const config = this.parseConfig(data.configYml, data.configYaml) + const defaultName = data.name.replace(new RegExp(this.repositoryNameSuffix + "$"), "") + + let imageURL: string | undefined + if (config?.image) { + imageURL = `/api/blob/${data.owner}/${data.name}/${encodeURIComponent(config.image)}?ref=${data.defaultBranchRef.target.oid}` + } + + const branchVersions = data.branches.map(branch => { + const pr = data.pullRequests.get(branch.name) + return this.mapVersion({ + owner: data.owner, + repoName: data.name, + ref: branch, + isDefault: branch.name === data.defaultBranchRef.name, + pr + }) + }) + + const tagVersions = data.tags.map(tag => + this.mapVersion({ owner: data.owner, repoName: data.name, ref: tag }) + ) + + const versions = this.sortVersions( + this.addRemoteVersions( + [...branchVersions, ...tagVersions], + config?.remoteVersions || [] + ), + data.defaultBranchRef.name + ) + .filter(v => v.specifications.length > 0) + .map(v => this.setDefaultSpecification(v, config?.defaultSpecificationName)) + + return { + id: `${data.owner}-${defaultName}`, + owner: data.owner, + name: defaultName, + displayName: config?.name || defaultName, + versions, + imageURL, + ownerUrl: `https://github.com/${data.owner}`, + url: `https://github.com/${data.owner}/${data.name}` + } + } + + private parseConfig(configYml?: { text: string }, configYaml?: { text: string }): IProjectConfig | null { + const yml = configYml || configYaml + if (!yml?.text) return null + return new ProjectConfigParser().parse(yml.text) + } + + private mapVersion(params: { + owner: string + repoName: string + ref: GraphQLRef + isDefault?: boolean + pr?: { number: number; baseRefName: string; baseRefOid: string; changedFiles: string[] } + }): Version { + const { owner, repoName, ref, isDefault, pr } = params + + const specifications = ref.target.tree.entries + .filter(f => this.isOpenAPISpec(f.name)) + .map(file => { + const isChanged = pr?.changedFiles.includes(file.name) ?? false + return { + id: file.name, + name: file.name, + url: `/api/blob/${owner}/${repoName}/${encodeURIComponent(file.name)}?ref=${ref.target.oid}`, + editURL: `https://github.com/${owner}/${repoName}/edit/${ref.name}/${encodeURIComponent(file.name)}`, + diffURL: isChanged ? `/api/diff/${owner}/${repoName}/${encodeURIComponent(file.name)}?baseRefOid=${pr!.baseRefOid}&to=${ref.target.oid}` : undefined, + diffBaseBranch: isChanged ? pr!.baseRefName : undefined, + diffBaseOid: isChanged ? pr!.baseRefOid : undefined, + diffPrUrl: isChanged ? `https://github.com/${owner}/${repoName}/pull/${pr!.number}` : undefined, + isDefault: false + } + }) + .sort((a, b) => a.name.localeCompare(b.name)) + + return { + id: ref.name, + name: ref.name, + specifications, + url: `https://github.com/${owner}/${repoName}/tree/${ref.name}`, + isDefault: isDefault || false + } + } + + private isOpenAPISpec(filename: string): boolean { + return !filename.startsWith(".") && (filename.endsWith(".yml") || filename.endsWith(".yaml")) + } + + private sortVersions(versions: Version[], defaultBranchName: string): Version[] { + const priority = [defaultBranchName, "main", "master", "develop", "development", "trunk"].reverse() + const sorted = [...versions].sort((a, b) => a.name.localeCompare(b.name)) + + for (const branch of priority) { + const idx = sorted.findIndex(v => v.name === branch) + if (idx !== -1) { + const [version] = sorted.splice(idx, 1) + sorted.unshift(version) + } + } + return sorted + } + + private addRemoteVersions(versions: Version[], remoteVersions: ProjectConfigRemoteVersion[]): Version[] { + const result = [...versions] + const ids = result.map(v => v.id) + + for (const rv of remoteVersions) { + const baseId = this.makeURLSafeID((rv.id || rv.name).toLowerCase()) + const count = ids.filter(id => id === baseId).length + const versionId = baseId + (count > 0 ? count : "") + + const specifications = rv.specifications.map(spec => { + const remoteConfig: RemoteConfig = { + url: spec.url, + auth: this.tryDecryptAuth(spec) + } + const encoded = this.remoteConfigEncoder.encode(remoteConfig) + const hash = createHash("sha256").update(JSON.stringify(remoteConfig)).digest("hex").slice(0, 16) + + return { + id: this.makeURLSafeID((spec.id || spec.name).toLowerCase()), + name: spec.name, + url: `/api/remotes/${encoded}`, + urlHash: hash, + isDefault: false + } + }) + + result.push({ id: versionId, name: rv.name, specifications, isDefault: false }) + ids.push(baseId) + } + + return result + } + + private makeURLSafeID(str: string): string { + return str.replace(/ /g, "-").replace(/[^A-Za-z0-9-]/g, "") + } + + private tryDecryptAuth(spec: ProjectConfigRemoteSpecification) { + if (!spec.auth) return undefined + try { + return { + type: spec.auth.type, + username: this.encryptionService.decrypt(spec.auth.encryptedUsername), + password: this.encryptionService.decrypt(spec.auth.encryptedPassword) + } + } catch (error) { + console.info(`Failed to decrypt remote specification auth for ${spec.name} (${spec.url}). Perhaps a different public key was used?:`, error) + return undefined + } + } + + private setDefaultSpecification(version: Version, defaultName?: string): Version { + return { + ...version, + specifications: version.specifications.map(spec => ({ + ...spec, + isDefault: spec.name === defaultName + })) + } + } + + private isNotFoundError(error: unknown): boolean { + if (!(error instanceof Error)) return false + const anyError = error as { type?: string; errors?: { type?: string }[] } + if (anyError.type === "NOT_FOUND") return true + if (anyError.errors?.some(e => e.type === "NOT_FOUND")) return true + return false + } +} diff --git a/src/features/projects/data/GitHubProjectListDataSource.ts b/src/features/projects/data/GitHubProjectListDataSource.ts new file mode 100644 index 00000000..dc5f6a37 --- /dev/null +++ b/src/features/projects/data/GitHubProjectListDataSource.ts @@ -0,0 +1,165 @@ +import { splitOwnerAndRepository } from "@/common" +import { + ProjectSummary, + IProjectListDataSource, + IGitHubLoginDataSource, + IGitHubGraphQLClient, + ProjectConfigParser +} from "../domain" + +type GraphQLProjectListRepository = { + readonly name: string + readonly owner: { + readonly login: string + } + readonly defaultBranchRef?: { + readonly target: { + readonly oid: string + } + } + readonly configYml?: { + readonly text: string + } + readonly configYaml?: { + readonly text: string + } +} + +export default class GitHubProjectListDataSource implements IProjectListDataSource { + private readonly loginsDataSource: IGitHubLoginDataSource + private readonly graphQlClient: IGitHubGraphQLClient + private readonly repositoryNameSuffix: string + private readonly projectConfigurationFilename: string + private readonly hiddenRepositories: { owner: string; repository: string }[] + + constructor(config: { + loginsDataSource: IGitHubLoginDataSource + graphQlClient: IGitHubGraphQLClient + repositoryNameSuffix: string + projectConfigurationFilename: string + hiddenRepositories: string[] + }) { + this.loginsDataSource = config.loginsDataSource + this.graphQlClient = config.graphQlClient + this.repositoryNameSuffix = config.repositoryNameSuffix + this.projectConfigurationFilename = config.projectConfigurationFilename.replace(/\.ya?ml$/, "") + this.hiddenRepositories = config.hiddenRepositories + .map(splitOwnerAndRepository) + .filter((e): e is { owner: string; repository: string } => e !== undefined) + } + + async getProjectList(): Promise { + const logins = await this.loginsDataSource.getLogins() + const repositories = await this.getRepositoriesForLogins(logins) + return repositories + .filter(repo => !this.isHidden(repo)) + .map(repo => this.mapToSummary(repo)) + .sort((a, b) => a.name.localeCompare(b.name)) + } + + private isHidden(repo: GraphQLProjectListRepository): boolean { + return this.hiddenRepositories.some( + hidden => hidden.owner === repo.owner.login && hidden.repository === repo.name + ) + } + + private async getRepositoriesForLogins(logins: string[]): Promise { + const searchQueries: string[] = [ + `"${this.repositoryNameSuffix}" in:name is:private`, + ...logins.map(login => `"${this.repositoryNameSuffix}" in:name user:${login} is:public`) + ] + + const results = await Promise.all( + searchQueries.map(query => this.searchRepositories(query)) + ) + + const allRepos = results.flat() + const uniqueRepos = this.deduplicateRepositories(allRepos) + return uniqueRepos.filter(repo => repo.name.endsWith(this.repositoryNameSuffix)) + } + + private async searchRepositories( + searchQuery: string, + cursor?: string + ): Promise { + const request = { + query: ` + query ProjectList($searchQuery: String!, $cursor: String) { + search(query: $searchQuery, type: REPOSITORY, first: 100, after: $cursor) { + results: nodes { + ... on Repository { + name + owner { login } + defaultBranchRef { + target { + ... on Commit { oid } + } + } + configYml: object(expression: "HEAD:${this.projectConfigurationFilename}.yml") { + ... on Blob { text } + } + configYaml: object(expression: "HEAD:${this.projectConfigurationFilename}.yaml") { + ... on Blob { text } + } + } + } + pageInfo { + hasNextPage + endCursor + } + } + } + `, + variables: { searchQuery, cursor } + } + + const response = await this.graphQlClient.graphql(request) + if (!response.search?.results) { + return [] + } + + const pageInfo = response.search.pageInfo + if (!pageInfo?.hasNextPage || !pageInfo?.endCursor) { + return response.search.results + } + + const nextResults = await this.searchRepositories(searchQuery, pageInfo.endCursor) + return response.search.results.concat(nextResults) + } + + private deduplicateRepositories(repos: GraphQLProjectListRepository[]): GraphQLProjectListRepository[] { + const seen = new Set() + return repos.filter(repo => { + const key = `${repo.owner.login}/${repo.name}` + if (seen.has(key)) return false + seen.add(key) + return true + }) + } + + private mapToSummary(repo: GraphQLProjectListRepository): ProjectSummary { + const config = this.parseConfig(repo) + const defaultName = repo.name.replace(new RegExp(this.repositoryNameSuffix + "$"), "") + + return { + id: `${repo.owner.login}-${defaultName}`, + name: defaultName, + displayName: config?.name || defaultName, + owner: repo.owner.login, + imageURL: config?.image ? this.makeImageURL(repo.owner.login, repo.name, config.image, repo.defaultBranchRef?.target.oid) : undefined, + url: `https://github.com/${repo.owner.login}/${repo.name}`, + ownerUrl: `https://github.com/${repo.owner.login}` + } + } + + private parseConfig(repo: GraphQLProjectListRepository) { + const yml = repo.configYml || repo.configYaml + if (!yml?.text) return null + const parser = new ProjectConfigParser() + return parser.parse(yml.text) + } + + private makeImageURL(owner: string, repo: string, imagePath: string, oid?: string): string { + return `/api/blob/${owner}/${repo}/${encodeURIComponent(imagePath)}?ref=${oid ?? "HEAD"}` + } +} diff --git a/src/features/projects/data/GitHubRepositoryDataSource.ts b/src/features/projects/data/GitHubRepositoryDataSource.ts deleted file mode 100644 index 6a776e34..00000000 --- a/src/features/projects/data/GitHubRepositoryDataSource.ts +++ /dev/null @@ -1,330 +0,0 @@ -import { - GitHubRepository, - IGitHubRepositoryDataSource, - IGitHubLoginDataSource, - IGitHubGraphQLClient -} from "../domain" - -type GraphQLGitHubRepository = { - readonly name: string - readonly owner: { - readonly login: string - } - readonly defaultBranchRef: { - readonly name: string - readonly target: { - readonly oid: string - } - } - readonly configYml?: { - readonly text: string - } - readonly configYaml?: { - readonly text: string - } - readonly branches: EdgesContainer - readonly tags: EdgesContainer -} - -type EdgesContainer = { - readonly edges: Edge[] -} - -type Edge = { - readonly node: T -} - -type GraphQLGitHubRepositoryRef = { - readonly name: string - readonly target: { - readonly oid: string - readonly tree: { - readonly entries: { - readonly name: string - }[] - } - } -} - -type GraphQLPullRequest = { - readonly number: number - readonly headRefName: string - readonly baseRefName: string - readonly baseRefOid: string - readonly changedFiles: string[] -} - -export default class GitHubProjectDataSource implements IGitHubRepositoryDataSource { - private readonly loginsDataSource: IGitHubLoginDataSource - private readonly graphQlClient: IGitHubGraphQLClient - private readonly repositoryNameSuffix: string - private readonly projectConfigurationFilename: string - - constructor(config: { - loginsDataSource: IGitHubLoginDataSource, - graphQlClient: IGitHubGraphQLClient, - repositoryNameSuffix: string, - projectConfigurationFilename: string - }) { - this.loginsDataSource = config.loginsDataSource - this.graphQlClient = config.graphQlClient - this.repositoryNameSuffix = config.repositoryNameSuffix - this.projectConfigurationFilename = config.projectConfigurationFilename.replace(/\.ya?ml$/, "") - } - - async getRepositories(): Promise { - const logins = await this.loginsDataSource.getLogins() - return await this.getRepositoriesForLogins({ logins }) - } - - private async getRepositoriesForLogins({ logins }: { logins: string[] }): Promise { - let searchQueries: string[] = [] - // Search for all private repositories the user has access to. This is needed to find - // repositories for external collaborators who do not belong to an organization. - searchQueries.push(`"${this.repositoryNameSuffix}" in:name is:private`) - // Search for public repositories belonging to a user or organization. - searchQueries = searchQueries.concat(logins.map(login => { - return `"${this.repositoryNameSuffix}" in:name user:${login} is:public` - })) - return await Promise.all(searchQueries.map(searchQuery => { - return this.getRepositoriesForSearchQuery({ searchQuery }) - })) - .then(e => e.flat()) - .then(repositories => { - // GitHub's search API does not enable searching for repositories whose name ends with "-openapi", - // only repositories whose names include "openapi" so we filter the results ourselves. - return repositories.filter(repository => { - return repository.name.endsWith(this.repositoryNameSuffix) - }) - }) - .then(repositories => { - // Ensure we don't have duplicates in the resulting repositories. - const uniqueIdentifiers = new Set() - return repositories.filter(repository => { - const identifier = `${repository.owner.login}-${repository.name}` - const alreadyAdded = uniqueIdentifiers.has(identifier) - uniqueIdentifiers.add(identifier) - return !alreadyAdded - }) - }) - .then(async repositories => { - // Fetch PRs for all repositories in a single query - const allPullRequests = await this.getOpenPullRequestsForRepositories( - repositories.map(repo => ({ - owner: repo.owner.login, - name: repo.name - })) - ) - - // Map from the internal model to the public model. - return repositories.map(repository => { - const repoKey = `${repository.owner.login}/${repository.name}` - const pullRequests = allPullRequests.get(repoKey) || new Map() - - const branches = repository.branches.edges.map(branch => { - const pr = pullRequests.get(branch.node.name) - - return { - id: branch.node.target.oid, - name: branch.node.name, - baseRef: pr?.baseRefName, - baseRefOid: pr?.baseRefOid, - prNumber: pr?.number, - files: branch.node.target.tree.entries, - changedFiles: pr?.changedFiles - } - }) - - return { - name: repository.name, - owner: repository.owner.login, - defaultBranchRef: { - id: repository.defaultBranchRef.target.oid, - name: repository.defaultBranchRef.name - }, - configYml: repository.configYml, - configYaml: repository.configYaml, - branches: branches, - tags: repository.tags.edges.map(branch => { - return { - id: branch.node.target.oid, - name: branch.node.name, - files: branch.node.target.tree.entries - } - }) - } - }) - }) - } - - private async getOpenPullRequestsForRepositories( - repositories: Array<{ owner: string, name: string }> - ): Promise>> { - if (repositories.length === 0) { - return new Map() - } - - // Build a query that fetches PRs for all repositories - const repoQueries = repositories - .map((repo, index) => { - return ` - repo${index}: repository(owner: "${repo.owner}", name: "${repo.name}") { - pullRequests(first: 100, states: [OPEN]) { - edges { - node { - number - headRefName - baseRefName - baseRefOid - files(first: 100) { - nodes { - path - } - } - } - } - } - }` - }) - .join("\n") - - const request = { - query: ` - query PullRequests { - ${repoQueries} - } - `, - variables: {} - } - - const response = await this.graphQlClient.graphql(request) - const allPullRequests = new Map>() - - repositories.forEach((repo, index) => { - const repoKey = `${repo.owner}/${repo.name}` - const repoData = response[`repo${index}`] - const pullRequests = new Map() - - if (repoData?.pullRequests?.edges) { - type RawGraphQLPullRequest = { - number: number - headRefName: string - baseRefName: string - baseRefOid: string - files?: { - nodes?: { path: string }[] - } - } - const pullRequestEdges = repoData.pullRequests.edges as Edge[] - - pullRequestEdges.forEach(edge => { - const pr = edge.node - const changedFiles = pr.files?.nodes?.map(f => f.path) || [] - pullRequests.set(pr.headRefName, { - number: pr.number, - headRefName: pr.headRefName, - baseRefName: pr.baseRefName, - baseRefOid: pr.baseRefOid, - changedFiles - }) - }) - } - - allPullRequests.set(repoKey, pullRequests) - }) - - return allPullRequests - } - - private async getRepositoriesForSearchQuery(params: { - searchQuery: string, - cursor?: string - }): Promise { - const { searchQuery, cursor } = params - const request = { - query: ` - query Repositories($searchQuery: String!, $cursor: String) { - search(query: $searchQuery, type: REPOSITORY, first: 100, after: $cursor) { - results: nodes { - ... on Repository { - name - owner { - login - } - defaultBranchRef { - name - target { - ...on Commit { - oid - } - } - } - configYml: object(expression: "HEAD:${this.projectConfigurationFilename}.yml") { - ...ConfigParts - } - configYaml: object(expression: "HEAD:${this.projectConfigurationFilename}.yaml") { - ...ConfigParts - } - branches: refs(refPrefix: "refs/heads/", first: 100) { - ...RefConnectionParts - } - tags: refs(refPrefix: "refs/tags/", first: 100) { - ...RefConnectionParts - } - } - } - - pageInfo { - hasNextPage - endCursor - } - } - } - - fragment RefConnectionParts on RefConnection { - edges { - node { - name - ... on Ref { - name - target { - ... on Commit { - oid - tree { - entries { - name - } - } - } - } - } - } - } - } - - fragment ConfigParts on GitObject { - ... on Blob { - text - } - } - `, - variables: { searchQuery, cursor } - } - const response = await this.graphQlClient.graphql(request) - if (!response.search || !response.search.results) { - return [] - } - const pageInfo = response.search.pageInfo - if (!pageInfo) { - return response.search.results - } - if (!pageInfo.hasNextPage || !pageInfo.endCursor) { - return response.search.results - } - const nextResults = await this.getRepositoriesForSearchQuery({ - searchQuery, - cursor: pageInfo.endCursor - }) - return response.search.results.concat(nextResults) - } -} diff --git a/src/features/projects/data/index.ts b/src/features/projects/data/index.ts index 748a41b9..e8827b99 100644 --- a/src/features/projects/data/index.ts +++ b/src/features/projects/data/index.ts @@ -1,5 +1,4 @@ -export { default as GitHubProjectDataSource } from "./GitHubProjectDataSource" -export * from "./GitHubProjectDataSource" export { default as useProjectSelection } from "./useProjectSelection" export { default as GitHubLoginDataSource } from "./GitHubLoginDataSource" -export { default as GitHubRepositoryDataSource } from "./GitHubRepositoryDataSource" +export { default as GitHubProjectListDataSource } from "./GitHubProjectListDataSource" +export { default as GitHubProjectDetailsDataSource } from "./GitHubProjectDetailsDataSource" diff --git a/src/features/projects/data/useProjectSelection.ts b/src/features/projects/data/useProjectSelection.ts index de8327b4..0361dfc8 100644 --- a/src/features/projects/data/useProjectSelection.ts +++ b/src/features/projects/data/useProjectSelection.ts @@ -1,72 +1,112 @@ "use client" +import { useCallback, useMemo } from "react" import NProgress from "nprogress" import { useRouter, usePathname } from "next/navigation" -import { useContext } from "react" -import { ProjectsContext } from "@/common" import { Project, + ProjectSummary, ProjectNavigator, getProjectSelectionFromPath, getDefaultSpecification - } from "../domain" +import { useProjectDetails } from "../view/ProjectDetailsContext" export default function useProjectSelection() { const router = useRouter() const pathname = usePathname() - const { projects } = useContext(ProjectsContext) - const selection = getProjectSelectionFromPath({ projects, path: pathname }) - const pathnameReader = { - get pathname() { - return pathname + const { getProject } = useProjectDetails() + + const projectNavigator = useMemo(() => { + const pathnameReader = { + get pathname() { + return pathname + } } - } - const projectNavigator = new ProjectNavigator({ router, pathnameReader }) + return new ProjectNavigator({ router, pathnameReader }) + }, [router, pathname]) + + // Parse owner/name from URL to look up the project + const pathParts = pathname.split("/").filter(Boolean) + const owner = pathParts[0] + const name = pathParts[1] + const hasVersionInUrl = pathParts.length >= 3 + + // Get project from cache (if loaded). + // null = fetched but not found; undefined = not yet fetched. + // Only pass a real Project to the selection logic. + const cachedProject = owner && name ? getProject(owner, name) : undefined + const resolvedProject = cachedProject ?? undefined + + // Use existing getProjectSelectionFromPath for full selection logic + // It handles complex cases like version IDs with slashes, remote versions, etc. + const selection = resolvedProject + ? getProjectSelectionFromPath({ projects: [resolvedProject], path: pathname }) + : { project: undefined, version: undefined, specification: undefined } + + const currentProject = selection.project + const currentVersion = selection.version + const currentSpecification = selection.specification + + const navigateToSelectionIfNeeded = useCallback(() => { + // Only redirect to defaults if URL has no version/spec at all + // (i.e., user navigated to just /owner/repo) + if (currentProject && !hasVersionInUrl) { + const defaultVersion = currentProject.versions[0] + if (defaultVersion) { + const defaultSpec = getDefaultSpecification(defaultVersion) + if (defaultSpec) { + router.replace(`/${currentProject.owner}/${currentProject.name}/${encodeURIComponent(defaultVersion.id)}/${encodeURIComponent(defaultSpec.id)}`) + return + } + } + } + + projectNavigator.navigateIfNeeded({ + projectOwner: currentProject?.owner, + projectName: currentProject?.name, + versionId: currentVersion?.id, + specificationId: currentSpecification?.id + }) + }, [currentProject, currentVersion, currentSpecification, hasVersionInUrl, projectNavigator, router]) + return { get project() { - return selection.project + return currentProject }, get version() { - return selection.version + return currentVersion }, get specification() { - return selection.specification + return currentSpecification }, - selectProject: (project: Project) => { - const version = project.versions[0] - const specification = getDefaultSpecification(version) + // Immediate selection from URL - doesn't require project details to be loaded + selectedOwner: owner, + selectedName: name, + selectProject: (project: ProjectSummary | Project) => { NProgress.start() - projectNavigator.navigate( - project.owner, - project.name, - version.id, - specification.id - ) + // Navigate to project base - the page will handle loading details and redirecting + router.push(`/${project.owner}/${project.name}`) }, selectVersion: (versionId: string) => { + if (!currentProject || !currentSpecification) return NProgress.start() projectNavigator.navigateToVersion( - selection.project!, + currentProject, versionId, - selection.specification!.name + currentSpecification.name ) }, selectSpecification: (specificationId: string) => { + if (!currentProject || !currentVersion) return NProgress.start() projectNavigator.navigate( - selection.project!.owner, - selection.project!.name, - selection.version!.id, specificationId + currentProject.owner, + currentProject.name, + currentVersion.id, + specificationId ) }, - navigateToSelectionIfNeeded: () => { - projectNavigator.navigateIfNeeded({ - projectOwner: selection.project?.owner, - projectName: selection.project?.name, - versionId: selection.version?.id, - specificationId: selection.specification?.id - }) - } + navigateToSelectionIfNeeded } } diff --git a/src/features/projects/domain/CachingProjectDataSource.ts b/src/features/projects/domain/CachingProjectDataSource.ts deleted file mode 100644 index 73f85281..00000000 --- a/src/features/projects/domain/CachingProjectDataSource.ts +++ /dev/null @@ -1,33 +0,0 @@ -import Project from "./Project"; -import IProjectDataSource from "./IProjectDataSource"; -import IProjectRepository from "./IProjectRepository"; - - -export default class CachingProjectDataSource implements IProjectDataSource { - private dataSource: IProjectDataSource; - private repository: IProjectRepository; - - constructor(config: { - dataSource: IProjectDataSource; - repository: IProjectRepository; - }) { - this.dataSource = config.dataSource; - this.repository = config.repository; - } - - async getProjects(): Promise { - const cache = await this.repository.get(); - if (cache && cache.length > 0) { - return cache; - } - const projects = await this.dataSource.getProjects(); - await this.repository.set(projects); - return projects; - } - - async refreshProjects(): Promise { - const projects = await this.dataSource.getProjects(); - await this.repository.set(projects); - return projects; - } -} diff --git a/src/features/projects/domain/CachingProjectListDataSource.ts b/src/features/projects/domain/CachingProjectListDataSource.ts new file mode 100644 index 00000000..49e2f731 --- /dev/null +++ b/src/features/projects/domain/CachingProjectListDataSource.ts @@ -0,0 +1,28 @@ +import ProjectSummary from "./ProjectSummary" +import IProjectListDataSource from "./IProjectListDataSource" +import IProjectListRepository from "./IProjectListRepository" + +export default class CachingProjectListDataSource implements IProjectListDataSource { + private dataSource: IProjectListDataSource + private repository: IProjectListRepository + + constructor(config: { + dataSource: IProjectListDataSource + repository: IProjectListRepository + }) { + this.dataSource = config.dataSource + this.repository = config.repository + } + + async getProjectList(options?: { refresh?: boolean }): Promise { + if (!options?.refresh) { + const cache = await this.repository.get() + if (cache !== undefined) { + return cache + } + } + const projects = await this.dataSource.getProjectList() + await this.repository.set(projects) + return projects + } +} diff --git a/src/features/projects/domain/FilteringGitHubRepositoryDataSource.ts b/src/features/projects/domain/FilteringGitHubRepositoryDataSource.ts deleted file mode 100644 index 647770c6..00000000 --- a/src/features/projects/domain/FilteringGitHubRepositoryDataSource.ts +++ /dev/null @@ -1,30 +0,0 @@ -import IGitHubRepositoryDataSource, { - GitHubRepository -} from "./IGitHubRepositoryDataSource" -import { splitOwnerAndRepository } from "@/common" - -export default class FilteringGitHubRepositoryDataSource implements IGitHubRepositoryDataSource { - private readonly dataSource: IGitHubRepositoryDataSource - private readonly rawHiddenRepositories: string[] - - constructor(config: { - dataSource: IGitHubRepositoryDataSource, - hiddenRepositories: string[] - }) { - this.dataSource = config.dataSource - this.rawHiddenRepositories = config.hiddenRepositories - } - - async getRepositories(): Promise { - const repositories = await this.dataSource.getRepositories() - const hiddenRepositories = this.rawHiddenRepositories - .map(splitOwnerAndRepository) - .filter(e => e !== undefined) - return repositories.filter(repository => { - const hiddenMatch = hiddenRepositories.find(e => - e.owner == repository.owner && e.repository == repository.name - ) - return hiddenMatch === undefined - }) - } -} \ No newline at end of file diff --git a/src/features/projects/domain/IGitHubRepositoryDataSource.ts b/src/features/projects/domain/IGitHubRepositoryDataSource.ts deleted file mode 100644 index c9c86e4e..00000000 --- a/src/features/projects/domain/IGitHubRepositoryDataSource.ts +++ /dev/null @@ -1,32 +0,0 @@ -export type GitHubRepository = { - readonly name: string - readonly owner: string - readonly defaultBranchRef: { - readonly id: string - readonly name: string - } - readonly configYml?: { - readonly text: string - } - readonly configYaml?: { - readonly text: string - } - readonly branches: GitHubRepositoryRef[] - readonly tags: GitHubRepositoryRef[] -} - -export type GitHubRepositoryRef = { - readonly id: string - readonly name: string - readonly baseRef?: string - readonly baseRefOid?: string - readonly prNumber?: number - readonly files: { - readonly name: string - }[] - readonly changedFiles?: string[] -} - -export default interface IGitHubRepositoryDataSource { - getRepositories(): Promise -} diff --git a/src/features/projects/domain/IProjectDataSource.ts b/src/features/projects/domain/IProjectDataSource.ts deleted file mode 100644 index d89b9ff5..00000000 --- a/src/features/projects/domain/IProjectDataSource.ts +++ /dev/null @@ -1,5 +0,0 @@ -import Project from "./Project" - -export default interface IProjectDataSource { - getProjects(): Promise -} diff --git a/src/features/projects/domain/IProjectDetailsDataSource.ts b/src/features/projects/domain/IProjectDetailsDataSource.ts new file mode 100644 index 00000000..3ae6bb1a --- /dev/null +++ b/src/features/projects/domain/IProjectDetailsDataSource.ts @@ -0,0 +1,5 @@ +import Project from "./Project" + +export default interface IProjectDetailsDataSource { + getProjectDetails(owner: string, repo: string): Promise +} diff --git a/src/features/projects/domain/IProjectListDataSource.ts b/src/features/projects/domain/IProjectListDataSource.ts new file mode 100644 index 00000000..9412b98e --- /dev/null +++ b/src/features/projects/domain/IProjectListDataSource.ts @@ -0,0 +1,5 @@ +import ProjectSummary from "./ProjectSummary" + +export default interface IProjectListDataSource { + getProjectList(options?: { refresh?: boolean }): Promise +} diff --git a/src/features/projects/domain/IProjectListRepository.ts b/src/features/projects/domain/IProjectListRepository.ts new file mode 100644 index 00000000..b18b46a9 --- /dev/null +++ b/src/features/projects/domain/IProjectListRepository.ts @@ -0,0 +1,7 @@ +import ProjectSummary from "./ProjectSummary" + +export default interface IProjectListRepository { + get(): Promise + set(projects: ProjectSummary[]): Promise + delete(): Promise +} diff --git a/src/features/projects/domain/IProjectRepository.ts b/src/features/projects/domain/IProjectRepository.ts deleted file mode 100644 index 52a4af69..00000000 --- a/src/features/projects/domain/IProjectRepository.ts +++ /dev/null @@ -1,7 +0,0 @@ -import Project from "./Project" - -export default interface IProjectRepository { - get(): Promise - set(projects: Project[]): Promise - delete(): Promise -} diff --git a/src/features/projects/domain/ProjectRepository.ts b/src/features/projects/domain/ProjectListRepository.ts similarity index 58% rename from src/features/projects/domain/ProjectRepository.ts rename to src/features/projects/domain/ProjectListRepository.ts index 0b68bfdb..6defc7b6 100644 --- a/src/features/projects/domain/ProjectRepository.ts +++ b/src/features/projects/domain/ProjectListRepository.ts @@ -1,41 +1,41 @@ import { IUserDataRepository, ZodJSONCoder } from "@/common" -import IProjectRepository from "./IProjectRepository" -import Project, { ProjectSchema } from "./Project" +import IProjectListRepository from "./IProjectListRepository" +import ProjectSummary, { ProjectSummarySchema } from "./ProjectSummary" interface IUserIDReader { getUserId(): Promise } -export default class ProjectRepository implements IProjectRepository { +export default class ProjectListRepository implements IProjectListRepository { private readonly userIDReader: IUserIDReader private readonly repository: IUserDataRepository - + constructor(config: { userIDReader: IUserIDReader, repository: IUserDataRepository }) { this.userIDReader = config.userIDReader this.repository = config.repository } - - async get(): Promise { + + async get(): Promise { const userId = await this.userIDReader.getUserId() const string = await this.repository.get(userId) - + if (!string) { return undefined } try { - return ZodJSONCoder.decode(ProjectSchema.array(), string) - } catch { // swallow decode errors and treat as missing cache - console.warn("[ProjectRepository] Failed to decode cached projects – treating as cache miss") + return ZodJSONCoder.decode(ProjectSummarySchema.array(), string) + } catch { + console.warn("[ProjectListRepository] Failed to decode cached project list – treating as cache miss") return undefined } } - - async set(projects: Project[]): Promise { + + async set(projects: ProjectSummary[]): Promise { const userId = await this.userIDReader.getUserId() - const string = ZodJSONCoder.encode(ProjectSchema.array(), projects) - await this.repository.setExpiring(userId, string, 30 * 24 * 3600) + const string = ZodJSONCoder.encode(ProjectSummarySchema.array(), projects) + await this.repository.setExpiring(userId, string, 30 * 24 * 3600) // 30 days TTL } - + async delete(): Promise { const userId = await this.userIDReader.getUserId() await this.repository.delete(userId) diff --git a/src/features/projects/domain/ProjectSummary.ts b/src/features/projects/domain/ProjectSummary.ts new file mode 100644 index 00000000..8c5987e3 --- /dev/null +++ b/src/features/projects/domain/ProjectSummary.ts @@ -0,0 +1,15 @@ +import { z } from "zod" + +export const ProjectSummarySchema = z.object({ + id: z.string(), + name: z.string(), + displayName: z.string(), + owner: z.string(), + imageURL: z.string().optional(), + url: z.string().optional(), + ownerUrl: z.string() +}) + +type ProjectSummary = z.infer + +export default ProjectSummary diff --git a/src/features/projects/domain/index.ts b/src/features/projects/domain/index.ts index 2bf08445..86351891 100644 --- a/src/features/projects/domain/index.ts +++ b/src/features/projects/domain/index.ts @@ -1,19 +1,20 @@ -export { default as CachingProjectDataSource } from "./CachingProjectDataSource" -export { default as FilteringGitHubRepositoryDataSource } from "./FilteringGitHubRepositoryDataSource" +export { default as CachingProjectListDataSource } from "./CachingProjectListDataSource" export { default as getProjectSelectionFromPath } from "./getProjectSelectionFromPath" export type { default as IGitHubLoginDataSource } from "./IGitHubLoginDataSource" -export type { default as IGitHubRepositoryDataSource } from "./IGitHubRepositoryDataSource" -export * from "./IGitHubRepositoryDataSource" export type { default as IGitHubGraphQLClient } from "./IGitHubGraphQLClient" export * from "./IGitHubGraphQLClient" export type { default as IProjectConfig } from "./IProjectConfig" export * from "./IProjectConfig" -export type { default as IProjectDataSource } from "./IProjectDataSource" +export type { default as IProjectListDataSource } from "./IProjectListDataSource" +export type { default as IProjectListRepository } from "./IProjectListRepository" +export type { default as IProjectDetailsDataSource } from "./IProjectDetailsDataSource" +export { default as ProjectListRepository } from "./ProjectListRepository" export type { default as OpenApiSpecification } from "./OpenApiSpecification" export type { default as Project } from "./Project" export { default as ProjectConfigParser } from "./ProjectConfigParser" +export type { default as ProjectSummary } from "./ProjectSummary" +export { ProjectSummarySchema } from "./ProjectSummary" export { default as ProjectNavigator } from "./ProjectNavigator" -export { default as ProjectRepository } from "./ProjectRepository" export { default as updateWindowTitle } from "./updateWindowTitle" export type { default as Version } from "./Version" export { getDefaultSpecification } from "./Version" diff --git a/src/features/projects/view/ProjectDetailsContext.tsx b/src/features/projects/view/ProjectDetailsContext.tsx new file mode 100644 index 00000000..b8f60556 --- /dev/null +++ b/src/features/projects/view/ProjectDetailsContext.tsx @@ -0,0 +1,21 @@ +"use client" + +import { createContext, useContext } from "react" +import { Project } from "@/features/projects/domain" + +export interface ProjectDetailsContextValue { + getProject: (owner: string, repo: string) => Project | null | undefined + fetchProject: (owner: string, repo: string) => Promise + isLoading: (owner: string, repo: string) => boolean + getError: (owner: string, repo: string) => string | null +} + +export const ProjectDetailsContext = createContext(null) + +export function useProjectDetails() { + const context = useContext(ProjectDetailsContext) + if (!context) { + throw new Error("useProjectDetails must be used within ProjectDetailsContextProvider") + } + return context +} diff --git a/src/features/projects/view/ProjectDetailsContextProvider.tsx b/src/features/projects/view/ProjectDetailsContextProvider.tsx new file mode 100644 index 00000000..374b960c --- /dev/null +++ b/src/features/projects/view/ProjectDetailsContextProvider.tsx @@ -0,0 +1,92 @@ +"use client" + +import { useState, useCallback, useRef } from "react" +import { Project } from "@/features/projects/domain" +import { ProjectDetailsContext } from "./ProjectDetailsContext" + +type CacheEntry = { + project: Project | null + loading: boolean + error: string | null +} + +export default function ProjectDetailsContextProvider({ + children +}: { + children: React.ReactNode +}) { + const [cache, setCache] = useState>(new Map()) + const inFlightRef = useRef>>(new Map()) + + const makeKey = (owner: string, repo: string) => `${owner}/${repo}` + + // Note: These callbacks intentionally include `cache` in deps to trigger + // re-renders in consuming components when cache updates + const getProject = useCallback((owner: string, repo: string): Project | null | undefined => { + const entry = cache.get(makeKey(owner, repo)) + if (!entry) return undefined // not yet fetched + return entry.project // null = fetched, not found; Project = fetched, found + }, [cache]) + + const isLoading = useCallback((owner: string, repo: string): boolean => { + return cache.get(makeKey(owner, repo))?.loading ?? false + }, [cache]) + + const getError = useCallback((owner: string, repo: string): string | null => { + return cache.get(makeKey(owner, repo))?.error ?? null + }, [cache]) + + const fetchProject = useCallback(async (owner: string, repo: string): Promise => { + const key = makeKey(owner, repo) + + // Return in-flight request if exists + const inFlight = inFlightRef.current.get(key) + if (inFlight) return inFlight + + // Mark as loading + setCache(prev => { + const next = new Map(prev) + next.set(key, { project: prev.get(key)?.project ?? null, loading: true, error: null }) + return next + }) + + const promise = fetch(`/api/projects/${owner}/${repo}`) + .then((res): Promise<{ project: Project | null }> | { project: null } => { + if (res.status === 404) { + // Project not found - treat as null project, not an error + return { project: null } + } + if (!res.ok) throw new Error(`HTTP ${res.status}`) + return res.json() as Promise<{ project: Project }> + }) + .then(({ project }: { project: Project | null }) => { + setCache(prev => { + const next = new Map(prev) + next.set(key, { project, loading: false, error: null }) + return next + }) + return project + }) + .catch(err => { + console.error(`Failed to fetch project ${key}:`, err) + setCache(prev => { + const next = new Map(prev) + next.set(key, { project: null, loading: false, error: "Failed to load project" }) + return next + }) + return null + }) + .finally(() => { + inFlightRef.current.delete(key) + }) + + inFlightRef.current.set(key, promise) + return promise + }, []) + + return ( + + {children} + + ) +} diff --git a/src/features/projects/view/ProjectListContext.tsx b/src/features/projects/view/ProjectListContext.tsx new file mode 100644 index 00000000..e6e547bc --- /dev/null +++ b/src/features/projects/view/ProjectListContext.tsx @@ -0,0 +1,21 @@ +"use client" + +import { createContext, useContext } from "react" +import { ProjectSummary } from "@/features/projects/domain" + +export interface ProjectListContextValue { + projects: ProjectSummary[] + loading: boolean + error: string | null + refresh: () => void +} + +export const ProjectListContext = createContext(null) + +export function useProjectList() { + const context = useContext(ProjectListContext) + if (!context) { + throw new Error("useProjectList must be used within ProjectListContextProvider") + } + return context +} diff --git a/src/features/projects/view/ProjectListContextProvider.tsx b/src/features/projects/view/ProjectListContextProvider.tsx new file mode 100644 index 00000000..c0e83e2f --- /dev/null +++ b/src/features/projects/view/ProjectListContextProvider.tsx @@ -0,0 +1,71 @@ +"use client" + +import { useState, useEffect, useCallback, useRef } from "react" +import { ProjectSummary } from "@/features/projects/domain" +import { ProjectListContext } from "./ProjectListContext" + +export default function ProjectListContextProvider({ + children +}: { + children: React.ReactNode +}) { + const [projects, setProjects] = useState([]) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + const isRefreshingRef = useRef(false) + + const fetchProjects = useCallback(async (forceRefresh: boolean): Promise => { + const url = forceRefresh ? "/api/projects?refresh=true" : "/api/projects" + const res = await fetch(url) + if (!res.ok) throw new Error(`HTTP ${res.status}`) + const { projects: newProjects } = await res.json() + setProjects(newProjects || []) + setError(null) + return newProjects || [] + }, []) + + const refresh = useCallback(() => { + if (isRefreshingRef.current) return + isRefreshingRef.current = true + fetchProjects(true) + .catch(err => console.error("Failed to refresh project list:", err)) + .finally(() => { isRefreshingRef.current = false }) + }, [fetchProjects]) + + // Initial load: show cached data immediately, then refresh in the background + useEffect(() => { + const load = async () => { + try { + await fetchProjects(false) + } catch (err) { + console.error("Failed to fetch project list:", err) + setError("Failed to load projects") + } finally { + setLoading(false) + } + fetchProjects(true).catch(err => + console.error("Failed to refresh project list:", err) + ) + } + load() + }, [fetchProjects]) + + // Refresh on visibility change and focus + useEffect(() => { + const handleVisibilityChange = () => { + if (!document.hidden) refresh() + } + document.addEventListener("visibilitychange", handleVisibilityChange) + window.addEventListener("focus", refresh) + return () => { + document.removeEventListener("visibilitychange", handleVisibilityChange) + window.removeEventListener("focus", refresh) + } + }, [refresh]) + + return ( + + {children} + + ) +} diff --git a/src/features/projects/view/ProjectsContextProvider.tsx b/src/features/projects/view/ProjectsContextProvider.tsx deleted file mode 100644 index 2d60c844..00000000 --- a/src/features/projects/view/ProjectsContextProvider.tsx +++ /dev/null @@ -1,66 +0,0 @@ -"use client"; - -import { useState, useEffect, useRef, useCallback } from "react"; -import { ProjectsContext } from "@/common"; -import { Project } from "@/features/projects/domain"; - -const ProjectsContextProvider = ({ - initialProjects, - children, -}: { - initialProjects?: Project[]; - children?: React.ReactNode; -}) => { - const [projects, setProjects] = useState(initialProjects || []); - const [refreshing, setRefreshing] = useState(false); - const isLoadingRef = useRef(false); - - // Fingerprint uses urlHash for remote specs (stable), URL for others (already stable) - const fingerprint = (list: Project[]) => - list.flatMap(p => p.versions.flatMap(v => v.specifications.map(s => s.urlHash ?? s.url))).sort().join(); - -const refreshProjects = useCallback(() => { - if (isLoadingRef.current) return; - isLoadingRef.current = true; - setRefreshing(true); - fetch("/api/refresh-projects", { method: "POST" }) - .then((res) => res.json()) - .then(({ projects: newProjects }) => { - if (newProjects) { - setProjects(prev => fingerprint(prev) === fingerprint(newProjects) ? prev : newProjects); - } - }) - .catch((error) => console.error("Failed to refresh projects", error)) - .finally(() => { - isLoadingRef.current = false; - setRefreshing(false); - }); -}, []); - - // Trigger background refresh after initial mount - -useEffect(() => { - // Initial refresh - const timeout = window.setTimeout(() => { - refreshProjects(); - }, 0); - const handleVisibilityChange = () => { - if (!document.hidden) refreshProjects(); - }; - document.addEventListener("visibilitychange", handleVisibilityChange); - window.addEventListener("focus", refreshProjects); - return () => { - document.removeEventListener("visibilitychange", handleVisibilityChange); - window.removeEventListener("focus", refreshProjects); - window.clearTimeout(timeout); - }; -}, [refreshProjects]); - - return ( - - {children} - - ); -}; - -export default ProjectsContextProvider; diff --git a/src/features/projects/view/index.ts b/src/features/projects/view/index.ts new file mode 100644 index 00000000..d04532e2 --- /dev/null +++ b/src/features/projects/view/index.ts @@ -0,0 +1,4 @@ +export { ProjectListContext, useProjectList } from "./ProjectListContext" +export { default as ProjectListContextProvider } from "./ProjectListContextProvider" +export { ProjectDetailsContext, useProjectDetails } from "./ProjectDetailsContext" +export { default as ProjectDetailsContextProvider } from "./ProjectDetailsContextProvider" diff --git a/src/features/sidebar/view/internal/sidebar/Header.tsx b/src/features/sidebar/view/internal/sidebar/Header.tsx index 3b6a2258..e5cf8d44 100644 --- a/src/features/sidebar/view/internal/sidebar/Header.tsx +++ b/src/features/sidebar/view/internal/sidebar/Header.tsx @@ -1,48 +1,15 @@ "use client" -import { useContext, useEffect, useRef, useState } from "react" import Image from "next/image" -import { Box, Button, CircularProgress, Tooltip, Typography } from "@mui/material" -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" -import { faCheck } from "@fortawesome/free-solid-svg-icons" +import { Box, Button, Typography } from "@mui/material" import { useRouter } from "next/navigation" import * as NProgress from "nprogress" -import { ProjectsContext } from "@/common" import { useCloseSidebarOnSelection } from "@/features/sidebar/data" const Header = ({ siteName }: { siteName?: string }) => { const router = useRouter() const { closeSidebarIfNeeded } = useCloseSidebarOnSelection() - const { refreshing } = useContext(ProjectsContext) - const [showCheck, setShowCheck] = useState(false) - const [fadeOut, setFadeOut] = useState(false) - const wasRefreshing = useRef(false) - useEffect(() => { - if (refreshing) { - // Clear any existing checkmark when a new refresh starts - const clearTimeout_ = setTimeout(() => { - setShowCheck(false) - setFadeOut(false) - }, 0) - wasRefreshing.current = true - return () => clearTimeout(clearTimeout_) - } else if (wasRefreshing.current) { - wasRefreshing.current = false - // Delay checkmark appearance to let spinner fade out first - const showTimeout = setTimeout(() => { - setShowCheck(true) - setFadeOut(false) - }, 400) - const fadeTimeout = setTimeout(() => setFadeOut(true), 1600) - const hideTimeout = setTimeout(() => setShowCheck(false), 2200) - return () => { - clearTimeout(showTimeout) - clearTimeout(fadeTimeout) - clearTimeout(hideTimeout) - } - } - }, [refreshing]) return ( { paddingLeft: 2.1, minHeight: 64, display: "flex", - justifyContent: "space-between", alignItems: "center" }}> - - - - - - - - - - ) } diff --git a/src/features/sidebar/view/internal/sidebar/projects/PopulatedProjectList.tsx b/src/features/sidebar/view/internal/sidebar/projects/PopulatedProjectList.tsx index a9d16e39..2fa3815a 100644 --- a/src/features/sidebar/view/internal/sidebar/projects/PopulatedProjectList.tsx +++ b/src/features/sidebar/view/internal/sidebar/projects/PopulatedProjectList.tsx @@ -1,12 +1,12 @@ "use client" import SpacedList from "@/common/ui/SpacedList" -import { Project } from "@/features/projects/domain" +import { ProjectSummary } from "@/features/projects/domain" import ProjectListItem from "./ProjectListItem" -const PopulatedProjectList = ({ projects }: { projects: Project[] }) => { +const PopulatedProjectList = ({ projects }: { projects: ProjectSummary[] }) => { return ( - {projects.map(project => ( + {projects.map(project => ( ))} diff --git a/src/features/sidebar/view/internal/sidebar/projects/ProjectAvatar.tsx b/src/features/sidebar/view/internal/sidebar/projects/ProjectAvatar.tsx index fd2d3bcb..7cb1f5d0 100644 --- a/src/features/sidebar/view/internal/sidebar/projects/ProjectAvatar.tsx +++ b/src/features/sidebar/view/internal/sidebar/projects/ProjectAvatar.tsx @@ -1,6 +1,6 @@ import { Box, SxProps } from "@mui/system" import { Avatar } from "@mui/material" -import { Project } from "@/features/projects/domain" +import { ProjectSummary } from "@/features/projects/domain" import { getSvgPath } from "figma-squircle" function ProjectAvatar({ @@ -8,7 +8,7 @@ function ProjectAvatar({ width, height }: { - project: Project, + project: ProjectSummary, width: number, height: number }) { diff --git a/src/features/sidebar/view/internal/sidebar/projects/ProjectList.tsx b/src/features/sidebar/view/internal/sidebar/projects/ProjectList.tsx index 0215d9e8..658b4519 100644 --- a/src/features/sidebar/view/internal/sidebar/projects/ProjectList.tsx +++ b/src/features/sidebar/view/internal/sidebar/projects/ProjectList.tsx @@ -1,14 +1,17 @@ -'use client' +'use client' -import { Suspense, useContext } from "react" +import { Suspense } from "react" import { Typography } from "@mui/material" import ProjectListFallback from "./ProjectListFallback" import PopulatedProjectList from "./PopulatedProjectList" -import { ProjectsContext } from "@/common" +import { useProjectList } from "@/features/projects/view/ProjectListContext" const ProjectList = () => { + const { projects, loading } = useProjectList() - const { projects } = useContext(ProjectsContext) + if (loading && projects.length === 0) { + return + } return ( }> diff --git a/src/features/sidebar/view/internal/sidebar/projects/ProjectListFallback.tsx b/src/features/sidebar/view/internal/sidebar/projects/ProjectListFallback.tsx index 9084ca2a..946d6607 100644 --- a/src/features/sidebar/view/internal/sidebar/projects/ProjectListFallback.tsx +++ b/src/features/sidebar/view/internal/sidebar/projects/ProjectListFallback.tsx @@ -1,13 +1,12 @@ "use client" -import { useContext } from "react" -import { ProjectsContext } from "@/common" import SpacedList from "@/common/ui/SpacedList" import PopulatedProjectList from "./PopulatedProjectList" import { Skeleton as ProjectListItemSkeleton } from "./ProjectListItem" +import { useProjectList } from "@/features/projects/view/ProjectListContext" const StaleProjectList = () => { - const { projects } = useContext(ProjectsContext) + const { projects } = useProjectList() if (projects.length > 0) { return } else { diff --git a/src/features/sidebar/view/internal/sidebar/projects/ProjectListItem.tsx b/src/features/sidebar/view/internal/sidebar/projects/ProjectListItem.tsx index cfa9294b..72381868 100644 --- a/src/features/sidebar/view/internal/sidebar/projects/ProjectListItem.tsx +++ b/src/features/sidebar/view/internal/sidebar/projects/ProjectListItem.tsx @@ -10,10 +10,8 @@ import { Typography, } from "@mui/material"; import MenuItemHover from "@/common/ui/MenuItemHover"; -import { Project } from "@/features/projects/domain"; +import { ProjectSummary } from "@/features/projects/domain"; import { useProjectSelection } from "@/features/projects/data"; -import { useContext } from "react"; -import { ProjectsContext } from "@/common"; import ProjectAvatar, { Squircle as ProjectAvatarSquircle, } from "./ProjectAvatar"; @@ -21,10 +19,9 @@ import { useCloseSidebarOnSelection } from "@/features/sidebar/data"; const AVATAR_SIZE = { width: 40, height: 40 }; -const ProjectListItem = ({ project }: { project: Project }) => { - const { project: selectedProject, selectProject } = useProjectSelection(); - const { refreshProjects } = useContext(ProjectsContext); - const selected = project.id === selectedProject?.id; +const ProjectListItem = ({ project }: { project: ProjectSummary }) => { + const { selectedOwner, selectedName, selectProject } = useProjectSelection(); + const selected = project.owner === selectedOwner && project.name === selectedName; const { closeSidebarIfNeeded } = useCloseSidebarOnSelection(); return ( @@ -33,7 +30,6 @@ const ProjectListItem = ({ project }: { project: Project }) => { onSelect={() => { closeSidebarIfNeeded(); selectProject(project); - refreshProjects(); }} avatar={