@giveitsmaller/sdk 0.2.3 → 0.4.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 ADDED
@@ -0,0 +1,58 @@
1
+ # @giveitsmaller/sdk
2
+
3
+ TypeScript / Node.js SDK for the [GISL](https://giveitsmaller.com) (Give It Smaller) file compression and processing API.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @giveitsmaller/sdk
9
+ ```
10
+
11
+ Node.js 18+ required.
12
+
13
+ ## Quickstart
14
+
15
+ ```ts
16
+ import { GislClient, uploadSource, OperationType } from '@giveitsmaller/sdk';
17
+
18
+ const client = new GislClient({
19
+ baseUrl: 'https://api.giveitsmaller.com',
20
+ apiKey: 'REPLACE_ME_API_KEY',
21
+ });
22
+
23
+ const upload = await client.uploadFile('./photo.jpg');
24
+
25
+ const workflow = await client.createWorkflow({
26
+ jobs: [
27
+ {
28
+ id: 'compressed',
29
+ source: uploadSource(upload.fileId),
30
+ operations: [
31
+ { type: OperationType.compress, options: { mode: 'lossy', quality: 80 } },
32
+ ],
33
+ },
34
+ ],
35
+ });
36
+
37
+ await client.waitForWorkflow(workflow.workflowId);
38
+
39
+ const dls = await client.getWorkflowDownloads(workflow.workflowId);
40
+ console.log('Compressed:', dls.downloads[0].files[0].downloadUrl);
41
+ ```
42
+
43
+ ## Full documentation
44
+
45
+ Docs are published in the [giveitsmaller-sdks](https://github.com/AntonioCS/giveitsmaller-sdks) repository — they are **not** shipped in the npm tarball (only `dist/` is published).
46
+
47
+ - **Getting started & concepts** — [`docs/typescript/index.md`](https://github.com/AntonioCS/giveitsmaller-sdks/blob/main/docs/typescript/index.md)
48
+ - **Client reference** (all `GislClient` methods + operation option list) — [`docs/typescript/client.md`](https://github.com/AntonioCS/giveitsmaller-sdks/blob/main/docs/typescript/client.md)
49
+ - **Types & authoring primitives** (config, payloads, job factories) — [`docs/typescript/types.md`](https://github.com/AntonioCS/giveitsmaller-sdks/blob/main/docs/typescript/types.md)
50
+ - **SSE / live progress** — [`docs/typescript/sse.md`](https://github.com/AntonioCS/giveitsmaller-sdks/blob/main/docs/typescript/sse.md)
51
+ - **Webhooks** — [`docs/typescript/webhook.md`](https://github.com/AntonioCS/giveitsmaller-sdks/blob/main/docs/typescript/webhook.md)
52
+ - **Errors & retry guidance** — [`docs/typescript/errors.md`](https://github.com/AntonioCS/giveitsmaller-sdks/blob/main/docs/typescript/errors.md)
53
+ - **Troubleshooting** — [`docs/typescript/troubleshooting.md`](https://github.com/AntonioCS/giveitsmaller-sdks/blob/main/docs/typescript/troubleshooting.md)
54
+ - **Examples** — compress, thumbnail, convert, merge, archive — [`docs/typescript/examples/`](https://github.com/AntonioCS/giveitsmaller-sdks/tree/main/docs/typescript/examples)
55
+
56
+ ## License
57
+
58
+ MIT — see the [LICENSE](https://github.com/AntonioCS/giveitsmaller-sdks/blob/main/LICENSE) file.
@@ -0,0 +1 @@
1
+ export declare function _runAudit(): void;
package/dist/_audit.js ADDED
@@ -0,0 +1,57 @@
1
+ // Per ticket J65ZERTi (T20) — public-API completeness audit gate.
2
+ //
3
+ // This file sits inside `src/` so it is type-checked by `tsc --noEmit`
4
+ // (the test directory is excluded from `tsconfig.json` so a test-side
5
+ // audit is dead — earlier follow-up review caught this). The list of
6
+ // type-only imports here IS the gate: if a regen drops or renames any
7
+ // symbol, `tsc` fails on the import line.
8
+ //
9
+ // Each `accept<T>()` call resolves the type parameter; the function
10
+ // itself is a no-op. The body is never invoked at runtime — only the
11
+ // import resolution matters.
12
+ function accept(_value) {
13
+ // intentionally empty — type-presence is the assertion
14
+ }
15
+ // One call per imported type. tsc resolves the type parameter against
16
+ // the import; if the import is broken, the call site fails to compile.
17
+ export function _runAudit() {
18
+ accept();
19
+ accept();
20
+ accept();
21
+ accept();
22
+ accept();
23
+ accept();
24
+ accept();
25
+ accept();
26
+ accept();
27
+ accept();
28
+ accept();
29
+ accept();
30
+ accept();
31
+ accept();
32
+ accept();
33
+ accept();
34
+ accept();
35
+ accept();
36
+ accept();
37
+ accept();
38
+ accept();
39
+ accept();
40
+ accept();
41
+ accept();
42
+ accept();
43
+ accept();
44
+ accept();
45
+ accept();
46
+ accept();
47
+ accept();
48
+ accept();
49
+ accept();
50
+ accept();
51
+ accept();
52
+ accept();
53
+ accept();
54
+ accept();
55
+ accept();
56
+ accept();
57
+ }
package/dist/client.d.ts CHANGED
@@ -1,15 +1,29 @@
1
- import type { UploadResponse, WorkflowCreateResponse, WorkflowStatusResponse, WorkflowDownloadResponse, MetadataResponse, OperationsSchemaResponse, RetryResponse } from '@giveitsmaller/contracts/openapi';
2
- import type { GislClientConfig, GislSseEvent, UploadOptions, WaitOptions, WorkflowCreatePayload } from './types.js';
1
+ import type { AudioWatermarkDecodeRequest, AudioWatermarkDecodeResponse, ExternalImportCreatedResponse, ExternalImportRequest, LoginUserRequest, LoginUser200ResponseData, ContactRequest, CreditsBalanceResponse, CreditsUsageResponse, UploadResponse, UploadProbeResponse, WorkflowCancelResponse, WorkflowCreateResponse, WorkflowResumeResponse, WorkflowStatusResponse, WorkflowDownloadResponse, MetadataResponse, RetryResponse } from '@giveitsmaller/contracts/openapi';
2
+ import type { CreditsUsageOptions, GetSchemaOptions, GetSchemaResult, GislClientConfig, GislSseEvent, PreflightClipsResult, UploadOptions, WaitOptions, WorkflowCreatePayload } from './types.js';
3
+ export declare const MULTIPART_CONCURRENCY_DEFAULT: 4;
3
4
  export declare const DEFAULT_MULTIPART_FIRST_CHUNK_SIZE: number;
5
+ export interface ValidationDetail {
6
+ message: string;
7
+ field?: string;
8
+ operation?: string;
9
+ option?: string;
10
+ messageKey?: string;
11
+ locale?: string;
12
+ messageParams?: Record<string, unknown>;
13
+ }
4
14
  export declare class GislClient {
5
15
  private readonly baseUrl;
6
16
  private readonly headers;
7
17
  private readonly timeoutMs;
8
18
  private readonly multipartThreshold;
9
19
  private readonly multipartConcurrency;
20
+ private readonly multipartMaxAttempts;
21
+ private readonly multipartRetryBaseMs;
22
+ private readonly useSessionCookie;
10
23
  constructor(config: GislClientConfig);
11
24
  private request;
12
25
  private handleResponse;
26
+ private isAuthErrorType;
13
27
  /**
14
28
  * Upload a file. Automatically uses multipart upload for files exceeding
15
29
  * the configured threshold (default 10 MB).
@@ -43,6 +57,41 @@ export declare class GislClient {
43
57
  * Poll until the workflow reaches a terminal status.
44
58
  */
45
59
  waitForWorkflow(workflowId: string, options?: WaitOptions): Promise<WorkflowStatusResponse>;
60
+ /**
61
+ * Cancel a workflow. Idempotent — cancelling an already-cancelled
62
+ * workflow returns 200 with the same shape (and the original
63
+ * `cancelledAt`). Cancelling a `completed` / `failed` /
64
+ * `partially_failed` / `expired` workflow returns 409.
65
+ *
66
+ * The response's `billingEffect` field tells the caller what
67
+ * happened to outstanding reservations:
68
+ * - `unspent_reservation_released` — workflow was active or paused
69
+ * and the unspent portion of the reservation has been refunded.
70
+ * The refund appears as a separate `CreditTransaction` with
71
+ * `type: refund`.
72
+ * - `none` — no refund (all reserved credits were already consumed
73
+ * by completed jobs, or this is an idempotent re-cancel).
74
+ *
75
+ * In-flight operations may continue running briefly after the
76
+ * cancel response while their Lambda processes terminate; the
77
+ * response is the binding "no further reservations will be made"
78
+ * signal.
79
+ */
80
+ cancelWorkflow(workflowId: string): Promise<WorkflowCancelResponse>;
81
+ /**
82
+ * Resume a workflow that is in `paused_insufficient_credits`.
83
+ *
84
+ * Resume succeeds only when `availableCredits` covers the next
85
+ * reservation. If the balance is still insufficient, throws
86
+ * `GislBalanceExhaustedError` (402, same envelope as the workflow-
87
+ * create 402 path) and the workflow stays paused. If the workflow
88
+ * is past its `expiresAt` (default 7-day TTL from `pausedAt`),
89
+ * throws `GislWorkflowExpiredError` (422) and the workflow has
90
+ * transitioned to `expired` — callers cannot un-expire a workflow.
91
+ * Resuming a workflow that is not in `paused_insufficient_credits`
92
+ * is a 409 (no-op).
93
+ */
94
+ resumeWorkflow(workflowId: string): Promise<WorkflowResumeResponse>;
46
95
  /**
47
96
  * Get download URLs for a completed workflow.
48
97
  */
@@ -57,11 +106,128 @@ export declare class GislClient {
57
106
  getMetadata(fileId: string): Promise<MetadataResponse>;
58
107
  /**
59
108
  * Get the operations schema (available types, options, constraints).
60
- * This endpoint returns raw JSON (no envelope) and is CDN-cacheable.
109
+ *
110
+ * Returns raw JSON (no envelope). The response is **per-tier private**
111
+ * (cache key includes the caller's `user_tier`); CDN-style public
112
+ * caching is not used. Pass `ifNoneMatch` / `ifModifiedSince` from a
113
+ * previous response to revalidate — a 304 surfaces as
114
+ * `{ notModified: true, etag, lastModified }` so callers can keep
115
+ * using their cached copy.
61
116
  */
62
- getSchema(): Promise<OperationsSchemaResponse>;
117
+ getSchema(options?: GetSchemaOptions): Promise<GetSchemaResult>;
63
118
  /**
64
119
  * Retry a failed operation.
65
120
  */
66
121
  retryOperation(operationId: string): Promise<RetryResponse>;
122
+ /**
123
+ * Submit a contact-form message. The endpoint returns 204 No Content on
124
+ * success, so this method resolves to `void`.
125
+ *
126
+ * Validation errors (e.g. missing `email`, non-empty honeypot `website`)
127
+ * surface as `GislValidationError` from the standard error envelope.
128
+ */
129
+ submitContact(payload: ContactRequest): Promise<void>;
130
+ /**
131
+ * Get a snapshot of the caller's current credit position. The canonical
132
+ * billing-state surface — `BalanceExhaustedResponse` (402) on workflow
133
+ * creation includes pre-error counters for context, but UIs should drive
134
+ * spend-now affordances and tier-upgrade prompts off this endpoint, not
135
+ * off the error envelope.
136
+ */
137
+ getCreditsBalance(): Promise<CreditsBalanceResponse>;
138
+ /**
139
+ * Authenticate with email/password. On success the server issues a
140
+ * session cookie via `Set-Cookie`; subsequent requests authenticate
141
+ * via that cookie when the client is configured with
142
+ * `useSessionCookie: true`.
143
+ *
144
+ * Failure modes per ticket FX6mbTJD:
145
+ * - **401** `invalid_credentials` (collapsed with unverified
146
+ * accounts for anti-enumeration) → `GislAuthError`.
147
+ * - **403** account-state failures (`account_locked`,
148
+ * `account_disabled`, `account_deleted`,
149
+ * `account_deletion_expired`) → `GislAuthError`.
150
+ * - **429** infrastructure rate-limit → `GislApiError` with
151
+ * the `Retry-After` header echoed on the response.
152
+ *
153
+ * Node session persistence (cookie-jar across processes) is out of
154
+ * scope — this method only touches the request side.
155
+ */
156
+ login(credentials: LoginUserRequest): Promise<LoginUser200ResponseData>;
157
+ /**
158
+ * Invalidate the current session.
159
+ *
160
+ * Idempotent: calling logout without an active session returns 401,
161
+ * but the SDK collapses both 200 and 401 into a single "logged out"
162
+ * outcome — `logout()` resolves to `void` in either case so caller
163
+ * cleanup code does not need to special-case the not-currently-
164
+ * authenticated path. Other errors (e.g. 500, network failures)
165
+ * still throw.
166
+ */
167
+ logout(): Promise<void>;
168
+ /**
169
+ * Register a one-shot bearer URL (S3 presigned, GCS signed, Azure
170
+ * SAS, Dropbox shared link, public HTTPS) and receive an opaque
171
+ * `externalSourceId` handle. Subsequent workflows reference the
172
+ * handle via `WorkflowSource` of `type: external_import` —
173
+ * compose with the [`externalImportSource()`](./types.ts) factory.
174
+ *
175
+ * Per ADR-0005 §"SSRF posture": the server validates 8 rules at
176
+ * registration time AND again at fetch time. HTTPS-only;
177
+ * private/loopback/cloud-metadata IPs are rejected (403). The
178
+ * original URL + password are encrypted at rest and never
179
+ * returned in any response.
180
+ *
181
+ * Currently `availability: planned` — the runtime endpoint returns
182
+ * 422 `feature_not_available` (or 404, per the cross-repo rollout)
183
+ * until the external-import infrastructure ships. The method
184
+ * exists today so consumers can write the integration ahead of
185
+ * time.
186
+ */
187
+ createExternalImport(payload: ExternalImportRequest): Promise<ExternalImportCreatedResponse>;
188
+ /**
189
+ * Decode a previously-embedded steganographic audio watermark
190
+ * (per ticket I20). Pairs with the `audio_watermark` operation —
191
+ * the operation embeds; this endpoint decodes.
192
+ *
193
+ * **Enterprise tier only.** Free / pro callers receive
194
+ * `GislFeatureTierRestrictedError` (403).
195
+ *
196
+ * **Own watermarks only.** The decoder will refuse to extract from
197
+ * media the caller did not mark themselves — mismatches return 404
198
+ * (rather than leaking that *some* watermark was detected).
199
+ *
200
+ * Currently `availability: planned` — calls return
201
+ * `GislFeatureNotAvailableError` (422) until the cross-repo Lambda
202
+ * support ships. Decode requests are rate-limited independently
203
+ * from workflow-create.
204
+ */
205
+ decodeAudioWatermark(payload: AudioWatermarkDecodeRequest): Promise<AudioWatermarkDecodeResponse>;
206
+ /**
207
+ * Probe an uploaded file for workflow-readiness — detects corruption,
208
+ * unsupported codecs, and pre-assigns the processing class the server
209
+ * would route the file to. Designed for the long-form merge edge case
210
+ * where a single bad input would fail the whole workflow.
211
+ *
212
+ * Currently `availability: planned` — calls return
213
+ * `GislFeatureNotAvailableError` (422) until the cross-repo Lambda
214
+ * support ships. Idempotent: probing the same `fileId` twice returns
215
+ * the cached result.
216
+ */
217
+ probeUpload(fileId: string): Promise<UploadProbeResponse>;
218
+ /**
219
+ * Probe N uploaded files in parallel and partition the results by
220
+ * outcome. Returns `{ ok, rejected, errors }` so the caller can
221
+ * cleanly drop bad clips before submitting a long-form merge
222
+ * workflow. Probe-call failures (including the
223
+ * `feature_not_available` 422 returned while the endpoint is
224
+ * `availability: planned`) land in `errors` rather than throwing,
225
+ * so a partially-successful batch still yields useful aggregation.
226
+ */
227
+ preflightClips(fileIds: string[]): Promise<PreflightClipsResult>;
228
+ /**
229
+ * Get a paginated page of credit transaction history for the caller.
230
+ * Server defaults: `limit=20`, `offset=0`. Most-recent-first.
231
+ */
232
+ getCreditsUsage(options?: CreditsUsageOptions): Promise<CreditsUsageResponse>;
67
233
  }