@giveitsmaller/sdk 0.6.0 → 0.8.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.
Files changed (52) hide show
  1. package/dist/_audit.js +71 -0
  2. package/dist/builder.d.ts +406 -0
  3. package/dist/builder.js +706 -0
  4. package/dist/client.d.ts +10 -0
  5. package/dist/client.js +42 -6
  6. package/dist/credentials.d.ts +61 -0
  7. package/dist/credentials.js +200 -0
  8. package/dist/ergonomic/preset_resolver.d.ts +75 -0
  9. package/dist/ergonomic/preset_resolver.js +568 -0
  10. package/dist/ergonomic/presets/_translate.d.ts +11 -0
  11. package/dist/ergonomic/presets/_translate.js +35 -0
  12. package/dist/ergonomic/presets/audio_compress.d.ts +16 -0
  13. package/dist/ergonomic/presets/audio_compress.js +45 -0
  14. package/dist/ergonomic/presets/document_epub_compress.d.ts +14 -0
  15. package/dist/ergonomic/presets/document_epub_compress.js +34 -0
  16. package/dist/ergonomic/presets/document_odf_compress.d.ts +14 -0
  17. package/dist/ergonomic/presets/document_odf_compress.js +34 -0
  18. package/dist/ergonomic/presets/document_office_compress.d.ts +16 -0
  19. package/dist/ergonomic/presets/document_office_compress.js +40 -0
  20. package/dist/ergonomic/presets/document_pdf_compress.d.ts +14 -0
  21. package/dist/ergonomic/presets/document_pdf_compress.js +35 -0
  22. package/dist/ergonomic/presets/image_compress.d.ts +43 -0
  23. package/dist/ergonomic/presets/image_compress.js +95 -0
  24. package/dist/ergonomic/presets/index.d.ts +77 -0
  25. package/dist/ergonomic/presets/index.js +216 -0
  26. package/dist/ergonomic/presets/video_compress.d.ts +30 -0
  27. package/dist/ergonomic/presets/video_compress.js +83 -0
  28. package/dist/errors.d.ts +196 -1
  29. package/dist/errors.js +216 -0
  30. package/dist/file-first.d.ts +284 -0
  31. package/dist/file-first.js +445 -0
  32. package/dist/generated/sdk_spec/enums.d.ts +195 -0
  33. package/dist/generated/sdk_spec/enums.js +127 -0
  34. package/dist/generated/sdk_spec/errors.d.ts +16 -0
  35. package/dist/generated/sdk_spec/errors.js +523 -0
  36. package/dist/generated/sdk_spec/index.d.ts +4 -0
  37. package/dist/generated/sdk_spec/index.js +7 -0
  38. package/dist/generated/sdk_spec/presets.d.ts +6 -0
  39. package/dist/generated/sdk_spec/presets.js +157 -0
  40. package/dist/generated/sdk_spec/version.d.ts +3 -0
  41. package/dist/generated/sdk_spec/version.js +6 -0
  42. package/dist/gisl.d.ts +122 -0
  43. package/dist/gisl.js +283 -0
  44. package/dist/http-downloader.d.ts +9 -0
  45. package/dist/http-downloader.js +55 -0
  46. package/dist/index.d.ts +20 -5
  47. package/dist/index.js +45 -4
  48. package/dist/merge.d.ts +142 -0
  49. package/dist/merge.js +411 -0
  50. package/dist/types.d.ts +12 -14
  51. package/dist/types.js +18 -0
  52. package/package.json +3 -3
package/dist/errors.js CHANGED
@@ -67,6 +67,39 @@ export class GislFeatureNotAvailableError extends GislApiError {
67
67
  this.name = 'GislFeatureNotAvailableError';
68
68
  }
69
69
  }
70
+ /**
71
+ * 422 response on `POST /api/workflows` when a job references an upload
72
+ * whose server-side probe hasn't completed at workflow-create time. The
73
+ * server rejects rather than silently routing as `short_form` (which
74
+ * hard-fails long video clips).
75
+ *
76
+ * **Recovery contract** (per contracts ProbePendingResponse docblock):
77
+ * poll `POST /api/uploads/{id}/probe` for the pending upload until
78
+ * `probe_status` is terminal (`ok` → re-`POST /api/workflows` the same
79
+ * request; `corrupt` / `unsupported_codec` → surface the probe error).
80
+ * The `Retry-After` response header (when present) suggests a delay
81
+ * in seconds before the next poll/retry.
82
+ *
83
+ * `payload.jobRef` identifies which job in the multi-job request triggered
84
+ * the probe-pending rejection.
85
+ *
86
+ * @example
87
+ * try {
88
+ * await client.createWorkflow({ jobs });
89
+ * } catch (e) {
90
+ * if (e instanceof GislProbePendingError) {
91
+ * await waitForProbe(e.payload.jobRef);
92
+ * // retry...
93
+ * }
94
+ * throw e;
95
+ * }
96
+ */
97
+ export class GislProbePendingError extends GislApiError {
98
+ constructor(statusCode, errorMessage, payload, path, extra) {
99
+ super(statusCode, errorMessage, path, undefined, buildOptionsWithPayload(payload, extra));
100
+ this.name = 'GislProbePendingError';
101
+ }
102
+ }
70
103
  export class GislWorkflowExpiredError extends GislApiError {
71
104
  constructor(statusCode, errorMessage, payload, path, extra) {
72
105
  super(statusCode, errorMessage, path, undefined, buildOptionsWithPayload(payload, extra));
@@ -153,12 +186,155 @@ export class GislMultipartSessionAuthRequiredError extends GislApiError {
153
186
  this.name = 'GislMultipartSessionAuthRequiredError';
154
187
  }
155
188
  }
189
+ /**
190
+ * Root of the LOCAL config-error tree — thrown before any HTTP/file I/O.
191
+ * Sibling of `GislApiError` (which represents server-side error envelopes).
192
+ * Reserve for fail-early errors raised by the ergonomic-layer factory or
193
+ * credential-chain resolver when the caller hasn't supplied something the
194
+ * SDK needs to make a request. Never carries an HTTP status code.
195
+ *
196
+ * Optional `metadata` (T4b — `27rE1fZn`) carries structured fields used
197
+ * by the preset resolver and other ergonomic-layer validators. Existing
198
+ * call sites that pass `(message)` keep working — metadata is purely
199
+ * additive and defaults to `undefined`.
200
+ */
201
+ export class GislConfigError extends GislError {
202
+ reason;
203
+ conflictingFields;
204
+ resolvedSnapshot;
205
+ suggestion;
206
+ constructor(message, metadata) {
207
+ super(message);
208
+ this.name = 'GislConfigError';
209
+ if (metadata !== undefined) {
210
+ if (metadata.reason !== undefined)
211
+ this.reason = metadata.reason;
212
+ if (metadata.conflictingFields !== undefined) {
213
+ this.conflictingFields = metadata.conflictingFields;
214
+ }
215
+ if (metadata.resolvedSnapshot !== undefined) {
216
+ this.resolvedSnapshot = metadata.resolvedSnapshot;
217
+ }
218
+ if (metadata.suggestion !== undefined)
219
+ this.suggestion = metadata.suggestion;
220
+ }
221
+ }
222
+ }
223
+ /**
224
+ * The ergonomic-layer factory `gisl.create()` could not resolve an API key
225
+ * from any of explicit arg, `GISL_API_KEY` env, or shared-config profile,
226
+ * AND the caller did not opt into anonymous or cookie-mode. Thrown BEFORE
227
+ * any file read or HTTP request — calls to `client.compress(...)`, `.run()`,
228
+ * etc., synchronously fail with this error.
229
+ */
230
+ export class GislMissingCredentialsError extends GislConfigError {
231
+ constructor(message) {
232
+ super(message);
233
+ this.name = 'GislMissingCredentialsError';
234
+ }
235
+ }
236
+ /**
237
+ * The caller used `gisl.anonymous()` and then invoked an operation that is
238
+ * not in the anonymous-capable allowlist. Local-only — thrown before any I/O.
239
+ * Distinct from server-side `GislAuthError` (401/403 on the wire).
240
+ */
241
+ export class GislFeatureRequiresAuthError extends GislConfigError {
242
+ operation;
243
+ constructor(operation, message) {
244
+ super(message);
245
+ this.name = 'GislFeatureRequiresAuthError';
246
+ this.operation = operation;
247
+ }
248
+ }
249
+ /**
250
+ * `MergeBuilder.sequence(...)` referenced an asset that wasn't declared in
251
+ * the prior `client.merge(...)` call. Local validation runs BEFORE upload
252
+ * so the caller fails fast on the typo without burning bandwidth.
253
+ */
254
+ export class GislUndeclaredAssetError extends GislConfigError {
255
+ assetId;
256
+ declaredAssets;
257
+ constructor(assetId, declaredAssets) {
258
+ super(`Sequence references asset '${assetId}' but it wasn't declared in merge(...). ` +
259
+ `Declared assets: [${declaredAssets.join(', ')}]. ` +
260
+ `Either pass it to merge(...) before sequencing, or remove the reference.`);
261
+ this.name = 'GislUndeclaredAssetError';
262
+ this.assetId = assetId;
263
+ this.declaredAssets = declaredAssets;
264
+ }
265
+ }
266
+ /**
267
+ * `MergeBuilder.sequence(...)` was called but at least one declared asset
268
+ * wasn't referenced. Almost always a bug (wasted upload). Escape via
269
+ * `allowUnusedAssets: true` on the merge options.
270
+ */
271
+ export class GislUnusedAssetError extends GislConfigError {
272
+ unusedAssets;
273
+ constructor(unusedAssets) {
274
+ super(`Assets [${unusedAssets.join(', ')}] were declared in merge(...) but never sequenced. ` +
275
+ `Reference them in .sequence(...), remove them from the declaration, ` +
276
+ `or pass {allowUnusedAssets: true} to opt out of this check.`);
277
+ this.name = 'GislUnusedAssetError';
278
+ this.unusedAssets = unusedAssets;
279
+ }
280
+ }
281
+ /**
282
+ * `MergeBuilder.sequence(...)` on an image merge was given a `clip(ref, opts)`
283
+ * entry. Image merges have NO per-input options in the wire today — `transition`
284
+ * applies at the merge level and is uniform across all joins.
285
+ */
286
+ export class GislPerInputOptionsNotSupportedError extends GislConfigError {
287
+ mediaKind;
288
+ constructor(mediaKind) {
289
+ super(`${mediaKind} merge has no per-input options today; set 'transition' at the ` +
290
+ `.merge(...) level instead — it applies to every join.`);
291
+ this.name = 'GislPerInputOptionsNotSupportedError';
292
+ this.mediaKind = mediaKind;
293
+ }
294
+ }
295
+ /**
296
+ * Thrown by future chain methods (`.compress()` / `.thumbnail()` /
297
+ * `.convert()` on an `OperationBuilder`) when the previous step produces
298
+ * MULTIPLE artifacts and the caller didn't explicitly call `.mapEach(...)`
299
+ * to opt into per-artifact fan-out. T6 ships the error class + the
300
+ * `.mapEach(...)` method; the chain methods themselves are a separate
301
+ * follow-up card, so this error is currently dormant — but the type +
302
+ * audit-gate registration land here so the future chain-method PR is a
303
+ * pure addition with no public-API churn.
304
+ */
305
+ export class GislChainCardinalityMismatchError extends GislConfigError {
306
+ previousOperation;
307
+ attemptedOperation;
308
+ constructor(previousOperation, attemptedOperation) {
309
+ super(`Previous step (${previousOperation}) produces multiple artifacts; ` +
310
+ `use .mapEach(art => art.${attemptedOperation}(...)) to apply the chain per-artifact, ` +
311
+ `or branch to a single artifact first.`);
312
+ this.name = 'GislChainCardinalityMismatchError';
313
+ this.previousOperation = previousOperation;
314
+ this.attemptedOperation = attemptedOperation;
315
+ }
316
+ }
156
317
  export class GislTimeoutError extends GislError {
157
318
  constructor(message) {
158
319
  super(message);
159
320
  this.name = 'GislTimeoutError';
160
321
  }
161
322
  }
323
+ /**
324
+ * Transport-level failure: the underlying `fetch` (or other transport) could
325
+ * not produce a usable response — DNS, TCP, TLS, a mid-stream disconnect, or a
326
+ * non-ok status / empty body when fetching a result download. Mirrors the PHP
327
+ * `Gisl\Sdk\Errors\GislNetworkError`. Subclasses `GislError` (not
328
+ * `GislApiError`) because it carries no contract error envelope. The concrete
329
+ * file-first {@link Downloader} raises this when the output URL cannot be read
330
+ * (a destination-WRITE failure is `GislSinkError` reason `write_failed`).
331
+ */
332
+ export class GislNetworkError extends GislError {
333
+ constructor(message) {
334
+ super(message);
335
+ this.name = 'GislNetworkError';
336
+ }
337
+ }
162
338
  export class GislAbortError extends GislError {
163
339
  constructor(message) {
164
340
  super(message);
@@ -198,3 +374,43 @@ export class GislMultipartPartCountError extends GislError {
198
374
  this.maxParts = maxParts;
199
375
  }
200
376
  }
377
+ /**
378
+ * Thrown by the file-first `RunResult.byKey()` (FF1) when no result entry
379
+ * matches the requested key. A keyless run (no `key:` supplied to `file()`)
380
+ * is addressable positionally only — `byKey()` always throws.
381
+ *
382
+ * Mirrors the PHP `Gisl\Sdk\Errors\GislNoSuchKeyError`.
383
+ */
384
+ export class GislNoSuchKeyError extends GislError {
385
+ constructor(message) {
386
+ super(message);
387
+ this.name = 'GislNoSuchKeyError';
388
+ }
389
+ }
390
+ /**
391
+ * Thrown by the file-first `RunResult` sinks (`toFile()` / `downloadTo()`,
392
+ * FF1) when they cannot deliver. The machine-readable `reason` discriminates
393
+ * the three cases, mirroring the `reason`-bag convention on
394
+ * {@link GislConfigError}:
395
+ *
396
+ * - `not_single_output` — `toFile()` requires exactly one output but the
397
+ * run produced zero or more than one.
398
+ * - `downloader_unavailable` — the `RunResult` has no downloader bound (e.g. a
399
+ * browser / no-I/O context).
400
+ * - `partial_failure` — `downloadTo({ failOnPartial: true })` and the
401
+ * run had at least one failed input.
402
+ * - `duplicate_filename` — two outputs share a destination filename in one
403
+ * `downloadTo(dir)`, which would silently overwrite.
404
+ * - `write_failed` — a concrete {@link Downloader} could not open or
405
+ * stream to the destination path.
406
+ *
407
+ * Mirrors the PHP `Gisl\Sdk\Errors\GislSinkError`.
408
+ */
409
+ export class GislSinkError extends GislError {
410
+ reason;
411
+ constructor(message, options) {
412
+ super(message);
413
+ this.name = 'GislSinkError';
414
+ this.reason = options.reason;
415
+ }
416
+ }
@@ -0,0 +1,284 @@
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 { OptimizeFor } from './generated/sdk_spec/enums.js';
15
+ import type { PresetDefaults } from './ergonomic/presets/index.js';
16
+ import type { WorkflowCreatePayload } from './types.js';
17
+ /**
18
+ * Streams a single output URL to a local path. The seam between the
19
+ * file-first {@link RunResult} sinks and the SDK's HTTP/auth layer.
20
+ *
21
+ * FF1 defines ONLY this type — the concrete implementation (fetch +
22
+ * filesystem streamer) is wired by the producer tickets (`run()`/`submit()`,
23
+ * FF2b/FF5), which construct a `RunResult` with a real downloader bound to
24
+ * the client's auth context. Unit tests inject a small stub. A `RunResult`
25
+ * built WITHOUT a downloader (e.g. in a browser, or any no-I/O context)
26
+ * throws {@link GislSinkError} from its sinks rather than reaching for a
27
+ * global client.
28
+ *
29
+ * Mirrors the PHP `Downloader` interface.
30
+ *
31
+ * STREAMING CONTRACT: implementations MUST stream the URL body to
32
+ * `destPath` — they MUST NOT buffer the whole output in memory. The
33
+ * `Promise<void>` return exists precisely so no buffered-bytes value can
34
+ * leak into the calling convention.
35
+ */
36
+ export interface Downloader {
37
+ /**
38
+ * Stream the body at `url` to the local filesystem path `destPath`.
39
+ * Implementations create/overwrite `destPath`. Failures reject.
40
+ */
41
+ downloadTo(url: string, destPath: string): Promise<void>;
42
+ }
43
+ /**
44
+ * A single deliverable output of a file-first run — the file-first layer's
45
+ * flat output type. Leaner than the operation-first `Artifact`: just the
46
+ * four fields a caller needs to identify + fetch an output.
47
+ *
48
+ * Mirrors the PHP `OutputFile`.
49
+ */
50
+ export interface OutputFile {
51
+ readonly url: string;
52
+ readonly filename: string;
53
+ readonly sizeBytes: number;
54
+ readonly operation: string;
55
+ }
56
+ /**
57
+ * One succeeded entry in {@link RunResult.succeeded}: a single input's
58
+ * outputs, addressable by the `key:` the caller gave that file (null when
59
+ * no key was supplied). Mirrors the PHP `ItemResult`.
60
+ */
61
+ export interface ItemResult {
62
+ readonly key: string | null;
63
+ readonly outputs: readonly OutputFile[];
64
+ }
65
+ /**
66
+ * One failed entry in {@link RunResult.failed}: an input that did not
67
+ * produce a deliverable, paired with the cause. One bad input does not sink
68
+ * the rest of a multi-input run. `error` is `unknown` (mirroring the PHP
69
+ * `\Throwable`) so the caller narrows with `instanceof`. Mirrors the PHP
70
+ * `ItemFailure`.
71
+ */
72
+ export interface ItemFailure {
73
+ readonly key: string | null;
74
+ readonly error: unknown;
75
+ }
76
+ /**
77
+ * Return value of {@link RunResult.downloadTo} — the local paths written,
78
+ * in the SAME order as {@link RunResult.artifacts}. Mirrors the PHP
79
+ * `Manifest`.
80
+ */
81
+ export interface Manifest {
82
+ readonly paths: readonly string[];
83
+ }
84
+ /**
85
+ * Result of a file-first run. Coexists with the operation-first `Result`
86
+ * (in `builder.ts`) until FF6.
87
+ *
88
+ * Mirrors the PHP `RunResult` class. A class (not a bare interface) because
89
+ * it carries the `byKey()`/`toFile()`/`downloadTo()` behaviour; the data
90
+ * fields stay public + readonly so `toArray()` round-trips.
91
+ *
92
+ * Field notes:
93
+ * - `url`: single-output sugar — the lone artifact's URL when exactly one
94
+ * output exists, else undefined.
95
+ * - `ok`: true iff `failed` is empty. (A boolean — the partition lists are
96
+ * `succeeded`/`failed`; resolves the design doc's `ok` bool-vs-list
97
+ * contradiction.)
98
+ * - `state`: lifecycle state (`completed` | `failed` | ...). Named `state`,
99
+ * NOT `status`, matching the file-first `StatusSnapshot.state`.
100
+ * - sinks fetch via the injected {@link Downloader}; a result with no
101
+ * downloader throws {@link GislSinkError} (reason `downloader_unavailable`).
102
+ */
103
+ export declare class RunResult {
104
+ readonly workflowId: string;
105
+ readonly state: string;
106
+ readonly artifacts: readonly OutputFile[];
107
+ readonly succeeded: readonly ItemResult[];
108
+ readonly failed: readonly ItemFailure[];
109
+ private readonly downloader?;
110
+ /** Single-output sugar: the lone artifact's URL, or undefined for 0 / >1. */
111
+ readonly url?: string;
112
+ /** True iff {@link failed} is empty. */
113
+ readonly ok: boolean;
114
+ constructor(workflowId: string, state: string, artifacts: readonly OutputFile[], succeeded: readonly ItemResult[], failed: readonly ItemFailure[], downloader?: Downloader | undefined);
115
+ /**
116
+ * Address a succeeded input by the `key:` given to `file()`. Duplicate keys
117
+ * are not valid input — the producer enforces key uniqueness (a later
118
+ * ticket); the first match is returned.
119
+ * @throws {GislNoSuchKeyError} when no succeeded entry has that key (a
120
+ * keyless run always throws — it is positionally addressable only).
121
+ */
122
+ byKey(key: string): ItemResult;
123
+ /**
124
+ * Write the single output to `path`. Requires EXACTLY ONE artifact.
125
+ * @throws {GislSinkError} reason `not_single_output` for 0/>1 outputs;
126
+ * reason `downloader_unavailable` when no downloader is bound.
127
+ */
128
+ toFile(path: string): Promise<void>;
129
+ /**
130
+ * Download every output into `dir` (filename per output), in output order.
131
+ * Returns the {@link Manifest} of local paths written.
132
+ * @throws {GislSinkError} reason `partial_failure` when `failOnPartial` and
133
+ * the run had failed inputs; reason `downloader_unavailable` when no
134
+ * downloader is bound.
135
+ */
136
+ downloadTo(dir: string, options?: {
137
+ failOnPartial?: boolean;
138
+ }): Promise<Manifest>;
139
+ /**
140
+ * Plain-object projection. Field ORDER (workflowId, state, ok, url?,
141
+ * artifacts, succeeded, failed) is fixed to match the PHP `toArray()`
142
+ * reference so JSON-string parity holds (FF1 shape assertion + FF2b harness
143
+ * fixture). `url` is omitted entirely when undefined — `JSON.stringify`
144
+ * then produces the identical shape to PHP's omit-when-null `toArray()`.
145
+ */
146
+ toJSON(): {
147
+ workflowId: string;
148
+ state: string;
149
+ ok: boolean;
150
+ url?: string;
151
+ artifacts: readonly OutputFile[];
152
+ succeeded: readonly {
153
+ key: string | null;
154
+ outputs: readonly OutputFile[];
155
+ }[];
156
+ failed: readonly {
157
+ key: string | null;
158
+ error: string;
159
+ }[];
160
+ };
161
+ private requireDownloader;
162
+ }
163
+ /**
164
+ * The primary file a {@link Recipe} operates on — the "subject" of the
165
+ * file-first surface. A discriminated union over the ways a caller names an
166
+ * input:
167
+ *
168
+ * - `path` — a local filesystem path (Node; the common case).
169
+ * - `blob` — an in-memory `Blob`/`File` (browser, or Node 18+).
170
+ * - `uploadId` — a previously-uploaded `file_id` (reuse across recipes).
171
+ *
172
+ * FF2a does NO upload, so only the `path` + `uploadId` arms are exercised
173
+ * end-to-end here; the `blob` arm is DEFINED and type-checked but its upload
174
+ * is wired by FF2b (`run()`). Mirrors the PHP `FileInput` value object.
175
+ */
176
+ export type FileInput = {
177
+ readonly kind: 'path';
178
+ readonly path: string;
179
+ } | {
180
+ readonly kind: 'blob';
181
+ readonly blob: Blob;
182
+ } | {
183
+ readonly kind: 'uploadId';
184
+ readonly fileId: string;
185
+ };
186
+ /** Named constructors for {@link FileInput} — mirror the PHP static factories. */
187
+ export declare const fileInput: {
188
+ readonly path: (path: string) => FileInput;
189
+ readonly blob: (blob: Blob) => FileInput;
190
+ readonly uploadId: (fileId: string) => FileInput;
191
+ };
192
+ /** One step in a {@link Recipe}'s chain — an op kind + captured ergonomic args. */
193
+ interface RecipeStep {
194
+ readonly opType: 'compress' | 'convert' | 'thumbnail' | 'text_watermark';
195
+ readonly options: Readonly<Record<string, unknown>>;
196
+ }
197
+ /**
198
+ * The file-first builder value. `client.file(path)` returns a `Recipe`;
199
+ * single-input operations called on it (`compress`, `convert`, `thumbnail`,
200
+ * `textWatermark`) chain SEQUENTIALLY — each op feeds the next, and the chain
201
+ * lowers to ONE workflow job with an ordered `operations[]` (per ADR-0004:
202
+ * operations execute sequentially, each consuming the previous output). A
203
+ * chain yields the TERMINAL output only; intermediates are consumed (surfaced
204
+ * by FF2b's `run()`/{@link RunResult}).
205
+ *
206
+ * **Immutable / clone-on-write.** Every op returns a NEW `Recipe` carrying the
207
+ * appended step — `this` is never mutated. A Recipe is therefore a reusable
208
+ * value: branching the same base recipe two different ways cannot let one
209
+ * branch observe the other's steps (the aliasing trap mutable builders fall
210
+ * into).
211
+ *
212
+ * FF2a is network-free: there is NO `run()` here (that is FF2b). The lowering
213
+ * seam {@link toWorkflowPayload} takes the resolved upload id as a parameter
214
+ * so it stays pure — FF2b's `run()` calls the SAME method after uploading, and
215
+ * the parity harness calls it with a fixed id to assert the lowered shape.
216
+ *
217
+ * Mirrors the PHP `Recipe`.
218
+ */
219
+ export declare class Recipe {
220
+ private readonly input;
221
+ private readonly recipeKey;
222
+ private readonly steps;
223
+ private readonly presetDefaults?;
224
+ private readonly scopedPresetDefaults?;
225
+ private readonly client?;
226
+ constructor(input: FileInput, recipeKey?: string | undefined, steps?: readonly RecipeStep[], presetDefaults?: PresetDefaults | undefined, scopedPresetDefaults?: PresetDefaults | undefined, client?: GislClient | undefined);
227
+ /**
228
+ * Reduce file size. `optimize` selects a per-media preset (resolved to
229
+ * concrete wire fields at lower-time, exactly as `client.compress()` does).
230
+ */
231
+ compress(optimize?: OptimizeFor): Recipe;
232
+ /** Change format. `format` is lowered verbatim to the `format` wire option. */
233
+ convert(format: string): Recipe;
234
+ /**
235
+ * Generate a preview. Width and/or height in pixels; an omitted dimension is
236
+ * dropped from the wire options (not sent as `undefined`).
237
+ */
238
+ thumbnail(options?: {
239
+ width?: number;
240
+ height?: number;
241
+ }): Recipe;
242
+ /**
243
+ * Apply a text watermark. Single-input (the text is an option, not a
244
+ * secondary file) — lowers to the `text_watermark` op with a `text` option.
245
+ */
246
+ textWatermark(text: string): Recipe;
247
+ /**
248
+ * Lower this recipe to a workflow-create payload against a resolved upload
249
+ * id. Single-input chain → ONE job, `source: upload(fileId)`, ordered
250
+ * `operations[]`; the job `id` is omitted (a single job referenced by
251
+ * nothing — the server auto-assigns `job_N`).
252
+ *
253
+ * @internal Consumed by FF2b's `run()` (after a real upload) and by the
254
+ * cross-language parity harness (with a fixed id). Not part of the
255
+ * caller-facing fluent surface.
256
+ */
257
+ toWorkflowPayload(fileId: string): WorkflowCreatePayload;
258
+ /** The result-addressing key passed to `file()`, or undefined. */
259
+ key(): string | undefined;
260
+ /** The number of operations chained so far (introspection / tests). */
261
+ get stepCount(): number;
262
+ /**
263
+ * Execute the recipe end-to-end: upload the input (when required), create
264
+ * the workflow, await a terminal state (SSE with poll fallback), then
265
+ * resolve the produced downloads into a flat {@link RunResult}. Throws
266
+ * {@link GislTimeoutError} if `maxWait` elapses before terminal status.
267
+ *
268
+ * Mirrors the operation-first `OperationBuilder.run` (in `builder.ts`).
269
+ * Requires a client bound at construction time — `gisl().file(...)` wires
270
+ * it; a directly-constructed `Recipe` (e.g. in a lowering-only test) has no
271
+ * client and throws {@link GislConfigError}.
272
+ */
273
+ run(options?: {
274
+ maxWait?: string | number;
275
+ onProgress?: (event: ProgressEvent) => void;
276
+ signal?: AbortSignal;
277
+ pollIntervalMs?: number;
278
+ }): Promise<RunResult>;
279
+ private withStep;
280
+ private lowerStep;
281
+ private lowerCompressOptions;
282
+ private compressMediaHint;
283
+ }
284
+ export {};