diff --git a/infrastructure/eid-wallet/package.json b/infrastructure/eid-wallet/package.json index 3e50760f..1d6b402a 100644 --- a/infrastructure/eid-wallet/package.json +++ b/infrastructure/eid-wallet/package.json @@ -27,9 +27,16 @@ "@tauri-apps/plugin-biometric": "^2.2.0", "@tauri-apps/plugin-opener": "^2", "@tauri-apps/plugin-store": "^2.2.0", + "@veriff/incontext-sdk": "^2.4.0", + "@veriff/js-sdk": "^1.5.1", + "axios": "^1.6.7", "clsx": "^2.1.1", + "dotenv": "^16.5.0", "flag-icons": "^7.3.2", - "tailwind-merge": "^3.0.2" + "import": "^0.0.6", + "svelte-loading-spinners": "^0.3.6", + "tailwind-merge": "^3.0.2", + "uuid": "^11.1.0" }, "devDependencies": { "@biomejs/biome": "^1.9.4", diff --git a/infrastructure/eid-wallet/src-tauri/gen/apple/eid-wallet.xcodeproj/project.pbxproj b/infrastructure/eid-wallet/src-tauri/gen/apple/eid-wallet.xcodeproj/project.pbxproj index f58fe249..7512db49 100644 --- a/infrastructure/eid-wallet/src-tauri/gen/apple/eid-wallet.xcodeproj/project.pbxproj +++ b/infrastructure/eid-wallet/src-tauri/gen/apple/eid-wallet.xcodeproj/project.pbxproj @@ -377,7 +377,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = "eid-wallet_iOS/eid-wallet_iOS.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; - DEVELOPMENT_TEAM = 7F2T2WK6DR; + DEVELOPMENT_TEAM = 3FS4B734X5; ENABLE_BITCODE = NO; "EXCLUDED_ARCHS[sdk=iphoneos*]" = "arm64-sim x86_64"; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; @@ -430,7 +430,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = "eid-wallet_iOS/eid-wallet_iOS.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; - DEVELOPMENT_TEAM = 7F2T2WK6DR; + DEVELOPMENT_TEAM = 3FS4B734X5; ENABLE_BITCODE = NO; "EXCLUDED_ARCHS[sdk=iphoneos*]" = "arm64-sim x86_64"; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; diff --git a/infrastructure/eid-wallet/src/lib/fragments/IdentityCard/IdentityCard.svelte b/infrastructure/eid-wallet/src/lib/fragments/IdentityCard/IdentityCard.svelte index 98483ab4..624727ab 100644 --- a/infrastructure/eid-wallet/src/lib/fragments/IdentityCard/IdentityCard.svelte +++ b/infrastructure/eid-wallet/src/lib/fragments/IdentityCard/IdentityCard.svelte @@ -1,103 +1,145 @@
-
-
-
+
+
+
- {#if variant === 'eName'} - -
- {#if shareBtn} - + {#if variant === "eName"} + +
+ {#if shareBtn} + {/if} {#if viewBtn} - + {/if} -
- {:else if variant === 'ePassport'} -

HIGH SECURITY

+
+ {:else if variant === "ePassport"} +

+ HIGH SECURITY +

{#if viewBtn} - + {/if} - {:else if variant === 'eVault'} -

{state.progressWidth} Used

+ {:else if variant === "eVault"} +

+ {state.progressWidth} Used +

{/if}
- {#if variant === "eName"} + {#if variant === "eName"}

Your eName

-

@{userId}

+

{userId}

{:else if variant === "ePassport"}
{#if userData} - {#each Object.entries(userData) as [fieldName, value] } + {#each Object.entries(userData) as [fieldName, value]}
-

{fieldName}

+

{fieldName}

{value}

{/each} {/if}
{:else if variant === "eVault"} -
-
-

{usedStorage}GB Used

-

{totalStorage}GB total storage

-
-
-
+
+
+

{usedStorage}GB Used

+

{totalStorage}GB total storage

+
+
+
+
-
{/if}
-
\ No newline at end of file +
+ diff --git a/infrastructure/eid-wallet/src/lib/global/controllers/evault.ts b/infrastructure/eid-wallet/src/lib/global/controllers/evault.ts new file mode 100644 index 00000000..d3f514e6 --- /dev/null +++ b/infrastructure/eid-wallet/src/lib/global/controllers/evault.ts @@ -0,0 +1,42 @@ +import type { Store } from "@tauri-apps/plugin-store"; + +export class VaultController { + #store: Store; + constructor(store: Store) { + this.#store = store; + } + + set vault( + vault: + | Promise | undefined> + | Record + | undefined, + ) { + if (vault instanceof Promise) { + vault + .then((resolvedUser) => { + this.#store.set("vault", resolvedUser); + }) + .catch((error) => { + console.error("Failed to set vault:", error); + }); + } else { + this.#store.set("vault", vault); + } + } + + get vault() { + return this.#store + .get>("vault") + .then((vault) => { + if (!vault) { + return undefined; + } + return vault; + }) + .catch((error) => { + console.error("Failed to get vault:", error); + return undefined; + }); + } +} diff --git a/infrastructure/eid-wallet/src/lib/global/controllers/user.ts b/infrastructure/eid-wallet/src/lib/global/controllers/user.ts index bf37ff5a..ac34e78d 100644 --- a/infrastructure/eid-wallet/src/lib/global/controllers/user.ts +++ b/infrastructure/eid-wallet/src/lib/global/controllers/user.ts @@ -54,10 +54,12 @@ export class UserController { * ``` * @throws {Error} If the user state cannot be set in the store */ - set user(user: - | Promise | undefined> - | Record - | undefined) { + set user( + user: + | Promise | undefined> + | Record + | undefined, + ) { if (user instanceof Promise) { user.then((resolvedUser) => { this.#store.set("user", resolvedUser); @@ -83,4 +85,38 @@ export class UserController { return undefined; }); } + + set document( + document: + | Promise | undefined> + | Record + | undefined, + ) { + if (document instanceof Promise) { + document + .then((resolvedDoc) => { + this.#store.set("doc", resolvedDoc); + }) + .catch((error) => { + console.error("Failed to set doc:", error); + }); + } else { + this.#store.set("doc", document); + } + } + + get document() { + return this.#store + .get>("doc") + .then((user) => { + if (!user) { + return undefined; + } + return user; + }) + .catch((error) => { + console.error("Failed to get doc:", error); + return undefined; + }); + } } diff --git a/infrastructure/eid-wallet/src/lib/global/state.ts b/infrastructure/eid-wallet/src/lib/global/state.ts index 1da2647d..4c8f703e 100644 --- a/infrastructure/eid-wallet/src/lib/global/state.ts +++ b/infrastructure/eid-wallet/src/lib/global/state.ts @@ -1,6 +1,7 @@ import { Store } from "@tauri-apps/plugin-store"; import { SecurityController } from "./controllers/security"; import { UserController } from "./controllers/user"; +import { VaultController } from "./controllers/evault"; /** * @author SoSweetHam * @description A centralized state that can be used to control the global state of the application, meant to be used as a singleton through the main layout component. @@ -23,10 +24,13 @@ export class GlobalState { #store: Store; securityController: SecurityController; userController: UserController; + vaultController: VaultController; + private constructor(store: Store) { this.#store = store; this.securityController = new SecurityController(store); this.userController = new UserController(store); + this.vaultController = new VaultController(store); } /** diff --git a/infrastructure/eid-wallet/src/lib/ui/Button/ButtonAction.svelte b/infrastructure/eid-wallet/src/lib/ui/Button/ButtonAction.svelte index 404d8b53..5368b4f3 100644 --- a/infrastructure/eid-wallet/src/lib/ui/Button/ButtonAction.svelte +++ b/infrastructure/eid-wallet/src/lib/ui/Button/ButtonAction.svelte @@ -106,7 +106,7 @@ const classes = $derived({
- - - - - - - \ No newline at end of file + + + + + Delete Account + diff --git a/infrastructure/eid-wallet/src/routes/(auth)/e-passport/+page.svelte b/infrastructure/eid-wallet/src/routes/(auth)/e-passport/+page.svelte index 7f8e3dfd..8a2d1bb9 100644 --- a/infrastructure/eid-wallet/src/routes/(auth)/e-passport/+page.svelte +++ b/infrastructure/eid-wallet/src/routes/(auth)/e-passport/+page.svelte @@ -1,12 +1,22 @@
{ subtitle="Log into any W3DS platform without passwords. It’s tied to this phone; if lost, you’ll need to revoke and reissue it on a new device." class="mb-2" /> - +

Your eVault

@@ -35,7 +37,7 @@ const handleFinish = async () => { personal data. W3DS platforms access it directly, keeping you in control.

- +
Finish
diff --git a/infrastructure/eid-wallet/src/routes/(auth)/review/+page.svelte b/infrastructure/eid-wallet/src/routes/(auth)/review/+page.svelte index 5f821509..ecd9ed1d 100644 --- a/infrastructure/eid-wallet/src/routes/(auth)/review/+page.svelte +++ b/infrastructure/eid-wallet/src/routes/(auth)/review/+page.svelte @@ -1,21 +1,35 @@ -
+
- +
Next -
\ No newline at end of file +
+ diff --git a/infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte b/infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte index 6498f342..43649956 100644 --- a/infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte +++ b/infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte @@ -1,44 +1,181 @@ -
+
- passport + passport
- { - try { - await handleVerification(); - } catch (error) { - console.error("Verification failed:", error); - // Consider adding user-facing error handling here - } - }}>I'm ready + I'm ready + + {#if $verifStep === 0} + + {:else if $verifStep === 1} + + {:else if loading} +
+
+ +

Generating your eName

+
+
+ {:else} +
+ {#if $status === "approved"} +
+

Your verification was a success

+

You can now continue on to create your eName

+
+ {:else if $status === "resubmission_requested"} +

Your verification failed due to the reason

+

{$reason}

+ {:else} +

Your verification failed

+ +

{$reason}

+ {/if} +
+
+ {#if $status !== "declined"} + {$status === "approved" + ? "Continue" + : "Retry"} + {/if} +
+ {/if} +
diff --git a/infrastructure/eid-wallet/src/routes/(auth)/verify/steps/passport.svelte b/infrastructure/eid-wallet/src/routes/(auth)/verify/steps/passport.svelte new file mode 100644 index 00000000..559110ac --- /dev/null +++ b/infrastructure/eid-wallet/src/routes/(auth)/verify/steps/passport.svelte @@ -0,0 +1,123 @@ + + +
+ {#if error} +
{error}
+ {/if} +
+
+
+

Present your Passport

+

+ Please place your passport's photo page within the rectangle + and press the take photo button +

+
+
+ + + +
+
+ + + {loading ? "Processing..." : "Take Photo"} +
+
+
diff --git a/infrastructure/eid-wallet/src/routes/(auth)/verify/steps/selfie.svelte b/infrastructure/eid-wallet/src/routes/(auth)/verify/steps/selfie.svelte new file mode 100644 index 00000000..eed8fd72 --- /dev/null +++ b/infrastructure/eid-wallet/src/routes/(auth)/verify/steps/selfie.svelte @@ -0,0 +1,123 @@ + + +
+ {#if !load} +
+

Take a Selfie

+

+ Place your face in the center of the circle and press the take + photo button +

+
+ +
+
+ + + +
+
+
+
+ +
+
+
+
+ Please make sure that your face is in the frame and clearly visible. +
+ + {"Take Photo"} + {:else} +
+
+ +

Verifying your identity

+
+
+ {/if} +
diff --git a/infrastructure/eid-wallet/src/routes/(auth)/verify/store.ts b/infrastructure/eid-wallet/src/routes/(auth)/verify/store.ts new file mode 100644 index 00000000..12f35d30 --- /dev/null +++ b/infrastructure/eid-wallet/src/routes/(auth)/verify/store.ts @@ -0,0 +1,9 @@ +import { writable } from "svelte/store"; + +export const verifStep = writable(0); +export const permissionGranted = writable(false); +export const DocFront = writable(); +export const Selfie = writable(); +export const verificaitonId = writable(); +export const status = writable(); +export const reason = writable(); diff --git a/infrastructure/eid-wallet/src/routes/+layout.svelte b/infrastructure/eid-wallet/src/routes/+layout.svelte index f1e25224..0444fe77 100644 --- a/infrastructure/eid-wallet/src/routes/+layout.svelte +++ b/infrastructure/eid-wallet/src/routes/+layout.svelte @@ -102,11 +102,11 @@ {#if showSplashScreen} {:else} -
+
{@render children?.()}
{/if} - + diff --git a/infrastructure/eid-wallet/static/images/CameraCircle.svg b/infrastructure/eid-wallet/static/images/CameraCircle.svg new file mode 100644 index 00000000..fe2ba93a --- /dev/null +++ b/infrastructure/eid-wallet/static/images/CameraCircle.svg @@ -0,0 +1,3 @@ + + + diff --git a/infrastructure/eid-wallet/static/images/CameraFrame.svg b/infrastructure/eid-wallet/static/images/CameraFrame.svg new file mode 100644 index 00000000..24451d32 --- /dev/null +++ b/infrastructure/eid-wallet/static/images/CameraFrame.svg @@ -0,0 +1,3 @@ + + + diff --git a/infrastructure/eid-wallet/svelte.config.js b/infrastructure/eid-wallet/svelte.config.js index 1b6a6b8d..9e995d0f 100644 --- a/infrastructure/eid-wallet/svelte.config.js +++ b/infrastructure/eid-wallet/svelte.config.js @@ -6,10 +6,13 @@ import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; /** @type {import('@sveltejs/kit').Config} */ const config = { - preprocess: vitePreprocess(), - kit: { - adapter: adapter(), - }, + preprocess: vitePreprocess(), + kit: { + adapter: adapter(), + env: { + dir: "../../", + }, + }, }; export default config; diff --git a/infrastructure/evault-provisioner/package.json b/infrastructure/evault-provisioner/package.json index 5c560787..ffe10dd9 100644 --- a/infrastructure/evault-provisioner/package.json +++ b/infrastructure/evault-provisioner/package.json @@ -3,26 +3,36 @@ "version": "1.0.0", "description": "API for provisioning evault instances on Nomad", "main": "dist/index.js", - "type": "module", "scripts": { "start": "node dist/index.js", - "dev": "tsx watch src/index.ts", + "dev": "ts-node-dev --respawn --transpile-only src/index.ts", "build": "tsc", - "test": "vitest" + "test": "vitest", + "typeorm": "typeorm-ts-node-commonjs", + "migration:generate": "npm run typeorm migration:generate -- -d src/config/database.ts", + "migration:run": "npm run typeorm migration:run -- -d src/config/database.ts", + "migration:revert": "npm run typeorm migration:revert -- -d src/config/database.ts" }, "dependencies": { "@kubernetes/client-node": "^1.3.0", "axios": "^1.6.7", + "cors": "^2.8.5", "dotenv": "^16.4.5", "express": "^4.18.2", "jose": "^5.2.2", + "pg": "^8.11.3", + "reflect-metadata": "^0.2.1", "sha256": "^0.2.0", + "typeorm": "^0.3.24", "w3id": "workspace:*" }, "devDependencies": { + "@types/cors": "^2.8.18", "@types/express": "^4.17.21", "@types/node": "^20.11.24", + "@types/sha256": "^0.2.2", "nodemon": "^3.0.3", + "ts-node-dev": "^2.0.0", "tsx": "^4.7.1", "typescript": "^5.3.3", "vitest": "^1.3.1" diff --git a/infrastructure/evault-provisioner/src/config/database.ts b/infrastructure/evault-provisioner/src/config/database.ts new file mode 100644 index 00000000..2ae7b03c --- /dev/null +++ b/infrastructure/evault-provisioner/src/config/database.ts @@ -0,0 +1,17 @@ +import { DataSource } from "typeorm" +import { Verification } from "../entities/Verification" +import * as dotenv from "dotenv" +import { join } from "path" + +// Load environment variables from root .env file +dotenv.config({ path: join(__dirname, "../../../../.env") }) + +export const AppDataSource = new DataSource({ + type: "postgres", + url: process.env.PROVISIONER_DATABASE_URL || "postgresql://postgres:postgres@localhost:5432/evault", + logging: process.env.NODE_ENV !== "production", + entities: [Verification], + migrations: [join(__dirname, "../migrations/*.{ts,js}")], + migrationsTableName: "migrations", + subscribers: [], +}) \ No newline at end of file diff --git a/infrastructure/evault-provisioner/src/controllers/VerificationController.ts b/infrastructure/evault-provisioner/src/controllers/VerificationController.ts new file mode 100644 index 00000000..c31adf0d --- /dev/null +++ b/infrastructure/evault-provisioner/src/controllers/VerificationController.ts @@ -0,0 +1,255 @@ +import { Request, Response } from "express"; +import { VerificationService } from "../services/VerificationService"; +import { eventEmitter } from "../utils/eventEmitter"; +import { createHmacSignature } from "../utils/hmac"; +import { default as Axios } from "axios"; + +const veriffClient = Axios.create({ + baseURL: "https://stationapi.veriff.com", + withCredentials: true, +}); + +veriffClient.interceptors.response.use( + (response) => { + return response; + }, + async function (error) { + if (!error.response) return Promise.reject(error); + return Promise.reject(error); + }, +); + +export class VerificationController { + constructor(private readonly verificationService: VerificationService) {} + + registerRoutes(app: any) { + // SSE endpoint for verification status updates + + app.get( + "/verification/sessions/:id", + async (req: Request, res: Response) => { + const { id } = req.params; + + // Set headers for SSE + res.writeHead(200, { + "Content-Type": "text/event-stream", + "Cache-Control": "no-cache", + Connection: "keep-alive", + "Access-Control-Allow-Origin": "*", + }); + + // Initial heartbeat to keep connection open + res.write( + `event: connected\ndata: ${JSON.stringify({ hi: "hi" })}\n\n`, + ); + + const handler = (data: any) => { + console.log("hi?"); + res.write(`data: ${JSON.stringify(data)}\n\n`); + }; + + eventEmitter.on(id, handler); + + // Handle client disconnect + req.on("close", () => { + eventEmitter.off(id, handler); + res.end(); + }); + + req.on("error", (error) => { + console.error("SSE Error:", error); + eventEmitter.off(id, handler); + res.end(); + }); + }, + ); + + app.post( + "/verification/:id/media", + async (req: Request, res: Response) => { + const { img, type } = req.body; + const types = ["document-front", "document-back", "face"]; + if (!types.includes(type)) + throw new Error( + `Wrong type specified, accepted types are ${types}`, + ); + const verification = await this.verificationService.findById( + req.params.id, + ); + if (!verification) throw new Error("Verification not found"); + const veriffBody = { + image: { + context: type, + content: img, + }, + }; + + const signature = createHmacSignature( + veriffBody, + process.env.VERIFF_HMAC_KEY as string, + ); + await veriffClient.post( + `/v1/sessions/${verification.veriffId}/media`, + veriffBody, + { + headers: { + "X-HMAC-SIGNATURE": signature, + "X-AUTH-CLIENT": process.env.PUBLIC_VERIFF_KEY, + }, + }, + ); + res.sendStatus(201); + }, + ); + + // Get verification session + app.get("/verification/:id", async (req: Request, res: Response) => { + const { id } = req.params; + const session = await this.verificationService.findById(id); + if (!session) { + return res + .status(404) + .json({ error: "Verification session not found" }); + } + return res.json(session); + }); + + // Create new verification + app.post("/verification", async (req: Request, res: Response) => { + const { referenceId } = req.body; + + if (referenceId) { + const existing = await this.verificationService.findOne({ + referenceId, + }); + if (existing) { + return res + .status(409) + .json({ error: "Reference ID Already Exists" }); + } + } + + const verification = await this.verificationService.create({ + referenceId, + }); + const veriffBody = { + verification: { + vendorData: verification.id, + }, + }; + const signature = createHmacSignature( + veriffBody, + process.env.VERIFF_HMAC_KEY as string, + ); + const { data: veriffSession } = await veriffClient.post( + "/v1/sessions", + veriffBody, + { + headers: { + "X-HMAC-SIGNATURE": signature, + "X-AUTH-CLIENT": process.env.PUBLIC_VERIFF_KEY, + }, + }, + ); + await this.verificationService.findByIdAndUpdate(verification.id, { + veriffId: veriffSession.verification.id, + }); + + return res.status(201).json(verification); + }); + + app.patch("/verification/:id", async (req: Request, res: Response) => { + const verification = await this.verificationService.findById( + req.params.id, + ); + const body = { + verification: { + status: "submitted", + }, + }; + const signature = createHmacSignature( + body, + process.env.VERIFF_HMAC_KEY as string, + ); + await veriffClient.patch( + `/v1/sessions/${verification?.veriffId}`, + body, + { + headers: { + "X-HMAC-SIGNATURE": signature, + "X-AUTH-CLIENT": process.env.PUBLIC_VERIFF_KEY, + }, + }, + ); + res.sendStatus(201); + }); + + // Webhook for verification decisions + app.post( + "/verification/decisions", + async (req: Request, res: Response) => { + const body = req.body; + console.log(body); + const id = body.vendorData; + + const verification = + await this.verificationService.findById(id); + if (!verification) { + return res + .status(404) + .json({ error: "Verification not found" }); + } + + let status = body.data.verification.decision; + let reason = body.data.verification.decision; + + const affirmativeStatusTypes = [ + "approved", + "declined", + "expired", + "abandoned", + ]; + if ( + affirmativeStatusTypes.includes( + body.data.verification.decision, + ) + ) { + let approved = + body.data.verification.decision === "approved"; + if (process.env.DUPLICATES_POLICY !== "allow") { + const verificationMatch = + await this.verificationService.findOne({ + documentId: + body.data.verification.document.number.value + }); + console.log("matched", verificationMatch) + if (verificationMatch) { + approved = false; + status = "declined"; + reason = + "Document already used to create an eVault"; + } + } + await this.verificationService.findByIdAndUpdate(id, { + approved, + data: { + person: body.data.verification.person, + document: body.data.verification.document, + }, + documentId: + body.data.verification.document.number.value, + }); + } + + eventEmitter.emit(id, { + reason, + status, + person: body.data.verification.person ?? null, + document: body.data.verification.document, + }); + + return res.json({ success: true }); + }, + ); + } +} diff --git a/infrastructure/evault-provisioner/src/entities/Verification.ts b/infrastructure/evault-provisioner/src/entities/Verification.ts new file mode 100644 index 00000000..7b258309 --- /dev/null +++ b/infrastructure/evault-provisioner/src/entities/Verification.ts @@ -0,0 +1,37 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, +} from "typeorm"; + +@Entity() +export class Verification { + @PrimaryGeneratedColumn("uuid") + id!: string; + + @Column({ nullable: true }) + veriffId!: string; + + @Column({ nullable: true }) + approved!: boolean; + + @Column({ type: "jsonb", nullable: true }) + data!: Record; + + @Column({ nullable: true }) + referenceId!: string; + + @Column({ nullable: true }) + documentId!: string; + + @Column({ default: false }) + consumed!: boolean; + + @CreateDateColumn() + createdAt!: Date; + + @UpdateDateColumn() + updatedAt!: Date; +} diff --git a/infrastructure/evault-provisioner/src/index.ts b/infrastructure/evault-provisioner/src/index.ts index 23f1912a..ae4cce77 100644 --- a/infrastructure/evault-provisioner/src/index.ts +++ b/infrastructure/evault-provisioner/src/index.ts @@ -1,29 +1,64 @@ +import "reflect-metadata"; import express, { Request, Response } from "express"; import axios, { AxiosError } from "axios"; -import { provisionEVault } from "./templates/evault.nomad.js"; +import { provisionEVault } from "./templates/evault.nomad"; import dotenv from "dotenv"; import { W3IDBuilder } from "w3id"; import * as jose from "jose"; import path from "path"; -import { fileURLToPath } from "url"; +import { createHmacSignature } from "./utils/hmac"; +import cors from "cors"; +import { AppDataSource } from "./config/database"; +import { VerificationService } from "./services/VerificationService"; +import { VerificationController } from "./controllers/VerificationController"; -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); dotenv.config({ path: path.resolve(__dirname, "../../../.env") }); const app = express(); const port = process.env.PORT || 3001; -app.use(express.json()); +// Configure CORS for SSE +app.use( + cors({ + origin: "*", + methods: ["GET", "POST", "OPTIONS", "PATCH"], + allowedHeaders: ["Content-Type", "Authorization"], + credentials: true, + }), +); + +// Increase JSON payload limit to 50MB +app.use(express.json({ limit: "50mb" })); +// Increase URL-encoded payload limit to 50MB +app.use(express.urlencoded({ limit: "50mb", extended: true })); + +// Initialize database connection +const initializeDatabase = async () => { + try { + await AppDataSource.initialize(); + console.log("Database connection initialized"); + } catch (error) { + console.error("Error during database initialization:", error); + process.exit(1); + } +}; + +// Initialize services and controllers +const verificationService = new VerificationService( + AppDataSource.getRepository("Verification"), +); +const verificationController = new VerificationController(verificationService); interface ProvisionRequest { registryEntropy: string; namespace: string; + verificationId: string; } interface ProvisionResponse { success: boolean; uri?: string; + w3id?: string; message?: string; error?: string | unknown; } @@ -41,20 +76,30 @@ app.post( res: Response, ) => { try { - - if (!process.env.REGISTRY_URI) throw new Error("REGISTRY_URI is not set"); - const { registryEntropy, namespace } = req.body; - - if (!registryEntropy || !namespace) { + if (!process.env.PUBLIC_REGISTRY_URL) + throw new Error("PUBLIC_REGISTRY_URL is not set"); + const { registryEntropy, namespace, verificationId } = req.body; + if (!registryEntropy || !namespace || !verificationId) { return res.status(400).json({ success: false, error: "registryEntropy and namespace are required", message: - "Missing required fields: registryEntropy, namespace", + "Missing required fields: registryEntropy, namespace, verifficationId", }); } + const verification = + await verificationService.findById(verificationId); + if (!verification) throw new Error("verification doesn't exist"); + if (!verification.approved) + throw new Error("verification not approved"); + if (verification.consumed) + throw new Error("This verification ID has already been used"); + const jwksResponse = await axios.get( - `http://localhost:4321/.well-known/jwks.json`, + new URL( + `/.well-known/jwks.json`, + process.env.PUBLIC_REGISTRY_URL, + ).toString(), ); const JWKS = jose.createLocalJWKSet(jwksResponse.data); @@ -72,23 +117,28 @@ app.post( const uri = await provisionEVault(w3id, evaultId.id); - - await axios.post(new URL("/register", process.env.REGISTRY_URI).toString(), { - ename: w3id, - uri, - evault: evaultId.id, - }, { - headers: { - "Authorization": `Bearer ${process.env.REGISTRY_SHARED_SECRET}` - } - }); + await axios.post( + new URL( + "/register", + process.env.PUBLIC_REGISTRY_URL, + ).toString(), + { + ename: w3id, + uri, + evault: evaultId.id, + }, + { + headers: { + Authorization: `Bearer ${process.env.REGISTRY_SHARED_SECRET}`, + }, + }, + ); res.json({ success: true, + w3id, uri, }); - - } catch (error) { const axiosError = error as AxiosError; res.status(500).json({ @@ -100,6 +150,20 @@ app.post( }, ); -app.listen(port, () => { - console.log(`Evault Provisioner API running on port ${port}`); -}); +// Register verification routes +verificationController.registerRoutes(app); + +// Start the server +const start = async () => { + try { + await initializeDatabase(); + app.listen(port, () => { + console.log(`Evault Provisioner API running on port ${port}`); + }); + } catch (err) { + console.error(err); + process.exit(1); + } +}; + +start(); diff --git a/infrastructure/evault-provisioner/src/migrations/1748932757644-migration.ts b/infrastructure/evault-provisioner/src/migrations/1748932757644-migration.ts new file mode 100644 index 00000000..b9e70e53 --- /dev/null +++ b/infrastructure/evault-provisioner/src/migrations/1748932757644-migration.ts @@ -0,0 +1,14 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class Migration1748932757644 implements MigrationInterface { + name = 'Migration1748932757644' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE "verification" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "veriffId" character varying, "approved" boolean, "data" jsonb, "referenceId" character varying, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_f7e3a90ca384e71d6e2e93bb340" PRIMARY KEY ("id"))`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE "verification"`); + } + +} diff --git a/infrastructure/evault-provisioner/src/migrations/1748966722767-migration.ts b/infrastructure/evault-provisioner/src/migrations/1748966722767-migration.ts new file mode 100644 index 00000000..f51e3af8 --- /dev/null +++ b/infrastructure/evault-provisioner/src/migrations/1748966722767-migration.ts @@ -0,0 +1,14 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class Migration1748966722767 implements MigrationInterface { + name = 'Migration1748966722767' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "verification" ADD "documentId" character varying`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "verification" DROP COLUMN "documentId"`); + } + +} diff --git a/infrastructure/evault-provisioner/src/migrations/1748968097591-migration.ts b/infrastructure/evault-provisioner/src/migrations/1748968097591-migration.ts new file mode 100644 index 00000000..aa2e5b82 --- /dev/null +++ b/infrastructure/evault-provisioner/src/migrations/1748968097591-migration.ts @@ -0,0 +1,14 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class Migration1748968097591 implements MigrationInterface { + name = 'Migration1748968097591' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "verification" ADD "consumed" boolean NOT NULL DEFAULT false`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "verification" DROP COLUMN "consumed"`); + } + +} diff --git a/infrastructure/evault-provisioner/src/services/VerificationService.ts b/infrastructure/evault-provisioner/src/services/VerificationService.ts new file mode 100644 index 00000000..ced2185f --- /dev/null +++ b/infrastructure/evault-provisioner/src/services/VerificationService.ts @@ -0,0 +1,50 @@ +import { DeepPartial, Repository } from "typeorm"; +import { Verification } from "../entities/Verification"; + +export class VerificationService { + constructor( + private readonly verificationRepository: Repository, + ) {} + + async create(data: Partial): Promise { + const verification = this.verificationRepository.create(data); + return await this.verificationRepository.save(verification); + } + + async findById(id: string): Promise { + return await this.verificationRepository.findOneBy({ id }); + } + + async findByIdAndUpdate( + id: string, + data: DeepPartial, + ): Promise { + const current = await this.findById(id); + const toSave = this.verificationRepository.create({ + ...current, + ...data, + }); + + const updated = await this.verificationRepository.save(toSave); + return updated; + } + + async findOne(where: Partial): Promise { + return await this.verificationRepository.findOneBy(where); + } + + async findManyAndCount( + where: Partial, + relations: Record = {}, + order: Record = {}, + pagination: { take: number; skip: number } = { take: 10, skip: 0 }, + ): Promise<[Verification[], number]> { + return await this.verificationRepository.findAndCount({ + where, + relations, + order, + take: pagination.take, + skip: pagination.skip, + }); + } +} diff --git a/infrastructure/evault-provisioner/src/utils/eventEmitter.ts b/infrastructure/evault-provisioner/src/utils/eventEmitter.ts new file mode 100644 index 00000000..669fe1f8 --- /dev/null +++ b/infrastructure/evault-provisioner/src/utils/eventEmitter.ts @@ -0,0 +1,3 @@ +import { EventEmitter } from "events" + +export const eventEmitter = new EventEmitter() \ No newline at end of file diff --git a/infrastructure/evault-provisioner/src/utils/hmac.ts b/infrastructure/evault-provisioner/src/utils/hmac.ts new file mode 100644 index 00000000..2091f7f2 --- /dev/null +++ b/infrastructure/evault-provisioner/src/utils/hmac.ts @@ -0,0 +1,31 @@ +import { createHmac } from "crypto"; + +/** + * Generates an HMAC SHA-256 signature for a JSON-serializable object using the provided secret key. + * + * @param body - The object to be signed. + * @param secret - The secret key used for HMAC generation. + * @returns The hexadecimal string representation of the HMAC signature. + */ +export function createHmacSignature(body: Record, secret: string) { + return createHmac("sha256", secret) + .update(JSON.stringify(body)) + .digest("hex"); +} + +/** + * Verifies that a provided HMAC signature matches the expected signature for a given object and secret key. + * + * @param body - The object whose signature is to be verified. + * @param signature - The HMAC signature to compare against. + * @param secret - The secret key used to generate the expected signature. + * @returns `true` if the signature is valid; otherwise, `false`. + */ +export function verifyHmacSignature( + body: Record, + signature: string, + secret: string, +) { + const expectedSignature = createHmacSignature(body, secret); + return expectedSignature === signature; +} diff --git a/infrastructure/evault-provisioner/tsconfig.json b/infrastructure/evault-provisioner/tsconfig.json index 0b488cd7..87ff34e2 100644 --- a/infrastructure/evault-provisioner/tsconfig.json +++ b/infrastructure/evault-provisioner/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "ES2020", - "module": "ESNext", + "module": "commonjs", "moduleResolution": "node", "esModuleInterop": true, "strict": true, @@ -10,7 +10,9 @@ "outDir": "dist", "rootDir": "src", "sourceMap": true, - "declaration": true + "declaration": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] diff --git a/platforms/registry/package.json b/platforms/registry/package.json index 850d23f1..005264dd 100644 --- a/platforms/registry/package.json +++ b/platforms/registry/package.json @@ -1,7 +1,7 @@ { "name": "registry", "version": "1.0.0", - "description": "Registry service for entropy and service discovery", + "description": "Registry service for managing vault entries", "main": "dist/index.js", "scripts": { "build": "tsc", @@ -15,6 +15,7 @@ "migration:create": "npm run typeorm migration:create" }, "dependencies": { + "@fastify/cors": "8.4.1", "@fastify/jwt": "^7.2.3", "axios": "^1.6.7", "dotenv": "^16.5.0", diff --git a/platforms/registry/src/index.ts b/platforms/registry/src/index.ts index bd381b9c..28fe83ab 100644 --- a/platforms/registry/src/index.ts +++ b/platforms/registry/src/index.ts @@ -4,11 +4,20 @@ import dotenv from "dotenv"; import path from "path"; import { AppDataSource } from "./config/database"; import { VaultService } from "./services/VaultService"; +import cors from "@fastify/cors"; dotenv.config({ path: path.resolve(__dirname, "../../../.env") }); const server = fastify({ logger: true }); +// Register CORS +server.register(cors, { + origin: "*", + methods: ["GET", "POST", "OPTIONS"], + allowedHeaders: ["Content-Type", "Authorization"], + credentials: true, +}); + // Initialize database connection const initializeDatabase = async () => { try { @@ -26,91 +35,103 @@ const vaultService = new VaultService(AppDataSource.getRepository("Vault")); // Middleware to check shared secret const checkSharedSecret = async (request: any, reply: any) => { const authHeader = request.headers.authorization; - if (!authHeader || !authHeader.startsWith('Bearer ')) { - return reply.status(401).send({ error: 'Missing or invalid authorization header' }); + if (!authHeader || !authHeader.startsWith("Bearer ")) { + return reply + .status(401) + .send({ error: "Missing or invalid authorization header" }); } - const secret = authHeader.split(' ')[1]; + const secret = authHeader.split(" ")[1]; if (secret !== process.env.REGISTRY_SHARED_SECRET) { - return reply.status(401).send({ error: 'Invalid shared secret' }); + return reply.status(401).send({ error: "Invalid shared secret" }); } }; // Create a new vault entry -server.post("/register", { - preHandler: checkSharedSecret -}, async (request, reply) => { - try { - const { ename, uri, evault } = request.body as { ename: string; uri: string; evault: string }; - - if (!ename || !uri || !evault) { - return reply.status(400).send({ - error: "Missing required fields. Please provide ename, uri, and evault" - }); +server.post( + "/register", + { + preHandler: checkSharedSecret, + }, + async (request, reply) => { + try { + const { ename, uri, evault } = request.body as { + ename: string; + uri: string; + evault: string; + }; + + if (!ename || !uri || !evault) { + return reply.status(400).send({ + error: "Missing required fields. Please provide ename, uri, and evault", + }); + } + + const vault = await vaultService.create(ename, uri, evault); + return reply.status(201).send(vault); + } catch (error) { + server.log.error(error); + reply.status(500).send({ error: "Failed to create vault entry" }); } + }, +); - const vault = await vaultService.create(ename, uri, evault); - return reply.status(201).send(vault); +// Generate and return a signed JWT with entropy +server.get("/entropy", async (request, reply) => { + try { + const token = await generateEntropy(); + return { token }; } catch (error) { server.log.error(error); - reply.status(500).send({ error: "Failed to create vault entry" }); + reply.status(500).send({ error: "Failed to generate entropy" }); } }); -// Generate and return a signed JWT with entropy -server.get("/entropy", async (request, reply) => { - try { - const token = await generateEntropy(); - return { token }; - } catch (error) { - server.log.error(error); - reply.status(500).send({ error: "Failed to generate entropy" }); - } -}); - // Expose the JWK used for signing server.get("/.well-known/jwks.json", async (request, reply) => { - try { - const jwk = await getJWK(); - return jwk; - } catch (error) { - server.log.error(error); - reply.status(500).send({ error: "Failed to get JWK" }); - } + try { + const jwk = await getJWK(); + return jwk; + } catch (error) { + server.log.error(error); + reply.status(500).send({ error: "Failed to get JWK" }); + } }); // Resolve service from database based on w3id server.get("/resolve", async (request, reply) => { - try { - const { w3id } = request.query as { w3id: string }; - if (!w3id) { - return reply.status(400).send({ error: "w3id parameter is required" }); - } + try { + const { w3id } = request.query as { w3id: string }; + if (!w3id) { + return reply + .status(400) + .send({ error: "w3id parameter is required" }); + } - const vault = await vaultService.findByEname(w3id); - if (!vault) { - return reply.status(404).send({ error: "Service not found" }); - } + const vault = await vaultService.findByEname(w3id); + if (!vault) { + return reply.status(404).send({ error: "Service not found" }); + } - return { - ename: vault.ename, - uri: vault.uri, - evault: vault.evault - }; - } catch (error) { - server.log.error(error); - reply.status(500).send({ error: "Failed to resolve service" }); - } + return { + ename: vault.ename, + uri: vault.uri, + evault: vault.evault, + }; + } catch (error) { + server.log.error(error); + reply.status(500).send({ error: "Failed to resolve service" }); + } }); const start = async () => { - try { - await initializeDatabase(); - await server.listen({ port: 4321, host: "0.0.0.0" }); - } catch (err) { - server.log.error(err); - process.exit(1); - } + try { + await initializeDatabase(); + await server.listen({ port: 4321, host: "0.0.0.0" }); + } catch (err) { + server.log.error(err); + process.exit(1); + } }; start(); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0b943e3a..6bd9cdb4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,15 +44,36 @@ importers: '@tauri-apps/plugin-store': specifier: ^2.2.0 version: 2.2.0 + '@veriff/incontext-sdk': + specifier: ^2.4.0 + version: 2.4.0 + '@veriff/js-sdk': + specifier: ^1.5.1 + version: 1.5.1 + axios: + specifier: ^1.6.7 + version: 1.9.0 clsx: specifier: ^2.1.1 version: 2.1.1 + dotenv: + specifier: ^16.5.0 + version: 16.5.0 flag-icons: specifier: ^7.3.2 version: 7.3.2 + import: + specifier: ^0.0.6 + version: 0.0.6 + svelte-loading-spinners: + specifier: ^0.3.6 + version: 0.3.6 tailwind-merge: specifier: ^3.0.2 version: 3.3.0 + uuid: + specifier: ^11.1.0 + version: 11.1.0 devDependencies: '@biomejs/biome': specifier: ^1.9.4 @@ -232,6 +253,9 @@ importers: axios: specifier: ^1.6.7 version: 1.9.0 + cors: + specifier: ^2.8.5 + version: 2.8.5 dotenv: specifier: ^16.4.5 version: 16.5.0 @@ -241,22 +265,40 @@ importers: jose: specifier: ^5.2.2 version: 5.10.0 + pg: + specifier: ^8.11.3 + version: 8.16.0 + reflect-metadata: + specifier: ^0.2.1 + version: 0.2.2 sha256: specifier: ^0.2.0 version: 0.2.0 + typeorm: + specifier: ^0.3.24 + version: 0.3.24(babel-plugin-macros@3.1.0)(pg@8.16.0)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@20.17.50)(typescript@5.8.3)) w3id: specifier: workspace:* version: link:../w3id devDependencies: + '@types/cors': + specifier: ^2.8.18 + version: 2.8.18 '@types/express': specifier: ^4.17.21 version: 4.17.22 '@types/node': specifier: ^20.11.24 version: 20.17.50 + '@types/sha256': + specifier: ^0.2.2 + version: 0.2.2 nodemon: specifier: ^3.0.3 version: 3.1.10 + ts-node-dev: + specifier: ^2.0.0 + version: 2.0.0(@types/node@20.17.50)(typescript@5.8.3) tsx: specifier: ^4.7.1 version: 4.19.4 @@ -308,7 +350,7 @@ importers: version: 3.3.0 vitest: specifier: ^3.1.2 - version: 3.1.4(@types/node@22.15.21)(@vitest/browser@3.1.4)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0) + version: 3.1.4(@types/node@20.17.50)(@vitest/browser@3.1.4)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0) devDependencies: '@types/jest': specifier: ^29.5.0 @@ -324,10 +366,10 @@ importers: version: 8.57.1 jest: specifier: ^29.5.0 - version: 29.7.0(@types/node@22.15.21)(babel-plugin-macros@3.1.0) + version: 29.7.0(@types/node@20.17.50)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.17.50)(typescript@5.8.3)) ts-jest: specifier: ^29.1.0 - version: 29.3.4(@babel/core@7.27.1)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.1))(jest@29.7.0(@types/node@22.15.21)(babel-plugin-macros@3.1.0))(typescript@5.8.3) + version: 29.3.4(@babel/core@7.27.1)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.1))(jest@29.7.0(@types/node@20.17.50)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.17.50)(typescript@5.8.3)))(typescript@5.8.3) typescript: specifier: ^5.0.4 version: 5.8.3 @@ -481,6 +523,9 @@ importers: platforms/registry: dependencies: + '@fastify/cors': + specifier: 8.4.1 + version: 8.4.1 '@fastify/jwt': specifier: ^7.2.3 version: 7.2.4 @@ -1202,6 +1247,9 @@ packages: '@fastify/busboy@3.1.1': resolution: {integrity: sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==} + '@fastify/cors@8.4.1': + resolution: {integrity: sha512-iYQJtrY3pFiDS5mo5zRaudzg2OcUdJ96PD6xfkKOOEilly5nnrFZx/W6Sce2T79xxlEn2qpU3t5+qS2phS369w==} + '@fastify/error@3.4.1': resolution: {integrity: sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==} @@ -2249,6 +2297,9 @@ packages: '@types/cookie@0.6.0': resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + '@types/cors@2.8.18': + resolution: {integrity: sha512-nX3d0sxJW41CqQvfOzVG1NCTXfFDrDWIghCZncpHeWlVFd81zxB/DLhg7avFg6eHLCRX7ckBmoIIcqa++upvJA==} + '@types/docker-modem@3.0.6': resolution: {integrity: sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==} @@ -2341,6 +2392,9 @@ packages: '@types/serve-static@1.15.7': resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} + '@types/sha256@0.2.2': + resolution: {integrity: sha512-uKMaDzyzfcDYGEwTgLh+hmgDMxXWyIVodY8T+qt7A+NYvikW0lmGLMGbQ7BipCB8dzXHa55C9g+Ii/3Lgt1KmA==} + '@types/ssh2-streams@0.1.12': resolution: {integrity: sha512-Sy8tpEmCce4Tq0oSOYdfqaBpA3hDM8SoxoFh5vzFsu2oL+znzGz8oVWW7xb4K920yYMUY+PIG31qZnFMfPWNCg==} @@ -2356,6 +2410,12 @@ packages: '@types/stream-buffers@3.0.7': resolution: {integrity: sha512-azOCy05sXVXrO+qklf0c/B07H/oHaIuDDAiHPVwlk3A9Ek+ksHyTeMajLZl3r76FxpPpxem//4Te61G1iW3Giw==} + '@types/strip-bom@3.0.0': + resolution: {integrity: sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==} + + '@types/strip-json-comments@0.0.30': + resolution: {integrity: sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==} + '@types/uuid@9.0.8': resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} @@ -2473,6 +2533,13 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + '@veriff/incontext-sdk@2.4.0': + resolution: {integrity: sha512-Bz+sRoIQvarH/bfGXFtAp3LK7cpA4G4AOD4Cr63V0Y4bJDYX8uHSeBfRH1HxL2l6dFlO+2H+HwEhkrkKL5wBRQ==} + + '@veriff/js-sdk@1.5.1': + resolution: {integrity: sha512-THHcyRTctdtXUpKAEGnddTkwgFdRi2taAigtnhOfGmoCp23m+9vlZp5GnT7qmswfyZk48BCZ4Uc2gT7AUIhqTA==} + engines: {node: '>=16', npm: '>=8'} + '@vitest/browser@3.1.4': resolution: {integrity: sha512-2L4vR/tuUZBxKU72Qe+unIp1P8lZ0T5nlqPegkXxyZFR5gWqItV8VPPR261GOzl49Zw2AhzMABzMMHJagQ0a2g==} peerDependencies: @@ -3109,6 +3176,10 @@ packages: core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + cosmiconfig@7.1.0: resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} engines: {node: '>=10'} @@ -3299,6 +3370,9 @@ packages: dom-accessibility-api@0.6.3: resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + dom-focus-lock@1.0.4: + resolution: {integrity: sha512-Tiz2EYedB14UW0/lY5PEzFAie66e9itoEVKhz0M9xz1cHqP4Jd0XUg/AT2m6DtbhCFa9DHo6nQyePuLH7Mx9+A==} + dom-helpers@5.2.1: resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} @@ -3335,6 +3409,9 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + dynamic-dedupe@0.3.0: + resolution: {integrity: sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==} + eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -3770,6 +3847,9 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + focus-lock@0.6.8: + resolution: {integrity: sha512-vkHTluRCoq9FcsrldC0ulQHiyBYgVJB2CX53I8r0nTC6KnEij7Of0jpBspjt3/CuNb6fyoj3aOh9J2HgQUM0og==} + follow-redirects@1.15.9: resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} engines: {node: '>=4.0'} @@ -4032,6 +4112,11 @@ packages: engines: {node: '>=8'} hasBin: true + import@0.0.6: + resolution: {integrity: sha512-QPhTdjy9J4wUzmWSG7APkSgMFuPGPw+iJTYUblcfc2AfpqaatbwgCldK1HoLYx+v/+lWvab63GWZtNkcnj9JcQ==} + engines: {node: '>=0.10.0'} + hasBin: true + imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -4752,6 +4837,9 @@ packages: mlly@1.7.4: resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} + mnemonist@0.39.5: + resolution: {integrity: sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==} + mnemonist@0.39.8: resolution: {integrity: sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ==} @@ -4905,6 +4993,9 @@ packages: openid-client@6.5.0: resolution: {integrity: sha512-fAfYaTnOYE2kQCqEJGX9KDObW2aw7IQy4jWpU/+3D3WoCFLbix5Hg6qIPQ6Js9r7f8jDUmsnnguRNCSw4wU/IQ==} + optimist@0.3.7: + resolution: {integrity: sha512-TCx0dXQzVtSCg2OgY/bO9hjM9cV4XYx09TVK+s3+FhkjT6LovsLe+pPMzpWf+6yXK/hUizs2gUoTw3jHM0VaTQ==} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -5727,6 +5818,10 @@ packages: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} engines: {node: '>=12'} + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + strip-bom@4.0.0: resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} engines: {node: '>=8'} @@ -5743,6 +5838,10 @@ packages: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -5795,6 +5894,9 @@ packages: svelte-gestures@5.1.4: resolution: {integrity: sha512-gfSO/GqWLu9nRMCz12jqdyA0+NTsojYcIBcRqZjwWrpQbqMXr0zWPFpZBtzfYbRHtuFxZImMZp9MrVaFCYbhDg==} + svelte-loading-spinners@0.3.6: + resolution: {integrity: sha512-mthHQ2TwiwzTWzbFry3CBnVEfzqPOD9WkVw84OfSYzHRq6N9wgQ+yv37u81uPeuLU/ZOIPqhujpXquB1aol5ZQ==} + svelte-preprocess@5.1.4: resolution: {integrity: sha512-IvnbQ6D6Ao3Gg6ftiM5tdbR6aAETwjhHV+UKGf5bHGYR69RQvF1ho0JKPcbUON4vy4R7zom13jPjgdOWCQ5hDA==} engines: {node: '>= 16.0.0'} @@ -5967,6 +6069,10 @@ packages: tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + ts-api-utils@2.1.0: resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} engines: {node: '>=18.12'} @@ -6001,6 +6107,17 @@ packages: esbuild: optional: true + ts-node-dev@2.0.0: + resolution: {integrity: sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==} + engines: {node: '>=0.8.0'} + hasBin: true + peerDependencies: + node-notifier: '*' + typescript: '*' + peerDependenciesMeta: + node-notifier: + optional: true + ts-node@10.9.2: resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true @@ -6015,6 +6132,9 @@ packages: '@swc/wasm': optional: true + tsconfig@7.0.0: + resolution: {integrity: sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==} + tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} @@ -6032,6 +6152,9 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + tua-body-scroll-lock@1.2.1: + resolution: {integrity: sha512-DuGbQxIBSbz7/aAwh0oMThOwCwDdYzsU8nF5XQPAvoeOBWp/qhjrZfOtc84gj6CQLZu5L1+TgMNX6fW3OuafHA==} + turbo-darwin-64@2.5.3: resolution: {integrity: sha512-YSItEVBUIvAGPUDpAB9etEmSqZI3T6BHrkBkeSErvICXn3dfqXUfeLx35LfptLDEbrzFUdwYFNmt8QXOwe9yaw==} cpu: [x64] @@ -6475,6 +6598,10 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wordwrap@0.0.3: + resolution: {integrity: sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==} + engines: {node: '>=0.4.0'} + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -7148,6 +7275,11 @@ snapshots: '@fastify/busboy@3.1.1': {} + '@fastify/cors@8.4.1': + dependencies: + fastify-plugin: 4.5.1 + mnemonist: 0.39.5 + '@fastify/error@3.4.1': {} '@fastify/fast-json-stringify-compiler@4.3.0': @@ -8452,6 +8584,10 @@ snapshots: '@types/cookie@0.6.0': {} + '@types/cors@2.8.18': + dependencies: + '@types/node': 20.17.50 + '@types/docker-modem@3.0.6': dependencies: '@types/node': 20.17.50 @@ -8529,7 +8665,7 @@ snapshots: '@types/pg@8.15.4': dependencies: - '@types/node': 20.17.50 + '@types/node': 22.15.21 pg-protocol: 1.10.0 pg-types: 2.2.0 @@ -8562,6 +8698,10 @@ snapshots: '@types/node': 20.17.50 '@types/send': 0.17.4 + '@types/sha256@0.2.2': + dependencies: + '@types/node': 20.17.50 + '@types/ssh2-streams@0.1.12': dependencies: '@types/node': 20.17.50 @@ -8581,6 +8721,10 @@ snapshots: dependencies: '@types/node': 20.17.50 + '@types/strip-bom@3.0.0': {} + + '@types/strip-json-comments@0.0.30': {} + '@types/uuid@9.0.8': {} '@types/yargs-parser@21.0.3': {} @@ -8752,6 +8896,33 @@ snapshots: '@ungap/structured-clone@1.3.0': {} + '@veriff/incontext-sdk@2.4.0': + dependencies: + dom-focus-lock: 1.0.4 + tua-body-scroll-lock: 1.2.1 + + '@veriff/js-sdk@1.5.1': {} + + '@vitest/browser@3.1.4(playwright@1.52.0)(vite@6.3.5(@types/node@20.17.50)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0))(vitest@3.1.4)': + dependencies: + '@testing-library/dom': 10.4.0 + '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.0) + '@vitest/mocker': 3.1.4(vite@6.3.5(@types/node@20.17.50)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0)) + '@vitest/utils': 3.1.4 + magic-string: 0.30.17 + sirv: 3.0.1 + tinyrainbow: 2.0.0 + vitest: 3.1.4(@types/node@20.17.50)(@vitest/browser@3.1.4)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0) + ws: 8.18.2 + optionalDependencies: + playwright: 1.52.0 + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + optional: true + '@vitest/browser@3.1.4(playwright@1.52.0)(vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0))(vitest@3.1.4)': dependencies: '@testing-library/dom': 10.4.0 @@ -8811,6 +8982,14 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 + '@vitest/mocker@3.1.4(vite@6.3.5(@types/node@20.17.50)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0))': + dependencies: + '@vitest/spy': 3.1.4 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 6.3.5(@types/node@20.17.50)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0) + '@vitest/mocker@3.1.4(vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0))': dependencies: '@vitest/spy': 3.1.4 @@ -9488,6 +9667,11 @@ snapshots: core-util-is@1.0.3: {} + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + cosmiconfig@7.1.0: dependencies: '@types/parse-json': 4.0.2 @@ -9524,21 +9708,6 @@ snapshots: - supports-color - ts-node - create-jest@29.7.0(@types/node@22.15.21)(babel-plugin-macros@3.1.0): - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@22.15.21)(babel-plugin-macros@3.1.0) - jest-util: 29.7.0 - prompts: 2.4.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - create-require@1.1.1: {} cross-inspect@1.0.1: @@ -9701,6 +9870,10 @@ snapshots: dom-accessibility-api@0.6.3: {} + dom-focus-lock@1.0.4: + dependencies: + focus-lock: 0.6.8 + dom-helpers@5.2.1: dependencies: '@babel/runtime': 7.27.1 @@ -9740,6 +9913,10 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + dynamic-dedupe@0.3.0: + dependencies: + xtend: 4.0.2 + eastasianwidth@0.2.0: {} ecdsa-sig-formatter@1.0.11: @@ -10475,6 +10652,8 @@ snapshots: flatted@3.3.3: {} + focus-lock@0.6.8: {} + follow-redirects@1.15.9: {} for-each@0.3.5: @@ -10736,6 +10915,10 @@ snapshots: pkg-dir: 4.2.0 resolve-cwd: 3.0.0 + import@0.0.6: + dependencies: + optimist: 0.3.7 + imurmurhash@0.1.4: {} indent-string@4.0.0: {} @@ -11028,25 +11211,6 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(@types/node@22.15.21)(babel-plugin-macros@3.1.0): - dependencies: - '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.17.50)(typescript@5.8.3)) - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.15.21)(babel-plugin-macros@3.1.0) - exit: 0.1.2 - import-local: 3.2.0 - jest-config: 29.7.0(@types/node@22.15.21)(babel-plugin-macros@3.1.0) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - jest-config@29.7.0(@types/node@20.17.50)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.17.50)(typescript@5.8.3)): dependencies: '@babel/core': 7.27.1 @@ -11078,36 +11242,6 @@ snapshots: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@22.15.21)(babel-plugin-macros@3.1.0): - dependencies: - '@babel/core': 7.27.1 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.27.1) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0(babel-plugin-macros@3.1.0) - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 22.15.21 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - jest-diff@29.7.0: dependencies: chalk: 4.1.2 @@ -11335,18 +11469,6 @@ snapshots: - supports-color - ts-node - jest@29.7.0(@types/node@22.15.21)(babel-plugin-macros@3.1.0): - dependencies: - '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.17.50)(typescript@5.8.3)) - '@jest/types': 29.6.3 - import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@22.15.21)(babel-plugin-macros@3.1.0) - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - jiti@2.4.2: {} jose@5.10.0: {} @@ -11648,6 +11770,10 @@ snapshots: pkg-types: 1.3.1 ufo: 1.6.1 + mnemonist@0.39.5: + dependencies: + obliterator: 2.0.5 + mnemonist@0.39.8: dependencies: obliterator: 2.0.5 @@ -11801,6 +11927,10 @@ snapshots: jose: 6.0.11 oauth4webapi: 3.5.1 + optimist@0.3.7: + dependencies: + wordwrap: 0.0.3 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -12676,6 +12806,8 @@ snapshots: dependencies: ansi-regex: 6.1.0 + strip-bom@3.0.0: {} + strip-bom@4.0.0: {} strip-final-newline@2.0.0: {} @@ -12686,6 +12818,8 @@ snapshots: dependencies: min-indent: 1.0.1 + strip-json-comments@2.0.1: {} + strip-json-comments@3.1.1: {} strip-literal@2.1.1: @@ -12751,6 +12885,8 @@ snapshots: svelte-gestures@5.1.4: {} + svelte-loading-spinners@0.3.6: {} + svelte-preprocess@5.1.4(@babel/core@7.27.1)(postcss-load-config@3.1.4(postcss@8.5.3)(ts-node@10.9.2(@types/node@22.15.21)(typescript@5.6.3)))(postcss@8.5.3)(svelte@5.33.1)(typescript@5.8.3): dependencies: '@types/pug': 2.0.10 @@ -12949,6 +13085,8 @@ snapshots: tr46@0.0.3: {} + tree-kill@1.2.2: {} + ts-api-utils@2.1.0(typescript@5.8.3): dependencies: typescript: 5.8.3 @@ -12975,25 +13113,23 @@ snapshots: '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.27.1) - ts-jest@29.3.4(@babel/core@7.27.1)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.1))(jest@29.7.0(@types/node@22.15.21)(babel-plugin-macros@3.1.0))(typescript@5.8.3): + ts-node-dev@2.0.0(@types/node@20.17.50)(typescript@5.8.3): dependencies: - bs-logger: 0.2.6 - ejs: 3.1.10 - fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@22.15.21)(babel-plugin-macros@3.1.0) - jest-util: 29.7.0 - json5: 2.2.3 - lodash.memoize: 4.1.2 - make-error: 1.3.6 - semver: 7.7.2 - type-fest: 4.41.0 + chokidar: 3.6.0 + dynamic-dedupe: 0.3.0 + minimist: 1.2.8 + mkdirp: 1.0.4 + resolve: 1.22.10 + rimraf: 2.7.1 + source-map-support: 0.5.13 + tree-kill: 1.2.2 + ts-node: 10.9.2(@types/node@20.17.50)(typescript@5.8.3) + tsconfig: 7.0.0 typescript: 5.8.3 - yargs-parser: 21.1.1 - optionalDependencies: - '@babel/core': 7.27.1 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.27.1) + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + - '@types/node' ts-node@10.9.2(@types/node@20.17.50)(typescript@5.8.3): dependencies: @@ -13051,6 +13187,13 @@ snapshots: yn: 3.1.1 optional: true + tsconfig@7.0.0: + dependencies: + '@types/strip-bom': 3.0.0 + '@types/strip-json-comments': 0.0.30 + strip-bom: 3.0.0 + strip-json-comments: 2.0.1 + tslib@1.14.1: {} tslib@2.8.1: {} @@ -13067,6 +13210,8 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + tua-body-scroll-lock@1.2.1: {} + turbo-darwin-64@2.5.3: optional: true @@ -13286,6 +13431,27 @@ snapshots: - supports-color - terser + vite-node@3.1.4(@types/node@20.17.50)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0): + dependencies: + cac: 6.7.14 + debug: 4.4.1(supports-color@5.5.0) + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 6.3.5(@types/node@20.17.50)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + vite-node@3.1.4(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0): dependencies: cac: 6.7.14 @@ -13317,6 +13483,22 @@ snapshots: fsevents: 2.3.3 lightningcss: 1.30.1 + vite@6.3.5(@types/node@20.17.50)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0): + dependencies: + esbuild: 0.25.4 + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 + postcss: 8.5.3 + rollup: 4.41.0 + tinyglobby: 0.2.13 + optionalDependencies: + '@types/node': 20.17.50 + fsevents: 2.3.3 + jiti: 2.4.2 + lightningcss: 1.30.1 + tsx: 4.19.4 + yaml: 2.8.0 + vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0): dependencies: esbuild: 0.25.4 @@ -13371,6 +13553,46 @@ snapshots: - supports-color - terser + vitest@3.1.4(@types/node@20.17.50)(@vitest/browser@3.1.4)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0): + dependencies: + '@vitest/expect': 3.1.4 + '@vitest/mocker': 3.1.4(vite@6.3.5(@types/node@20.17.50)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0)) + '@vitest/pretty-format': 3.1.4 + '@vitest/runner': 3.1.4 + '@vitest/snapshot': 3.1.4 + '@vitest/spy': 3.1.4 + '@vitest/utils': 3.1.4 + chai: 5.2.0 + debug: 4.4.1(supports-color@5.5.0) + expect-type: 1.2.1 + magic-string: 0.30.17 + pathe: 2.0.3 + std-env: 3.9.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.13 + tinypool: 1.0.2 + tinyrainbow: 2.0.0 + vite: 6.3.5(@types/node@20.17.50)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0) + vite-node: 3.1.4(@types/node@20.17.50)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 20.17.50 + '@vitest/browser': 3.1.4(playwright@1.52.0)(vite@6.3.5(@types/node@20.17.50)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0))(vitest@3.1.4) + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + vitest@3.1.4(@types/node@22.15.21)(@vitest/browser@3.1.4)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0): dependencies: '@vitest/expect': 3.1.4 @@ -13476,6 +13698,8 @@ snapshots: word-wrap@1.2.5: {} + wordwrap@0.0.3: {} + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0