@nhtio/adk 1.20260609.0 → 1.20260610.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/CHANGELOG.md +132 -9
- package/batteries/llm/ollama/helpers.cjs +9 -0
- package/batteries/llm/ollama/helpers.cjs.map +1 -1
- package/batteries/llm/ollama/helpers.mjs +9 -0
- package/batteries/llm/ollama/helpers.mjs.map +1 -1
- package/batteries/llm/openai_chat_completions/helpers.cjs +19 -0
- package/batteries/llm/openai_chat_completions/helpers.cjs.map +1 -1
- package/batteries/llm/openai_chat_completions/helpers.mjs +19 -0
- package/batteries/llm/openai_chat_completions/helpers.mjs.map +1 -1
- package/batteries/media/builder.d.ts +245 -0
- package/batteries/media/contracts.cjs +119 -0
- package/batteries/media/contracts.cjs.map +1 -0
- package/batteries/media/contracts.d.ts +321 -0
- package/batteries/media/contracts.mjs +110 -0
- package/batteries/media/contracts.mjs.map +1 -0
- package/batteries/media/engines/audio_decode.cjs +92 -0
- package/batteries/media/engines/audio_decode.cjs.map +1 -0
- package/batteries/media/engines/audio_decode.d.ts +46 -0
- package/batteries/media/engines/audio_decode.mjs +90 -0
- package/batteries/media/engines/audio_decode.mjs.map +1 -0
- package/batteries/media/engines/execa_executor.cjs +64 -0
- package/batteries/media/engines/execa_executor.cjs.map +1 -0
- package/batteries/media/engines/execa_executor.d.ts +54 -0
- package/batteries/media/engines/execa_executor.mjs +62 -0
- package/batteries/media/engines/execa_executor.mjs.map +1 -0
- package/batteries/media/engines/fs_workspace.cjs +84 -0
- package/batteries/media/engines/fs_workspace.cjs.map +1 -0
- package/batteries/media/engines/fs_workspace.d.ts +51 -0
- package/batteries/media/engines/fs_workspace.mjs +82 -0
- package/batteries/media/engines/fs_workspace.mjs.map +1 -0
- package/batteries/media/engines/jimp.cjs +116 -0
- package/batteries/media/engines/jimp.cjs.map +1 -0
- package/batteries/media/engines/jimp.d.ts +32 -0
- package/batteries/media/engines/jimp.mjs +114 -0
- package/batteries/media/engines/jimp.mjs.map +1 -0
- package/batteries/media/engines/sharp.cjs +120 -0
- package/batteries/media/engines/sharp.cjs.map +1 -0
- package/batteries/media/engines/sharp.d.ts +42 -0
- package/batteries/media/engines/sharp.mjs +117 -0
- package/batteries/media/engines/sharp.mjs.map +1 -0
- package/batteries/media/engines/soffice.cjs +246 -0
- package/batteries/media/engines/soffice.cjs.map +1 -0
- package/batteries/media/engines/soffice.d.ts +39 -0
- package/batteries/media/engines/soffice.mjs +244 -0
- package/batteries/media/engines/soffice.mjs.map +1 -0
- package/batteries/media/engines/tesseract_js.cjs +87 -0
- package/batteries/media/engines/tesseract_js.cjs.map +1 -0
- package/batteries/media/engines/tesseract_js.d.ts +41 -0
- package/batteries/media/engines/tesseract_js.mjs +85 -0
- package/batteries/media/engines/tesseract_js.mjs.map +1 -0
- package/batteries/media/engines/transformers_asr.cjs +111 -0
- package/batteries/media/engines/transformers_asr.cjs.map +1 -0
- package/batteries/media/engines/transformers_asr.d.ts +41 -0
- package/batteries/media/engines/transformers_asr.mjs +109 -0
- package/batteries/media/engines/transformers_asr.mjs.map +1 -0
- package/batteries/media/exceptions.d.ts +103 -0
- package/batteries/media/forge.cjs +403 -0
- package/batteries/media/forge.cjs.map +1 -0
- package/batteries/media/forge.d.ts +90 -0
- package/batteries/media/forge.mjs +399 -0
- package/batteries/media/forge.mjs.map +1 -0
- package/batteries/media/formats.d.ts +72 -0
- package/batteries/media/index.d.ts +136 -0
- package/batteries/media/lint.cjs +339 -0
- package/batteries/media/lint.cjs.map +1 -0
- package/batteries/media/lint.d.ts +117 -0
- package/batteries/media/lint.mjs +331 -0
- package/batteries/media/lint.mjs.map +1 -0
- package/batteries/media/pipe.d.ts +66 -0
- package/batteries/media/plan.d.ts +133 -0
- package/batteries/media/registry.d.ts +92 -0
- package/batteries/media/runtime.d.ts +105 -0
- package/batteries/media/steps/doc.d.ts +33 -0
- package/batteries/media/steps/image_audio.d.ts +24 -0
- package/batteries/media/steps/ingest.d.ts +25 -0
- package/batteries/media/steps/pages.d.ts +18 -0
- package/batteries/media/steps/sheet.d.ts +36 -0
- package/batteries/media/steps/slides.d.ts +35 -0
- package/batteries/media/steps/text.d.ts +43 -0
- package/batteries/media/validate.d.ts +49 -0
- package/batteries/media/verbs.d.ts +126 -0
- package/batteries/media.cjs +3049 -0
- package/batteries/media.cjs.map +1 -0
- package/batteries/media.mjs +3009 -0
- package/batteries/media.mjs.map +1 -0
- package/batteries/tools/_shared/index.d.ts +142 -0
- package/batteries/tools/_shared.cjs +173 -0
- package/batteries/tools/_shared.cjs.map +1 -0
- package/batteries/tools/_shared.mjs +164 -0
- package/batteries/tools/_shared.mjs.map +1 -0
- package/batteries/tools/index.d.ts +2 -0
- package/batteries/tools/scrapper/exceptions.d.ts +21 -0
- package/batteries/tools/scrapper/index.d.ts +172 -0
- package/batteries/tools/scrapper/shared.d.ts +146 -0
- package/batteries/tools/scrapper.cjs +8 -0
- package/batteries/tools/scrapper.mjs +2 -0
- package/batteries/tools/searxng/index.d.ts +54 -20
- package/batteries/tools/searxng.cjs +2 -1
- package/batteries/tools/searxng.mjs +2 -2
- package/batteries/tools/web_retrieval/index.d.ts +186 -0
- package/batteries/tools/web_retrieval.cjs +206 -0
- package/batteries/tools/web_retrieval.cjs.map +1 -0
- package/batteries/tools/web_retrieval.mjs +201 -0
- package/batteries/tools/web_retrieval.mjs.map +1 -0
- package/batteries/tools.cjs +13 -1
- package/batteries/tools.mjs +4 -2
- package/batteries.cjs +13 -1
- package/batteries.mjs +4 -2
- package/common.d.ts +1 -1
- package/eslint.cjs +1 -1
- package/eslint.mjs +1 -1
- package/exceptions-C7FSHEnV.mjs +87 -0
- package/exceptions-C7FSHEnV.mjs.map +1 -0
- package/exceptions-CQi_lNs1.js +152 -0
- package/exceptions-CQi_lNs1.js.map +1 -0
- package/index.cjs +2 -2
- package/index.mjs +2 -2
- package/mcp/adk-docs-corpus.json +1 -1
- package/package.json +301 -178
- package/scrapper-BOLWYGbD.js +463 -0
- package/scrapper-BOLWYGbD.js.map +1 -0
- package/scrapper-hDKlNuCT.mjs +433 -0
- package/scrapper-hDKlNuCT.mjs.map +1 -0
- package/{searxng-Bkrwhwhw.js → searxng-CJtEpa8p.js} +82 -85
- package/searxng-CJtEpa8p.js.map +1 -0
- package/{searxng-CyA-nEu5.mjs → searxng-riarj_0u.mjs} +76 -85
- package/searxng-riarj_0u.mjs.map +1 -0
- package/skills/adk-assembly/SKILL.md +2 -2
- package/validate-BFaUYHDN.js +1298 -0
- package/validate-BFaUYHDN.js.map +1 -0
- package/validate-DSZ3wicB.mjs +1215 -0
- package/validate-DSZ3wicB.mjs.map +1 -0
- package/searxng-Bkrwhwhw.js.map +0 -1
- package/searxng-CyA-nEu5.mjs.map +0 -1
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The chainable, thenable media builder — the implementor-facing front-end that compiles to
|
|
3
|
+
* the same {@link MediaPlan} as the pipe string and JSON ops.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* Internal sibling of the `@nhtio/adk/batteries/media` entry. The knex lessons applied:
|
|
7
|
+
* `mp(input)` opens a fresh immutable builder; every verb returns a new builder; awaiting the
|
|
8
|
+
* builder executes the chain (no `.execute()`). Domain verbs hang off typed namespaces
|
|
9
|
+
* (`.sheet`, `.slides`, `.image`, `.audio`); shared transforms live on the root.
|
|
10
|
+
*
|
|
11
|
+
* Each verb method appends an op and revalidates lazily — the full plan validates (verb table,
|
|
12
|
+
* arg schemas, engine narrowing) when the chain executes, so building is cheap and the same
|
|
13
|
+
* validator serves all three front-ends.
|
|
14
|
+
*/
|
|
15
|
+
import type { PlanResult, StepPayload } from "./runtime";
|
|
16
|
+
import type { MediaOp, MediaArgValue } from "./plan";
|
|
17
|
+
/** The function a builder calls to execute its accumulated ops. Bound by the pipeline. */
|
|
18
|
+
export type ChainExecutor = (ops: MediaOp[]) => Promise<PlanResult>;
|
|
19
|
+
/** Options accepted by image resize. */
|
|
20
|
+
export interface ResizeOptions {
|
|
21
|
+
/** Target width in pixels. */
|
|
22
|
+
width?: number;
|
|
23
|
+
/** Target height in pixels. */
|
|
24
|
+
height?: number;
|
|
25
|
+
/** Resize fit mode (default cover). */
|
|
26
|
+
fit?: 'cover' | 'contain' | 'fill' | 'inside' | 'outside';
|
|
27
|
+
}
|
|
28
|
+
/** A cell update for `sheet.updateCells`. */
|
|
29
|
+
export interface CellUpdate {
|
|
30
|
+
/** A1-notation address, e.g. `B2`. Alternative to row/col. */
|
|
31
|
+
address?: string;
|
|
32
|
+
/** One-based row index. Pair with `col`. */
|
|
33
|
+
row?: number;
|
|
34
|
+
/** One-based column index, or a column letter. Pair with `row`. */
|
|
35
|
+
col?: number | string;
|
|
36
|
+
/** The value to set. A leading `=` writes a formula. */
|
|
37
|
+
value: string | number | boolean | null;
|
|
38
|
+
}
|
|
39
|
+
/** How other media are referenced from builder verbs: an id string or `{ mediaId }`. */
|
|
40
|
+
export type MediaChainRef = string | {
|
|
41
|
+
mediaId: string;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* The chainable builder. Immutable — every verb returns a new instance sharing the executor.
|
|
45
|
+
* Thenable — `await` runs the chain and resolves to the terminal step's natural type.
|
|
46
|
+
*/
|
|
47
|
+
export declare class MediaChain implements PromiseLike<unknown> {
|
|
48
|
+
#private;
|
|
49
|
+
constructor(exec: ChainExecutor, ops?: MediaOp[]);
|
|
50
|
+
/** Convert to another format (requires a convert engine). */
|
|
51
|
+
convert(to: string): MediaChain;
|
|
52
|
+
/** Keep only the listed 1-based pages. */
|
|
53
|
+
select(options: {
|
|
54
|
+
pages: number[];
|
|
55
|
+
}): MediaChain;
|
|
56
|
+
/** Split by page (optionally grouped `[[start,end], …]`). Terminal: resolves to media list. */
|
|
57
|
+
split(options?: {
|
|
58
|
+
by?: 'page' | 'section';
|
|
59
|
+
ranges?: number[][];
|
|
60
|
+
}): MediaChain;
|
|
61
|
+
/** Merge other media into this one, in order. */
|
|
62
|
+
merge(...others: MediaChainRef[]): MediaChain;
|
|
63
|
+
/** Reorder pages by 1-based index. */
|
|
64
|
+
reorder(order: number[]): MediaChain;
|
|
65
|
+
/** Redact matching text (literals or RegExp). */
|
|
66
|
+
redact(options: {
|
|
67
|
+
match: Array<string | RegExp> | string | RegExp;
|
|
68
|
+
replace?: string;
|
|
69
|
+
}): MediaChain;
|
|
70
|
+
/** Remove potentially unsafe embedded content. */
|
|
71
|
+
sanitize(): MediaChain;
|
|
72
|
+
/** Normalize structure/encoding. */
|
|
73
|
+
normalize(): MediaChain;
|
|
74
|
+
/** Replace the first occurrence of anchor text. */
|
|
75
|
+
updateText(anchor: string, replace: string): MediaChain;
|
|
76
|
+
/** Compare against another media. Terminal: resolves to a structured diff. */
|
|
77
|
+
diff(other: MediaChainRef): MediaChain;
|
|
78
|
+
/** Apply a unified-diff patch. */
|
|
79
|
+
applyPatch(patch: string, options?: {
|
|
80
|
+
with?: MediaChainRef[];
|
|
81
|
+
}): MediaChain;
|
|
82
|
+
/** Extract text (routes by format; OCR engine used when needed). Terminal: resolves to text. */
|
|
83
|
+
extractText(options?: {
|
|
84
|
+
ocr?: 'off' | 'auto' | 'force';
|
|
85
|
+
ocrOut?: 'txt' | 'hocr' | 'json';
|
|
86
|
+
lang?: string[];
|
|
87
|
+
}): MediaChain;
|
|
88
|
+
/** Extract metadata. Terminal: resolves to a metadata object. */
|
|
89
|
+
extractMetadata(): MediaChain;
|
|
90
|
+
/** Extract embedded assets. Terminal: resolves to a media list. */
|
|
91
|
+
extractAssets(options?: {
|
|
92
|
+
types?: Array<'image' | 'font' | 'attachment' | 'all'>;
|
|
93
|
+
}): MediaChain;
|
|
94
|
+
/** Chunk extracted text. Terminal: resolves to a chunk array. */
|
|
95
|
+
chunk(options?: {
|
|
96
|
+
strategy?: 'sentence' | 'paragraph' | 'fixed';
|
|
97
|
+
size?: number;
|
|
98
|
+
overlap?: number;
|
|
99
|
+
}): MediaChain;
|
|
100
|
+
/** Spreadsheet mutations. */
|
|
101
|
+
get sheet(): SheetNamespace;
|
|
102
|
+
/** Presentation mutations. */
|
|
103
|
+
get slides(): SlidesNamespace;
|
|
104
|
+
/** Image transforms. */
|
|
105
|
+
get image(): ImageNamespace;
|
|
106
|
+
/** Audio operations. */
|
|
107
|
+
get audio(): AudioNamespace;
|
|
108
|
+
/** The accumulated ops (a copy). */
|
|
109
|
+
toOps(): MediaOp[];
|
|
110
|
+
/** The canonical pipe form of the accumulated chain. */
|
|
111
|
+
toPipe(): string;
|
|
112
|
+
/** Internal — used by namespaces to append. */
|
|
113
|
+
withOp(verb: string, args: Record<string, MediaArgValue>): MediaChain;
|
|
114
|
+
/** Thenable: awaiting the chain executes it. */
|
|
115
|
+
then<TResult1 = unknown, TResult2 = never>(onfulfilled?: ((value: unknown) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): PromiseLike<TResult1 | TResult2>;
|
|
116
|
+
/** Execute and return the raw {@link PlanResult} (no unwrapping). */
|
|
117
|
+
run(): Promise<PlanResult>;
|
|
118
|
+
}
|
|
119
|
+
/** `sheet.*` verbs, mirrored from the verb table. */
|
|
120
|
+
export declare class SheetNamespace {
|
|
121
|
+
#private;
|
|
122
|
+
constructor(chain: MediaChain);
|
|
123
|
+
/** Insert rows. */
|
|
124
|
+
addRows(rows: Array<Array<string | number | boolean | null>>, options?: {
|
|
125
|
+
sheet?: string | number;
|
|
126
|
+
before?: number;
|
|
127
|
+
after?: number;
|
|
128
|
+
}): MediaChain;
|
|
129
|
+
/** Insert columns. */
|
|
130
|
+
addColumns(options: {
|
|
131
|
+
sheet?: string | number;
|
|
132
|
+
headers?: string[];
|
|
133
|
+
columns?: Array<{
|
|
134
|
+
header: string;
|
|
135
|
+
values?: Array<string | number | boolean | null>;
|
|
136
|
+
}>;
|
|
137
|
+
before?: number;
|
|
138
|
+
after?: number;
|
|
139
|
+
}): MediaChain;
|
|
140
|
+
/** Update cells. */
|
|
141
|
+
updateCells(updates: CellUpdate[], options?: {
|
|
142
|
+
sheet?: string | number;
|
|
143
|
+
}): MediaChain;
|
|
144
|
+
/** Delete rows by 1-based index. */
|
|
145
|
+
deleteRows(rows: number[], options?: {
|
|
146
|
+
sheet?: string | number;
|
|
147
|
+
}): MediaChain;
|
|
148
|
+
/** Delete columns by 1-based index. */
|
|
149
|
+
deleteColumns(columns: number[], options?: {
|
|
150
|
+
sheet?: string | number;
|
|
151
|
+
}): MediaChain;
|
|
152
|
+
/** Rename a worksheet (name-targeted). */
|
|
153
|
+
renameSheet(sheet: string, to: string): MediaChain;
|
|
154
|
+
/** Add a worksheet. */
|
|
155
|
+
addSheet(name: string, options?: {
|
|
156
|
+
at?: number;
|
|
157
|
+
}): MediaChain;
|
|
158
|
+
/** Remove a worksheet (name-targeted). */
|
|
159
|
+
removeSheet(sheet: string): MediaChain;
|
|
160
|
+
/** Reorder worksheets by names and/or 1-based indices. */
|
|
161
|
+
reorderSheets(order: Array<string | number>): MediaChain;
|
|
162
|
+
/** Table transforms by header name. */
|
|
163
|
+
transformTable(options: {
|
|
164
|
+
sheet?: string | number;
|
|
165
|
+
headerRow?: number;
|
|
166
|
+
select?: string[];
|
|
167
|
+
drop?: string[];
|
|
168
|
+
rename?: Array<{
|
|
169
|
+
from: string;
|
|
170
|
+
to: string;
|
|
171
|
+
}>;
|
|
172
|
+
}): MediaChain;
|
|
173
|
+
}
|
|
174
|
+
/** `slides.*` verbs. */
|
|
175
|
+
export declare class SlidesNamespace {
|
|
176
|
+
#private;
|
|
177
|
+
constructor(chain: MediaChain);
|
|
178
|
+
/** Add a slide. */
|
|
179
|
+
add(options?: {
|
|
180
|
+
at?: number;
|
|
181
|
+
title?: string;
|
|
182
|
+
layout?: string;
|
|
183
|
+
}): MediaChain;
|
|
184
|
+
/** Update text on a slide. */
|
|
185
|
+
updateText(text: string, options?: {
|
|
186
|
+
slide?: string | number;
|
|
187
|
+
placeholder?: string;
|
|
188
|
+
}): MediaChain;
|
|
189
|
+
/** Update table cells on a slide. */
|
|
190
|
+
updateTable(updates: Array<{
|
|
191
|
+
row: number;
|
|
192
|
+
col: number;
|
|
193
|
+
value: string;
|
|
194
|
+
}>, options?: {
|
|
195
|
+
slide?: string | number;
|
|
196
|
+
}): MediaChain;
|
|
197
|
+
/** Replace an image on a slide with another media. */
|
|
198
|
+
updateImage(withMedia: MediaChainRef, options?: {
|
|
199
|
+
slide?: string | number;
|
|
200
|
+
placeholder?: string;
|
|
201
|
+
}): MediaChain;
|
|
202
|
+
/** Update chart data on a slide. */
|
|
203
|
+
updateChart(data: unknown[][], options?: {
|
|
204
|
+
slide?: string | number;
|
|
205
|
+
}): MediaChain;
|
|
206
|
+
/** Delete slides by 1-based index. */
|
|
207
|
+
delete(slides: number[]): MediaChain;
|
|
208
|
+
/** Reorder slides. */
|
|
209
|
+
reorder(order: number[]): MediaChain;
|
|
210
|
+
/** Duplicate a slide. */
|
|
211
|
+
duplicate(slide: number, options?: {
|
|
212
|
+
at?: number;
|
|
213
|
+
}): MediaChain;
|
|
214
|
+
}
|
|
215
|
+
/** `image.*` verbs (the runtime fuses adjacent image steps into one engine pass). */
|
|
216
|
+
export declare class ImageNamespace {
|
|
217
|
+
#private;
|
|
218
|
+
constructor(chain: MediaChain);
|
|
219
|
+
/** Resize. */
|
|
220
|
+
resize(options: ResizeOptions): MediaChain;
|
|
221
|
+
/** Re-encode to another format. */
|
|
222
|
+
format(to: string, options?: {
|
|
223
|
+
quality?: number;
|
|
224
|
+
stripMetadata?: boolean;
|
|
225
|
+
}): MediaChain;
|
|
226
|
+
/** Rotate clockwise. */
|
|
227
|
+
rotate(deg: 90 | 180 | 270): MediaChain;
|
|
228
|
+
/** Flip. */
|
|
229
|
+
flip(axis: 'horizontal' | 'vertical' | 'both'): MediaChain;
|
|
230
|
+
/** Strip EXIF/ICC metadata. */
|
|
231
|
+
stripMetadata(): MediaChain;
|
|
232
|
+
}
|
|
233
|
+
/** `audio.*` verbs. */
|
|
234
|
+
export declare class AudioNamespace {
|
|
235
|
+
#private;
|
|
236
|
+
constructor(chain: MediaChain);
|
|
237
|
+
/** Transcribe speech. Terminal: resolves to text (or srt/vtt/json per `out`). */
|
|
238
|
+
transcribe(options?: {
|
|
239
|
+
language?: string;
|
|
240
|
+
out?: 'txt' | 'srt' | 'vtt' | 'json';
|
|
241
|
+
translate?: boolean;
|
|
242
|
+
}): MediaChain;
|
|
243
|
+
}
|
|
244
|
+
/** The input shapes `mp(...)` accepts. */
|
|
245
|
+
export type ChainInput = StepPayload;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
require("../../chunk-Ble4zEEl.js");
|
|
3
|
+
const require_tool_registry = require("../../tool_registry-CKJPze3j.js");
|
|
4
|
+
require("../../guards.cjs");
|
|
5
|
+
//#region src/batteries/media/contracts.ts
|
|
6
|
+
/**
|
|
7
|
+
* Generic engine contracts for the media pipeline battery — the seams every implementation
|
|
8
|
+
* (bundled or BYO) plugs into.
|
|
9
|
+
*
|
|
10
|
+
* @module @nhtio/adk/batteries/media/contracts
|
|
11
|
+
*
|
|
12
|
+
* @remarks
|
|
13
|
+
* Engines are seams, not policies. An engine is a self-declaring capability provider: it
|
|
14
|
+
* states exactly which transforms it supports — {@link ConvertCapability} edges (input MIME
|
|
15
|
+
* patterns to output format tokens) and {@link MutateCapability} groups (same-format content
|
|
16
|
+
* transforms) — and the pipeline dispatches against those declarations. There are only two
|
|
17
|
+
* capability shapes because there are only two things a media engine ever does: change the
|
|
18
|
+
* format, or change the content. OCR is a convert (image to text). Transcription is a convert
|
|
19
|
+
* (PCM to text). Decoding audio is a convert (container to PCM). Resizing is a mutate. A new
|
|
20
|
+
* capability is a new edge in the data, never a new contract.
|
|
21
|
+
*
|
|
22
|
+
* Engines are supplied to `createMediaPipeline` as a flat ordered array and resolved eagerly
|
|
23
|
+
* at construction (declarations drive verb narrowing, so they must be known up front).
|
|
24
|
+
* Bundled engines stay cheap to resolve: their heavy peer dependencies load lazily inside
|
|
25
|
+
* the capability methods, on first actual use.
|
|
26
|
+
*
|
|
27
|
+
* All contracts are duck-typed: validation guards check structure, not class identity, so a
|
|
28
|
+
* consumer can implement an interface from scratch or adapt an existing client. Contracts are
|
|
29
|
+
* enforced at runtime — construction validates every engine and every declared capability and
|
|
30
|
+
* throws `E_INVALID_MEDIA_PIPELINE_CONFIG` naming the offending index when a value fails.
|
|
31
|
+
*
|
|
32
|
+
* Two further contracts exist so that even "run a binary" and "give a binary a file" are
|
|
33
|
+
* movable seams rather than Node assumptions:
|
|
34
|
+
*
|
|
35
|
+
* - {@link BinaryExecutor} — how an invocation runs. The bundled `execa_executor` wraps execa;
|
|
36
|
+
* a browser/remote/sandbox executor satisfies the same contract.
|
|
37
|
+
* - {@link ScratchWorkspace} — bytes ⇄ executor-visible paths. A sibling of `ByteStore`, NOT a
|
|
38
|
+
* `ByteStore`: byte stores promise in-process readers, while binaries are foreign processes
|
|
39
|
+
* that need real paths. The bundled `fs_workspace` uses `node:fs/promises` via an async
|
|
40
|
+
* resolver; any implementation whose paths the chosen executor can see is valid — that
|
|
41
|
+
* compatibility is the consumer's composition decision.
|
|
42
|
+
*/
|
|
43
|
+
/**
|
|
44
|
+
* The virtual MIME type for decoded mono PCM audio — the intermediate between an audio
|
|
45
|
+
* container and a transcription. Bytes are little-endian Float32 samples in `[-1, 1]`;
|
|
46
|
+
* a {@link ConvertOutput} carrying PCM must set `meta.sampleRate` (Hz).
|
|
47
|
+
*/
|
|
48
|
+
var PCM_MIME = "audio/x-adk-pcm";
|
|
49
|
+
/**
|
|
50
|
+
* Pack PCM samples into transport bytes for a {@link ConvertOutput}.
|
|
51
|
+
*
|
|
52
|
+
* @remarks
|
|
53
|
+
* A `Float32Array` view over arbitrary `Uint8Array` bytes requires 4-byte alignment, which
|
|
54
|
+
* sliced buffers do not guarantee — this helper (and {@link bytesToPcm}) copy-normalize so
|
|
55
|
+
* neither side has to think about alignment.
|
|
56
|
+
*
|
|
57
|
+
* @param pcm - Mono PCM samples in `[-1, 1]`.
|
|
58
|
+
* @returns The samples as little-endian Float32 bytes.
|
|
59
|
+
*/
|
|
60
|
+
var pcmToBytes = (pcm) => {
|
|
61
|
+
const copy = new Float32Array(pcm);
|
|
62
|
+
return new Uint8Array(copy.buffer, 0, copy.byteLength);
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Read PCM samples back out of transport bytes.
|
|
66
|
+
*
|
|
67
|
+
* @param bytes - Little-endian Float32 bytes (as produced by {@link pcmToBytes}).
|
|
68
|
+
* @returns The mono PCM samples.
|
|
69
|
+
*/
|
|
70
|
+
var bytesToPcm = (bytes) => {
|
|
71
|
+
const aligned = new Uint8Array(bytes.length);
|
|
72
|
+
aligned.set(bytes);
|
|
73
|
+
return new Float32Array(aligned.buffer, 0, Math.floor(bytes.length / 4));
|
|
74
|
+
};
|
|
75
|
+
var hasFns = (value, names) => require_tool_registry.isObject(value) && names.every((n) => typeof value[n] === "function");
|
|
76
|
+
var isStringArray = (value) => Array.isArray(value) && value.every((v) => typeof v === "string");
|
|
77
|
+
/** `true` when `value` structurally implements {@link BinaryExecutor}. */
|
|
78
|
+
var implementsBinaryExecutor = (value) => hasFns(value, ["exec"]);
|
|
79
|
+
/** `true` when `value` structurally implements {@link ScratchWorkspace}. */
|
|
80
|
+
var implementsScratchWorkspace = (value) => hasFns(value, [
|
|
81
|
+
"materialize",
|
|
82
|
+
"read",
|
|
83
|
+
"dir",
|
|
84
|
+
"list",
|
|
85
|
+
"dispose"
|
|
86
|
+
]);
|
|
87
|
+
/** `true` when `value` structurally implements {@link ConvertCapability}. */
|
|
88
|
+
var implementsConvertCapability = (value) => hasFns(value, ["convert"]) && isStringArray(value.from) && isStringArray(value.to);
|
|
89
|
+
/** `true` when `value` structurally implements {@link MutateCapability}. */
|
|
90
|
+
var implementsMutateCapability = (value) => hasFns(value, ["mutate"]) && isStringArray(value.over) && isStringArray(value.ops) && isStringArray(value.encodes);
|
|
91
|
+
/**
|
|
92
|
+
* `true` when `value` structurally implements {@link MediaEngine}: a string id plus at least
|
|
93
|
+
* one well-formed capability entry. Every declared entry must pass its capability guard.
|
|
94
|
+
*/
|
|
95
|
+
var implementsMediaEngine = (value) => {
|
|
96
|
+
if (!require_tool_registry.isObject(value)) return false;
|
|
97
|
+
const engine = value;
|
|
98
|
+
if (typeof engine.id !== "string" || engine.id.length === 0) return false;
|
|
99
|
+
const converts = engine.converts;
|
|
100
|
+
const mutates = engine.mutates;
|
|
101
|
+
if (converts !== void 0) {
|
|
102
|
+
if (!Array.isArray(converts) || !converts.every(implementsConvertCapability)) return false;
|
|
103
|
+
}
|
|
104
|
+
if (mutates !== void 0) {
|
|
105
|
+
if (!Array.isArray(mutates) || !mutates.every(implementsMutateCapability)) return false;
|
|
106
|
+
}
|
|
107
|
+
return (Array.isArray(converts) ? converts.length : 0) + (Array.isArray(mutates) ? mutates.length : 0) > 0;
|
|
108
|
+
};
|
|
109
|
+
//#endregion
|
|
110
|
+
exports.PCM_MIME = PCM_MIME;
|
|
111
|
+
exports.bytesToPcm = bytesToPcm;
|
|
112
|
+
exports.implementsBinaryExecutor = implementsBinaryExecutor;
|
|
113
|
+
exports.implementsConvertCapability = implementsConvertCapability;
|
|
114
|
+
exports.implementsMediaEngine = implementsMediaEngine;
|
|
115
|
+
exports.implementsMutateCapability = implementsMutateCapability;
|
|
116
|
+
exports.implementsScratchWorkspace = implementsScratchWorkspace;
|
|
117
|
+
exports.pcmToBytes = pcmToBytes;
|
|
118
|
+
|
|
119
|
+
//# sourceMappingURL=contracts.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contracts.cjs","names":[],"sources":["../../../src/batteries/media/contracts.ts"],"sourcesContent":["/**\n * Generic engine contracts for the media pipeline battery — the seams every implementation\n * (bundled or BYO) plugs into.\n *\n * @module @nhtio/adk/batteries/media/contracts\n *\n * @remarks\n * Engines are seams, not policies. An engine is a self-declaring capability provider: it\n * states exactly which transforms it supports — {@link ConvertCapability} edges (input MIME\n * patterns to output format tokens) and {@link MutateCapability} groups (same-format content\n * transforms) — and the pipeline dispatches against those declarations. There are only two\n * capability shapes because there are only two things a media engine ever does: change the\n * format, or change the content. OCR is a convert (image to text). Transcription is a convert\n * (PCM to text). Decoding audio is a convert (container to PCM). Resizing is a mutate. A new\n * capability is a new edge in the data, never a new contract.\n *\n * Engines are supplied to `createMediaPipeline` as a flat ordered array and resolved eagerly\n * at construction (declarations drive verb narrowing, so they must be known up front).\n * Bundled engines stay cheap to resolve: their heavy peer dependencies load lazily inside\n * the capability methods, on first actual use.\n *\n * All contracts are duck-typed: validation guards check structure, not class identity, so a\n * consumer can implement an interface from scratch or adapt an existing client. Contracts are\n * enforced at runtime — construction validates every engine and every declared capability and\n * throws `E_INVALID_MEDIA_PIPELINE_CONFIG` naming the offending index when a value fails.\n *\n * Two further contracts exist so that even \"run a binary\" and \"give a binary a file\" are\n * movable seams rather than Node assumptions:\n *\n * - {@link BinaryExecutor} — how an invocation runs. The bundled `execa_executor` wraps execa;\n * a browser/remote/sandbox executor satisfies the same contract.\n * - {@link ScratchWorkspace} — bytes ⇄ executor-visible paths. A sibling of `ByteStore`, NOT a\n * `ByteStore`: byte stores promise in-process readers, while binaries are foreign processes\n * that need real paths. The bundled `fs_workspace` uses `node:fs/promises` via an async\n * resolver; any implementation whose paths the chosen executor can see is valid — that\n * compatibility is the consumer's composition decision.\n */\n\nimport { isObject } from '@nhtio/adk/guards'\n\n// ── shared helper shapes ─────────────────────────────────────────────────────\n\n/**\n * A value-or-resolver: the canonical way to supply an engine. Resolvers may be sync or async\n * (dynamic import) and may resolve to the value directly or a `{ default: value }` module\n * namespace. Engine resolvers run eagerly at pipeline construction — the engine module itself\n * is cheap; heavy peer dependencies load lazily inside capability methods.\n */\nexport type EngineResolver<T = MediaEngine> =\n | T\n | (() => T | { default: T } | Promise<T | { default: T }>)\n\n/** Common result shape for engines that transform bytes to bytes. */\nexport interface EngineBytesResult {\n /** The output bytes. */\n bytes: Uint8Array\n /** The output MIME type. */\n mimeType: string\n}\n\n// ── process execution + scratch filesystem ──────────────────────────────────\n\n/** A single binary invocation handed to a {@link BinaryExecutor}. */\nexport interface BinaryInvocation {\n /** The command to run (an absolute path or a name the executor can resolve). */\n cmd: string\n /** Arguments, exec-style (no shell interpolation). */\n args: string[]\n /** Wall-clock timeout in milliseconds. */\n timeoutMs?: number\n /** Abort signal to cancel the invocation. */\n signal?: AbortSignal\n}\n\n/** The settled result of a {@link BinaryExecutor.exec} call. */\nexport interface BinaryExecResult {\n /** The process exit code (or -1 when the process failed to start). */\n exitCode: number\n /** Captured standard output. */\n stdout: string\n /** Captured standard error. */\n stderr: string\n /** `true` when the invocation failed (non-zero exit, spawn failure, timeout, abort). */\n failed: boolean\n}\n\n/**\n * Runs a binary invocation to completion. How and where it runs — local child process, remote\n * runner, sandbox, container, a browser-side WASI shim — is the implementation's business.\n */\nexport interface BinaryExecutor {\n /**\n * Run one invocation to completion and report the result. Implementations must not throw on\n * non-zero exits — report via `failed`/`exitCode` so callers map failures to readable errors.\n *\n * @param invocation - The command, args, and limits to run.\n * @returns The settled result.\n */\n exec(invocation: BinaryInvocation): Promise<BinaryExecResult>\n}\n\n/**\n * Bytes ⇄ executor-visible paths. The seam that lets binary-backed engines exchange files\n * with the process (or remote runner) that executes them.\n */\nexport interface ScratchWorkspace {\n /**\n * Write `bytes` into the workspace under `filename` and return the absolute path the\n * paired executor can open.\n *\n * @param bytes - The content to materialize.\n * @param filename - The basename to use (extension matters to format-sniffing binaries).\n * @returns The absolute path.\n */\n materialize(bytes: Uint8Array, filename: string): Promise<string>\n /**\n * Read a file the executor produced inside the workspace.\n *\n * @param path - The absolute path to read.\n * @returns The file bytes.\n */\n read(path: string): Promise<Uint8Array>\n /** The workspace root directory, for `--outdir`-style binary arguments. */\n dir(): string\n /** List the files currently in the workspace root (basenames). */\n list(): Promise<string[]>\n /** Remove the workspace and everything in it. Engines call this in `finally`. */\n dispose(): Promise<void>\n}\n\n/**\n * A factory for per-execution scratch workspaces. Engines mint one workspace per invocation\n * so concurrent executions never share a directory.\n */\nexport type ScratchWorkspaceFactory = () => ScratchWorkspace | Promise<ScratchWorkspace>\n\n// ── the format vocabulary ────────────────────────────────────────────────────\n\n/**\n * An input-matching pattern: an exact MIME type (`application/pdf`), a family wildcard\n * (`image/*`), or a virtual MIME such as {@link PCM_MIME}.\n */\nexport type MimePattern = string\n\n/**\n * The virtual MIME type for decoded mono PCM audio — the intermediate between an audio\n * container and a transcription. Bytes are little-endian Float32 samples in `[-1, 1]`;\n * a {@link ConvertOutput} carrying PCM must set `meta.sampleRate` (Hz).\n */\nexport const PCM_MIME = 'audio/x-adk-pcm'\n\n/**\n * Pack PCM samples into transport bytes for a {@link ConvertOutput}.\n *\n * @remarks\n * A `Float32Array` view over arbitrary `Uint8Array` bytes requires 4-byte alignment, which\n * sliced buffers do not guarantee — this helper (and {@link bytesToPcm}) copy-normalize so\n * neither side has to think about alignment.\n *\n * @param pcm - Mono PCM samples in `[-1, 1]`.\n * @returns The samples as little-endian Float32 bytes.\n */\nexport const pcmToBytes = (pcm: Float32Array): Uint8Array => {\n const copy = new Float32Array(pcm)\n return new Uint8Array(copy.buffer, 0, copy.byteLength)\n}\n\n/**\n * Read PCM samples back out of transport bytes.\n *\n * @param bytes - Little-endian Float32 bytes (as produced by {@link pcmToBytes}).\n * @returns The mono PCM samples.\n */\nexport const bytesToPcm = (bytes: Uint8Array): Float32Array => {\n const aligned = new Uint8Array(bytes.length)\n aligned.set(bytes)\n return new Float32Array(aligned.buffer, 0, Math.floor(bytes.length / 4))\n}\n\n// ── convert options (typed, augmentable) ─────────────────────────────────────\n\n/** Options understood by OCR-flavored converts (`image/*` → `txt`/`hocr`/`json`). */\nexport interface OcrConvertOptions {\n /** Recognition language hints (e.g. `['eng','deu']`). */\n languages?: readonly string[]\n}\n\n/** Options understood by transcription-flavored converts ({@link PCM_MIME} → `txt`/`srt`/`vtt`/`json`). */\nexport interface AsrConvertOptions {\n /** Source-language hint (BCP-47-ish). */\n lang?: string\n /** Translate the transcription to English. */\n translate?: boolean\n}\n\n/** Options understood by embedded-image extraction converts (`application/pdf` → `images`). */\nexport interface ImagesConvertOptions {\n /**\n * Preferred output encoding token (`jpg`, `png`, …). An extractor that can emit it natively\n * should; outputs in other encodings are re-encoded downstream by the requesting step.\n */\n format?: string\n}\n\n/**\n * The options bag carried by a {@link ConvertRequest} — one typed, augmentable interface\n * merging every documented convention.\n *\n * @remarks\n * Consumers add their own keys via declaration merging against this module:\n *\n * ```ts\n * declare module '@nhtio/adk/batteries/media/contracts' {\n * interface ConvertOptions {\n * watermark?: { text: string }\n * }\n * }\n * ```\n *\n * Typo'd keys become excess-property compile errors at literal call sites; the runtime stays\n * open — engines must ignore keys they don't understand (multi-hop conversion forwards one\n * bag to every hop). The namespace is flat and globally merged, so BYO keys should be named\n * to avoid collisions (prefix by engine where ambiguous).\n */\nexport interface ConvertOptions\n extends OcrConvertOptions, AsrConvertOptions, ImagesConvertOptions {}\n\n// ── the two capabilities ─────────────────────────────────────────────────────\n\n/** A format-changing request handed to a {@link ConvertCapability}. */\nexport interface ConvertRequest {\n /** The input content bytes. */\n bytes: Uint8Array\n /** The input MIME type. */\n mimeType: string\n /** The input filename (extension informs format sniffing). */\n filename: string\n /** The target format token (`pdf`, `docx`, `txt`, `pcm`, `images`, …). */\n to: string\n /** Capability-specific options — see {@link ConvertOptions}. */\n options?: ConvertOptions\n /** Abort signal threaded from the pipeline execution. */\n signal?: AbortSignal\n}\n\n/** One output of a convert — most converts yield exactly one; `images` yields many. */\nexport interface ConvertOutput {\n /** The output bytes. */\n bytes: Uint8Array\n /** The output MIME type (honest — native encoding, no silent re-encode). */\n mimeType: string\n /** Output metadata (e.g. `{ sampleRate: 44100 }` on {@link PCM_MIME} outputs). */\n meta?: Record<string, unknown>\n}\n\n/** The settled result of a convert. */\nexport interface ConvertResult {\n /** The outputs, in source order. */\n outputs: readonly ConvertOutput[]\n}\n\n/**\n * One uniform block of an engine's conversion matrix: every format token in `to` is\n * producible from every input matching `from`.\n *\n * @remarks\n * Declarations are plain data — the registry reads them without calling engine code. An\n * input-dependent matrix (LibreOffice: docx→pdf yes, docx→xlsx no, ods→xlsx yes) is expressed\n * as several capability groups, each a uniform from×to block.\n */\nexport interface ConvertCapability {\n /** Input patterns this block accepts. */\n from: readonly MimePattern[]\n /** Format tokens producible from every `from` member. */\n to: readonly string[]\n /**\n * Perform the conversion.\n *\n * @param request - The input bytes, target token, and options.\n * @returns The conversion outputs.\n */\n convert(request: ConvertRequest): Promise<ConvertResult>\n}\n\n/**\n * A same-format content transform handed to a {@link MutateCapability} — the fused image\n * request: adjacent `image.*` steps fold into ONE request so a resize→rotate→format chain\n * costs a single decode/encode.\n */\nexport interface MutateRequest {\n /** The input bytes. */\n bytes: Uint8Array\n /** The input MIME type. */\n mimeType: string\n /** Resize, when requested. */\n resize?: {\n width?: number\n height?: number\n fit?: 'cover' | 'contain' | 'fill' | 'inside' | 'outside'\n }\n /** Clockwise rotation in degrees. */\n rotate?: 90 | 180 | 270\n /** Flip axes. */\n flip?: { horizontal?: boolean; vertical?: boolean }\n /** Remove EXIF/ICC metadata. */\n stripMetadata?: boolean\n /** Re-encode target, when requested (rides the same fused call). */\n format?: { to: string; quality?: number }\n /** Abort signal threaded from the pipeline execution. */\n signal?: AbortSignal\n}\n\n/** A same-format content-transform capability group. */\nexport interface MutateCapability {\n /** Input patterns this block mutates. */\n over: readonly MimePattern[]\n /** Content operations supported (`resize`, `rotate`, `flip`, `strip_metadata`). */\n ops: readonly string[]\n /** Format tokens reachable via `request.format` in the same fused call. */\n encodes: readonly string[]\n /**\n * Apply the fused transform.\n *\n * @param request - The folded operations and input bytes.\n * @returns The transformed bytes.\n */\n mutate(request: MutateRequest): Promise<EngineBytesResult>\n}\n\n/**\n * A self-declaring media engine: an id for error messages plus the capabilities it provides.\n * At least one capability entry is required.\n */\nexport interface MediaEngine {\n /** Stable identifier used in config and dispatch error messages (`jimp`, `soffice`, …). */\n readonly id: string\n /** Format-changing capability groups. */\n readonly converts?: readonly ConvertCapability[]\n /** Same-format content-transform capability groups. */\n readonly mutates?: readonly MutateCapability[]\n}\n\n// ── duck-typed guards ────────────────────────────────────────────────────────\n\nconst hasFns = (value: unknown, names: readonly string[]): boolean =>\n isObject(value) && names.every((n) => typeof (value as Record<string, unknown>)[n] === 'function')\n\nconst isStringArray = (value: unknown): boolean =>\n Array.isArray(value) && value.every((v) => typeof v === 'string')\n\n/** `true` when `value` structurally implements {@link BinaryExecutor}. */\nexport const implementsBinaryExecutor = (value: unknown): value is BinaryExecutor =>\n hasFns(value, ['exec'])\n\n/** `true` when `value` structurally implements {@link ScratchWorkspace}. */\nexport const implementsScratchWorkspace = (value: unknown): value is ScratchWorkspace =>\n hasFns(value, ['materialize', 'read', 'dir', 'list', 'dispose'])\n\n/** `true` when `value` structurally implements {@link ConvertCapability}. */\nexport const implementsConvertCapability = (value: unknown): value is ConvertCapability =>\n hasFns(value, ['convert']) &&\n isStringArray((value as ConvertCapability).from) &&\n isStringArray((value as ConvertCapability).to)\n\n/** `true` when `value` structurally implements {@link MutateCapability}. */\nexport const implementsMutateCapability = (value: unknown): value is MutateCapability =>\n hasFns(value, ['mutate']) &&\n isStringArray((value as MutateCapability).over) &&\n isStringArray((value as MutateCapability).ops) &&\n isStringArray((value as MutateCapability).encodes)\n\n/**\n * `true` when `value` structurally implements {@link MediaEngine}: a string id plus at least\n * one well-formed capability entry. Every declared entry must pass its capability guard.\n */\nexport const implementsMediaEngine = (value: unknown): value is MediaEngine => {\n if (!isObject(value)) return false\n const engine = value as unknown as MediaEngine\n if (typeof engine.id !== 'string' || engine.id.length === 0) return false\n const converts = engine.converts\n const mutates = engine.mutates\n if (converts !== undefined) {\n if (!Array.isArray(converts) || !converts.every(implementsConvertCapability)) return false\n }\n if (mutates !== undefined) {\n if (!Array.isArray(mutates) || !mutates.every(implementsMutateCapability)) return false\n }\n const convertCount = Array.isArray(converts) ? converts.length : 0\n const mutateCount = Array.isArray(mutates) ? mutates.length : 0\n return convertCount + mutateCount > 0\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqJA,IAAa,WAAW;;;;;;;;;;;;AAaxB,IAAa,cAAc,QAAkC;CAC3D,MAAM,OAAO,IAAI,aAAa,GAAG;CACjC,OAAO,IAAI,WAAW,KAAK,QAAQ,GAAG,KAAK,UAAU;AACvD;;;;;;;AAQA,IAAa,cAAc,UAAoC;CAC7D,MAAM,UAAU,IAAI,WAAW,MAAM,MAAM;CAC3C,QAAQ,IAAI,KAAK;CACjB,OAAO,IAAI,aAAa,QAAQ,QAAQ,GAAG,KAAK,MAAM,MAAM,SAAS,CAAC,CAAC;AACzE;AAuKA,IAAM,UAAU,OAAgB,UAC9B,sBAAA,SAAS,KAAK,KAAK,MAAM,OAAO,MAAM,OAAQ,MAAkC,OAAO,UAAU;AAEnG,IAAM,iBAAiB,UACrB,MAAM,QAAQ,KAAK,KAAK,MAAM,OAAO,MAAM,OAAO,MAAM,QAAQ;;AAGlE,IAAa,4BAA4B,UACvC,OAAO,OAAO,CAAC,MAAM,CAAC;;AAGxB,IAAa,8BAA8B,UACzC,OAAO,OAAO;CAAC;CAAe;CAAQ;CAAO;CAAQ;AAAS,CAAC;;AAGjE,IAAa,+BAA+B,UAC1C,OAAO,OAAO,CAAC,SAAS,CAAC,KACzB,cAAe,MAA4B,IAAI,KAC/C,cAAe,MAA4B,EAAE;;AAG/C,IAAa,8BAA8B,UACzC,OAAO,OAAO,CAAC,QAAQ,CAAC,KACxB,cAAe,MAA2B,IAAI,KAC9C,cAAe,MAA2B,GAAG,KAC7C,cAAe,MAA2B,OAAO;;;;;AAMnD,IAAa,yBAAyB,UAAyC;CAC7E,IAAI,CAAC,sBAAA,SAAS,KAAK,GAAG,OAAO;CAC7B,MAAM,SAAS;CACf,IAAI,OAAO,OAAO,OAAO,YAAY,OAAO,GAAG,WAAW,GAAG,OAAO;CACpE,MAAM,WAAW,OAAO;CACxB,MAAM,UAAU,OAAO;CACvB,IAAI,aAAa,KAAA;MACX,CAAC,MAAM,QAAQ,QAAQ,KAAK,CAAC,SAAS,MAAM,2BAA2B,GAAG,OAAO;CAAA;CAEvF,IAAI,YAAY,KAAA;MACV,CAAC,MAAM,QAAQ,OAAO,KAAK,CAAC,QAAQ,MAAM,0BAA0B,GAAG,OAAO;CAAA;CAIpF,QAFqB,MAAM,QAAQ,QAAQ,IAAI,SAAS,SAAS,MAC7C,MAAM,QAAQ,OAAO,IAAI,QAAQ,SAAS,KAC1B;AACtC"}
|