@giveitsmaller/sdk 0.16.0 → 0.18.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/README.md +21 -0
- package/dist/_audit.js +12 -2
- package/dist/client.d.ts +8 -1
- package/dist/client.js +12 -1
- package/dist/ergonomic/image_output_routes.d.ts +89 -0
- package/dist/ergonomic/image_output_routes.js +155 -0
- package/dist/ergonomic/option_types.d.ts +147 -0
- package/dist/ergonomic/option_types.js +57 -0
- package/dist/ergonomic/option_validation.d.ts +58 -0
- package/dist/ergonomic/option_validation.js +143 -0
- package/dist/ergonomic/preset_resolver.d.ts +1 -1
- package/dist/ergonomic/preset_resolver.js +35 -33
- package/dist/ergonomic/presets/document_pdf_compress.d.ts +3 -5
- package/dist/ergonomic/presets/document_pdf_compress.js +10 -12
- package/dist/ergonomic/presets/image_compress.d.ts +4 -11
- package/dist/ergonomic/presets/image_compress.js +12 -25
- package/dist/ergonomic/presets/index.d.ts +21 -1
- package/dist/ergonomic/presets/index.js +4 -4
- package/dist/errors.d.ts +25 -0
- package/dist/errors.js +34 -0
- package/dist/file-first.d.ts +78 -58
- package/dist/file-first.js +220 -66
- package/dist/generated/sdk_spec/enums.d.ts +10 -46
- package/dist/generated/sdk_spec/enums.js +14 -33
- package/dist/generated/sdk_spec/presets.js +9 -20
- package/dist/generated/sdk_spec/version.d.ts +3 -3
- package/dist/generated/sdk_spec/version.js +3 -3
- package/dist/gisl.d.ts +8 -1
- package/dist/gisl.js +12 -0
- package/dist/index.core.d.ts +5 -4
- package/dist/index.core.js +3 -1
- package/package.json +3 -3
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { convertMetadata, thumbnailMetadata, textWatermarkMetadata, imageWatermarkMetadata, videoWatermarkMetadata, } from '@giveitsmaller/contracts/operations';
|
|
2
|
+
import { GislConfigError } from '../errors.js';
|
|
3
|
+
import { VERB_OPTION_KEYS } from './option_types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Eager, synchronous, PRE-UPLOAD option-key validation for the ergonomic verbs
|
|
6
|
+
* (card Dhje3Faq). The file-first builders accept verb options as untyped bags;
|
|
7
|
+
* a user typo (`{ quaity: 80 }`) would otherwise flow to the server and 422.
|
|
8
|
+
* This module rejects unknown keys at the verb call — before any upload —
|
|
9
|
+
* mirroring the existing `compress()` optimize-check and the watermark eager
|
|
10
|
+
* gate. The allowed key set is read from the generated `OperationMetadata`
|
|
11
|
+
* sidecars (the same contract-anchored source the wire-key conformance guard
|
|
12
|
+
* uses), so it can never silently drift from the contract.
|
|
13
|
+
*
|
|
14
|
+
* SCOPE: `convert` / `thumbnail` / `textWatermark` / `watermark` only. `compress`
|
|
15
|
+
* is deliberately EXCLUDED — its bag legitimately carries SDK-only keys
|
|
16
|
+
* (`optimize`, `presetOverrides`) and camelCase resolver aliases (`targetSize`,
|
|
17
|
+
* `outputFormat`) that are not `operationOptionKeys(compressMetadata)`; it has its
|
|
18
|
+
* own `unknown_field` validation through the preset resolver.
|
|
19
|
+
*
|
|
20
|
+
* Mirrored by the PHP `OptionValidation` helper — keep the two in lockstep.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* OPERATION-LEVEL contract option keys (the keys valid in `OperationDef.options`):
|
|
24
|
+
* the union of every mime group's `options` plus `direct_options` for
|
|
25
|
+
* media-agnostic ops. Deliberately EXCLUDES `per_input_options` (valid only on a
|
|
26
|
+
* merge input). Promoted from the wire-key conformance guard for runtime reuse.
|
|
27
|
+
*/
|
|
28
|
+
export function operationOptionKeys(metadata) {
|
|
29
|
+
const keys = new Set();
|
|
30
|
+
for (const group of Object.values(metadata.mime_groups)) {
|
|
31
|
+
for (const k of Object.keys(group.options))
|
|
32
|
+
keys.add(k);
|
|
33
|
+
}
|
|
34
|
+
for (const k of Object.keys(metadata.direct_options ?? {}))
|
|
35
|
+
keys.add(k);
|
|
36
|
+
return keys;
|
|
37
|
+
}
|
|
38
|
+
function union(...sets) {
|
|
39
|
+
const out = new Set();
|
|
40
|
+
for (const set of sets)
|
|
41
|
+
for (const k of set)
|
|
42
|
+
out.add(k);
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The contract option-key set per validated verb (the GENERIC allowed set).
|
|
47
|
+
* `watermark` is the UNION of `image_watermark` + `video_watermark` because the
|
|
48
|
+
* base media may be undetectable at the `.watermark()` call; the existing
|
|
49
|
+
* media-routing gate still rejects the wrong route by media.
|
|
50
|
+
*/
|
|
51
|
+
const ALLOWED_KEYS = {
|
|
52
|
+
convert: operationOptionKeys(convertMetadata),
|
|
53
|
+
thumbnail: operationOptionKeys(thumbnailMetadata),
|
|
54
|
+
textWatermark: operationOptionKeys(textWatermarkMetadata),
|
|
55
|
+
watermark: union(operationOptionKeys(imageWatermarkMetadata), operationOptionKeys(videoWatermarkMetadata)),
|
|
56
|
+
// `output` is the image Output facade — its allowed keys are the UNION of every
|
|
57
|
+
// image route's honored+planned options (the image-output-routes projection),
|
|
58
|
+
// INCLUDING `output_format` (in every cell's honored set) which — like `convert`
|
|
59
|
+
// — is in the allowed set but rejected first by the positional-owned guard. This
|
|
60
|
+
// is the COARSE static gate (reject keys no image route ever honors, e.g. a video
|
|
61
|
+
// `crf`); the precise per-route honored/planned narrowing happens in the
|
|
62
|
+
// `output()` lowering (`resolveOutputRoute`). Pinned to the projection union by
|
|
63
|
+
// the output-route conformance test.
|
|
64
|
+
output: new Set([...VERB_OPTION_KEYS.output, 'output_format']),
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Keys a verb OWNS via a positional argument: a user must not also supply them
|
|
68
|
+
* in the options bag (they would be silently overridden by the positional). The
|
|
69
|
+
* guard runs BEFORE the generic check so these get a specific, actionable
|
|
70
|
+
* message rather than the generic "unknown option" one. `format` is an SDK alias
|
|
71
|
+
* for the positional (not a contract key) and is owned too.
|
|
72
|
+
*/
|
|
73
|
+
const POSITIONAL_OWNED = {
|
|
74
|
+
convert: ['output_format', 'format'],
|
|
75
|
+
textWatermark: ['text'],
|
|
76
|
+
// `output(format, …)` sets the target format via its first argument; the wire
|
|
77
|
+
// key `output_format` and the SDK alias `format` must not be supplied in the bag.
|
|
78
|
+
output: ['output_format', 'format'],
|
|
79
|
+
};
|
|
80
|
+
/** Accessor for the conformance guard (pins these sets to the contract metadata). */
|
|
81
|
+
export function allowedKeysFor(verb) {
|
|
82
|
+
return ALLOWED_KEYS[verb];
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Validate a USER-supplied options bag for an ergonomic verb. Throws
|
|
86
|
+
* {@link GislConfigError} (reason `unknown_field`) synchronously, BEFORE any
|
|
87
|
+
* upload or wire-key injection. Call this at the TOP of every verb body, before
|
|
88
|
+
* the `format`-drop / `output_format` / `text` injection.
|
|
89
|
+
*
|
|
90
|
+
* @throws {GislConfigError} reason `unknown_field` when the bag carries a key the
|
|
91
|
+
* verb owns via a positional argument, or a key absent from the op's contract
|
|
92
|
+
* option set.
|
|
93
|
+
*/
|
|
94
|
+
export function validateVerbOptions(verb, options) {
|
|
95
|
+
// The typed signatures require an options object, but an untyped JS caller can
|
|
96
|
+
// still omit it (e.g. `thumbnail()` — which dropped its `= {}` default when
|
|
97
|
+
// width/height became required). A nullish bag has no keys to reject; the
|
|
98
|
+
// separate `assertThumbnailDimensions` then reports the missing dimensions as a
|
|
99
|
+
// clean GislConfigError rather than a raw TypeError.
|
|
100
|
+
if (options === null || options === undefined)
|
|
101
|
+
return;
|
|
102
|
+
const owned = POSITIONAL_OWNED[verb];
|
|
103
|
+
if (owned !== undefined) {
|
|
104
|
+
for (const key of owned) {
|
|
105
|
+
if (Object.prototype.hasOwnProperty.call(options, key)) {
|
|
106
|
+
const arg = verb === 'textWatermark' ? 'text' : 'output format';
|
|
107
|
+
throw new GislConfigError(`${verb}() sets the ${arg} via its first argument; remove '${key}' from the options bag.`, { reason: 'unknown_field', conflictingFields: [key] });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const allowed = ALLOWED_KEYS[verb];
|
|
112
|
+
for (const key of Object.keys(options)) {
|
|
113
|
+
if (!allowed.has(key)) {
|
|
114
|
+
throw new GislConfigError(`${verb}: unknown option '${key}'. Valid options: ${[...allowed].sort().join(', ')}.`, { reason: 'unknown_field', conflictingFields: [key] });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Assert thumbnail `width` AND `height` are both present and non-nullish (the
|
|
120
|
+
* contract marks both `required` for image/video/document). The typed signature
|
|
121
|
+
* already enforces this at compile time; this RUNTIME guard catches JS callers and
|
|
122
|
+
* an explicit `undefined`/`null` BEFORE upload. Rejecting `null` (not just
|
|
123
|
+
* `undefined`) keeps TS in lockstep with the PHP `assertThumbnailDimensions`, which
|
|
124
|
+
* must reject `null` because PHP drops null values pre-lower — so a `null` dimension
|
|
125
|
+
* is a pre-upload error in BOTH languages, never a wire `null` that 422s. Mirrored
|
|
126
|
+
* in PHP.
|
|
127
|
+
*
|
|
128
|
+
* @throws {GislConfigError} reason `missing_required_field` naming the absent
|
|
129
|
+
* dimension(s) in `conflictingFields`.
|
|
130
|
+
*/
|
|
131
|
+
export function assertThumbnailDimensions(options) {
|
|
132
|
+
// Null-safe so an untyped JS `thumbnail()` (no args, no default) reports both
|
|
133
|
+
// dimensions missing as a clean GislConfigError instead of a raw TypeError.
|
|
134
|
+
const o = options ?? {};
|
|
135
|
+
const missing = [];
|
|
136
|
+
if (o.width === undefined || o.width === null)
|
|
137
|
+
missing.push('width');
|
|
138
|
+
if (o.height === undefined || o.height === null)
|
|
139
|
+
missing.push('height');
|
|
140
|
+
if (missing.length > 0) {
|
|
141
|
+
throw new GislConfigError(`thumbnail requires both width and height (the contract marks both required); missing: ${missing.join(', ')}.`, { reason: 'missing_required_field', conflictingFields: missing });
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -9,7 +9,7 @@ import { type PresetDefaults, type PresetMedia, type PresetOp } from './presets/
|
|
|
9
9
|
* construction. Previously a hand-typed literal that the v2.73.0 regen had to
|
|
10
10
|
* bump manually (yREs0srv).
|
|
11
11
|
*/
|
|
12
|
-
export declare const PRESET_VERSION: "1.
|
|
12
|
+
export declare const PRESET_VERSION: "1.4";
|
|
13
13
|
/**
|
|
14
14
|
* Inputs to {@link resolveCompressOptions}. `media` selects which leaf
|
|
15
15
|
* DTO drives sdkDefault + clientDefault lookups + invalid-combo
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
// `quality: 100`, `codec: 'h264'`, …).
|
|
22
22
|
//
|
|
23
23
|
// Validation runs AFTER merge so post-merge-only invariants
|
|
24
|
-
// (targetSize + codec
|
|
25
|
-
//
|
|
24
|
+
// (e.g. targetSize + codec on video) catch combinations where
|
|
25
|
+
// the explicit knob and a layered default disagree. Resolver
|
|
26
26
|
// throws `GislConfigError` BEFORE any network round-trip, with a
|
|
27
27
|
// `resolvedSnapshot` showing the would-have-been-sent payload.
|
|
28
28
|
//
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
import { sha256Hex } from '../sha256.js';
|
|
38
38
|
import { GislConfigError } from '../errors.js';
|
|
39
39
|
import { PRESET_VERSION as GENERATED_PRESET_VERSION } from '../generated/sdk_spec/version.js';
|
|
40
|
-
import { ImageCompressPresetOptions, AudioCompressPresetOptions, VideoCompressPresetOptions, DocumentPdfCompressPresetOptions, DocumentOfficeCompressPresetOptions, DocumentOdfCompressPresetOptions, DocumentEpubCompressPresetOptions, } from './presets/index.js';
|
|
40
|
+
import { ImageCompressPresetOptions, AudioCompressPresetOptions, VideoCompressPresetOptions, DocumentPdfCompressPresetOptions, DocumentOfficeCompressPresetOptions, DocumentOdfCompressPresetOptions, DocumentEpubCompressPresetOptions, definedFieldsOf, } from './presets/index.js';
|
|
41
41
|
/**
|
|
42
42
|
* The preset matrix version emitted on every resolve. Re-exported from the
|
|
43
43
|
* GENERATED `sdk_spec/version.ts` (source of truth: contracts
|
|
@@ -53,18 +53,16 @@ export const PRESET_VERSION = GENERATED_PRESET_VERSION;
|
|
|
53
53
|
//
|
|
54
54
|
// Lifted from docs/plans/sdk-ergonomics/plan.md §11a. Maps camelCase
|
|
55
55
|
// ergonomic-DTO field names to their snake_case wire counterparts.
|
|
56
|
-
// Fields whose ergonomic name IS the wire name (`
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
// `
|
|
60
|
-
// safe path.
|
|
56
|
+
// Fields whose ergonomic name IS the wire name (`quality`, `codec`,
|
|
57
|
+
// …) are NOT in this map — `applyAlias` returns them unchanged. A
|
|
58
|
+
// generic snake-case regex would mistranslate acronym/camel names
|
|
59
|
+
// (e.g. an `outputFormat` → `output_format` rename or an acronym like
|
|
60
|
+
// the former `iccProfile`); the declarative map is the only safe path.
|
|
61
61
|
const WIRE_ALIASES = Object.freeze({
|
|
62
|
-
iccProfile: 'icc_profile',
|
|
63
62
|
outputFormat: 'output_format',
|
|
64
63
|
sampleRate: 'sample_rate',
|
|
65
64
|
audioCodec: 'audio_codec',
|
|
66
65
|
audioBitrate: 'audio_bitrate',
|
|
67
|
-
flattenForms: 'flatten_forms',
|
|
68
66
|
imageQuality: 'image_quality',
|
|
69
67
|
stripMacros: 'strip_macros',
|
|
70
68
|
stripHiddenData: 'strip_hidden_data',
|
|
@@ -201,10 +199,10 @@ function presetDefaultsCellRecord(defaults, media, op, optimize) {
|
|
|
201
199
|
if (op !== 'compress')
|
|
202
200
|
return undefined;
|
|
203
201
|
// Each overload returns `<Specific>PresetOptions | undefined`. We
|
|
204
|
-
// erase the per-media type at runtime by
|
|
205
|
-
//
|
|
206
|
-
//
|
|
207
|
-
//
|
|
202
|
+
// erase the per-media type at runtime by reducing the instance to a
|
|
203
|
+
// plain Record. `cellFor` returns `undefined` when no delta was
|
|
204
|
+
// registered for the tuple — the resolver treats that the same as
|
|
205
|
+
// "this layer did not participate."
|
|
208
206
|
let cell;
|
|
209
207
|
switch (media) {
|
|
210
208
|
case 'image':
|
|
@@ -231,7 +229,16 @@ function presetDefaultsCellRecord(defaults, media, op, optimize) {
|
|
|
231
229
|
}
|
|
232
230
|
if (cell === undefined)
|
|
233
231
|
return undefined;
|
|
234
|
-
|
|
232
|
+
// Reduce to a SPARSE camelCase record, dropping undefined-valued keys.
|
|
233
|
+
// The leaf DTO's field DECLARATIONS define every field as an
|
|
234
|
+
// own-enumerable `undefined` property under `useDefineForClassFields`
|
|
235
|
+
// (ES2022) even when the ctor skipped the assignment — a naive spread
|
|
236
|
+
// would carry those `undefined`s into the presetConfigHash input,
|
|
237
|
+
// diverging from PHP's sparse `leafToRecord` (SVQcoR1K). `mergeLayer`
|
|
238
|
+
// already ignores `undefined`, so this only affects the hash path.
|
|
239
|
+
// Reuses the same helper `PresetDefaults.merge` uses for this exact
|
|
240
|
+
// `useDefineForClassFields` problem.
|
|
241
|
+
return definedFieldsOf(cell);
|
|
235
242
|
}
|
|
236
243
|
// ---------------------------------------------------------------------------
|
|
237
244
|
// presetOverrides type-mismatch detection
|
|
@@ -243,10 +250,10 @@ function presetDefaultsCellRecord(defaults, media, op, optimize) {
|
|
|
243
250
|
// for clearly-typed plain objects whose key set only intersects with a
|
|
244
251
|
// non-matching media.
|
|
245
252
|
const MEDIA_FIELDS = Object.freeze({
|
|
246
|
-
image: new Set(['
|
|
253
|
+
image: new Set(['quality', 'metadata', 'outputFormat']),
|
|
247
254
|
audio: new Set(['bitrate', 'channels', 'sampleRate', 'normalize']),
|
|
248
255
|
video: new Set(['codec', 'targetSize', 'crf', 'preset', 'width', 'height', 'fit', 'fps', 'faststart', 'audioCodec', 'audioBitrate']),
|
|
249
|
-
document_pdf: new Set(['profile', '
|
|
256
|
+
document_pdf: new Set(['profile', 'grayscale']),
|
|
250
257
|
document_office: new Set(['imageQuality', 'stripMacros', 'stripHiddenData', 'stripUnusedFonts']),
|
|
251
258
|
document_odf: new Set(['imageQuality', 'stripMetadata', 'stripUnusedStyles']),
|
|
252
259
|
document_epub: new Set(['imageQuality', 'fontSubsetting', 'stripUnusedCss']),
|
|
@@ -312,10 +319,10 @@ function mergeLayer(acc, layer, source) {
|
|
|
312
319
|
// can pin this hand-maintained allowlist to the generated contract metadata: every
|
|
313
320
|
// field the resolver may emit MUST be a real contract option key for `compress`.
|
|
314
321
|
export const KNOWN_WIRE_FIELDS = Object.freeze({
|
|
315
|
-
image: new Set(['
|
|
322
|
+
image: new Set(['quality', 'metadata', 'output_format']),
|
|
316
323
|
audio: new Set(['bitrate', 'channels', 'sample_rate', 'normalize', 'trim_start', 'trim_end']),
|
|
317
324
|
video: new Set(['codec', 'encoding_mode', 'crf', 'target_size_bytes', 'preset', 'width', 'height', 'fit', 'fps', 'faststart', 'audio_codec', 'audio_bitrate', 'trim_start', 'trim_end']),
|
|
318
|
-
document_pdf: new Set(['profile', '
|
|
325
|
+
document_pdf: new Set(['profile', 'grayscale']),
|
|
319
326
|
document_office: new Set(['image_quality', 'strip_macros', 'strip_hidden_data', 'strip_unused_fonts']),
|
|
320
327
|
document_odf: new Set(['image_quality', 'strip_metadata', 'strip_unused_styles']),
|
|
321
328
|
document_epub: new Set(['image_quality', 'font_subsetting', 'strip_unused_css']),
|
|
@@ -337,19 +344,6 @@ function validateMerged(media, merged, explicitKeys, winners) {
|
|
|
337
344
|
});
|
|
338
345
|
}
|
|
339
346
|
}
|
|
340
|
-
// Image: `mode: Lossless` + `quality` set is invalid per the wire
|
|
341
|
-
// contract (`depends_on: { mode: lossy }`). Runs on post-merge so a
|
|
342
|
-
// caller passing explicit `quality` and inheriting `mode=Lossless`
|
|
343
|
-
// from a client preset is caught.
|
|
344
|
-
if (media === 'image' && merged.mode === 'lossless' && merged.quality !== undefined) {
|
|
345
|
-
const snapshot = { ...merged };
|
|
346
|
-
throw new GislConfigError(`Image compress: 'quality' is ignored when 'mode' is Lossless — passing both is a configuration bug.`, {
|
|
347
|
-
reason: 'missing_dependency',
|
|
348
|
-
conflictingFields: ['quality', 'mode'],
|
|
349
|
-
resolvedSnapshot: Object.freeze(snapshot),
|
|
350
|
-
suggestion: "Either drop 'quality' for lossless output, or set 'mode' to Lossy.",
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
347
|
// Video: targetSize-derived encoding_mode='target_size' is only
|
|
354
348
|
// valid for H264 today. Catch the combination post-merge — explicit
|
|
355
349
|
// codec overrides a layered default and either resolution must end
|
|
@@ -408,7 +402,15 @@ function canonicalJson(value) {
|
|
|
408
402
|
return '[' + value.map(canonicalJson).join(',') + ']';
|
|
409
403
|
}
|
|
410
404
|
const record = value;
|
|
411
|
-
|
|
405
|
+
// Skip undefined-valued keys (mirror `JSON.stringify` object semantics,
|
|
406
|
+
// which omit undefined members). Without this, `canonicalJson(undefined)`
|
|
407
|
+
// would serialise the literal token `undefined` into the hash input — a
|
|
408
|
+
// latent foot-gun. The registered-cell records are already sparse (see
|
|
409
|
+
// `presetDefaultsCellRecord`), so this is defense-in-depth; the
|
|
410
|
+
// override-path anchors carry no undefined and are unaffected (SVQcoR1K).
|
|
411
|
+
const keys = Object.keys(record)
|
|
412
|
+
.filter((k) => record[k] !== undefined)
|
|
413
|
+
.sort();
|
|
412
414
|
return ('{' +
|
|
413
415
|
keys
|
|
414
416
|
.map((k) => JSON.stringify(k) + ':' + canonicalJson(record[k]))
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import { PdfProfile,
|
|
1
|
+
import { PdfProfile, OptimizeFor } from '../../generated/sdk_spec/enums.js';
|
|
2
2
|
export interface DocumentPdfCompressPresetOptionsInput {
|
|
3
3
|
readonly profile?: PdfProfile;
|
|
4
|
-
readonly
|
|
5
|
-
readonly flattenForms?: boolean;
|
|
4
|
+
readonly grayscale?: boolean;
|
|
6
5
|
}
|
|
7
6
|
export declare class DocumentPdfCompressPresetOptions {
|
|
8
7
|
readonly profile?: PdfProfile;
|
|
9
|
-
readonly
|
|
10
|
-
readonly flattenForms?: boolean;
|
|
8
|
+
readonly grayscale?: boolean;
|
|
11
9
|
private constructor();
|
|
12
10
|
static from(input: DocumentPdfCompressPresetOptionsInput): DocumentPdfCompressPresetOptions;
|
|
13
11
|
static shippedDefaultsFor(level: OptimizeFor): DocumentPdfCompressPresetOptions;
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
// T4a — DocumentPdfCompressPresetOptions leaf DTO.
|
|
2
2
|
//
|
|
3
|
-
// Field set
|
|
4
|
-
//
|
|
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.
|
|
5
8
|
import { shippedDefaultsFor as f3ShippedDefaultsFor } from '../../generated/sdk_spec/presets.js';
|
|
6
9
|
import { translateEnum } from './_translate.js';
|
|
7
10
|
export class DocumentPdfCompressPresetOptions {
|
|
8
11
|
profile;
|
|
9
|
-
|
|
10
|
-
flattenForms;
|
|
12
|
+
grayscale;
|
|
11
13
|
constructor(input) {
|
|
12
14
|
if (input.profile !== undefined)
|
|
13
15
|
this.profile = input.profile;
|
|
14
|
-
if (input.
|
|
15
|
-
this.
|
|
16
|
-
if (input.flattenForms !== undefined)
|
|
17
|
-
this.flattenForms = input.flattenForms;
|
|
16
|
+
if (input.grayscale !== undefined)
|
|
17
|
+
this.grayscale = input.grayscale;
|
|
18
18
|
Object.freeze(this);
|
|
19
19
|
}
|
|
20
20
|
static from(input) {
|
|
@@ -26,10 +26,8 @@ export class DocumentPdfCompressPresetOptions {
|
|
|
26
26
|
const mut = input;
|
|
27
27
|
if ('profile' in cell)
|
|
28
28
|
mut.profile = translateEnum('PdfProfile', cell.profile);
|
|
29
|
-
if ('
|
|
30
|
-
mut.
|
|
31
|
-
if ('flattenForms' in cell)
|
|
32
|
-
mut.flattenForms = cell.flattenForms;
|
|
29
|
+
if ('grayscale' in cell)
|
|
30
|
+
mut.grayscale = cell.grayscale;
|
|
33
31
|
return new DocumentPdfCompressPresetOptions(input);
|
|
34
32
|
}
|
|
35
33
|
}
|
|
@@ -1,18 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ImageMetadataPolicy, ImageFormat, OptimizeFor } from '../../generated/sdk_spec/enums.js';
|
|
2
2
|
export interface ImageCompressPresetOptionsInput {
|
|
3
|
-
readonly mode?: ImageMode;
|
|
4
3
|
readonly quality?: number;
|
|
5
4
|
readonly metadata?: ImageMetadataPolicy;
|
|
6
|
-
readonly iccProfile?: IccProfilePolicy;
|
|
7
|
-
readonly progressive?: boolean;
|
|
8
5
|
readonly outputFormat?: ImageFormat;
|
|
9
6
|
}
|
|
10
7
|
export declare class ImageCompressPresetOptions {
|
|
11
|
-
readonly mode?: ImageMode;
|
|
12
8
|
readonly quality?: number;
|
|
13
9
|
readonly metadata?: ImageMetadataPolicy;
|
|
14
|
-
readonly iccProfile?: IccProfilePolicy;
|
|
15
|
-
readonly progressive?: boolean;
|
|
16
10
|
readonly outputFormat?: ImageFormat;
|
|
17
11
|
private constructor();
|
|
18
12
|
/**
|
|
@@ -26,10 +20,9 @@ export declare class ImageCompressPresetOptions {
|
|
|
26
20
|
* the given level. Reads the F3 PRESETS matrix and translates member
|
|
27
21
|
* names to wire backing values.
|
|
28
22
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* `quality`, so shipping a default would mislead callers.
|
|
23
|
+
* Since the v2.80.0 honesty pass the worker is lossy-only, so every
|
|
24
|
+
* level ships a concrete `quality` (Size 65 / Balanced 80 / Quality 92),
|
|
25
|
+
* `metadata: All`, and `outputFormat: Original`.
|
|
33
26
|
*/
|
|
34
27
|
static shippedDefaultsFor(level: OptimizeFor): ImageCompressPresetOptions;
|
|
35
28
|
}
|
|
@@ -7,32 +7,26 @@
|
|
|
7
7
|
// values ARE the wire backing values — so the leaf DTO is wire-compatible
|
|
8
8
|
// once the resolver (T4b) snake_cases the property names.
|
|
9
9
|
//
|
|
10
|
-
// Field set (
|
|
11
|
-
// (
|
|
12
|
-
// `
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
10
|
+
// Field set (3) per contracts v2.80.0 compress.image honesty pass (Option B,
|
|
11
|
+
// lossy-only): image = (quality, metadata, outputFormat).
|
|
12
|
+
// `mode` + `iccProfile` were REMOVED — the worker is lossy-only and always
|
|
13
|
+
// strips metadata, so advertising a lossless mode or ICC-profile policy was
|
|
14
|
+
// an over-claim. `progressive` is still a per-JPEG wire option but is no
|
|
15
|
+
// longer carried in the preset cell. `width`/`height`/`fit`/`autoOrient`
|
|
16
|
+
// were removed earlier — the image-compress worker never resized (resize-fit
|
|
17
|
+
// lives on thumbnail/convert; video keeps its own fit). Per-call knobs are
|
|
18
|
+
// deliberately excluded — they belong on the per-call argument shape.
|
|
16
19
|
import { shippedDefaultsFor as f3ShippedDefaultsFor } from '../../generated/sdk_spec/presets.js';
|
|
17
20
|
import { translateEnum } from './_translate.js';
|
|
18
21
|
export class ImageCompressPresetOptions {
|
|
19
|
-
mode;
|
|
20
22
|
quality;
|
|
21
23
|
metadata;
|
|
22
|
-
iccProfile;
|
|
23
|
-
progressive;
|
|
24
24
|
outputFormat;
|
|
25
25
|
constructor(input) {
|
|
26
|
-
if (input.mode !== undefined)
|
|
27
|
-
this.mode = input.mode;
|
|
28
26
|
if (input.quality !== undefined)
|
|
29
27
|
this.quality = input.quality;
|
|
30
28
|
if (input.metadata !== undefined)
|
|
31
29
|
this.metadata = input.metadata;
|
|
32
|
-
if (input.iccProfile !== undefined)
|
|
33
|
-
this.iccProfile = input.iccProfile;
|
|
34
|
-
if (input.progressive !== undefined)
|
|
35
|
-
this.progressive = input.progressive;
|
|
36
30
|
if (input.outputFormat !== undefined)
|
|
37
31
|
this.outputFormat = input.outputFormat;
|
|
38
32
|
Object.freeze(this);
|
|
@@ -50,25 +44,18 @@ export class ImageCompressPresetOptions {
|
|
|
50
44
|
* the given level. Reads the F3 PRESETS matrix and translates member
|
|
51
45
|
* names to wire backing values.
|
|
52
46
|
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* `quality`, so shipping a default would mislead callers.
|
|
47
|
+
* Since the v2.80.0 honesty pass the worker is lossy-only, so every
|
|
48
|
+
* level ships a concrete `quality` (Size 65 / Balanced 80 / Quality 92),
|
|
49
|
+
* `metadata: All`, and `outputFormat: Original`.
|
|
57
50
|
*/
|
|
58
51
|
static shippedDefaultsFor(level) {
|
|
59
52
|
const cell = f3ShippedDefaultsFor('image_compress', level);
|
|
60
53
|
const input = {};
|
|
61
54
|
const mut = input;
|
|
62
|
-
if ('mode' in cell)
|
|
63
|
-
mut.mode = translateEnum('ImageMode', cell.mode);
|
|
64
55
|
if ('quality' in cell)
|
|
65
56
|
mut.quality = cell.quality;
|
|
66
57
|
if ('metadata' in cell)
|
|
67
58
|
mut.metadata = translateEnum('ImageMetadataPolicy', cell.metadata);
|
|
68
|
-
if ('iccProfile' in cell)
|
|
69
|
-
mut.iccProfile = translateEnum('IccProfilePolicy', cell.iccProfile);
|
|
70
|
-
if ('progressive' in cell)
|
|
71
|
-
mut.progressive = cell.progressive;
|
|
72
59
|
if ('outputFormat' in cell)
|
|
73
60
|
mut.outputFormat = translateEnum('ImageFormat', cell.outputFormat);
|
|
74
61
|
return new ImageCompressPresetOptions(input);
|
|
@@ -13,7 +13,7 @@ export { DocumentPdfCompressPresetOptions, type DocumentPdfCompressPresetOptions
|
|
|
13
13
|
export { DocumentOfficeCompressPresetOptions, type DocumentOfficeCompressPresetOptionsInput, } from './document_office_compress.js';
|
|
14
14
|
export { DocumentOdfCompressPresetOptions, type DocumentOdfCompressPresetOptionsInput, } from './document_odf_compress.js';
|
|
15
15
|
export { DocumentEpubCompressPresetOptions, type DocumentEpubCompressPresetOptionsInput, } from './document_epub_compress.js';
|
|
16
|
-
export { OptimizeFor,
|
|
16
|
+
export { OptimizeFor, ImageMetadataPolicy, ImageFormat, VideoCodec, VideoPreset, VideoFit, AudioBitrate, AudioCodec, AudioSampleRate, PdfProfile, PdfColorspace, } from '../../generated/sdk_spec/enums.js';
|
|
17
17
|
/** Supported media×op pairs for preset cells in T4a. Compress-only. */
|
|
18
18
|
export type PresetMedia = 'image' | 'audio' | 'video' | 'document_pdf' | 'document_office' | 'document_odf' | 'document_epub';
|
|
19
19
|
export type PresetOp = 'compress';
|
|
@@ -23,6 +23,26 @@ export type PresetOp = 'compress';
|
|
|
23
23
|
* narrows the return automatically via the overload set below.
|
|
24
24
|
*/
|
|
25
25
|
export type AnyPresetOptions = ImageCompressPresetOptions | AudioCompressPresetOptions | VideoCompressPresetOptions | DocumentPdfCompressPresetOptions | DocumentOfficeCompressPresetOptions | DocumentOdfCompressPresetOptions | DocumentEpubCompressPresetOptions;
|
|
26
|
+
/**
|
|
27
|
+
* Per-cell field-merge: parent fields ⊕ child fields where defined.
|
|
28
|
+
* Re-construct the leaf DTO via the matching `<LeafClass>.from(merged)`
|
|
29
|
+
* call so the result is a freshly-frozen `*PresetOptions` instance —
|
|
30
|
+
* NOT a mutated reference into either input. Used by
|
|
31
|
+
* {@link PresetDefaults.merge} when both parent and child registered
|
|
32
|
+
* the same `(cellKey, level)` tuple.
|
|
33
|
+
*
|
|
34
|
+
* `definedFieldsOf` filters undefined values out of each instance
|
|
35
|
+
* BEFORE the merge: with TS `useDefineForClassFields` (the ES2022
|
|
36
|
+
* default), `readonly outputFormat?: ImageFormat` declarations initialise
|
|
37
|
+
* the field as an enumerable own property with value `undefined` BEFORE
|
|
38
|
+
* the ctor body runs. A naive `Object.assign({}, parent, child)`
|
|
39
|
+
* therefore lets child's `undefined` overwrite parent's defined value
|
|
40
|
+
* — caught by CI on PR #125 first run. Filter-then-spread restores
|
|
41
|
+
* the documented merge-not-replace semantics.
|
|
42
|
+
*
|
|
43
|
+
* @internal
|
|
44
|
+
*/
|
|
45
|
+
export declare function definedFieldsOf<T extends object>(opts: T): Partial<Record<string, unknown>>;
|
|
26
46
|
export declare class PresetDefaults {
|
|
27
47
|
private readonly cells;
|
|
28
48
|
private constructor();
|
|
@@ -41,7 +41,7 @@ export { DocumentOfficeCompressPresetOptions, } from './document_office_compress
|
|
|
41
41
|
export { DocumentOdfCompressPresetOptions, } from './document_odf_compress.js';
|
|
42
42
|
export { DocumentEpubCompressPresetOptions, } from './document_epub_compress.js';
|
|
43
43
|
// Re-export ergonomic enums for callers (single canonical path).
|
|
44
|
-
export { OptimizeFor,
|
|
44
|
+
export { OptimizeFor, ImageMetadataPolicy, ImageFormat, VideoCodec, VideoPreset, VideoFit, AudioBitrate, AudioCodec, AudioSampleRate, PdfProfile, PdfColorspace, } from '../../generated/sdk_spec/enums.js';
|
|
45
45
|
function cellKeyOf(media, op) {
|
|
46
46
|
return `${media}_${op}`;
|
|
47
47
|
}
|
|
@@ -55,8 +55,8 @@ function cellKeyOf(media, op) {
|
|
|
55
55
|
*
|
|
56
56
|
* `definedFieldsOf` filters undefined values out of each instance
|
|
57
57
|
* BEFORE the merge: with TS `useDefineForClassFields` (the ES2022
|
|
58
|
-
* default), `readonly
|
|
59
|
-
* field as an enumerable own property with value `undefined` BEFORE
|
|
58
|
+
* default), `readonly outputFormat?: ImageFormat` declarations initialise
|
|
59
|
+
* the field as an enumerable own property with value `undefined` BEFORE
|
|
60
60
|
* the ctor body runs. A naive `Object.assign({}, parent, child)`
|
|
61
61
|
* therefore lets child's `undefined` overwrite parent's defined value
|
|
62
62
|
* — caught by CI on PR #125 first run. Filter-then-spread restores
|
|
@@ -64,7 +64,7 @@ function cellKeyOf(media, op) {
|
|
|
64
64
|
*
|
|
65
65
|
* @internal
|
|
66
66
|
*/
|
|
67
|
-
function definedFieldsOf(opts) {
|
|
67
|
+
export function definedFieldsOf(opts) {
|
|
68
68
|
const out = {};
|
|
69
69
|
for (const key of Object.keys(opts)) {
|
|
70
70
|
const value = opts[key];
|
package/dist/errors.d.ts
CHANGED
|
@@ -461,3 +461,28 @@ export declare class GislSinkError extends GislError {
|
|
|
461
461
|
readonly reason: GislSinkErrorReason;
|
|
462
462
|
});
|
|
463
463
|
}
|
|
464
|
+
/**
|
|
465
|
+
* A terminal item failure in {@link RunResult.failed} — an input whose job did
|
|
466
|
+
* not reach `completed`. Stored in `ItemFailure.error` so a caller can branch on
|
|
467
|
+
* the failure reason WITHOUT string-parsing.
|
|
468
|
+
*
|
|
469
|
+
* - `state`: the terminal lifecycle state (`failed` / `expired` / `cancelled` /
|
|
470
|
+
* `partially_failed` / `paused_insufficient_credits`, or a per-job
|
|
471
|
+
* non-`completed` status).
|
|
472
|
+
* - `errorMessage` / `errorCode`: the human + machine fields read from the first
|
|
473
|
+
* failing operation (`OperationResponse.error_message` / `.error_code`). BOTH
|
|
474
|
+
* are absent for non-`failed` terminal states — cancel / expire / credit-pause
|
|
475
|
+
* carry only the bare `state`.
|
|
476
|
+
*
|
|
477
|
+
* `message` is `state` optionally suffixed `: errorMessage`, preserving the
|
|
478
|
+
* pre-typed string exactly (an empty-string `errorMessage` still adds the colon).
|
|
479
|
+
*
|
|
480
|
+
* Mirrors the PHP `Gisl\Sdk\Errors\GislItemFailedError`.
|
|
481
|
+
*/
|
|
482
|
+
export declare class GislItemFailedError extends GislError {
|
|
483
|
+
readonly key: string | null;
|
|
484
|
+
readonly state: string;
|
|
485
|
+
readonly errorMessage?: string;
|
|
486
|
+
readonly errorCode?: string;
|
|
487
|
+
constructor(key: string | null, state: string, errorMessage?: string, errorCode?: string);
|
|
488
|
+
}
|
package/dist/errors.js
CHANGED
|
@@ -503,3 +503,37 @@ export class GislSinkError extends GislError {
|
|
|
503
503
|
this.reason = options.reason;
|
|
504
504
|
}
|
|
505
505
|
}
|
|
506
|
+
/**
|
|
507
|
+
* A terminal item failure in {@link RunResult.failed} — an input whose job did
|
|
508
|
+
* not reach `completed`. Stored in `ItemFailure.error` so a caller can branch on
|
|
509
|
+
* the failure reason WITHOUT string-parsing.
|
|
510
|
+
*
|
|
511
|
+
* - `state`: the terminal lifecycle state (`failed` / `expired` / `cancelled` /
|
|
512
|
+
* `partially_failed` / `paused_insufficient_credits`, or a per-job
|
|
513
|
+
* non-`completed` status).
|
|
514
|
+
* - `errorMessage` / `errorCode`: the human + machine fields read from the first
|
|
515
|
+
* failing operation (`OperationResponse.error_message` / `.error_code`). BOTH
|
|
516
|
+
* are absent for non-`failed` terminal states — cancel / expire / credit-pause
|
|
517
|
+
* carry only the bare `state`.
|
|
518
|
+
*
|
|
519
|
+
* `message` is `state` optionally suffixed `: errorMessage`, preserving the
|
|
520
|
+
* pre-typed string exactly (an empty-string `errorMessage` still adds the colon).
|
|
521
|
+
*
|
|
522
|
+
* Mirrors the PHP `Gisl\Sdk\Errors\GislItemFailedError`.
|
|
523
|
+
*/
|
|
524
|
+
export class GislItemFailedError extends GislError {
|
|
525
|
+
key;
|
|
526
|
+
state;
|
|
527
|
+
errorMessage;
|
|
528
|
+
errorCode;
|
|
529
|
+
constructor(key, state, errorMessage, errorCode) {
|
|
530
|
+
super(state + (errorMessage !== undefined ? `: ${errorMessage}` : ''));
|
|
531
|
+
this.name = 'GislItemFailedError';
|
|
532
|
+
this.key = key;
|
|
533
|
+
this.state = state;
|
|
534
|
+
if (errorMessage !== undefined)
|
|
535
|
+
this.errorMessage = errorMessage;
|
|
536
|
+
if (errorCode !== undefined)
|
|
537
|
+
this.errorCode = errorCode;
|
|
538
|
+
}
|
|
539
|
+
}
|