From 34c2ecb504d6654c8455e717ce63ae6e161637e7 Mon Sep 17 00:00:00 2001 From: aggre Date: Fri, 8 May 2026 03:21:43 +0000 Subject: [PATCH 1/2] feat: add KYC attribute proof hook (mock-based, phase 1) - Agent: KycCredential types, buildKycCredential/buildIdentityArtifact - Worker: KYC verification (hasRole/hasScope/hasPermission), /kyc-check endpoint - Predefined gates: basic, amlCompliance, stablecoinPayment, stablecoinIssuance, institutional - Mock implementations for @trust402/identity, @trust402/protocol, @trust402/roles - 55 tests passing --- packages/agent/package.json | 4 + packages/agent/src/kyc/artifact.test.ts | 201 +++++++++++++ packages/agent/src/kyc/artifact.ts | 175 ++++++++++++ packages/agent/src/kyc/index.ts | 33 +++ packages/agent/src/kyc/types.ts | 106 +++++++ packages/agent/src/mocks/identity.ts | 194 +++++++++++++ packages/agent/src/mocks/index.ts | 37 +++ packages/agent/src/mocks/protocol.ts | 116 ++++++++ packages/agent/src/mocks/roles.ts | 121 ++++++++ packages/worker/package.json | 5 +- packages/worker/src/index.ts | 69 +++++ packages/worker/src/kyc/index.ts | 19 ++ packages/worker/src/kyc/verify.test.ts | 365 ++++++++++++++++++++++++ packages/worker/src/kyc/verify.ts | 290 +++++++++++++++++++ pnpm-lock.yaml | 69 +++++ 15 files changed, 1803 insertions(+), 1 deletion(-) create mode 100644 packages/agent/src/kyc/artifact.test.ts create mode 100644 packages/agent/src/kyc/artifact.ts create mode 100644 packages/agent/src/kyc/index.ts create mode 100644 packages/agent/src/kyc/types.ts create mode 100644 packages/agent/src/mocks/identity.ts create mode 100644 packages/agent/src/mocks/index.ts create mode 100644 packages/agent/src/mocks/protocol.ts create mode 100644 packages/agent/src/mocks/roles.ts create mode 100644 packages/worker/src/kyc/index.ts create mode 100644 packages/worker/src/kyc/verify.test.ts create mode 100644 packages/worker/src/kyc/verify.ts diff --git a/packages/agent/package.json b/packages/agent/package.json index bc98420..b571024 100644 --- a/packages/agent/package.json +++ b/packages/agent/package.json @@ -9,6 +9,8 @@ "test": "vitest run" }, "dependencies": { + "@lemmaoracle/agent": "^0.0.24-alpha.1", + "@lemmaoracle/sdk": "^0.0.23", "@x402/core": "^2.10.0", "@x402/evm": "^2.10.0", "@x402/fetch": "^2.10.0", @@ -18,10 +20,12 @@ "cli-spinners": "^3.4.0", "dotenv": "^16.6.1", "ora": "^9.4.0", + "ramda": "^0.30.0", "viem": "^2.48.4" }, "devDependencies": { "@types/node": "^22.19.17", + "@types/ramda": "^0.30.0", "tsx": "^4.21.0", "typescript": "^5.9.3", "vitest": "^3.2.4" diff --git a/packages/agent/src/kyc/artifact.test.ts b/packages/agent/src/kyc/artifact.test.ts new file mode 100644 index 0000000..10085c6 --- /dev/null +++ b/packages/agent/src/kyc/artifact.test.ts @@ -0,0 +1,201 @@ +/** + * KYC module tests — Identity artifact creation and verification. + */ + +import { describe, it, expect } from "vitest"; +import { + buildKycCredential, + type BuildCredentialInput, +} from "./artifact.js"; +import type { KycRole, KycScope, KycPermission, KycCredential } from "./types.js"; + +// --------------------------------------------------------------------------- +// Test fixtures +// --------------------------------------------------------------------------- + +const sampleKycCredential: KycCredential = { + roles: [{ name: "kyc-verified" }, { name: "aml-cleared" }], + scopes: [{ name: "payment:stablecoin" }], + permissions: [{ resource: "stablecoin:transfer", action: "execute" }], +}; + +const sampleCredentialInput: BuildCredentialInput = { + agentId: "agent-001", + subjectId: "subject-001", + issuerId: "issuer-001", + kyc: sampleKycCredential, + spendLimit: 1000, + currency: "USDC", + validForSeconds: 3600, +}; + +// --------------------------------------------------------------------------- +// Tests +// --------------------------------------------------------------------------- + +describe("KYC Artifact Builder", () => { + describe("buildKycCredential", () => { + it("should build an AgentCredential from KYC credential input", () => { + const result = buildKycCredential(sampleCredentialInput); + + expect(result.schema).toBe("agent-credential-v1"); + expect(result.identity.agentId).toBe("agent-001"); + expect(result.identity.subjectId).toBe("subject-001"); + }); + + it("should map KYC roles to authority.roles", () => { + const result = buildKycCredential(sampleCredentialInput); + + expect(result.authority.roles).toHaveLength(2); + expect(result.authority.roles[0].name).toBe("kyc-verified"); + expect(result.authority.roles[1].name).toBe("aml-cleared"); + }); + + it("should map KYC scopes to authority.scopes", () => { + const result = buildKycCredential(sampleCredentialInput); + + expect(result.authority.scopes).toHaveLength(1); + expect(result.authority.scopes[0].name).toBe("payment:stablecoin"); + }); + + it("should map KYC permissions to authority.permissions", () => { + const result = buildKycCredential(sampleCredentialInput); + + expect(result.authority.permissions).toHaveLength(1); + expect(result.authority.permissions[0].resource).toBe("stablecoin:transfer"); + expect(result.authority.permissions[0].action).toBe("execute"); + }); + + it("should include financial data when provided", () => { + const result = buildKycCredential(sampleCredentialInput); + + expect(result.financial).toBeDefined(); + expect(result.financial?.spendLimit).toBe(1000); + expect(result.financial?.currency).toBe("USDC"); + }); + + it("should not include financial data when not provided", () => { + const input: BuildCredentialInput = { + ...sampleCredentialInput, + spendLimit: undefined, + currency: undefined, + }; + + const result = buildKycCredential(input); + expect(result.financial).toBeUndefined(); + }); + + it("should set lifecycle timestamps correctly", () => { + const before = Math.floor(Date.now() / 1000); + const result = buildKycCredential({ + ...sampleCredentialInput, + validForSeconds: 3600, + }); + const after = Math.floor(Date.now() / 1000); + + expect(result.lifecycle.issuedAt).toBeGreaterThanOrEqual(before); + expect(result.lifecycle.issuedAt).toBeLessThanOrEqual(after); + expect(result.lifecycle.expiresAt).toBe(result.lifecycle.issuedAt + 3600); + }); + + it("should not set expiresAt when validForSeconds is not provided", () => { + const input: BuildCredentialInput = { + ...sampleCredentialInput, + validForSeconds: undefined, + }; + + const result = buildKycCredential(input); + expect(result.lifecycle.expiresAt).toBeUndefined(); + }); + + it("should set revoked to false by default", () => { + const result = buildKycCredential(sampleCredentialInput); + expect(result.lifecycle.revoked).toBe(false); + }); + + it("should include provenance information", () => { + const result = buildKycCredential(sampleCredentialInput); + + expect(result.provenance.issuerId).toBe("issuer-001"); + expect(result.provenance.sourceSystem).toBe("trust402-kyc"); + }); + + it("should include controllerId and orgId when provided", () => { + const input: BuildCredentialInput = { + ...sampleCredentialInput, + controllerId: "controller-001", + orgId: "org-001", + }; + + const result = buildKycCredential(input); + expect(result.identity.controllerId).toBe("controller-001"); + expect(result.identity.orgId).toBe("org-001"); + }); + + it("should not include controllerId and orgId when not provided", () => { + const result = buildKycCredential(sampleCredentialInput); + + expect(result.identity.controllerId).toBeUndefined(); + expect(result.identity.orgId).toBeUndefined(); + }); + }); + + describe("KYC role types", () => { + it("should accept standard KYC role names", () => { + const roles: ReadonlyArray = [ + { name: "kyc-verified" }, + { name: "aml-cleared" }, + { name: "sanctions-clear" }, + { name: "accredited-investor" }, + { name: "institutional" }, + ]; + + const input: BuildCredentialInput = { + ...sampleCredentialInput, + kyc: { ...sampleKycCredential, roles }, + }; + + const result = buildKycCredential(input); + expect(result.authority.roles).toHaveLength(5); + }); + }); + + describe("KYC scope types", () => { + it("should accept standard KYC scope names", () => { + const scopes: ReadonlyArray = [ + { name: "payment:stablecoin" }, + { name: "payment:fiat" }, + { name: "payment:crypto" }, + { name: "payment:cross-border" }, + ]; + + const input: BuildCredentialInput = { + ...sampleCredentialInput, + kyc: { ...sampleKycCredential, scopes }, + }; + + const result = buildKycCredential(input); + expect(result.authority.scopes).toHaveLength(4); + }); + }); + + describe("KYC permission types", () => { + it("should accept standard KYC permission combinations", () => { + const permissions: ReadonlyArray = [ + { resource: "stablecoin:issue", action: "execute" }, + { resource: "stablecoin:redeem", action: "execute" }, + { resource: "stablecoin:transfer", action: "read" }, + { resource: "fiat:on-ramp", action: "write" }, + { resource: "fiat:off-ramp", action: "execute" }, + ]; + + const input: BuildCredentialInput = { + ...sampleCredentialInput, + kyc: { ...sampleKycCredential, permissions }, + }; + + const result = buildKycCredential(input); + expect(result.authority.permissions).toHaveLength(5); + }); + }); +}); diff --git a/packages/agent/src/kyc/artifact.ts b/packages/agent/src/kyc/artifact.ts new file mode 100644 index 0000000..ff5d016 --- /dev/null +++ b/packages/agent/src/kyc/artifact.ts @@ -0,0 +1,175 @@ +/** + * KYC Identity Artifact creation utilities. + * + * Builds IdentityArtifact from AgentCredential with KYC roles/scopes/permissions. + */ + +import * as R from "ramda"; +import type { AgentCredential } from "@lemmaoracle/agent"; +import { commit, prove } from "../mocks/identity.js"; +import type { CommitOutput, ProveOutput } from "../mocks/identity.js"; +import type { IdentityArtifact } from "../mocks/protocol.js"; +import type { KycCredential, KycRole, KycScope, KycPermission } from "./types.js"; + +// ── Helpers ─────────────────────────────────────────────────────────────── + +/** + * Convert KYC roles to AgentCredential authority format. + */ +const rolesToAuthorityRoles = ( + roles: ReadonlyArray, +): ReadonlyArray<{ name: string }> => + R.map((r) => ({ name: r.name }), roles); + +/** + * Convert KYC scopes to AgentCredential authority format. + */ +const scopesToAuthorityScopes = ( + scopes: ReadonlyArray, +): ReadonlyArray<{ name: string }> => + R.map((s) => ({ name: s.name }), scopes); + +/** + * Convert KYC permissions to AgentCredential authority format. + */ +const permissionsToAuthorityPermissions = ( + permissions: ReadonlyArray, +): ReadonlyArray<{ resource: string; action: string }> => + R.map((p) => ({ resource: p.resource, action: p.action }), permissions); + +// ── AgentCredential Builder ────────────────────────────────────────────── + +/** + * Input for building an AgentCredential with KYC attributes. + */ +export type BuildCredentialInput = Readonly<{ + agentId: string; + subjectId: string; + issuerId: string; + kyc: KycCredential; + spendLimit?: number; + currency?: string; + validForSeconds?: number; + controllerId?: string; + orgId?: string; +}>; + +/** + * Current Unix timestamp in seconds. + */ +const nowSeconds = (): number => Math.floor(Date.now() / 1000); + +/** + * Build an AgentCredential with KYC roles/scopes/permissions. + */ +export const buildKycCredential = (input: BuildCredentialInput): AgentCredential => { + const issuedAt = nowSeconds(); + const expiresAt = input.validForSeconds + ? issuedAt + input.validForSeconds + : undefined; + + return { + schema: "agent-credential-v1", + identity: { + agentId: input.agentId, + subjectId: input.subjectId, + ...(input.controllerId ? { controllerId: input.controllerId } : {}), + ...(input.orgId ? { orgId: input.orgId } : {}), + }, + authority: { + roles: rolesToAuthorityRoles(input.kyc.roles), + scopes: scopesToAuthorityScopes(input.kyc.scopes), + permissions: permissionsToAuthorityPermissions(input.kyc.permissions), + }, + ...(input.spendLimit !== undefined || input.currency !== undefined + ? { + financial: { + ...(input.spendLimit !== undefined ? { spendLimit: input.spendLimit } : {}), + ...(input.currency ? { currency: input.currency } : {}), + }, + } + : {}), + lifecycle: { + issuedAt, + ...(expiresAt ? { expiresAt } : {}), + revoked: false, + }, + provenance: { + issuerId: input.issuerId, + sourceSystem: "trust402-kyc", + }, + }; +}; + +// ── Identity Artifact Builder ───────────────────────────────────────────── + +/** + * Input for creating an IdentityArtifact. + */ +export type BuildArtifactInput = Readonly<{ + credential: AgentCredential; + issuerSecretKey: string; + mac: string; + issuerPublicKey: string; + holderKey: string; + docHash?: string; +}>; + +/** + * Result of building an identity artifact. + */ +export type BuildArtifactResult = Readonly<{ + artifact: IdentityArtifact; + commitOutput: CommitOutput; + proveOutput: ProveOutput; + docHash: string; +}>; + +/** + * Build an IdentityArtifact from a credential. + * + * This performs: + * 1. commit() - Creates the commitment structure + * 2. prove() - Generates the ZK proof + * 3. Returns the IdentityArtifact + */ +export const buildIdentityArtifact = async ( + lemmaClient: Parameters[0], + input: BuildArtifactInput, +): Promise => { + // Step 1: Commit the credential + const commitOutput = await commit(lemmaClient, input.credential); + + // Step 2: Generate the identity proof + const proveOutput = await prove(lemmaClient, { + commitOutput, + issuerSecretKey: input.issuerSecretKey, + mac: input.mac, + issuerPublicKey: input.issuerPublicKey, + // Default 'nowSec' to current time + nowSec: Math.floor(Date.now() / 1000).toString(), + }); + + // Use provided docHash or compute from credential + const docHash = input.docHash ?? commitOutput.root; + + // Step 3: Build the IdentityArtifact + const artifact: IdentityArtifact = { + commitOutput, + identityProof: proveOutput, + docHash, + credential: input.credential, + }; + + return { + artifact, + commitOutput, + proveOutput, + docHash, + }; +}; + +// ── Re-exports for convenience ──────────────────────────────────────────── + +// Re-export IdentityArtifact type from mocks +export type { IdentityArtifact } from "../mocks/protocol.js"; diff --git a/packages/agent/src/kyc/index.ts b/packages/agent/src/kyc/index.ts new file mode 100644 index 0000000..99992c8 --- /dev/null +++ b/packages/agent/src/kyc/index.ts @@ -0,0 +1,33 @@ +/** + * KYC attribute proof system for Trust402. + * + * Provides utilities for: + * - Building AgentCredentials with KYC roles/scopes/permissions + * - Creating IdentityArtifacts for proof attachment + * - Verifying KYC attributes on the worker side + */ + +// Types +export type { + KycRole, + KycRoles, + KycScope, + KycScopes, + KycPermission, + KycPermissions, + KycCredential, + KycVerificationResult, + KycGateConfig, +} from "./types.js"; + +// Artifact building +export { + buildKycCredential, + buildIdentityArtifact, + type BuildCredentialInput, + type BuildArtifactInput, + type BuildArtifactResult, +} from "./artifact.js"; + +// Re-export IdentityArtifact type from mocks +export type { IdentityArtifact } from "../mocks/protocol.js"; diff --git a/packages/agent/src/kyc/types.ts b/packages/agent/src/kyc/types.ts new file mode 100644 index 0000000..375322b --- /dev/null +++ b/packages/agent/src/kyc/types.ts @@ -0,0 +1,106 @@ +/** + * KYC-specific types for Trust402 identity proof system. + * + * These types encode KYC attributes as roles, scopes, and permissions + * within the AgentCredential structure. + */ + +import type { ReadonlyDeep } from "type-fest"; + +// ── KYC Roles ───────────────────────────────────────────────────────────── + +/** + * Standard KYC verification roles. + * + * These roles indicate the level of identity verification the agent has completed. + */ +export type KycRole = Readonly<{ + name: + | "kyc-verified" + | "aml-cleared" + | "sanctions-clear" + | "accredited-investor" + | "institutional"; +}>; + +/** + * Array of KYC roles. + */ +export type KycRoles = ReadonlyArray; + +// ── KYC Scopes ──────────────────────────────────────────────────────────── + +/** + * KYC scopes define the operational boundaries for payments. + */ +export type KycScope = Readonly<{ + name: + | "payment:stablecoin" + | "payment:fiat" + | "payment:crypto" + | "payment:cross-border"; +}>; + +/** + * Array of KYC scopes. + */ +export type KycScopes = ReadonlyArray; + +// ── KYC Permissions ─────────────────────────────────────────────────────── + +/** + * KYC permissions define specific actions the agent can perform. + */ +export type KycPermission = Readonly<{ + resource: + | "stablecoin:issue" + | "stablecoin:redeem" + | "stablecoin:transfer" + | "fiat:on-ramp" + | "fiat:off-ramp"; + action: "execute" | "read" | "write"; +}>; + +/** + * Array of KYC permissions. + */ +export type KycPermissions = ReadonlyArray; + +// ── KYC Credential ──────────────────────────────────────────────────────── + +/** + * Full KYC credential structure for an agent. + */ +export type KycCredential = ReadonlyDeep<{ + roles: KycRoles; + scopes: KycScopes; + permissions: KycPermissions; +}>; + +// ── KYC Verification Result ─────────────────────────────────────────────── + +/** + * Result of KYC verification on the worker side. + */ +export type KycVerificationResult = Readonly<{ + verified: boolean; + agentId?: string; + roles: ReadonlyArray; + scopes: ReadonlyArray; + permissions: ReadonlyArray; + missingRoles?: ReadonlyArray; + reason?: string; +}>; + +// ── KYC Gate Configuration ─────────────────────────────────────────────── + +/** + * Configuration for KYC gate requirements. + * Defines which roles/scopes/permissions are required to access a resource. + */ +export type KycGateConfig = Readonly<{ + requiredRoles?: ReadonlyArray; + requiredScopes?: ReadonlyArray; + requiredPermissions?: ReadonlyArray<{ resource: string; action: string }>; + requireAll?: boolean; // If true, all requirements must be met. If false, any one is sufficient. +}>; diff --git a/packages/agent/src/mocks/identity.ts b/packages/agent/src/mocks/identity.ts new file mode 100644 index 0000000..01f9ae2 --- /dev/null +++ b/packages/agent/src/mocks/identity.ts @@ -0,0 +1,194 @@ +/** + * Mock @trust402/identity package for testing KYC flow. + * + * This provides a minimal implementation that matches the expected interface + * for identity proof generation. + */ + +import type { AgentCredential } from "@lemmaoracle/agent"; + +// ── Types ────────────────────────────────────────────────────────────────── + +export type CommitOutput = Readonly<{ + root: string; + salt: string; + sectionHashes: Readonly>; + normalized: Readonly<{ + identity: Readonly<{ + agentId: string; + subjectId: string; + controllerId?: string; + orgId?: string; + }>; + authority: Readonly<{ + roles: ReadonlyArray<{ name: string }>; + scopes: ReadonlyArray<{ name: string }>; + permissions: ReadonlyArray<{ resource: string; action: string }>; + }>; + financial: Readonly<{ + spendLimit?: string; + currency?: string; + paymentPolicy?: string; + }>; + lifecycle: Readonly<{ + issuedAt: string; + expiresAt: string; + revoked: string; + }>; + provenance: Readonly<{ + issuerId: string; + sourceSystem?: string; + generatorId?: string; + }>; + }>; +}>; + +export type ProveInput = Readonly<{ + commitOutput: CommitOutput; + issuerSecretKey: string; + mac: string; + issuerPublicKey: string; + nowSec?: string; +}>; + +// Use ProveOutput from @lemmaoracle/sdk +import type { ProveOutput } from "@lemmaoracle/sdk"; + +// Re-export for convenience +export type { ProveOutput } from "@lemmaoracle/sdk"; + +// ── commit() ──────────────────────────────────────────────────────────────── + +const computeSectionHash = (section: unknown): string => { + // Simple hash simulation - in real implementation this uses Poseidon + const json = JSON.stringify(section); + let hash = 0; + for (let i = 0; i < json.length; i++) { + const char = json.charCodeAt(i); + hash = ((hash << 5) - hash) + char; + hash = hash & hash; + } + return Math.abs(hash).toString(16).padStart(64, "0").slice(0, 64); +}; + +const normalizeCredential = (cred: AgentCredential): CommitOutput["normalized"] => ({ + identity: { + agentId: cred.identity.agentId, + subjectId: cred.identity.subjectId, + ...("controllerId" in cred.identity && { controllerId: cred.identity.controllerId }), + ...("orgId" in cred.identity && { orgId: cred.identity.orgId }), + }, + authority: { + roles: cred.authority.roles, + scopes: cred.authority.scopes, + permissions: cred.authority.permissions, + }, + financial: { + ...(cred.financial?.spendLimit !== undefined && { spendLimit: cred.financial.spendLimit.toString() }), + ...(cred.financial?.currency && { currency: cred.financial.currency }), + ...(cred.financial?.paymentPolicy && { paymentPolicy: cred.financial.paymentPolicy }), + }, + lifecycle: { + issuedAt: cred.lifecycle.issuedAt.toString(), + expiresAt: (cred.lifecycle.expiresAt ?? 0).toString(), + revoked: (cred.lifecycle.revoked ?? false).toString(), + }, + provenance: { + issuerId: cred.provenance.issuerId, + ...(cred.provenance.sourceSystem && { sourceSystem: cred.provenance.sourceSystem }), + ...(cred.provenance.generatorId && { generatorId: cred.provenance.generatorId }), + }, +}); + +/** + * Commit an AgentCredential to create the commitment structure. + * This is a mock implementation for testing. + */ +export const commit = async ( + _client: unknown, + credential: AgentCredential, +): Promise => { + const normalized = normalizeCredential(credential); + + // Compute section hashes + const sectionHashes: Record = { + identityHash: computeSectionHash(normalized.identity), + authorityHash: computeSectionHash(normalized.authority), + financialHash: computeSectionHash(normalized.financial), + lifecycleHash: computeSectionHash(normalized.lifecycle), + provenanceHash: computeSectionHash(normalized.provenance), + }; + + // Compute root as combination of section hashes + const rootInput = Object.values(sectionHashes).join(""); + const root = computeSectionHash(rootInput); + + // Generate random salt + const salt = Array.from(crypto.getRandomValues(new Uint8Array(32))) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); + + return { + root, + salt, + sectionHashes, + normalized, + }; +}; + +// ── prove() ───────────────────────────────────────────────────────────────── + +/** + * Generate an identity proof from a commitment. + * This is a mock implementation for testing. + */ +export const prove = async ( + _client: unknown, + input: ProveInput, +): Promise => { + // Mock proof generation - in real implementation this calls the prover API + const proofBytes = Array.from(crypto.getRandomValues(new Uint8Array(128))) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); + + const inputs = [ + input.commitOutput.root, + input.commitOutput.salt, + input.issuerPublicKey, + input.nowSec ?? Math.floor(Date.now() / 1000).toString(), + ]; + + return { + proof: proofBytes, + inputs, + }; +}; + +// ── submit() ──────────────────────────────────────────────────────────────── + +/** + * Submit an identity proof to the chain. + * This is a mock implementation for testing. + */ +export const submit = async ( + _client: unknown, + _docHash: string, + _proofResult: ProveOutput, + _chainId?: number, +): Promise => { + // Mock submission - in real implementation this submits to the chain + return { txHash: "0x" + "00".repeat(32) }; +}; + +// ── Re-exports from @lemmaoracle/agent ───────────────────────────────────── + +export type { + AgentCredential, + AgentCredentialInput, + NormalizedAgentCredential, + ValidationResult, + ValidationError, + ValidationErrorKind, +} from "@lemmaoracle/agent"; + +export { computeCredentialCommitment } from "@lemmaoracle/agent"; diff --git a/packages/agent/src/mocks/index.ts b/packages/agent/src/mocks/index.ts new file mode 100644 index 0000000..6aac294 --- /dev/null +++ b/packages/agent/src/mocks/index.ts @@ -0,0 +1,37 @@ +/** + * Mock implementations for @trust402/* packages. + * + * These are used when the local trust402 packages are not available + * through workspace linking. + */ + +// Identity mock exports +export { + commit, + prove, + submit, + type CommitOutput, + type ProveInput, +} from "./identity.js"; + +export type { ProveOutput } from "@lemmaoracle/sdk"; + +// Roles mock exports +export { + fieldHash, + witness, + prove as proveRole, + submit as submitRole, + connect as connectRoles, + type PaymentGate, + type CircuitWitness, +} from "./roles.js"; + +// Protocol mock exports +export { + proveRoleFromArtifact, + wrapFetchWithProof, + type IdentityArtifact, + type ProveRoleResult, + type WrapFetchWithProofOptions, +} from "./protocol.js"; diff --git a/packages/agent/src/mocks/protocol.ts b/packages/agent/src/mocks/protocol.ts new file mode 100644 index 0000000..127c100 --- /dev/null +++ b/packages/agent/src/mocks/protocol.ts @@ -0,0 +1,116 @@ +/** + * Mock @trust402/protocol package for testing KYC flow. + * + * Provides IdentityArtifact type and wrapFetchWithProof for proof attachment. + */ + +import type { AgentCredential } from "@lemmaoracle/agent"; +import type { ProveOutput } from "@lemmaoracle/sdk"; +import type { CommitOutput } from "./identity.js"; +import type { PaymentGate } from "./roles.js"; + +// ── Types ────────────────────────────────────────────────────────────────── + +/** + * Pre-generated identity proof artifact. + */ +export type IdentityArtifact = Readonly<{ + commitOutput: CommitOutput; + identityProof: ProveOutput; + docHash: string; + credential: AgentCredential; +}>; + +/** + * Result of generating a role proof from a cached identity artifact. + */ +export type ProveRoleResult = Readonly<{ + identityProof: ProveOutput; + roleProof: ProveOutput; + identitySubmission: unknown; + roleSubmission: unknown; +}>; + +export type WrapFetchWithProofOptions = Readonly<{ + chainId?: number; + onProofResult?: (result: ProveRoleResult) => void; + webhookUrl?: string; + webhookApiKey?: string; + agentId?: string; + attemptedSpend?: number; +}>; + +// ── proveRoleFromArtifact() ──────────────────────────────────────────────── + +/** + * Generate a role proof from an identity artifact. + * Mock implementation for testing. + */ +export const proveRoleFromArtifact = async ( + _client: unknown, + artifact: IdentityArtifact, + gate: PaymentGate, + _options?: WrapFetchWithProofOptions, +): Promise => { + // Mock role proof generation + const roleProof: ProveOutput = { + proof: Array.from(crypto.getRandomValues(new Uint8Array(128))) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""), + inputs: [artifact.docHash, gate.role, gate.maxSpend.toString()], + }; + + return { + identityProof: artifact.identityProof, + roleProof, + identitySubmission: { txHash: "0x" + "00".repeat(32) }, + roleSubmission: { txHash: "0x" + "00".repeat(32) }, + }; +}; + +// ── wrapFetchWithProof() ──────────────────────────────────────────────────── + +/** + * Wrap fetch to attach identity proof to payments. + * + * This composes the x402 payment flow with the identity proof attachment. + * The resulting fetch will: + * 1. Generate a role proof before making the request + * 2. Attach the proof to the PAYMENT header + * 3. Make the actual request with proof attached + */ +export const wrapFetchWithProof = ( + baseFetch: typeof fetch, + artifact: IdentityArtifact, + gate: PaymentGate, + lemmaClient: unknown, + options?: WrapFetchWithProofOptions, +): typeof fetch => { + return async (input: URL | Request | string, init?: RequestInit): Promise => { + // Generate proof before making request + const proofResult = await proveRoleFromArtifact(lemmaClient, artifact, gate, options); + + // Call callback if provided + options?.onProofResult?.(proofResult); + + // Encode credential and proof for headers + const credentialEncoded = btoa(JSON.stringify(artifact.credential)); + const proofEncoded = btoa(JSON.stringify(proofResult.identityProof)); + + // Merge proof headers with existing headers + const headers = new Headers(init?.headers); + headers.set("X-Lemma-Credential", credentialEncoded); + headers.set("X-Lemma-Identity-Proof", proofEncoded); + + // Make the actual request + return baseFetch(input, { + ...init, + headers, + }); + }; +}; + +// ── Re-exports ───────────────────────────────────────────────────────────── + +export type { CommitOutput } from "./identity.js"; +export type { PaymentGate } from "./roles.js"; diff --git a/packages/agent/src/mocks/roles.ts b/packages/agent/src/mocks/roles.ts new file mode 100644 index 0000000..cefe955 --- /dev/null +++ b/packages/agent/src/mocks/roles.ts @@ -0,0 +1,121 @@ +/** + * Mock @trust402/roles package for testing KYC flow. + */ + +import type { ProveOutput } from "@lemmaoracle/sdk"; +import type { CommitOutput } from "./identity.js"; + +// ── Types ────────────────────────────────────────────────────────────────── + +export type PaymentGate = Readonly<{ + role: string; + maxSpend: number; +}>; + +export type CircuitWitness = Readonly<{ + credentialCommitment: string; + roleHash: string; + spendLimit: string; + salt: string; + requiredRoleHash: string; + maxSpend: string; + nowSec: string; + roleGateCommitment: string; + credentialCommitmentPublic: string; +}>; + +// ── Constants ────────────────────────────────────────────────────────────── + +const BN254_PRIME = BigInt( + "21888242871839275222246405745257275088548364400416034343698204186575808495617", +); + +// ── fieldHash() ───────────────────────────────────────────────────────────── + +import { createHash } from "crypto"; + +/** + * SHA-256 with top-nibble masking for BN254 field-element derivation. + */ +export const fieldHash = (name: string): string => { + const hash = createHash("sha256").update(name, "utf8").digest("hex"); + const maskedFirstByte = (parseInt(hash.slice(0, 2), 16) & 0x0f).toString(16).padStart(2, "0"); + const maskedHash = maskedFirstByte + hash.slice(2); + const scalar = BigInt(`0x${maskedHash}`) % BN254_PRIME; + return scalar.toString(); +}; + +// ── witness() ─────────────────────────────────────────────────────────────── + +/** + * Build a CircuitWitness for the role-spend-limit-v1 circuit. + */ +export const witness = ( + gate: PaymentGate, + commitOutput: CommitOutput, +): CircuitWitness => { + const roleHash = fieldHash(gate.role); + const spendLimit = commitOutput.normalized.financial.spendLimit ?? "0"; + const saltScalar = BigInt(commitOutput.salt).toString(); + const nowSec = Math.floor(Date.now() / 1000).toString(); + + // Simple poseidon-like combination (mock) + const roleGateCommitment = fieldHash( + commitOutput.root + roleHash + spendLimit + saltScalar, + ); + + return { + credentialCommitment: commitOutput.root, + roleHash, + spendLimit, + salt: saltScalar, + requiredRoleHash: roleHash, + maxSpend: gate.maxSpend.toString(), + nowSec, + roleGateCommitment, + credentialCommitmentPublic: commitOutput.root, + }; +}; + +// ── prove() ───────────────────────────────────────────────────────────────── + +/** + * Generate a role proof. + * Mock implementation for testing. + */ +export const prove = async ( + _client: unknown, + circuitWitness: CircuitWitness, +): Promise => { + // Mock proof generation + const proofBytes = Array.from(crypto.getRandomValues(new Uint8Array(128))) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); + + return { + proof: proofBytes, + inputs: Object.values(circuitWitness), + }; +}; + +// ── submit() ─────────────────────────────────────────────────────────────── + +/** + * Submit a role proof. + * Mock implementation for testing. + */ +export const submit = async ( + _client: unknown, + _docHash: string, + _proofResult: ProveOutput, + _chainId?: number, +): Promise => { + return { txHash: "0x" + "00".repeat(32) }; +}; + +// ── Client factory ───────────────────────────────────────────────────────── + +export const connect = (apiBase: string) => (apiKey: string) => ({ + apiBase, + apiKey, +}); diff --git a/packages/worker/package.json b/packages/worker/package.json index 65a2e97..9301a3d 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -11,13 +11,16 @@ }, "dependencies": { "@coinbase/x402": "^2.1.0", + "@lemmaoracle/agent": "^0.0.24-alpha.1", "@lemmaoracle/sdk": "^0.0.21", "@lemmaoracle/spec": "^0.0.21", "@lemmaoracle/x402": "^0.1.6", - "hono": "^4.12.15" + "hono": "^4.12.15", + "ramda": "^0.30.0" }, "devDependencies": { "@cloudflare/workers-types": "^4.20260426.1", + "@types/ramda": "^0.30.0", "typescript": "^5.9.3", "vitest": "^3.2.4", "wrangler": "^4.85.0" diff --git a/packages/worker/src/index.ts b/packages/worker/src/index.ts index 46270e5..b8ad55f 100644 --- a/packages/worker/src/index.ts +++ b/packages/worker/src/index.ts @@ -8,6 +8,7 @@ * Endpoints: * GET /example/verify/:hash -- Provenance verification ($0.001 USDC) * POST /example/query -- Full BBS+ selective disclosure ($0.001 USDC) + * GET /example/kyc-check -- KYC attribute verification ($0.001 USDC) * GET / -- Health check * * Content is free. Trust costs $0.001. @@ -21,6 +22,13 @@ import { ExactEvmScheme, } from "@lemmaoracle/x402"; import { createFacilitatorConfig } from "@coinbase/x402"; +import { + verifyKycGate, + KycGates, + extractCredential, + type KycVerificationResult, + type KycGateConfig, +} from "./kyc/index.js"; // --------------------------------------------------------------------------- // Types @@ -177,6 +185,23 @@ const staticRoutes = { mimeType: "application/json", extensions: { lemma: {} }, }, + "GET /example/kyc-check": { + accepts: [ + { + scheme: "exact" as const, + price: "$0.001", + network: "eip155:84532" as const, + payTo: "", // resolved dynamically below + extra: { + name: "USDC", + version: "2", + }, + }, + ], + description: "KYC attribute verification from identity proof", + mimeType: "application/json", + extensions: { lemma: { requiresIdentity: true } }, + }, }; // --------------------------------------------------------------------------- @@ -238,6 +263,12 @@ app.use("*", async (c, next) => { (accept) => ({ ...accept, payTo }), ), }, + "GET /example/kyc-check": { + ...staticRoutes["GET /example/kyc-check"], + accepts: staticRoutes["GET /example/kyc-check"].accepts.map( + (accept) => ({ ...accept, payTo }), + ), + }, }; const middleware = paymentMiddleware(routes, resourceServer); @@ -296,6 +327,43 @@ app.post("/example/query", async (c) => { }); }); +// --------------------------------------------------------------------------- +// GET /example/kyc-check -- KYC attribute verification endpoint +// --------------------------------------------------------------------------- +app.get("/example/kyc-check", async (c) => { + // Extract credential from payment headers + const headers: Record = {}; + c.req.raw.headers.forEach((v, k) => { + headers[k] = v; + }); + + const credential = extractCredential(headers); + + if (!credential) { + return c.json( + { + verified: false, + roles: [], + scopes: [], + permissions: [], + reason: "No credential found in payment headers", + }, + 403, + ); + } + + // Use basic KYC gate by default, but allow customization via query params + const gateParam = c.req.query("gate"); + const gate: KycGateConfig = + gateParam && gateParam in KycGates + ? KycGates[gateParam as keyof typeof KycGates] + : KycGates.basic; + + const result = verifyKycGate(credential, gate); + + return c.json(result, result.verified ? 200 : 403); +}); + // --------------------------------------------------------------------------- // Health check // --------------------------------------------------------------------------- @@ -307,6 +375,7 @@ app.get("/", (c) => endpoints: { verify: "GET /example/verify/:hash (provenance verification)", query: "POST /example/query (BBS+ selective disclosure)", + kycCheck: "GET /example/kyc-check (KYC attribute verification)", health: "GET /", }, }), diff --git a/packages/worker/src/kyc/index.ts b/packages/worker/src/kyc/index.ts new file mode 100644 index 0000000..5888e3d --- /dev/null +++ b/packages/worker/src/kyc/index.ts @@ -0,0 +1,19 @@ +/** + * KYC verification module for worker-side enforcement. + */ + +export { + IDENTITY_PROOF_HEADER, + CREDENTIAL_HEADER, + extractIdentityProof, + extractCredential, + hasRole, + hasScope, + hasPermission, + isExpired, + isRevoked, + verifyKycGate, + KycGates, +} from "./verify.js"; + +export type { KycVerificationResult, KycGateConfig } from "./verify.js"; diff --git a/packages/worker/src/kyc/verify.test.ts b/packages/worker/src/kyc/verify.test.ts new file mode 100644 index 0000000..49be464 --- /dev/null +++ b/packages/worker/src/kyc/verify.test.ts @@ -0,0 +1,365 @@ +/** + * KYC verification tests — Worker-side enforcement. + */ + +import { describe, it, expect } from "vitest"; +import { + verifyKycGate, + hasRole, + hasScope, + hasPermission, + isExpired, + isRevoked, + KycGates, + extractCredential, + type KycVerificationResult, +} from "./verify.js"; +import type { AgentCredential } from "@lemmaoracle/agent"; + +// --------------------------------------------------------------------------- +// Test fixtures +// --------------------------------------------------------------------------- + +const createCredential = ( + overrides: Partial = {}, +): AgentCredential => ({ + schema: "agent-credential-v1", + identity: { + agentId: "agent-001", + subjectId: "subject-001", + }, + authority: { + roles: [{ name: "kyc-verified" }], + scopes: [{ name: "payment:stablecoin" }], + permissions: [{ resource: "stablecoin:transfer", action: "execute" }], + }, + lifecycle: { + issuedAt: Math.floor(Date.now() / 1000), + revoked: false, + }, + provenance: { + issuerId: "issuer-001", + sourceSystem: "trust402-kyc", + }, + ...overrides, +}); + +// --------------------------------------------------------------------------- +// Tests +// --------------------------------------------------------------------------- + +describe("KYC Verification", () => { + describe("hasRole", () => { + it("should return true when credential has the role", () => { + const cred = createCredential(); + expect(hasRole(cred, "kyc-verified")).toBe(true); + }); + + it("should return false when credential lacks the role", () => { + const cred = createCredential(); + expect(hasRole(cred, "aml-cleared")).toBe(false); + }); + + it("should check multiple roles correctly", () => { + const cred = createCredential({ + authority: { + roles: [ + { name: "kyc-verified" }, + { name: "aml-cleared" }, + { name: "sanctions-clear" }, + ], + scopes: [], + permissions: [], + }, + }); + + expect(hasRole(cred, "kyc-verified")).toBe(true); + expect(hasRole(cred, "aml-cleared")).toBe(true); + expect(hasRole(cred, "sanctions-clear")).toBe(true); + expect(hasRole(cred, "institutional")).toBe(false); + }); + }); + + describe("hasScope", () => { + it("should return true when credential has the scope", () => { + const cred = createCredential(); + expect(hasScope(cred, "payment:stablecoin")).toBe(true); + }); + + it("should return false when credential lacks the scope", () => { + const cred = createCredential(); + expect(hasScope(cred, "payment:fiat")).toBe(false); + }); + }); + + describe("hasPermission", () => { + it("should return true when credential has the permission", () => { + const cred = createCredential(); + expect(hasPermission(cred, "stablecoin:transfer", "execute")).toBe(true); + }); + + it("should return false when action does not match", () => { + const cred = createCredential(); + expect(hasPermission(cred, "stablecoin:transfer", "read")).toBe(false); + }); + + it("should return false when resource does not match", () => { + const cred = createCredential(); + expect(hasPermission(cred, "stablecoin:issue", "execute")).toBe(false); + }); + }); + + describe("isExpired", () => { + it("should return false for credential without expiry", () => { + const cred = createCredential(); + expect(isExpired(cred)).toBe(false); + }); + + it("should return false for non-expired credential", () => { + const cred = createCredential({ + lifecycle: { + issuedAt: Math.floor(Date.now() / 1000) - 3600, + expiresAt: Math.floor(Date.now() / 1000) + 3600, + revoked: false, + }, + }); + expect(isExpired(cred)).toBe(false); + }); + + it("should return true for expired credential", () => { + const cred = createCredential({ + lifecycle: { + issuedAt: Math.floor(Date.now() / 1000) - 7200, + expiresAt: Math.floor(Date.now() / 1000) - 3600, + revoked: false, + }, + }); + expect(isExpired(cred)).toBe(true); + }); + }); + + describe("isRevoked", () => { + it("should return false for non-revoked credential", () => { + const cred = createCredential(); + expect(isRevoked(cred)).toBe(false); + }); + + it("should return true for revoked credential", () => { + const cred = createCredential({ + lifecycle: { + issuedAt: Math.floor(Date.now() / 1000), + revoked: true, + }, + }); + expect(isRevoked(cred)).toBe(true); + }); + }); + + describe("verifyKycGate", () => { + it("should verify basic KYC gate with matching role", () => { + const cred = createCredential(); + const result = verifyKycGate(cred, KycGates.basic); + + expect(result.verified).toBe(true); + expect(result.agentId).toBe("agent-001"); + expect(result.roles).toContain("kyc-verified"); + }); + + it("should reject when missing required role", () => { + const cred = createCredential({ + authority: { + roles: [], + scopes: [], + permissions: [], + }, + }); + + const result = verifyKycGate(cred, KycGates.basic); + + expect(result.verified).toBe(false); + expect(result.reason).toBe("Missing required KYC attributes"); + expect(result.missingRoles).toContain("kyc-verified"); + }); + + it("should verify AML compliance gate with all required roles", () => { + const cred = createCredential({ + authority: { + roles: [ + { name: "kyc-verified" }, + { name: "aml-cleared" }, + { name: "sanctions-clear" }, + ], + scopes: [], + permissions: [], + }, + }); + + const result = verifyKycGate(cred, KycGates.amlCompliance); + + expect(result.verified).toBe(true); + }); + + it("should reject AML compliance gate with missing role", () => { + const cred = createCredential({ + authority: { + roles: [{ name: "kyc-verified" }, { name: "aml-cleared" }], + scopes: [], + permissions: [], + }, + }); + + const result = verifyKycGate(cred, KycGates.amlCompliance); + + expect(result.verified).toBe(false); + expect(result.missingRoles).toContain("sanctions-clear"); + }); + + it("should verify stablecoin payment gate with matching scope", () => { + const cred = createCredential(); + const result = verifyKycGate(cred, KycGates.stablecoinPayment); + + expect(result.verified).toBe(true); + expect(result.scopes).toContain("payment:stablecoin"); + }); + + it("should reject stablecoin payment gate without scope", () => { + const cred = createCredential({ + authority: { + roles: [{ name: "kyc-verified" }], + scopes: [], + permissions: [], + }, + }); + + const result = verifyKycGate(cred, KycGates.stablecoinPayment); + + expect(result.verified).toBe(false); + }); + + it("should verify issuance gate with matching permission", () => { + const cred = createCredential({ + authority: { + roles: [{ name: "kyc-verified" }], + scopes: [], + permissions: [{ resource: "stablecoin:issue", action: "execute" }], + }, + }); + + const result = verifyKycGate(cred, KycGates.stablecoinIssuance); + + expect(result.verified).toBe(true); + }); + + it("should verify institutional gate for institutional role", () => { + const cred = createCredential({ + authority: { + roles: [{ name: "institutional" }], + scopes: [], + permissions: [], + }, + }); + + const result = verifyKycGate(cred, KycGates.institutional); + + expect(result.verified).toBe(true); + }); + + it("should reject expired credential", () => { + const cred = createCredential({ + lifecycle: { + issuedAt: Math.floor(Date.now() / 1000) - 7200, + expiresAt: Math.floor(Date.now() / 1000) - 3600, + revoked: false, + }, + }); + + const result = verifyKycGate(cred, KycGates.basic); + + expect(result.verified).toBe(false); + expect(result.reason).toBe("Credential expired"); + }); + + it("should reject revoked credential", () => { + const cred = createCredential({ + lifecycle: { + issuedAt: Math.floor(Date.now() / 1000), + revoked: true, + }, + }); + + const result = verifyKycGate(cred, KycGates.basic); + + expect(result.verified).toBe(false); + expect(result.reason).toBe("Credential revoked"); + }); + + it("should support requireAll: false for any-one semantics", () => { + const cred = createCredential({ + authority: { + roles: [{ name: "aml-cleared" }], // Has one of three required roles + scopes: [], + permissions: [], + }, + }); + + const gate = { + requiredRoles: ["kyc-verified", "aml-cleared", "sanctions-clear"], + requireAll: false, + }; + + const result = verifyKycGate(cred, gate); + + expect(result.verified).toBe(true); + }); + + it("should reject all-requireAll: false when no requirements are met", () => { + const cred = createCredential({ + authority: { + roles: [{ name: "some-other-role" }], + scopes: [], + permissions: [], + }, + }); + + const gate = { + requiredRoles: ["kyc-verified", "aml-cleared"], + requireAll: false, + }; + + const result = verifyKycGate(cred, gate); + + expect(result.verified).toBe(false); + }); + }); + + describe("extractCredential", () => { + it("should extract valid credential from headers", () => { + const cred = createCredential(); + const encoded = btoa(JSON.stringify(cred)); + const headers = { "X-Lemma-Credential": encoded }; + + const result = extractCredential(headers); + + expect(result).not.toBeNull(); + expect(result?.identity.agentId).toBe("agent-001"); + }); + + it("should return null for missing header", () => { + const headers = {}; + const result = extractCredential(headers); + expect(result).toBeNull(); + }); + + it("should return null for invalid base64", () => { + const headers = { "X-Lemma-Credential": "not-valid-base64!!!" }; + const result = extractCredential(headers); + expect(result).toBeNull(); + }); + + it("should return null for invalid JSON", () => { + const headers = { "X-Lemma-Credential": btoa("not json") }; + const result = extractCredential(headers); + expect(result).toBeNull(); + }); + }); +}); diff --git a/packages/worker/src/kyc/verify.ts b/packages/worker/src/kyc/verify.ts new file mode 100644 index 0000000..8e8e537 --- /dev/null +++ b/packages/worker/src/kyc/verify.ts @@ -0,0 +1,290 @@ +/** + * KYC verification utilities for worker-side enforcement. + * + * Verifies identity proofs from x402 payment headers and checks KYC attributes. + */ + +import * as R from "ramda"; + +// ── Types (defined locally to avoid cross-package imports) ───────────────────── + +type KycVerificationResult = Readonly<{ + verified: boolean; + agentId?: string; + roles: ReadonlyArray; + scopes: ReadonlyArray; + permissions: ReadonlyArray; + missingRoles?: ReadonlyArray; + reason?: string; +}>; + +type KycGateConfig = Readonly<{ + requiredRoles?: ReadonlyArray; + requiredScopes?: ReadonlyArray; + requiredPermissions?: ReadonlyArray<{ resource: string; action: string }>; + requireAll?: boolean; +}>; + +// Import AgentCredential type +import type { AgentCredential } from "@lemmaoracle/agent"; + +// Re-export types +export type { KycVerificationResult, KycGateConfig }; + +// ── Proof Header Extraction ─────────────────────────────────────────────── + +/** + * Header name for identity proof in x402 payment. + */ +export const IDENTITY_PROOF_HEADER = "X-Lemma-Identity-Proof"; + +/** + * Header name for credential in x402 payment. + */ +export const CREDENTIAL_HEADER = "X-Lemma-Credential"; + +/** + * Extract identity proof from payment headers. + */ +export const extractIdentityProof = ( + headers: Record, +): Readonly> | null => { + const proofHeader = headers[IDENTITY_PROOF_HEADER]; + if (!proofHeader) return null; + + try { + return JSON.parse(atob(proofHeader)) as Record; + } catch { + return null; + } +}; + +/** + * Extract credential from payment headers. + */ +export const extractCredential = ( + headers: Record, +): AgentCredential | null => { + const credHeader = headers[CREDENTIAL_HEADER]; + if (!credHeader) return null; + + try { + return JSON.parse(atob(credHeader)) as AgentCredential; + } catch { + return null; + } +}; + +// ── KYC Verification ────────────────────────────────────────────────────── + +/** + * Extract role names from credential. + */ +const extractRoleNames = (cred: AgentCredential): ReadonlyArray => + R.map((r) => r.name, cred.authority.roles); + +/** + * Extract scope names from credential. + */ +const extractScopeNames = (cred: AgentCredential): ReadonlyArray => + R.map((s) => s.name, cred.authority.scopes); + +/** + * Extract permission strings from credential. + */ +const extractPermissionStrings = (cred: AgentCredential): ReadonlyArray => + R.map((p) => `${p.resource}:${p.action}`, cred.authority.permissions); + +/** + * Check if credential has a specific role. + */ +export const hasRole = (cred: AgentCredential, roleName: string): boolean => + R.any((r) => r.name === roleName, cred.authority.roles); + +/** + * Check if credential has a specific scope. + */ +export const hasScope = (cred: AgentCredential, scopeName: string): boolean => + R.any((s) => s.name === scopeName, cred.authority.scopes); + +/** + * Check if credential has a specific permission. + */ +export const hasPermission = ( + cred: AgentCredential, + resource: string, + action: string, +): boolean => + R.any( + (p) => p.resource === resource && p.action === action, + cred.authority.permissions, + ); + +/** + * Check if credential is expired. + */ +export const isExpired = (cred: AgentCredential): boolean => { + if (!cred.lifecycle.expiresAt) return false; + return cred.lifecycle.expiresAt < Math.floor(Date.now() / 1000); +}; + +/** + * Check if credential is revoked. + */ +export const isRevoked = (cred: AgentCredential): boolean => + cred.lifecycle.revoked === true; + +/** + * Verify KYC gate requirements against a credential. + */ +export const verifyKycGate = ( + cred: AgentCredential, + gate: KycGateConfig, +): KycVerificationResult => { + // Check expiration and revocation + if (isExpired(cred)) { + return { + verified: false, + agentId: cred.identity.agentId, + roles: extractRoleNames(cred), + scopes: extractScopeNames(cred), + permissions: extractPermissionStrings(cred), + reason: "Credential expired", + }; + } + + if (isRevoked(cred)) { + return { + verified: false, + agentId: cred.identity.agentId, + roles: extractRoleNames(cred), + scopes: extractScopeNames(cred), + permissions: extractPermissionStrings(cred), + reason: "Credential revoked", + }; + } + + const requiredRoles = gate.requiredRoles ?? []; + const requiredScopes = gate.requiredScopes ?? []; + const requiredPermissions = gate.requiredPermissions ?? []; + const requireAll = gate.requireAll ?? true; + + // Collect missing requirements + const missingRoles = R.filter( + (name: string) => !hasRole(cred, name), + requiredRoles, + ); + + const missingScopes = R.filter( + (name: string) => !hasScope(cred, name), + requiredScopes, + ); + + const missingPermissions = R.filter( + (req: { resource: string; action: string }) => + !hasPermission(cred, req.resource, req.action), + requiredPermissions, + ); + + // Determine if verified based on requireAll flag + let verified: boolean; + let missingRolesList: ReadonlyArray; + + if (requireAll) { + // All requirements must be met + verified = + missingRoles.length === 0 && + missingScopes.length === 0 && + missingPermissions.length === 0; + missingRolesList = [ + ...missingRoles, + ...missingScopes, + ...R.map( + (p: { resource: string; action: string }) => `${p.resource}:${p.action}`, + missingPermissions, + ), + ]; + } else { + // Any one requirement being met is sufficient + const hasAnyRole = + requiredRoles.length > 0 && + R.any((name: string) => hasRole(cred, name), requiredRoles); + const hasAnyScope = + requiredScopes.length > 0 && + R.any((name: string) => hasScope(cred, name), requiredScopes); + const hasAnyPermission = + requiredPermissions.length > 0 && + R.any( + (req: { resource: string; action: string }) => + hasPermission(cred, req.resource, req.action), + requiredPermissions, + ); + + verified = + hasAnyRole || hasAnyScope || hasAnyPermission || + (requiredRoles.length === 0 && requiredScopes.length === 0 && requiredPermissions.length === 0); + + missingRolesList = verified + ? [] + : [...requiredRoles, ...requiredScopes, ...R.map((p) => `${p.resource}:${p.action}`, requiredPermissions)]; + } + + return { + verified, + agentId: cred.identity.agentId, + roles: extractRoleNames(cred), + scopes: extractScopeNames(cred), + permissions: extractPermissionStrings(cred), + ...(missingRolesList.length > 0 ? { missingRoles: missingRolesList } : {}), + ...(verified ? {} : { reason: "Missing required KYC attributes" }), + }; +}; + +// ── Predefined KYC Gates ───────────────────────────────────────────────── + +/** + * Standard KYC gates for common use cases. + */ +export const KycGates = { + /** + * Basic KYC verification - requires kyc-verified role. + */ + basic: { + requiredRoles: ["kyc-verified"], + requireAll: true, + } as const satisfies KycGateConfig, + + /** + * AML/CFT compliance - requires KYC + sanctions + AML clearance. + */ + amlCompliance: { + requiredRoles: ["kyc-verified", "aml-cleared", "sanctions-clear"], + requireAll: true, + } as const satisfies KycGateConfig, + + /** + * Stablecoin operations - requires KYC + stablecoin scope. + */ + stablecoinPayment: { + requiredRoles: ["kyc-verified"], + requiredScopes: ["payment:stablecoin"], + requireAll: true, + } as const satisfies KycGateConfig, + + /** + * Issuance operations - requires specific permissions. + */ + stablecoinIssuance: { + requiredRoles: ["kyc-verified"], + requiredPermissions: [{ resource: "stablecoin:issue", action: "execute" }], + requireAll: true, + } as const satisfies KycGateConfig, + + /** + * High-value payments - requires institutional KYC level. + */ + institutional: { + requiredRoles: ["institutional"], + requireAll: true, + } as const satisfies KycGateConfig, +} as const; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 987f0b3..1988999 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,6 +42,12 @@ importers: packages/agent: dependencies: + '@lemmaoracle/agent': + specifier: ^0.0.24-alpha.1 + version: 0.0.24-alpha.1 + '@lemmaoracle/sdk': + specifier: ^0.0.23 + version: 0.0.23 '@x402/core': specifier: ^2.10.0 version: 2.10.0 @@ -69,6 +75,9 @@ importers: ora: specifier: ^9.4.0 version: 9.4.0 + ramda: + specifier: ^0.30.0 + version: 0.30.1 viem: specifier: ^2.48.4 version: 2.48.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) @@ -76,6 +85,9 @@ importers: '@types/node': specifier: ^22.19.17 version: 22.19.17 + '@types/ramda': + specifier: ^0.30.0 + version: 0.30.2 tsx: specifier: ^4.21.0 version: 4.21.0 @@ -111,6 +123,9 @@ importers: '@coinbase/x402': specifier: ^2.1.0 version: 2.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@lemmaoracle/agent': + specifier: ^0.0.24-alpha.1 + version: 0.0.24-alpha.1 '@lemmaoracle/sdk': specifier: ^0.0.21 version: 0.0.21 @@ -123,10 +138,16 @@ importers: hono: specifier: ^4.12.15 version: 4.12.15 + ramda: + specifier: ^0.30.0 + version: 0.30.1 devDependencies: '@cloudflare/workers-types': specifier: ^4.20260426.1 version: 4.20260426.1 + '@types/ramda': + specifier: ^0.30.0 + version: 0.30.2 typescript: specifier: ^5.9.3 version: 5.9.3 @@ -689,12 +710,21 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@lemmaoracle/agent@0.0.24-alpha.1': + resolution: {integrity: sha512-e9M3vHwn1iV7PcBUMh/PeLMYMnkR2GMCaAH4y1rLGZCwTnpbeBDLyDey+Fw9FDrNZ8eaoDzEWoS1DwR3qRZJBg==} + '@lemmaoracle/sdk@0.0.21': resolution: {integrity: sha512-7v5QVlt33ecWzKfE20XYI+y7ed8OOd6YAybtmDa/uQHHBlzNqFGi4XFBB6f5YZyuXBZ0NDKSGGtyrbmRJEi6sg==} + '@lemmaoracle/sdk@0.0.23': + resolution: {integrity: sha512-4R+OtIRO618EhuumG0uNRH0airN8i7jsnYJgD1sq0vfNWm5GGwNHl36+Lk9IKTQ2rMLRgDD9YgyppdDPPxCXpQ==} + '@lemmaoracle/spec@0.0.21': resolution: {integrity: sha512-6jEORWeNZh4J2HD+IvjYkf0BpEPscbS6vxjY5JhX9Q6aTh716ZhmWAdMxPRFMfFHDlTkibSSnd/p6AYPCGFWow==} + '@lemmaoracle/spec@0.0.23': + resolution: {integrity: sha512-/46I22Y3M6+yTDA5Y0rIbuCNpEZ68IRQzZlLZQoJptHIxsoWigwtP2P8nuI4Pw4onGLeraAtw3oc+3OCKQALbg==} + '@lemmaoracle/x402@0.1.6': resolution: {integrity: sha512-YK2U3pFAtKEcdPezmFR8C6HNHqJyi+Psd6/qO1MJSi4JKNpPB3yM6M3AWO1NSp03JTJHFtd1GaHniIbdgN1cTw==} peerDependencies: @@ -1294,6 +1324,9 @@ packages: '@types/node@22.7.5': resolution: {integrity: sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==} + '@types/ramda@0.30.2': + resolution: {integrity: sha512-PyzHvjCalm2BRYjAU6nIB3TprYwMNOUY/7P/N8bSzp9W/yM2YrtGtAnnVtaCNSeOZ8DzKyFDvaqQs7LnWwwmBA==} + '@vitest/expect@3.2.4': resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} @@ -2031,6 +2064,9 @@ packages: tryer@1.0.1: resolution: {integrity: sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==} + ts-toolbelt@9.6.0: + resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==} + tslib@2.7.0: resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} @@ -2045,6 +2081,9 @@ packages: tweetnacl@1.0.3: resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} + types-ramda@0.30.1: + resolution: {integrity: sha512-1HTsf5/QVRmLzcGfldPFvkVsAdi1db1BBKzi7iW3KBUlOICg/nKnFS+jGqDJS3YD8VsWbAh7JiHeBvbsw8RPxA==} + typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} @@ -2603,6 +2642,13 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@lemmaoracle/agent@0.0.24-alpha.1': + dependencies: + '@lemmaoracle/sdk': 0.0.23 + '@lemmaoracle/spec': 0.0.23 + poseidon-lite: 0.3.0 + ramda: 0.30.1 + '@lemmaoracle/sdk@0.0.21': dependencies: '@docknetwork/crypto-wasm': 0.35.0 @@ -2614,8 +2660,21 @@ snapshots: ramda: 0.30.1 snarkjs: 0.7.6 + '@lemmaoracle/sdk@0.0.23': + dependencies: + '@docknetwork/crypto-wasm': 0.35.0 + '@lemmaoracle/spec': 0.0.23 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + poseidon-lite: 0.2.1 + ramda: 0.30.1 + snarkjs: 0.7.6 + '@lemmaoracle/spec@0.0.21': {} + '@lemmaoracle/spec@0.0.23': {} + '@lemmaoracle/x402@0.1.6(@lemmaoracle/sdk@0.0.21)(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(typescript@5.9.3)(utf-8-validate@5.0.10)': dependencies: '@lemmaoracle/sdk': 0.0.21 @@ -3235,6 +3294,10 @@ snapshots: undici-types: 6.19.8 optional: true + '@types/ramda@0.30.2': + dependencies: + types-ramda: 0.30.1 + '@vitest/expect@3.2.4': dependencies: '@types/chai': 5.2.3 @@ -4123,6 +4186,8 @@ snapshots: tryer@1.0.1: {} + ts-toolbelt@9.6.0: {} + tslib@2.7.0: optional: true @@ -4138,6 +4203,10 @@ snapshots: tweetnacl@1.0.3: {} + types-ramda@0.30.1: + dependencies: + ts-toolbelt: 9.6.0 + typescript@5.9.3: {} uncrypto@0.1.3: {} From 423d4d1892f8aa901918ad094c668cf9b911d40d Mon Sep 17 00:00:00 2001 From: aggre Date: Fri, 8 May 2026 03:35:59 +0000 Subject: [PATCH 2/2] feat: integrate trust402 for KYC proof-before-payment - Add trust402 as git submodule (vendors/trust402) - Configure pnpm workspace to include trust402 packages - Replace mock implementations with real @trust402/identity + @trust402/protocol - Agent: buildIdentityArtifact() uses register() + prove() from trust402 - Worker: extractIdentityArtifact() from X-PAYMENT-IDENTITY header (Base64) - Add .env.example for local development - Delete packages/agent/src/mocks/ (replaced by real trust402 imports) - 61 tests passing, TypeScript strict compilation OK --- .gitmodules | 3 + packages/agent/.env.example | 19 + packages/agent/package.json | 3 + packages/agent/src/kyc/artifact.test.ts | 3 +- packages/agent/src/kyc/artifact.ts | 51 +- packages/agent/src/kyc/index.ts | 7 +- packages/agent/src/mocks/identity.ts | 194 ---- packages/agent/src/mocks/index.ts | 37 - packages/agent/src/mocks/protocol.ts | 116 --- packages/agent/src/mocks/roles.ts | 121 --- packages/worker/package.json | 2 + packages/worker/src/kyc/index.ts | 2 + packages/worker/src/kyc/verify.test.ts | 131 ++- packages/worker/src/kyc/verify.ts | 79 +- pnpm-lock.yaml | 1103 +++++++++++++++++++++++ pnpm-workspace.yaml | 1 + vendors/trust402 | 1 + 17 files changed, 1358 insertions(+), 515 deletions(-) create mode 100644 packages/agent/.env.example delete mode 100644 packages/agent/src/mocks/identity.ts delete mode 100644 packages/agent/src/mocks/index.ts delete mode 100644 packages/agent/src/mocks/protocol.ts delete mode 100644 packages/agent/src/mocks/roles.ts create mode 160000 vendors/trust402 diff --git a/.gitmodules b/.gitmodules index f5d600a..09d131f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "packages/circuit/lib/forge-std"] path = packages/circuit/lib/forge-std url = https://github.com/foundry-rs/forge-std +[submodule "vendors/trust402"] + path = vendors/trust402 + url = https://github.com/lemmaoracle/trust402.git diff --git a/packages/agent/.env.example b/packages/agent/.env.example new file mode 100644 index 0000000..2e43627 --- /dev/null +++ b/packages/agent/.env.example @@ -0,0 +1,19 @@ +# Example x402 Agent Environment Variables +# Copy this file to .env and fill in your values + +# Private key for signing x402 payments (0x-prefixed hex) +# Must have USDC on Base Sepolia testnet +PRIVATE_KEY=0x... + +# Resource server URL (the x402-enabled worker) +RESOURCE_SERVER_URL=http://localhost:8787 + +# Lemma API credentials (for identity proof generation) +LEMMA_API_BASE=https://api.lemma.frame00.com +LEMMA_API_KEY=your-api-key-here + +# Optional: Issuer keys for identity proof generation +ISSUER_SECRET_KEY=... +ISSUER_PUBLIC_KEY=... +ISSUER_MAC=... +HOLDER_KEY=... diff --git a/packages/agent/package.json b/packages/agent/package.json index b571024..6bc526b 100644 --- a/packages/agent/package.json +++ b/packages/agent/package.json @@ -11,6 +11,9 @@ "dependencies": { "@lemmaoracle/agent": "^0.0.24-alpha.1", "@lemmaoracle/sdk": "^0.0.23", + "@trust402/identity": "workspace:*", + "@trust402/protocol": "workspace:*", + "@trust402/roles": "workspace:*", "@x402/core": "^2.10.0", "@x402/evm": "^2.10.0", "@x402/fetch": "^2.10.0", diff --git a/packages/agent/src/kyc/artifact.test.ts b/packages/agent/src/kyc/artifact.test.ts index 10085c6..8e445a6 100644 --- a/packages/agent/src/kyc/artifact.test.ts +++ b/packages/agent/src/kyc/artifact.test.ts @@ -2,12 +2,13 @@ * KYC module tests — Identity artifact creation and verification. */ -import { describe, it, expect } from "vitest"; +import { describe, it, expect, vi, beforeEach } from "vitest"; import { buildKycCredential, type BuildCredentialInput, } from "./artifact.js"; import type { KycRole, KycScope, KycPermission, KycCredential } from "./types.js"; +import type { AgentCredential } from "@lemmaoracle/agent"; // --------------------------------------------------------------------------- // Test fixtures diff --git a/packages/agent/src/kyc/artifact.ts b/packages/agent/src/kyc/artifact.ts index ff5d016..2ad13d2 100644 --- a/packages/agent/src/kyc/artifact.ts +++ b/packages/agent/src/kyc/artifact.ts @@ -2,15 +2,24 @@ * KYC Identity Artifact creation utilities. * * Builds IdentityArtifact from AgentCredential with KYC roles/scopes/permissions. + * Uses real @trust402/identity package for proof generation. */ import * as R from "ramda"; import type { AgentCredential } from "@lemmaoracle/agent"; -import { commit, prove } from "../mocks/identity.js"; -import type { CommitOutput, ProveOutput } from "../mocks/identity.js"; -import type { IdentityArtifact } from "../mocks/protocol.js"; +import { + register, + prove, + type RegisterOutput, + type CommitOutput, +} from "@trust402/identity"; +import type { ProveOutput } from "@lemmaoracle/sdk"; +import type { IdentityArtifact } from "@trust402/protocol"; import type { KycCredential, KycRole, KycScope, KycPermission } from "./types.js"; +// Import LemmaClient type for function signatures +import type { LemmaClient } from "@lemmaoracle/sdk"; + // ── Helpers ─────────────────────────────────────────────────────────────── /** @@ -112,7 +121,6 @@ export type BuildArtifactInput = Readonly<{ mac: string; issuerPublicKey: string; holderKey: string; - docHash?: string; }>; /** @@ -120,29 +128,31 @@ export type BuildArtifactInput = Readonly<{ */ export type BuildArtifactResult = Readonly<{ artifact: IdentityArtifact; - commitOutput: CommitOutput; + registerOutput: RegisterOutput; proveOutput: ProveOutput; - docHash: string; }>; /** * Build an IdentityArtifact from a credential. * * This performs: - * 1. commit() - Creates the commitment structure - * 2. prove() - Generates the ZK proof + * 1. register() - Creates the commitment structure and encrypts the credential + * 2. prove() - Generates the ZK identity proof * 3. Returns the IdentityArtifact */ export const buildIdentityArtifact = async ( - lemmaClient: Parameters[0], + lemmaClient: LemmaClient, input: BuildArtifactInput, ): Promise => { - // Step 1: Commit the credential - const commitOutput = await commit(lemmaClient, input.credential); + // Step 1: Register the credential (commit + encrypt) + const registerOutput = await register(lemmaClient, { + credential: input.credential, + holderKey: input.holderKey, + }); // Step 2: Generate the identity proof const proveOutput = await prove(lemmaClient, { - commitOutput, + commitOutput: registerOutput.commitOutput, issuerSecretKey: input.issuerSecretKey, mac: input.mac, issuerPublicKey: input.issuerPublicKey, @@ -150,26 +160,25 @@ export const buildIdentityArtifact = async ( nowSec: Math.floor(Date.now() / 1000).toString(), }); - // Use provided docHash or compute from credential - const docHash = input.docHash ?? commitOutput.root; - // Step 3: Build the IdentityArtifact const artifact: IdentityArtifact = { - commitOutput, + commitOutput: registerOutput.commitOutput, identityProof: proveOutput, - docHash, + docHash: registerOutput.docHash, credential: input.credential, }; return { artifact, - commitOutput, + registerOutput, proveOutput, - docHash, }; }; // ── Re-exports for convenience ──────────────────────────────────────────── -// Re-export IdentityArtifact type from mocks -export type { IdentityArtifact } from "../mocks/protocol.js"; +// Re-export IdentityArtifact type from @trust402/protocol +export type { IdentityArtifact } from "@trust402/protocol"; + +// Re-export LemmaClient type for convenience +export type { LemmaClient } from "@lemmaoracle/sdk"; diff --git a/packages/agent/src/kyc/index.ts b/packages/agent/src/kyc/index.ts index 99992c8..190ed1a 100644 --- a/packages/agent/src/kyc/index.ts +++ b/packages/agent/src/kyc/index.ts @@ -29,5 +29,8 @@ export { type BuildArtifactResult, } from "./artifact.js"; -// Re-export IdentityArtifact type from mocks -export type { IdentityArtifact } from "../mocks/protocol.js"; +// Re-export IdentityArtifact type from @trust402/protocol +export type { IdentityArtifact } from "@trust402/protocol"; + +// Re-export LemmaClient for convenience +export type { LemmaClient } from "@lemmaoracle/sdk"; diff --git a/packages/agent/src/mocks/identity.ts b/packages/agent/src/mocks/identity.ts deleted file mode 100644 index 01f9ae2..0000000 --- a/packages/agent/src/mocks/identity.ts +++ /dev/null @@ -1,194 +0,0 @@ -/** - * Mock @trust402/identity package for testing KYC flow. - * - * This provides a minimal implementation that matches the expected interface - * for identity proof generation. - */ - -import type { AgentCredential } from "@lemmaoracle/agent"; - -// ── Types ────────────────────────────────────────────────────────────────── - -export type CommitOutput = Readonly<{ - root: string; - salt: string; - sectionHashes: Readonly>; - normalized: Readonly<{ - identity: Readonly<{ - agentId: string; - subjectId: string; - controllerId?: string; - orgId?: string; - }>; - authority: Readonly<{ - roles: ReadonlyArray<{ name: string }>; - scopes: ReadonlyArray<{ name: string }>; - permissions: ReadonlyArray<{ resource: string; action: string }>; - }>; - financial: Readonly<{ - spendLimit?: string; - currency?: string; - paymentPolicy?: string; - }>; - lifecycle: Readonly<{ - issuedAt: string; - expiresAt: string; - revoked: string; - }>; - provenance: Readonly<{ - issuerId: string; - sourceSystem?: string; - generatorId?: string; - }>; - }>; -}>; - -export type ProveInput = Readonly<{ - commitOutput: CommitOutput; - issuerSecretKey: string; - mac: string; - issuerPublicKey: string; - nowSec?: string; -}>; - -// Use ProveOutput from @lemmaoracle/sdk -import type { ProveOutput } from "@lemmaoracle/sdk"; - -// Re-export for convenience -export type { ProveOutput } from "@lemmaoracle/sdk"; - -// ── commit() ──────────────────────────────────────────────────────────────── - -const computeSectionHash = (section: unknown): string => { - // Simple hash simulation - in real implementation this uses Poseidon - const json = JSON.stringify(section); - let hash = 0; - for (let i = 0; i < json.length; i++) { - const char = json.charCodeAt(i); - hash = ((hash << 5) - hash) + char; - hash = hash & hash; - } - return Math.abs(hash).toString(16).padStart(64, "0").slice(0, 64); -}; - -const normalizeCredential = (cred: AgentCredential): CommitOutput["normalized"] => ({ - identity: { - agentId: cred.identity.agentId, - subjectId: cred.identity.subjectId, - ...("controllerId" in cred.identity && { controllerId: cred.identity.controllerId }), - ...("orgId" in cred.identity && { orgId: cred.identity.orgId }), - }, - authority: { - roles: cred.authority.roles, - scopes: cred.authority.scopes, - permissions: cred.authority.permissions, - }, - financial: { - ...(cred.financial?.spendLimit !== undefined && { spendLimit: cred.financial.spendLimit.toString() }), - ...(cred.financial?.currency && { currency: cred.financial.currency }), - ...(cred.financial?.paymentPolicy && { paymentPolicy: cred.financial.paymentPolicy }), - }, - lifecycle: { - issuedAt: cred.lifecycle.issuedAt.toString(), - expiresAt: (cred.lifecycle.expiresAt ?? 0).toString(), - revoked: (cred.lifecycle.revoked ?? false).toString(), - }, - provenance: { - issuerId: cred.provenance.issuerId, - ...(cred.provenance.sourceSystem && { sourceSystem: cred.provenance.sourceSystem }), - ...(cred.provenance.generatorId && { generatorId: cred.provenance.generatorId }), - }, -}); - -/** - * Commit an AgentCredential to create the commitment structure. - * This is a mock implementation for testing. - */ -export const commit = async ( - _client: unknown, - credential: AgentCredential, -): Promise => { - const normalized = normalizeCredential(credential); - - // Compute section hashes - const sectionHashes: Record = { - identityHash: computeSectionHash(normalized.identity), - authorityHash: computeSectionHash(normalized.authority), - financialHash: computeSectionHash(normalized.financial), - lifecycleHash: computeSectionHash(normalized.lifecycle), - provenanceHash: computeSectionHash(normalized.provenance), - }; - - // Compute root as combination of section hashes - const rootInput = Object.values(sectionHashes).join(""); - const root = computeSectionHash(rootInput); - - // Generate random salt - const salt = Array.from(crypto.getRandomValues(new Uint8Array(32))) - .map((b) => b.toString(16).padStart(2, "0")) - .join(""); - - return { - root, - salt, - sectionHashes, - normalized, - }; -}; - -// ── prove() ───────────────────────────────────────────────────────────────── - -/** - * Generate an identity proof from a commitment. - * This is a mock implementation for testing. - */ -export const prove = async ( - _client: unknown, - input: ProveInput, -): Promise => { - // Mock proof generation - in real implementation this calls the prover API - const proofBytes = Array.from(crypto.getRandomValues(new Uint8Array(128))) - .map((b) => b.toString(16).padStart(2, "0")) - .join(""); - - const inputs = [ - input.commitOutput.root, - input.commitOutput.salt, - input.issuerPublicKey, - input.nowSec ?? Math.floor(Date.now() / 1000).toString(), - ]; - - return { - proof: proofBytes, - inputs, - }; -}; - -// ── submit() ──────────────────────────────────────────────────────────────── - -/** - * Submit an identity proof to the chain. - * This is a mock implementation for testing. - */ -export const submit = async ( - _client: unknown, - _docHash: string, - _proofResult: ProveOutput, - _chainId?: number, -): Promise => { - // Mock submission - in real implementation this submits to the chain - return { txHash: "0x" + "00".repeat(32) }; -}; - -// ── Re-exports from @lemmaoracle/agent ───────────────────────────────────── - -export type { - AgentCredential, - AgentCredentialInput, - NormalizedAgentCredential, - ValidationResult, - ValidationError, - ValidationErrorKind, -} from "@lemmaoracle/agent"; - -export { computeCredentialCommitment } from "@lemmaoracle/agent"; diff --git a/packages/agent/src/mocks/index.ts b/packages/agent/src/mocks/index.ts deleted file mode 100644 index 6aac294..0000000 --- a/packages/agent/src/mocks/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Mock implementations for @trust402/* packages. - * - * These are used when the local trust402 packages are not available - * through workspace linking. - */ - -// Identity mock exports -export { - commit, - prove, - submit, - type CommitOutput, - type ProveInput, -} from "./identity.js"; - -export type { ProveOutput } from "@lemmaoracle/sdk"; - -// Roles mock exports -export { - fieldHash, - witness, - prove as proveRole, - submit as submitRole, - connect as connectRoles, - type PaymentGate, - type CircuitWitness, -} from "./roles.js"; - -// Protocol mock exports -export { - proveRoleFromArtifact, - wrapFetchWithProof, - type IdentityArtifact, - type ProveRoleResult, - type WrapFetchWithProofOptions, -} from "./protocol.js"; diff --git a/packages/agent/src/mocks/protocol.ts b/packages/agent/src/mocks/protocol.ts deleted file mode 100644 index 127c100..0000000 --- a/packages/agent/src/mocks/protocol.ts +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Mock @trust402/protocol package for testing KYC flow. - * - * Provides IdentityArtifact type and wrapFetchWithProof for proof attachment. - */ - -import type { AgentCredential } from "@lemmaoracle/agent"; -import type { ProveOutput } from "@lemmaoracle/sdk"; -import type { CommitOutput } from "./identity.js"; -import type { PaymentGate } from "./roles.js"; - -// ── Types ────────────────────────────────────────────────────────────────── - -/** - * Pre-generated identity proof artifact. - */ -export type IdentityArtifact = Readonly<{ - commitOutput: CommitOutput; - identityProof: ProveOutput; - docHash: string; - credential: AgentCredential; -}>; - -/** - * Result of generating a role proof from a cached identity artifact. - */ -export type ProveRoleResult = Readonly<{ - identityProof: ProveOutput; - roleProof: ProveOutput; - identitySubmission: unknown; - roleSubmission: unknown; -}>; - -export type WrapFetchWithProofOptions = Readonly<{ - chainId?: number; - onProofResult?: (result: ProveRoleResult) => void; - webhookUrl?: string; - webhookApiKey?: string; - agentId?: string; - attemptedSpend?: number; -}>; - -// ── proveRoleFromArtifact() ──────────────────────────────────────────────── - -/** - * Generate a role proof from an identity artifact. - * Mock implementation for testing. - */ -export const proveRoleFromArtifact = async ( - _client: unknown, - artifact: IdentityArtifact, - gate: PaymentGate, - _options?: WrapFetchWithProofOptions, -): Promise => { - // Mock role proof generation - const roleProof: ProveOutput = { - proof: Array.from(crypto.getRandomValues(new Uint8Array(128))) - .map((b) => b.toString(16).padStart(2, "0")) - .join(""), - inputs: [artifact.docHash, gate.role, gate.maxSpend.toString()], - }; - - return { - identityProof: artifact.identityProof, - roleProof, - identitySubmission: { txHash: "0x" + "00".repeat(32) }, - roleSubmission: { txHash: "0x" + "00".repeat(32) }, - }; -}; - -// ── wrapFetchWithProof() ──────────────────────────────────────────────────── - -/** - * Wrap fetch to attach identity proof to payments. - * - * This composes the x402 payment flow with the identity proof attachment. - * The resulting fetch will: - * 1. Generate a role proof before making the request - * 2. Attach the proof to the PAYMENT header - * 3. Make the actual request with proof attached - */ -export const wrapFetchWithProof = ( - baseFetch: typeof fetch, - artifact: IdentityArtifact, - gate: PaymentGate, - lemmaClient: unknown, - options?: WrapFetchWithProofOptions, -): typeof fetch => { - return async (input: URL | Request | string, init?: RequestInit): Promise => { - // Generate proof before making request - const proofResult = await proveRoleFromArtifact(lemmaClient, artifact, gate, options); - - // Call callback if provided - options?.onProofResult?.(proofResult); - - // Encode credential and proof for headers - const credentialEncoded = btoa(JSON.stringify(artifact.credential)); - const proofEncoded = btoa(JSON.stringify(proofResult.identityProof)); - - // Merge proof headers with existing headers - const headers = new Headers(init?.headers); - headers.set("X-Lemma-Credential", credentialEncoded); - headers.set("X-Lemma-Identity-Proof", proofEncoded); - - // Make the actual request - return baseFetch(input, { - ...init, - headers, - }); - }; -}; - -// ── Re-exports ───────────────────────────────────────────────────────────── - -export type { CommitOutput } from "./identity.js"; -export type { PaymentGate } from "./roles.js"; diff --git a/packages/agent/src/mocks/roles.ts b/packages/agent/src/mocks/roles.ts deleted file mode 100644 index cefe955..0000000 --- a/packages/agent/src/mocks/roles.ts +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Mock @trust402/roles package for testing KYC flow. - */ - -import type { ProveOutput } from "@lemmaoracle/sdk"; -import type { CommitOutput } from "./identity.js"; - -// ── Types ────────────────────────────────────────────────────────────────── - -export type PaymentGate = Readonly<{ - role: string; - maxSpend: number; -}>; - -export type CircuitWitness = Readonly<{ - credentialCommitment: string; - roleHash: string; - spendLimit: string; - salt: string; - requiredRoleHash: string; - maxSpend: string; - nowSec: string; - roleGateCommitment: string; - credentialCommitmentPublic: string; -}>; - -// ── Constants ────────────────────────────────────────────────────────────── - -const BN254_PRIME = BigInt( - "21888242871839275222246405745257275088548364400416034343698204186575808495617", -); - -// ── fieldHash() ───────────────────────────────────────────────────────────── - -import { createHash } from "crypto"; - -/** - * SHA-256 with top-nibble masking for BN254 field-element derivation. - */ -export const fieldHash = (name: string): string => { - const hash = createHash("sha256").update(name, "utf8").digest("hex"); - const maskedFirstByte = (parseInt(hash.slice(0, 2), 16) & 0x0f).toString(16).padStart(2, "0"); - const maskedHash = maskedFirstByte + hash.slice(2); - const scalar = BigInt(`0x${maskedHash}`) % BN254_PRIME; - return scalar.toString(); -}; - -// ── witness() ─────────────────────────────────────────────────────────────── - -/** - * Build a CircuitWitness for the role-spend-limit-v1 circuit. - */ -export const witness = ( - gate: PaymentGate, - commitOutput: CommitOutput, -): CircuitWitness => { - const roleHash = fieldHash(gate.role); - const spendLimit = commitOutput.normalized.financial.spendLimit ?? "0"; - const saltScalar = BigInt(commitOutput.salt).toString(); - const nowSec = Math.floor(Date.now() / 1000).toString(); - - // Simple poseidon-like combination (mock) - const roleGateCommitment = fieldHash( - commitOutput.root + roleHash + spendLimit + saltScalar, - ); - - return { - credentialCommitment: commitOutput.root, - roleHash, - spendLimit, - salt: saltScalar, - requiredRoleHash: roleHash, - maxSpend: gate.maxSpend.toString(), - nowSec, - roleGateCommitment, - credentialCommitmentPublic: commitOutput.root, - }; -}; - -// ── prove() ───────────────────────────────────────────────────────────────── - -/** - * Generate a role proof. - * Mock implementation for testing. - */ -export const prove = async ( - _client: unknown, - circuitWitness: CircuitWitness, -): Promise => { - // Mock proof generation - const proofBytes = Array.from(crypto.getRandomValues(new Uint8Array(128))) - .map((b) => b.toString(16).padStart(2, "0")) - .join(""); - - return { - proof: proofBytes, - inputs: Object.values(circuitWitness), - }; -}; - -// ── submit() ─────────────────────────────────────────────────────────────── - -/** - * Submit a role proof. - * Mock implementation for testing. - */ -export const submit = async ( - _client: unknown, - _docHash: string, - _proofResult: ProveOutput, - _chainId?: number, -): Promise => { - return { txHash: "0x" + "00".repeat(32) }; -}; - -// ── Client factory ───────────────────────────────────────────────────────── - -export const connect = (apiBase: string) => (apiKey: string) => ({ - apiBase, - apiKey, -}); diff --git a/packages/worker/package.json b/packages/worker/package.json index 9301a3d..83ff094 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -15,6 +15,8 @@ "@lemmaoracle/sdk": "^0.0.21", "@lemmaoracle/spec": "^0.0.21", "@lemmaoracle/x402": "^0.1.6", + "@trust402/identity": "workspace:*", + "@trust402/protocol": "workspace:*", "hono": "^4.12.15", "ramda": "^0.30.0" }, diff --git a/packages/worker/src/kyc/index.ts b/packages/worker/src/kyc/index.ts index 5888e3d..8a1387d 100644 --- a/packages/worker/src/kyc/index.ts +++ b/packages/worker/src/kyc/index.ts @@ -3,8 +3,10 @@ */ export { + IDENTITY_ARTIFACT_HEADER, IDENTITY_PROOF_HEADER, CREDENTIAL_HEADER, + extractIdentityArtifact, extractIdentityProof, extractCredential, hasRole, diff --git a/packages/worker/src/kyc/verify.test.ts b/packages/worker/src/kyc/verify.test.ts index 49be464..104f371 100644 --- a/packages/worker/src/kyc/verify.test.ts +++ b/packages/worker/src/kyc/verify.test.ts @@ -12,9 +12,11 @@ import { isRevoked, KycGates, extractCredential, + extractIdentityArtifact, type KycVerificationResult, } from "./verify.js"; import type { AgentCredential } from "@lemmaoracle/agent"; +import type { IdentityArtifact } from "@trust402/protocol"; // --------------------------------------------------------------------------- // Test fixtures @@ -44,6 +46,62 @@ const createCredential = ( ...overrides, }); +const createIdentityArtifact = ( + credential: AgentCredential = createCredential(), + overrides: Partial = {}, +): IdentityArtifact => ({ + commitOutput: { + root: "1234567890123456789012345678901234567890123456789012345678901234", + salt: "0".repeat(64), + sectionHashes: { + identityHash: "a".repeat(64), + authorityHash: "b".repeat(64), + financialHash: "c".repeat(64), + lifecycleHash: "d".repeat(64), + provenanceHash: "e".repeat(64), + }, + normalized: { + schema: "agent-identity-authority-v1", + identity: { + agentId: credential.identity.agentId, + subjectId: credential.identity.subjectId, + controllerId: credential.identity.controllerId ?? "", + orgId: credential.identity.orgId ?? "", + }, + authority: { + roles: credential.authority.roles.map((r) => r.name).join(","), + scopes: credential.authority.scopes.map((s) => s.name).join(","), + permissions: credential.authority.permissions.map((p) => `${p.resource}:${p.action}`).join(","), + }, + financial: { + spendLimit: "1000", + currency: "USDC", + paymentPolicy: "", + }, + lifecycle: { + issuedAt: String(credential.lifecycle.issuedAt), + expiresAt: String(credential.lifecycle.expiresAt ?? 0), + revoked: "false", + revocationRef: "", + }, + provenance: { + issuerId: credential.provenance.issuerId, + sourceSystem: credential.provenance.sourceSystem ?? "", + generatorId: credential.provenance.generatorId ?? "", + chainId: "84532", + network: "base-sepolia", + }, + }, + }, + identityProof: { + proof: "test-proof", + inputs: ["input1", "input2"], + }, + docHash: "test-doc-hash", + credential, + ...overrides, +}); + // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- @@ -296,7 +354,7 @@ describe("KYC Verification", () => { it("should support requireAll: false for any-one semantics", () => { const cred = createCredential({ authority: { - roles: [{ name: "aml-cleared" }], // Has one of three required roles + roles: [{ name: "aml-cleared" }], scopes: [], permissions: [], }, @@ -332,8 +390,53 @@ describe("KYC Verification", () => { }); }); + describe("extractIdentityArtifact", () => { + it("should extract valid IdentityArtifact from X-PAYMENT-IDENTITY header", () => { + const credential = createCredential(); + const artifact = createIdentityArtifact(credential); + const encoded = btoa(JSON.stringify(artifact)); + const headers = { "X-PAYMENT-IDENTITY": encoded }; + + const result = extractIdentityArtifact(headers); + + expect(result).not.toBeNull(); + expect(result?.docHash).toBe("test-doc-hash"); + expect(result?.credential.identity.agentId).toBe("agent-001"); + }); + + it("should return null for missing header", () => { + const headers = {}; + const result = extractIdentityArtifact(headers); + expect(result).toBeNull(); + }); + + it("should return null for invalid base64", () => { + const headers = { "X-PAYMENT-IDENTITY": "not-valid-base64!!!" }; + const result = extractIdentityArtifact(headers); + expect(result).toBeNull(); + }); + + it("should return null for invalid JSON", () => { + const headers = { "X-PAYMENT-IDENTITY": btoa("not json") }; + const result = extractIdentityArtifact(headers); + expect(result).toBeNull(); + }); + }); + describe("extractCredential", () => { - it("should extract valid credential from headers", () => { + it("should extract credential from X-PAYMENT-IDENTITY header", () => { + const credential = createCredential(); + const artifact = createIdentityArtifact(credential); + const encoded = btoa(JSON.stringify(artifact)); + const headers = { "X-PAYMENT-IDENTITY": encoded }; + + const result = extractCredential(headers); + + expect(result).not.toBeNull(); + expect(result?.identity.agentId).toBe("agent-001"); + }); + + it("should fallback to X-Lemma-Credential header for backward compatibility", () => { const cred = createCredential(); const encoded = btoa(JSON.stringify(cred)); const headers = { "X-Lemma-Credential": encoded }; @@ -344,8 +447,28 @@ describe("KYC Verification", () => { expect(result?.identity.agentId).toBe("agent-001"); }); - it("should return null for missing header", () => { - const headers = {}; + it("should prefer X-PAYMENT-IDENTITY over X-Lemma-Credential", () => { + const cred1 = createCredential({ + identity: { agentId: "agent-001", subjectId: "subject-001" }, + }); + const cred2 = createCredential({ + identity: { agentId: "agent-002", subjectId: "subject-002" }, + }); + + const artifact = createIdentityArtifact(cred1); + const headers = { + "X-PAYMENT-IDENTITY": btoa(JSON.stringify(artifact)), + "X-Lemma-Credential": btoa(JSON.stringify(cred2)), + }; + + const result = extractCredential(headers); + + // Should use X-PAYMENT-IDENTITY + expect(result?.identity.agentId).toBe("agent-001"); + }); + + it("should return null for missing headers", () => { + const headers: Record = {}; const result = extractCredential(headers); expect(result).toBeNull(); }); diff --git a/packages/worker/src/kyc/verify.ts b/packages/worker/src/kyc/verify.ts index 8e8e537..3170f2b 100644 --- a/packages/worker/src/kyc/verify.ts +++ b/packages/worker/src/kyc/verify.ts @@ -2,13 +2,16 @@ * KYC verification utilities for worker-side enforcement. * * Verifies identity proofs from x402 payment headers and checks KYC attributes. + * Reads from X-PAYMENT-IDENTITY header (Base64-encoded JSON IdentityArtifact). */ import * as R from "ramda"; +import type { AgentCredential } from "@lemmaoracle/agent"; +import type { IdentityArtifact } from "@trust402/protocol"; // ── Types (defined locally to avoid cross-package imports) ───────────────────── -type KycVerificationResult = Readonly<{ +export type KycVerificationResult = Readonly<{ verified: boolean; agentId?: string; roles: ReadonlyArray; @@ -18,53 +21,67 @@ type KycVerificationResult = Readonly<{ reason?: string; }>; -type KycGateConfig = Readonly<{ +export type KycGateConfig = Readonly<{ requiredRoles?: ReadonlyArray; requiredScopes?: ReadonlyArray; requiredPermissions?: ReadonlyArray<{ resource: string; action: string }>; requireAll?: boolean; }>; -// Import AgentCredential type -import type { AgentCredential } from "@lemmaoracle/agent"; - -// Re-export types -export type { KycVerificationResult, KycGateConfig }; - -// ── Proof Header Extraction ─────────────────────────────────────────────── +// ── Constants ───────────────────────────────────────────────────────────── /** - * Header name for identity proof in x402 payment. + * Header name for identity artifact in x402 payment. + * Contains Base64-encoded JSON IdentityArtifact. */ -export const IDENTITY_PROOF_HEADER = "X-Lemma-Identity-Proof"; +export const IDENTITY_ARTIFACT_HEADER = "X-PAYMENT-IDENTITY"; /** - * Header name for credential in x402 payment. + * Legacy header names (for backward compatibility). */ +export const IDENTITY_PROOF_HEADER = "X-Lemma-Identity-Proof"; export const CREDENTIAL_HEADER = "X-Lemma-Credential"; +// ── Header Extraction ─────────────────────────────────────────────────────── + /** - * Extract identity proof from payment headers. + * Extract IdentityArtifact from X-PAYMENT-IDENTITY header. + * + * The header contains Base64-encoded JSON with structure: + * { + * commitOutput: { ... }, + * identityProof: { proof: string, inputs: string[] }, + * docHash: string, + * credential: AgentCredential + * } */ -export const extractIdentityProof = ( +export const extractIdentityArtifact = ( headers: Record, -): Readonly> | null => { - const proofHeader = headers[IDENTITY_PROOF_HEADER]; - if (!proofHeader) return null; +): IdentityArtifact | null => { + const artifactHeader = headers[IDENTITY_ARTIFACT_HEADER]; + if (!artifactHeader) return null; try { - return JSON.parse(atob(proofHeader)) as Record; + return JSON.parse(atob(artifactHeader)) as IdentityArtifact; } catch { return null; } }; /** - * Extract credential from payment headers. + * Extract credential from X-PAYMENT-IDENTITY header. + * This is the primary method for KYC verification. */ export const extractCredential = ( headers: Record, ): AgentCredential | null => { + // Primary path: extract from X-PAYMENT-IDENTITY header + const artifact = extractIdentityArtifact(headers); + if (artifact) { + return artifact.credential; + } + + // Fallback: legacy X-Lemma-Credential header (for backward compatibility) const credHeader = headers[CREDENTIAL_HEADER]; if (!credHeader) return null; @@ -75,6 +92,30 @@ export const extractCredential = ( } }; +/** + * Extract identity proof from payment headers. + * Returns the identityProof from the IdentityArtifact. + */ +export const extractIdentityProof = ( + headers: Record, +): Readonly> | null => { + // Primary path: extract from X-PAYMENT-IDENTITY header + const artifact = extractIdentityArtifact(headers); + if (artifact) { + return artifact.identityProof as unknown as Record; + } + + // Fallback: legacy X-Lemma-Identity-Proof header + const proofHeader = headers[IDENTITY_PROOF_HEADER]; + if (!proofHeader) return null; + + try { + return JSON.parse(atob(proofHeader)) as Record; + } catch { + return null; + } +}; + // ── KYC Verification ────────────────────────────────────────────────────── /** diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1988999..16dd8da 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,6 +48,15 @@ importers: '@lemmaoracle/sdk': specifier: ^0.0.23 version: 0.0.23 + '@trust402/identity': + specifier: workspace:* + version: link:../../vendors/trust402/packages/identity + '@trust402/protocol': + specifier: workspace:* + version: link:../../vendors/trust402/packages/protocol + '@trust402/roles': + specifier: workspace:* + version: link:../../vendors/trust402/packages/roles '@x402/core': specifier: ^2.10.0 version: 2.10.0 @@ -135,6 +144,12 @@ importers: '@lemmaoracle/x402': specifier: ^0.1.6 version: 0.1.6(@lemmaoracle/sdk@0.0.21)(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(typescript@5.9.3)(utf-8-validate@5.0.10) + '@trust402/identity': + specifier: workspace:* + version: link:../../vendors/trust402/packages/identity + '@trust402/protocol': + specifier: workspace:* + version: link:../../vendors/trust402/packages/protocol hono: specifier: ^4.12.15 version: 4.12.15 @@ -158,6 +173,224 @@ importers: specifier: ^4.85.0 version: 4.85.0(@cloudflare/workers-types@4.20260426.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + vendors/trust402/packages/cli: + dependencies: + '@lemmaoracle/agent': + specifier: ^0.0.24-alpha.1 + version: 0.0.24-alpha.1 + '@lemmaoracle/sdk': + specifier: ^0.0.23 + version: 0.0.23 + '@lemmaoracle/spec': + specifier: ^0.0.22 + version: 0.0.22 + '@trust402/identity': + specifier: workspace:* + version: link:../identity + commander: + specifier: ^13.0.0 + version: 13.1.0 + ramda: + specifier: ^0.30.0 + version: 0.30.1 + devDependencies: + '@eslint/js': + specifier: ^9.0.0 + version: 9.39.4 + '@types/node': + specifier: ^22.0.0 + version: 22.19.17 + '@types/ramda': + specifier: ^0.30.0 + version: 0.30.2 + eslint: + specifier: ^9.0.0 + version: 9.39.4 + eslint-config-prettier: + specifier: ^10.0.0 + version: 10.1.8(eslint@9.39.4) + eslint-plugin-functional: + specifier: ^7.0.0 + version: 7.3.1(eslint@9.39.4)(typescript@5.9.3) + tsx: + specifier: ^4.19.0 + version: 4.21.0 + typescript: + specifier: ^5.8.0 + version: 5.9.3 + typescript-eslint: + specifier: ^8.0.0 + version: 8.59.2(eslint@9.39.4)(typescript@5.9.3) + vitest: + specifier: ^3.0.0 + version: 3.2.4(@types/node@22.19.17)(tsx@4.21.0) + + vendors/trust402/packages/demo: + dependencies: + poseidon-lite: + specifier: ^0.3.0 + version: 0.3.0 + devDependencies: + '@lemmaoracle/sdk': + specifier: ^0.0.23 + version: 0.0.23 + '@lemmaoracle/spec': + specifier: ^0.0.22 + version: 0.0.22 + '@types/ramda': + specifier: ^0.30.0 + version: 0.30.2 + dotenv: + specifier: ^16.4.7 + version: 16.6.1 + ramda: + specifier: ^0.30.0 + version: 0.30.1 + tsx: + specifier: ^4.19.0 + version: 4.21.0 + typescript: + specifier: ^5.8.0 + version: 5.9.3 + + vendors/trust402/packages/identity: + dependencies: + '@lemmaoracle/agent': + specifier: ^0.0.24-alpha.1 + version: 0.0.24-alpha.1 + '@lemmaoracle/sdk': + specifier: ^0.0.23 + version: 0.0.23 + '@lemmaoracle/spec': + specifier: ^0.0.22 + version: 0.0.22 + devDependencies: + '@eslint/js': + specifier: ^9.0.0 + version: 9.39.4 + '@types/node': + specifier: ^22.0.0 + version: 22.19.17 + eslint: + specifier: ^9.0.0 + version: 9.39.4 + eslint-config-prettier: + specifier: ^10.0.0 + version: 10.1.8(eslint@9.39.4) + eslint-plugin-functional: + specifier: ^7.0.0 + version: 7.3.1(eslint@9.39.4)(typescript@5.9.3) + tsx: + specifier: ^4.19.0 + version: 4.21.0 + typescript: + specifier: ^5.8.0 + version: 5.9.3 + typescript-eslint: + specifier: ^8.0.0 + version: 8.59.2(eslint@9.39.4)(typescript@5.9.3) + vitest: + specifier: ^3.0.0 + version: 3.2.4(@types/node@22.19.17)(tsx@4.21.0) + + vendors/trust402/packages/protocol: + dependencies: + '@lemmaoracle/agent': + specifier: ^0.0.24-alpha.1 + version: 0.0.24-alpha.1 + '@lemmaoracle/sdk': + specifier: ^0.0.23 + version: 0.0.23 + '@lemmaoracle/spec': + specifier: ^0.0.22 + version: 0.0.22 + '@trust402/identity': + specifier: workspace:* + version: link:../identity + '@trust402/roles': + specifier: workspace:* + version: link:../roles + ramda: + specifier: ^0.30.0 + version: 0.30.1 + devDependencies: + '@eslint/js': + specifier: ^9.0.0 + version: 9.39.4 + '@types/node': + specifier: ^22.0.0 + version: 22.19.17 + '@types/ramda': + specifier: ^0.30.0 + version: 0.30.2 + eslint: + specifier: ^9.0.0 + version: 9.39.4 + eslint-config-prettier: + specifier: ^10.0.0 + version: 10.1.8(eslint@9.39.4) + eslint-plugin-functional: + specifier: ^7.0.0 + version: 7.3.1(eslint@9.39.4)(typescript@5.9.3) + tsx: + specifier: ^4.19.0 + version: 4.21.0 + typescript: + specifier: ^5.8.0 + version: 5.9.3 + typescript-eslint: + specifier: ^8.0.0 + version: 8.59.2(eslint@9.39.4)(typescript@5.9.3) + vitest: + specifier: ^3.0.0 + version: 3.2.4(@types/node@22.19.17)(tsx@4.21.0) + + vendors/trust402/packages/roles: + dependencies: + '@lemmaoracle/agent': + specifier: ^0.0.24-alpha.1 + version: 0.0.24-alpha.1 + '@lemmaoracle/sdk': + specifier: ^0.0.23 + version: 0.0.23 + '@lemmaoracle/spec': + specifier: ^0.0.22 + version: 0.0.22 + devDependencies: + '@eslint/js': + specifier: ^9.0.0 + version: 9.39.4 + '@types/node': + specifier: ^22.0.0 + version: 22.19.17 + dotenv: + specifier: ^16.4.7 + version: 16.6.1 + eslint: + specifier: ^9.0.0 + version: 9.39.4 + eslint-config-prettier: + specifier: ^10.0.0 + version: 10.1.8(eslint@9.39.4) + eslint-plugin-functional: + specifier: ^7.0.0 + version: 7.3.1(eslint@9.39.4)(typescript@5.9.3) + poseidon-lite: + specifier: 0.3.0 + version: 0.3.0 + tsx: + specifier: ^4.19.0 + version: 4.21.0 + typescript: + specifier: ^5.8.0 + version: 5.9.3 + typescript-eslint: + specifier: ^8.0.0 + version: 8.59.2(eslint@9.39.4)(typescript@5.9.3) + vitest: + specifier: ^3.0.0 + version: 3.2.4(@types/node@22.19.17)(tsx@4.21.0) + packages: '@adraffy/ens-normalize@1.10.1': @@ -541,6 +774,64 @@ packages: cpu: [x64] os: [win32] + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.5': + resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.4': + resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + '@iden3/bigarray@0.0.2': resolution: {integrity: sha512-Xzdyxqm1bOFF6pdIsiHLLl3HkSLjbhqJHVyqaTxXt3RqXBEnmsUmEW47H7VOi/ak7TdkRpNkxjyK5Zbkm+y52g==} @@ -722,6 +1013,9 @@ packages: '@lemmaoracle/spec@0.0.21': resolution: {integrity: sha512-6jEORWeNZh4J2HD+IvjYkf0BpEPscbS6vxjY5JhX9Q6aTh716ZhmWAdMxPRFMfFHDlTkibSSnd/p6AYPCGFWow==} + '@lemmaoracle/spec@0.0.22': + resolution: {integrity: sha512-MDFoBjzEWZCYgCMWqYHzNn4BpI7dSDOhLHRQX+VVWkK9+9sg36cjU6jshzzuuGjS+ZCPCb28WjhQ8RqwPByevA==} + '@lemmaoracle/spec@0.0.23': resolution: {integrity: sha512-/46I22Y3M6+yTDA5Y0rIbuCNpEZ68IRQzZlLZQoJptHIxsoWigwtP2P8nuI4Pw4onGLeraAtw3oc+3OCKQALbg==} @@ -1318,6 +1612,9 @@ packages: '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/node@22.19.17': resolution: {integrity: sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==} @@ -1327,6 +1624,65 @@ packages: '@types/ramda@0.30.2': resolution: {integrity: sha512-PyzHvjCalm2BRYjAU6nIB3TprYwMNOUY/7P/N8bSzp9W/yM2YrtGtAnnVtaCNSeOZ8DzKyFDvaqQs7LnWwwmBA==} + '@typescript-eslint/eslint-plugin@8.59.2': + resolution: {integrity: sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.59.2 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.59.2': + resolution: {integrity: sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.59.2': + resolution: {integrity: sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.59.2': + resolution: {integrity: sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.59.2': + resolution: {integrity: sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.59.2': + resolution: {integrity: sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.59.2': + resolution: {integrity: sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.59.2': + resolution: {integrity: sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.59.2': + resolution: {integrity: sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.59.2': + resolution: {integrity: sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@vitest/expect@3.2.4': resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} @@ -1425,9 +1781,22 @@ packages: zod: optional: true + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + aes-js@4.0.0-beta.5: resolution: {integrity: sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==} + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + ajv@8.20.0: resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} @@ -1435,9 +1804,16 @@ packages: resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + apg-js@4.4.0: resolution: {integrity: sha512-fefmXFknJmtgtNEXfPwZKYkMFX4Fyeyz+fNF6JWp87biGOPslJbCBVU158zvKRZfHBKnJDy8CMM40oLFGkXT8Q==} + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -1463,6 +1839,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + bfj@7.1.0: resolution: {integrity: sha512-I6MMLkn+anzNdCUp9hMRyui1HaNEUCco50lxbvNS4+EyXg8lN3nJ48PjPWtbH8UVS9CuMoaKE9U2V3l29DaRQw==} engines: {node: '>= 8.0.0'} @@ -1473,9 +1853,16 @@ packages: bluebird@3.7.2: resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} + brace-expansion@1.1.14: + resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} + brace-expansion@2.1.0: resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} + brace-expansion@5.0.5: + resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} + engines: {node: 18 || 20 || >=22} + browser-process-hrtime@1.0.0: resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==} @@ -1502,10 +1889,18 @@ packages: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + chai@5.3.3: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + chalk@5.6.2: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} @@ -1540,14 +1935,28 @@ packages: resolution: {integrity: sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==} engines: {node: '>=18.20'} + color-convert@2.0.1: + 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==} + combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + commander@13.1.0: + resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} + engines: {node: '>=18'} + commander@14.0.2: resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==} engines: {node: '>=20'} + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + cookie@1.1.1: resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} engines: {node: '>=18'} @@ -1572,6 +1981,13 @@ packages: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge-ts@7.1.5: + resolution: {integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==} + engines: {node: '>=16.0.0'} + define-data-property@1.1.4: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} @@ -1641,11 +2057,66 @@ packages: engines: {node: '>=18'} hasBin: true + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + escodegen@2.1.0: resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} engines: {node: '>=6.0'} hasBin: true + eslint-config-prettier@10.1.8: + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-plugin-functional@7.3.1: + resolution: {integrity: sha512-f7pa83aVW20RCu5k9xNE929QQ1N+yOt1csoHcEhkG4HLoERk7grroRxK8qginCSS4EOq0KcdyqpB1cHKQTxNQQ==} + engines: {node: '>=v18.18.0'} + deprecated: wrong semantic version number + peerDependencies: + eslint: ^9.0.0 + typescript: '>=4.7.4' + peerDependenciesMeta: + typescript: + optional: true + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@9.39.4: + resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + esprima@1.2.5: resolution: {integrity: sha512-S9VbPDU0adFErpDai3qDkjq8+G05ONtKzcyNrPKg/ZKa+tf879nX2KexNU95b31UoTJjRLInNBHHHjFPoCd7lQ==} engines: {node: '>=0.4.0'} @@ -1656,6 +2127,14 @@ packages: engines: {node: '>=4'} hasBin: true + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + estraverse@5.3.0: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} @@ -1681,6 +2160,12 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} @@ -1702,9 +2187,24 @@ packages: ffjavascript@0.3.1: resolution: {integrity: sha512-4PbK1WYodQtuF47D4pRI5KUg3Q392vuP5WjE1THSnceHdXwU3ijaoS0OqxTzLknCtz4Z2TtABzkBdBdMn3B/Aw==} + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + filelist@1.0.6: resolution: {integrity: sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==} + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + follow-redirects@1.16.0: resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} engines: {node: '>=4.0'} @@ -1745,10 +2245,22 @@ packages: get-tsconfig@4.14.0: resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + has-property-descriptors@1.0.2: resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} @@ -1772,6 +2284,22 @@ packages: resolution: {integrity: sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==} engines: {node: '>= 6.0.0'} + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + is-buffer@1.1.6: resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} @@ -1779,6 +2307,20 @@ packages: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-immutable-type@5.0.1: + resolution: {integrity: sha512-LkHEOGVZZXxGl8vDs+10k3DvP++SEoYEAJLRk6buTFi6kD7QekThV7xHS0j6gpnUCQ0zpud/gMDGiV4dQneLTg==} + peerDependencies: + eslint: '*' + typescript: '>=4.7.4' + is-interactive@2.0.0: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} engines: {node: '>=12'} @@ -1817,16 +2359,43 @@ packages: js-tokens@9.0.1: resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + jsonpath@1.3.0: resolution: {integrity: sha512-0kjkYHJBkAy50Z5QzArZ7udmvxrJzkpKYW27fiF//BrMY7TQibYLl+FYIXN2BiYmwMIVzSfD8aDRj6IzgBX2/w==} + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + kleur@4.1.5: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + log-symbols@7.0.1: resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} engines: {node: '>=18'} @@ -1864,6 +2433,13 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + minimatch@5.1.9: resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} engines: {node: '>=10'} @@ -1879,6 +2455,9 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + node-gyp-build@4.8.4: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true @@ -1887,6 +2466,10 @@ packages: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + ora@9.4.0: resolution: {integrity: sha512-84cglkRILFxdtA8hAvLNdMrtBpPNBTrQ9/ulg0FA7xLMnD6mifv+enAIeRmvtv+WgdCE+LPGOfQmtJRrVaIVhQ==} engines: {node: '>=20'} @@ -1899,9 +2482,25 @@ packages: typescript: optional: true + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -1937,9 +2536,17 @@ packages: resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} engines: {node: ^10 || ^12 || >=14} + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + r1csfile@0.0.48: resolution: {integrity: sha512-kHRkKUJNaor31l05f2+RFzvcH5XSa7OfEfd/l4hzjte6NL6fjRkSMfZ4BjySW9wmfdwPOtq3mXurzPvPGEf5Tw==} @@ -1956,6 +2563,10 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} @@ -2032,6 +2643,10 @@ packages: resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + strip-literal@3.1.0: resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} @@ -2039,6 +2654,10 @@ packages: resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} engines: {node: '>=18'} + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -2064,6 +2683,17 @@ packages: tryer@1.0.1: resolution: {integrity: sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==} + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + ts-declaration-location@1.0.7: + resolution: {integrity: sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==} + peerDependencies: + typescript: '>=4.0.0' + ts-toolbelt@9.6.0: resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==} @@ -2081,9 +2711,20 @@ packages: tweetnacl@1.0.3: resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + types-ramda@0.30.1: resolution: {integrity: sha512-1HTsf5/QVRmLzcGfldPFvkVsAdi1db1BBKzi7iW3KBUlOICg/nKnFS+jGqDJS3YD8VsWbAh7JiHeBvbsw8RPxA==} + typescript-eslint@8.59.2: + resolution: {integrity: sha512-pJw051uomb3ZeCzGTpRb8RbEqB5Y4WWet8gl/GcTlU35BSx0PVdZ86/bqkQCyKKuraVQEK7r6kBHQXF+fBhkoQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} @@ -2111,6 +2752,9 @@ packages: unenv@2.0.0-rc.24: resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + utf-8-validate@5.0.10: resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==} engines: {node: '>=6.14.2'} @@ -2219,6 +2863,10 @@ packages: engines: {node: '>=8'} hasBin: true + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + workerd@1.20260424.1: resolution: {integrity: sha512-oKsB0Xo/mfkYMdSACoS06XZg09VUK4rXwHfF/1t3P++sMbwzf4UHQvMO57+zxpEB2nVrY/ZkW0bYFGq4GdAFSQ==} engines: {node: '>=16'} @@ -2282,6 +2930,10 @@ packages: utf-8-validate: optional: true + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + yoctocolors@2.1.2: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} @@ -2530,6 +3182,68 @@ snapshots: '@esbuild/win32-x64@0.27.7': optional: true + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4)': + dependencies: + eslint: 9.39.4 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.2': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.5': + dependencies: + ajv: 6.15.0 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.4': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + '@iden3/bigarray@0.0.2': {} '@iden3/binfileutils@0.0.12': @@ -2673,6 +3387,8 @@ snapshots: '@lemmaoracle/spec@0.0.21': {} + '@lemmaoracle/spec@0.0.22': {} + '@lemmaoracle/spec@0.0.23': {} '@lemmaoracle/x402@0.1.6(@lemmaoracle/sdk@0.0.21)(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(typescript@5.9.3)(utf-8-validate@5.0.10)': @@ -3285,6 +4001,8 @@ snapshots: '@types/estree@1.0.9': {} + '@types/json-schema@7.0.15': {} + '@types/node@22.19.17': dependencies: undici-types: 6.21.0 @@ -3298,6 +4016,97 @@ snapshots: dependencies: types-ramda: 0.30.1 + '@typescript-eslint/eslint-plugin@8.59.2(@typescript-eslint/parser@8.59.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.59.2(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.59.2 + '@typescript-eslint/type-utils': 8.59.2(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/utils': 8.59.2(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.59.2 + eslint: 9.39.4 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.59.2(eslint@9.39.4)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.59.2 + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/typescript-estree': 8.59.2(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.59.2 + debug: 4.4.3 + eslint: 9.39.4 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.59.2(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.59.2(typescript@5.9.3) + '@typescript-eslint/types': 8.59.2 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.59.2': + dependencies: + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/visitor-keys': 8.59.2 + + '@typescript-eslint/tsconfig-utils@8.59.2(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.59.2(eslint@9.39.4)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/typescript-estree': 8.59.2(typescript@5.9.3) + '@typescript-eslint/utils': 8.59.2(eslint@9.39.4)(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.4 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.59.2': {} + + '@typescript-eslint/typescript-estree@8.59.2(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.59.2(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.59.2(typescript@5.9.3) + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/visitor-keys': 8.59.2 + debug: 4.4.3 + minimatch: 10.2.5 + semver: 7.7.4 + tinyglobby: 0.2.16 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.59.2(eslint@9.39.4)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) + '@typescript-eslint/scope-manager': 8.59.2 + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/typescript-estree': 8.59.2(typescript@5.9.3) + eslint: 9.39.4 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.59.2': + dependencies: + '@typescript-eslint/types': 8.59.2 + eslint-visitor-keys: 5.0.1 + '@vitest/expect@3.2.4': dependencies: '@types/chai': 5.2.3 @@ -3468,9 +4277,22 @@ snapshots: typescript: 5.9.3 zod: 4.3.6 + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + aes-js@4.0.0-beta.5: optional: true + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 @@ -3480,8 +4302,14 @@ snapshots: ansi-regex@6.2.2: {} + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + apg-js@4.4.0: {} + argparse@2.0.1: {} + assertion-error@2.0.1: {} async@3.2.6: {} @@ -3507,6 +4335,8 @@ snapshots: balanced-match@1.0.2: {} + balanced-match@4.0.4: {} + bfj@7.1.0: dependencies: bluebird: 3.7.2 @@ -3519,10 +4349,19 @@ snapshots: bluebird@3.7.2: {} + brace-expansion@1.1.14: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + brace-expansion@2.1.0: dependencies: balanced-match: 1.0.2 + brace-expansion@5.0.5: + dependencies: + balanced-match: 4.0.4 + browser-process-hrtime@1.0.0: {} buffer-es6@4.9.3: {} @@ -3551,6 +4390,8 @@ snapshots: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 + callsites@3.1.0: {} + chai@5.3.3: dependencies: assertion-error: 2.0.1 @@ -3559,6 +4400,11 @@ snapshots: loupe: 3.2.1 pathval: 2.0.1 + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + chalk@5.6.2: {} charenc@0.0.2: {} @@ -3585,12 +4431,22 @@ snapshots: cli-spinners@3.4.0: {} + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 + commander@13.1.0: {} + commander@14.0.2: {} + concat-map@0.0.1: {} + cookie@1.1.1: {} cross-spawn@7.0.6: @@ -3607,6 +4463,10 @@ snapshots: deep-eql@5.0.2: {} + deep-is@0.1.4: {} + + deepmerge-ts@7.1.5: {} + define-data-property@1.1.4: dependencies: es-define-property: 1.0.1 @@ -3719,6 +4579,10 @@ snapshots: '@esbuild/win32-ia32': 0.27.7 '@esbuild/win32-x64': 0.27.7 + escape-string-regexp@4.0.0: {} + + escape-string-regexp@5.0.0: {} + escodegen@2.1.0: dependencies: esprima: 4.0.1 @@ -3727,10 +4591,92 @@ snapshots: optionalDependencies: source-map: 0.6.1 + eslint-config-prettier@10.1.8(eslint@9.39.4): + dependencies: + eslint: 9.39.4 + + eslint-plugin-functional@7.3.1(eslint@9.39.4)(typescript@5.9.3): + dependencies: + '@typescript-eslint/utils': 8.59.2(eslint@9.39.4)(typescript@5.9.3) + deepmerge-ts: 7.1.5 + escape-string-regexp: 5.0.0 + eslint: 9.39.4 + is-immutable-type: 5.0.1(eslint@9.39.4)(typescript@5.9.3) + ts-api-utils: 2.5.0(typescript@5.9.3) + ts-declaration-location: 1.0.7(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@9.39.4: + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.5 + '@eslint/js': 9.39.4 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 4.2.1 + esprima@1.2.5: {} esprima@4.0.1: {} + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + estraverse@5.3.0: {} estree-walker@3.0.3: @@ -3759,6 +4705,10 @@ snapshots: fast-deep-equal@3.1.3: {} + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + fast-uri@3.1.0: {} fastfile@0.0.20: {} @@ -3779,10 +4729,26 @@ snapshots: wasmcurves: 0.2.2 web-worker: 1.2.0 + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + filelist@1.0.6: dependencies: minimatch: 5.1.9 + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + + flatted@3.4.2: {} + follow-redirects@1.16.0: {} for-each@0.3.5: @@ -3826,8 +4792,16 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + gopd@1.2.0: {} + has-flag@4.0.0: {} + has-property-descriptors@1.0.2: dependencies: es-define-property: 1.0.1 @@ -3846,10 +4820,37 @@ snapshots: hoopy@0.1.4: {} + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + is-buffer@1.1.6: {} is-callable@1.2.7: {} + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-immutable-type@5.0.1(eslint@9.39.4)(typescript@5.9.3): + dependencies: + '@typescript-eslint/type-utils': 8.59.2(eslint@9.39.4)(typescript@5.9.3) + eslint: 9.39.4 + ts-api-utils: 2.5.0(typescript@5.9.3) + ts-declaration-location: 1.0.7(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + is-interactive@2.0.0: {} is-retry-allowed@2.2.0: {} @@ -3878,16 +4879,41 @@ snapshots: js-tokens@9.0.1: {} + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} + jsonpath@1.3.0: dependencies: esprima: 1.2.5 static-eval: 2.1.1 underscore: 1.13.6 + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + kleur@4.1.5: {} + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + log-symbols@7.0.1: dependencies: is-unicode-supported: 2.1.0 @@ -3929,6 +4955,14 @@ snapshots: - bufferutil - utf-8-validate + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.5 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.14 + minimatch@5.1.9: dependencies: brace-expansion: 2.1.0 @@ -3939,6 +4973,8 @@ snapshots: nanoid@3.3.12: {} + natural-compare@1.4.0: {} + node-gyp-build@4.8.4: optional: true @@ -3946,6 +4982,15 @@ snapshots: dependencies: mimic-function: 5.0.1 + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + ora@9.4.0: dependencies: chalk: 5.6.2 @@ -3987,8 +5032,22 @@ snapshots: transitivePeerDependencies: - zod + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + path-browserify@1.0.1: {} + path-exists@4.0.0: {} + path-key@3.1.1: {} path-to-regexp@6.3.0: {} @@ -4013,8 +5072,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + prelude-ls@1.2.1: {} + proxy-from-env@1.1.0: {} + punycode@2.3.1: {} + r1csfile@0.0.48: dependencies: '@iden3/bigarray': 0.0.2 @@ -4035,6 +5098,8 @@ snapshots: require-from-string@2.0.2: {} + resolve-from@4.0.0: {} + resolve-pkg-maps@1.0.0: {} restore-cursor@5.1.0: @@ -4163,12 +5228,18 @@ snapshots: dependencies: ansi-regex: 6.2.2 + strip-json-comments@3.1.1: {} + strip-literal@3.1.0: dependencies: js-tokens: 9.0.1 supports-color@10.2.2: {} + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + tinybench@2.9.0: {} tinyexec@0.3.2: {} @@ -4186,6 +5257,15 @@ snapshots: tryer@1.0.1: {} + ts-api-utils@2.5.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + ts-declaration-location@1.0.7(typescript@5.9.3): + dependencies: + picomatch: 4.0.4 + typescript: 5.9.3 + ts-toolbelt@9.6.0: {} tslib@2.7.0: @@ -4203,10 +5283,25 @@ snapshots: tweetnacl@1.0.3: {} + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + types-ramda@0.30.1: dependencies: ts-toolbelt: 9.6.0 + typescript-eslint@8.59.2(eslint@9.39.4)(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.59.2(@typescript-eslint/parser@8.59.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/parser': 8.59.2(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.59.2(typescript@5.9.3) + '@typescript-eslint/utils': 8.59.2(eslint@9.39.4)(typescript@5.9.3) + eslint: 9.39.4 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + typescript@5.9.3: {} uncrypto@0.1.3: {} @@ -4226,6 +5321,10 @@ snapshots: dependencies: pathe: 2.0.3 + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + utf-8-validate@5.0.10: dependencies: node-gyp-build: 4.8.4 @@ -4367,6 +5466,8 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 + word-wrap@1.2.5: {} + workerd@1.20260424.1: optionalDependencies: '@cloudflare/workerd-darwin-64': 1.20260424.1 @@ -4413,6 +5514,8 @@ snapshots: bufferutil: 4.1.0 utf-8-validate: 5.0.10 + yocto-queue@0.1.0: {} + yoctocolors@2.1.2: {} youch-core@0.3.3: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index dee51e9..f822adc 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,2 +1,3 @@ packages: - "packages/*" + - "vendors/trust402/packages/*" diff --git a/vendors/trust402 b/vendors/trust402 new file mode 160000 index 0000000..a4e0797 --- /dev/null +++ b/vendors/trust402 @@ -0,0 +1 @@ +Subproject commit a4e07973e18702273c84f91ff7de0c9a591cebfe