@giveitsmaller/sdk 0.18.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +1 -1
  2. package/dist/_audit.js +12 -0
  3. package/dist/builder.d.ts +1 -0
  4. package/dist/client.d.ts +8 -1
  5. package/dist/client.js +27 -32
  6. package/dist/ergonomic/image_output_routes.d.ts +6 -5
  7. package/dist/ergonomic/image_output_routes.js +29 -21
  8. package/dist/ergonomic/option_types.d.ts +85 -11
  9. package/dist/ergonomic/option_types.js +11 -6
  10. package/dist/ergonomic/option_validation.d.ts +21 -3
  11. package/dist/ergonomic/option_validation.js +36 -3
  12. package/dist/ergonomic/preset_resolver.d.ts +1 -1
  13. package/dist/ergonomic/preset_resolver.js +6 -7
  14. package/dist/ergonomic/presets/document_epub_compress.d.ts +0 -2
  15. package/dist/ergonomic/presets/document_epub_compress.js +2 -7
  16. package/dist/ergonomic/presets/document_odf_compress.d.ts +0 -2
  17. package/dist/ergonomic/presets/document_odf_compress.js +2 -7
  18. package/dist/ergonomic/presets/document_office_compress.d.ts +0 -2
  19. package/dist/ergonomic/presets/document_office_compress.js +2 -7
  20. package/dist/errors.d.ts +57 -1
  21. package/dist/errors.js +82 -1
  22. package/dist/file-first.d.ts +195 -8
  23. package/dist/file-first.js +462 -124
  24. package/dist/generated/sdk_spec/enums.d.ts +4 -2
  25. package/dist/generated/sdk_spec/enums.js +11 -5
  26. package/dist/generated/sdk_spec/presets.js +3 -12
  27. package/dist/generated/sdk_spec/version.d.ts +3 -3
  28. package/dist/generated/sdk_spec/version.js +3 -3
  29. package/dist/gisl.d.ts +72 -3
  30. package/dist/gisl.js +72 -2
  31. package/dist/index.core.d.ts +8 -6
  32. package/dist/index.core.js +9 -1
  33. package/dist/merge.d.ts +12 -0
  34. package/dist/merge.js +12 -0
  35. package/dist/retry-metadata.d.ts +37 -0
  36. package/dist/retry-metadata.js +86 -0
  37. package/dist/sse.d.ts +2 -1
  38. package/dist/sse.js +26 -6
  39. package/dist/types.d.ts +43 -1
  40. package/package.json +2 -2
@@ -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)
package/dist/errors.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import type { AuthErrorResponse, AuthRejectionEnvelope, AuthRejectionEnvelopeErrorTypeEnum, BalanceExhaustedResponse, FeatureNotAvailableResponse, FeatureTierRestrictedResponse, ProbePendingResponse, TierRestrictionResponse, UploadDurationExceedsTierResponse, UploadSizeExceedsTierResponse, WorkflowExpiredResponse } from '@giveitsmaller/contracts/openapi';
2
+ import type { ErrorCategory } from './generated/sdk_spec/errors.js';
3
+ import type { RateLimitSnapshot } from './retry-metadata.js';
2
4
  export declare class GislError extends Error {
3
5
  constructor(message: string);
4
6
  }
@@ -14,6 +16,11 @@ export interface GislApiErrorOptions {
14
16
  readonly locale?: string;
15
17
  readonly messageParams?: Record<string, unknown>;
16
18
  readonly payload?: unknown;
19
+ /**
20
+ * The wire-stable machine error code (the response envelope's `error` field,
21
+ * SCREAMING_SNAKE, never localised). See {@link GislApiError.errorCode}.
22
+ */
23
+ readonly errorCode?: string;
17
24
  /**
18
25
  * The response headers from the HTTP response that produced this error.
19
26
  * Keys are LOWERCASED (HTTP header names are case-insensitive per RFC 9110,
@@ -33,6 +40,19 @@ export interface GislApiErrorOptions {
33
40
  export declare class GislApiError extends GislError {
34
41
  readonly statusCode: number;
35
42
  readonly errorMessage: string;
43
+ /**
44
+ * The wire-stable machine error code — the response envelope's `error` field
45
+ * (SCREAMING_SNAKE, never localised). DISTINCT from {@link errorMessage},
46
+ * which is the human `message`. Mirrors the PHP `GislApiError.errorCode`.
47
+ *
48
+ * Optional here (PHP's is a required field defaulting to `'unknown_error'`):
49
+ * a DELIBERATE optional-vs-sentinel divergence — `undefined` when the wire
50
+ * envelope carries no `error` (e.g. a non-JSON / invalid-JSON response). When
51
+ * the wire DOES carry `error`, both SDKs surface the same value. Machine
52
+ * dispatch still keys off the typed subclasses (`payload.errorType`); this is
53
+ * the flat machine code for a base `GislApiError` (e.g. a plain 404).
54
+ */
55
+ readonly errorCode?: string;
36
56
  readonly path?: string;
37
57
  readonly details?: unknown;
38
58
  readonly messageKey?: string;
@@ -53,6 +73,42 @@ export declare class GislApiError extends GislError {
53
73
  */
54
74
  readonly contentLanguage?: string;
55
75
  constructor(statusCode: number, errorMessage: string, path?: string, details?: unknown, options?: GislApiErrorOptions);
76
+ /**
77
+ * Resolve the generated `ERROR_CODES` entry for this error, SOURCE-AWARE
78
+ * (plan D1). ~9 registry codes are keyed by the `error_type` discriminator
79
+ * rather than the envelope `error` field, so try the typed discriminator
80
+ * FIRST (camel `errorType`, raw-snake `error_type` fallback), then fall back
81
+ * to the flat machine {@link errorCode}. Returns `undefined` when neither
82
+ * resolves — e.g. a bare base error whose payload carries no discriminator.
83
+ * NEVER throws on a missing payload / discriminator.
84
+ */
85
+ private resolveErrorEntry;
86
+ /**
87
+ * Whether retrying this request could plausibly succeed. `true` when the HTTP
88
+ * status is inherently retryable (408 / 429 / 5xx) OR the resolved taxonomy
89
+ * entry marks the code retryable (e.g. `probe_pending`). Note: logical OR
90
+ * (not `??`) — a 429 is retryable regardless of the taxonomy, and a
91
+ * registry-retryable code is retryable regardless of status.
92
+ */
93
+ get retryable(): boolean;
94
+ /**
95
+ * The taxonomy category for this error's machine code, from the generated
96
+ * `ERROR_CODES` registry, or `undefined` when the code isn't in the registry
97
+ * (e.g. a bare base error whose payload carries no discriminator).
98
+ */
99
+ get category(): ErrorCategory | undefined;
100
+ /**
101
+ * The rate-limit snapshot parsed from the `x-ratelimit-*` response headers,
102
+ * or `undefined` when they aren't all present as non-negative integers. Read
103
+ * this after a 429 to schedule a back-off.
104
+ */
105
+ get rateLimit(): RateLimitSnapshot | undefined;
106
+ /**
107
+ * The server-suggested back-off delay in whole seconds, parsed from the
108
+ * `Retry-After` response header, or `undefined` when absent / zero / past /
109
+ * malformed. Mirrors the retry-loop parser's semantics.
110
+ */
111
+ get retryAfterSeconds(): number | undefined;
56
112
  }
57
113
  /**
58
114
  * Shape of a single validation detail entry. Mirrors the v2
@@ -439,7 +495,7 @@ export type GislSinkErrorReason = 'not_single_output' | 'downloader_unavailable'
439
495
  /**
440
496
  * Thrown by the file-first `RunResult` sinks (`toFile()` / `downloadTo()`,
441
497
  * FF1) when they cannot deliver. The machine-readable `reason` discriminates
442
- * the three cases, mirroring the `reason`-bag convention on
498
+ * the six cases below, mirroring the `reason`-bag convention on
443
499
  * {@link GislConfigError}:
444
500
  *
445
501
  * - `not_single_output` — `toFile()` requires exactly one output but the
package/dist/errors.js CHANGED
@@ -1,3 +1,12 @@
1
+ // W8v4jWzx — the generated error-taxonomy registry stays INTERNAL to this
2
+ // module (only the `ErrorCategory` TYPE is re-exported from the public barrel).
3
+ import { ERROR_CODES } from './generated/sdk_spec/errors.js';
4
+ import { isApiRetryableStatus, rateLimitFromHeaders, retryAfterSecondsFromHeaders, } from './retry-metadata.js';
5
+ // Registry keys are lowercase_snake; normalise the wire code / discriminator
6
+ // (trim + lowercase) before looking it up in ERROR_CODES.
7
+ function normalizeErrorCode(rawCode) {
8
+ return rawCode.trim().toLowerCase();
9
+ }
1
10
  export class GislError extends Error {
2
11
  constructor(message) {
3
12
  super(message);
@@ -7,6 +16,19 @@ export class GislError extends Error {
7
16
  export class GislApiError extends GislError {
8
17
  statusCode;
9
18
  errorMessage;
19
+ /**
20
+ * The wire-stable machine error code — the response envelope's `error` field
21
+ * (SCREAMING_SNAKE, never localised). DISTINCT from {@link errorMessage},
22
+ * which is the human `message`. Mirrors the PHP `GislApiError.errorCode`.
23
+ *
24
+ * Optional here (PHP's is a required field defaulting to `'unknown_error'`):
25
+ * a DELIBERATE optional-vs-sentinel divergence — `undefined` when the wire
26
+ * envelope carries no `error` (e.g. a non-JSON / invalid-JSON response). When
27
+ * the wire DOES carry `error`, both SDKs surface the same value. Machine
28
+ * dispatch still keys off the typed subclasses (`payload.errorType`); this is
29
+ * the flat machine code for a base `GislApiError` (e.g. a plain 404).
30
+ */
31
+ errorCode;
10
32
  path;
11
33
  details;
12
34
  messageKey;
@@ -41,10 +63,69 @@ export class GislApiError extends GislError {
41
63
  this.locale = options.locale;
42
64
  this.messageParams = options.messageParams;
43
65
  this.payload = options.payload;
66
+ this.errorCode = options.errorCode;
44
67
  this.responseHeaders = options.responseHeaders;
45
68
  this.contentLanguage = options.contentLanguage;
46
69
  }
47
70
  }
71
+ /**
72
+ * Resolve the generated `ERROR_CODES` entry for this error, SOURCE-AWARE
73
+ * (plan D1). ~9 registry codes are keyed by the `error_type` discriminator
74
+ * rather than the envelope `error` field, so try the typed discriminator
75
+ * FIRST (camel `errorType`, raw-snake `error_type` fallback), then fall back
76
+ * to the flat machine {@link errorCode}. Returns `undefined` when neither
77
+ * resolves — e.g. a bare base error whose payload carries no discriminator.
78
+ * NEVER throws on a missing payload / discriminator.
79
+ */
80
+ resolveErrorEntry() {
81
+ const payload = this.payload;
82
+ const rawErrorType = payload?.errorType ?? payload?.error_type;
83
+ if (typeof rawErrorType === 'string') {
84
+ const byType = ERROR_CODES[normalizeErrorCode(rawErrorType)];
85
+ if (byType !== undefined)
86
+ return byType;
87
+ }
88
+ if (this.errorCode !== undefined) {
89
+ const byCode = ERROR_CODES[normalizeErrorCode(this.errorCode)];
90
+ if (byCode !== undefined)
91
+ return byCode;
92
+ }
93
+ return undefined;
94
+ }
95
+ /**
96
+ * Whether retrying this request could plausibly succeed. `true` when the HTTP
97
+ * status is inherently retryable (408 / 429 / 5xx) OR the resolved taxonomy
98
+ * entry marks the code retryable (e.g. `probe_pending`). Note: logical OR
99
+ * (not `??`) — a 429 is retryable regardless of the taxonomy, and a
100
+ * registry-retryable code is retryable regardless of status.
101
+ */
102
+ get retryable() {
103
+ return (isApiRetryableStatus(this.statusCode) || (this.resolveErrorEntry()?.retryable ?? false));
104
+ }
105
+ /**
106
+ * The taxonomy category for this error's machine code, from the generated
107
+ * `ERROR_CODES` registry, or `undefined` when the code isn't in the registry
108
+ * (e.g. a bare base error whose payload carries no discriminator).
109
+ */
110
+ get category() {
111
+ return this.resolveErrorEntry()?.category;
112
+ }
113
+ /**
114
+ * The rate-limit snapshot parsed from the `x-ratelimit-*` response headers,
115
+ * or `undefined` when they aren't all present as non-negative integers. Read
116
+ * this after a 429 to schedule a back-off.
117
+ */
118
+ get rateLimit() {
119
+ return rateLimitFromHeaders(this.responseHeaders);
120
+ }
121
+ /**
122
+ * The server-suggested back-off delay in whole seconds, parsed from the
123
+ * `Retry-After` response header, or `undefined` when absent / zero / past /
124
+ * malformed. Mirrors the retry-loop parser's semantics.
125
+ */
126
+ get retryAfterSeconds() {
127
+ return retryAfterSecondsFromHeaders(this.responseHeaders);
128
+ }
48
129
  }
49
130
  export class GislValidationError extends GislApiError {
50
131
  constructor(statusCode, errorMessage, details, path, options) {
@@ -479,7 +560,7 @@ export class GislResultNotReadyError extends GislError {
479
560
  /**
480
561
  * Thrown by the file-first `RunResult` sinks (`toFile()` / `downloadTo()`,
481
562
  * FF1) when they cannot deliver. The machine-readable `reason` discriminates
482
- * the three cases, mirroring the `reason`-bag convention on
563
+ * the six cases below, mirroring the `reason`-bag convention on
483
564
  * {@link GislConfigError}:
484
565
  *
485
566
  * - `not_single_output` — `toFile()` requires exactly one output but the