diff --git a/packages/aiken-uplc/src/Evaluator.ts b/packages/aiken-uplc/src/Evaluator.ts index 76950b7..87d0cea 100644 --- a/packages/aiken-uplc/src/Evaluator.ts +++ b/packages/aiken-uplc/src/Evaluator.ts @@ -6,6 +6,7 @@ import * as Bytes from "@evolution-sdk/evolution/core/Bytes" import * as CBOR from "@evolution-sdk/evolution/core/CBOR" +import * as CostModel from "@evolution-sdk/evolution/core/CostModel" import * as Redeemer from "@evolution-sdk/evolution/core/Redeemer" import * as Script from "@evolution-sdk/evolution/core/Script" import * as ScriptRef from "@evolution-sdk/evolution/core/ScriptRef" @@ -154,11 +155,14 @@ export function makeEvaluator(wasmModule: WasmLoader.WasmModule): TransactionBui const { slotLength, zeroSlot, zeroTime } = context.slotConfig + // Encode cost models to CBOR bytes for WASM evaluator + const costModelsCBOR = CostModel.toCBOR(context.costModels) + yield* Effect.logDebug( `[Aiken UPLC] Slot config - zeroTime: ${zeroTime}, zeroSlot: ${zeroSlot}, slotLength: ${slotLength}` ) - yield* Effect.logDebug(`[Aiken UPLC] Cost models CBOR length: ${context.costModels.length} bytes`) - yield* Effect.logDebug(`[Aiken UPLC] Cost models hex: ${Bytes.toHex(context.costModels)}`) + yield* Effect.logDebug(`[Aiken UPLC] Cost models CBOR length: ${costModelsCBOR.length} bytes`) + yield* Effect.logDebug(`[Aiken UPLC] Cost models hex: ${Bytes.toHex(costModelsCBOR)}`) yield* Effect.logDebug( `[Aiken UPLC] Max execution - steps: ${context.maxTxExSteps}, mem: ${context.maxTxExMem}` ) @@ -173,7 +177,7 @@ export function makeEvaluator(wasmModule: WasmLoader.WasmModule): TransactionBui txBytes, utxosX, utxosY, - context.costModels, + costModelsCBOR, context.maxTxExSteps, context.maxTxExMem, BigInt(zeroTime), diff --git a/packages/evolution-devnet/package.json b/packages/evolution-devnet/package.json index b3b9596..4712a43 100644 --- a/packages/evolution-devnet/package.json +++ b/packages/evolution-devnet/package.json @@ -49,6 +49,7 @@ "peerDependencies": { "@evolution-sdk/aiken-uplc": "workspace:*", "@evolution-sdk/evolution": "workspace:*", + "@evolution-sdk/scalus-uplc": "workspace:*", "@effect/platform": "^0.90.10", "@effect/platform-node": "^0.96.1", "effect": "^3.19.3" diff --git a/packages/evolution-devnet/test/TxBuilder.Scripts.test.ts b/packages/evolution-devnet/test/TxBuilder.Scripts.test.ts index 24d2536..ac9de2e 100644 --- a/packages/evolution-devnet/test/TxBuilder.Scripts.test.ts +++ b/packages/evolution-devnet/test/TxBuilder.Scripts.test.ts @@ -9,6 +9,7 @@ import * as ScriptHash from "@evolution-sdk/evolution/core/ScriptHash" import type { TxBuilderConfig } from "@evolution-sdk/evolution/sdk/builders/TransactionBuilder" import { makeTxBuilder } from "@evolution-sdk/evolution/sdk/builders/TransactionBuilder" import { KupmiosProvider } from "@evolution-sdk/evolution/sdk/provider/Kupmios" +import { createScalusEvaluator } from "@evolution-sdk/scalus-uplc" import { Schema } from "effect" import * as Cluster from "../src/Cluster.js" @@ -264,11 +265,75 @@ describe("TxBuilder Script Handling", () => { const redeemer = tx.witnessSet.redeemers![0] expect(redeemer.tag).toBe("spend") - expect(redeemer.exUnits.mem).toBeGreaterThan(0n) // mem > 0 - expect(redeemer.exUnits.steps).toBeGreaterThan(0n) // steps > 0 - - // eslint-disable-next-line no-console - console.log(`✓ Aiken evaluator: mem=${redeemer.exUnits.mem}, steps=${redeemer.exUnits.steps}`) + expect(redeemer.exUnits.mem).toBe(1100n) + expect(redeemer.exUnits.steps).toBe(160100n) + }) + + it.fails("should build transaction with Scalus evaluator", async () => { + const alwaysSucceedsScript = makePlutusV2Script(ALWAYS_SUCCEED_SCRIPT_CBOR) + const scriptAddress = scriptToAddress(ALWAYS_SUCCEED_SCRIPT_CBOR) + + // Create script UTxO with inline datum + const ownerPubKeyHash = "00000000000000000000000000000000000000000000000000000000" + const datum = Data.toCBORHex(Data.constr(0n, [Data.bytearray(ownerPubKeyHash)])) + + const scriptUtxo = createCoreTestUtxo({ + transactionId: "a".repeat(64), + index: 0, + address: scriptAddress, + lovelace: 5_000_000n, + datumOption: { type: "inlineDatum", inline: datum } + }) + + // Create funding UTxO + const fundingUtxo = createCoreTestUtxo({ + transactionId: "b".repeat(64), + index: 0, + address: CHANGE_ADDRESS, + lovelace: 10_000_000n + }) + + // Create redeemer + const redeemerData = Data.constr(0n, [Data.bytearray("48656c6c6f2c20576f726c6421")]) + + const builder = makeTxBuilder(baseConfig) + .collectFrom({ + inputs: [scriptUtxo], + redeemer: redeemerData + }) + .attachScript({ script: alwaysSucceedsScript }) + .payToAddress({ + address: CoreAddress.fromBech32(RECEIVER_ADDRESS), + assets: CoreAssets.fromLovelace(2_000_000n) + }) + + // Build with Scalus evaluator and debug enabled + const signBuilder = await builder.build({ + changeAddress: CoreAddress.fromBech32(CHANGE_ADDRESS), + availableUtxos: [fundingUtxo], + protocolParameters: PROTOCOL_PARAMS, + evaluator: createScalusEvaluator, + debug: true // Enable debug logging + }) + + const tx = await signBuilder.toTransaction() + + // Verify transaction structure + expect(tx.body.inputs.length).toBe(1) + expect(tx.body.outputs.length).toBeGreaterThanOrEqual(2) // Payment + change + + // Verify script witnesses + expect(tx.witnessSet.plutusV2Scripts).toBeDefined() + expect(tx.witnessSet.plutusV2Scripts!.length).toBe(1) + + // Verify redeemers with evaluated exUnits + expect(tx.witnessSet.redeemers).toBeDefined() + expect(tx.witnessSet.redeemers!.length).toBe(1) + + const redeemer = tx.witnessSet.redeemers![0] + expect(redeemer.tag).toBe("spend") + expect(redeemer.exUnits.mem).toBe(1100n) + expect(redeemer.exUnits.steps).toBe(160100n) }) it("should handle collateral inputs with multiassets and return excess to user as collateral return", async () => { diff --git a/packages/evolution/src/sdk/builders/TransactionBuilder.ts b/packages/evolution/src/sdk/builders/TransactionBuilder.ts index 7672d18..f92c02b 100644 --- a/packages/evolution/src/sdk/builders/TransactionBuilder.ts +++ b/packages/evolution/src/sdk/builders/TransactionBuilder.ts @@ -33,6 +33,7 @@ import * as CoreAssets from "../../core/Assets/index.js" import type * as AuxiliaryData from "../../core/AuxiliaryData.js" import type * as Certificate from "../../core/Certificate.js" import type * as Coin from "../../core/Coin.js" +import type * as CostModel from "../../core/CostModel.js" import type * as PlutusData from "../../core/Data.js" import type * as KeyHash from "../../core/KeyHash.js" import type * as Mint from "../../core/Mint.js" @@ -687,7 +688,7 @@ export interface ChainResult { */ export interface EvaluationContext { /** Cost models for script evaluation */ - readonly costModels: Uint8Array + readonly costModels: CostModel.CostModels /** Maximum execution steps allowed */ readonly maxTxExSteps: bigint /** Maximum execution memory allowed */ @@ -763,7 +764,7 @@ export interface ScriptFailure { * @category errors */ export class EvaluationError extends Data.TaggedError("EvaluationError")<{ - readonly cause: unknown + readonly cause?: unknown readonly message?: string /** Parsed script failures with labels */ readonly failures?: ReadonlyArray diff --git a/packages/evolution/src/sdk/builders/phases/Evaluation.ts b/packages/evolution/src/sdk/builders/phases/Evaluation.ts index 20dd0a0..725bcd3 100644 --- a/packages/evolution/src/sdk/builders/phases/Evaluation.ts +++ b/packages/evolution/src/sdk/builders/phases/Evaluation.ts @@ -34,14 +34,14 @@ import { assembleTransaction, buildTransactionInputs } from "../TxBuilderImpl.js import type { PhaseResult } from "./Phases.js" /** - * Convert ProtocolParameters cost models to CBOR bytes for evaluation. + * Convert ProtocolParameters cost models to CostModels core type for evaluation. * * Takes the cost models from protocol parameters (Record format) - * and converts them to the CBOR-encoded format expected by UPLC evaluators. + * and converts them to the CostModels core type. */ -const costModelsToCBOR = ( +const buildCostModels = ( protocolParams: ProtocolParametersModule.ProtocolParameters -): Effect.Effect => +): Effect.Effect => Effect.gen(function* () { // Convert Record format to bigint arrays const plutusV1Costs = Object.values(protocolParams.costModels.PlutusV1).map((v) => BigInt(v)) @@ -51,22 +51,12 @@ const costModelsToCBOR = ( .filter((v) => v <= INT64_MAX) const plutusV3Costs = Object.values(protocolParams.costModels.PlutusV3).map((v) => BigInt(v)) - // Create CostModels instance - const costModels = new CostModel.CostModels({ + // Create and return CostModels instance + return new CostModel.CostModels({ PlutusV1: new CostModel.CostModel({ costs: plutusV1Costs }), PlutusV2: new CostModel.CostModel({ costs: plutusV2Costs }), PlutusV3: new CostModel.CostModel({ costs: plutusV3Costs }) }) - - // Encode to CBOR bytes - return yield* Effect.try({ - try: () => CostModel.toCBOR(costModels), - catch: (error) => - new TransactionBuilderError({ - message: "Failed to encode cost models to CBOR", - cause: error - }) - }) }) /** @@ -457,8 +447,8 @@ export const executeEvaluation = (): Effect.Effect< } // Step 6: Prepare evaluation context - // Encode cost models from full protocol parameters - const costModelsCBOR = yield* costModelsToCBOR(fullProtocolParams) + // Build cost models from full protocol parameters + const costModels = yield* buildCostModels(fullProtocolParams) // Get slot configuration from BuildOptions (resolved from network or explicit override) const slotConfig = buildOptions.slotConfig ?? { @@ -468,7 +458,7 @@ export const executeEvaluation = (): Effect.Effect< } const evaluationContext: EvaluationContext = { - costModels: costModelsCBOR, + costModels, maxTxExSteps: fullProtocolParams.maxTxExSteps, maxTxExMem: fullProtocolParams.maxTxExMem, slotConfig diff --git a/packages/scalus-uplc/package.json b/packages/scalus-uplc/package.json new file mode 100644 index 0000000..34d8608 --- /dev/null +++ b/packages/scalus-uplc/package.json @@ -0,0 +1,32 @@ +{ + "name": "@evolution-sdk/scalus-uplc", + "version": "0.0.1", + "description": "Scalus UPLC evaluator adapter for Evolution SDK", + "type": "module", + "exports": { + ".": { + "types": "./src/index.node.ts", + "node": "./src/index.node.ts", + "default": "./src/index.node.ts" + } + }, + "scripts": { + "build": "tsc -b tsconfig.build.json", + "dev": "tsc -b tsconfig.build.json --watch", + "type-check": "tsc --noEmit", + "test": "vitest run", + "clean": "rm -rf dist .turbo .tsbuildinfo" + }, + "dependencies": { + "effect": "^3.19.3", + "scalus": "^0.14.0" + }, + "peerDependencies": { + "@evolution-sdk/evolution": "workspace:*" + }, + "devDependencies": { + "typescript": "^5.9.2", + "@effect/vitest": "^0.19.3", + "vitest": "^3.2.4" + } +} diff --git a/packages/scalus-uplc/src/Evaluator.ts b/packages/scalus-uplc/src/Evaluator.ts new file mode 100644 index 0000000..917f0bc --- /dev/null +++ b/packages/scalus-uplc/src/Evaluator.ts @@ -0,0 +1,159 @@ +import * as Bytes from "@evolution-sdk/evolution/core/Bytes" +import * as CBOR from "@evolution-sdk/evolution/core/CBOR" +import type * as CostModel from "@evolution-sdk/evolution/core/CostModel" +import * as Script from "@evolution-sdk/evolution/core/Script" +import * as ScriptRef from "@evolution-sdk/evolution/core/ScriptRef" +import * as Transaction from "@evolution-sdk/evolution/core/Transaction" +import * as TransactionInput from "@evolution-sdk/evolution/core/TransactionInput" +import * as TxOut from "@evolution-sdk/evolution/core/TxOut" +import type * as UTxO from "@evolution-sdk/evolution/core/UTxO" +import * as TransactionBuilder from "@evolution-sdk/evolution/sdk/builders/TransactionBuilder" +import type * as EvalRedeemer from "@evolution-sdk/evolution/sdk/EvalRedeemer" +import { Effect, Schema } from "effect" +import * as Scalus from "scalus" + +/** + * Build CBOR-encoded map of TransactionInput → TransactionOutput from UTxOs. + * + * Uses FromCDDL schemas to get CBOR values directly, avoiding wasteful + * bytes → CBOR → bytes roundtrip encoding. + */ +function buildUtxoMapCBOR(utxos: ReadonlyArray): Uint8Array { + const utxoMap = new Map() + + for (const utxo of utxos) { + // Use FromCDDL to get CBOR values directly (no double encoding) + const txInput = new TransactionInput.TransactionInput({ + transactionId: utxo.transactionId, + index: utxo.index + }) + const inputCBOR = Schema.encodeSync(TransactionInput.FromCDDL)(txInput) + + const scriptRef = utxo.scriptRef ? new ScriptRef.ScriptRef({ bytes: Script.toCBOR(utxo.scriptRef) }) : undefined + const txOut = new TxOut.TransactionOutput({ + address: utxo.address, + assets: utxo.assets, + datumOption: utxo.datumOption, + scriptRef + }) + const outputCBOR = Schema.encodeSync(TxOut.FromCDDL)(txOut) + + utxoMap.set(inputCBOR, outputCBOR) + } + + return CBOR.toCBORBytes(utxoMap, CBOR.CML_DEFAULT_OPTIONS) +} + +function decodeCostModels(costModels: CostModel.CostModels): Array> { + // Scalus expects a flattened representation of the cost models as number arrays + const plutusV1 = costModels.PlutusV1.costs.map((c) => Number(c)) + const plutusV2 = costModels.PlutusV2.costs.map((c) => Number(c)) + const plutusV3 = costModels.PlutusV3.costs.map((c) => Number(c)) + return [plutusV1, plutusV2, plutusV3] +} + +export function makeEvaluator(): TransactionBuilder.Evaluator { + return { + evaluate: ( + tx: Transaction.Transaction, + additionalUtxos: ReadonlyArray | undefined, + context: TransactionBuilder.EvaluationContext + ) => + Effect.gen(function* () { + yield* Effect.logDebug("[Scalus UPLC] Starting evaluation") + + // Serialize transaction to CBOR bytes + const txBytes = Transaction.toCBORBytes(tx) + + yield* Effect.logDebug(`[Scalus UPLC] Transaction CBOR bytes: ${txBytes.length}`) + + const utxos = additionalUtxos ?? [] + yield* Effect.logDebug(`[Scalus UPLC] Additional UTxOs: ${utxos.length}`) + + // Build UTxO map CBOR + const utxosBytes = buildUtxoMapCBOR(utxos) + yield* Effect.logDebug(`[Scalus UPLC] UTxO map CBOR bytes: ${utxosBytes.length}`) + yield* Effect.logDebug(`[Scalus UPLC] UTxO map CBOR hex: ${Bytes.toHex(utxosBytes)}`) + + const { slotLength, zeroSlot, zeroTime } = context.slotConfig + + yield* Effect.logDebug( + `[Scalus UPLC] Slot config - zeroTime: ${zeroTime}, zeroSlot: ${zeroSlot}, slotLength: ${slotLength}` + ) + + const costModels = decodeCostModels(context.costModels) + yield* Effect.logDebug( + `[Scalus UPLC] Cost models - V1: ${costModels[0].length}, V2: ${costModels[1].length}, V3: ${costModels[2].length} costs` + ) + yield* Effect.logDebug( + `[Scalus UPLC] Max execution - steps: ${context.maxTxExSteps}, mem: ${context.maxTxExMem}` + ) + + // Scalus-specific slot config + const slotConfig = new Scalus.SlotConfig(Number(zeroTime), Number(zeroSlot), slotLength) + + yield* Effect.logDebug("[Scalus UPLC] Calling evalPlutusScripts...") + const redeemers = yield* Effect.try({ + try: () => + Scalus.Scalus.evalPlutusScripts(Array.from(txBytes), Array.from(utxosBytes), slotConfig, costModels), + catch: (error) => { + // Scalus error messages and evaluation logs, if any, are available to form an exception + const errorObj = error as any + const msg: string = errorObj?.message ?? "Unknown evaluation error" + + return new TransactionBuilder.EvaluationError({ + cause: error, + message: msg, + failures: [] + }) + } + }) + + yield* Effect.logDebug(`[Scalus UPLC] Evaluation successful - ${redeemers.length} redeemer(s) returned`) + + // Check if redeemers array is empty + if (redeemers.length === 0) { + return yield* new TransactionBuilder.EvaluationError({ + message: "Scalus evaluation returned no redeemers", + failures: [] + }) + } + + // Transform Scalus redeemers to Evolution format and check for zero execution units + const evalRedeemers: Array = [] + for (const r of redeemers) { + const exUnits = { + mem: Number(r.budget.memory), + steps: Number(r.budget.steps) + } + + // Check if execution units are zero (indicates evaluation failure) + if (exUnits.mem === 0 && exUnits.steps === 0) { + return yield* Effect.fail( + new TransactionBuilder.EvaluationError({ + message: `Scalus evaluation returned zero execution units for redeemer ${r.tag}:${r.index}`, + failures: [] + }) + ) + } + + const tagMap: Record = { + Spend: "spend", + Mint: "mint", + Cert: "publish", + Reward: "withdraw", + Voting: "vote", + Proposing: "propose" + } + + evalRedeemers.push({ + redeemer_tag: tagMap[r.tag] || "spend", + redeemer_index: r.index, + ex_units: exUnits + }) + } + + return evalRedeemers + }) + } +} diff --git a/packages/scalus-uplc/src/index.browser.ts b/packages/scalus-uplc/src/index.browser.ts new file mode 100644 index 0000000..fa6a2ae --- /dev/null +++ b/packages/scalus-uplc/src/index.browser.ts @@ -0,0 +1,16 @@ +/** + * Browser entry point - not implemented in rough draft + * + * @packageDocumentation + */ + +import type * as TransactionBuilder from "@evolution-sdk/evolution/sdk/builders/TransactionBuilder" + +/** + * Create a Scalus UPLC evaluator instance for browser environments. + * + * @throws Error - Browser support not yet implemented + */ +export function createScalusEvaluator(): TransactionBuilder.Evaluator { + throw new Error("Browser support not yet implemented for Scalus UPLC evaluator") +} diff --git a/packages/scalus-uplc/src/index.node.ts b/packages/scalus-uplc/src/index.node.ts new file mode 100644 index 0000000..807e35d --- /dev/null +++ b/packages/scalus-uplc/src/index.node.ts @@ -0,0 +1,16 @@ +import { makeEvaluator } from "./Evaluator.js" + +/** + * Create a Scalus UPLC evaluator instance. + * + * @returns A TransactionBuilder.Evaluator that uses Scalus for script evaluation + * + * @example + * ```typescript + * import { createScalusEvaluator } from "@evolution-sdk/scalus-uplc" + * + * const evaluator = createScalusEvaluator() + * const redeemers = await Effect.runPromise(evaluator.evaluate(tx, utxos, context)) + * ``` + */ +export const createScalusEvaluator = makeEvaluator() diff --git a/packages/scalus-uplc/tsconfig.build.json b/packages/scalus-uplc/tsconfig.build.json new file mode 100644 index 0000000..02c325c --- /dev/null +++ b/packages/scalus-uplc/tsconfig.build.json @@ -0,0 +1,10 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + "extends": "./tsconfig.src.json", + "compilerOptions": { + "tsBuildInfoFile": ".tsbuildinfo/build.tsbuildinfo", + "outDir": "dist", + "types": ["node"], + "stripInternal": true + } +} diff --git a/packages/scalus-uplc/tsconfig.json b/packages/scalus-uplc/tsconfig.json new file mode 100644 index 0000000..6e773df --- /dev/null +++ b/packages/scalus-uplc/tsconfig.json @@ -0,0 +1,8 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + "files": [], + "references": [ + { "path": "tsconfig.src.json" }, + { "path": "tsconfig.test.json" } + ] +} diff --git a/packages/scalus-uplc/tsconfig.src.json b/packages/scalus-uplc/tsconfig.src.json new file mode 100644 index 0000000..0f91661 --- /dev/null +++ b/packages/scalus-uplc/tsconfig.src.json @@ -0,0 +1,10 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + "extends": "../../tsconfig.base.json", + "include": ["src"], + "compilerOptions": { + "tsBuildInfoFile": ".tsbuildinfo/src.tsbuildinfo", + "outDir": ".tsbuildinfo/src", + "rootDir": "src" + } +} diff --git a/packages/scalus-uplc/tsconfig.test.json b/packages/scalus-uplc/tsconfig.test.json new file mode 100644 index 0000000..63bfa3b --- /dev/null +++ b/packages/scalus-uplc/tsconfig.test.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + "extends": "../../tsconfig.base.json", + "include": ["test/**/*", "**/*.test.ts", "**/*.spec.ts"], + "references": [{ "path": "tsconfig.src.json" }], + "compilerOptions": { + "tsBuildInfoFile": ".tsbuildinfo/test.tsbuildinfo", + "outDir": ".tsbuildinfo/test", + "noEmit": true, + "baseUrl": ".", + "paths": { + "@evolution-sdk/scalus-uplc": ["src/index.node.ts"], + "@evolution-sdk/scalus-uplc/*": ["src/*/index.ts", "src/*.ts"] + } + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d6abdc3..16dc381 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -260,6 +260,9 @@ importers: '@evolution-sdk/evolution': specifier: workspace:* version: link:../evolution + '@evolution-sdk/scalus-uplc': + specifier: workspace:* + version: link:../scalus-uplc '@noble/hashes': specifier: ^1.8.0 version: 1.8.0 @@ -280,6 +283,28 @@ importers: specifier: ^5.9.2 version: 5.9.2 + packages/scalus-uplc: + dependencies: + '@evolution-sdk/evolution': + specifier: workspace:* + version: link:../evolution + effect: + specifier: ^3.19.3 + version: 3.19.3 + scalus: + specifier: ^0.14.0 + version: 0.14.0 + devDependencies: + '@effect/vitest': + specifier: ^0.19.3 + version: 0.19.10(effect@3.19.3)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1)) + typescript: + specifier: ^5.9.2 + version: 5.9.2 + vitest: + specifier: ^3.2.4 + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1) + packages: '@algolia/abtesting@1.9.0': @@ -615,6 +640,12 @@ packages: '@effect/platform': ^0.90.4 effect: ^3.17.7 + '@effect/vitest@0.19.10': + resolution: {integrity: sha512-eV+Vu/3mqqpzAzo2Cb5/ZpBnUwIeDMOy4vAGCUv5bRzKq4HiTK23yYCEB9g5G+ZkPyQ63oOSO/7GHxS5f1sNtg==} + peerDependencies: + effect: ^3.13.12 + vitest: ^3.0.0 + '@effect/vitest@0.25.1': resolution: {integrity: sha512-OMYvOU8iGed8GZXxgVBXlYtjG+jwWj5cJxFk0hOHOfTbCHXtdCMEWlXNba5zxbE7dBnW4srbnSYrP/NGGTC3qQ==} peerDependencies: @@ -5343,6 +5374,9 @@ packages: engines: {node: '>=18'} hasBin: true + scalus@0.14.0: + resolution: {integrity: sha512-Gv+t24uJo8rt08zUYO7g6EzXByht0wuAmUwhd2gLhzbopalmFm0EnOsfa363ta84YKO9+V1ouTVD5Y6NNRV8jQ==} + scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} @@ -5658,9 +5692,6 @@ packages: tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinyexec@1.0.1: - resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} - tinyexec@1.0.2: resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} engines: {node: '>=18'} @@ -6275,7 +6306,7 @@ snapshots: '@antfu/install-pkg@1.1.0': dependencies: package-manager-detector: 1.5.0 - tinyexec: 1.0.1 + tinyexec: 1.0.2 '@antfu/utils@9.3.0': {} @@ -6390,7 +6421,7 @@ snapshots: '@babel/parser': 7.28.3 '@babel/template': 7.27.2 '@babel/types': 7.28.2 - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -6690,6 +6721,11 @@ snapshots: effect: 3.19.3 uuid: 11.1.0 + '@effect/vitest@0.19.10(effect@3.19.3)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1))': + dependencies: + effect: 3.19.3 + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1) + '@effect/vitest@0.25.1(effect@3.19.3)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1))': dependencies: effect: 3.19.3 @@ -6816,7 +6852,7 @@ snapshots: '@eslint/config-array@0.21.0': dependencies: '@eslint/object-schema': 2.1.6 - debug: 4.4.1 + debug: 4.4.3 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -9955,7 +9991,7 @@ snapshots: dependencies: basic-ftp: 5.0.5 data-uri-to-buffer: 6.0.2 - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -10163,14 +10199,14 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -10468,7 +10504,7 @@ snapshots: istanbul-lib-source-maps@5.0.6: dependencies: '@jridgewell/trace-mapping': 0.3.30 - debug: 4.4.1 + debug: 4.4.3 istanbul-lib-coverage: 3.2.2 transitivePeerDependencies: - supports-color @@ -11499,7 +11535,7 @@ snapshots: dependencies: '@tootallnate/quickjs-emscripten': 0.23.0 agent-base: 7.1.4 - debug: 4.4.1 + debug: 4.4.3 get-uri: 6.0.5 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 @@ -11691,7 +11727,7 @@ snapshots: proxy-agent@6.5.0: dependencies: agent-base: 7.1.4 - debug: 4.4.1 + debug: 4.4.3 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 lru-cache: 7.18.3 @@ -12060,6 +12096,8 @@ snapshots: commander: 12.1.0 enhanced-resolve: 5.18.3 + scalus@0.14.0: {} + scheduler@0.27.0: {} scroll-into-view-if-needed@3.1.0: @@ -12204,7 +12242,7 @@ snapshots: socks-proxy-agent@8.0.5: dependencies: agent-base: 7.1.4 - debug: 4.4.1 + debug: 4.4.3 socks: 2.8.7 transitivePeerDependencies: - supports-color @@ -12421,8 +12459,6 @@ snapshots: tinyexec@0.3.2: {} - tinyexec@1.0.1: {} - tinyexec@1.0.2: {} tinyglobby@0.2.14: @@ -12755,7 +12791,7 @@ snapshots: vite-node@3.2.4(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1): dependencies: cac: 6.7.14 - debug: 4.4.1 + debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 vite: 7.1.3(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1)