spectrum-ts 1.16.0 → 1.16.1

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.ts CHANGED
@@ -2629,8 +2629,17 @@ interface FusorClient<TPayload = unknown> {
2629
2629
  /**
2630
2630
  * Request-scoped handler invoked once per inbound message that
2631
2631
  * `spectrum.webhook()` resolves. Receives the same fully-built `[space,
2632
- * message]` pair that `spectrum.messages` yields; awaited before the HTTP
2633
- * response is returned to fusor.
2632
+ * message]` pair that `spectrum.messages` yields.
2633
+ *
2634
+ * Runs **fire-and-forget**: it is dispatched after the HTTP response (the
2635
+ * platform's `respond()` reply) has already been computed, so its outcome never
2636
+ * affects the response, and a throw is caught + logged rather than surfaced —
2637
+ * mirroring the body of a `for await (… of spectrum.messages)` loop.
2638
+ *
2639
+ * On a long-running server the event loop keeps the handler alive. On
2640
+ * serverless/edge runtimes the function may be frozen once the response is
2641
+ * returned, so keeping background work alive is the caller's responsibility —
2642
+ * the usual pattern is to enqueue the work and process it in a separate worker.
2634
2643
  */
2635
2644
  type WebhookHandler = (space: Space, message: Message) => void | Promise<void>;
2636
2645
  /**
@@ -2752,9 +2761,17 @@ type SpectrumInstance<Providers extends PlatformProviderConfig[] = PlatformProvi
2752
2761
  /**
2753
2762
  * Handle one inbound fusor webhook delivery. Call this from your HTTP
2754
2763
  * server's POST route — it verifies, decodes, routes to the matching
2755
- * provider's verify + message pipeline, invokes `handler` once per resolved
2756
- * message, and returns the HTTP response fusor relays back to the platform
2757
- * (including protocol echoes like Slack `url_verification`).
2764
+ * provider's verify + message pipeline, and returns the HTTP response fusor
2765
+ * relays back to the platform: the platform's `respond()` reply (including
2766
+ * protocol echoes like Slack `url_verification`), computed synchronously and
2767
+ * returned immediately.
2768
+ *
2769
+ * `handler` is invoked once per resolved message **fire-and-forget** — it is
2770
+ * dispatched after the response is computed and is NOT awaited, so its
2771
+ * outcome never changes the response (a throw is logged, not surfaced). On a
2772
+ * long-running server the event loop keeps it alive; on serverless/edge,
2773
+ * keeping the work alive past the response is the caller's job (e.g. enqueue
2774
+ * and process in a separate worker).
2758
2775
  *
2759
2776
  * Stateless and request-scoped: it does NOT feed `spectrum.messages`, and it
2760
2777
  * never opens the gRPC stream. fusor delivers at-least-once, so `handler`
package/dist/index.js CHANGED
@@ -28013,11 +28013,23 @@ async function Spectrum(options) {
28013
28013
  }
28014
28014
  return { asWeb: false, bodyBytes, headers };
28015
28015
  };
28016
- const deliverWebhookMessages = async (collected, runtime, handler) => {
28016
+ const deliverWebhookMessages = async (collected, runtime, handler, event) => {
28017
28017
  for (const record of collected) {
28018
28018
  const tuples = await resolveRecordToMessages(record, runtime);
28019
28019
  for (const [space, message] of tuples) {
28020
- await handler(space, message);
28020
+ try {
28021
+ await handler(space, message);
28022
+ } catch (error) {
28023
+ lifecycleLog.error(
28024
+ `spectrum.webhook: handler threw (async), ${error}`,
28025
+ {
28026
+ eventId: event.eventId,
28027
+ platform: event.platform,
28028
+ messageId: message.id,
28029
+ error: error instanceof Error ? error.message : String(error)
28030
+ }
28031
+ );
28032
+ }
28021
28033
  }
28022
28034
  }
28023
28035
  };
@@ -28037,19 +28049,6 @@ async function Spectrum(options) {
28037
28049
  return null;
28038
28050
  }
28039
28051
  };
28040
- const runWebhookDelivery = async (collected, runtime, handler, event) => {
28041
- try {
28042
- await deliverWebhookMessages(collected, runtime, handler);
28043
- return null;
28044
- } catch (error) {
28045
- lifecycleLog.error(`spectrum.webhook: handler threw, ${error}`, {
28046
- eventId: event.eventId,
28047
- platform: event.platform,
28048
- error: error instanceof Error ? error.message : String(error)
28049
- });
28050
- return { status: 500, headers: {}, body: new Uint8Array(0) };
28051
- }
28052
- };
28053
28052
  const processWebhookEvent = async (core, event, handler) => {
28054
28053
  const collected = [];
28055
28054
  const reply2 = await core.processEvent(event, (record) => {
@@ -28062,23 +28061,27 @@ async function Spectrum(options) {
28062
28061
  body: encodeText(reply2.errorReason)
28063
28062
  };
28064
28063
  }
28065
- const runtime = platformStates.get(event.platform);
28066
- if (runtime) {
28067
- const failure = await runWebhookDelivery(
28068
- collected,
28069
- runtime,
28070
- handler,
28071
- event
28072
- );
28073
- if (failure) {
28074
- return failure;
28075
- }
28076
- }
28077
- return {
28064
+ const result = {
28078
28065
  status: reply2.status === 0 ? 200 : reply2.status,
28079
28066
  headers: reply2.headers ?? {},
28080
28067
  body: reply2.body ?? new Uint8Array(0)
28081
28068
  };
28069
+ const runtime = platformStates.get(event.platform);
28070
+ if (runtime && collected.length > 0) {
28071
+ deliverWebhookMessages(collected, runtime, handler, event).catch(
28072
+ (error) => {
28073
+ lifecycleLog.error(
28074
+ `spectrum.webhook: delivery failed (async), ${error}`,
28075
+ {
28076
+ eventId: event.eventId,
28077
+ platform: event.platform,
28078
+ error: error instanceof Error ? error.message : String(error)
28079
+ }
28080
+ );
28081
+ }
28082
+ );
28083
+ }
28084
+ return result;
28082
28085
  };
28083
28086
  const handleWebhook = async (request, handler) => {
28084
28087
  if (!fusorCore) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spectrum-ts",
3
- "version": "1.16.0",
3
+ "version": "1.16.1",
4
4
  "description": "Bring agents to any interface — unified messaging SDK for TypeScript.",
5
5
  "repository": {
6
6
  "type": "git",