@giveitsmaller/sdk 0.9.0 → 0.10.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 (42) hide show
  1. package/README.md +8 -0
  2. package/dist/_audit.js +0 -1
  3. package/dist/builder.js +65 -13
  4. package/dist/client.d.ts +38 -2
  5. package/dist/client.js +131 -7
  6. package/dist/credentials.js +4 -2
  7. package/dist/ergonomic/preset_resolver.js +4 -5
  8. package/dist/ergonomic/presets/image_compress.d.ts +1 -9
  9. package/dist/ergonomic/presets/image_compress.js +6 -25
  10. package/dist/ergonomic/presets/index.d.ts +1 -1
  11. package/dist/ergonomic/presets/index.js +1 -1
  12. package/dist/errors.d.ts +59 -1
  13. package/dist/errors.js +51 -0
  14. package/dist/file-first.d.ts +233 -0
  15. package/dist/file-first.js +585 -21
  16. package/dist/generated/sdk_spec/enums.d.ts +0 -11
  17. package/dist/generated/sdk_spec/enums.js +0 -7
  18. package/dist/generated/sdk_spec/errors.d.ts +1 -1
  19. package/dist/generated/sdk_spec/errors.js +26 -0
  20. package/dist/generated/sdk_spec/presets.js +0 -3
  21. package/dist/generated/sdk_spec/version.d.ts +2 -2
  22. package/dist/generated/sdk_spec/version.js +2 -2
  23. package/dist/handle.js +34 -14
  24. package/dist/index.browser.d.ts +1 -0
  25. package/dist/index.browser.js +14 -0
  26. package/dist/index.core.d.ts +35 -0
  27. package/dist/index.core.js +102 -0
  28. package/dist/index.d.ts +1 -32
  29. package/dist/index.js +9 -89
  30. package/dist/lazy-downloader.d.ts +19 -0
  31. package/dist/lazy-downloader.js +19 -0
  32. package/dist/merge.d.ts +11 -0
  33. package/dist/merge.js +144 -45
  34. package/dist/node-fs.browser.d.ts +17 -0
  35. package/dist/node-fs.browser.js +7 -0
  36. package/dist/node-fs.d.ts +14 -0
  37. package/dist/node-fs.js +14 -0
  38. package/dist/sha256.d.ts +20 -0
  39. package/dist/sha256.js +108 -0
  40. package/dist/types.d.ts +54 -2
  41. package/dist/types.js +2 -0
  42. package/package.json +15 -2
package/dist/errors.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { AuthErrorResponse, BalanceExhaustedResponse, FeatureNotAvailableResponse, FeatureTierRestrictedResponse, ProbePendingResponse, TierRestrictionResponse, UploadDurationExceedsTierResponse, UploadSizeExceedsTierResponse, WorkflowExpiredResponse } from '@giveitsmaller/contracts/openapi';
1
+ import type { AuthErrorResponse, AuthRejectionEnvelope, AuthRejectionEnvelopeErrorTypeEnum, BalanceExhaustedResponse, FeatureNotAvailableResponse, FeatureTierRestrictedResponse, ProbePendingResponse, TierRestrictionResponse, UploadDurationExceedsTierResponse, UploadSizeExceedsTierResponse, WorkflowExpiredResponse } from '@giveitsmaller/contracts/openapi';
2
2
  export declare class GislError extends Error {
3
3
  constructor(message: string);
4
4
  }
@@ -14,6 +14,21 @@ export interface GislApiErrorOptions {
14
14
  readonly locale?: string;
15
15
  readonly messageParams?: Record<string, unknown>;
16
16
  readonly payload?: unknown;
17
+ /**
18
+ * The response headers from the HTTP response that produced this error.
19
+ * Keys are LOWERCASED (HTTP header names are case-insensitive per RFC 9110,
20
+ * and `Headers.forEach` yields lowercased keys). Multi-value headers (e.g.
21
+ * `set-cookie`) are collapsed to a single comma-joined string — do NOT rely
22
+ * on this map for cookies.
23
+ */
24
+ readonly responseHeaders?: Record<string, string>;
25
+ /**
26
+ * The resolved language the server reported via the `Content-Language`
27
+ * response header. DISTINCT from `locale`, which is the body-envelope
28
+ * localisation tag (the I26 `ErrorEnvelope.locale` field); `contentLanguage`
29
+ * is the transport-level header the server echoes for content negotiation.
30
+ */
31
+ readonly contentLanguage?: string;
17
32
  }
18
33
  export declare class GislApiError extends GislError {
19
34
  readonly statusCode: number;
@@ -24,6 +39,19 @@ export declare class GislApiError extends GislError {
24
39
  readonly locale?: string;
25
40
  readonly messageParams?: Record<string, unknown>;
26
41
  readonly payload?: unknown;
42
+ /**
43
+ * Response headers from the HTTP response that produced this error, with
44
+ * LOWERCASED keys (RFC 9110 case-insensitive). Multi-value headers such as
45
+ * `set-cookie` are collapsed into a single comma-joined string — don't rely
46
+ * on this map for cookies.
47
+ */
48
+ readonly responseHeaders?: Record<string, string>;
49
+ /**
50
+ * The `Content-Language` response header value (the language the server
51
+ * actually resolved). DISTINCT from `locale`, which is the body-envelope
52
+ * localisation tag.
53
+ */
54
+ readonly contentLanguage?: string;
27
55
  constructor(statusCode: number, errorMessage: string, path?: string, details?: unknown, options?: GislApiErrorOptions);
28
56
  }
29
57
  /**
@@ -101,6 +129,23 @@ export declare class GislAuthError extends GislApiError {
101
129
  readonly payload: AuthErrorResponse;
102
130
  constructor(statusCode: number, errorMessage: string, payload: AuthErrorResponse, path?: string, extra?: Omit<GislApiErrorOptions, 'payload'>);
103
131
  }
132
+ /**
133
+ * 422 Unprocessable Entity — domain rejection on auth side-effect endpoints
134
+ * (register / verify-email / api-keys duplicate-or-invalid; profile PATCH email
135
+ * unchanged). Flat `AuthRejectionEnvelope`, no `details[]`. Mirrors the PHP
136
+ * `Gisl\Sdk\Errors\GislAuthRejectionError`.
137
+ *
138
+ * `payload.errorType` is the auth-422 `oneOf` discriminator
139
+ * (`unprocessable_entity` or `email_same`); `errorType` re-exposes it directly
140
+ * for caller-side narrowing without unwrapping the typed payload. Distinct from
141
+ * `GislValidationError` (the `validation_error` branch of the same `oneOf`,
142
+ * which carries `details[]`).
143
+ */
144
+ export declare class GislAuthRejectionError extends GislApiError {
145
+ readonly payload: AuthRejectionEnvelope;
146
+ readonly errorType: AuthRejectionEnvelopeErrorTypeEnum;
147
+ constructor(statusCode: number, errorMessage: string, payload: AuthRejectionEnvelope, path?: string, extra?: Omit<GislApiErrorOptions, 'payload'>);
148
+ }
104
149
  /**
105
150
  * Discriminates the four upload-too-big shapes the server can return:
106
151
  * - `size_tier` — 422 `upload_size_exceeds_tier` (typed payload present)
@@ -312,6 +357,19 @@ export declare class GislTimeoutError extends GislError {
312
357
  export declare class GislNetworkError extends GislError {
313
358
  constructor(message: string);
314
359
  }
360
+ /**
361
+ * Internal control-flow marker (TDqmkWpX): the SSE event stream closed cleanly
362
+ * WITHOUT a terminal (`workflow_completed`/`failed`/`partially_failed`) event.
363
+ * Raised by {@link _consumeSseToTerminal} so the await-terminal callers can
364
+ * distinguish a benign server-side stream close (→ fall back to polling) from a
365
+ * genuine failure that must propagate (an `onProgress` callback throw, an API
366
+ * error, a caller abort). Mirrors the PHP `SseStreamEndedWithoutTerminal`
367
+ * sealed marker. Not part of the public error contract — never surfaced to a
368
+ * caller (the await-terminal path catches it internally and polls).
369
+ */
370
+ export declare class SseEndedWithoutTerminal extends GislError {
371
+ constructor(message?: string);
372
+ }
315
373
  export declare class GislAbortError extends GislError {
316
374
  constructor(message: string);
317
375
  }
package/dist/errors.js CHANGED
@@ -13,6 +13,19 @@ export class GislApiError extends GislError {
13
13
  locale;
14
14
  messageParams;
15
15
  payload;
16
+ /**
17
+ * Response headers from the HTTP response that produced this error, with
18
+ * LOWERCASED keys (RFC 9110 case-insensitive). Multi-value headers such as
19
+ * `set-cookie` are collapsed into a single comma-joined string — don't rely
20
+ * on this map for cookies.
21
+ */
22
+ responseHeaders;
23
+ /**
24
+ * The `Content-Language` response header value (the language the server
25
+ * actually resolved). DISTINCT from `locale`, which is the body-envelope
26
+ * localisation tag.
27
+ */
28
+ contentLanguage;
16
29
  constructor(statusCode, errorMessage, path, details, options) {
17
30
  const prefix = path
18
31
  ? `API error ${statusCode} at ${path}`
@@ -28,6 +41,8 @@ export class GislApiError extends GislError {
28
41
  this.locale = options.locale;
29
42
  this.messageParams = options.messageParams;
30
43
  this.payload = options.payload;
44
+ this.responseHeaders = options.responseHeaders;
45
+ this.contentLanguage = options.contentLanguage;
31
46
  }
32
47
  }
33
48
  }
@@ -112,6 +127,26 @@ export class GislAuthError extends GislApiError {
112
127
  this.name = 'GislAuthError';
113
128
  }
114
129
  }
130
+ /**
131
+ * 422 Unprocessable Entity — domain rejection on auth side-effect endpoints
132
+ * (register / verify-email / api-keys duplicate-or-invalid; profile PATCH email
133
+ * unchanged). Flat `AuthRejectionEnvelope`, no `details[]`. Mirrors the PHP
134
+ * `Gisl\Sdk\Errors\GislAuthRejectionError`.
135
+ *
136
+ * `payload.errorType` is the auth-422 `oneOf` discriminator
137
+ * (`unprocessable_entity` or `email_same`); `errorType` re-exposes it directly
138
+ * for caller-side narrowing without unwrapping the typed payload. Distinct from
139
+ * `GislValidationError` (the `validation_error` branch of the same `oneOf`,
140
+ * which carries `details[]`).
141
+ */
142
+ export class GislAuthRejectionError extends GislApiError {
143
+ errorType;
144
+ constructor(statusCode, errorMessage, payload, path, extra) {
145
+ super(statusCode, errorMessage, path, undefined, buildOptionsWithPayload(payload, extra));
146
+ this.name = 'GislAuthRejectionError';
147
+ this.errorType = payload.errorType;
148
+ }
149
+ }
115
150
  /**
116
151
  * A single class covering all three "upload exceeds a size/duration cap"
117
152
  * responses (422 size-tier, 422 duration-tier, 413 absolute).
@@ -335,6 +370,22 @@ export class GislNetworkError extends GislError {
335
370
  this.name = 'GislNetworkError';
336
371
  }
337
372
  }
373
+ /**
374
+ * Internal control-flow marker (TDqmkWpX): the SSE event stream closed cleanly
375
+ * WITHOUT a terminal (`workflow_completed`/`failed`/`partially_failed`) event.
376
+ * Raised by {@link _consumeSseToTerminal} so the await-terminal callers can
377
+ * distinguish a benign server-side stream close (→ fall back to polling) from a
378
+ * genuine failure that must propagate (an `onProgress` callback throw, an API
379
+ * error, a caller abort). Mirrors the PHP `SseStreamEndedWithoutTerminal`
380
+ * sealed marker. Not part of the public error contract — never surfaced to a
381
+ * caller (the await-terminal path catches it internally and polls).
382
+ */
383
+ export class SseEndedWithoutTerminal extends GislError {
384
+ constructor(message = 'SSE stream ended without a terminal event') {
385
+ super(message);
386
+ this.name = 'SseEndedWithoutTerminal';
387
+ }
388
+ }
338
389
  export class GislAbortError extends GislError {
339
390
  constructor(message) {
340
391
  super(message);
@@ -15,6 +15,7 @@ import type { OperationDownload, WorkflowStatusResponse } from '@giveitsmaller/c
15
15
  import { OptimizeFor } from './generated/sdk_spec/enums.js';
16
16
  import type { PresetDefaults } from './ergonomic/presets/index.js';
17
17
  import type { WorkflowCreatePayload } from './types.js';
18
+ import type { MergeOptions } from './merge.js';
18
19
  import { Handle } from './handle.js';
19
20
  /**
20
21
  * Streams a single output URL to a local path. The seam between the
@@ -224,6 +225,30 @@ export declare function projectMultiJobToRunResult(workflowId: string, finalStat
224
225
  * @internal Exported for the file-first `Handle`; not part of the public API.
225
226
  */
226
227
  export declare function isFanoutStatus(finalStatus: WorkflowStatusResponse): boolean;
228
+ /**
229
+ * True when a terminal status describes a fluent `files([...]).merge(...)`
230
+ * combine — at least one job ref `merge` and every OTHER job ref is `src_{i}`
231
+ * (the ids the {@link MergedRecipe} lowering assigns). The data-driven seam that
232
+ * lets {@link Handle.wait}/{@link Handle.result} project ONLY the merged output
233
+ * — filtering the `src_*` passthrough plumbing — even after a
234
+ * `client.workflow(id)` reattach (no construction-time marker), matching
235
+ * {@link MergedRecipe.run}'s `ref === 'merge'` filter. Mutually exclusive with
236
+ * {@link isFanoutStatus} (a fan-out's refs are all `file-{i}`).
237
+ *
238
+ * @internal Exported for the file-first `Handle`; not part of the public API.
239
+ */
240
+ export declare function isMergeStatus(finalStatus: WorkflowStatusResponse): boolean;
241
+ /**
242
+ * True when a terminal status describes a fluent `files([...]).archive(...)`
243
+ * bundle — at least one job ref `archive` and every OTHER job ref is `src_{i}`
244
+ * (the ids the {@link ArchivedRecipe} lowering assigns). Lets
245
+ * {@link Handle.wait}/{@link Handle.result} project ONLY the archive output —
246
+ * filtering the `src_*` passthrough plumbing — even after a `client.workflow(id)`
247
+ * reattach. Mutually exclusive with {@link isFanoutStatus} / {@link isMergeStatus}.
248
+ *
249
+ * @internal Exported for the file-first `Handle`; not part of the public API.
250
+ */
251
+ export declare function isArchiveStatus(finalStatus: WorkflowStatusResponse): boolean;
227
252
  /**
228
253
  * The primary file a {@link Recipe} operates on — the "subject" of the
229
254
  * file-first surface. A discriminated union over the ways a caller names an
@@ -251,6 +276,19 @@ export type FileInput = {
251
276
  export declare const fileInput: {
252
277
  readonly path: (path: string) => FileInput;
253
278
  readonly blob: (blob: Blob) => FileInput;
279
+ /**
280
+ * Reference an already-uploaded file by its upload id, instead of
281
+ * re-uploading bytes.
282
+ *
283
+ * Auth-ownership: an upload created by an **authenticated** caller is owned
284
+ * by that caller. If you reuse the id from a client configured with a
285
+ * *different* auth context (a different `apiKey` / session), workflow-create
286
+ * returns `404 upload_not_found` — the server enforces ownership (api
287
+ * PqpD9ySv). Reference an upload id only under the SAME auth that created it.
288
+ * The normal upload-then-create-in-one-client flow is consistent by
289
+ * construction (the same `Authorization` rides every request). Ownerless
290
+ * (anonymous-intake) uploads are unaffected.
291
+ */
254
292
  readonly uploadId: (fileId: string) => FileInput;
255
293
  };
256
294
  /** One step in a {@link Recipe}'s chain — an op kind + captured ergonomic args. */
@@ -427,6 +465,28 @@ export declare class FilesRecipe {
427
465
  }): FilesRecipe;
428
466
  /** Apply the same text watermark to every input. */
429
467
  textWatermark(text: string): FilesRecipe;
468
+ /**
469
+ * Combine the inputs into ONE output (N→1), in array order (FF3b). Returns a
470
+ * single-output {@link MergedRecipe} you chain further ops on
471
+ * (`files([...]).merge().compress()`). Reuses the operation-first
472
+ * {@link MergeOptions} for the merge-level options, so the wire shape matches
473
+ * `client.merge([...], options)`.
474
+ *
475
+ * `merge()` must be the FIRST op on `files([...])` — per-file ops before a
476
+ * combine (compress-each-then-merge) are a separate follow-up, rejected here
477
+ * with `GislConfigError` reason `pre_merge_ops_unsupported`.
478
+ */
479
+ merge(options?: MergeOptions): MergedRecipe;
480
+ /**
481
+ * Bundle the inputs into ONE archive (N→1, zip / tar.gz) — media-agnostic,
482
+ * inputs may mix types. Returns a terminal {@link ArchivedRecipe} (a zip is
483
+ * the final artefact — no post-bundle chain). `format` / `folderStructure` are
484
+ * optional; the server defaults to zip + flat.
485
+ *
486
+ * `archive()` must be the FIRST op on `files([...])` → `GislConfigError` reason
487
+ * `pre_archive_ops_unsupported` otherwise.
488
+ */
489
+ archive(options?: ArchiveRecipeOptions): ArchivedRecipe;
430
490
  /** The number of inputs in this fan-out (introspection / tests). */
431
491
  get inputCount(): number;
432
492
  /** The number of operations chained so far (introspection / tests). */
@@ -500,4 +560,177 @@ export declare class FilesRecipe {
500
560
  private baseRecipe;
501
561
  private withStep;
502
562
  }
563
+ /**
564
+ * The single-output recipe you're in AFTER a fluent `files([...]).merge(...)`
565
+ * (FF3b). Merge collapses the N inputs into ONE output, so the per-file ops
566
+ * ({@link FilesRecipe.compress} etc.) no longer apply — instead this exposes the
567
+ * SAME chain ops as the single-file {@link Recipe}, applied to the merged
568
+ * result. `files([...]).merge().compress()` is the flagship case (example 14).
569
+ *
570
+ * **Lowering (one workflow):** each input is uploaded once and wrapped in its
571
+ * own single-input `passthrough` source job (`src_N`); the `merge` job consumes
572
+ * those via `job_output` inputs (array order = play order) and carries the merge
573
+ * op FIRST in its `operations[]`, followed by any post-combine ops (compress /
574
+ * convert / thumbnail) so they run on the merged output in the same job. The
575
+ * merge-level wire options reuse {@link wireMergeOptions} so a fluent merge
576
+ * lowers identically to the operation-first `client.merge()`.
577
+ *
578
+ * Immutable / clone-on-write like {@link Recipe} / {@link FilesRecipe}. Mirrors
579
+ * the PHP `MergedRecipe` in `packages/php/src/FileFirst/MergedRecipe.php`.
580
+ */
581
+ export declare class MergedRecipe {
582
+ private readonly inputs;
583
+ private readonly mergeOptions;
584
+ private readonly postSteps;
585
+ private readonly presetDefaults?;
586
+ private readonly scopedPresetDefaults?;
587
+ private readonly client?;
588
+ constructor(inputs: readonly FileInput[], mergeOptions: MergeOptions, postSteps?: readonly RecipeStep[], presetDefaults?: PresetDefaults | undefined, scopedPresetDefaults?: PresetDefaults | undefined, client?: GislClient | undefined);
589
+ /** Reduce the merged output's size. See {@link Recipe.compress}. */
590
+ compress(optimize?: OptimizeFor): MergedRecipe;
591
+ /** Change the merged output's format. See {@link Recipe.convert}. */
592
+ convert(format: string): MergedRecipe;
593
+ /** Thumbnail the merged output. Omitted dimensions are dropped from the wire options. */
594
+ thumbnail(options?: {
595
+ width?: number;
596
+ height?: number;
597
+ }): MergedRecipe;
598
+ /**
599
+ * Lower to the merge DAG: one `passthrough` source job per input + one
600
+ * `merge` job whose `operations[]` is `[merge, ...post-combine ops]`. The
601
+ * merge job's `inputs[]` consume the source jobs via `job_output` in input
602
+ * (play) order.
603
+ *
604
+ * @internal Consumed by {@link run} (after uploading all inputs), {@link submit}
605
+ * (with a webhook), and the cross-language parity harness (with fixed ids).
606
+ */
607
+ toWorkflowPayload(fileIds: readonly string[], callbackUrl?: string): WorkflowCreatePayload;
608
+ /** The number of inputs being combined (introspection / tests). */
609
+ get inputCount(): number;
610
+ /** The number of post-combine ops chained so far (introspection / tests). */
611
+ get stepCount(): number;
612
+ /**
613
+ * Execute end-to-end: upload every input, create the merge workflow, await a
614
+ * terminal state (SSE with poll fallback), then resolve ONLY the merged output
615
+ * into a {@link RunResult}. Throws {@link GislTimeoutError} on `maxWait`.
616
+ *
617
+ * Requires a client bound at construction time — `gisl().files(...).merge(...)`
618
+ * wires it; a directly-constructed `MergedRecipe` throws {@link GislConfigError}.
619
+ * Mirrors the single-file {@link Recipe.run}.
620
+ */
621
+ run(options?: {
622
+ maxWait?: string | number;
623
+ onProgress?: (event: ProgressEvent) => void;
624
+ signal?: AbortSignal;
625
+ pollIntervalMs?: number;
626
+ }): Promise<RunResult>;
627
+ /**
628
+ * Fire-and-forget: upload + create the merge workflow (wiring `webhook` into
629
+ * `callback_url` when given), return a client-bound {@link Handle}. Does NOT
630
+ * wait for terminal status. Mirrors {@link Recipe.submit}.
631
+ */
632
+ submit(webhook?: string): Promise<Handle>;
633
+ /**
634
+ * Upload every input (verbatim for a pre-uploaded id; uploading a path / blob
635
+ * otherwise, emitting `{phase:'upload'}` progress) then create ONE merge
636
+ * workflow. Rejects fewer than 2 inputs BEFORE any upload fires. Shared first
637
+ * half of {@link run} + {@link submit}.
638
+ */
639
+ private _uploadAllAndCreate;
640
+ /**
641
+ * Reject an invalid combine BEFORE any upload fires — mirrors the operation-
642
+ * first `MergeBuilder.planSequence()` bounds so a typo'd merge costs no
643
+ * bandwidth: 2–10 inputs (merge schema `min/max_inputs`), and an image merge
644
+ * must carry an explicit `output_type` (the server rejects image merges
645
+ * without one). Shared by {@link run} + {@link submit} via
646
+ * {@link _uploadAllAndCreate}.
647
+ */
648
+ private validatePreUpload;
649
+ /**
650
+ * Lower the post-combine chain by composing a single-file {@link Recipe} over a
651
+ * synthetic input whose extension matches the merged OUTPUT media — so
652
+ * `compress(optimize)` resolves the correct preset for the merged result (it
653
+ * needs a media hint, which a merge output carries no filename for). Reuses
654
+ * Recipe's `lowerStep` rather than duplicating it.
655
+ */
656
+ private lowerPostSteps;
657
+ /**
658
+ * The merged-output media. Honours an explicit {@link MergeOptions.mediaKind};
659
+ * otherwise infers from the first PATH input's extension (mirrors
660
+ * {@link MergeBuilder}); defaults to video.
661
+ */
662
+ private inferMediaKind;
663
+ private outputExtensionFor;
664
+ private withStep;
665
+ }
666
+ /**
667
+ * Options for a fluent `files([...]).archive(...)` bundle. Both fields are
668
+ * optional — the server defaults `format` to `zip` and `folderStructure` to
669
+ * `flat` (archive op schema). Mirrors the PHP `ArchivedRecipe` ctor params.
670
+ */
671
+ export interface ArchiveRecipeOptions {
672
+ /** Archive container format. */
673
+ readonly format?: 'zip' | 'tar.gz';
674
+ /** `flat` = all files at the top level; `by_job` = a subfolder per source. */
675
+ readonly folderStructure?: 'flat' | 'by_job';
676
+ }
677
+ /**
678
+ * The single-output recipe you're in AFTER a fluent `files([...]).archive(...)`
679
+ * (FF3b). Archive bundles the N inputs into ONE downloadable archive (zip /
680
+ * tar.gz) — media-agnostic, inputs may mix types. Unlike {@link MergedRecipe},
681
+ * archive is TERMINAL: a zip is the final artefact, so there is no post-bundle
682
+ * chain — this exposes only `run()` / `submit()`.
683
+ *
684
+ * **Lowering (one workflow):** each input is uploaded once and wrapped in its
685
+ * own single-input `passthrough` source job (`src_N`); the `archive` job
686
+ * consumes those via `job_output` inputs (array order = entry order) and carries
687
+ * the single `archive` op. The archive job's id is `archive`, so {@link RunResult}
688
+ * projects ONLY its output. Mirrors the PHP `ArchivedRecipe`.
689
+ */
690
+ export declare class ArchivedRecipe {
691
+ private readonly inputs;
692
+ private readonly options;
693
+ private readonly client?;
694
+ constructor(inputs: readonly FileInput[], options?: ArchiveRecipeOptions, client?: GislClient | undefined);
695
+ /** The number of inputs being bundled (introspection / tests). */
696
+ get inputCount(): number;
697
+ /**
698
+ * Lower to the archive DAG: one `passthrough` source job per input + one
699
+ * `archive` job consuming them via `job_output`.
700
+ *
701
+ * @internal Consumed by {@link run} / {@link submit} (after uploading) and the
702
+ * cross-language parity harness (with fixed ids).
703
+ */
704
+ toWorkflowPayload(fileIds: readonly string[], callbackUrl?: string): WorkflowCreatePayload;
705
+ /**
706
+ * Execute end-to-end: upload every input, create the archive workflow, await a
707
+ * terminal state (SSE with poll fallback), then resolve ONLY the archive output
708
+ * into a {@link RunResult}. Throws {@link GislTimeoutError} on `maxWait`.
709
+ * Requires a client bound via `gisl().files(...).archive(...)`.
710
+ */
711
+ run(options?: {
712
+ maxWait?: string | number;
713
+ onProgress?: (event: ProgressEvent) => void;
714
+ signal?: AbortSignal;
715
+ pollIntervalMs?: number;
716
+ }): Promise<RunResult>;
717
+ /**
718
+ * Fire-and-forget: upload + create the archive workflow (wiring `webhook` into
719
+ * `callback_url` when given), return a client-bound {@link Handle}. Mirrors
720
+ * {@link MergedRecipe.submit}.
721
+ */
722
+ submit(webhook?: string): Promise<Handle>;
723
+ private _uploadAllAndCreate;
724
+ /**
725
+ * Reject an invalid bundle BEFORE any upload fires — the archive schema allows
726
+ * 2–50 inputs (`min/max_inputs`), so a typo'd bundle costs no bandwidth.
727
+ */
728
+ private validatePreUpload;
729
+ /**
730
+ * Project the archive options into the wire shape. Both fields are optional
731
+ * (the server defaults `format` to zip and `folder_structure` to flat), so an
732
+ * omitted option is dropped rather than sent.
733
+ */
734
+ private wireArchiveOptions;
735
+ }
503
736
  export {};