@giveitsmaller/sdk 0.20.0 → 0.22.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/LICENSE +202 -0
  2. package/README.md +55 -14
  3. package/dist/_audit.js +2 -4
  4. package/dist/builder.d.ts +4 -4
  5. package/dist/builder.js +47 -22
  6. package/dist/client.d.ts +9 -6
  7. package/dist/client.js +90 -31
  8. package/dist/ergonomic/image_output_routes.d.ts +97 -0
  9. package/dist/ergonomic/image_output_routes.js +227 -25
  10. package/dist/ergonomic/option_types.d.ts +11 -2
  11. package/dist/ergonomic/preset_resolver.d.ts +24 -2
  12. package/dist/ergonomic/preset_resolver.js +100 -10
  13. package/dist/ergonomic/presets/image_compress.js +16 -4
  14. package/dist/ergonomic/presets/index.d.ts +9 -8
  15. package/dist/ergonomic/presets/index.js +1 -9
  16. package/dist/ergonomic/presets/video_compress.d.ts +14 -0
  17. package/dist/errors.d.ts +101 -2
  18. package/dist/errors.js +108 -1
  19. package/dist/file-first.d.ts +81 -13
  20. package/dist/file-first.js +329 -69
  21. package/dist/generated/sdk_spec/enums.d.ts +0 -26
  22. package/dist/generated/sdk_spec/enums.js +0 -16
  23. package/dist/generated/sdk_spec/errors.d.ts +1 -1
  24. package/dist/generated/sdk_spec/errors.js +159 -1
  25. package/dist/generated/sdk_spec/presets.js +0 -14
  26. package/dist/generated/sdk_spec/version.d.ts +2 -2
  27. package/dist/generated/sdk_spec/version.js +2 -2
  28. package/dist/gisl.d.ts +21 -2
  29. package/dist/handle.d.ts +6 -1
  30. package/dist/handle.js +42 -13
  31. package/dist/index.core.d.ts +4 -4
  32. package/dist/index.core.js +6 -3
  33. package/dist/merge.d.ts +23 -0
  34. package/dist/merge.js +2 -2
  35. package/dist/sse.js +49 -1
  36. package/dist/types.d.ts +11 -3
  37. package/dist/types.js +1 -0
  38. package/package.json +3 -3
  39. package/dist/ergonomic/presets/document_pdf_compress.d.ts +0 -12
  40. package/dist/ergonomic/presets/document_pdf_compress.js +0 -33
package/dist/merge.js CHANGED
@@ -128,14 +128,14 @@ export class MergeBuilder {
128
128
  });
129
129
  // 5. Fetch downloads + project.
130
130
  if (Date.now() >= deadline) {
131
- throw new GislTimeoutError(`Merge workflow ${created.workflowId} reached terminal status but maxWait elapsed before downloads could be fetched`);
131
+ throw new GislTimeoutError(`Merge workflow ${created.workflowId} reached terminal status but maxWait elapsed before downloads could be fetched`, created.workflowId);
132
132
  }
133
133
  const downloads = await this.client.getWorkflowDownloads(created.workflowId);
134
134
  // TDqmkWpX: the maxWait deadline also covers the downloads fetch itself —
135
135
  // re-check AFTER the call so a slow getWorkflowDownloads cannot return a
136
136
  // success past the advertised whole-run deadline.
137
137
  if (Date.now() >= deadline) {
138
- throw new GislTimeoutError(`Merge workflow ${created.workflowId} downloads fetch completed after maxWait elapsed`);
138
+ throw new GislTimeoutError(`Merge workflow ${created.workflowId} downloads fetch completed after maxWait elapsed`, created.workflowId);
139
139
  }
140
140
  // p0SuJEeK — project ONLY the merge job's output. getWorkflowDownloads
141
141
  // returns a download group per terminal job, which now INCLUDES the
package/dist/sse.js CHANGED
@@ -105,7 +105,55 @@ export async function* parseSseStream(response, opts = {}) {
105
105
  continue;
106
106
  }
107
107
  if (line.startsWith(':')) {
108
- // Comment line (keep-alive), skip
108
+ // Comment line (keep-alive), skip.
109
+ //
110
+ // ⚠️ DO NOT "FIX" THIS INTO SURFACING COMMENT FRAMES WITHOUT READING
111
+ // THIS. Dropping them is CORRECT per the SSE spec — a comment carries
112
+ // no event — but it is also LOAD-BEARING FOR A CONSUMER, and that
113
+ // consumer is in another repo.
114
+ //
115
+ // The frontend does NOT close its SSE reader on the terminal event:
116
+ // `workflow_completed` patches status and breaks WITHOUT aborting.
117
+ // What actually closes the stream is an IDLE WATCHDOG at 20s. That
118
+ // watchdog only fires because heartbeats are never yielded here, so
119
+ // they never re-arm it — roughly 20s after the last real event the
120
+ // reader closes and the PHP worker is released.
121
+ //
122
+ // HEARTBEAT ~16s vs IDLE TIMEOUT 20s: a FOUR-SECOND MARGIN that holds
123
+ // only while the heartbeats are invisible. Surfacing comment frames —
124
+ // a completely reasonable change, since EventSource semantics treat
125
+ // keep-alive comments as liveness signals that legitimately reset
126
+ // timeouts — would CONTINUOUSLY re-arm the watchdog. The stream would
127
+ // never go idle, and every completed job left on screen would hold a
128
+ // PHP worker for the full 570s. That is worker exhaustion from
129
+ // ordinary users leaving tabs open.
130
+ //
131
+ // A ONE-LINE CHANGE HERE SILENTLY CONVERTS THE FRONTEND FROM BOUNDED
132
+ // TO UNBOUNDED, and nothing on either side would flag it.
133
+ //
134
+ // ⚠️ THIS NOTE IS PERMANENT. DO NOT DELETE IT WHEN THE FRONTEND
135
+ // ADDS AN EXPLICIT CLOSE-ON-TERMINAL. That fix removes the TERMINAL
136
+ // case and does NOT remove the dependency, because closing finished
137
+ // streams was never the watchdog's job — that was a side effect
138
+ // nobody knew about until 2026-08-12.
139
+ //
140
+ // THE WATCHDOG'S ACTUAL JOB IS DETECTING A STREAM THAT HAS GONE
141
+ // SILENT WHILE THE WORKFLOW IS STILL RUNNING, and resuming the
142
+ // fallback poll. That case survives every fix in flight, and it is
143
+ // not exotic: a long operation between progress events produces it
144
+ // exactly — heartbeats flowing, no data frames, job still running.
145
+ //
146
+ // If this parser surfaced comment frames, heartbeats at ~16s would
147
+ // re-arm the 20s watchdog FOREVER and STALL DETECTION WOULD NEVER
148
+ // FIRE AT ALL, on a stream that is genuinely stuck. The UI would go
149
+ // on believing a dead job is fine. That is worse than the worker
150
+ // leak: a leak is bounded by 570s, a missed stall is not bounded at
151
+ // all.
152
+ //
153
+ // The PHP SDK does the same thing at GislClient.php (`$line[0] === ':'`),
154
+ // verified 2026-08-12 — the two languages agree on this axis, and
155
+ // they must stay agreed: this property governs whether a stuck
156
+ // stream is EVER detected, so a divergence here is not cosmetic.
109
157
  continue;
110
158
  }
111
159
  const colonIndex = line.indexOf(':');
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { OperationType, OperationsSchemaResponse, OperationCapability, OutputProperties, ImageEncodeCapabilities, CallbackEventType, SseEventType, SseOperationProgressData, SseOperationCompletedData, SseOperationFailedData, SseJobCompletedData, SseJobFailedData, SseWorkflowTerminalData, MultipartInitiateRequestMetadataHint, UploadProbeResponse } from '@giveitsmaller/contracts/openapi';
2
- import type { JobInputV2RoleEnum } from '@giveitsmaller/contracts/openapi';
2
+ import type { JobInputV2RoleEnum, NotifyConfig } from '@giveitsmaller/contracts/openapi';
3
3
  export interface GislClientConfig {
4
4
  baseUrl: string;
5
5
  apiKey?: string;
@@ -188,6 +188,14 @@ export interface WorkflowCreatePayload {
188
188
  export?: ExternalDestinationPayload;
189
189
  delivery?: DeliveryPayload;
190
190
  processing?: WorkflowProcessingPayload;
191
+ /**
192
+ * Per-job completion notification (contracts v2.164.0, `notify.email`).
193
+ * Opaque passthrough wire shape — the ergonomic `notifyEmail` surface
194
+ * (SubmitOptions/RunOptions + builder convenience) is a separate follow-up
195
+ * (card y6jsQCpb); this field only acknowledges the wire key so a
196
+ * hand-built payload can carry it. Mirrors `export`/`delivery`/`processing`.
197
+ */
198
+ notify?: NotifyConfig;
191
199
  }
192
200
  /**
193
201
  * Single source of truth for WorkflowCreatePayload's top-level wire keys.
@@ -196,7 +204,7 @@ export interface WorkflowCreatePayload {
196
204
  * only via deep imports and should not be treated as public API.
197
205
  * @internal
198
206
  */
199
- export declare const WORKFLOW_CREATE_PAYLOAD_KEYS: readonly ["jobs", "source", "operations", "workflow_edges", "callback_url", "callback_events", "export", "delivery", "processing"];
207
+ export declare const WORKFLOW_CREATE_PAYLOAD_KEYS: readonly ["jobs", "source", "operations", "workflow_edges", "callback_url", "callback_events", "export", "delivery", "processing", "notify"];
200
208
  export interface GetSchemaOptions {
201
209
  /** Filter the schema to operations that accept this MIME type (e.g. `image/jpeg`). */
202
210
  mimeType?: string;
@@ -322,7 +330,7 @@ export interface ReadCapabilityOptions {
322
330
  export interface WaitOptions {
323
331
  /** Poll interval in milliseconds (default: 2000) */
324
332
  intervalMs?: number;
325
- /** Maximum wait time in milliseconds (default: 300000 = 5 min) */
333
+ /** Maximum wait time in milliseconds (default: 600000 = 10 min) */
326
334
  timeoutMs?: number;
327
335
  /** Called after each poll with current status */
328
336
  onPoll?: (status: string) => void;
package/dist/types.js CHANGED
@@ -50,4 +50,5 @@ export const WORKFLOW_CREATE_PAYLOAD_KEYS = Object.freeze([
50
50
  'export',
51
51
  'delivery',
52
52
  'processing',
53
+ 'notify',
53
54
  ]);
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@giveitsmaller/sdk",
3
- "version": "0.20.0",
3
+ "version": "0.22.0",
4
4
  "description": "Node.js SDK for the GISL (Give It Smaller) file compression and processing API",
5
- "license": "MIT",
5
+ "license": "Apache-2.0",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
8
8
  "exports": {
@@ -31,7 +31,7 @@
31
31
  "node": ">=18"
32
32
  },
33
33
  "dependencies": {
34
- "@giveitsmaller/contracts": "^0.53.0"
34
+ "@giveitsmaller/contracts": "^0.65.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/node": "^22",
@@ -1,12 +0,0 @@
1
- import { PdfProfile, OptimizeFor } from '../../generated/sdk_spec/enums.js';
2
- export interface DocumentPdfCompressPresetOptionsInput {
3
- readonly profile?: PdfProfile;
4
- readonly grayscale?: boolean;
5
- }
6
- export declare class DocumentPdfCompressPresetOptions {
7
- readonly profile?: PdfProfile;
8
- readonly grayscale?: boolean;
9
- private constructor();
10
- static from(input: DocumentPdfCompressPresetOptionsInput): DocumentPdfCompressPresetOptions;
11
- static shippedDefaultsFor(level: OptimizeFor): DocumentPdfCompressPresetOptions;
12
- }
@@ -1,33 +0,0 @@
1
- // T4a — DocumentPdfCompressPresetOptions leaf DTO.
2
- //
3
- // Field set (2): profile, grayscale — the worker-honored stable PDF controls
4
- // (contracts v2.96.0 Acrobat-PDF realignment Lw1LseYr). The earlier
5
- // {profile, colorspace, flattenForms} set was retired: colorspace + flatten_forms
6
- // are `planned` (not read by the worker) so presets never emit them, and
7
- // `image_dpi` + `pages` are per-call knobs, not preset cells.
8
- import { shippedDefaultsFor as f3ShippedDefaultsFor } from '../../generated/sdk_spec/presets.js';
9
- import { translateEnum } from './_translate.js';
10
- export class DocumentPdfCompressPresetOptions {
11
- profile;
12
- grayscale;
13
- constructor(input) {
14
- if (input.profile !== undefined)
15
- this.profile = input.profile;
16
- if (input.grayscale !== undefined)
17
- this.grayscale = input.grayscale;
18
- Object.freeze(this);
19
- }
20
- static from(input) {
21
- return new DocumentPdfCompressPresetOptions(input);
22
- }
23
- static shippedDefaultsFor(level) {
24
- const cell = f3ShippedDefaultsFor('document_pdf_compress', level);
25
- const input = {};
26
- const mut = input;
27
- if ('profile' in cell)
28
- mut.profile = translateEnum('PdfProfile', cell.profile);
29
- if ('grayscale' in cell)
30
- mut.grayscale = cell.grayscale;
31
- return new DocumentPdfCompressPresetOptions(input);
32
- }
33
- }