@giveitsmaller/sdk 0.18.0 → 0.19.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 CHANGED
@@ -67,7 +67,7 @@ Docs are published in the [giveitsmaller-sdks](https://github.com/AntonioCS/give
67
67
  - **Webhooks** — [`docs/typescript/webhook.md`](https://github.com/AntonioCS/giveitsmaller-sdks/blob/main/docs/typescript/webhook.md)
68
68
  - **Errors & retry guidance** — [`docs/typescript/errors.md`](https://github.com/AntonioCS/giveitsmaller-sdks/blob/main/docs/typescript/errors.md)
69
69
  - **Troubleshooting** — [`docs/typescript/troubleshooting.md`](https://github.com/AntonioCS/giveitsmaller-sdks/blob/main/docs/typescript/troubleshooting.md)
70
- - **Examples** — compress, thumbnail, convert, merge, archive — [`docs/typescript/examples/`](https://github.com/AntonioCS/giveitsmaller-sdks/tree/main/docs/typescript/examples)
70
+ - **Examples** — compress, thumbnail, convert, merge, archive, watermark, output — [`docs/typescript/examples/`](https://github.com/AntonioCS/giveitsmaller-sdks/tree/main/docs/typescript/examples)
71
71
 
72
72
  ## Contributing: the committed `dist/`
73
73
 
@@ -12,11 +12,12 @@
12
12
  *
13
13
  * Each route cell lists the options the worker HONORS (live) and PLANS (advertised,
14
14
  * not yet honored — gated unavailable). Resize (`width`/`height`/`fit`) is
15
- * **INPUT-keyed**: it lives only on the `same_format[input]` cell but applies on
16
- * EITHER route, gated by input resizability (raster only `svg` is vector and
17
- * carries no resize). So a `png webp + resize` request reads its resize
18
- * capability from `same_format.png` and its transcoder options from
19
- * `format_change.webp`.
15
+ * **INPUT-gated**: since v2.103.0 convert is the resize engine, so the projection
16
+ * lists resize on every `format_change` cell toobut resizability is keyed to the
17
+ * INPUT (raster only `svg` is vector and carries no resize). The lowering reads
18
+ * resize capability from `same_format[input]` on BOTH routes, so a `png → webp +
19
+ * resize` request resizes (png is raster) while an `svg → png + resize` request
20
+ * does NOT (svg's same_format cell has no resize keys).
20
21
  *
21
22
  * This hand table MIRRORS the generated projection and is PINNED to it by
22
23
  * `output-route-conformance.test.ts` (the watermark-capability-gate precedent) —
@@ -12,11 +12,12 @@
12
12
  *
13
13
  * Each route cell lists the options the worker HONORS (live) and PLANS (advertised,
14
14
  * not yet honored — gated unavailable). Resize (`width`/`height`/`fit`) is
15
- * **INPUT-keyed**: it lives only on the `same_format[input]` cell but applies on
16
- * EITHER route, gated by input resizability (raster only `svg` is vector and
17
- * carries no resize). So a `png webp + resize` request reads its resize
18
- * capability from `same_format.png` and its transcoder options from
19
- * `format_change.webp`.
15
+ * **INPUT-gated**: since v2.103.0 convert is the resize engine, so the projection
16
+ * lists resize on every `format_change` cell toobut resizability is keyed to the
17
+ * INPUT (raster only `svg` is vector and carries no resize). The lowering reads
18
+ * resize capability from `same_format[input]` on BOTH routes, so a `png → webp +
19
+ * resize` request resizes (png is raster) while an `svg → png + resize` request
20
+ * does NOT (svg's same_format cell has no resize keys).
20
21
  *
21
22
  * This hand table MIRRORS the generated projection and is PINNED to it by
22
23
  * `output-route-conformance.test.ts` (the watermark-capability-gate precedent) —
@@ -28,6 +29,8 @@
28
29
  import { compressMetadata } from '@giveitsmaller/contracts/operations';
29
30
  /** The resize option keys — input-keyed, raster-only (see module doc). */
30
31
  export const RESIZE_KEYS = ['width', 'height', 'fit'];
32
+ /** Set form of {@link RESIZE_KEYS} for `string`-keyed membership tests. */
33
+ const RESIZE_KEY_SET = new Set(RESIZE_KEYS);
31
34
  /** Image area cap shared by every resizable route (projection `max_output_pixels`). */
32
35
  export const MAX_OUTPUT_PIXELS = 16_000_000;
33
36
  /**
@@ -61,21 +64,21 @@ const EXT_TOKEN = {
61
64
  */
62
65
  export const IMAGE_OUTPUT_ROUTES = {
63
66
  same_format: {
64
- avif: { honored: ['avif_speed', 'fit', 'height', 'metadata', 'output_format', 'quality', 'width'], planned: [] },
65
- gif: { honored: ['fit', 'height', 'metadata', 'output_format', 'quality', 'width'], planned: [] },
66
- jpeg: { honored: ['fit', 'height', 'lossless', 'metadata', 'output_format', 'progressive', 'quality', 'width'], planned: [] },
67
- png: { honored: ['fit', 'height', 'metadata', 'optimization_level', 'output_format', 'quality', 'width'], planned: ['lossy'] },
68
- svg: { honored: ['metadata', 'output_format', 'quality'], planned: [] },
69
- tiff: { honored: ['fit', 'height', 'metadata', 'output_format', 'quality', 'width'], planned: [] },
70
- webp: { honored: ['fit', 'height', 'lossless', 'metadata', 'output_format', 'quality', 'width'], planned: [] },
67
+ avif: { honored: ['auto_orient', 'avif_speed', 'color_profile', 'encoding_mode', 'fit', 'height', 'metadata', 'output_format', 'quality', 'quality_preset', 'target_size_bytes', 'width'], planned: [] },
68
+ gif: { honored: ['auto_orient', 'color_profile', 'fit', 'height', 'metadata', 'output_format', 'quality', 'width'], planned: [] },
69
+ jpeg: { honored: ['auto_orient', 'chroma_subsampling', 'color_profile', 'encoding_mode', 'fit', 'height', 'lossless', 'metadata', 'output_format', 'progressive', 'quality', 'quality_preset', 'target_size_bytes', 'width'], planned: [] },
70
+ png: { honored: ['auto_orient', 'color_profile', 'fit', 'height', 'metadata', 'optimization_level', 'output_format', 'quality', 'width'], planned: [] },
71
+ svg: { honored: ['metadata', 'output_format'], planned: [] },
72
+ tiff: { honored: ['auto_orient', 'color_profile', 'fit', 'height', 'metadata', 'output_format', 'quality', 'width'], planned: [] },
73
+ webp: { honored: ['auto_orient', 'color_profile', 'encoding_mode', 'fit', 'height', 'lossless', 'metadata', 'output_format', 'quality', 'quality_preset', 'target_size_bytes', 'width'], planned: [] },
71
74
  },
72
75
  format_change: {
73
- avif: { honored: ['output_format', 'quality'], planned: [] },
74
- gif: { honored: ['output_format'], planned: [] },
75
- jpeg: { honored: ['background', 'output_format', 'quality'], planned: [] },
76
- png: { honored: ['output_format'], planned: [] },
77
- tiff: { honored: ['output_format'], planned: [] },
78
- webp: { honored: ['output_format', 'quality'], planned: [] },
76
+ avif: { honored: ['auto_orient', 'color_profile', 'fit', 'height', 'output_format', 'quality', 'width'], planned: ['metadata'] },
77
+ gif: { honored: ['auto_orient', 'color_profile', 'fit', 'height', 'output_format', 'width'], planned: ['metadata'] },
78
+ jpeg: { honored: ['auto_orient', 'background', 'color_profile', 'fit', 'height', 'output_format', 'quality', 'width'], planned: ['metadata'] },
79
+ png: { honored: ['auto_orient', 'color_profile', 'fit', 'height', 'output_format', 'width'], planned: ['metadata'] },
80
+ tiff: { honored: ['auto_orient', 'color_profile', 'fit', 'height', 'output_format', 'width'], planned: ['metadata'] },
81
+ webp: { honored: ['auto_orient', 'color_profile', 'fit', 'height', 'output_format', 'quality', 'width'], planned: ['metadata'] },
79
82
  },
80
83
  };
81
84
  /** The bare format token for a MIME type, or undefined if not a known image MIME. */
@@ -114,8 +117,13 @@ export function resolveOutputRoute(inputToken, outputFormat) {
114
117
  const cell = IMAGE_OUTPUT_ROUTES.format_change[outToken];
115
118
  if (cell === undefined)
116
119
  return undefined;
117
- // Input-keyed resize: the format_change cell carries only transcoder options;
118
- // resize capability comes from the INPUT's same_format cell (raster only).
120
+ // Resize is INPUT-gated. Since v2.103.0 convert is the resize engine, so the
121
+ // projection lists width/height/fit on EVERY format_change cell but an SVG
122
+ // INPUT cannot be raster-resized (the convert worker rejects it). So strip the
123
+ // cell's resize keys and re-add only those the INPUT's same_format cell honors:
124
+ // raster inputs carry them, svg does not. The transcoder options (output_format/
125
+ // quality/background) ride the cell directly.
126
+ const transcoderHonored = cell.honored.filter((k) => !RESIZE_KEY_SET.has(k));
119
127
  const inCell = IMAGE_OUTPUT_ROUTES.same_format[inputToken];
120
128
  const resize = inCell ? RESIZE_KEYS.filter((k) => inCell.honored.includes(k)) : [];
121
129
  return {
@@ -123,7 +131,7 @@ export function resolveOutputRoute(inputToken, outputFormat) {
123
131
  sourceOp: 'convert',
124
132
  outputFormatWire: outToken,
125
133
  inputToken,
126
- honored: new Set([...cell.honored, ...resize]),
134
+ honored: new Set([...transcoderHonored, ...resize]),
127
135
  planned: new Set(cell.planned),
128
136
  };
129
137
  }
@@ -29,8 +29,18 @@ export interface ConvertOptions {
29
29
  trim_end?: number;
30
30
  /** Output frame rate for video → GIF. */
31
31
  fps?: number;
32
- /** Max output width in pixels (video → GIF downscale cap). */
32
+ /** Resize width in px. Image convert: resize-on-convert (1-16384, width*height ≤ 16MP, v2.103.0). Video → GIF: downscale cap. */
33
33
  width?: number;
34
+ /** Resize height in px for image convert (1-16384, v2.103.0). Omit for width-only / aspect-preserving resize. Not honored for SVG input. */
35
+ height?: number;
36
+ /** Resize mode for image convert (applies when width or height is set, v2.103.0). `max` never upscales. */
37
+ fit?: 'max' | 'crop' | 'scale';
38
+ /** Metadata policy for image convert (`strip` removes EXIF/IPTC/XMP, `keep` preserves). PLANNED on convert.image (v2.106.0). */
39
+ metadata?: 'strip' | 'keep';
40
+ /** ICC colour-profile handling for image convert (`keep`/`srgb`/`strip`). PLANNED on convert.image (v2.112.0). */
41
+ color_profile?: 'keep' | 'srgb' | 'strip';
42
+ /** Auto-rotate per EXIF orientation for image convert. STABLE since v2.120.0. */
43
+ auto_orient?: boolean;
34
44
  /** GIF palette size (2-256). */
35
45
  max_colors?: number;
36
46
  /** GIF loop count (0 infinite, N>0 N times, -1 once). */
@@ -55,6 +65,8 @@ export interface ThumbnailOptions {
55
65
  format?: 'jpg' | 'png' | 'webp';
56
66
  /** Output quality for lossy thumbnail formats (image input). */
57
67
  quality?: number;
68
+ /** Background fill colour (hex) for transparency flattened to JPG output (v2.118.0). */
69
+ background?: string;
58
70
  /** Frame timestamp for video input (e.g. '00:00:01'). */
59
71
  timestamp?: string;
60
72
  /** Document source: a printed page or the cover. */
@@ -98,8 +110,27 @@ export interface WatermarkOptions {
98
110
  }
99
111
  /** Resize mode (contract `fit` enum, v2.97.0). */
100
112
  export type OutputFit = 'max' | 'crop' | 'scale';
101
- /** Metadata policy (contract `metadata` enum). Both values stable since v2.102.0 (keep/strip un-parked). */
102
- export type OutputMetadata = 'all' | 'keep';
113
+ /**
114
+ * Metadata policy (contract `metadata` enum). `strip` (default) removes all
115
+ * EXIF/IPTC/XMP; `keep` preserves them. Honored on same_format; PLANNED on
116
+ * format_change (v2.106.0). Renamed in contracts v2.107.0 — the legacy `all`
117
+ * token is now a DEPRECATED alias of `strip` (still accepted on the wire, emits
118
+ * a Deprecation header); use `strip`.
119
+ */
120
+ export type OutputMetadata = 'strip' | 'keep';
121
+ /**
122
+ * Compression mode on the optimiser (same_format) route (contract `encoding_mode`
123
+ * enum). `quality` (default) drives the encode by the quality slider; `target_size`
124
+ * targets a byte budget via the worker's encode-measure loop — STABLE since
125
+ * contracts v2.108.0 (jpeg/webp/avif).
126
+ */
127
+ export type OutputEncodingMode = 'quality' | 'target_size';
128
+ /** Chroma subsampling for JPEG output (contract `chroma_subsampling` enum, v2.110.0). `420` smallest → `444` highest fidelity. Honored: same_format jpeg only. */
129
+ export type OutputChromaSubsampling = '420' | '422' | '444';
130
+ /** ICC colour-profile handling (contract `color_profile` enum, v2.112.0). `keep` preserves the embedded profile; `srgb` converts to sRGB; `strip` removes it. Route/value availability is gated by the output lowering. */
131
+ export type OutputColorProfile = 'keep' | 'srgb' | 'strip';
132
+ /** Named quality preset (contract `quality_preset` enum, v2.148.0) — an alternative to the numeric `quality` slider. `best` highest fidelity → `low` smallest. Honored: same_format avif/jpeg/webp. */
133
+ export type OutputQualityPreset = 'best' | 'good' | 'fair' | 'low';
103
134
  /**
104
135
  * Options for the file-first `output()` image transform. The KEY SET is the
105
136
  * UNION of every image route's honored + planned option keys (image-output-routes
@@ -110,8 +141,16 @@ export type OutputMetadata = 'all' | 'keep';
110
141
  * `fit`) is honored on raster routes; `height` is optional (width-only resize).
111
142
  */
112
143
  export interface OutputOptions {
113
- /** Output quality for lossy formats (1-100). Honored: avif/jpeg/webp routes. */
144
+ /** Output quality (1-100). Honored on same-format avif/jpeg/png/webp and lossy format-change routes. */
114
145
  quality?: number;
146
+ /** Named quality preset (`best`/`good`/`fair`/`low`) — an alternative to the numeric `quality` slider (v2.148.0). Honored: same_format avif/jpeg/webp. */
147
+ quality_preset?: OutputQualityPreset;
148
+ /** Compression mode (same_format avif/jpeg/webp). `quality` (default) or `target_size` — STABLE since v2.108.0. */
149
+ encoding_mode?: OutputEncodingMode;
150
+ /** Target output size in bytes (≥1024) for `encoding_mode: 'target_size'`. STABLE since v2.108.0. Honored: same_format avif/jpeg/webp. */
151
+ target_size_bytes?: number;
152
+ /** Chroma subsampling for JPEG output. Honored: same_format jpeg only (v2.110.0). */
153
+ chroma_subsampling?: OutputChromaSubsampling;
115
154
  /** Resize target width in px (1-16384; width*height <= 16MP). */
116
155
  width?: number;
117
156
  /** Resize target height in px (optional — width-only resize preserves aspect). */
@@ -126,12 +165,14 @@ export interface OutputOptions {
126
165
  optimization_level?: number;
127
166
  /** AVIF encode speed. Honored: same_format avif only. */
128
167
  avif_speed?: number;
129
- /** Metadata policy. Honored: same_format routes (both `all` and `keep` since v2.102.0). */
168
+ /** Metadata policy (`strip` default / `keep`). Honored: same_format; PLANNED on format_change (v2.106.0). */
130
169
  metadata?: OutputMetadata;
170
+ /** ICC colour-profile handling (`keep`/`srgb`/`strip`). Availability depends on route and value. */
171
+ color_profile?: OutputColorProfile;
172
+ /** Auto-rotate per EXIF orientation. STABLE since v2.120.0 (both routes). */
173
+ auto_orient?: boolean;
131
174
  /** JPEG/WebP lossless. Honored: same_format jpeg/webp (stable since v2.101.0). */
132
175
  lossless?: boolean;
133
- /** Lossy PNG quantization. PLANNED (gated unavailable; licence-gated). */
134
- lossy?: boolean;
135
176
  }
136
177
  /**
137
178
  * The user-supplyable option keys per verb (excludes positional-owned keys).
@@ -139,9 +180,9 @@ export interface OutputOptions {
139
180
  * positional-owned keys equals the contract `operationOptionKeys(metadata)`.
140
181
  */
141
182
  export declare const VERB_OPTION_KEYS: {
142
- readonly convert: readonly ["quality", "background", "crf", "trim_start", "trim_end", "fps", "width", "max_colors", "loop", "dither", "bitrate", "pages", "dpi"];
143
- readonly thumbnail: readonly ["width", "height", "fit", "format", "quality", "timestamp", "source", "page"];
183
+ readonly convert: readonly ["quality", "background", "crf", "trim_start", "trim_end", "fps", "width", "height", "fit", "metadata", "color_profile", "auto_orient", "max_colors", "loop", "dither", "bitrate", "pages", "dpi"];
184
+ readonly thumbnail: readonly ["width", "height", "fit", "format", "quality", "background", "timestamp", "source", "page"];
144
185
  readonly textWatermark: readonly ["font_size", "color", "font_family", "rotation", "watermark_mode", "tile_spacing", "anchor", "margin_x", "margin_y", "opacity"];
145
186
  readonly watermark: readonly ["anchor", "margin_x", "margin_y", "opacity", "overlay_width"];
146
- readonly output: readonly ["quality", "width", "height", "fit", "background", "progressive", "optimization_level", "avif_speed", "metadata", "lossless", "lossy"];
187
+ readonly output: readonly ["quality", "quality_preset", "encoding_mode", "target_size_bytes", "chroma_subsampling", "width", "height", "fit", "background", "progressive", "optimization_level", "avif_speed", "metadata", "color_profile", "auto_orient", "lossless"];
147
188
  };
@@ -15,11 +15,11 @@
15
15
  * Mirrored by the PHP array-shape docblocks — keep in lockstep.
16
16
  */
17
17
  const CONVERT_OPTION_KEYS = [
18
- 'quality', 'background', 'crf', 'trim_start', 'trim_end', 'fps', 'width',
19
- 'max_colors', 'loop', 'dither', 'bitrate', 'pages', 'dpi',
18
+ 'quality', 'background', 'crf', 'trim_start', 'trim_end', 'fps', 'width', 'height', 'fit', 'metadata',
19
+ 'color_profile', 'auto_orient', 'max_colors', 'loop', 'dither', 'bitrate', 'pages', 'dpi',
20
20
  ];
21
21
  const THUMBNAIL_OPTION_KEYS = [
22
- 'width', 'height', 'fit', 'format', 'quality', 'timestamp', 'source', 'page',
22
+ 'width', 'height', 'fit', 'format', 'quality', 'background', 'timestamp', 'source', 'page',
23
23
  ];
24
24
  const TEXT_WATERMARK_OPTION_KEYS = [
25
25
  'font_size', 'color', 'font_family', 'rotation', 'watermark_mode',
@@ -29,8 +29,9 @@ const WATERMARK_OPTION_KEYS = [
29
29
  'anchor', 'margin_x', 'margin_y', 'opacity', 'overlay_width',
30
30
  ];
31
31
  const OUTPUT_OPTION_KEYS = [
32
- 'quality', 'width', 'height', 'fit', 'background', 'progressive',
33
- 'optimization_level', 'avif_speed', 'metadata', 'lossless', 'lossy',
32
+ 'quality', 'quality_preset', 'encoding_mode', 'target_size_bytes', 'chroma_subsampling', 'width', 'height', 'fit',
33
+ 'background', 'progressive', 'optimization_level', 'avif_speed', 'metadata',
34
+ 'color_profile', 'auto_orient', 'lossless',
34
35
  ];
35
36
  const _convertKeysMatch = true;
36
37
  const _thumbnailKeysMatch = true;
@@ -9,8 +9,8 @@ import { type OperationMetadata } from '@giveitsmaller/contracts/operations';
9
9
  * sidecars (the same contract-anchored source the wire-key conformance guard
10
10
  * uses), so it can never silently drift from the contract.
11
11
  *
12
- * SCOPE: `convert` / `thumbnail` / `textWatermark` / `watermark` only. `compress`
13
- * is deliberately EXCLUDED — its bag legitimately carries SDK-only keys
12
+ * SCOPE: `convert` / `thumbnail` / `textWatermark` / `watermark` / `output`
13
+ * only. `compress` is deliberately EXCLUDED — its bag legitimately carries SDK-only keys
14
14
  * (`optimize`, `presetOverrides`) and camelCase resolver aliases (`targetSize`,
15
15
  * `outputFormat`) that are not `operationOptionKeys(compressMetadata)`; it has its
16
16
  * own `unknown_field` validation through the preset resolver.
@@ -11,8 +11,8 @@ import { VERB_OPTION_KEYS } from './option_types.js';
11
11
  * sidecars (the same contract-anchored source the wire-key conformance guard
12
12
  * uses), so it can never silently drift from the contract.
13
13
  *
14
- * SCOPE: `convert` / `thumbnail` / `textWatermark` / `watermark` only. `compress`
15
- * is deliberately EXCLUDED — its bag legitimately carries SDK-only keys
14
+ * SCOPE: `convert` / `thumbnail` / `textWatermark` / `watermark` / `output`
15
+ * only. `compress` is deliberately EXCLUDED — its bag legitimately carries SDK-only keys
16
16
  * (`optimize`, `presetOverrides`) and camelCase resolver aliases (`targetSize`,
17
17
  * `outputFormat`) that are not `operationOptionKeys(compressMetadata)`; it has its
18
18
  * own `unknown_field` validation through the preset resolver.
@@ -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.4";
12
+ export declare const PRESET_VERSION: "1.6";
13
13
  /**
14
14
  * Inputs to {@link resolveCompressOptions}. `media` selects which leaf
15
15
  * DTO drives sdkDefault + clientDefault lookups + invalid-combo
@@ -63,7 +63,6 @@ const WIRE_ALIASES = Object.freeze({
63
63
  sampleRate: 'sample_rate',
64
64
  audioCodec: 'audio_codec',
65
65
  audioBitrate: 'audio_bitrate',
66
- imageQuality: 'image_quality',
67
66
  stripMacros: 'strip_macros',
68
67
  stripHiddenData: 'strip_hidden_data',
69
68
  stripUnusedFonts: 'strip_unused_fonts',
@@ -254,9 +253,9 @@ const MEDIA_FIELDS = Object.freeze({
254
253
  audio: new Set(['bitrate', 'channels', 'sampleRate', 'normalize']),
255
254
  video: new Set(['codec', 'targetSize', 'crf', 'preset', 'width', 'height', 'fit', 'fps', 'faststart', 'audioCodec', 'audioBitrate']),
256
255
  document_pdf: new Set(['profile', 'grayscale']),
257
- document_office: new Set(['imageQuality', 'stripMacros', 'stripHiddenData', 'stripUnusedFonts']),
258
- document_odf: new Set(['imageQuality', 'stripMetadata', 'stripUnusedStyles']),
259
- document_epub: new Set(['imageQuality', 'fontSubsetting', 'stripUnusedCss']),
256
+ document_office: new Set(['stripMacros', 'stripHiddenData', 'stripUnusedFonts']),
257
+ document_odf: new Set(['stripMetadata', 'stripUnusedStyles']),
258
+ document_epub: new Set(['fontSubsetting', 'stripUnusedCss']),
260
259
  });
261
260
  function detectMismatchedOverrides(media, overrides) {
262
261
  const expected = MEDIA_FIELDS[media];
@@ -323,9 +322,9 @@ export const KNOWN_WIRE_FIELDS = Object.freeze({
323
322
  audio: new Set(['bitrate', 'channels', 'sample_rate', 'normalize', 'trim_start', 'trim_end']),
324
323
  video: new Set(['codec', 'encoding_mode', 'crf', 'target_size_bytes', 'preset', 'width', 'height', 'fit', 'fps', 'faststart', 'audio_codec', 'audio_bitrate', 'trim_start', 'trim_end']),
325
324
  document_pdf: new Set(['profile', 'grayscale']),
326
- document_office: new Set(['image_quality', 'strip_macros', 'strip_hidden_data', 'strip_unused_fonts']),
327
- document_odf: new Set(['image_quality', 'strip_metadata', 'strip_unused_styles']),
328
- document_epub: new Set(['image_quality', 'font_subsetting', 'strip_unused_css']),
325
+ document_office: new Set(['strip_macros', 'strip_hidden_data', 'strip_unused_fonts']),
326
+ document_odf: new Set(['strip_metadata', 'strip_unused_styles']),
327
+ document_epub: new Set(['font_subsetting', 'strip_unused_css']),
329
328
  });
330
329
  function validateMerged(media, merged, explicitKeys, winners) {
331
330
  // Unknown-field defence-in-depth: every key must belong to the
@@ -1,11 +1,9 @@
1
1
  import { OptimizeFor } from '../../generated/sdk_spec/enums.js';
2
2
  export interface DocumentEpubCompressPresetOptionsInput {
3
- readonly imageQuality?: number;
4
3
  readonly fontSubsetting?: boolean;
5
4
  readonly stripUnusedCss?: boolean;
6
5
  }
7
6
  export declare class DocumentEpubCompressPresetOptions {
8
- readonly imageQuality?: number;
9
7
  readonly fontSubsetting?: boolean;
10
8
  readonly stripUnusedCss?: boolean;
11
9
  private constructor();
@@ -1,15 +1,12 @@
1
1
  // T4a — DocumentEpubCompressPresetOptions leaf DTO.
2
2
  //
3
- // Field set per ticket VhIj4S7T: EPUB = 3 fields
4
- // (imageQuality, fontSubsetting, stripUnusedCss). Primitives only.
3
+ // Field set per ticket VhIj4S7T: EPUB = 2 fields
4
+ // (fontSubsetting, stripUnusedCss). Primitives only.
5
5
  import { shippedDefaultsFor as f3ShippedDefaultsFor } from '../../generated/sdk_spec/presets.js';
6
6
  export class DocumentEpubCompressPresetOptions {
7
- imageQuality;
8
7
  fontSubsetting;
9
8
  stripUnusedCss;
10
9
  constructor(input) {
11
- if (input.imageQuality !== undefined)
12
- this.imageQuality = input.imageQuality;
13
10
  if (input.fontSubsetting !== undefined)
14
11
  this.fontSubsetting = input.fontSubsetting;
15
12
  if (input.stripUnusedCss !== undefined)
@@ -23,8 +20,6 @@ export class DocumentEpubCompressPresetOptions {
23
20
  const cell = f3ShippedDefaultsFor('document_epub_compress', level);
24
21
  const input = {};
25
22
  const mut = input;
26
- if ('imageQuality' in cell)
27
- mut.imageQuality = cell.imageQuality;
28
23
  if ('fontSubsetting' in cell)
29
24
  mut.fontSubsetting = cell.fontSubsetting;
30
25
  if ('stripUnusedCss' in cell)
@@ -1,11 +1,9 @@
1
1
  import { OptimizeFor } from '../../generated/sdk_spec/enums.js';
2
2
  export interface DocumentOdfCompressPresetOptionsInput {
3
- readonly imageQuality?: number;
4
3
  readonly stripMetadata?: boolean;
5
4
  readonly stripUnusedStyles?: boolean;
6
5
  }
7
6
  export declare class DocumentOdfCompressPresetOptions {
8
- readonly imageQuality?: number;
9
7
  readonly stripMetadata?: boolean;
10
8
  readonly stripUnusedStyles?: boolean;
11
9
  private constructor();
@@ -1,15 +1,12 @@
1
1
  // T4a — DocumentOdfCompressPresetOptions leaf DTO.
2
2
  //
3
- // Field set per ticket VhIj4S7T: ODF = 3 fields
4
- // (imageQuality, stripMetadata, stripUnusedStyles). Primitives only.
3
+ // Field set per ticket VhIj4S7T: ODF = 2 fields
4
+ // (stripMetadata, stripUnusedStyles). Primitives only.
5
5
  import { shippedDefaultsFor as f3ShippedDefaultsFor } from '../../generated/sdk_spec/presets.js';
6
6
  export class DocumentOdfCompressPresetOptions {
7
- imageQuality;
8
7
  stripMetadata;
9
8
  stripUnusedStyles;
10
9
  constructor(input) {
11
- if (input.imageQuality !== undefined)
12
- this.imageQuality = input.imageQuality;
13
10
  if (input.stripMetadata !== undefined)
14
11
  this.stripMetadata = input.stripMetadata;
15
12
  if (input.stripUnusedStyles !== undefined)
@@ -23,8 +20,6 @@ export class DocumentOdfCompressPresetOptions {
23
20
  const cell = f3ShippedDefaultsFor('document_odf_compress', level);
24
21
  const input = {};
25
22
  const mut = input;
26
- if ('imageQuality' in cell)
27
- mut.imageQuality = cell.imageQuality;
28
23
  if ('stripMetadata' in cell)
29
24
  mut.stripMetadata = cell.stripMetadata;
30
25
  if ('stripUnusedStyles' in cell)
@@ -1,12 +1,10 @@
1
1
  import { OptimizeFor } from '../../generated/sdk_spec/enums.js';
2
2
  export interface DocumentOfficeCompressPresetOptionsInput {
3
- readonly imageQuality?: number;
4
3
  readonly stripMacros?: boolean;
5
4
  readonly stripHiddenData?: boolean;
6
5
  readonly stripUnusedFonts?: boolean;
7
6
  }
8
7
  export declare class DocumentOfficeCompressPresetOptions {
9
- readonly imageQuality?: number;
10
8
  readonly stripMacros?: boolean;
11
9
  readonly stripHiddenData?: boolean;
12
10
  readonly stripUnusedFonts?: boolean;
@@ -1,17 +1,14 @@
1
1
  // T4a — DocumentOfficeCompressPresetOptions leaf DTO.
2
2
  //
3
- // Field set per ticket VhIj4S7T: office = 4 fields
4
- // (imageQuality, stripMacros, stripHiddenData, stripUnusedFonts).
3
+ // Field set per ticket VhIj4S7T: office = 3 fields
4
+ // (stripMacros, stripHiddenData, stripUnusedFonts).
5
5
  // All primitive values — no enum translation needed.
6
6
  import { shippedDefaultsFor as f3ShippedDefaultsFor } from '../../generated/sdk_spec/presets.js';
7
7
  export class DocumentOfficeCompressPresetOptions {
8
- imageQuality;
9
8
  stripMacros;
10
9
  stripHiddenData;
11
10
  stripUnusedFonts;
12
11
  constructor(input) {
13
- if (input.imageQuality !== undefined)
14
- this.imageQuality = input.imageQuality;
15
12
  if (input.stripMacros !== undefined)
16
13
  this.stripMacros = input.stripMacros;
17
14
  if (input.stripHiddenData !== undefined)
@@ -27,8 +24,6 @@ export class DocumentOfficeCompressPresetOptions {
27
24
  const cell = f3ShippedDefaultsFor('document_office_compress', level);
28
25
  const input = {};
29
26
  const mut = input;
30
- if ('imageQuality' in cell)
31
- mut.imageQuality = cell.imageQuality;
32
27
  if ('stripMacros' in cell)
33
28
  mut.stripMacros = cell.stripMacros;
34
29
  if ('stripHiddenData' in cell)
@@ -57,6 +57,21 @@ export interface OutputFile {
57
57
  readonly filename: string;
58
58
  readonly sizeBytes: number;
59
59
  readonly operation: string;
60
+ /**
61
+ * For a `target_size` encode: the quality the encode-measure loop settled
62
+ * on. Projected from the generated {@link OperationDownload.chosenQuality};
63
+ * undefined (omitted) for non-target-size outputs. Pairs with
64
+ * {@link targetSizeMet}.
65
+ */
66
+ readonly chosenQuality?: number;
67
+ /**
68
+ * For a `target_size` encode: whether the output landed at or under the
69
+ * requested byte target. `false` is an honest best-effort outcome (target
70
+ * unreachable at min quality), NOT a failure. Projected from the generated
71
+ * {@link OperationDownload.targetSizeMet}; undefined for non-target-size
72
+ * outputs.
73
+ */
74
+ readonly targetSizeMet?: boolean;
60
75
  }
61
76
  /**
62
77
  * One succeeded entry in {@link RunResult.succeeded}: a single input's
@@ -101,6 +116,10 @@ export interface Manifest {
101
116
  * - `ok`: true iff `failed` is empty. (A boolean — the partition lists are
102
117
  * `succeeded`/`failed`; resolves the design doc's `ok` bool-vs-list
103
118
  * contradiction.)
119
+ * - `targetSizeMissed`: derived target-size signal — undefined when no output
120
+ * reports a target-size outcome (not a target_size run); otherwise true iff
121
+ * some artifact has `targetSizeMet === false`. Omitted from the JSON when
122
+ * undefined so non-target-size runs keep the common-case shape.
104
123
  * - `state`: lifecycle state (`completed` | `failed` | ...). Named `state`,
105
124
  * NOT `status`, matching the file-first `StatusSnapshot.state`.
106
125
  * - sinks fetch via the injected {@link Downloader}; a result with no
@@ -117,6 +136,14 @@ export declare class RunResult {
117
136
  readonly url?: string;
118
137
  /** True iff {@link failed} is empty. */
119
138
  readonly ok: boolean;
139
+ /**
140
+ * Whether any output missed its requested byte target. Derived from the
141
+ * per-output {@link OutputFile.targetSizeMet}: undefined when NO artifact
142
+ * reports a target-size outcome (every `targetSizeMet` undefined — not a
143
+ * target_size run); otherwise true iff some artifact has
144
+ * `targetSizeMet === false`.
145
+ */
146
+ readonly targetSizeMissed?: boolean;
120
147
  constructor(workflowId: string, state: string, artifacts: readonly OutputFile[], succeeded: readonly ItemResult[], failed: readonly ItemFailure[], downloader?: Downloader | undefined);
121
148
  /**
122
149
  * Address a succeeded input by the `key:` given to `file()`. Duplicate keys
@@ -143,16 +170,18 @@ export declare class RunResult {
143
170
  failOnPartial?: boolean;
144
171
  }): Promise<Manifest>;
145
172
  /**
146
- * Plain-object projection. Field ORDER (workflowId, state, ok, url?,
147
- * artifacts, succeeded, failed) is fixed to match the PHP `toArray()`
148
- * reference so JSON-string parity holds (FF1 shape assertion + FF2b harness
149
- * fixture). `url` is omitted entirely when undefined — `JSON.stringify`
150
- * then produces the identical shape to PHP's omit-when-null `toArray()`.
173
+ * Plain-object projection. Field ORDER (workflowId, state, ok,
174
+ * targetSizeMissed?, url?, artifacts, succeeded, failed) is fixed to match
175
+ * the PHP `toArray()` reference so JSON-string parity holds (FF1 shape
176
+ * assertion + FF2b harness fixture). `targetSizeMissed` + `url` are omitted
177
+ * entirely when undefined `JSON.stringify` then produces the identical
178
+ * shape to PHP's omit-when-null `toArray()`.
151
179
  */
152
180
  toJSON(): {
153
181
  workflowId: string;
154
182
  state: string;
155
183
  ok: boolean;
184
+ targetSizeMissed?: boolean;
156
185
  url?: string;
157
186
  artifacts: readonly OutputFile[];
158
187
  succeeded: readonly {
@@ -536,6 +565,14 @@ export declare const WATERMARK_CAPABILITY: {
536
565
  readonly mimes: readonly ["image/gif"];
537
566
  readonly availability: "planned";
538
567
  };
568
+ readonly image_tiff: {
569
+ readonly mimes: readonly ["image/tiff"];
570
+ readonly availability: "stable";
571
+ };
572
+ readonly image_bmp: {
573
+ readonly mimes: readonly ["image/bmp"];
574
+ readonly availability: "stable";
575
+ };
539
576
  };
540
577
  readonly video_watermark: {
541
578
  readonly video: {
@@ -40,6 +40,10 @@ import { Handle } from './handle.js';
40
40
  * - `ok`: true iff `failed` is empty. (A boolean — the partition lists are
41
41
  * `succeeded`/`failed`; resolves the design doc's `ok` bool-vs-list
42
42
  * contradiction.)
43
+ * - `targetSizeMissed`: derived target-size signal — undefined when no output
44
+ * reports a target-size outcome (not a target_size run); otherwise true iff
45
+ * some artifact has `targetSizeMet === false`. Omitted from the JSON when
46
+ * undefined so non-target-size runs keep the common-case shape.
43
47
  * - `state`: lifecycle state (`completed` | `failed` | ...). Named `state`,
44
48
  * NOT `status`, matching the file-first `StatusSnapshot.state`.
45
49
  * - sinks fetch via the injected {@link Downloader}; a result with no
@@ -56,6 +60,14 @@ export class RunResult {
56
60
  url;
57
61
  /** True iff {@link failed} is empty. */
58
62
  ok;
63
+ /**
64
+ * Whether any output missed its requested byte target. Derived from the
65
+ * per-output {@link OutputFile.targetSizeMet}: undefined when NO artifact
66
+ * reports a target-size outcome (every `targetSizeMet` undefined — not a
67
+ * target_size run); otherwise true iff some artifact has
68
+ * `targetSizeMet === false`.
69
+ */
70
+ targetSizeMissed;
59
71
  constructor(workflowId, state, artifacts, succeeded, failed, downloader) {
60
72
  this.workflowId = workflowId;
61
73
  this.state = state;
@@ -65,6 +77,9 @@ export class RunResult {
65
77
  this.downloader = downloader;
66
78
  this.url = artifacts.length === 1 ? artifacts[0].url : undefined;
67
79
  this.ok = failed.length === 0;
80
+ this.targetSizeMissed = artifacts.every((a) => a.targetSizeMet === undefined)
81
+ ? undefined
82
+ : artifacts.some((a) => a.targetSizeMet === false);
68
83
  }
69
84
  /**
70
85
  * Address a succeeded input by the `key:` given to `file()`. Duplicate keys
@@ -134,20 +149,26 @@ export class RunResult {
134
149
  return { paths };
135
150
  }
136
151
  /**
137
- * Plain-object projection. Field ORDER (workflowId, state, ok, url?,
138
- * artifacts, succeeded, failed) is fixed to match the PHP `toArray()`
139
- * reference so JSON-string parity holds (FF1 shape assertion + FF2b harness
140
- * fixture). `url` is omitted entirely when undefined — `JSON.stringify`
141
- * then produces the identical shape to PHP's omit-when-null `toArray()`.
152
+ * Plain-object projection. Field ORDER (workflowId, state, ok,
153
+ * targetSizeMissed?, url?, artifacts, succeeded, failed) is fixed to match
154
+ * the PHP `toArray()` reference so JSON-string parity holds (FF1 shape
155
+ * assertion + FF2b harness fixture). `targetSizeMissed` + `url` are omitted
156
+ * entirely when undefined `JSON.stringify` then produces the identical
157
+ * shape to PHP's omit-when-null `toArray()`.
142
158
  */
143
159
  toJSON() {
144
- // Re-project each OutputFile to exactly its four fields so structurally
160
+ // Re-project each OutputFile to exactly its known fields so structurally
145
161
  // compatible inputs carrying extra properties can't leak into the JSON.
162
+ // The target-size fields (chosenQuality/targetSizeMet) are OMITTED when
163
+ // undefined, mirroring PHP's omit-when-null so non-target-size outputs
164
+ // stay byte-identical across languages.
146
165
  const file = (o) => ({
147
166
  url: o.url,
148
167
  filename: o.filename,
149
168
  sizeBytes: o.sizeBytes,
150
169
  operation: o.operation,
170
+ ...(o.chosenQuality !== undefined ? { chosenQuality: o.chosenQuality } : {}),
171
+ ...(o.targetSizeMet !== undefined ? { targetSizeMet: o.targetSizeMet } : {}),
151
172
  });
152
173
  const rest = {
153
174
  artifacts: this.artifacts.map(file),
@@ -164,13 +185,18 @@ export class RunResult {
164
185
  }),
165
186
  };
166
187
  const head = { workflowId: this.workflowId, state: this.state, ok: this.ok };
167
- // Insert `url` BETWEEN ok and artifacts when present, matching the PHP
168
- // toArray() field order (workflowId, state, ok, url?, artifacts, ...) so
169
- // JSON-string parity holds. Omitted entirely when undefined (PHP omits
170
- // null), so `JSON.stringify` produces the identical shape.
188
+ // Insert `targetSizeMissed` immediately after `ok` (before `url`) when
189
+ // present, then `url` BETWEEN it and artifacts, matching the PHP toArray()
190
+ // field order (workflowId, state, ok, targetSizeMissed?, url?, artifacts,
191
+ // ...) so JSON-string parity holds. Both are omitted entirely when
192
+ // undefined (PHP omits null), so `JSON.stringify` produces the identical
193
+ // shape.
194
+ const headWithMissed = this.targetSizeMissed === undefined
195
+ ? head
196
+ : { ...head, targetSizeMissed: this.targetSizeMissed };
171
197
  return this.url === undefined
172
- ? { ...head, ...rest }
173
- : { ...head, url: this.url, ...rest };
198
+ ? { ...headWithMissed, ...rest }
199
+ : { ...headWithMissed, url: this.url, ...rest };
174
200
  }
175
201
  requireDownloader() {
176
202
  if (this.downloader === undefined) {
@@ -200,6 +226,10 @@ export function projectDownloadsToRunResult(workflowId, finalStatus, jobDownload
200
226
  filename: f.filename,
201
227
  sizeBytes: f.sizeBytes,
202
228
  operation: f.operation,
229
+ // Omit-when-absent on the LIVE OutputFile too (not just toJSON): a
230
+ // non-target-size output carries no chosenQuality/targetSizeMet key.
231
+ ...(f.chosenQuality !== undefined ? { chosenQuality: f.chosenQuality } : {}),
232
+ ...(f.targetSizeMet !== undefined ? { targetSizeMet: f.targetSizeMet } : {}),
203
233
  });
204
234
  }
205
235
  }
@@ -259,6 +289,10 @@ export function projectMultiJobToRunResult(workflowId, finalStatus, jobDownloads
259
289
  filename: f.filename,
260
290
  sizeBytes: f.sizeBytes,
261
291
  operation: f.operation,
292
+ // Omit-when-absent on the LIVE OutputFile too (mirrors toJSON + the
293
+ // single-job projector).
294
+ ...(f.chosenQuality !== undefined ? { chosenQuality: f.chosenQuality } : {}),
295
+ ...(f.targetSizeMet !== undefined ? { targetSizeMet: f.targetSizeMet } : {}),
262
296
  }));
263
297
  // The flat artifacts[] keeps every job's outputs in job order.
264
298
  artifacts.push(...outputs);
@@ -1055,6 +1089,8 @@ export const WATERMARK_CAPABILITY = {
1055
1089
  image_watermark: {
1056
1090
  image: { mimes: ['image/jpeg', 'image/png', 'image/webp'], availability: 'stable' },
1057
1091
  image_gif: { mimes: ['image/gif'], availability: 'planned' },
1092
+ image_tiff: { mimes: ['image/tiff'], availability: 'stable' },
1093
+ image_bmp: { mimes: ['image/bmp'], availability: 'stable' },
1058
1094
  },
1059
1095
  video_watermark: {
1060
1096
  video: { mimes: ['video/mp4', 'video/webm'], availability: 'beta' },
@@ -10,8 +10,9 @@ export declare const ImageFormat: {
10
10
  };
11
11
  export type ImageFormat = typeof ImageFormat[keyof typeof ImageFormat];
12
12
  export declare const ImageMetadataPolicy: {
13
- readonly All: "all";
13
+ readonly Strip: "strip";
14
14
  readonly Keep: "keep";
15
+ readonly All: "all";
15
16
  };
16
17
  export type ImageMetadataPolicy = typeof ImageMetadataPolicy[keyof typeof ImageMetadataPolicy];
17
18
  export declare const VideoCodec: {
@@ -88,8 +89,9 @@ export declare const ERGONOMIC_ENUMS: {
88
89
  readonly Webp: "webp";
89
90
  };
90
91
  readonly ImageMetadataPolicy: {
91
- readonly All: "all";
92
+ readonly Strip: "strip";
92
93
  readonly Keep: "keep";
94
+ readonly All: "all";
93
95
  };
94
96
  readonly VideoCodec: {
95
97
  readonly H264: "h264";
@@ -13,15 +13,21 @@ export const ImageFormat = {
13
13
  Webp: "webp",
14
14
  };
15
15
  // Image metadata handling (compress.image, optimiser/same_format route):
16
- // All = strip all EXIF/IPTC/XMP (default, smallest file)
17
- // Keep = preserve EXIF/ICC/XMP worker-proven on the libcaesium formats
18
- // (JPEG/PNG/WebP/GIF/TIFF; lambdas PR #260, un-parked 2026-06-23).
16
+ // Strip = remove all EXIF/IPTC/XMP (default, smallest file) — the canonical
17
+ // strip token (renamed from `all` 2026-06-23; `all` reading as
18
+ // "strip all" was counterintuitive).
19
+ // Keep = preserve EXIF/ICC/XMP — worker-proven on the libcaesium formats
20
+ // (JPEG/PNG/WebP/GIF/TIFF; lambdas PR #260, un-parked 2026-06-23).
21
+ // All = DEPRECATED alias of Strip (still accepted on the wire, emits
22
+ // Deprecation/Sunset; the API lowers `strip`→`all` at the worker
23
+ // boundary). Migrate to Strip.
19
24
  // Keep is NOT available for AVIF (ravif) / SVG (SVGO) — those re-encode from
20
25
  // pixels and cannot preserve metadata, so the worker rejects it there (the
21
- // `metadata` enum is narrowed to [all] on those groups).
26
+ // `metadata` enum is narrowed to [strip, all] on those groups).
22
27
  export const ImageMetadataPolicy = {
23
- All: "all",
28
+ Strip: "strip",
24
29
  Keep: "keep",
30
+ All: "all",
25
31
  };
26
32
  // Video codec. H264 = widest compatibility; H265/Av1 = better compression but slower.
27
33
  export const VideoCodec = {
@@ -5,17 +5,17 @@ export const PRESETS = Object.freeze({
5
5
  "image_compress": Object.freeze({
6
6
  Size: Object.freeze({
7
7
  "quality": 65,
8
- "metadata": "All",
8
+ "metadata": "Strip",
9
9
  "outputFormat": "Original",
10
10
  }),
11
11
  Balanced: Object.freeze({
12
12
  "quality": 80,
13
- "metadata": "All",
13
+ "metadata": "Strip",
14
14
  "outputFormat": "Original",
15
15
  }),
16
16
  Quality: Object.freeze({
17
17
  "quality": 92,
18
- "metadata": "All",
18
+ "metadata": "Strip",
19
19
  "outputFormat": "Original",
20
20
  }),
21
21
  }),
@@ -66,19 +66,16 @@ export const PRESETS = Object.freeze({
66
66
  }),
67
67
  "document_office_compress": Object.freeze({
68
68
  Size: Object.freeze({
69
- "imageQuality": 60,
70
69
  "stripMacros": true,
71
70
  "stripHiddenData": true,
72
71
  "stripUnusedFonts": true,
73
72
  }),
74
73
  Balanced: Object.freeze({
75
- "imageQuality": 80,
76
74
  "stripMacros": true,
77
75
  "stripHiddenData": false,
78
76
  "stripUnusedFonts": false,
79
77
  }),
80
78
  Quality: Object.freeze({
81
- "imageQuality": 92,
82
79
  "stripMacros": false,
83
80
  "stripHiddenData": false,
84
81
  "stripUnusedFonts": false,
@@ -86,34 +83,28 @@ export const PRESETS = Object.freeze({
86
83
  }),
87
84
  "document_odf_compress": Object.freeze({
88
85
  Size: Object.freeze({
89
- "imageQuality": 60,
90
86
  "stripMetadata": true,
91
87
  "stripUnusedStyles": true,
92
88
  }),
93
89
  Balanced: Object.freeze({
94
- "imageQuality": 80,
95
90
  "stripMetadata": true,
96
91
  "stripUnusedStyles": false,
97
92
  }),
98
93
  Quality: Object.freeze({
99
- "imageQuality": 92,
100
94
  "stripMetadata": false,
101
95
  "stripUnusedStyles": false,
102
96
  }),
103
97
  }),
104
98
  "document_epub_compress": Object.freeze({
105
99
  Size: Object.freeze({
106
- "imageQuality": 60,
107
100
  "fontSubsetting": true,
108
101
  "stripUnusedCss": true,
109
102
  }),
110
103
  Balanced: Object.freeze({
111
- "imageQuality": 80,
112
104
  "fontSubsetting": true,
113
105
  "stripUnusedCss": false,
114
106
  }),
115
107
  Quality: Object.freeze({
116
- "imageQuality": 92,
117
108
  "fontSubsetting": false,
118
109
  "stripUnusedCss": false,
119
110
  }),
@@ -1,3 +1,3 @@
1
- export declare const SDK_SPEC_VERSION: "2.1.0";
2
- export declare const PRESET_VERSION: "1.4";
3
- export declare const PRESET_CONFIG_HASH: "sha256:62fbada24e33f983dacb0f717b0c9bb80824a208c3dc7f9af8523026e783543a";
1
+ export declare const SDK_SPEC_VERSION: "2.2.0";
2
+ export declare const PRESET_VERSION: "1.6";
3
+ export declare const PRESET_CONFIG_HASH: "sha256:0fe215aede1da13430380384c58226d074c3b8344c091efc53e097c2930a34f7";
@@ -1,6 +1,6 @@
1
1
  // CODE GENERATED — DO NOT EDIT.
2
2
  // Source: compression_contracts/sdk-spec/ (see sdk-spec/README.md).
3
3
  // Regenerate with: scripts/generate.py.
4
- export const SDK_SPEC_VERSION = "2.1.0";
5
- export const PRESET_VERSION = "1.4";
6
- export const PRESET_CONFIG_HASH = "sha256:62fbada24e33f983dacb0f717b0c9bb80824a208c3dc7f9af8523026e783543a";
4
+ export const SDK_SPEC_VERSION = "2.2.0";
5
+ export const PRESET_VERSION = "1.6";
6
+ export const PRESET_CONFIG_HASH = "sha256:0fe215aede1da13430380384c58226d074c3b8344c091efc53e097c2930a34f7";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@giveitsmaller/sdk",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "description": "Node.js SDK for the GISL (Give It Smaller) file compression and processing API",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -31,7 +31,7 @@
31
31
  "node": ">=18"
32
32
  },
33
33
  "dependencies": {
34
- "@giveitsmaller/contracts": "^0.35.0"
34
+ "@giveitsmaller/contracts": "^0.50.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/node": "^22",