@giveitsmaller/sdk 0.19.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +8 -15
- package/dist/_audit.js +14 -4
- package/dist/builder.d.ts +5 -4
- package/dist/builder.js +16 -16
- package/dist/client.d.ts +17 -7
- package/dist/client.js +116 -62
- package/dist/ergonomic/image_output_routes.d.ts +30 -0
- package/dist/ergonomic/image_output_routes.js +82 -11
- package/dist/ergonomic/option_types.d.ts +34 -1
- package/dist/ergonomic/option_types.js +5 -1
- package/dist/ergonomic/option_validation.d.ts +19 -1
- package/dist/ergonomic/option_validation.js +34 -1
- package/dist/ergonomic/preset_resolver.d.ts +2 -2
- package/dist/ergonomic/preset_resolver.js +6 -10
- package/dist/ergonomic/presets/index.d.ts +9 -8
- package/dist/ergonomic/presets/index.js +1 -9
- package/dist/errors.d.ts +105 -3
- package/dist/errors.js +136 -2
- package/dist/file-first.d.ts +234 -16
- package/dist/file-first.js +692 -175
- package/dist/generated/sdk_spec/enums.d.ts +0 -26
- package/dist/generated/sdk_spec/enums.js +0 -16
- package/dist/generated/sdk_spec/errors.d.ts +1 -1
- package/dist/generated/sdk_spec/errors.js +12 -0
- package/dist/generated/sdk_spec/presets.js +0 -14
- package/dist/generated/sdk_spec/version.d.ts +2 -2
- package/dist/generated/sdk_spec/version.js +2 -2
- package/dist/gisl.d.ts +93 -5
- package/dist/gisl.js +72 -2
- package/dist/handle.d.ts +6 -1
- package/dist/handle.js +42 -13
- package/dist/index.core.d.ts +10 -8
- package/dist/index.core.js +11 -3
- package/dist/merge.d.ts +12 -0
- package/dist/merge.js +14 -2
- package/dist/retry-metadata.d.ts +37 -0
- package/dist/retry-metadata.js +86 -0
- package/dist/sse.d.ts +2 -1
- package/dist/sse.js +26 -6
- package/dist/types.d.ts +54 -4
- package/dist/types.js +1 -0
- package/package.json +3 -3
- package/dist/ergonomic/presets/document_pdf_compress.d.ts +0 -12
- package/dist/ergonomic/presets/document_pdf_compress.js +0 -33
package/dist/index.core.js
CHANGED
|
@@ -9,7 +9,7 @@ export { GislClient, DEFAULT_MULTIPART_FIRST_CHUNK_SIZE } from './client.js';
|
|
|
9
9
|
export { parseSseStream } from './sse.js';
|
|
10
10
|
export { uploadSource, jobOutputSource, externalImportSource, connectionSource, } from './types.js';
|
|
11
11
|
// Errors
|
|
12
|
-
export { GislError, GislApiError, GislValidationError, GislBalanceExhaustedError, GislTierRestrictedError, GislFeatureTierRestrictedError, GislFeatureNotAvailableError, GislWorkflowExpiredError, GislProbePendingError, GislAuthError, GislUploadCapExceededError, GislMultipartPartError, GislMultipartPartCountError,
|
|
12
|
+
export { GislError, GislApiError, GislValidationError, GislBalanceExhaustedError, GislLongFormConcurrencyError, GislTierRestrictedError, GislFeatureTierRestrictedError, GislFeatureNotAvailableError, GislWorkflowExpiredError, GislProbePendingError, GislAuthError, GislUploadCapExceededError, GislMultipartPartError, GislMultipartPartCountError,
|
|
13
13
|
// SDK-3 (Wb6ebOMM) — typed errors for the 3 resume-support endpoints.
|
|
14
14
|
GislMultipartSessionNotFoundError, GislMultipartSessionOwnershipError, GislMultipartSessionAuthRequiredError, GislTimeoutError, GislAbortError,
|
|
15
15
|
// FF2b / tywwynmN — transport-level failure (mirrors PHP GislNetworkError);
|
|
@@ -55,6 +55,11 @@ export { ArchivedRecipe } from './file-first.js';
|
|
|
55
55
|
// post-watermark ops on, then run()/submit(). Routes image_watermark / video_watermark
|
|
56
56
|
// by base media; gates planned/unsupported bases locally pre-upload.
|
|
57
57
|
export { WatermarkedRecipe } from './file-first.js';
|
|
58
|
+
// File-first keyed multi-recipe batch (FF7 / MFaCjL8d) — `client.batch([r1, r2, …])`
|
|
59
|
+
// runs N DISTINCT single-input keyed recipes as ONE workflow; run() partitions the
|
|
60
|
+
// RunResult by each entry's caller key. v1 = single-input keyed, run()-only; the
|
|
61
|
+
// multi-input builders are rejected pre-upload.
|
|
62
|
+
export { BatchRecipe } from './file-first.js';
|
|
58
63
|
// `HttpDownloader` (Node streaming downloader) is re-exported from the Node-only
|
|
59
64
|
// entry `index.ts`, NOT here — it statically imports node:fs/node:stream.
|
|
60
65
|
// `projectDownloadsToRunResult` is intentionally NOT re-exported here — it is an
|
|
@@ -72,7 +77,7 @@ export { gisl, create } from './gisl.js';
|
|
|
72
77
|
// Ergonomic preset defaults (T4a / VhIj4S7T) — typed leaf DTOs + immutable
|
|
73
78
|
// `PresetDefaults` builder + `presetDefaults()` factory + ergonomic enum
|
|
74
79
|
// re-exports. Resolver wiring (T4b) consumes `PresetDefaults.cellFor()`.
|
|
75
|
-
export { presetDefaults, PresetDefaults, ImageCompressPresetOptions, AudioCompressPresetOptions, VideoCompressPresetOptions,
|
|
80
|
+
export { presetDefaults, PresetDefaults, ImageCompressPresetOptions, AudioCompressPresetOptions, VideoCompressPresetOptions, DocumentOfficeCompressPresetOptions, DocumentOdfCompressPresetOptions, DocumentEpubCompressPresetOptions, OptimizeFor, ImageMetadataPolicy, ImageFormat, VideoCodec, VideoPreset, VideoFit, AudioBitrate, AudioCodec, AudioSampleRate, } from './ergonomic/presets/index.js';
|
|
76
81
|
// Operation-builder surface (T2 / xVDTIm8C) — `client.compress/convert/thumbnail`
|
|
77
82
|
// returns an `OperationBuilder`; `.run()` projects to a flat `Result` /
|
|
78
83
|
// `.submit({webhook})` returns a `Handle`. Progress events are the
|
|
@@ -109,4 +114,7 @@ AudioToVideoAudioOutputResolution, AudioToVideoAudioImageFit, AudioToVideoAudioO
|
|
|
109
114
|
// surfaces as `GislFeatureNotAvailableError`).
|
|
110
115
|
export { archiveMetadata, audioOverlayMetadata, audioWatermarkMetadata, compressMetadata, convertMetadata, customLumaMetadata, imageWatermarkMetadata, mergeMetadata, textWatermarkMetadata, thumbnailMetadata,
|
|
111
116
|
// New planned operation metadata sidecars — contracts v2.15 (AJCLLGaG).
|
|
112
|
-
audioToVideoMetadata, videoWatermarkMetadata, videoTextWatermarkMetadata, splitMetadata,
|
|
117
|
+
audioToVideoMetadata, videoWatermarkMetadata, videoTextWatermarkMetadata, splitMetadata,
|
|
118
|
+
// transform is `availability: planned` — consumers can inspect this sidecar to
|
|
119
|
+
// gate UI before submitting (the API otherwise returns feature_not_available/422). T4.
|
|
120
|
+
transformMetadata, } from '@giveitsmaller/contracts/operations';
|
package/dist/merge.d.ts
CHANGED
|
@@ -90,13 +90,25 @@ export interface MergeOptions {
|
|
|
90
90
|
readonly crossfadeDuration?: number;
|
|
91
91
|
readonly gapDuration?: number;
|
|
92
92
|
readonly normalizeAudio?: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Video re-encode policy (`auto` | `always` | `never`). Passed through
|
|
95
|
+
* verbatim — `codec`/`crf`/`preset`/`targetResolution`/`targetSize` are only
|
|
96
|
+
* honoured by the worker when re-encoding (`auto`/`always`); the server owns
|
|
97
|
+
* that dependency validation (the SDK is a passthrough allowlist, same as the
|
|
98
|
+
* pre-existing codec/crf/preset fields). Video merge only.
|
|
99
|
+
*/
|
|
100
|
+
readonly reEncodeMode?: string;
|
|
93
101
|
readonly codec?: string;
|
|
94
102
|
readonly crf?: number;
|
|
95
103
|
readonly preset?: string;
|
|
104
|
+
/** Video output dimensions `WxH` (e.g. `"1920x1080"`); omit to inherit from inputs. Video merge only. */
|
|
105
|
+
readonly targetResolution?: string;
|
|
96
106
|
readonly targetSize?: string | number;
|
|
97
107
|
readonly transitionDuration?: number;
|
|
98
108
|
readonly fps?: number;
|
|
99
109
|
readonly durationPerImage?: number;
|
|
110
|
+
/** Milliseconds between frames for an animated-GIF image merge (`output_type: gif`). Image merge only. */
|
|
111
|
+
readonly delay?: number;
|
|
100
112
|
readonly loopCount?: number;
|
|
101
113
|
readonly output?: string;
|
|
102
114
|
readonly videoFormat?: string;
|
package/dist/merge.js
CHANGED
|
@@ -128,14 +128,14 @@ export class MergeBuilder {
|
|
|
128
128
|
});
|
|
129
129
|
// 5. Fetch downloads + project.
|
|
130
130
|
if (Date.now() >= deadline) {
|
|
131
|
-
throw new GislTimeoutError(`Merge workflow ${created.workflowId} reached terminal status but maxWait elapsed before downloads could be fetched
|
|
131
|
+
throw new GislTimeoutError(`Merge workflow ${created.workflowId} reached terminal status but maxWait elapsed before downloads could be fetched`, created.workflowId);
|
|
132
132
|
}
|
|
133
133
|
const downloads = await this.client.getWorkflowDownloads(created.workflowId);
|
|
134
134
|
// TDqmkWpX: the maxWait deadline also covers the downloads fetch itself —
|
|
135
135
|
// re-check AFTER the call so a slow getWorkflowDownloads cannot return a
|
|
136
136
|
// success past the advertised whole-run deadline.
|
|
137
137
|
if (Date.now() >= deadline) {
|
|
138
|
-
throw new GislTimeoutError(`Merge workflow ${created.workflowId} downloads fetch completed after maxWait elapsed
|
|
138
|
+
throw new GislTimeoutError(`Merge workflow ${created.workflowId} downloads fetch completed after maxWait elapsed`, created.workflowId);
|
|
139
139
|
}
|
|
140
140
|
// p0SuJEeK — project ONLY the merge job's output. getWorkflowDownloads
|
|
141
141
|
// returns a download group per terminal job, which now INCLUDES the
|
|
@@ -406,12 +406,16 @@ export class MergeBuilder {
|
|
|
406
406
|
out.gapDuration = o.gapDuration;
|
|
407
407
|
// Video only.
|
|
408
408
|
if (mediaKind === 'video') {
|
|
409
|
+
if (o.reEncodeMode !== undefined)
|
|
410
|
+
out.reEncodeMode = o.reEncodeMode;
|
|
409
411
|
if (o.codec !== undefined)
|
|
410
412
|
out.codec = o.codec;
|
|
411
413
|
if (o.crf !== undefined)
|
|
412
414
|
out.crf = o.crf;
|
|
413
415
|
if (o.preset !== undefined)
|
|
414
416
|
out.preset = o.preset;
|
|
417
|
+
if (o.targetResolution !== undefined)
|
|
418
|
+
out.targetResolution = o.targetResolution;
|
|
415
419
|
if (o.targetSize !== undefined)
|
|
416
420
|
out.targetSize = o.targetSize;
|
|
417
421
|
}
|
|
@@ -423,6 +427,8 @@ export class MergeBuilder {
|
|
|
423
427
|
out.fps = o.fps;
|
|
424
428
|
if (o.durationPerImage !== undefined)
|
|
425
429
|
out.durationPerImage = o.durationPerImage;
|
|
430
|
+
if (o.delay !== undefined)
|
|
431
|
+
out.delay = o.delay;
|
|
426
432
|
if (o.loopCount !== undefined)
|
|
427
433
|
out.loopCount = o.loopCount;
|
|
428
434
|
if (o.videoFormat !== undefined)
|
|
@@ -519,12 +525,16 @@ export function wireMergeOptions(opts, mediaKind) {
|
|
|
519
525
|
out.gap_duration = opts.gapDuration;
|
|
520
526
|
// Video only.
|
|
521
527
|
if (mediaKind === 'video') {
|
|
528
|
+
if (opts.reEncodeMode !== undefined)
|
|
529
|
+
out.re_encode_mode = opts.reEncodeMode;
|
|
522
530
|
if (opts.codec !== undefined)
|
|
523
531
|
out.codec = opts.codec;
|
|
524
532
|
if (opts.crf !== undefined)
|
|
525
533
|
out.crf = opts.crf;
|
|
526
534
|
if (opts.preset !== undefined)
|
|
527
535
|
out.preset = opts.preset;
|
|
536
|
+
if (opts.targetResolution !== undefined)
|
|
537
|
+
out.target_resolution = opts.targetResolution;
|
|
528
538
|
if (opts.targetSize !== undefined) {
|
|
529
539
|
out.target_size_bytes = typeof opts.targetSize === 'number'
|
|
530
540
|
? opts.targetSize
|
|
@@ -540,6 +550,8 @@ export function wireMergeOptions(opts, mediaKind) {
|
|
|
540
550
|
out.fps = opts.fps;
|
|
541
551
|
if (opts.durationPerImage !== undefined)
|
|
542
552
|
out.duration_per_image = opts.durationPerImage;
|
|
553
|
+
if (opts.delay !== undefined)
|
|
554
|
+
out.delay = opts.delay;
|
|
543
555
|
if (opts.loopCount !== undefined)
|
|
544
556
|
out.loop_count = opts.loopCount;
|
|
545
557
|
if (opts.videoFormat !== undefined)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A rate-limit snapshot derived from the `x-ratelimit-*` response headers.
|
|
3
|
+
* `resetSeconds` is the server's seconds-to-reset value (`X-RateLimit-Reset`),
|
|
4
|
+
* NOT an absolute epoch — read {@link GislApiError.rateLimit} to obtain one.
|
|
5
|
+
*/
|
|
6
|
+
export interface RateLimitSnapshot {
|
|
7
|
+
readonly limit: number;
|
|
8
|
+
readonly remaining: number;
|
|
9
|
+
readonly resetSeconds: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Whether an HTTP status is retryable per the API-error taxonomy: request
|
|
13
|
+
* timeout (408), rate-limit (429), or any 5xx (500–599). BOUNDED at 599 — a
|
|
14
|
+
* non-standard 6xx-and-up status is NOT classified retryable.
|
|
15
|
+
*
|
|
16
|
+
* Now value-identical to the S3-PUT retry predicate (`isRetryableStatus`) in
|
|
17
|
+
* `client.ts` (both are `408 || 429 || 500-599` after qz7MjNTy), but kept
|
|
18
|
+
* DELIBERATELY SEPARATE: they guard different retry paths (S3-PUT chunk uploads
|
|
19
|
+
* vs the API-error taxonomy) and may diverge again, so they must NOT be merged.
|
|
20
|
+
*/
|
|
21
|
+
export declare function isApiRetryableStatus(status: number): boolean;
|
|
22
|
+
export declare function parseRetryAfterMs(headerValue: string | undefined): number | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* The server-suggested back-off delay in WHOLE seconds, parsed from the
|
|
25
|
+
* `Retry-After` response header. Derived from {@link parseRetryAfterMs}
|
|
26
|
+
* (`Math.floor(ms / 1000)`) so the semantics mirror the retry-loop parser:
|
|
27
|
+
* absent / malformed / zero / past all collapse to `undefined`, as does a
|
|
28
|
+
* sub-second future HTTP-date (floors to zero → treated as absent).
|
|
29
|
+
*/
|
|
30
|
+
export declare function retryAfterSecondsFromHeaders(headers: Record<string, string> | undefined): number | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* A rate-limit snapshot parsed from the `x-ratelimit-*` response headers.
|
|
33
|
+
* Present ONLY when `x-ratelimit-limit`, `x-ratelimit-remaining`, and
|
|
34
|
+
* `x-ratelimit-reset` all parse as non-negative integers; otherwise
|
|
35
|
+
* `undefined` (a partial set is not a usable snapshot).
|
|
36
|
+
*/
|
|
37
|
+
export declare function rateLimitFromHeaders(headers: Record<string, string> | undefined): RateLimitSnapshot | undefined;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Shared HTTP retry-metadata helpers. Extracted here so `errors.ts` can consume
|
|
2
|
+
// them WITHOUT importing `client.ts`: `client.ts` already imports `errors.ts`, so
|
|
3
|
+
// pulling the module-private `parseRetryAfterMs` back out of `client.ts` would
|
|
4
|
+
// form a `client → errors → client` circular import. The millisecond parser is
|
|
5
|
+
// MOVED here verbatim; `client.ts` re-imports it so the retry-loop timing stays
|
|
6
|
+
// byte-identical.
|
|
7
|
+
/**
|
|
8
|
+
* Whether an HTTP status is retryable per the API-error taxonomy: request
|
|
9
|
+
* timeout (408), rate-limit (429), or any 5xx (500–599). BOUNDED at 599 — a
|
|
10
|
+
* non-standard 6xx-and-up status is NOT classified retryable.
|
|
11
|
+
*
|
|
12
|
+
* Now value-identical to the S3-PUT retry predicate (`isRetryableStatus`) in
|
|
13
|
+
* `client.ts` (both are `408 || 429 || 500-599` after qz7MjNTy), but kept
|
|
14
|
+
* DELIBERATELY SEPARATE: they guard different retry paths (S3-PUT chunk uploads
|
|
15
|
+
* vs the API-error taxonomy) and may diverge again, so they must NOT be merged.
|
|
16
|
+
*/
|
|
17
|
+
export function isApiRetryableStatus(status) {
|
|
18
|
+
return status === 408 || status === 429 || (status >= 500 && status <= 599);
|
|
19
|
+
}
|
|
20
|
+
// Parse an HTTP `Retry-After` header into milliseconds. Accepts the two RFC
|
|
21
|
+
// 9110 forms: delta-seconds (e.g. "5") or an HTTP-date. Returns `undefined`
|
|
22
|
+
// for an absent / unparseable / negative value (caller falls back to its own
|
|
23
|
+
// backoff). A past HTTP-date clamps to 0.
|
|
24
|
+
export function parseRetryAfterMs(headerValue) {
|
|
25
|
+
if (headerValue === undefined)
|
|
26
|
+
return undefined;
|
|
27
|
+
const trimmed = headerValue.trim();
|
|
28
|
+
if (trimmed === '')
|
|
29
|
+
return undefined;
|
|
30
|
+
let ms;
|
|
31
|
+
if (/^\d+$/.test(trimmed)) {
|
|
32
|
+
ms = Number(trimmed) * 1000;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
const when = Date.parse(trimmed);
|
|
36
|
+
if (Number.isNaN(when))
|
|
37
|
+
return undefined;
|
|
38
|
+
ms = when - Date.now();
|
|
39
|
+
}
|
|
40
|
+
// A non-positive Retry-After (e.g. "0" or a past HTTP-date) must NOT short-
|
|
41
|
+
// circuit the backoff to zero — treat it as absent so the caller falls back
|
|
42
|
+
// to jitter and the loop can't busy-poll until timeout.
|
|
43
|
+
return ms > 0 ? ms : undefined;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The server-suggested back-off delay in WHOLE seconds, parsed from the
|
|
47
|
+
* `Retry-After` response header. Derived from {@link parseRetryAfterMs}
|
|
48
|
+
* (`Math.floor(ms / 1000)`) so the semantics mirror the retry-loop parser:
|
|
49
|
+
* absent / malformed / zero / past all collapse to `undefined`, as does a
|
|
50
|
+
* sub-second future HTTP-date (floors to zero → treated as absent).
|
|
51
|
+
*/
|
|
52
|
+
export function retryAfterSecondsFromHeaders(headers) {
|
|
53
|
+
const ms = parseRetryAfterMs(headers?.['retry-after']);
|
|
54
|
+
if (ms === undefined)
|
|
55
|
+
return undefined;
|
|
56
|
+
const seconds = Math.floor(ms / 1000);
|
|
57
|
+
return seconds > 0 ? seconds : undefined;
|
|
58
|
+
}
|
|
59
|
+
// Parse a non-negative integer response header. Returns `undefined` for an
|
|
60
|
+
// absent value or anything that isn't a bare run of decimal digits (so a
|
|
61
|
+
// float, sign, or units suffix is rejected rather than silently truncated).
|
|
62
|
+
function parseIntHeader(headerValue) {
|
|
63
|
+
if (headerValue === undefined)
|
|
64
|
+
return undefined;
|
|
65
|
+
const trimmed = headerValue.trim();
|
|
66
|
+
if (!/^\d+$/.test(trimmed))
|
|
67
|
+
return undefined;
|
|
68
|
+
return Number(trimmed);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* A rate-limit snapshot parsed from the `x-ratelimit-*` response headers.
|
|
72
|
+
* Present ONLY when `x-ratelimit-limit`, `x-ratelimit-remaining`, and
|
|
73
|
+
* `x-ratelimit-reset` all parse as non-negative integers; otherwise
|
|
74
|
+
* `undefined` (a partial set is not a usable snapshot).
|
|
75
|
+
*/
|
|
76
|
+
export function rateLimitFromHeaders(headers) {
|
|
77
|
+
if (headers === undefined)
|
|
78
|
+
return undefined;
|
|
79
|
+
const limit = parseIntHeader(headers['x-ratelimit-limit']);
|
|
80
|
+
const remaining = parseIntHeader(headers['x-ratelimit-remaining']);
|
|
81
|
+
const resetSeconds = parseIntHeader(headers['x-ratelimit-reset']);
|
|
82
|
+
if (limit === undefined || remaining === undefined || resetSeconds === undefined) {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
return { limit, remaining, resetSeconds };
|
|
86
|
+
}
|
package/dist/sse.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { GislSseEvent } from './types.js';
|
|
1
|
+
import type { GislSseEvent, GislSseParseFailure } from './types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Parse an SSE stream from a fetch Response into an AsyncIterable of typed events.
|
|
4
4
|
*
|
|
@@ -27,4 +27,5 @@ import type { GislSseEvent } from './types.js';
|
|
|
27
27
|
*/
|
|
28
28
|
export declare function parseSseStream(response: Response, opts?: {
|
|
29
29
|
signal?: AbortSignal;
|
|
30
|
+
onParseError?: (diagnostic: GislSseParseFailure) => void;
|
|
30
31
|
}): AsyncGenerator<GislSseEvent>;
|
package/dist/sse.js
CHANGED
|
@@ -76,15 +76,27 @@ export async function* parseSseStream(response, opts = {}) {
|
|
|
76
76
|
// Empty line = end of event
|
|
77
77
|
if (dataLines.length > 0) {
|
|
78
78
|
const rawData = dataLines.join('\n');
|
|
79
|
+
const frameEvent = eventType || 'message';
|
|
79
80
|
let parsed;
|
|
80
81
|
try {
|
|
81
82
|
parsed = JSON.parse(rawData);
|
|
82
83
|
}
|
|
83
|
-
catch {
|
|
84
|
-
|
|
84
|
+
catch (err) {
|
|
85
|
+
// TYNjcjpo — a malformed-JSON frame is SKIPPED (not yielded as a
|
|
86
|
+
// raw string) so the stream stays resilient, but the failure is
|
|
87
|
+
// surfaced via the optional onParseError diagnostic rather than
|
|
88
|
+
// silently lost. Identical to the PHP `flushSseFrame` drop-path.
|
|
89
|
+
opts.onParseError?.({
|
|
90
|
+
raw: rawData,
|
|
91
|
+
event: frameEvent,
|
|
92
|
+
error: err instanceof Error ? err.message : String(err),
|
|
93
|
+
});
|
|
94
|
+
eventType = '';
|
|
95
|
+
dataLines = [];
|
|
96
|
+
continue;
|
|
85
97
|
}
|
|
86
98
|
yield {
|
|
87
|
-
event:
|
|
99
|
+
event: frameEvent,
|
|
88
100
|
data: parsed,
|
|
89
101
|
};
|
|
90
102
|
}
|
|
@@ -124,15 +136,23 @@ export async function* parseSseStream(response, opts = {}) {
|
|
|
124
136
|
// not be yielded once the consumer has abandoned the stream.
|
|
125
137
|
if (!aborted && dataLines.length > 0) {
|
|
126
138
|
const rawData = dataLines.join('\n');
|
|
139
|
+
const frameEvent = eventType || 'message';
|
|
127
140
|
let parsed;
|
|
128
141
|
try {
|
|
129
142
|
parsed = JSON.parse(rawData);
|
|
130
143
|
}
|
|
131
|
-
catch {
|
|
132
|
-
|
|
144
|
+
catch (err) {
|
|
145
|
+
// TYNjcjpo — trailing-flush malformed frame: skip + diagnostic (same as
|
|
146
|
+
// the in-loop path above).
|
|
147
|
+
opts.onParseError?.({
|
|
148
|
+
raw: rawData,
|
|
149
|
+
event: frameEvent,
|
|
150
|
+
error: err instanceof Error ? err.message : String(err),
|
|
151
|
+
});
|
|
152
|
+
return;
|
|
133
153
|
}
|
|
134
154
|
yield {
|
|
135
|
-
event:
|
|
155
|
+
event: frameEvent,
|
|
136
156
|
data: parsed,
|
|
137
157
|
};
|
|
138
158
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { OperationType, OperationsSchemaResponse, CallbackEventType, SseEventType, SseOperationProgressData, SseOperationCompletedData, SseOperationFailedData, SseJobCompletedData, SseJobFailedData, SseWorkflowTerminalData, MultipartInitiateRequestMetadataHint, UploadProbeResponse } from '@giveitsmaller/contracts/openapi';
|
|
2
|
-
import type { JobInputV2RoleEnum } from '@giveitsmaller/contracts/openapi';
|
|
1
|
+
import type { OperationType, OperationsSchemaResponse, OperationCapability, OutputProperties, ImageEncodeCapabilities, CallbackEventType, SseEventType, SseOperationProgressData, SseOperationCompletedData, SseOperationFailedData, SseJobCompletedData, SseJobFailedData, SseWorkflowTerminalData, MultipartInitiateRequestMetadataHint, UploadProbeResponse } from '@giveitsmaller/contracts/openapi';
|
|
2
|
+
import type { JobInputV2RoleEnum, NotifyConfig } from '@giveitsmaller/contracts/openapi';
|
|
3
3
|
export interface GislClientConfig {
|
|
4
4
|
baseUrl: string;
|
|
5
5
|
apiKey?: string;
|
|
@@ -188,6 +188,14 @@ export interface WorkflowCreatePayload {
|
|
|
188
188
|
export?: ExternalDestinationPayload;
|
|
189
189
|
delivery?: DeliveryPayload;
|
|
190
190
|
processing?: WorkflowProcessingPayload;
|
|
191
|
+
/**
|
|
192
|
+
* Per-job completion notification (contracts v2.164.0, `notify.email`).
|
|
193
|
+
* Opaque passthrough wire shape — the ergonomic `notifyEmail` surface
|
|
194
|
+
* (SubmitOptions/RunOptions + builder convenience) is a separate follow-up
|
|
195
|
+
* (card y6jsQCpb); this field only acknowledges the wire key so a
|
|
196
|
+
* hand-built payload can carry it. Mirrors `export`/`delivery`/`processing`.
|
|
197
|
+
*/
|
|
198
|
+
notify?: NotifyConfig;
|
|
191
199
|
}
|
|
192
200
|
/**
|
|
193
201
|
* Single source of truth for WorkflowCreatePayload's top-level wire keys.
|
|
@@ -196,7 +204,7 @@ export interface WorkflowCreatePayload {
|
|
|
196
204
|
* only via deep imports and should not be treated as public API.
|
|
197
205
|
* @internal
|
|
198
206
|
*/
|
|
199
|
-
export declare const WORKFLOW_CREATE_PAYLOAD_KEYS: readonly ["jobs", "source", "operations", "workflow_edges", "callback_url", "callback_events", "export", "delivery", "processing"];
|
|
207
|
+
export declare const WORKFLOW_CREATE_PAYLOAD_KEYS: readonly ["jobs", "source", "operations", "workflow_edges", "callback_url", "callback_events", "export", "delivery", "processing", "notify"];
|
|
200
208
|
export interface GetSchemaOptions {
|
|
201
209
|
/** Filter the schema to operations that accept this MIME type (e.g. `image/jpeg`). */
|
|
202
210
|
mimeType?: string;
|
|
@@ -227,6 +235,32 @@ export type GetSchemaResult = {
|
|
|
227
235
|
etag?: string;
|
|
228
236
|
lastModified?: string;
|
|
229
237
|
};
|
|
238
|
+
/**
|
|
239
|
+
* Typed projection of the operation-capability surface returned by
|
|
240
|
+
* {@link ErgonomicClient.capabilities} (qUhxfDA5). Bundles the three v2.124
|
|
241
|
+
* capability fields of `OperationsSchemaResponse` — previously typed but with
|
|
242
|
+
* no ergonomic consumer — so a caller can read them without dropping to
|
|
243
|
+
* `getSchema()` and its not-modified union.
|
|
244
|
+
*
|
|
245
|
+
* Mirrors the PHP `Gisl\Sdk\Ergonomic\CapabilitiesSnapshot` value object.
|
|
246
|
+
*/
|
|
247
|
+
export interface CapabilitiesSnapshot {
|
|
248
|
+
/**
|
|
249
|
+
* Tier-scoped operation-capability matrix, keyed by operation type
|
|
250
|
+
* (`compress`, `convert`, …). Empty when the server omits the field.
|
|
251
|
+
*/
|
|
252
|
+
readonly operations: Record<string, OperationCapability>;
|
|
253
|
+
/**
|
|
254
|
+
* Output-format property table (`hasAudioTrack` / `isAnimated`), keyed by
|
|
255
|
+
* `output_format`. Tier-invariant. Empty when the server omits the field.
|
|
256
|
+
*/
|
|
257
|
+
readonly outputProperties: Record<string, OutputProperties>;
|
|
258
|
+
/**
|
|
259
|
+
* Pre-flight image-encode capability matrix (`webpQualitySupported`,
|
|
260
|
+
* `backgroundFlatten`). Tier-invariant. `undefined` when the server omits it.
|
|
261
|
+
*/
|
|
262
|
+
readonly imageEncode?: ImageEncodeCapabilities;
|
|
263
|
+
}
|
|
230
264
|
export interface CreditsUsageOptions {
|
|
231
265
|
/**
|
|
232
266
|
* Page size. Server defaults to 20 and rejects values outside `[1, 100]`
|
|
@@ -296,7 +330,7 @@ export interface ReadCapabilityOptions {
|
|
|
296
330
|
export interface WaitOptions {
|
|
297
331
|
/** Poll interval in milliseconds (default: 2000) */
|
|
298
332
|
intervalMs?: number;
|
|
299
|
-
/** Maximum wait time in milliseconds (default:
|
|
333
|
+
/** Maximum wait time in milliseconds (default: 600000 = 10 min) */
|
|
300
334
|
timeoutMs?: number;
|
|
301
335
|
/** Called after each poll with current status */
|
|
302
336
|
onPoll?: (status: string) => void;
|
|
@@ -373,6 +407,22 @@ export type GislSseEvent = {
|
|
|
373
407
|
event: string;
|
|
374
408
|
data: unknown;
|
|
375
409
|
};
|
|
410
|
+
/**
|
|
411
|
+
* A typed, non-throwing diagnostic surfaced when an SSE frame's `data:` body fails
|
|
412
|
+
* to JSON-parse (TYNjcjpo). The malformed frame is SKIPPED from the event stream —
|
|
413
|
+
* a long-running consumer must not break on one garbled server frame — but the
|
|
414
|
+
* failure is observable via the `onParseError` callback on `streamEvents` /
|
|
415
|
+
* `parseSseStream` rather than silently lost. Mirrors the PHP `GislSseParseFailure`
|
|
416
|
+
* value object; the shape is identical across the two SDKs (cross-SDK parity).
|
|
417
|
+
*/
|
|
418
|
+
export interface GislSseParseFailure {
|
|
419
|
+
/** The joined `data:` line(s) that failed to parse. */
|
|
420
|
+
readonly raw: string;
|
|
421
|
+
/** The frame's event type (or `'message'` when the frame had no `event:` field). */
|
|
422
|
+
readonly event: string;
|
|
423
|
+
/** The parse error message (e.g. the `JSON.parse` `SyntaxError` text). */
|
|
424
|
+
readonly error: string;
|
|
425
|
+
}
|
|
376
426
|
export interface UploadOptions {
|
|
377
427
|
/** Called with bytes uploaded so far (only for multipart) */
|
|
378
428
|
onProgress?: (uploadedBytes: number, totalBytes: number) => void;
|
package/dist/types.js
CHANGED
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@giveitsmaller/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "Node.js SDK for the GISL (Give It Smaller) file compression and processing API",
|
|
5
|
-
"license": "
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.js",
|
|
8
8
|
"exports": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"node": ">=18"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@giveitsmaller/contracts": "^0.
|
|
34
|
+
"@giveitsmaller/contracts": "^0.59.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "^22",
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { PdfProfile, OptimizeFor } from '../../generated/sdk_spec/enums.js';
|
|
2
|
-
export interface DocumentPdfCompressPresetOptionsInput {
|
|
3
|
-
readonly profile?: PdfProfile;
|
|
4
|
-
readonly grayscale?: boolean;
|
|
5
|
-
}
|
|
6
|
-
export declare class DocumentPdfCompressPresetOptions {
|
|
7
|
-
readonly profile?: PdfProfile;
|
|
8
|
-
readonly grayscale?: boolean;
|
|
9
|
-
private constructor();
|
|
10
|
-
static from(input: DocumentPdfCompressPresetOptionsInput): DocumentPdfCompressPresetOptions;
|
|
11
|
-
static shippedDefaultsFor(level: OptimizeFor): DocumentPdfCompressPresetOptions;
|
|
12
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
// T4a — DocumentPdfCompressPresetOptions leaf DTO.
|
|
2
|
-
//
|
|
3
|
-
// Field set (2): profile, grayscale — the worker-honored stable PDF controls
|
|
4
|
-
// (contracts v2.96.0 Acrobat-PDF realignment Lw1LseYr). The earlier
|
|
5
|
-
// {profile, colorspace, flattenForms} set was retired: colorspace + flatten_forms
|
|
6
|
-
// are `planned` (not read by the worker) so presets never emit them, and
|
|
7
|
-
// `image_dpi` + `pages` are per-call knobs, not preset cells.
|
|
8
|
-
import { shippedDefaultsFor as f3ShippedDefaultsFor } from '../../generated/sdk_spec/presets.js';
|
|
9
|
-
import { translateEnum } from './_translate.js';
|
|
10
|
-
export class DocumentPdfCompressPresetOptions {
|
|
11
|
-
profile;
|
|
12
|
-
grayscale;
|
|
13
|
-
constructor(input) {
|
|
14
|
-
if (input.profile !== undefined)
|
|
15
|
-
this.profile = input.profile;
|
|
16
|
-
if (input.grayscale !== undefined)
|
|
17
|
-
this.grayscale = input.grayscale;
|
|
18
|
-
Object.freeze(this);
|
|
19
|
-
}
|
|
20
|
-
static from(input) {
|
|
21
|
-
return new DocumentPdfCompressPresetOptions(input);
|
|
22
|
-
}
|
|
23
|
-
static shippedDefaultsFor(level) {
|
|
24
|
-
const cell = f3ShippedDefaultsFor('document_pdf_compress', level);
|
|
25
|
-
const input = {};
|
|
26
|
-
const mut = input;
|
|
27
|
-
if ('profile' in cell)
|
|
28
|
-
mut.profile = translateEnum('PdfProfile', cell.profile);
|
|
29
|
-
if ('grayscale' in cell)
|
|
30
|
-
mut.grayscale = cell.grayscale;
|
|
31
|
-
return new DocumentPdfCompressPresetOptions(input);
|
|
32
|
-
}
|
|
33
|
-
}
|