From 521e3acaed9ea3570c7810184a4e9e6c0580d327 Mon Sep 17 00:00:00 2001 From: Takis Kakalis <80459599+Takaros999@users.noreply.github.com> Date: Wed, 29 Apr 2026 13:35:49 -0700 Subject: [PATCH 01/12] feat(core): enforce user presence bridge contract --- js/packages/core/src/__tests__/smoke.test.ts | 1 + .../core/src/transports/native.test.ts | 47 ++++ js/packages/core/src/transports/native.ts | 17 ++ js/packages/core/src/types/index.ts | 1 - js/packages/core/src/types/result.ts | 1 + .../react/src/__tests__/hooks.test.tsx | 12 +- .../react/src/__tests__/widgets.test.tsx | 4 +- .../main/kotlin/com/worldcoin/idkit/IdKit.kt | 2 + .../kotlin/com/worldcoin/idkit/IDKitTests.kt | 10 +- rust/core/src/bridge.rs | 232 +++++++++++++++++- rust/core/src/error.rs | 4 + rust/core/src/types.rs | 8 + rust/core/src/wasm_bindings.rs | 11 + swift/Sources/IDKit/IDKit.swift | 3 + swift/Tests/IDKitTests/IDKitTests.swift | 4 +- 15 files changed, 335 insertions(+), 22 deletions(-) diff --git a/js/packages/core/src/__tests__/smoke.test.ts b/js/packages/core/src/__tests__/smoke.test.ts index 26aef3dc..b8614baa 100644 --- a/js/packages/core/src/__tests__/smoke.test.ts +++ b/js/packages/core/src/__tests__/smoke.test.ts @@ -196,6 +196,7 @@ describe("Enums", () => { expect(IDKitErrorCodes.CredentialUnavailable).toBe( "credential_unavailable", ); + expect(IDKitErrorCodes.UserPresenceFailed).toBe("user_presence_failed"); expect(IDKitErrorCodes.Timeout).toBe("timeout"); expect(IDKitErrorCodes.Cancelled).toBe("cancelled"); }); diff --git a/js/packages/core/src/transports/native.test.ts b/js/packages/core/src/transports/native.test.ts index 0fa1e8b6..a390cc24 100644 --- a/js/packages/core/src/transports/native.test.ts +++ b/js/packages/core/src/transports/native.test.ts @@ -106,6 +106,53 @@ describe("native transport request lifecycle", () => { expect(completion.success).toBe(true); }); + it("defaults missing user_presence_completed to false on success", async () => { + const req = createNativeRequest({ payload: 1 }, baseConfig, {}, ""); + activeRequest = req; + + const completionPromise = req.pollUntilCompletion({ timeout: 1000 }); + + miniKitHandlers["miniapp-verify-action"]?.({ + status: "success", + protocol_version: "3.0", + verification_level: "orb", + signal_hash: "0xabc", + proof: "0x01", + merkle_root: "0x02", + nullifier_hash: "0x03", + }); + + const completion = await completionPromise; + expect(completion.success).toBe(true); + if (completion.success) { + expect(completion.result.user_presence_completed).toBe(false); + } + }); + + it("preserves completed user presence on success", async () => { + const req = createNativeRequest({ payload: 1 }, baseConfig, {}, ""); + activeRequest = req; + + const completionPromise = req.pollUntilCompletion({ timeout: 1000 }); + + miniKitHandlers["miniapp-verify-action"]?.({ + status: "success", + user_presence_completed: true, + protocol_version: "3.0", + verification_level: "orb", + signal_hash: "0xabc", + proof: "0x01", + merkle_root: "0x02", + nullifier_hash: "0x03", + }); + + const completion = await completionPromise; + expect(completion.success).toBe(true); + if (completion.success) { + expect(completion.result.user_presence_completed).toBe(true); + } + }); + it("uses per-identifier signal hashes when response omits signal_hash", async () => { const signalHashes = { proof_of_human: hashSignal("poh-signal"), diff --git a/js/packages/core/src/transports/native.ts b/js/packages/core/src/transports/native.ts index 4d56b003..fcee9fb8 100644 --- a/js/packages/core/src/transports/native.ts +++ b/js/packages/core/src/transports/native.ts @@ -167,6 +167,8 @@ class NativeIDKitRequest implements IDKitRequest { return; } + const userPresenceCompleted = getUserPresenceCompleted(responsePayload); + this.complete({ success: true, result: nativeResultToIDKitResult( @@ -174,6 +176,7 @@ class NativeIDKitRequest implements IDKitRequest { config, signalHashes, legacySignalHash, + userPresenceCompleted, ), }); }; @@ -349,6 +352,7 @@ function nativeResultToIDKitResult( config: BuilderConfig, signalHashes: Record, legacySignalHash: string, + userPresenceCompleted: boolean, ): IDKitResult { const p = payload as Record; const rpNonce = config.rp_context?.nonce ?? ""; @@ -372,6 +376,7 @@ function nativeResultToIDKitResult( issuer_schema_id: item.issuer_schema_id, expires_at_min: item.expires_at_min, })), + user_presence_completed: userPresenceCompleted, environment: config.environment ?? "production", } satisfies IDKitResultSession; } @@ -389,6 +394,7 @@ function nativeResultToIDKitResult( issuer_schema_id: item.issuer_schema_id, expires_at_min: item.expires_at_min, })), + user_presence_completed: userPresenceCompleted, environment: config.environment ?? "production", } satisfies IDKitResultV4; } @@ -414,6 +420,7 @@ function nativeResultToIDKitResult( merkle_root: v.merkle_root, nullifier: v.nullifier_hash, })), + user_presence_completed: userPresenceCompleted, environment: config.environment ?? "production", } satisfies IDKitResultV3; } @@ -435,6 +442,16 @@ function nativeResultToIDKitResult( nullifier: p.nullifier_hash, }, ], + user_presence_completed: userPresenceCompleted, environment: config.environment ?? "production", } satisfies IDKitResultV3; } + +function getUserPresenceCompleted(payload: unknown): boolean { + const p = payload as Record; + return ( + p?.user_presence_completed === true || + (p?.proof_response as Record | undefined) + ?.user_presence_completed === true + ); +} diff --git a/js/packages/core/src/types/index.ts b/js/packages/core/src/types/index.ts index 5a543a1e..287f5d08 100644 --- a/js/packages/core/src/types/index.ts +++ b/js/packages/core/src/types/index.ts @@ -1,3 +1,2 @@ -export * from "./bridge"; export * from "./config"; export * from "./result"; diff --git a/js/packages/core/src/types/result.ts b/js/packages/core/src/types/result.ts index cb6aec21..e6b35a09 100644 --- a/js/packages/core/src/types/result.ts +++ b/js/packages/core/src/types/result.ts @@ -40,6 +40,7 @@ export enum IDKitErrorCodes { ConnectionFailed = "connection_failed", MaxVerificationsReached = "max_verifications_reached", FailedByHostApp = "failed_by_host_app", + UserPresenceFailed = "user_presence_failed", GenericError = "generic_error", // Client-side errors Timeout = "timeout", diff --git a/js/packages/react/src/__tests__/hooks.test.tsx b/js/packages/react/src/__tests__/hooks.test.tsx index 7c002a37..381cd24e 100644 --- a/js/packages/react/src/__tests__/hooks.test.tsx +++ b/js/packages/react/src/__tests__/hooks.test.tsx @@ -152,7 +152,7 @@ describe("request/session hooks", () => { useIDKitSession({ app_id: "app_test", rp_context: baseRpContext, - constraints: { type: "All", children: [] }, + constraints: { all: [] }, }), ); @@ -194,7 +194,7 @@ describe("request/session hooks", () => { app_id: "app_test", rp_context: baseRpContext, existing_session_id: SESSION_ID_2, - preset: { type: "OrbLegacy" }, + constraints: { all: [] }, }), ); @@ -276,7 +276,7 @@ describe("request/session hooks", () => { app_id: "app_test", rp_context: baseRpContext, return_to: "idkit://callback?step=create", - constraints: { type: "All", children: [] }, + constraints: { all: [] }, }), ); @@ -317,7 +317,7 @@ describe("request/session hooks", () => { rp_context: baseRpContext, existing_session_id: validSessionId, return_to: "idkit://callback?step=prove", - constraints: { type: "All", children: [] }, + constraints: { all: [] }, }), ); @@ -346,7 +346,7 @@ describe("request/session hooks", () => { app_id: "app_test", rp_context: baseRpContext, existing_session_id: " " as unknown as `session_${string}`, - constraints: { type: "All", children: [] }, + constraints: { all: [] }, }), ); @@ -367,7 +367,7 @@ describe("request/session hooks", () => { app_id: "app_test", rp_context: baseRpContext, existing_session_id: "session_2" as `session_${string}`, - constraints: { type: "All", children: [] }, + constraints: { all: [] }, }), ); diff --git a/js/packages/react/src/__tests__/widgets.test.tsx b/js/packages/react/src/__tests__/widgets.test.tsx index 522b573c..8652e3ab 100644 --- a/js/packages/react/src/__tests__/widgets.test.tsx +++ b/js/packages/react/src/__tests__/widgets.test.tsx @@ -76,7 +76,7 @@ function createRequestProps( allow_legacy_proofs: false, preset: { type: "OrbLegacy" }, ...overrides, - }; + } as IDKitRequestWidgetProps; } function createSessionProps( @@ -88,7 +88,7 @@ function createSessionProps( onSuccess: vi.fn(), app_id: "app_test", rp_context: baseRpContext, - preset: { type: "OrbLegacy" }, + constraints: { all: [] }, ...overrides, }; } diff --git a/kotlin/bindings/src/main/kotlin/com/worldcoin/idkit/IdKit.kt b/kotlin/bindings/src/main/kotlin/com/worldcoin/idkit/IdKit.kt index 1c94943e..24e68a3b 100644 --- a/kotlin/bindings/src/main/kotlin/com/worldcoin/idkit/IdKit.kt +++ b/kotlin/bindings/src/main/kotlin/com/worldcoin/idkit/IdKit.kt @@ -52,6 +52,7 @@ enum class IDKitErrorCode(val rawValue: String) { CONNECTION_FAILED("connection_failed"), MAX_VERIFICATIONS_REACHED("max_verifications_reached"), FAILED_BY_HOST_APP("failed_by_host_app"), + USER_PRESENCE_FAILED("user_presence_failed"), GENERIC_ERROR("generic_error"), TIMEOUT("timeout"), CANCELLED("cancelled"); @@ -69,6 +70,7 @@ enum class IDKitErrorCode(val rawValue: String) { AppError.CONNECTION_FAILED -> CONNECTION_FAILED AppError.MAX_VERIFICATIONS_REACHED -> MAX_VERIFICATIONS_REACHED AppError.FAILED_BY_HOST_APP -> FAILED_BY_HOST_APP + AppError.USER_PRESENCE_FAILED -> USER_PRESENCE_FAILED AppError.GENERIC_ERROR -> GENERIC_ERROR } } diff --git a/kotlin/bindings/src/test/kotlin/com/worldcoin/idkit/IDKitTests.kt b/kotlin/bindings/src/test/kotlin/com/worldcoin/idkit/IDKitTests.kt index 17ee9974..661f6610 100644 --- a/kotlin/bindings/src/test/kotlin/com/worldcoin/idkit/IDKitTests.kt +++ b/kotlin/bindings/src/test/kotlin/com/worldcoin/idkit/IDKitTests.kt @@ -16,7 +16,10 @@ import uniffi.idkit_core.RpContext import uniffi.idkit_core.StatusWrapper class IDKitTests { - private fun sampleResult(sessionId: String? = null): IDKitResult = + private fun sampleResult( + sessionId: String? = null, + userPresenceCompleted: Boolean = false, + ): IDKitResult = IDKitResult( protocolVersion = "4.0", nonce = "0x1234", @@ -24,6 +27,7 @@ class IDKitTests { actionDescription = "Sample action", sessionId = sessionId, responses = emptyList(), + userPresenceCompleted = userPresenceCompleted, environment = "production", ) @@ -89,6 +93,10 @@ class IDKitTests { IDKitStatus.Failed(IDKitErrorCode.INVALID_NETWORK), IDKitRequest.mapStatus(StatusWrapper.Failed(AppError.INVALID_NETWORK)), ) + assertEquals( + IDKitStatus.Failed(IDKitErrorCode.USER_PRESENCE_FAILED), + IDKitRequest.mapStatus(StatusWrapper.Failed(AppError.USER_PRESENCE_FAILED)), + ) assertEquals( IDKitStatus.NetworkingError(IDKitErrorCode.CONNECTION_FAILED), IDKitRequest.mapStatus(StatusWrapper.NetworkingError(AppError.CONNECTION_FAILED)), diff --git a/rust/core/src/bridge.rs b/rust/core/src/bridge.rs index 460306d5..7bb65249 100644 --- a/rust/core/src/bridge.rs +++ b/rust/core/src/bridge.rs @@ -12,7 +12,10 @@ use crate::{ }; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use world_id_primitives::{FieldElement, ProofRequest, SessionId}; +use world_id_primitives::{ + FieldElement, ProofRequest, ProofResponse, RequestVersion, + ResponseItem as ProtocolResponseItem, SessionId, +}; #[cfg(feature = "native-crypto")] use crate::crypto::CryptoKey; @@ -116,6 +119,9 @@ struct BridgeRequestPayload { /// - `false`: Only accept v4 proofs. Use after migration cutoff or for new apps. allow_legacy_proofs: bool, + /// Whether World App should require a user-presence check before verification. + require_user_presence: bool, + /// Environment for the bridge request environment: Environment, } @@ -219,15 +225,68 @@ enum BridgeResponse { Error { error_code: AppError }, /// World ID 4.0 protocol response - ResponseV2(world_id_primitives::ProofResponse), + ResponseV2(BridgeResponseV2), /// Multi-credential legacy: bridge sends v3 proofs via `legacy_responses` MultiLegacyResponse { legacy_responses: Vec, + #[serde(default)] + user_presence_completed: bool, }, /// V1 legacy (old World App 3.0, single credential) - ResponseV1(BridgeResponseV1), + ResponseV1(BridgeResponseV1WithMetadata), +} + +#[derive(Debug, Deserialize)] +struct BridgeResponseV2 { + /// The response id references request id + id: String, + /// Version corresponding to request version + version: RequestVersion, + /// Optional session identifier for session proofs + #[serde(default)] + session_id: Option, + /// Protocol-level proof response error + #[serde(default)] + error: Option, + /// Per-credential proof responses + responses: Vec, + /// Whether World App completed the requested user-presence check. + #[serde(default)] + user_presence_completed: bool, +} + +impl BridgeResponseV2 { + fn into_proof_response(self) -> ProofResponse { + ProofResponse { + id: self.id, + version: self.version, + session_id: self.session_id, + error: self.error, + responses: self.responses, + } + } +} + +#[derive(Debug, Deserialize)] +struct BridgeResponseV1WithMetadata { + #[serde(flatten)] + response: BridgeResponseV1, + /// Whether World App completed the requested user-presence check. + #[serde(default)] + user_presence_completed: bool, +} + +fn user_presence_failure_status( + require_user_presence: bool, + user_presence_completed: bool, +) -> Option { + if require_user_presence && !user_presence_completed { + Some(Status::Failed(AppError::UserPresenceFailed)) + } else { + None + } } /// Status of a verification request @@ -259,6 +318,7 @@ pub struct BridgeConnectionParams { pub legacy_signal: String, pub bridge_url: Option, pub allow_legacy_proofs: bool, + pub require_user_presence: bool, /// Optional override for the connect base URL (e.g., for staging environments) pub override_connect_base_url: Option, /// Optional deep-link callback URL appended as `return_to` on the connector URL @@ -346,6 +406,8 @@ pub struct BridgeConnection { return_to: Option, /// Resolved environment for this connection environment: Environment, + /// Whether a successful response must prove user presence was completed. + require_user_presence: bool, } /// Builds a `BridgeRequestPayload` from params without connecting to the bridge. @@ -444,6 +506,7 @@ pub fn build_request_payload( signal: legacy_signal_hash, timestamp, allow_legacy_proofs: params.allow_legacy_proofs, + require_user_presence: params.require_user_presence, environment: params.environment.unwrap_or_default(), }; @@ -588,6 +651,7 @@ impl BridgeConnection { override_connect_base_url: params.override_connect_base_url, return_to: params.return_to, environment: params.environment.unwrap_or_default(), + require_user_presence: params.require_user_presence, }) } @@ -666,7 +730,17 @@ impl BridgeConnection { match bridge_response { BridgeResponse::Error { error_code } => Ok(Status::Failed(error_code)), - BridgeResponse::ResponseV2(proof_response) => { + BridgeResponse::ResponseV2(response) => { + let user_presence_completed = response.user_presence_completed; + if let Some(status) = user_presence_failure_status( + self.require_user_presence, + user_presence_completed, + ) { + return Ok(status); + } + + let proof_response = response.into_proof_response(); + // Check for protocol-level error if proof_response.error.is_some() { return Ok(Status::Failed(AppError::GenericError)); @@ -696,6 +770,7 @@ impl BridgeConnection { .to_owned(), self.action_description.clone(), responses, + user_presence_completed, self.environment.as_ref(), ) } else { @@ -705,12 +780,23 @@ impl BridgeConnection { self.action.clone(), self.action_description.clone(), responses, + user_presence_completed, self.environment.as_ref(), ) }, )) } - BridgeResponse::MultiLegacyResponse { legacy_responses } => { + BridgeResponse::MultiLegacyResponse { + legacy_responses, + user_presence_completed, + } => { + if let Some(status) = user_presence_failure_status( + self.require_user_presence, + user_presence_completed, + ) { + return Ok(status); + } + let responses: Vec = legacy_responses .into_iter() .map(|item| { @@ -729,20 +815,30 @@ impl BridgeConnection { self.action.clone(), self.action_description.clone(), responses, + user_presence_completed, self.environment.as_ref(), ))) } BridgeResponse::ResponseV1(response) => { + if let Some(status) = user_presence_failure_status( + self.require_user_presence, + response.user_presence_completed, + ) { + return Ok(status); + } + // V1 responses are always protocol 3.0 // For V1 we don't have identifier, use verification_level as key let signal_hash = self.cached_signal_hashes.legacy(); - let item = response.into_response_item(signal_hash); + let user_presence_completed = response.user_presence_completed; + let item = response.response.into_response_item(signal_hash); Ok(Status::Confirmed(IDKitResult::new( "3.0", self.nonce.clone(), self.action.clone(), self.action_description.clone(), vec![item], + user_presence_completed, self.environment.as_ref(), ))) } @@ -866,6 +962,7 @@ impl IDKitConfig { legacy_signal: String::new(), bridge_url, allow_legacy_proofs: config.allow_legacy_proofs, + require_user_presence: false, override_connect_base_url: config.override_connect_base_url.clone(), return_to: config.return_to.clone(), environment: config.environment, @@ -890,6 +987,7 @@ impl IDKitConfig { legacy_signal: String::new(), bridge_url, allow_legacy_proofs: false, + require_user_presence: false, override_connect_base_url: config.override_connect_base_url.clone(), return_to: config.return_to.clone(), environment: config.environment, @@ -916,6 +1014,7 @@ impl IDKitConfig { legacy_signal: String::new(), bridge_url, allow_legacy_proofs: false, + require_user_presence: false, override_connect_base_url: config.override_connect_base_url.clone(), return_to: config.return_to.clone(), environment: config.environment, @@ -960,6 +1059,7 @@ impl IDKitConfig { legacy_signal: legacy_signal.unwrap_or_default(), bridge_url, allow_legacy_proofs: config.allow_legacy_proofs, + require_user_presence: false, override_connect_base_url: config.override_connect_base_url.clone(), return_to: config.return_to.clone(), environment: config.environment, @@ -983,6 +1083,7 @@ impl IDKitConfig { legacy_signal: legacy_signal.unwrap_or_default(), bridge_url, allow_legacy_proofs: false, + require_user_presence: false, override_connect_base_url: config.override_connect_base_url.clone(), return_to: config.return_to.clone(), environment: config.environment, @@ -1008,6 +1109,7 @@ impl IDKitConfig { legacy_signal: legacy_signal.unwrap_or_default(), bridge_url, allow_legacy_proofs: false, + require_user_presence: false, override_connect_base_url: config.override_connect_base_url.clone(), return_to: config.return_to.clone(), environment: config.environment, @@ -1319,6 +1421,7 @@ mod tests { timestamp: None, proof_request: Some(proof_request), allow_legacy_proofs: false, + require_user_presence: false, environment: Environment::Production, }; @@ -1329,6 +1432,7 @@ mod tests { assert!(json.contains("proof_request")); assert!(json.contains("rp_1234567890abcdef")); assert!(json.contains("allow_legacy_proofs")); + assert!(json.contains("require_user_presence")); } #[test] @@ -1442,10 +1546,10 @@ mod tests { let response: BridgeResponse = serde_json::from_str(json).unwrap(); match response { BridgeResponse::ResponseV1(v1) => { - assert_eq!(v1.verification_level, VerificationLevel::Device); - assert_eq!(v1.proof, "0xproof"); - assert_eq!(v1.merkle_root, "0xroot"); - assert_eq!(v1.nullifier_hash, "0xnull"); + assert_eq!(v1.response.verification_level, VerificationLevel::Device); + assert_eq!(v1.response.proof, "0xproof"); + assert_eq!(v1.response.merkle_root, "0xroot"); + assert_eq!(v1.response.nullifier_hash, "0xnull"); } other => panic!("Expected ResponseV1, got: {other:?}"), } @@ -1465,12 +1569,57 @@ mod tests { let response: BridgeResponse = serde_json::from_str(json).unwrap(); match response { BridgeResponse::ResponseV1(v1) => { - assert_eq!(v1.verification_level, VerificationLevel::Orb); + assert_eq!(v1.response.verification_level, VerificationLevel::Orb); } other => panic!("Expected ResponseV1, got: {other:?}"), } } + #[test] + fn test_bridge_response_v2_user_presence_defaults_false() { + let json = r#"{ + "id": "request-id", + "version": 1, + "responses": [] + }"#; + + let response: BridgeResponse = serde_json::from_str(json).unwrap(); + match response { + BridgeResponse::ResponseV2(v2) => { + assert!(!v2.user_presence_completed); + } + other => panic!("Expected ResponseV2, got: {other:?}"), + } + } + + #[test] + fn test_bridge_response_v2_user_presence_explicit_true() { + let json = r#"{ + "id": "request-id", + "version": 1, + "responses": [], + "user_presence_completed": true + }"#; + + let response: BridgeResponse = serde_json::from_str(json).unwrap(); + match response { + BridgeResponse::ResponseV2(v2) => { + assert!(v2.user_presence_completed); + } + other => panic!("Expected ResponseV2, got: {other:?}"), + } + } + + #[test] + fn test_required_user_presence_fails_when_not_completed() { + assert_eq!( + user_presence_failure_status(true, false), + Some(Status::Failed(AppError::UserPresenceFailed)) + ); + assert_eq!(user_presence_failure_status(true, true), None); + assert_eq!(user_presence_failure_status(false, false), None); + } + #[test] fn test_bridge_response_error_deserialization() { let json = r#"{"error_code": "user_rejected"}"#; @@ -1507,6 +1656,7 @@ mod tests { legacy_signal: legacy_signal.unwrap_or_default(), bridge_url: None, allow_legacy_proofs: false, + require_user_presence: false, override_connect_base_url: None, return_to: None, @@ -1545,6 +1695,7 @@ mod tests { legacy_signal: legacy_signal.unwrap_or_default(), bridge_url: None, allow_legacy_proofs: false, + require_user_presence: false, override_connect_base_url: None, return_to: None, @@ -1607,6 +1758,7 @@ mod tests { legacy_signal: "test-signal".to_string(), bridge_url: None, allow_legacy_proofs: false, + require_user_presence: false, override_connect_base_url: None, return_to: None, @@ -1652,6 +1804,7 @@ mod tests { legacy_signal: "test-signal".to_string(), bridge_url: None, allow_legacy_proofs: false, + require_user_presence: false, override_connect_base_url: None, return_to: None, environment: None, @@ -1660,10 +1813,65 @@ mod tests { // native=true includes timestamp let payload = build_request_payload(¶ms, true).unwrap(); assert_eq!(payload["timestamp"], "2023-11-14T22:13:20Z"); + assert_eq!(payload["require_user_presence"], serde_json::json!(false)); // native=false omits timestamp let payload_bridge = build_request_payload(¶ms, false).unwrap(); assert!(payload_bridge.get("timestamp").is_none()); + assert_eq!( + payload_bridge["require_user_presence"], + serde_json::json!(false) + ); + } + + #[test] + fn test_build_request_payload_serializes_user_presence_requirement() { + let app_id = AppId::new("app_test").unwrap(); + let sig = "0x".to_string() + &"00".repeat(64) + "1b"; + let rp_context = RpContext::new( + "rp_1234567890abcdef", + "0x0000000000000000000000000000000000000000000000000000000000000001", + 1_700_000_000, + 1_700_003_600, + &sig, + ) + .unwrap(); + + let item = CredentialRequest::new( + CredentialType::ProofOfHuman, + Some(Signal::from_string("test")), + ); + let constraints = ConstraintNode::item(item); + + let params = BridgeConnectionParams { + app_id, + kind: RequestKind::Uniqueness { + action: "my-action".to_string(), + }, + constraints: Some(constraints), + rp_context, + action_description: None, + legacy_verification_level: VerificationLevel::Orb, + legacy_signal: "test-signal".to_string(), + bridge_url: None, + allow_legacy_proofs: false, + require_user_presence: true, + override_connect_base_url: None, + return_to: None, + environment: None, + }; + + let bridge_payload = build_request_payload(¶ms, false).unwrap(); + assert_eq!( + bridge_payload["require_user_presence"], + serde_json::json!(true) + ); + + let native_payload = build_request_payload(¶ms, true).unwrap(); + assert_eq!( + native_payload["require_user_presence"], + serde_json::json!(true) + ); } #[test] @@ -1697,6 +1905,7 @@ mod tests { legacy_signal: address.to_string(), bridge_url: None, allow_legacy_proofs: false, + require_user_presence: false, override_connect_base_url: None, return_to: None, @@ -1734,6 +1943,7 @@ mod tests { override_connect_base_url: None, return_to, environment: Environment::Production, + require_user_presence: false, } } diff --git a/rust/core/src/error.rs b/rust/core/src/error.rs index d974dcd8..02a63faf 100644 --- a/rust/core/src/error.rs +++ b/rust/core/src/error.rs @@ -108,6 +108,10 @@ pub enum AppError { #[error("Verification failed by host app")] FailedByHostApp, + /// User presence check failed or was not completed + #[error("User presence check failed")] + UserPresenceFailed, + /// Generic error #[error("An error occurred")] #[serde(other)] diff --git a/rust/core/src/types.rs b/rust/core/src/types.rs index 3809fd2e..33fa5693 100644 --- a/rust/core/src/types.rs +++ b/rust/core/src/types.rs @@ -592,6 +592,9 @@ pub struct IDKitResult { /// Array of credential responses (always successful - errors at `BridgeResponse` level) pub responses: Vec, + /// Whether World App completed the requested user-presence check. + pub user_presence_completed: bool, + /// The environment used for this request ("production" or "staging") pub environment: String, } @@ -605,6 +608,7 @@ impl IDKitResult { action: Option, action_description: Option, responses: Vec, + user_presence_completed: bool, environment: impl Into, ) -> Self { Self { @@ -614,6 +618,7 @@ impl IDKitResult { action_description, session_id: None, responses, + user_presence_completed, environment: environment.into(), } } @@ -626,6 +631,7 @@ impl IDKitResult { session_id: String, action_description: Option, responses: Vec, + user_presence_completed: bool, environment: impl Into, ) -> Self { Self { @@ -635,6 +641,7 @@ impl IDKitResult { action_description, session_id: Some(session_id), responses, + user_presence_completed, environment: environment.into(), } } @@ -1227,6 +1234,7 @@ mod tests { None, None, responses, + false, "production", ); assert_eq!(result.protocol_version, "3.0"); diff --git a/rust/core/src/wasm_bindings.rs b/rust/core/src/wasm_bindings.rs index d7998748..73058f67 100644 --- a/rust/core/src/wasm_bindings.rs +++ b/rust/core/src/wasm_bindings.rs @@ -542,6 +542,7 @@ impl IDKitConfigWasm { legacy_signal: String::new(), bridge_url, allow_legacy_proofs: *allow_legacy_proofs, + require_user_presence: false, override_connect_base_url: override_connect_base_url.clone(), return_to: return_to.clone(), @@ -579,6 +580,7 @@ impl IDKitConfigWasm { legacy_signal: String::new(), bridge_url, allow_legacy_proofs: false, + require_user_presence: false, override_connect_base_url: override_connect_base_url.clone(), return_to: return_to.clone(), @@ -619,6 +621,7 @@ impl IDKitConfigWasm { legacy_signal: String::new(), bridge_url, allow_legacy_proofs: false, + require_user_presence: false, override_connect_base_url: override_connect_base_url.clone(), return_to: return_to.clone(), @@ -967,6 +970,7 @@ pub fn request( /// `session_`. #[must_use] #[wasm_bindgen(js_name = createSession)] +#[allow(clippy::too_many_arguments)] pub fn create_session( app_id: String, rp_context: RpContextWasm, @@ -1203,6 +1207,8 @@ export interface IDKitResultV3 { action_description?: string; /** Array of V3 credential responses */ responses: ResponseItemV3[]; + /** Whether World App completed the requested user-presence check. */ + user_presence_completed: boolean; /** The environment used for this request ("production" or "staging") */ environment: string; } @@ -1219,6 +1225,8 @@ export interface IDKitResultV4 { action_description?: string; /** Array of V4 credential responses */ responses: ResponseItemV4[]; + /** Whether World App completed the requested user-presence check. */ + user_presence_completed: boolean; /** The environment used for this request ("production" or "staging") */ environment: string; } @@ -1235,6 +1243,8 @@ export interface IDKitResultSession { session_id: `session_${string}`; /** Array of session credential responses */ responses: ResponseItemSession[]; + /** Whether World App completed the requested user-presence check. */ + user_presence_completed: boolean; /** The environment used for this request ("production" or "staging") */ environment: string; } @@ -1288,6 +1298,7 @@ export type IDKitErrorCode = | "connection_failed" | "max_verifications_reached" | "failed_by_host_app" + | "user_presence_failed" | "generic_error"; /** Status returned from pollForStatus() */ diff --git a/swift/Sources/IDKit/IDKit.swift b/swift/Sources/IDKit/IDKit.swift index 5bc68316..140d0a27 100644 --- a/swift/Sources/IDKit/IDKit.swift +++ b/swift/Sources/IDKit/IDKit.swift @@ -97,6 +97,7 @@ public enum IDKitErrorCode: String, Equatable { case connectionFailed = "connection_failed" case maxVerificationsReached = "max_verifications_reached" case failedByHostApp = "failed_by_host_app" + case userPresenceFailed = "user_presence_failed" case genericError = "generic_error" case timeout = "timeout" case cancelled = "cancelled" @@ -125,6 +126,8 @@ public enum IDKitErrorCode: String, Equatable { .maxVerificationsReached case .failedByHostApp: .failedByHostApp + case .userPresenceFailed: + .userPresenceFailed case .genericError: .genericError } diff --git a/swift/Tests/IDKitTests/IDKitTests.swift b/swift/Tests/IDKitTests/IDKitTests.swift index e094d48c..bf659e46 100644 --- a/swift/Tests/IDKitTests/IDKitTests.swift +++ b/swift/Tests/IDKitTests/IDKitTests.swift @@ -17,7 +17,7 @@ private actor StatusPoller { } } -private func sampleResult(sessionId: String? = nil) -> IDKitResult { +private func sampleResult(sessionId: String? = nil, userPresenceCompleted: Bool = false) -> IDKitResult { IDKitResult( protocolVersion: "4.0", nonce: "0x1234", @@ -25,6 +25,7 @@ private func sampleResult(sessionId: String? = nil) -> IDKitResult { actionDescription: "Sample action", sessionId: sessionId, responses: [], + userPresenceCompleted: userPresenceCompleted, environment: "production" ) } @@ -82,6 +83,7 @@ func statusMapping() { #expect(IDKitRequest.mapStatus(.awaitingConfirmation) == .awaitingConfirmation) #expect(IDKitRequest.mapStatus(.confirmed(result: result)) == .confirmed(result)) #expect(IDKitRequest.mapStatus(.failed(error: .invalidNetwork)) == .failed(.invalidNetwork)) + #expect(IDKitRequest.mapStatus(.failed(error: .userPresenceFailed)) == .failed(.userPresenceFailed)) #expect(IDKitRequest.mapStatus(.networkingError(error: .connectionFailed)) == .networkingError(.connectionFailed)) } From af468d6fba9486be8ac9b2aa3bfb4bc552a9a89b Mon Sep 17 00:00:00 2001 From: Takis Kakalis <80459599+Takaros999@users.noreply.github.com> Date: Wed, 29 Apr 2026 13:36:58 -0700 Subject: [PATCH 02/12] feat(sdk): expose require user presence config --- js/packages/core/src/__tests__/smoke.test.ts | 30 +++++++++++ js/packages/core/src/request.ts | 6 +++ .../core/src/transports/native.test.ts | 34 +++++++++++- js/packages/core/src/transports/native.ts | 9 ++++ js/packages/core/src/types/config.ts | 5 ++ .../react/src/__tests__/hooks.test.tsx | 41 ++++++++++++++ .../react/src/hooks/useIDKitRequest.ts | 1 + .../react/src/hooks/useIDKitSession.ts | 2 + .../kotlin/com/worldcoin/idkit/IDKitTests.kt | 2 + rust/core/src/bridge.rs | 49 ++++++++++++++--- rust/core/src/wasm_bindings.rs | 53 +++++++++++++++++-- swift/Tests/IDKitTests/IDKitTests.swift | 2 + 12 files changed, 224 insertions(+), 10 deletions(-) diff --git a/js/packages/core/src/__tests__/smoke.test.ts b/js/packages/core/src/__tests__/smoke.test.ts index b8614baa..6c3a4ea5 100644 --- a/js/packages/core/src/__tests__/smoke.test.ts +++ b/js/packages/core/src/__tests__/smoke.test.ts @@ -105,6 +105,35 @@ describe("IDKitRequest API", () => { ).toThrow("rp_context is required"); }); + it("should default require_user_presence to false in request config", () => { + const builder = IDKit.request({ + app_id: "app_staging_test", + action: "test-action", + rp_context: TEST_SESSION_CONFIG.rp_context, + allow_legacy_proofs: false, + }); + + expect((builder as any).config.require_user_presence).toBe(false); + }); + + it("should preserve require_user_presence in request and session configs", () => { + const requestBuilder = IDKit.request({ + app_id: "app_staging_test", + action: "test-action", + rp_context: TEST_SESSION_CONFIG.rp_context, + allow_legacy_proofs: false, + require_user_presence: true, + }); + + const sessionBuilder = IDKit.createSession({ + ...TEST_SESSION_CONFIG, + require_user_presence: true, + }); + + expect((requestBuilder as any).config.require_user_presence).toBe(true); + expect((sessionBuilder as any).config.require_user_presence).toBe(true); + }); + it("should reject malformed session_id values in proveSession", () => { expect(() => IDKit.proveSession( @@ -173,6 +202,7 @@ describe("IDKitRequest API", () => { null, null, true, + false, null, null, "production", diff --git a/js/packages/core/src/request.ts b/js/packages/core/src/request.ts index 303eacc5..aeb878a8 100644 --- a/js/packages/core/src/request.ts +++ b/js/packages/core/src/request.ts @@ -372,6 +372,7 @@ function createWasmBuilderFromConfig( config.action_description ?? null, config.bridge_url ?? null, config.allow_legacy_proofs ?? false, + config.require_user_presence ?? false, config.override_connect_base_url ?? null, config.return_to ?? null, config.environment ?? null, @@ -385,6 +386,7 @@ function createWasmBuilderFromConfig( rpContext, config.action_description ?? null, config.bridge_url ?? null, + config.require_user_presence ?? false, config.override_connect_base_url ?? null, config.return_to ?? null, config.environment ?? null, @@ -397,6 +399,7 @@ function createWasmBuilderFromConfig( rpContext, config.action_description ?? null, config.bridge_url ?? null, + config.require_user_presence ?? false, config.override_connect_base_url ?? null, config.return_to ?? null, config.environment ?? null, @@ -618,6 +621,7 @@ function createRequest(config: IDKitRequestConfig): IDKitBuilder { bridge_url: config.bridge_url, return_to: config.return_to, allow_legacy_proofs: config.allow_legacy_proofs, + require_user_presence: config.require_user_presence ?? false, override_connect_base_url: config.override_connect_base_url, environment: config.environment, }); @@ -667,6 +671,7 @@ function createSession(config: IDKitSessionConfig): IDKitBuilder { action_description: config.action_description, bridge_url: config.bridge_url, return_to: config.return_to, + require_user_presence: config.require_user_presence ?? false, override_connect_base_url: config.override_connect_base_url, environment: config.environment, }); @@ -728,6 +733,7 @@ function proveSession( action_description: config.action_description, bridge_url: config.bridge_url, return_to: config.return_to, + require_user_presence: config.require_user_presence ?? false, override_connect_base_url: config.override_connect_base_url, environment: config.environment, }); diff --git a/js/packages/core/src/transports/native.test.ts b/js/packages/core/src/transports/native.test.ts index a390cc24..7c88a9e4 100644 --- a/js/packages/core/src/transports/native.test.ts +++ b/js/packages/core/src/transports/native.test.ts @@ -129,8 +129,40 @@ describe("native transport request lifecycle", () => { } }); + it("fails when user presence is required but not completed", async () => { + const req = createNativeRequest( + { payload: 1 }, + { ...baseConfig, require_user_presence: true }, + {}, + "", + ); + activeRequest = req; + + const completionPromise = req.pollUntilCompletion({ timeout: 1000 }); + + miniKitHandlers["miniapp-verify-action"]?.({ + status: "success", + protocol_version: "3.0", + verification_level: "orb", + signal_hash: "0xabc", + proof: "0x01", + merkle_root: "0x02", + nullifier_hash: "0x03", + }); + + await expect(completionPromise).resolves.toEqual({ + success: false, + error: IDKitErrorCodes.UserPresenceFailed, + }); + }); + it("preserves completed user presence on success", async () => { - const req = createNativeRequest({ payload: 1 }, baseConfig, {}, ""); + const req = createNativeRequest( + { payload: 1 }, + { ...baseConfig, require_user_presence: true }, + {}, + "", + ); activeRequest = req; const completionPromise = req.pollUntilCompletion({ timeout: 1000 }); diff --git a/js/packages/core/src/transports/native.ts b/js/packages/core/src/transports/native.ts index fcee9fb8..b0b031d3 100644 --- a/js/packages/core/src/transports/native.ts +++ b/js/packages/core/src/transports/native.ts @@ -76,6 +76,7 @@ export interface BuilderConfig { bridge_url?: string; return_to?: string; allow_legacy_proofs?: boolean; + require_user_presence?: boolean; override_connect_base_url?: string; environment?: string; } @@ -169,6 +170,14 @@ class NativeIDKitRequest implements IDKitRequest { const userPresenceCompleted = getUserPresenceCompleted(responsePayload); + if (config.require_user_presence === true && !userPresenceCompleted) { + this.complete({ + success: false, + error: IDKitErrorCodes.UserPresenceFailed, + }); + return; + } + this.complete({ success: true, result: nativeResultToIDKitResult( diff --git a/js/packages/core/src/types/config.ts b/js/packages/core/src/types/config.ts index 0fa77403..994ae987 100644 --- a/js/packages/core/src/types/config.ts +++ b/js/packages/core/src/types/config.ts @@ -58,6 +58,9 @@ export type IDKitRequestConfig = { */ allow_legacy_proofs: boolean; + /** Require World App to perform a user-presence check before verification. Defaults to false. */ + require_user_presence?: boolean; + /** Optional override for the connect base URL (e.g., for staging environments) */ override_connect_base_url?: string; @@ -84,6 +87,8 @@ export type IDKitSessionConfig = { bridge_url?: string; /** Optional deep-link callback URL appended as `return_to` on the connector URL. */ return_to?: string; + /** Require World App to perform a user-presence check before verification. Defaults to false. */ + require_user_presence?: boolean; /** Optional override for the connect base URL (e.g., for staging environments) */ override_connect_base_url?: string; diff --git a/js/packages/react/src/__tests__/hooks.test.tsx b/js/packages/react/src/__tests__/hooks.test.tsx index 381cd24e..0403b0b0 100644 --- a/js/packages/react/src/__tests__/hooks.test.tsx +++ b/js/packages/react/src/__tests__/hooks.test.tsx @@ -130,6 +130,7 @@ describe("request/session hooks", () => { bridge_url: undefined, return_to: undefined, allow_legacy_proofs: false, + require_user_presence: false, override_connect_base_url: undefined, environment: undefined, }); @@ -170,6 +171,7 @@ describe("request/session hooks", () => { rp_context: baseRpContext, action_description: undefined, bridge_url: undefined, + require_user_presence: false, override_connect_base_url: undefined, return_to: undefined, environment: undefined, @@ -211,6 +213,7 @@ describe("request/session hooks", () => { rp_context: baseRpContext, action_description: undefined, bridge_url: undefined, + require_user_presence: false, override_connect_base_url: undefined, return_to: undefined, environment: undefined, @@ -255,11 +258,46 @@ describe("request/session hooks", () => { bridge_url: undefined, return_to: "idkit://callback?step=proof", allow_legacy_proofs: false, + require_user_presence: false, override_connect_base_url: undefined, environment: undefined, }); }); + it("request hook forwards require_user_presence to core", async () => { + requestMock.mockReturnValue({ + preset: vi.fn(async () => + makeRequest(async () => ({ + type: "confirmed", + result: { proof: "ok" }, + })), + ), + }); + + const { result } = renderHook(() => + useIDKitRequest({ + app_id: "app_test", + action: "test-action", + rp_context: baseRpContext, + allow_legacy_proofs: false, + require_user_presence: true, + preset: { type: "OrbLegacy" }, + }), + ); + + act(() => { + result.current.open(); + }); + + await waitFor(() => { + expect(result.current.isSuccess).toBe(true); + }); + + expect(requestMock).toHaveBeenCalledWith( + expect.objectContaining({ require_user_presence: true }), + ); + }); + it("session hook forwards return_to to createSession", async () => { createSessionMock.mockReturnValue({ constraints: vi.fn(async () => ({ @@ -276,6 +314,7 @@ describe("request/session hooks", () => { app_id: "app_test", rp_context: baseRpContext, return_to: "idkit://callback?step=create", + require_user_presence: true, constraints: { all: [] }, }), ); @@ -293,6 +332,7 @@ describe("request/session hooks", () => { rp_context: baseRpContext, action_description: undefined, bridge_url: undefined, + require_user_presence: true, override_connect_base_url: undefined, return_to: "idkit://callback?step=create", environment: undefined, @@ -334,6 +374,7 @@ describe("request/session hooks", () => { rp_context: baseRpContext, action_description: undefined, bridge_url: undefined, + require_user_presence: false, override_connect_base_url: undefined, return_to: "idkit://callback?step=prove", environment: undefined, diff --git a/js/packages/react/src/hooks/useIDKitRequest.ts b/js/packages/react/src/hooks/useIDKitRequest.ts index 22ee05e8..916439ec 100644 --- a/js/packages/react/src/hooks/useIDKitRequest.ts +++ b/js/packages/react/src/hooks/useIDKitRequest.ts @@ -17,6 +17,7 @@ export function useIDKitRequest( bridge_url: config.bridge_url, return_to: config.return_to, allow_legacy_proofs: config.allow_legacy_proofs, + require_user_presence: config.require_user_presence ?? false, override_connect_base_url: config.override_connect_base_url, environment: config.environment, }); diff --git a/js/packages/react/src/hooks/useIDKitSession.ts b/js/packages/react/src/hooks/useIDKitSession.ts index 9e826128..97bd300c 100644 --- a/js/packages/react/src/hooks/useIDKitSession.ts +++ b/js/packages/react/src/hooks/useIDKitSession.ts @@ -36,6 +36,7 @@ export function useIDKitSession( rp_context: config.rp_context, action_description: config.action_description, bridge_url: config.bridge_url, + require_user_presence: config.require_user_presence ?? false, override_connect_base_url: config.override_connect_base_url, return_to: config.return_to, environment: config.environment, @@ -45,6 +46,7 @@ export function useIDKitSession( rp_context: config.rp_context, action_description: config.action_description, bridge_url: config.bridge_url, + require_user_presence: config.require_user_presence ?? false, override_connect_base_url: config.override_connect_base_url, return_to: config.return_to, environment: config.environment, diff --git a/kotlin/bindings/src/test/kotlin/com/worldcoin/idkit/IDKitTests.kt b/kotlin/bindings/src/test/kotlin/com/worldcoin/idkit/IDKitTests.kt index 661f6610..8fec8d3c 100644 --- a/kotlin/bindings/src/test/kotlin/com/worldcoin/idkit/IDKitTests.kt +++ b/kotlin/bindings/src/test/kotlin/com/worldcoin/idkit/IDKitTests.kt @@ -51,6 +51,7 @@ class IDKitTests { actionDescription = null, bridgeUrl = null, allowLegacyProofs = false, + requireUserPresence = false, overrideConnectBaseUrl = null, returnTo = null, environment = Environment.STAGING, @@ -62,6 +63,7 @@ class IDKitTests { // rpContext = sampleRpContext(), // actionDescription = null, // bridgeUrl = null, + // requireUserPresence = false, // overrideConnectBaseUrl = null, // returnTo = null, // environment = Environment.STAGING, diff --git a/rust/core/src/bridge.rs b/rust/core/src/bridge.rs index 7bb65249..301c468b 100644 --- a/rust/core/src/bridge.rs +++ b/rust/core/src/bridge.rs @@ -877,6 +877,8 @@ pub struct IDKitRequestConfig { /// - `true`: Accept both v3 and v4 proofs. Use during migration. /// - `false`: Only accept v4 proofs. Use after migration cutoff or for new apps. pub allow_legacy_proofs: bool, + /// Optional user-presence requirement. Defaults to false when omitted. + pub require_user_presence: Option, /// Optional override for the connect base URL (e.g., for staging environments) pub override_connect_base_url: Option, /// Optional deep-link callback URL appended as `return_to` on the connector URL @@ -901,6 +903,8 @@ pub struct IDKitSessionConfig { pub action_description: Option, /// Optional bridge URL (defaults to production) pub bridge_url: Option, + /// Optional user-presence requirement. Defaults to false when omitted. + pub require_user_presence: Option, /// Optional override for the connect base URL (e.g., for staging environments) pub override_connect_base_url: Option, /// Optional deep-link callback URL appended as `return_to` on the connector URL @@ -962,7 +966,7 @@ impl IDKitConfig { legacy_signal: String::new(), bridge_url, allow_legacy_proofs: config.allow_legacy_proofs, - require_user_presence: false, + require_user_presence: config.require_user_presence.unwrap_or(false), override_connect_base_url: config.override_connect_base_url.clone(), return_to: config.return_to.clone(), environment: config.environment, @@ -987,7 +991,7 @@ impl IDKitConfig { legacy_signal: String::new(), bridge_url, allow_legacy_proofs: false, - require_user_presence: false, + require_user_presence: config.require_user_presence.unwrap_or(false), override_connect_base_url: config.override_connect_base_url.clone(), return_to: config.return_to.clone(), environment: config.environment, @@ -1014,7 +1018,7 @@ impl IDKitConfig { legacy_signal: String::new(), bridge_url, allow_legacy_proofs: false, - require_user_presence: false, + require_user_presence: config.require_user_presence.unwrap_or(false), override_connect_base_url: config.override_connect_base_url.clone(), return_to: config.return_to.clone(), environment: config.environment, @@ -1059,7 +1063,7 @@ impl IDKitConfig { legacy_signal: legacy_signal.unwrap_or_default(), bridge_url, allow_legacy_proofs: config.allow_legacy_proofs, - require_user_presence: false, + require_user_presence: config.require_user_presence.unwrap_or(false), override_connect_base_url: config.override_connect_base_url.clone(), return_to: config.return_to.clone(), environment: config.environment, @@ -1083,7 +1087,7 @@ impl IDKitConfig { legacy_signal: legacy_signal.unwrap_or_default(), bridge_url, allow_legacy_proofs: false, - require_user_presence: false, + require_user_presence: config.require_user_presence.unwrap_or(false), override_connect_base_url: config.override_connect_base_url.clone(), return_to: config.return_to.clone(), environment: config.environment, @@ -1109,7 +1113,7 @@ impl IDKitConfig { legacy_signal: legacy_signal.unwrap_or_default(), bridge_url, allow_legacy_proofs: false, - require_user_presence: false, + require_user_presence: config.require_user_presence.unwrap_or(false), override_connect_base_url: config.override_connect_base_url.clone(), return_to: config.return_to.clone(), environment: config.environment, @@ -1628,6 +1632,39 @@ mod tests { assert!(matches!(response, BridgeResponse::Error { .. })); } + #[cfg(feature = "ffi")] + #[test] + fn test_request_config_defaults_user_presence_requirement_to_false() { + let signature = "0x".to_string() + &"00".repeat(64) + "1b"; + let rp_context = RpContext::new( + "rp_1234567890abcdef", + "0x0000000000000000000000000000000000000000000000000000000000000001", + 1_700_000_000, + 1_700_003_600, + &signature, + ) + .unwrap(); + let config = IDKitConfig::Request(IDKitRequestConfig { + app_id: "app_test".to_string(), + action: "test-action".to_string(), + rp_context: std::sync::Arc::new(rp_context), + action_description: None, + bridge_url: None, + allow_legacy_proofs: false, + require_user_presence: None, + override_connect_base_url: None, + return_to: None, + environment: None, + connect_url_mode: None, + }); + + let params = config + .to_params(ConstraintNode::Any { any: Vec::new() }) + .unwrap(); + + assert!(!params.require_user_presence); + } + #[test] fn test_selfie_check_legacy_preset_serializes_face_verification_level() { let preset = crate::preset::Preset::selfie_check_legacy(Some("face-signal".to_string())); diff --git a/rust/core/src/wasm_bindings.rs b/rust/core/src/wasm_bindings.rs index 73058f67..0b0f53dd 100644 --- a/rust/core/src/wasm_bindings.rs +++ b/rust/core/src/wasm_bindings.rs @@ -478,6 +478,7 @@ enum IDKitConfigWasm { action_description: Option, bridge_url: Option, allow_legacy_proofs: bool, + require_user_presence: bool, override_connect_base_url: Option, return_to: Option, environment: Option, @@ -487,6 +488,7 @@ enum IDKitConfigWasm { rp_context: RpContext, action_description: Option, bridge_url: Option, + require_user_presence: bool, override_connect_base_url: Option, return_to: Option, environment: Option, @@ -497,6 +499,7 @@ enum IDKitConfigWasm { rp_context: RpContext, action_description: Option, bridge_url: Option, + require_user_presence: bool, override_connect_base_url: Option, return_to: Option, environment: Option, @@ -517,6 +520,7 @@ impl IDKitConfigWasm { action_description, bridge_url, allow_legacy_proofs, + require_user_presence, override_connect_base_url, return_to, environment, @@ -542,7 +546,7 @@ impl IDKitConfigWasm { legacy_signal: String::new(), bridge_url, allow_legacy_proofs: *allow_legacy_proofs, - require_user_presence: false, + require_user_presence: *require_user_presence, override_connect_base_url: override_connect_base_url.clone(), return_to: return_to.clone(), @@ -557,6 +561,7 @@ impl IDKitConfigWasm { rp_context, action_description, bridge_url, + require_user_presence, override_connect_base_url, return_to, environment, @@ -580,7 +585,7 @@ impl IDKitConfigWasm { legacy_signal: String::new(), bridge_url, allow_legacy_proofs: false, - require_user_presence: false, + require_user_presence: *require_user_presence, override_connect_base_url: override_connect_base_url.clone(), return_to: return_to.clone(), @@ -596,6 +601,7 @@ impl IDKitConfigWasm { rp_context, action_description, bridge_url, + require_user_presence, override_connect_base_url, return_to, environment, @@ -621,7 +627,7 @@ impl IDKitConfigWasm { legacy_signal: String::new(), bridge_url, allow_legacy_proofs: false, - require_user_presence: false, + require_user_presence: *require_user_presence, override_connect_base_url: override_connect_base_url.clone(), return_to: return_to.clone(), @@ -671,6 +677,7 @@ impl IDKitBuilderWasm { action_description: Option, bridge_url: Option, allow_legacy_proofs: bool, + require_user_presence: bool, override_connect_base_url: Option, return_to: Option, environment: Option, @@ -683,6 +690,7 @@ impl IDKitBuilderWasm { action_description, bridge_url, allow_legacy_proofs, + require_user_presence, override_connect_base_url, return_to, environment, @@ -698,6 +706,7 @@ impl IDKitBuilderWasm { rp_context: RpContextWasm, action_description: Option, bridge_url: Option, + require_user_presence: bool, override_connect_base_url: Option, return_to: Option, environment: Option, @@ -708,6 +717,7 @@ impl IDKitBuilderWasm { rp_context: rp_context.into_inner(), action_description, bridge_url, + require_user_presence, override_connect_base_url, return_to, environment, @@ -724,6 +734,7 @@ impl IDKitBuilderWasm { rp_context: RpContextWasm, action_description: Option, bridge_url: Option, + require_user_presence: bool, override_connect_base_url: Option, return_to: Option, environment: Option, @@ -735,6 +746,7 @@ impl IDKitBuilderWasm { rp_context: rp_context.into_inner(), action_description, bridge_url, + require_user_presence, override_connect_base_url, return_to, environment, @@ -947,6 +959,7 @@ pub fn request( action_description: Option, bridge_url: Option, allow_legacy_proofs: bool, + require_user_presence: bool, override_connect_base_url: Option, return_to: Option, environment: Option, @@ -958,6 +971,7 @@ pub fn request( action_description, bridge_url, allow_legacy_proofs, + require_user_presence, override_connect_base_url, return_to, environment, @@ -976,6 +990,7 @@ pub fn create_session( rp_context: RpContextWasm, action_description: Option, bridge_url: Option, + require_user_presence: bool, override_connect_base_url: Option, return_to: Option, environment: Option, @@ -985,6 +1000,7 @@ pub fn create_session( rp_context, action_description, bridge_url, + require_user_presence, override_connect_base_url, return_to, environment, @@ -1004,6 +1020,7 @@ pub fn prove_session( rp_context: RpContextWasm, action_description: Option, bridge_url: Option, + require_user_presence: bool, override_connect_base_url: Option, return_to: Option, environment: Option, @@ -1014,6 +1031,7 @@ pub fn prove_session( rp_context, action_description, bridge_url, + require_user_presence, override_connect_base_url, return_to, environment, @@ -1269,6 +1287,8 @@ export interface IDKitSessionConfig { bridge_url?: string; /** Optional deep-link callback URL appended as `return_to` on the connector URL */ return_to?: string; + /** Require World App to perform a user-presence check before verification. Defaults to false. */ + require_user_presence?: boolean; } /** RpContext for proof requests */ @@ -1401,6 +1421,7 @@ export function createSession( rp_context: RpContextWasm, action_description?: string, bridge_url?: string, + require_user_presence?: boolean, override_connect_base_url?: string, return_to?: string, environment?: string @@ -1419,6 +1440,7 @@ export function proveSession( rp_context: RpContextWasm, action_description?: string, bridge_url?: string, + require_user_presence?: boolean, override_connect_base_url?: string, return_to?: string, environment?: string @@ -1443,6 +1465,7 @@ mod tests { action_description: None, bridge_url: None, allow_legacy_proofs: false, + require_user_presence: false, override_connect_base_url: None, return_to: Some("idkit://callback?step=request".to_string()), environment: None, @@ -1458,6 +1481,28 @@ mod tests { ); } + #[test] + fn request_params_preserve_user_presence_requirement() { + let config = IDKitConfigWasm::Request { + app_id: "app_staging_test".to_string(), + action: "test-action".to_string(), + rp_context: sample_rp_context(), + action_description: None, + bridge_url: None, + allow_legacy_proofs: false, + require_user_presence: true, + override_connect_base_url: None, + return_to: None, + environment: None, + }; + + let params = config + .to_params(ConstraintNode::Any { any: Vec::new() }) + .expect("request params"); + + assert!(params.require_user_presence); + } + #[test] fn create_session_params_preserve_return_to() { let config = IDKitConfigWasm::CreateSession { @@ -1465,6 +1510,7 @@ mod tests { rp_context: sample_rp_context(), action_description: None, bridge_url: None, + require_user_presence: false, override_connect_base_url: None, return_to: Some("idkit://callback?step=create".to_string()), environment: None, @@ -1488,6 +1534,7 @@ mod tests { rp_context: sample_rp_context(), action_description: None, bridge_url: None, + require_user_presence: false, override_connect_base_url: None, return_to: Some("idkit://callback?step=prove".to_string()), environment: None, diff --git a/swift/Tests/IDKitTests/IDKitTests.swift b/swift/Tests/IDKitTests/IDKitTests.swift index bf659e46..5e74578e 100644 --- a/swift/Tests/IDKitTests/IDKitTests.swift +++ b/swift/Tests/IDKitTests/IDKitTests.swift @@ -50,6 +50,7 @@ func idkitEntrypoints() throws { actionDescription: nil, bridgeUrl: nil, allowLegacyProofs: false, + requireUserPresence: false, overrideConnectBaseUrl: nil, returnTo: nil, environment: nil, @@ -62,6 +63,7 @@ func idkitEntrypoints() throws { // rpContext: try sampleRpContext(), // actionDescription: nil, // bridgeUrl: nil, + // requireUserPresence: false, // overrideConnectBaseUrl: nil, // returnTo: nil, // environment: nil From db61d810052c85949577d78f2bb5faf57da7b233 Mon Sep 17 00:00:00 2001 From: Takis Kakalis <80459599+Takaros999@users.noreply.github.com> Date: Wed, 29 Apr 2026 14:18:53 -0700 Subject: [PATCH 03/12] fix(sdk): default native user presence config --- .../main/kotlin/com/worldcoin/idkit/IdKit.kt | 67 +++++++++-- swift/Sources/IDKit/IDKit.swift | 110 +++++++++++++++++- 2 files changed, 163 insertions(+), 14 deletions(-) diff --git a/kotlin/bindings/src/main/kotlin/com/worldcoin/idkit/IdKit.kt b/kotlin/bindings/src/main/kotlin/com/worldcoin/idkit/IdKit.kt index 24e68a3b..04a71b77 100644 --- a/kotlin/bindings/src/main/kotlin/com/worldcoin/idkit/IdKit.kt +++ b/kotlin/bindings/src/main/kotlin/com/worldcoin/idkit/IdKit.kt @@ -13,11 +13,10 @@ import uniffi.idkit_core.AppError // import uniffi.idkit_core.CredentialRequest // import uniffi.idkit_core.CredentialType import uniffi.idkit_core.IdKitBuilder -import uniffi.idkit_core.IdKitRequestConfig +import uniffi.idkit_core.IdKitRequestConfig as NativeIDKitRequestConfig import uniffi.idkit_core.IdKitRequestWrapper import uniffi.idkit_core.IdKitResult -// TODO: Re-enable when World ID 4.0 is live -// import uniffi.idkit_core.IdKitSessionConfig +import uniffi.idkit_core.IdKitSessionConfig as NativeIDKitSessionConfig import uniffi.idkit_core.Preset import uniffi.idkit_core.Signal import uniffi.idkit_core.StatusWrapper @@ -31,12 +30,62 @@ import uniffi.idkit_core.idkitResultToJson as nativeIdkitResultToJson // import uniffi.idkit_core.proveSession as nativeProveSession import uniffi.idkit_core.request as nativeRequest -typealias IDKitRequestConfig = IdKitRequestConfig -// TODO: Re-enable when World ID 4.0 is live -// typealias IDKitSessionConfig = IdKitSessionConfig typealias IDKitResult = IdKitResult typealias RpContext = uniffi.idkit_core.RpContext typealias Environment = uniffi.idkit_core.Environment +typealias ConnectUrlMode = uniffi.idkit_core.ConnectUrlMode + +data class IDKitRequestConfig( + val appId: String, + val action: String, + val rpContext: RpContext, + val actionDescription: String? = null, + val bridgeUrl: String? = null, + val allowLegacyProofs: Boolean = false, + val requireUserPresence: Boolean = false, + val overrideConnectBaseUrl: String? = null, + val returnTo: String? = null, + val environment: Environment? = null, + val connectUrlMode: ConnectUrlMode? = null, +) { + internal fun toNative(): NativeIDKitRequestConfig = + NativeIDKitRequestConfig( + appId = appId, + action = action, + rpContext = rpContext, + actionDescription = actionDescription, + bridgeUrl = bridgeUrl, + allowLegacyProofs = allowLegacyProofs, + requireUserPresence = requireUserPresence, + overrideConnectBaseUrl = overrideConnectBaseUrl, + returnTo = returnTo, + environment = environment, + connectUrlMode = connectUrlMode, + ) +} + +data class IDKitSessionConfig( + val appId: String, + val rpContext: RpContext, + val actionDescription: String? = null, + val bridgeUrl: String? = null, + val requireUserPresence: Boolean = false, + val overrideConnectBaseUrl: String? = null, + val returnTo: String? = null, + val environment: Environment? = null, +) { + internal fun toNative(): NativeIDKitSessionConfig = + NativeIDKitSessionConfig( + appId = appId, + rpContext = rpContext, + actionDescription = actionDescription, + bridgeUrl = bridgeUrl, + requireUserPresence = requireUserPresence, + overrideConnectBaseUrl = overrideConnectBaseUrl, + returnTo = returnTo, + environment = environment, + ) +} class IDKitClientError(message: String) : IllegalArgumentException(message) @@ -181,19 +230,19 @@ object IDKit { fun request(config: IDKitRequestConfig): IDKitBuilder { require(config.appId.isNotBlank()) { "app_id is required" } require(config.action.isNotBlank()) { "action is required" } - return IDKitBuilder(nativeRequest(config)) + return IDKitBuilder(nativeRequest(config.toNative())) } // TODO: Re-enable when World ID 4.0 is live // fun createSession(config: IDKitSessionConfig): IDKitBuilder { // require(config.appId.isNotBlank()) { "app_id is required" } - // return IDKitBuilder(nativeCreateSession(config)) + // return IDKitBuilder(nativeCreateSession(config.toNative())) // } // fun proveSession(sessionId: String, config: IDKitSessionConfig): IDKitBuilder { // require(sessionId.isNotBlank()) { "session_id is required" } // require(config.appId.isNotBlank()) { "app_id is required" } - // return IDKitBuilder(nativeProveSession(sessionId, config)) + // return IDKitBuilder(nativeProveSession(sessionId, config.toNative())) // } fun hashSignal(signal: String): String = hashSignalFfi(Signal.fromString(signal)) diff --git a/swift/Sources/IDKit/IDKit.swift b/swift/Sources/IDKit/IDKit.swift index 140d0a27..59510422 100644 --- a/swift/Sources/IDKit/IDKit.swift +++ b/swift/Sources/IDKit/IDKit.swift @@ -1,27 +1,127 @@ import Foundation -public typealias IDKitRequestConfig = IdKitRequestConfig -public typealias IDKitSessionConfig = IdKitSessionConfig public typealias IDKitResult = IdKitResult +/// Configuration for uniqueness proof requests. +public struct IDKitRequestConfig { + public let appId: String + public let action: String + public let rpContext: RpContext + public let actionDescription: String? + public let bridgeUrl: String? + public let allowLegacyProofs: Bool + public let requireUserPresence: Bool + public let overrideConnectBaseUrl: String? + public let returnTo: String? + public let environment: Environment? + public let connectUrlMode: ConnectUrlMode? + + public init( + appId: String, + action: String, + rpContext: RpContext, + actionDescription: String? = nil, + bridgeUrl: String? = nil, + allowLegacyProofs: Bool = false, + requireUserPresence: Bool = false, + overrideConnectBaseUrl: String? = nil, + returnTo: String? = nil, + environment: Environment? = nil, + connectUrlMode: ConnectUrlMode? = nil + ) { + self.appId = appId + self.action = action + self.rpContext = rpContext + self.actionDescription = actionDescription + self.bridgeUrl = bridgeUrl + self.allowLegacyProofs = allowLegacyProofs + self.requireUserPresence = requireUserPresence + self.overrideConnectBaseUrl = overrideConnectBaseUrl + self.returnTo = returnTo + self.environment = environment + self.connectUrlMode = connectUrlMode + } + + fileprivate var native: IdKitRequestConfig { + IdKitRequestConfig( + appId: appId, + action: action, + rpContext: rpContext, + actionDescription: actionDescription, + bridgeUrl: bridgeUrl, + allowLegacyProofs: allowLegacyProofs, + requireUserPresence: requireUserPresence, + overrideConnectBaseUrl: overrideConnectBaseUrl, + returnTo: returnTo, + environment: environment, + connectUrlMode: connectUrlMode + ) + } +} + +/// Configuration for session requests. +public struct IDKitSessionConfig { + public let appId: String + public let rpContext: RpContext + public let actionDescription: String? + public let bridgeUrl: String? + public let requireUserPresence: Bool + public let overrideConnectBaseUrl: String? + public let returnTo: String? + public let environment: Environment? + + public init( + appId: String, + rpContext: RpContext, + actionDescription: String? = nil, + bridgeUrl: String? = nil, + requireUserPresence: Bool = false, + overrideConnectBaseUrl: String? = nil, + returnTo: String? = nil, + environment: Environment? = nil + ) { + self.appId = appId + self.rpContext = rpContext + self.actionDescription = actionDescription + self.bridgeUrl = bridgeUrl + self.requireUserPresence = requireUserPresence + self.overrideConnectBaseUrl = overrideConnectBaseUrl + self.returnTo = returnTo + self.environment = environment + } + + fileprivate var native: IdKitSessionConfig { + IdKitSessionConfig( + appId: appId, + rpContext: rpContext, + actionDescription: actionDescription, + bridgeUrl: bridgeUrl, + requireUserPresence: requireUserPresence, + overrideConnectBaseUrl: overrideConnectBaseUrl, + returnTo: returnTo, + environment: environment + ) + } +} + /// Main entry point for IDKit Swift SDK. public enum IDKit { public static let version = "4.0.7" /// Creates a builder for uniqueness proof requests. public static func request(config: IDKitRequestConfig) -> IDKitBuilder { - IDKitBuilder(inner: IdKitBuilder.fromRequest(config: config)) + IDKitBuilder(inner: IdKitBuilder.fromRequest(config: config.native)) } // TODO: Re-enable when World ID 4.0 is live // /// Creates a builder for creating a new session. // public static func createSession(config: IDKitSessionConfig) -> IDKitBuilder { - // IDKitBuilder(inner: IdKitBuilder.fromCreateSession(config: config)) + // IDKitBuilder(inner: IdKitBuilder.fromCreateSession(config: config.native)) // } // /// Creates a builder for proving an existing session. // public static func proveSession(sessionId: String, config: IDKitSessionConfig) -> IDKitBuilder { - // IDKitBuilder(inner: IdKitBuilder.fromProveSession(sessionId: sessionId, config: config)) + // IDKitBuilder(inner: IdKitBuilder.fromProveSession(sessionId: sessionId, config: config.native)) // } /// Hashes a string signal to the canonical 0x-prefixed field element string. From e85e4b06a034eba7c7623ffca63908793e1913da Mon Sep 17 00:00:00 2001 From: Takis Kakalis <80459599+Takaros999@users.noreply.github.com> Date: Wed, 29 Apr 2026 13:37:48 -0700 Subject: [PATCH 04/12] docs(examples): add user presence examples --- js/examples/nextjs/app/ui.tsx | 13 +++++++++++++ .../java/com/worldcoin/idkit/sample/MainActivity.kt | 1 + kotlin/README.md | 1 + swift/Examples/BasicVerification.swift | 1 + .../IDKitSampleApp/IDKitSampleApp/ContentView.swift | 1 + swift/README.md | 1 + 6 files changed, 18 insertions(+) diff --git a/js/examples/nextjs/app/ui.tsx b/js/examples/nextjs/app/ui.tsx index a8ca40aa..ca91afcf 100644 --- a/js/examples/nextjs/app/ui.tsx +++ b/js/examples/nextjs/app/ui.tsx @@ -152,6 +152,7 @@ export function DemoClient(): ReactElement { const [useReturnTo, setUseReturnTo] = useState(false); const [returnTo, setReturnTo] = useState(""); const [isReturnToTooltipOpen, setIsReturnToTooltipOpen] = useState(false); + const [requireUserPresence, setRequireUserPresence] = useState(false); const genesisIssuedAtMin = genesisEnabled && genesisDate @@ -485,6 +486,17 @@ export function DemoClient(): ReactElement { placeholder="googlechromes://" /> +
+ + setRequireUserPresence(e.target.checked)} + /> +
@@ -514,6 +526,7 @@ export function DemoClient(): ReactElement { action={action || "test-action"} rp_context={widgetRpContext} allow_legacy_proofs={true} + require_user_presence={requireUserPresence} {...widgetConstraintsOrPreset} onSuccess={(result) => { setWidgetIdkitResult(result); diff --git a/kotlin/Examples/IDKitSampleApp/app/src/main/java/com/worldcoin/idkit/sample/MainActivity.kt b/kotlin/Examples/IDKitSampleApp/app/src/main/java/com/worldcoin/idkit/sample/MainActivity.kt index 57957a5d..dae800be 100644 --- a/kotlin/Examples/IDKitSampleApp/app/src/main/java/com/worldcoin/idkit/sample/MainActivity.kt +++ b/kotlin/Examples/IDKitSampleApp/app/src/main/java/com/worldcoin/idkit/sample/MainActivity.kt @@ -346,6 +346,7 @@ private class SampleModel { actionDescription = "Local Android sample", bridgeUrl = null, allowLegacyProofs = false, + requireUserPresence = false, overrideConnectBaseUrl = null, returnTo = returnToURL, environment = when (environment) { diff --git a/kotlin/README.md b/kotlin/README.md index 06b56991..4da56b4d 100644 --- a/kotlin/README.md +++ b/kotlin/README.md @@ -84,6 +84,7 @@ val config = IDKitRequestConfig( actionDescription = "Log in", bridgeUrl = null, allowLegacyProofs = false, + requireUserPresence = false, overrideConnectBaseUrl = null, returnTo = null, environment = Environment.STAGING, diff --git a/swift/Examples/BasicVerification.swift b/swift/Examples/BasicVerification.swift index 478abfd6..22d774b7 100644 --- a/swift/Examples/BasicVerification.swift +++ b/swift/Examples/BasicVerification.swift @@ -27,6 +27,7 @@ func basicVerification() async throws { actionDescription: "Example verification", bridgeUrl: nil, allowLegacyProofs: false, + requireUserPresence: false, overrideConnectBaseUrl: nil, returnTo: nil, environment: .staging, diff --git a/swift/Examples/IDKitSampleApp/IDKitSampleApp/ContentView.swift b/swift/Examples/IDKitSampleApp/IDKitSampleApp/ContentView.swift index 38b94838..79fb84bb 100644 --- a/swift/Examples/IDKitSampleApp/IDKitSampleApp/ContentView.swift +++ b/swift/Examples/IDKitSampleApp/IDKitSampleApp/ContentView.swift @@ -175,6 +175,7 @@ final class SampleModel: ObservableObject { actionDescription: "Local iOS sample", bridgeUrl: nil, allowLegacyProofs: false, + requireUserPresence: false, overrideConnectBaseUrl: nil, returnTo: returnToURL, environment: { diff --git a/swift/README.md b/swift/README.md index 70171361..b2d2dc9a 100644 --- a/swift/README.md +++ b/swift/README.md @@ -38,6 +38,7 @@ let config = IDKitRequestConfig( actionDescription: "Log in", bridgeUrl: nil, allowLegacyProofs: false, + requireUserPresence: false, overrideConnectBaseUrl: nil, returnTo: nil, environment: .staging From dcf24dcecd887055385f709becbbc7152cb38599 Mon Sep 17 00:00:00 2001 From: Vladimir Burdukov Date: Tue, 21 Apr 2026 12:12:42 +0300 Subject: [PATCH 05/12] feat(rust): IdentityAttribute enum + IdentityCheck preset + identity_attested field --- Cargo.lock | 54 ++-- Cargo.toml | 2 +- rust/core/src/bridge.rs | 392 ++++++++++++++++++++---- rust/core/src/preset.rs | 339 +++++++++++++------- rust/core/src/types.rs | 204 ++++++++++++ rust/core/src/wasm_bindings.rs | 36 ++- swift/Tests/IDKitTests/IDKitTests.swift | 13 +- 7 files changed, 830 insertions(+), 210 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9315589a..4917f822 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1124,11 +1124,11 @@ dependencies = [ [[package]] name = "askama" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4744ed2eef2645831b441d8f5459689ade2ab27c854488fbab1fbe94fce1a7" +checksum = "f75363874b771be265f4ffe307ca705ef6f3baa19011c149da8674a87f1b75c4" dependencies = [ - "askama_derive 0.13.1", + "askama_derive 0.14.0", "itoa", "percent-encoding", "serde", @@ -1150,11 +1150,11 @@ dependencies = [ [[package]] name = "askama_derive" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d661e0f57be36a5c14c48f78d09011e67e0cb618f269cca9f2fd8d15b68c46ac" +checksum = "129397200fe83088e8a68407a8e2b1f826cf0086b21ccdb866a722c8bcd3a94f" dependencies = [ - "askama_parser 0.13.0", + "askama_parser 0.14.0", "basic-toml", "memchr", "proc-macro2", @@ -1193,9 +1193,9 @@ dependencies = [ [[package]] name = "askama_parser" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf315ce6524c857bb129ff794935cf6d42c82a6cff60526fe2a63593de4d0d4f" +checksum = "d6ab5630b3d5eaf232620167977f95eb51f3432fc76852328774afbd242d4358" dependencies = [ "memchr", "serde", @@ -4073,9 +4073,9 @@ dependencies = [ [[package]] name = "siphasher" -version = "0.3.11" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" [[package]] name = "slab" @@ -4854,9 +4854,9 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "uniffi" -version = "0.30.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c866f627c3f04c3df068b68bb2d725492caaa539dd313e2a9d26bb85b1a32f4e" +checksum = "dc5f2297ee5b893405bed1a6929faec4713a061df158ecf5198089f23910d470" dependencies = [ "anyhow", "camino", @@ -4877,12 +4877,12 @@ dependencies = [ [[package]] name = "uniffi_bindgen" -version = "0.30.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c8ca600167641ebe7c8ba9254af40492dda3397c528cc3b2f511bd23e8541a5" +checksum = "8bc0c60a9607e7ab77a2ad47ec5530178015014839db25af7512447d2238016c" dependencies = [ "anyhow", - "askama 0.13.1", + "askama 0.14.0", "camino", "cargo_metadata", "fs-err", @@ -4903,9 +4903,9 @@ dependencies = [ [[package]] name = "uniffi_core" -version = "0.30.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e7a5a038ebffe8f4cf91416b154ef3c2468b18e828b7009e01b1b99938089f9" +checksum = "77baf5d539fe2e1ad6805e942dbc5dbdeb2b83eb5f2b3a6535d422ca4b02a12f" dependencies = [ "anyhow", "bytes", @@ -4915,9 +4915,9 @@ dependencies = [ [[package]] name = "uniffi_internal_macros" -version = "0.30.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c2a6f93e7b73726e2015696ece25ca0ac5a5f1cf8d6a7ab5214dd0a01d2edf" +checksum = "b4b42137524f4be6400fcaca9d02c1d4ecb6ad917e4013c0b93235526d8396e5" dependencies = [ "anyhow", "indexmap 2.13.0", @@ -4928,9 +4928,9 @@ dependencies = [ [[package]] name = "uniffi_macros" -version = "0.30.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c6309fc36c7992afc03bc0c5b059c656bccbef3f2a4bc362980017f8936141" +checksum = "d9273ec45330d8fe9a3701b7b983cea7a4e218503359831967cb95d26b873561" dependencies = [ "camino", "fs-err", @@ -4945,9 +4945,9 @@ dependencies = [ [[package]] name = "uniffi_meta" -version = "0.30.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a138823392dba19b0aa494872689f97d0ee157de5852e2bec157ce6de9cdc22" +checksum = "431d2f443e7828a6c29d188de98b6771a6491ee98bba2d4372643bf93f988a18" dependencies = [ "anyhow", "siphasher", @@ -4957,9 +4957,9 @@ dependencies = [ [[package]] name = "uniffi_pipeline" -version = "0.30.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c27c4b515d25f8e53cc918e238c39a79c3144a40eaf2e51c4a7958973422c29" +checksum = "761ef74f6175e15603d0424cc5f98854c5baccfe7bf4ccb08e5816f9ab8af689" dependencies = [ "anyhow", "heck", @@ -4970,9 +4970,9 @@ dependencies = [ [[package]] name = "uniffi_udl" -version = "0.30.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0adacdd848aeed7af4f5af7d2f621d5e82531325d405e29463482becfdeafca" +checksum = "68773ec0e1c067b6505a73bbf6a5782f31a7f9209333a0df97b87565c46bf370" dependencies = [ "anyhow", "textwrap", diff --git a/Cargo.toml b/Cargo.toml index c93b7f0a..ce2f0727 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,7 +62,7 @@ ruint = "1.11.1" taceo-oprf = { version = "0.8", default-features = false, features = ["types"] } # UniFFI -uniffi = "0.30" +uniffi = "0.31" # WASM wasm-bindgen = "0.2" diff --git a/rust/core/src/bridge.rs b/rust/core/src/bridge.rs index ef44a86d..bc090fd1 100644 --- a/rust/core/src/bridge.rs +++ b/rust/core/src/bridge.rs @@ -6,7 +6,8 @@ use crate::{ crypto::{base64_decode, base64_encode, decrypt, encrypt}, error::{AppError, Error, Result}, types::{ - AppId, BridgeResponseV1, BridgeUrl, IDKitResult, ResponseItem, RpContext, VerificationLevel, + AppId, BridgeResponseV1, BridgeUrl, IDKitResult, IdentityAttribute, ResponseItem, + RpContext, VerificationLevel, }, ConstraintNode, Signal, }; @@ -111,6 +112,11 @@ struct BridgeRequestPayload { #[serde(skip_serializing_if = "Option::is_none")] proof_request: Option, + /// Optional identity attribute filters for identity-attestation presets. + /// Only present for World ID 4.0 identity check requests. + #[serde(skip_serializing_if = "Option::is_none")] + identity_attributes: Option>, + /// Whether to accept legacy (v3) proofs as fallback. /// - `true`: Accept both v3 and v4 proofs. Use during migration. /// - `false`: Only accept v4 proofs. Use after migration cutoff or for new apps. @@ -221,6 +227,12 @@ enum BridgeResponse { /// World ID 4.0 protocol response ResponseV2(world_id_primitives::ProofResponse), + /// World ID 4.0 protocol response with extensions + ResponseV2_1 { + proof_response: world_id_primitives::ProofResponse, + identity_attested: Option, + }, + /// Multi-credential legacy: bridge sends v3 proofs via `legacy_responses` MultiLegacyResponse { legacy_responses: Vec, @@ -265,6 +277,8 @@ pub struct BridgeConnectionParams { pub return_to: Option, /// Optional environment override (defaults to Production when not specified) pub environment: Option, + /// Present only on World ID 4.0 requests created from `IdentityCheck` presets + pub identity_attributes: Option>, } /// A helper struct to cache the signal hashes of a request @@ -440,6 +454,7 @@ pub fn build_request_payload( action: action_str, action_description: params.action_description.clone(), proof_request, + identity_attributes: params.identity_attributes.clone(), verification_level: params.legacy_verification_level, signal: legacy_signal_hash, timestamp, @@ -667,49 +682,12 @@ impl BridgeConnection { match bridge_response { BridgeResponse::Error { error_code } => Ok(Status::Failed(error_code)), BridgeResponse::ResponseV2(proof_response) => { - // Check for protocol-level error - if let Some(error_code) = proof_response.error.as_deref() { - return Ok(Status::Failed(AppError::from_code(error_code))); - } - - let responses: Vec = proof_response - .responses - .into_iter() - .map(|item| { - let signal_hash = self.cached_signal_hashes.get(&item.identifier); - ResponseItem::from_protocol_item(item, signal_hash) - }) - .collect::>>()?; - - Ok(Status::Confirmed( - if let Some(session_id) = proof_response.session_id { - IDKitResult::new_session( - self.nonce.clone(), - serde_json::to_value(session_id)? - .as_str() - .ok_or_else(|| { - Error::InvalidConfiguration( - "SessionId did not serialize as a string" - .to_string(), - ) - })? - .to_owned(), - self.action_description.clone(), - responses, - self.environment.as_ref(), - ) - } else { - IDKitResult::new( - "4.0", - self.nonce.clone(), - self.action.clone(), - self.action_description.clone(), - responses, - self.environment.as_ref(), - ) - }, - )) + self.handle_bridge_v2_response(proof_response, None) } + BridgeResponse::ResponseV2_1 { + proof_response, + identity_attested, + } => self.handle_bridge_v2_response(proof_response, identity_attested), BridgeResponse::MultiLegacyResponse { legacy_responses } => { let responses: Vec = legacy_responses .into_iter() @@ -752,6 +730,56 @@ impl BridgeConnection { } } + fn handle_bridge_v2_response( + &self, + proof_response: world_id_primitives::ProofResponse, + identity_attested: Option, + ) -> Result { + // Check for protocol-level error + if let Some(error_code) = proof_response.error.as_deref() { + return Ok(Status::Failed(AppError::from_code(error_code))); + } + + let responses: Vec = proof_response + .responses + .into_iter() + .map(|item| { + let signal_hash = self.cached_signal_hashes.get(&item.identifier); + ResponseItem::from_protocol_item(item, signal_hash) + }) + .collect::>>()?; + + Ok(Status::Confirmed( + if let Some(session_id) = proof_response.session_id { + IDKitResult::new_session( + self.nonce.clone(), + serde_json::to_value(session_id)? + .as_str() + .ok_or_else(|| { + Error::InvalidConfiguration( + "SessionId did not serialize as a string".to_string(), + ) + })? + .to_owned(), + self.action_description.clone(), + responses, + self.environment.as_ref(), + ) + } else { + let mut result = IDKitResult::new( + "4.0", + self.nonce.clone(), + self.action.clone(), + self.action_description.clone(), + responses, + self.environment.as_ref(), + ); + result.identity_attested = identity_attested; + result + }, + )) + } + /// Returns the request ID for this request #[must_use] pub const fn request_id(&self) -> Uuid { @@ -869,6 +897,7 @@ impl IDKitConfig { override_connect_base_url: config.override_connect_base_url.clone(), return_to: config.return_to.clone(), environment: config.environment, + identity_attributes: None, }) } Self::CreateSession(config) => { @@ -893,6 +922,7 @@ impl IDKitConfig { override_connect_base_url: config.override_connect_base_url.clone(), return_to: config.return_to.clone(), environment: config.environment, + identity_attributes: None, }) } Self::ProveSession { session_id, config } => { @@ -919,6 +949,7 @@ impl IDKitConfig { override_connect_base_url: config.override_connect_base_url.clone(), return_to: config.return_to.clone(), environment: config.environment, + identity_attributes: None, }) } } @@ -937,7 +968,14 @@ impl IDKitConfig { }); } - let (_constraints, legacy_verification_level, legacy_signal) = preset.to_bridge_params(); + let bridge_params = preset.into_bridge_params(); + + // Default to Device so existing World App versions can still parse + // the bridge payload. V4 requests use `proof_request` for real + // credential selection; this field is only for v3 backwards compat. + let legacy_verification_level = bridge_params + .legacy_verification_level + .unwrap_or(VerificationLevel::Device); match self { Self::Request(config) => { @@ -948,21 +986,30 @@ impl IDKitConfig { .map(|url| BridgeUrl::new(url, &app_id)) .transpose()?; + let allow_legacy_proofs = + bridge_params + .allow_legacy_proofs_override + .unwrap_or(match self { + Self::Request(config) => config.allow_legacy_proofs, + Self::CreateSession(_) | Self::ProveSession { .. } => false, + }); + Ok(BridgeConnectionParams { app_id, kind: RequestKind::Uniqueness { action: config.action.clone(), }, - constraints: None, + constraints: bridge_params.constraints, rp_context: (*config.rp_context).clone(), action_description: config.action_description.clone(), legacy_verification_level, - legacy_signal: legacy_signal.unwrap_or_default(), + legacy_signal: bridge_params.legacy_signal.unwrap_or_default(), bridge_url, - allow_legacy_proofs: config.allow_legacy_proofs, + allow_legacy_proofs, override_connect_base_url: config.override_connect_base_url.clone(), return_to: config.return_to.clone(), environment: config.environment, + identity_attributes: bridge_params.identity_attributes, }) } Self::CreateSession(config) => { @@ -976,16 +1023,17 @@ impl IDKitConfig { Ok(BridgeConnectionParams { app_id, kind: RequestKind::CreateSession, - constraints: None, + constraints: bridge_params.constraints, rp_context: (*config.rp_context).clone(), action_description: config.action_description.clone(), legacy_verification_level, - legacy_signal: legacy_signal.unwrap_or_default(), + legacy_signal: bridge_params.legacy_signal.unwrap_or_default(), bridge_url, allow_legacy_proofs: false, override_connect_base_url: config.override_connect_base_url.clone(), return_to: config.return_to.clone(), environment: config.environment, + identity_attributes: bridge_params.identity_attributes, }) } Self::ProveSession { session_id, config } => { @@ -1001,16 +1049,17 @@ impl IDKitConfig { kind: RequestKind::ProveSession { session_id: session_id.clone(), }, - constraints: None, + constraints: bridge_params.constraints, rp_context: (*config.rp_context).clone(), action_description: config.action_description.clone(), legacy_verification_level, - legacy_signal: legacy_signal.unwrap_or_default(), + legacy_signal: bridge_params.legacy_signal.unwrap_or_default(), bridge_url, allow_legacy_proofs: false, override_connect_base_url: config.override_connect_base_url.clone(), return_to: config.return_to.clone(), environment: config.environment, + identity_attributes: bridge_params.identity_attributes, }) } } @@ -1318,6 +1367,7 @@ mod tests { verification_level: VerificationLevel::Device, timestamp: None, proof_request: Some(proof_request), + identity_attributes: None, allow_legacy_proofs: false, environment: Environment::Production, }; @@ -1331,6 +1381,55 @@ mod tests { assert!(json.contains("allow_legacy_proofs")); } + #[test] + fn test_build_request_payload_serializes_identity_attributes() { + let app_id = AppId::new("app_test").unwrap(); + let rp_context = RpContext::new( + "rp_1234567890abcdef", + "0x0000000000000000000000000000000000000000000000000000000000000001", + 1_700_000_000, + 1_700_003_600, + &("0x".to_string() + &"00".repeat(64) + "1b"), + ) + .unwrap(); + let constraints = ConstraintNode::any(vec![ + ConstraintNode::item(CredentialRequest::new(CredentialType::Passport, None)), + ConstraintNode::item(CredentialRequest::new(CredentialType::Mnc, None)), + ]); + + let params = BridgeConnectionParams { + app_id, + kind: RequestKind::Uniqueness { + action: "test-action".to_string(), + }, + constraints: Some(constraints), + rp_context, + action_description: Some("Identity check".to_string()), + legacy_verification_level: VerificationLevel::Device, + legacy_signal: String::new(), + bridge_url: None, + allow_legacy_proofs: false, + override_connect_base_url: None, + return_to: None, + environment: Some(Environment::Production), + identity_attributes: Some(vec![ + IdentityAttribute::MinimumAge(21), + IdentityAttribute::Nationality("JPN".to_string()), + ]), + }; + + let payload = build_request_payload(¶ms, false).unwrap(); + + assert_eq!( + payload["identity_attributes"], + serde_json::json!([ + {"type": "minimum_age", "value": 21}, + {"type": "nationality", "value": "JPN"} + ]) + ); + assert!(payload.get("proof_request").is_some()); + } + #[test] fn test_session_id_sdk_round_trip_uses_protocol_format() { let session_id = SessionId::default(); @@ -1479,6 +1578,172 @@ mod tests { assert!(matches!(response, BridgeResponse::Error { .. })); } + // ───────────────────────────────────────────────────────────────────────── + // BridgeResponse::ResponseV2 Parsing Tests + // ───────────────────────────────────────────────────────────────────────── + + const ZERO_PROOF: &str = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + const ZERO_NULLIFIER: &str = + "nil_0000000000000000000000000000000000000000000000000000000000000000"; + + #[test] + fn test_bridge_response_v2_single_uniqueness_proof() { + let json = format!( + r#"{{ + "id": "req_abc123", + "version": 1, + "responses": [{{ + "identifier": "orb", + "issuer_schema_id": 1, + "proof": "{ZERO_PROOF}", + "nullifier": "{ZERO_NULLIFIER}", + "expires_at_min": 1735689600 + }}] + }}"# + ); + + let response: BridgeResponse = serde_json::from_str(&json).unwrap(); + match response { + BridgeResponse::ResponseV2(proof_response) => { + assert_eq!(proof_response.id, "req_abc123"); + assert!(proof_response.error.is_none()); + assert_eq!(proof_response.responses.len(), 1); + assert_eq!(proof_response.responses[0].identifier, "orb"); + assert_eq!(proof_response.responses[0].issuer_schema_id, 1); + assert!(proof_response.responses[0].nullifier.is_some()); + assert!(proof_response.responses[0].session_nullifier.is_none()); + } + other => panic!("Expected ResponseV2, got: {other:?}"), + } + } + + #[test] + fn test_bridge_response_v2_multiple_credentials() { + let json = format!( + r#"{{ + "id": "req_multi", + "version": 1, + "responses": [ + {{ + "identifier": "orb", + "issuer_schema_id": 1, + "proof": "{ZERO_PROOF}", + "nullifier": "{ZERO_NULLIFIER}", + "expires_at_min": 1735689600 + }}, + {{ + "identifier": "passport", + "issuer_schema_id": 9303, + "proof": "{ZERO_PROOF}", + "nullifier": "{ZERO_NULLIFIER}", + "expires_at_min": 1735689600 + }} + ] + }}"# + ); + + let response: BridgeResponse = serde_json::from_str(&json).unwrap(); + match response { + BridgeResponse::ResponseV2(proof_response) => { + assert_eq!(proof_response.responses.len(), 2); + assert_eq!(proof_response.responses[0].identifier, "orb"); + assert_eq!(proof_response.responses[1].identifier, "passport"); + assert_eq!(proof_response.responses[1].issuer_schema_id, 9303); + } + other => panic!("Expected ResponseV2, got: {other:?}"), + } + } + + #[test] + fn test_bridge_response_v2_protocol_level_error() { + let json = r#"{ + "id": "req_failed", + "version": 1, + "error": "credential_not_found", + "responses": [] + }"#; + + let response: BridgeResponse = serde_json::from_str(json).unwrap(); + match response { + BridgeResponse::ResponseV2(proof_response) => { + assert!(proof_response.error.is_some()); + assert_eq!( + proof_response.error.as_deref(), + Some("credential_not_found") + ); + assert!(proof_response.responses.is_empty()); + } + other => panic!("Expected ResponseV2, got: {other:?}"), + } + } + + // ───────────────────────────────────────────────────────────────────────── + // BridgeResponse::MultiLegacyResponse Parsing Tests + // ───────────────────────────────────────────────────────────────────────── + + #[test] + fn test_bridge_response_multi_legacy_single() { + let json = r#"{ + "legacy_responses": [ + { + "proof": "0xproof", + "merkle_root": "0xroot", + "nullifier_hash": "0xnull", + "verification_level": "orb" + } + ] + }"#; + + let response: BridgeResponse = serde_json::from_str(json).unwrap(); + match response { + BridgeResponse::MultiLegacyResponse { legacy_responses } => { + assert_eq!(legacy_responses.len(), 1); + assert_eq!( + legacy_responses[0].verification_level, + VerificationLevel::Orb + ); + assert_eq!(legacy_responses[0].proof, "0xproof"); + } + other => panic!("Expected MultiLegacyResponse, got: {other:?}"), + } + } + + #[test] + fn test_bridge_response_multi_legacy_multiple() { + let json = r#"{ + "legacy_responses": [ + { + "proof": "0xproof1", + "merkle_root": "0xroot1", + "nullifier_hash": "0xnull1", + "verification_level": "orb" + }, + { + "proof": "0xproof2", + "merkle_root": "0xroot2", + "nullifier_hash": "0xnull2", + "credential_type": "device" + } + ] + }"#; + + let response: BridgeResponse = serde_json::from_str(json).unwrap(); + match response { + BridgeResponse::MultiLegacyResponse { legacy_responses } => { + assert_eq!(legacy_responses.len(), 2); + assert_eq!( + legacy_responses[0].verification_level, + VerificationLevel::Orb + ); + assert_eq!( + legacy_responses[1].verification_level, + VerificationLevel::Device + ); + } + other => panic!("Expected MultiLegacyResponse, got: {other:?}"), + } + } + #[test] fn test_bridge_response_new_error_codes_deserialization() { let invalid_signature: BridgeResponse = @@ -1525,7 +1790,7 @@ mod tests { #[test] fn test_selfie_check_legacy_preset_serializes_face_verification_level() { let preset = crate::preset::Preset::selfie_check_legacy(Some("face-signal".to_string())); - let (constraints, legacy_verification_level, legacy_signal) = preset.to_bridge_params(); + let bridge_params = preset.into_bridge_params(); let app_id = AppId::new("app_test").unwrap(); let signature = "0x".to_string() + &"00".repeat(64) + "1b"; @@ -1543,17 +1808,20 @@ mod tests { kind: RequestKind::Uniqueness { action: "test-action".to_string(), }, - constraints: Some(constraints), + constraints: bridge_params.constraints, rp_context, action_description: Some("Selfie check".to_string()), - legacy_verification_level, - legacy_signal: legacy_signal.unwrap_or_default(), + legacy_verification_level: bridge_params + .legacy_verification_level + .expect("this preset should return legacy_verification_level"), + legacy_signal: bridge_params.legacy_signal.unwrap_or_default(), bridge_url: None, allow_legacy_proofs: false, override_connect_base_url: None, return_to: None, environment: Some(Environment::Production), + identity_attributes: None, }; let payload = build_request_payload(¶ms, false).unwrap(); @@ -1563,7 +1831,7 @@ mod tests { #[test] fn test_device_legacy_preset_serializes_device_verification_level() { let preset = crate::preset::Preset::device_legacy(Some("device-signal".to_string())); - let (constraints, legacy_verification_level, legacy_signal) = preset.to_bridge_params(); + let bridge_params = preset.into_bridge_params(); let app_id = AppId::new("app_test").unwrap(); let signature = "0x".to_string() + &"00".repeat(64) + "1b"; @@ -1581,17 +1849,20 @@ mod tests { kind: RequestKind::Uniqueness { action: "test-action".to_string(), }, - constraints: Some(constraints), + constraints: bridge_params.constraints, rp_context, action_description: Some("Device check".to_string()), - legacy_verification_level, - legacy_signal: legacy_signal.unwrap_or_default(), + legacy_verification_level: bridge_params + .legacy_verification_level + .expect("this preset should return legacy_verification_level"), + legacy_signal: bridge_params.legacy_signal.unwrap_or_default(), bridge_url: None, allow_legacy_proofs: false, override_connect_base_url: None, return_to: None, environment: Some(Environment::Production), + identity_attributes: None, }; let payload = build_request_payload(¶ms, false).unwrap(); @@ -1654,6 +1925,7 @@ mod tests { override_connect_base_url: None, return_to: None, environment: None, + identity_attributes: None, }; let payload = build_native_v1_payload(¶ms).unwrap(); @@ -1698,6 +1970,7 @@ mod tests { override_connect_base_url: None, return_to: None, environment: None, + identity_attributes: None, }; // native=true includes timestamp @@ -1744,6 +2017,7 @@ mod tests { override_connect_base_url: None, return_to: None, environment: None, + identity_attributes: None, }; let cached = CachedSignalHashes::compute(¶ms); diff --git a/rust/core/src/preset.rs b/rust/core/src/preset.rs index 01588e94..808cfd12 100644 --- a/rust/core/src/preset.rs +++ b/rust/core/src/preset.rs @@ -3,7 +3,10 @@ //! Presets provide a simplified API for common credential request patterns, //! automatically handling both World ID 4.0 and 3.0 protocol formats. -use crate::types::{CredentialRequest, CredentialType, Signal, VerificationLevel}; +use crate::types::IdentityAttribute; +#[cfg(any(test, feature = "ffi", feature = "wasm-bindings"))] +use crate::types::{CredentialRequest, CredentialType, VerificationLevel}; +#[cfg(any(test, feature = "ffi", feature = "wasm-bindings"))] use crate::ConstraintNode; use serde::{Deserialize, Serialize}; @@ -73,6 +76,29 @@ pub enum Preset { /// Can be a plain string or hex-encoded ABI value (with 0x prefix). signal: Option, }, + + /// Document-based identity attestation (World ID 4.0) + /// + /// Requests passport or national identity card credentials, with optional + /// proof-of-humanity requirement. + /// + /// This preset requires World ID 4.0-compatible clients. It is not supported + /// for native v1 payloads or session flows. + IdentityCheck { + /// Identity attribute filters the verifier wants to assert. + attributes: Vec, + /// When `true`, also requires an orb-verified proof-of-humanity credential. + require_proof_of_humanity: bool, + }, +} + +#[cfg(any(test, feature = "ffi", feature = "wasm-bindings"))] +pub(crate) struct BridgeParams { + pub constraints: Option, + pub legacy_verification_level: Option, + pub legacy_signal: Option, + pub identity_attributes: Option>, + pub allow_legacy_proofs_override: Option, } impl Preset { @@ -108,69 +134,92 @@ impl Preset { Self::DeviceLegacy { signal } } + #[must_use] + pub fn identity_check( + attributes: Vec, + require_proof_of_humanity: bool, + ) -> Self { + Self::IdentityCheck { + attributes, + require_proof_of_humanity, + } + } + /// Converts the preset to bridge session parameters /// /// Returns a tuple of: /// - `ConstraintNode` - World ID 4.0 constraint tree /// - `VerificationLevel` - World ID 3.0 legacy verification level /// - `Option` - Legacy signal string (if configured) + /// - `Option>` - a list of identity attributes + /// - `Option` - override for `allow_legacy_proofs` (`None` = let caller decide) // TODO: This should be removed it was introduced to keep legacy preset compatible with proof_request // TODO: but we decided to keep legacy presets only 3.0, will tackle separately + #[cfg(any(test, feature = "ffi", feature = "wasm-bindings"))] #[must_use] - pub fn to_bridge_params(&self) -> (ConstraintNode, VerificationLevel, Option) { + pub(crate) fn into_bridge_params(self) -> BridgeParams { match self { - Self::OrbLegacy { signal } => { - let signal_opt = signal.as_ref().map(|s| Signal::from_string(s.clone())); - let orb = CredentialRequest::new(CredentialType::ProofOfHuman, signal_opt); - let constraints = ConstraintNode::Item(orb); // OrbLegacy doesn't need constraints - let legacy_verification_level = VerificationLevel::Orb; - let legacy_signal = signal.clone(); - - (constraints, legacy_verification_level, legacy_signal) - } - Self::SecureDocumentLegacy { signal } => { - let signal_opt = signal.as_ref().map(|s| Signal::from_string(s.clone())); - let orb = CredentialRequest::new(CredentialType::ProofOfHuman, signal_opt.clone()); - let passport = CredentialRequest::new(CredentialType::Passport, signal_opt); - let constraints = ConstraintNode::any(vec![ - ConstraintNode::Item(orb), - ConstraintNode::Item(passport), - ]); - let legacy_verification_level = VerificationLevel::SecureDocument; - let legacy_signal = signal.clone(); + Self::OrbLegacy { signal } => BridgeParams { + constraints: None, + legacy_verification_level: Some(VerificationLevel::Orb), + legacy_signal: signal, + identity_attributes: None, + allow_legacy_proofs_override: None, + }, + Self::SecureDocumentLegacy { signal } => BridgeParams { + constraints: None, + legacy_verification_level: Some(VerificationLevel::SecureDocument), + legacy_signal: signal, + identity_attributes: None, + allow_legacy_proofs_override: None, + }, + Self::DocumentLegacy { signal } => BridgeParams { + constraints: None, + legacy_verification_level: Some(VerificationLevel::Document), + legacy_signal: signal, + identity_attributes: None, + allow_legacy_proofs_override: None, + }, + Self::SelfieCheckLegacy { signal } => BridgeParams { + constraints: None, + legacy_verification_level: Some(VerificationLevel::Face), + legacy_signal: signal, + identity_attributes: None, + allow_legacy_proofs_override: None, + }, + Self::DeviceLegacy { signal } => BridgeParams { + constraints: None, + legacy_verification_level: Some(VerificationLevel::Device), + legacy_signal: signal, + identity_attributes: None, + allow_legacy_proofs_override: None, + }, + Self::IdentityCheck { + attributes, + require_proof_of_humanity, + } => { + let passport = CredentialRequest::new(CredentialType::Passport, None); + let mnc = CredentialRequest::new(CredentialType::Mnc, None); + let proof_of_human = CredentialRequest::new(CredentialType::ProofOfHuman, None); - (constraints, legacy_verification_level, legacy_signal) - } - Self::DocumentLegacy { signal } => { - let signal_opt = signal.as_ref().map(|s| Signal::from_string(s.clone())); - let orb = CredentialRequest::new(CredentialType::ProofOfHuman, signal_opt.clone()); - let passport = CredentialRequest::new(CredentialType::Passport, signal_opt); - let constraints = ConstraintNode::any(vec![ - ConstraintNode::Item(orb), - ConstraintNode::Item(passport), + let documents = ConstraintNode::any(vec![ + ConstraintNode::item(passport), + ConstraintNode::item(mnc), ]); - let legacy_verification_level = VerificationLevel::Document; - let legacy_signal = signal.clone(); - (constraints, legacy_verification_level, legacy_signal) - } - Self::SelfieCheckLegacy { signal } => { - let signal_opt = signal.as_ref().map(|s| Signal::from_string(s.clone())); - let face = CredentialRequest::new(CredentialType::Face, signal_opt); - let constraints = ConstraintNode::Item(face); - let legacy_verification_level = VerificationLevel::Face; - let legacy_signal = signal.clone(); - - (constraints, legacy_verification_level, legacy_signal) - } - Self::DeviceLegacy { signal } => { - let signal_opt = signal.as_ref().map(|s| Signal::from_string(s.clone())); - let orb = CredentialRequest::new(CredentialType::ProofOfHuman, signal_opt); - let constraints = ConstraintNode::Item(orb); - let legacy_verification_level = VerificationLevel::Device; - let legacy_signal = signal.clone(); - - (constraints, legacy_verification_level, legacy_signal) + let constraints = if require_proof_of_humanity { + ConstraintNode::all(vec![documents, ConstraintNode::item(proof_of_human)]) + } else { + documents + }; + + BridgeParams { + constraints: Some(constraints), + legacy_verification_level: None, + legacy_signal: None, + identity_attributes: Some(attributes), + allow_legacy_proofs_override: Some(false), + } } } } @@ -179,72 +228,155 @@ impl Preset { #[cfg(test)] mod tests { use super::*; + use crate::Signal; #[test] fn selfie_check_legacy_preset_builds_face_only_constraints_and_face_legacy_level() { let preset = Preset::selfie_check_legacy(Some("face-signal".to_string())); - let (constraints, verification_level, legacy_signal) = preset.to_bridge_params(); + let bridge_params = preset.into_bridge_params(); - assert_eq!(verification_level, VerificationLevel::Face); - assert_eq!(legacy_signal, Some("face-signal".to_string())); - - match constraints { - ConstraintNode::Item(item) => { - assert_eq!(item.credential_type, CredentialType::Face); - assert_eq!(item.signal, Some(Signal::from_string("face-signal"))); - } - _ => panic!("expected selfieCheckLegacy constraints to be a single item"), - } + assert_eq!( + bridge_params.legacy_verification_level, + Some(VerificationLevel::Face) + ); + assert_eq!(bridge_params.legacy_signal, Some("face-signal".to_string())); + assert_eq!(bridge_params.identity_attributes, None); + assert!(bridge_params.constraints.is_none()); } #[test] fn selfie_check_legacy_preset_without_signal_preserves_empty_signal() { let preset = Preset::selfie_check_legacy(None); - let (constraints, verification_level, legacy_signal) = preset.to_bridge_params(); + let bridge_params = preset.into_bridge_params(); - assert_eq!(verification_level, VerificationLevel::Face); - assert_eq!(legacy_signal, None); - - match constraints { - ConstraintNode::Item(item) => { - assert_eq!(item.credential_type, CredentialType::Face); - assert_eq!(item.signal, None); - } - _ => panic!("expected selfieCheckLegacy constraints to be a single item"), - } + assert_eq!( + bridge_params.legacy_verification_level, + Some(VerificationLevel::Face) + ); + assert_eq!(bridge_params.legacy_signal, None); + assert_eq!(bridge_params.identity_attributes, None); + assert!(bridge_params.constraints.is_none()); } #[test] fn device_legacy_preset_builds_orb_only_constraints_and_device_legacy_level() { let preset = Preset::device_legacy(Some("device-signal".to_string())); - let (constraints, verification_level, legacy_signal) = preset.to_bridge_params(); + let bridge_params = preset.into_bridge_params(); + + assert_eq!( + bridge_params.legacy_verification_level, + Some(VerificationLevel::Device) + ); + assert_eq!( + bridge_params.legacy_signal, + Some("device-signal".to_string()) + ); + assert_eq!(bridge_params.identity_attributes, None); + assert!(bridge_params.constraints.is_none()); + } + + #[test] + fn device_legacy_preset_without_signal_preserves_empty_signal() { + let preset = Preset::device_legacy(None); + let bridge_params = preset.into_bridge_params(); + + assert_eq!( + bridge_params.legacy_verification_level, + Some(VerificationLevel::Device) + ); + assert_eq!(bridge_params.legacy_signal, None); + assert_eq!(bridge_params.identity_attributes, None); + assert!(bridge_params.constraints.is_none()); + } + + #[test] + fn identity_check_preset_builds_document_constraints_and_preserves_attributes() { + let attributes = vec![ + IdentityAttribute::Nationality("JPN".to_string()), + IdentityAttribute::MinimumAge(21), + ]; + let preset = Preset::identity_check(attributes.clone(), false); + let bridge_params = preset.into_bridge_params(); - assert_eq!(verification_level, VerificationLevel::Device); - assert_eq!(legacy_signal, Some("device-signal".to_string())); + assert_eq!(bridge_params.legacy_verification_level, None); + assert_eq!(bridge_params.legacy_signal, None); + assert_eq!(bridge_params.identity_attributes, Some(attributes)); - match constraints { - ConstraintNode::Item(orb) => { - assert_eq!(orb.credential_type, CredentialType::ProofOfHuman); - assert_eq!(orb.signal, Some(Signal::from_string("device-signal"))); + match bridge_params.constraints { + Some(ConstraintNode::Any { any }) => { + assert_eq!(any.len(), 2); + + match &any[0] { + ConstraintNode::Item(item) => { + assert_eq!(item.credential_type, CredentialType::Passport); + assert_eq!(item.signal, None); + } + _ => panic!("expected first identityCheck constraint to be passport"), + } + + match &any[1] { + ConstraintNode::Item(item) => { + assert_eq!(item.credential_type, CredentialType::Mnc); + assert_eq!(item.signal, None); + } + _ => panic!("expected second identityCheck constraint to be mnc"), + } } - _ => panic!("expected deviceLegacy constraints to be a single orb item"), + _ => panic!("expected identityCheck constraints to be an any node"), } } #[test] - fn device_legacy_preset_without_signal_preserves_empty_signal() { - let preset = Preset::device_legacy(None); - let (constraints, verification_level, legacy_signal) = preset.to_bridge_params(); + fn identity_check_preset_with_orb_builds_enumerated_constraints_and_preserves_attributes() { + let attributes = vec![ + IdentityAttribute::IssuingCountry("JPN".to_string()), + IdentityAttribute::DocumentNumber("AB123456".to_string()), + ]; + let preset = Preset::identity_check(attributes.clone(), true); + let bridge_params = preset.into_bridge_params(); + + assert_eq!(bridge_params.legacy_verification_level, None); + assert_eq!(bridge_params.legacy_signal, None); + assert_eq!(bridge_params.identity_attributes, Some(attributes)); - assert_eq!(verification_level, VerificationLevel::Device); - assert_eq!(legacy_signal, None); + match bridge_params.constraints { + Some(ConstraintNode::All { all }) => { + assert_eq!(all.len(), 2); - match constraints { - ConstraintNode::Item(orb) => { - assert_eq!(orb.credential_type, CredentialType::ProofOfHuman); - assert_eq!(orb.signal, None); + match &all[0] { + ConstraintNode::Any { any } => { + assert_eq!(any.len(), 2); + + match &any[0] { + ConstraintNode::Item(item) => { + assert_eq!(item.credential_type, CredentialType::Passport); + assert_eq!(item.signal, None); + } + _ => panic!( + "expected first identityCheck with_orb branch to be passport" + ), + } + + match &any[1] { + ConstraintNode::Item(item) => { + assert_eq!(item.credential_type, CredentialType::Mnc); + assert_eq!(item.signal, None); + } + _ => panic!("expected second identityCheck with_orb branch to be mnc"), + } + } + _ => panic!("expected first identityCheck with_orb node to be any"), + } + + match &all[1] { + ConstraintNode::Item(item) => { + assert_eq!(item.credential_type, CredentialType::ProofOfHuman); + assert_eq!(item.signal, None); + } + _ => panic!("expected second identityCheck with_orb node to be orb"), + } } - _ => panic!("expected deviceLegacy constraints to be a single orb item"), + _ => panic!("expected identityCheck with_orb constraints to be an all node"), } } @@ -252,19 +384,20 @@ mod tests { fn orb_legacy_preset_decodes_address_shaped_signal_as_bytes() { let address = "0x3df41d9d0ba00d8fbe5a9896bb01efc4b3787b7c"; let preset = Preset::orb_legacy(Some(address.to_string())); - let (constraints, verification_level, legacy_signal) = preset.to_bridge_params(); + let bridge_params = preset.into_bridge_params(); - assert_eq!(verification_level, VerificationLevel::Orb); - assert_eq!(legacy_signal, Some(address.to_string())); + assert_eq!( + bridge_params.legacy_verification_level, + Some(VerificationLevel::Orb) + ); + assert_eq!(bridge_params.legacy_signal, Some(address.to_string())); + assert_eq!(bridge_params.identity_attributes, None); + assert!(bridge_params.constraints.is_none()); - match constraints { - ConstraintNode::Item(orb) => { - assert_eq!(orb.credential_type, CredentialType::ProofOfHuman); - let signal = orb.signal.expect("expected signal"); - assert!(matches!(signal, Signal::Bytes(_))); - assert_eq!(signal.as_bytes().len(), 20); - } - _ => panic!("expected orbLegacy constraints to be a single orb item"), - } + // Signal decoding (address → 20-byte Bytes) happens in the bridge layer + // via CachedSignalHashes::compute; verify the Signal type here independently. + let signal = Signal::from_string(address.to_string()); + assert!(matches!(signal, Signal::Bytes(_))); + assert_eq!(signal.as_bytes().len(), 20); } } diff --git a/rust/core/src/types.rs b/rust/core/src/types.rs index 3809fd2e..faa9f589 100644 --- a/rust/core/src/types.rs +++ b/rust/core/src/types.rs @@ -438,6 +438,125 @@ impl CredentialRequest { } } +/// A single identity attribute criterion for identity attestation. +/// +/// Each variant carries the expected value for that attribute. +/// Numeric variants (e.g. `MinimumAge`) serialize their value as a JSON integer; +/// all other variants serialize as a JSON string. +/// +/// Wire format: `{"type": "minimum_age", "value": 18}` +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ffi", derive(uniffi::Enum))] +pub enum IdentityAttribute { + /// The type of identity document presented + DocumentType(DocumentType), + /// Document number + DocumentNumber(String), + /// Issuing country (ISO 3166-1 alpha-3, e.g., "JPN") + IssuingCountry(String), + /// Full name as it appears on the document + FullName(String), + /// Minimum age in years + MinimumAge(u8), + /// Nationality (ISO 3166-1 alpha-3, e.g., "JPN") + Nationality(String), +} + +impl Serialize for IdentityAttribute { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use serde::ser::SerializeMap; + let mut map = serializer.serialize_map(Some(2))?; + match self { + Self::DocumentType(v) => { + map.serialize_entry("type", "document_type")?; + map.serialize_entry("value", v)?; + } + Self::DocumentNumber(v) => { + map.serialize_entry("type", "document_number")?; + map.serialize_entry("value", v)?; + } + Self::IssuingCountry(v) => { + map.serialize_entry("type", "issuing_country")?; + map.serialize_entry("value", v)?; + } + Self::FullName(v) => { + map.serialize_entry("type", "full_name")?; + map.serialize_entry("value", v)?; + } + Self::MinimumAge(v) => { + map.serialize_entry("type", "minimum_age")?; + map.serialize_entry("value", v)?; + } + Self::Nationality(v) => { + map.serialize_entry("type", "nationality")?; + map.serialize_entry("value", v)?; + } + } + map.end() + } +} + +impl<'de> Deserialize<'de> for IdentityAttribute { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper { + #[serde(rename = "type")] + attribute_type: String, + value: serde_json::Value, + } + + let h = Helper::deserialize(deserializer)?; + let str_val = |attr: &str| { + h.value.as_str().map(String::from).ok_or_else(|| { + serde::de::Error::custom(format!("expected string value for {attr}")) + }) + }; + let u8_val = |attr: &str| { + let n = h.value.as_u64().ok_or_else(|| { + serde::de::Error::custom(format!("expected integer value for {attr}")) + })?; + u8::try_from(n).map_err(|_| { + serde::de::Error::custom(format!("value {n} is out of range for {attr}")) + }) + }; + + match h.attribute_type.as_str() { + "document_type" => { + let doc_type = serde_json::from_value::(h.value) + .map_err(serde::de::Error::custom)?; + Ok(Self::DocumentType(doc_type)) + } + "document_number" => Ok(Self::DocumentNumber(str_val("document_number")?)), + "issuing_country" => Ok(Self::IssuingCountry(str_val("issuing_country")?)), + "full_name" => Ok(Self::FullName(str_val("full_name")?)), + "minimum_age" => Ok(Self::MinimumAge(u8_val("minimum_age")?)), + "nationality" => Ok(Self::Nationality(str_val("nationality")?)), + other => Err(serde::de::Error::custom(format!( + "unknown identity attribute type: {other}" + ))), + } + } +} + +/// Identity document type used in [`IdentityAttribute::DocumentType`]. +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] +#[serde(rename_all = "snake_case")] +#[cfg_attr(feature = "ffi", derive(uniffi::Enum))] +pub enum DocumentType { + /// Biometric passport (ICAO 9303) + Passport, + /// National electronic identity card + Eid, + /// Japan's My Number Card + Mnc, +} + /// Legacy bridge response (protocol v1 / World ID v3) #[derive(Debug, Clone, PartialEq, Eq, Serialize)] #[cfg_attr(feature = "ffi", derive(uniffi::Record))] @@ -594,6 +713,11 @@ pub struct IDKitResult { /// The environment used for this request ("production" or "staging") pub environment: String, + + /// Whether identity attributes were attested. + /// Only present on responses from an `IdentityCheck` request. + #[serde(skip_serializing_if = "Option::is_none")] + pub identity_attested: Option, } impl IDKitResult { @@ -615,6 +739,7 @@ impl IDKitResult { session_id: None, responses, environment: environment.into(), + identity_attested: None, } } @@ -636,6 +761,7 @@ impl IDKitResult { session_id: Some(session_id), responses, environment: environment.into(), + identity_attested: None, } } @@ -1327,4 +1453,82 @@ mod tests { let prod_app = AppId::new("app_123").unwrap(); assert!(BridgeUrl::new("http://localhost:3000", &prod_app).is_err()); } + + // ───────────────────────────────────────────────────────────────────────── + // IdentityAttribute serialization / deserialization + // ───────────────────────────────────────────────────────────────────────── + + #[test] + fn test_identity_attribute_string_variants_serialize() { + let cases: &[(IdentityAttribute, &str)] = &[ + ( + IdentityAttribute::DocumentType(DocumentType::Passport), + r#"{"type":"document_type","value":"passport"}"#, + ), + ( + IdentityAttribute::DocumentType(DocumentType::Eid), + r#"{"type":"document_type","value":"eid"}"#, + ), + ( + IdentityAttribute::DocumentType(DocumentType::Mnc), + r#"{"type":"document_type","value":"mnc"}"#, + ), + ( + IdentityAttribute::DocumentNumber("X12345678".into()), + r#"{"type":"document_number","value":"X12345678"}"#, + ), + ( + IdentityAttribute::IssuingCountry("JPN".into()), + r#"{"type":"issuing_country","value":"JPN"}"#, + ), + ( + IdentityAttribute::FullName("John Smith".into()), + r#"{"type":"full_name","value":"John Smith"}"#, + ), + ( + IdentityAttribute::Nationality("JPN".into()), + r#"{"type":"nationality","value":"JPN"}"#, + ), + ]; + for (attr, expected) in cases { + assert_eq!(serde_json::to_string(attr).unwrap(), *expected); + } + } + + #[test] + fn test_identity_attribute_roundtrip() { + let attrs = vec![ + IdentityAttribute::DocumentType(DocumentType::Passport), + IdentityAttribute::DocumentType(DocumentType::Eid), + IdentityAttribute::DocumentType(DocumentType::Mnc), + IdentityAttribute::DocumentNumber("X12345678".into()), + IdentityAttribute::IssuingCountry("JPN".into()), + IdentityAttribute::FullName("John Smith".into()), + IdentityAttribute::MinimumAge(18), + IdentityAttribute::Nationality("JPN".into()), + ]; + for attr in &attrs { + let json = serde_json::to_string(attr).unwrap(); + let back: IdentityAttribute = serde_json::from_str(&json).unwrap(); + assert_eq!(attr, &back); + } + } + + #[test] + fn test_identity_attribute_full_array_matches_spec() { + let json = r#"[ + {"type":"document_type","value":"passport"}, + {"type":"document_number","value":"X12345678"}, + {"type":"issuing_country","value":"JPN"}, + {"type":"full_name","value":"John Smith"}, + {"type":"minimum_age","value":18}, + {"type":"nationality","value":"JPN"} + ]"#; + let attrs: Vec = serde_json::from_str(json).unwrap(); + assert_eq!( + attrs[0], + IdentityAttribute::DocumentType(DocumentType::Passport) + ); + assert_eq!(attrs[4], IdentityAttribute::MinimumAge(18)); + } } diff --git a/rust/core/src/wasm_bindings.rs b/rust/core/src/wasm_bindings.rs index 1cdbd9f3..e11fa0d6 100644 --- a/rust/core/src/wasm_bindings.rs +++ b/rust/core/src/wasm_bindings.rs @@ -507,7 +507,7 @@ impl IDKitConfigWasm { #[allow(clippy::too_many_lines)] fn to_params( &self, - constraints: ConstraintNode, + constraints: Option, ) -> Result { match self { Self::Request { @@ -534,7 +534,7 @@ impl IDKitConfigWasm { kind: crate::bridge::RequestKind::Uniqueness { action: action.clone(), }, - constraints: Some(constraints), + constraints, rp_context: rp_context.clone(), action_description: action_description.clone(), // Default to Device for v3 backwards compat — v4 uses proof_request instead. @@ -549,6 +549,7 @@ impl IDKitConfigWasm { "staging" => crate::bridge::Environment::Staging, _ => crate::bridge::Environment::Production, }), + identity_attributes: None, }) } Self::CreateSession { @@ -571,7 +572,7 @@ impl IDKitConfigWasm { Ok(crate::bridge::BridgeConnectionParams { app_id, kind: crate::bridge::RequestKind::CreateSession, - constraints: Some(constraints), + constraints, rp_context: rp_context.clone(), action_description: action_description.clone(), // Default to Device for v3 backwards compat — v4 uses proof_request instead. @@ -586,6 +587,7 @@ impl IDKitConfigWasm { "staging" => crate::bridge::Environment::Staging, _ => crate::bridge::Environment::Production, }), + identity_attributes: None, }) } Self::ProveSession { @@ -611,7 +613,7 @@ impl IDKitConfigWasm { kind: crate::bridge::RequestKind::ProveSession { session_id: session_id.clone(), }, - constraints: Some(constraints), + constraints, rp_context: rp_context.clone(), action_description: action_description.clone(), // Default to Device for v3 backwards compat — v4 uses proof_request instead. @@ -626,6 +628,7 @@ impl IDKitConfigWasm { "staging" => crate::bridge::Environment::Staging, _ => crate::bridge::Environment::Production, }), + identity_attributes: None, }) } } @@ -640,11 +643,16 @@ impl IDKitConfigWasm { "Presets are not supported for session flows. Use .constraints() instead.", )); } - let (constraints, legacy_verification_level, legacy_signal) = preset.to_bridge_params(); - let mut params = self.to_params(constraints)?; - params.constraints = None; - params.legacy_verification_level = legacy_verification_level; - params.legacy_signal = legacy_signal.unwrap_or_default(); + let bridge_params = preset.into_bridge_params(); + let mut params = self.to_params(bridge_params.constraints)?; + params.legacy_verification_level = bridge_params + .legacy_verification_level + .unwrap_or(crate::VerificationLevel::Device); + params.legacy_signal = bridge_params.legacy_signal.unwrap_or_default(); + params.identity_attributes = bridge_params.identity_attributes; + if let Some(v) = bridge_params.allow_legacy_proofs_override { + params.allow_legacy_proofs = v; + } Ok(params) } } @@ -752,7 +760,7 @@ impl IDKitBuilderWasm { let constraints: ConstraintNode = serde_wasm_bindgen::from_value(constraints_json) .map_err(|e| JsValue::from_str(&format!("Invalid constraints: {e}")))?; - let params = self.config.to_params(constraints)?; + let params = self.config.to_params(Some(constraints))?; let payload = crate::bridge::build_request_payload(¶ms, true) .map_err(|e| JsValue::from_str(&format!("Failed to build payload: {e}")))?; @@ -903,7 +911,7 @@ impl IDKitBuilderWasm { let constraints: ConstraintNode = serde_wasm_bindgen::from_value(constraints_json) .map_err(|e| JsValue::from_str(&format!("Invalid constraints: {e}")))?; - let params = config.to_params(constraints)?; + let params = config.to_params(Some(constraints))?; let connection = crate::bridge::BridgeConnection::create(params) .await .map_err(|e| JsValue::from_str(&format!("Failed: {e}")))?; @@ -1447,7 +1455,7 @@ mod tests { }; let params = config - .to_params(ConstraintNode::Any { any: Vec::new() }) + .to_params(Some(ConstraintNode::Any { any: Vec::new() })) .expect("request params"); assert_eq!( @@ -1469,7 +1477,7 @@ mod tests { }; let params = config - .to_params(ConstraintNode::Any { any: Vec::new() }) + .to_params(Some(ConstraintNode::Any { any: Vec::new() })) .expect("create session params"); assert_eq!( @@ -1492,7 +1500,7 @@ mod tests { }; let params = config - .to_params(ConstraintNode::Any { any: Vec::new() }) + .to_params(Some(ConstraintNode::Any { any: Vec::new() })) .expect("prove session params"); assert_eq!( diff --git a/swift/Tests/IDKitTests/IDKitTests.swift b/swift/Tests/IDKitTests/IDKitTests.swift index 7dd473e3..e1628e14 100644 --- a/swift/Tests/IDKitTests/IDKitTests.swift +++ b/swift/Tests/IDKitTests/IDKitTests.swift @@ -25,7 +25,8 @@ private func sampleResult(sessionId: String? = nil) -> IDKitResult { actionDescription: "Sample action", sessionId: sessionId, responses: [], - environment: "production" + environment: "production", + identityAttested: nil ) } @@ -244,35 +245,35 @@ func legacyPresetHelpers() { switch orb { case .orbLegacy(let signal): #expect(signal == "x") - case .secureDocumentLegacy, .documentLegacy, .deviceLegacy, .selfieCheckLegacy: + case .secureDocumentLegacy, .documentLegacy, .deviceLegacy, .selfieCheckLegacy, .identityCheck: Issue.record("Expected orbLegacy preset") } switch secureDoc { case .secureDocumentLegacy(let signal): #expect(signal == "y") - case .orbLegacy, .documentLegacy, .deviceLegacy, .selfieCheckLegacy: + case .orbLegacy, .documentLegacy, .deviceLegacy, .selfieCheckLegacy, .identityCheck: Issue.record("Expected secureDocumentLegacy preset") } switch doc { case .documentLegacy(let signal): #expect(signal == "z") - case .orbLegacy, .secureDocumentLegacy, .deviceLegacy, .selfieCheckLegacy: + case .orbLegacy, .secureDocumentLegacy, .deviceLegacy, .selfieCheckLegacy, .identityCheck: Issue.record("Expected documentLegacy preset") } switch device { case .deviceLegacy(let signal): #expect(signal == "d") - case .orbLegacy, .secureDocumentLegacy, .documentLegacy, .selfieCheckLegacy: + case .orbLegacy, .secureDocumentLegacy, .documentLegacy, .selfieCheckLegacy, .identityCheck: Issue.record("Expected deviceLegacy preset") } switch face { case .selfieCheckLegacy(let signal): #expect(signal == "f") - case .orbLegacy, .secureDocumentLegacy, .documentLegacy, .deviceLegacy: + case .orbLegacy, .secureDocumentLegacy, .documentLegacy, .deviceLegacy, .identityCheck: Issue.record("Expected selfieCheckLegacy preset") } } From 04c7c3d431c3b5824188fdcc1b54e760d345f0ce Mon Sep 17 00:00:00 2001 From: Vladimir Burdukov Date: Tue, 21 Apr 2026 12:17:59 +0300 Subject: [PATCH 06/12] feat(wasm/js): expose IdentityCheck in WASM and TS + identity_attested field --- js/packages/core/src/__tests__/smoke.test.ts | 71 +++++++++++++++ js/packages/core/src/index.ts | 4 + js/packages/core/src/lib/wasm.ts | 3 + js/packages/core/src/request.ts | 26 ++++++ rust/core/src/wasm_bindings.rs | 93 +++++++++++++++++++- 5 files changed, 194 insertions(+), 3 deletions(-) diff --git a/js/packages/core/src/__tests__/smoke.test.ts b/js/packages/core/src/__tests__/smoke.test.ts index 7887abd7..c8c5a52b 100644 --- a/js/packages/core/src/__tests__/smoke.test.ts +++ b/js/packages/core/src/__tests__/smoke.test.ts @@ -12,6 +12,7 @@ import { orbLegacy, deviceLegacy, selfieCheckLegacy, + identityCheck, isNode, IDKitErrorCodes, signRequest, @@ -93,6 +94,36 @@ describe("IDKitRequest API", () => { expect(preset).toHaveProperty("signal", "device-signal"); }); + it("should create identityCheck preset correctly", () => { + const preset = identityCheck({ + attributes: [ + { type: "minimum_age", value: 21 }, + { type: "nationality", value: "JPN" }, + ], + require_proof_of_humanity: false, + }); + expect(preset).toEqual({ + type: "IdentityCheck", + attributes: [ + { type: "minimum_age", value: 21 }, + { type: "nationality", value: "JPN" }, + ], + require_proof_of_humanity: false, + }); + }); + + it("should create identityCheck preset with proof-of-humanity requirement correctly", () => { + const preset = identityCheck({ + attributes: [{ type: "document_number", value: "AB123456" }], + require_proof_of_humanity: true, + }); + expect(preset).toEqual({ + type: "IdentityCheck", + attributes: [{ type: "document_number", value: "AB123456" }], + require_proof_of_humanity: true, + }); + }); + it("should throw error when rp_context is missing", () => { expect(() => IDKit.request({ @@ -187,6 +218,46 @@ describe("IDKitRequest API", () => { expect(result.payload.signal).toBe(rawAddressSignalHash); expect(result.legacy_signal_hash).toBe(rawAddressSignalHash); }); + + it("should include identity attributes in native payload from preset", () => { + const rpContext = new WasmModule.RpContextWasm( + "rp_123456789abcdef0", + "0x0000000000000000000000000000000000000000000000000000000000000001", + 1n, + 2n, + "0x" + "00".repeat(64) + "1b", + ); + const builder = WasmModule.request( + "app_staging_test", + "test-action", + rpContext, + null, + null, + false, + null, + null, + null, + ); + + const result = builder.nativePayloadFromPreset( + identityCheck({ + attributes: [ + { type: "minimum_age", value: 21 }, + { type: "nationality", value: "JPN" }, + ], + require_proof_of_humanity: false, + }), + ) as { + payload: { + identity_attributes: Array<{ type: string; value: number | string }>; + }; + }; + + expect(result.payload.identity_attributes).toEqual([ + { type: "minimum_age", value: 21 }, + { type: "nationality", value: "JPN" }, + ]); + }); }); describe("Enums", () => { diff --git a/js/packages/core/src/index.ts b/js/packages/core/src/index.ts index 8cd12fc6..488c739d 100644 --- a/js/packages/core/src/index.ts +++ b/js/packages/core/src/index.ts @@ -18,17 +18,21 @@ export { documentLegacy, deviceLegacy, selfieCheckLegacy, + identityCheck, // Types type IDKitRequest, type IDKitCompletionResult, type WaitOptions, type RpContext, type Preset, + type IdentityAttribute, + type DocumentType, type OrbLegacyPreset, type SecureDocumentLegacyPreset, type DocumentLegacyPreset, type DeviceLegacyPreset, type SelfieCheckLegacyPreset, + type IdentityCheckPreset, } from "./request"; // Config types diff --git a/js/packages/core/src/lib/wasm.ts b/js/packages/core/src/lib/wasm.ts index b3fde565..dabe8f06 100644 --- a/js/packages/core/src/lib/wasm.ts +++ b/js/packages/core/src/lib/wasm.ts @@ -60,6 +60,8 @@ export type { CredentialType, ConstraintNode, CredentialRequestType, + DocumentType, + IdentityAttribute, // Preset types Preset, OrbLegacyPreset, @@ -67,6 +69,7 @@ export type { DocumentLegacyPreset, DeviceLegacyPreset, SelfieCheckLegacyPreset, + IdentityCheckPreset, // Native transport types NativePayloadResult, } from "../../wasm/idkit_wasm.js"; diff --git a/js/packages/core/src/request.ts b/js/packages/core/src/request.ts index 303eacc5..d436565d 100644 --- a/js/packages/core/src/request.ts +++ b/js/packages/core/src/request.ts @@ -227,21 +227,26 @@ export function enumerate(...nodes: ConstraintNode[]): { // Re-export preset types from WASM (source of truth in rust/core/src/wasm_bindings.rs) export type { Preset, + IdentityAttribute, + DocumentType, OrbLegacyPreset, SecureDocumentLegacyPreset, DocumentLegacyPreset, SelfieCheckLegacyPreset, DeviceLegacyPreset, + IdentityCheckPreset, } from "./lib/wasm"; // Import WASM preset type for function return types import type { Preset, + IdentityAttribute, OrbLegacyPreset, SecureDocumentLegacyPreset, DocumentLegacyPreset, SelfieCheckLegacyPreset, DeviceLegacyPreset, + IdentityCheckPreset, } from "./lib/wasm"; /** @@ -345,6 +350,25 @@ export function selfieCheckLegacy( return { type: "SelfieCheckLegacy", signal: opts.signal }; } +/** + * Creates an IdentityCheck preset for document-based identity attestation. + * + * This preset requires World ID 4.0-compatible clients. + * + * @param params - Identity attribute filters and proof-of-humanity requirement + * @returns An IdentityCheck preset + */ +export function identityCheck(params: { + attributes: IdentityAttribute[]; + require_proof_of_humanity: boolean; +}): IdentityCheckPreset { + return { + type: "IdentityCheck", + attributes: params.attributes, + require_proof_of_humanity: params.require_proof_of_humanity, + }; +} + // ───────────────────────────────────────────────────────────────────────────── // WASM builder factory (used for both native and bridge paths) // ───────────────────────────────────────────────────────────────────────────── @@ -779,4 +803,6 @@ export const IDKit = { deviceLegacy, /** Create a SelfieCheckLegacy preset for face verification */ selfieCheckLegacy, + /** Create an IdentityCheck preset for World ID 4.0 identity attestation */ + identityCheck, }; diff --git a/rust/core/src/wasm_bindings.rs b/rust/core/src/wasm_bindings.rs index e11fa0d6..7cfe9136 100644 --- a/rust/core/src/wasm_bindings.rs +++ b/rust/core/src/wasm_bindings.rs @@ -657,6 +657,16 @@ impl IDKitConfigWasm { } } +fn validate_v1_preset_support(preset: &Preset) -> Result<(), &'static str> { + if matches!(preset, Preset::IdentityCheck { .. }) { + return Err( + "IdentityCheck presets are not supported for nativePayloadV1FromPreset. Use nativePayloadFromPreset with a World ID 4.0-compatible client instead.", + ); + } + + Ok(()) +} + /// Unified builder for creating `IDKit` requests and sessions (WASM) #[wasm_bindgen(js_name = IDKitBuilder)] pub struct IDKitBuilderWasm { @@ -865,6 +875,8 @@ impl IDKitBuilderWasm { let preset: Preset = serde_wasm_bindgen::from_value(preset_json) .map_err(|e| JsValue::from_str(&format!("Invalid preset: {e}")))?; + validate_v1_preset_support(&preset).map_err(JsValue::from_str)?; + let params = self.config.to_params_from_preset(preset)?; let payload = crate::bridge::build_native_v1_payload(¶ms) @@ -1229,6 +1241,8 @@ export interface IDKitResultV4 { responses: ResponseItemV4[]; /** The environment used for this request ("production" or "staging") */ environment: string; + /** Whether identity attributes were attested. Only present on IdentityCheck responses. */ + identity_attested?: boolean; } /** V4 result for session proofs */ @@ -1318,6 +1332,16 @@ export type Status = // Export preset types #[wasm_bindgen(typescript_custom_section)] const TS_PRESET: &str = r#" +export type DocumentType = "passport" | "eid" | "mnc"; + +export type IdentityAttribute = + | { type: "document_type"; value: DocumentType } + | { type: "document_number"; value: string } + | { type: "issuing_country"; value: string } + | { type: "full_name"; value: string } + | { type: "minimum_age"; value: number } + | { type: "nationality"; value: string }; + export interface OrbLegacyPreset { /** This preset only returns World ID 3.0 proofs. Use it for compatibility with older IDKit versions. */ type: "OrbLegacy"; @@ -1349,7 +1373,20 @@ export interface DeviceLegacyPreset { signal?: string; } -export type Preset = OrbLegacyPreset | SecureDocumentLegacyPreset | DocumentLegacyPreset | SelfieCheckLegacyPreset | DeviceLegacyPreset; +export interface IdentityCheckPreset { + /** This preset requires World ID 4.0-compatible clients. */ + type: "IdentityCheck"; + attributes: IdentityAttribute[]; + require_proof_of_humanity: boolean; +} + +export type Preset = + | OrbLegacyPreset + | SecureDocumentLegacyPreset + | DocumentLegacyPreset + | SelfieCheckLegacyPreset + | DeviceLegacyPreset + | IdentityCheckPreset; export function orbLegacy(signal?: string): Preset; export function secureDocumentLegacy(signal?: string): Preset; @@ -1357,6 +1394,10 @@ export function documentLegacy(signal?: string): Preset; /** Preview: Selfie Check is currently in preview. Contact us if you need it enabled. */ export function selfieCheckLegacy(signal?: string): Preset; export function deviceLegacy(signal?: string): Preset; +export function identityCheck(params: { + attributes: IdentityAttribute[]; + require_proof_of_humanity: boolean; +}): Preset; "#; // Export RP signature types @@ -1433,13 +1474,27 @@ export function proveSession( #[cfg(test)] mod tests { - use super::IDKitConfigWasm; - use crate::{ConstraintNode, RpContext}; + use super::{validate_v1_preset_support, IDKitConfigWasm}; + use crate::{types::IdentityAttribute, ConstraintNode, Preset, RpContext}; fn sample_rp_context() -> RpContext { RpContext::new("rp_123456789abcdef0", "0x01", 1, 2, "0x1234").expect("valid rp_context") } + fn sample_request_config() -> IDKitConfigWasm { + IDKitConfigWasm::Request { + app_id: "app_staging_test".to_string(), + action: "test-action".to_string(), + rp_context: sample_rp_context(), + action_description: None, + bridge_url: None, + allow_legacy_proofs: false, + override_connect_base_url: None, + return_to: None, + environment: None, + } + } + #[test] fn request_params_preserve_return_to() { let config = IDKitConfigWasm::Request { @@ -1508,4 +1563,36 @@ mod tests { Some("idkit://callback?step=prove") ); } + + #[test] + fn native_payload_v1_from_preset_rejects_identity_check() { + let preset = Preset::identity_check(vec![IdentityAttribute::MinimumAge(21)], false); + + assert!(validate_v1_preset_support(&preset) + .expect_err("identity check should be rejected for v1") + .contains("IdentityCheck presets are not supported")); + } + + #[test] + fn native_payload_v1_from_preset_rejects_identity_check_with_orb() { + let preset = Preset::identity_check( + vec![IdentityAttribute::Nationality("JPN".to_string())], + true, + ); + + assert!(validate_v1_preset_support(&preset) + .expect_err("identity check with orb should be rejected for v1") + .contains("IdentityCheck presets are not supported")); + } + + #[test] + fn native_payload_v1_from_preset_allows_legacy_presets() { + let config = sample_request_config(); + let preset = Preset::device_legacy(Some("device-signal".to_string())); + + validate_v1_preset_support(&preset).expect("legacy preset should be allowed for v1"); + config + .to_params_from_preset(preset) + .expect("legacy preset should produce a v1 payload"); + } } From 4c3b16191c1452add2dc6b4b9474416a575592b7 Mon Sep 17 00:00:00 2001 From: Takis Kakalis <80459599+Takaros999@users.noreply.github.com> Date: Fri, 1 May 2026 11:53:34 -0700 Subject: [PATCH 07/12] feat(examples): expose identity check preset --- js/examples/nextjs/app/globals.css | 7 + js/examples/nextjs/app/page.tsx | 4 +- js/examples/nextjs/app/ui.tsx | 457 +++++++++++++++++++++++++---- js/packages/react/src/index.ts | 4 + 4 files changed, 413 insertions(+), 59 deletions(-) diff --git a/js/examples/nextjs/app/globals.css b/js/examples/nextjs/app/globals.css index e2de06a5..419ce504 100644 --- a/js/examples/nextjs/app/globals.css +++ b/js/examples/nextjs/app/globals.css @@ -83,6 +83,11 @@ button.secondary { color: var(--text-color); } +button:disabled { + cursor: not-allowed; + opacity: 0.55; +} + pre { background: var(--code-bg); color: var(--code-text); @@ -136,6 +141,8 @@ pre { } .config-row input[type="text"], +.config-row input[type="number"], +.config-row input[type="datetime-local"], .config-row select { flex: 1; font-family: monospace; diff --git a/js/examples/nextjs/app/page.tsx b/js/examples/nextjs/app/page.tsx index f39fb04a..0c2244c2 100644 --- a/js/examples/nextjs/app/page.tsx +++ b/js/examples/nextjs/app/page.tsx @@ -6,8 +6,8 @@ export default function HomePage(): ReactElement {

IDKit Next.js Example

- This example shows the widget request flow with the same legacy presets - as the browser example. + This example shows the widget request flow for legacy presets, World ID + 4.0 credential requests, and identity checks.

diff --git a/js/examples/nextjs/app/ui.tsx b/js/examples/nextjs/app/ui.tsx index a8ca40aa..9e824631 100644 --- a/js/examples/nextjs/app/ui.tsx +++ b/js/examples/nextjs/app/ui.tsx @@ -5,15 +5,18 @@ import { CredentialRequest, documentLegacy, deviceLegacy, + identityCheck, selfieCheckLegacy, IDKitRequestWidget, orbLegacy, secureDocumentLegacy, setDebug, type ConstraintNode, + type DocumentType, type IDKitResult, + type IdentityAttribute, + type Preset, type RpContext, - Preset, } from "@worldcoin/idkit"; setDebug(true); @@ -32,11 +35,30 @@ const RETURN_TO_TOOLTIP = type PresetKind = "orb" | "secure_document" | "document" | "device" | "selfie"; -type V4CredentialType = "proof_of_human" | "passport"; +type V4CredentialType = "proof_of_human" | "passport" | "identity_check"; + +type IdentityAttributesConfig = { + document_type: { enabled: boolean; value: DocumentType }; + document_number: { enabled: boolean; value: string }; + issuing_country: { enabled: boolean; value: string }; + full_name: { enabled: boolean; value: string }; + minimum_age: { enabled: boolean; value: string }; + nationality: { enabled: boolean; value: string }; +}; + +const DEFAULT_IDENTITY_ATTRIBUTES: IdentityAttributesConfig = { + document_type: { enabled: true, value: "passport" }, + document_number: { enabled: false, value: "" }, + issuing_country: { enabled: false, value: "" }, + full_name: { enabled: false, value: "" }, + minimum_age: { enabled: false, value: "" }, + nationality: { enabled: false, value: "" }, +}; const V4_CREDENTIAL_TO_NAME: Record = { proof_of_human: "Proof of Human", passport: "Passport", + identity_check: "Identity Check", }; const PRESET_KIND_TO_NAME: Record = { @@ -47,6 +69,54 @@ const PRESET_KIND_TO_NAME: Record = { selfie: "Selfie Check", }; +function normalizeAlpha3(value: string): string { + return value.trim().toUpperCase(); +} + +function buildIdentityAttributes( + config: IdentityAttributesConfig, +): IdentityAttribute[] { + const attributes: IdentityAttribute[] = []; + + if (config.document_type.enabled) { + attributes.push({ + type: "document_type", + value: config.document_type.value, + }); + } + + const documentNumber = config.document_number.value.trim(); + if (config.document_number.enabled && documentNumber.length > 0) { + attributes.push({ type: "document_number", value: documentNumber }); + } + + const issuingCountry = normalizeAlpha3(config.issuing_country.value); + if (config.issuing_country.enabled && issuingCountry.length > 0) { + attributes.push({ type: "issuing_country", value: issuingCountry }); + } + + const fullName = config.full_name.value.trim(); + if (config.full_name.enabled && fullName.length > 0) { + attributes.push({ type: "full_name", value: fullName }); + } + + const minimumAge = Number(config.minimum_age.value); + if ( + config.minimum_age.enabled && + Number.isInteger(minimumAge) && + minimumAge > 0 + ) { + attributes.push({ type: "minimum_age", value: minimumAge }); + } + + const nationality = normalizeAlpha3(config.nationality.value); + if (config.nationality.enabled && nationality.length > 0) { + attributes.push({ type: "nationality", value: nationality }); + } + + return attributes; +} + function createChromeAppDeeplink(url: string): string { const parsed = new URL(url); return parsed.protocol === "https:" ? "googlechromes://" : "googlechrome://"; @@ -145,6 +215,9 @@ export function DemoClient(): ReactElement { const [worldIdVersion, setWorldIdVersion] = useState<"3.0" | "4.0">("3.0"); const [v4CredentialType, setV4CredentialType] = useState("proof_of_human"); + const [identityAttributes, setIdentityAttributes] = + useState(DEFAULT_IDENTITY_ATTRIBUTES); + const [requireProofOfHumanity, setRequireProofOfHumanity] = useState(false); const [presetKind, setPresetKind] = useState("orb"); const [genesisEnabled, setGenesisEnabled] = useState(false); const [genesisDate, setGenesisDate] = useState(""); @@ -158,6 +231,16 @@ export function DemoClient(): ReactElement { ? Math.floor(new Date(genesisDate).getTime() / 1000) : undefined; + const identityAttributesPayload = useMemo( + () => buildIdentityAttributes(identityAttributes), + [identityAttributes], + ); + + const isIdentityCheck = + worldIdVersion === "4.0" && v4CredentialType === "identity_check"; + const canStartWidgetFlow = + !isIdentityCheck || identityAttributesPayload.length > 0; + const widgetConstraintsOrPreset: | { constraints: ConstraintNode; @@ -167,17 +250,26 @@ export function DemoClient(): ReactElement { } = useMemo( () => worldIdVersion === "4.0" - ? { - constraints: CredentialRequest(v4CredentialType, { - genesis_issued_at_min: genesisIssuedAtMin, - }), - } + ? v4CredentialType === "identity_check" + ? { + preset: identityCheck({ + attributes: identityAttributesPayload, + require_proof_of_humanity: requireProofOfHumanity, + }), + } + : { + constraints: CredentialRequest(v4CredentialType, { + genesis_issued_at_min: genesisIssuedAtMin, + }), + } : { preset: createPreset(presetKind, widgetSignal) }, [ worldIdVersion, presetKind, v4CredentialType, genesisIssuedAtMin, + identityAttributesPayload, + requireProofOfHumanity, widgetSignal, ], ); @@ -208,9 +300,18 @@ export function DemoClient(): ReactElement { if (worldIdVersion !== "4.0") { setGenesisEnabled(false); setGenesisDate(""); + setIdentityAttributes(DEFAULT_IDENTITY_ATTRIBUTES); + setRequireProofOfHumanity(false); } }, [worldIdVersion]); + useEffect(() => { + if (v4CredentialType === "identity_check") { + setGenesisEnabled(false); + setGenesisDate(""); + } + }, [v4CredentialType]); + useEffect(() => { if (typeof window === "undefined") { return; @@ -228,6 +329,11 @@ export function DemoClient(): ReactElement { setWidgetVerifyResult(null); setWidgetIdkitResult(null); + if (!canStartWidgetFlow) { + setWidgetError("Select at least one identity attribute."); + return; + } + try { const rpContext = await fetchRpContext(action || "test-action"); setWidgetSignal(`demo-signal-${Date.now()}`); @@ -385,58 +491,292 @@ export function DemoClient(): ReactElement { > +

-
- -
setIsGenesisTooltipOpen(true)} - onMouseLeave={() => setIsGenesisTooltipOpen(false)} - > - - {isGenesisTooltipOpen && ( - +
+ +
setIsGenesisTooltipOpen(true)} + onMouseLeave={() => setIsGenesisTooltipOpen(false)} > - {GENESIS_ISSUED_AT_MIN_TOOLTIP} - - )} -
- setGenesisEnabled(e.target.checked)} - /> - {genesisEnabled && ( - setGenesisDate(e.target.value)} - /> - )} -
+ + {isGenesisTooltipOpen && ( + + {GENESIS_ISSUED_AT_MIN_TOOLTIP} + + )} +
+ setGenesisEnabled(e.target.checked)} + /> + {genesisEnabled && ( + setGenesisDate(e.target.value)} + /> + )} +
+ + )} + {v4CredentialType === "identity_check" && ( + <> +
+ + + setRequireProofOfHumanity(e.target.checked) + } + /> +
+
+ + + setIdentityAttributes((current) => ({ + ...current, + document_type: { + ...current.document_type, + enabled: e.target.checked, + }, + })) + } + /> + {identityAttributes.document_type.enabled && ( + + )} +
+
+ + + setIdentityAttributes((current) => ({ + ...current, + document_number: { + ...current.document_number, + enabled: e.target.checked, + }, + })) + } + /> + {identityAttributes.document_number.enabled && ( + + setIdentityAttributes((current) => ({ + ...current, + document_number: { + ...current.document_number, + value: e.target.value, + }, + })) + } + placeholder="A1234567" + /> + )} +
+
+ + + setIdentityAttributes((current) => ({ + ...current, + issuing_country: { + ...current.issuing_country, + enabled: e.target.checked, + }, + })) + } + /> + {identityAttributes.issuing_country.enabled && ( + + setIdentityAttributes((current) => ({ + ...current, + issuing_country: { + ...current.issuing_country, + value: normalizeAlpha3(e.target.value), + }, + })) + } + maxLength={3} + placeholder="JPN" + /> + )} +
+
+ + + setIdentityAttributes((current) => ({ + ...current, + full_name: { + ...current.full_name, + enabled: e.target.checked, + }, + })) + } + /> + {identityAttributes.full_name.enabled && ( + + setIdentityAttributes((current) => ({ + ...current, + full_name: { + ...current.full_name, + value: e.target.value, + }, + })) + } + placeholder="Jane Doe" + /> + )} +
+
+ + + setIdentityAttributes((current) => ({ + ...current, + minimum_age: { + ...current.minimum_age, + enabled: e.target.checked, + }, + })) + } + /> + {identityAttributes.minimum_age.enabled && ( + + setIdentityAttributes((current) => ({ + ...current, + minimum_age: { + ...current.minimum_age, + value: e.target.value, + }, + })) + } + min={1} + max={255} + placeholder="18" + /> + )} +
+
+ + + setIdentityAttributes((current) => ({ + ...current, + nationality: { + ...current.nationality, + enabled: e.target.checked, + }, + })) + } + /> + {identityAttributes.nationality.enabled && ( + + setIdentityAttributes((current) => ({ + ...current, + nationality: { + ...current.nationality, + value: normalizeAlpha3(e.target.value), + }, + })) + } + maxLength={3} + placeholder="JPN" + /> + )} +
+ + )} )}
@@ -498,12 +838,15 @@ export function DemoClient(): ReactElement { {worldIdVersion === "4.0" && ( <> - )}
+ {isIdentityCheck && identityAttributesPayload.length === 0 && ( +

Select at least one identity attribute.

+ )} {widgetError &&

Error: {widgetError}

} {widgetRpContext && ( diff --git a/js/packages/react/src/index.ts b/js/packages/react/src/index.ts index 5d889a71..8f2fa86c 100644 --- a/js/packages/react/src/index.ts +++ b/js/packages/react/src/index.ts @@ -30,6 +30,7 @@ export { secureDocumentLegacy, deviceLegacy, selfieCheckLegacy, + identityCheck, IDKitErrorCodes, signRequest, isDebug, @@ -50,4 +51,7 @@ export type { ResponseItemV4, ResponseItemSession, IDKitErrorCode, + DocumentType, + IdentityAttribute, + IdentityCheckPreset, } from "@worldcoin/idkit-core"; From 1f34c6d3dc58e1dbd9a17587b9a949f91bfd4fbb Mon Sep 17 00:00:00 2001 From: Vladimir Burdukov Date: Tue, 28 Apr 2026 19:12:47 +0300 Subject: [PATCH 08/12] feat(examples): add IdentityCheck preset support to sample apps - Add World ID version + request type selectors (Credential Request / Identity Check) - Interactive identity attribute picker with ISO 3166-1 alpha-3 country validation - Align layout, field sizes, and staging URL override across both examples --- js/examples/browser/index.html | 545 ++++- js/examples/browser/package.json | 3 +- js/examples/nextjs/app/globals.css | 5 +- js/examples/nextjs/app/ui.tsx | 141 +- js/examples/nextjs/package.json | 1 + pnpm-lock.yaml | 3443 ++++++++++++++++++---------- 6 files changed, 2842 insertions(+), 1296 deletions(-) diff --git a/js/examples/browser/index.html b/js/examples/browser/index.html index fb6eef5a..8f8e347e 100644 --- a/js/examples/browser/index.html +++ b/js/examples/browser/index.html @@ -23,6 +23,14 @@ --border-color: #ddd; } + * { + box-sizing: border-box; + } + + [hidden] { + display: none !important; + } + body { background-color: var(--bg-color); color: var(--text-color); @@ -30,10 +38,12 @@ system-ui, -apple-system, sans-serif; - padding: 20px; + padding: 40px 16px 80px; transition: background-color 0.3s, color 0.3s; + max-width: 980px; + margin: 0 auto; } button { @@ -88,7 +98,6 @@ background-color: #22c55e; color: white; } - .verify-result.error { background-color: #ef4444; color: white; @@ -105,12 +114,10 @@ background-color: #22c55e; color: white; } - .verify-summary.partial-success { background-color: #f59e0b; color: white; } - .verify-summary.all-failed { background-color: #ef4444; color: white; @@ -126,17 +133,21 @@ display: flex; align-items: center; gap: 10px; + min-height: 34px; } .config-row > label { - min-width: 100px; + width: 200px; + flex-shrink: 0; opacity: 0.7; font-size: 14px; } .config-row input[type="text"], + .config-row input[type="number"], .config-row select { flex: 1; + height: 34px; font-family: monospace; font-size: 14px; padding: 6px 10px; @@ -146,9 +157,48 @@ color: var(--text-color); } + .config-row input[type="checkbox"] { + width: 16px; + height: 16px; + margin: 0; + } + .config-row input[type="text"][readonly] { opacity: 0.5; } + + .config-note { + font-family: monospace; + font-size: 12px; + opacity: 0.7; + } + + .config-divider { + border: none; + border-top: 1px solid var(--border-color); + margin: 4px 0; + } + + .config-section-label { + font-size: 12px; + opacity: 0.5; + text-transform: uppercase; + letter-spacing: 0.05em; + } + + #v3Options, + #v4Options, + #identityCheckOptions { + display: flex; + flex-direction: column; + gap: 10px; + } + + #v3Options[hidden], + #v4Options[hidden], + #identityCheckOptions[hidden] { + display: none; + } @@ -183,14 +233,139 @@

IDKit Browser Example

+ +
+ + +
+ + + + + +
+
+ + +
+ +
+ + +
+ + +
- - - - - +
diff --git a/js/examples/browser/package.json b/js/examples/browser/package.json index 4e873344..4ae7158f 100644 --- a/js/examples/browser/package.json +++ b/js/examples/browser/package.json @@ -12,7 +12,8 @@ "dependencies": { "@worldcoin/idkit-core": "workspace:*", "dotenv": "^16.4.5", - "express": "^5.1.0" + "express": "^5.1.0", + "i18n-iso-countries": "^7.14.0" }, "devDependencies": { "@types/express": "^5.0.2", diff --git a/js/examples/nextjs/app/globals.css b/js/examples/nextjs/app/globals.css index 419ce504..4e5a1e63 100644 --- a/js/examples/nextjs/app/globals.css +++ b/js/examples/nextjs/app/globals.css @@ -132,10 +132,12 @@ pre { display: flex; align-items: center; gap: 10px; + min-height: 34px; } .config-row > label { - min-width: 100px; + width: 200px; + flex-shrink: 0; opacity: 0.7; font-size: 14px; } @@ -145,6 +147,7 @@ pre { .config-row input[type="datetime-local"], .config-row select { flex: 1; + height: 34px; font-family: monospace; font-size: 14px; padding: 6px 10px; diff --git a/js/examples/nextjs/app/ui.tsx b/js/examples/nextjs/app/ui.tsx index 9e824631..fb3547de 100644 --- a/js/examples/nextjs/app/ui.tsx +++ b/js/examples/nextjs/app/ui.tsx @@ -1,6 +1,8 @@ "use client"; import { useEffect, useMemo, useState, type ReactElement } from "react"; +import countries from "i18n-iso-countries"; +import enLocale from "i18n-iso-countries/langs/en.json"; import { CredentialRequest, documentLegacy, @@ -20,6 +22,7 @@ import { } from "@worldcoin/idkit"; setDebug(true); +countries.registerLocale(enLocale); const APP_ID = process.env.NEXT_PUBLIC_APP_ID as `app_${string}` | undefined; const RP_ID = process.env.NEXT_PUBLIC_RP_ID; @@ -35,7 +38,11 @@ const RETURN_TO_TOOLTIP = type PresetKind = "orb" | "secure_document" | "document" | "device" | "selfie"; -type V4CredentialType = "proof_of_human" | "passport" | "identity_check"; +type V4CredentialType = + | "proof_of_human" + | "passport" + | "mnc" + | "identity_check"; type IdentityAttributesConfig = { document_type: { enabled: boolean; value: DocumentType }; @@ -58,6 +65,7 @@ const DEFAULT_IDENTITY_ATTRIBUTES: IdentityAttributesConfig = { const V4_CREDENTIAL_TO_NAME: Record = { proof_of_human: "Proof of Human", passport: "Passport", + mnc: "My Number Card", identity_check: "Identity Check", }; @@ -69,6 +77,10 @@ const PRESET_KIND_TO_NAME: Record = { selfie: "Selfie Check", }; +function isValidAlpha3(code: string): boolean { + return code.length === 3 && countries.isValid(code.toUpperCase()); +} + function normalizeAlpha3(value: string): string { return value.trim().toUpperCase(); } @@ -212,7 +224,7 @@ export function DemoClient(): ReactElement { const [useStagingConnectBaseUrl, setUseStagingConnectBaseUrl] = useState(false); const [isConnectUrlTooltipOpen, setIsConnectUrlTooltipOpen] = useState(false); - const [worldIdVersion, setWorldIdVersion] = useState<"3.0" | "4.0">("3.0"); + const [worldIdVersion, setWorldIdVersion] = useState<"3.0" | "4.0">("4.0"); const [v4CredentialType, setV4CredentialType] = useState("proof_of_human"); const [identityAttributes, setIdentityAttributes] = @@ -491,6 +503,7 @@ export function DemoClient(): ReactElement { > + @@ -652,22 +665,45 @@ export function DemoClient(): ReactElement { } /> {identityAttributes.issuing_country.enabled && ( - - setIdentityAttributes((current) => ({ - ...current, - issuing_country: { - ...current.issuing_country, - value: normalizeAlpha3(e.target.value), - }, - })) - } - maxLength={3} - placeholder="JPN" - /> + <> + + setIdentityAttributes((current) => ({ + ...current, + issuing_country: { + ...current.issuing_country, + value: normalizeAlpha3(e.target.value), + }, + })) + } + maxLength={3} + placeholder="JPN" + /> + {identityAttributes.issuing_country.value && + (isValidAlpha3( + identityAttributes.issuing_country.value, + ) ? ( + + {countries.getName( + identityAttributes.issuing_country.value, + "en", + )} + + ) : ( + + ISO 3166-1 alpha-3 + + ))} + )}
@@ -757,22 +793,43 @@ export function DemoClient(): ReactElement { } /> {identityAttributes.nationality.enabled && ( - - setIdentityAttributes((current) => ({ - ...current, - nationality: { - ...current.nationality, - value: normalizeAlpha3(e.target.value), - }, - })) - } - maxLength={3} - placeholder="JPN" - /> + <> + + setIdentityAttributes((current) => ({ + ...current, + nationality: { + ...current.nationality, + value: normalizeAlpha3(e.target.value), + }, + })) + } + maxLength={3} + placeholder="JPN" + /> + {identityAttributes.nationality.value && + (isValidAlpha3(identityAttributes.nationality.value) ? ( + + {countries.getName( + identityAttributes.nationality.value, + "en", + )} + + ) : ( + + ISO 3166-1 alpha-3 + + ))} + )}
@@ -877,6 +934,22 @@ export function DemoClient(): ReactElement { {widgetIdkitResult && ( <>

IDKit response

+ {widgetIdkitResult.protocol_version === "4.0" && + !("session_id" in widgetIdkitResult) && + widgetIdkitResult.identity_attested !== undefined && ( +

+ Identity Attested:{" "} + {widgetIdkitResult.identity_attested ? "✓ Yes" : "✗ No"} +

+ )}
             {JSON.stringify(widgetIdkitResult, null, 2)}
           
diff --git a/js/examples/nextjs/package.json b/js/examples/nextjs/package.json index 9507e67a..a4576bb4 100644 --- a/js/examples/nextjs/package.json +++ b/js/examples/nextjs/package.json @@ -12,6 +12,7 @@ "@worldcoin/idkit-server": "workspace:*", "takis-minikit-js": "2.0.0-dev.1", "eruda": "^3.4.1", + "i18n-iso-countries": "^7.14.0", "next": "^15.4.0", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f2248c54..3a2a618c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,14 +1,13 @@ -lockfileVersion: '9.0' +lockfileVersion: "9.0" settings: autoInstallPeers: false excludeLinksFromLockfile: false overrides: - happy-dom: '>=20.0.0' + happy-dom: ">=20.0.0" importers: - .: devDependencies: prettier: @@ -17,7 +16,7 @@ importers: js/examples/browser: dependencies: - '@worldcoin/idkit-core': + "@worldcoin/idkit-core": specifier: workspace:* version: link:../../packages/core dotenv: @@ -26,8 +25,11 @@ importers: express: specifier: ^5.1.0 version: 5.2.1 + i18n-iso-countries: + specifier: ^7.14.0 + version: 7.14.0 devDependencies: - '@types/express': + "@types/express": specifier: ^5.0.2 version: 5.0.6 concurrently: @@ -42,15 +44,18 @@ importers: js/examples/nextjs: dependencies: - '@worldcoin/idkit': + "@worldcoin/idkit": specifier: workspace:* version: link:../../packages/react - '@worldcoin/idkit-server': + "@worldcoin/idkit-server": specifier: workspace:* version: link:../../packages/server eruda: specifier: ^3.4.1 version: 3.4.3 + i18n-iso-countries: + specifier: ^7.14.0 + version: 7.14.0 next: specifier: ^15.4.0 version: 15.5.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -67,10 +72,10 @@ importers: specifier: ^2.47.2 version: 2.47.2(typescript@5.9.3) devDependencies: - '@types/react': + "@types/react": specifier: ^18.3.12 version: 18.3.28 - '@types/react-dom': + "@types/react-dom": specifier: ^18.3.1 version: 18.3.7(@types/react@18.3.28) typescript: @@ -79,17 +84,17 @@ importers: js/packages/core: dependencies: - '@noble/hashes': + "@noble/hashes": specifier: ^1.7.2 version: 1.8.0 - '@worldcoin/idkit-server': + "@worldcoin/idkit-server": specifier: workspace:* version: link:../server devDependencies: - '@types/node': + "@types/node": specifier: ^20.19.30 version: 20.19.30 - '@vitest/coverage-v8': + "@vitest/coverage-v8": specifier: ^4.0.18 version: 4.0.18(vitest@4.0.18(@types/node@20.19.30)(happy-dom@20.0.0)(tsx@4.21.0)) tsup: @@ -104,33 +109,33 @@ importers: js/packages/react: dependencies: - '@worldcoin/idkit-core': + "@worldcoin/idkit-core": specifier: workspace:* version: link:../core qrcode: specifier: ^1.5.4 version: 1.5.4 devDependencies: - '@testing-library/dom': + "@testing-library/dom": specifier: ^10.4.1 version: 10.4.1 - '@testing-library/jest-dom': + "@testing-library/jest-dom": specifier: ^6.6.3 version: 6.9.1 - '@testing-library/react': + "@testing-library/react": specifier: ^16.1.0 version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@types/qrcode': + "@types/qrcode": specifier: ^1.5.5 version: 1.5.6 - '@types/react': + "@types/react": specifier: ^18.3.12 version: 18.3.28 - '@types/react-dom': + "@types/react-dom": specifier: ^18.3.1 version: 18.3.7(@types/react@18.3.28) happy-dom: - specifier: '>=20.0.0' + specifier: ">=20.0.0" version: 20.0.0 react: specifier: ^18.3.1 @@ -150,17 +155,17 @@ importers: js/packages/server: dependencies: - '@noble/hashes': + "@noble/hashes": specifier: ^1.7.2 version: 1.8.0 - '@noble/secp256k1': + "@noble/secp256k1": specifier: ^2.2.3 version: 2.3.0 devDependencies: - '@types/node': + "@types/node": specifier: ^20.19.30 version: 20.19.30 - '@vitest/coverage-v8': + "@vitest/coverage-v8": specifier: ^4.0.18 version: 4.0.18(vitest@4.0.18(@types/node@20.19.30)(happy-dom@20.0.0)(tsx@4.21.0)) tsup: @@ -174,653 +179,1084 @@ importers: version: 4.0.18(@types/node@20.19.30)(happy-dom@20.0.0)(tsx@4.21.0) packages: - - '@adobe/css-tools@4.4.4': - resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} - - '@adraffy/ens-normalize@1.11.1': - resolution: {integrity: sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==} - - '@babel/code-frame@7.29.0': - resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-string-parser@7.27.1': - resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} - engines: {node: '>=6.9.0'} - - '@babel/parser@7.28.6': - resolution: {integrity: sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==} - engines: {node: '>=6.0.0'} + "@adobe/css-tools@4.4.4": + resolution: + { + integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==, + } + + "@adraffy/ens-normalize@1.11.1": + resolution: + { + integrity: sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==, + } + + "@babel/code-frame@7.29.0": + resolution: + { + integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==, + } + engines: { node: ">=6.9.0" } + + "@babel/helper-string-parser@7.27.1": + resolution: + { + integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==, + } + engines: { node: ">=6.9.0" } + + "@babel/helper-validator-identifier@7.28.5": + resolution: + { + integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==, + } + engines: { node: ">=6.9.0" } + + "@babel/parser@7.28.6": + resolution: + { + integrity: sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==, + } + engines: { node: ">=6.0.0" } hasBin: true - '@babel/runtime@7.28.6': - resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.28.6': - resolution: {integrity: sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==} - engines: {node: '>=6.9.0'} - - '@bcoe/v8-coverage@1.0.2': - resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} - engines: {node: '>=18'} - - '@emnapi/runtime@1.8.1': - resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} - - '@esbuild/aix-ppc64@0.27.2': - resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} - engines: {node: '>=18'} + "@babel/runtime@7.28.6": + resolution: + { + integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==, + } + engines: { node: ">=6.9.0" } + + "@babel/types@7.28.6": + resolution: + { + integrity: sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==, + } + engines: { node: ">=6.9.0" } + + "@bcoe/v8-coverage@1.0.2": + resolution: + { + integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==, + } + engines: { node: ">=18" } + + "@emnapi/runtime@1.8.1": + resolution: + { + integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==, + } + + "@esbuild/aix-ppc64@0.27.2": + resolution: + { + integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==, + } + engines: { node: ">=18" } cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.27.2': - resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} - engines: {node: '>=18'} + "@esbuild/android-arm64@0.27.2": + resolution: + { + integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==, + } + engines: { node: ">=18" } cpu: [arm64] os: [android] - '@esbuild/android-arm@0.27.2': - resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} - engines: {node: '>=18'} + "@esbuild/android-arm@0.27.2": + resolution: + { + integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==, + } + engines: { node: ">=18" } cpu: [arm] os: [android] - '@esbuild/android-x64@0.27.2': - resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} - engines: {node: '>=18'} + "@esbuild/android-x64@0.27.2": + resolution: + { + integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==, + } + engines: { node: ">=18" } cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.27.2': - resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} - engines: {node: '>=18'} + "@esbuild/darwin-arm64@0.27.2": + resolution: + { + integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==, + } + engines: { node: ">=18" } cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.27.2': - resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} - engines: {node: '>=18'} + "@esbuild/darwin-x64@0.27.2": + resolution: + { + integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==, + } + engines: { node: ">=18" } cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.27.2': - resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} - engines: {node: '>=18'} + "@esbuild/freebsd-arm64@0.27.2": + resolution: + { + integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==, + } + engines: { node: ">=18" } cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.2': - resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} - engines: {node: '>=18'} + "@esbuild/freebsd-x64@0.27.2": + resolution: + { + integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==, + } + engines: { node: ">=18" } cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.27.2': - resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} - engines: {node: '>=18'} + "@esbuild/linux-arm64@0.27.2": + resolution: + { + integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==, + } + engines: { node: ">=18" } cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.27.2': - resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} - engines: {node: '>=18'} + "@esbuild/linux-arm@0.27.2": + resolution: + { + integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==, + } + engines: { node: ">=18" } cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.27.2': - resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} - engines: {node: '>=18'} + "@esbuild/linux-ia32@0.27.2": + resolution: + { + integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==, + } + engines: { node: ">=18" } cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.27.2': - resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} - engines: {node: '>=18'} + "@esbuild/linux-loong64@0.27.2": + resolution: + { + integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==, + } + engines: { node: ">=18" } cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.27.2': - resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} - engines: {node: '>=18'} + "@esbuild/linux-mips64el@0.27.2": + resolution: + { + integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==, + } + engines: { node: ">=18" } cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.27.2': - resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} - engines: {node: '>=18'} + "@esbuild/linux-ppc64@0.27.2": + resolution: + { + integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==, + } + engines: { node: ">=18" } cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.27.2': - resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} - engines: {node: '>=18'} + "@esbuild/linux-riscv64@0.27.2": + resolution: + { + integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==, + } + engines: { node: ">=18" } cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.27.2': - resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} - engines: {node: '>=18'} + "@esbuild/linux-s390x@0.27.2": + resolution: + { + integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==, + } + engines: { node: ">=18" } cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.27.2': - resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} - engines: {node: '>=18'} + "@esbuild/linux-x64@0.27.2": + resolution: + { + integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==, + } + engines: { node: ">=18" } cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.27.2': - resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} - engines: {node: '>=18'} + "@esbuild/netbsd-arm64@0.27.2": + resolution: + { + integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==, + } + engines: { node: ">=18" } cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.2': - resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} - engines: {node: '>=18'} + "@esbuild/netbsd-x64@0.27.2": + resolution: + { + integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==, + } + engines: { node: ">=18" } cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.27.2': - resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} - engines: {node: '>=18'} + "@esbuild/openbsd-arm64@0.27.2": + resolution: + { + integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==, + } + engines: { node: ">=18" } cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.27.2': - resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} - engines: {node: '>=18'} + "@esbuild/openbsd-x64@0.27.2": + resolution: + { + integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==, + } + engines: { node: ">=18" } cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.27.2': - resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} - engines: {node: '>=18'} + "@esbuild/openharmony-arm64@0.27.2": + resolution: + { + integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==, + } + engines: { node: ">=18" } cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.27.2': - resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} - engines: {node: '>=18'} + "@esbuild/sunos-x64@0.27.2": + resolution: + { + integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==, + } + engines: { node: ">=18" } cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.27.2': - resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} - engines: {node: '>=18'} + "@esbuild/win32-arm64@0.27.2": + resolution: + { + integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==, + } + engines: { node: ">=18" } cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.27.2': - resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} - engines: {node: '>=18'} + "@esbuild/win32-ia32@0.27.2": + resolution: + { + integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==, + } + engines: { node: ">=18" } cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.27.2': - resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} - engines: {node: '>=18'} + "@esbuild/win32-x64@0.27.2": + resolution: + { + integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==, + } + engines: { node: ">=18" } cpu: [x64] os: [win32] - '@img/colour@1.0.0': - resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} - engines: {node: '>=18'} - - '@img/sharp-darwin-arm64@0.34.5': - resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + "@img/colour@1.0.0": + resolution: + { + integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==, + } + engines: { node: ">=18" } + + "@img/sharp-darwin-arm64@0.34.5": + resolution: + { + integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.34.5': - resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + "@img/sharp-darwin-x64@0.34.5": + resolution: + { + integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [x64] os: [darwin] - '@img/sharp-libvips-darwin-arm64@1.2.4': - resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + "@img/sharp-libvips-darwin-arm64@1.2.4": + resolution: + { + integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==, + } cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.2.4': - resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + "@img/sharp-libvips-darwin-x64@1.2.4": + resolution: + { + integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==, + } cpu: [x64] os: [darwin] - '@img/sharp-libvips-linux-arm64@1.2.4': - resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + "@img/sharp-libvips-linux-arm64@1.2.4": + resolution: + { + integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==, + } cpu: [arm64] os: [linux] + libc: [glibc] - '@img/sharp-libvips-linux-arm@1.2.4': - resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + "@img/sharp-libvips-linux-arm@1.2.4": + resolution: + { + integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==, + } cpu: [arm] os: [linux] + libc: [glibc] - '@img/sharp-libvips-linux-ppc64@1.2.4': - resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + "@img/sharp-libvips-linux-ppc64@1.2.4": + resolution: + { + integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==, + } cpu: [ppc64] os: [linux] + libc: [glibc] - '@img/sharp-libvips-linux-riscv64@1.2.4': - resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + "@img/sharp-libvips-linux-riscv64@1.2.4": + resolution: + { + integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==, + } cpu: [riscv64] os: [linux] + libc: [glibc] - '@img/sharp-libvips-linux-s390x@1.2.4': - resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} + "@img/sharp-libvips-linux-s390x@1.2.4": + resolution: + { + integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==, + } cpu: [s390x] os: [linux] + libc: [glibc] - '@img/sharp-libvips-linux-x64@1.2.4': - resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + "@img/sharp-libvips-linux-x64@1.2.4": + resolution: + { + integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==, + } cpu: [x64] os: [linux] + libc: [glibc] - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + "@img/sharp-libvips-linuxmusl-arm64@1.2.4": + resolution: + { + integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==, + } cpu: [arm64] os: [linux] + libc: [musl] - '@img/sharp-libvips-linuxmusl-x64@1.2.4': - resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + "@img/sharp-libvips-linuxmusl-x64@1.2.4": + resolution: + { + integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==, + } cpu: [x64] os: [linux] - - '@img/sharp-linux-arm64@0.34.5': - resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + libc: [musl] + + "@img/sharp-linux-arm64@0.34.5": + resolution: + { + integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [arm64] os: [linux] - - '@img/sharp-linux-arm@0.34.5': - resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + libc: [glibc] + + "@img/sharp-linux-arm@0.34.5": + resolution: + { + integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [arm] os: [linux] - - '@img/sharp-linux-ppc64@0.34.5': - resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + libc: [glibc] + + "@img/sharp-linux-ppc64@0.34.5": + resolution: + { + integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [ppc64] os: [linux] - - '@img/sharp-linux-riscv64@0.34.5': - resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + libc: [glibc] + + "@img/sharp-linux-riscv64@0.34.5": + resolution: + { + integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [riscv64] os: [linux] - - '@img/sharp-linux-s390x@0.34.5': - resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + libc: [glibc] + + "@img/sharp-linux-s390x@0.34.5": + resolution: + { + integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [s390x] os: [linux] - - '@img/sharp-linux-x64@0.34.5': - resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + libc: [glibc] + + "@img/sharp-linux-x64@0.34.5": + resolution: + { + integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [x64] os: [linux] - - '@img/sharp-linuxmusl-arm64@0.34.5': - resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + libc: [glibc] + + "@img/sharp-linuxmusl-arm64@0.34.5": + resolution: + { + integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [arm64] os: [linux] - - '@img/sharp-linuxmusl-x64@0.34.5': - resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + libc: [musl] + + "@img/sharp-linuxmusl-x64@0.34.5": + resolution: + { + integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [x64] os: [linux] - - '@img/sharp-wasm32@0.34.5': - resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + libc: [musl] + + "@img/sharp-wasm32@0.34.5": + resolution: + { + integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [wasm32] - '@img/sharp-win32-arm64@0.34.5': - resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + "@img/sharp-win32-arm64@0.34.5": + resolution: + { + integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [arm64] os: [win32] - '@img/sharp-win32-ia32@0.34.5': - resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + "@img/sharp-win32-ia32@0.34.5": + resolution: + { + integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.34.5': - resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + "@img/sharp-win32-x64@0.34.5": + resolution: + { + integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [x64] os: [win32] - '@jridgewell/gen-mapping@0.3.13': - resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - - '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - - '@jridgewell/trace-mapping@0.3.31': - resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - - '@next/env@15.5.12': - resolution: {integrity: sha512-pUvdJN1on574wQHjaBfNGDt9Mz5utDSZFsIIQkMzPgNS8ZvT4H2mwOrOIClwsQOb6EGx5M76/CZr6G8i6pSpLg==} - - '@next/swc-darwin-arm64@15.5.12': - resolution: {integrity: sha512-RnRjBtH8S8eXCpUNkQ+543DUc7ys8y15VxmFU9HRqlo9BG3CcBUiwNtF8SNoi2xvGCVJq1vl2yYq+3oISBS0Zg==} - engines: {node: '>= 10'} + "@jridgewell/gen-mapping@0.3.13": + resolution: + { + integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==, + } + + "@jridgewell/resolve-uri@3.1.2": + resolution: + { + integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==, + } + engines: { node: ">=6.0.0" } + + "@jridgewell/sourcemap-codec@1.5.5": + resolution: + { + integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==, + } + + "@jridgewell/trace-mapping@0.3.31": + resolution: + { + integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==, + } + + "@next/env@15.5.12": + resolution: + { + integrity: sha512-pUvdJN1on574wQHjaBfNGDt9Mz5utDSZFsIIQkMzPgNS8ZvT4H2mwOrOIClwsQOb6EGx5M76/CZr6G8i6pSpLg==, + } + + "@next/swc-darwin-arm64@15.5.12": + resolution: + { + integrity: sha512-RnRjBtH8S8eXCpUNkQ+543DUc7ys8y15VxmFU9HRqlo9BG3CcBUiwNtF8SNoi2xvGCVJq1vl2yYq+3oISBS0Zg==, + } + engines: { node: ">= 10" } cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@15.5.12': - resolution: {integrity: sha512-nqa9/7iQlboF1EFtNhWxQA0rQstmYRSBGxSM6g3GxvxHxcoeqVXfGNr9stJOme674m2V7r4E3+jEhhGvSQhJRA==} - engines: {node: '>= 10'} + "@next/swc-darwin-x64@15.5.12": + resolution: + { + integrity: sha512-nqa9/7iQlboF1EFtNhWxQA0rQstmYRSBGxSM6g3GxvxHxcoeqVXfGNr9stJOme674m2V7r4E3+jEhhGvSQhJRA==, + } + engines: { node: ">= 10" } cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@15.5.12': - resolution: {integrity: sha512-dCzAjqhDHwmoB2M4eYfVKqXs99QdQxNQVpftvP1eGVppamXh/OkDAwV737Zr0KPXEqRUMN4uCjh6mjO+XtF3Mw==} - engines: {node: '>= 10'} + "@next/swc-linux-arm64-gnu@15.5.12": + resolution: + { + integrity: sha512-dCzAjqhDHwmoB2M4eYfVKqXs99QdQxNQVpftvP1eGVppamXh/OkDAwV737Zr0KPXEqRUMN4uCjh6mjO+XtF3Mw==, + } + engines: { node: ">= 10" } cpu: [arm64] os: [linux] - - '@next/swc-linux-arm64-musl@15.5.12': - resolution: {integrity: sha512-+fpGWvQiITgf7PUtbWY1H7qUSnBZsPPLyyq03QuAKpVoTy/QUx1JptEDTQMVvQhvizCEuNLEeghrQUyXQOekuw==} - engines: {node: '>= 10'} + libc: [glibc] + + "@next/swc-linux-arm64-musl@15.5.12": + resolution: + { + integrity: sha512-+fpGWvQiITgf7PUtbWY1H7qUSnBZsPPLyyq03QuAKpVoTy/QUx1JptEDTQMVvQhvizCEuNLEeghrQUyXQOekuw==, + } + engines: { node: ">= 10" } cpu: [arm64] os: [linux] - - '@next/swc-linux-x64-gnu@15.5.12': - resolution: {integrity: sha512-jSLvgdRRL/hrFAPqEjJf1fFguC719kmcptjNVDJl26BnJIpjL3KH5h6mzR4mAweociLQaqvt4UyzfbFjgAdDcw==} - engines: {node: '>= 10'} + libc: [musl] + + "@next/swc-linux-x64-gnu@15.5.12": + resolution: + { + integrity: sha512-jSLvgdRRL/hrFAPqEjJf1fFguC719kmcptjNVDJl26BnJIpjL3KH5h6mzR4mAweociLQaqvt4UyzfbFjgAdDcw==, + } + engines: { node: ">= 10" } cpu: [x64] os: [linux] - - '@next/swc-linux-x64-musl@15.5.12': - resolution: {integrity: sha512-/uaF0WfmYqQgLfPmN6BvULwxY0dufI2mlN2JbOKqqceZh1G4hjREyi7pg03zjfyS6eqNemHAZPSoP84x17vo6w==} - engines: {node: '>= 10'} + libc: [glibc] + + "@next/swc-linux-x64-musl@15.5.12": + resolution: + { + integrity: sha512-/uaF0WfmYqQgLfPmN6BvULwxY0dufI2mlN2JbOKqqceZh1G4hjREyi7pg03zjfyS6eqNemHAZPSoP84x17vo6w==, + } + engines: { node: ">= 10" } cpu: [x64] os: [linux] - - '@next/swc-win32-arm64-msvc@15.5.12': - resolution: {integrity: sha512-xhsL1OvQSfGmlL5RbOmU+FV120urrgFpYLq+6U8C6KIym32gZT6XF/SDE92jKzzlPWskkbjOKCpqk5m4i8PEfg==} - engines: {node: '>= 10'} + libc: [musl] + + "@next/swc-win32-arm64-msvc@15.5.12": + resolution: + { + integrity: sha512-xhsL1OvQSfGmlL5RbOmU+FV120urrgFpYLq+6U8C6KIym32gZT6XF/SDE92jKzzlPWskkbjOKCpqk5m4i8PEfg==, + } + engines: { node: ">= 10" } cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@15.5.12': - resolution: {integrity: sha512-Z1Dh6lhFkxvBDH1FoW6OU/L6prYwPSlwjLiZkExIAh8fbP6iI/M7iGTQAJPYJ9YFlWobCZ1PHbchFhFYb2ADkw==} - engines: {node: '>= 10'} + "@next/swc-win32-x64-msvc@15.5.12": + resolution: + { + integrity: sha512-Z1Dh6lhFkxvBDH1FoW6OU/L6prYwPSlwjLiZkExIAh8fbP6iI/M7iGTQAJPYJ9YFlWobCZ1PHbchFhFYb2ADkw==, + } + engines: { node: ">= 10" } cpu: [x64] os: [win32] - '@noble/ciphers@1.3.0': - resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} - engines: {node: ^14.21.3 || >=16} - - '@noble/curves@1.9.1': - resolution: {integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==} - engines: {node: ^14.21.3 || >=16} - - '@noble/hashes@1.8.0': - resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} - engines: {node: ^14.21.3 || >=16} - - '@noble/secp256k1@2.3.0': - resolution: {integrity: sha512-0TQed2gcBbIrh7Ccyw+y/uZQvbJwm7Ao4scBUxqpBCcsOlZG0O4KGfjtNAy/li4W8n1xt3dxrwJ0beZ2h2G6Kw==} - - '@rollup/rollup-android-arm-eabi@4.57.0': - resolution: {integrity: sha512-tPgXB6cDTndIe1ah7u6amCI1T0SsnlOuKgg10Xh3uizJk4e5M1JGaUMk7J4ciuAUcFpbOiNhm2XIjP9ON0dUqA==} + "@noble/ciphers@1.3.0": + resolution: + { + integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==, + } + engines: { node: ^14.21.3 || >=16 } + + "@noble/curves@1.9.1": + resolution: + { + integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==, + } + engines: { node: ^14.21.3 || >=16 } + + "@noble/hashes@1.8.0": + resolution: + { + integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==, + } + engines: { node: ^14.21.3 || >=16 } + + "@noble/secp256k1@2.3.0": + resolution: + { + integrity: sha512-0TQed2gcBbIrh7Ccyw+y/uZQvbJwm7Ao4scBUxqpBCcsOlZG0O4KGfjtNAy/li4W8n1xt3dxrwJ0beZ2h2G6Kw==, + } + + "@rollup/rollup-android-arm-eabi@4.57.0": + resolution: + { + integrity: sha512-tPgXB6cDTndIe1ah7u6amCI1T0SsnlOuKgg10Xh3uizJk4e5M1JGaUMk7J4ciuAUcFpbOiNhm2XIjP9ON0dUqA==, + } cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.57.0': - resolution: {integrity: sha512-sa4LyseLLXr1onr97StkU1Nb7fWcg6niokTwEVNOO7awaKaoRObQ54+V/hrF/BP1noMEaaAW6Fg2d/CfLiq3Mg==} + "@rollup/rollup-android-arm64@4.57.0": + resolution: + { + integrity: sha512-sa4LyseLLXr1onr97StkU1Nb7fWcg6niokTwEVNOO7awaKaoRObQ54+V/hrF/BP1noMEaaAW6Fg2d/CfLiq3Mg==, + } cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.57.0': - resolution: {integrity: sha512-/NNIj9A7yLjKdmkx5dC2XQ9DmjIECpGpwHoGmA5E1AhU0fuICSqSWScPhN1yLCkEdkCwJIDu2xIeLPs60MNIVg==} + "@rollup/rollup-darwin-arm64@4.57.0": + resolution: + { + integrity: sha512-/NNIj9A7yLjKdmkx5dC2XQ9DmjIECpGpwHoGmA5E1AhU0fuICSqSWScPhN1yLCkEdkCwJIDu2xIeLPs60MNIVg==, + } cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.57.0': - resolution: {integrity: sha512-xoh8abqgPrPYPr7pTYipqnUi1V3em56JzE/HgDgitTqZBZ3yKCWI+7KUkceM6tNweyUKYru1UMi7FC060RyKwA==} + "@rollup/rollup-darwin-x64@4.57.0": + resolution: + { + integrity: sha512-xoh8abqgPrPYPr7pTYipqnUi1V3em56JzE/HgDgitTqZBZ3yKCWI+7KUkceM6tNweyUKYru1UMi7FC060RyKwA==, + } cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.57.0': - resolution: {integrity: sha512-PCkMh7fNahWSbA0OTUQ2OpYHpjZZr0hPr8lId8twD7a7SeWrvT3xJVyza+dQwXSSq4yEQTMoXgNOfMCsn8584g==} + "@rollup/rollup-freebsd-arm64@4.57.0": + resolution: + { + integrity: sha512-PCkMh7fNahWSbA0OTUQ2OpYHpjZZr0hPr8lId8twD7a7SeWrvT3xJVyza+dQwXSSq4yEQTMoXgNOfMCsn8584g==, + } cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.57.0': - resolution: {integrity: sha512-1j3stGx+qbhXql4OCDZhnK7b01s6rBKNybfsX+TNrEe9JNq4DLi1yGiR1xW+nL+FNVvI4D02PUnl6gJ/2y6WJA==} + "@rollup/rollup-freebsd-x64@4.57.0": + resolution: + { + integrity: sha512-1j3stGx+qbhXql4OCDZhnK7b01s6rBKNybfsX+TNrEe9JNq4DLi1yGiR1xW+nL+FNVvI4D02PUnl6gJ/2y6WJA==, + } cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.57.0': - resolution: {integrity: sha512-eyrr5W08Ms9uM0mLcKfM/Uzx7hjhz2bcjv8P2uynfj0yU8GGPdz8iYrBPhiLOZqahoAMB8ZiolRZPbbU2MAi6Q==} + "@rollup/rollup-linux-arm-gnueabihf@4.57.0": + resolution: + { + integrity: sha512-eyrr5W08Ms9uM0mLcKfM/Uzx7hjhz2bcjv8P2uynfj0yU8GGPdz8iYrBPhiLOZqahoAMB8ZiolRZPbbU2MAi6Q==, + } cpu: [arm] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.57.0': - resolution: {integrity: sha512-Xds90ITXJCNyX9pDhqf85MKWUI4lqjiPAipJ8OLp8xqI2Ehk+TCVhF9rvOoN8xTbcafow3QOThkNnrM33uCFQA==} + "@rollup/rollup-linux-arm-musleabihf@4.57.0": + resolution: + { + integrity: sha512-Xds90ITXJCNyX9pDhqf85MKWUI4lqjiPAipJ8OLp8xqI2Ehk+TCVhF9rvOoN8xTbcafow3QOThkNnrM33uCFQA==, + } cpu: [arm] os: [linux] + libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.57.0': - resolution: {integrity: sha512-Xws2KA4CLvZmXjy46SQaXSejuKPhwVdaNinldoYfqruZBaJHqVo6hnRa8SDo9z7PBW5x84SH64+izmldCgbezw==} + "@rollup/rollup-linux-arm64-gnu@4.57.0": + resolution: + { + integrity: sha512-Xws2KA4CLvZmXjy46SQaXSejuKPhwVdaNinldoYfqruZBaJHqVo6hnRa8SDo9z7PBW5x84SH64+izmldCgbezw==, + } cpu: [arm64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.57.0': - resolution: {integrity: sha512-hrKXKbX5FdaRJj7lTMusmvKbhMJSGWJ+w++4KmjiDhpTgNlhYobMvKfDoIWecy4O60K6yA4SnztGuNTQF+Lplw==} + "@rollup/rollup-linux-arm64-musl@4.57.0": + resolution: + { + integrity: sha512-hrKXKbX5FdaRJj7lTMusmvKbhMJSGWJ+w++4KmjiDhpTgNlhYobMvKfDoIWecy4O60K6yA4SnztGuNTQF+Lplw==, + } cpu: [arm64] os: [linux] + libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.57.0': - resolution: {integrity: sha512-6A+nccfSDGKsPm00d3xKcrsBcbqzCTAukjwWK6rbuAnB2bHaL3r9720HBVZ/no7+FhZLz/U3GwwZZEh6tOSI8Q==} + "@rollup/rollup-linux-loong64-gnu@4.57.0": + resolution: + { + integrity: sha512-6A+nccfSDGKsPm00d3xKcrsBcbqzCTAukjwWK6rbuAnB2bHaL3r9720HBVZ/no7+FhZLz/U3GwwZZEh6tOSI8Q==, + } cpu: [loong64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.57.0': - resolution: {integrity: sha512-4P1VyYUe6XAJtQH1Hh99THxr0GKMMwIXsRNOceLrJnaHTDgk1FTcTimDgneRJPvB3LqDQxUmroBclQ1S0cIJwQ==} + "@rollup/rollup-linux-loong64-musl@4.57.0": + resolution: + { + integrity: sha512-4P1VyYUe6XAJtQH1Hh99THxr0GKMMwIXsRNOceLrJnaHTDgk1FTcTimDgneRJPvB3LqDQxUmroBclQ1S0cIJwQ==, + } cpu: [loong64] os: [linux] + libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.57.0': - resolution: {integrity: sha512-8Vv6pLuIZCMcgXre6c3nOPhE0gjz1+nZP6T+hwWjr7sVH8k0jRkH+XnfjjOTglyMBdSKBPPz54/y1gToSKwrSQ==} + "@rollup/rollup-linux-ppc64-gnu@4.57.0": + resolution: + { + integrity: sha512-8Vv6pLuIZCMcgXre6c3nOPhE0gjz1+nZP6T+hwWjr7sVH8k0jRkH+XnfjjOTglyMBdSKBPPz54/y1gToSKwrSQ==, + } cpu: [ppc64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.57.0': - resolution: {integrity: sha512-r1te1M0Sm2TBVD/RxBPC6RZVwNqUTwJTA7w+C/IW5v9Ssu6xmxWEi+iJQlpBhtUiT1raJ5b48pI8tBvEjEFnFA==} + "@rollup/rollup-linux-ppc64-musl@4.57.0": + resolution: + { + integrity: sha512-r1te1M0Sm2TBVD/RxBPC6RZVwNqUTwJTA7w+C/IW5v9Ssu6xmxWEi+iJQlpBhtUiT1raJ5b48pI8tBvEjEFnFA==, + } cpu: [ppc64] os: [linux] + libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.57.0': - resolution: {integrity: sha512-say0uMU/RaPm3CDQLxUUTF2oNWL8ysvHkAjcCzV2znxBr23kFfaxocS9qJm+NdkRhF8wtdEEAJuYcLPhSPbjuQ==} + "@rollup/rollup-linux-riscv64-gnu@4.57.0": + resolution: + { + integrity: sha512-say0uMU/RaPm3CDQLxUUTF2oNWL8ysvHkAjcCzV2znxBr23kFfaxocS9qJm+NdkRhF8wtdEEAJuYcLPhSPbjuQ==, + } cpu: [riscv64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.57.0': - resolution: {integrity: sha512-/MU7/HizQGsnBREtRpcSbSV1zfkoxSTR7wLsRmBPQ8FwUj5sykrP1MyJTvsxP5KBq9SyE6kH8UQQQwa0ASeoQQ==} + "@rollup/rollup-linux-riscv64-musl@4.57.0": + resolution: + { + integrity: sha512-/MU7/HizQGsnBREtRpcSbSV1zfkoxSTR7wLsRmBPQ8FwUj5sykrP1MyJTvsxP5KBq9SyE6kH8UQQQwa0ASeoQQ==, + } cpu: [riscv64] os: [linux] + libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.57.0': - resolution: {integrity: sha512-Q9eh+gUGILIHEaJf66aF6a414jQbDnn29zeu0eX3dHMuysnhTvsUvZTCAyZ6tJhUjnvzBKE4FtuaYxutxRZpOg==} + "@rollup/rollup-linux-s390x-gnu@4.57.0": + resolution: + { + integrity: sha512-Q9eh+gUGILIHEaJf66aF6a414jQbDnn29zeu0eX3dHMuysnhTvsUvZTCAyZ6tJhUjnvzBKE4FtuaYxutxRZpOg==, + } cpu: [s390x] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.57.0': - resolution: {integrity: sha512-OR5p5yG5OKSxHReWmwvM0P+VTPMwoBS45PXTMYaskKQqybkS3Kmugq1W+YbNWArF8/s7jQScgzXUhArzEQ7x0A==} + "@rollup/rollup-linux-x64-gnu@4.57.0": + resolution: + { + integrity: sha512-OR5p5yG5OKSxHReWmwvM0P+VTPMwoBS45PXTMYaskKQqybkS3Kmugq1W+YbNWArF8/s7jQScgzXUhArzEQ7x0A==, + } cpu: [x64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.57.0': - resolution: {integrity: sha512-XeatKzo4lHDsVEbm1XDHZlhYZZSQYym6dg2X/Ko0kSFgio+KXLsxwJQprnR48GvdIKDOpqWqssC3iBCjoMcMpw==} + "@rollup/rollup-linux-x64-musl@4.57.0": + resolution: + { + integrity: sha512-XeatKzo4lHDsVEbm1XDHZlhYZZSQYym6dg2X/Ko0kSFgio+KXLsxwJQprnR48GvdIKDOpqWqssC3iBCjoMcMpw==, + } cpu: [x64] os: [linux] + libc: [musl] - '@rollup/rollup-openbsd-x64@4.57.0': - resolution: {integrity: sha512-Lu71y78F5qOfYmubYLHPcJm74GZLU6UJ4THkf/a1K7Tz2ycwC2VUbsqbJAXaR6Bx70SRdlVrt2+n5l7F0agTUw==} + "@rollup/rollup-openbsd-x64@4.57.0": + resolution: + { + integrity: sha512-Lu71y78F5qOfYmubYLHPcJm74GZLU6UJ4THkf/a1K7Tz2ycwC2VUbsqbJAXaR6Bx70SRdlVrt2+n5l7F0agTUw==, + } cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.57.0': - resolution: {integrity: sha512-v5xwKDWcu7qhAEcsUubiav7r+48Uk/ENWdr82MBZZRIm7zThSxCIVDfb3ZeRRq9yqk+oIzMdDo6fCcA5DHfMyA==} + "@rollup/rollup-openharmony-arm64@4.57.0": + resolution: + { + integrity: sha512-v5xwKDWcu7qhAEcsUubiav7r+48Uk/ENWdr82MBZZRIm7zThSxCIVDfb3ZeRRq9yqk+oIzMdDo6fCcA5DHfMyA==, + } cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.57.0': - resolution: {integrity: sha512-XnaaaSMGSI6Wk8F4KK3QP7GfuuhjGchElsVerCplUuxRIzdvZ7hRBpLR0omCmw+kI2RFJB80nenhOoGXlJ5TfQ==} + "@rollup/rollup-win32-arm64-msvc@4.57.0": + resolution: + { + integrity: sha512-XnaaaSMGSI6Wk8F4KK3QP7GfuuhjGchElsVerCplUuxRIzdvZ7hRBpLR0omCmw+kI2RFJB80nenhOoGXlJ5TfQ==, + } cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.57.0': - resolution: {integrity: sha512-3K1lP+3BXY4t4VihLw5MEg6IZD3ojSYzqzBG571W3kNQe4G4CcFpSUQVgurYgib5d+YaCjeFow8QivWp8vuSvA==} + "@rollup/rollup-win32-ia32-msvc@4.57.0": + resolution: + { + integrity: sha512-3K1lP+3BXY4t4VihLw5MEg6IZD3ojSYzqzBG571W3kNQe4G4CcFpSUQVgurYgib5d+YaCjeFow8QivWp8vuSvA==, + } cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.57.0': - resolution: {integrity: sha512-MDk610P/vJGc5L5ImE4k5s+GZT3en0KoK1MKPXCRgzmksAMk79j4h3k1IerxTNqwDLxsGxStEZVBqG0gIqZqoA==} + "@rollup/rollup-win32-x64-gnu@4.57.0": + resolution: + { + integrity: sha512-MDk610P/vJGc5L5ImE4k5s+GZT3en0KoK1MKPXCRgzmksAMk79j4h3k1IerxTNqwDLxsGxStEZVBqG0gIqZqoA==, + } cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.57.0': - resolution: {integrity: sha512-Zv7v6q6aV+VslnpwzqKAmrk5JdVkLUzok2208ZXGipjb+msxBr/fJPZyeEXiFgH7k62Ak0SLIfxQRZQvTuf7rQ==} + "@rollup/rollup-win32-x64-msvc@4.57.0": + resolution: + { + integrity: sha512-Zv7v6q6aV+VslnpwzqKAmrk5JdVkLUzok2208ZXGipjb+msxBr/fJPZyeEXiFgH7k62Ak0SLIfxQRZQvTuf7rQ==, + } cpu: [x64] os: [win32] - '@scure/base@1.2.6': - resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==} - - '@scure/bip32@1.7.0': - resolution: {integrity: sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==} - - '@scure/bip39@1.6.0': - resolution: {integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==} - - '@standard-schema/spec@1.1.0': - resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - - '@swc/helpers@0.5.15': - resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} - - '@testing-library/dom@10.4.1': - resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} - engines: {node: '>=18'} - - '@testing-library/jest-dom@6.9.1': - resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} - engines: {node: '>=14', npm: '>=6', yarn: '>=1'} - - '@testing-library/react@16.3.2': - resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==} - engines: {node: '>=18'} + "@scure/base@1.2.6": + resolution: + { + integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==, + } + + "@scure/bip32@1.7.0": + resolution: + { + integrity: sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==, + } + + "@scure/bip39@1.6.0": + resolution: + { + integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==, + } + + "@standard-schema/spec@1.1.0": + resolution: + { + integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==, + } + + "@swc/helpers@0.5.15": + resolution: + { + integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==, + } + + "@testing-library/dom@10.4.1": + resolution: + { + integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==, + } + engines: { node: ">=18" } + + "@testing-library/jest-dom@6.9.1": + resolution: + { + integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==, + } + engines: { node: ">=14", npm: ">=6", yarn: ">=1" } + + "@testing-library/react@16.3.2": + resolution: + { + integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==, + } + engines: { node: ">=18" } peerDependencies: - '@testing-library/dom': ^10.0.0 - '@types/react': ^18.0.0 || ^19.0.0 - '@types/react-dom': ^18.0.0 || ^19.0.0 + "@testing-library/dom": ^10.0.0 + "@types/react": ^18.0.0 || ^19.0.0 + "@types/react-dom": ^18.0.0 || ^19.0.0 react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 peerDependenciesMeta: - '@types/react': + "@types/react": optional: true - '@types/react-dom': + "@types/react-dom": optional: true - '@types/aria-query@5.0.4': - resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} - - '@types/body-parser@1.19.6': - resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} - - '@types/chai@5.2.3': - resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} - - '@types/connect@3.4.38': - resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - - '@types/deep-eql@4.0.2': - resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - - '@types/express-serve-static-core@5.1.1': - resolution: {integrity: sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==} - - '@types/express@5.0.6': - resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==} - - '@types/http-errors@2.0.5': - resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} - - '@types/node@20.19.30': - resolution: {integrity: sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==} - - '@types/prop-types@15.7.15': - resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} - - '@types/qrcode@1.5.6': - resolution: {integrity: sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==} - - '@types/qs@6.14.0': - resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} - - '@types/range-parser@1.2.7': - resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - - '@types/react-dom@18.3.7': - resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==} + "@types/aria-query@5.0.4": + resolution: + { + integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==, + } + + "@types/body-parser@1.19.6": + resolution: + { + integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==, + } + + "@types/chai@5.2.3": + resolution: + { + integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==, + } + + "@types/connect@3.4.38": + resolution: + { + integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==, + } + + "@types/deep-eql@4.0.2": + resolution: + { + integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==, + } + + "@types/estree@1.0.8": + resolution: + { + integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==, + } + + "@types/express-serve-static-core@5.1.1": + resolution: + { + integrity: sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==, + } + + "@types/express@5.0.6": + resolution: + { + integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==, + } + + "@types/http-errors@2.0.5": + resolution: + { + integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==, + } + + "@types/node@20.19.30": + resolution: + { + integrity: sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==, + } + + "@types/prop-types@15.7.15": + resolution: + { + integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==, + } + + "@types/qrcode@1.5.6": + resolution: + { + integrity: sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==, + } + + "@types/qs@6.14.0": + resolution: + { + integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==, + } + + "@types/range-parser@1.2.7": + resolution: + { + integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==, + } + + "@types/react-dom@18.3.7": + resolution: + { + integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==, + } peerDependencies: - '@types/react': ^18.0.0 - - '@types/react@18.3.28': - resolution: {integrity: sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==} - - '@types/send@1.2.1': - resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} - - '@types/serve-static@2.2.0': - resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} - - '@types/whatwg-mimetype@3.0.2': - resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==} - - '@vitest/coverage-v8@4.0.18': - resolution: {integrity: sha512-7i+N2i0+ME+2JFZhfuz7Tg/FqKtilHjGyGvoHYQ6iLV0zahbsJ9sljC9OcFcPDbhYKCet+sG8SsVqlyGvPflZg==} + "@types/react": ^18.0.0 + + "@types/react@18.3.28": + resolution: + { + integrity: sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==, + } + + "@types/send@1.2.1": + resolution: + { + integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==, + } + + "@types/serve-static@2.2.0": + resolution: + { + integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==, + } + + "@types/whatwg-mimetype@3.0.2": + resolution: + { + integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==, + } + + "@vitest/coverage-v8@4.0.18": + resolution: + { + integrity: sha512-7i+N2i0+ME+2JFZhfuz7Tg/FqKtilHjGyGvoHYQ6iLV0zahbsJ9sljC9OcFcPDbhYKCet+sG8SsVqlyGvPflZg==, + } peerDependencies: - '@vitest/browser': 4.0.18 + "@vitest/browser": 4.0.18 vitest: 4.0.18 peerDependenciesMeta: - '@vitest/browser': + "@vitest/browser": optional: true - '@vitest/expect@4.0.18': - resolution: {integrity: sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==} - - '@vitest/mocker@4.0.18': - resolution: {integrity: sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==} + "@vitest/expect@4.0.18": + resolution: + { + integrity: sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==, + } + + "@vitest/mocker@4.0.18": + resolution: + { + integrity: sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==, + } peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0-0 @@ -830,25 +1266,43 @@ packages: vite: optional: true - '@vitest/pretty-format@4.0.18': - resolution: {integrity: sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==} - - '@vitest/runner@4.0.18': - resolution: {integrity: sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==} - - '@vitest/snapshot@4.0.18': - resolution: {integrity: sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==} - - '@vitest/spy@4.0.18': - resolution: {integrity: sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==} - - '@vitest/utils@4.0.18': - resolution: {integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==} + "@vitest/pretty-format@4.0.18": + resolution: + { + integrity: sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==, + } + + "@vitest/runner@4.0.18": + resolution: + { + integrity: sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==, + } + + "@vitest/snapshot@4.0.18": + resolution: + { + integrity: sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==, + } + + "@vitest/spy@4.0.18": + resolution: + { + integrity: sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==, + } + + "@vitest/utils@4.0.18": + resolution: + { + integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==, + } abitype@1.2.3: - resolution: {integrity: sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==} + resolution: + { + integrity: sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==, + } peerDependencies: - typescript: '>=5.0.4' + typescript: ">=5.0.4" zod: ^3.22.0 || ^4.0.0 peerDependenciesMeta: typescript: @@ -857,246 +1311,441 @@ packages: optional: true accepts@2.0.0: - resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} - engines: {node: '>= 0.6'} + resolution: + { + integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==, + } + engines: { node: ">= 0.6" } acorn@8.15.0: - resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} - engines: {node: '>=0.4.0'} + resolution: + { + integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==, + } + engines: { node: ">=0.4.0" } hasBin: true ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==, + } + engines: { node: ">=8" } ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==, + } + engines: { node: ">=8" } ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==, + } + engines: { node: ">=10" } any-promise@1.3.0: - resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + resolution: + { + integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==, + } aria-query@5.3.0: - resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + resolution: + { + integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==, + } aria-query@5.3.2: - resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==, + } + engines: { node: ">= 0.4" } assertion-error@2.0.1: - resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==, + } + engines: { node: ">=12" } ast-v8-to-istanbul@0.3.10: - resolution: {integrity: sha512-p4K7vMz2ZSk3wN8l5o3y2bJAoZXT3VuJI5OLTATY/01CYWumWvwkUw0SqDBnNq6IiTO3qDa1eSQDibAV8g7XOQ==} + resolution: + { + integrity: sha512-p4K7vMz2ZSk3wN8l5o3y2bJAoZXT3VuJI5OLTATY/01CYWumWvwkUw0SqDBnNq6IiTO3qDa1eSQDibAV8g7XOQ==, + } body-parser@2.2.2: - resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==, + } + engines: { node: ">=18" } bundle-require@5.1.0: - resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + resolution: + { + integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==, + } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } peerDependencies: - esbuild: '>=0.18' + esbuild: ">=0.18" bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} + resolution: + { + integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==, + } + engines: { node: ">= 0.8" } cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==, + } + engines: { node: ">=8" } call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==, + } + engines: { node: ">= 0.4" } call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==, + } + engines: { node: ">= 0.4" } camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==, + } + engines: { node: ">=6" } caniuse-lite@1.0.30001769: - resolution: {integrity: sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==} + resolution: + { + integrity: sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==, + } chai@6.2.2: - resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==, + } + engines: { node: ">=18" } chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==, + } + engines: { node: ">=10" } chokidar@4.0.3: - resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} - engines: {node: '>= 14.16.0'} + resolution: + { + integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==, + } + engines: { node: ">= 14.16.0" } client-only@0.0.1: - resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + resolution: + { + integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==, + } cliui@6.0.0: - resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + resolution: + { + integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==, + } cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==, + } + engines: { node: ">=12" } color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} + resolution: + { + integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==, + } + engines: { node: ">=7.0.0" } color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + resolution: + { + integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, + } commander@4.1.1: - resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} - engines: {node: '>= 6'} + resolution: + { + integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==, + } + engines: { node: ">= 6" } concurrently@9.2.1: - resolution: {integrity: sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==, + } + engines: { node: ">=18" } hasBin: true confbox@0.1.8: - resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + resolution: + { + integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==, + } consola@3.4.2: - resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} - engines: {node: ^14.18.0 || >=16.10.0} + resolution: + { + integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==, + } + engines: { node: ^14.18.0 || >=16.10.0 } content-disposition@1.0.1: - resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==, + } + engines: { node: ">=18" } content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} + resolution: + { + integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==, + } + engines: { node: ">= 0.6" } cookie-signature@1.2.2: - resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} - engines: {node: '>=6.6.0'} + resolution: + { + integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==, + } + engines: { node: ">=6.6.0" } cookie@0.7.2: - resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} - engines: {node: '>= 0.6'} + resolution: + { + integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==, + } + engines: { node: ">= 0.6" } css.escape@1.5.1: - resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + resolution: + { + integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==, + } csstype@3.2.3: - resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + resolution: + { + integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==, + } debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} - engines: {node: '>=6.0'} + resolution: + { + integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==, + } + engines: { node: ">=6.0" } peerDependencies: - supports-color: '*' + supports-color: "*" peerDependenciesMeta: supports-color: optional: true decamelize@1.2.0: - resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==, + } + engines: { node: ">=0.10.0" } depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} + resolution: + { + integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==, + } + engines: { node: ">= 0.8" } dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==, + } + engines: { node: ">=6" } detect-libc@2.1.2: - resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==, + } + engines: { node: ">=8" } + + diacritics@1.3.0: + resolution: + { + integrity: sha512-wlwEkqcsaxvPJML+rDh/2iS824jbREk6DUMUKkEaSlxdYHeS43cClJtsWglvw2RfeXGm6ohKDqsXteJ5sP5enA==, + } dijkstrajs@1.0.3: - resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} + resolution: + { + integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==, + } dom-accessibility-api@0.5.16: - resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + resolution: + { + integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==, + } dom-accessibility-api@0.6.3: - resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + resolution: + { + integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==, + } dotenv@16.6.1: - resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==, + } + engines: { node: ">=12" } dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==, + } + engines: { node: ">= 0.4" } ee-first@1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + resolution: + { + integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==, + } emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + resolution: + { + integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==, + } encodeurl@2.0.0: - resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} - engines: {node: '>= 0.8'} + resolution: + { + integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==, + } + engines: { node: ">= 0.8" } eruda@3.4.3: - resolution: {integrity: sha512-J2TsF4dXSspOXev5bJ6mljv0dRrxj21wklrDzbvPmYaEmVoC+2psylyRi70nUPFh1mTQfIBsSusUtAMZtUN+/w==} + resolution: + { + integrity: sha512-J2TsF4dXSspOXev5bJ6mljv0dRrxj21wklrDzbvPmYaEmVoC+2psylyRi70nUPFh1mTQfIBsSusUtAMZtUN+/w==, + } es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==, + } + engines: { node: ">= 0.4" } es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==, + } + engines: { node: ">= 0.4" } es-module-lexer@1.7.0: - resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + resolution: + { + integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==, + } es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==, + } + engines: { node: ">= 0.4" } esbuild@0.27.2: - resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==, + } + engines: { node: ">=18" } hasBin: true escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==, + } + engines: { node: ">=6" } escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + resolution: + { + integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==, + } estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + resolution: + { + integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==, + } etag@1.8.1: - resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} - engines: {node: '>= 0.6'} + resolution: + { + integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==, + } + engines: { node: ">= 0.6" } eventemitter3@5.0.1: - resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + resolution: + { + integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==, + } expect-type@1.3.0: - resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} - engines: {node: '>=12.0.0'} + resolution: + { + integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==, + } + engines: { node: ">=12.0.0" } express@5.2.1: - resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} - engines: {node: '>= 18'} + resolution: + { + integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==, + } + engines: { node: ">= 18" } fdir@6.5.0: - resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} - engines: {node: '>=12.0.0'} + resolution: + { + integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==, + } + engines: { node: ">=12.0.0" } peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: @@ -1104,213 +1753,376 @@ packages: optional: true finalhandler@2.1.1: - resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} - engines: {node: '>= 18.0.0'} + resolution: + { + integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==, + } + engines: { node: ">= 18.0.0" } find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==, + } + engines: { node: ">=8" } fix-dts-default-cjs-exports@1.0.1: - resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} + resolution: + { + integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==, + } forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} + resolution: + { + integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==, + } + engines: { node: ">= 0.6" } fresh@2.0.0: - resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} - engines: {node: '>= 0.8'} + resolution: + { + integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==, + } + engines: { node: ">= 0.8" } fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + resolution: + { + integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, + } + engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } os: [darwin] function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + resolution: + { + integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==, + } get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} + resolution: + { + integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==, + } + engines: { node: 6.* || 8.* || >= 10.* } get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==, + } + engines: { node: ">= 0.4" } get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==, + } + engines: { node: ">= 0.4" } get-tsconfig@4.13.0: - resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} + resolution: + { + integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==, + } gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==, + } + engines: { node: ">= 0.4" } happy-dom@20.0.0: - resolution: {integrity: sha512-GkWnwIFxVGCf2raNrxImLo397RdGhLapj5cT3R2PT7FwL62Ze1DROhzmYW7+J3p9105DYMVenEejEbnq5wA37w==} - engines: {node: '>=20.0.0'} + resolution: + { + integrity: sha512-GkWnwIFxVGCf2raNrxImLo397RdGhLapj5cT3R2PT7FwL62Ze1DROhzmYW7+J3p9105DYMVenEejEbnq5wA37w==, + } + engines: { node: ">=20.0.0" } has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==, + } + engines: { node: ">=8" } has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==, + } + engines: { node: ">= 0.4" } hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==, + } + engines: { node: ">= 0.4" } html-escaper@2.0.2: - resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + resolution: + { + integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==, + } http-errors@2.0.1: - resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} - engines: {node: '>= 0.8'} + resolution: + { + integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==, + } + engines: { node: ">= 0.8" } + + i18n-iso-countries@7.14.0: + resolution: + { + integrity: sha512-nXHJZYtNrfsi1UQbyRqm3Gou431elgLjKl//CYlnBGt5aTWdRPH1PiS2T/p/n8Q8LnqYqzQJik3Q7mkwvLokeg==, + } + engines: { node: ">= 12" } iconv-lite@0.7.2: - resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==, + } + engines: { node: ">=0.10.0" } indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==, + } + engines: { node: ">=8" } inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + resolution: + { + integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==, + } ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} + resolution: + { + integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==, + } + engines: { node: ">= 0.10" } is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==, + } + engines: { node: ">=8" } is-promise@4.0.0: - resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + resolution: + { + integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==, + } isows@1.0.7: - resolution: {integrity: sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==} + resolution: + { + integrity: sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==, + } peerDependencies: - ws: '*' + ws: "*" istanbul-lib-coverage@3.2.2: - resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==, + } + engines: { node: ">=8" } istanbul-lib-report@3.0.1: - resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==, + } + engines: { node: ">=10" } istanbul-reports@3.2.0: - resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==, + } + engines: { node: ">=8" } joycon@3.1.1: - resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==, + } + engines: { node: ">=10" } js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + resolution: + { + integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==, + } js-tokens@9.0.1: - resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + resolution: + { + integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==, + } lilconfig@3.1.3: - resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==, + } + engines: { node: ">=14" } lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + resolution: + { + integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==, + } load-tsconfig@0.2.5: - resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + resolution: + { + integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==, + } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==, + } + engines: { node: ">=8" } loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + resolution: + { + integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==, + } hasBin: true lz-string@1.5.0: - resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + resolution: + { + integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==, + } hasBin: true magic-string@0.30.21: - resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + resolution: + { + integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==, + } magicast@0.5.1: - resolution: {integrity: sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==} + resolution: + { + integrity: sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==, + } make-dir@4.0.0: - resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==, + } + engines: { node: ">=10" } math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==, + } + engines: { node: ">= 0.4" } media-typer@1.1.0: - resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} - engines: {node: '>= 0.8'} + resolution: + { + integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==, + } + engines: { node: ">= 0.8" } merge-descriptors@2.0.0: - resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==, + } + engines: { node: ">=18" } mime-db@1.54.0: - resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} - engines: {node: '>= 0.6'} + resolution: + { + integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==, + } + engines: { node: ">= 0.6" } mime-types@3.0.2: - resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==, + } + engines: { node: ">=18" } min-indent@1.0.1: - resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==, + } + engines: { node: ">=4" } mlly@1.8.0: - resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} + resolution: + { + integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==, + } ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + resolution: + { + integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, + } mz@2.7.0: - resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + resolution: + { + integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==, + } nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + resolution: + { + integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==, + } + engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } hasBin: true negotiator@1.0.0: - resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} - engines: {node: '>= 0.6'} + resolution: + { + integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==, + } + engines: { node: ">= 0.6" } next@15.5.12: - resolution: {integrity: sha512-Fi/wQ4Etlrn60rz78bebG1i1SR20QxvV8tVp6iJspjLUSHcZoeUXCt+vmWoEcza85ElZzExK/jJ/F6SvtGktjA==} - engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + resolution: + { + integrity: sha512-Fi/wQ4Etlrn60rz78bebG1i1SR20QxvV8tVp6iJspjLUSHcZoeUXCt+vmWoEcza85ElZzExK/jJ/F6SvtGktjA==, + } + engines: { node: ^18.18.0 || ^19.8.0 || >= 20.0.0 } hasBin: true peerDependencies: - '@opentelemetry/api': ^1.1.0 - '@playwright/test': ^1.51.1 - babel-plugin-react-compiler: '*' + "@opentelemetry/api": ^1.1.0 + "@playwright/test": ^1.51.1 + babel-plugin-react-compiler: "*" react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 sass: ^1.3.0 peerDependenciesMeta: - '@opentelemetry/api': + "@opentelemetry/api": optional: true - '@playwright/test': + "@playwright/test": optional: true babel-plugin-react-compiler: optional: true @@ -1318,81 +2130,138 @@ packages: optional: true object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==, + } + engines: { node: ">=0.10.0" } object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==, + } + engines: { node: ">= 0.4" } obug@2.1.1: - resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + resolution: + { + integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==, + } on-finished@2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} - engines: {node: '>= 0.8'} + resolution: + { + integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==, + } + engines: { node: ">= 0.8" } once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + resolution: + { + integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==, + } ox@0.14.0: - resolution: {integrity: sha512-WLOB7IKnmI3Ol6RAqY7CJdZKl8QaI44LN91OGF1061YIeN6bL5IsFcdp7+oQShRyamE/8fW/CBRWhJAOzI35Dw==} + resolution: + { + integrity: sha512-WLOB7IKnmI3Ol6RAqY7CJdZKl8QaI44LN91OGF1061YIeN6bL5IsFcdp7+oQShRyamE/8fW/CBRWhJAOzI35Dw==, + } peerDependencies: - typescript: '>=5.4.0' + typescript: ">=5.4.0" peerDependenciesMeta: typescript: optional: true p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==, + } + engines: { node: ">=6" } p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==, + } + engines: { node: ">=8" } p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==, + } + engines: { node: ">=6" } parseurl@1.3.3: - resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} - engines: {node: '>= 0.8'} + resolution: + { + integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==, + } + engines: { node: ">= 0.8" } path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==, + } + engines: { node: ">=8" } path-to-regexp@8.3.0: - resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} + resolution: + { + integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==, + } pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + resolution: + { + integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==, + } picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + resolution: + { + integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==, + } picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==, + } + engines: { node: ">=12" } pirates@4.0.7: - resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} - engines: {node: '>= 6'} + resolution: + { + integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==, + } + engines: { node: ">= 6" } pkg-types@1.3.1: - resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + resolution: + { + integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==, + } pngjs@5.0.0: - resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} - engines: {node: '>=10.13.0'} + resolution: + { + integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==, + } + engines: { node: ">=10.13.0" } postcss-load-config@6.0.1: - resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} - engines: {node: '>= 18'} + resolution: + { + integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==, + } + engines: { node: ">= 18" } peerDependencies: - jiti: '>=1.21.0' - postcss: '>=8.0.9' + jiti: ">=1.21.0" + postcss: ">=8.0.9" tsx: ^4.8.1 yaml: ^2.4.2 peerDependenciesMeta: @@ -1406,200 +2275,344 @@ packages: optional: true postcss@8.4.31: - resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} - engines: {node: ^10 || ^12 || >=14} + resolution: + { + integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==, + } + engines: { node: ^10 || ^12 || >=14 } postcss@8.5.6: - resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} - engines: {node: ^10 || ^12 || >=14} + resolution: + { + integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==, + } + engines: { node: ^10 || ^12 || >=14 } prettier@3.8.1: - resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==, + } + engines: { node: ">=14" } hasBin: true pretty-format@27.5.1: - resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + resolution: + { + integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==, + } + engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} + resolution: + { + integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==, + } + engines: { node: ">= 0.10" } qrcode@1.5.4: - resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==} - engines: {node: '>=10.13.0'} + resolution: + { + integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==, + } + engines: { node: ">=10.13.0" } hasBin: true qs@6.14.1: - resolution: {integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==} - engines: {node: '>=0.6'} + resolution: + { + integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==, + } + engines: { node: ">=0.6" } range-parser@1.2.1: - resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} - engines: {node: '>= 0.6'} + resolution: + { + integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==, + } + engines: { node: ">= 0.6" } raw-body@3.0.2: - resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} - engines: {node: '>= 0.10'} + resolution: + { + integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==, + } + engines: { node: ">= 0.10" } react-dom@18.3.1: - resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + resolution: + { + integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==, + } peerDependencies: react: ^18.3.1 react-is@17.0.2: - resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + resolution: + { + integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==, + } react@18.3.1: - resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==, + } + engines: { node: ">=0.10.0" } readdirp@4.1.2: - resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} - engines: {node: '>= 14.18.0'} + resolution: + { + integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==, + } + engines: { node: ">= 14.18.0" } redent@3.0.0: - resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==, + } + engines: { node: ">=8" } require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==, + } + engines: { node: ">=0.10.0" } require-main-filename@2.0.0: - resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + resolution: + { + integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==, + } resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==, + } + engines: { node: ">=8" } resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolution: + { + integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==, + } rollup@4.57.0: - resolution: {integrity: sha512-e5lPJi/aui4TO1LpAXIRLySmwXSE8k3b9zoGfd42p67wzxog4WHjiZF3M2uheQih4DGyc25QEV4yRBbpueNiUA==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} + resolution: + { + integrity: sha512-e5lPJi/aui4TO1LpAXIRLySmwXSE8k3b9zoGfd42p67wzxog4WHjiZF3M2uheQih4DGyc25QEV4yRBbpueNiUA==, + } + engines: { node: ">=18.0.0", npm: ">=8.0.0" } hasBin: true router@2.2.0: - resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} - engines: {node: '>= 18'} + resolution: + { + integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==, + } + engines: { node: ">= 18" } rxjs@7.8.2: - resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + resolution: + { + integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==, + } safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + resolution: + { + integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==, + } scheduler@0.23.2: - resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + resolution: + { + integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==, + } semver@7.7.3: - resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==, + } + engines: { node: ">=10" } hasBin: true send@1.2.1: - resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} - engines: {node: '>= 18'} + resolution: + { + integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==, + } + engines: { node: ">= 18" } serve-static@2.2.1: - resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} - engines: {node: '>= 18'} + resolution: + { + integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==, + } + engines: { node: ">= 18" } set-blocking@2.0.0: - resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + resolution: + { + integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==, + } setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + resolution: + { + integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==, + } sharp@0.34.5: - resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + resolution: + { + integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } shell-quote@1.8.3: - resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==, + } + engines: { node: ">= 0.4" } side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==, + } + engines: { node: ">= 0.4" } side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==, + } + engines: { node: ">= 0.4" } side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==, + } + engines: { node: ">= 0.4" } side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==, + } + engines: { node: ">= 0.4" } siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + resolution: + { + integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==, + } source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==, + } + engines: { node: ">=0.10.0" } source-map@0.7.6: - resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} - engines: {node: '>= 12'} + resolution: + { + integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==, + } + engines: { node: ">= 12" } stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + resolution: + { + integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==, + } statuses@2.0.2: - resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} - engines: {node: '>= 0.8'} + resolution: + { + integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==, + } + engines: { node: ">= 0.8" } std-env@3.10.0: - resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + resolution: + { + integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==, + } string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==, + } + engines: { node: ">=8" } strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==, + } + engines: { node: ">=8" } strip-indent@3.0.0: - resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==, + } + engines: { node: ">=8" } styled-jsx@5.1.6: - resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==, + } + engines: { node: ">= 12.0.0" } peerDependencies: - '@babel/core': '*' - babel-plugin-macros: '*' - react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' + "@babel/core": "*" + babel-plugin-macros: "*" + react: ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" peerDependenciesMeta: - '@babel/core': + "@babel/core": optional: true babel-plugin-macros: optional: true sucrase@3.35.1: - resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} - engines: {node: '>=16 || 14 >=14.17'} + resolution: + { + integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==, + } + engines: { node: ">=16 || 14 >=14.17" } hasBin: true supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==, + } + engines: { node: ">=8" } supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==, + } + engines: { node: ">=10" } takis-minikit-js@2.0.0-dev.1: - resolution: {integrity: sha512-VhZcoDCB74xJA37pAx6TqATruS+zhD96lY0wUlMVMbeAF7t/23klAKHQJjDPJfR7Zgy8BiZtmfFRVJOHntRyEQ==} - engines: {node: '>= 16'} + resolution: + { + integrity: sha512-VhZcoDCB74xJA37pAx6TqATruS+zhD96lY0wUlMVMbeAF7t/23klAKHQJjDPJfR7Zgy8BiZtmfFRVJOHntRyEQ==, + } + engines: { node: ">= 16" } peerDependencies: react: ^17 || ^18 || ^19 siwe: ^3.0.0 @@ -1614,57 +2627,93 @@ packages: optional: true thenify-all@1.6.0: - resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} - engines: {node: '>=0.8'} + resolution: + { + integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==, + } + engines: { node: ">=0.8" } thenify@3.3.1: - resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + resolution: + { + integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==, + } tinybench@2.9.0: - resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + resolution: + { + integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==, + } tinyexec@0.3.2: - resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + resolution: + { + integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==, + } tinyexec@1.0.2: - resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==, + } + engines: { node: ">=18" } tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} - engines: {node: '>=12.0.0'} + resolution: + { + integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==, + } + engines: { node: ">=12.0.0" } tinyrainbow@3.0.3: - resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} - engines: {node: '>=14.0.0'} + resolution: + { + integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==, + } + engines: { node: ">=14.0.0" } toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} + resolution: + { + integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==, + } + engines: { node: ">=0.6" } tree-kill@1.2.2: - resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + resolution: + { + integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==, + } hasBin: true ts-interface-checker@0.1.13: - resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + resolution: + { + integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==, + } tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + resolution: + { + integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==, + } tsup@8.5.1: - resolution: {integrity: sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==, + } + engines: { node: ">=18" } hasBin: true peerDependencies: - '@microsoft/api-extractor': ^7.36.0 - '@swc/core': ^1 + "@microsoft/api-extractor": ^7.36.0 + "@swc/core": ^1 postcss: ^8.4.12 - typescript: '>=4.5.0' + typescript: ">=4.5.0" peerDependenciesMeta: - '@microsoft/api-extractor': + "@microsoft/api-extractor": optional: true - '@swc/core': + "@swc/core": optional: true postcss: optional: true @@ -1672,59 +2721,86 @@ packages: optional: true tsx@4.21.0: - resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} - engines: {node: '>=18.0.0'} + resolution: + { + integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==, + } + engines: { node: ">=18.0.0" } hasBin: true type-is@2.0.1: - resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} - engines: {node: '>= 0.6'} + resolution: + { + integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==, + } + engines: { node: ">= 0.6" } typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} - engines: {node: '>=14.17'} + resolution: + { + integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==, + } + engines: { node: ">=14.17" } hasBin: true ufo@1.6.3: - resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} + resolution: + { + integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==, + } undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + resolution: + { + integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==, + } unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} + resolution: + { + integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==, + } + engines: { node: ">= 0.8" } vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} + resolution: + { + integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==, + } + engines: { node: ">= 0.8" } viem@2.47.2: - resolution: {integrity: sha512-etDIwDgmDiGaPg8rUbJtUFuC3/nAJCbhMYyfh5dOcqNNkzBWTNcS2VluPSM5JVo+9U3b2hle2RkBEq3+xyvlvg==} + resolution: + { + integrity: sha512-etDIwDgmDiGaPg8rUbJtUFuC3/nAJCbhMYyfh5dOcqNNkzBWTNcS2VluPSM5JVo+9U3b2hle2RkBEq3+xyvlvg==, + } peerDependencies: - typescript: '>=5.0.4' + typescript: ">=5.0.4" peerDependenciesMeta: typescript: optional: true vite@7.3.1: - resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} - engines: {node: ^20.19.0 || >=22.12.0} + resolution: + { + integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==, + } + engines: { node: ^20.19.0 || >=22.12.0 } hasBin: true peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 - jiti: '>=1.21.0' + "@types/node": ^20.19.0 || >=22.12.0 + jiti: ">=1.21.0" less: ^4.0.0 lightningcss: ^1.21.0 sass: ^1.70.0 sass-embedded: ^1.70.0 - stylus: '>=0.54.8' + stylus: ">=0.54.8" sugarss: ^5.0.0 terser: ^5.16.0 tsx: ^4.8.1 yaml: ^2.4.2 peerDependenciesMeta: - '@types/node': + "@types/node": optional: true jiti: optional: true @@ -1748,33 +2824,36 @@ packages: optional: true vitest@4.0.18: - resolution: {integrity: sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==} - engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + resolution: + { + integrity: sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==, + } + engines: { node: ^20.0.0 || ^22.0.0 || >=24.0.0 } hasBin: true peerDependencies: - '@edge-runtime/vm': '*' - '@opentelemetry/api': ^1.9.0 - '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.0.18 - '@vitest/browser-preview': 4.0.18 - '@vitest/browser-webdriverio': 4.0.18 - '@vitest/ui': 4.0.18 - happy-dom: '>=20.0.0' - jsdom: '*' + "@edge-runtime/vm": "*" + "@opentelemetry/api": ^1.9.0 + "@types/node": ^20.0.0 || ^22.0.0 || >=24.0.0 + "@vitest/browser-playwright": 4.0.18 + "@vitest/browser-preview": 4.0.18 + "@vitest/browser-webdriverio": 4.0.18 + "@vitest/ui": 4.0.18 + happy-dom: ">=20.0.0" + jsdom: "*" peerDependenciesMeta: - '@edge-runtime/vm': + "@edge-runtime/vm": optional: true - '@opentelemetry/api': + "@opentelemetry/api": optional: true - '@types/node': + "@types/node": optional: true - '@vitest/browser-playwright': + "@vitest/browser-playwright": optional: true - '@vitest/browser-preview': + "@vitest/browser-preview": optional: true - '@vitest/browser-webdriverio': + "@vitest/browser-webdriverio": optional: true - '@vitest/ui': + "@vitest/ui": optional: true happy-dom: optional: true @@ -1782,34 +2861,55 @@ packages: optional: true whatwg-mimetype@3.0.0: - resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==, + } + engines: { node: ">=12" } which-module@2.0.1: - resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + resolution: + { + integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==, + } why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==, + } + engines: { node: ">=8" } hasBin: true wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==, + } + engines: { node: ">=8" } wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==, + } + engines: { node: ">=10" } wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + resolution: + { + integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==, + } ws@8.18.3: - resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} - engines: {node: '>=10.0.0'} + resolution: + { + integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==, + } + engines: { node: ">=10.0.0" } peerDependencies: bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' + utf-8-validate: ">=5.0.2" peerDependenciesMeta: bufferutil: optional: true @@ -1817,484 +2917,501 @@ packages: optional: true y18n@4.0.3: - resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + resolution: + { + integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==, + } y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==, + } + engines: { node: ">=10" } yargs-parser@18.1.3: - resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==, + } + engines: { node: ">=6" } yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==, + } + engines: { node: ">=12" } yargs@15.4.1: - resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==, + } + engines: { node: ">=8" } yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==, + } + engines: { node: ">=12" } snapshots: + "@adobe/css-tools@4.4.4": {} - '@adobe/css-tools@4.4.4': {} + "@adraffy/ens-normalize@1.11.1": {} - '@adraffy/ens-normalize@1.11.1': {} - - '@babel/code-frame@7.29.0': + "@babel/code-frame@7.29.0": dependencies: - '@babel/helper-validator-identifier': 7.28.5 + "@babel/helper-validator-identifier": 7.28.5 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/helper-string-parser@7.27.1': {} + "@babel/helper-string-parser@7.27.1": {} - '@babel/helper-validator-identifier@7.28.5': {} + "@babel/helper-validator-identifier@7.28.5": {} - '@babel/parser@7.28.6': + "@babel/parser@7.28.6": dependencies: - '@babel/types': 7.28.6 + "@babel/types": 7.28.6 - '@babel/runtime@7.28.6': {} + "@babel/runtime@7.28.6": {} - '@babel/types@7.28.6': + "@babel/types@7.28.6": dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 + "@babel/helper-string-parser": 7.27.1 + "@babel/helper-validator-identifier": 7.28.5 - '@bcoe/v8-coverage@1.0.2': {} + "@bcoe/v8-coverage@1.0.2": {} - '@emnapi/runtime@1.8.1': + "@emnapi/runtime@1.8.1": dependencies: tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.27.2': + "@esbuild/aix-ppc64@0.27.2": optional: true - '@esbuild/android-arm64@0.27.2': + "@esbuild/android-arm64@0.27.2": optional: true - '@esbuild/android-arm@0.27.2': + "@esbuild/android-arm@0.27.2": optional: true - '@esbuild/android-x64@0.27.2': + "@esbuild/android-x64@0.27.2": optional: true - '@esbuild/darwin-arm64@0.27.2': + "@esbuild/darwin-arm64@0.27.2": optional: true - '@esbuild/darwin-x64@0.27.2': + "@esbuild/darwin-x64@0.27.2": optional: true - '@esbuild/freebsd-arm64@0.27.2': + "@esbuild/freebsd-arm64@0.27.2": optional: true - '@esbuild/freebsd-x64@0.27.2': + "@esbuild/freebsd-x64@0.27.2": optional: true - '@esbuild/linux-arm64@0.27.2': + "@esbuild/linux-arm64@0.27.2": optional: true - '@esbuild/linux-arm@0.27.2': + "@esbuild/linux-arm@0.27.2": optional: true - '@esbuild/linux-ia32@0.27.2': + "@esbuild/linux-ia32@0.27.2": optional: true - '@esbuild/linux-loong64@0.27.2': + "@esbuild/linux-loong64@0.27.2": optional: true - '@esbuild/linux-mips64el@0.27.2': + "@esbuild/linux-mips64el@0.27.2": optional: true - '@esbuild/linux-ppc64@0.27.2': + "@esbuild/linux-ppc64@0.27.2": optional: true - '@esbuild/linux-riscv64@0.27.2': + "@esbuild/linux-riscv64@0.27.2": optional: true - '@esbuild/linux-s390x@0.27.2': + "@esbuild/linux-s390x@0.27.2": optional: true - '@esbuild/linux-x64@0.27.2': + "@esbuild/linux-x64@0.27.2": optional: true - '@esbuild/netbsd-arm64@0.27.2': + "@esbuild/netbsd-arm64@0.27.2": optional: true - '@esbuild/netbsd-x64@0.27.2': + "@esbuild/netbsd-x64@0.27.2": optional: true - '@esbuild/openbsd-arm64@0.27.2': + "@esbuild/openbsd-arm64@0.27.2": optional: true - '@esbuild/openbsd-x64@0.27.2': + "@esbuild/openbsd-x64@0.27.2": optional: true - '@esbuild/openharmony-arm64@0.27.2': + "@esbuild/openharmony-arm64@0.27.2": optional: true - '@esbuild/sunos-x64@0.27.2': + "@esbuild/sunos-x64@0.27.2": optional: true - '@esbuild/win32-arm64@0.27.2': + "@esbuild/win32-arm64@0.27.2": optional: true - '@esbuild/win32-ia32@0.27.2': + "@esbuild/win32-ia32@0.27.2": optional: true - '@esbuild/win32-x64@0.27.2': + "@esbuild/win32-x64@0.27.2": optional: true - '@img/colour@1.0.0': + "@img/colour@1.0.0": optional: true - '@img/sharp-darwin-arm64@0.34.5': + "@img/sharp-darwin-arm64@0.34.5": optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.2.4 + "@img/sharp-libvips-darwin-arm64": 1.2.4 optional: true - '@img/sharp-darwin-x64@0.34.5': + "@img/sharp-darwin-x64@0.34.5": optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.2.4 + "@img/sharp-libvips-darwin-x64": 1.2.4 optional: true - '@img/sharp-libvips-darwin-arm64@1.2.4': + "@img/sharp-libvips-darwin-arm64@1.2.4": optional: true - '@img/sharp-libvips-darwin-x64@1.2.4': + "@img/sharp-libvips-darwin-x64@1.2.4": optional: true - '@img/sharp-libvips-linux-arm64@1.2.4': + "@img/sharp-libvips-linux-arm64@1.2.4": optional: true - '@img/sharp-libvips-linux-arm@1.2.4': + "@img/sharp-libvips-linux-arm@1.2.4": optional: true - '@img/sharp-libvips-linux-ppc64@1.2.4': + "@img/sharp-libvips-linux-ppc64@1.2.4": optional: true - '@img/sharp-libvips-linux-riscv64@1.2.4': + "@img/sharp-libvips-linux-riscv64@1.2.4": optional: true - '@img/sharp-libvips-linux-s390x@1.2.4': + "@img/sharp-libvips-linux-s390x@1.2.4": optional: true - '@img/sharp-libvips-linux-x64@1.2.4': + "@img/sharp-libvips-linux-x64@1.2.4": optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + "@img/sharp-libvips-linuxmusl-arm64@1.2.4": optional: true - '@img/sharp-libvips-linuxmusl-x64@1.2.4': + "@img/sharp-libvips-linuxmusl-x64@1.2.4": optional: true - '@img/sharp-linux-arm64@0.34.5': + "@img/sharp-linux-arm64@0.34.5": optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.2.4 + "@img/sharp-libvips-linux-arm64": 1.2.4 optional: true - '@img/sharp-linux-arm@0.34.5': + "@img/sharp-linux-arm@0.34.5": optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.2.4 + "@img/sharp-libvips-linux-arm": 1.2.4 optional: true - '@img/sharp-linux-ppc64@0.34.5': + "@img/sharp-linux-ppc64@0.34.5": optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.2.4 + "@img/sharp-libvips-linux-ppc64": 1.2.4 optional: true - '@img/sharp-linux-riscv64@0.34.5': + "@img/sharp-linux-riscv64@0.34.5": optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.2.4 + "@img/sharp-libvips-linux-riscv64": 1.2.4 optional: true - '@img/sharp-linux-s390x@0.34.5': + "@img/sharp-linux-s390x@0.34.5": optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.2.4 + "@img/sharp-libvips-linux-s390x": 1.2.4 optional: true - '@img/sharp-linux-x64@0.34.5': + "@img/sharp-linux-x64@0.34.5": optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.2.4 + "@img/sharp-libvips-linux-x64": 1.2.4 optional: true - '@img/sharp-linuxmusl-arm64@0.34.5': + "@img/sharp-linuxmusl-arm64@0.34.5": optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + "@img/sharp-libvips-linuxmusl-arm64": 1.2.4 optional: true - '@img/sharp-linuxmusl-x64@0.34.5': + "@img/sharp-linuxmusl-x64@0.34.5": optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + "@img/sharp-libvips-linuxmusl-x64": 1.2.4 optional: true - '@img/sharp-wasm32@0.34.5': + "@img/sharp-wasm32@0.34.5": dependencies: - '@emnapi/runtime': 1.8.1 + "@emnapi/runtime": 1.8.1 optional: true - '@img/sharp-win32-arm64@0.34.5': + "@img/sharp-win32-arm64@0.34.5": optional: true - '@img/sharp-win32-ia32@0.34.5': + "@img/sharp-win32-ia32@0.34.5": optional: true - '@img/sharp-win32-x64@0.34.5': + "@img/sharp-win32-x64@0.34.5": optional: true - '@jridgewell/gen-mapping@0.3.13': + "@jridgewell/gen-mapping@0.3.13": dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.31 + "@jridgewell/sourcemap-codec": 1.5.5 + "@jridgewell/trace-mapping": 0.3.31 - '@jridgewell/resolve-uri@3.1.2': {} + "@jridgewell/resolve-uri@3.1.2": {} - '@jridgewell/sourcemap-codec@1.5.5': {} + "@jridgewell/sourcemap-codec@1.5.5": {} - '@jridgewell/trace-mapping@0.3.31': + "@jridgewell/trace-mapping@0.3.31": dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 + "@jridgewell/resolve-uri": 3.1.2 + "@jridgewell/sourcemap-codec": 1.5.5 - '@next/env@15.5.12': {} + "@next/env@15.5.12": {} - '@next/swc-darwin-arm64@15.5.12': + "@next/swc-darwin-arm64@15.5.12": optional: true - '@next/swc-darwin-x64@15.5.12': + "@next/swc-darwin-x64@15.5.12": optional: true - '@next/swc-linux-arm64-gnu@15.5.12': + "@next/swc-linux-arm64-gnu@15.5.12": optional: true - '@next/swc-linux-arm64-musl@15.5.12': + "@next/swc-linux-arm64-musl@15.5.12": optional: true - '@next/swc-linux-x64-gnu@15.5.12': + "@next/swc-linux-x64-gnu@15.5.12": optional: true - '@next/swc-linux-x64-musl@15.5.12': + "@next/swc-linux-x64-musl@15.5.12": optional: true - '@next/swc-win32-arm64-msvc@15.5.12': + "@next/swc-win32-arm64-msvc@15.5.12": optional: true - '@next/swc-win32-x64-msvc@15.5.12': + "@next/swc-win32-x64-msvc@15.5.12": optional: true - '@noble/ciphers@1.3.0': {} + "@noble/ciphers@1.3.0": {} - '@noble/curves@1.9.1': + "@noble/curves@1.9.1": dependencies: - '@noble/hashes': 1.8.0 + "@noble/hashes": 1.8.0 - '@noble/hashes@1.8.0': {} + "@noble/hashes@1.8.0": {} - '@noble/secp256k1@2.3.0': {} + "@noble/secp256k1@2.3.0": {} - '@rollup/rollup-android-arm-eabi@4.57.0': + "@rollup/rollup-android-arm-eabi@4.57.0": optional: true - '@rollup/rollup-android-arm64@4.57.0': + "@rollup/rollup-android-arm64@4.57.0": optional: true - '@rollup/rollup-darwin-arm64@4.57.0': + "@rollup/rollup-darwin-arm64@4.57.0": optional: true - '@rollup/rollup-darwin-x64@4.57.0': + "@rollup/rollup-darwin-x64@4.57.0": optional: true - '@rollup/rollup-freebsd-arm64@4.57.0': + "@rollup/rollup-freebsd-arm64@4.57.0": optional: true - '@rollup/rollup-freebsd-x64@4.57.0': + "@rollup/rollup-freebsd-x64@4.57.0": optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.57.0': + "@rollup/rollup-linux-arm-gnueabihf@4.57.0": optional: true - '@rollup/rollup-linux-arm-musleabihf@4.57.0': + "@rollup/rollup-linux-arm-musleabihf@4.57.0": optional: true - '@rollup/rollup-linux-arm64-gnu@4.57.0': + "@rollup/rollup-linux-arm64-gnu@4.57.0": optional: true - '@rollup/rollup-linux-arm64-musl@4.57.0': + "@rollup/rollup-linux-arm64-musl@4.57.0": optional: true - '@rollup/rollup-linux-loong64-gnu@4.57.0': + "@rollup/rollup-linux-loong64-gnu@4.57.0": optional: true - '@rollup/rollup-linux-loong64-musl@4.57.0': + "@rollup/rollup-linux-loong64-musl@4.57.0": optional: true - '@rollup/rollup-linux-ppc64-gnu@4.57.0': + "@rollup/rollup-linux-ppc64-gnu@4.57.0": optional: true - '@rollup/rollup-linux-ppc64-musl@4.57.0': + "@rollup/rollup-linux-ppc64-musl@4.57.0": optional: true - '@rollup/rollup-linux-riscv64-gnu@4.57.0': + "@rollup/rollup-linux-riscv64-gnu@4.57.0": optional: true - '@rollup/rollup-linux-riscv64-musl@4.57.0': + "@rollup/rollup-linux-riscv64-musl@4.57.0": optional: true - '@rollup/rollup-linux-s390x-gnu@4.57.0': + "@rollup/rollup-linux-s390x-gnu@4.57.0": optional: true - '@rollup/rollup-linux-x64-gnu@4.57.0': + "@rollup/rollup-linux-x64-gnu@4.57.0": optional: true - '@rollup/rollup-linux-x64-musl@4.57.0': + "@rollup/rollup-linux-x64-musl@4.57.0": optional: true - '@rollup/rollup-openbsd-x64@4.57.0': + "@rollup/rollup-openbsd-x64@4.57.0": optional: true - '@rollup/rollup-openharmony-arm64@4.57.0': + "@rollup/rollup-openharmony-arm64@4.57.0": optional: true - '@rollup/rollup-win32-arm64-msvc@4.57.0': + "@rollup/rollup-win32-arm64-msvc@4.57.0": optional: true - '@rollup/rollup-win32-ia32-msvc@4.57.0': + "@rollup/rollup-win32-ia32-msvc@4.57.0": optional: true - '@rollup/rollup-win32-x64-gnu@4.57.0': + "@rollup/rollup-win32-x64-gnu@4.57.0": optional: true - '@rollup/rollup-win32-x64-msvc@4.57.0': + "@rollup/rollup-win32-x64-msvc@4.57.0": optional: true - '@scure/base@1.2.6': {} + "@scure/base@1.2.6": {} - '@scure/bip32@1.7.0': + "@scure/bip32@1.7.0": dependencies: - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/base': 1.2.6 + "@noble/curves": 1.9.1 + "@noble/hashes": 1.8.0 + "@scure/base": 1.2.6 - '@scure/bip39@1.6.0': + "@scure/bip39@1.6.0": dependencies: - '@noble/hashes': 1.8.0 - '@scure/base': 1.2.6 + "@noble/hashes": 1.8.0 + "@scure/base": 1.2.6 - '@standard-schema/spec@1.1.0': {} + "@standard-schema/spec@1.1.0": {} - '@swc/helpers@0.5.15': + "@swc/helpers@0.5.15": dependencies: tslib: 2.8.1 - '@testing-library/dom@10.4.1': + "@testing-library/dom@10.4.1": dependencies: - '@babel/code-frame': 7.29.0 - '@babel/runtime': 7.28.6 - '@types/aria-query': 5.0.4 + "@babel/code-frame": 7.29.0 + "@babel/runtime": 7.28.6 + "@types/aria-query": 5.0.4 aria-query: 5.3.0 dom-accessibility-api: 0.5.16 lz-string: 1.5.0 picocolors: 1.1.1 pretty-format: 27.5.1 - '@testing-library/jest-dom@6.9.1': + "@testing-library/jest-dom@6.9.1": dependencies: - '@adobe/css-tools': 4.4.4 + "@adobe/css-tools": 4.4.4 aria-query: 5.3.2 css.escape: 1.5.1 dom-accessibility-api: 0.6.3 picocolors: 1.1.1 redent: 3.0.0 - '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + "@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)": dependencies: - '@babel/runtime': 7.28.6 - '@testing-library/dom': 10.4.1 + "@babel/runtime": 7.28.6 + "@testing-library/dom": 10.4.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) + "@types/react": 18.3.28 + "@types/react-dom": 18.3.7(@types/react@18.3.28) - '@types/aria-query@5.0.4': {} + "@types/aria-query@5.0.4": {} - '@types/body-parser@1.19.6': + "@types/body-parser@1.19.6": dependencies: - '@types/connect': 3.4.38 - '@types/node': 20.19.30 + "@types/connect": 3.4.38 + "@types/node": 20.19.30 - '@types/chai@5.2.3': + "@types/chai@5.2.3": dependencies: - '@types/deep-eql': 4.0.2 + "@types/deep-eql": 4.0.2 assertion-error: 2.0.1 - '@types/connect@3.4.38': + "@types/connect@3.4.38": dependencies: - '@types/node': 20.19.30 + "@types/node": 20.19.30 - '@types/deep-eql@4.0.2': {} + "@types/deep-eql@4.0.2": {} - '@types/estree@1.0.8': {} + "@types/estree@1.0.8": {} - '@types/express-serve-static-core@5.1.1': + "@types/express-serve-static-core@5.1.1": dependencies: - '@types/node': 20.19.30 - '@types/qs': 6.14.0 - '@types/range-parser': 1.2.7 - '@types/send': 1.2.1 + "@types/node": 20.19.30 + "@types/qs": 6.14.0 + "@types/range-parser": 1.2.7 + "@types/send": 1.2.1 - '@types/express@5.0.6': + "@types/express@5.0.6": dependencies: - '@types/body-parser': 1.19.6 - '@types/express-serve-static-core': 5.1.1 - '@types/serve-static': 2.2.0 + "@types/body-parser": 1.19.6 + "@types/express-serve-static-core": 5.1.1 + "@types/serve-static": 2.2.0 - '@types/http-errors@2.0.5': {} + "@types/http-errors@2.0.5": {} - '@types/node@20.19.30': + "@types/node@20.19.30": dependencies: undici-types: 6.21.0 - '@types/prop-types@15.7.15': {} + "@types/prop-types@15.7.15": {} - '@types/qrcode@1.5.6': + "@types/qrcode@1.5.6": dependencies: - '@types/node': 20.19.30 + "@types/node": 20.19.30 - '@types/qs@6.14.0': {} + "@types/qs@6.14.0": {} - '@types/range-parser@1.2.7': {} + "@types/range-parser@1.2.7": {} - '@types/react-dom@18.3.7(@types/react@18.3.28)': + "@types/react-dom@18.3.7(@types/react@18.3.28)": dependencies: - '@types/react': 18.3.28 + "@types/react": 18.3.28 - '@types/react@18.3.28': + "@types/react@18.3.28": dependencies: - '@types/prop-types': 15.7.15 + "@types/prop-types": 15.7.15 csstype: 3.2.3 - '@types/send@1.2.1': + "@types/send@1.2.1": dependencies: - '@types/node': 20.19.30 + "@types/node": 20.19.30 - '@types/serve-static@2.2.0': + "@types/serve-static@2.2.0": dependencies: - '@types/http-errors': 2.0.5 - '@types/node': 20.19.30 + "@types/http-errors": 2.0.5 + "@types/node": 20.19.30 - '@types/whatwg-mimetype@3.0.2': {} + "@types/whatwg-mimetype@3.0.2": {} - '@vitest/coverage-v8@4.0.18(vitest@4.0.18(@types/node@20.19.30)(happy-dom@20.0.0)(tsx@4.21.0))': + "@vitest/coverage-v8@4.0.18(vitest@4.0.18(@types/node@20.19.30)(happy-dom@20.0.0)(tsx@4.21.0))": dependencies: - '@bcoe/v8-coverage': 1.0.2 - '@vitest/utils': 4.0.18 + "@bcoe/v8-coverage": 1.0.2 + "@vitest/utils": 4.0.18 ast-v8-to-istanbul: 0.3.10 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 @@ -2305,43 +3422,43 @@ snapshots: tinyrainbow: 3.0.3 vitest: 4.0.18(@types/node@20.19.30)(happy-dom@20.0.0)(tsx@4.21.0) - '@vitest/expect@4.0.18': + "@vitest/expect@4.0.18": dependencies: - '@standard-schema/spec': 1.1.0 - '@types/chai': 5.2.3 - '@vitest/spy': 4.0.18 - '@vitest/utils': 4.0.18 + "@standard-schema/spec": 1.1.0 + "@types/chai": 5.2.3 + "@vitest/spy": 4.0.18 + "@vitest/utils": 4.0.18 chai: 6.2.2 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@20.19.30)(tsx@4.21.0))': + "@vitest/mocker@4.0.18(vite@7.3.1(@types/node@20.19.30)(tsx@4.21.0))": dependencies: - '@vitest/spy': 4.0.18 + "@vitest/spy": 4.0.18 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: vite: 7.3.1(@types/node@20.19.30)(tsx@4.21.0) - '@vitest/pretty-format@4.0.18': + "@vitest/pretty-format@4.0.18": dependencies: tinyrainbow: 3.0.3 - '@vitest/runner@4.0.18': + "@vitest/runner@4.0.18": dependencies: - '@vitest/utils': 4.0.18 + "@vitest/utils": 4.0.18 pathe: 2.0.3 - '@vitest/snapshot@4.0.18': + "@vitest/snapshot@4.0.18": dependencies: - '@vitest/pretty-format': 4.0.18 + "@vitest/pretty-format": 4.0.18 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.0.18': {} + "@vitest/spy@4.0.18": {} - '@vitest/utils@4.0.18': + "@vitest/utils@4.0.18": dependencies: - '@vitest/pretty-format': 4.0.18 + "@vitest/pretty-format": 4.0.18 tinyrainbow: 3.0.3 abitype@1.2.3(typescript@5.9.3): @@ -2375,7 +3492,7 @@ snapshots: ast-v8-to-istanbul@0.3.10: dependencies: - '@jridgewell/trace-mapping': 0.3.31 + "@jridgewell/trace-mapping": 0.3.31 estree-walker: 3.0.3 js-tokens: 9.0.1 @@ -2487,6 +3604,8 @@ snapshots: detect-libc@2.1.2: optional: true + diacritics@1.3.0: {} + dijkstrajs@1.0.3: {} dom-accessibility-api@0.5.16: {} @@ -2521,32 +3640,32 @@ snapshots: esbuild@0.27.2: optionalDependencies: - '@esbuild/aix-ppc64': 0.27.2 - '@esbuild/android-arm': 0.27.2 - '@esbuild/android-arm64': 0.27.2 - '@esbuild/android-x64': 0.27.2 - '@esbuild/darwin-arm64': 0.27.2 - '@esbuild/darwin-x64': 0.27.2 - '@esbuild/freebsd-arm64': 0.27.2 - '@esbuild/freebsd-x64': 0.27.2 - '@esbuild/linux-arm': 0.27.2 - '@esbuild/linux-arm64': 0.27.2 - '@esbuild/linux-ia32': 0.27.2 - '@esbuild/linux-loong64': 0.27.2 - '@esbuild/linux-mips64el': 0.27.2 - '@esbuild/linux-ppc64': 0.27.2 - '@esbuild/linux-riscv64': 0.27.2 - '@esbuild/linux-s390x': 0.27.2 - '@esbuild/linux-x64': 0.27.2 - '@esbuild/netbsd-arm64': 0.27.2 - '@esbuild/netbsd-x64': 0.27.2 - '@esbuild/openbsd-arm64': 0.27.2 - '@esbuild/openbsd-x64': 0.27.2 - '@esbuild/openharmony-arm64': 0.27.2 - '@esbuild/sunos-x64': 0.27.2 - '@esbuild/win32-arm64': 0.27.2 - '@esbuild/win32-ia32': 0.27.2 - '@esbuild/win32-x64': 0.27.2 + "@esbuild/aix-ppc64": 0.27.2 + "@esbuild/android-arm": 0.27.2 + "@esbuild/android-arm64": 0.27.2 + "@esbuild/android-x64": 0.27.2 + "@esbuild/darwin-arm64": 0.27.2 + "@esbuild/darwin-x64": 0.27.2 + "@esbuild/freebsd-arm64": 0.27.2 + "@esbuild/freebsd-x64": 0.27.2 + "@esbuild/linux-arm": 0.27.2 + "@esbuild/linux-arm64": 0.27.2 + "@esbuild/linux-ia32": 0.27.2 + "@esbuild/linux-loong64": 0.27.2 + "@esbuild/linux-mips64el": 0.27.2 + "@esbuild/linux-ppc64": 0.27.2 + "@esbuild/linux-riscv64": 0.27.2 + "@esbuild/linux-s390x": 0.27.2 + "@esbuild/linux-x64": 0.27.2 + "@esbuild/netbsd-arm64": 0.27.2 + "@esbuild/netbsd-x64": 0.27.2 + "@esbuild/openbsd-arm64": 0.27.2 + "@esbuild/openbsd-x64": 0.27.2 + "@esbuild/openharmony-arm64": 0.27.2 + "@esbuild/sunos-x64": 0.27.2 + "@esbuild/win32-arm64": 0.27.2 + "@esbuild/win32-ia32": 0.27.2 + "@esbuild/win32-x64": 0.27.2 escalade@3.2.0: {} @@ -2554,7 +3673,7 @@ snapshots: estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.8 + "@types/estree": 1.0.8 etag@1.8.1: {} @@ -2658,8 +3777,8 @@ snapshots: happy-dom@20.0.0: dependencies: - '@types/node': 20.19.30 - '@types/whatwg-mimetype': 3.0.2 + "@types/node": 20.19.30 + "@types/whatwg-mimetype": 3.0.2 whatwg-mimetype: 3.0.0 has-flag@4.0.0: {} @@ -2680,6 +3799,10 @@ snapshots: statuses: 2.0.2 toidentifier: 1.0.1 + i18n-iso-countries@7.14.0: + dependencies: + diacritics: 1.3.0 + iconv-lite@0.7.2: dependencies: safer-buffer: 2.1.2 @@ -2735,12 +3858,12 @@ snapshots: magic-string@0.30.21: dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 + "@jridgewell/sourcemap-codec": 1.5.5 magicast@0.5.1: dependencies: - '@babel/parser': 7.28.6 - '@babel/types': 7.28.6 + "@babel/parser": 7.28.6 + "@babel/types": 7.28.6 source-map-js: 1.2.1 make-dir@4.0.0: @@ -2782,25 +3905,25 @@ snapshots: next@15.5.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@next/env': 15.5.12 - '@swc/helpers': 0.5.15 + "@next/env": 15.5.12 + "@swc/helpers": 0.5.15 caniuse-lite: 1.0.30001769 postcss: 8.4.31 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) styled-jsx: 5.1.6(react@18.3.1) optionalDependencies: - '@next/swc-darwin-arm64': 15.5.12 - '@next/swc-darwin-x64': 15.5.12 - '@next/swc-linux-arm64-gnu': 15.5.12 - '@next/swc-linux-arm64-musl': 15.5.12 - '@next/swc-linux-x64-gnu': 15.5.12 - '@next/swc-linux-x64-musl': 15.5.12 - '@next/swc-win32-arm64-msvc': 15.5.12 - '@next/swc-win32-x64-msvc': 15.5.12 + "@next/swc-darwin-arm64": 15.5.12 + "@next/swc-darwin-x64": 15.5.12 + "@next/swc-linux-arm64-gnu": 15.5.12 + "@next/swc-linux-arm64-musl": 15.5.12 + "@next/swc-linux-x64-gnu": 15.5.12 + "@next/swc-linux-x64-musl": 15.5.12 + "@next/swc-win32-arm64-msvc": 15.5.12 + "@next/swc-win32-x64-msvc": 15.5.12 sharp: 0.34.5 transitivePeerDependencies: - - '@babel/core' + - "@babel/core" - babel-plugin-macros object-assign@4.1.1: {} @@ -2819,12 +3942,12 @@ snapshots: ox@0.14.0(typescript@5.9.3): dependencies: - '@adraffy/ens-normalize': 1.11.1 - '@noble/ciphers': 1.3.0 - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 + "@adraffy/ens-normalize": 1.11.1 + "@noble/ciphers": 1.3.0 + "@noble/curves": 1.9.1 + "@noble/hashes": 1.8.0 + "@scure/bip32": 1.7.0 + "@scure/bip39": 1.6.0 abitype: 1.2.3(typescript@5.9.3) eventemitter3: 5.0.1 optionalDependencies: @@ -2944,33 +4067,33 @@ snapshots: rollup@4.57.0: dependencies: - '@types/estree': 1.0.8 + "@types/estree": 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.57.0 - '@rollup/rollup-android-arm64': 4.57.0 - '@rollup/rollup-darwin-arm64': 4.57.0 - '@rollup/rollup-darwin-x64': 4.57.0 - '@rollup/rollup-freebsd-arm64': 4.57.0 - '@rollup/rollup-freebsd-x64': 4.57.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.57.0 - '@rollup/rollup-linux-arm-musleabihf': 4.57.0 - '@rollup/rollup-linux-arm64-gnu': 4.57.0 - '@rollup/rollup-linux-arm64-musl': 4.57.0 - '@rollup/rollup-linux-loong64-gnu': 4.57.0 - '@rollup/rollup-linux-loong64-musl': 4.57.0 - '@rollup/rollup-linux-ppc64-gnu': 4.57.0 - '@rollup/rollup-linux-ppc64-musl': 4.57.0 - '@rollup/rollup-linux-riscv64-gnu': 4.57.0 - '@rollup/rollup-linux-riscv64-musl': 4.57.0 - '@rollup/rollup-linux-s390x-gnu': 4.57.0 - '@rollup/rollup-linux-x64-gnu': 4.57.0 - '@rollup/rollup-linux-x64-musl': 4.57.0 - '@rollup/rollup-openbsd-x64': 4.57.0 - '@rollup/rollup-openharmony-arm64': 4.57.0 - '@rollup/rollup-win32-arm64-msvc': 4.57.0 - '@rollup/rollup-win32-ia32-msvc': 4.57.0 - '@rollup/rollup-win32-x64-gnu': 4.57.0 - '@rollup/rollup-win32-x64-msvc': 4.57.0 + "@rollup/rollup-android-arm-eabi": 4.57.0 + "@rollup/rollup-android-arm64": 4.57.0 + "@rollup/rollup-darwin-arm64": 4.57.0 + "@rollup/rollup-darwin-x64": 4.57.0 + "@rollup/rollup-freebsd-arm64": 4.57.0 + "@rollup/rollup-freebsd-x64": 4.57.0 + "@rollup/rollup-linux-arm-gnueabihf": 4.57.0 + "@rollup/rollup-linux-arm-musleabihf": 4.57.0 + "@rollup/rollup-linux-arm64-gnu": 4.57.0 + "@rollup/rollup-linux-arm64-musl": 4.57.0 + "@rollup/rollup-linux-loong64-gnu": 4.57.0 + "@rollup/rollup-linux-loong64-musl": 4.57.0 + "@rollup/rollup-linux-ppc64-gnu": 4.57.0 + "@rollup/rollup-linux-ppc64-musl": 4.57.0 + "@rollup/rollup-linux-riscv64-gnu": 4.57.0 + "@rollup/rollup-linux-riscv64-musl": 4.57.0 + "@rollup/rollup-linux-s390x-gnu": 4.57.0 + "@rollup/rollup-linux-x64-gnu": 4.57.0 + "@rollup/rollup-linux-x64-musl": 4.57.0 + "@rollup/rollup-openbsd-x64": 4.57.0 + "@rollup/rollup-openharmony-arm64": 4.57.0 + "@rollup/rollup-win32-arm64-msvc": 4.57.0 + "@rollup/rollup-win32-ia32-msvc": 4.57.0 + "@rollup/rollup-win32-x64-gnu": 4.57.0 + "@rollup/rollup-win32-x64-msvc": 4.57.0 fsevents: 2.3.3 router@2.2.0: @@ -3026,34 +4149,34 @@ snapshots: sharp@0.34.5: dependencies: - '@img/colour': 1.0.0 + "@img/colour": 1.0.0 detect-libc: 2.1.2 semver: 7.7.3 optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.5 - '@img/sharp-darwin-x64': 0.34.5 - '@img/sharp-libvips-darwin-arm64': 1.2.4 - '@img/sharp-libvips-darwin-x64': 1.2.4 - '@img/sharp-libvips-linux-arm': 1.2.4 - '@img/sharp-libvips-linux-arm64': 1.2.4 - '@img/sharp-libvips-linux-ppc64': 1.2.4 - '@img/sharp-libvips-linux-riscv64': 1.2.4 - '@img/sharp-libvips-linux-s390x': 1.2.4 - '@img/sharp-libvips-linux-x64': 1.2.4 - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - '@img/sharp-linux-arm': 0.34.5 - '@img/sharp-linux-arm64': 0.34.5 - '@img/sharp-linux-ppc64': 0.34.5 - '@img/sharp-linux-riscv64': 0.34.5 - '@img/sharp-linux-s390x': 0.34.5 - '@img/sharp-linux-x64': 0.34.5 - '@img/sharp-linuxmusl-arm64': 0.34.5 - '@img/sharp-linuxmusl-x64': 0.34.5 - '@img/sharp-wasm32': 0.34.5 - '@img/sharp-win32-arm64': 0.34.5 - '@img/sharp-win32-ia32': 0.34.5 - '@img/sharp-win32-x64': 0.34.5 + "@img/sharp-darwin-arm64": 0.34.5 + "@img/sharp-darwin-x64": 0.34.5 + "@img/sharp-libvips-darwin-arm64": 1.2.4 + "@img/sharp-libvips-darwin-x64": 1.2.4 + "@img/sharp-libvips-linux-arm": 1.2.4 + "@img/sharp-libvips-linux-arm64": 1.2.4 + "@img/sharp-libvips-linux-ppc64": 1.2.4 + "@img/sharp-libvips-linux-riscv64": 1.2.4 + "@img/sharp-libvips-linux-s390x": 1.2.4 + "@img/sharp-libvips-linux-x64": 1.2.4 + "@img/sharp-libvips-linuxmusl-arm64": 1.2.4 + "@img/sharp-libvips-linuxmusl-x64": 1.2.4 + "@img/sharp-linux-arm": 0.34.5 + "@img/sharp-linux-arm64": 0.34.5 + "@img/sharp-linux-ppc64": 0.34.5 + "@img/sharp-linux-riscv64": 0.34.5 + "@img/sharp-linux-s390x": 0.34.5 + "@img/sharp-linux-x64": 0.34.5 + "@img/sharp-linuxmusl-arm64": 0.34.5 + "@img/sharp-linuxmusl-x64": 0.34.5 + "@img/sharp-wasm32": 0.34.5 + "@img/sharp-win32-arm64": 0.34.5 + "@img/sharp-win32-ia32": 0.34.5 + "@img/sharp-win32-x64": 0.34.5 optional: true shell-quote@1.8.3: {} @@ -3119,7 +4242,7 @@ snapshots: sucrase@3.35.1: dependencies: - '@jridgewell/gen-mapping': 0.3.13 + "@jridgewell/gen-mapping": 0.3.13 commander: 4.1.1 lines-and-columns: 1.2.4 mz: 2.7.0 @@ -3227,10 +4350,10 @@ snapshots: viem@2.47.2(typescript@5.9.3): dependencies: - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 + "@noble/curves": 1.9.1 + "@noble/hashes": 1.8.0 + "@scure/bip32": 1.7.0 + "@scure/bip39": 1.6.0 abitype: 1.2.3(typescript@5.9.3) isows: 1.0.7(ws@8.18.3) ox: 0.14.0(typescript@5.9.3) @@ -3251,19 +4374,19 @@ snapshots: rollup: 4.57.0 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 20.19.30 + "@types/node": 20.19.30 fsevents: 2.3.3 tsx: 4.21.0 vitest@4.0.18(@types/node@20.19.30)(happy-dom@20.0.0)(tsx@4.21.0): dependencies: - '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@20.19.30)(tsx@4.21.0)) - '@vitest/pretty-format': 4.0.18 - '@vitest/runner': 4.0.18 - '@vitest/snapshot': 4.0.18 - '@vitest/spy': 4.0.18 - '@vitest/utils': 4.0.18 + "@vitest/expect": 4.0.18 + "@vitest/mocker": 4.0.18(vite@7.3.1(@types/node@20.19.30)(tsx@4.21.0)) + "@vitest/pretty-format": 4.0.18 + "@vitest/runner": 4.0.18 + "@vitest/snapshot": 4.0.18 + "@vitest/spy": 4.0.18 + "@vitest/utils": 4.0.18 es-module-lexer: 1.7.0 expect-type: 1.3.0 magic-string: 0.30.21 @@ -3278,7 +4401,7 @@ snapshots: vite: 7.3.1(@types/node@20.19.30)(tsx@4.21.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 20.19.30 + "@types/node": 20.19.30 happy-dom: 20.0.0 transitivePeerDependencies: - jiti From d40f36d5354acfaf66a67f991257af2406762182 Mon Sep 17 00:00:00 2001 From: Takis Kakalis <80459599+Takaros999@users.noreply.github.com> Date: Mon, 4 May 2026 14:29:53 -0700 Subject: [PATCH 09/12] feat(core): add world id availability errors --- js/packages/core/src/__tests__/smoke.test.ts | 6 ++++++ js/packages/core/src/types/result.ts | 2 ++ .../src/main/kotlin/com/worldcoin/idkit/IdKit.kt | 4 ++++ rust/core/src/bridge.rs | 2 ++ rust/core/src/error.rs | 12 ++++++++++++ rust/core/src/wasm_bindings.rs | 2 ++ swift/Sources/IDKit/IDKit.swift | 6 ++++++ 7 files changed, 34 insertions(+) diff --git a/js/packages/core/src/__tests__/smoke.test.ts b/js/packages/core/src/__tests__/smoke.test.ts index 7887abd7..39f75fc8 100644 --- a/js/packages/core/src/__tests__/smoke.test.ts +++ b/js/packages/core/src/__tests__/smoke.test.ts @@ -196,6 +196,12 @@ describe("Enums", () => { expect(IDKitErrorCodes.CredentialUnavailable).toBe( "credential_unavailable", ); + expect(IDKitErrorCodes.WorldId4NotAvailable).toBe( + "world_id_4_not_available", + ); + expect(IDKitErrorCodes.WorldId3NotAvailable).toBe( + "world_id_3_not_available", + ); expect(IDKitErrorCodes.InvalidRpSignature).toBe("invalid_rp_signature"); expect(IDKitErrorCodes.NullifierReplayed).toBe("nullifier_replayed"); expect(IDKitErrorCodes.DuplicateNonce).toBe("duplicate_nonce"); diff --git a/js/packages/core/src/types/result.ts b/js/packages/core/src/types/result.ts index deb54858..a38c8336 100644 --- a/js/packages/core/src/types/result.ts +++ b/js/packages/core/src/types/result.ts @@ -32,6 +32,8 @@ export enum IDKitErrorCodes { UserRejected = "user_rejected", VerificationRejected = "verification_rejected", CredentialUnavailable = "credential_unavailable", + WorldId4NotAvailable = "world_id_4_not_available", + WorldId3NotAvailable = "world_id_3_not_available", MalformedRequest = "malformed_request", InvalidNetwork = "invalid_network", InclusionProofPending = "inclusion_proof_pending", diff --git a/kotlin/bindings/src/main/kotlin/com/worldcoin/idkit/IdKit.kt b/kotlin/bindings/src/main/kotlin/com/worldcoin/idkit/IdKit.kt index fe25fcee..a4927d2e 100644 --- a/kotlin/bindings/src/main/kotlin/com/worldcoin/idkit/IdKit.kt +++ b/kotlin/bindings/src/main/kotlin/com/worldcoin/idkit/IdKit.kt @@ -44,6 +44,8 @@ enum class IDKitErrorCode(val rawValue: String) { USER_REJECTED("user_rejected"), VERIFICATION_REJECTED("verification_rejected"), CREDENTIAL_UNAVAILABLE("credential_unavailable"), + WORLD_ID_4_NOT_AVAILABLE("world_id_4_not_available"), + WORLD_ID_3_NOT_AVAILABLE("world_id_3_not_available"), MALFORMED_REQUEST("malformed_request"), INVALID_NETWORK("invalid_network"), INCLUSION_PROOF_PENDING("inclusion_proof_pending"), @@ -70,6 +72,8 @@ enum class IDKitErrorCode(val rawValue: String) { AppError.USER_REJECTED -> USER_REJECTED AppError.VERIFICATION_REJECTED -> VERIFICATION_REJECTED AppError.CREDENTIAL_UNAVAILABLE -> CREDENTIAL_UNAVAILABLE + AppError.WORLD_ID4_NOT_AVAILABLE -> WORLD_ID_4_NOT_AVAILABLE + AppError.WORLD_ID3_NOT_AVAILABLE -> WORLD_ID_3_NOT_AVAILABLE AppError.MALFORMED_REQUEST -> MALFORMED_REQUEST AppError.INVALID_NETWORK -> INVALID_NETWORK AppError.INCLUSION_PROOF_PENDING -> INCLUSION_PROOF_PENDING diff --git a/rust/core/src/bridge.rs b/rust/core/src/bridge.rs index bc090fd1..5d4363a7 100644 --- a/rust/core/src/bridge.rs +++ b/rust/core/src/bridge.rs @@ -1765,6 +1765,8 @@ mod tests { )); let cases = [ + ("world_id_4_not_available", AppError::WorldId4NotAvailable), + ("world_id_3_not_available", AppError::WorldId3NotAvailable), ("duplicate_nonce", AppError::DuplicateNonce), ("unknown_rp", AppError::UnknownRp), ("inactive_rp", AppError::InactiveRp), diff --git a/rust/core/src/error.rs b/rust/core/src/error.rs index a4285388..b11ad3be 100644 --- a/rust/core/src/error.rs +++ b/rust/core/src/error.rs @@ -76,6 +76,16 @@ pub enum AppError { #[error("Requested credential is not available")] CredentialUnavailable, + /// World ID 4.0 credential is not available + #[error("World ID 4.0 is not available")] + #[serde(rename = "world_id_4_not_available")] + WorldId4NotAvailable, + + /// World ID 3.0 credential is not available + #[error("World ID 3.0 is not available")] + #[serde(rename = "world_id_3_not_available")] + WorldId3NotAvailable, + /// Malformed request #[error("Request is malformed")] MalformedRequest, @@ -158,6 +168,8 @@ impl AppError { "user_rejected" => Self::UserRejected, "verification_rejected" => Self::VerificationRejected, "credential_unavailable" => Self::CredentialUnavailable, + "world_id_4_not_available" => Self::WorldId4NotAvailable, + "world_id_3_not_available" => Self::WorldId3NotAvailable, "malformed_request" => Self::MalformedRequest, "invalid_network" => Self::InvalidNetwork, "inclusion_proof_pending" => Self::InclusionProofPending, diff --git a/rust/core/src/wasm_bindings.rs b/rust/core/src/wasm_bindings.rs index e11fa0d6..ac52f001 100644 --- a/rust/core/src/wasm_bindings.rs +++ b/rust/core/src/wasm_bindings.rs @@ -1288,6 +1288,8 @@ export type IDKitErrorCode = | "user_rejected" | "verification_rejected" | "credential_unavailable" + | "world_id_4_not_available" + | "world_id_3_not_available" | "malformed_request" | "invalid_network" | "inclusion_proof_pending" diff --git a/swift/Sources/IDKit/IDKit.swift b/swift/Sources/IDKit/IDKit.swift index 0f3d29cd..79d09a19 100644 --- a/swift/Sources/IDKit/IDKit.swift +++ b/swift/Sources/IDKit/IDKit.swift @@ -89,6 +89,8 @@ public enum IDKitErrorCode: String, Equatable { case userRejected = "user_rejected" case verificationRejected = "verification_rejected" case credentialUnavailable = "credential_unavailable" + case worldId4NotAvailable = "world_id_4_not_available" + case worldId3NotAvailable = "world_id_3_not_available" case malformedRequest = "malformed_request" case invalidNetwork = "invalid_network" case inclusionProofPending = "inclusion_proof_pending" @@ -118,6 +120,10 @@ public enum IDKitErrorCode: String, Equatable { .verificationRejected case .credentialUnavailable: .credentialUnavailable + case .worldId4NotAvailable: + .worldId4NotAvailable + case .worldId3NotAvailable: + .worldId3NotAvailable case .malformedRequest: .malformedRequest case .invalidNetwork: From ab8c5321552850e32764648c17f2d66cccd55a34 Mon Sep 17 00:00:00 2001 From: Takis Kakalis <80459599+Takaros999@users.noreply.github.com> Date: Mon, 4 May 2026 14:30:31 -0700 Subject: [PATCH 10/12] feat(examples): add idkit arena --- js/examples/nextjs/README.md | 4 + .../rp-context/route.ts | 15 +- js/examples/nextjs/app/arena/page.tsx | 18 + js/examples/nextjs/app/arena/ui.tsx | 1326 +++++++++++++++++ js/examples/nextjs/app/error-cases/page.tsx | 18 - js/examples/nextjs/app/error-cases/ui.tsx | 667 --------- js/examples/nextjs/app/globals.css | 51 +- js/examples/nextjs/app/page.tsx | 4 +- js/examples/nextjs/app/ui.tsx | 4 +- 9 files changed, 1411 insertions(+), 696 deletions(-) rename js/examples/nextjs/app/api/{error-cases => arena}/rp-context/route.ts (96%) create mode 100644 js/examples/nextjs/app/arena/page.tsx create mode 100644 js/examples/nextjs/app/arena/ui.tsx delete mode 100644 js/examples/nextjs/app/error-cases/page.tsx delete mode 100644 js/examples/nextjs/app/error-cases/ui.tsx diff --git a/js/examples/nextjs/README.md b/js/examples/nextjs/README.md index 8d5246a6..94a5fdd2 100644 --- a/js/examples/nextjs/README.md +++ b/js/examples/nextjs/README.md @@ -14,6 +14,10 @@ The UI includes request buttons matching the browser example presets: - Device Legacy - Selfie Check Legacy +The `/arena` route includes grouped mobile implementation test cases for +World ID 3.0 presets, World ID 4.0 constraints, migration fallback behavior, +and 4.0 error handling. + ## Run From repo root: diff --git a/js/examples/nextjs/app/api/error-cases/rp-context/route.ts b/js/examples/nextjs/app/api/arena/rp-context/route.ts similarity index 96% rename from js/examples/nextjs/app/api/error-cases/rp-context/route.ts rename to js/examples/nextjs/app/api/arena/rp-context/route.ts index 6ff229cd..8217d6a9 100644 --- a/js/examples/nextjs/app/api/error-cases/rp-context/route.ts +++ b/js/examples/nextjs/app/api/arena/rp-context/route.ts @@ -6,7 +6,7 @@ import { import { bytesToHex, hexToBytes, keccak256, type Hex } from "viem"; import { privateKeyToAccount } from "viem/accounts"; -type TestCaseId = +type RpContextCaseId = | "valid_success" | "invalid_rp_signature" | "duplicate_nonce" @@ -28,12 +28,12 @@ type RpContextPayload = { }; type RequestBody = { - caseId: TestCaseId; + caseId: RpContextCaseId; action?: string; nonce?: string; }; -const KNOWN_CASES = new Set([ +const KNOWN_CASES = new Set([ "valid_success", "invalid_rp_signature", "duplicate_nonce", @@ -47,7 +47,7 @@ const KNOWN_CASES = new Set([ "rp_signature_expired", ]); -const DEFAULT_ACTION = "idkit-test-case"; +const DEFAULT_ACTION = "idkit-arena"; const DEFAULT_TTL_SEC = 300; const RP_ID_FORMAT = /^rp_[0-9a-fA-F]{1,16}$/; @@ -167,7 +167,10 @@ export async function POST(request: Request): Promise { try { const body = (await request.json()) as RequestBody; if (!KNOWN_CASES.has(body.caseId)) { - return jsonError(`Unknown test case: ${String(body.caseId)}`, 400); + return jsonError( + `Unknown Arena context case: ${String(body.caseId)}`, + 400, + ); } const signingKey = process.env.RP_SIGNING_KEY; @@ -314,7 +317,7 @@ export async function POST(request: Request): Promise { note, }); } catch (error) { - console.error("Error generating test-case RP context:", error); + console.error("Error generating Arena RP context:", error); return jsonError( error instanceof Error ? error.message : "Unknown server error", 500, diff --git a/js/examples/nextjs/app/arena/page.tsx b/js/examples/nextjs/app/arena/page.tsx new file mode 100644 index 00000000..2d2793cb --- /dev/null +++ b/js/examples/nextjs/app/arena/page.tsx @@ -0,0 +1,18 @@ +import type { ReactElement } from "react"; +import { ArenaClient } from "./ui"; + +export default function ArenaPage(): ReactElement { + return ( +
+ + Back to demo + +

IDKit Arena

+

+ Run World ID 3.0 preset, World ID 4.0 constraint, migration fallback, + and error-path cases against a local World App build. +

+ +
+ ); +} diff --git a/js/examples/nextjs/app/arena/ui.tsx b/js/examples/nextjs/app/arena/ui.tsx new file mode 100644 index 00000000..1570ea0f --- /dev/null +++ b/js/examples/nextjs/app/arena/ui.tsx @@ -0,0 +1,1326 @@ +"use client"; + +import { useEffect, useMemo, useState, type ReactElement } from "react"; +import { + CredentialRequest, + IDKitErrorCodes, + IDKitRequestWidget, + all, + any, + documentLegacy, + deviceLegacy, + orbLegacy, + secureDocumentLegacy, + selfieCheckLegacy, + setDebug, + type ConstraintNode, + type CredentialType, + type IDKitResult, + type Preset, + type RpContext, +} from "@worldcoin/idkit"; + +setDebug(true); + +type RpContextCaseId = + | "valid_success" + | "invalid_rp_signature" + | "duplicate_nonce" + | "nullifier_replayed" + | "invalid_rp_id_format" + | "unknown_rp" + | "inactive_rp" + | "timestamp_too_old" + | "timestamp_too_far_in_future" + | "invalid_timestamp" + | "rp_signature_expired"; + +type ArenaCaseId = + | "v3_orb_success" + | "v3_selfie_success" + | "v3_secure_document_success" + | "v3_document_success" + | "v3_device_success" + | "v4_poh_success" + | "v4_passport_success" + | "v4_mnc_success" + | "v4_poh_and_passport_success" + | "v4_poh_or_passport_success" + | "v4_poh_and_mnc_success" + | "v4_poh_or_mnc_success" + | "v4_passport_or_mnc_success" + | "v4_poh_and_passport_or_mnc_success" + | "v4_3_only_no_fallback" + | "v4_3_only_fallback_success" + | "v4_3_only_fallback_no_match" + | "v4_constraint_unsatisfied" + | "v4_future_genesis_no_fallback" + | "v4_future_genesis_fallback_success" + | "error_invalid_rp_signature" + | "error_duplicate_nonce" + | "error_nullifier_replayed" + | "error_invalid_rp_id_format" + | "error_unknown_rp" + | "error_inactive_rp" + | "error_timestamp_too_old" + | "error_timestamp_too_far_in_future" + | "error_invalid_timestamp" + | "error_rp_signature_expired"; + +type PresetKind = "orb" | "secure_document" | "document" | "device" | "selfie"; + +type ConstraintKind = + | "proof_of_human" + | "passport" + | "mnc" + | "poh_and_passport" + | "poh_or_passport" + | "poh_and_mnc" + | "poh_or_mnc" + | "passport_or_mnc" + | "poh_and_passport_or_mnc"; + +type ExpectedOutcome = "success" | IDKitErrorCodes; +type ExpectedProtocol = "3.0" | "4.0"; +type RunPhase = "run" | "prime"; +type ResultStatus = "idle" | "running" | "ready" | "pass" | "fail" | "skipped"; + +type RequestDefinition = + | { + type: "preset"; + presetKind: PresetKind; + } + | { + type: "constraints"; + constraintKind: ConstraintKind; + allowLegacyProofs: boolean; + genesisIssuedAtMin?: "future"; + }; + +type ArenaCaseDefinition = { + id: ArenaCaseId; + title: string; + expected: ExpectedOutcome; + expectedProtocol?: ExpectedProtocol; + description: string; + prerequisite: string; + request: RequestDefinition; + contextCaseId?: RpContextCaseId; +}; + +type ArenaSection = { + id: string; + title: string; + description: string; + cases: ArenaCaseDefinition[]; +}; + +type TestCaseResult = { + status: ResultStatus; + message: string; + expected?: string; + actual?: string; + note?: string; +}; + +type TestCaseResponse = + | { + configured: true; + caseId: RpContextCaseId; + action: string; + rpContext: RpContext; + note?: string; + } + | { + configured: false; + error: string; + }; + +type ActiveRun = { + caseId: ArenaCaseId; + contextCaseId: RpContextCaseId; + phase: RunPhase; + expected: ExpectedOutcome; + expectedProtocol?: ExpectedProtocol; + finalExpected?: IDKitErrorCodes; + action: string; + rpContext: RpContext; + signal: string; + request: RequestDefinition; +}; + +type FlowConfig = + | { + preset: Preset; + constraints?: never; + } + | { + constraints: ConstraintNode; + preset?: never; + }; + +const APP_ID = process.env.NEXT_PUBLIC_APP_ID as `app_${string}` | undefined; +const RP_ID = process.env.NEXT_PUBLIC_RP_ID; +const STAGING_CONNECT_BASE_URL = "https://staging.world.org/verify"; +const FUTURE_GENESIS_OFFSET_SEC = 365 * 24 * 60 * 60; + +const PRESET_KIND_TO_NAME: Record = { + orb: "Orb / Proof of Human", + secure_document: "Secure Document", + document: "Document", + device: "Device", + selfie: "Selfie Check", +}; + +const CONSTRAINT_KIND_TO_NAME: Record = { + proof_of_human: "Proof of Human", + passport: "Passport", + mnc: "MNC", + poh_and_passport: "Proof of Human AND Passport", + poh_or_passport: "Proof of Human OR Passport", + poh_and_mnc: "Proof of Human AND MNC", + poh_or_mnc: "Proof of Human OR MNC", + passport_or_mnc: "Passport OR MNC", + poh_and_passport_or_mnc: "Proof of Human AND (Passport OR MNC)", +}; + +const ARENA_SECTIONS: ArenaSection[] = [ + { + id: "world-id-3-presets", + title: "World ID 3.0 Preset Cases", + description: + "Legacy preset requests that should keep using the existing 3.0 flow.", + cases: [ + { + id: "v3_orb_success", + title: "Orb / Proof of Human", + expected: "success", + expectedProtocol: "3.0", + prerequisite: "Account has the matching 3.0 Orb credential.", + description: "Runs the Orb legacy preset and expects a 3.0 proof.", + request: { type: "preset", presetKind: "orb" }, + }, + { + id: "v3_selfie_success", + title: "Selfie Check", + expected: "success", + expectedProtocol: "3.0", + prerequisite: "Account can satisfy the legacy selfie-check preset.", + description: "Runs the selfie-check legacy preset and expects success.", + request: { type: "preset", presetKind: "selfie" }, + }, + { + id: "v3_secure_document_success", + title: "Secure Document", + expected: "success", + expectedProtocol: "3.0", + prerequisite: "Account can satisfy the secure document legacy preset.", + description: + "Runs the secure document legacy preset and expects a 3.0 proof.", + request: { type: "preset", presetKind: "secure_document" }, + }, + { + id: "v3_document_success", + title: "Document", + expected: "success", + expectedProtocol: "3.0", + prerequisite: "Account can satisfy the document legacy preset.", + description: "Runs the document legacy preset and expects a 3.0 proof.", + request: { type: "preset", presetKind: "document" }, + }, + { + id: "v3_device_success", + title: "Device", + expected: "success", + expectedProtocol: "3.0", + prerequisite: "Account can satisfy the device legacy preset.", + description: "Runs the device legacy preset and expects a 3.0 proof.", + request: { type: "preset", presetKind: "device" }, + }, + ], + }, + { + id: "world-id-4-success", + title: "World ID 4.0 Success Cases", + description: + "Direct 4.0 proof_request cases where the account satisfies the requested constraints.", + cases: [ + { + id: "v4_poh_success", + title: "Proof of Human", + expected: "success", + expectedProtocol: "4.0", + prerequisite: "Account has a 4.0 Proof of Human credential.", + description: "Requests proof_of_human with legacy fallback disabled.", + request: { + type: "constraints", + constraintKind: "proof_of_human", + allowLegacyProofs: false, + }, + }, + { + id: "v4_passport_success", + title: "Passport", + expected: "success", + expectedProtocol: "4.0", + prerequisite: "Account has a 4.0 Passport credential.", + description: "Requests passport with legacy fallback disabled.", + request: { + type: "constraints", + constraintKind: "passport", + allowLegacyProofs: false, + }, + }, + { + id: "v4_mnc_success", + title: "MNC", + expected: "success", + expectedProtocol: "4.0", + prerequisite: "Account has a 4.0 MNC credential.", + description: "Requests mnc with legacy fallback disabled.", + request: { + type: "constraints", + constraintKind: "mnc", + allowLegacyProofs: false, + }, + }, + { + id: "v4_poh_and_passport_success", + title: "Proof of Human AND Passport", + expected: "success", + expectedProtocol: "4.0", + prerequisite: "Account has both 4.0 Proof of Human and Passport.", + description: "Exercises the all(...) branch for two credentials.", + request: { + type: "constraints", + constraintKind: "poh_and_passport", + allowLegacyProofs: false, + }, + }, + { + id: "v4_poh_or_passport_success", + title: "Proof of Human OR Passport", + expected: "success", + expectedProtocol: "4.0", + prerequisite: "Account has either 4.0 Proof of Human or Passport.", + description: "Exercises the any(...) branch for two credentials.", + request: { + type: "constraints", + constraintKind: "poh_or_passport", + allowLegacyProofs: false, + }, + }, + { + id: "v4_poh_and_mnc_success", + title: "Proof of Human AND MNC", + expected: "success", + expectedProtocol: "4.0", + prerequisite: "Account has both 4.0 Proof of Human and MNC.", + description: "Exercises the all(...) branch with MNC.", + request: { + type: "constraints", + constraintKind: "poh_and_mnc", + allowLegacyProofs: false, + }, + }, + { + id: "v4_poh_or_mnc_success", + title: "Proof of Human OR MNC", + expected: "success", + expectedProtocol: "4.0", + prerequisite: "Account has either 4.0 Proof of Human or MNC.", + description: "Exercises the any(...) branch with MNC.", + request: { + type: "constraints", + constraintKind: "poh_or_mnc", + allowLegacyProofs: false, + }, + }, + { + id: "v4_passport_or_mnc_success", + title: "Passport OR MNC", + expected: "success", + expectedProtocol: "4.0", + prerequisite: "Account has either 4.0 Passport or MNC.", + description: "Exercises document-style alternatives in 4.0.", + request: { + type: "constraints", + constraintKind: "passport_or_mnc", + allowLegacyProofs: false, + }, + }, + { + id: "v4_poh_and_passport_or_mnc_success", + title: "Proof of Human AND (Passport OR MNC)", + expected: "success", + expectedProtocol: "4.0", + prerequisite: + "Account has 4.0 Proof of Human plus either Passport or MNC.", + description: "Exercises the nested all(..., any(...)) branch.", + request: { + type: "constraints", + constraintKind: "poh_and_passport_or_mnc", + allowLegacyProofs: false, + }, + }, + ], + }, + { + id: "world-id-4-migration", + title: "World ID 4.0 Migration / Fallback Cases", + description: + "Decision-tree cases for account state, allow_legacy_proofs, constraint failures, and genesis cutoffs.", + cases: [ + { + id: "v4_3_only_no_fallback", + title: "3.0-only account, fallback disabled", + expected: IDKitErrorCodes.WorldId4NotAvailable, + prerequisite: + "Run with an account that only has the matching 3.0 Orb credential.", + description: + "Requests proof_of_human with allow_legacy_proofs=false and expects the 4.0-not-available branch.", + request: { + type: "constraints", + constraintKind: "proof_of_human", + allowLegacyProofs: false, + }, + }, + { + id: "v4_3_only_fallback_success", + title: "3.0-only account, fallback enabled", + expected: "success", + expectedProtocol: "3.0", + prerequisite: + "Run with an account that only has the matching 3.0 Orb credential.", + description: + "Requests proof_of_human with allow_legacy_proofs=true and expects legacy fallback success.", + request: { + type: "constraints", + constraintKind: "proof_of_human", + allowLegacyProofs: true, + }, + }, + { + id: "v4_3_only_fallback_no_match", + title: "3.0-only account, fallback has no match", + expected: IDKitErrorCodes.CredentialUnavailable, + prerequisite: + "Run with a 3.0-only account that cannot satisfy the requested document credential.", + description: + "Requests passport with allow_legacy_proofs=true and expects constraint failure after fallback evaluation.", + request: { + type: "constraints", + constraintKind: "passport", + allowLegacyProofs: true, + }, + }, + { + id: "v4_constraint_unsatisfied", + title: "4.0 constraints unsatisfied", + expected: IDKitErrorCodes.CredentialUnavailable, + prerequisite: + "Run with a 4.0 account that has Proof of Human but not Passport.", + description: + "Requests Proof of Human AND Passport with fallback disabled and expects credential_unavailable.", + request: { + type: "constraints", + constraintKind: "poh_and_passport", + allowLegacyProofs: false, + }, + }, + { + id: "v4_future_genesis_no_fallback", + title: "Genesis cutoff fails, fallback disabled", + expected: IDKitErrorCodes.WorldId4NotAvailable, + prerequisite: + "Run with a 4.0 Proof of Human account whose credential predates the generated future genesis cutoff.", + description: + "Adds a future genesis_issued_at_min and expects the 4.0-not-available branch.", + request: { + type: "constraints", + constraintKind: "proof_of_human", + allowLegacyProofs: false, + genesisIssuedAtMin: "future", + }, + }, + { + id: "v4_future_genesis_fallback_success", + title: "Genesis cutoff fails, fallback enabled", + expected: "success", + expectedProtocol: "3.0", + prerequisite: + "Run with an account whose 4.0 credential predates the generated future cutoff and has matching 3.0 fallback.", + description: + "Adds a future genesis_issued_at_min with allow_legacy_proofs=true and expects legacy fallback success.", + request: { + type: "constraints", + constraintKind: "proof_of_human", + allowLegacyProofs: true, + genesisIssuedAtMin: "future", + }, + }, + ], + }, + { + id: "world-id-4-errors", + title: "World ID 4.0 Error Cases", + description: + "Malformed or rejected 4.0 requests that should return a specific IDKit error code.", + cases: [ + { + id: "error_invalid_rp_signature", + title: "Invalid RP signature", + expected: IDKitErrorCodes.InvalidRpSignature, + prerequisite: "Any account that can open a 4.0 Proof of Human request.", + description: + "Flips one byte in the RP signature while keeping the payload parseable.", + request: { + type: "constraints", + constraintKind: "proof_of_human", + allowLegacyProofs: false, + }, + contextCaseId: "invalid_rp_signature", + }, + { + id: "error_duplicate_nonce", + title: "Duplicate nonce", + expected: IDKitErrorCodes.DuplicateNonce, + prerequisite: + "Any account that can complete a 4.0 Proof of Human request. This case requires two runs.", + description: + "Consumes a signed nonce once, then signs a fresh action with that nonce.", + request: { + type: "constraints", + constraintKind: "proof_of_human", + allowLegacyProofs: false, + }, + contextCaseId: "duplicate_nonce", + }, + { + id: "error_nullifier_replayed", + title: "Nullifier replayed", + expected: IDKitErrorCodes.NullifierReplayed, + prerequisite: + "Any account that can complete a 4.0 Proof of Human request. This case requires two runs.", + description: + "Verifies once for a stable action, then requests another proof for it.", + request: { + type: "constraints", + constraintKind: "proof_of_human", + allowLegacyProofs: false, + }, + contextCaseId: "nullifier_replayed", + }, + { + id: "error_invalid_rp_id_format", + title: "Invalid RP ID format", + expected: IDKitErrorCodes.InvalidRpIdFormat, + prerequisite: "No World App account state required.", + description: "Uses an RP ID that cannot be parsed by the SDK.", + request: { + type: "constraints", + constraintKind: "proof_of_human", + allowLegacyProofs: false, + }, + contextCaseId: "invalid_rp_id_format", + }, + { + id: "error_unknown_rp", + title: "Unknown RP", + expected: IDKitErrorCodes.UnknownRp, + prerequisite: "Any account that can open a 4.0 Proof of Human request.", + description: "Uses an RP ID that should not exist in the RP registry.", + request: { + type: "constraints", + constraintKind: "proof_of_human", + allowLegacyProofs: false, + }, + contextCaseId: "unknown_rp", + }, + { + id: "error_inactive_rp", + title: "Inactive RP", + expected: IDKitErrorCodes.InactiveRp, + prerequisite: + "Requires TEST_INACTIVE_RP_ID for an RP that exists but is inactive.", + description: + "Requires an RP ID that exists in the registry but is inactive.", + request: { + type: "constraints", + constraintKind: "proof_of_human", + allowLegacyProofs: false, + }, + contextCaseId: "inactive_rp", + }, + { + id: "error_timestamp_too_old", + title: "Timestamp too old", + expected: IDKitErrorCodes.TimestampTooOld, + prerequisite: "Any account that can open a 4.0 Proof of Human request.", + description: + "Signs a request created outside the accepted freshness window.", + request: { + type: "constraints", + constraintKind: "proof_of_human", + allowLegacyProofs: false, + }, + contextCaseId: "timestamp_too_old", + }, + { + id: "error_timestamp_too_far_in_future", + title: "Timestamp too far in future", + expected: IDKitErrorCodes.TimestampTooFarInFuture, + prerequisite: "Any account that can open a 4.0 Proof of Human request.", + description: + "Signs a request with a created_at timestamp ahead of the freshness window.", + request: { + type: "constraints", + constraintKind: "proof_of_human", + allowLegacyProofs: false, + }, + contextCaseId: "timestamp_too_far_in_future", + }, + { + id: "error_invalid_timestamp", + title: "Invalid timestamp", + expected: IDKitErrorCodes.InvalidTimestamp, + prerequisite: "No World App account state required.", + description: + "Uses a timestamp value intended to fail parser-level timestamp validation.", + request: { + type: "constraints", + constraintKind: "proof_of_human", + allowLegacyProofs: false, + }, + contextCaseId: "invalid_timestamp", + }, + { + id: "error_rp_signature_expired", + title: "RP signature expired", + expected: IDKitErrorCodes.RpSignatureExpired, + prerequisite: "Any account that can open a 4.0 Proof of Human request.", + description: + "Signs a request whose expires_at value is already in the past.", + request: { + type: "constraints", + constraintKind: "proof_of_human", + allowLegacyProofs: false, + }, + contextCaseId: "rp_signature_expired", + }, + ], + }, +]; + +const FINAL_EXPECTED: Partial> = { + error_duplicate_nonce: IDKitErrorCodes.DuplicateNonce, + error_nullifier_replayed: IDKitErrorCodes.NullifierReplayed, +}; + +function createChromeAppDeeplink(url: string): string { + const parsed = new URL(url); + return parsed.protocol === "https:" ? "googlechromes://" : "googlechrome://"; +} + +function resultClass(status: ResultStatus): string { + return `test-case-status ${status}`; +} + +function expectedLabel( + expected: ExpectedOutcome, + expectedProtocol?: ExpectedProtocol, +): string { + if (expected === "success" && expectedProtocol) { + return `success (${expectedProtocol})`; + } + + return expected; +} + +function caseExpectedLabel(testCase: ArenaCaseDefinition): string { + return expectedLabel(testCase.expected, testCase.expectedProtocol); +} + +function makeAction(caseId: ArenaCaseId, actionPrefix: string): string { + const base = actionPrefix.trim() || "idkit-arena"; + if (caseId === "error_nullifier_replayed") { + return `${base}-${caseId}`; + } + return `${base}-${caseId}-${Date.now()}`; +} + +function makeSignal(caseId: ArenaCaseId, action: string): string { + if (caseId === "error_nullifier_replayed") { + return `${action}-signal`; + } + + return `${caseId}-${Date.now()}`; +} + +function createPreset(kind: PresetKind, signal: string): Preset { + switch (kind) { + case "orb": + return orbLegacy({ signal }); + case "secure_document": + return secureDocumentLegacy({ signal }); + case "document": + return documentLegacy({ signal }); + case "device": + return deviceLegacy({ signal }); + case "selfie": + return selfieCheckLegacy({ signal }); + default: { + const exhaustive: never = kind; + throw new Error(`Unsupported preset: ${String(exhaustive)}`); + } + } +} + +function credential( + type: CredentialType, + signal: string, + genesisIssuedAtMin?: number, +) { + return CredentialRequest(type, { + signal, + genesis_issued_at_min: genesisIssuedAtMin, + }); +} + +function buildConstraint( + kind: ConstraintKind, + signal: string, + genesisIssuedAtMin?: number, +): ConstraintNode { + const poh = () => credential("proof_of_human", signal, genesisIssuedAtMin); + const passport = () => credential("passport", signal, genesisIssuedAtMin); + const mnc = () => credential("mnc", signal, genesisIssuedAtMin); + + switch (kind) { + case "proof_of_human": + return poh(); + case "passport": + return passport(); + case "mnc": + return mnc(); + case "poh_and_passport": + return all(poh(), passport()); + case "poh_or_passport": + return any(poh(), passport()); + case "poh_and_mnc": + return all(poh(), mnc()); + case "poh_or_mnc": + return any(poh(), mnc()); + case "passport_or_mnc": + return any(passport(), mnc()); + case "poh_and_passport_or_mnc": + return all(poh(), any(passport(), mnc())); + default: { + const exhaustive: never = kind; + throw new Error(`Unsupported constraint: ${String(exhaustive)}`); + } + } +} + +function futureGenesisIssuedAtMin(): number { + return Math.floor(Date.now() / 1000) + FUTURE_GENESIS_OFFSET_SEC; +} + +function buildFlowConfig(activeRun: ActiveRun): FlowConfig { + if (activeRun.request.type === "preset") { + return { + preset: createPreset(activeRun.request.presetKind, activeRun.signal), + }; + } + + const genesisIssuedAtMin = + activeRun.request.genesisIssuedAtMin === "future" + ? futureGenesisIssuedAtMin() + : undefined; + + return { + constraints: buildConstraint( + activeRun.request.constraintKind, + activeRun.signal, + genesisIssuedAtMin, + ), + }; +} + +function requestLabel(request: RequestDefinition): string { + if (request.type === "preset") { + return `preset: ${PRESET_KIND_TO_NAME[request.presetKind]}`; + } + + const fallback = request.allowLegacyProofs + ? "allow_legacy_proofs=true" + : "allow_legacy_proofs=false"; + const genesis = + request.genesisIssuedAtMin === "future" ? ", future genesis min" : ""; + + return `${CONSTRAINT_KIND_TO_NAME[request.constraintKind]} (${fallback}${genesis})`; +} + +function resultProtocol(result: IDKitResult): ExpectedProtocol { + return result.protocol_version === "3.0" ? "3.0" : "4.0"; +} + +async function fetchArenaContext( + caseId: RpContextCaseId, + action: string, + nonce?: string, +): Promise { + const response = await fetch("/api/arena/rp-context", { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ caseId, action, nonce }), + }); + const payload = (await response.json()) as + | TestCaseResponse + | { error?: string }; + + if (response.status === 422 && "configured" in payload) { + return payload as TestCaseResponse; + } + + if (!response.ok) { + throw new Error( + "error" in payload && payload.error + ? payload.error + : "Failed to create Arena RP context", + ); + } + + return payload as TestCaseResponse; +} + +async function verifyProof( + payload: IDKitResult, + rpContext: RpContext, +): Promise { + const response = await fetch("/api/verify-proof", { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ rp_id: rpContext.rp_id, devPortalPayload: payload }), + }); + + const json = await response.json(); + if (!response.ok) { + throw new Error(json.error ?? "Verification failed"); + } + + return json; +} + +export function ArenaClient(): ReactElement { + const [isLightTheme, setIsLightTheme] = useState(false); + const [environment, setEnvironment] = useState<"production" | "staging">( + "production", + ); + const [useStagingConnectBaseUrl, setUseStagingConnectBaseUrl] = + useState(false); + const [useReturnTo, setUseReturnTo] = useState(false); + const [returnTo, setReturnTo] = useState(""); + const [actionPrefix, setActionPrefix] = useState("idkit-arena"); + const [results, setResults] = useState< + Partial> + >({}); + const [activeRun, setActiveRun] = useState(null); + const [widgetOpen, setWidgetOpen] = useState(false); + const [duplicateNoncePrime, setDuplicateNoncePrime] = useState<{ + caseId: ArenaCaseId; + nonce: string; + } | null>(null); + const [nullifierReplayAction, setNullifierReplayAction] = useState<{ + action: string; + caseId: ArenaCaseId; + } | null>(null); + const [lastProof, setLastProof] = useState(null); + const [lastVerifyResult, setLastVerifyResult] = useState(null); + + const overrideConnectBaseUrl = + environment === "staging" && useStagingConnectBaseUrl + ? STAGING_CONNECT_BASE_URL + : undefined; + const effectiveReturnTo = useReturnTo + ? returnTo.trim() || undefined + : undefined; + const activeFlowConfig = useMemo( + () => (activeRun ? buildFlowConfig(activeRun) : null), + [activeRun], + ); + + useEffect(() => { + document.documentElement.setAttribute( + "data-theme", + isLightTheme ? "light" : "dark", + ); + }, [isLightTheme]); + + useEffect(() => { + if (environment !== "staging") { + setUseStagingConnectBaseUrl(false); + } + }, [environment]); + + useEffect(() => { + if (typeof window === "undefined") { + return; + } + + setReturnTo((current) => + current.length > 0 + ? current + : createChromeAppDeeplink(window.location.href), + ); + }, []); + + const setCaseResult = (caseId: ArenaCaseId, result: TestCaseResult) => { + setResults((current) => ({ ...current, [caseId]: result })); + }; + + const resetTwoStepState = () => { + setDuplicateNoncePrime(null); + setNullifierReplayAction(null); + }; + + const startCase = async (testCase: ArenaCaseDefinition) => { + if (!APP_ID || !RP_ID) { + setCaseResult(testCase.id, { + status: "fail", + message: "Missing NEXT_PUBLIC_APP_ID or NEXT_PUBLIC_RP_ID", + }); + return; + } + + setLastProof(null); + setLastVerifyResult(null); + setWidgetOpen(false); + + try { + const contextCaseId = testCase.contextCaseId ?? "valid_success"; + const finalExpected = FINAL_EXPECTED[testCase.id]; + let phase: RunPhase = "run"; + let expected = testCase.expected; + let action = makeAction(testCase.id, actionPrefix); + let nonce: string | undefined; + let note: string | undefined; + + if ( + contextCaseId === "duplicate_nonce" && + duplicateNoncePrime?.caseId === testCase.id + ) { + nonce = duplicateNoncePrime.nonce; + expected = IDKitErrorCodes.DuplicateNonce; + } else if (contextCaseId === "duplicate_nonce") { + phase = "prime"; + expected = "success"; + } + + if ( + contextCaseId === "nullifier_replayed" && + nullifierReplayAction?.caseId === testCase.id + ) { + action = nullifierReplayAction.action; + expected = IDKitErrorCodes.NullifierReplayed; + } else if (contextCaseId === "nullifier_replayed") { + phase = "prime"; + expected = "success"; + } + + setCaseResult(testCase.id, { + status: "running", + message: + phase === "prime" + ? "Priming with a successful request first" + : "Waiting for World App", + expected: finalExpected + ? expectedLabel(finalExpected) + : expectedLabel(expected, testCase.expectedProtocol), + }); + + const response = await fetchArenaContext(contextCaseId, action, nonce); + if (!response.configured) { + setCaseResult(testCase.id, { + status: "skipped", + message: response.error, + expected: caseExpectedLabel(testCase), + }); + return; + } + + action = response.action; + note = response.note; + + setActiveRun({ + caseId: testCase.id, + contextCaseId, + phase, + expected, + expectedProtocol: testCase.expectedProtocol, + finalExpected, + action, + rpContext: response.rpContext, + signal: makeSignal(testCase.id, action), + request: testCase.request, + }); + setWidgetOpen(true); + + if (note) { + setCaseResult(testCase.id, { + status: "running", + message: "Waiting for World App", + expected: finalExpected + ? expectedLabel(finalExpected) + : caseExpectedLabel(testCase), + note, + }); + } + } catch (error) { + setCaseResult(testCase.id, { + status: "fail", + message: error instanceof Error ? error.message : "Unknown error", + expected: caseExpectedLabel(testCase), + }); + } + }; + + const completeSuccess = (result: IDKitResult) => { + if (!activeRun) { + return; + } + + setLastProof(result); + setWidgetOpen(false); + + if (activeRun.expected !== "success") { + setCaseResult(activeRun.caseId, { + status: "fail", + message: "Expected an error, but the request succeeded", + expected: expectedLabel(activeRun.expected), + actual: `success (${resultProtocol(result)})`, + }); + setActiveRun(null); + return; + } + + if ( + activeRun.expectedProtocol && + resultProtocol(result) !== activeRun.expectedProtocol + ) { + setCaseResult(activeRun.caseId, { + status: "fail", + message: "Received success with a different protocol version", + expected: expectedLabel("success", activeRun.expectedProtocol), + actual: `success (${resultProtocol(result)})`, + }); + setActiveRun(null); + return; + } + + if ( + activeRun.phase === "prime" && + activeRun.contextCaseId === "duplicate_nonce" + ) { + setDuplicateNoncePrime({ + caseId: activeRun.caseId, + nonce: activeRun.rpContext.nonce, + }); + setCaseResult(activeRun.caseId, { + status: "ready", + message: + "Nonce consumed. Run this case again to sign a fresh action with that nonce.", + expected: activeRun.finalExpected + ? expectedLabel(activeRun.finalExpected) + : undefined, + actual: `success (${resultProtocol(result)})`, + }); + setActiveRun(null); + return; + } + + if ( + activeRun.phase === "prime" && + activeRun.contextCaseId === "nullifier_replayed" + ) { + setNullifierReplayAction({ + action: activeRun.action, + caseId: activeRun.caseId, + }); + setCaseResult(activeRun.caseId, { + status: "ready", + message: + "Action verified once. Run this case again to expect nullifier_replayed.", + expected: activeRun.finalExpected + ? expectedLabel(activeRun.finalExpected) + : undefined, + actual: `success (${resultProtocol(result)})`, + }); + setActiveRun(null); + return; + } + + setCaseResult(activeRun.caseId, { + status: "pass", + message: "Received expected success", + expected: expectedLabel("success", activeRun.expectedProtocol), + actual: `success (${resultProtocol(result)})`, + }); + setActiveRun(null); + }; + + const completeError = (errorCode: IDKitErrorCodes) => { + if (!activeRun) { + return; + } + + setWidgetOpen(false); + + const expected = + activeRun.phase === "prime" && activeRun.finalExpected + ? activeRun.finalExpected + : activeRun.expected; + + if (errorCode === expected) { + setCaseResult(activeRun.caseId, { + status: "pass", + message: "Received expected error code", + expected: expectedLabel(expected), + actual: errorCode, + }); + setActiveRun(null); + return; + } + + setCaseResult(activeRun.caseId, { + status: "fail", + message: "Received a different result than expected", + expected: expectedLabel(expected), + actual: errorCode, + }); + setActiveRun(null); + }; + + if (!APP_ID || !RP_ID) { + return ( + <> + +
+

Missing environment configuration

+

+ Set NEXT_PUBLIC_APP_ID, NEXT_PUBLIC_RP_ID, + and RP_SIGNING_KEY in .env.local. +

+
+ + ); + } + + return ( + <> + + +
+
+ + +
+
+ + +
+
+ + { + setActionPrefix(event.target.value); + resetTwoStepState(); + }} + /> +
+
+ + +
+ {environment === "staging" && ( +
+ + + setUseStagingConnectBaseUrl(event.target.checked) + } + /> + {STAGING_CONNECT_BASE_URL} +
+ )} +
+ + setUseReturnTo(event.target.checked)} + /> + setReturnTo(event.target.value)} + disabled={!useReturnTo} + /> +
+
+ +
+ {ARENA_SECTIONS.map((section) => ( +
+
+

{section.title}

+

{section.description}

+
+ +
+ {section.cases.map((testCase) => { + const result = results[testCase.id] ?? { + status: "idle", + message: "Not run", + }; + const isRunning = + result.status === "running" || + activeRun?.caseId === testCase.id; + const buttonLabel = + result.status === "ready" + ? "Run Again" + : isRunning + ? "Running" + : "Run"; + + return ( +
+
+

{testCase.title}

+

{testCase.description}

+
+
+
Request
+
{requestLabel(testCase.request)}
+
+
+
Prerequisite
+
{testCase.prerequisite}
+
+
+
Expected
+
+ {caseExpectedLabel(testCase)} +
+
+
+
+
+ + + {result.status} + +
+
+ {result.message} + {result.expected && ( + + Expected: {result.expected} + + )} + {result.actual && ( + + Actual: {result.actual} + + )} + {result.note && {result.note}} +
+
+ ); + })} +
+
+ ))} +
+ + {activeRun && activeFlowConfig && ( + { + if (activeRun.expected !== "success") { + return; + } + + const verified = await verifyProof(result, activeRun.rpContext); + setLastVerifyResult(verified); + }} + onSuccess={completeSuccess} + onError={completeError} + environment={environment} + override_connect_base_url={overrideConnectBaseUrl} + return_to={effectiveReturnTo} + /> + )} + + {(lastProof || lastVerifyResult !== null) && ( +
+ {lastProof && ( + <> +

Last IDKit response

+
{JSON.stringify(lastProof, null, 2)}
+ + )} + {lastVerifyResult !== null && ( + <> +

Last verification response

+
{JSON.stringify(lastVerifyResult, null, 2)}
+ + )} +
+ )} + + ); +} diff --git a/js/examples/nextjs/app/error-cases/page.tsx b/js/examples/nextjs/app/error-cases/page.tsx deleted file mode 100644 index 97af960c..00000000 --- a/js/examples/nextjs/app/error-cases/page.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import type { ReactElement } from "react"; -import { TestCasesClient } from "./ui"; - -export default function ErrorCasesPage(): ReactElement { - return ( -
- - Back to demo - -

IDKit E2E Error Cases

-

- Run malformed World ID 4.0 requests against a local World App build and - compare the returned IDKit error code with the expected result. -

- -
- ); -} diff --git a/js/examples/nextjs/app/error-cases/ui.tsx b/js/examples/nextjs/app/error-cases/ui.tsx deleted file mode 100644 index d83547d9..00000000 --- a/js/examples/nextjs/app/error-cases/ui.tsx +++ /dev/null @@ -1,667 +0,0 @@ -"use client"; - -import { useEffect, useMemo, useState, type ReactElement } from "react"; -import { - CredentialRequest, - IDKitErrorCodes, - IDKitRequestWidget, - setDebug, - type IDKitResult, - type RpContext, -} from "@worldcoin/idkit"; - -setDebug(true); - -type TestCaseId = - | "valid_success" - | "invalid_rp_signature" - | "duplicate_nonce" - | "nullifier_replayed" - | "invalid_rp_id_format" - | "unknown_rp" - | "inactive_rp" - | "timestamp_too_old" - | "timestamp_too_far_in_future" - | "invalid_timestamp" - | "rp_signature_expired"; - -type ExpectedOutcome = "success" | IDKitErrorCodes; -type RunPhase = "run" | "prime"; -type ResultStatus = "idle" | "running" | "ready" | "pass" | "fail" | "skipped"; - -type TestCaseDefinition = { - id: TestCaseId; - title: string; - expected: ExpectedOutcome; - description: string; -}; - -type TestCaseResult = { - status: ResultStatus; - message: string; - expected?: ExpectedOutcome; - actual?: string; - note?: string; -}; - -type TestCaseResponse = - | { - configured: true; - caseId: TestCaseId; - action: string; - rpContext: RpContext; - note?: string; - } - | { - configured: false; - error: string; - }; - -type ActiveRun = { - caseId: TestCaseId; - phase: RunPhase; - expected: ExpectedOutcome; - finalExpected?: IDKitErrorCodes; - action: string; - rpContext: RpContext; - signal: string; -}; - -const APP_ID = process.env.NEXT_PUBLIC_APP_ID as `app_${string}` | undefined; -const RP_ID = process.env.NEXT_PUBLIC_RP_ID; -const STAGING_CONNECT_BASE_URL = "https://staging.world.org/verify"; - -const TEST_CASES: TestCaseDefinition[] = [ - { - id: "valid_success", - title: "Valid request", - expected: "success", - description: "Baseline proof request that should generate and verify.", - }, - { - id: "invalid_rp_signature", - title: "Invalid RP signature", - expected: IDKitErrorCodes.InvalidRpSignature, - description: - "Flips one byte in the RP signature while keeping the payload parseable.", - }, - { - id: "duplicate_nonce", - title: "Duplicate nonce", - expected: IDKitErrorCodes.DuplicateNonce, - description: - "Consumes a signed nonce once, then signs a fresh action with that nonce.", - }, - { - id: "nullifier_replayed", - title: "Nullifier replayed", - expected: IDKitErrorCodes.NullifierReplayed, - description: - "Verifies once for a stable action, then requests another proof for it.", - }, - { - id: "invalid_rp_id_format", - title: "Invalid RP ID format", - expected: IDKitErrorCodes.InvalidRpIdFormat, - description: "Uses an RP ID that cannot be parsed by the SDK.", - }, - { - id: "unknown_rp", - title: "Unknown RP", - expected: IDKitErrorCodes.UnknownRp, - description: "Uses an RP ID that should not exist in the RP registry.", - }, - { - id: "inactive_rp", - title: "Inactive RP", - expected: IDKitErrorCodes.InactiveRp, - description: - "Requires an RP ID that exists in the registry but is inactive.", - }, - { - id: "timestamp_too_old", - title: "Timestamp too old", - expected: IDKitErrorCodes.TimestampTooOld, - description: - "Signs a request created outside the accepted freshness window.", - }, - { - id: "timestamp_too_far_in_future", - title: "Timestamp too far in future", - expected: IDKitErrorCodes.TimestampTooFarInFuture, - description: - "Signs a request with a created_at timestamp ahead of the freshness window.", - }, - { - id: "invalid_timestamp", - title: "Invalid timestamp", - expected: IDKitErrorCodes.InvalidTimestamp, - description: - "Uses a timestamp value intended to fail parser-level timestamp validation.", - }, - { - id: "rp_signature_expired", - title: "RP signature expired", - expected: IDKitErrorCodes.RpSignatureExpired, - description: - "Signs a request whose expires_at value is already in the past.", - }, -]; - -const FINAL_EXPECTED: Partial> = { - duplicate_nonce: IDKitErrorCodes.DuplicateNonce, - nullifier_replayed: IDKitErrorCodes.NullifierReplayed, -}; - -function createChromeAppDeeplink(url: string): string { - const parsed = new URL(url); - return parsed.protocol === "https:" ? "googlechromes://" : "googlechrome://"; -} - -function resultClass(status: ResultStatus): string { - return `test-case-status ${status}`; -} - -function makeAction(caseId: TestCaseId, actionPrefix: string): string { - const base = actionPrefix.trim() || "idkit-test-case"; - if (caseId === "nullifier_replayed") { - return `${base}-${caseId}`; - } - return `${base}-${caseId}-${Date.now()}`; -} - -function makeSignal(caseId: TestCaseId, action: string): string { - if (caseId === "nullifier_replayed") { - return `${action}-signal`; - } - - return `${caseId}-${Date.now()}`; -} - -async function fetchTestCaseContext( - caseId: TestCaseId, - action: string, - nonce?: string, -): Promise { - const response = await fetch("/api/error-cases/rp-context", { - method: "POST", - headers: { "content-type": "application/json" }, - body: JSON.stringify({ caseId, action, nonce }), - }); - const payload = (await response.json()) as - | TestCaseResponse - | { error?: string }; - - if (response.status === 422 && "configured" in payload) { - return payload as TestCaseResponse; - } - - if (!response.ok) { - throw new Error( - "error" in payload && payload.error - ? payload.error - : "Failed to create test-case RP context", - ); - } - - return payload as TestCaseResponse; -} - -async function verifyProof( - payload: IDKitResult, - rpContext: RpContext, -): Promise { - const response = await fetch("/api/verify-proof", { - method: "POST", - headers: { "content-type": "application/json" }, - body: JSON.stringify({ rp_id: rpContext.rp_id, devPortalPayload: payload }), - }); - - const json = await response.json(); - if (!response.ok) { - throw new Error(json.error ?? "Verification failed"); - } - - return json; -} - -export function TestCasesClient(): ReactElement { - const [isLightTheme, setIsLightTheme] = useState(false); - const [environment, setEnvironment] = useState<"production" | "staging">( - "production", - ); - const [useStagingConnectBaseUrl, setUseStagingConnectBaseUrl] = - useState(false); - const [useReturnTo, setUseReturnTo] = useState(false); - const [returnTo, setReturnTo] = useState(""); - const [actionPrefix, setActionPrefix] = useState("idkit-e2e"); - const [results, setResults] = useState< - Partial> - >({}); - const [activeRun, setActiveRun] = useState(null); - const [widgetOpen, setWidgetOpen] = useState(false); - const [duplicateNoncePrime, setDuplicateNoncePrime] = useState<{ - nonce: string; - } | null>(null); - const [nullifierReplayAction, setNullifierReplayAction] = useState< - string | null - >(null); - const [lastProof, setLastProof] = useState(null); - const [lastVerifyResult, setLastVerifyResult] = useState(null); - - const overrideConnectBaseUrl = - environment === "staging" && useStagingConnectBaseUrl - ? STAGING_CONNECT_BASE_URL - : undefined; - const effectiveReturnTo = useReturnTo - ? returnTo.trim() || undefined - : undefined; - - const constraints = useMemo( - () => - CredentialRequest("proof_of_human", { - signal: activeRun?.signal, - }), - [activeRun?.signal], - ); - - useEffect(() => { - document.documentElement.setAttribute( - "data-theme", - isLightTheme ? "light" : "dark", - ); - }, [isLightTheme]); - - useEffect(() => { - if (environment !== "staging") { - setUseStagingConnectBaseUrl(false); - } - }, [environment]); - - useEffect(() => { - if (typeof window === "undefined") { - return; - } - - setReturnTo((current) => - current.length > 0 - ? current - : createChromeAppDeeplink(window.location.href), - ); - }, []); - - const setCaseResult = (caseId: TestCaseId, result: TestCaseResult) => { - setResults((current) => ({ ...current, [caseId]: result })); - }; - - const startCase = async (testCase: TestCaseDefinition) => { - if (!APP_ID || !RP_ID) { - setCaseResult(testCase.id, { - status: "fail", - message: "Missing NEXT_PUBLIC_APP_ID or NEXT_PUBLIC_RP_ID", - }); - return; - } - - setLastProof(null); - setLastVerifyResult(null); - setWidgetOpen(false); - - try { - const finalExpected = FINAL_EXPECTED[testCase.id]; - let phase: RunPhase = "run"; - let expected = testCase.expected; - let action = makeAction(testCase.id, actionPrefix); - let rpContext: RpContext | null = null; - let nonce: string | undefined; - let note: string | undefined; - - if (testCase.id === "duplicate_nonce" && duplicateNoncePrime) { - nonce = duplicateNoncePrime.nonce; - expected = IDKitErrorCodes.DuplicateNonce; - } else if (testCase.id === "duplicate_nonce") { - phase = "prime"; - expected = "success"; - } - - if (testCase.id === "nullifier_replayed" && nullifierReplayAction) { - action = nullifierReplayAction; - expected = IDKitErrorCodes.NullifierReplayed; - } else if (testCase.id === "nullifier_replayed") { - phase = "prime"; - expected = "success"; - } - - setCaseResult(testCase.id, { - status: "running", - message: - phase === "prime" - ? "Priming with a successful request first" - : "Waiting for World App", - expected: finalExpected ?? expected, - }); - - if (!rpContext) { - const response = await fetchTestCaseContext(testCase.id, action, nonce); - if (!response.configured) { - setCaseResult(testCase.id, { - status: "skipped", - message: response.error, - expected: testCase.expected, - }); - return; - } - - action = response.action; - rpContext = response.rpContext; - note = response.note; - } - - setActiveRun({ - caseId: testCase.id, - phase, - expected, - finalExpected, - action, - rpContext, - signal: makeSignal(testCase.id, action), - }); - setWidgetOpen(true); - - if (note) { - setCaseResult(testCase.id, { - status: "running", - message: "Waiting for World App", - expected: finalExpected ?? expected, - note, - }); - } - } catch (error) { - setCaseResult(testCase.id, { - status: "fail", - message: error instanceof Error ? error.message : "Unknown error", - expected: testCase.expected, - }); - } - }; - - const completeSuccess = (result: IDKitResult) => { - if (!activeRun) { - return; - } - - setLastProof(result); - setWidgetOpen(false); - - if (activeRun.expected !== "success") { - setCaseResult(activeRun.caseId, { - status: "fail", - message: "Expected an error, but the request succeeded", - expected: activeRun.expected, - actual: "success", - }); - setActiveRun(null); - return; - } - - if (activeRun.phase === "prime" && activeRun.caseId === "duplicate_nonce") { - setDuplicateNoncePrime({ - nonce: activeRun.rpContext.nonce, - }); - setCaseResult(activeRun.caseId, { - status: "ready", - message: - "Nonce consumed. Run this case again to sign a fresh action with that nonce.", - expected: activeRun.finalExpected, - actual: "success", - }); - setActiveRun(null); - return; - } - - if ( - activeRun.phase === "prime" && - activeRun.caseId === "nullifier_replayed" - ) { - setNullifierReplayAction(activeRun.action); - setCaseResult(activeRun.caseId, { - status: "ready", - message: - "Action verified once. Run this case again to expect nullifier_replayed.", - expected: activeRun.finalExpected, - actual: "success", - }); - setActiveRun(null); - return; - } - - setCaseResult(activeRun.caseId, { - status: "pass", - message: "Received expected success", - expected: "success", - actual: "success", - }); - setActiveRun(null); - }; - - const completeError = (errorCode: IDKitErrorCodes) => { - if (!activeRun) { - return; - } - - setWidgetOpen(false); - - const expected = - activeRun.phase === "prime" && activeRun.finalExpected - ? activeRun.finalExpected - : activeRun.expected; - - if (errorCode === expected) { - setCaseResult(activeRun.caseId, { - status: "pass", - message: "Received expected error code", - expected, - actual: errorCode, - }); - setActiveRun(null); - return; - } - - setCaseResult(activeRun.caseId, { - status: "fail", - message: "Received a different result than expected", - expected, - actual: errorCode, - }); - setActiveRun(null); - }; - - if (!APP_ID || !RP_ID) { - return ( - <> - -
-

Missing environment configuration

-

- Set NEXT_PUBLIC_APP_ID, NEXT_PUBLIC_RP_ID, - and RP_SIGNING_KEY in .env.local. -

-
- - ); - } - - return ( - <> - - -
-
- - -
-
- - -
-
- - { - setActionPrefix(event.target.value); - setDuplicateNoncePrime(null); - setNullifierReplayAction(null); - }} - /> -
-
- - -
- {environment === "staging" && ( -
- - - setUseStagingConnectBaseUrl(event.target.checked) - } - /> - {STAGING_CONNECT_BASE_URL} -
- )} -
- - setUseReturnTo(event.target.checked)} - /> - setReturnTo(event.target.value)} - disabled={!useReturnTo} - /> -
-
- -
- {TEST_CASES.map((testCase) => { - const result = results[testCase.id] ?? { - status: "idle", - message: "Not run", - }; - const isRunning = - result.status === "running" || activeRun?.caseId === testCase.id; - const buttonLabel = - result.status === "ready" - ? "Run Again" - : isRunning - ? "Running" - : "Run"; - - return ( -
-
-

{testCase.title}

-

{testCase.description}

- {testCase.expected} -
-
- - - {result.status} - -
-
- {result.message} - {result.expected && ( - - Expected: {result.expected} - - )} - {result.actual && ( - - Actual: {result.actual} - - )} - {result.note && {result.note}} -
-
- ); - })} -
- - {activeRun && ( - { - const verified = await verifyProof(result, activeRun.rpContext); - setLastVerifyResult(verified); - }} - onSuccess={completeSuccess} - onError={completeError} - environment={environment} - override_connect_base_url={overrideConnectBaseUrl} - return_to={effectiveReturnTo} - /> - )} - - {(lastProof || lastVerifyResult !== null) && ( -
- {lastProof && ( - <> -

Last IDKit response

-
{JSON.stringify(lastProof, null, 2)}
- - )} - {lastVerifyResult !== null && ( - <> -

Last verification response

-
{JSON.stringify(lastVerifyResult, null, 2)}
- - )} -
- )} - - ); -} diff --git a/js/examples/nextjs/app/globals.css b/js/examples/nextjs/app/globals.css index e2de06a5..27fe02ca 100644 --- a/js/examples/nextjs/app/globals.css +++ b/js/examples/nextjs/app/globals.css @@ -212,12 +212,29 @@ pre { word-break: break-all; } -.error-cases { +.arena-sections, +.arena-cases { display: flex; flex-direction: column; gap: 12px; } +.arena-section-header { + margin-bottom: 14px; +} + +.arena-section-header h2 { + margin: 0 0 6px; + font-size: 20px; +} + +.arena-section-header p { + margin: 0; + color: var(--status-color); + font-size: 14px; + line-height: 1.4; +} + .test-case-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; @@ -227,7 +244,7 @@ pre { border-radius: 10px; } -.test-case-copy h2 { +.test-case-copy h3 { margin: 0 0 6px; font-size: 16px; } @@ -239,6 +256,31 @@ pre { line-height: 1.4; } +.test-case-meta { + display: grid; + gap: 7px; + margin: 0; +} + +.test-case-meta > div { + display: grid; + grid-template-columns: 100px minmax(0, 1fr); + gap: 10px; +} + +.test-case-meta dt { + color: var(--muted-color); + font-size: 12px; + text-transform: uppercase; +} + +.test-case-meta dd { + margin: 0; + color: var(--status-color); + font-size: 13px; + line-height: 1.4; +} + .test-case-actions { display: flex; align-items: flex-start; @@ -294,4 +336,9 @@ pre { .test-case-actions { justify-content: space-between; } + + .test-case-meta > div { + grid-template-columns: 1fr; + gap: 2px; + } } diff --git a/js/examples/nextjs/app/page.tsx b/js/examples/nextjs/app/page.tsx index f39fb04a..a36467e2 100644 --- a/js/examples/nextjs/app/page.tsx +++ b/js/examples/nextjs/app/page.tsx @@ -10,8 +10,8 @@ export default function HomePage(): ReactElement { as the browser example.

- - Open E2E error cases + + Open Arena

diff --git a/js/examples/nextjs/app/ui.tsx b/js/examples/nextjs/app/ui.tsx index a8ca40aa..77a751b4 100644 --- a/js/examples/nextjs/app/ui.tsx +++ b/js/examples/nextjs/app/ui.tsx @@ -32,11 +32,12 @@ const RETURN_TO_TOOLTIP = type PresetKind = "orb" | "secure_document" | "document" | "device" | "selfie"; -type V4CredentialType = "proof_of_human" | "passport"; +type V4CredentialType = "proof_of_human" | "passport" | "mnc"; const V4_CREDENTIAL_TO_NAME: Record = { proof_of_human: "Proof of Human", passport: "Passport", + mnc: "MNC", }; const PRESET_KIND_TO_NAME: Record = { @@ -385,6 +386,7 @@ export function DemoClient(): ReactElement { > +
From eeea02b62003406cdc9af88bc69927c724422a75 Mon Sep 17 00:00:00 2001 From: Takis Kakalis <80459599+Takaros999@users.noreply.github.com> Date: Mon, 4 May 2026 16:08:42 -0700 Subject: [PATCH 11/12] feat(examples): add face auth arena cases --- js/examples/nextjs/app/arena/ui.tsx | 122 ++++++++++++++++++++++++++-- 1 file changed, 113 insertions(+), 9 deletions(-) diff --git a/js/examples/nextjs/app/arena/ui.tsx b/js/examples/nextjs/app/arena/ui.tsx index 1570ea0f..77745242 100644 --- a/js/examples/nextjs/app/arena/ui.tsx +++ b/js/examples/nextjs/app/arena/ui.tsx @@ -56,6 +56,11 @@ type ArenaCaseId = | "v4_constraint_unsatisfied" | "v4_future_genesis_no_fallback" | "v4_future_genesis_fallback_success" + | "user_presence_v3_orb_failed" + | "user_presence_v3_secure_document_failed" + | "user_presence_v4_poh_failed" + | "user_presence_v4_passport_failed" + | "user_presence_v4_mnc_failed" | "error_invalid_rp_signature" | "error_duplicate_nonce" | "error_nullifier_replayed" @@ -105,6 +110,7 @@ type ArenaCaseDefinition = { description: string; prerequisite: string; request: RequestDefinition; + requireUserPresence?: boolean; contextCaseId?: RpContextCaseId; }; @@ -147,6 +153,7 @@ type ActiveRun = { rpContext: RpContext; signal: string; request: RequestDefinition; + requireUserPresence: boolean; }; type FlowConfig = @@ -461,6 +468,81 @@ const ARENA_SECTIONS: ArenaSection[] = [ }, ], }, + { + id: "user-presence-face-auth", + title: "User presence (Face Auth)", + description: + "Requests that require Face Auth and should fail when the user reaches Face Auth but does not complete it.", + cases: [ + { + id: "user_presence_v3_orb_failed", + title: "WID 3.0 Orb with Face Auth", + expected: IDKitErrorCodes.UserPresenceFailed, + prerequisite: + "Account can satisfy the 3.0 Orb preset. Reach the Face Auth step and fail or cancel that step.", + description: + "Runs the Orb legacy preset with require_user_presence=true and expects user_presence_failed.", + request: { type: "preset", presetKind: "orb" }, + requireUserPresence: true, + }, + { + id: "user_presence_v3_secure_document_failed", + title: "WID 3.0 Secure Document with Face Auth", + expected: IDKitErrorCodes.UserPresenceFailed, + prerequisite: + "Account can satisfy the secure document legacy preset. Reach the Face Auth step and fail or cancel that step.", + description: + "Runs the secure document legacy preset with require_user_presence=true and expects user_presence_failed.", + request: { type: "preset", presetKind: "secure_document" }, + requireUserPresence: true, + }, + { + id: "user_presence_v4_poh_failed", + title: "WID 4.0 Proof of Human with Face Auth", + expected: IDKitErrorCodes.UserPresenceFailed, + prerequisite: + "Account has a 4.0 Proof of Human credential. Reach the Face Auth step and fail or cancel that step.", + description: + "Requests proof_of_human with require_user_presence=true and expects user_presence_failed.", + request: { + type: "constraints", + constraintKind: "proof_of_human", + allowLegacyProofs: false, + }, + requireUserPresence: true, + }, + { + id: "user_presence_v4_passport_failed", + title: "WID 4.0 Passport with Face Auth", + expected: IDKitErrorCodes.UserPresenceFailed, + prerequisite: + "Account has a 4.0 Passport credential. Reach the Face Auth step and fail or cancel that step.", + description: + "Requests passport with require_user_presence=true and expects user_presence_failed.", + request: { + type: "constraints", + constraintKind: "passport", + allowLegacyProofs: false, + }, + requireUserPresence: true, + }, + { + id: "user_presence_v4_mnc_failed", + title: "WID 4.0 MNC with Face Auth", + expected: IDKitErrorCodes.UserPresenceFailed, + prerequisite: + "Account has a 4.0 MNC credential. Reach the Face Auth step and fail or cancel that step.", + description: + "Requests mnc with require_user_presence=true and expects user_presence_failed.", + request: { + type: "constraints", + constraintKind: "mnc", + allowLegacyProofs: false, + }, + requireUserPresence: true, + }, + ], + }, { id: "world-id-4-errors", title: "World ID 4.0 Error Cases", @@ -747,18 +829,33 @@ function buildFlowConfig(activeRun: ActiveRun): FlowConfig { }; } -function requestLabel(request: RequestDefinition): string { +function requestLabel( + request: RequestDefinition, + requireUserPresence = false, +): string { + const userPresence = requireUserPresence ? "require_user_presence=true" : ""; + if (request.type === "preset") { - return `preset: ${PRESET_KIND_TO_NAME[request.presetKind]}`; + return userPresence + ? `preset: ${PRESET_KIND_TO_NAME[request.presetKind]} (${userPresence})` + : `preset: ${PRESET_KIND_TO_NAME[request.presetKind]}`; } - const fallback = request.allowLegacyProofs - ? "allow_legacy_proofs=true" - : "allow_legacy_proofs=false"; - const genesis = - request.genesisIssuedAtMin === "future" ? ", future genesis min" : ""; + const flags = [ + request.allowLegacyProofs + ? "allow_legacy_proofs=true" + : "allow_legacy_proofs=false", + ]; - return `${CONSTRAINT_KIND_TO_NAME[request.constraintKind]} (${fallback}${genesis})`; + if (request.genesisIssuedAtMin === "future") { + flags.push("future genesis min"); + } + + if (userPresence) { + flags.push(userPresence); + } + + return `${CONSTRAINT_KIND_TO_NAME[request.constraintKind]} (${flags.join(", ")})`; } function resultProtocol(result: IDKitResult): ExpectedProtocol { @@ -963,6 +1060,7 @@ export function ArenaClient(): ReactElement { rpContext: response.rpContext, signal: makeSignal(testCase.id, action), request: testCase.request, + requireUserPresence: testCase.requireUserPresence ?? false, }); setWidgetOpen(true); @@ -1228,7 +1326,12 @@ export function ArenaClient(): ReactElement {
Request
-
{requestLabel(testCase.request)}
+
+ {requestLabel( + testCase.request, + testCase.requireUserPresence ?? false, + )} +
Prerequisite
@@ -1288,6 +1391,7 @@ export function ArenaClient(): ReactElement { ? activeRun.request.allowLegacyProofs : true } + require_user_presence={activeRun.requireUserPresence} {...activeFlowConfig} handleVerify={async (result) => { if (activeRun.expected !== "success") { From 30adc4c5249ec6d5be396bd1c2349583c9852846 Mon Sep 17 00:00:00 2001 From: Takis Kakalis <80459599+Takaros999@users.noreply.github.com> Date: Thu, 14 May 2026 23:29:46 +0900 Subject: [PATCH 12/12] feat(arena): configure legacy proofs toggle --- js/examples/nextjs/app/arena/ui.tsx | 63 +++++++++++++++++++++++++---- js/examples/nextjs/app/globals.css | 18 +++++++++ 2 files changed, 74 insertions(+), 7 deletions(-) diff --git a/js/examples/nextjs/app/arena/ui.tsx b/js/examples/nextjs/app/arena/ui.tsx index 77745242..6e6f23ff 100644 --- a/js/examples/nextjs/app/arena/ui.tsx +++ b/js/examples/nextjs/app/arena/ui.tsx @@ -170,6 +170,7 @@ const APP_ID = process.env.NEXT_PUBLIC_APP_ID as `app_${string}` | undefined; const RP_ID = process.env.NEXT_PUBLIC_RP_ID; const STAGING_CONNECT_BASE_URL = "https://staging.world.org/verify"; const FUTURE_GENESIS_OFFSET_SEC = 365 * 24 * 60 * 60; +const CONFIGURABLE_ALLOW_LEGACY_SECTION_ID = "world-id-4-success"; const PRESET_KIND_TO_NAME: Record = { orb: "Orb / Proof of Human", @@ -258,7 +259,7 @@ const ARENA_SECTIONS: ArenaSection[] = [ expected: "success", expectedProtocol: "4.0", prerequisite: "Account has a 4.0 Proof of Human credential.", - description: "Requests proof_of_human with legacy fallback disabled.", + description: "Requests proof_of_human as a 4.0 success path.", request: { type: "constraints", constraintKind: "proof_of_human", @@ -271,7 +272,7 @@ const ARENA_SECTIONS: ArenaSection[] = [ expected: "success", expectedProtocol: "4.0", prerequisite: "Account has a 4.0 Passport credential.", - description: "Requests passport with legacy fallback disabled.", + description: "Requests passport as a 4.0 success path.", request: { type: "constraints", constraintKind: "passport", @@ -284,7 +285,7 @@ const ARENA_SECTIONS: ArenaSection[] = [ expected: "success", expectedProtocol: "4.0", prerequisite: "Account has a 4.0 MNC credential.", - description: "Requests mnc with legacy fallback disabled.", + description: "Requests mnc as a 4.0 success path.", request: { type: "constraints", constraintKind: "mnc", @@ -723,6 +724,24 @@ function caseExpectedLabel(testCase: ArenaCaseDefinition): string { return expectedLabel(testCase.expected, testCase.expectedProtocol); } +function withSectionAllowLegacyProofs( + request: RequestDefinition, + sectionId: string, + allowLegacyProofs: boolean, +): RequestDefinition { + if ( + sectionId !== CONFIGURABLE_ALLOW_LEGACY_SECTION_ID || + request.type !== "constraints" + ) { + return request; + } + + return { + ...request, + allowLegacyProofs, + }; +} + function makeAction(caseId: ArenaCaseId, actionPrefix: string): string { const base = actionPrefix.trim() || "idkit-arena"; if (caseId === "error_nullifier_replayed") { @@ -919,6 +938,8 @@ export function ArenaClient(): ReactElement { const [useReturnTo, setUseReturnTo] = useState(false); const [returnTo, setReturnTo] = useState(""); const [actionPrefix, setActionPrefix] = useState("idkit-arena"); + const [allowLegacyProofsForV4Success, setAllowLegacyProofsForV4Success] = + useState(false); const [results, setResults] = useState< Partial> >({}); @@ -1298,12 +1319,35 @@ export function ArenaClient(): ReactElement { {ARENA_SECTIONS.map((section) => (
-

{section.title}

-

{section.description}

+
+

{section.title}

+

{section.description}

+
+ {section.id === CONFIGURABLE_ALLOW_LEGACY_SECTION_ID && ( + + )}
{section.cases.map((testCase) => { + const request = withSectionAllowLegacyProofs( + testCase.request, + section.id, + allowLegacyProofsForV4Success, + ); const result = results[testCase.id] ?? { status: "idle", message: "Not run", @@ -1328,7 +1372,7 @@ export function ArenaClient(): ReactElement {
Request
{requestLabel( - testCase.request, + request, testCase.requireUserPresence ?? false, )}
@@ -1348,7 +1392,12 @@ export function ArenaClient(): ReactElement {