{"version":3,"file":"common-oI1niK9Z.mjs","names":[],"sources":["../src/lib/contracts/byte_store.ts","../src/lib/helpers/media_readers.ts"],"sourcesContent":["import { validator } from '@nhtio/validation'\nimport { passesSchema } from '../utils/validation'\nimport type { SpoolReader } from './spool_reader'\nimport type { MediaReader } from './media_reader'\n\n/**\n * Unified \"give bytes, get a reader\" persistence contract.\n *\n * @remarks\n * For the purposes of storage there is no meaningful distinction between text and binary — bytes\n * are bytes. `ByteStore` is the single low-level shape every ADK storage layer implements: hand it\n * bytes under an `id`, get back a replayable reader `R`; read or delete by the same `id` later. The\n * generic `R` is the reader the store hands out — different reader contracts (line-indexed\n * {@link @nhtio/adk!SpoolReader} vs binary-streamed {@link @nhtio/adk!MediaReader}) are\n * distinguished by the `R` instantiation, not by separate store interfaces. See the {@link SpoolStore}\n * and {@link MediaStore} aliases for the two concrete semantics.\n *\n * `write` accepts a `string`, a `Uint8Array`, or a `ReadableStream<Uint8Array>`. The stream form is\n * the point of the contract: a durable store can persist an arbitrarily large payload straight to\n * disk/object storage without first materializing it in memory. **String input is encoded as\n * UTF-8.** The returned reader is only guaranteed readable once the `write` result has resolved.\n *\n * All three methods may be synchronous or asynchronous so that in-memory implementations are not\n * forced to pay promise overhead while I/O-backed implementations stay async. Note that any\n * implementation accepting a `ReadableStream` must return a `Promise` for that input — draining a\n * stream cannot be synchronous.\n */\nexport interface ByteStore<R> {\n  /**\n   * Persists `bytes` under `id` and returns a reader over them.\n   *\n   * @remarks\n   * Re-writing the same `id` replaces the prior entry. `string` input is encoded as UTF-8;\n   * `Uint8Array` and `ReadableStream<Uint8Array>` are stored byte-faithfully. Stream input\n   * necessarily resolves asynchronously.\n   *\n   * @param id - Identifier used to retrieve or delete the bytes later.\n   * @param bytes - The payload, as a `string`, `Uint8Array`, or `ReadableStream<Uint8Array>`.\n   * @returns A reader over the stored bytes (or a `Promise` of one).\n   */\n  write(id: string, bytes: string | Uint8Array | ReadableStream<Uint8Array>): R | Promise<R>\n\n  /**\n   * Returns a reader over the bytes previously written under `id`, or `undefined` if no entry\n   * exists.\n   *\n   * @param id - Identifier supplied to a prior {@link ByteStore.write} call.\n   * @returns A reader over the stored bytes, `undefined`, or a `Promise` of either.\n   */\n  read(id: string): R | undefined | Promise<R | undefined>\n\n  /**\n   * Removes the entry under `id`.\n   *\n   * @param id - Identifier whose entry should be removed.\n   * @returns `true` if an entry existed and was removed; `false` otherwise (or a `Promise` of one).\n   */\n  delete(id: string): boolean | Promise<boolean>\n}\n\n/**\n * A {@link ByteStore} that hands out line-indexed text readers ({@link @nhtio/adk!SpoolReader}).\n *\n * @remarks\n * The store backing tool-output artifacts. Stored bytes are decoded as UTF-8 text for line-oriented\n * reads; binary input is stored byte-faithfully but `SpoolReader.readAll()` interprets it as text,\n * so opaque binary belongs in a {@link MediaStore} / `Media`, not here.\n */\nexport type SpoolStore = ByteStore<SpoolReader>\n\n/**\n * A {@link ByteStore} that hands out binary-streamed readers ({@link @nhtio/adk!MediaReader}).\n *\n * @remarks\n * The store backing persisted media bytes. Stored bytes are opaque and replayable via\n * `MediaReader.stream()`; no text decoding is implied.\n */\nexport type MediaStore = ByteStore<MediaReader>\n\n/**\n * Validator schema used to validate a {@link ByteStore} value.\n *\n * @remarks\n * Because `ByteStore` is a structural interface with no associated constructor, validation is\n * duck-typed: the value must be non-null with `write`, `read`, and `delete` present as callable\n * properties. Arity is not enforced — implementations may add optional parameters beyond the\n * contract. The reader type `R` cannot be checked structurally here; conformance of the reader is\n * the caller's concern at the point of use.\n */\nexport const byteStoreSchema = validator\n  .any()\n  .required()\n  .custom((value, helpers) => {\n    if (\n      value !== null &&\n      value !== undefined &&\n      typeof (value as any).write === 'function' &&\n      typeof (value as any).read === 'function' &&\n      typeof (value as any).delete === 'function'\n    ) {\n      return value\n    }\n    return helpers.error('any.invalid')\n  })\n\n/**\n * Returns `true` if `value` implements the {@link ByteStore} interface.\n *\n * @remarks\n * Duck-typed: checks that `value` is non-null with `write`, `read`, and `delete` as callable\n * functions. Does not use `instanceof` — there is no `ByteStore` constructor.\n *\n * @param value - The value to test.\n * @returns `true` when `value` conforms to the {@link ByteStore} interface.\n */\nexport const implementsByteStore = <R = unknown>(value: unknown): value is ByteStore<R> => {\n  return passesSchema(byteStoreSchema, value)\n}\n","import { encodeBase64 } from './base64'\nimport { isInstanceOf } from '../utils/guards'\nimport type { MediaReader } from '../contracts/media_reader'\n\n/**\n * Resolver tag for the in-memory media reader handle. The locator inlines the bytes as base64 because an\n * in-memory reader owns its buffer outright — there is no external store to point at.\n */\nexport const MEDIA_READER_TAG_IN_MEMORY = 'media:in-memory'\n\n/**\n * Resolver tag for the fetch-backed media reader handle. The locator captures the URL (and any fetch\n * init) so decode can re-issue the request — no live binding to re-inject.\n */\nexport const MEDIA_READER_TAG_FETCH = 'media:fetch'\n\n/**\n * Constructs a {@link @nhtio/adk!MediaReader} backed by an in-memory `Uint8Array`.\n *\n * @remarks\n * Each `stream()` call returns a fresh single-chunk `ReadableStream` over the same buffer. The\n * reader is re-openable by construction — call `stream()` as many times as needed.\n *\n * @param bytes - The buffer to serve.\n * @returns A {@link @nhtio/adk!MediaReader} that re-reads `bytes` on every call.\n */\nexport const inMemoryMediaReader = (bytes: Uint8Array): MediaReader => {\n  return {\n    stream(): ReadableStream<Uint8Array> {\n      return new ReadableStream<Uint8Array>({\n        start(controller) {\n          controller.enqueue(bytes)\n          controller.close()\n        },\n      })\n    },\n    byteLength(): number {\n      return bytes.byteLength\n    },\n    describe() {\n      // The buffer IS the backing store — inline it as base64, no external locator exists.\n      return { tag: MEDIA_READER_TAG_IN_MEMORY, locator: { bytesBase64: encodeBase64(bytes) } }\n    },\n  }\n}\n\n/**\n * Constructs a {@link @nhtio/adk!MediaReader} backed by a fetch call.\n *\n * @remarks\n * Each `stream()` call re-issues the fetch. Tool authors whose underlying source is rate-limited\n * or expensive must cache locally before constructing the reader — the framework cannot make\n * that decision for them.\n *\n * `byteLength()` returns `undefined` because most remote sources do not promise it without an\n * extra HEAD request; consumers that need a byte size should resolve it out-of-band.\n *\n * @param url - The URL to fetch on each call.\n * @param init - Optional `fetch` init forwarded verbatim.\n * @returns A {@link @nhtio/adk!MediaReader} that re-issues `fetch(url, init)` on every call.\n */\nexport const fromFetch = (url: string | URL, init?: RequestInit): MediaReader => {\n  return {\n    async stream(): Promise<ReadableStream<Uint8Array>> {\n      const response = await fetch(url, init)\n      if (!response.ok) {\n        throw new Error(`fromFetch: fetch failed with status ${response.status}`)\n      }\n      if (!response.body) {\n        throw new Error('fromFetch: response has no body')\n      }\n      return response.body as ReadableStream<Uint8Array>\n    },\n    byteLength(): undefined {\n      return undefined\n    },\n    describe() {\n      // The URL is the re-openable locator; decode re-issues fetch(url, init). Only JSON-expressible\n      // init is captured (method/headers) — a streaming/AbortSignal init cannot survive serialisation\n      // and is dropped, which is correct: a re-issued fetch on decode starts fresh.\n      const locator: { url: string; init?: { method?: string; headers?: Record<string, string> } } =\n        {\n          url: typeof url === 'string' ? url : url.toString(),\n        }\n      if (init) {\n        const captured: { method?: string; headers?: Record<string, string> } = {}\n        if (typeof init.method === 'string') captured.method = init.method\n        if (\n          init.headers &&\n          !isInstanceOf(init.headers, 'Headers', Headers) &&\n          !Array.isArray(init.headers)\n        ) {\n          captured.headers = { ...(init.headers as Record<string, string>) }\n        }\n        if (Object.keys(captured).length > 0) locator.init = captured\n      }\n      return { tag: MEDIA_READER_TAG_FETCH, locator }\n    },\n  }\n}\n\n/**\n * Constructs a {@link @nhtio/adk!MediaReader} backed by a browser `File` or `Blob`.\n *\n * @remarks\n * Each `stream()` call re-streams the underlying File via `File.stream()`. `byteLength()`\n * resolves from `file.size`.\n *\n * @remarks\n * **Not describable / not encodable.** A browser `Blob` is not re-openable across a serialisation\n * boundary (it has no stable locator), and draining its bytes requires `await` — which the encoder's\n * synchronous `[ENCODE_METHOD]()` cannot do. So this reader intentionally omits `describe()`, and\n * `encode()`-ing a {@link @nhtio/adk!Media} backed by it throws {@link @nhtio/adk!E_READER_NOT_DESCRIBABLE}.\n * To serialise such media, persist the bytes to a media/spool store and wrap them in a describable\n * reader first.\n *\n * @param file - The browser `File` or `Blob` to stream.\n * @returns A {@link @nhtio/adk!MediaReader} that re-streams `file` on every call.\n */\nexport const fromWebFile = (file: Blob): MediaReader => {\n  return {\n    stream(): ReadableStream<Uint8Array> {\n      return file.stream() as ReadableStream<Uint8Array>\n    },\n    byteLength(): number {\n      return file.size\n    },\n  }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAyFA,IAAa,kBAAkB,UAC5B,IAAI,EACJ,SAAS,EACT,QAAQ,OAAO,YAAY;CAC1B,IACE,UAAU,QACV,UAAU,KAAA,KACV,OAAQ,MAAc,UAAU,cAChC,OAAQ,MAAc,SAAS,cAC/B,OAAQ,MAAc,WAAW,YAEjC,OAAO;CAET,OAAO,QAAQ,MAAM,aAAa;AACpC,CAAC;;;;;;;;;;;AAYH,IAAa,uBAAoC,UAA0C;CACzF,OAAO,aAAa,iBAAiB,KAAK;AAC5C;;;;;;;AC7GA,IAAa,6BAA6B;;;;;AAM1C,IAAa,yBAAyB;;;;;;;;;;;AAYtC,IAAa,uBAAuB,UAAmC;CACrE,OAAO;EACL,SAAqC;GACnC,OAAO,IAAI,eAA2B,EACpC,MAAM,YAAY;IAChB,WAAW,QAAQ,KAAK;IACxB,WAAW,MAAM;GACnB,EACF,CAAC;EACH;EACA,aAAqB;GACnB,OAAO,MAAM;EACf;EACA,WAAW;GAET,OAAO;IAAE,KAAK;IAA4B,SAAS,EAAE,aAAa,aAAa,KAAK,EAAE;GAAE;EAC1F;CACF;AACF;;;;;;;;;;;;;;;;AAiBA,IAAa,aAAa,KAAmB,SAAoC;CAC/E,OAAO;EACL,MAAM,SAA8C;GAClD,MAAM,WAAW,MAAM,MAAM,KAAK,IAAI;GACtC,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,uCAAuC,SAAS,QAAQ;GAE1E,IAAI,CAAC,SAAS,MACZ,MAAM,IAAI,MAAM,iCAAiC;GAEnD,OAAO,SAAS;EAClB;EACA,aAAwB,CAExB;EACA,WAAW;GAIT,MAAM,UACJ,EACE,KAAK,OAAO,QAAQ,WAAW,MAAM,IAAI,SAAS,EACpD;GACF,IAAI,MAAM;IACR,MAAM,WAAkE,CAAC;IACzE,IAAI,OAAO,KAAK,WAAW,UAAU,SAAS,SAAS,KAAK;IAC5D,IACE,KAAK,WACL,CAAC,aAAa,KAAK,SAAS,WAAW,OAAO,KAC9C,CAAC,MAAM,QAAQ,KAAK,OAAO,GAE3B,SAAS,UAAU,EAAE,GAAI,KAAK,QAAmC;IAEnE,IAAI,OAAO,KAAK,QAAQ,EAAE,SAAS,GAAG,QAAQ,OAAO;GACvD;GACA,OAAO;IAAE,KAAK;IAAwB;GAAQ;EAChD;CACF;AACF;;;;;;;;;;;;;;;;;;;AAoBA,IAAa,eAAe,SAA4B;CACtD,OAAO;EACL,SAAqC;GACnC,OAAO,KAAK,OAAO;EACrB;EACA,aAAqB;GACnB,OAAO,KAAK;EACd;CACF;AACF"}