{"version":3,"file":"conformance-FncyAbRs.mjs","names":[],"sources":["../src/batteries/sandbox/conformance/index.ts"],"sourcesContent":["import type { HitFrame, ListFrame, PathFrame } from '../types'\n\n/** A framed adapter surface exercised by the shared protocol battery. */\nexport type FramedSource<T> = (signal?: AbortSignal, onStart?: () => void) => AsyncIterable<T>\n/**\n * The adapter surfaces the shared conformance battery exercises.\n *\n * @remarks\n * A DUCK-TYPE GUARD CANNOT CHECK BEHAVIOUR, which is why this suite exists. `implementsX()` proves a\n * backend has the right method names; it cannot prove the iterable is lazy, that it emits the mandatory\n * terminal frame, or that `signal` actually aborts mid-stream. Without those checks a conformant-LOOKING\n * OPFS adapter could silently impose limits the Node one does not, and the boundary would differ by\n * environment.\n *\n * Every framed surface must be lazy, emit each `item` frame (there is no count cap to stop at), then\n * EXACTLY ONE terminal `done` frame matching one union arm exactly. **A stream that ends without `done`\n * is a protocol violation** the tools classify as `io-failure` — never as \"no overflow\", because\n * silence is precisely what an unannounced truncation looks like.\n */\nexport interface ConformanceSources {\n  /** Directory traversal: `item` frames carrying a path and entry kind, then one terminal frame. */\n  list: FramedSource<ListFrame>\n  /** Name search: `item` frames carrying a path, then one terminal frame. */\n  findPaths: FramedSource<PathFrame>\n  /** Content search: `item` frames carrying path, line, and the WHOLE matched line — no per-hit cut. */\n  searchContent: FramedSource<HitFrame>\n  /** Must hand back a FRESH stream per call: readers are replayable, not single-use. */\n  read(): Promise<ReadableStream<Uint8Array>>\n  /**\n   * Metadata, including the opaque change token.\n   *\n   * @remarks\n   * `version` is compared for equality and never parsed, and only ONE direction is sound: a CHANGED\n   * token is evidence the file may have changed, while an UNCHANGED token is NOT evidence it did not —\n   * a same-size write inside the timestamp resolution preserves it. The suite asserts the safe\n   * direction only.\n   */\n  stat(): Promise<{ size: number; version: string }>\n}\n\n/** Optional mutation fixture for adapters that expose filesystem mutators. */\nexport interface ConformanceMutations {\n  /** A fresh disposable path in the selected existing directory, so checks cannot collide with repository files. */\n  makeFile(text: string, options?: { directory?: 'default' | 'other' }): Promise<string>\n  /** A fresh absent path with an existing parent, allowing rename checks without requiring mkdir. */\n  freePath(options?: { directory?: 'default' | 'other' }): Promise<string>\n  /** Observe the post-mutation metadata, including entry kind for directory checks. */\n  stat(path: string): Promise<{ size: number; version: string; kind: string }>\n  /** Read fixture content after a mutation, proving that the operation preserved or wrote the expected text. */\n  read(path: string): Promise<string>\n  /** Replace fixture content and make the new text observable through a subsequent read. */\n  write(path: string, text: string): Promise<void>\n  /** Remove a fixture; the optional operation must also resolve when the path is already absent. */\n  delete?(path: string): Promise<void>\n  /** Move a fixture and overwrite an existing destination, matching the filesystem contract. */\n  rename?(from: string, to: string): Promise<void>\n  /** Create the requested directory hierarchy and accept an already-existing directory. */\n  mkdir?(path: string): Promise<void>\n}\n\n/** Result metadata from mutation conformance, including operations that were not exercised. */\nexport interface SandboxConformanceReport {\n  /** Names each skipped mutation group rather than silently presenting it as passed. */\n  skipped: string[]\n}\n\nconst isDone = (value: unknown): value is ListFrame & { kind: 'done' } => {\n  if (value === null || typeof value !== 'object' || (value as { kind?: unknown }).kind !== 'done')\n    return false\n  const frame = value as Record<string, unknown>\n  if (frame.complete === true) return Object.keys(frame).length === 2\n  if (\n    frame.complete === false &&\n    frame.omitted === 'unexplored' &&\n    frame.bound === 'maxDepth' &&\n    typeof frame.atDepth === 'number' &&\n    Object.keys(frame).length === 5\n  )\n    return true\n  return (\n    frame.complete === false &&\n    frame.omitted === 'over-limit' &&\n    frame.bound === 'limit' &&\n    typeof frame.shown === 'number' &&\n    Object.keys(frame).length === 5\n  )\n}\nconst isItem = (value: unknown, name: string): boolean => {\n  if (value === null || typeof value !== 'object') return false\n  const frame = value as Record<string, unknown>\n  if (frame.kind !== 'item') return false\n  if (name === 'list')\n    return (\n      typeof frame.path === 'string' &&\n      (frame.entryKind === 'file' || frame.entryKind === 'dir') &&\n      Object.keys(frame).length === 3\n    )\n  if (name === 'findPaths') return typeof frame.path === 'string' && Object.keys(frame).length === 2\n  return (\n    typeof frame.path === 'string' &&\n    typeof frame.line === 'number' &&\n    Number.isInteger(frame.line) &&\n    typeof frame.text === 'string' &&\n    Object.keys(frame).length === 4\n  )\n}\nconst assert: (condition: boolean, message: string) => asserts condition = (condition, message) => {\n  if (!condition) throw new Error(message)\n}\nconst rejects = async (operation: () => Promise<unknown>, message: string) => {\n  let failed = false\n  try {\n    await operation()\n  } catch {\n    failed = true\n  }\n  assert(failed, message)\n}\n\n/** Run protocol and, when supplied, optional filesystem mutation checks. */\nexport function runSandboxConformance(sources: ConformanceSources): Promise<void>\nexport function runSandboxConformance(\n  sources: ConformanceSources,\n  mutations: ConformanceMutations\n): Promise<SandboxConformanceReport>\nexport async function runSandboxConformance(\n  sources: ConformanceSources,\n  mutations?: ConformanceMutations\n): Promise<void | SandboxConformanceReport> {\n  for (const [name, source] of Object.entries({\n    list: sources.list,\n    findPaths: sources.findPaths,\n    searchContent: sources.searchContent,\n  })) {\n    let started = false\n    const iterable = source(undefined, () => {\n      started = true\n    })\n    const iterator = iterable[Symbol.asyncIterator]()\n    assert(!started, `${name} must be lazy before next()`)\n    const frames: unknown[] = []\n    const firstFrame = await iterator.next()\n    assert(started, `${name} did not expose source work through onStart`)\n    if (!firstFrame.done) {\n      assert(\n        isDone(firstFrame.value) || isItem(firstFrame.value, name),\n        `${name} emitted malformed first frame`\n      )\n      frames.push(firstFrame.value)\n    }\n    for await (const frame of { [Symbol.asyncIterator]: () => iterator }) {\n      frames.push(frame)\n      if (frame && typeof frame === 'object' && (frame as { kind?: unknown }).kind === 'done')\n        assert(isDone(frame), `${name} emitted malformed done frame`)\n      else assert(isItem(frame, name), `${name} emitted a malformed item frame`)\n    }\n    assert(frames.length > 0 && isDone(frames.at(-1)), `${name} ended without done`)\n    assert(\n      frames.filter(\n        (frame) =>\n          frame && typeof frame === 'object' && (frame as { kind?: unknown }).kind === 'done'\n      ).length === 1,\n      `${name} emitted multiple done frames`\n    )\n  }\n  const first = await sources.read()\n  const second = await sources.read()\n  assert(first !== second, 'read must return a fresh stream')\n  const before = await sources.stat()\n  const after = await sources.stat()\n  if (before.size !== after.size || before.version !== after.version)\n    assert(before.version !== after.version, 'version must change when metadata changes')\n\n  const skipped: string[] = []\n  if (!mutations) return undefined\n  if (typeof mutations.delete === 'function') {\n    const path = await mutations.makeFile('delete me')\n    await mutations.delete(path)\n    await rejects(() => mutations.stat(path), 'delete must remove the file')\n    await mutations.delete(path)\n  } else skipped.push('delete')\n  if (typeof mutations.rename === 'function') {\n    const source = await mutations.makeFile('rename source')\n    const destination = await mutations.makeFile('old destination')\n    const { size } = await mutations.stat(source)\n    await mutations.rename(source, destination)\n    await rejects(() => mutations.stat(source), 'rename must remove its source')\n    const renamedMetadata = await mutations.stat(destination)\n    assert(renamedMetadata.size === size, 'rename must preserve source size')\n    const crossSource = await mutations.makeFile('cross directory', { directory: 'default' })\n    const crossDestination = await mutations.freePath({ directory: 'other' })\n    await mutations.rename(crossSource, crossDestination)\n    await rejects(() => mutations.stat(crossSource), 'cross-directory rename left its source')\n    assert(\n      (await mutations.read(crossDestination)) === 'cross directory',\n      'cross-directory rename lost content'\n    )\n  } else skipped.push('rename')\n  const mkdir = mutations.mkdir\n  if (typeof mkdir === 'function') {\n    const directory = await mutations.freePath()\n    await mkdir(directory)\n    const directoryMetadata = await mutations.stat(directory)\n    assert(directoryMetadata.kind === 'dir', 'mkdir must create a directory')\n    await mkdir(directory)\n    const file = `${directory}/round-trip.txt`\n    await mutations.write(file, 'round trip')\n    assert((await mutations.read(file)) === 'round trip', 'write must round-trip text')\n    await rejects(() => mkdir(file), 'mkdir on a file must reject')\n  } else skipped.push('mkdir')\n  {\n    const path = await mutations.makeFile('initial')\n    await mutations.write(path, 'round trip')\n    assert((await mutations.read(path)) === 'round trip', 'write must round-trip text')\n  }\n  return { skipped }\n}\n"],"mappings":"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"}