{"version":3,"file":"index.mjs","names":["results: ConformanceResult[]","sandbox","createdSnapshot: Awaited<ReturnType<typeof sandbox.snapshots.create>> | undefined"],"sources":["../../src/testing/conformance.ts"],"sourcesContent":["import { createSandbox, type CreateSandboxOptions } from \"../core/sandbox\";\nimport { supports } from \"../core/capabilities\";\nimport type { SandboxProvider } from \"../core/provider\";\n\nexport const conformanceCases = [\n  \"create and stop\",\n  \"idempotent stop\",\n  \"text files\",\n  \"binary files\",\n  \"directories\",\n  \"relative paths\",\n  \"invalid paths\",\n  \"successful command\",\n  \"stdout\",\n  \"stderr\",\n  \"nonzero exit\",\n  \"timeout\",\n  \"background process\",\n  \"stdin\",\n  \"ports\",\n  \"snapshot create\",\n  \"snapshot delete\",\n  \"snapshot restore\",\n  \"cleanup after failure\",\n] as const;\n\nexport interface ConformanceSubject<TProvider extends SandboxProvider<unknown>> {\n  create: CreateSandboxOptions<TProvider>;\n  commands: {\n    success: string;\n    stdout: string;\n    stderr: string;\n    nonzero: string;\n    timeout: string;\n    background: string;\n    stdin: string;\n  };\n}\n\nexport interface ConformanceResult {\n  name: (typeof conformanceCases)[number];\n  status: \"passed\" | \"failed\" | \"skipped\";\n  error?: unknown;\n}\n\nexport async function runConformance<TProvider extends SandboxProvider<unknown>>(\n  subject: ConformanceSubject<TProvider>,\n): Promise<ConformanceResult[]> {\n  const results: ConformanceResult[] = [];\n  const check = async (name: ConformanceResult[\"name\"], test: () => Promise<void>) => {\n    try {\n      await test();\n      results.push({ name, status: \"passed\" });\n    } catch (error) {\n      results.push({ name, status: \"failed\", error });\n    }\n  };\n  await check(\"create and stop\", async () => {\n    const sandbox = await createSandbox(subject.create);\n    await sandbox.stop();\n  });\n  await check(\"idempotent stop\", async () => {\n    const sandbox = await createSandbox(subject.create);\n    await sandbox.stop();\n    await sandbox.stop();\n  });\n  const sandbox = await createSandbox(subject.create);\n  try {\n    await check(\"text files\", async () => {\n      await sandbox.files.write(\"text.txt\", \"hello\");\n      if ((await sandbox.files.text(\"text.txt\")) !== \"hello\") throw new Error(\"text mismatch\");\n    });\n    await check(\"binary files\", async () => {\n      const expected = new Uint8Array([0, 1, 2, 255]);\n      await sandbox.files.write(\"binary.bin\", expected);\n      const actual = await sandbox.files.read(\"binary.bin\");\n      if (!actual.every((value, index) => value === expected[index]))\n        throw new Error(\"binary mismatch\");\n    });\n    await check(\"directories\", async () => {\n      await sandbox.files.mkdir(\"nested\");\n      await sandbox.files.write(\"nested/item.txt\", \"item\");\n      if (!(await sandbox.files.list(\"nested\")).some((entry) => entry.name === \"item.txt\"))\n        throw new Error(\"list mismatch\");\n      await sandbox.files.remove(\"nested\");\n    });\n    await check(\"relative paths\", async () => {\n      await sandbox.files.write(\"relative.txt\", \"yes\");\n      if (!(await sandbox.files.exists(\"relative.txt\")))\n        throw new Error(\"relative path not resolved\");\n    });\n    await check(\"invalid paths\", async () => {\n      try {\n        await sandbox.files.read(\"../escape\");\n      } catch {\n        return;\n      }\n      throw new Error(\"path traversal accepted\");\n    });\n    await check(\"successful command\", async () => {\n      if (!(await sandbox.run(subject.commands.success)).success) throw new Error(\"command failed\");\n    });\n    await check(\"stdout\", async () => {\n      if (!(await sandbox.run(subject.commands.stdout)).stdout.includes(\"stdout\"))\n        throw new Error(\"stdout missing\");\n    });\n    await check(\"stderr\", async () => {\n      const result = await sandbox.run(subject.commands.stderr);\n      const output =\n        sandbox.capabilities[\"process.run\"] === \"combined-stream\"\n          ? `${result.stdout}${result.stderr}`\n          : result.stderr;\n      if (!output.includes(\"stderr\")) throw new Error(\"stderr missing\");\n    });\n    await check(\"nonzero exit\", async () => {\n      if ((await sandbox.run(subject.commands.nonzero)).exitCode === 0)\n        throw new Error(\"expected nonzero exit\");\n    });\n    await check(\"timeout\", async () => {\n      try {\n        await sandbox.run(subject.commands.timeout, { timeout: 20 });\n      } catch {\n        return;\n      }\n      throw new Error(\"timeout not enforced\");\n    });\n    if (supports(sandbox, \"process.background\"))\n      await check(\"background process\", async () => {\n        const process = await sandbox.processes.start(subject.commands.background);\n        await process.kill();\n      });\n    else results.push({ name: \"background process\", status: \"skipped\" });\n    if (supports(sandbox, \"process.stdin\"))\n      await check(\"stdin\", async () => {\n        const process = await sandbox.processes.start(subject.commands.stdin);\n        await process.write(\"received\\n\");\n        for await (const event of process.output()) {\n          const data =\n            typeof event.data === \"string\" ? event.data : new TextDecoder().decode(event.data);\n          if (data.includes(\"received\")) return;\n        }\n        throw new Error(\"stdin output missing\");\n      });\n    else results.push({ name: \"stdin\", status: \"skipped\" });\n    if (supports(sandbox, \"ports.expose\"))\n      await check(\"ports\", async () => {\n        const port = await sandbox.ports.expose(30_00);\n        if (port.port !== 3000 || !port.url) throw new Error(\"invalid exposed port\");\n      });\n    else results.push({ name: \"ports\", status: \"skipped\" });\n    let createdSnapshot: Awaited<ReturnType<typeof sandbox.snapshots.create>> | undefined;\n    if (supports(sandbox, \"snapshot.create\"))\n      await check(\"snapshot create\", async () => {\n        await sandbox.files.write(\"snapshot.txt\", \"before\");\n        createdSnapshot = await sandbox.snapshots.create({ name: \"conformance\" });\n      });\n    else results.push({ name: \"snapshot create\", status: \"skipped\" });\n    if (createdSnapshot && supports(sandbox, \"snapshot.restore\"))\n      await check(\"snapshot restore\", async () => {\n        await sandbox.files.write(\"snapshot.txt\", \"after\");\n        await sandbox.snapshots.restore(createdSnapshot!);\n        if ((await sandbox.files.text(\"snapshot.txt\")) !== \"before\")\n          throw new Error(\"snapshot not restored\");\n      });\n    else results.push({ name: \"snapshot restore\", status: \"skipped\" });\n    if (createdSnapshot && supports(sandbox, \"snapshot.delete\"))\n      await check(\"snapshot delete\", async () => {\n        await sandbox.snapshots.delete(createdSnapshot!);\n      });\n    else results.push({ name: \"snapshot delete\", status: \"skipped\" });\n  } finally {\n    await sandbox.stop();\n  }\n  await check(\"cleanup after failure\", async () => {\n    const temporary = await createSandbox(subject.create);\n    try {\n      throw new Error(\"expected\");\n    } catch {\n      /* expected */\n    } finally {\n      await temporary.stop();\n    }\n  });\n  return results;\n}\n"],"mappings":"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"}