@nhtio/adk 0.1.0-master-445a9ed0 → 1.20260529.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/batteries/llm/openai_chat_completions/adapter.cjs +10 -9
- package/batteries/llm/openai_chat_completions/adapter.cjs.map +1 -1
- package/batteries/llm/openai_chat_completions/adapter.mjs +8 -8
- package/batteries/llm/openai_chat_completions/adapter.mjs.map +1 -1
- package/batteries/llm/openai_chat_completions/exceptions.cjs +1 -1
- package/batteries/llm/openai_chat_completions/helpers.cjs +16 -16
- package/batteries/llm/openai_chat_completions/helpers.cjs.map +1 -1
- package/batteries/llm/openai_chat_completions/helpers.d.ts +10 -10
- package/batteries/llm/openai_chat_completions/helpers.mjs +16 -16
- package/batteries/llm/openai_chat_completions/helpers.mjs.map +1 -1
- package/batteries/llm/openai_chat_completions/types.d.ts +6 -26
- package/batteries/llm/openai_chat_completions/validation.cjs +1 -3
- package/batteries/llm/openai_chat_completions/validation.cjs.map +1 -1
- package/batteries/llm/openai_chat_completions/validation.mjs +0 -2
- package/batteries/llm/openai_chat_completions/validation.mjs.map +1 -1
- package/batteries/llm/webllm_chat_completions/adapter.cjs +10 -9
- package/batteries/llm/webllm_chat_completions/adapter.cjs.map +1 -1
- package/batteries/llm/webllm_chat_completions/adapter.mjs +8 -8
- package/batteries/llm/webllm_chat_completions/adapter.mjs.map +1 -1
- package/batteries/llm/webllm_chat_completions/exceptions.cjs +1 -1
- package/batteries/llm/webllm_chat_completions/validation.cjs +1 -3
- package/batteries/llm/webllm_chat_completions/validation.cjs.map +1 -1
- package/batteries/llm/webllm_chat_completions/validation.mjs +0 -2
- package/batteries/llm/webllm_chat_completions/validation.mjs.map +1 -1
- package/batteries/storage/flydrive/index.d.ts +4 -10
- package/batteries/storage/flydrive.cjs +3 -12
- package/batteries/storage/flydrive.cjs.map +1 -1
- package/batteries/storage/flydrive.mjs +2 -11
- package/batteries/storage/flydrive.mjs.map +1 -1
- package/batteries/storage/in_memory/index.d.ts +17 -31
- package/batteries/storage/in_memory.cjs +30 -89
- package/batteries/storage/in_memory.cjs.map +1 -1
- package/batteries/storage/in_memory.mjs +30 -89
- package/batteries/storage/in_memory.mjs.map +1 -1
- package/batteries/storage/opfs/index.d.ts +4 -10
- package/batteries/storage/opfs.cjs +5 -55
- package/batteries/storage/opfs.cjs.map +1 -1
- package/batteries/storage/opfs.mjs +4 -54
- package/batteries/storage/opfs.mjs.map +1 -1
- package/batteries/tools/color.cjs +3 -3
- package/batteries/tools/color.mjs +2 -2
- package/batteries/tools/comparison.cjs +4 -3
- package/batteries/tools/comparison.cjs.map +1 -1
- package/batteries/tools/comparison.mjs +2 -2
- package/batteries/tools/data_structure.cjs +4 -3
- package/batteries/tools/data_structure.cjs.map +1 -1
- package/batteries/tools/data_structure.mjs +2 -2
- package/batteries/tools/datetime_extended.cjs +4 -4
- package/batteries/tools/datetime_extended.mjs +2 -2
- package/batteries/tools/datetime_math.cjs +3 -3
- package/batteries/tools/datetime_math.mjs +2 -2
- package/batteries/tools/encoding.cjs +4 -3
- package/batteries/tools/encoding.cjs.map +1 -1
- package/batteries/tools/encoding.mjs +2 -2
- package/batteries/tools/formatting.cjs +4 -3
- package/batteries/tools/formatting.cjs.map +1 -1
- package/batteries/tools/formatting.mjs +2 -2
- package/batteries/tools/geo_basics.cjs +3 -3
- package/batteries/tools/geo_basics.mjs +2 -2
- package/batteries/tools/math.cjs +4 -3
- package/batteries/tools/math.cjs.map +1 -1
- package/batteries/tools/math.mjs +2 -2
- package/batteries/tools/memory.cjs +7 -6
- package/batteries/tools/memory.cjs.map +1 -1
- package/batteries/tools/memory.mjs +5 -5
- package/batteries/tools/parsing.cjs +6 -5
- package/batteries/tools/parsing.cjs.map +1 -1
- package/batteries/tools/parsing.mjs +3 -3
- package/batteries/tools/retrievables.cjs +11 -11
- package/batteries/tools/retrievables.cjs.map +1 -1
- package/batteries/tools/retrievables.mjs +9 -10
- package/batteries/tools/retrievables.mjs.map +1 -1
- package/batteries/tools/standing_instructions.cjs +5 -4
- package/batteries/tools/standing_instructions.cjs.map +1 -1
- package/batteries/tools/standing_instructions.mjs +3 -3
- package/batteries/tools/statistics.cjs +5 -4
- package/batteries/tools/statistics.cjs.map +1 -1
- package/batteries/tools/statistics.mjs +3 -3
- package/batteries/tools/string_processing.cjs +4 -3
- package/batteries/tools/string_processing.cjs.map +1 -1
- package/batteries/tools/string_processing.mjs +2 -2
- package/batteries/tools/structured_data.cjs +4 -3
- package/batteries/tools/structured_data.cjs.map +1 -1
- package/batteries/tools/structured_data.mjs +2 -2
- package/batteries/tools/text_analysis.cjs +4 -4
- package/batteries/tools/text_analysis.mjs +3 -3
- package/batteries/tools/text_comparison.cjs +3 -3
- package/batteries/tools/text_comparison.mjs +2 -2
- package/batteries/tools/time.cjs +3 -3
- package/batteries/tools/time.mjs +2 -2
- package/batteries/tools/unit_conversion.cjs +3 -3
- package/batteries/tools/unit_conversion.mjs +2 -2
- package/batteries/tools.cjs +1 -1
- package/batteries/tools.mjs +1 -1
- package/batteries.cjs +1 -1
- package/batteries.mjs +1 -1
- package/chunk-KmRHZBOW.js +35 -0
- package/{common-aFmr9Oqs.mjs → common-DeZaonK1.mjs} +10 -76
- package/common-DeZaonK1.mjs.map +1 -0
- package/{common-BJ6V6dsH.js → common-Od8edUXU.js} +12 -89
- package/common-Od8edUXU.js.map +1 -0
- package/common.cjs +7 -9
- package/common.d.ts +0 -8
- package/common.mjs +7 -7
- package/{dispatch_runner-OimGCkk7.mjs → dispatch_runner-9j6bXHL3.mjs} +2 -34
- package/dispatch_runner-9j6bXHL3.mjs.map +1 -0
- package/{dispatch_runner-BWYNxmnp.js → dispatch_runner-CsoH0nld.js} +6 -37
- package/dispatch_runner-CsoH0nld.js.map +1 -0
- package/dispatch_runner.cjs +1 -1
- package/dispatch_runner.mjs +1 -1
- package/{exceptions-CSqzbL1N.js → exceptions-D5YrO9Vm.js} +2 -2
- package/{exceptions-CSqzbL1N.js.map → exceptions-D5YrO9Vm.js.map} +1 -1
- package/exceptions.cjs +2 -2
- package/factories.cjs +1 -1
- package/forge.cjs +4 -4
- package/forge.mjs +3 -3
- package/guards.cjs +9 -9
- package/guards.mjs +7 -7
- package/index.cjs +13 -13
- package/index.cjs.map +1 -1
- package/index.d.ts +1 -1
- package/index.mjs +10 -10
- package/index.mjs.map +1 -1
- package/lib/classes/retrievable.d.ts +4 -47
- package/lib/contracts/dispatch_context.d.ts +0 -44
- package/lib/contracts/turn_runner_config.d.ts +1 -5
- package/lib/contracts/turn_runner_context.d.ts +0 -25
- package/package.json +74 -74
- package/{runtime-BUDWyd-R.js → runtime-BJVkrGQe.js} +2 -2
- package/{runtime-BUDWyd-R.js.map → runtime-BJVkrGQe.js.map} +1 -1
- package/skills/adk-assembly/SKILL.md +2 -2
- package/{spooled_artifact-B_tVDDdB.mjs → spooled_artifact-C5ZtGxuJ.mjs} +2 -2
- package/{spooled_artifact-B_tVDDdB.mjs.map → spooled_artifact-C5ZtGxuJ.mjs.map} +1 -1
- package/{spooled_artifact-CFstzlqX.js → spooled_artifact-Cm9Te22K.js} +6 -5
- package/{spooled_artifact-CFstzlqX.js.map → spooled_artifact-Cm9Te22K.js.map} +1 -1
- package/spooled_artifact.cjs +2 -2
- package/spooled_artifact.mjs +2 -2
- package/{spooled_markdown_artifact-DWWak35I.mjs → spooled_markdown_artifact-BpUJol0W.mjs} +2 -2
- package/{spooled_markdown_artifact-DWWak35I.mjs.map → spooled_markdown_artifact-BpUJol0W.mjs.map} +1 -1
- package/{spooled_markdown_artifact-DK-T8Hy6.js → spooled_markdown_artifact-RRB113sy.js} +7 -6
- package/{spooled_markdown_artifact-DK-T8Hy6.js.map → spooled_markdown_artifact-RRB113sy.js.map} +1 -1
- package/{thought-DDqjQu3m.mjs → thought-CDb457b4.mjs} +2 -2
- package/{thought-DDqjQu3m.mjs.map → thought-CDb457b4.mjs.map} +1 -1
- package/{thought-DTsFRGdE.js → thought-DuN2PgdO.js} +6 -5
- package/{thought-DTsFRGdE.js.map → thought-DuN2PgdO.js.map} +1 -1
- package/{tool-cwJyEHI9.js → tool-COSeH8I6.js} +5 -4
- package/{tool-cwJyEHI9.js.map → tool-COSeH8I6.js.map} +1 -1
- package/{tool-q4LskG7K.mjs → tool-D2WB1EA1.mjs} +1 -1
- package/{tool-q4LskG7K.mjs.map → tool-D2WB1EA1.mjs.map} +1 -1
- package/{tool_call-BKIdAAoY.mjs → tool_call-BKyyxGaZ.mjs} +2 -2
- package/{tool_call-BKIdAAoY.mjs.map → tool_call-BKyyxGaZ.mjs.map} +1 -1
- package/{tool_call-3T0xTXlD.js → tool_call-DFgzcVcU.js} +6 -5
- package/{tool_call-3T0xTXlD.js.map → tool_call-DFgzcVcU.js.map} +1 -1
- package/{tool_registry-snPjF0zJ.js → tool_registry-Dkfprsck.js} +5 -39
- package/{tool_registry-snPjF0zJ.js.map → tool_registry-Dkfprsck.js.map} +1 -1
- package/{turn_runner-BScT8OgA.js → turn_runner-CMm2BHdX.js} +7 -10
- package/turn_runner-CMm2BHdX.js.map +1 -0
- package/{turn_runner-DRBLN2Y_.mjs → turn_runner-y7eyEcJH.mjs} +3 -7
- package/turn_runner-y7eyEcJH.mjs.map +1 -0
- package/turn_runner.cjs +1 -1
- package/turn_runner.mjs +1 -1
- package/types.d.ts +2 -2
- package/CHANGELOG.md +0 -49
- package/common-BJ6V6dsH.js.map +0 -1
- package/common-aFmr9Oqs.mjs.map +0 -1
- package/dispatch_runner-BWYNxmnp.js.map +0 -1
- package/dispatch_runner-OimGCkk7.mjs.map +0 -1
- package/lib/contracts/byte_store.d.ts +0 -93
- package/turn_runner-BScT8OgA.js.map +0 -1
- package/turn_runner-DRBLN2Y_.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opfs.mjs","names":["#handle","#threshold","#load","#readRange","#ready","#init","#buildStreamingIndex","#resolveRoot","#prefix","#defaultThreshold","#keyFor","#getRoot","#writeStreamViaSyncHandle","#writeStreamViaWritable","#writeViaSyncHandle","#writeViaWritable","#isNotFoundError","#root"],"sources":["../../../src/batteries/storage/opfs/index.ts"],"sourcesContent":["/**\n * Browser-only Origin Private File System storage for spooled artifacts.\n *\n * @module @nhtio/adk/batteries/storage/opfs\n *\n * @remarks\n * Opt-in **browser-only** storage battery backed by the\n * [Origin Private File System](https://developer.mozilla.org/docs/Web/API/File_System_API/Origin_private_file_system)\n * (OPFS). Provides {@link OpfsSpoolReader} (a {@link @nhtio/adk!SpoolReader} over a `OpfsFileHandle`)\n * and {@link OpfsSpoolStore} (a `write(callId, bytes) → reader` persistence layer that wraps an\n * OPFS directory).\n *\n * The reader has two modes selected lazily on first method invocation based on the size of the\n * underlying file:\n *\n * - **Eager mode** — when `file.size` is below `streamThresholdBytes` (default 10 MiB), the\n * reader calls `file.text()` once, splits the content on `\\n`, and caches lines + byte count.\n * All subsequent calls resolve from memory.\n * - **Streaming mode** — when `file.size` meets or exceeds the threshold, the reader streams the\n * file once via `file.stream().getReader()` to build a line-offset index (`number[]` of byte\n * offsets per line), then serves each `line(i)` request by slicing the underlying `Blob` —\n * `Blob.slice(start, end).text()` decodes only the requested range, no head-of-file scan.\n * Caps RAM at one index + one line buffer regardless of file size.\n *\n * The store auto-selects its write API by execution scope:\n *\n * - In **worker scopes** (`self instanceof WorkerGlobalScope`), it acquires a\n * `FileSystemSyncAccessHandle` and writes synchronously. Sync handles are the only API\n * available in workers and the fastest path for the spool-write hot path.\n * - On the **main thread**, it uses `OpfsFileHandle.createWritable()` and the async\n * stream API. Sync access handles are not exposed on the main thread.\n *\n * This module assumes a browser-equivalent runtime — `navigator.storage`,\n * `OpfsFileHandle`, `TextEncoder`/`TextDecoder`, and `Blob` must all exist. It must not\n * be imported from Node code; do so and you will fail at resolve time when `navigator` is\n * referenced.\n *\n * @example\n * ```ts\n * import { OpfsSpoolStore } from '@nhtio/adk/batteries/storage/opfs'\n *\n * const store = new OpfsSpoolStore({ keyPrefix: 'agent-runs/' })\n * const reader = await store.write(callId, bytes)\n * const Ctor = tool.artifactConstructor?.() ?? SpooledArtifact\n * const artifact = new Ctor(reader)\n * ```\n */\n\nimport { isInstanceOf } from '@nhtio/adk/guards'\nimport type { SpoolReader, SpoolStore } from '@nhtio/adk/common'\n\n// The project's tsconfig limits `lib` to `ESNext`, so the DOM and File System Access types\n// referenced below are not in scope by default — neither `tsc --noEmit` nor the downstream dts\n// pipeline (api-extractor) can see them. Re-declare here the **minimum** surface this module\n// touches via a local handle-shape interface. Public API uses `OpfsFileHandle` /\n// `OpfsDirectoryHandle` instead of the DOM globals so the published `.d.ts` is self-contained\n// and consumers do not have to chase the lib graph.\n\n/**\n * Minimal subset of the\n * [File System Access](https://developer.mozilla.org/docs/Web/API/File_System_API)\n * `OpfsFileHandle` interface that this module touches at runtime. Structurally compatible\n * with the DOM-lib `OpfsFileHandle` — at call sites you pass real OPFS handles directly.\n */\nexport interface OpfsFileHandle {\n readonly kind: 'file'\n readonly name: string\n getFile(): Promise<OpfsFile>\n createWritable(): Promise<OpfsWritableFileStream>\n}\n\n/**\n * Minimal subset of the\n * [File System Access](https://developer.mozilla.org/docs/Web/API/File_System_API)\n * `OpfsDirectoryHandle` interface that this module touches at runtime. Structurally\n * compatible with the DOM-lib `OpfsDirectoryHandle` — at call sites you pass real OPFS\n * handles directly.\n */\nexport interface OpfsDirectoryHandle {\n readonly kind: 'directory'\n readonly name: string\n getFileHandle(name: string, options?: { create?: boolean }): Promise<OpfsFileHandle>\n getDirectoryHandle(name: string, options?: { create?: boolean }): Promise<OpfsDirectoryHandle>\n removeEntry(name: string, options?: { recursive?: boolean }): Promise<void>\n}\n\n/**\n * Minimal subset of the DOM `FileSystemWritableFileStream` interface used by the OPFS battery's\n * main-thread write path.\n */\nexport interface OpfsWritableFileStream {\n write(data: Uint8Array | ArrayBufferView | ArrayBuffer | string): Promise<void>\n close(): Promise<void>\n}\n\n/**\n * Minimal subset of the DOM `Blob` interface used by {@link OpfsSpoolReader} streaming-mode\n * random-access reads. Real OPFS handles return a `File` here; we narrow to the methods we\n * actually call.\n */\nexport interface OpfsBlob {\n readonly size: number\n slice(start?: number, end?: number, contentType?: string): OpfsBlob\n text(): Promise<string>\n stream(): OpfsReadableStream\n}\n\n/**\n * Minimal subset of the DOM `File` interface used by {@link OpfsSpoolReader}.\n */\nexport interface OpfsFile extends OpfsBlob {\n readonly name: string\n}\n\n/**\n * Minimal subset of the DOM `ReadableStream<Uint8Array>` interface used by streaming-mode\n * index construction.\n */\nexport interface OpfsReadableStream {\n getReader(): OpfsReadableStreamReader\n}\n\n/**\n * Minimal subset of the DOM `ReadableStreamDefaultReader<Uint8Array>` interface used by\n * streaming-mode index construction.\n */\nexport interface OpfsReadableStreamReader {\n read(): Promise<{ done: false; value: Uint8Array } | { done: true; value: undefined }>\n releaseLock(): void\n}\n\ndeclare const navigator: {\n storage: { getDirectory(): Promise<OpfsDirectoryHandle> }\n}\ndeclare class TextEncoder {\n encode(input?: string): Uint8Array\n}\ndeclare const self: unknown\ndeclare const WorkerGlobalScope: { new (): unknown } | undefined\n\ninterface FileSystemSyncAccessHandle {\n truncate(newSize: number): void\n write(buffer: Uint8Array | ArrayBuffer | ArrayBufferView, options?: { at?: number }): number\n flush(): void\n close(): void\n}\ninterface OpfsFileHandleWithSyncAccess extends OpfsFileHandle {\n createSyncAccessHandle(): Promise<FileSystemSyncAccessHandle>\n}\n\nconst DEFAULT_STREAM_THRESHOLD_BYTES = 10 * 1024 * 1024 // 10 MiB\n\nconst LF = 0x0a // '\\n'\n\nconst isNonNegativeFiniteNumber = (n: unknown): n is number =>\n typeof n === 'number' && Number.isFinite(n) && n >= 0\n\n/**\n * Constructor options for {@link OpfsSpoolReader}.\n */\nexport interface OpfsSpoolReaderOptions {\n /**\n * Byte-length threshold that switches between eager and streaming modes.\n *\n * @remarks\n * - Below the threshold → eager (whole-file in memory).\n * - At or above the threshold → streaming (line-offset index + per-line slice reads).\n *\n * Set to `0` to force streaming mode; set to `Number.POSITIVE_INFINITY` to force eager mode.\n *\n * @defaultValue `10 * 1024 * 1024` (10 MiB)\n */\n streamThresholdBytes?: number\n}\n\ninterface EagerState {\n mode: 'eager'\n lines: string[]\n bytes: number\n content: string\n}\n\ninterface StreamingState {\n mode: 'streaming'\n file: OpfsFile\n /**\n * Byte offsets where each line *starts*. Length equals lineCount + 1; the final entry equals\n * the total byte length. So `offsets[i + 1] - offsets[i]` is the byte length of line `i`\n * including any trailing `\\n`.\n */\n offsets: number[]\n bytes: number\n}\n\ntype ReaderState = EagerState | StreamingState\n\n/**\n * Returns `true` when the current global scope is a Web Worker (`DedicatedWorkerGlobalScope`,\n * `SharedWorkerGlobalScope`, or `ServiceWorkerGlobalScope` all inherit from `WorkerGlobalScope`).\n *\n * @remarks\n * The check is needed at runtime because `FileSystemSyncAccessHandle` is only exposed in worker\n * scopes — calling it from the main thread throws. We pick the write strategy based on the\n * answer here.\n *\n * @internal\n */\nconst isWorkerScope = (): boolean => {\n if (typeof WorkerGlobalScope === 'undefined') return false\n // eslint-disable-next-line adk/use-is-instance-of -- native built-in narrowing on `self`; no cross-realm risk\n return self instanceof WorkerGlobalScope\n}\n\n/**\n * Reads an OPFS-backed file as a {@link @nhtio/adk!SpoolReader}.\n *\n * @remarks\n * Constructor is **not** async — but the first method call awaits a private readiness promise\n * that fetches the underlying `File` (and in eager mode, its contents). Subsequent calls reuse\n * the cached state. This keeps construction call sites synchronous while still doing real I/O\n * lazily.\n *\n * All four `SpoolReader` methods on this reader return promises. The `SpoolReader` contract\n * supports both sync and async return; consumers of `SpooledArtifact` handle either.\n */\nexport class OpfsSpoolReader implements SpoolReader {\n readonly #handle: OpfsFileHandle\n readonly #threshold: number\n #ready: Promise<ReaderState> | undefined\n\n constructor(handle: OpfsFileHandle, opts: OpfsSpoolReaderOptions = {}) {\n this.#handle = handle\n const raw = opts.streamThresholdBytes ?? DEFAULT_STREAM_THRESHOLD_BYTES\n // Allow `Infinity` (forces eager) but reject anything non-finite-negative.\n if (typeof raw !== 'number' || Number.isNaN(raw) || raw < 0) {\n throw new TypeError(\n `OpfsSpoolReader: streamThresholdBytes must be a non-negative number or Infinity, got ${String(raw)}`\n )\n }\n this.#threshold = raw\n }\n\n /**\n * Returns `true` if `value` is an {@link OpfsSpoolReader} instance.\n *\n * @remarks\n * Uses {@link @nhtio/adk!isInstanceOf} for cross-realm safety.\n *\n * @param value - The value to test.\n * @returns `true` when `value` is an {@link OpfsSpoolReader} instance.\n */\n public static isOpfsSpoolReader(value: unknown): value is OpfsSpoolReader {\n return isInstanceOf(value, 'OpfsSpoolReader', OpfsSpoolReader)\n }\n\n async line(index: number): Promise<string | undefined> {\n const state = await this.#load()\n if (state.mode === 'eager') return state.lines[index]\n if (index < 0 || index >= state.offsets.length - 1) return undefined\n return this.#readRange(state.file, state.offsets[index], state.offsets[index + 1])\n }\n\n async byteLength(): Promise<number> {\n const state = await this.#load()\n return state.bytes\n }\n\n async lineCount(): Promise<number> {\n const state = await this.#load()\n return state.mode === 'eager' ? state.lines.length : state.offsets.length - 1\n }\n\n /**\n * Returns the full underlying content as a single decoded string, byte-faithful to the source.\n *\n * @remarks\n * In **eager mode** the content is already cached at first-call load and this method is\n * effectively a property access. In **streaming mode** there is no cache: the file is re-read\n * (as a single `File.text()` call) on every invocation. Use `SpooledArtifact.asString()`\n * judiciously on large streaming-mode artifacts.\n */\n async readAll(): Promise<string> {\n const state = await this.#load()\n if (state.mode === 'eager') return state.content\n return state.file.text()\n }\n\n /**\n * Lazily initialise the reader's mode-specific state. Called by every public method; the\n * promise is cached so the work runs at most once.\n */\n #load(): Promise<ReaderState> {\n if (!this.#ready) this.#ready = this.#init()\n return this.#ready\n }\n\n async #init(): Promise<ReaderState> {\n const file = await this.#handle.getFile()\n const bytes = file.size\n if (!isNonNegativeFiniteNumber(bytes)) {\n throw new Error(`OpfsSpoolReader: file handle returned a non-finite size (${String(bytes)})`)\n }\n if (bytes < this.#threshold) {\n // Eager — pull the whole thing into memory.\n const content = await file.text()\n const lines = content === '' ? [] : content.split('\\n')\n return { mode: 'eager', lines, bytes, content }\n }\n // Streaming — build a line-offset index by scanning bytes once.\n return this.#buildStreamingIndex(file, bytes)\n }\n\n async #buildStreamingIndex(file: OpfsFile, bytes: number): Promise<StreamingState> {\n // Edge case first — an empty file is one offset (the EOF), zero lines.\n if (bytes === 0) return { mode: 'streaming', file, offsets: [0], bytes }\n\n // offsets[i] is the byte position where line `i` starts. offsets[lineCount] is one-past-end.\n // For \"a\\nb\\nc\" → offsets=[0, 2, 4, 5] (3 lines).\n // For \"a\\nb\\n\" → offsets=[0, 2, 4, 4] (3 lines, last is the trailing empty line). This\n // mirrors `String.prototype.split('\\n')` semantics so streaming and eager agree.\n const offsets: number[] = [0]\n let position = 0\n let lastByte = -1\n const reader = file.stream().getReader()\n try {\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n for (const byte of value) {\n position++\n if (byte === LF) offsets.push(position)\n lastByte = byte\n }\n }\n } finally {\n reader.releaseLock()\n }\n // If the file ends on a newline, the byte after the LF is the start of an empty trailing\n // line — record it. If it doesn't, the final line's end is the EOF and we need to push\n // it so line(N-1) can read up to bytes.\n if (lastByte === LF) offsets.push(position)\n else if (offsets[offsets.length - 1] !== position) offsets.push(position)\n return { mode: 'streaming', file, offsets, bytes }\n }\n\n /**\n * Slices the byte range `[start, end)` from the backing file and returns it as a UTF-8\n * string, stripping a trailing `\\n` if present.\n *\n * @remarks\n * `Blob.slice` is O(1) metadata; `Blob.text()` only decodes the slice. The line-offset index\n * brackets each line *with* its trailing LF (so `offsets[i+1]` points at the start of the\n * next line) and the `SpoolReader` contract returns lines *without* their trailing newline,\n * so we strip a single trailing LF if present.\n */\n async #readRange(file: OpfsFile, start: number, end: number): Promise<string> {\n if (start === end) return ''\n const slice = file.slice(start, end)\n const text = await slice.text()\n if (text.length > 0 && text.charCodeAt(text.length - 1) === LF) {\n return text.slice(0, -1)\n }\n return text\n }\n}\n\n/**\n * Constructor options for {@link OpfsSpoolStore}.\n */\nexport interface OpfsSpoolStoreOptions {\n /**\n * Optional thunk that resolves the {@link OpfsDirectoryHandle} used as the store root.\n *\n * @remarks\n * When omitted, the store resolves the root via `navigator.storage.getDirectory()` on its\n * first filesystem call. Override for tests (to point at a per-suite subdirectory) or to\n * scope the store to a nested directory inside OPFS.\n *\n * The thunk is invoked at most once per store; the returned handle is memoised.\n */\n directory?: () => Promise<OpfsDirectoryHandle>\n\n /**\n * Optional filename prefix prepended to every `callId`.\n *\n * @remarks\n * Prefix is a **filename prefix**, not a subdirectory — `keyPrefix: 'agent-runs/'` produces\n * a file literally named `agent-runs/<callId>` at the root, not a nested directory. (OPFS\n * filenames may not contain `/`, so use a non-`/` separator like `-` if you want a flat\n * namespace.) This mirrors the `keyPrefix` semantics in the flydrive and in-memory batteries.\n *\n * @defaultValue `\"\"`\n */\n keyPrefix?: string\n\n /**\n * Default `streamThresholdBytes` for readers produced by `write()` and `read()`. Individual\n * calls may override via their own `opts` argument.\n *\n * @defaultValue `10 * 1024 * 1024` (10 MiB)\n */\n streamThresholdBytes?: number\n}\n\n/**\n * \"Give bytes, get a reader\" persistence layer over an OPFS directory.\n *\n * @remarks\n * `write(callId, bytes)` resolves the root directory (lazily, on first call), opens or creates\n * the file named `keyPrefix + callId`, then writes via the API matching the current scope:\n * a `FileSystemSyncAccessHandle` in worker scopes, `OpfsFileHandle.createWritable()` on\n * the main thread. A fresh {@link OpfsSpoolReader} pointed at the same file is returned.\n *\n * `read(callId)` returns a reader without re-writing; `delete(callId)` removes the entry.\n *\n * The store is otherwise stateless — it owns no in-memory cache of writes. Multiple\n * `OpfsSpoolStore` instances sharing the same root directory and key prefix see the same data.\n *\n * @example\n * ```ts\n * import { OpfsSpoolStore } from '@nhtio/adk/batteries/storage/opfs'\n *\n * const store = new OpfsSpoolStore({ keyPrefix: 'agent-runs/' })\n *\n * const bytes = await tool.executor(ctx)(args)\n * const reader = await store.write(callId, bytes)\n * const Ctor = tool.artifactConstructor?.() ?? SpooledArtifact\n * const artifact = new Ctor(reader)\n * ```\n */\nexport class OpfsSpoolStore implements SpoolStore {\n readonly #resolveRoot: () => Promise<OpfsDirectoryHandle>\n readonly #prefix: string\n readonly #defaultThreshold: number\n #root: OpfsDirectoryHandle | undefined\n\n constructor(opts: OpfsSpoolStoreOptions = {}) {\n this.#resolveRoot = opts.directory ?? (() => navigator.storage.getDirectory())\n this.#prefix = opts.keyPrefix ?? ''\n this.#defaultThreshold = opts.streamThresholdBytes ?? DEFAULT_STREAM_THRESHOLD_BYTES\n }\n\n /**\n * Returns `true` if `value` is an {@link OpfsSpoolStore} instance.\n *\n * @remarks\n * Uses {@link @nhtio/adk!isInstanceOf} for cross-realm safety.\n *\n * @param value - The value to test.\n * @returns `true` when `value` is an {@link OpfsSpoolStore} instance.\n */\n public static isOpfsSpoolStore(value: unknown): value is OpfsSpoolStore {\n return isInstanceOf(value, 'OpfsSpoolStore', OpfsSpoolStore)\n }\n\n /**\n * Persists `bytes` under `callId` and returns a reader bound to the stored key.\n *\n * @remarks\n * `string` input is encoded as UTF-8; `Uint8Array` is stored byte-faithfully;\n * `ReadableStream<Uint8Array>` is written incrementally — the stream is consumed chunk-by-chunk\n * straight to OPFS without first materializing the whole payload in memory, which is the point\n * of accepting a stream for a durable store.\n *\n * @param callId - Identifier used to retrieve the bytes via {@link OpfsSpoolStore.read}.\n * @param bytes - The bytes to store, as a `string`, `Uint8Array`, or `ReadableStream<Uint8Array>`.\n * @param opts - Per-call override for `streamThresholdBytes`.\n * @returns An {@link OpfsSpoolReader} over the stored bytes.\n */\n async write(\n callId: string,\n bytes: string | Uint8Array | ReadableStream<Uint8Array>,\n opts?: OpfsSpoolReaderOptions\n ): Promise<OpfsSpoolReader> {\n const name = this.#keyFor(callId)\n const root = await this.#getRoot()\n const handle = await root.getFileHandle(name, { create: true })\n if (isInstanceOf(bytes, 'ReadableStream', ReadableStream)) {\n if (isWorkerScope()) {\n await this.#writeStreamViaSyncHandle(handle, bytes)\n } else {\n await this.#writeStreamViaWritable(handle, bytes)\n }\n } else {\n const payload = typeof bytes === 'string' ? new TextEncoder().encode(bytes) : bytes\n if (isWorkerScope()) {\n await this.#writeViaSyncHandle(handle, payload)\n } else {\n await this.#writeViaWritable(handle, payload)\n }\n }\n return new OpfsSpoolReader(handle, {\n streamThresholdBytes: opts?.streamThresholdBytes ?? this.#defaultThreshold,\n })\n }\n\n /**\n * Returns a reader over the bytes previously written under `callId`.\n *\n * @remarks\n * Returns `undefined` if the file does not exist.\n *\n * @param callId - Identifier supplied to a prior {@link OpfsSpoolStore.write} call.\n * @param opts - Per-call override for `streamThresholdBytes`.\n * @returns An {@link OpfsSpoolReader}, or `undefined` if the key is missing.\n */\n async read(callId: string, opts?: OpfsSpoolReaderOptions): Promise<OpfsSpoolReader | undefined> {\n const name = this.#keyFor(callId)\n const root = await this.#getRoot()\n let handle: OpfsFileHandle\n try {\n handle = await root.getFileHandle(name)\n } catch (err) {\n if (this.#isNotFoundError(err)) return undefined\n throw err\n }\n return new OpfsSpoolReader(handle, {\n streamThresholdBytes: opts?.streamThresholdBytes ?? this.#defaultThreshold,\n })\n }\n\n /**\n * Removes the entry under `callId`.\n *\n * @param callId - Identifier whose entry should be removed.\n * @returns `true` if the entry existed and was removed; `false` if it didn't exist.\n */\n async delete(callId: string): Promise<boolean> {\n const name = this.#keyFor(callId)\n const root = await this.#getRoot()\n try {\n await root.removeEntry(name)\n return true\n } catch (err) {\n if (this.#isNotFoundError(err)) return false\n throw err\n }\n }\n\n /**\n * Returns `true` if a file is present under `callId`.\n *\n * @param callId - Identifier to test.\n * @returns `true` when the file exists, `false` otherwise.\n */\n async has(callId: string): Promise<boolean> {\n const name = this.#keyFor(callId)\n const root = await this.#getRoot()\n try {\n await root.getFileHandle(name)\n return true\n } catch (err) {\n if (this.#isNotFoundError(err)) return false\n throw err\n }\n }\n\n /**\n * Returns the full filename for a given `callId` (i.e. `keyPrefix + callId`).\n *\n * @remarks\n * Useful for tests or for callers that want to interact with the underlying OPFS directory\n * directly.\n */\n keyFor(callId: string): string {\n return this.#keyFor(callId)\n }\n\n #keyFor(callId: string): string {\n return this.#prefix + callId\n }\n\n async #getRoot(): Promise<OpfsDirectoryHandle> {\n if (!this.#root) this.#root = await this.#resolveRoot()\n return this.#root\n }\n\n async #writeViaSyncHandle(handle: OpfsFileHandle, payload: Uint8Array): Promise<void> {\n const sync = await (handle as OpfsFileHandleWithSyncAccess).createSyncAccessHandle()\n try {\n sync.truncate(0)\n sync.write(payload, { at: 0 })\n sync.flush()\n } finally {\n sync.close()\n }\n }\n\n async #writeViaWritable(handle: OpfsFileHandle, payload: Uint8Array): Promise<void> {\n const writable = await handle.createWritable()\n try {\n await writable.write(payload)\n } finally {\n await writable.close()\n }\n }\n\n async #writeStreamViaWritable(\n handle: OpfsFileHandle,\n stream: ReadableStream<Uint8Array>\n ): Promise<void> {\n const writable = await handle.createWritable()\n try {\n const reader = stream.getReader()\n try {\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n if (value) await writable.write(value)\n }\n } finally {\n reader.releaseLock()\n }\n } finally {\n await writable.close()\n }\n }\n\n async #writeStreamViaSyncHandle(\n handle: OpfsFileHandle,\n stream: ReadableStream<Uint8Array>\n ): Promise<void> {\n const sync = await (handle as OpfsFileHandleWithSyncAccess).createSyncAccessHandle()\n try {\n sync.truncate(0)\n let at = 0\n const reader = stream.getReader()\n try {\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n if (value && value.byteLength > 0) {\n sync.write(value, { at })\n at += value.byteLength\n }\n }\n } finally {\n reader.releaseLock()\n }\n sync.flush()\n } finally {\n sync.close()\n }\n }\n\n #isNotFoundError(err: unknown): boolean {\n if (err === null || typeof err !== 'object') return false\n const name = (err as { name?: unknown }).name\n return name === 'NotFoundError'\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"opfs.mjs","names":["#handle","#threshold","#load","#readRange","#ready","#init","#buildStreamingIndex","#resolveRoot","#prefix","#defaultThreshold","#keyFor","#getRoot","#writeViaSyncHandle","#writeViaWritable","#isNotFoundError","#root"],"sources":["../../../src/batteries/storage/opfs/index.ts"],"sourcesContent":["/**\n * Browser-only Origin Private File System storage for spooled artifacts.\n *\n * @module @nhtio/adk/batteries/storage/opfs\n *\n * @remarks\n * Opt-in **browser-only** storage battery backed by the\n * [Origin Private File System](https://developer.mozilla.org/docs/Web/API/File_System_API/Origin_private_file_system)\n * (OPFS). Provides {@link OpfsSpoolReader} (a {@link @nhtio/adk!SpoolReader} over a `OpfsFileHandle`)\n * and {@link OpfsSpoolStore} (a `write(callId, bytes) → reader` persistence layer that wraps an\n * OPFS directory).\n *\n * The reader has two modes selected lazily on first method invocation based on the size of the\n * underlying file:\n *\n * - **Eager mode** — when `file.size` is below `streamThresholdBytes` (default 10 MiB), the\n * reader calls `file.text()` once, splits the content on `\\n`, and caches lines + byte count.\n * All subsequent calls resolve from memory.\n * - **Streaming mode** — when `file.size` meets or exceeds the threshold, the reader streams the\n * file once via `file.stream().getReader()` to build a line-offset index (`number[]` of byte\n * offsets per line), then serves each `line(i)` request by slicing the underlying `Blob` —\n * `Blob.slice(start, end).text()` decodes only the requested range, no head-of-file scan.\n * Caps RAM at one index + one line buffer regardless of file size.\n *\n * The store auto-selects its write API by execution scope:\n *\n * - In **worker scopes** (`self instanceof WorkerGlobalScope`), it acquires a\n * `FileSystemSyncAccessHandle` and writes synchronously. Sync handles are the only API\n * available in workers and the fastest path for the spool-write hot path.\n * - On the **main thread**, it uses `OpfsFileHandle.createWritable()` and the async\n * stream API. Sync access handles are not exposed on the main thread.\n *\n * This module assumes a browser-equivalent runtime — `navigator.storage`,\n * `OpfsFileHandle`, `TextEncoder`/`TextDecoder`, and `Blob` must all exist. It must not\n * be imported from Node code; do so and you will fail at resolve time when `navigator` is\n * referenced.\n *\n * @example\n * ```ts\n * import { OpfsSpoolStore } from '@nhtio/adk/batteries/storage/opfs'\n *\n * const store = new OpfsSpoolStore({ keyPrefix: 'agent-runs/' })\n * const reader = await store.write(callId, bytes)\n * const Ctor = tool.artifactConstructor?.() ?? SpooledArtifact\n * const artifact = new Ctor(reader)\n * ```\n */\n\nimport { isInstanceOf } from '@nhtio/adk/guards'\nimport type { SpoolReader } from '@nhtio/adk/common'\n\n// The project's tsconfig limits `lib` to `ESNext`, so the DOM and File System Access types\n// referenced below are not in scope by default — neither `tsc --noEmit` nor the downstream dts\n// pipeline (api-extractor) can see them. Re-declare here the **minimum** surface this module\n// touches via a local handle-shape interface. Public API uses `OpfsFileHandle` /\n// `OpfsDirectoryHandle` instead of the DOM globals so the published `.d.ts` is self-contained\n// and consumers do not have to chase the lib graph.\n\n/**\n * Minimal subset of the\n * [File System Access](https://developer.mozilla.org/docs/Web/API/File_System_API)\n * `OpfsFileHandle` interface that this module touches at runtime. Structurally compatible\n * with the DOM-lib `OpfsFileHandle` — at call sites you pass real OPFS handles directly.\n */\nexport interface OpfsFileHandle {\n readonly kind: 'file'\n readonly name: string\n getFile(): Promise<OpfsFile>\n createWritable(): Promise<OpfsWritableFileStream>\n}\n\n/**\n * Minimal subset of the\n * [File System Access](https://developer.mozilla.org/docs/Web/API/File_System_API)\n * `OpfsDirectoryHandle` interface that this module touches at runtime. Structurally\n * compatible with the DOM-lib `OpfsDirectoryHandle` — at call sites you pass real OPFS\n * handles directly.\n */\nexport interface OpfsDirectoryHandle {\n readonly kind: 'directory'\n readonly name: string\n getFileHandle(name: string, options?: { create?: boolean }): Promise<OpfsFileHandle>\n getDirectoryHandle(name: string, options?: { create?: boolean }): Promise<OpfsDirectoryHandle>\n removeEntry(name: string, options?: { recursive?: boolean }): Promise<void>\n}\n\n/**\n * Minimal subset of the DOM `FileSystemWritableFileStream` interface used by the OPFS battery's\n * main-thread write path.\n */\nexport interface OpfsWritableFileStream {\n write(data: Uint8Array | ArrayBufferView | ArrayBuffer | string): Promise<void>\n close(): Promise<void>\n}\n\n/**\n * Minimal subset of the DOM `Blob` interface used by {@link OpfsSpoolReader} streaming-mode\n * random-access reads. Real OPFS handles return a `File` here; we narrow to the methods we\n * actually call.\n */\nexport interface OpfsBlob {\n readonly size: number\n slice(start?: number, end?: number, contentType?: string): OpfsBlob\n text(): Promise<string>\n stream(): OpfsReadableStream\n}\n\n/**\n * Minimal subset of the DOM `File` interface used by {@link OpfsSpoolReader}.\n */\nexport interface OpfsFile extends OpfsBlob {\n readonly name: string\n}\n\n/**\n * Minimal subset of the DOM `ReadableStream<Uint8Array>` interface used by streaming-mode\n * index construction.\n */\nexport interface OpfsReadableStream {\n getReader(): OpfsReadableStreamReader\n}\n\n/**\n * Minimal subset of the DOM `ReadableStreamDefaultReader<Uint8Array>` interface used by\n * streaming-mode index construction.\n */\nexport interface OpfsReadableStreamReader {\n read(): Promise<{ done: false; value: Uint8Array } | { done: true; value: undefined }>\n releaseLock(): void\n}\n\ndeclare const navigator: {\n storage: { getDirectory(): Promise<OpfsDirectoryHandle> }\n}\ndeclare class TextEncoder {\n encode(input?: string): Uint8Array\n}\ndeclare const self: unknown\ndeclare const WorkerGlobalScope: { new (): unknown } | undefined\n\ninterface FileSystemSyncAccessHandle {\n truncate(newSize: number): void\n write(buffer: Uint8Array | ArrayBuffer | ArrayBufferView, options?: { at?: number }): number\n flush(): void\n close(): void\n}\ninterface OpfsFileHandleWithSyncAccess extends OpfsFileHandle {\n createSyncAccessHandle(): Promise<FileSystemSyncAccessHandle>\n}\n\nconst DEFAULT_STREAM_THRESHOLD_BYTES = 10 * 1024 * 1024 // 10 MiB\n\nconst LF = 0x0a // '\\n'\n\nconst isNonNegativeFiniteNumber = (n: unknown): n is number =>\n typeof n === 'number' && Number.isFinite(n) && n >= 0\n\n/**\n * Constructor options for {@link OpfsSpoolReader}.\n */\nexport interface OpfsSpoolReaderOptions {\n /**\n * Byte-length threshold that switches between eager and streaming modes.\n *\n * @remarks\n * - Below the threshold → eager (whole-file in memory).\n * - At or above the threshold → streaming (line-offset index + per-line slice reads).\n *\n * Set to `0` to force streaming mode; set to `Number.POSITIVE_INFINITY` to force eager mode.\n *\n * @defaultValue `10 * 1024 * 1024` (10 MiB)\n */\n streamThresholdBytes?: number\n}\n\ninterface EagerState {\n mode: 'eager'\n lines: string[]\n bytes: number\n content: string\n}\n\ninterface StreamingState {\n mode: 'streaming'\n file: OpfsFile\n /**\n * Byte offsets where each line *starts*. Length equals lineCount + 1; the final entry equals\n * the total byte length. So `offsets[i + 1] - offsets[i]` is the byte length of line `i`\n * including any trailing `\\n`.\n */\n offsets: number[]\n bytes: number\n}\n\ntype ReaderState = EagerState | StreamingState\n\n/**\n * Returns `true` when the current global scope is a Web Worker (`DedicatedWorkerGlobalScope`,\n * `SharedWorkerGlobalScope`, or `ServiceWorkerGlobalScope` all inherit from `WorkerGlobalScope`).\n *\n * @remarks\n * The check is needed at runtime because `FileSystemSyncAccessHandle` is only exposed in worker\n * scopes — calling it from the main thread throws. We pick the write strategy based on the\n * answer here.\n *\n * @internal\n */\nconst isWorkerScope = (): boolean => {\n if (typeof WorkerGlobalScope === 'undefined') return false\n // eslint-disable-next-line adk/use-is-instance-of -- native built-in narrowing on `self`; no cross-realm risk\n return self instanceof WorkerGlobalScope\n}\n\n/**\n * Reads an OPFS-backed file as a {@link @nhtio/adk!SpoolReader}.\n *\n * @remarks\n * Constructor is **not** async — but the first method call awaits a private readiness promise\n * that fetches the underlying `File` (and in eager mode, its contents). Subsequent calls reuse\n * the cached state. This keeps construction call sites synchronous while still doing real I/O\n * lazily.\n *\n * All four `SpoolReader` methods on this reader return promises. The `SpoolReader` contract\n * supports both sync and async return; consumers of `SpooledArtifact` handle either.\n */\nexport class OpfsSpoolReader implements SpoolReader {\n readonly #handle: OpfsFileHandle\n readonly #threshold: number\n #ready: Promise<ReaderState> | undefined\n\n constructor(handle: OpfsFileHandle, opts: OpfsSpoolReaderOptions = {}) {\n this.#handle = handle\n const raw = opts.streamThresholdBytes ?? DEFAULT_STREAM_THRESHOLD_BYTES\n // Allow `Infinity` (forces eager) but reject anything non-finite-negative.\n if (typeof raw !== 'number' || Number.isNaN(raw) || raw < 0) {\n throw new TypeError(\n `OpfsSpoolReader: streamThresholdBytes must be a non-negative number or Infinity, got ${String(raw)}`\n )\n }\n this.#threshold = raw\n }\n\n /**\n * Returns `true` if `value` is an {@link OpfsSpoolReader} instance.\n *\n * @remarks\n * Uses {@link @nhtio/adk!isInstanceOf} for cross-realm safety.\n *\n * @param value - The value to test.\n * @returns `true` when `value` is an {@link OpfsSpoolReader} instance.\n */\n public static isOpfsSpoolReader(value: unknown): value is OpfsSpoolReader {\n return isInstanceOf(value, 'OpfsSpoolReader', OpfsSpoolReader)\n }\n\n async line(index: number): Promise<string | undefined> {\n const state = await this.#load()\n if (state.mode === 'eager') return state.lines[index]\n if (index < 0 || index >= state.offsets.length - 1) return undefined\n return this.#readRange(state.file, state.offsets[index], state.offsets[index + 1])\n }\n\n async byteLength(): Promise<number> {\n const state = await this.#load()\n return state.bytes\n }\n\n async lineCount(): Promise<number> {\n const state = await this.#load()\n return state.mode === 'eager' ? state.lines.length : state.offsets.length - 1\n }\n\n /**\n * Returns the full underlying content as a single decoded string, byte-faithful to the source.\n *\n * @remarks\n * In **eager mode** the content is already cached at first-call load and this method is\n * effectively a property access. In **streaming mode** there is no cache: the file is re-read\n * (as a single `File.text()` call) on every invocation. Use `SpooledArtifact.asString()`\n * judiciously on large streaming-mode artifacts.\n */\n async readAll(): Promise<string> {\n const state = await this.#load()\n if (state.mode === 'eager') return state.content\n return state.file.text()\n }\n\n /**\n * Lazily initialise the reader's mode-specific state. Called by every public method; the\n * promise is cached so the work runs at most once.\n */\n #load(): Promise<ReaderState> {\n if (!this.#ready) this.#ready = this.#init()\n return this.#ready\n }\n\n async #init(): Promise<ReaderState> {\n const file = await this.#handle.getFile()\n const bytes = file.size\n if (!isNonNegativeFiniteNumber(bytes)) {\n throw new Error(`OpfsSpoolReader: file handle returned a non-finite size (${String(bytes)})`)\n }\n if (bytes < this.#threshold) {\n // Eager — pull the whole thing into memory.\n const content = await file.text()\n const lines = content === '' ? [] : content.split('\\n')\n return { mode: 'eager', lines, bytes, content }\n }\n // Streaming — build a line-offset index by scanning bytes once.\n return this.#buildStreamingIndex(file, bytes)\n }\n\n async #buildStreamingIndex(file: OpfsFile, bytes: number): Promise<StreamingState> {\n // Edge case first — an empty file is one offset (the EOF), zero lines.\n if (bytes === 0) return { mode: 'streaming', file, offsets: [0], bytes }\n\n // offsets[i] is the byte position where line `i` starts. offsets[lineCount] is one-past-end.\n // For \"a\\nb\\nc\" → offsets=[0, 2, 4, 5] (3 lines).\n // For \"a\\nb\\n\" → offsets=[0, 2, 4, 4] (3 lines, last is the trailing empty line). This\n // mirrors `String.prototype.split('\\n')` semantics so streaming and eager agree.\n const offsets: number[] = [0]\n let position = 0\n let lastByte = -1\n const reader = file.stream().getReader()\n try {\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n for (const byte of value) {\n position++\n if (byte === LF) offsets.push(position)\n lastByte = byte\n }\n }\n } finally {\n reader.releaseLock()\n }\n // If the file ends on a newline, the byte after the LF is the start of an empty trailing\n // line — record it. If it doesn't, the final line's end is the EOF and we need to push\n // it so line(N-1) can read up to bytes.\n if (lastByte === LF) offsets.push(position)\n else if (offsets[offsets.length - 1] !== position) offsets.push(position)\n return { mode: 'streaming', file, offsets, bytes }\n }\n\n /**\n * Slices the byte range `[start, end)` from the backing file and returns it as a UTF-8\n * string, stripping a trailing `\\n` if present.\n *\n * @remarks\n * `Blob.slice` is O(1) metadata; `Blob.text()` only decodes the slice. The line-offset index\n * brackets each line *with* its trailing LF (so `offsets[i+1]` points at the start of the\n * next line) and the `SpoolReader` contract returns lines *without* their trailing newline,\n * so we strip a single trailing LF if present.\n */\n async #readRange(file: OpfsFile, start: number, end: number): Promise<string> {\n if (start === end) return ''\n const slice = file.slice(start, end)\n const text = await slice.text()\n if (text.length > 0 && text.charCodeAt(text.length - 1) === LF) {\n return text.slice(0, -1)\n }\n return text\n }\n}\n\n/**\n * Constructor options for {@link OpfsSpoolStore}.\n */\nexport interface OpfsSpoolStoreOptions {\n /**\n * Optional thunk that resolves the {@link OpfsDirectoryHandle} used as the store root.\n *\n * @remarks\n * When omitted, the store resolves the root via `navigator.storage.getDirectory()` on its\n * first filesystem call. Override for tests (to point at a per-suite subdirectory) or to\n * scope the store to a nested directory inside OPFS.\n *\n * The thunk is invoked at most once per store; the returned handle is memoised.\n */\n directory?: () => Promise<OpfsDirectoryHandle>\n\n /**\n * Optional filename prefix prepended to every `callId`.\n *\n * @remarks\n * Prefix is a **filename prefix**, not a subdirectory — `keyPrefix: 'agent-runs/'` produces\n * a file literally named `agent-runs/<callId>` at the root, not a nested directory. (OPFS\n * filenames may not contain `/`, so use a non-`/` separator like `-` if you want a flat\n * namespace.) This mirrors the `keyPrefix` semantics in the flydrive and in-memory batteries.\n *\n * @defaultValue `\"\"`\n */\n keyPrefix?: string\n\n /**\n * Default `streamThresholdBytes` for readers produced by `write()` and `read()`. Individual\n * calls may override via their own `opts` argument.\n *\n * @defaultValue `10 * 1024 * 1024` (10 MiB)\n */\n streamThresholdBytes?: number\n}\n\n/**\n * \"Give bytes, get a reader\" persistence layer over an OPFS directory.\n *\n * @remarks\n * `write(callId, bytes)` resolves the root directory (lazily, on first call), opens or creates\n * the file named `keyPrefix + callId`, then writes via the API matching the current scope:\n * a `FileSystemSyncAccessHandle` in worker scopes, `OpfsFileHandle.createWritable()` on\n * the main thread. A fresh {@link OpfsSpoolReader} pointed at the same file is returned.\n *\n * `read(callId)` returns a reader without re-writing; `delete(callId)` removes the entry.\n *\n * The store is otherwise stateless — it owns no in-memory cache of writes. Multiple\n * `OpfsSpoolStore` instances sharing the same root directory and key prefix see the same data.\n *\n * @example\n * ```ts\n * import { OpfsSpoolStore } from '@nhtio/adk/batteries/storage/opfs'\n *\n * const store = new OpfsSpoolStore({ keyPrefix: 'agent-runs/' })\n *\n * const bytes = await tool.executor(ctx)(args)\n * const reader = await store.write(callId, bytes)\n * const Ctor = tool.artifactConstructor?.() ?? SpooledArtifact\n * const artifact = new Ctor(reader)\n * ```\n */\nexport class OpfsSpoolStore {\n readonly #resolveRoot: () => Promise<OpfsDirectoryHandle>\n readonly #prefix: string\n readonly #defaultThreshold: number\n #root: OpfsDirectoryHandle | undefined\n\n constructor(opts: OpfsSpoolStoreOptions = {}) {\n this.#resolveRoot = opts.directory ?? (() => navigator.storage.getDirectory())\n this.#prefix = opts.keyPrefix ?? ''\n this.#defaultThreshold = opts.streamThresholdBytes ?? DEFAULT_STREAM_THRESHOLD_BYTES\n }\n\n /**\n * Returns `true` if `value` is an {@link OpfsSpoolStore} instance.\n *\n * @remarks\n * Uses {@link @nhtio/adk!isInstanceOf} for cross-realm safety.\n *\n * @param value - The value to test.\n * @returns `true` when `value` is an {@link OpfsSpoolStore} instance.\n */\n public static isOpfsSpoolStore(value: unknown): value is OpfsSpoolStore {\n return isInstanceOf(value, 'OpfsSpoolStore', OpfsSpoolStore)\n }\n\n /**\n * Persists `bytes` under `callId` and returns a reader bound to the stored key.\n *\n * @param callId - Identifier used to retrieve the bytes via {@link OpfsSpoolStore.read}.\n * @param bytes - The bytes to store, as a `string` or `Uint8Array`.\n * @param opts - Per-call override for `streamThresholdBytes`.\n * @returns An {@link OpfsSpoolReader} over the stored bytes.\n */\n async write(\n callId: string,\n bytes: string | Uint8Array,\n opts?: OpfsSpoolReaderOptions\n ): Promise<OpfsSpoolReader> {\n const name = this.#keyFor(callId)\n const root = await this.#getRoot()\n const handle = await root.getFileHandle(name, { create: true })\n const payload = typeof bytes === 'string' ? new TextEncoder().encode(bytes) : bytes\n if (isWorkerScope()) {\n await this.#writeViaSyncHandle(handle, payload)\n } else {\n await this.#writeViaWritable(handle, payload)\n }\n return new OpfsSpoolReader(handle, {\n streamThresholdBytes: opts?.streamThresholdBytes ?? this.#defaultThreshold,\n })\n }\n\n /**\n * Returns a reader over the bytes previously written under `callId`.\n *\n * @remarks\n * Returns `undefined` if the file does not exist.\n *\n * @param callId - Identifier supplied to a prior {@link OpfsSpoolStore.write} call.\n * @param opts - Per-call override for `streamThresholdBytes`.\n * @returns An {@link OpfsSpoolReader}, or `undefined` if the key is missing.\n */\n async read(callId: string, opts?: OpfsSpoolReaderOptions): Promise<OpfsSpoolReader | undefined> {\n const name = this.#keyFor(callId)\n const root = await this.#getRoot()\n let handle: OpfsFileHandle\n try {\n handle = await root.getFileHandle(name)\n } catch (err) {\n if (this.#isNotFoundError(err)) return undefined\n throw err\n }\n return new OpfsSpoolReader(handle, {\n streamThresholdBytes: opts?.streamThresholdBytes ?? this.#defaultThreshold,\n })\n }\n\n /**\n * Removes the entry under `callId`.\n *\n * @param callId - Identifier whose entry should be removed.\n * @returns `true` if the entry existed and was removed; `false` if it didn't exist.\n */\n async delete(callId: string): Promise<boolean> {\n const name = this.#keyFor(callId)\n const root = await this.#getRoot()\n try {\n await root.removeEntry(name)\n return true\n } catch (err) {\n if (this.#isNotFoundError(err)) return false\n throw err\n }\n }\n\n /**\n * Returns `true` if a file is present under `callId`.\n *\n * @param callId - Identifier to test.\n * @returns `true` when the file exists, `false` otherwise.\n */\n async has(callId: string): Promise<boolean> {\n const name = this.#keyFor(callId)\n const root = await this.#getRoot()\n try {\n await root.getFileHandle(name)\n return true\n } catch (err) {\n if (this.#isNotFoundError(err)) return false\n throw err\n }\n }\n\n /**\n * Returns the full filename for a given `callId` (i.e. `keyPrefix + callId`).\n *\n * @remarks\n * Useful for tests or for callers that want to interact with the underlying OPFS directory\n * directly.\n */\n keyFor(callId: string): string {\n return this.#keyFor(callId)\n }\n\n #keyFor(callId: string): string {\n return this.#prefix + callId\n }\n\n async #getRoot(): Promise<OpfsDirectoryHandle> {\n if (!this.#root) this.#root = await this.#resolveRoot()\n return this.#root\n }\n\n async #writeViaSyncHandle(handle: OpfsFileHandle, payload: Uint8Array): Promise<void> {\n const sync = await (handle as OpfsFileHandleWithSyncAccess).createSyncAccessHandle()\n try {\n sync.truncate(0)\n sync.write(payload, { at: 0 })\n sync.flush()\n } finally {\n sync.close()\n }\n }\n\n async #writeViaWritable(handle: OpfsFileHandle, payload: Uint8Array): Promise<void> {\n const writable = await handle.createWritable()\n try {\n await writable.write(payload)\n } finally {\n await writable.close()\n }\n }\n\n #isNotFoundError(err: unknown): boolean {\n if (err === null || typeof err !== 'object') return false\n const name = (err as { name?: unknown }).name\n return name === 'NotFoundError'\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsJA,IAAM,iCAAiC,KAAK,OAAO;AAEnD,IAAM,KAAK;AAEX,IAAM,6BAA6B,MACjC,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC,KAAK,KAAK;;;;;;;;;;;;AAoDtD,IAAM,sBAA+B;CACnC,IAAI,OAAO,sBAAsB,aAAa,OAAO;CAErD,OAAO,gBAAgB;AACzB;;;;;;;;;;;;;AAcA,IAAa,kBAAb,MAAa,gBAAuC;CAClD;CACA;CACA;CAEA,YAAY,QAAwB,OAA+B,CAAC,GAAG;EACrE,KAAKA,UAAU;EACf,MAAM,MAAM,KAAK,wBAAwB;EAEzC,IAAI,OAAO,QAAQ,YAAY,OAAO,MAAM,GAAG,KAAK,MAAM,GACxD,MAAM,IAAI,UACR,wFAAwF,OAAO,GAAG,GACpG;EAEF,KAAKC,aAAa;CACpB;;;;;;;;;;CAWA,OAAc,kBAAkB,OAA0C;EACxE,OAAO,aAAa,OAAO,mBAAmB,eAAe;CAC/D;CAEA,MAAM,KAAK,OAA4C;EACrD,MAAM,QAAQ,MAAM,KAAKC,MAAM;EAC/B,IAAI,MAAM,SAAS,SAAS,OAAO,MAAM,MAAM;EAC/C,IAAI,QAAQ,KAAK,SAAS,MAAM,QAAQ,SAAS,GAAG,OAAO,KAAA;EAC3D,OAAO,KAAKC,WAAW,MAAM,MAAM,MAAM,QAAQ,QAAQ,MAAM,QAAQ,QAAQ,EAAE;CACnF;CAEA,MAAM,aAA8B;EAElC,QAAO,MADa,KAAKD,MAAM,GAClB;CACf;CAEA,MAAM,YAA6B;EACjC,MAAM,QAAQ,MAAM,KAAKA,MAAM;EAC/B,OAAO,MAAM,SAAS,UAAU,MAAM,MAAM,SAAS,MAAM,QAAQ,SAAS;CAC9E;;;;;;;;;;CAWA,MAAM,UAA2B;EAC/B,MAAM,QAAQ,MAAM,KAAKA,MAAM;EAC/B,IAAI,MAAM,SAAS,SAAS,OAAO,MAAM;EACzC,OAAO,MAAM,KAAK,KAAK;CACzB;;;;;CAMA,QAA8B;EAC5B,IAAI,CAAC,KAAKE,QAAQ,KAAKA,SAAS,KAAKC,MAAM;EAC3C,OAAO,KAAKD;CACd;CAEA,MAAMC,QAA8B;EAClC,MAAM,OAAO,MAAM,KAAKL,QAAQ,QAAQ;EACxC,MAAM,QAAQ,KAAK;EACnB,IAAI,CAAC,0BAA0B,KAAK,GAClC,MAAM,IAAI,MAAM,4DAA4D,OAAO,KAAK,EAAE,EAAE;EAE9F,IAAI,QAAQ,KAAKC,YAAY;GAE3B,MAAM,UAAU,MAAM,KAAK,KAAK;GAEhC,OAAO;IAAE,MAAM;IAAS,OADV,YAAY,KAAK,CAAC,IAAI,QAAQ,MAAM,IAAI;IACvB;IAAO;GAAQ;EAChD;EAEA,OAAO,KAAKK,qBAAqB,MAAM,KAAK;CAC9C;CAEA,MAAMA,qBAAqB,MAAgB,OAAwC;EAEjF,IAAI,UAAU,GAAG,OAAO;GAAE,MAAM;GAAa;GAAM,SAAS,CAAC,CAAC;GAAG;EAAM;EAMvE,MAAM,UAAoB,CAAC,CAAC;EAC5B,IAAI,WAAW;EACf,IAAI,WAAW;EACf,MAAM,SAAS,KAAK,OAAO,EAAE,UAAU;EACvC,IAAI;GACF,SAAS;IACP,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,KAAK;IAC1C,IAAI,MAAM;IACV,KAAK,MAAM,QAAQ,OAAO;KACxB;KACA,IAAI,SAAS,IAAI,QAAQ,KAAK,QAAQ;KACtC,WAAW;IACb;GACF;EACF,UAAU;GACR,OAAO,YAAY;EACrB;EAIA,IAAI,aAAa,IAAI,QAAQ,KAAK,QAAQ;OACrC,IAAI,QAAQ,QAAQ,SAAS,OAAO,UAAU,QAAQ,KAAK,QAAQ;EACxE,OAAO;GAAE,MAAM;GAAa;GAAM;GAAS;EAAM;CACnD;;;;;;;;;;;CAYA,MAAMH,WAAW,MAAgB,OAAe,KAA8B;EAC5E,IAAI,UAAU,KAAK,OAAO;EAE1B,MAAM,OAAO,MADC,KAAK,MAAM,OAAO,GACb,EAAM,KAAK;EAC9B,IAAI,KAAK,SAAS,KAAK,KAAK,WAAW,KAAK,SAAS,CAAC,MAAM,IAC1D,OAAO,KAAK,MAAM,GAAG,EAAE;EAEzB,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkEA,IAAa,iBAAb,MAAa,eAAe;CAC1B;CACA;CACA;CACA;CAEA,YAAY,OAA8B,CAAC,GAAG;EAC5C,KAAKI,eAAe,KAAK,oBAAoB,UAAU,QAAQ,aAAa;EAC5E,KAAKC,UAAU,KAAK,aAAa;EACjC,KAAKC,oBAAoB,KAAK,wBAAwB;CACxD;;;;;;;;;;CAWA,OAAc,iBAAiB,OAAyC;EACtE,OAAO,aAAa,OAAO,kBAAkB,cAAc;CAC7D;;;;;;;;;CAUA,MAAM,MACJ,QACA,OACA,MAC0B;EAC1B,MAAM,OAAO,KAAKC,QAAQ,MAAM;EAEhC,MAAM,SAAS,OAAM,MADF,KAAKC,SAAS,GACP,cAAc,MAAM,EAAE,QAAQ,KAAK,CAAC;EAC9D,MAAM,UAAU,OAAO,UAAU,WAAW,IAAI,YAAY,EAAE,OAAO,KAAK,IAAI;EAC9E,IAAI,cAAc,GAChB,MAAM,KAAKC,oBAAoB,QAAQ,OAAO;OAE9C,MAAM,KAAKC,kBAAkB,QAAQ,OAAO;EAE9C,OAAO,IAAI,gBAAgB,QAAQ,EACjC,sBAAsB,MAAM,wBAAwB,KAAKJ,kBAC3D,CAAC;CACH;;;;;;;;;;;CAYA,MAAM,KAAK,QAAgB,MAAqE;EAC9F,MAAM,OAAO,KAAKC,QAAQ,MAAM;EAChC,MAAM,OAAO,MAAM,KAAKC,SAAS;EACjC,IAAI;EACJ,IAAI;GACF,SAAS,MAAM,KAAK,cAAc,IAAI;EACxC,SAAS,KAAK;GACZ,IAAI,KAAKG,iBAAiB,GAAG,GAAG,OAAO,KAAA;GACvC,MAAM;EACR;EACA,OAAO,IAAI,gBAAgB,QAAQ,EACjC,sBAAsB,MAAM,wBAAwB,KAAKL,kBAC3D,CAAC;CACH;;;;;;;CAQA,MAAM,OAAO,QAAkC;EAC7C,MAAM,OAAO,KAAKC,QAAQ,MAAM;EAChC,MAAM,OAAO,MAAM,KAAKC,SAAS;EACjC,IAAI;GACF,MAAM,KAAK,YAAY,IAAI;GAC3B,OAAO;EACT,SAAS,KAAK;GACZ,IAAI,KAAKG,iBAAiB,GAAG,GAAG,OAAO;GACvC,MAAM;EACR;CACF;;;;;;;CAQA,MAAM,IAAI,QAAkC;EAC1C,MAAM,OAAO,KAAKJ,QAAQ,MAAM;EAChC,MAAM,OAAO,MAAM,KAAKC,SAAS;EACjC,IAAI;GACF,MAAM,KAAK,cAAc,IAAI;GAC7B,OAAO;EACT,SAAS,KAAK;GACZ,IAAI,KAAKG,iBAAiB,GAAG,GAAG,OAAO;GACvC,MAAM;EACR;CACF;;;;;;;;CASA,OAAO,QAAwB;EAC7B,OAAO,KAAKJ,QAAQ,MAAM;CAC5B;CAEA,QAAQ,QAAwB;EAC9B,OAAO,KAAKF,UAAU;CACxB;CAEA,MAAMG,WAAyC;EAC7C,IAAI,CAAC,KAAKI,OAAO,KAAKA,QAAQ,MAAM,KAAKR,aAAa;EACtD,OAAO,KAAKQ;CACd;CAEA,MAAMH,oBAAoB,QAAwB,SAAoC;EACpF,MAAM,OAAO,MAAO,OAAwC,uBAAuB;EACnF,IAAI;GACF,KAAK,SAAS,CAAC;GACf,KAAK,MAAM,SAAS,EAAE,IAAI,EAAE,CAAC;GAC7B,KAAK,MAAM;EACb,UAAU;GACR,KAAK,MAAM;EACb;CACF;CAEA,MAAMC,kBAAkB,QAAwB,SAAoC;EAClF,MAAM,WAAW,MAAM,OAAO,eAAe;EAC7C,IAAI;GACF,MAAM,SAAS,MAAM,OAAO;EAC9B,UAAU;GACR,MAAM,SAAS,MAAM;EACvB;CACF;CAEA,iBAAiB,KAAuB;EACtC,IAAI,QAAQ,QAAQ,OAAO,QAAQ,UAAU,OAAO;EAEpD,OADc,IAA2B,SACzB;CAClB;AACF"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
require("../../
|
|
3
|
-
|
|
4
|
-
require("../../
|
|
2
|
+
require("../../chunk-KmRHZBOW.js");
|
|
3
|
+
require("../../common-Od8edUXU.js");
|
|
4
|
+
const require_tool = require("../../tool-COSeH8I6.js");
|
|
5
5
|
let _nhtio_validation = require("@nhtio/validation");
|
|
6
6
|
//#region src/batteries/tools/color/index.ts
|
|
7
7
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import "../../
|
|
1
|
+
import "../../common-DeZaonK1.mjs";
|
|
2
|
+
import { t as Tool } from "../../tool-D2WB1EA1.mjs";
|
|
3
3
|
import { validator } from "@nhtio/validation";
|
|
4
4
|
//#region src/batteries/tools/color/index.ts
|
|
5
5
|
/**
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
require("../../common-
|
|
2
|
+
require("../../chunk-KmRHZBOW.js");
|
|
3
|
+
const require_tool_registry = require("../../tool_registry-Dkfprsck.js");
|
|
4
|
+
require("../../common-Od8edUXU.js");
|
|
5
|
+
const require_tool = require("../../tool-COSeH8I6.js");
|
|
5
6
|
require("../../guards.cjs");
|
|
6
7
|
let _nhtio_validation = require("@nhtio/validation");
|
|
7
8
|
//#region src/batteries/tools/comparison/index.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comparison.cjs","names":[],"sources":["../../../src/batteries/tools/comparison/index.ts"],"sourcesContent":["/**\n * Pre-constructed tools for comparing primitive values, arrays, and ranges.\n *\n * @module @nhtio/adk/batteries/tools/comparison\n *\n * @remarks\n * Pre-constructed bundled tools for the `comparison` category. Import individually, the whole\n * category, or import every tool via `@nhtio/adk/batteries`.\n */\n\nimport { Tool } from '@nhtio/adk/common'\nimport { isError } from '@nhtio/adk/guards'\nimport { validator } from '@nhtio/validation'\n\ntype Primitive = string | number | boolean | null | undefined\n\nfunction coerce(value: Primitive): number | string | boolean | null {\n if (value === null || value === undefined) return null\n if (typeof value === 'number' || typeof value === 'boolean') return value\n const n = Number(value)\n if (!Number.isNaN(n) && value.trim() !== '') return n\n return value\n}\n\nfunction compareTwo(a: Primitive, b: Primitive): number {\n const ca = coerce(a)\n const cb = coerce(b)\n if (ca === null && cb === null) return 0\n if (ca === null) return -1\n if (cb === null) return 1\n if (typeof ca === 'number' && typeof cb === 'number') return ca - cb\n return String(ca).localeCompare(String(cb))\n}\n\n/**\n * Compare two values and return their relationship (`equal`, `greater_than`, `less_than`).\n *\n * @remarks\n * Handles numbers, strings (lexicographic), booleans, ISO date strings, and null/undefined.\n * Use `type_hint` to force a specific interpretation; the default `auto` infers from the\n * values themselves.\n */\nexport const compareValuesTool = new Tool({\n name: 'compare_values',\n description:\n 'Compare two values and return their relationship: equal, greater_than, less_than, or not_comparable. Handles numbers, strings (lexicographic), booleans, dates (ISO strings), null/undefined.',\n inputSchema: validator.object({\n a: validator.any().required().description('First value to compare'),\n b: validator.any().required().description('Second value to compare'),\n type_hint: validator\n .string()\n .valid('auto', 'string', 'number', 'date')\n .default('auto')\n .description(\n 'Force interpretation: \"auto\" detects, \"date\" parses ISO date strings, \"string\" compares lexicographically, \"number\" coerces to numbers (default: auto)'\n ),\n case_insensitive: validator\n .boolean()\n .default(false)\n .description('For string comparisons: ignore case (default: false)'),\n }),\n handler: async (args) => {\n const {\n a: rawA,\n b: rawB,\n type_hint: typeHint,\n case_insensitive: ci,\n } = args as {\n a: unknown\n b: unknown\n type_hint: string\n case_insensitive: boolean\n }\n\n let av: unknown = rawA\n let bv: unknown = rawB\n\n try {\n if (typeHint === 'date') {\n const da = new Date(av as string)\n const db = new Date(bv as string)\n if (Number.isNaN(da.getTime())) return `Error: Cannot parse \"${av}\" as a date.`\n if (Number.isNaN(db.getTime())) return `Error: Cannot parse \"${bv}\" as a date.`\n const diff = da.getTime() - db.getTime()\n const rel = diff === 0 ? 'equal' : diff > 0 ? 'greater_than' : 'less_than'\n return `${rel}\\na = ${da.toISOString()}\\nb = ${db.toISOString()}`\n }\n\n if (typeHint === 'number') {\n const na = Number(av)\n const nb = Number(bv)\n if (Number.isNaN(na)) return `Error: Cannot convert \"${av}\" to a number.`\n if (Number.isNaN(nb)) return `Error: Cannot convert \"${bv}\" to a number.`\n const diff = na - nb\n return diff === 0 ? 'equal' : diff > 0 ? 'greater_than' : 'less_than'\n }\n\n if (typeHint === 'string') {\n const sa = ci ? String(av).toLowerCase() : String(av)\n const sb = ci ? String(bv).toLowerCase() : String(bv)\n const cmp = sa.localeCompare(sb)\n return cmp === 0 ? 'equal' : cmp > 0 ? 'greater_than' : 'less_than'\n }\n\n // auto\n if (ci && typeof av === 'string' && typeof bv === 'string') {\n av = av.toLowerCase()\n bv = bv.toLowerCase()\n }\n const cmp = compareTwo(av as Primitive, bv as Primitive)\n return cmp === 0 ? 'equal' : cmp > 0 ? 'greater_than' : 'less_than'\n } catch (err) {\n return `Error: ${isError(err) ? err.message : String(err)}`\n }\n },\n})\n\n/**\n * Compare two JSON objects and report keys only in A, keys only in B, keys with matching\n * values, and keys with differing values.\n *\n * @remarks\n * Accepts JSON strings (not pre-parsed objects) so the schema can validate the input\n * shape uniformly across providers. When `deep` is true (default), nested objects and arrays\n * are compared structurally; when false, comparison reduces to JSON serialisation equality.\n */\nexport const compareRecordsTool = new Tool({\n name: 'compare_records',\n description:\n 'Compare two JSON objects and report: keys only in A, keys only in B, keys present in both with the same value, and keys present in both with different values.',\n inputSchema: validator.object({\n record_a: validator.string().required().description('First JSON object (as a string)'),\n record_b: validator.string().required().description('Second JSON object (as a string)'),\n deep: validator\n .boolean()\n .default(true)\n .description(\n 'Use deep equality for nested values (default: true). When false, compares by JSON serialization.'\n ),\n }),\n handler: async (args) => {\n const {\n record_a: recordA,\n record_b: recordB,\n deep,\n } = args as { record_a: string; record_b: string; deep: boolean }\n\n let a: Record<string, unknown>\n let b: Record<string, unknown>\n\n try {\n a = JSON.parse(recordA) as Record<string, unknown>\n } catch {\n return 'Error: record_a is not valid JSON.'\n }\n try {\n b = JSON.parse(recordB) as Record<string, unknown>\n } catch {\n return 'Error: record_b is not valid JSON.'\n }\n\n if (typeof a !== 'object' || a === null || Array.isArray(a))\n return 'Error: record_a must be a JSON object (not array or primitive).'\n if (typeof b !== 'object' || b === null || Array.isArray(b))\n return 'Error: record_b must be a JSON object (not array or primitive).'\n\n function isEqual(x: unknown, y: unknown): boolean {\n if (!deep) return JSON.stringify(x) === JSON.stringify(y)\n if (x === y) return true\n if (typeof x !== typeof y) return false\n if (x === null || y === null) return x === y\n if (Array.isArray(x) && Array.isArray(y)) {\n if (x.length !== y.length) return false\n for (const [i, element] of x.entries()) {\n if (!isEqual(element, y[i])) return false\n }\n return true\n }\n if (typeof x === 'object' && typeof y === 'object') {\n const xk = Object.keys(x as object).sort()\n const yk = Object.keys(y as object).sort()\n if (JSON.stringify(xk) !== JSON.stringify(yk)) return false\n for (const k of xk) {\n if (!isEqual((x as Record<string, unknown>)[k], (y as Record<string, unknown>)[k]))\n return false\n }\n return true\n }\n return false\n }\n\n const keysA = new Set(Object.keys(a))\n const keysB = new Set(Object.keys(b))\n\n const onlyInA: string[] = []\n const onlyInB: string[] = []\n const same: string[] = []\n const different: Array<{ key: string; a: unknown; b: unknown }> = []\n\n for (const k of keysA) {\n if (!keysB.has(k)) {\n onlyInA.push(k)\n } else if (isEqual(a[k], b[k])) {\n same.push(k)\n } else {\n different.push({ key: k, a: a[k], b: b[k] })\n }\n }\n\n for (const k of keysB) {\n if (!keysA.has(k)) onlyInB.push(k)\n }\n\n const lines: string[] = []\n\n if (onlyInA.length > 0) lines.push(`Only in A (${onlyInA.length}): ${onlyInA.join(', ')}`)\n if (onlyInB.length > 0) lines.push(`Only in B (${onlyInB.length}): ${onlyInB.join(', ')}`)\n if (same.length > 0) lines.push(`Same value (${same.length}): ${same.join(', ')}`)\n\n if (different.length > 0) {\n lines.push(`Different value (${different.length}):`)\n for (const { key, a: av, b: bv } of different) {\n lines.push(` ${key}: A=${JSON.stringify(av)}, B=${JSON.stringify(bv)}`)\n }\n }\n\n if (lines.length === 0) return 'Records are identical.'\n return lines.join('\\n')\n },\n})\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"comparison.cjs","names":[],"sources":["../../../src/batteries/tools/comparison/index.ts"],"sourcesContent":["/**\n * Pre-constructed tools for comparing primitive values, arrays, and ranges.\n *\n * @module @nhtio/adk/batteries/tools/comparison\n *\n * @remarks\n * Pre-constructed bundled tools for the `comparison` category. Import individually, the whole\n * category, or import every tool via `@nhtio/adk/batteries`.\n */\n\nimport { Tool } from '@nhtio/adk/common'\nimport { isError } from '@nhtio/adk/guards'\nimport { validator } from '@nhtio/validation'\n\ntype Primitive = string | number | boolean | null | undefined\n\nfunction coerce(value: Primitive): number | string | boolean | null {\n if (value === null || value === undefined) return null\n if (typeof value === 'number' || typeof value === 'boolean') return value\n const n = Number(value)\n if (!Number.isNaN(n) && value.trim() !== '') return n\n return value\n}\n\nfunction compareTwo(a: Primitive, b: Primitive): number {\n const ca = coerce(a)\n const cb = coerce(b)\n if (ca === null && cb === null) return 0\n if (ca === null) return -1\n if (cb === null) return 1\n if (typeof ca === 'number' && typeof cb === 'number') return ca - cb\n return String(ca).localeCompare(String(cb))\n}\n\n/**\n * Compare two values and return their relationship (`equal`, `greater_than`, `less_than`).\n *\n * @remarks\n * Handles numbers, strings (lexicographic), booleans, ISO date strings, and null/undefined.\n * Use `type_hint` to force a specific interpretation; the default `auto` infers from the\n * values themselves.\n */\nexport const compareValuesTool = new Tool({\n name: 'compare_values',\n description:\n 'Compare two values and return their relationship: equal, greater_than, less_than, or not_comparable. Handles numbers, strings (lexicographic), booleans, dates (ISO strings), null/undefined.',\n inputSchema: validator.object({\n a: validator.any().required().description('First value to compare'),\n b: validator.any().required().description('Second value to compare'),\n type_hint: validator\n .string()\n .valid('auto', 'string', 'number', 'date')\n .default('auto')\n .description(\n 'Force interpretation: \"auto\" detects, \"date\" parses ISO date strings, \"string\" compares lexicographically, \"number\" coerces to numbers (default: auto)'\n ),\n case_insensitive: validator\n .boolean()\n .default(false)\n .description('For string comparisons: ignore case (default: false)'),\n }),\n handler: async (args) => {\n const {\n a: rawA,\n b: rawB,\n type_hint: typeHint,\n case_insensitive: ci,\n } = args as {\n a: unknown\n b: unknown\n type_hint: string\n case_insensitive: boolean\n }\n\n let av: unknown = rawA\n let bv: unknown = rawB\n\n try {\n if (typeHint === 'date') {\n const da = new Date(av as string)\n const db = new Date(bv as string)\n if (Number.isNaN(da.getTime())) return `Error: Cannot parse \"${av}\" as a date.`\n if (Number.isNaN(db.getTime())) return `Error: Cannot parse \"${bv}\" as a date.`\n const diff = da.getTime() - db.getTime()\n const rel = diff === 0 ? 'equal' : diff > 0 ? 'greater_than' : 'less_than'\n return `${rel}\\na = ${da.toISOString()}\\nb = ${db.toISOString()}`\n }\n\n if (typeHint === 'number') {\n const na = Number(av)\n const nb = Number(bv)\n if (Number.isNaN(na)) return `Error: Cannot convert \"${av}\" to a number.`\n if (Number.isNaN(nb)) return `Error: Cannot convert \"${bv}\" to a number.`\n const diff = na - nb\n return diff === 0 ? 'equal' : diff > 0 ? 'greater_than' : 'less_than'\n }\n\n if (typeHint === 'string') {\n const sa = ci ? String(av).toLowerCase() : String(av)\n const sb = ci ? String(bv).toLowerCase() : String(bv)\n const cmp = sa.localeCompare(sb)\n return cmp === 0 ? 'equal' : cmp > 0 ? 'greater_than' : 'less_than'\n }\n\n // auto\n if (ci && typeof av === 'string' && typeof bv === 'string') {\n av = av.toLowerCase()\n bv = bv.toLowerCase()\n }\n const cmp = compareTwo(av as Primitive, bv as Primitive)\n return cmp === 0 ? 'equal' : cmp > 0 ? 'greater_than' : 'less_than'\n } catch (err) {\n return `Error: ${isError(err) ? err.message : String(err)}`\n }\n },\n})\n\n/**\n * Compare two JSON objects and report keys only in A, keys only in B, keys with matching\n * values, and keys with differing values.\n *\n * @remarks\n * Accepts JSON strings (not pre-parsed objects) so the schema can validate the input\n * shape uniformly across providers. When `deep` is true (default), nested objects and arrays\n * are compared structurally; when false, comparison reduces to JSON serialisation equality.\n */\nexport const compareRecordsTool = new Tool({\n name: 'compare_records',\n description:\n 'Compare two JSON objects and report: keys only in A, keys only in B, keys present in both with the same value, and keys present in both with different values.',\n inputSchema: validator.object({\n record_a: validator.string().required().description('First JSON object (as a string)'),\n record_b: validator.string().required().description('Second JSON object (as a string)'),\n deep: validator\n .boolean()\n .default(true)\n .description(\n 'Use deep equality for nested values (default: true). When false, compares by JSON serialization.'\n ),\n }),\n handler: async (args) => {\n const {\n record_a: recordA,\n record_b: recordB,\n deep,\n } = args as { record_a: string; record_b: string; deep: boolean }\n\n let a: Record<string, unknown>\n let b: Record<string, unknown>\n\n try {\n a = JSON.parse(recordA) as Record<string, unknown>\n } catch {\n return 'Error: record_a is not valid JSON.'\n }\n try {\n b = JSON.parse(recordB) as Record<string, unknown>\n } catch {\n return 'Error: record_b is not valid JSON.'\n }\n\n if (typeof a !== 'object' || a === null || Array.isArray(a))\n return 'Error: record_a must be a JSON object (not array or primitive).'\n if (typeof b !== 'object' || b === null || Array.isArray(b))\n return 'Error: record_b must be a JSON object (not array or primitive).'\n\n function isEqual(x: unknown, y: unknown): boolean {\n if (!deep) return JSON.stringify(x) === JSON.stringify(y)\n if (x === y) return true\n if (typeof x !== typeof y) return false\n if (x === null || y === null) return x === y\n if (Array.isArray(x) && Array.isArray(y)) {\n if (x.length !== y.length) return false\n for (const [i, element] of x.entries()) {\n if (!isEqual(element, y[i])) return false\n }\n return true\n }\n if (typeof x === 'object' && typeof y === 'object') {\n const xk = Object.keys(x as object).sort()\n const yk = Object.keys(y as object).sort()\n if (JSON.stringify(xk) !== JSON.stringify(yk)) return false\n for (const k of xk) {\n if (!isEqual((x as Record<string, unknown>)[k], (y as Record<string, unknown>)[k]))\n return false\n }\n return true\n }\n return false\n }\n\n const keysA = new Set(Object.keys(a))\n const keysB = new Set(Object.keys(b))\n\n const onlyInA: string[] = []\n const onlyInB: string[] = []\n const same: string[] = []\n const different: Array<{ key: string; a: unknown; b: unknown }> = []\n\n for (const k of keysA) {\n if (!keysB.has(k)) {\n onlyInA.push(k)\n } else if (isEqual(a[k], b[k])) {\n same.push(k)\n } else {\n different.push({ key: k, a: a[k], b: b[k] })\n }\n }\n\n for (const k of keysB) {\n if (!keysA.has(k)) onlyInB.push(k)\n }\n\n const lines: string[] = []\n\n if (onlyInA.length > 0) lines.push(`Only in A (${onlyInA.length}): ${onlyInA.join(', ')}`)\n if (onlyInB.length > 0) lines.push(`Only in B (${onlyInB.length}): ${onlyInB.join(', ')}`)\n if (same.length > 0) lines.push(`Same value (${same.length}): ${same.join(', ')}`)\n\n if (different.length > 0) {\n lines.push(`Different value (${different.length}):`)\n for (const { key, a: av, b: bv } of different) {\n lines.push(` ${key}: A=${JSON.stringify(av)}, B=${JSON.stringify(bv)}`)\n }\n }\n\n if (lines.length === 0) return 'Records are identical.'\n return lines.join('\\n')\n },\n})\n"],"mappings":";;;;;;;;;;;;;;;;;AAgBA,SAAS,OAAO,OAAoD;CAClE,IAAI,UAAU,QAAQ,UAAU,KAAA,GAAW,OAAO;CAClD,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW,OAAO;CACpE,MAAM,IAAI,OAAO,KAAK;CACtB,IAAI,CAAC,OAAO,MAAM,CAAC,KAAK,MAAM,KAAK,MAAM,IAAI,OAAO;CACpD,OAAO;AACT;AAEA,SAAS,WAAW,GAAc,GAAsB;CACtD,MAAM,KAAK,OAAO,CAAC;CACnB,MAAM,KAAK,OAAO,CAAC;CACnB,IAAI,OAAO,QAAQ,OAAO,MAAM,OAAO;CACvC,IAAI,OAAO,MAAM,OAAO;CACxB,IAAI,OAAO,MAAM,OAAO;CACxB,IAAI,OAAO,OAAO,YAAY,OAAO,OAAO,UAAU,OAAO,KAAK;CAClE,OAAO,OAAO,EAAE,EAAE,cAAc,OAAO,EAAE,CAAC;AAC5C;;;;;;;;;AAUA,IAAa,oBAAoB,IAAI,aAAA,KAAK;CACxC,MAAM;CACN,aACE;CACF,aAAa,kBAAA,UAAU,OAAO;EAC5B,GAAG,kBAAA,UAAU,IAAI,EAAE,SAAS,EAAE,YAAY,wBAAwB;EAClE,GAAG,kBAAA,UAAU,IAAI,EAAE,SAAS,EAAE,YAAY,yBAAyB;EACnE,WAAW,kBAAA,UACR,OAAO,EACP,MAAM,QAAQ,UAAU,UAAU,MAAM,EACxC,QAAQ,MAAM,EACd,YACC,gKACF;EACF,kBAAkB,kBAAA,UACf,QAAQ,EACR,QAAQ,KAAK,EACb,YAAY,sDAAsD;CACvE,CAAC;CACD,SAAS,OAAO,SAAS;EACvB,MAAM,EACJ,GAAG,MACH,GAAG,MACH,WAAW,UACX,kBAAkB,OAChB;EAOJ,IAAI,KAAc;EAClB,IAAI,KAAc;EAElB,IAAI;GACF,IAAI,aAAa,QAAQ;IACvB,MAAM,KAAK,IAAI,KAAK,EAAY;IAChC,MAAM,KAAK,IAAI,KAAK,EAAY;IAChC,IAAI,OAAO,MAAM,GAAG,QAAQ,CAAC,GAAG,OAAO,wBAAwB,GAAG;IAClE,IAAI,OAAO,MAAM,GAAG,QAAQ,CAAC,GAAG,OAAO,wBAAwB,GAAG;IAClE,MAAM,OAAO,GAAG,QAAQ,IAAI,GAAG,QAAQ;IAEvC,OAAO,GADK,SAAS,IAAI,UAAU,OAAO,IAAI,iBAAiB,YACjD,QAAQ,GAAG,YAAY,EAAE,QAAQ,GAAG,YAAY;GAChE;GAEA,IAAI,aAAa,UAAU;IACzB,MAAM,KAAK,OAAO,EAAE;IACpB,MAAM,KAAK,OAAO,EAAE;IACpB,IAAI,OAAO,MAAM,EAAE,GAAG,OAAO,0BAA0B,GAAG;IAC1D,IAAI,OAAO,MAAM,EAAE,GAAG,OAAO,0BAA0B,GAAG;IAC1D,MAAM,OAAO,KAAK;IAClB,OAAO,SAAS,IAAI,UAAU,OAAO,IAAI,iBAAiB;GAC5D;GAEA,IAAI,aAAa,UAAU;IACzB,MAAM,KAAK,KAAK,OAAO,EAAE,EAAE,YAAY,IAAI,OAAO,EAAE;IACpD,MAAM,KAAK,KAAK,OAAO,EAAE,EAAE,YAAY,IAAI,OAAO,EAAE;IACpD,MAAM,MAAM,GAAG,cAAc,EAAE;IAC/B,OAAO,QAAQ,IAAI,UAAU,MAAM,IAAI,iBAAiB;GAC1D;GAGA,IAAI,MAAM,OAAO,OAAO,YAAY,OAAO,OAAO,UAAU;IAC1D,KAAK,GAAG,YAAY;IACpB,KAAK,GAAG,YAAY;GACtB;GACA,MAAM,MAAM,WAAW,IAAiB,EAAe;GACvD,OAAO,QAAQ,IAAI,UAAU,MAAM,IAAI,iBAAiB;EAC1D,SAAS,KAAK;GACZ,OAAO,UAAU,sBAAA,QAAQ,GAAG,IAAI,IAAI,UAAU,OAAO,GAAG;EAC1D;CACF;AACF,CAAC;;;;;;;;;;AAWD,IAAa,qBAAqB,IAAI,aAAA,KAAK;CACzC,MAAM;CACN,aACE;CACF,aAAa,kBAAA,UAAU,OAAO;EAC5B,UAAU,kBAAA,UAAU,OAAO,EAAE,SAAS,EAAE,YAAY,iCAAiC;EACrF,UAAU,kBAAA,UAAU,OAAO,EAAE,SAAS,EAAE,YAAY,kCAAkC;EACtF,MAAM,kBAAA,UACH,QAAQ,EACR,QAAQ,IAAI,EACZ,YACC,kGACF;CACJ,CAAC;CACD,SAAS,OAAO,SAAS;EACvB,MAAM,EACJ,UAAU,SACV,UAAU,SACV,SACE;EAEJ,IAAI;EACJ,IAAI;EAEJ,IAAI;GACF,IAAI,KAAK,MAAM,OAAO;EACxB,QAAQ;GACN,OAAO;EACT;EACA,IAAI;GACF,IAAI,KAAK,MAAM,OAAO;EACxB,QAAQ;GACN,OAAO;EACT;EAEA,IAAI,OAAO,MAAM,YAAY,MAAM,QAAQ,MAAM,QAAQ,CAAC,GACxD,OAAO;EACT,IAAI,OAAO,MAAM,YAAY,MAAM,QAAQ,MAAM,QAAQ,CAAC,GACxD,OAAO;EAET,SAAS,QAAQ,GAAY,GAAqB;GAChD,IAAI,CAAC,MAAM,OAAO,KAAK,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC;GACxD,IAAI,MAAM,GAAG,OAAO;GACpB,IAAI,OAAO,MAAM,OAAO,GAAG,OAAO;GAClC,IAAI,MAAM,QAAQ,MAAM,MAAM,OAAO,MAAM;GAC3C,IAAI,MAAM,QAAQ,CAAC,KAAK,MAAM,QAAQ,CAAC,GAAG;IACxC,IAAI,EAAE,WAAW,EAAE,QAAQ,OAAO;IAClC,KAAK,MAAM,CAAC,GAAG,YAAY,EAAE,QAAQ,GACnC,IAAI,CAAC,QAAQ,SAAS,EAAE,EAAE,GAAG,OAAO;IAEtC,OAAO;GACT;GACA,IAAI,OAAO,MAAM,YAAY,OAAO,MAAM,UAAU;IAClD,MAAM,KAAK,OAAO,KAAK,CAAW,EAAE,KAAK;IACzC,MAAM,KAAK,OAAO,KAAK,CAAW,EAAE,KAAK;IACzC,IAAI,KAAK,UAAU,EAAE,MAAM,KAAK,UAAU,EAAE,GAAG,OAAO;IACtD,KAAK,MAAM,KAAK,IACd,IAAI,CAAC,QAAS,EAA8B,IAAK,EAA8B,EAAE,GAC/E,OAAO;IAEX,OAAO;GACT;GACA,OAAO;EACT;EAEA,MAAM,QAAQ,IAAI,IAAI,OAAO,KAAK,CAAC,CAAC;EACpC,MAAM,QAAQ,IAAI,IAAI,OAAO,KAAK,CAAC,CAAC;EAEpC,MAAM,UAAoB,CAAC;EAC3B,MAAM,UAAoB,CAAC;EAC3B,MAAM,OAAiB,CAAC;EACxB,MAAM,YAA4D,CAAC;EAEnE,KAAK,MAAM,KAAK,OACd,IAAI,CAAC,MAAM,IAAI,CAAC,GACd,QAAQ,KAAK,CAAC;OACT,IAAI,QAAQ,EAAE,IAAI,EAAE,EAAE,GAC3B,KAAK,KAAK,CAAC;OAEX,UAAU,KAAK;GAAE,KAAK;GAAG,GAAG,EAAE;GAAI,GAAG,EAAE;EAAG,CAAC;EAI/C,KAAK,MAAM,KAAK,OACd,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,QAAQ,KAAK,CAAC;EAGnC,MAAM,QAAkB,CAAC;EAEzB,IAAI,QAAQ,SAAS,GAAG,MAAM,KAAK,cAAc,QAAQ,OAAO,KAAK,QAAQ,KAAK,IAAI,GAAG;EACzF,IAAI,QAAQ,SAAS,GAAG,MAAM,KAAK,cAAc,QAAQ,OAAO,KAAK,QAAQ,KAAK,IAAI,GAAG;EACzF,IAAI,KAAK,SAAS,GAAG,MAAM,KAAK,eAAe,KAAK,OAAO,KAAK,KAAK,KAAK,IAAI,GAAG;EAEjF,IAAI,UAAU,SAAS,GAAG;GACxB,MAAM,KAAK,oBAAoB,UAAU,OAAO,GAAG;GACnD,KAAK,MAAM,EAAE,KAAK,GAAG,IAAI,GAAG,QAAQ,WAClC,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,UAAU,EAAE,EAAE,MAAM,KAAK,UAAU,EAAE,GAAG;EAE3E;EAEA,IAAI,MAAM,WAAW,GAAG,OAAO;EAC/B,OAAO,MAAM,KAAK,IAAI;CACxB;AACF,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { o as isError } from "../../tool_registry-DqLOyGyG.mjs";
|
|
2
|
-
import
|
|
3
|
-
import "../../
|
|
2
|
+
import "../../common-DeZaonK1.mjs";
|
|
3
|
+
import { t as Tool } from "../../tool-D2WB1EA1.mjs";
|
|
4
4
|
import "../../guards.mjs";
|
|
5
5
|
import { validator } from "@nhtio/validation";
|
|
6
6
|
//#region src/batteries/tools/comparison/index.ts
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
require("../../common-
|
|
2
|
+
require("../../chunk-KmRHZBOW.js");
|
|
3
|
+
const require_tool_registry = require("../../tool_registry-Dkfprsck.js");
|
|
4
|
+
require("../../common-Od8edUXU.js");
|
|
5
|
+
const require_tool = require("../../tool-COSeH8I6.js");
|
|
5
6
|
require("../../guards.cjs");
|
|
6
7
|
let _nhtio_validation = require("@nhtio/validation");
|
|
7
8
|
//#region src/batteries/tools/data_structure/index.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data_structure.cjs","names":[],"sources":["../../../src/batteries/tools/data_structure/index.ts"],"sourcesContent":["/**\n * Pre-constructed tools for querying, filtering, grouping, and reshaping structured values.\n *\n * @module @nhtio/adk/batteries/tools/data_structure\n *\n * @remarks\n * Pre-constructed bundled tools for the `data_structure` category. Import individually, the whole\n * category, or import every tool via `@nhtio/adk/batteries`.\n */\n\nimport { Tool } from '@nhtio/adk/common'\nimport { validator } from '@nhtio/validation'\nimport { isError, isObject } from '@nhtio/adk/guards'\n\nfunction getPath(obj: unknown, path: string): unknown {\n const parts = path.split('.')\n let current = obj\n for (const part of parts) {\n if (current === null || current === undefined || typeof current !== 'object') return undefined\n current = (current as Record<string, unknown>)[part]\n }\n return current\n}\n\ntype FilterOperator =\n | 'eq'\n | 'ne'\n | 'gt'\n | 'gte'\n | 'lt'\n | 'lte'\n | 'contains'\n | 'starts_with'\n | 'ends_with'\n | 'exists'\n\nfunction matchesFilter(\n item: unknown,\n key: string,\n operator: FilterOperator,\n value: unknown\n): boolean {\n const actual = getPath(item, key)\n switch (operator) {\n case 'eq':\n return actual === value\n case 'ne':\n return actual !== value\n case 'gt':\n return typeof actual === 'number' && actual > (value as number)\n case 'gte':\n return typeof actual === 'number' && actual >= (value as number)\n case 'lt':\n return typeof actual === 'number' && actual < (value as number)\n case 'lte':\n return typeof actual === 'number' && actual <= (value as number)\n case 'contains':\n return typeof actual === 'string' && actual.includes(String(value))\n case 'starts_with':\n return typeof actual === 'string' && actual.startsWith(String(value))\n case 'ends_with':\n return typeof actual === 'string' && actual.endsWith(String(value))\n case 'exists':\n return actual !== undefined && actual !== null\n default:\n return false\n }\n}\n\nfunction medianOf(nums: number[]): number {\n const sorted = [...nums].sort((a, b) => a - b)\n const mid = Math.floor(sorted.length / 2)\n return sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid]\n}\n\nfunction applyTemplate(template: string, item: unknown): string {\n if (typeof item !== 'object' || item === null) return String(item)\n return template.replace(/\\{\\{([^}]+)\\}\\}/g, (_, key) => {\n const val = getPath(item, key.trim())\n return val === undefined || val === null ? '' : String(val)\n })\n}\n\ntype Operation =\n | { op: 'filter'; key: string; operator: FilterOperator; value?: unknown }\n | { op: 'sort'; key: string; direction?: 'asc' | 'desc' }\n | { op: 'select_keys'; keys: string[] }\n | { op: 'pluck'; key: string }\n | { op: 'slice'; start: number; end?: number }\n | { op: 'unique' }\n | { op: 'unique_by'; key: string }\n | { op: 'group_by'; key: string }\n | { op: 'flatten'; depth?: number }\n | { op: 'reverse' }\n | { op: 'count' }\n | { op: 'sum'; key?: string }\n | { op: 'avg'; key?: string }\n | { op: 'median'; key?: string }\n | { op: 'min'; key?: string }\n | { op: 'max'; key?: string }\n | { op: 'first'; n?: number }\n | { op: 'last'; n?: number }\n | { op: 'chunk'; size: number }\n | { op: 'frequency_count'; key?: string }\n | { op: 'top_n'; n: number; key: string; direction?: 'asc' | 'desc' }\n | { op: 'map_template'; template: string }\n\nfunction applyOperation(data: unknown, op: Operation): unknown {\n if (op.op === 'count') {\n if (Array.isArray(data)) return data.length\n if (isObject(data)) return Object.keys(data).length\n return 0\n }\n\n if (!Array.isArray(data)) {\n throw new Error(`Operation \"${op.op}\" requires an array input.`)\n }\n\n const arr = data as unknown[]\n\n switch (op.op) {\n case 'filter':\n return arr.filter((item) => matchesFilter(item, op.key, op.operator, op.value))\n\n case 'sort': {\n const dir = op.direction === 'desc' ? -1 : 1\n return [...arr].sort((a, b) => {\n const av = getPath(a, op.key)\n const bv = getPath(b, op.key)\n if (av === bv) return 0\n if (av === undefined || av === null) return 1\n if (bv === undefined || bv === null) return -1\n if (typeof av === 'number' && typeof bv === 'number') return (av - bv) * dir\n return String(av).localeCompare(String(bv)) * dir\n })\n }\n\n case 'select_keys':\n return arr.map((item) => {\n if (typeof item !== 'object' || item === null) return item\n const result: Record<string, unknown> = {}\n for (const key of op.keys) result[key] = (item as Record<string, unknown>)[key]\n return result\n })\n\n case 'pluck':\n return arr.map((item) => getPath(item, op.key))\n\n case 'slice':\n return arr.slice(op.start, op.end)\n\n case 'unique': {\n const seen = new Set<string>()\n return arr.filter((item) => {\n const key = typeof item === 'object' ? JSON.stringify(item) : String(item)\n if (seen.has(key)) return false\n seen.add(key)\n return true\n })\n }\n\n case 'unique_by': {\n const seen = new Set<unknown>()\n return arr.filter((item) => {\n const key = getPath(item, op.key)\n if (seen.has(key)) return false\n seen.add(key)\n return true\n })\n }\n\n case 'group_by': {\n const groups: Record<string, unknown[]> = {}\n for (const item of arr) {\n const key = String(getPath(item, op.key) ?? '__undefined__')\n if (!groups[key]) groups[key] = []\n groups[key].push(item)\n }\n return groups\n }\n\n case 'flatten':\n return arr.flat(op.depth ?? 1)\n\n case 'reverse':\n return [...arr].reverse()\n\n case 'sum': {\n const values = op.key ? arr.map((i) => getPath(i, op.key!)) : arr\n return values.reduce<number>((acc, v) => acc + (typeof v === 'number' ? v : 0), 0)\n }\n\n case 'avg': {\n const values = op.key ? arr.map((i) => getPath(i, op.key!)) : arr\n const nums = values.filter((v): v is number => typeof v === 'number')\n if (nums.length === 0) return null\n return nums.reduce((a, b) => a + b, 0) / nums.length\n }\n\n case 'median': {\n const values = op.key ? arr.map((i) => getPath(i, op.key!)) : arr\n const nums = values.filter((v): v is number => typeof v === 'number')\n if (nums.length === 0) return null\n return medianOf(nums)\n }\n\n case 'min': {\n const values = op.key ? arr.map((i) => getPath(i, op.key!)) : arr\n const nums = values.filter((v): v is number => typeof v === 'number')\n if (nums.length === 0) return null\n return Math.min(...nums)\n }\n\n case 'max': {\n const values = op.key ? arr.map((i) => getPath(i, op.key!)) : arr\n const nums = values.filter((v): v is number => typeof v === 'number')\n if (nums.length === 0) return null\n return Math.max(...nums)\n }\n\n case 'first':\n return op.n !== undefined ? arr.slice(0, op.n) : arr[0]\n\n case 'last':\n return op.n !== undefined ? arr.slice(-op.n) : arr[arr.length - 1]\n\n case 'chunk': {\n const size = Math.max(1, Math.floor(op.size))\n const chunks: unknown[][] = []\n for (let i = 0; i < arr.length; i += size) chunks.push(arr.slice(i, i + size))\n return chunks\n }\n\n case 'frequency_count': {\n const freq: Record<string, number> = {}\n for (const item of arr) {\n const key = op.key ? String(getPath(item, op.key) ?? '__undefined__') : String(item)\n freq[key] = (freq[key] ?? 0) + 1\n }\n return freq\n }\n\n case 'top_n': {\n const dir = op.direction === 'asc' ? 1 : -1\n return [...arr]\n .sort((a, b) => {\n const av = getPath(a, op.key)\n const bv = getPath(b, op.key)\n if (typeof av === 'number' && typeof bv === 'number') return (bv - av) * dir\n return String(bv).localeCompare(String(av)) * dir\n })\n .slice(0, op.n)\n }\n\n case 'map_template':\n return arr.map((item) => applyTemplate(op.template, item))\n\n default:\n throw new Error(`Unknown operation: ${(op as { op: string }).op}`)\n }\n}\n\n/**\n * Apply a pipeline of operations to a JSON array or object.\n *\n * @remarks\n * Operations are applied in order; each step transforms the output of the previous. Supported\n * operations: `filter`, `sort`, `select_keys`, `pluck`, `slice`, `unique`, `unique_by`,\n * `group_by`, `flatten`, `reverse`, `count`, `sum`, `avg`, `median`, `min`, `max`, `first`,\n * `last`, `chunk`, `frequency_count`, `top_n`, `map_template`. Dot-notation paths are supported\n * for nested key access in every operation that takes a `key` field.\n */\nexport const jsonTransformTool = new Tool({\n name: 'json_transform',\n description:\n 'Apply a pipeline of operations to a JSON array or object: filter, sort, group, aggregate (sum/avg/median/min/max), pluck fields, deduplicate, flatten, chunk, frequency count, top-N, and more.',\n inputSchema: validator.object({\n data: validator.string().required().description('JSON data as a string (array or object)'),\n operations: validator\n .array()\n .items(validator.object().unknown(true))\n .required()\n .description(\n 'Pipeline of operations to apply in order. Each step transforms the output of the previous.'\n ),\n }),\n handler: async (args) => {\n const { data: dataStr, operations } = args as {\n data: string\n operations: Operation[]\n }\n\n let data: unknown\n try {\n data = JSON.parse(dataStr)\n } catch {\n return 'Error: Invalid JSON input.'\n }\n\n let current: unknown = data\n\n for (const [i, operation] of operations.entries()) {\n try {\n current = applyOperation(current, operation)\n } catch (err) {\n return `Error in operation ${i + 1} (\"${operation.op}\"): ${isError(err) ? err.message : String(err)}`\n }\n }\n\n if (typeof current === 'string') return current\n return JSON.stringify(current, null, 2)\n },\n})\n\n/**\n * Perform set operations on two JSON arrays.\n *\n * @remarks\n * Supported operations: `intersection`, `union`, `difference`, `symmetric_difference`,\n * `is_member`, `is_subset`, `is_superset`. For arrays of objects, an optional `compare_key`\n * narrows equality to a single property rather than deep structural comparison.\n */\nexport const setOperationsTool = new Tool({\n name: 'set_operations',\n description:\n 'Perform set operations on two JSON arrays: intersection (common elements), union (all elements), difference (in A but not B), symmetric difference, or membership check.',\n inputSchema: validator.object({\n data_a: validator.string().required().description('First JSON array'),\n data_b: validator.string().optional().description('Second JSON array'),\n operation: validator\n .string()\n .valid(\n 'intersection',\n 'union',\n 'difference',\n 'symmetric_difference',\n 'is_member',\n 'is_subset',\n 'is_superset'\n )\n .required()\n .description('Set operation to perform'),\n item: validator.any().optional().description('For is_member: the value to look up in data_a.'),\n compare_key: validator\n .string()\n .optional()\n .description(\n 'For arrays of objects: use this key for equality comparison instead of deep equality.'\n ),\n }),\n handler: async (args) => {\n const {\n data_a: dataA,\n data_b: dataB,\n operation,\n item,\n compare_key: compareKey,\n } = args as {\n data_a: string\n data_b?: string\n operation: string\n item?: unknown\n compare_key?: string\n }\n\n let a: unknown[]\n let b: unknown[] = []\n\n try {\n a = JSON.parse(dataA)\n } catch {\n return 'Error: data_a is not valid JSON.'\n }\n if (!Array.isArray(a)) return 'Error: data_a must be a JSON array.'\n\n if (dataB !== undefined) {\n try {\n b = JSON.parse(dataB)\n } catch {\n return 'Error: data_b is not valid JSON.'\n }\n if (!Array.isArray(b)) return 'Error: data_b must be a JSON array.'\n }\n\n const toKey = (val: unknown): string =>\n compareKey && isObject(val)\n ? String((val as Record<string, unknown>)[compareKey])\n : JSON.stringify(val)\n\n if (operation === 'is_member') {\n const needle = JSON.stringify(item)\n const found = a.some((entry) => JSON.stringify(entry) === needle)\n return found ? `Found: item is in the array.` : `Not found: item is not in the array.`\n }\n\n const setA = new Set(a.map(toKey))\n const setB = new Set(b.map(toKey))\n const indexA = new Map(a.map((entry) => [toKey(entry), entry]))\n\n switch (operation) {\n case 'intersection': {\n const result = [...setA].filter((k) => setB.has(k)).map((k) => indexA.get(k))\n return JSON.stringify(result, null, 2)\n }\n case 'union': {\n const result = [...a, ...b.filter((entry) => !setA.has(toKey(entry)))]\n return JSON.stringify(result, null, 2)\n }\n case 'difference': {\n const result = a.filter((entry) => !setB.has(toKey(entry)))\n return JSON.stringify(result, null, 2)\n }\n case 'symmetric_difference': {\n const result = [\n ...a.filter((entry) => !setB.has(toKey(entry))),\n ...b.filter((entry) => !setA.has(toKey(entry))),\n ]\n return JSON.stringify(result, null, 2)\n }\n case 'is_subset':\n return [...setA].every((k) => setB.has(k))\n ? `Yes: A is a subset of B (all ${a.length} elements of A are in B).`\n : `No: A is not a subset of B.`\n case 'is_superset':\n return [...setB].every((k) => setA.has(k))\n ? `Yes: A is a superset of B (A contains all ${b.length} elements of B).`\n : `No: A is not a superset of B.`\n default:\n return `Error: Unknown operation \"${operation}\".`\n }\n },\n})\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"data_structure.cjs","names":[],"sources":["../../../src/batteries/tools/data_structure/index.ts"],"sourcesContent":["/**\n * Pre-constructed tools for querying, filtering, grouping, and reshaping structured values.\n *\n * @module @nhtio/adk/batteries/tools/data_structure\n *\n * @remarks\n * Pre-constructed bundled tools for the `data_structure` category. Import individually, the whole\n * category, or import every tool via `@nhtio/adk/batteries`.\n */\n\nimport { Tool } from '@nhtio/adk/common'\nimport { validator } from '@nhtio/validation'\nimport { isError, isObject } from '@nhtio/adk/guards'\n\nfunction getPath(obj: unknown, path: string): unknown {\n const parts = path.split('.')\n let current = obj\n for (const part of parts) {\n if (current === null || current === undefined || typeof current !== 'object') return undefined\n current = (current as Record<string, unknown>)[part]\n }\n return current\n}\n\ntype FilterOperator =\n | 'eq'\n | 'ne'\n | 'gt'\n | 'gte'\n | 'lt'\n | 'lte'\n | 'contains'\n | 'starts_with'\n | 'ends_with'\n | 'exists'\n\nfunction matchesFilter(\n item: unknown,\n key: string,\n operator: FilterOperator,\n value: unknown\n): boolean {\n const actual = getPath(item, key)\n switch (operator) {\n case 'eq':\n return actual === value\n case 'ne':\n return actual !== value\n case 'gt':\n return typeof actual === 'number' && actual > (value as number)\n case 'gte':\n return typeof actual === 'number' && actual >= (value as number)\n case 'lt':\n return typeof actual === 'number' && actual < (value as number)\n case 'lte':\n return typeof actual === 'number' && actual <= (value as number)\n case 'contains':\n return typeof actual === 'string' && actual.includes(String(value))\n case 'starts_with':\n return typeof actual === 'string' && actual.startsWith(String(value))\n case 'ends_with':\n return typeof actual === 'string' && actual.endsWith(String(value))\n case 'exists':\n return actual !== undefined && actual !== null\n default:\n return false\n }\n}\n\nfunction medianOf(nums: number[]): number {\n const sorted = [...nums].sort((a, b) => a - b)\n const mid = Math.floor(sorted.length / 2)\n return sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid]\n}\n\nfunction applyTemplate(template: string, item: unknown): string {\n if (typeof item !== 'object' || item === null) return String(item)\n return template.replace(/\\{\\{([^}]+)\\}\\}/g, (_, key) => {\n const val = getPath(item, key.trim())\n return val === undefined || val === null ? '' : String(val)\n })\n}\n\ntype Operation =\n | { op: 'filter'; key: string; operator: FilterOperator; value?: unknown }\n | { op: 'sort'; key: string; direction?: 'asc' | 'desc' }\n | { op: 'select_keys'; keys: string[] }\n | { op: 'pluck'; key: string }\n | { op: 'slice'; start: number; end?: number }\n | { op: 'unique' }\n | { op: 'unique_by'; key: string }\n | { op: 'group_by'; key: string }\n | { op: 'flatten'; depth?: number }\n | { op: 'reverse' }\n | { op: 'count' }\n | { op: 'sum'; key?: string }\n | { op: 'avg'; key?: string }\n | { op: 'median'; key?: string }\n | { op: 'min'; key?: string }\n | { op: 'max'; key?: string }\n | { op: 'first'; n?: number }\n | { op: 'last'; n?: number }\n | { op: 'chunk'; size: number }\n | { op: 'frequency_count'; key?: string }\n | { op: 'top_n'; n: number; key: string; direction?: 'asc' | 'desc' }\n | { op: 'map_template'; template: string }\n\nfunction applyOperation(data: unknown, op: Operation): unknown {\n if (op.op === 'count') {\n if (Array.isArray(data)) return data.length\n if (isObject(data)) return Object.keys(data).length\n return 0\n }\n\n if (!Array.isArray(data)) {\n throw new Error(`Operation \"${op.op}\" requires an array input.`)\n }\n\n const arr = data as unknown[]\n\n switch (op.op) {\n case 'filter':\n return arr.filter((item) => matchesFilter(item, op.key, op.operator, op.value))\n\n case 'sort': {\n const dir = op.direction === 'desc' ? -1 : 1\n return [...arr].sort((a, b) => {\n const av = getPath(a, op.key)\n const bv = getPath(b, op.key)\n if (av === bv) return 0\n if (av === undefined || av === null) return 1\n if (bv === undefined || bv === null) return -1\n if (typeof av === 'number' && typeof bv === 'number') return (av - bv) * dir\n return String(av).localeCompare(String(bv)) * dir\n })\n }\n\n case 'select_keys':\n return arr.map((item) => {\n if (typeof item !== 'object' || item === null) return item\n const result: Record<string, unknown> = {}\n for (const key of op.keys) result[key] = (item as Record<string, unknown>)[key]\n return result\n })\n\n case 'pluck':\n return arr.map((item) => getPath(item, op.key))\n\n case 'slice':\n return arr.slice(op.start, op.end)\n\n case 'unique': {\n const seen = new Set<string>()\n return arr.filter((item) => {\n const key = typeof item === 'object' ? JSON.stringify(item) : String(item)\n if (seen.has(key)) return false\n seen.add(key)\n return true\n })\n }\n\n case 'unique_by': {\n const seen = new Set<unknown>()\n return arr.filter((item) => {\n const key = getPath(item, op.key)\n if (seen.has(key)) return false\n seen.add(key)\n return true\n })\n }\n\n case 'group_by': {\n const groups: Record<string, unknown[]> = {}\n for (const item of arr) {\n const key = String(getPath(item, op.key) ?? '__undefined__')\n if (!groups[key]) groups[key] = []\n groups[key].push(item)\n }\n return groups\n }\n\n case 'flatten':\n return arr.flat(op.depth ?? 1)\n\n case 'reverse':\n return [...arr].reverse()\n\n case 'sum': {\n const values = op.key ? arr.map((i) => getPath(i, op.key!)) : arr\n return values.reduce<number>((acc, v) => acc + (typeof v === 'number' ? v : 0), 0)\n }\n\n case 'avg': {\n const values = op.key ? arr.map((i) => getPath(i, op.key!)) : arr\n const nums = values.filter((v): v is number => typeof v === 'number')\n if (nums.length === 0) return null\n return nums.reduce((a, b) => a + b, 0) / nums.length\n }\n\n case 'median': {\n const values = op.key ? arr.map((i) => getPath(i, op.key!)) : arr\n const nums = values.filter((v): v is number => typeof v === 'number')\n if (nums.length === 0) return null\n return medianOf(nums)\n }\n\n case 'min': {\n const values = op.key ? arr.map((i) => getPath(i, op.key!)) : arr\n const nums = values.filter((v): v is number => typeof v === 'number')\n if (nums.length === 0) return null\n return Math.min(...nums)\n }\n\n case 'max': {\n const values = op.key ? arr.map((i) => getPath(i, op.key!)) : arr\n const nums = values.filter((v): v is number => typeof v === 'number')\n if (nums.length === 0) return null\n return Math.max(...nums)\n }\n\n case 'first':\n return op.n !== undefined ? arr.slice(0, op.n) : arr[0]\n\n case 'last':\n return op.n !== undefined ? arr.slice(-op.n) : arr[arr.length - 1]\n\n case 'chunk': {\n const size = Math.max(1, Math.floor(op.size))\n const chunks: unknown[][] = []\n for (let i = 0; i < arr.length; i += size) chunks.push(arr.slice(i, i + size))\n return chunks\n }\n\n case 'frequency_count': {\n const freq: Record<string, number> = {}\n for (const item of arr) {\n const key = op.key ? String(getPath(item, op.key) ?? '__undefined__') : String(item)\n freq[key] = (freq[key] ?? 0) + 1\n }\n return freq\n }\n\n case 'top_n': {\n const dir = op.direction === 'asc' ? 1 : -1\n return [...arr]\n .sort((a, b) => {\n const av = getPath(a, op.key)\n const bv = getPath(b, op.key)\n if (typeof av === 'number' && typeof bv === 'number') return (bv - av) * dir\n return String(bv).localeCompare(String(av)) * dir\n })\n .slice(0, op.n)\n }\n\n case 'map_template':\n return arr.map((item) => applyTemplate(op.template, item))\n\n default:\n throw new Error(`Unknown operation: ${(op as { op: string }).op}`)\n }\n}\n\n/**\n * Apply a pipeline of operations to a JSON array or object.\n *\n * @remarks\n * Operations are applied in order; each step transforms the output of the previous. Supported\n * operations: `filter`, `sort`, `select_keys`, `pluck`, `slice`, `unique`, `unique_by`,\n * `group_by`, `flatten`, `reverse`, `count`, `sum`, `avg`, `median`, `min`, `max`, `first`,\n * `last`, `chunk`, `frequency_count`, `top_n`, `map_template`. Dot-notation paths are supported\n * for nested key access in every operation that takes a `key` field.\n */\nexport const jsonTransformTool = new Tool({\n name: 'json_transform',\n description:\n 'Apply a pipeline of operations to a JSON array or object: filter, sort, group, aggregate (sum/avg/median/min/max), pluck fields, deduplicate, flatten, chunk, frequency count, top-N, and more.',\n inputSchema: validator.object({\n data: validator.string().required().description('JSON data as a string (array or object)'),\n operations: validator\n .array()\n .items(validator.object().unknown(true))\n .required()\n .description(\n 'Pipeline of operations to apply in order. Each step transforms the output of the previous.'\n ),\n }),\n handler: async (args) => {\n const { data: dataStr, operations } = args as {\n data: string\n operations: Operation[]\n }\n\n let data: unknown\n try {\n data = JSON.parse(dataStr)\n } catch {\n return 'Error: Invalid JSON input.'\n }\n\n let current: unknown = data\n\n for (const [i, operation] of operations.entries()) {\n try {\n current = applyOperation(current, operation)\n } catch (err) {\n return `Error in operation ${i + 1} (\"${operation.op}\"): ${isError(err) ? err.message : String(err)}`\n }\n }\n\n if (typeof current === 'string') return current\n return JSON.stringify(current, null, 2)\n },\n})\n\n/**\n * Perform set operations on two JSON arrays.\n *\n * @remarks\n * Supported operations: `intersection`, `union`, `difference`, `symmetric_difference`,\n * `is_member`, `is_subset`, `is_superset`. For arrays of objects, an optional `compare_key`\n * narrows equality to a single property rather than deep structural comparison.\n */\nexport const setOperationsTool = new Tool({\n name: 'set_operations',\n description:\n 'Perform set operations on two JSON arrays: intersection (common elements), union (all elements), difference (in A but not B), symmetric difference, or membership check.',\n inputSchema: validator.object({\n data_a: validator.string().required().description('First JSON array'),\n data_b: validator.string().optional().description('Second JSON array'),\n operation: validator\n .string()\n .valid(\n 'intersection',\n 'union',\n 'difference',\n 'symmetric_difference',\n 'is_member',\n 'is_subset',\n 'is_superset'\n )\n .required()\n .description('Set operation to perform'),\n item: validator.any().optional().description('For is_member: the value to look up in data_a.'),\n compare_key: validator\n .string()\n .optional()\n .description(\n 'For arrays of objects: use this key for equality comparison instead of deep equality.'\n ),\n }),\n handler: async (args) => {\n const {\n data_a: dataA,\n data_b: dataB,\n operation,\n item,\n compare_key: compareKey,\n } = args as {\n data_a: string\n data_b?: string\n operation: string\n item?: unknown\n compare_key?: string\n }\n\n let a: unknown[]\n let b: unknown[] = []\n\n try {\n a = JSON.parse(dataA)\n } catch {\n return 'Error: data_a is not valid JSON.'\n }\n if (!Array.isArray(a)) return 'Error: data_a must be a JSON array.'\n\n if (dataB !== undefined) {\n try {\n b = JSON.parse(dataB)\n } catch {\n return 'Error: data_b is not valid JSON.'\n }\n if (!Array.isArray(b)) return 'Error: data_b must be a JSON array.'\n }\n\n const toKey = (val: unknown): string =>\n compareKey && isObject(val)\n ? String((val as Record<string, unknown>)[compareKey])\n : JSON.stringify(val)\n\n if (operation === 'is_member') {\n const needle = JSON.stringify(item)\n const found = a.some((entry) => JSON.stringify(entry) === needle)\n return found ? `Found: item is in the array.` : `Not found: item is not in the array.`\n }\n\n const setA = new Set(a.map(toKey))\n const setB = new Set(b.map(toKey))\n const indexA = new Map(a.map((entry) => [toKey(entry), entry]))\n\n switch (operation) {\n case 'intersection': {\n const result = [...setA].filter((k) => setB.has(k)).map((k) => indexA.get(k))\n return JSON.stringify(result, null, 2)\n }\n case 'union': {\n const result = [...a, ...b.filter((entry) => !setA.has(toKey(entry)))]\n return JSON.stringify(result, null, 2)\n }\n case 'difference': {\n const result = a.filter((entry) => !setB.has(toKey(entry)))\n return JSON.stringify(result, null, 2)\n }\n case 'symmetric_difference': {\n const result = [\n ...a.filter((entry) => !setB.has(toKey(entry))),\n ...b.filter((entry) => !setA.has(toKey(entry))),\n ]\n return JSON.stringify(result, null, 2)\n }\n case 'is_subset':\n return [...setA].every((k) => setB.has(k))\n ? `Yes: A is a subset of B (all ${a.length} elements of A are in B).`\n : `No: A is not a subset of B.`\n case 'is_superset':\n return [...setB].every((k) => setA.has(k))\n ? `Yes: A is a superset of B (A contains all ${b.length} elements of B).`\n : `No: A is not a superset of B.`\n default:\n return `Error: Unknown operation \"${operation}\".`\n }\n },\n})\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { c as isObject, o as isError } from "../../tool_registry-DqLOyGyG.mjs";
|
|
2
|
-
import
|
|
3
|
-
import "../../
|
|
2
|
+
import "../../common-DeZaonK1.mjs";
|
|
3
|
+
import { t as Tool } from "../../tool-D2WB1EA1.mjs";
|
|
4
4
|
import "../../guards.mjs";
|
|
5
5
|
import { validator } from "@nhtio/validation";
|
|
6
6
|
//#region src/batteries/tools/data_structure/index.ts
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
require("../../
|
|
2
|
+
const require_chunk = require("../../chunk-KmRHZBOW.js");
|
|
3
|
+
require("../../common-Od8edUXU.js");
|
|
4
|
+
const require_tool = require("../../tool-COSeH8I6.js");
|
|
5
5
|
let _nhtio_validation = require("@nhtio/validation");
|
|
6
6
|
let luxon = require("luxon");
|
|
7
7
|
let chrono_node = require("chrono-node");
|
|
8
|
-
chrono_node =
|
|
8
|
+
chrono_node = require_chunk.__toESM(chrono_node);
|
|
9
9
|
//#region src/batteries/tools/datetime_extended/index.ts
|
|
10
10
|
/**
|
|
11
11
|
* Pre-constructed tools for parsing natural-language dates and business-calendar calculations.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import "../../
|
|
1
|
+
import "../../common-DeZaonK1.mjs";
|
|
2
|
+
import { t as Tool } from "../../tool-D2WB1EA1.mjs";
|
|
3
3
|
import { validator } from "@nhtio/validation";
|
|
4
4
|
import { DateTime, IANAZone } from "luxon";
|
|
5
5
|
import * as chrono from "chrono-node";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
require("../../
|
|
3
|
-
|
|
4
|
-
require("../../
|
|
2
|
+
require("../../chunk-KmRHZBOW.js");
|
|
3
|
+
require("../../common-Od8edUXU.js");
|
|
4
|
+
const require_tool = require("../../tool-COSeH8I6.js");
|
|
5
5
|
let _nhtio_validation = require("@nhtio/validation");
|
|
6
6
|
let luxon = require("luxon");
|
|
7
7
|
//#region src/batteries/tools/datetime_math/index.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import "../../
|
|
1
|
+
import "../../common-DeZaonK1.mjs";
|
|
2
|
+
import { t as Tool } from "../../tool-D2WB1EA1.mjs";
|
|
3
3
|
import { validator } from "@nhtio/validation";
|
|
4
4
|
import { DateTime, Duration, IANAZone } from "luxon";
|
|
5
5
|
//#region src/batteries/tools/datetime_math/index.ts
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
require("../../common-
|
|
2
|
+
require("../../chunk-KmRHZBOW.js");
|
|
3
|
+
const require_tool_registry = require("../../tool_registry-Dkfprsck.js");
|
|
4
|
+
require("../../common-Od8edUXU.js");
|
|
5
|
+
const require_tool = require("../../tool-COSeH8I6.js");
|
|
5
6
|
require("../../guards.cjs");
|
|
6
7
|
let _nhtio_validation = require("@nhtio/validation");
|
|
7
8
|
//#region src/batteries/tools/encoding/index.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encoding.cjs","names":[],"sources":["../../../src/batteries/tools/encoding/index.ts"],"sourcesContent":["/**\n * Pre-constructed tools for common text encodings and decoding operations.\n *\n * @module @nhtio/adk/batteries/tools/encoding\n *\n * @remarks\n * Pre-constructed bundled tools for the `encoding` category. Import individually, the whole\n * category, or import every tool via `@nhtio/adk/batteries`.\n */\n\nimport { Tool } from '@nhtio/adk/common'\nimport { isError } from '@nhtio/adk/guards'\nimport { validator } from '@nhtio/validation'\n\nconst utf8ToBase64 = (text: string): string => {\n const bytes = new TextEncoder().encode(text)\n let binary = ''\n for (const byte of bytes) binary += String.fromCharCode(byte)\n return globalThis.btoa(binary)\n}\n\nconst base64ToUtf8 = (b64: string): string => {\n const binary = globalThis.atob(b64)\n const bytes = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i)\n return new TextDecoder().decode(bytes)\n}\n\n/**\n * Encode or decode text using common schemes: base64, url (percent-encoding), html_entities.\n *\n * @remarks\n * `base64` uses the portable `globalThis.btoa`/`atob` pair so the tool works in browsers, Node,\n * and edge runtimes. Encoding/decoding errors are returned as `Error:` strings rather than\n * thrown so the model can react in-line.\n */\nexport const encodeTextTool = new Tool({\n name: 'encode_text',\n description:\n 'Encode or decode text using common schemes: base64, url (percent-encoding), html_entities. Specify direction: encode or decode.',\n inputSchema: validator.object({\n text: validator.string().required().description('Text to encode or decode'),\n scheme: validator\n .string()\n .valid('base64', 'url', 'html_entities')\n .required()\n .description('Encoding scheme'),\n direction: validator\n .string()\n .valid('encode', 'decode')\n .default('encode')\n .description('Whether to encode or decode (default: encode)'),\n }),\n handler: async (args) => {\n const { text, scheme, direction } = args as {\n text: string\n scheme: string\n direction: string\n }\n\n try {\n if (scheme === 'base64') {\n if (direction === 'encode') return utf8ToBase64(text)\n return base64ToUtf8(text)\n }\n\n if (scheme === 'url') {\n if (direction === 'encode') return encodeURIComponent(text)\n return decodeURIComponent(text)\n }\n\n if (scheme === 'html_entities') {\n if (direction === 'encode') {\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\n }\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/&#(\\d+);/g, (_, code: string) => String.fromCharCode(Number.parseInt(code, 10)))\n .replace(/&#x([0-9a-fA-F]+);/g, (_, hex: string) =>\n String.fromCharCode(Number.parseInt(hex, 16))\n )\n }\n\n return `Error: Unknown scheme \"${scheme}\".`\n } catch (err) {\n return `Error: ${isError(err) ? err.message : String(err)}`\n }\n },\n})\n\n/**\n * Escape or unescape special characters for a target context.\n *\n * @remarks\n * Supports `json_string`, `regex`, `csv_field`, `sql_like`, and `markdown` targets. Returns\n * an error string for unknown targets rather than throwing.\n */\nexport const textEscapeTool = new Tool({\n name: 'text_escape',\n description:\n 'Escape or unescape special characters for a target context: json_string, regex, csv_field, sql_like, markdown.',\n inputSchema: validator.object({\n text: validator.string().required().description('Text to escape or unescape'),\n target: validator\n .string()\n .valid('json_string', 'regex', 'csv_field', 'sql_like', 'markdown')\n .required()\n .description('Target context for escaping'),\n direction: validator\n .string()\n .valid('escape', 'unescape')\n .default('escape')\n .description('Whether to escape or unescape (default: escape)'),\n }),\n handler: async (args) => {\n const { text, target, direction } = args as {\n text: string\n target: string\n direction: string\n }\n\n try {\n if (target === 'json_string') {\n if (direction === 'escape') return JSON.stringify(text).slice(1, -1)\n return JSON.parse(`\"${text}\"`) as string\n }\n\n if (target === 'regex') {\n if (direction === 'escape') return text.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n return text.replace(/\\\\([.*+?^${}()|[\\]\\\\])/g, '$1')\n }\n\n if (target === 'csv_field') {\n if (direction === 'escape') {\n if (/[,\"\\n\\r]/.test(text)) return `\"${text.replace(/\"/g, '\"\"')}\"`\n return text\n }\n if (text.startsWith('\"') && text.endsWith('\"')) {\n return text.slice(1, -1).replace(/\"\"/g, '\"')\n }\n return text\n }\n\n if (target === 'sql_like') {\n if (direction === 'escape') return text.replace(/[%_\\\\]/g, '\\\\$&')\n return text.replace(/\\\\([%_\\\\])/g, '$1')\n }\n\n if (target === 'markdown') {\n const mdChars = /[\\\\`*_{}[\\]()#+\\-.!|]/g\n if (direction === 'escape') return text.replace(mdChars, '\\\\$&')\n return text.replace(/\\\\([\\\\`*_{}[\\]()#+\\-.!|])/g, '$1')\n }\n\n return `Error: Unknown target \"${target}\".`\n } catch (err) {\n return `Error: ${isError(err) ? err.message : String(err)}`\n }\n },\n})\n\n/**\n * Normalize Unicode text or extract code points.\n *\n * @remarks\n * Operations: NFC, NFD, NFKC, NFKD Unicode normalization forms (via `String.prototype.normalize`),\n * `strip_accents` (NFD-decompose, remove combining diacritics, re-compose to NFC), or\n * `code_points` (one `U+HHHH (char)` per line).\n */\nexport const unicodeNormalizeTool = new Tool({\n name: 'unicode_normalize',\n description:\n 'Normalize Unicode text (NFC, NFD, NFKC, NFKD), strip accents/diacritics, or get Unicode code points for each character.',\n inputSchema: validator.object({\n text: validator.string().required().description('Text to process'),\n operation: validator\n .string()\n .valid('nfc', 'nfd', 'nfkc', 'nfkd', 'strip_accents', 'code_points')\n .required()\n .description(\n 'Operation: NFC/NFD/NFKC/NFKD normalization, strip_accents (remove diacritics), or code_points (list hex values)'\n ),\n }),\n handler: async (args) => {\n const { text, operation } = args as { text: string; operation: string }\n\n try {\n if (operation === 'strip_accents') {\n return text.normalize('NFD').replace(/[̀-ͯ]/g, '').normalize('NFC')\n }\n\n if (operation === 'code_points') {\n return [...text]\n .map((ch) => {\n const cp = ch.codePointAt(0)!\n const hex = cp.toString(16).toUpperCase().padStart(4, '0')\n return `U+${hex} (${ch})`\n })\n .join('\\n')\n }\n\n const formMap: Record<string, string> = { nfc: 'NFC', nfd: 'NFD', nfkc: 'NFKC', nfkd: 'NFKD' }\n const form = formMap[operation]\n if (!form) return `Error: Unknown operation \"${operation}\".`\n return text.normalize(form)\n } catch (err) {\n return `Error: ${isError(err) ? err.message : String(err)}`\n }\n },\n})\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"encoding.cjs","names":[],"sources":["../../../src/batteries/tools/encoding/index.ts"],"sourcesContent":["/**\n * Pre-constructed tools for common text encodings and decoding operations.\n *\n * @module @nhtio/adk/batteries/tools/encoding\n *\n * @remarks\n * Pre-constructed bundled tools for the `encoding` category. Import individually, the whole\n * category, or import every tool via `@nhtio/adk/batteries`.\n */\n\nimport { Tool } from '@nhtio/adk/common'\nimport { isError } from '@nhtio/adk/guards'\nimport { validator } from '@nhtio/validation'\n\nconst utf8ToBase64 = (text: string): string => {\n const bytes = new TextEncoder().encode(text)\n let binary = ''\n for (const byte of bytes) binary += String.fromCharCode(byte)\n return globalThis.btoa(binary)\n}\n\nconst base64ToUtf8 = (b64: string): string => {\n const binary = globalThis.atob(b64)\n const bytes = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i)\n return new TextDecoder().decode(bytes)\n}\n\n/**\n * Encode or decode text using common schemes: base64, url (percent-encoding), html_entities.\n *\n * @remarks\n * `base64` uses the portable `globalThis.btoa`/`atob` pair so the tool works in browsers, Node,\n * and edge runtimes. Encoding/decoding errors are returned as `Error:` strings rather than\n * thrown so the model can react in-line.\n */\nexport const encodeTextTool = new Tool({\n name: 'encode_text',\n description:\n 'Encode or decode text using common schemes: base64, url (percent-encoding), html_entities. Specify direction: encode or decode.',\n inputSchema: validator.object({\n text: validator.string().required().description('Text to encode or decode'),\n scheme: validator\n .string()\n .valid('base64', 'url', 'html_entities')\n .required()\n .description('Encoding scheme'),\n direction: validator\n .string()\n .valid('encode', 'decode')\n .default('encode')\n .description('Whether to encode or decode (default: encode)'),\n }),\n handler: async (args) => {\n const { text, scheme, direction } = args as {\n text: string\n scheme: string\n direction: string\n }\n\n try {\n if (scheme === 'base64') {\n if (direction === 'encode') return utf8ToBase64(text)\n return base64ToUtf8(text)\n }\n\n if (scheme === 'url') {\n if (direction === 'encode') return encodeURIComponent(text)\n return decodeURIComponent(text)\n }\n\n if (scheme === 'html_entities') {\n if (direction === 'encode') {\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\n }\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/&#(\\d+);/g, (_, code: string) => String.fromCharCode(Number.parseInt(code, 10)))\n .replace(/&#x([0-9a-fA-F]+);/g, (_, hex: string) =>\n String.fromCharCode(Number.parseInt(hex, 16))\n )\n }\n\n return `Error: Unknown scheme \"${scheme}\".`\n } catch (err) {\n return `Error: ${isError(err) ? err.message : String(err)}`\n }\n },\n})\n\n/**\n * Escape or unescape special characters for a target context.\n *\n * @remarks\n * Supports `json_string`, `regex`, `csv_field`, `sql_like`, and `markdown` targets. Returns\n * an error string for unknown targets rather than throwing.\n */\nexport const textEscapeTool = new Tool({\n name: 'text_escape',\n description:\n 'Escape or unescape special characters for a target context: json_string, regex, csv_field, sql_like, markdown.',\n inputSchema: validator.object({\n text: validator.string().required().description('Text to escape or unescape'),\n target: validator\n .string()\n .valid('json_string', 'regex', 'csv_field', 'sql_like', 'markdown')\n .required()\n .description('Target context for escaping'),\n direction: validator\n .string()\n .valid('escape', 'unescape')\n .default('escape')\n .description('Whether to escape or unescape (default: escape)'),\n }),\n handler: async (args) => {\n const { text, target, direction } = args as {\n text: string\n target: string\n direction: string\n }\n\n try {\n if (target === 'json_string') {\n if (direction === 'escape') return JSON.stringify(text).slice(1, -1)\n return JSON.parse(`\"${text}\"`) as string\n }\n\n if (target === 'regex') {\n if (direction === 'escape') return text.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n return text.replace(/\\\\([.*+?^${}()|[\\]\\\\])/g, '$1')\n }\n\n if (target === 'csv_field') {\n if (direction === 'escape') {\n if (/[,\"\\n\\r]/.test(text)) return `\"${text.replace(/\"/g, '\"\"')}\"`\n return text\n }\n if (text.startsWith('\"') && text.endsWith('\"')) {\n return text.slice(1, -1).replace(/\"\"/g, '\"')\n }\n return text\n }\n\n if (target === 'sql_like') {\n if (direction === 'escape') return text.replace(/[%_\\\\]/g, '\\\\$&')\n return text.replace(/\\\\([%_\\\\])/g, '$1')\n }\n\n if (target === 'markdown') {\n const mdChars = /[\\\\`*_{}[\\]()#+\\-.!|]/g\n if (direction === 'escape') return text.replace(mdChars, '\\\\$&')\n return text.replace(/\\\\([\\\\`*_{}[\\]()#+\\-.!|])/g, '$1')\n }\n\n return `Error: Unknown target \"${target}\".`\n } catch (err) {\n return `Error: ${isError(err) ? err.message : String(err)}`\n }\n },\n})\n\n/**\n * Normalize Unicode text or extract code points.\n *\n * @remarks\n * Operations: NFC, NFD, NFKC, NFKD Unicode normalization forms (via `String.prototype.normalize`),\n * `strip_accents` (NFD-decompose, remove combining diacritics, re-compose to NFC), or\n * `code_points` (one `U+HHHH (char)` per line).\n */\nexport const unicodeNormalizeTool = new Tool({\n name: 'unicode_normalize',\n description:\n 'Normalize Unicode text (NFC, NFD, NFKC, NFKD), strip accents/diacritics, or get Unicode code points for each character.',\n inputSchema: validator.object({\n text: validator.string().required().description('Text to process'),\n operation: validator\n .string()\n .valid('nfc', 'nfd', 'nfkc', 'nfkd', 'strip_accents', 'code_points')\n .required()\n .description(\n 'Operation: NFC/NFD/NFKC/NFKD normalization, strip_accents (remove diacritics), or code_points (list hex values)'\n ),\n }),\n handler: async (args) => {\n const { text, operation } = args as { text: string; operation: string }\n\n try {\n if (operation === 'strip_accents') {\n return text.normalize('NFD').replace(/[̀-ͯ]/g, '').normalize('NFC')\n }\n\n if (operation === 'code_points') {\n return [...text]\n .map((ch) => {\n const cp = ch.codePointAt(0)!\n const hex = cp.toString(16).toUpperCase().padStart(4, '0')\n return `U+${hex} (${ch})`\n })\n .join('\\n')\n }\n\n const formMap: Record<string, string> = { nfc: 'NFC', nfd: 'NFD', nfkc: 'NFKC', nfkd: 'NFKD' }\n const form = formMap[operation]\n if (!form) return `Error: Unknown operation \"${operation}\".`\n return text.normalize(form)\n } catch (err) {\n return `Error: ${isError(err) ? err.message : String(err)}`\n }\n },\n})\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { o as isError } from "../../tool_registry-DqLOyGyG.mjs";
|
|
2
|
-
import
|
|
3
|
-
import "../../
|
|
2
|
+
import "../../common-DeZaonK1.mjs";
|
|
3
|
+
import { t as Tool } from "../../tool-D2WB1EA1.mjs";
|
|
4
4
|
import "../../guards.mjs";
|
|
5
5
|
import { validator } from "@nhtio/validation";
|
|
6
6
|
//#region src/batteries/tools/encoding/index.ts
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
require("../../common-
|
|
2
|
+
require("../../chunk-KmRHZBOW.js");
|
|
3
|
+
const require_tool_registry = require("../../tool_registry-Dkfprsck.js");
|
|
4
|
+
require("../../common-Od8edUXU.js");
|
|
5
|
+
const require_tool = require("../../tool-COSeH8I6.js");
|
|
5
6
|
require("../../guards.cjs");
|
|
6
7
|
let _nhtio_validation = require("@nhtio/validation");
|
|
7
8
|
//#region src/batteries/tools/formatting/index.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatting.cjs","names":[],"sources":["../../../src/batteries/tools/formatting/index.ts"],"sourcesContent":["/**\n * Pre-constructed tools for locale-aware number, list, table, and text formatting.\n *\n * @module @nhtio/adk/batteries/tools/formatting\n *\n * @remarks\n * Pre-constructed bundled tools for the `formatting` category. Import individually, the whole\n * category, or import every tool via `@nhtio/adk/batteries`.\n */\n\nimport { Tool } from '@nhtio/adk/common'\nimport { isError } from '@nhtio/adk/guards'\nimport { validator } from '@nhtio/validation'\n\n/**\n * Format a number using locale-aware styles.\n *\n * @remarks\n * Supported styles: `decimal`, `currency`, `percent`, `compact` (e.g. `1.2K`), `scientific`\n * (e.g. `1.2e+3`), and `ordinal` (`1st`, `2nd`, `3rd`). Uses `Intl.NumberFormat` and\n * `Intl.PluralRules` from the JS standard library. Returns an error string for non-finite values\n * or invalid currency-without-currency-code.\n */\nexport const formatNumberTool = new Tool({\n name: 'format_number',\n description:\n 'Format a number using locale-aware styles: decimal, currency, percent, compact (1.2K), scientific (1.2e3), or ordinal (1st/2nd/3rd). Supports locale and precision options.',\n inputSchema: validator.object({\n value: validator.number().required().description('The number to format'),\n style: validator\n .string()\n .valid('decimal', 'currency', 'percent', 'compact', 'scientific', 'ordinal')\n .default('decimal')\n .description('Formatting style (default: decimal)'),\n currency: validator\n .string()\n .optional()\n .description(\n 'ISO 4217 currency code — required when style is \"currency\" (e.g. \"USD\", \"EUR\")'\n ),\n locale: validator.string().default('en-US').description('BCP 47 locale tag (default: \"en-US\")'),\n min_decimals: validator\n .number()\n .optional()\n .description('Minimum fraction digits (default: style-dependent)'),\n max_decimals: validator\n .number()\n .optional()\n .description('Maximum fraction digits (default: style-dependent)'),\n }),\n handler: async (args) => {\n const {\n value,\n style,\n locale,\n currency,\n min_decimals: minDec,\n max_decimals: maxDec,\n } = args as {\n value: number\n style: string\n locale: string\n currency?: string\n min_decimals?: number\n max_decimals?: number\n }\n\n if (!Number.isFinite(value)) return `Error: Value must be a finite number (got ${value}).`\n\n try {\n if (style === 'ordinal') {\n const pr = new Intl.PluralRules(locale, { type: 'ordinal' })\n const suffixes: Record<string, string> = { one: 'st', two: 'nd', few: 'rd', other: 'th' }\n const rule = pr.select(value)\n const suffix = suffixes[rule] ?? 'th'\n return `${new Intl.NumberFormat(locale).format(value)}${suffix}`\n }\n\n if (style === 'scientific') {\n const exp = value === 0 ? 0 : Math.floor(Math.log10(Math.abs(value)))\n const mantissa = value / Math.pow(10, exp)\n const mFormatted = new Intl.NumberFormat(locale, {\n minimumFractionDigits: minDec ?? 2,\n maximumFractionDigits: maxDec ?? 6,\n }).format(mantissa)\n return `${mFormatted}e${exp >= 0 ? '+' : ''}${exp}`\n }\n\n const opts: Intl.NumberFormatOptions = {}\n\n if (style === 'currency') {\n if (!currency) return 'Error: \"currency\" parameter is required when style is \"currency\".'\n opts.style = 'currency'\n opts.currency = currency.toUpperCase()\n } else if (style === 'percent') {\n opts.style = 'percent'\n opts.minimumFractionDigits = minDec ?? 1\n opts.maximumFractionDigits = maxDec ?? 2\n } else if (style === 'compact') {\n opts.notation = 'compact'\n opts.compactDisplay = 'short'\n } else {\n opts.style = 'decimal'\n }\n\n if (minDec !== undefined && style !== 'percent') opts.minimumFractionDigits = minDec\n if (maxDec !== undefined && style !== 'percent') opts.maximumFractionDigits = maxDec\n\n return new Intl.NumberFormat(locale, opts).format(value)\n } catch (err) {\n return `Error: ${isError(err) ? err.message : String(err)}`\n }\n },\n})\n\n/**\n * Format an array of items as a list.\n *\n * @remarks\n * Supported styles: `bullet` (`• item`), `numbered` (`1. item`), `inline_and`\n * (`a, b, and c`), `inline_or` (`a, b, or c`), `newline` (one per line).\n */\nexport const formatListTool = new Tool({\n name: 'format_list',\n description:\n 'Format an array of items as a list. Styles: bullet (• item), numbered (1. item), inline_and (\"a, b, and c\"), inline_or (\"a, b, or c\"), newline (one per line).',\n inputSchema: validator.object({\n items: validator\n .array()\n .items(validator.string())\n .required()\n .description('Array of items to format'),\n style: validator\n .string()\n .valid('bullet', 'numbered', 'inline_and', 'inline_or', 'newline')\n .default('bullet')\n .description('List format style (default: bullet)'),\n indent: validator.number().default(0).description('Spaces to indent each item (default: 0)'),\n }),\n handler: async (args) => {\n const {\n items,\n style,\n indent: rawIndent,\n } = args as {\n items: string[]\n style: string\n indent: number\n }\n const indent = Math.max(0, Math.floor(rawIndent))\n const pad = ' '.repeat(indent)\n\n if (items.length === 0) return ''\n\n switch (style) {\n case 'bullet':\n return items.map((item) => `${pad}• ${item}`).join('\\n')\n case 'numbered':\n return items.map((item, i) => `${pad}${i + 1}. ${item}`).join('\\n')\n case 'newline':\n return items.map((item) => `${pad}${item}`).join('\\n')\n case 'inline_and':\n case 'inline_or': {\n const conj = style === 'inline_and' ? 'and' : 'or'\n if (items.length === 1) return items[0]\n if (items.length === 2) return `${items[0]} ${conj} ${items[1]}`\n const last = items[items.length - 1]\n const rest = items.slice(0, -1)\n return `${rest.join(', ')}, ${conj} ${last}`\n }\n default:\n return `Error: Unknown style \"${style}\".`\n }\n },\n})\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"formatting.cjs","names":[],"sources":["../../../src/batteries/tools/formatting/index.ts"],"sourcesContent":["/**\n * Pre-constructed tools for locale-aware number, list, table, and text formatting.\n *\n * @module @nhtio/adk/batteries/tools/formatting\n *\n * @remarks\n * Pre-constructed bundled tools for the `formatting` category. Import individually, the whole\n * category, or import every tool via `@nhtio/adk/batteries`.\n */\n\nimport { Tool } from '@nhtio/adk/common'\nimport { isError } from '@nhtio/adk/guards'\nimport { validator } from '@nhtio/validation'\n\n/**\n * Format a number using locale-aware styles.\n *\n * @remarks\n * Supported styles: `decimal`, `currency`, `percent`, `compact` (e.g. `1.2K`), `scientific`\n * (e.g. `1.2e+3`), and `ordinal` (`1st`, `2nd`, `3rd`). Uses `Intl.NumberFormat` and\n * `Intl.PluralRules` from the JS standard library. Returns an error string for non-finite values\n * or invalid currency-without-currency-code.\n */\nexport const formatNumberTool = new Tool({\n name: 'format_number',\n description:\n 'Format a number using locale-aware styles: decimal, currency, percent, compact (1.2K), scientific (1.2e3), or ordinal (1st/2nd/3rd). Supports locale and precision options.',\n inputSchema: validator.object({\n value: validator.number().required().description('The number to format'),\n style: validator\n .string()\n .valid('decimal', 'currency', 'percent', 'compact', 'scientific', 'ordinal')\n .default('decimal')\n .description('Formatting style (default: decimal)'),\n currency: validator\n .string()\n .optional()\n .description(\n 'ISO 4217 currency code — required when style is \"currency\" (e.g. \"USD\", \"EUR\")'\n ),\n locale: validator.string().default('en-US').description('BCP 47 locale tag (default: \"en-US\")'),\n min_decimals: validator\n .number()\n .optional()\n .description('Minimum fraction digits (default: style-dependent)'),\n max_decimals: validator\n .number()\n .optional()\n .description('Maximum fraction digits (default: style-dependent)'),\n }),\n handler: async (args) => {\n const {\n value,\n style,\n locale,\n currency,\n min_decimals: minDec,\n max_decimals: maxDec,\n } = args as {\n value: number\n style: string\n locale: string\n currency?: string\n min_decimals?: number\n max_decimals?: number\n }\n\n if (!Number.isFinite(value)) return `Error: Value must be a finite number (got ${value}).`\n\n try {\n if (style === 'ordinal') {\n const pr = new Intl.PluralRules(locale, { type: 'ordinal' })\n const suffixes: Record<string, string> = { one: 'st', two: 'nd', few: 'rd', other: 'th' }\n const rule = pr.select(value)\n const suffix = suffixes[rule] ?? 'th'\n return `${new Intl.NumberFormat(locale).format(value)}${suffix}`\n }\n\n if (style === 'scientific') {\n const exp = value === 0 ? 0 : Math.floor(Math.log10(Math.abs(value)))\n const mantissa = value / Math.pow(10, exp)\n const mFormatted = new Intl.NumberFormat(locale, {\n minimumFractionDigits: minDec ?? 2,\n maximumFractionDigits: maxDec ?? 6,\n }).format(mantissa)\n return `${mFormatted}e${exp >= 0 ? '+' : ''}${exp}`\n }\n\n const opts: Intl.NumberFormatOptions = {}\n\n if (style === 'currency') {\n if (!currency) return 'Error: \"currency\" parameter is required when style is \"currency\".'\n opts.style = 'currency'\n opts.currency = currency.toUpperCase()\n } else if (style === 'percent') {\n opts.style = 'percent'\n opts.minimumFractionDigits = minDec ?? 1\n opts.maximumFractionDigits = maxDec ?? 2\n } else if (style === 'compact') {\n opts.notation = 'compact'\n opts.compactDisplay = 'short'\n } else {\n opts.style = 'decimal'\n }\n\n if (minDec !== undefined && style !== 'percent') opts.minimumFractionDigits = minDec\n if (maxDec !== undefined && style !== 'percent') opts.maximumFractionDigits = maxDec\n\n return new Intl.NumberFormat(locale, opts).format(value)\n } catch (err) {\n return `Error: ${isError(err) ? err.message : String(err)}`\n }\n },\n})\n\n/**\n * Format an array of items as a list.\n *\n * @remarks\n * Supported styles: `bullet` (`• item`), `numbered` (`1. item`), `inline_and`\n * (`a, b, and c`), `inline_or` (`a, b, or c`), `newline` (one per line).\n */\nexport const formatListTool = new Tool({\n name: 'format_list',\n description:\n 'Format an array of items as a list. Styles: bullet (• item), numbered (1. item), inline_and (\"a, b, and c\"), inline_or (\"a, b, or c\"), newline (one per line).',\n inputSchema: validator.object({\n items: validator\n .array()\n .items(validator.string())\n .required()\n .description('Array of items to format'),\n style: validator\n .string()\n .valid('bullet', 'numbered', 'inline_and', 'inline_or', 'newline')\n .default('bullet')\n .description('List format style (default: bullet)'),\n indent: validator.number().default(0).description('Spaces to indent each item (default: 0)'),\n }),\n handler: async (args) => {\n const {\n items,\n style,\n indent: rawIndent,\n } = args as {\n items: string[]\n style: string\n indent: number\n }\n const indent = Math.max(0, Math.floor(rawIndent))\n const pad = ' '.repeat(indent)\n\n if (items.length === 0) return ''\n\n switch (style) {\n case 'bullet':\n return items.map((item) => `${pad}• ${item}`).join('\\n')\n case 'numbered':\n return items.map((item, i) => `${pad}${i + 1}. ${item}`).join('\\n')\n case 'newline':\n return items.map((item) => `${pad}${item}`).join('\\n')\n case 'inline_and':\n case 'inline_or': {\n const conj = style === 'inline_and' ? 'and' : 'or'\n if (items.length === 1) return items[0]\n if (items.length === 2) return `${items[0]} ${conj} ${items[1]}`\n const last = items[items.length - 1]\n const rest = items.slice(0, -1)\n return `${rest.join(', ')}, ${conj} ${last}`\n }\n default:\n return `Error: Unknown style \"${style}\".`\n }\n },\n})\n"],"mappings":"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"}
|