@giveitsmaller/sdk 0.19.0 → 0.20.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.
@@ -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
@@ -324,7 +337,7 @@ export declare const fileInput: {
324
337
  };
325
338
  /** One step in a {@link Recipe}'s chain — an op kind + captured ergonomic args. */
326
339
  interface RecipeStep {
327
- readonly opType: 'compress' | 'convert' | 'thumbnail' | 'text_watermark' | 'output';
340
+ readonly opType: 'compress' | 'convert' | 'thumbnail' | 'text_watermark' | 'output' | 'transform';
328
341
  readonly options: Readonly<Record<string, unknown>>;
329
342
  }
330
343
  /**
@@ -378,6 +391,19 @@ export declare class Recipe {
378
391
  * dropped from the wire options (not sent as `undefined`).
379
392
  */
380
393
  thumbnail(options: ThumbnailOptions): Recipe;
394
+ /**
395
+ * Geometric transform: rotate (0/90/180/270°) and/or flip. Chainable — the
396
+ * canonical single-job order is `transform → convert → compress → thumbnail`,
397
+ * so downstream size options refer to the final (post-transform) frame.
398
+ *
399
+ * Passthrough: `rotate`/`flip` are forwarded as-is; the SDK does NOT narrow
400
+ * per media (a `flip` on a PDF input passes SDK validation but the server
401
+ * rejects it — documents rotate only). The transform op is `availability:
402
+ * planned` today, so workflow-create returns `feature_not_available` (422)
403
+ * until the per-media Lambdas ship. A no-op (`rotate:0` + `flip:none`) is
404
+ * rejected server-side as `invalid_options`.
405
+ */
406
+ transform(options?: TransformOptions): Recipe;
381
407
  /**
382
408
  * Produce ONE transformed image: keep or change format, plus quality, resize
383
409
  * and route-honored controls. The single user-facing image transform — the SDK
@@ -414,7 +440,8 @@ export declare class Recipe {
414
440
  * (beta). Audio/document/animated-GIF/unsupported-subtype/undetectable bases
415
441
  * throw locally BEFORE any upload (the planned-op gate). `options` carries the
416
442
  * wire watermark options (`anchor`, `opacity`, `margin_x`, `margin_y`,
417
- * `overlay_width`). Returns a {@link WatermarkedRecipe} (chain post-watermark
443
+ * `overlay_width`, or `overlays[]` for the multi-overlay stack). Returns a
444
+ * {@link WatermarkedRecipe} (chain post-watermark
418
445
  * `compress`/`convert`/`thumbnail`, then `run`/`submit`). Distinct from
419
446
  * {@link textWatermark} (single-input text overlay).
420
447
  */
@@ -466,6 +493,8 @@ export declare class Recipe {
466
493
  maxWait?: string | number;
467
494
  onProgress?: (event: ProgressEvent) => void;
468
495
  signal?: AbortSignal;
496
+ /** Force the poll fallback instead of attempting SSE. Default true (SSE-first, poll fallback). */
497
+ useSSE?: boolean;
469
498
  pollIntervalMs?: number;
470
499
  probeBeforeCreate?: boolean;
471
500
  probeTimeoutMs?: number;
@@ -625,6 +654,8 @@ export declare class FilesRecipe {
625
654
  convert(format: string, options?: ConvertOptions): FilesRecipe;
626
655
  /** Generate a preview of every input. `width` AND `height` are required; validated via the base {@link Recipe} before any upload. */
627
656
  thumbnail(options: ThumbnailOptions): FilesRecipe;
657
+ /** Apply the same geometric transform (rotate/flip) to every input. Validated via the base {@link Recipe}. */
658
+ transform(options?: TransformOptions): FilesRecipe;
628
659
  /** Apply the same text watermark to every input. Option keys validated via the base {@link Recipe}. */
629
660
  textWatermark(text: string, options?: TextWatermarkOptions): FilesRecipe;
630
661
  /**
@@ -681,6 +712,8 @@ export declare class FilesRecipe {
681
712
  maxWait?: string | number;
682
713
  onProgress?: (event: ProgressEvent) => void;
683
714
  signal?: AbortSignal;
715
+ /** Force the poll fallback instead of attempting SSE. Default true (SSE-first, poll fallback). */
716
+ useSSE?: boolean;
684
717
  pollIntervalMs?: number;
685
718
  probeBeforeCreate?: boolean;
686
719
  probeTimeoutMs?: number;
@@ -761,6 +794,8 @@ export declare class MergedRecipe {
761
794
  convert(format: string, options?: ConvertOptions): MergedRecipe;
762
795
  /** Thumbnail the merged output. `width` AND `height` are required; validated pre-upload. */
763
796
  thumbnail(options: ThumbnailOptions): MergedRecipe;
797
+ /** Geometric transform (rotate/flip) of the merged output. Passthrough; see {@link Recipe.transform}. */
798
+ transform(options?: TransformOptions): MergedRecipe;
764
799
  /**
765
800
  * Lower to the merge DAG: one `passthrough` source job per input + one
766
801
  * `merge` job whose `operations[]` is `[merge, ...post-combine ops]`. The
@@ -788,6 +823,8 @@ export declare class MergedRecipe {
788
823
  maxWait?: string | number;
789
824
  onProgress?: (event: ProgressEvent) => void;
790
825
  signal?: AbortSignal;
826
+ /** Force the poll fallback instead of attempting SSE. Default true (SSE-first, poll fallback). */
827
+ useSSE?: boolean;
791
828
  pollIntervalMs?: number;
792
829
  probeBeforeCreate?: boolean;
793
830
  probeTimeoutMs?: number;
@@ -887,6 +924,8 @@ export declare class ArchivedRecipe {
887
924
  maxWait?: string | number;
888
925
  onProgress?: (event: ProgressEvent) => void;
889
926
  signal?: AbortSignal;
927
+ /** Force the poll fallback instead of attempting SSE. Default true (SSE-first, poll fallback). */
928
+ useSSE?: boolean;
890
929
  pollIntervalMs?: number;
891
930
  probeBeforeCreate?: boolean;
892
931
  probeTimeoutMs?: number;
@@ -944,6 +983,8 @@ export declare class WatermarkedRecipe {
944
983
  convert(format: string, options?: ConvertOptions): WatermarkedRecipe;
945
984
  /** Thumbnail the watermarked output. `width` AND `height` are required; validated pre-upload. */
946
985
  thumbnail(options: ThumbnailOptions): WatermarkedRecipe;
986
+ /** Geometric transform (rotate/flip) of the watermarked output. Passthrough; see {@link Recipe.transform}. */
987
+ transform(options?: TransformOptions): WatermarkedRecipe;
947
988
  /**
948
989
  * Lower to the watermark DAG: a `src_0` passthrough/base-steps job + a `src_1`
949
990
  * passthrough/overlay-steps job + one `watermark` job whose `inputs[]` consume
@@ -967,6 +1008,8 @@ export declare class WatermarkedRecipe {
967
1008
  maxWait?: string | number;
968
1009
  onProgress?: (event: ProgressEvent) => void;
969
1010
  signal?: AbortSignal;
1011
+ /** Force the poll fallback instead of attempting SSE. Default true (SSE-first, poll fallback). */
1012
+ useSSE?: boolean;
970
1013
  pollIntervalMs?: number;
971
1014
  probeBeforeCreate?: boolean;
972
1015
  probeTimeoutMs?: number;
@@ -1005,4 +1048,111 @@ export declare class WatermarkedRecipe {
1005
1048
  private lowerPostSteps;
1006
1049
  private withStep;
1007
1050
  }
1051
+ /**
1052
+ * The keyed multi-recipe batch builder (FF7 / MFaCjL8d). `client.batch([r1, r2, …])`
1053
+ * runs N DISTINCT single-input keyed {@link Recipe}s as ONE workflow; the
1054
+ * partitioned {@link RunResult} addresses each entry's outputs by the caller key
1055
+ * given at `client.file(input, key)` time (`res.byKey('hero')`), and one failed
1056
+ * entry lands in `failed` without sinking the rest.
1057
+ *
1058
+ * **v1 scope (locked):** `.run()` only (no `submit()` / reattach — a follow-up);
1059
+ * single-input {@link Recipe} entries only — the multi-input builders
1060
+ * ({@link FilesRecipe}, {@link MergedRecipe}, {@link WatermarkedRecipe},
1061
+ * {@link ArchivedRecipe}) are REJECTED pre-upload. Cross-entry upload dedupe
1062
+ * IS applied (1LwSJcz1): two entries sourcing the SAME input (by
1063
+ * {@link inputIdentity}) upload ONCE and share the resulting fileId —
1064
+ * correctness-neutral (same bytes → same per-job output), it only elides
1065
+ * redundant uploads. Observable caveat: `onProgress` upload-phase events drop
1066
+ * to one-per-UNIQUE input rather than one-per-entry.
1067
+ *
1068
+ * **Lowering (one workflow):** for each entry `i`, lower its single job via
1069
+ * {@link Recipe.toWorkflowPayload} and re-id it `b{i}` — a POSITIONAL namespace
1070
+ * DISTINCT from the fan-out `file-{i}` / merge-archive-watermark `src_{i}` refs so
1071
+ * a future reattach can't misdetect the wire as a fan-out / merge. `keyByRef`
1072
+ * maps each `b{i}` ref to that entry's caller key, so
1073
+ * {@link projectMultiJobToRunResult} partitions per entry (1 job ↔ 1 key:
1074
+ * `completed` → `succeeded`, else → `failed` with a {@link GislItemFailedError}).
1075
+ *
1076
+ * **Immutability:** the ctor is CLIENT-ONLY (the ordered entries + the client) —
1077
+ * entries are already-built Recipes that captured their own preset defaults at
1078
+ * `client.file(...)` time, so batch never re-plumbs
1079
+ * presetDefaults/scopedPresetDefaults. Mirrors the PHP `BatchRecipe`.
1080
+ */
1081
+ export declare class BatchRecipe {
1082
+ private readonly client?;
1083
+ private readonly recipes;
1084
+ constructor(recipes: ReadonlyArray<Recipe>, client?: GislClient | undefined);
1085
+ /**
1086
+ * Execute the batch end-to-end: validate + lowering-preflight EVERY entry
1087
+ * BEFORE any upload, upload each entry's input, create ONE multi-job workflow
1088
+ * (one `b{i}` job per entry), await a terminal state (SSE with poll fallback,
1089
+ * honouring `useSSE`), then partition the per-job downloads into a keyed
1090
+ * {@link RunResult}. `partially_failed` is a NORMAL terminal state here — the
1091
+ * completed entries land in `succeeded`, the rest in `failed`.
1092
+ *
1093
+ * Requires a client bound at construction time — `gisl().batch([...])` wires
1094
+ * it; a directly-constructed {@link BatchRecipe} (e.g. a lowering-only test)
1095
+ * throws {@link GislConfigError}. Mirrors the fan-out {@link FilesRecipe.run}.
1096
+ */
1097
+ run(options?: {
1098
+ maxWait?: string | number;
1099
+ onProgress?: (event: ProgressEvent) => void;
1100
+ signal?: AbortSignal;
1101
+ /** Force the poll fallback instead of attempting SSE. Default true (SSE-first, poll fallback). */
1102
+ useSSE?: boolean;
1103
+ pollIntervalMs?: number;
1104
+ probeBeforeCreate?: boolean;
1105
+ probeTimeoutMs?: number;
1106
+ }): Promise<RunResult>;
1107
+ /**
1108
+ * Lower the batch to ONE multi-job workflow-create payload against a list of
1109
+ * resolved upload ids (one per entry, in entry order). Each entry `i` becomes
1110
+ * ONE job re-id'd `b{i}` carrying that entry's lowered `source` + `operations`.
1111
+ * Composes the single-file {@link Recipe.toWorkflowPayload} per entry so each
1112
+ * keeps its own media-hint + preset resolution and lowering logic is not
1113
+ * duplicated. `callback_url` is built in ONLY when a webhook is supplied
1114
+ * (batch v1 run() supplies none, so it is omitted).
1115
+ *
1116
+ * @internal Consumed by {@link run} (after uploading) and the cross-language
1117
+ * golden-payload lowering test (with fixed ids). Not caller-facing.
1118
+ */
1119
+ toWorkflowPayload(fileIds: readonly string[], callbackUrl?: string): WorkflowCreatePayload;
1120
+ /** The number of recipe entries in this batch (introspection / tests). */
1121
+ get recipeCount(): number;
1122
+ /**
1123
+ * Validate the batch AND lowering-preflight every entry BEFORE any upload
1124
+ * fires — an invalid entry costs no bandwidth. TWO PASSES (mirrors PHP
1125
+ * `BatchRecipe`'s structural-loop-then-preflight-loop), throwing
1126
+ * {@link GislConfigError}:
1127
+ * 0. empty batch → `no_recipes` (checked first).
1128
+ * PASS 1 (structural, ALL entries in order):
1129
+ * - a KNOWN multi-input builder (checked FIRST — they do NOT extend
1130
+ * {@link Recipe}, so the not-a-Recipe catch-all would otherwise misreport
1131
+ * them as plain type errors) → `multi_input_recipe_unsupported`;
1132
+ * - a non-{@link Recipe} entry → `invalid_recipe`;
1133
+ * - a missing/empty key → `missing_key`;
1134
+ * - a duplicate key → `duplicate_key`.
1135
+ * PASS 2 (lowering preflight, ALL entries): lower each entry (via
1136
+ * {@link Recipe.toWorkflowPayload}) so an invalid lowering throws BEFORE any
1137
+ * upload, mirroring what {@link FilesRecipe} lowers pre-create.
1138
+ *
1139
+ * Two passes so a batch with MULTIPLE distinct violations throws the SAME
1140
+ * reason regardless of entry order (a structural error anywhere wins over a
1141
+ * lowering error elsewhere) — converging TS + PHP error reporting. The
1142
+ * offending key/index rides the MESSAGE (not `conflictingFields`, which is
1143
+ * reserved for wire FIELD names).
1144
+ */
1145
+ private validatePreUpload;
1146
+ /**
1147
+ * Collapse the entry inputs to a first-appearance-unique list for cross-entry
1148
+ * upload dedupe: two entries sourcing the SAME input (by {@link inputIdentity})
1149
+ * upload ONCE and share the fileId. Returns the ordered `uniqueInputs` plus an
1150
+ * `entryToUnique` index map (length N, entry order) so {@link run} can expand
1151
+ * the unique fileIds back to one-per-entry before {@link toWorkflowPayload} —
1152
+ * keeping the b{i} refs + {@link keyByRef} N-length and correctness-neutral.
1153
+ */
1154
+ private planUploads;
1155
+ /** Map each `b{i}` job ref to that entry's caller key (validated non-empty). */
1156
+ private keyByRef;
1157
+ }
1008
1158
  export {};