@hostwebhook/node 1.4.0 → 1.4.2

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/dist/index.d.mts CHANGED
@@ -219,15 +219,29 @@ interface SendOptions {
219
219
  * Wait for the full pipeline result via SSE streaming (blocking).
220
220
  * The SDK will POST the payload, then open an SSE stream to receive
221
221
  * pipeline steps and the final result. Returns when the pipeline completes.
222
- * Timeout: ~125s (slightly above the server's 120s SSE timeout).
222
+ *
223
+ * **Timeout:** ~125s (slightly above the server's 120s SSE timeout).
224
+ *
225
+ * **When to use:** Short pipelines where you need the result before
226
+ * responding to the user (e.g., validation, quick transforms).
227
+ *
228
+ * **When NOT to use:**
229
+ * - Long-running pipelines (file processing, scraping) — use fire-and-forget instead
230
+ * - Approval nodes — the stream returns `{ awaitingApproval: true }` immediately,
231
+ * it does NOT wait for the approval to be resolved (that can take hours/days)
232
+ * - Delay nodes — if the delay exceeds the 125s timeout, the stream times out
233
+ *
234
+ * For approval workflows, use `responseMode: 'sync'` on the endpoint instead —
235
+ * it returns the approval status inline without blocking the SSE stream.
223
236
  */
224
237
  waitForResult?: boolean;
225
238
  /**
226
239
  * Non-blocking pipeline result callback. `send()` returns immediately with
227
240
  * the eventId, and the callback fires in the background when the pipeline
228
- * completes (via SSE stream). Perfect for UIs that shouldn't block.
241
+ * completes (via SSE stream).
229
242
  *
230
- * Requires the endpoint to be in `none` response mode (async).
243
+ * Same timeout limitations as `waitForResult` (125s max).
244
+ * For long pipelines, use WebSocket subscription instead.
231
245
  *
232
246
  * @example
233
247
  * ```ts
@@ -340,6 +354,48 @@ declare class HostWebhook {
340
354
  fileName: string;
341
355
  mimeType: string;
342
356
  }> | SendOptions, maybeOptions?: SendOptions): Promise<SendResult>;
357
+ /**
358
+ * Send a webhook and stream pipeline steps as an async iterable.
359
+ *
360
+ * Much simpler than `waitForResult` or `onResult` callbacks.
361
+ * Iteration ends when the pipeline completes. Access `stream.result`
362
+ * after the loop for the final pipeline result.
363
+ *
364
+ * @overload stream(payload) — uses default token
365
+ * @overload stream(token, payload) — specify token per call
366
+ *
367
+ * @example
368
+ * ```ts
369
+ * const hw = new HostWebhook({ apiKey: 'hwk_...', token: 'ingress_token' });
370
+ * const stream = await hw.stream({ event: 'order.created' });
371
+ *
372
+ * for await (const step of stream) {
373
+ * console.log(`${step.nodeType}: ${step.status}`);
374
+ * }
375
+ * console.log('Done:', stream.result);
376
+ * ```
377
+ */
378
+ stream(tokenOrPayload: string | Record<string, unknown>, maybePayload?: Record<string, unknown>): Promise<PipelineStream>;
379
+ }
380
+ /**
381
+ * Async iterable pipeline stream. Yields PipelineStep objects as
382
+ * nodes execute, then completes. Access `.result` after iteration.
383
+ *
384
+ * @example
385
+ * ```ts
386
+ * const stream = await hw.stream(payload);
387
+ * for await (const step of stream) {
388
+ * console.log(step.nodeType, step.status);
389
+ * }
390
+ * console.log(stream.result); // { syncStatus: 200, ... }
391
+ * ```
392
+ */
393
+ declare class PipelineStream {
394
+ #private;
395
+ result: SendResult;
396
+ constructor(steps: PipelineStep[], result: SendResult);
397
+ static connect(streamUrl: string, ingressResult: SendResult): Promise<PipelineStream>;
398
+ [Symbol.asyncIterator](): AsyncGenerator<PipelineStep, void, undefined>;
343
399
  }
344
400
  /**
345
401
  * Standalone send function (no class instantiation needed).
package/dist/index.d.ts CHANGED
@@ -219,15 +219,29 @@ interface SendOptions {
219
219
  * Wait for the full pipeline result via SSE streaming (blocking).
220
220
  * The SDK will POST the payload, then open an SSE stream to receive
221
221
  * pipeline steps and the final result. Returns when the pipeline completes.
222
- * Timeout: ~125s (slightly above the server's 120s SSE timeout).
222
+ *
223
+ * **Timeout:** ~125s (slightly above the server's 120s SSE timeout).
224
+ *
225
+ * **When to use:** Short pipelines where you need the result before
226
+ * responding to the user (e.g., validation, quick transforms).
227
+ *
228
+ * **When NOT to use:**
229
+ * - Long-running pipelines (file processing, scraping) — use fire-and-forget instead
230
+ * - Approval nodes — the stream returns `{ awaitingApproval: true }` immediately,
231
+ * it does NOT wait for the approval to be resolved (that can take hours/days)
232
+ * - Delay nodes — if the delay exceeds the 125s timeout, the stream times out
233
+ *
234
+ * For approval workflows, use `responseMode: 'sync'` on the endpoint instead —
235
+ * it returns the approval status inline without blocking the SSE stream.
223
236
  */
224
237
  waitForResult?: boolean;
225
238
  /**
226
239
  * Non-blocking pipeline result callback. `send()` returns immediately with
227
240
  * the eventId, and the callback fires in the background when the pipeline
228
- * completes (via SSE stream). Perfect for UIs that shouldn't block.
241
+ * completes (via SSE stream).
229
242
  *
230
- * Requires the endpoint to be in `none` response mode (async).
243
+ * Same timeout limitations as `waitForResult` (125s max).
244
+ * For long pipelines, use WebSocket subscription instead.
231
245
  *
232
246
  * @example
233
247
  * ```ts
@@ -340,6 +354,48 @@ declare class HostWebhook {
340
354
  fileName: string;
341
355
  mimeType: string;
342
356
  }> | SendOptions, maybeOptions?: SendOptions): Promise<SendResult>;
357
+ /**
358
+ * Send a webhook and stream pipeline steps as an async iterable.
359
+ *
360
+ * Much simpler than `waitForResult` or `onResult` callbacks.
361
+ * Iteration ends when the pipeline completes. Access `stream.result`
362
+ * after the loop for the final pipeline result.
363
+ *
364
+ * @overload stream(payload) — uses default token
365
+ * @overload stream(token, payload) — specify token per call
366
+ *
367
+ * @example
368
+ * ```ts
369
+ * const hw = new HostWebhook({ apiKey: 'hwk_...', token: 'ingress_token' });
370
+ * const stream = await hw.stream({ event: 'order.created' });
371
+ *
372
+ * for await (const step of stream) {
373
+ * console.log(`${step.nodeType}: ${step.status}`);
374
+ * }
375
+ * console.log('Done:', stream.result);
376
+ * ```
377
+ */
378
+ stream(tokenOrPayload: string | Record<string, unknown>, maybePayload?: Record<string, unknown>): Promise<PipelineStream>;
379
+ }
380
+ /**
381
+ * Async iterable pipeline stream. Yields PipelineStep objects as
382
+ * nodes execute, then completes. Access `.result` after iteration.
383
+ *
384
+ * @example
385
+ * ```ts
386
+ * const stream = await hw.stream(payload);
387
+ * for await (const step of stream) {
388
+ * console.log(step.nodeType, step.status);
389
+ * }
390
+ * console.log(stream.result); // { syncStatus: 200, ... }
391
+ * ```
392
+ */
393
+ declare class PipelineStream {
394
+ #private;
395
+ result: SendResult;
396
+ constructor(steps: PipelineStep[], result: SendResult);
397
+ static connect(streamUrl: string, ingressResult: SendResult): Promise<PipelineStream>;
398
+ [Symbol.asyncIterator](): AsyncGenerator<PipelineStep, void, undefined>;
343
399
  }
344
400
  /**
345
401
  * Standalone send function (no class instantiation needed).
package/dist/index.js CHANGED
@@ -337,6 +337,47 @@ var HostWebhook = class {
337
337
  }
338
338
  return __privateMethod(this, _HostWebhook_instances, doSend_fn).call(this, token, enrichedPayload, options);
339
339
  }
340
+ /**
341
+ * Send a webhook and stream pipeline steps as an async iterable.
342
+ *
343
+ * Much simpler than `waitForResult` or `onResult` callbacks.
344
+ * Iteration ends when the pipeline completes. Access `stream.result`
345
+ * after the loop for the final pipeline result.
346
+ *
347
+ * @overload stream(payload) — uses default token
348
+ * @overload stream(token, payload) — specify token per call
349
+ *
350
+ * @example
351
+ * ```ts
352
+ * const hw = new HostWebhook({ apiKey: 'hwk_...', token: 'ingress_token' });
353
+ * const stream = await hw.stream({ event: 'order.created' });
354
+ *
355
+ * for await (const step of stream) {
356
+ * console.log(`${step.nodeType}: ${step.status}`);
357
+ * }
358
+ * console.log('Done:', stream.result);
359
+ * ```
360
+ */
361
+ async stream(tokenOrPayload, maybePayload) {
362
+ let token;
363
+ let payload;
364
+ if (typeof tokenOrPayload === "string") {
365
+ token = tokenOrPayload;
366
+ payload = maybePayload ?? {};
367
+ } else {
368
+ if (!__privateGet(this, _token)) {
369
+ throw new Error("HostWebhook: no default token set \u2014 pass token as first argument: stream(token, payload)");
370
+ }
371
+ token = __privateGet(this, _token);
372
+ payload = tokenOrPayload;
373
+ }
374
+ const ingressResult = await __privateMethod(this, _HostWebhook_instances, doSend_fn).call(this, token, payload);
375
+ if (!ingressResult.streamToken) {
376
+ return new PipelineStream([], ingressResult);
377
+ }
378
+ const streamUrl = `${__privateGet(this, _baseUrl)}/api/events/${ingressResult.eventId}/stream?token=${ingressResult.streamToken}`;
379
+ return PipelineStream.connect(streamUrl, ingressResult);
380
+ }
340
381
  };
341
382
  _apiKey = new WeakMap();
342
383
  _token = new WeakMap();
@@ -355,7 +396,7 @@ doSend_fn = async function(token, payload, options) {
355
396
  }
356
397
  const headers = {
357
398
  "Content-Type": options?.contentType ?? "application/json",
358
- "User-Agent": `HostWebhook-SDK/${"1.4.0"}`,
399
+ "User-Agent": `HostWebhook-SDK/${"1.4.2"}`,
359
400
  "X-HostWebhook-Sent-At": (/* @__PURE__ */ new Date()).toISOString(),
360
401
  ...tz ? { "X-HostWebhook-Timezone": tz } : {},
361
402
  ...__privateGet(this, _defaultHeaders),
@@ -492,6 +533,114 @@ waitForStream_fn = async function(ingressResult, onStep) {
492
533
  clearTimeout(timer);
493
534
  }
494
535
  };
536
+ var _steps, _pendingSteps, _done, _doneResult, _resolve, _reader, _abortController;
537
+ var _PipelineStream = class _PipelineStream {
538
+ constructor(steps, result) {
539
+ __privateAdd(this, _steps);
540
+ __privateAdd(this, _pendingSteps, []);
541
+ __privateAdd(this, _done, false);
542
+ __privateAdd(this, _doneResult, null);
543
+ __privateAdd(this, _resolve, null);
544
+ __privateAdd(this, _reader, null);
545
+ __privateAdd(this, _abortController, null);
546
+ __privateSet(this, _steps, steps);
547
+ this.result = result;
548
+ __privateSet(this, _done, true);
549
+ }
550
+ static async connect(streamUrl, ingressResult) {
551
+ const stream = new _PipelineStream([], ingressResult);
552
+ __privateSet(stream, _done, false);
553
+ const controller = new AbortController();
554
+ __privateSet(stream, _abortController, controller);
555
+ const timer = setTimeout(() => controller.abort(), 125e3);
556
+ try {
557
+ const res = await fetch(streamUrl, { signal: controller.signal });
558
+ if (!res.ok) {
559
+ clearTimeout(timer);
560
+ __privateSet(stream, _done, true);
561
+ return stream;
562
+ }
563
+ __privateSet(stream, _reader, res.body.getReader());
564
+ const decoder = new TextDecoder();
565
+ let buffer = "";
566
+ let currentEvent = "";
567
+ (async () => {
568
+ var _a, _b, _c, _d;
569
+ try {
570
+ while (true) {
571
+ const { done, value } = await __privateGet(stream, _reader).read();
572
+ if (done) break;
573
+ buffer += decoder.decode(value, { stream: true });
574
+ const lines = buffer.split("\n");
575
+ buffer = lines.pop();
576
+ for (const line of lines) {
577
+ if (line.startsWith("event: ")) {
578
+ currentEvent = line.slice(7).trim();
579
+ } else if (line.startsWith("data: ")) {
580
+ try {
581
+ const parsed = JSON.parse(line.slice(6));
582
+ if (currentEvent === "step") {
583
+ __privateGet(stream, _steps).push(parsed);
584
+ __privateGet(stream, _pendingSteps).push(parsed);
585
+ (_a = __privateGet(stream, _resolve)) == null ? void 0 : _a.call(stream);
586
+ } else if (currentEvent === "done") {
587
+ __privateSet(stream, _doneResult, parsed);
588
+ stream.result = {
589
+ ...ingressResult,
590
+ steps: __privateGet(stream, _steps),
591
+ syncStatus: parsed.status ?? 200,
592
+ ...parsed.body ?? parsed
593
+ };
594
+ __privateSet(stream, _done, true);
595
+ (_b = __privateGet(stream, _resolve)) == null ? void 0 : _b.call(stream);
596
+ return;
597
+ } else if (currentEvent === "timeout") {
598
+ stream.result = { ...ingressResult, steps: __privateGet(stream, _steps), syncStatus: 408, error: "Pipeline timeout" };
599
+ __privateSet(stream, _done, true);
600
+ (_c = __privateGet(stream, _resolve)) == null ? void 0 : _c.call(stream);
601
+ return;
602
+ }
603
+ } catch {
604
+ }
605
+ currentEvent = "";
606
+ }
607
+ }
608
+ }
609
+ } catch {
610
+ } finally {
611
+ clearTimeout(timer);
612
+ __privateSet(stream, _done, true);
613
+ (_d = __privateGet(stream, _resolve)) == null ? void 0 : _d.call(stream);
614
+ }
615
+ })();
616
+ } catch {
617
+ clearTimeout(timer);
618
+ __privateSet(stream, _done, true);
619
+ }
620
+ return stream;
621
+ }
622
+ async *[Symbol.asyncIterator]() {
623
+ while (true) {
624
+ if (__privateGet(this, _pendingSteps).length > 0) {
625
+ yield __privateGet(this, _pendingSteps).shift();
626
+ continue;
627
+ }
628
+ if (__privateGet(this, _done)) return;
629
+ await new Promise((resolve) => {
630
+ __privateSet(this, _resolve, resolve);
631
+ });
632
+ __privateSet(this, _resolve, null);
633
+ }
634
+ }
635
+ };
636
+ _steps = new WeakMap();
637
+ _pendingSteps = new WeakMap();
638
+ _done = new WeakMap();
639
+ _doneResult = new WeakMap();
640
+ _resolve = new WeakMap();
641
+ _reader = new WeakMap();
642
+ _abortController = new WeakMap();
643
+ var PipelineStream = _PipelineStream;
495
644
  async function send(config, payload, options) {
496
645
  const client = new HostWebhook(config);
497
646
  return client.send(payload, options);
package/dist/index.mjs CHANGED
@@ -304,6 +304,47 @@ var HostWebhook = class {
304
304
  }
305
305
  return __privateMethod(this, _HostWebhook_instances, doSend_fn).call(this, token, enrichedPayload, options);
306
306
  }
307
+ /**
308
+ * Send a webhook and stream pipeline steps as an async iterable.
309
+ *
310
+ * Much simpler than `waitForResult` or `onResult` callbacks.
311
+ * Iteration ends when the pipeline completes. Access `stream.result`
312
+ * after the loop for the final pipeline result.
313
+ *
314
+ * @overload stream(payload) — uses default token
315
+ * @overload stream(token, payload) — specify token per call
316
+ *
317
+ * @example
318
+ * ```ts
319
+ * const hw = new HostWebhook({ apiKey: 'hwk_...', token: 'ingress_token' });
320
+ * const stream = await hw.stream({ event: 'order.created' });
321
+ *
322
+ * for await (const step of stream) {
323
+ * console.log(`${step.nodeType}: ${step.status}`);
324
+ * }
325
+ * console.log('Done:', stream.result);
326
+ * ```
327
+ */
328
+ async stream(tokenOrPayload, maybePayload) {
329
+ let token;
330
+ let payload;
331
+ if (typeof tokenOrPayload === "string") {
332
+ token = tokenOrPayload;
333
+ payload = maybePayload ?? {};
334
+ } else {
335
+ if (!__privateGet(this, _token)) {
336
+ throw new Error("HostWebhook: no default token set \u2014 pass token as first argument: stream(token, payload)");
337
+ }
338
+ token = __privateGet(this, _token);
339
+ payload = tokenOrPayload;
340
+ }
341
+ const ingressResult = await __privateMethod(this, _HostWebhook_instances, doSend_fn).call(this, token, payload);
342
+ if (!ingressResult.streamToken) {
343
+ return new PipelineStream([], ingressResult);
344
+ }
345
+ const streamUrl = `${__privateGet(this, _baseUrl)}/api/events/${ingressResult.eventId}/stream?token=${ingressResult.streamToken}`;
346
+ return PipelineStream.connect(streamUrl, ingressResult);
347
+ }
307
348
  };
308
349
  _apiKey = new WeakMap();
309
350
  _token = new WeakMap();
@@ -322,7 +363,7 @@ doSend_fn = async function(token, payload, options) {
322
363
  }
323
364
  const headers = {
324
365
  "Content-Type": options?.contentType ?? "application/json",
325
- "User-Agent": `HostWebhook-SDK/${"1.4.0"}`,
366
+ "User-Agent": `HostWebhook-SDK/${"1.4.2"}`,
326
367
  "X-HostWebhook-Sent-At": (/* @__PURE__ */ new Date()).toISOString(),
327
368
  ...tz ? { "X-HostWebhook-Timezone": tz } : {},
328
369
  ...__privateGet(this, _defaultHeaders),
@@ -459,6 +500,114 @@ waitForStream_fn = async function(ingressResult, onStep) {
459
500
  clearTimeout(timer);
460
501
  }
461
502
  };
503
+ var _steps, _pendingSteps, _done, _doneResult, _resolve, _reader, _abortController;
504
+ var _PipelineStream = class _PipelineStream {
505
+ constructor(steps, result) {
506
+ __privateAdd(this, _steps);
507
+ __privateAdd(this, _pendingSteps, []);
508
+ __privateAdd(this, _done, false);
509
+ __privateAdd(this, _doneResult, null);
510
+ __privateAdd(this, _resolve, null);
511
+ __privateAdd(this, _reader, null);
512
+ __privateAdd(this, _abortController, null);
513
+ __privateSet(this, _steps, steps);
514
+ this.result = result;
515
+ __privateSet(this, _done, true);
516
+ }
517
+ static async connect(streamUrl, ingressResult) {
518
+ const stream = new _PipelineStream([], ingressResult);
519
+ __privateSet(stream, _done, false);
520
+ const controller = new AbortController();
521
+ __privateSet(stream, _abortController, controller);
522
+ const timer = setTimeout(() => controller.abort(), 125e3);
523
+ try {
524
+ const res = await fetch(streamUrl, { signal: controller.signal });
525
+ if (!res.ok) {
526
+ clearTimeout(timer);
527
+ __privateSet(stream, _done, true);
528
+ return stream;
529
+ }
530
+ __privateSet(stream, _reader, res.body.getReader());
531
+ const decoder = new TextDecoder();
532
+ let buffer = "";
533
+ let currentEvent = "";
534
+ (async () => {
535
+ var _a, _b, _c, _d;
536
+ try {
537
+ while (true) {
538
+ const { done, value } = await __privateGet(stream, _reader).read();
539
+ if (done) break;
540
+ buffer += decoder.decode(value, { stream: true });
541
+ const lines = buffer.split("\n");
542
+ buffer = lines.pop();
543
+ for (const line of lines) {
544
+ if (line.startsWith("event: ")) {
545
+ currentEvent = line.slice(7).trim();
546
+ } else if (line.startsWith("data: ")) {
547
+ try {
548
+ const parsed = JSON.parse(line.slice(6));
549
+ if (currentEvent === "step") {
550
+ __privateGet(stream, _steps).push(parsed);
551
+ __privateGet(stream, _pendingSteps).push(parsed);
552
+ (_a = __privateGet(stream, _resolve)) == null ? void 0 : _a.call(stream);
553
+ } else if (currentEvent === "done") {
554
+ __privateSet(stream, _doneResult, parsed);
555
+ stream.result = {
556
+ ...ingressResult,
557
+ steps: __privateGet(stream, _steps),
558
+ syncStatus: parsed.status ?? 200,
559
+ ...parsed.body ?? parsed
560
+ };
561
+ __privateSet(stream, _done, true);
562
+ (_b = __privateGet(stream, _resolve)) == null ? void 0 : _b.call(stream);
563
+ return;
564
+ } else if (currentEvent === "timeout") {
565
+ stream.result = { ...ingressResult, steps: __privateGet(stream, _steps), syncStatus: 408, error: "Pipeline timeout" };
566
+ __privateSet(stream, _done, true);
567
+ (_c = __privateGet(stream, _resolve)) == null ? void 0 : _c.call(stream);
568
+ return;
569
+ }
570
+ } catch {
571
+ }
572
+ currentEvent = "";
573
+ }
574
+ }
575
+ }
576
+ } catch {
577
+ } finally {
578
+ clearTimeout(timer);
579
+ __privateSet(stream, _done, true);
580
+ (_d = __privateGet(stream, _resolve)) == null ? void 0 : _d.call(stream);
581
+ }
582
+ })();
583
+ } catch {
584
+ clearTimeout(timer);
585
+ __privateSet(stream, _done, true);
586
+ }
587
+ return stream;
588
+ }
589
+ async *[Symbol.asyncIterator]() {
590
+ while (true) {
591
+ if (__privateGet(this, _pendingSteps).length > 0) {
592
+ yield __privateGet(this, _pendingSteps).shift();
593
+ continue;
594
+ }
595
+ if (__privateGet(this, _done)) return;
596
+ await new Promise((resolve) => {
597
+ __privateSet(this, _resolve, resolve);
598
+ });
599
+ __privateSet(this, _resolve, null);
600
+ }
601
+ }
602
+ };
603
+ _steps = new WeakMap();
604
+ _pendingSteps = new WeakMap();
605
+ _done = new WeakMap();
606
+ _doneResult = new WeakMap();
607
+ _resolve = new WeakMap();
608
+ _reader = new WeakMap();
609
+ _abortController = new WeakMap();
610
+ var PipelineStream = _PipelineStream;
462
611
  async function send(config, payload, options) {
463
612
  const client = new HostWebhook(config);
464
613
  return client.send(payload, options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hostwebhook/node",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "description": "Official HostWebhook SDK — verify signatures, parse headers, and send webhooks",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",