@giveitsmaller/sdk 0.15.0 → 0.17.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 +15 -2
- package/dist/client.d.ts +27 -4
- package/dist/client.js +46 -4
- 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 +7 -6
- package/dist/ergonomic/preset_resolver.js +43 -39
- 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 +241 -49
- package/dist/file-first.js +803 -249
- package/dist/generated/sdk_spec/enums.d.ts +8 -46
- package/dist/generated/sdk_spec/enums.js +12 -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/handle.js +9 -1
- package/dist/index.core.d.ts +7 -4
- package/dist/index.core.js +8 -1
- package/dist/types.d.ts +21 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -69,6 +69,27 @@ Docs are published in the [giveitsmaller-sdks](https://github.com/AntonioCS/give
|
|
|
69
69
|
- **Troubleshooting** — [`docs/typescript/troubleshooting.md`](https://github.com/AntonioCS/giveitsmaller-sdks/blob/main/docs/typescript/troubleshooting.md)
|
|
70
70
|
- **Examples** — compress, thumbnail, convert, merge, archive — [`docs/typescript/examples/`](https://github.com/AntonioCS/giveitsmaller-sdks/tree/main/docs/typescript/examples)
|
|
71
71
|
|
|
72
|
+
## Contributing: the committed `dist/`
|
|
73
|
+
|
|
74
|
+
Unlike most packages, `packages/typescript/dist/` is **committed**, not gitignored.
|
|
75
|
+
This is deliberate: consumers that install the SDK via the `file:` protocol (the
|
|
76
|
+
e2e canary, the frontend, the API repo, local dev) get whatever is on disk — npm
|
|
77
|
+
does **not** run `prepare`/`prepack` for `file:` deps, so a `dist/` that lagged
|
|
78
|
+
behind `src/` would silently ship stale exports.
|
|
79
|
+
|
|
80
|
+
When you change anything under `src/`, rebuild and commit `dist/`:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npm run build # tsc → dist/
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
CI enforces this with a freshness guard that rebuilds `dist/` and fails the PR on
|
|
87
|
+
any `git diff` against the committed tree (mirroring the `git diff --exit-code
|
|
88
|
+
generated/` drift rule). `typescript` is pinned to an exact version so the rebuild
|
|
89
|
+
is reproducible. New hand-written SDK packages (PHP/Python) that grow a build step
|
|
90
|
+
should follow the same commit-`dist`-and-guard convention to avoid reintroducing
|
|
91
|
+
the `file:` gap.
|
|
92
|
+
|
|
72
93
|
## License
|
|
73
94
|
|
|
74
95
|
MIT — see the [LICENSE](https://github.com/AntonioCS/giveitsmaller-sdks/blob/main/LICENSE) file.
|
package/dist/_audit.js
CHANGED
|
@@ -68,6 +68,17 @@ export function _runAudit() {
|
|
|
68
68
|
accept();
|
|
69
69
|
// T2 / xVDTIm8C — operation-builder surface.
|
|
70
70
|
accept();
|
|
71
|
+
// 8yqUXLCS — pin the credits/limits accessor SIGNATURES on ErgonomicClient.
|
|
72
|
+
// accept<ErgonomicClient>() proves the type compiles; these prove the three
|
|
73
|
+
// methods EXIST and their signatures/return types match (indexed access errors
|
|
74
|
+
// if a method is missing; the typed LHS errors if the signature drifts). The
|
|
75
|
+
// RHS is a type-only cast (`null as unknown as …`) — no runtime property read.
|
|
76
|
+
const _creditsSig = null;
|
|
77
|
+
const _creditsUsageSig = null;
|
|
78
|
+
const _limitsSig = null;
|
|
79
|
+
void _creditsSig;
|
|
80
|
+
void _creditsUsageSig;
|
|
81
|
+
void _limitsSig;
|
|
71
82
|
accept();
|
|
72
83
|
accept();
|
|
73
84
|
accept();
|
|
@@ -113,8 +124,6 @@ export function _runAudit() {
|
|
|
113
124
|
accept();
|
|
114
125
|
accept();
|
|
115
126
|
accept();
|
|
116
|
-
accept();
|
|
117
|
-
accept();
|
|
118
127
|
// T4b / 27rE1fZn — preset resolver public types.
|
|
119
128
|
accept();
|
|
120
129
|
accept();
|
|
@@ -130,6 +139,10 @@ export function _runAudit() {
|
|
|
130
139
|
accept();
|
|
131
140
|
// FF3a / u0hBt6fl — homogeneous fan-out builder surface.
|
|
132
141
|
accept();
|
|
142
|
+
// FF4a / Z7zTr789 — multi-input watermark recipe surface.
|
|
143
|
+
accept();
|
|
144
|
+
accept();
|
|
145
|
+
accept();
|
|
133
146
|
accept();
|
|
134
147
|
accept();
|
|
135
148
|
accept();
|
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { AudioWatermarkDecodeRequest, AudioWatermarkDecodeResponse, ExternalImportCreatedResponse, ExternalImportRequest, LoginUserRequest, LoginUser200ResponseData, ContactRequest, CreditsBalanceResponse, CreditsUsageResponse, UploadResponse, UploadProbeResponse, WorkflowCancelResponse, WorkflowCreateResponse, WorkflowResumeResponse, WorkflowStatusResponse, WorkflowListResponse, WorkflowSummary, WorkflowDownloadResponse, MetadataResponse, RetryResponse } from '@giveitsmaller/contracts/openapi';
|
|
2
|
-
import type { CreditsUsageOptions, ListWorkflowsOptions, GetSchemaOptions, GetSchemaResult, GislClientConfig, GislSseEvent, PreflightClipsResult, ProbeWaitOptions, ProbeWaitResult, UploadOptions, WaitOptions, WorkflowCreatePayload, _Sdk3HandCodedKeepaliveResult, _Sdk3HandCodedMultipartStatusResult, _Sdk3HandCodedPresignPartsResult } from './types.js';
|
|
1
|
+
import type { AudioWatermarkDecodeRequest, AudioWatermarkDecodeResponse, ExternalImportCreatedResponse, ExternalImportRequest, LoginUserRequest, LoginUser200ResponseData, ContactRequest, AccountLimits, CreditsBalanceResponse, CreditsUsageResponse, UploadResponse, UploadProbeResponse, WorkflowCancelResponse, WorkflowCreateResponse, WorkflowResumeResponse, WorkflowStatusResponse, WorkflowListResponse, WorkflowSummary, WorkflowDownloadResponse, MetadataResponse, RetryResponse } from '@giveitsmaller/contracts/openapi';
|
|
2
|
+
import type { CreditsUsageOptions, ListWorkflowsOptions, GetSchemaOptions, GetSchemaResult, GislClientConfig, GislSseEvent, PreflightClipsResult, ProbeWaitOptions, ProbeWaitResult, ReadCapabilityOptions, UploadOptions, WaitOptions, WorkflowCreatePayload, _Sdk3HandCodedKeepaliveResult, _Sdk3HandCodedMultipartStatusResult, _Sdk3HandCodedPresignPartsResult } from './types.js';
|
|
3
3
|
export declare const MULTIPART_CONCURRENCY_DEFAULT: 4;
|
|
4
4
|
export declare const DEFAULT_MULTIPART_FIRST_CHUNK_SIZE: number;
|
|
5
5
|
export interface ValidationDetail {
|
|
@@ -133,8 +133,13 @@ export declare class GislClient {
|
|
|
133
133
|
createWorkflow(payload: WorkflowCreatePayload): Promise<WorkflowCreateResponse>;
|
|
134
134
|
/**
|
|
135
135
|
* Get current workflow status.
|
|
136
|
+
*
|
|
137
|
+
* For an anonymous (null-owner) workflow, pass `opts.capability` (the `cap`
|
|
138
|
+
* from the anonymous workflow-create response) so a session-less caller can
|
|
139
|
+
* read it — the SDK sends it as the `X-Workflow-Capability` header. Omit for
|
|
140
|
+
* authenticated reads. A wrong/missing cap on a null-owner workflow is a 404.
|
|
136
141
|
*/
|
|
137
|
-
getWorkflowStatus(workflowId: string): Promise<WorkflowStatusResponse>;
|
|
142
|
+
getWorkflowStatus(workflowId: string, opts?: ReadCapabilityOptions): Promise<WorkflowStatusResponse>;
|
|
138
143
|
/**
|
|
139
144
|
* Poll until the workflow reaches a terminal status.
|
|
140
145
|
*/
|
|
@@ -176,13 +181,24 @@ export declare class GislClient {
|
|
|
176
181
|
resumeWorkflow(workflowId: string): Promise<WorkflowResumeResponse>;
|
|
177
182
|
/**
|
|
178
183
|
* Get download URLs for a completed workflow.
|
|
184
|
+
*
|
|
185
|
+
* For an anonymous (null-owner) workflow, pass `opts.capability` (the `cap`
|
|
186
|
+
* from the anonymous workflow-create response) so a session-less caller can
|
|
187
|
+
* read it — the SDK sends it as the `X-Workflow-Capability` header. Omit for
|
|
188
|
+
* authenticated reads. A wrong/missing cap on a null-owner workflow is a 404.
|
|
179
189
|
*/
|
|
180
|
-
getWorkflowDownloads(workflowId: string): Promise<WorkflowDownloadResponse>;
|
|
190
|
+
getWorkflowDownloads(workflowId: string, opts?: ReadCapabilityOptions): Promise<WorkflowDownloadResponse>;
|
|
181
191
|
/**
|
|
182
192
|
* Stream SSE events for a workflow. Returns an async iterable.
|
|
193
|
+
*
|
|
194
|
+
* For an anonymous (null-owner) workflow, pass `opts.capability` (the `cap`
|
|
195
|
+
* from the anonymous workflow-create response) so a session-less caller can
|
|
196
|
+
* read it — the SDK sends it as the `X-Workflow-Capability` header. Omit for
|
|
197
|
+
* authenticated reads. A wrong/missing cap on a null-owner workflow is a 404.
|
|
183
198
|
*/
|
|
184
199
|
streamEvents(workflowId: string, opts?: {
|
|
185
200
|
signal?: AbortSignal;
|
|
201
|
+
capability?: string;
|
|
186
202
|
}): Promise<AsyncGenerator<GislSseEvent>>;
|
|
187
203
|
/**
|
|
188
204
|
* Get metadata for an uploaded file.
|
|
@@ -229,6 +245,13 @@ export declare class GislClient {
|
|
|
229
245
|
* off the error envelope.
|
|
230
246
|
*/
|
|
231
247
|
getCreditsBalance(): Promise<CreditsBalanceResponse>;
|
|
248
|
+
/**
|
|
249
|
+
* Fetch the caller's effective account limits (the tier-resolved caps:
|
|
250
|
+
* upload/merge size + total caps, surfaced override-aware). `GET
|
|
251
|
+
* /api/v2/account/limits`. The success envelope's `data` is unwrapped to the
|
|
252
|
+
* {@link AccountLimits} model (mirrors {@link getCreditsBalance}).
|
|
253
|
+
*/
|
|
254
|
+
getAccountLimits(): Promise<AccountLimits>;
|
|
232
255
|
/**
|
|
233
256
|
* Authenticate with email/password. On success the server issues a
|
|
234
257
|
* session cookie via `Set-Cookie`; subsequent requests authenticate
|
package/dist/client.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// blobByteSource, which never touches these). Kept as a STATIC import (not a
|
|
5
5
|
// dynamic one) so `vi.mock('node:fs/promises')` still intercepts it in tests.
|
|
6
6
|
import { open, stat, basename } from './node-fs.js';
|
|
7
|
-
import { AudioWatermarkDecodeRequestToJSON, AudioWatermarkDecodeResponseFromJSON, ExternalImportCreatedResponseFromJSON, ExternalImportRequestToJSON, LoginUser200ResponseDataFromJSON, CreditsBalanceResponseFromJSON, CreditsUsageResponseFromJSON, UploadResponseFromJSON, UploadProbeResponseFromJSON, MultipartInitiateResponseFromJSON, MultipartInitiateRequestMetadataHintToJSON, MultipartCompleteResponseFromJSON, MultipartCompleteRequestToJSON, WorkflowCancelResponseFromJSON, WorkflowCreateResponseFromJSON, WorkflowResumeResponseFromJSON, WorkflowStatusResponseFromJSON, WorkflowListResponseFromJSON, WorkflowDownloadResponseFromJSON, MetadataResponseFromJSON, OperationsSchemaResponseFromJSON, RetryResponseFromJSON, WorkflowStatus, AuthErrorResponseFromJSON, AuthErrorType, AuthRejectionEnvelopeFromJSON, AuthRejectionEnvelopeErrorTypeEnum, BalanceExhaustedResponseFromJSON, BalanceExhaustedResponseRequiredActionEnum, FeatureNotAvailableResponseFromJSON, FeatureTierRestrictedResponseFromJSON, TierRestrictionKind, TierRestrictionResponseFromJSON, UserTier, WorkflowExpiredResponseFromJSON, ProbePendingResponseFromJSON, UploadSizeExceedsTierResponseFromJSON, UploadDurationExceedsTierResponseFromJSON, UploadConstraintsAppliedProcessingClassPreAssignmentEnum, UploadThresholdsSingleShotMaxBytesEnum, UploadThresholdsMultipartChunkSizeEnum, UploadThresholdsMultipartConcurrencyDefaultEnum, } from '@giveitsmaller/contracts/openapi';
|
|
7
|
+
import { AudioWatermarkDecodeRequestToJSON, AudioWatermarkDecodeResponseFromJSON, ExternalImportCreatedResponseFromJSON, ExternalImportRequestToJSON, LoginUser200ResponseDataFromJSON, AccountLimitsFromJSON, CreditsBalanceResponseFromJSON, CreditsUsageResponseFromJSON, UploadResponseFromJSON, UploadProbeResponseFromJSON, MultipartInitiateResponseFromJSON, MultipartInitiateRequestMetadataHintToJSON, MultipartCompleteResponseFromJSON, MultipartCompleteRequestToJSON, WorkflowCancelResponseFromJSON, WorkflowCreateResponseFromJSON, WorkflowResumeResponseFromJSON, WorkflowStatusResponseFromJSON, WorkflowListResponseFromJSON, WorkflowDownloadResponseFromJSON, MetadataResponseFromJSON, OperationsSchemaResponseFromJSON, RetryResponseFromJSON, WorkflowStatus, AuthErrorResponseFromJSON, AuthErrorType, AuthRejectionEnvelopeFromJSON, AuthRejectionEnvelopeErrorTypeEnum, BalanceExhaustedResponseFromJSON, BalanceExhaustedResponseRequiredActionEnum, FeatureNotAvailableResponseFromJSON, FeatureTierRestrictedResponseFromJSON, TierRestrictionKind, TierRestrictionResponseFromJSON, UserTier, WorkflowExpiredResponseFromJSON, ProbePendingResponseFromJSON, UploadSizeExceedsTierResponseFromJSON, UploadDurationExceedsTierResponseFromJSON, UploadConstraintsAppliedProcessingClassPreAssignmentEnum, UploadThresholdsSingleShotMaxBytesEnum, UploadThresholdsMultipartChunkSizeEnum, UploadThresholdsMultipartConcurrencyDefaultEnum, } from '@giveitsmaller/contracts/openapi';
|
|
8
8
|
import { GislAbortError, GislApiError, GislAuthError, GislAuthRejectionError, GislBalanceExhaustedError, GislError, GislFeatureNotAvailableError, GislFeatureTierRestrictedError, GislMultipartPartCountError, GislMultipartPartError, GislMultipartSessionNotFoundError, GislMultipartSessionOwnershipError, GislMultipartSessionAuthRequiredError, GislTierRestrictedError, GislTimeoutError, GislProbePendingError, GislUploadCapExceededError, GislValidationError, GislWorkflowExpiredError, } from './errors.js';
|
|
9
9
|
import { parseSseStream } from './sse.js';
|
|
10
10
|
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
@@ -56,6 +56,17 @@ const S3_MAX_MULTIPART_PARTS = 10_000;
|
|
|
56
56
|
const RECOMMENDED_CHUNK_SIZE_MAX_BYTES = 104_857_600; // 100 MiB
|
|
57
57
|
const DEFAULT_POLL_INTERVAL_MS = 2_000;
|
|
58
58
|
const DEFAULT_POLL_TIMEOUT_MS = 300_000; // 5 min
|
|
59
|
+
// Anonymous-read capability header. An anonymous (null-owner) workflow create
|
|
60
|
+
// returns a one-time `cap` token (WorkflowCreateResponse.cap); the session-less
|
|
61
|
+
// caller passes it back on status/downloads/events reads via this header so the
|
|
62
|
+
// server can authorize the read. A wrong/missing cap on a null-owner workflow
|
|
63
|
+
// returns 404 (no existence oracle), per contracts ticket YQt88cq2.
|
|
64
|
+
const WORKFLOW_CAPABILITY_HEADER = 'X-Workflow-Capability';
|
|
65
|
+
// Build the capability header set for a workflow read. Empty when no token is
|
|
66
|
+
// supplied (authenticated reads — the session authorizes those).
|
|
67
|
+
function workflowCapabilityHeaders(capability) {
|
|
68
|
+
return capability ? { [WORKFLOW_CAPABILITY_HEADER]: capability } : {};
|
|
69
|
+
}
|
|
59
70
|
// Statuses that waitForWorkflow() returns immediately on. Per ticket I24,
|
|
60
71
|
// `cancelled` and `expired` are terminal (a workflow cannot leave either
|
|
61
72
|
// state). `paused_insufficient_credits` is a soft-pause: not terminal, but
|
|
@@ -1599,10 +1610,16 @@ export class GislClient {
|
|
|
1599
1610
|
}
|
|
1600
1611
|
/**
|
|
1601
1612
|
* Get current workflow status.
|
|
1613
|
+
*
|
|
1614
|
+
* For an anonymous (null-owner) workflow, pass `opts.capability` (the `cap`
|
|
1615
|
+
* from the anonymous workflow-create response) so a session-less caller can
|
|
1616
|
+
* read it — the SDK sends it as the `X-Workflow-Capability` header. Omit for
|
|
1617
|
+
* authenticated reads. A wrong/missing cap on a null-owner workflow is a 404.
|
|
1602
1618
|
*/
|
|
1603
|
-
async getWorkflowStatus(workflowId) {
|
|
1619
|
+
async getWorkflowStatus(workflowId, opts = {}) {
|
|
1604
1620
|
return this.request('GET', `/api/workflows/${encodeURIComponent(workflowId)}/status`, {
|
|
1605
1621
|
deserialize: WorkflowStatusResponseFromJSON,
|
|
1622
|
+
headers: workflowCapabilityHeaders(opts.capability),
|
|
1606
1623
|
});
|
|
1607
1624
|
}
|
|
1608
1625
|
/**
|
|
@@ -1613,7 +1630,9 @@ export class GislClient {
|
|
|
1613
1630
|
const timeoutMs = options?.timeoutMs ?? DEFAULT_POLL_TIMEOUT_MS;
|
|
1614
1631
|
const deadline = Date.now() + timeoutMs;
|
|
1615
1632
|
while (true) {
|
|
1616
|
-
const status = await this.getWorkflowStatus(workflowId
|
|
1633
|
+
const status = await this.getWorkflowStatus(workflowId, {
|
|
1634
|
+
capability: options?.capability,
|
|
1635
|
+
});
|
|
1617
1636
|
options?.onPoll?.(status.status);
|
|
1618
1637
|
if (TERMINAL_STATUSES.has(status.status)) {
|
|
1619
1638
|
return status;
|
|
@@ -1669,14 +1688,25 @@ export class GislClient {
|
|
|
1669
1688
|
}
|
|
1670
1689
|
/**
|
|
1671
1690
|
* Get download URLs for a completed workflow.
|
|
1691
|
+
*
|
|
1692
|
+
* For an anonymous (null-owner) workflow, pass `opts.capability` (the `cap`
|
|
1693
|
+
* from the anonymous workflow-create response) so a session-less caller can
|
|
1694
|
+
* read it — the SDK sends it as the `X-Workflow-Capability` header. Omit for
|
|
1695
|
+
* authenticated reads. A wrong/missing cap on a null-owner workflow is a 404.
|
|
1672
1696
|
*/
|
|
1673
|
-
async getWorkflowDownloads(workflowId) {
|
|
1697
|
+
async getWorkflowDownloads(workflowId, opts = {}) {
|
|
1674
1698
|
return this.request('GET', `/api/workflows/${encodeURIComponent(workflowId)}/downloads`, {
|
|
1675
1699
|
deserialize: WorkflowDownloadResponseFromJSON,
|
|
1700
|
+
headers: workflowCapabilityHeaders(opts.capability),
|
|
1676
1701
|
});
|
|
1677
1702
|
}
|
|
1678
1703
|
/**
|
|
1679
1704
|
* Stream SSE events for a workflow. Returns an async iterable.
|
|
1705
|
+
*
|
|
1706
|
+
* For an anonymous (null-owner) workflow, pass `opts.capability` (the `cap`
|
|
1707
|
+
* from the anonymous workflow-create response) so a session-less caller can
|
|
1708
|
+
* read it — the SDK sends it as the `X-Workflow-Capability` header. Omit for
|
|
1709
|
+
* authenticated reads. A wrong/missing cap on a null-owner workflow is a 404.
|
|
1680
1710
|
*/
|
|
1681
1711
|
async streamEvents(workflowId, opts = {}) {
|
|
1682
1712
|
const eventsPath = `/api/workflows/${encodeURIComponent(workflowId)}/events`;
|
|
@@ -1702,6 +1732,7 @@ export class GislClient {
|
|
|
1702
1732
|
response = await this.request('GET', eventsPath, {
|
|
1703
1733
|
rawResponse: true,
|
|
1704
1734
|
signal: controller.signal,
|
|
1735
|
+
headers: workflowCapabilityHeaders(opts.capability),
|
|
1705
1736
|
});
|
|
1706
1737
|
}
|
|
1707
1738
|
catch (err) {
|
|
@@ -1903,6 +1934,17 @@ export class GislClient {
|
|
|
1903
1934
|
deserialize: CreditsBalanceResponseFromJSON,
|
|
1904
1935
|
});
|
|
1905
1936
|
}
|
|
1937
|
+
/**
|
|
1938
|
+
* Fetch the caller's effective account limits (the tier-resolved caps:
|
|
1939
|
+
* upload/merge size + total caps, surfaced override-aware). `GET
|
|
1940
|
+
* /api/v2/account/limits`. The success envelope's `data` is unwrapped to the
|
|
1941
|
+
* {@link AccountLimits} model (mirrors {@link getCreditsBalance}).
|
|
1942
|
+
*/
|
|
1943
|
+
async getAccountLimits() {
|
|
1944
|
+
return this.request('GET', '/api/v2/account/limits', {
|
|
1945
|
+
deserialize: AccountLimitsFromJSON,
|
|
1946
|
+
});
|
|
1947
|
+
}
|
|
1906
1948
|
// -----------------------------------------------------------------------
|
|
1907
1949
|
// Auth
|
|
1908
1950
|
// -----------------------------------------------------------------------
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route-aware image "Output" model (card YNLrGhNo, contracts tewB37Jg / v2.97.0).
|
|
3
|
+
*
|
|
4
|
+
* The file-first `output()`/`resize()` helpers resolve a single user-facing
|
|
5
|
+
* "Output" operation to the right underlying wire op + options, driven by the
|
|
6
|
+
* contract's `accepted-options/image-output-routes.json` projection. The route is
|
|
7
|
+
* `(input format token, output_format token)`:
|
|
8
|
+
* - `same_format` (output == input) → `source_op: compress` (libcaesium optimiser),
|
|
9
|
+
* wire `output_format: 'original'`;
|
|
10
|
+
* - `format_change` (output != input) → `source_op: convert` (transcoder),
|
|
11
|
+
* wire `output_format: <token>`.
|
|
12
|
+
*
|
|
13
|
+
* Each route cell lists the options the worker HONORS (live) and PLANS (advertised,
|
|
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`.
|
|
20
|
+
*
|
|
21
|
+
* This hand table MIRRORS the generated projection and is PINNED to it by
|
|
22
|
+
* `output-route-conformance.test.ts` (the watermark-capability-gate precedent) —
|
|
23
|
+
* a contract regen that changes a route's source_op / honored / planned options
|
|
24
|
+
* fails that test. Kept a hand table (not a runtime JSON read) so the gate is
|
|
25
|
+
* browser-safe, exactly like {@link WATERMARK_CAPABILITY}. Mirrored by the PHP
|
|
26
|
+
* `ImageOutputRoutes`.
|
|
27
|
+
*/
|
|
28
|
+
/** The resize option keys — input-keyed, raster-only (see module doc). */
|
|
29
|
+
export declare const RESIZE_KEYS: readonly ["width", "height", "fit"];
|
|
30
|
+
/** Image area cap shared by every resizable route (projection `max_output_pixels`). */
|
|
31
|
+
export declare const MAX_OUTPUT_PIXELS = 16000000;
|
|
32
|
+
/**
|
|
33
|
+
* Output formats reachable via the legacy `compress(output_format=…)` facade
|
|
34
|
+
* (projection `facade_managed_outputs`). Used ONLY as the undetectable-input
|
|
35
|
+
* fallback — a detectable input always routes via `source_op`.
|
|
36
|
+
*/
|
|
37
|
+
export declare const FACADE_MANAGED_OUTPUTS: readonly string[];
|
|
38
|
+
interface RouteCell {
|
|
39
|
+
readonly honored: readonly string[];
|
|
40
|
+
readonly planned: readonly string[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Per-route, per-output-format honored + planned option keys, mirroring
|
|
44
|
+
* `image-output-routes.json` `media.image`. `source_op` is uniform
|
|
45
|
+
* (same_format→compress, format_change→convert) so it is a derivation rule, not
|
|
46
|
+
* a table column. `same_format` is keyed by the INPUT token; `format_change` by
|
|
47
|
+
* the OUTPUT token.
|
|
48
|
+
*/
|
|
49
|
+
export declare const IMAGE_OUTPUT_ROUTES: {
|
|
50
|
+
readonly same_format: Readonly<Record<string, RouteCell>>;
|
|
51
|
+
readonly format_change: Readonly<Record<string, RouteCell>>;
|
|
52
|
+
};
|
|
53
|
+
export type OutputRoute = 'same_format' | 'format_change';
|
|
54
|
+
/** A resolved Output lowering target. */
|
|
55
|
+
export interface ResolvedOutputRoute {
|
|
56
|
+
readonly route: OutputRoute;
|
|
57
|
+
/** The wire op to emit. */
|
|
58
|
+
readonly sourceOp: 'compress' | 'convert';
|
|
59
|
+
/** The wire `output_format` value ('original' for same_format, the token for format_change). */
|
|
60
|
+
readonly outputFormatWire: string;
|
|
61
|
+
/** The input format token the route resolved against (for per-value gating). */
|
|
62
|
+
readonly inputToken: string;
|
|
63
|
+
/** Effective honored option keys (incl. input-keyed resize on format_change). */
|
|
64
|
+
readonly honored: ReadonlySet<string>;
|
|
65
|
+
/** Planned option keys → gate as `feature_not_available`. */
|
|
66
|
+
readonly planned: ReadonlySet<string>;
|
|
67
|
+
}
|
|
68
|
+
/** The bare format token for a MIME type, or undefined if not a known image MIME. */
|
|
69
|
+
export declare function tokenForMime(mime: string): string | undefined;
|
|
70
|
+
/** The bare format token for a filename / path extension, or undefined. */
|
|
71
|
+
export declare function tokenForPath(path: string): string | undefined;
|
|
72
|
+
/** Every image format token the projection knows (for validation / tests). */
|
|
73
|
+
export declare function knownImageTokens(): ReadonlySet<string>;
|
|
74
|
+
/**
|
|
75
|
+
* Resolve an Output request to its wire op + gating sets. Returns undefined when
|
|
76
|
+
* the route is unrepresentable (e.g. converting TO a format no `format_change`
|
|
77
|
+
* cell covers). `outputFormat` undefined → same-format (keep input format).
|
|
78
|
+
*/
|
|
79
|
+
export declare function resolveOutputRoute(inputToken: string, outputFormat: string | undefined): ResolvedOutputRoute | undefined;
|
|
80
|
+
/**
|
|
81
|
+
* Whether a specific VALUE of an option is `availability: 'planned'` for the
|
|
82
|
+
* given input format — the per-value gate (e.g. `metadata: 'keep'` is planned
|
|
83
|
+
* even though the `metadata` key is honored). Reads the generated
|
|
84
|
+
* `compressMetadata` `per_value_availability`; same_format only (the only route
|
|
85
|
+
* where value-level options like `metadata` are honored). Returns false when the
|
|
86
|
+
* option / value / group is unknown (no gate).
|
|
87
|
+
*/
|
|
88
|
+
export declare function isPlannedValue(inputToken: string, optionKey: string, value: unknown): boolean;
|
|
89
|
+
export {};
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route-aware image "Output" model (card YNLrGhNo, contracts tewB37Jg / v2.97.0).
|
|
3
|
+
*
|
|
4
|
+
* The file-first `output()`/`resize()` helpers resolve a single user-facing
|
|
5
|
+
* "Output" operation to the right underlying wire op + options, driven by the
|
|
6
|
+
* contract's `accepted-options/image-output-routes.json` projection. The route is
|
|
7
|
+
* `(input format token, output_format token)`:
|
|
8
|
+
* - `same_format` (output == input) → `source_op: compress` (libcaesium optimiser),
|
|
9
|
+
* wire `output_format: 'original'`;
|
|
10
|
+
* - `format_change` (output != input) → `source_op: convert` (transcoder),
|
|
11
|
+
* wire `output_format: <token>`.
|
|
12
|
+
*
|
|
13
|
+
* Each route cell lists the options the worker HONORS (live) and PLANS (advertised,
|
|
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`.
|
|
20
|
+
*
|
|
21
|
+
* This hand table MIRRORS the generated projection and is PINNED to it by
|
|
22
|
+
* `output-route-conformance.test.ts` (the watermark-capability-gate precedent) —
|
|
23
|
+
* a contract regen that changes a route's source_op / honored / planned options
|
|
24
|
+
* fails that test. Kept a hand table (not a runtime JSON read) so the gate is
|
|
25
|
+
* browser-safe, exactly like {@link WATERMARK_CAPABILITY}. Mirrored by the PHP
|
|
26
|
+
* `ImageOutputRoutes`.
|
|
27
|
+
*/
|
|
28
|
+
import { compressMetadata } from '@giveitsmaller/contracts/operations';
|
|
29
|
+
/** The resize option keys — input-keyed, raster-only (see module doc). */
|
|
30
|
+
export const RESIZE_KEYS = ['width', 'height', 'fit'];
|
|
31
|
+
/** Image area cap shared by every resizable route (projection `max_output_pixels`). */
|
|
32
|
+
export const MAX_OUTPUT_PIXELS = 16_000_000;
|
|
33
|
+
/**
|
|
34
|
+
* Output formats reachable via the legacy `compress(output_format=…)` facade
|
|
35
|
+
* (projection `facade_managed_outputs`). Used ONLY as the undetectable-input
|
|
36
|
+
* fallback — a detectable input always routes via `source_op`.
|
|
37
|
+
*/
|
|
38
|
+
export const FACADE_MANAGED_OUTPUTS = ['webp'];
|
|
39
|
+
/** Canonical MIME → bare format token (projection `mime_tokens`). */
|
|
40
|
+
const MIME_TOKEN = {
|
|
41
|
+
'image/avif': 'avif',
|
|
42
|
+
'image/gif': 'gif',
|
|
43
|
+
'image/jpeg': 'jpeg',
|
|
44
|
+
'image/png': 'png',
|
|
45
|
+
'image/svg+xml': 'svg',
|
|
46
|
+
'image/tiff': 'tiff',
|
|
47
|
+
'image/webp': 'webp',
|
|
48
|
+
};
|
|
49
|
+
/** File extension → bare format token (for path / named-blob inputs). */
|
|
50
|
+
const EXT_TOKEN = {
|
|
51
|
+
jpg: 'jpeg', jpeg: 'jpeg', jpe: 'jpeg', jfif: 'jpeg',
|
|
52
|
+
png: 'png', webp: 'webp', gif: 'gif', avif: 'avif',
|
|
53
|
+
tif: 'tiff', tiff: 'tiff', svg: 'svg',
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Per-route, per-output-format honored + planned option keys, mirroring
|
|
57
|
+
* `image-output-routes.json` `media.image`. `source_op` is uniform
|
|
58
|
+
* (same_format→compress, format_change→convert) so it is a derivation rule, not
|
|
59
|
+
* a table column. `same_format` is keyed by the INPUT token; `format_change` by
|
|
60
|
+
* the OUTPUT token.
|
|
61
|
+
*/
|
|
62
|
+
export const IMAGE_OUTPUT_ROUTES = {
|
|
63
|
+
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', 'metadata', 'output_format', 'progressive', 'quality', 'width'], planned: ['lossless'] },
|
|
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', 'metadata', 'output_format', 'quality', 'width'], planned: ['lossless'] },
|
|
71
|
+
},
|
|
72
|
+
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: [] },
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
/** The bare format token for a MIME type, or undefined if not a known image MIME. */
|
|
82
|
+
export function tokenForMime(mime) {
|
|
83
|
+
return MIME_TOKEN[mime.split(';')[0].trim().toLowerCase()];
|
|
84
|
+
}
|
|
85
|
+
/** The bare format token for a filename / path extension, or undefined. */
|
|
86
|
+
export function tokenForPath(path) {
|
|
87
|
+
const ext = path.toLowerCase().split('.').pop();
|
|
88
|
+
return ext !== undefined ? EXT_TOKEN[ext] : undefined;
|
|
89
|
+
}
|
|
90
|
+
/** Every image format token the projection knows (for validation / tests). */
|
|
91
|
+
export function knownImageTokens() {
|
|
92
|
+
return new Set(Object.keys(IMAGE_OUTPUT_ROUTES.same_format));
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Resolve an Output request to its wire op + gating sets. Returns undefined when
|
|
96
|
+
* the route is unrepresentable (e.g. converting TO a format no `format_change`
|
|
97
|
+
* cell covers). `outputFormat` undefined → same-format (keep input format).
|
|
98
|
+
*/
|
|
99
|
+
export function resolveOutputRoute(inputToken, outputFormat) {
|
|
100
|
+
const outToken = outputFormat ?? inputToken;
|
|
101
|
+
if (outToken === inputToken) {
|
|
102
|
+
const cell = IMAGE_OUTPUT_ROUTES.same_format[inputToken];
|
|
103
|
+
if (cell === undefined)
|
|
104
|
+
return undefined;
|
|
105
|
+
return {
|
|
106
|
+
route: 'same_format',
|
|
107
|
+
sourceOp: 'compress',
|
|
108
|
+
outputFormatWire: 'original',
|
|
109
|
+
inputToken,
|
|
110
|
+
honored: new Set(cell.honored),
|
|
111
|
+
planned: new Set(cell.planned),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
const cell = IMAGE_OUTPUT_ROUTES.format_change[outToken];
|
|
115
|
+
if (cell === undefined)
|
|
116
|
+
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).
|
|
119
|
+
const inCell = IMAGE_OUTPUT_ROUTES.same_format[inputToken];
|
|
120
|
+
const resize = inCell ? RESIZE_KEYS.filter((k) => inCell.honored.includes(k)) : [];
|
|
121
|
+
return {
|
|
122
|
+
route: 'format_change',
|
|
123
|
+
sourceOp: 'convert',
|
|
124
|
+
outputFormatWire: outToken,
|
|
125
|
+
inputToken,
|
|
126
|
+
honored: new Set([...cell.honored, ...resize]),
|
|
127
|
+
planned: new Set(cell.planned),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/** Input token → its `compress.image*` mime-group name (for per-value availability lookup). */
|
|
131
|
+
function compressGroupForToken(token) {
|
|
132
|
+
if (token === 'jpeg')
|
|
133
|
+
return 'image_jpeg';
|
|
134
|
+
if (token === 'png')
|
|
135
|
+
return 'image_png';
|
|
136
|
+
if (token === 'avif')
|
|
137
|
+
return 'image_avif';
|
|
138
|
+
return 'image'; // webp / gif / svg / tiff
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Whether a specific VALUE of an option is `availability: 'planned'` for the
|
|
142
|
+
* given input format — the per-value gate (e.g. `metadata: 'keep'` is planned
|
|
143
|
+
* even though the `metadata` key is honored). Reads the generated
|
|
144
|
+
* `compressMetadata` `per_value_availability`; same_format only (the only route
|
|
145
|
+
* where value-level options like `metadata` are honored). Returns false when the
|
|
146
|
+
* option / value / group is unknown (no gate).
|
|
147
|
+
*/
|
|
148
|
+
export function isPlannedValue(inputToken, optionKey, value) {
|
|
149
|
+
const group = compressMetadata.mime_groups[compressGroupForToken(inputToken)];
|
|
150
|
+
const opt = group?.options[optionKey];
|
|
151
|
+
if (opt === undefined)
|
|
152
|
+
return false;
|
|
153
|
+
const entry = opt.per_value_availability[String(value)];
|
|
154
|
+
return entry?.availability === 'planned';
|
|
155
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed per-op option interfaces for the ergonomic verbs (card Dhje3Faq).
|
|
3
|
+
* These replace the untyped `Record<string, unknown>` bags so the IDE can offer
|
|
4
|
+
* key completion and `tsc` rejects typos. The KEY SET of each interface is pinned
|
|
5
|
+
* to the contract two ways: (1) the source-level `Equal<...>` assertions below tie
|
|
6
|
+
* each interface to its `*_OPTION_KEYS` tuple at `tsc` time; (2) the wire-key
|
|
7
|
+
* conformance guard ties each tuple (∪ positional-owned) to the generated
|
|
8
|
+
* `OperationMetadata` at test time. Value types are best-effort (per-value/enum
|
|
9
|
+
* sync is out of scope — keys are the contract anchor).
|
|
10
|
+
*
|
|
11
|
+
* Keys a verb owns via a positional argument are EXCLUDED from its interface
|
|
12
|
+
* (`output_format` on convert, `text` on textWatermark) — they are set by the
|
|
13
|
+
* first argument and rejected if supplied in the bag (see `option_validation.ts`).
|
|
14
|
+
*
|
|
15
|
+
* Mirrored by the PHP array-shape docblocks — keep in lockstep.
|
|
16
|
+
*/
|
|
17
|
+
/** 9-grid anchor shared by text/image/video watermark. */
|
|
18
|
+
export type WatermarkAnchor = 'top_left' | 'top_center' | 'top_right' | 'center_left' | 'center' | 'center_right' | 'bottom_left' | 'bottom_center' | 'bottom_right';
|
|
19
|
+
export interface ConvertOptions {
|
|
20
|
+
/** Output quality for lossy image formats (1-100). */
|
|
21
|
+
quality?: number;
|
|
22
|
+
/** Background colour (hex) for transparent images → JPEG. */
|
|
23
|
+
background?: string;
|
|
24
|
+
/** Video Constant Rate Factor (0 best — 51 worst). */
|
|
25
|
+
crf?: number;
|
|
26
|
+
/** Trim from the start, in seconds (video). */
|
|
27
|
+
trim_start?: number;
|
|
28
|
+
/** Trim from the end, in seconds (video). */
|
|
29
|
+
trim_end?: number;
|
|
30
|
+
/** Output frame rate for video → GIF. */
|
|
31
|
+
fps?: number;
|
|
32
|
+
/** Max output width in pixels (video → GIF downscale cap). */
|
|
33
|
+
width?: number;
|
|
34
|
+
/** GIF palette size (2-256). */
|
|
35
|
+
max_colors?: number;
|
|
36
|
+
/** GIF loop count (0 infinite, N>0 N times, -1 once). */
|
|
37
|
+
loop?: number;
|
|
38
|
+
/** GIF dithering method. */
|
|
39
|
+
dither?: 'none' | 'bayer' | 'floyd_steinberg' | 'sierra2' | 'sierra2_4a';
|
|
40
|
+
/** Output bitrate in kbps (lossy audio). */
|
|
41
|
+
bitrate?: 64 | 96 | 128 | 192 | 256 | 320;
|
|
42
|
+
/** Page selection for PDF → image (e.g. '1-5,8'). */
|
|
43
|
+
pages?: string;
|
|
44
|
+
/** Render resolution in DPI for PDF → image. */
|
|
45
|
+
dpi?: number;
|
|
46
|
+
}
|
|
47
|
+
export interface ThumbnailOptions {
|
|
48
|
+
/** Target width in pixels (1-16384). REQUIRED. */
|
|
49
|
+
width: number;
|
|
50
|
+
/** Target height in pixels (1-16384). REQUIRED. */
|
|
51
|
+
height: number;
|
|
52
|
+
/** Resize mode. */
|
|
53
|
+
fit?: 'max' | 'crop' | 'scale';
|
|
54
|
+
/** Output format for the thumbnail. */
|
|
55
|
+
format?: 'jpg' | 'png' | 'webp';
|
|
56
|
+
/** Output quality for lossy thumbnail formats (image input). */
|
|
57
|
+
quality?: number;
|
|
58
|
+
/** Frame timestamp for video input (e.g. '00:00:01'). */
|
|
59
|
+
timestamp?: string;
|
|
60
|
+
/** Document source: a printed page or the cover. */
|
|
61
|
+
source?: 'page' | 'cover';
|
|
62
|
+
/** 1-based page index for document input. */
|
|
63
|
+
page?: number;
|
|
64
|
+
}
|
|
65
|
+
export interface TextWatermarkOptions {
|
|
66
|
+
/** Font size in pixels (8-512). */
|
|
67
|
+
font_size?: number;
|
|
68
|
+
/** Text colour as hex RGB/RGBA (e.g. '#FFFFFF80'). */
|
|
69
|
+
color?: string;
|
|
70
|
+
/** Font family (bundled). */
|
|
71
|
+
font_family?: 'liberation_sans';
|
|
72
|
+
/** Rotation angle in degrees (-360..360). */
|
|
73
|
+
rotation?: number;
|
|
74
|
+
/** Rendering mode. */
|
|
75
|
+
watermark_mode?: 'single' | 'tiled';
|
|
76
|
+
/** Spacing between tiled labels in pixels (tiled mode). */
|
|
77
|
+
tile_spacing?: number;
|
|
78
|
+
/** 9-grid anchor position. */
|
|
79
|
+
anchor?: WatermarkAnchor;
|
|
80
|
+
/** Horizontal offset from the anchor (e.g. '40px' or '5%'). */
|
|
81
|
+
margin_x?: string;
|
|
82
|
+
/** Vertical offset from the anchor. */
|
|
83
|
+
margin_y?: string;
|
|
84
|
+
/** Overlay opacity (0-1). */
|
|
85
|
+
opacity?: number;
|
|
86
|
+
}
|
|
87
|
+
export interface WatermarkOptions {
|
|
88
|
+
/** 9-grid anchor position. */
|
|
89
|
+
anchor?: WatermarkAnchor;
|
|
90
|
+
/** Horizontal offset from the anchor (e.g. '40px' or '5%'). */
|
|
91
|
+
margin_x?: string;
|
|
92
|
+
/** Vertical offset from the anchor. */
|
|
93
|
+
margin_y?: string;
|
|
94
|
+
/** Overlay opacity (0-1). */
|
|
95
|
+
opacity?: number;
|
|
96
|
+
/** Overlay width (e.g. '120px' or '20%'). */
|
|
97
|
+
overlay_width?: string;
|
|
98
|
+
}
|
|
99
|
+
/** Resize mode (contract `fit` enum, v2.97.0). */
|
|
100
|
+
export type OutputFit = 'max' | 'crop' | 'scale';
|
|
101
|
+
/** Metadata policy (contract `metadata` enum, v2.97.0). `keep` is `availability:planned`. */
|
|
102
|
+
export type OutputMetadata = 'all' | 'keep';
|
|
103
|
+
/**
|
|
104
|
+
* Options for the file-first `output()` image transform. The KEY SET is the
|
|
105
|
+
* UNION of every image route's honored + planned option keys (image-output-routes
|
|
106
|
+
* projection); the PER-ROUTE honored/planned narrowing happens in the lowering
|
|
107
|
+
* (`resolveOutputRoute`), so supplying an option not honored on the resolved
|
|
108
|
+
* route (or a planned one) throws pre-upload. `output_format` is set via the
|
|
109
|
+
* positional `format` argument, so it is excluded here. Resize (`width`/`height`/
|
|
110
|
+
* `fit`) is honored on raster routes; `height` is optional (width-only resize).
|
|
111
|
+
*/
|
|
112
|
+
export interface OutputOptions {
|
|
113
|
+
/** Output quality for lossy formats (1-100). Honored: avif/jpeg/webp routes. */
|
|
114
|
+
quality?: number;
|
|
115
|
+
/** Resize target width in px (1-16384; width*height <= 16MP). */
|
|
116
|
+
width?: number;
|
|
117
|
+
/** Resize target height in px (optional — width-only resize preserves aspect). */
|
|
118
|
+
height?: number;
|
|
119
|
+
/** Resize mode (applies when width or height is set). */
|
|
120
|
+
fit?: OutputFit;
|
|
121
|
+
/** Background colour (hex) for transparent images → JPEG. Honored: format_change→jpeg only. */
|
|
122
|
+
background?: string;
|
|
123
|
+
/** Progressive JPEG. Honored: same_format jpeg only. */
|
|
124
|
+
progressive?: boolean;
|
|
125
|
+
/** PNG lossless optimisation effort. Honored: same_format png only. */
|
|
126
|
+
optimization_level?: number;
|
|
127
|
+
/** AVIF encode speed. Honored: same_format avif only. */
|
|
128
|
+
avif_speed?: number;
|
|
129
|
+
/** Metadata policy. Honored: same_format routes. (`keep` value is planned.) */
|
|
130
|
+
metadata?: OutputMetadata;
|
|
131
|
+
/** JPEG/WebP lossless. PLANNED (gated unavailable). */
|
|
132
|
+
lossless?: boolean;
|
|
133
|
+
/** Lossy PNG quantization. PLANNED (gated unavailable; licence-gated). */
|
|
134
|
+
lossy?: boolean;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* The user-supplyable option keys per verb (excludes positional-owned keys).
|
|
138
|
+
* Exported for the wire-key conformance guard, which asserts each tuple ∪ its
|
|
139
|
+
* positional-owned keys equals the contract `operationOptionKeys(metadata)`.
|
|
140
|
+
*/
|
|
141
|
+
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"];
|
|
144
|
+
readonly textWatermark: readonly ["font_size", "color", "font_family", "rotation", "watermark_mode", "tile_spacing", "anchor", "margin_x", "margin_y", "opacity"];
|
|
145
|
+
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"];
|
|
147
|
+
};
|