@giveitsmaller/sdk 0.15.0 → 0.16.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 CHANGED
@@ -130,6 +130,9 @@ export function _runAudit() {
130
130
  accept();
131
131
  // FF3a / u0hBt6fl — homogeneous fan-out builder surface.
132
132
  accept();
133
+ // FF4a / Z7zTr789 — multi-input watermark recipe surface.
134
+ accept();
135
+ accept();
133
136
  accept();
134
137
  accept();
135
138
  accept();
package/dist/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { AudioWatermarkDecodeRequest, AudioWatermarkDecodeResponse, ExternalImportCreatedResponse, ExternalImportRequest, LoginUserRequest, LoginUser200ResponseData, ContactRequest, CreditsBalanceResponse, CreditsUsageResponse, UploadResponse, UploadProbeResponse, WorkflowCancelResponse, WorkflowCreateResponse, WorkflowResumeResponse, WorkflowStatusResponse, WorkflowListResponse, WorkflowSummary, WorkflowDownloadResponse, MetadataResponse, RetryResponse } from '@giveitsmaller/contracts/openapi';
2
- import type { CreditsUsageOptions, ListWorkflowsOptions, GetSchemaOptions, GetSchemaResult, GislClientConfig, GislSseEvent, PreflightClipsResult, ProbeWaitOptions, ProbeWaitResult, UploadOptions, WaitOptions, WorkflowCreatePayload, _Sdk3HandCodedKeepaliveResult, _Sdk3HandCodedMultipartStatusResult, _Sdk3HandCodedPresignPartsResult } from './types.js';
2
+ import type { CreditsUsageOptions, ListWorkflowsOptions, GetSchemaOptions, GetSchemaResult, GislClientConfig, GislSseEvent, PreflightClipsResult, ProbeWaitOptions, ProbeWaitResult, ReadCapabilityOptions, UploadOptions, WaitOptions, WorkflowCreatePayload, _Sdk3HandCodedKeepaliveResult, _Sdk3HandCodedMultipartStatusResult, _Sdk3HandCodedPresignPartsResult } from './types.js';
3
3
  export declare const MULTIPART_CONCURRENCY_DEFAULT: 4;
4
4
  export declare const DEFAULT_MULTIPART_FIRST_CHUNK_SIZE: number;
5
5
  export interface ValidationDetail {
@@ -133,8 +133,13 @@ export declare class GislClient {
133
133
  createWorkflow(payload: WorkflowCreatePayload): Promise<WorkflowCreateResponse>;
134
134
  /**
135
135
  * Get current workflow status.
136
+ *
137
+ * For an anonymous (null-owner) workflow, pass `opts.capability` (the `cap`
138
+ * from the anonymous workflow-create response) so a session-less caller can
139
+ * read it — the SDK sends it as the `X-Workflow-Capability` header. Omit for
140
+ * authenticated reads. A wrong/missing cap on a null-owner workflow is a 404.
136
141
  */
137
- getWorkflowStatus(workflowId: string): Promise<WorkflowStatusResponse>;
142
+ getWorkflowStatus(workflowId: string, opts?: ReadCapabilityOptions): Promise<WorkflowStatusResponse>;
138
143
  /**
139
144
  * Poll until the workflow reaches a terminal status.
140
145
  */
@@ -176,13 +181,24 @@ export declare class GislClient {
176
181
  resumeWorkflow(workflowId: string): Promise<WorkflowResumeResponse>;
177
182
  /**
178
183
  * Get download URLs for a completed workflow.
184
+ *
185
+ * For an anonymous (null-owner) workflow, pass `opts.capability` (the `cap`
186
+ * from the anonymous workflow-create response) so a session-less caller can
187
+ * read it — the SDK sends it as the `X-Workflow-Capability` header. Omit for
188
+ * authenticated reads. A wrong/missing cap on a null-owner workflow is a 404.
179
189
  */
180
- getWorkflowDownloads(workflowId: string): Promise<WorkflowDownloadResponse>;
190
+ getWorkflowDownloads(workflowId: string, opts?: ReadCapabilityOptions): Promise<WorkflowDownloadResponse>;
181
191
  /**
182
192
  * Stream SSE events for a workflow. Returns an async iterable.
193
+ *
194
+ * For an anonymous (null-owner) workflow, pass `opts.capability` (the `cap`
195
+ * from the anonymous workflow-create response) so a session-less caller can
196
+ * read it — the SDK sends it as the `X-Workflow-Capability` header. Omit for
197
+ * authenticated reads. A wrong/missing cap on a null-owner workflow is a 404.
183
198
  */
184
199
  streamEvents(workflowId: string, opts?: {
185
200
  signal?: AbortSignal;
201
+ capability?: string;
186
202
  }): Promise<AsyncGenerator<GislSseEvent>>;
187
203
  /**
188
204
  * Get metadata for an uploaded file.
package/dist/client.js CHANGED
@@ -56,6 +56,17 @@ const S3_MAX_MULTIPART_PARTS = 10_000;
56
56
  const RECOMMENDED_CHUNK_SIZE_MAX_BYTES = 104_857_600; // 100 MiB
57
57
  const DEFAULT_POLL_INTERVAL_MS = 2_000;
58
58
  const DEFAULT_POLL_TIMEOUT_MS = 300_000; // 5 min
59
+ // Anonymous-read capability header. An anonymous (null-owner) workflow create
60
+ // returns a one-time `cap` token (WorkflowCreateResponse.cap); the session-less
61
+ // caller passes it back on status/downloads/events reads via this header so the
62
+ // server can authorize the read. A wrong/missing cap on a null-owner workflow
63
+ // returns 404 (no existence oracle), per contracts ticket YQt88cq2.
64
+ const WORKFLOW_CAPABILITY_HEADER = 'X-Workflow-Capability';
65
+ // Build the capability header set for a workflow read. Empty when no token is
66
+ // supplied (authenticated reads — the session authorizes those).
67
+ function workflowCapabilityHeaders(capability) {
68
+ return capability ? { [WORKFLOW_CAPABILITY_HEADER]: capability } : {};
69
+ }
59
70
  // Statuses that waitForWorkflow() returns immediately on. Per ticket I24,
60
71
  // `cancelled` and `expired` are terminal (a workflow cannot leave either
61
72
  // state). `paused_insufficient_credits` is a soft-pause: not terminal, but
@@ -1599,10 +1610,16 @@ export class GislClient {
1599
1610
  }
1600
1611
  /**
1601
1612
  * Get current workflow status.
1613
+ *
1614
+ * For an anonymous (null-owner) workflow, pass `opts.capability` (the `cap`
1615
+ * from the anonymous workflow-create response) so a session-less caller can
1616
+ * read it — the SDK sends it as the `X-Workflow-Capability` header. Omit for
1617
+ * authenticated reads. A wrong/missing cap on a null-owner workflow is a 404.
1602
1618
  */
1603
- async getWorkflowStatus(workflowId) {
1619
+ async getWorkflowStatus(workflowId, opts = {}) {
1604
1620
  return this.request('GET', `/api/workflows/${encodeURIComponent(workflowId)}/status`, {
1605
1621
  deserialize: WorkflowStatusResponseFromJSON,
1622
+ headers: workflowCapabilityHeaders(opts.capability),
1606
1623
  });
1607
1624
  }
1608
1625
  /**
@@ -1613,7 +1630,9 @@ export class GislClient {
1613
1630
  const timeoutMs = options?.timeoutMs ?? DEFAULT_POLL_TIMEOUT_MS;
1614
1631
  const deadline = Date.now() + timeoutMs;
1615
1632
  while (true) {
1616
- const status = await this.getWorkflowStatus(workflowId);
1633
+ const status = await this.getWorkflowStatus(workflowId, {
1634
+ capability: options?.capability,
1635
+ });
1617
1636
  options?.onPoll?.(status.status);
1618
1637
  if (TERMINAL_STATUSES.has(status.status)) {
1619
1638
  return status;
@@ -1669,14 +1688,25 @@ export class GislClient {
1669
1688
  }
1670
1689
  /**
1671
1690
  * Get download URLs for a completed workflow.
1691
+ *
1692
+ * For an anonymous (null-owner) workflow, pass `opts.capability` (the `cap`
1693
+ * from the anonymous workflow-create response) so a session-less caller can
1694
+ * read it — the SDK sends it as the `X-Workflow-Capability` header. Omit for
1695
+ * authenticated reads. A wrong/missing cap on a null-owner workflow is a 404.
1672
1696
  */
1673
- async getWorkflowDownloads(workflowId) {
1697
+ async getWorkflowDownloads(workflowId, opts = {}) {
1674
1698
  return this.request('GET', `/api/workflows/${encodeURIComponent(workflowId)}/downloads`, {
1675
1699
  deserialize: WorkflowDownloadResponseFromJSON,
1700
+ headers: workflowCapabilityHeaders(opts.capability),
1676
1701
  });
1677
1702
  }
1678
1703
  /**
1679
1704
  * Stream SSE events for a workflow. Returns an async iterable.
1705
+ *
1706
+ * For an anonymous (null-owner) workflow, pass `opts.capability` (the `cap`
1707
+ * from the anonymous workflow-create response) so a session-less caller can
1708
+ * read it — the SDK sends it as the `X-Workflow-Capability` header. Omit for
1709
+ * authenticated reads. A wrong/missing cap on a null-owner workflow is a 404.
1680
1710
  */
1681
1711
  async streamEvents(workflowId, opts = {}) {
1682
1712
  const eventsPath = `/api/workflows/${encodeURIComponent(workflowId)}/events`;
@@ -1702,6 +1732,7 @@ export class GislClient {
1702
1732
  response = await this.request('GET', eventsPath, {
1703
1733
  rawResponse: true,
1704
1734
  signal: controller.signal,
1735
+ headers: workflowCapabilityHeaders(opts.capability),
1705
1736
  });
1706
1737
  }
1707
1738
  catch (err) {
@@ -2,13 +2,14 @@ import type { ResolvedOptions } from '../builder.js';
2
2
  import type { OptimizeFor } from '../generated/sdk_spec/enums.js';
3
3
  import { type PresetDefaults, type PresetMedia, type PresetOp } from './presets/index.js';
4
4
  /**
5
- * Bumped on any change to a `*PresetOptions.shippedDefaultsFor(...)` cell value.
6
- * Must track the contracts `sdk-spec/version.yaml` `presetVersion` (mirrored in
7
- * the generated `sdk_spec/version.ts`). 1.0 1.2 on the contracts v2.71.0
8
- * (video_compress `audioBitrate` dropped) + v2.73.0 (image Size/Balanced
9
- * `outputFormat` Smallest/Auto Original VcPeRWdD facade self-422 guard) cuts.
5
+ * The preset matrix version emitted on every resolve. Re-exported from the
6
+ * GENERATED `sdk_spec/version.ts` (source of truth: contracts
7
+ * `sdk-spec/version.yaml` `presetVersion`) so it can NEVER drift from the
8
+ * generated preset cells a regen that bumps the cells bumps this by
9
+ * construction. Previously a hand-typed literal that the v2.73.0 regen had to
10
+ * bump manually (yREs0srv).
10
11
  */
11
- export declare const PRESET_VERSION = "1.2";
12
+ export declare const PRESET_VERSION: "1.2";
12
13
  /**
13
14
  * Inputs to {@link resolveCompressOptions}. `media` selects which leaf
14
15
  * DTO drives sdkDefault + clientDefault lookups + invalid-combo
@@ -36,15 +36,17 @@
36
36
  // to the wire; conversely if `crf` is explicit, `encoding_mode='crf'`.
37
37
  import { sha256Hex } from '../sha256.js';
38
38
  import { GislConfigError } from '../errors.js';
39
+ import { PRESET_VERSION as GENERATED_PRESET_VERSION } from '../generated/sdk_spec/version.js';
39
40
  import { ImageCompressPresetOptions, AudioCompressPresetOptions, VideoCompressPresetOptions, DocumentPdfCompressPresetOptions, DocumentOfficeCompressPresetOptions, DocumentOdfCompressPresetOptions, DocumentEpubCompressPresetOptions, } from './presets/index.js';
40
41
  /**
41
- * Bumped on any change to a `*PresetOptions.shippedDefaultsFor(...)` cell value.
42
- * Must track the contracts `sdk-spec/version.yaml` `presetVersion` (mirrored in
43
- * the generated `sdk_spec/version.ts`). 1.0 1.2 on the contracts v2.71.0
44
- * (video_compress `audioBitrate` dropped) + v2.73.0 (image Size/Balanced
45
- * `outputFormat` Smallest/Auto Original VcPeRWdD facade self-422 guard) cuts.
42
+ * The preset matrix version emitted on every resolve. Re-exported from the
43
+ * GENERATED `sdk_spec/version.ts` (source of truth: contracts
44
+ * `sdk-spec/version.yaml` `presetVersion`) so it can NEVER drift from the
45
+ * generated preset cells a regen that bumps the cells bumps this by
46
+ * construction. Previously a hand-typed literal that the v2.73.0 regen had to
47
+ * bump manually (yREs0srv).
46
48
  */
47
- export const PRESET_VERSION = '1.2';
49
+ export const PRESET_VERSION = GENERATED_PRESET_VERSION;
48
50
  // ---------------------------------------------------------------------------
49
51
  // Wire-field alias map (declarative — NOT generic toSnakeCase).
50
52
  // ---------------------------------------------------------------------------
@@ -249,6 +249,19 @@ export declare function isMergeStatus(finalStatus: WorkflowStatusResponse): bool
249
249
  * @internal Exported for the file-first `Handle`; not part of the public API.
250
250
  */
251
251
  export declare function isArchiveStatus(finalStatus: WorkflowStatusResponse): boolean;
252
+ /**
253
+ * True when a terminal status describes a fluent `file(...).watermark(overlay)`
254
+ * — at least one job ref `watermark` and every OTHER job ref is `src_{i}` (the
255
+ * ids the {@link WatermarkedRecipe} lowering assigns: `src_0` base, `src_1`
256
+ * overlay). Lets {@link Handle.wait}/{@link Handle.result} AND
257
+ * {@link WatermarkedRecipe.run} project ONLY the watermark output — filtering
258
+ * the `src_*` passthrough plumbing — even after a `client.workflow(id)` reattach.
259
+ * Mutually exclusive with {@link isFanoutStatus} / {@link isMergeStatus} /
260
+ * {@link isArchiveStatus}.
261
+ *
262
+ * @internal Exported for the file-first `Handle`; not part of the public API.
263
+ */
264
+ export declare function isWatermarkStatus(finalStatus: WorkflowStatusResponse): boolean;
252
265
  /**
253
266
  * The primary file a {@link Recipe} operates on — the "subject" of the
254
267
  * file-first surface. A discriminated union over the ways a caller names an
@@ -355,6 +368,19 @@ export declare class Recipe {
355
368
  * `options` carries any additional per-op watermark options.
356
369
  */
357
370
  textWatermark(text: string, options?: Record<string, unknown>): Recipe;
371
+ /**
372
+ * Composite an image OVERLAY onto this file (a multi-input op). `overlay` is a
373
+ * secondary file-NODE (a {@link Recipe} — e.g. `client.file('logo.png')`),
374
+ * itself optionally processed first. Routes by THIS file's effective media:
375
+ * image base → `image_watermark` (stable), video base → `video_watermark`
376
+ * (beta). Audio/document/animated-GIF/unsupported-subtype/undetectable bases
377
+ * throw locally BEFORE any upload (the planned-op gate). `options` carries the
378
+ * wire watermark options (`anchor`, `opacity`, `margin_x`, `margin_y`,
379
+ * `overlay_width`). Returns a {@link WatermarkedRecipe} (chain post-watermark
380
+ * `compress`/`convert`/`thumbnail`, then `run`/`submit`). Distinct from
381
+ * {@link textWatermark} (single-input text overlay).
382
+ */
383
+ watermark(overlay: Recipe, options?: Record<string, unknown>): WatermarkedRecipe;
358
384
  /**
359
385
  * Lower this recipe to a workflow-create payload against a resolved upload
360
386
  * id. Single-input chain → ONE job, `source: upload(fileId)`, ordered
@@ -380,6 +406,13 @@ export declare class Recipe {
380
406
  * @internal
381
407
  */
382
408
  get recipeSteps(): readonly RecipeStep[];
409
+ /**
410
+ * The primary input this recipe operates on. Read by {@link WatermarkedRecipe}
411
+ * to lift an overlay Recipe's input (for upload + media inference + src-job
412
+ * lowering) without making the ctor field public.
413
+ * @internal
414
+ */
415
+ get recipeInput(): FileInput;
383
416
  /**
384
417
  * Execute the recipe end-to-end: upload the input (when required), create
385
418
  * the workflow, await a terminal state (SSE with poll fallback), then
@@ -433,9 +466,57 @@ export declare class Recipe {
433
466
  private withStep;
434
467
  private lowerStep;
435
468
  private lowerCompressOptions;
469
+ /** Media of the original input (no chain context) — used by the probe gate. */
470
+ private inputMedia;
471
+ /**
472
+ * The media class a `compress` step at `uptoIndex` actually operates on. With no
473
+ * chain context (`uptoIndex` undefined) this is the original input's media. With
474
+ * context, FOLD the preceding `convert` steps: each `convert(output_format)` changes
475
+ * the media the next step sees (56N4chXY / N8eESzQN — a chain like
476
+ * `mp3 -> convert(flac) -> compress` must resolve against flac, not mp3). Reuses the
477
+ * synthetic-filename detection precedent from {@link MergedRecipe} (`merged.<ext>`).
478
+ */
436
479
  private compressMediaHint;
480
+ /**
481
+ * Whether the media a `compress` step at `uptoIndex` operates on is lossless audio.
482
+ * Determined by the most recent preceding `convert` target (`flac`/`wav` -> lossless)
483
+ * when there is one, else by the original input. Lossless is unaffected by the
484
+ * video/ogg guard (ogg is never lossless either way).
485
+ */
437
486
  private compressAudioLossless;
438
487
  }
488
+ /**
489
+ * The single SDK-side source of truth for which `(wire op, base mime)`
490
+ * combinations the file-first `watermark()` verb may emit, and their
491
+ * availability. The generated typed metadata sidecar does NOT carry the
492
+ * supported-mime allowlist (`MimeGroupMetadata` has no `mimes` field and
493
+ * `per_mime_availability` is empty for these ops), so this hand table is the
494
+ * gate's source — PINNED to the generated `availability.json` by a conformance
495
+ * test (mirrors the wire-key-conformance pattern): a contract regen that
496
+ * changes the supported mimes or availability of `image_watermark` /
497
+ * `video_watermark` fails that test. The gate reads ONLY this table.
498
+ * @internal
499
+ */
500
+ export declare const WATERMARK_CAPABILITY: {
501
+ readonly image_watermark: {
502
+ readonly image: {
503
+ readonly mimes: readonly ["image/jpeg", "image/png", "image/webp"];
504
+ readonly availability: "stable";
505
+ };
506
+ readonly image_gif: {
507
+ readonly mimes: readonly ["image/gif"];
508
+ readonly availability: "planned";
509
+ };
510
+ };
511
+ readonly video_watermark: {
512
+ readonly video: {
513
+ readonly mimes: readonly ["video/mp4", "video/webm"];
514
+ readonly availability: "beta";
515
+ };
516
+ };
517
+ };
518
+ /** Wire op types the file-first `watermark()` verb can route to. */
519
+ export type WatermarkWireOp = 'image_watermark' | 'video_watermark';
439
520
  /**
440
521
  * The homogeneous fan-out builder value (FF3a). `client.files([a, b, c])`
441
522
  * returns a `FilesRecipe`; the op-chain methods (`compress`, `convert`,
@@ -776,4 +857,95 @@ export declare class ArchivedRecipe {
776
857
  */
777
858
  private wireArchiveOptions;
778
859
  }
860
+ /**
861
+ * The single-output recipe you're in AFTER `file(base).watermark(overlay, …)`
862
+ * (FF4a). Composites an image OVERLAY onto the base (image_watermark for image
863
+ * bases, video_watermark for video bases — routed at lowering by the base's
864
+ * effective media). A multi-input op: base + overlay each enter via their own
865
+ * `passthrough` source job (`src_0` base, `src_1` overlay; their own preceding
866
+ * steps lower into those jobs), and the `watermark` job consumes them via
867
+ * `job_output` inputs tagged `role: base` / `role: overlay`. Post-watermark
868
+ * `compress`/`convert`/`thumbnail` chain onto the watermark output. Mirrors
869
+ * {@link MergedRecipe}. `textWatermark` is intentionally NOT a post-verb here.
870
+ */
871
+ export declare class WatermarkedRecipe {
872
+ private readonly baseInput;
873
+ private readonly baseSteps;
874
+ private readonly overlay;
875
+ private readonly watermarkOptions;
876
+ private readonly postSteps;
877
+ private readonly presetDefaults?;
878
+ private readonly scopedPresetDefaults?;
879
+ private readonly client?;
880
+ constructor(baseInput: FileInput, baseSteps: readonly RecipeStep[], overlay: Recipe, watermarkOptions: Readonly<Record<string, unknown>>, postSteps?: readonly RecipeStep[], presetDefaults?: PresetDefaults | undefined, scopedPresetDefaults?: PresetDefaults | undefined, client?: GislClient | undefined);
881
+ /** Reduce the watermarked output's size. See {@link Recipe.compress}. */
882
+ compress(optimize?: OptimizeFor, options?: Record<string, unknown>): WatermarkedRecipe;
883
+ /** Change the watermarked output's format. See {@link Recipe.convert}. */
884
+ convert(format: string, options?: Record<string, unknown>): WatermarkedRecipe;
885
+ /** Thumbnail the watermarked output. Omitted dimensions are dropped from the wire options. */
886
+ thumbnail(options?: {
887
+ width?: number;
888
+ height?: number;
889
+ } & Record<string, unknown>): WatermarkedRecipe;
890
+ /**
891
+ * Lower to the watermark DAG: a `src_0` passthrough/base-steps job + a `src_1`
892
+ * passthrough/overlay-steps job + one `watermark` job whose `inputs[]` consume
893
+ * them via `job_output` (role base/overlay) and whose `operations[]` is
894
+ * `[image_watermark|video_watermark, ...post-watermark ops]`. `fileIds` is
895
+ * `[baseId, overlayId]` (upload order). Throws pre-lowering if the base media
896
+ * is undetectable/unsupported (the planned-op gate).
897
+ *
898
+ * @internal Consumed by {@link run}/{@link submit} (after upload) + the parity harness.
899
+ */
900
+ toWorkflowPayload(fileIds: readonly string[], callbackUrl?: string): WorkflowCreatePayload;
901
+ /** The number of post-watermark ops chained so far (introspection / tests). */
902
+ get stepCount(): number;
903
+ /**
904
+ * Execute end-to-end: upload base + overlay, create the watermark workflow,
905
+ * await terminal (SSE with poll fallback), then resolve ONLY the watermark
906
+ * output into a {@link RunResult}. Requires a client bound at construction.
907
+ * Mirrors {@link MergedRecipe.run}.
908
+ */
909
+ run(options?: {
910
+ maxWait?: string | number;
911
+ onProgress?: (event: ProgressEvent) => void;
912
+ signal?: AbortSignal;
913
+ pollIntervalMs?: number;
914
+ probeBeforeCreate?: boolean;
915
+ probeTimeoutMs?: number;
916
+ }): Promise<RunResult>;
917
+ /**
918
+ * Fire-and-forget: upload base + overlay + create the watermark workflow
919
+ * (wiring `webhook` into `callback_url` when given), return a client-bound
920
+ * {@link Handle}. Does NOT wait for terminal status. Mirrors {@link MergedRecipe.submit}.
921
+ */
922
+ submit(webhook?: string, options?: {
923
+ probeBeforeCreate?: boolean;
924
+ probeTimeoutMs?: number;
925
+ }): Promise<Handle>;
926
+ /** Base + overlay inputs, in upload/lowering order (`[base, overlay]`). */
927
+ private inputsInOrder;
928
+ /**
929
+ * Validate the watermark BEFORE any upload: the base must route to a shippable
930
+ * wire op (throws for undetectable/unsupported/planned bases), and the overlay
931
+ * must be an image. Shared by {@link run}/{@link submit}. Mirrors
932
+ * {@link MergedRecipe.validatePreUpload}.
933
+ */
934
+ private validatePreUpload;
935
+ /**
936
+ * Upload base + overlay (verbatim for a pre-uploaded id; uploading a path /
937
+ * blob otherwise) then create ONE watermark workflow. Validates pre-upload.
938
+ * Shared first half of {@link run} + {@link submit}; mirrors
939
+ * {@link MergedRecipe._uploadAllAndCreate}.
940
+ */
941
+ private _uploadAllAndCreate;
942
+ /**
943
+ * Lower the post-watermark chain over a synthetic input whose extension
944
+ * matches the watermark OUTPUT media (image→png, video→mp4) so
945
+ * `compress(optimize)` resolves the correct preset — mirrors
946
+ * {@link MergedRecipe.lowerPostSteps}.
947
+ */
948
+ private lowerPostSteps;
949
+ private withStep;
950
+ }
779
951
  export {};