@giveitsmaller/sdk 0.19.0 → 0.21.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 (45) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +8 -15
  3. package/dist/_audit.js +14 -4
  4. package/dist/builder.d.ts +5 -4
  5. package/dist/builder.js +16 -16
  6. package/dist/client.d.ts +17 -7
  7. package/dist/client.js +116 -62
  8. package/dist/ergonomic/image_output_routes.d.ts +30 -0
  9. package/dist/ergonomic/image_output_routes.js +82 -11
  10. package/dist/ergonomic/option_types.d.ts +34 -1
  11. package/dist/ergonomic/option_types.js +5 -1
  12. package/dist/ergonomic/option_validation.d.ts +19 -1
  13. package/dist/ergonomic/option_validation.js +34 -1
  14. package/dist/ergonomic/preset_resolver.d.ts +2 -2
  15. package/dist/ergonomic/preset_resolver.js +6 -10
  16. package/dist/ergonomic/presets/index.d.ts +9 -8
  17. package/dist/ergonomic/presets/index.js +1 -9
  18. package/dist/errors.d.ts +105 -3
  19. package/dist/errors.js +136 -2
  20. package/dist/file-first.d.ts +234 -16
  21. package/dist/file-first.js +692 -175
  22. package/dist/generated/sdk_spec/enums.d.ts +0 -26
  23. package/dist/generated/sdk_spec/enums.js +0 -16
  24. package/dist/generated/sdk_spec/errors.d.ts +1 -1
  25. package/dist/generated/sdk_spec/errors.js +12 -0
  26. package/dist/generated/sdk_spec/presets.js +0 -14
  27. package/dist/generated/sdk_spec/version.d.ts +2 -2
  28. package/dist/generated/sdk_spec/version.js +2 -2
  29. package/dist/gisl.d.ts +93 -5
  30. package/dist/gisl.js +72 -2
  31. package/dist/handle.d.ts +6 -1
  32. package/dist/handle.js +42 -13
  33. package/dist/index.core.d.ts +10 -8
  34. package/dist/index.core.js +11 -3
  35. package/dist/merge.d.ts +12 -0
  36. package/dist/merge.js +14 -2
  37. package/dist/retry-metadata.d.ts +37 -0
  38. package/dist/retry-metadata.js +86 -0
  39. package/dist/sse.d.ts +2 -1
  40. package/dist/sse.js +26 -6
  41. package/dist/types.d.ts +54 -4
  42. package/dist/types.js +1 -0
  43. package/package.json +3 -3
  44. package/dist/ergonomic/presets/document_pdf_compress.d.ts +0 -12
  45. package/dist/ergonomic/presets/document_pdf_compress.js +0 -33
@@ -13,7 +13,7 @@ import { GislItemFailedError } from './errors.js';
13
13
  import { type ProgressEvent } from './builder.js';
14
14
  import type { GislClient } from './client.js';
15
15
  import type { OperationDownload, WorkflowStatusResponse } from '@giveitsmaller/contracts/openapi';
16
- import type { ConvertOptions, ThumbnailOptions, TextWatermarkOptions, WatermarkOptions, OutputOptions, OutputFit } from './ergonomic/option_types.js';
16
+ import type { ConvertOptions, ThumbnailOptions, TransformOptions, TextWatermarkOptions, WatermarkOptions, OutputOptions, OutputFit } from './ergonomic/option_types.js';
17
17
  import { OptimizeFor } from './generated/sdk_spec/enums.js';
18
18
  import type { PresetDefaults } from './ergonomic/presets/index.js';
19
19
  import type { WorkflowCreatePayload } from './types.js';
@@ -72,6 +72,19 @@ export interface OutputFile {
72
72
  * outputs.
73
73
  */
74
74
  readonly targetSizeMet?: boolean;
75
+ /**
76
+ * The measured perceptual quality of an `auto_quality` encode (0-1). Projected
77
+ * from the generated {@link OperationDownload.measuredQuality}; undefined
78
+ * (omitted) when the worker reported no measurement. Pairs with
79
+ * {@link qualityMetric}, which names the metric it was measured on.
80
+ */
81
+ readonly measuredQuality?: number;
82
+ /**
83
+ * The metric {@link measuredQuality} was measured on (e.g. `ssimulacra2`).
84
+ * Projected from the generated {@link OperationDownload.qualityMetric};
85
+ * undefined when no measurement was reported.
86
+ */
87
+ readonly qualityMetric?: string;
75
88
  }
76
89
  /**
77
90
  * One succeeded entry in {@link RunResult.succeeded}: a single input's
@@ -243,10 +256,43 @@ export declare function projectMultiJobToRunResult(workflowId: string, finalStat
243
256
  * @internal Exported for the file-first `Handle`; not part of the public API.
244
257
  */
245
258
  export declare function isFanoutStatus(finalStatus: WorkflowStatusResponse): boolean;
259
+ /**
260
+ * Job id/ref for the DOWNSTREAM job that carries post-`sole_op` steps. A
261
+ * `sole_op` op (image_watermark / video_watermark / merge — ADR-0025) MUST be
262
+ * the only op in its job, so when a caller chains `compress()` / `convert()` /
263
+ * `thumbnail()` / `transform()` after `watermark()` / `merge()`, those steps
264
+ * lower into this separate job that consumes the sole_op output via
265
+ * `job_output` (the server derives the DAG from the `from` reference — no
266
+ * explicit `workflow_edges` needed). When present it is the TERMINAL deliverable,
267
+ * so `run()` / the {@link Handle} project THIS job's output, and the status-shape
268
+ * detectors accept it alongside the sole_op + `src_{i}` refs. PIiUit28.
269
+ */
270
+ export declare const _POST_STEP_JOB_REF = "post";
271
+ /**
272
+ * Job id/ref for the UPSTREAM job carrying any steps that PRECEDE a single-input
273
+ * `sole_op` op (e.g. `.compress().textWatermark()`): the pre-steps run in this
274
+ * job, the `sole_op` job then consumes its output via `job_output`. Distinct
275
+ * from the multi-input `src_{i}` fan-in refs. IQc01rj0.
276
+ */
277
+ export declare const _PRE_STEP_JOB_REF = "pre";
278
+ /**
279
+ * Wire op types the API marks `sole_op` (ADR-0025) — the op MUST be the ONLY op
280
+ * in its job. Mirrors `operation-capabilities.json` `operations.<op>.sole_op`,
281
+ * inlined as a browser-safe const (the raw-JSON sidecar subpath is Node-only)
282
+ * and PINNED to that projection by `sole-op-conformance.test.ts` — a contract
283
+ * regen that flips an op's `sole_op` fails there. The single-input
284
+ * {@link Recipe.toWorkflowPayload} reads THIS set to split a chain at every
285
+ * sole_op boundary into a `job_output`-linked job chain (so
286
+ * `.textWatermark('x').compress()` lowers to a valid DAG, not a contract-invalid
287
+ * co-bundled job). Mirrored by PHP `Recipe::SOLE_OP_TYPES`. IQc01rj0.
288
+ * @internal
289
+ */
290
+ export declare const SOLE_OP_TYPES: ReadonlySet<string>;
246
291
  /**
247
292
  * True when a terminal status describes a fluent `files([...]).merge(...)`
248
293
  * combine — at least one job ref `merge` and every OTHER job ref is `src_{i}`
249
- * (the ids the {@link MergedRecipe} lowering assigns). The data-driven seam that
294
+ * or the downstream `post` job (the ids the {@link MergedRecipe} lowering
295
+ * assigns; `post` carries any post-combine steps). The data-driven seam that
250
296
  * lets {@link Handle.wait}/{@link Handle.result} project ONLY the merged output
251
297
  * — filtering the `src_*` passthrough plumbing — even after a
252
298
  * `client.workflow(id)` reattach (no construction-time marker), matching
@@ -269,9 +315,10 @@ export declare function isMergeStatus(finalStatus: WorkflowStatusResponse): bool
269
315
  export declare function isArchiveStatus(finalStatus: WorkflowStatusResponse): boolean;
270
316
  /**
271
317
  * True when a terminal status describes a fluent `file(...).watermark(overlay)`
272
- * — at least one job ref `watermark` and every OTHER job ref is `src_{i}` (the
273
- * ids the {@link WatermarkedRecipe} lowering assigns: `src_0` base, `src_1`
274
- * overlay). Lets {@link Handle.wait}/{@link Handle.result} AND
318
+ * — at least one job ref `watermark` and every OTHER job ref is `src_{i}` or
319
+ * the downstream `post` job (the ids the {@link WatermarkedRecipe} lowering
320
+ * assigns: `src_0` base, `src_1` overlay, `post` any post-watermark steps).
321
+ * Lets {@link Handle.wait}/{@link Handle.result} AND
275
322
  * {@link WatermarkedRecipe.run} project ONLY the watermark output — filtering
276
323
  * the `src_*` passthrough plumbing — even after a `client.workflow(id)` reattach.
277
324
  * Mutually exclusive with {@link isFanoutStatus} / {@link isMergeStatus} /
@@ -280,6 +327,28 @@ export declare function isArchiveStatus(finalStatus: WorkflowStatusResponse): bo
280
327
  * @internal Exported for the file-first `Handle`; not part of the public API.
281
328
  */
282
329
  export declare function isWatermarkStatus(finalStatus: WorkflowStatusResponse): boolean;
330
+ /**
331
+ * True when a terminal status describes a SINGLE-INPUT `sole_op` chain — e.g.
332
+ * `.textWatermark('x').compress()` lowered to a `text_watermark` job + a
333
+ * downstream `post` job (and an optional upstream `pre` job for steps before the
334
+ * sole_op). Every job ref is a `sole_op` wire type ({@link SOLE_OP_TYPES}) or the
335
+ * `pre`/`post` chain refs, with NO `src_{i}` fan-in ref (which distinguishes it
336
+ * from the multi-input merge/watermark/archive DAGs). Lets a submitted/reattached
337
+ * {@link Handle} project ONLY the terminal deliverable — filtering the
338
+ * intermediate sole_op artifact — without builder state. IQc01rj0.
339
+ *
340
+ * @internal Exported for the file-first `Handle`; not part of the public API.
341
+ */
342
+ export declare function isSoleOpChainStatus(finalStatus: WorkflowStatusResponse): boolean;
343
+ /**
344
+ * The terminal deliverable ref for a single-input `sole_op` chain status: the
345
+ * downstream `post` job when present, else the `sole_op` job itself (the ref in
346
+ * {@link SOLE_OP_TYPES}). Mirrors how the merge/watermark paths pick their
347
+ * terminal via {@link terminalOutputRef} in `handle.ts`. IQc01rj0.
348
+ *
349
+ * @internal
350
+ */
351
+ export declare function soleOpChainDeliverableRef(finalStatus: WorkflowStatusResponse): string;
283
352
  /**
284
353
  * The primary file a {@link Recipe} operates on — the "subject" of the
285
354
  * file-first surface. A discriminated union over the ways a caller names an
@@ -324,7 +393,7 @@ export declare const fileInput: {
324
393
  };
325
394
  /** One step in a {@link Recipe}'s chain — an op kind + captured ergonomic args. */
326
395
  interface RecipeStep {
327
- readonly opType: 'compress' | 'convert' | 'thumbnail' | 'text_watermark' | 'output';
396
+ readonly opType: 'compress' | 'convert' | 'thumbnail' | 'text_watermark' | 'output' | 'transform';
328
397
  readonly options: Readonly<Record<string, unknown>>;
329
398
  }
330
399
  /**
@@ -378,6 +447,19 @@ export declare class Recipe {
378
447
  * dropped from the wire options (not sent as `undefined`).
379
448
  */
380
449
  thumbnail(options: ThumbnailOptions): Recipe;
450
+ /**
451
+ * Geometric transform: rotate (0/90/180/270°) and/or flip. Chainable — the
452
+ * canonical single-job order is `transform → convert → compress → thumbnail`,
453
+ * so downstream size options refer to the final (post-transform) frame.
454
+ *
455
+ * Passthrough: `rotate`/`flip` are forwarded as-is; the SDK does NOT narrow
456
+ * per media (a `flip` on a PDF input passes SDK validation but the server
457
+ * rejects it — documents rotate only). The transform op is `availability:
458
+ * planned` today, so workflow-create returns `feature_not_available` (422)
459
+ * until the per-media Lambdas ship. A no-op (`rotate:0` + `flip:none`) is
460
+ * rejected server-side as `invalid_options`.
461
+ */
462
+ transform(options?: TransformOptions): Recipe;
381
463
  /**
382
464
  * Produce ONE transformed image: keep or change format, plus quality, resize
383
465
  * and route-honored controls. The single user-facing image transform — the SDK
@@ -414,7 +496,8 @@ export declare class Recipe {
414
496
  * (beta). Audio/document/animated-GIF/unsupported-subtype/undetectable bases
415
497
  * throw locally BEFORE any upload (the planned-op gate). `options` carries the
416
498
  * wire watermark options (`anchor`, `opacity`, `margin_x`, `margin_y`,
417
- * `overlay_width`). Returns a {@link WatermarkedRecipe} (chain post-watermark
499
+ * `overlay_width`, or `overlays[]` for the multi-overlay stack). Returns a
500
+ * {@link WatermarkedRecipe} (chain post-watermark
418
501
  * `compress`/`convert`/`thumbnail`, then `run`/`submit`). Distinct from
419
502
  * {@link textWatermark} (single-input text overlay).
420
503
  */
@@ -434,6 +517,15 @@ export declare class Recipe {
434
517
  * fixed id). Not part of the caller-facing fluent surface.
435
518
  */
436
519
  toWorkflowPayload(fileId: string, callbackUrl?: string): WorkflowCreatePayload;
520
+ /**
521
+ * Trigger the per-step lowering purely for its validation side effects
522
+ * (route honoring, planned / out-of-enum values, `media_unknown`), discarding
523
+ * the result. Called BEFORE uploading bytes so a route-invalid recipe fails
524
+ * fast instead of after the upload is spent — parity with PHP
525
+ * `assertOperationsLowerable`. Lowering reads only `steps` + the input token,
526
+ * not the upload id, so this is a faithful preflight (0azjb6Rg).
527
+ */
528
+ private assertOperationsLowerable;
437
529
  /** The result-addressing key passed to `file()`, or undefined. */
438
530
  key(): string | undefined;
439
531
  /** The number of operations chained so far (introspection / tests). */
@@ -466,6 +558,8 @@ export declare class Recipe {
466
558
  maxWait?: string | number;
467
559
  onProgress?: (event: ProgressEvent) => void;
468
560
  signal?: AbortSignal;
561
+ /** Force the poll fallback instead of attempting SSE. Default true (SSE-first, poll fallback). */
562
+ useSSE?: boolean;
469
563
  pollIntervalMs?: number;
470
564
  probeBeforeCreate?: boolean;
471
565
  probeTimeoutMs?: number;
@@ -625,6 +719,8 @@ export declare class FilesRecipe {
625
719
  convert(format: string, options?: ConvertOptions): FilesRecipe;
626
720
  /** Generate a preview of every input. `width` AND `height` are required; validated via the base {@link Recipe} before any upload. */
627
721
  thumbnail(options: ThumbnailOptions): FilesRecipe;
722
+ /** Apply the same geometric transform (rotate/flip) to every input. Validated via the base {@link Recipe}. */
723
+ transform(options?: TransformOptions): FilesRecipe;
628
724
  /** Apply the same text watermark to every input. Option keys validated via the base {@link Recipe}. */
629
725
  textWatermark(text: string, options?: TextWatermarkOptions): FilesRecipe;
630
726
  /**
@@ -681,6 +777,8 @@ export declare class FilesRecipe {
681
777
  maxWait?: string | number;
682
778
  onProgress?: (event: ProgressEvent) => void;
683
779
  signal?: AbortSignal;
780
+ /** Force the poll fallback instead of attempting SSE. Default true (SSE-first, poll fallback). */
781
+ useSSE?: boolean;
684
782
  pollIntervalMs?: number;
685
783
  probeBeforeCreate?: boolean;
686
784
  probeTimeoutMs?: number;
@@ -738,9 +836,10 @@ export declare class FilesRecipe {
738
836
  *
739
837
  * **Lowering (one workflow):** each input is uploaded once and wrapped in its
740
838
  * own single-input `passthrough` source job (`src_N`); the `merge` job consumes
741
- * those via `job_output` inputs (array order = play order) and carries the merge
742
- * op FIRST in its `operations[]`, followed by any post-combine ops (compress /
743
- * convert / thumbnail) so they run on the merged output in the same job. The
839
+ * those via `job_output` inputs (array order = play order). `merge` is
840
+ * `sole_op` (ADR-0025), so it is the ONLY op in its job; any post-combine ops
841
+ * (compress / convert / thumbnail / transform) lower into a downstream `post`
842
+ * job that consumes the merged output via `job_output`. The
744
843
  * merge-level wire options reuse {@link wireMergeOptions} so a fluent merge
745
844
  * lowers identically to the operation-first `client.merge()`.
746
845
  *
@@ -761,11 +860,13 @@ export declare class MergedRecipe {
761
860
  convert(format: string, options?: ConvertOptions): MergedRecipe;
762
861
  /** Thumbnail the merged output. `width` AND `height` are required; validated pre-upload. */
763
862
  thumbnail(options: ThumbnailOptions): MergedRecipe;
863
+ /** Geometric transform (rotate/flip) of the merged output. Passthrough; see {@link Recipe.transform}. */
864
+ transform(options?: TransformOptions): MergedRecipe;
764
865
  /**
765
866
  * Lower to the merge DAG: one `passthrough` source job per input + one
766
- * `merge` job whose `operations[]` is `[merge, ...post-combine ops]`. The
767
- * merge job's `inputs[]` consume the source jobs via `job_output` in input
768
- * (play) order.
867
+ * `merge` job whose `operations[]` is exactly `[merge]` (sole_op). The merge
868
+ * job's `inputs[]` consume the source jobs via `job_output` in input (play)
869
+ * order; any post-combine ops lower into a downstream `post` job.
769
870
  *
770
871
  * @internal Consumed by {@link run} (after uploading all inputs), {@link submit}
771
872
  * (with a webhook), and the cross-language parity harness (with fixed ids).
@@ -788,6 +889,8 @@ export declare class MergedRecipe {
788
889
  maxWait?: string | number;
789
890
  onProgress?: (event: ProgressEvent) => void;
790
891
  signal?: AbortSignal;
892
+ /** Force the poll fallback instead of attempting SSE. Default true (SSE-first, poll fallback). */
893
+ useSSE?: boolean;
791
894
  pollIntervalMs?: number;
792
895
  probeBeforeCreate?: boolean;
793
896
  probeTimeoutMs?: number;
@@ -887,6 +990,8 @@ export declare class ArchivedRecipe {
887
990
  maxWait?: string | number;
888
991
  onProgress?: (event: ProgressEvent) => void;
889
992
  signal?: AbortSignal;
993
+ /** Force the poll fallback instead of attempting SSE. Default true (SSE-first, poll fallback). */
994
+ useSSE?: boolean;
890
995
  pollIntervalMs?: number;
891
996
  probeBeforeCreate?: boolean;
892
997
  probeTimeoutMs?: number;
@@ -925,7 +1030,8 @@ export declare class ArchivedRecipe {
925
1030
  * `passthrough` source job (`src_0` base, `src_1` overlay; their own preceding
926
1031
  * steps lower into those jobs), and the `watermark` job consumes them via
927
1032
  * `job_output` inputs tagged `role: base` / `role: overlay`. Post-watermark
928
- * `compress`/`convert`/`thumbnail` chain onto the watermark output. Mirrors
1033
+ * `compress`/`convert`/`thumbnail`/`transform` steps lower into a downstream
1034
+ * `post` job on the watermark output (`image_watermark` is `sole_op`). Mirrors
929
1035
  * {@link MergedRecipe}. `textWatermark` is intentionally NOT a post-verb here.
930
1036
  */
931
1037
  export declare class WatermarkedRecipe {
@@ -944,11 +1050,14 @@ export declare class WatermarkedRecipe {
944
1050
  convert(format: string, options?: ConvertOptions): WatermarkedRecipe;
945
1051
  /** Thumbnail the watermarked output. `width` AND `height` are required; validated pre-upload. */
946
1052
  thumbnail(options: ThumbnailOptions): WatermarkedRecipe;
1053
+ /** Geometric transform (rotate/flip) of the watermarked output. Passthrough; see {@link Recipe.transform}. */
1054
+ transform(options?: TransformOptions): WatermarkedRecipe;
947
1055
  /**
948
1056
  * Lower to the watermark DAG: a `src_0` passthrough/base-steps job + a `src_1`
949
1057
  * passthrough/overlay-steps job + one `watermark` job whose `inputs[]` consume
950
- * them via `job_output` (role base/overlay) and whose `operations[]` is
951
- * `[image_watermark|video_watermark, ...post-watermark ops]`. `fileIds` is
1058
+ * them via `job_output` (role base/overlay). The watermark op is `sole_op`
1059
+ * (ADR-0025), so `operations[]` is exactly `[image_watermark|video_watermark]`;
1060
+ * any post-watermark ops lower into a downstream `post` job. `fileIds` is
952
1061
  * `[baseId, overlayId]` (upload order). Throws pre-lowering if the base media
953
1062
  * is undetectable/unsupported (the planned-op gate).
954
1063
  *
@@ -967,6 +1076,8 @@ export declare class WatermarkedRecipe {
967
1076
  maxWait?: string | number;
968
1077
  onProgress?: (event: ProgressEvent) => void;
969
1078
  signal?: AbortSignal;
1079
+ /** Force the poll fallback instead of attempting SSE. Default true (SSE-first, poll fallback). */
1080
+ useSSE?: boolean;
970
1081
  pollIntervalMs?: number;
971
1082
  probeBeforeCreate?: boolean;
972
1083
  probeTimeoutMs?: number;
@@ -1005,4 +1116,111 @@ export declare class WatermarkedRecipe {
1005
1116
  private lowerPostSteps;
1006
1117
  private withStep;
1007
1118
  }
1119
+ /**
1120
+ * The keyed multi-recipe batch builder (FF7 / MFaCjL8d). `client.batch([r1, r2, …])`
1121
+ * runs N DISTINCT single-input keyed {@link Recipe}s as ONE workflow; the
1122
+ * partitioned {@link RunResult} addresses each entry's outputs by the caller key
1123
+ * given at `client.file(input, key)` time (`res.byKey('hero')`), and one failed
1124
+ * entry lands in `failed` without sinking the rest.
1125
+ *
1126
+ * **v1 scope (locked):** `.run()` only (no `submit()` / reattach — a follow-up);
1127
+ * single-input {@link Recipe} entries only — the multi-input builders
1128
+ * ({@link FilesRecipe}, {@link MergedRecipe}, {@link WatermarkedRecipe},
1129
+ * {@link ArchivedRecipe}) are REJECTED pre-upload. Cross-entry upload dedupe
1130
+ * IS applied (1LwSJcz1): two entries sourcing the SAME input (by
1131
+ * {@link inputIdentity}) upload ONCE and share the resulting fileId —
1132
+ * correctness-neutral (same bytes → same per-job output), it only elides
1133
+ * redundant uploads. Observable caveat: `onProgress` upload-phase events drop
1134
+ * to one-per-UNIQUE input rather than one-per-entry.
1135
+ *
1136
+ * **Lowering (one workflow):** for each entry `i`, lower its single job via
1137
+ * {@link Recipe.toWorkflowPayload} and re-id it `b{i}` — a POSITIONAL namespace
1138
+ * DISTINCT from the fan-out `file-{i}` / merge-archive-watermark `src_{i}` refs so
1139
+ * a future reattach can't misdetect the wire as a fan-out / merge. `keyByRef`
1140
+ * maps each `b{i}` ref to that entry's caller key, so
1141
+ * {@link projectMultiJobToRunResult} partitions per entry (1 job ↔ 1 key:
1142
+ * `completed` → `succeeded`, else → `failed` with a {@link GislItemFailedError}).
1143
+ *
1144
+ * **Immutability:** the ctor is CLIENT-ONLY (the ordered entries + the client) —
1145
+ * entries are already-built Recipes that captured their own preset defaults at
1146
+ * `client.file(...)` time, so batch never re-plumbs
1147
+ * presetDefaults/scopedPresetDefaults. Mirrors the PHP `BatchRecipe`.
1148
+ */
1149
+ export declare class BatchRecipe {
1150
+ private readonly client?;
1151
+ private readonly recipes;
1152
+ constructor(recipes: ReadonlyArray<Recipe>, client?: GislClient | undefined);
1153
+ /**
1154
+ * Execute the batch end-to-end: validate + lowering-preflight EVERY entry
1155
+ * BEFORE any upload, upload each entry's input, create ONE multi-job workflow
1156
+ * (one `b{i}` job per entry), await a terminal state (SSE with poll fallback,
1157
+ * honouring `useSSE`), then partition the per-job downloads into a keyed
1158
+ * {@link RunResult}. `partially_failed` is a NORMAL terminal state here — the
1159
+ * completed entries land in `succeeded`, the rest in `failed`.
1160
+ *
1161
+ * Requires a client bound at construction time — `gisl().batch([...])` wires
1162
+ * it; a directly-constructed {@link BatchRecipe} (e.g. a lowering-only test)
1163
+ * throws {@link GislConfigError}. Mirrors the fan-out {@link FilesRecipe.run}.
1164
+ */
1165
+ run(options?: {
1166
+ maxWait?: string | number;
1167
+ onProgress?: (event: ProgressEvent) => void;
1168
+ signal?: AbortSignal;
1169
+ /** Force the poll fallback instead of attempting SSE. Default true (SSE-first, poll fallback). */
1170
+ useSSE?: boolean;
1171
+ pollIntervalMs?: number;
1172
+ probeBeforeCreate?: boolean;
1173
+ probeTimeoutMs?: number;
1174
+ }): Promise<RunResult>;
1175
+ /**
1176
+ * Lower the batch to ONE multi-job workflow-create payload against a list of
1177
+ * resolved upload ids (one per entry, in entry order). Each entry `i` becomes
1178
+ * ONE job re-id'd `b{i}` carrying that entry's lowered `source` + `operations`.
1179
+ * Composes the single-file {@link Recipe.toWorkflowPayload} per entry so each
1180
+ * keeps its own media-hint + preset resolution and lowering logic is not
1181
+ * duplicated. `callback_url` is built in ONLY when a webhook is supplied
1182
+ * (batch v1 run() supplies none, so it is omitted).
1183
+ *
1184
+ * @internal Consumed by {@link run} (after uploading) and the cross-language
1185
+ * golden-payload lowering test (with fixed ids). Not caller-facing.
1186
+ */
1187
+ toWorkflowPayload(fileIds: readonly string[], callbackUrl?: string): WorkflowCreatePayload;
1188
+ /** The number of recipe entries in this batch (introspection / tests). */
1189
+ get recipeCount(): number;
1190
+ /**
1191
+ * Validate the batch AND lowering-preflight every entry BEFORE any upload
1192
+ * fires — an invalid entry costs no bandwidth. TWO PASSES (mirrors PHP
1193
+ * `BatchRecipe`'s structural-loop-then-preflight-loop), throwing
1194
+ * {@link GislConfigError}:
1195
+ * 0. empty batch → `no_recipes` (checked first).
1196
+ * PASS 1 (structural, ALL entries in order):
1197
+ * - a KNOWN multi-input builder (checked FIRST — they do NOT extend
1198
+ * {@link Recipe}, so the not-a-Recipe catch-all would otherwise misreport
1199
+ * them as plain type errors) → `multi_input_recipe_unsupported`;
1200
+ * - a non-{@link Recipe} entry → `invalid_recipe`;
1201
+ * - a missing/empty key → `missing_key`;
1202
+ * - a duplicate key → `duplicate_key`.
1203
+ * PASS 2 (lowering preflight, ALL entries): lower each entry (via
1204
+ * {@link Recipe.toWorkflowPayload}) so an invalid lowering throws BEFORE any
1205
+ * upload, mirroring what {@link FilesRecipe} lowers pre-create.
1206
+ *
1207
+ * Two passes so a batch with MULTIPLE distinct violations throws the SAME
1208
+ * reason regardless of entry order (a structural error anywhere wins over a
1209
+ * lowering error elsewhere) — converging TS + PHP error reporting. The
1210
+ * offending key/index rides the MESSAGE (not `conflictingFields`, which is
1211
+ * reserved for wire FIELD names).
1212
+ */
1213
+ private validatePreUpload;
1214
+ /**
1215
+ * Collapse the entry inputs to a first-appearance-unique list for cross-entry
1216
+ * upload dedupe: two entries sourcing the SAME input (by {@link inputIdentity})
1217
+ * upload ONCE and share the fileId. Returns the ordered `uniqueInputs` plus an
1218
+ * `entryToUnique` index map (length N, entry order) so {@link run} can expand
1219
+ * the unique fileIds back to one-per-entry before {@link toWorkflowPayload} —
1220
+ * keeping the b{i} refs + {@link keyByRef} N-length and correctness-neutral.
1221
+ */
1222
+ private planUploads;
1223
+ /** Map each `b{i}` job ref to that entry's caller key (validated non-empty). */
1224
+ private keyByRef;
1225
+ }
1008
1226
  export {};