@giveitsmaller/sdk 0.6.0 → 0.7.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 (48) hide show
  1. package/dist/_audit.js +60 -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 +28 -2
  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 +147 -1
  29. package/dist/errors.js +161 -0
  30. package/dist/generated/sdk_spec/enums.d.ts +195 -0
  31. package/dist/generated/sdk_spec/enums.js +127 -0
  32. package/dist/generated/sdk_spec/errors.d.ts +16 -0
  33. package/dist/generated/sdk_spec/errors.js +473 -0
  34. package/dist/generated/sdk_spec/index.d.ts +4 -0
  35. package/dist/generated/sdk_spec/index.js +7 -0
  36. package/dist/generated/sdk_spec/presets.d.ts +6 -0
  37. package/dist/generated/sdk_spec/presets.js +157 -0
  38. package/dist/generated/sdk_spec/version.d.ts +3 -0
  39. package/dist/generated/sdk_spec/version.js +6 -0
  40. package/dist/gisl.d.ts +112 -0
  41. package/dist/gisl.js +266 -0
  42. package/dist/index.d.ts +15 -5
  43. package/dist/index.js +32 -4
  44. package/dist/merge.d.ts +142 -0
  45. package/dist/merge.js +411 -0
  46. package/dist/types.d.ts +12 -14
  47. package/dist/types.js +18 -0
  48. package/package.json +2 -2
@@ -0,0 +1,216 @@
1
+ // T4a — PresetDefaults immutable builder + presetDefaults() factory.
2
+ //
3
+ // Implements the user-facing path for layered preset configuration:
4
+ //
5
+ // const defaults = presetDefaults()
6
+ // .imageCompress(OptimizeFor.Size, { quality: 75 })
7
+ // .videoCompress(OptimizeFor.Quality);
8
+ //
9
+ // const client = await gisl.create({ presetDefaults: defaults });
10
+ //
11
+ // `T4a` ships only the typed slot and the builder semantics — the
12
+ // resolver (T4b) reads `defaults.cellFor(media, op, level)` and merges
13
+ // shipped defaults + this user delta + per-call overrides at workflow-
14
+ // create time.
15
+ //
16
+ // **Semantics**
17
+ // - `cellFor(media, op, level)` returns the USER-SUPPLIED DELTA stored
18
+ // by the matching `.imageCompress(level, input)` call, or `undefined`
19
+ // if no such delta was registered. It does NOT return shipped defaults
20
+ // — those live on the leaf class (`*.shippedDefaultsFor(level)`).
21
+ // - Per-cell methods are immutable: each returns a fresh `PresetDefaults`
22
+ // carrying the original entries plus the new (cell, level) registration.
23
+ // Calling the same method twice on the same instance yields two
24
+ // distinct objects.
25
+ // - Calling `.imageCompress(level)` with no input registers an empty
26
+ // delta — the resolver will see "this cell asked for `level` with no
27
+ // overrides" and apply shipped defaults verbatim.
28
+ import { ImageCompressPresetOptions, } from './image_compress.js';
29
+ import { AudioCompressPresetOptions, } from './audio_compress.js';
30
+ import { VideoCompressPresetOptions, } from './video_compress.js';
31
+ import { DocumentPdfCompressPresetOptions, } from './document_pdf_compress.js';
32
+ import { DocumentOfficeCompressPresetOptions, } from './document_office_compress.js';
33
+ import { DocumentOdfCompressPresetOptions, } from './document_odf_compress.js';
34
+ import { DocumentEpubCompressPresetOptions, } from './document_epub_compress.js';
35
+ // Re-export everything callers need from this module's root.
36
+ export { ImageCompressPresetOptions, } from './image_compress.js';
37
+ export { AudioCompressPresetOptions, } from './audio_compress.js';
38
+ export { VideoCompressPresetOptions, } from './video_compress.js';
39
+ export { DocumentPdfCompressPresetOptions, } from './document_pdf_compress.js';
40
+ export { DocumentOfficeCompressPresetOptions, } from './document_office_compress.js';
41
+ export { DocumentOdfCompressPresetOptions, } from './document_odf_compress.js';
42
+ export { DocumentEpubCompressPresetOptions, } from './document_epub_compress.js';
43
+ // Re-export ergonomic enums for callers (single canonical path).
44
+ export { OptimizeFor, ImageMode, ImageFit, ImageMetadataPolicy, IccProfilePolicy, ImageFormat, VideoCodec, VideoPreset, VideoFit, AudioBitrate, AudioCodec, AudioSampleRate, PdfProfile, PdfColorspace, } from '../../generated/sdk_spec/enums.js';
45
+ function cellKeyOf(media, op) {
46
+ return `${media}_${op}`;
47
+ }
48
+ /**
49
+ * Per-cell field-merge: parent fields ⊕ child fields where defined.
50
+ * Re-construct the leaf DTO via the matching `<LeafClass>.from(merged)`
51
+ * call so the result is a freshly-frozen `*PresetOptions` instance —
52
+ * NOT a mutated reference into either input. Used by
53
+ * {@link PresetDefaults.merge} when both parent and child registered
54
+ * the same `(cellKey, level)` tuple.
55
+ *
56
+ * `definedFieldsOf` filters undefined values out of each instance
57
+ * BEFORE the merge: with TS `useDefineForClassFields` (the ES2022
58
+ * default), `readonly mode?: ImageMode` declarations initialise the
59
+ * field as an enumerable own property with value `undefined` BEFORE
60
+ * the ctor body runs. A naive `Object.assign({}, parent, child)`
61
+ * therefore lets child's `undefined` overwrite parent's defined value
62
+ * — caught by CI on PR #125 first run. Filter-then-spread restores
63
+ * the documented merge-not-replace semantics.
64
+ *
65
+ * @internal
66
+ */
67
+ function definedFieldsOf(opts) {
68
+ const out = {};
69
+ for (const key of Object.keys(opts)) {
70
+ const value = opts[key];
71
+ if (value !== undefined)
72
+ out[key] = value;
73
+ }
74
+ return out;
75
+ }
76
+ function mergePresetOptions(cellKey, parentOpts, childOpts) {
77
+ // Child fields win on overlap; parent fills the remaining gaps.
78
+ // `definedFieldsOf` strips undefined-valued slots that TS class
79
+ // field declarations create even when the ctor body skipped the
80
+ // assignment (see helper docblock).
81
+ const mergedFields = {
82
+ ...definedFieldsOf(parentOpts),
83
+ ...definedFieldsOf(childOpts),
84
+ };
85
+ switch (cellKey) {
86
+ case 'image_compress':
87
+ return ImageCompressPresetOptions.from(mergedFields);
88
+ case 'audio_compress':
89
+ return AudioCompressPresetOptions.from(mergedFields);
90
+ case 'video_compress':
91
+ return VideoCompressPresetOptions.from(mergedFields);
92
+ case 'document_pdf_compress':
93
+ return DocumentPdfCompressPresetOptions.from(mergedFields);
94
+ case 'document_office_compress':
95
+ return DocumentOfficeCompressPresetOptions.from(mergedFields);
96
+ case 'document_odf_compress':
97
+ return DocumentOdfCompressPresetOptions.from(mergedFields);
98
+ case 'document_epub_compress':
99
+ return DocumentEpubCompressPresetOptions.from(mergedFields);
100
+ }
101
+ }
102
+ /**
103
+ * Append `(level, options)` into a fresh map under `cellKey`, returning
104
+ * a new outer map. Both layers stay immutable — callers' references to
105
+ * the previous PresetDefaults remain unchanged.
106
+ */
107
+ function withCellEntry(prev, cellKey, level, options) {
108
+ const next = new Map(prev);
109
+ const prevEntries = prev.get(cellKey);
110
+ const nextEntries = new Map(prevEntries ?? []);
111
+ nextEntries.set(level, options);
112
+ next.set(cellKey, nextEntries);
113
+ return next;
114
+ }
115
+ export class PresetDefaults {
116
+ cells;
117
+ constructor(cells) {
118
+ this.cells = cells;
119
+ Object.freeze(this);
120
+ }
121
+ /** Empty builder — entry point for `presetDefaults()`. @internal */
122
+ static _empty() {
123
+ return new PresetDefaults(new Map());
124
+ }
125
+ /**
126
+ * Deep-merge two {@link PresetDefaults} into a new instance (T4c —
127
+ * `ULAlOP6j`). Used by `withPresetDefaults` to stack scoped derives:
128
+ * `client.withPresetDefaults(a).withPresetDefaults(b)` produces a
129
+ * scoped layer equivalent to `merge(a, b)` — `b`'s per-cell fields
130
+ * override `a`'s where defined; `a`'s fields fill gaps.
131
+ *
132
+ * Per-cell semantics (codex r2 #5 — scalar-leaf merge):
133
+ * - If a `(cellKey, level)` entry is present in EITHER only, take it
134
+ * verbatim.
135
+ * - If present in both, merge the per-cell `*Input` shapes via
136
+ * `Object.assign({}, parentInput, childInput)` and re-construct
137
+ * the leaf DTO. Every cell-DTO field is a scalar (primitive,
138
+ * enum-string, or `string | number` for `targetSize`) — shallow
139
+ * merge gives the correct field-wise override.
140
+ *
141
+ * Parent and child instances are unaffected.
142
+ */
143
+ static merge(parent, child) {
144
+ const merged = new Map();
145
+ // Seed with a clone of parent's entries (one inner Map per cellKey
146
+ // so child writes don't bleed back into parent's frozen structure).
147
+ for (const [cellKey, entries] of parent.cells) {
148
+ merged.set(cellKey, new Map(entries));
149
+ }
150
+ // Overlay child entries. Same (cellKey, level) → field-merge;
151
+ // child-only → take verbatim.
152
+ for (const [cellKey, childEntries] of child.cells) {
153
+ const target = merged.get(cellKey);
154
+ if (target === undefined) {
155
+ merged.set(cellKey, new Map(childEntries));
156
+ continue;
157
+ }
158
+ for (const [level, childOpts] of childEntries) {
159
+ const parentOpts = target.get(level);
160
+ if (parentOpts === undefined) {
161
+ target.set(level, childOpts);
162
+ continue;
163
+ }
164
+ target.set(level, mergePresetOptions(cellKey, parentOpts, childOpts));
165
+ }
166
+ }
167
+ return new PresetDefaults(merged);
168
+ }
169
+ /** Register a (level, delta) on the image-compress cell. Immutable. */
170
+ imageCompress(level, input = {}) {
171
+ return new PresetDefaults(withCellEntry(this.cells, 'image_compress', level, ImageCompressPresetOptions.from(input)));
172
+ }
173
+ /** Register a (level, delta) on the audio-compress cell. Immutable. */
174
+ audioCompress(level, input = {}) {
175
+ return new PresetDefaults(withCellEntry(this.cells, 'audio_compress', level, AudioCompressPresetOptions.from(input)));
176
+ }
177
+ /** Register a (level, delta) on the video-compress cell. Immutable. */
178
+ videoCompress(level, input = {}) {
179
+ return new PresetDefaults(withCellEntry(this.cells, 'video_compress', level, VideoCompressPresetOptions.from(input)));
180
+ }
181
+ /** Register a (level, delta) on the document-pdf-compress cell. Immutable. */
182
+ pdfCompress(level, input = {}) {
183
+ return new PresetDefaults(withCellEntry(this.cells, 'document_pdf_compress', level, DocumentPdfCompressPresetOptions.from(input)));
184
+ }
185
+ /** Register a (level, delta) on the document-office-compress cell. Immutable. */
186
+ officeCompress(level, input = {}) {
187
+ return new PresetDefaults(withCellEntry(this.cells, 'document_office_compress', level, DocumentOfficeCompressPresetOptions.from(input)));
188
+ }
189
+ /** Register a (level, delta) on the document-odf-compress cell. Immutable. */
190
+ odfCompress(level, input = {}) {
191
+ return new PresetDefaults(withCellEntry(this.cells, 'document_odf_compress', level, DocumentOdfCompressPresetOptions.from(input)));
192
+ }
193
+ /** Register a (level, delta) on the document-epub-compress cell. Immutable. */
194
+ epubCompress(level, input = {}) {
195
+ return new PresetDefaults(withCellEntry(this.cells, 'document_epub_compress', level, DocumentEpubCompressPresetOptions.from(input)));
196
+ }
197
+ /**
198
+ * Return the user-supplied delta registered for `(media, op, level)`,
199
+ * or `undefined` if none was registered.
200
+ *
201
+ * @internal — consumed by the T4b resolver. Not part of the public
202
+ * surface; the resolver imports it via the type-only re-export.
203
+ */
204
+ cellFor(media, op, level) {
205
+ const entries = this.cells.get(cellKeyOf(media, op));
206
+ return entries?.get(level);
207
+ }
208
+ }
209
+ /**
210
+ * Construct an empty {@link PresetDefaults} builder. Chain per-cell
211
+ * methods to register layered defaults; the resolver in T4b consumes
212
+ * the result via `cellFor(...)`.
213
+ */
214
+ export function presetDefaults() {
215
+ return PresetDefaults._empty();
216
+ }
@@ -0,0 +1,30 @@
1
+ import { VideoCodec, VideoPreset, VideoFit, AudioCodec, AudioBitrate, OptimizeFor } from '../../generated/sdk_spec/enums.js';
2
+ export interface VideoCompressPresetOptionsInput {
3
+ readonly codec?: VideoCodec;
4
+ readonly targetSize?: string | number;
5
+ readonly crf?: number;
6
+ readonly preset?: VideoPreset;
7
+ readonly width?: number;
8
+ readonly height?: number;
9
+ readonly fit?: VideoFit;
10
+ readonly fps?: number;
11
+ readonly faststart?: boolean;
12
+ readonly audioCodec?: AudioCodec;
13
+ readonly audioBitrate?: AudioBitrate;
14
+ }
15
+ export declare class VideoCompressPresetOptions {
16
+ readonly codec?: VideoCodec;
17
+ readonly targetSize?: string | number;
18
+ readonly crf?: number;
19
+ readonly preset?: VideoPreset;
20
+ readonly width?: number;
21
+ readonly height?: number;
22
+ readonly fit?: VideoFit;
23
+ readonly fps?: number;
24
+ readonly faststart?: boolean;
25
+ readonly audioCodec?: AudioCodec;
26
+ readonly audioBitrate?: AudioBitrate;
27
+ private constructor();
28
+ static from(input: VideoCompressPresetOptionsInput): VideoCompressPresetOptions;
29
+ static shippedDefaultsFor(level: OptimizeFor): VideoCompressPresetOptions;
30
+ }
@@ -0,0 +1,83 @@
1
+ // T4a — VideoCompressPresetOptions leaf DTO.
2
+ //
3
+ // Field set per ticket VhIj4S7T: video = 11 fields
4
+ // (codec, targetSize, crf, preset, width, height, fit, fps, faststart,
5
+ // audioCodec, audioBitrate).
6
+ // Deliberately excluded: encodingMode + targetSizeBytes (raw wire —
7
+ // replaced by ergonomic `targetSize: string|number` which the SDK derives
8
+ // at resolve time in T4b), trim_start/trim_end (per-call content selection).
9
+ //
10
+ // `targetSize` accepts `string` (e.g. "50MB") or `number` (bytes). The
11
+ // resolver in T4b converts to the wire `target_size_bytes` and sets
12
+ // `encoding_mode: target_size` accordingly.
13
+ import { shippedDefaultsFor as f3ShippedDefaultsFor } from '../../generated/sdk_spec/presets.js';
14
+ import { translateEnum } from './_translate.js';
15
+ export class VideoCompressPresetOptions {
16
+ codec;
17
+ targetSize;
18
+ crf;
19
+ preset;
20
+ width;
21
+ height;
22
+ fit;
23
+ fps;
24
+ faststart;
25
+ audioCodec;
26
+ audioBitrate;
27
+ constructor(input) {
28
+ if (input.codec !== undefined)
29
+ this.codec = input.codec;
30
+ if (input.targetSize !== undefined)
31
+ this.targetSize = input.targetSize;
32
+ if (input.crf !== undefined)
33
+ this.crf = input.crf;
34
+ if (input.preset !== undefined)
35
+ this.preset = input.preset;
36
+ if (input.width !== undefined)
37
+ this.width = input.width;
38
+ if (input.height !== undefined)
39
+ this.height = input.height;
40
+ if (input.fit !== undefined)
41
+ this.fit = input.fit;
42
+ if (input.fps !== undefined)
43
+ this.fps = input.fps;
44
+ if (input.faststart !== undefined)
45
+ this.faststart = input.faststart;
46
+ if (input.audioCodec !== undefined)
47
+ this.audioCodec = input.audioCodec;
48
+ if (input.audioBitrate !== undefined)
49
+ this.audioBitrate = input.audioBitrate;
50
+ Object.freeze(this);
51
+ }
52
+ static from(input) {
53
+ return new VideoCompressPresetOptions(input);
54
+ }
55
+ static shippedDefaultsFor(level) {
56
+ const cell = f3ShippedDefaultsFor('video_compress', level);
57
+ const input = {};
58
+ const mut = input;
59
+ if ('codec' in cell)
60
+ mut.codec = translateEnum('VideoCodec', cell.codec);
61
+ if ('targetSize' in cell)
62
+ mut.targetSize = cell.targetSize;
63
+ if ('crf' in cell)
64
+ mut.crf = cell.crf;
65
+ if ('preset' in cell)
66
+ mut.preset = translateEnum('VideoPreset', cell.preset);
67
+ if ('width' in cell)
68
+ mut.width = cell.width;
69
+ if ('height' in cell)
70
+ mut.height = cell.height;
71
+ if ('fit' in cell)
72
+ mut.fit = translateEnum('VideoFit', cell.fit);
73
+ if ('fps' in cell)
74
+ mut.fps = cell.fps;
75
+ if ('faststart' in cell)
76
+ mut.faststart = cell.faststart;
77
+ if ('audioCodec' in cell)
78
+ mut.audioCodec = translateEnum('AudioCodec', cell.audioCodec);
79
+ if ('audioBitrate' in cell)
80
+ mut.audioBitrate = translateEnum('AudioBitrate', cell.audioBitrate);
81
+ return new VideoCompressPresetOptions(input);
82
+ }
83
+ }
package/dist/errors.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { AuthErrorResponse, BalanceExhaustedResponse, FeatureNotAvailableResponse, FeatureTierRestrictedResponse, TierRestrictionResponse, UploadDurationExceedsTierResponse, UploadSizeExceedsTierResponse, WorkflowExpiredResponse } from '@giveitsmaller/contracts/openapi';
1
+ import type { AuthErrorResponse, 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
  }
@@ -62,6 +62,37 @@ export declare class GislFeatureNotAvailableError extends GislApiError {
62
62
  readonly payload: FeatureNotAvailableResponse;
63
63
  constructor(statusCode: number, errorMessage: string, payload: FeatureNotAvailableResponse, path?: string, extra?: Omit<GislApiErrorOptions, 'payload'>);
64
64
  }
65
+ /**
66
+ * 422 response on `POST /api/workflows` when a job references an upload
67
+ * whose server-side probe hasn't completed at workflow-create time. The
68
+ * server rejects rather than silently routing as `short_form` (which
69
+ * hard-fails long video clips).
70
+ *
71
+ * **Recovery contract** (per contracts ProbePendingResponse docblock):
72
+ * poll `POST /api/uploads/{id}/probe` for the pending upload until
73
+ * `probe_status` is terminal (`ok` → re-`POST /api/workflows` the same
74
+ * request; `corrupt` / `unsupported_codec` → surface the probe error).
75
+ * The `Retry-After` response header (when present) suggests a delay
76
+ * in seconds before the next poll/retry.
77
+ *
78
+ * `payload.jobRef` identifies which job in the multi-job request triggered
79
+ * the probe-pending rejection.
80
+ *
81
+ * @example
82
+ * try {
83
+ * await client.createWorkflow({ jobs });
84
+ * } catch (e) {
85
+ * if (e instanceof GislProbePendingError) {
86
+ * await waitForProbe(e.payload.jobRef);
87
+ * // retry...
88
+ * }
89
+ * throw e;
90
+ * }
91
+ */
92
+ export declare class GislProbePendingError extends GislApiError {
93
+ readonly payload: ProbePendingResponse;
94
+ constructor(statusCode: number, errorMessage: string, payload: ProbePendingResponse, path?: string, extra?: Omit<GislApiErrorOptions, 'payload'>);
95
+ }
65
96
  export declare class GislWorkflowExpiredError extends GislApiError {
66
97
  readonly payload: WorkflowExpiredResponse;
67
98
  constructor(statusCode: number, errorMessage: string, payload: WorkflowExpiredResponse, path?: string, extra?: Omit<GislApiErrorOptions, 'payload'>);
@@ -151,6 +182,121 @@ export declare class GislMultipartSessionOwnershipError extends GislApiError {
151
182
  export declare class GislMultipartSessionAuthRequiredError extends GislApiError {
152
183
  constructor(statusCode: number, errorMessage: string, path?: string, options?: GislApiErrorOptions);
153
184
  }
185
+ /**
186
+ * Optional structured metadata attached to a {@link GislConfigError}. The
187
+ * preset resolver (T4b) raises errors with these fields populated so
188
+ * callers can branch on machine-readable codes rather than parsing the
189
+ * human message. Every field is optional — existing call sites that
190
+ * throw `new GislConfigError(message)` keep working unchanged.
191
+ */
192
+ export interface GislConfigErrorMetadata {
193
+ /**
194
+ * Machine-readable error code. Resolver-side values today:
195
+ * `'invalid_combination'`, `'missing_dependency'`, `'type_mismatch'`,
196
+ * `'unknown_field'`, `'invalid_target_size'`. Other call sites may add
197
+ * codes — the union is open.
198
+ */
199
+ readonly reason?: string;
200
+ /**
201
+ * Field names (camelCase) that participated in the rejection. For
202
+ * `invalid_combination` reasons this is the *pair* that conflicts
203
+ * (e.g. `['targetSize', 'codec']`); for `missing_dependency` this is
204
+ * the dependent field plus the field whose value blocks it.
205
+ */
206
+ readonly conflictingFields?: readonly string[];
207
+ /**
208
+ * The merged wire-shape snapshot the resolver computed BEFORE the
209
+ * validation rejected it. Lets callers see "what would have been
210
+ * sent" for debugging without re-running the chain.
211
+ */
212
+ readonly resolvedSnapshot?: Readonly<Record<string, unknown>>;
213
+ /**
214
+ * Short human-readable remediation hint specific to the error. E.g.
215
+ * "Switch codec to H264, or drop targetSize and use crf instead."
216
+ */
217
+ readonly suggestion?: string;
218
+ }
219
+ /**
220
+ * Root of the LOCAL config-error tree — thrown before any HTTP/file I/O.
221
+ * Sibling of `GislApiError` (which represents server-side error envelopes).
222
+ * Reserve for fail-early errors raised by the ergonomic-layer factory or
223
+ * credential-chain resolver when the caller hasn't supplied something the
224
+ * SDK needs to make a request. Never carries an HTTP status code.
225
+ *
226
+ * Optional `metadata` (T4b — `27rE1fZn`) carries structured fields used
227
+ * by the preset resolver and other ergonomic-layer validators. Existing
228
+ * call sites that pass `(message)` keep working — metadata is purely
229
+ * additive and defaults to `undefined`.
230
+ */
231
+ export declare class GislConfigError extends GislError {
232
+ readonly reason?: string;
233
+ readonly conflictingFields?: readonly string[];
234
+ readonly resolvedSnapshot?: Readonly<Record<string, unknown>>;
235
+ readonly suggestion?: string;
236
+ constructor(message: string, metadata?: GislConfigErrorMetadata);
237
+ }
238
+ /**
239
+ * The ergonomic-layer factory `gisl.create()` could not resolve an API key
240
+ * from any of explicit arg, `GISL_API_KEY` env, or shared-config profile,
241
+ * AND the caller did not opt into anonymous or cookie-mode. Thrown BEFORE
242
+ * any file read or HTTP request — calls to `client.compress(...)`, `.run()`,
243
+ * etc., synchronously fail with this error.
244
+ */
245
+ export declare class GislMissingCredentialsError extends GislConfigError {
246
+ constructor(message: string);
247
+ }
248
+ /**
249
+ * The caller used `gisl.anonymous()` and then invoked an operation that is
250
+ * not in the anonymous-capable allowlist. Local-only — thrown before any I/O.
251
+ * Distinct from server-side `GislAuthError` (401/403 on the wire).
252
+ */
253
+ export declare class GislFeatureRequiresAuthError extends GislConfigError {
254
+ readonly operation: string;
255
+ constructor(operation: string, message: string);
256
+ }
257
+ /**
258
+ * `MergeBuilder.sequence(...)` referenced an asset that wasn't declared in
259
+ * the prior `client.merge(...)` call. Local validation runs BEFORE upload
260
+ * so the caller fails fast on the typo without burning bandwidth.
261
+ */
262
+ export declare class GislUndeclaredAssetError extends GislConfigError {
263
+ readonly assetId: string;
264
+ readonly declaredAssets: readonly string[];
265
+ constructor(assetId: string, declaredAssets: readonly string[]);
266
+ }
267
+ /**
268
+ * `MergeBuilder.sequence(...)` was called but at least one declared asset
269
+ * wasn't referenced. Almost always a bug (wasted upload). Escape via
270
+ * `allowUnusedAssets: true` on the merge options.
271
+ */
272
+ export declare class GislUnusedAssetError extends GislConfigError {
273
+ readonly unusedAssets: readonly string[];
274
+ constructor(unusedAssets: readonly string[]);
275
+ }
276
+ /**
277
+ * `MergeBuilder.sequence(...)` on an image merge was given a `clip(ref, opts)`
278
+ * entry. Image merges have NO per-input options in the wire today — `transition`
279
+ * applies at the merge level and is uniform across all joins.
280
+ */
281
+ export declare class GislPerInputOptionsNotSupportedError extends GislConfigError {
282
+ readonly mediaKind: string;
283
+ constructor(mediaKind: string);
284
+ }
285
+ /**
286
+ * Thrown by future chain methods (`.compress()` / `.thumbnail()` /
287
+ * `.convert()` on an `OperationBuilder`) when the previous step produces
288
+ * MULTIPLE artifacts and the caller didn't explicitly call `.mapEach(...)`
289
+ * to opt into per-artifact fan-out. T6 ships the error class + the
290
+ * `.mapEach(...)` method; the chain methods themselves are a separate
291
+ * follow-up card, so this error is currently dormant — but the type +
292
+ * audit-gate registration land here so the future chain-method PR is a
293
+ * pure addition with no public-API churn.
294
+ */
295
+ export declare class GislChainCardinalityMismatchError extends GislConfigError {
296
+ readonly previousOperation: string;
297
+ readonly attemptedOperation: string;
298
+ constructor(previousOperation: string, attemptedOperation: string);
299
+ }
154
300
  export declare class GislTimeoutError extends GislError {
155
301
  constructor(message: string);
156
302
  }
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,6 +186,134 @@ 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);