@giveitsmaller/sdk 0.7.0 → 0.9.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/dist/_audit.js +16 -0
- package/dist/builder.d.ts +1 -8
- package/dist/builder.js +7 -5
- package/dist/client.js +14 -4
- package/dist/errors.d.ts +65 -0
- package/dist/errors.js +77 -0
- package/dist/file-first.d.ts +503 -0
- package/dist/file-first.js +840 -0
- package/dist/generated/sdk_spec/errors.d.ts +1 -1
- package/dist/generated/sdk_spec/errors.js +50 -0
- package/dist/generated/sdk_spec/version.d.ts +1 -1
- package/dist/generated/sdk_spec/version.js +1 -1
- package/dist/gisl.d.ts +31 -0
- package/dist/gisl.js +47 -0
- package/dist/handle.d.ts +153 -0
- package/dist/handle.js +253 -0
- package/dist/http-downloader.d.ts +9 -0
- package/dist/http-downloader.js +55 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.js +30 -1
- package/dist/merge.d.ts +2 -1
- package/dist/merge.js +43 -11
- package/package.json +3 -3
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-first result surface — the value the file-first layer's `run()` /
|
|
3
|
+
* `Handle.wait()` / `Handle.result()` return (producers land in FF2b/FF5).
|
|
4
|
+
*
|
|
5
|
+
* Coexists with the operation-first `Result`/`Artifact` (in `builder.ts`)
|
|
6
|
+
* until the operation-first layer is removed (FF6). The file-first shape is
|
|
7
|
+
* flatter and adds an always-present per-input partition (`succeeded` /
|
|
8
|
+
* `failed`) so one bad input in a multi-input run doesn't sink the rest.
|
|
9
|
+
*
|
|
10
|
+
* Mirrors `packages/php/src/FileFirst/*`.
|
|
11
|
+
*/
|
|
12
|
+
import { type ProgressEvent } from './builder.js';
|
|
13
|
+
import type { GislClient } from './client.js';
|
|
14
|
+
import type { OperationDownload, WorkflowStatusResponse } from '@giveitsmaller/contracts/openapi';
|
|
15
|
+
import { OptimizeFor } from './generated/sdk_spec/enums.js';
|
|
16
|
+
import type { PresetDefaults } from './ergonomic/presets/index.js';
|
|
17
|
+
import type { WorkflowCreatePayload } from './types.js';
|
|
18
|
+
import { Handle } from './handle.js';
|
|
19
|
+
/**
|
|
20
|
+
* Streams a single output URL to a local path. The seam between the
|
|
21
|
+
* file-first {@link RunResult} sinks and the SDK's HTTP/auth layer.
|
|
22
|
+
*
|
|
23
|
+
* FF1 defines ONLY this type — the concrete implementation (fetch +
|
|
24
|
+
* filesystem streamer) is wired by the producer tickets (`run()`/`submit()`,
|
|
25
|
+
* FF2b/FF5), which construct a `RunResult` with a real downloader bound to
|
|
26
|
+
* the client's auth context. Unit tests inject a small stub. A `RunResult`
|
|
27
|
+
* built WITHOUT a downloader (e.g. in a browser, or any no-I/O context)
|
|
28
|
+
* throws {@link GislSinkError} from its sinks rather than reaching for a
|
|
29
|
+
* global client.
|
|
30
|
+
*
|
|
31
|
+
* Mirrors the PHP `Downloader` interface.
|
|
32
|
+
*
|
|
33
|
+
* STREAMING CONTRACT: implementations MUST stream the URL body to
|
|
34
|
+
* `destPath` — they MUST NOT buffer the whole output in memory. The
|
|
35
|
+
* `Promise<void>` return exists precisely so no buffered-bytes value can
|
|
36
|
+
* leak into the calling convention.
|
|
37
|
+
*/
|
|
38
|
+
export interface Downloader {
|
|
39
|
+
/**
|
|
40
|
+
* Stream the body at `url` to the local filesystem path `destPath`.
|
|
41
|
+
* Implementations create/overwrite `destPath`. Failures reject.
|
|
42
|
+
*/
|
|
43
|
+
downloadTo(url: string, destPath: string): Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A single deliverable output of a file-first run — the file-first layer's
|
|
47
|
+
* flat output type. Leaner than the operation-first `Artifact`: just the
|
|
48
|
+
* four fields a caller needs to identify + fetch an output.
|
|
49
|
+
*
|
|
50
|
+
* Mirrors the PHP `OutputFile`.
|
|
51
|
+
*/
|
|
52
|
+
export interface OutputFile {
|
|
53
|
+
readonly url: string;
|
|
54
|
+
readonly filename: string;
|
|
55
|
+
readonly sizeBytes: number;
|
|
56
|
+
readonly operation: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* One succeeded entry in {@link RunResult.succeeded}: a single input's
|
|
60
|
+
* outputs, addressable by the `key:` the caller gave that file (null when
|
|
61
|
+
* no key was supplied). Mirrors the PHP `ItemResult`.
|
|
62
|
+
*/
|
|
63
|
+
export interface ItemResult {
|
|
64
|
+
readonly key: string | null;
|
|
65
|
+
readonly outputs: readonly OutputFile[];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* One failed entry in {@link RunResult.failed}: an input that did not
|
|
69
|
+
* produce a deliverable, paired with the cause. One bad input does not sink
|
|
70
|
+
* the rest of a multi-input run. `error` is `unknown` (mirroring the PHP
|
|
71
|
+
* `\Throwable`) so the caller narrows with `instanceof`. Mirrors the PHP
|
|
72
|
+
* `ItemFailure`.
|
|
73
|
+
*/
|
|
74
|
+
export interface ItemFailure {
|
|
75
|
+
readonly key: string | null;
|
|
76
|
+
readonly error: unknown;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Return value of {@link RunResult.downloadTo} — the local paths written,
|
|
80
|
+
* in the SAME order as {@link RunResult.artifacts}. Mirrors the PHP
|
|
81
|
+
* `Manifest`.
|
|
82
|
+
*/
|
|
83
|
+
export interface Manifest {
|
|
84
|
+
readonly paths: readonly string[];
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Result of a file-first run. Coexists with the operation-first `Result`
|
|
88
|
+
* (in `builder.ts`) until FF6.
|
|
89
|
+
*
|
|
90
|
+
* Mirrors the PHP `RunResult` class. A class (not a bare interface) because
|
|
91
|
+
* it carries the `byKey()`/`toFile()`/`downloadTo()` behaviour; the data
|
|
92
|
+
* fields stay public + readonly so `toArray()` round-trips.
|
|
93
|
+
*
|
|
94
|
+
* Field notes:
|
|
95
|
+
* - `url`: single-output sugar — the lone artifact's URL when exactly one
|
|
96
|
+
* output exists, else undefined.
|
|
97
|
+
* - `ok`: true iff `failed` is empty. (A boolean — the partition lists are
|
|
98
|
+
* `succeeded`/`failed`; resolves the design doc's `ok` bool-vs-list
|
|
99
|
+
* contradiction.)
|
|
100
|
+
* - `state`: lifecycle state (`completed` | `failed` | ...). Named `state`,
|
|
101
|
+
* NOT `status`, matching the file-first `StatusSnapshot.state`.
|
|
102
|
+
* - sinks fetch via the injected {@link Downloader}; a result with no
|
|
103
|
+
* downloader throws {@link GislSinkError} (reason `downloader_unavailable`).
|
|
104
|
+
*/
|
|
105
|
+
export declare class RunResult {
|
|
106
|
+
readonly workflowId: string;
|
|
107
|
+
readonly state: string;
|
|
108
|
+
readonly artifacts: readonly OutputFile[];
|
|
109
|
+
readonly succeeded: readonly ItemResult[];
|
|
110
|
+
readonly failed: readonly ItemFailure[];
|
|
111
|
+
private readonly downloader?;
|
|
112
|
+
/** Single-output sugar: the lone artifact's URL, or undefined for 0 / >1. */
|
|
113
|
+
readonly url?: string;
|
|
114
|
+
/** True iff {@link failed} is empty. */
|
|
115
|
+
readonly ok: boolean;
|
|
116
|
+
constructor(workflowId: string, state: string, artifacts: readonly OutputFile[], succeeded: readonly ItemResult[], failed: readonly ItemFailure[], downloader?: Downloader | undefined);
|
|
117
|
+
/**
|
|
118
|
+
* Address a succeeded input by the `key:` given to `file()`. Duplicate keys
|
|
119
|
+
* are not valid input — the producer enforces key uniqueness (a later
|
|
120
|
+
* ticket); the first match is returned.
|
|
121
|
+
* @throws {GislNoSuchKeyError} when no succeeded entry has that key (a
|
|
122
|
+
* keyless run always throws — it is positionally addressable only).
|
|
123
|
+
*/
|
|
124
|
+
byKey(key: string): ItemResult;
|
|
125
|
+
/**
|
|
126
|
+
* Write the single output to `path`. Requires EXACTLY ONE artifact.
|
|
127
|
+
* @throws {GislSinkError} reason `not_single_output` for 0/>1 outputs;
|
|
128
|
+
* reason `downloader_unavailable` when no downloader is bound.
|
|
129
|
+
*/
|
|
130
|
+
toFile(path: string): Promise<void>;
|
|
131
|
+
/**
|
|
132
|
+
* Download every output into `dir` (filename per output), in output order.
|
|
133
|
+
* Returns the {@link Manifest} of local paths written.
|
|
134
|
+
* @throws {GislSinkError} reason `partial_failure` when `failOnPartial` and
|
|
135
|
+
* the run had failed inputs; reason `downloader_unavailable` when no
|
|
136
|
+
* downloader is bound.
|
|
137
|
+
*/
|
|
138
|
+
downloadTo(dir: string, options?: {
|
|
139
|
+
failOnPartial?: boolean;
|
|
140
|
+
}): Promise<Manifest>;
|
|
141
|
+
/**
|
|
142
|
+
* Plain-object projection. Field ORDER (workflowId, state, ok, url?,
|
|
143
|
+
* artifacts, succeeded, failed) is fixed to match the PHP `toArray()`
|
|
144
|
+
* reference so JSON-string parity holds (FF1 shape assertion + FF2b harness
|
|
145
|
+
* fixture). `url` is omitted entirely when undefined — `JSON.stringify`
|
|
146
|
+
* then produces the identical shape to PHP's omit-when-null `toArray()`.
|
|
147
|
+
*/
|
|
148
|
+
toJSON(): {
|
|
149
|
+
workflowId: string;
|
|
150
|
+
state: string;
|
|
151
|
+
ok: boolean;
|
|
152
|
+
url?: string;
|
|
153
|
+
artifacts: readonly OutputFile[];
|
|
154
|
+
succeeded: readonly {
|
|
155
|
+
key: string | null;
|
|
156
|
+
outputs: readonly OutputFile[];
|
|
157
|
+
}[];
|
|
158
|
+
failed: readonly {
|
|
159
|
+
key: string | null;
|
|
160
|
+
error: string;
|
|
161
|
+
}[];
|
|
162
|
+
};
|
|
163
|
+
private requireDownloader;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Flatten the terminal workflow status + its downloads into a {@link RunResult}.
|
|
167
|
+
*
|
|
168
|
+
* Shared by {@link Recipe.run} (passes its recipe key) and the file-first
|
|
169
|
+
* {@link Handle} reattach surface (`Handle.wait()`/`Handle.result()`, FF5a —
|
|
170
|
+
* passes `null` because a reattached handle carries no recipe key).
|
|
171
|
+
*
|
|
172
|
+
* **Partition invariant (carries a prior codex-review fix — do NOT let it
|
|
173
|
+
* drift):** success is ONLY `state === 'completed'`. Every other terminal
|
|
174
|
+
* state — `failed`, `partially_failed`, `cancelled`, `expired`,
|
|
175
|
+
* `paused_insufficient_credits` — partitions into `failed[]` so a caller's
|
|
176
|
+
* `ok`/`succeeded` check can never treat a cancelled/expired/paused run as a
|
|
177
|
+
* clean result.
|
|
178
|
+
*
|
|
179
|
+
* @internal Exported for reuse by the file-first `Handle`; not part of the
|
|
180
|
+
* caller-facing fluent surface.
|
|
181
|
+
*/
|
|
182
|
+
export declare function projectDownloadsToRunResult(workflowId: string, finalStatus: WorkflowStatusResponse, jobDownloads: readonly {
|
|
183
|
+
files: readonly OperationDownload[];
|
|
184
|
+
}[], key: string | null, downloader?: Downloader): RunResult;
|
|
185
|
+
/**
|
|
186
|
+
* Flatten a terminal multi-job workflow (the `client.files([...])` fan-out)
|
|
187
|
+
* into a partitioned {@link RunResult}. One job per input file, keyed by the
|
|
188
|
+
* `file-{i}` job ref the {@link FilesRecipe} lowering assigns; the result's
|
|
189
|
+
* `succeeded` / `failed` partition is PER JOB, so one bad input does not sink
|
|
190
|
+
* the rest.
|
|
191
|
+
*
|
|
192
|
+
* Join model: `finalStatus.jobs[]` carries the per-job {@link JobStatus} +
|
|
193
|
+
* `operations[]` (for the error message); `jobDownloads[]` carries the per-job
|
|
194
|
+
* output files. Both are joined on the job `ref` ("file-{i}"); the partition
|
|
195
|
+
* key is the index `"{i}"` parsed out of that ref. The flat `artifacts[]` is
|
|
196
|
+
* every job's outputs in job order (the order `finalStatus.jobs[]` lists them).
|
|
197
|
+
*
|
|
198
|
+
* **Partition invariant (mirrors {@link projectDownloadsToRunResult} PER JOB —
|
|
199
|
+
* do NOT let it drift):** a job is a SUCCESS only when its
|
|
200
|
+
* {@link JobResponse.status} `=== 'completed'`. Any other per-job status —
|
|
201
|
+
* `failed`, `pending`, `waiting`, `blocked_insufficient_credits`,
|
|
202
|
+
* `in_progress` — partitions that job into `failed[]` (with that job's first
|
|
203
|
+
* operation error message, scoped to THAT job only).
|
|
204
|
+
*
|
|
205
|
+
* @internal Exported for the file-first `client.files([...]).run()` producer;
|
|
206
|
+
* not part of the caller-facing fluent surface.
|
|
207
|
+
*/
|
|
208
|
+
export declare function projectMultiJobToRunResult(workflowId: string, finalStatus: WorkflowStatusResponse, jobDownloads: readonly {
|
|
209
|
+
ref: string;
|
|
210
|
+
files: readonly OperationDownload[];
|
|
211
|
+
}[], keyByRef: ReadonlyMap<string, string | null>, downloader?: Downloader): RunResult;
|
|
212
|
+
/**
|
|
213
|
+
* True when a terminal status describes a homogeneous `files([...])` fan-out —
|
|
214
|
+
* i.e. it has at least one job and EVERY job ref is `file-{i}` (the ids the
|
|
215
|
+
* {@link FilesRecipe} lowering assigns). A single-file {@link Recipe} omits the
|
|
216
|
+
* job id, so its job carries a non-`file-N` ref (e.g. `op`) and this is false.
|
|
217
|
+
*
|
|
218
|
+
* This is the data-driven seam that lets {@link Handle.wait}/{@link Handle.result}
|
|
219
|
+
* pick the per-job producer ({@link projectMultiJobToRunResult}) over the
|
|
220
|
+
* single-output one for a fan-out — WITHOUT a construction-time marker, so a
|
|
221
|
+
* fan-out **reattached** via `client.workflow(id)` (which carries no marker)
|
|
222
|
+
* still partitions per job. Keys are recovered from the `file-{i}` refs.
|
|
223
|
+
*
|
|
224
|
+
* @internal Exported for the file-first `Handle`; not part of the public API.
|
|
225
|
+
*/
|
|
226
|
+
export declare function isFanoutStatus(finalStatus: WorkflowStatusResponse): boolean;
|
|
227
|
+
/**
|
|
228
|
+
* The primary file a {@link Recipe} operates on — the "subject" of the
|
|
229
|
+
* file-first surface. A discriminated union over the ways a caller names an
|
|
230
|
+
* input:
|
|
231
|
+
*
|
|
232
|
+
* - `path` — a local filesystem path (Node; the common case).
|
|
233
|
+
* - `blob` — an in-memory `Blob`/`File` (browser, or Node 18+).
|
|
234
|
+
* - `uploadId` — a previously-uploaded `file_id` (reuse across recipes).
|
|
235
|
+
*
|
|
236
|
+
* FF2a does NO upload, so only the `path` + `uploadId` arms are exercised
|
|
237
|
+
* end-to-end here; the `blob` arm is DEFINED and type-checked but its upload
|
|
238
|
+
* is wired by FF2b (`run()`). Mirrors the PHP `FileInput` value object.
|
|
239
|
+
*/
|
|
240
|
+
export type FileInput = {
|
|
241
|
+
readonly kind: 'path';
|
|
242
|
+
readonly path: string;
|
|
243
|
+
} | {
|
|
244
|
+
readonly kind: 'blob';
|
|
245
|
+
readonly blob: Blob;
|
|
246
|
+
} | {
|
|
247
|
+
readonly kind: 'uploadId';
|
|
248
|
+
readonly fileId: string;
|
|
249
|
+
};
|
|
250
|
+
/** Named constructors for {@link FileInput} — mirror the PHP static factories. */
|
|
251
|
+
export declare const fileInput: {
|
|
252
|
+
readonly path: (path: string) => FileInput;
|
|
253
|
+
readonly blob: (blob: Blob) => FileInput;
|
|
254
|
+
readonly uploadId: (fileId: string) => FileInput;
|
|
255
|
+
};
|
|
256
|
+
/** One step in a {@link Recipe}'s chain — an op kind + captured ergonomic args. */
|
|
257
|
+
interface RecipeStep {
|
|
258
|
+
readonly opType: 'compress' | 'convert' | 'thumbnail' | 'text_watermark';
|
|
259
|
+
readonly options: Readonly<Record<string, unknown>>;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* The file-first builder value. `client.file(path)` returns a `Recipe`;
|
|
263
|
+
* single-input operations called on it (`compress`, `convert`, `thumbnail`,
|
|
264
|
+
* `textWatermark`) chain SEQUENTIALLY — each op feeds the next, and the chain
|
|
265
|
+
* lowers to ONE workflow job with an ordered `operations[]` (per ADR-0004:
|
|
266
|
+
* operations execute sequentially, each consuming the previous output). A
|
|
267
|
+
* chain yields the TERMINAL output only; intermediates are consumed (surfaced
|
|
268
|
+
* by FF2b's `run()`/{@link RunResult}).
|
|
269
|
+
*
|
|
270
|
+
* **Immutable / clone-on-write.** Every op returns a NEW `Recipe` carrying the
|
|
271
|
+
* appended step — `this` is never mutated. A Recipe is therefore a reusable
|
|
272
|
+
* value: branching the same base recipe two different ways cannot let one
|
|
273
|
+
* branch observe the other's steps (the aliasing trap mutable builders fall
|
|
274
|
+
* into).
|
|
275
|
+
*
|
|
276
|
+
* FF2a is network-free: there is NO `run()` here (that is FF2b). The lowering
|
|
277
|
+
* seam {@link toWorkflowPayload} takes the resolved upload id as a parameter
|
|
278
|
+
* so it stays pure — FF2b's `run()` calls the SAME method after uploading, and
|
|
279
|
+
* the parity harness calls it with a fixed id to assert the lowered shape.
|
|
280
|
+
*
|
|
281
|
+
* Mirrors the PHP `Recipe`.
|
|
282
|
+
*/
|
|
283
|
+
export declare class Recipe {
|
|
284
|
+
private readonly input;
|
|
285
|
+
private readonly recipeKey;
|
|
286
|
+
private readonly steps;
|
|
287
|
+
private readonly presetDefaults?;
|
|
288
|
+
private readonly scopedPresetDefaults?;
|
|
289
|
+
private readonly client?;
|
|
290
|
+
constructor(input: FileInput, recipeKey?: string | undefined, steps?: readonly RecipeStep[], presetDefaults?: PresetDefaults | undefined, scopedPresetDefaults?: PresetDefaults | undefined, client?: GislClient | undefined);
|
|
291
|
+
/**
|
|
292
|
+
* Reduce file size. `optimize` selects a per-media preset (resolved to
|
|
293
|
+
* concrete wire fields at lower-time, exactly as `client.compress()` does).
|
|
294
|
+
*/
|
|
295
|
+
compress(optimize?: OptimizeFor): Recipe;
|
|
296
|
+
/** Change format. `format` is lowered verbatim to the `format` wire option. */
|
|
297
|
+
convert(format: string): Recipe;
|
|
298
|
+
/**
|
|
299
|
+
* Generate a preview. Width and/or height in pixels; an omitted dimension is
|
|
300
|
+
* dropped from the wire options (not sent as `undefined`).
|
|
301
|
+
*/
|
|
302
|
+
thumbnail(options?: {
|
|
303
|
+
width?: number;
|
|
304
|
+
height?: number;
|
|
305
|
+
}): Recipe;
|
|
306
|
+
/**
|
|
307
|
+
* Apply a text watermark. Single-input (the text is an option, not a
|
|
308
|
+
* secondary file) — lowers to the `text_watermark` op with a `text` option.
|
|
309
|
+
*/
|
|
310
|
+
textWatermark(text: string): Recipe;
|
|
311
|
+
/**
|
|
312
|
+
* Lower this recipe to a workflow-create payload against a resolved upload
|
|
313
|
+
* id. Single-input chain → ONE job, `source: upload(fileId)`, ordered
|
|
314
|
+
* `operations[]`; the job `id` is omitted (a single job referenced by
|
|
315
|
+
* nothing — the server auto-assigns `job_N`).
|
|
316
|
+
*
|
|
317
|
+
* When `callbackUrl` is given (the file-first `submit()` path), it is built
|
|
318
|
+
* INTO the payload at construction (`callback_url`) rather than spread onto an
|
|
319
|
+
* already-built readonly payload. `run()` passes no `callbackUrl`.
|
|
320
|
+
*
|
|
321
|
+
* @internal Consumed by FF2b's `run()` (after a real upload), FF5b's
|
|
322
|
+
* `submit()` (with a webhook), and the cross-language parity harness (with a
|
|
323
|
+
* fixed id). Not part of the caller-facing fluent surface.
|
|
324
|
+
*/
|
|
325
|
+
toWorkflowPayload(fileId: string, callbackUrl?: string): WorkflowCreatePayload;
|
|
326
|
+
/** The result-addressing key passed to `file()`, or undefined. */
|
|
327
|
+
key(): string | undefined;
|
|
328
|
+
/** The number of operations chained so far (introspection / tests). */
|
|
329
|
+
get stepCount(): number;
|
|
330
|
+
/**
|
|
331
|
+
* The captured op chain. Read by {@link FilesRecipe} to compose a shared
|
|
332
|
+
* chain across many inputs without duplicating the chain-method validation.
|
|
333
|
+
* @internal
|
|
334
|
+
*/
|
|
335
|
+
get recipeSteps(): readonly RecipeStep[];
|
|
336
|
+
/**
|
|
337
|
+
* Execute the recipe end-to-end: upload the input (when required), create
|
|
338
|
+
* the workflow, await a terminal state (SSE with poll fallback), then
|
|
339
|
+
* resolve the produced downloads into a flat {@link RunResult}. Throws
|
|
340
|
+
* {@link GislTimeoutError} if `maxWait` elapses before terminal status.
|
|
341
|
+
*
|
|
342
|
+
* Mirrors the operation-first `OperationBuilder.run` (in `builder.ts`).
|
|
343
|
+
* Requires a client bound at construction time — `gisl().file(...)` wires
|
|
344
|
+
* it; a directly-constructed `Recipe` (e.g. in a lowering-only test) has no
|
|
345
|
+
* client and throws {@link GislConfigError}.
|
|
346
|
+
*/
|
|
347
|
+
run(options?: {
|
|
348
|
+
maxWait?: string | number;
|
|
349
|
+
onProgress?: (event: ProgressEvent) => void;
|
|
350
|
+
signal?: AbortSignal;
|
|
351
|
+
pollIntervalMs?: number;
|
|
352
|
+
}): Promise<RunResult>;
|
|
353
|
+
/**
|
|
354
|
+
* Fire-and-forget the recipe: upload the input (when required), create the
|
|
355
|
+
* workflow (wiring `webhook` into `callback_url` when given), and return a
|
|
356
|
+
* client-bound {@link Handle} carrying the workflow id + webhook secret + the
|
|
357
|
+
* recipe key. Does NOT wait for terminal status — call `handle.wait()` /
|
|
358
|
+
* `handle.result()` later to collect the {@link RunResult}.
|
|
359
|
+
*
|
|
360
|
+
* Requires a client bound at construction time (same `no_client` guard as
|
|
361
|
+
* {@link run}). `webhook` is OPTIONAL: when omitted, no `callback_url` is
|
|
362
|
+
* sent. Mirrors the PHP `Recipe.submit()`.
|
|
363
|
+
*
|
|
364
|
+
* @param webhook Absolute callback URL the server POSTs lifecycle events to.
|
|
365
|
+
*/
|
|
366
|
+
submit(webhook?: string): Promise<Handle>;
|
|
367
|
+
/**
|
|
368
|
+
* Resolve the upload id (verbatim for a pre-uploaded id; uploading a path /
|
|
369
|
+
* blob otherwise, emitting `{phase:'upload'}` progress), check the post-upload
|
|
370
|
+
* deadline, lower to the workflow-create payload (wiring `webhook` into
|
|
371
|
+
* `callback_url`), and create the workflow. Shared first half of
|
|
372
|
+
* {@link run} + {@link submit}.
|
|
373
|
+
*
|
|
374
|
+
* The post-upload deadline check carries a prior codex fix (9a117f04eb59): a
|
|
375
|
+
* slow upload must not proceed to createWorkflow past the deadline.
|
|
376
|
+
*/
|
|
377
|
+
private _uploadAndCreate;
|
|
378
|
+
private withStep;
|
|
379
|
+
private lowerStep;
|
|
380
|
+
private lowerCompressOptions;
|
|
381
|
+
private compressMediaHint;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* The homogeneous fan-out builder value (FF3a). `client.files([a, b, c])`
|
|
385
|
+
* returns a `FilesRecipe`; the op-chain methods (`compress`, `convert`,
|
|
386
|
+
* `thumbnail`, `textWatermark`) build ONE shared recipe (chain) that is applied
|
|
387
|
+
* to EVERY input file in ONE workflow. `run()` returns a partitioned
|
|
388
|
+
* {@link RunResult} — one `succeeded`/`failed` entry per input, keyed by its
|
|
389
|
+
* 0-based index ("0", "1", …) so one bad input does not sink the rest.
|
|
390
|
+
*
|
|
391
|
+
* **Immutable / clone-on-write**, exactly like {@link Recipe}: every op returns
|
|
392
|
+
* a NEW `FilesRecipe` carrying the appended step. The inputs are held as an
|
|
393
|
+
* ORDERED list (NOT a map) so the per-file index is the partition key.
|
|
394
|
+
*
|
|
395
|
+
* **Lowering composes {@link Recipe} per file** rather than duplicating
|
|
396
|
+
* `lowerStep`/`lowerCompressOptions`: for each input `i` it builds an internal
|
|
397
|
+
* single-file `Recipe(input_i, …, steps)`, calls its `toWorkflowPayload` to get
|
|
398
|
+
* that file's one-job payload, then merges all jobs into ONE
|
|
399
|
+
* {@link WorkflowCreatePayload} with `jobs[i].id = "file-{i}"`. This preserves
|
|
400
|
+
* each file's media-hint (different extensions per input resolve compress
|
|
401
|
+
* presets independently).
|
|
402
|
+
*
|
|
403
|
+
* Exposes both `run()` (blocking, returns a partitioned {@link RunResult}) and
|
|
404
|
+
* `submit(webhook?)` (fire-and-forget, returns a {@link Handle}). Mirrors the
|
|
405
|
+
* PHP `FilesRecipe`.
|
|
406
|
+
*/
|
|
407
|
+
export declare class FilesRecipe {
|
|
408
|
+
private readonly inputs;
|
|
409
|
+
private readonly steps;
|
|
410
|
+
private readonly presetDefaults?;
|
|
411
|
+
private readonly scopedPresetDefaults?;
|
|
412
|
+
private readonly client?;
|
|
413
|
+
constructor(inputs: readonly FileInput[], steps?: readonly RecipeStep[], presetDefaults?: PresetDefaults | undefined, scopedPresetDefaults?: PresetDefaults | undefined, client?: GislClient | undefined);
|
|
414
|
+
/**
|
|
415
|
+
* Reduce file size on every input. `optimize` selects a per-media preset
|
|
416
|
+
* (resolved per file at lower-time, so each input's extension picks its own
|
|
417
|
+
* preset). Reuses {@link Recipe}'s validation — a directly-constructed
|
|
418
|
+
* lowering builds an internal Recipe that throws the same `GislConfigError`.
|
|
419
|
+
*/
|
|
420
|
+
compress(optimize?: OptimizeFor): FilesRecipe;
|
|
421
|
+
/** Change every input's format. `format` lowers verbatim to the `format` option. */
|
|
422
|
+
convert(format: string): FilesRecipe;
|
|
423
|
+
/** Generate a preview of every input. Omitted dimensions are dropped from the wire options. */
|
|
424
|
+
thumbnail(options?: {
|
|
425
|
+
width?: number;
|
|
426
|
+
height?: number;
|
|
427
|
+
}): FilesRecipe;
|
|
428
|
+
/** Apply the same text watermark to every input. */
|
|
429
|
+
textWatermark(text: string): FilesRecipe;
|
|
430
|
+
/** The number of inputs in this fan-out (introspection / tests). */
|
|
431
|
+
get inputCount(): number;
|
|
432
|
+
/** The number of operations chained so far (introspection / tests). */
|
|
433
|
+
get stepCount(): number;
|
|
434
|
+
/**
|
|
435
|
+
* Lower this fan-out to a single multi-job workflow-create payload against a
|
|
436
|
+
* list of resolved upload ids (one per input, in input order). Each input `i`
|
|
437
|
+
* becomes ONE job with `id = "file-{i}"`, its `source: upload(fileIds[i])`,
|
|
438
|
+
* and the SHARED lowered `operations[]`. Composes the single-file
|
|
439
|
+
* {@link Recipe.toWorkflowPayload} per file so per-file media-hints resolve
|
|
440
|
+
* independently and lowering logic is not duplicated.
|
|
441
|
+
*
|
|
442
|
+
* @internal Consumed by {@link run} (after uploading all inputs) and the
|
|
443
|
+
* cross-language parity harness (with fixed ids). Not caller-facing.
|
|
444
|
+
*/
|
|
445
|
+
toWorkflowPayload(fileIds: readonly string[], callbackUrl?: string): WorkflowCreatePayload;
|
|
446
|
+
/**
|
|
447
|
+
* Execute the fan-out end-to-end: upload EVERY input, create ONE workflow
|
|
448
|
+
* with one job per input, await a terminal state (SSE with poll fallback),
|
|
449
|
+
* then resolve the per-job downloads into a partitioned {@link RunResult}.
|
|
450
|
+
* `partially_failed` is a NORMAL terminal state here — its successful jobs
|
|
451
|
+
* land in `succeeded`, its failed jobs in `failed`.
|
|
452
|
+
*
|
|
453
|
+
* Requires a client bound at construction time — `gisl().files(...)` wires
|
|
454
|
+
* it; a directly-constructed `FilesRecipe` throws {@link GislConfigError}.
|
|
455
|
+
* Mirrors the single-file {@link Recipe.run}; see {@link submit} for the
|
|
456
|
+
* fire-and-forget arm.
|
|
457
|
+
*/
|
|
458
|
+
run(options?: {
|
|
459
|
+
maxWait?: string | number;
|
|
460
|
+
onProgress?: (event: ProgressEvent) => void;
|
|
461
|
+
signal?: AbortSignal;
|
|
462
|
+
pollIntervalMs?: number;
|
|
463
|
+
}): Promise<RunResult>;
|
|
464
|
+
/**
|
|
465
|
+
* Fire-and-forget the fan-out: upload every input, create ONE multi-job
|
|
466
|
+
* workflow (wiring `webhook` into `callback_url` when given), and return a
|
|
467
|
+
* client-bound {@link Handle}. Does NOT wait for terminal status — call
|
|
468
|
+
* `handle.wait()` / `handle.result()` later to collect the partitioned
|
|
469
|
+
* {@link RunResult}. The Handle detects the fan-out from the wire `file-{i}`
|
|
470
|
+
* job refs, so per-file `byKey()` works even after a `client.workflow(id)`
|
|
471
|
+
* reattach (the keys are the input indices `"0"`, `"1"`, …).
|
|
472
|
+
*
|
|
473
|
+
* Requires a client bound at construction time (same `no_client` guard as
|
|
474
|
+
* {@link run}). `webhook` is OPTIONAL. Fire-and-forget, so NO whole-run
|
|
475
|
+
* deadline (a multi-GB upload is bounded by the HTTP client's own timeout).
|
|
476
|
+
* Mirrors the single-file {@link Recipe.submit}.
|
|
477
|
+
*
|
|
478
|
+
* @param webhook Absolute callback URL the server POSTs lifecycle events to.
|
|
479
|
+
*/
|
|
480
|
+
submit(webhook?: string): Promise<Handle>;
|
|
481
|
+
/**
|
|
482
|
+
* Upload every input (verbatim for a pre-uploaded id; uploading a path /
|
|
483
|
+
* blob otherwise, emitting `{phase:'upload'}` progress) then create ONE
|
|
484
|
+
* multi-job workflow (one job per input, `callback_url` built in when
|
|
485
|
+
* `webhook` is given). Shared first half of {@link run} + {@link submit}.
|
|
486
|
+
*
|
|
487
|
+
* Uploads are sequential so progress events stay ordered and the abort
|
|
488
|
+
* signal is honoured promptly; a resource arm is impossible in TS (Blob).
|
|
489
|
+
* `run()` passes a whole-run deadline (a slow upload must not proceed to
|
|
490
|
+
* createWorkflow past maxWait); `submit()` passes `undefined`, so the
|
|
491
|
+
* deadline checks are skipped.
|
|
492
|
+
*/
|
|
493
|
+
private _uploadAllAndCreate;
|
|
494
|
+
/**
|
|
495
|
+
* The shared single-file {@link Recipe} that captures the op chain (input is
|
|
496
|
+
* a placeholder — only the steps are read). Reuses Recipe's op-chain
|
|
497
|
+
* validation + coercion so a `FilesRecipe.compress(bad)` throws the identical
|
|
498
|
+
* `GislConfigError` as `Recipe.compress(bad)`.
|
|
499
|
+
*/
|
|
500
|
+
private baseRecipe;
|
|
501
|
+
private withStep;
|
|
502
|
+
}
|
|
503
|
+
export {};
|