diff --git a/.eslintrc b/.eslintrc index 37e59b2..cf6e89c 100644 --- a/.eslintrc +++ b/.eslintrc @@ -57,6 +57,12 @@ "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-unused-vars": "off" } + }, + { + "files": ["src/api/*", "src/api/**/*"], + "rules": { + "import/no-nodejs-modules": "off" + } } ] } diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml index 01dff0d..ff9b39d 100644 --- a/.github/workflows/deploy-dev.yml +++ b/.github/workflows/deploy-dev.yml @@ -6,8 +6,11 @@ on: pull_request: branches: - main + types: [opened, synchronize, reopened, ready_for_review] + jobs: test-unit: + if: github.event.pull_request.draft == false runs-on: ubuntu-latest name: Run Unit Tests steps: @@ -25,6 +28,7 @@ jobs: - name: Run unit testing run: make test_unit deploy-dev: + if: github.event.pull_request.draft == false runs-on: ubuntu-latest concurrency: group: ${{ github.event.repository.name }}-dev-env @@ -69,6 +73,7 @@ jobs: branch: main test-dev: + if: github.event.pull_request.draft == false runs-on: ubuntu-latest name: Run Live Tests needs: diff --git a/README.md b/README.md index b18953d..a43363c 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ This repository is split into multiple parts: * `src/common/` for common modules between the API and the UI (such as constants, types, errors, etc.) ## Getting Started -You will need node>=20 installed, as well as the AWS CLI and the AWS SAM CLI. The best way to work with all of this is to open the environment in a container within your IDE (VS Code should prompt you to do so: use "Clone in Container" for best performance). This container will have all needed software installed. +You will need node>=22 installed, as well as the AWS CLI and the AWS SAM CLI. The best way to work with all of this is to open the environment in a container within your IDE (VS Code should prompt you to do so: use "Clone in Container" for best performance). This container will have all needed software installed. Then, run `make install` to install all packages, and `make local` to start the UI and API servers! The UI will be accessible on `http://localhost:5173/` and the API on `http://localhost:8080/`. diff --git a/cloudformation/iam.yml b/cloudformation/iam.yml index 756c970..69452f8 100644 --- a/cloudformation/iam.yml +++ b/cloudformation/iam.yml @@ -77,7 +77,8 @@ Resources: - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-iam-userroles/* - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-iam-grouproles - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-iam-grouproles/* - + - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-linkry + - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-linkry/* PolicyName: lambda-dynamo Outputs: MainFunctionRoleArn: @@ -85,4 +86,4 @@ Outputs: Value: Fn::GetAtt: - ApiLambdaIAMRole - - Arn \ No newline at end of file + - Arn diff --git a/cloudformation/main.yml b/cloudformation/main.yml index 5edf7e3..bf14c13 100644 --- a/cloudformation/main.yml +++ b/cloudformation/main.yml @@ -226,6 +226,34 @@ Resources: Projection: ProjectionType: ALL + LinkryRecordsTable: + Type: "AWS::DynamoDB::Table" + Properties: + BillingMode: "PAY_PER_REQUEST" + TableName: "infra-core-api-linkry" + DeletionProtectionEnabled: true + PointInTimeRecoverySpecification: + PointInTimeRecoveryEnabled: !If [IsProd, true, false] + AttributeDefinitions: + - AttributeName: "slug" + AttributeType: "S" + - AttributeName: "access" + AttributeType: "S" + KeySchema: + - AttributeName: "slug" + KeyType: "HASH" + - AttributeName: "access" + KeyType: "RANGE" + GlobalSecondaryIndexes: + - IndexName: "AccessIndex" + KeySchema: + - AttributeName: "access" + KeyType: "HASH" + - AttributeName: "slug" + KeyType: "RANGE" + Projection: + ProjectionType: "ALL" + CacheRecordsTable: Type: 'AWS::DynamoDB::Table' DeletionPolicy: "Retain" diff --git a/src/api/index.ts b/src/api/index.ts index ed7f349..8f7b4c1 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -1,4 +1,3 @@ -/* eslint import/no-nodejs-modules: ["error", {"allow": ["crypto"]}] */ import { randomUUID } from "crypto"; import fastify, { FastifyInstance } from "fastify"; import FastifyAuthProvider from "@fastify/auth"; @@ -18,6 +17,9 @@ import * as dotenv from "dotenv"; import iamRoutes from "./routes/iam.js"; import ticketsPlugin from "./routes/tickets.js"; import { STSClient, GetCallerIdentityCommand } from "@aws-sdk/client-sts"; +import linkryRoutes from "./routes/linkry.js"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; import NodeCache from "node-cache"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { SecretsManagerClient } from "@aws-sdk/client-secrets-manager"; @@ -64,6 +66,12 @@ async function init() { return event.requestContext.requestId; }, }); + const __filename = fileURLToPath(import.meta.url); // get the resolved path to the file + const __dirname = path.dirname(__filename); + await app.register(import("@fastify/static"), { + root: path.join(__dirname, "public"), + }); + await app.register(fastifyAuthPlugin); await app.register(fastifyZodValidationPlugin); await app.register(FastifyAuthProvider); @@ -111,6 +119,7 @@ async function init() { api.register(icalPlugin, { prefix: "/ical" }); api.register(iamRoutes, { prefix: "/iam" }); api.register(ticketsPlugin, { prefix: "/tickets" }); + api.register(linkryRoutes, { prefix: "/linkry" }); if (app.runEnvironment === "dev") { api.register(vendingPlugin, { prefix: "/vending" }); } diff --git a/src/api/package.json b/src/api/package.json index 9f0467c..676d060 100644 --- a/src/api/package.json +++ b/src/api/package.json @@ -15,6 +15,7 @@ "prettier:write": "prettier --write *.ts **/*.ts" }, "dependencies": { + "@fastify/static": "^8.0.4", "@aws-sdk/client-dynamodb": "^3.624.0", "@aws-sdk/client-secrets-manager": "^3.624.0", "@aws-sdk/client-sts": "^3.726.0", diff --git a/src/api/plugins/auth.ts b/src/api/plugins/auth.ts index 1e5fa54..50b91bf 100644 --- a/src/api/plugins/auth.ts +++ b/src/api/plugins/auth.ts @@ -17,7 +17,7 @@ import { genericConfig, SecretConfig } from "../../common/config.js"; import { getGroupRoles, getUserRoles } from "../functions/authorization.js"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; -function intersection(setA: Set, setB: Set): Set { +export function intersection(setA: Set, setB: Set): Set { const _intersection = new Set(); for (const elem of setB) { if (setA.has(elem)) { diff --git a/src/api/public/404.html b/src/api/public/404.html new file mode 100644 index 0000000..d7eef16 --- /dev/null +++ b/src/api/public/404.html @@ -0,0 +1,65 @@ + + + + + + + Page Not Found | ACM @ UIUC + + + + + +
+

404

+

The page you are looking for could not be found.

+
© ACM @ UIUC
+
+ + + \ No newline at end of file diff --git a/src/api/public/favicon.ico b/src/api/public/favicon.ico new file mode 100644 index 0000000..fd44c45 Binary files /dev/null and b/src/api/public/favicon.ico differ diff --git a/src/api/routes/linkry.ts b/src/api/routes/linkry.ts new file mode 100644 index 0000000..c5b3664 --- /dev/null +++ b/src/api/routes/linkry.ts @@ -0,0 +1,154 @@ +import { FastifyPluginAsync } from "fastify"; +import { z } from "zod"; +import { AppRoles } from "../../common/roles.js"; +import { + BaseError, + DatabaseFetchError, + NotFoundError, + NotImplementedError, +} from "../../common/errors/index.js"; +import { intersection } from "../plugins/auth.js"; +import { NoDataRequest } from "../types.js"; +import { DynamoDBClient, QueryCommand } from "@aws-sdk/client-dynamodb"; +import { genericConfig } from "../../common/config.js"; +import { unmarshall } from "@aws-sdk/util-dynamodb"; + +type LinkrySlugOnlyRequest = { + Params: { id: string }; + Querystring: undefined; + Body: undefined; +}; + +const rawRequest = { + slug: z.string().min(1), + redirect: z.string().url().min(1), + groups: z.optional(z.array(z.string()).min(1)), +}; + +const createRequest = z.object(rawRequest); +const patchRequest = z.object({ redirect: z.string().url().min(1) }); + +type LinkyCreateRequest = { + Params: undefined; + Querystring: undefined; + Body: z.infer; +}; + +type LinkryPatchRequest = { + Params: { id: string }; + Querystring: undefined; + Body: z.infer; +}; + +const dynamoClient = new DynamoDBClient({ + region: genericConfig.AwsRegion, +}); + +const linkryRoutes: FastifyPluginAsync = async (fastify, _options) => { + fastify.get("/redir/:id", async (request, reply) => { + const id = request.params.id; + const command = new QueryCommand({ + TableName: genericConfig.LinkryDynamoTableName, + KeyConditionExpression: + "#slug = :slugVal AND begins_with(#access, :accessVal)", + ExpressionAttributeNames: { + "#slug": "slug", + "#access": "access", + }, + ExpressionAttributeValues: { + ":slugVal": { S: id }, + ":accessVal": { S: "OWNER#" }, + }, + }); + try { + const result = await dynamoClient.send(command); + if (!result || !result.Items || result.Items.length === 0) { + return reply + .headers({ "content-type": "text/html" }) + .status(404) + .sendFile("404.html"); + } + return reply.redirect(unmarshall(result.Items[0]).redirect); + } catch (e) { + if (e instanceof BaseError) { + throw e; + } + request.log.error(e); + throw new DatabaseFetchError({ + message: "Could not retrieve mapping, please try again later.", + }); + } + }); + fastify.post( + "/redir", + { + preValidation: async (request, reply) => { + await fastify.zodValidateBody(request, reply, createRequest); + }, + onRequest: async (request, reply) => { + await fastify.authorize(request, reply, [ + AppRoles.LINKS_MANAGER, + AppRoles.LINKS_ADMIN, + ]); + }, + }, + async (request, reply) => { + throw new NotImplementedError({}); + }, + ); + fastify.patch( + "/redir/:id", + { + preValidation: async (request, reply) => { + await fastify.zodValidateBody(request, reply, patchRequest); + }, + onRequest: async (request, reply) => { + await fastify.authorize(request, reply, [ + AppRoles.LINKS_MANAGER, + AppRoles.LINKS_ADMIN, + ]); + }, + }, + async (request, reply) => { + // make sure that a user can manage this link, either via owning or being in a group that has access to it, or is a LINKS_ADMIN. + // you can only change the URL it redirects to + throw new NotImplementedError({}); + }, + ); + fastify.delete( + "/redir/:id", + { + preValidation: async (request, reply) => { + await fastify.zodValidateBody(request, reply, createRequest); + }, + onRequest: async (request, reply) => { + await fastify.authorize(request, reply, [ + AppRoles.LINKS_MANAGER, + AppRoles.LINKS_ADMIN, + ]); + }, + }, + async (request, reply) => { + // make sure that a user can manage this link, either via owning or being in a group that has access to it, or is a LINKS_ADMIN. + throw new NotImplementedError({}); + }, + ); + fastify.get( + "/redir", + { + onRequest: async (request, reply) => { + await fastify.authorize(request, reply, [ + AppRoles.LINKS_MANAGER, + AppRoles.LINKS_ADMIN, + ]); + }, + }, + async (request, reply) => { + // if an admin, show all links + // if a links manager, show all my links + links I can manage + throw new NotImplementedError({}); + }, + ); +}; + +export default linkryRoutes; diff --git a/src/api/types.d.ts b/src/api/types.d.ts index 7f0e498..60ab3f9 100644 --- a/src/api/types.d.ts +++ b/src/api/types.d.ts @@ -33,3 +33,9 @@ declare module "fastify" { tokenPayload?: AadToken; } } + +export type NoDataRequest = { + Params: undefined; + Querystring: undefined; + Body: undefined; +}; diff --git a/src/common/config.ts b/src/common/config.ts index b0609d6..6e44474 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -17,6 +17,7 @@ export type ConfigType = { type GenericConfigType = { EventsDynamoTableName: string; CacheDynamoTableName: string; + LinkryDynamoTableName: string; ConfigSecretName: string; UpcomingEventThresholdSeconds: number; AwsRegion: string; @@ -42,6 +43,7 @@ export const execCouncilTestingGroupId = "dbe18eb2-9675-46c4-b1ef-749a6db4fedd"; const genericConfig: GenericConfigType = { EventsDynamoTableName: "infra-core-api-events", CacheDynamoTableName: "infra-core-api-cache", + LinkryDynamoTableName: "infra-core-api-linkry", ConfigSecretName: "infra-core-api-config", UpcomingEventThresholdSeconds: 1800, // 30 mins AwsRegion: process.env.AWS_REGION || "us-east-1", diff --git a/src/common/errors/index.ts b/src/common/errors/index.ts index 632669f..96f2185 100644 --- a/src/common/errors/index.ts +++ b/src/common/errors/index.ts @@ -180,6 +180,17 @@ export class NotSupportedError extends BaseError<"NotSupportedError"> { } } +export class DatabaseDeleteError extends BaseError<"DatabaseDeleteError"> { + constructor({ message }: { message: string }) { + super({ + name: "DatabaseDeleteError", + id: 111, + message, + httpStatusCode: 500, + }); + } +} + export class EntraGroupError extends BaseError<"EntraGroupError"> { group: string; constructor({ @@ -201,3 +212,4 @@ export class EntraGroupError extends BaseError<"EntraGroupError"> { this.group = group; } } + diff --git a/src/common/roles.ts b/src/common/roles.ts index 7fda951..ca68ec1 100644 --- a/src/common/roles.ts +++ b/src/common/roles.ts @@ -7,6 +7,8 @@ export enum AppRoles { TICKETS_MANAGER = "manage:tickets", IAM_ADMIN = "admin:iam", IAM_INVITE_ONLY = "invite:iam", + LINKS_MANAGER = "manage:links", + LINKS_ADMIN = "admin:links", } export const allAppRoles = Object.values(AppRoles).filter( (value) => typeof value === "string", diff --git a/src/common/types/linkry.ts b/src/common/types/linkry.ts new file mode 100644 index 0000000..475545c --- /dev/null +++ b/src/common/types/linkry.ts @@ -0,0 +1,5 @@ +export type ShortLinkEntry = { + slug: string; + access: string; + redir?: string; +} diff --git a/yarn.lock b/yarn.lock index d7df54e..c0e1a99 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1611,6 +1611,11 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== +"@fastify/accept-negotiator@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@fastify/accept-negotiator/-/accept-negotiator-2.0.1.tgz#77afd6254ba77f6c22c6f35c4fb0c1b6d005199b" + integrity sha512-/c/TW2bO/v9JeEgoD/g1G5GxGeCF1Hafdf79WPmUlgYiBXummY0oX3VVq4yFkKKVBKDNlaDUYoab7g38RpPqCQ== + "@fastify/ajv-compiler@^4.0.0": version "4.0.2" resolved "https://registry.yarnpkg.com/@fastify/ajv-compiler/-/ajv-compiler-4.0.2.tgz#da05938cf852901bfb953738764f553b5449b80b" @@ -1669,6 +1674,29 @@ dependencies: fast-deep-equal "^3.1.3" +"@fastify/send@^3.2.0": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@fastify/send/-/send-3.3.1.tgz#cb5759480eb4110b44d9af613d61b3f8b2933d8b" + integrity sha512-6pofeVwaHN+E/MAofCwDqkWUliE3i++jlD0VH/LOfU8TJlCkMUSgKvA9bawDdVXxjve7XrdYMyDmkiYaoGWEtA== + dependencies: + "@lukeed/ms" "^2.0.2" + escape-html "~1.0.3" + fast-decode-uri-component "^1.0.1" + http-errors "^2.0.0" + mime "^3" + +"@fastify/static@^8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@fastify/static/-/static-8.0.4.tgz#7410ac5f73d6027d46e5b759b6a0365f9c5bca76" + integrity sha512-JdJIlXDYXZxbTFQazWOEfHxyD5uRXqRsLnp4rV9MwJnxadA0rrWBI8ZelPF2TPk/xDi5wunY/6ZmfwHXld13bA== + dependencies: + "@fastify/accept-negotiator" "^2.0.0" + "@fastify/send" "^3.2.0" + content-disposition "^0.5.4" + fastify-plugin "^5.0.0" + fastq "^1.17.1" + glob "^11.0.0" + "@floating-ui/core@^1.6.0": version "1.6.9" resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.9.tgz#64d1da251433019dafa091de9b2886ff35ec14e6" @@ -1724,6 +1752,18 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@joshwooding/vite-plugin-react-docgen-typescript@0.4.2": version "0.4.2" resolved "https://registry.yarnpkg.com/@joshwooding/vite-plugin-react-docgen-typescript/-/vite-plugin-react-docgen-typescript-0.4.2.tgz#c2591d2d7b02160341672d6bf3cc248dd60f2530" @@ -1764,6 +1804,11 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@lukeed/ms@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@lukeed/ms/-/ms-2.0.2.tgz#07f09e59a74c52f4d88c6db5c1054e819538e2a8" + integrity sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA== + "@mantine/core@^7.12.0": version "7.15.3" resolved "https://registry.yarnpkg.com/@mantine/core/-/core-7.15.3.tgz#91fded05219112483d821cec46fcbb8a80964b79" @@ -4433,6 +4478,13 @@ content-disposition@0.5.2: resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" integrity sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA== +content-disposition@^0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + convert-source-map@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" @@ -4481,7 +4533,7 @@ cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3, cross-spawn@^7.0.6: +cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3, cross-spawn@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== @@ -4670,6 +4722,11 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + dequal@^2.0.2, dequal@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" @@ -5098,6 +5155,11 @@ escalade@^3.1.1, escalade@^3.2.0: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -5748,6 +5810,14 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +foreground-child@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77" + integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -5908,6 +5978,18 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" +glob@^11.0.0: + version "11.0.1" + resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.1.tgz#1c3aef9a59d680e611b53dcd24bb8639cef064d9" + integrity sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw== + dependencies: + foreground-child "^3.1.0" + jackspeak "^4.0.1" + minimatch "^10.0.0" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^2.0.0" + glob@^7.1.3: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -6088,6 +6170,17 @@ html5-qrcode@^2.3.8: resolved "https://registry.yarnpkg.com/html5-qrcode/-/html5-qrcode-2.3.8.tgz#0b0cdf7a9926cfd4be530e13a51db47592adfa0d" integrity sha512-jsr4vafJhwoLVEDW3n1KvPnCCXWaQfRng0/EEYk1vNcQGcG/htAdhJX0be8YyqMoSz7+hZvOZSTAepsabiuhiQ== +http-errors@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + http-proxy-agent@^7.0.2: version "7.0.2" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" @@ -6192,7 +6285,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -6515,6 +6608,13 @@ iterator.prototype@^1.1.4: has-symbols "^1.1.0" set-function-name "^2.0.2" +jackspeak@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.0.2.tgz#11f9468a3730c6ff6f56823a820d7e3be9bef015" + integrity sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw== + dependencies: + "@isaacs/cliui" "^8.0.2" + jose@^4.14.6: version "4.15.9" resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.9.tgz#9b68eda29e9a0614c042fa29387196c7dd800100" @@ -6871,6 +6971,11 @@ lru-cache@6.0.0: dependencies: yallist "^4.0.0" +lru-cache@^11.0.0: + version "11.0.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.0.2.tgz#fbd8e7cf8211f5e7e5d91905c415a3f55755ca39" + integrity sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA== + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -7031,6 +7136,11 @@ mime@2.6.0: resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== +mime@^3: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" + integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -7048,6 +7158,13 @@ minimatch@3.1.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch dependencies: brace-expansion "^1.1.7" +minimatch@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.1.tgz#ce0521856b453c86e25f2c4c0d03e6ff7ddc440b" + integrity sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ== + dependencies: + brace-expansion "^2.0.1" + minimatch@^9.0.4: version "9.0.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" @@ -7060,6 +7177,11 @@ minimist@^1.2.0, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== +minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + mkdirp@^0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" @@ -7363,6 +7485,11 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -7417,6 +7544,14 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-scurry@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.0.tgz#9f052289f23ad8bf9397a2a0425e7b8615c58580" + integrity sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg== + dependencies: + lru-cache "^11.0.0" + minipass "^7.1.2" + path-to-regexp@3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-3.3.0.tgz#f7f31d32e8518c2660862b644414b6d5c63a611b" @@ -8166,7 +8301,7 @@ safe-buffer@5.1.2: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@^5.0.1, safe-buffer@^5.1.2: +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.2: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -8305,6 +8440,11 @@ set-proto@^1.0.0: es-errors "^1.3.0" es-object-atoms "^1.0.0" +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -8490,6 +8630,11 @@ stackback@0.0.2: resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + std-env@^3.8.0: version "3.8.0" resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.8.0.tgz#b56ffc1baf1a29dcc80a3bdf11d7fca7c315e7d5" @@ -8524,6 +8669,15 @@ storybook@^8.2.8: dependencies: "@storybook/core" "8.4.7" +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" @@ -8619,6 +8773,13 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^5.1.0, strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" @@ -8966,6 +9127,11 @@ toad-cache@^3.7.0: resolved "https://registry.yarnpkg.com/toad-cache/-/toad-cache-3.7.0.tgz#b9b63304ea7c45ec34d91f1d2fa513517025c441" integrity sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw== +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + totalist@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8" @@ -9535,6 +9701,15 @@ word-wrap@^1.2.5, word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -9544,7 +9719,7 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^8.0.1: +wrap-ansi@^8.0.1, wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==