@visulima/notification 1.0.16 → 1.1.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/CHANGELOG.md +18 -0
- package/README.md +75 -15
- package/dist/channels/inapp/index.d.ts +4 -2
- package/dist/channels/inapp/index.js +1 -1
- package/dist/inbound/index.d.ts +97 -0
- package/dist/inbound/index.js +1 -0
- package/dist/packem_shared/chatReply-HANpcetF.js +1 -0
- package/dist/packem_shared/createDiscordReceiver-ErG7MJ_N.js +1 -0
- package/dist/packem_shared/createInboundRouter-DWk4xvCj.js +1 -0
- package/dist/packem_shared/createMessageBirdReceiver-DNPoScxB.js +1 -0
- package/dist/packem_shared/createMsTeamsReceiver-CKZsylzU.js +1 -0
- package/dist/packem_shared/createSlackReceiver-BHY2tumz.js +1 -0
- package/dist/packem_shared/{snsProvider-BtlNUN7u.js → createSnsProvider-Df9fqQ2A.js} +1 -1
- package/dist/packem_shared/createTelegramReceiver-BjZa0eVx.js +1 -0
- package/dist/packem_shared/createTelnyxReceiver-Ev-ThOtu.js +1 -0
- package/dist/packem_shared/createTwilioReceiver-CQmdSCMX.js +1 -0
- package/dist/packem_shared/createVonageReceiver-BWVR3E7q.js +1 -0
- package/dist/packem_shared/{webPushProvider-CUDMDRKw.js → createWebPushProvider-BNu3bPeV.js} +1 -1
- package/dist/packem_shared/hmacBase64-ocSXhrdQ.js +1 -0
- package/dist/packem_shared/isJwtTimeValid-CdCG24pW.js +1 -0
- package/dist/packem_shared/{slackWebhook-CGbFBgpo.js → slackWebhook-DILLGl3P.js} +1 -1
- package/dist/packem_shared/snsWebhook-DTBQW5U5.js +3 -0
- package/dist/packem_shared/{standardWebhook-DV_fVNtX.js → standardWebhook-CPjtIEKe.js} +1 -1
- package/dist/packem_shared/{twilioWebhook-DScQeZta.js → twilioWebhook-BjM8B0sB.js} +1 -1
- package/dist/packem_shared/types.d-4T7bNo4x.d.ts +182 -0
- package/dist/packem_shared/types.d-BXfGenMH.d.ts +34 -0
- package/dist/packem_shared/utils-DuaeYOiD.js +1 -0
- package/dist/packem_shared/verifyEd25519-lmU3nhP0.js +1 -0
- package/dist/packem_shared/{webcrypto-Cz40u9vp.js → webcrypto-BPm3NDEm.js} +1 -1
- package/dist/providers/chat/discord/index.d.ts +40 -3
- package/dist/providers/chat/discord/index.js +1 -1
- package/dist/providers/chat/msteams/index.d.ts +31 -3
- package/dist/providers/chat/msteams/index.js +1 -1
- package/dist/providers/chat/slack/index.d.ts +31 -3
- package/dist/providers/chat/slack/index.js +1 -1
- package/dist/providers/chat/telegram/index.d.ts +32 -3
- package/dist/providers/chat/telegram/index.js +1 -1
- package/dist/providers/failover/index.d.ts +4 -2
- package/dist/providers/failover/index.js +1 -1
- package/dist/providers/mock/index.d.ts +4 -2
- package/dist/providers/mock/index.js +1 -1
- package/dist/providers/opentelemetry/index.d.ts +4 -2
- package/dist/providers/opentelemetry/index.js +1 -1
- package/dist/providers/push/apns/index.d.ts +4 -2
- package/dist/providers/push/apns/index.js +1 -1
- package/dist/providers/push/expo/index.d.ts +4 -2
- package/dist/providers/push/expo/index.js +1 -1
- package/dist/providers/push/fcm/index.d.ts +4 -2
- package/dist/providers/push/fcm/index.js +1 -1
- package/dist/providers/push/web-push/index.d.ts +4 -2
- package/dist/providers/push/web-push/index.js +1 -1
- package/dist/providers/roundrobin/index.d.ts +4 -2
- package/dist/providers/roundrobin/index.js +1 -1
- package/dist/providers/sms/messagebird/index.d.ts +33 -3
- package/dist/providers/sms/messagebird/index.js +1 -1
- package/dist/providers/sms/plivo/index.d.ts +4 -2
- package/dist/providers/sms/plivo/index.js +1 -1
- package/dist/providers/sms/sns/index.d.ts +4 -2
- package/dist/providers/sms/sns/index.js +1 -1
- package/dist/providers/sms/telnyx/index.d.ts +28 -3
- package/dist/providers/sms/telnyx/index.js +1 -1
- package/dist/providers/sms/twilio/index.d.ts +36 -3
- package/dist/providers/sms/twilio/index.js +1 -1
- package/dist/providers/sms/vonage/index.d.ts +27 -3
- package/dist/providers/sms/vonage/index.js +1 -1
- package/dist/providers/webhook/index.d.ts +4 -2
- package/dist/providers/webhook/index.js +1 -1
- package/dist/webhooks/index.d.ts +35 -47
- package/dist/webhooks/index.js +1 -1
- package/package.json +10 -2
- package/dist/packem_shared/hmacBase64-Dtvoq4Ry.js +0 -1
- package/dist/packem_shared/snsWebhook-eOX1eh23.js +0 -1
- /package/dist/packem_shared/{apnsProvider-Dfgwt-6Z.js → createApnsProvider-Dfgwt-6Z.js} +0 -0
- /package/dist/packem_shared/{discordProvider-B9rc2nrV.js → createDiscordProvider-B9rc2nrV.js} +0 -0
- /package/dist/packem_shared/{expoProvider-DofT1sEf.js → createExpoProvider-DofT1sEf.js} +0 -0
- /package/dist/packem_shared/{failoverProvider-CyYOOitI.js → createFailoverProvider-CyYOOitI.js} +0 -0
- /package/dist/packem_shared/{fcmProvider-86S6m7zk.js → createFcmProvider-86S6m7zk.js} +0 -0
- /package/dist/packem_shared/{inAppProvider-DWO4D6VN.js → createInAppProvider-DWO4D6VN.js} +0 -0
- /package/dist/packem_shared/{messageBirdProvider-CnsBOuY6.js → createMessageBirdProvider-CnsBOuY6.js} +0 -0
- /package/dist/packem_shared/{mockProvider-OaWdPlKU.js → createMockProvider-OaWdPlKU.js} +0 -0
- /package/dist/packem_shared/{msTeamsProvider-BAgW_ba5.js → createMsTeamsProvider-BAgW_ba5.js} +0 -0
- /package/dist/packem_shared/{otelProvider-DdnT7q6B.js → createOtelProvider-DdnT7q6B.js} +0 -0
- /package/dist/packem_shared/{plivoProvider-C27zpwI3.js → createPlivoProvider-C27zpwI3.js} +0 -0
- /package/dist/packem_shared/{roundRobinProvider-BrujhDY6.js → createRoundRobinProvider-BrujhDY6.js} +0 -0
- /package/dist/packem_shared/{slackProvider-CP35fu9-.js → createSlackProvider-CP35fu9-.js} +0 -0
- /package/dist/packem_shared/{telegramProvider-CyaUiWkh.js → createTelegramProvider-CyaUiWkh.js} +0 -0
- /package/dist/packem_shared/{telnyxProvider-M23jXIyu.js → createTelnyxProvider-M23jXIyu.js} +0 -0
- /package/dist/packem_shared/{twilioProvider-CaoTEqz-.js → createTwilioProvider-CaoTEqz-.js} +0 -0
- /package/dist/packem_shared/{vonageProvider-CYNivc-x.js → createVonageProvider-CYNivc-x.js} +0 -0
- /package/dist/packem_shared/{webhookProvider-C13EqjLL.js → createWebhookProvider-C13EqjLL.js} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
## @visulima/notification [1.1.0](https://github.com/visulima/visulima/compare/@visulima/notification@1.0.17...@visulima/notification@1.1.0) (2026-09-03)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* **notification:** add inbound channel receivers with one-call replies ([#826](https://github.com/visulima/visulima/issues/826)) ([3bd0c6f](https://github.com/visulima/visulima/commit/3bd0c6fffd8b23067fbd5a5356c74e5da4af834c))
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Dependencies
|
|
9
|
+
|
|
10
|
+
* **@visulima/email:** upgraded to 3.0.4
|
|
11
|
+
|
|
12
|
+
## @visulima/notification [1.0.17](https://github.com/visulima/visulima/compare/@visulima/notification@1.0.16...@visulima/notification@1.0.17) (2026-08-19)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Dependencies
|
|
16
|
+
|
|
17
|
+
* **@visulima/email:** upgraded to 3.0.3
|
|
18
|
+
|
|
1
19
|
## @visulima/notification [1.0.16](https://github.com/visulima/visulima/compare/%40visulima%2Fnotification%401.0.15...%40visulima%2Fnotification%401.0.16) (2026-08-19)
|
|
2
20
|
|
|
3
21
|
|
package/README.md
CHANGED
|
@@ -68,23 +68,23 @@ pnpm add @visulima/notification
|
|
|
68
68
|
|
|
69
69
|
```typescript
|
|
70
70
|
import { send } from "@visulima/notification";
|
|
71
|
-
import {
|
|
71
|
+
import { createTwilioProvider } from "@visulima/notification/providers/twilio";
|
|
72
72
|
|
|
73
|
-
await send("sms",
|
|
73
|
+
await send("sms", createTwilioProvider({ accountSid: "AC…", authToken: "…", from: "+15555550100" }), { to: "+15555550100", text: "Your code is 123" });
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
### Multi-channel send
|
|
77
77
|
|
|
78
78
|
```typescript
|
|
79
79
|
import { createNotification } from "@visulima/notification";
|
|
80
|
-
import {
|
|
81
|
-
import {
|
|
82
|
-
import {
|
|
80
|
+
import { createTwilioProvider } from "@visulima/notification/providers/twilio";
|
|
81
|
+
import { createSlackProvider } from "@visulima/notification/providers/slack";
|
|
82
|
+
import { createFcmProvider } from "@visulima/notification/providers/fcm";
|
|
83
83
|
|
|
84
84
|
const notify = createNotification({
|
|
85
|
-
sms:
|
|
86
|
-
chat:
|
|
87
|
-
push:
|
|
85
|
+
sms: createTwilioProvider({ accountSid: "AC…", authToken: "…", from: "+15555550100" }),
|
|
86
|
+
chat: createSlackProvider({ token: "xoxb-…", defaultChannel: "C123" }),
|
|
87
|
+
push: createFcmProvider({ projectId: "my-app", getAccessToken: async () => getGoogleToken() }),
|
|
88
88
|
});
|
|
89
89
|
|
|
90
90
|
// Each present channel is delivered in parallel; you get one receipt per channel.
|
|
@@ -126,7 +126,7 @@ Providers are imported from `@visulima/notification/providers/<name>` so unused
|
|
|
126
126
|
| **SMS** | `twilio`, `vonage`, `plivo`, `messagebird`, `telnyx`, `sns` |
|
|
127
127
|
| **Push** | `fcm`, `expo`, `web-push`, `apns` |
|
|
128
128
|
| **Chat** | `slack`, `discord`, `msteams`, `telegram` |
|
|
129
|
-
| **In-app** | `
|
|
129
|
+
| **In-app** | `createInAppProvider` (memory or unstorage store) |
|
|
130
130
|
| **Webhook** | `webhook` |
|
|
131
131
|
| **Email** | `emailChannel(...)` → wraps a `@visulima/email` `Mail` instance |
|
|
132
132
|
| **Wrappers** | `failover`, `roundrobin`, `opentelemetry`, `mock` |
|
|
@@ -161,11 +161,15 @@ export const myProvider = defineProvider<MyConfig, SmsPayload>((config) => ({
|
|
|
161
161
|
Wrap several same-channel providers to gain resilience or load balancing:
|
|
162
162
|
|
|
163
163
|
```typescript
|
|
164
|
-
import {
|
|
165
|
-
import {
|
|
164
|
+
import { createFailoverProvider } from "@visulima/notification/providers/failover";
|
|
165
|
+
import { createPlivoProvider } from "@visulima/notification/providers/plivo";
|
|
166
|
+
import { createRoundRobinProvider } from "@visulima/notification/providers/roundrobin";
|
|
167
|
+
import { createTelnyxProvider } from "@visulima/notification/providers/telnyx";
|
|
168
|
+
import { createTwilioProvider } from "@visulima/notification/providers/twilio";
|
|
169
|
+
import { createVonageProvider } from "@visulima/notification/providers/vonage";
|
|
166
170
|
|
|
167
|
-
const sms =
|
|
168
|
-
const balanced =
|
|
171
|
+
const sms = createFailoverProvider([createTwilioProvider({ … }), createVonageProvider({ … })]); // try Twilio, fall back to Vonage
|
|
172
|
+
const balanced = createRoundRobinProvider([createPlivoProvider({ … }), createTelnyxProvider({ … })]);
|
|
169
173
|
|
|
170
174
|
const notify = createNotification({ sms });
|
|
171
175
|
```
|
|
@@ -239,9 +243,9 @@ const queue = new UnstorageQueue(createStorage());
|
|
|
239
243
|
## In-app inbox
|
|
240
244
|
|
|
241
245
|
```typescript
|
|
242
|
-
import {
|
|
246
|
+
import { createInAppProvider } from "@visulima/notification/channels/inapp";
|
|
243
247
|
|
|
244
|
-
const inapp =
|
|
248
|
+
const inapp = createInAppProvider();
|
|
245
249
|
const notify = createNotification({ inapp });
|
|
246
250
|
|
|
247
251
|
await notify.sendToChannel("inapp", { to: "user-1", title: "Welcome", body: "Thanks for joining" });
|
|
@@ -281,6 +285,62 @@ bus.on("sent", (event) => console.log(event.messageId));
|
|
|
281
285
|
bus.on("*", (event) => store.append(event));
|
|
282
286
|
```
|
|
283
287
|
|
|
288
|
+
## Inbound (two-way channels for bots & AI agents)
|
|
289
|
+
|
|
290
|
+
Channels are two-way. Each `@visulima/notification/providers/<name>` subpath ships both halves —
|
|
291
|
+
the outbound provider (`create<Name>Provider`) and the inbound receiver (`create<Name>Receiver`), which
|
|
292
|
+
verifies and normalises incoming webhooks (a Slack mention, a Discord slash command, a Telegram
|
|
293
|
+
message, an inbound SMS/WhatsApp) into a single `InboundMessage` you can hand to an AI agent that
|
|
294
|
+
then replies through the same provider. Receivers are framework-agnostic (`handle(request:
|
|
295
|
+
Request) => Promise<Response>`) and edge-safe (Web Crypto only). The `@visulima/notification/inbound`
|
|
296
|
+
barrel holds the shared types, `createInboundRouter` and verification/reply helpers.
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
import { createSlackReceiver } from "@visulima/notification/providers/slack";
|
|
300
|
+
|
|
301
|
+
// Read the secret however your runtime exposes it: an `env` binding on
|
|
302
|
+
// Cloudflare Workers, `Deno.env.get`, `process.env` on Node.
|
|
303
|
+
declare const signingSecret: string;
|
|
304
|
+
|
|
305
|
+
const slack = createSlackReceiver({
|
|
306
|
+
signingSecret,
|
|
307
|
+
onMessage: async (message) => {
|
|
308
|
+
if (message.type === "message") {
|
|
309
|
+
return { text: await runAgent(message.text ?? "") };
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return undefined; // acknowledge; reply out-of-band via the outbound provider
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
// Cloudflare Workers / Deno / Bun / Hono / Next.js route handlers …
|
|
317
|
+
export default { fetch: (request: Request) => slack.handle(request) };
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Receivers: `createSlackReceiver` (Events API, slash commands, interactions), `createDiscordReceiver`
|
|
321
|
+
(Ed25519 interactions), `createTelegramReceiver` (bot webhooks), `createTwilioReceiver` (SMS &
|
|
322
|
+
WhatsApp), `createMsTeamsReceiver` (outgoing-webhook HMAC), `createTelnyxReceiver` (SMS, Ed25519),
|
|
323
|
+
`createMessageBirdReceiver` and `createVonageReceiver` (SMS, signed-JWT). `createInboundRouter`
|
|
324
|
+
mounts several behind one fetch handler.
|
|
325
|
+
|
|
326
|
+
For the full receive → think → reply loop in one call, pass the matching outbound `provider` to
|
|
327
|
+
the receiver and answer with `context.reply(...)` — it maps the inbound message to the right
|
|
328
|
+
outbound payload (recipient, thread, WhatsApp prefix) and sends it:
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
import { createSlackProvider, createSlackReceiver } from "@visulima/notification/providers/slack";
|
|
332
|
+
|
|
333
|
+
const slack = createSlackReceiver({
|
|
334
|
+
signingSecret,
|
|
335
|
+
provider: createSlackProvider({ token }),
|
|
336
|
+
onMessage: async (message, context) => {
|
|
337
|
+
await context.reply(await runAgent(message.text ?? ""));
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
See the [inbound docs](./docs/inbound.mdx).
|
|
343
|
+
|
|
284
344
|
## Supported Node.js Versions
|
|
285
345
|
|
|
286
346
|
Libraries in this ecosystem make the best effort to track [Node.js' release schedule](https://github.com/nodejs/release#release-schedule).
|
|
@@ -63,7 +63,7 @@ interface InAppProviderConfig {
|
|
|
63
63
|
* @param config Provider `id` and the backing {@link InAppStore} (defaults to in-memory).
|
|
64
64
|
* @returns A provider that writes notifications into the configured store.
|
|
65
65
|
*/
|
|
66
|
-
declare const inAppProvider: (config?: InAppProviderConfig) => Provider<InAppProviderConfig, InAppPayload, InAppStore>;
|
|
66
|
+
declare const inAppProvider$1: (config?: InAppProviderConfig) => Provider<InAppProviderConfig, InAppPayload, InAppStore>;
|
|
67
67
|
/**
|
|
68
68
|
* An {@link InAppStore} backed by an [unstorage](https://unstorage.unjs.io) driver, giving durable,
|
|
69
69
|
* multi-backend persistence (Redis, filesystem, Cloudflare KV, ...). `unstorage` is an optional peer
|
|
@@ -96,4 +96,6 @@ declare class UnstorageInAppStore implements InAppStore {
|
|
|
96
96
|
* @returns A new {@link UnstorageInAppStore}.
|
|
97
97
|
*/
|
|
98
98
|
declare const createUnstorageInAppStore: (storage: Storage, prefix?: string) => UnstorageInAppStore;
|
|
99
|
-
|
|
99
|
+
/** @deprecated Renamed to `createInAppProvider`; removed in the next major. */
|
|
100
|
+
declare const inAppProvider: typeof inAppProvider$1;
|
|
101
|
+
export { type InAppProviderConfig, type InAppStore, type ListOptions, MemoryInAppStore, type StoredNotification, UnstorageInAppStore, inAppProvider$1 as createInAppProvider, createUnstorageInAppStore, inAppProvider };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{inAppProvider as
|
|
1
|
+
import{inAppProvider as r}from"../../packem_shared/createInAppProvider-DWO4D6VN.js";import{MemoryInAppStore as n}from"../../packem_shared/MemoryInAppStore-B-j6qUql.js";import{UnstorageInAppStore as A,createUnstorageInAppStore as a}from"../../packem_shared/UnstorageInAppStore-ygHFsRM3.js";const p=r;export{n as MemoryInAppStore,A as UnstorageInAppStore,r as createInAppProvider,a as createUnstorageInAppStore,p as inAppProvider};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { P as Provider } from "../packem_shared/provider.d-Dh32nRm9.js";
|
|
2
|
+
import { b as ChatPayload, S as SmsPayload } from "../packem_shared/types.d-C7l7qdMG.js";
|
|
3
|
+
import { I as InboundMessage, a as InboundReplyFunction, b as InboundReplyInput, c as InboundReply, R as Receiver } from "../packem_shared/types.d-4T7bNo4x.js";
|
|
4
|
+
export type { d as InboundAttachment, e as InboundChannel, f as InboundContext, g as InboundErrorReason, h as InboundHandler, i as InboundMessageType, j as InboundParticipant, k as InboundResponse, l as ReceiverOptions } from "../packem_shared/types.d-4T7bNo4x.js";
|
|
5
|
+
import "../packem_shared/types.d-BXfGenMH.js";
|
|
6
|
+
/**
|
|
7
|
+
* Verifies an Ed25519 signature over `timestamp + body`, Discord's interaction signing
|
|
8
|
+
* scheme. Edge-safe — uses Web Crypto's Ed25519 primitive only (available on Node 22+ and
|
|
9
|
+
* Cloudflare Workers). Any malformed input or unsupported-algorithm error resolves to
|
|
10
|
+
* `false` rather than throwing.
|
|
11
|
+
* @param publicKeyHex The application's Ed25519 public key (hex), from the Discord dashboard.
|
|
12
|
+
* @param signatureHex The `X-Signature-Ed25519` header value (hex).
|
|
13
|
+
* @param timestamp The `X-Signature-Timestamp` header value.
|
|
14
|
+
* @param body The raw request body.
|
|
15
|
+
* @returns `true` when the signature is valid.
|
|
16
|
+
*/
|
|
17
|
+
declare const verifyEd25519: (publicKeyHex: string, signatureHex: string, timestamp: string, body: string) => Promise<boolean>;
|
|
18
|
+
/**
|
|
19
|
+
* Verifies an Ed25519 signature over an arbitrary message, with the public key and signature
|
|
20
|
+
* supplied as base64 (Telnyx's webhook signing scheme, where the signed message is
|
|
21
|
+
* `{timestamp}|{body}`). Any malformed input resolves to `false` rather than throwing.
|
|
22
|
+
* @param publicKeyBase64 The Ed25519 public key (base64).
|
|
23
|
+
* @param signatureBase64 The header signature to check (base64).
|
|
24
|
+
* @param message The exact string that was signed (e.g. `{timestamp}|{body}` for Telnyx).
|
|
25
|
+
* @returns `true` when the signature is valid.
|
|
26
|
+
*/
|
|
27
|
+
declare const verifyEd25519Base64: (publicKeyBase64: string, signatureBase64: string, message: string) => Promise<boolean>;
|
|
28
|
+
/**
|
|
29
|
+
* Computes the lowercase hex SHA-256 digest of a UTF-8 string — the encoding used by the
|
|
30
|
+
* `payload_hash` / `url_hash` claims in MessageBird and Vonage signed webhooks.
|
|
31
|
+
* @param value The string to hash.
|
|
32
|
+
* @returns The hex digest.
|
|
33
|
+
*/
|
|
34
|
+
declare const sha256Hex: (value: string) => Promise<string>;
|
|
35
|
+
/**
|
|
36
|
+
* Verifies a compact HS256 JWT against `secret` and returns its decoded payload claims, or
|
|
37
|
+
* `undefined` when the token is malformed, uses a different algorithm, or the signature does
|
|
38
|
+
* not match. Signature comparison is constant-time. Edge-safe — Web Crypto only.
|
|
39
|
+
* @param token The compact JWT (`header.payload.signature`).
|
|
40
|
+
* @param secret The shared signing secret.
|
|
41
|
+
* @returns The verified payload claims, or `undefined`.
|
|
42
|
+
*/
|
|
43
|
+
declare const verifyHs256Jwt: (token: string, secret: string) => Promise<Record<string, unknown> | undefined>;
|
|
44
|
+
/**
|
|
45
|
+
* Checks that a JWT's `exp` (and optional `nbf`) claims place `now` within the token's validity
|
|
46
|
+
* window, allowing a small clock-skew tolerance. Missing claims are treated as unbounded.
|
|
47
|
+
* @param payload The decoded claims.
|
|
48
|
+
* @param toleranceSeconds The allowed clock skew (default 5 seconds).
|
|
49
|
+
* @returns `true` when the token is temporally valid.
|
|
50
|
+
*/
|
|
51
|
+
declare const isJwtTimeValid: (payload: Record<string, unknown>, toleranceSeconds?: number) => boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Normalises the shorthand string form of a reply into an {@link InboundReply}.
|
|
54
|
+
* @param input The reply content, as an object or a plain string.
|
|
55
|
+
* @returns The reply as an object, wrapping a bare string as `{ text }`.
|
|
56
|
+
*/
|
|
57
|
+
declare const normaliseReply: (input: InboundReplyInput) => InboundReply;
|
|
58
|
+
/**
|
|
59
|
+
* Creates the reply sender for a chat channel (Slack, Discord, Telegram), mapping the inbound
|
|
60
|
+
* message and reply into a {@link ChatPayload} sent through `outbound`. The reply targets the
|
|
61
|
+
* originating conversation and thread. The returned function rejects when `outbound` is absent.
|
|
62
|
+
* @param provider The provider id, used in the not-configured error.
|
|
63
|
+
* @param message The inbound message being replied to.
|
|
64
|
+
* @param outbound The matching outbound chat provider, or `undefined`.
|
|
65
|
+
* @returns A reply sender bound to `message`.
|
|
66
|
+
*/
|
|
67
|
+
declare const chatReply: (provider: string, message: InboundMessage, outbound: Provider<unknown, ChatPayload> | undefined) => InboundReplyFunction;
|
|
68
|
+
/**
|
|
69
|
+
* Creates the reply sender for an SMS channel (Twilio, Telnyx, Vonage, MessageBird), mapping the
|
|
70
|
+
* inbound message and reply into an {@link SmsPayload} sent through `outbound`. The reply is
|
|
71
|
+
* addressed back to the sender from the receiving number, re-applying the `whatsapp:` prefix for
|
|
72
|
+
* WhatsApp. The returned function rejects when `outbound` is absent.
|
|
73
|
+
* @param message The inbound message being replied to.
|
|
74
|
+
* @param outbound The matching outbound SMS provider, or `undefined`.
|
|
75
|
+
* @returns A reply sender bound to `message`.
|
|
76
|
+
*/
|
|
77
|
+
declare const smsReply: (message: InboundMessage, outbound: Provider<unknown, SmsPayload> | undefined) => InboundReplyFunction;
|
|
78
|
+
/**
|
|
79
|
+
* A web-standard request handler: the shape returned by {@link createInboundRouter} and the
|
|
80
|
+
* signature platforms like Cloudflare Workers, Deno, Bun and Node's `fetch` server expect.
|
|
81
|
+
* @param request The inbound request.
|
|
82
|
+
* @returns The response.
|
|
83
|
+
*/
|
|
84
|
+
type FetchHandler = (request: Request) => Promise<Response>;
|
|
85
|
+
/**
|
|
86
|
+
* Mounts several {@link Receiver}s behind a single fetch handler, dispatching by URL
|
|
87
|
+
* path. Each key is matched against the request's path — exactly, or as a suffix beginning at a
|
|
88
|
+
* path-segment boundary, so a mount base path does not need to be known ahead of time. The most
|
|
89
|
+
* specific route wins; unmatched paths get a `404`.
|
|
90
|
+
*
|
|
91
|
+
* The result is directly usable as a Cloudflare Workers / Deno / Bun `fetch` export, or via a
|
|
92
|
+
* Node/Express/Hono web-request adapter.
|
|
93
|
+
* @param routes A map of path (e.g. `"/webhooks/slack"`) to channel receiver.
|
|
94
|
+
* @returns A fetch handler dispatching to the matching channel.
|
|
95
|
+
*/
|
|
96
|
+
declare const createInboundRouter: (routes: Record<string, Receiver>) => FetchHandler;
|
|
97
|
+
export { type FetchHandler, type InboundMessage, type InboundReply, type InboundReplyFunction, type InboundReplyInput, type Receiver, chatReply, createInboundRouter, isJwtTimeValid, normaliseReply, sha256Hex, smsReply, verifyEd25519, verifyEd25519Base64, verifyHs256Jwt };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{verifyEd25519 as o,verifyEd25519Base64 as t}from"../packem_shared/verifyEd25519-lmU3nhP0.js";import{isJwtTimeValid as i,sha256Hex as m,verifyHs256Jwt as p}from"../packem_shared/isJwtTimeValid-CdCG24pW.js";import{chatReply as a,normaliseReply as y,smsReply as l}from"../packem_shared/chatReply-HANpcetF.js";import{createInboundRouter as d}from"../packem_shared/createInboundRouter-DWk4xvCj.js";export{a as chatReply,d as createInboundRouter,i as isJwtTimeValid,y as normaliseReply,m as sha256Hex,l as smsReply,o as verifyEd25519,t as verifyEd25519Base64,p as verifyHs256Jwt};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import i from"./NotificationError-tZsXfB7V.js";const d=t=>typeof t=="string"?{text:t}:t,p=(t,o,e)=>async n=>{if(e===void 0)throw new i(`${t}-inbound`,"No `provider` configured to send replies",{hint:`Pass \`provider\` to the ${t} inbound receiver to enable context.reply()`});const r=d(n);return e.send({blocks:r.blocks,text:r.text??"",threadId:r.threadId??o.threadId,to:o.conversationId})},s=(t,o)=>async e=>{if(o===void 0)throw new i(`${t.provider}-inbound`,"No `provider` configured to send replies",{hint:`Pass \`provider\` to the ${t.provider} inbound receiver to enable context.reply()`});const n=d(e),r=t.metadata?.transport==="whatsapp"?"whatsapp:":"";return o.send({from:t.conversationId===void 0?void 0:`${r}${t.conversationId}`,text:n.text??"",to:`${r}${t.from.id}`})};export{p as chatReply,d as normaliseReply,s as smsReply};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{getHeader as p,tryParseObject as A}from"./getHeader-BF9s-Tgp.js";import{verifyEd25519 as S}from"./verifyEd25519-lmU3nhP0.js";import{chatReply as h}from"./chatReply-HANpcetF.js";import{h as b,r as u,j as c,n as g,a as s,b as l,c as y}from"./utils-DuaeYOiD.js";const D="X-Signature-Ed25519",M="X-Signature-Timestamp",N=1,T=2,P=3,w=1,I=4,v=5,C=2e3,R=Symbol("defer"),O=async e=>new Promise(t=>{setTimeout(t,e,R).unref?.()}),G=e=>e.member?.user??e.user,H=e=>{const{type:t}=e,r=G(e),o={id:s(r?.id)??"",isBot:r?.bot===!0,username:s(r?.username)},i={channel:"chat",conversationId:s(e.channel_id),from:o,id:s(e.id)??"",metadata:{applicationId:e.application_id,guildId:e.guild_id,token:e.token},provider:"discord",raw:e,timestamp:new Date};if(t===T){const n=e.data;return{...i,command:{name:s(n?.name)??""},type:"command"}}if(t===P){const n=e.data;return{...i,text:s(n?.custom_id),type:"callback"}}},U=e=>({channel:"chat",handle:async t=>{const r=await t.text(),o=b(t.headers),i=p(o,D),n=p(o,M);if(i===void 0||n===void 0)return u("missing_signature",t,e.onError);if(!await S(e.publicKey,i,n,r))return u("invalid_signature",t,e.onError);const a=A(r);if(a===void 0)return u("invalid_body",t,e.onError);if(a.type===N)return c({type:w});const d=H(a);if(d===void 0)return g();const f=Promise.resolve(e.onMessage(d,{body:r,headers:o,reply:h("discord",d,e.provider),request:t})),m=await Promise.race([f,O(e.deferAfterMs??C)]);if(m===R)return f.catch(()=>{}),c({type:v});const _=l(m);if(_!==void 0)return _;const E=y(m);return E!==void 0?c({data:{content:E.text,...E.raw},type:I}):c({type:v})},provider:"discord"});export{U as createDiscordReceiver};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{j as c}from"./utils-DuaeYOiD.js";const d=o=>{const s=Object.entries(o).map(([n,t])=>[n.startsWith("/")?n:`/${n}`,t]).toSorted(([n],[t])=>t.length-n.length);return async n=>{const{pathname:t}=new URL(n.url),e=s.find(([r])=>t===r||t.endsWith(r));return e===void 0?c({error:"not_found"},404):e[1].handle(n)}};export{d as createInboundRouter};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{getHeader as l,tryParseObject as v}from"./getHeader-BF9s-Tgp.js";import{verifyHs256Jwt as f,isJwtTimeValid as h,sha256Hex as c}from"./isJwtTimeValid-CdCG24pW.js";import{smsReply as u}from"./chatReply-HANpcetF.js";import{h as y,r as t,n as g,b as p,a as n,d as w}from"./utils-DuaeYOiD.js";const E="messagebird-signature-jwt",R=e=>{const r=v(e),i=n(r?.originator),o=n(r?.body),s=n(r?.id);if(r===void 0||i===void 0||s===void 0)return;const a=n(r.createdDatetime);return{channel:"sms",conversationId:n(r.recipient),from:{id:i},id:s,metadata:{direction:r.direction},provider:"messagebird",raw:r,text:o,timestamp:w(a),type:"message"}},H=e=>({channel:"sms",handle:async r=>{const i=await r.text(),o=y(r.headers),s=l(o,E);if(s===void 0)return t("missing_signature",r,e.onError);const a=await f(s,e.signingKey);if(a===void 0)return t("invalid_signature",r,e.onError);if(a.iss!=="MessageBird"||typeof a.jti!="string"||!h(a))return t("invalid_signature",r,e.onError);if(a.url_hash!==await c(e.url??r.url))return t("invalid_signature",r,e.onError);if(i.length>0&&a.payload_hash!==await c(i))return t("invalid_signature",r,e.onError);const d=R(i);if(d===void 0)return g();const m=await e.onMessage(d,{body:i,headers:o,reply:u(d,e.provider),request:r});return p(m)??g()},provider:"messagebird"});export{H as createMessageBirdReceiver};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{f as l}from"./webcrypto-BPm3NDEm.js";import{hmacBase64 as f,timingSafeEqual as v}from"./hmacBase64-ocSXhrdQ.js";import{getHeader as u,tryParseObject as g}from"./getHeader-BF9s-Tgp.js";import{chatReply as y}from"./chatReply-HANpcetF.js";import{h as R,r as c,n as w,j as x,a as r,d as E,b as A,c as H}from"./utils-DuaeYOiD.js";const I="Authorization",h="HMAC ",T=t=>{const e=g(t);if(e?.type!=="message")return;const a=e.from,s=e.conversation,n=r(e.timestamp);return{channel:"chat",conversationId:r(s?.id),from:{id:r(a?.id)??"",name:r(a?.name)},id:r(e.id)??"",metadata:{channelId:e.channelId,serviceUrl:e.serviceUrl},provider:"msteams",raw:e,text:r(e.text),threadId:r(s?.id),timestamp:E(n),type:"message"}},C=t=>({channel:"chat",handle:async e=>{const a=await e.text(),s=R(e.headers),n=u(s,I);if(n?.startsWith(h)!==!0)return c("missing_signature",e,t.onError);let m;try{m=await f(l(t.securityToken),a,"SHA-256")}catch{return c("invalid_signature",e,t.onError)}if(!v(m,n.slice(h.length)))return c("invalid_signature",e,t.onError);const o=T(a);if(o===void 0)return w();const d=await t.onMessage(o,{body:a,headers:s,reply:y("msteams",o,t.provider),request:e}),p=A(d);if(p!==void 0)return p;const i=H(d);return i!==void 0?x({text:i.text,type:"message",...i.raw}):new Response(void 0,{status:200})},provider:"msteams"});export{C as createMsTeamsReceiver};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{slackWebhook as w}from"./slackWebhook-DILLGl3P.js";import{getHeader as h,tryParseObject as u}from"./getHeader-BF9s-Tgp.js";import{chatReply as k}from"./chatReply-HANpcetF.js";import{h as b,r as I,j as _,n as R,a as t,b as S,c as x}from"./utils-DuaeYOiD.js";const N="X-Slack-Signature",A=/^\//u,s=(i,o)=>i.get(o)??void 0,j=(i,o)=>{if(o.includes("application/x-www-form-urlencoded")){const e=new URLSearchParams(i),d=s(e,"payload");if(d!==void 0){const c=u(d);if(c===void 0)return;const f=c.user,g=c.container,y=c.channel;return{channel:"chat",conversationId:t(y?.id),from:{id:t(f?.id)??"",name:t(f?.name)},id:t(c.trigger_id)??"",metadata:{apiAppId:c.api_app_id,teamId:c.team?.id},provider:"slack",raw:c,threadId:t(g?.message_ts),timestamp:new Date,type:"callback"}}const p=s(e,"command");return p!==void 0?{channel:"chat",command:{args:s(e,"text"),name:p.replace(A,"")},conversationId:s(e,"channel_id"),from:{id:s(e,"user_id")??"",username:s(e,"user_name")},id:s(e,"trigger_id")??"",metadata:{apiAppId:s(e,"api_app_id"),teamId:s(e,"team_id")},provider:"slack",raw:Object.fromEntries(e),text:s(e,"text"),timestamp:new Date,type:"command"}:void 0}const n=u(i);if(n?.type!=="event_callback")return;const a=n.event;if(a===void 0)return;const l=t(a.type)??"",v=l==="message"||l==="app_mention",r=t(a.ts),m=r===void 0?Number.NaN:Number.parseFloat(r);return{channel:"chat",conversationId:t(a.channel),from:{id:t(a.user)??"",isBot:typeof a.bot_id=="string"},id:t(n.event_id)??r??"",metadata:{apiAppId:n.api_app_id,eventType:l,teamId:n.team_id},provider:"slack",raw:n,text:t(a.text),threadId:t(a.thread_ts)??r,timestamp:Number.isNaN(m)?new Date:new Date(m*1e3),type:v?"message":"event"}},L=i=>({channel:"chat",handle:async o=>{const n=await o.text(),a=b(o.headers);if(!await w.verify(n,a,i.signingSecret)){const p=h(a,N)===void 0?"missing_signature":"invalid_signature";return I(p,o,i.onError)}const v=h(a,"content-type")??"";if(v.includes("application/json")){const p=u(n);if(p?.type==="url_verification")return _({challenge:p.challenge})}const r=j(n,v);if(r===void 0)return R();const m=await i.onMessage(r,{body:n,headers:a,reply:k("slack",r,i.provider),request:o}),e=S(m);if(e!==void 0)return e;const d=x(m);return d!==void 0&&(r.type==="command"||r.type==="callback")?_({blocks:d.blocks,text:d.text,...d.raw}):new Response(void 0,{status:200})},provider:"slack"});export{L as createSlackReceiver};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import H from"./RequiredOptionError-CAuRlc0R.js";import{defineProvider as K}from"./defineProvider-C9q-7iwt.js";import{t as T}from"./credentials-D9KXFoo0.js";import{r as $}from"./http-YFt2eXXy.js";import{g as E}from"./id-FYdbBSpb.js";import{s as x,b as z}from"./sms-CT04IxF8.js";import{
|
|
1
|
+
import H from"./RequiredOptionError-CAuRlc0R.js";import{defineProvider as K}from"./defineProvider-C9q-7iwt.js";import{t as T}from"./credentials-D9KXFoo0.js";import{r as $}from"./http-YFt2eXXy.js";import{g as E}from"./id-FYdbBSpb.js";import{s as x,b as z}from"./sms-CT04IxF8.js";import{t as w,a as I,u as c,i as g}from"./webcrypto-BPm3NDEm.js";const R="sns",C="us-east-1",D=/<Message>([\s\S]*?)<\/Message>/,q=/<MessageId>([\s\S]*?)<\/MessageId>/,v=t=>{const e=t.toISOString().replaceAll(/[:-]|\.\d{3}/g,"");return{amzDate:e,dateStamp:e.slice(0,8)}},O=async(t,e,o)=>{const i=await g(c(`AWS4${t}`),c(e)),m=await g(i,c(o)),d=await g(m,c(R));return g(d,c("aws4_request"))},P=t=>{const{pathname:e}=new URL(t);return e===""||e==="/"?"/":e.split("/").map(o=>encodeURIComponent(o).replaceAll("*","%2A").replaceAll("'","%27").replaceAll("(","%28").replaceAll(")","%29").replaceAll("!","%21")).join("/")},j=async(t,e,o,i,m)=>{const{amzDate:d,dateStamp:S}=v(new Date),p=w(await I(c(m))),a={"Content-Type":"application/x-www-form-urlencoded",host:o,"x-amz-content-sha256":p,"x-amz-date":d};t.sessionToken&&(a["x-amz-security-token"]=t.sessionToken);const r={};for(const[l,y]of Object.entries(a))r[l.toLowerCase()]=y.trim();const s=Object.keys(r).toSorted((l,y)=>l.localeCompare(y)),u=s.join(";"),A=s.map(l=>`${l}:${r[l]??""}
|
|
2
2
|
`).join(""),n=["POST",P(i),"",A,u,p].join(`
|
|
3
3
|
`),f=`${S}/${e}/${R}/aws4_request`,h=["AWS4-HMAC-SHA256",d,f,w(await I(c(n)))].join(`
|
|
4
4
|
`),M=await O(t.secretAccessKey,S,e),b=w(await g(M,c(h)));return a.Authorization=`AWS4-HMAC-SHA256 Credential=${t.accessKeyId}/${f}, SignedHeaders=${u}, Signature=${b}`,{headers:a,url:i}},W=K(t=>{const e=t??{};if(!e.accessKeyId||!e.secretAccessKey)throw new H("sns",["accessKeyId","secretAccessKey"]);const o=e.region??C,i=e.endpoint??`https://sns.${o}.amazonaws.com/`,{host:m}=new URL(i),d=e.retries??3,S=e.timeout??3e4,p=async(a,r)=>{const s=new URLSearchParams;s.set("Action","Publish"),s.set("Version","2010-03-31"),s.set("PhoneNumber",a),s.set("Message",r.text),r.from&&(s.set("MessageAttributes.entry.1.Name","AWS.SNS.SMS.SenderID"),s.set("MessageAttributes.entry.1.Value.DataType","String"),s.set("MessageAttributes.entry.1.Value.StringValue",r.from));const u=s.toString(),A=await j(e,o,m,i,u),n=await $(A.url,{body:u,headers:A.headers,method:"POST",timeout:S},d);if(!n.success||!n.data)return{error:n.error instanceof Error?n.error.message:"Request failed",id:a,status:"failed"};if(n.data.status>=400)return{error:D.exec(n.data.body)?.[1]??`HTTP ${String(n.data.status)}`,id:a,status:"failed"};const f=q.exec(n.data.body)?.[1];return{id:a,messageId:f??E("sns"),status:"sent"}};return{channel:"sms",endpoint:i,features:{batchSending:!1,deliveryReceipts:!1,media:!1,scheduling:!1},id:"sns",initialize:()=>{},isAvailable:()=>!!(e.accessKeyId&&e.secretAccessKey),options:e,send:async a=>{const r=await x(T(a.to),async s=>p(s,a));return z("sns",r)},validateCredentials:()=>!!(e.accessKeyId&&e.secretAccessKey)}});export{W as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{timingSafeEqual as p}from"./hmacBase64-ocSXhrdQ.js";import{getHeader as f,tryParseObject as u}from"./getHeader-BF9s-Tgp.js";import{chatReply as _}from"./chatReply-HANpcetF.js";import{h as y,r as l,n as g,j as I,e as d,a as h,b,c as T}from"./utils-DuaeYOiD.js";const w="X-Telegram-Bot-Api-Secret-Token",E=/\s/u,R=/^\d+$/u,k=e=>{if(!e?.startsWith("/"))return;const t=e.slice(1),a=t.search(E),n=(a===-1?t:t.slice(0,a)).split("@")[0]??"";if(n.length===0)return;const s=a===-1?"":t.slice(a+1).trim();return{args:s.length===0?void 0:s,name:n}},v=e=>({id:d(e?.id)??"",isBot:e?.is_bot===!0,name:h(e?.first_name),username:h(e?.username)}),N=e=>{const t=d(e.update_id)??"",a=e.callback_query;if(a!==void 0){const i=a.message?.chat;return{channel:"chat",conversationId:d(i?.id),from:v(a.from),id:h(a.id)??t,provider:"telegram",raw:e,text:h(a.data),timestamp:new Date,type:"callback"}}const r=e.message??e.edited_message;if(r===void 0)return;const n=r.chat,s=typeof r.date=="number"?r.date:Number.NaN,c=h(r.text),m=k(c);return{channel:"chat",command:m,conversationId:d(n?.id),from:v(r.from),id:d(r.message_id)??t,metadata:{chatType:n?.type},provider:"telegram",raw:e,text:c,threadId:d(r.message_thread_id),timestamp:Number.isNaN(s)?new Date:new Date(s*1e3),type:m===void 0?"message":"command"}},j=e=>({channel:"chat",handle:async t=>{const a=await t.text(),r=y(t.headers);if(e.secretToken!==void 0&&e.secretToken!==""){const i=f(r,w);if(i===void 0)return l("missing_signature",t,e.onError);if(!p(i,e.secretToken))return l("invalid_signature",t,e.onError)}const n=u(a);if(n===void 0)return l("invalid_body",t,e.onError);const s=N(n);if(s===void 0)return g();const c=await e.onMessage(s,{body:a,headers:r,reply:_("telegram",s,e.provider),request:t}),m=b(c);if(m!==void 0)return m;const o=T(c);if(o!==void 0&&s.conversationId!==void 0){const i=o.threadId!==void 0&&R.test(o.threadId)?Number.parseInt(o.threadId,10):void 0;return I({chat_id:s.conversationId,message_thread_id:i,method:"sendMessage",text:o.text,...o.raw})}return g()},provider:"telegram"});export{j as createTelegramReceiver};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{isWithinReplayWindow as l,REPLAY_WINDOW_SECONDS as y}from"./hmacBase64-ocSXhrdQ.js";import{getHeader as c,tryParseObject as v}from"./getHeader-BF9s-Tgp.js";import{verifyEd25519Base64 as f}from"./verifyEd25519-lmU3nhP0.js";import{smsReply as g}from"./chatReply-HANpcetF.js";import{h as u,r as d,n as m,b as _,a as n,d as h}from"./utils-DuaeYOiD.js";const E="telnyx-signature-ed25519",R="telnyx-timestamp",x=t=>{const{media:r}=t;if(!(!Array.isArray(r)||r.length===0))return r.map(a=>{const e=a;return{contentType:n(e.content_type),url:n(e.url)}})},A=t=>{const r=v(t),a=r?.data,e=a?.payload;if(e===void 0||n(a?.event_type)!=="message.received")return;const i=e.from,o=e.to,s=n(e.received_at);return{attachments:x(e),channel:"sms",conversationId:n(o?.[0]?.phone_number),from:{id:n(i?.phone_number)??""},id:n(e.id)??"",metadata:{direction:e.direction,messagingProfileId:e.messaging_profile_id,transport:n(e.type)},provider:"telnyx",raw:r,text:n(e.text),timestamp:h(s),type:"message"}},S=t=>({channel:"sms",handle:async r=>{const a=await r.text(),e=u(r.headers),i=c(e,E),o=c(e,R);if(i===void 0||o===void 0)return d("missing_signature",r,t.onError);if(!l(o,y))return d("invalid_signature",r,t.onError);if(!await f(t.publicKey,i,`${o}|${a}`))return d("invalid_signature",r,t.onError);const s=A(a);if(s===void 0)return m();const p=await t.onMessage(s,{body:a,headers:e,reply:g(s,t.provider),request:r});return _(p)??m()},provider:"telnyx"});export{S as createTelnyxReceiver};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{twilioWebhook as d}from"./twilioWebhook-BjM8B0sB.js";import{getHeader as p}from"./getHeader-BF9s-Tgp.js";import{smsReply as m}from"./chatReply-HANpcetF.js";import{h as g,r as u,c as h,b as w}from"./utils-DuaeYOiD.js";const v="X-Twilio-Signature",a="whatsapp:",f=/[\u0000-\u0008\v\f\u000E-\u001F]/gu,R=e=>e.replaceAll(f,"").replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'"),A=e=>{const s=`<?xml version="1.0" encoding="UTF-8"?><Response>${e===void 0?"":`<Message>${R(e)}</Message>`}</Response>`;return new Response(s,{headers:{"content-type":"text/xml; charset=utf-8"},status:200})},T=e=>{const t=e.get("From")??"",s=e.get("To")??"",o=t.startsWith(a)||s.startsWith(a),l=Number.parseInt(e.get("NumMedia")??"0",10),i=[];for(let n=0;n<l;n+=1){const r=e.get(`MediaUrl${String(n)}`);r!==null&&i.push({contentType:e.get(`MediaContentType${String(n)}`)??void 0,url:r})}return{attachments:i.length>0?i:void 0,channel:"sms",conversationId:s.replace(a,""),from:{id:t.replace(a,""),name:e.get("ProfileName")??void 0},id:e.get("MessageSid")??e.get("SmsSid")??"",metadata:{transport:o?"whatsapp":"sms"},provider:"twilio",raw:Object.fromEntries(e),text:e.get("Body")??void 0,timestamp:new Date,type:"message"}},M=e=>({channel:"sms",handle:async t=>{const s=await t.text(),o=g(t.headers);if(o["x-twilio-signature-url"]=e.url??t.url,!await d.verify(s,o,e.authToken)){const c=p(o,v)===void 0?"missing_signature":"invalid_signature";return u(c,t,e.onError)}const i=T(new URLSearchParams(s)),n=await e.onMessage(i,{body:s,headers:o,reply:m(i,e.provider),request:t}),r=w(n);return r!==void 0?r:A(h(n)?.text)},provider:"twilio"});export{M as createTwilioReceiver};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{getHeader as h,tryParseObject as f}from"./getHeader-BF9s-Tgp.js";import{verifyHs256Jwt as l,isJwtTimeValid as u,sha256Hex as v}from"./isJwtTimeValid-CdCG24pW.js";import{smsReply as y}from"./chatReply-HANpcetF.js";import{h as R,r as m,n as c,b as w,a as n,d as E}from"./utils-DuaeYOiD.js";const _="Authorization",d="Bearer ",g=r=>typeof r=="string"?r:n(r?.number),b=r=>{const e=f(r),t=g(e?.from),a=n(e?.message_uuid);if(e===void 0||t===void 0||a===void 0)return;const s=n(e.timestamp);return{channel:"sms",conversationId:g(e.to),from:{id:t},id:a,metadata:{channelType:e.channel,messageType:e.message_type},provider:"vonage",raw:e,text:n(e.text),timestamp:E(s),type:"message"}},V=r=>({channel:"sms",handle:async e=>{const t=await e.text(),a=R(e.headers),s=h(a,_);if(s?.startsWith(d)!==!0)return m("missing_signature",e,r.onError);const o=await l(s.slice(d.length),r.signatureSecret);if(o===void 0||!u(o))return m("invalid_signature",e,r.onError);if(t.length>0&&o.payload_hash!==await v(t))return m("invalid_signature",e,r.onError);const i=b(t);if(i===void 0)return c();const p=await r.onMessage(i,{body:t,headers:a,reply:y(i,r.provider),request:e});return w(p)??c()},provider:"vonage"});export{V as createVonageReceiver};
|
package/dist/packem_shared/{webPushProvider-CUDMDRKw.js → createWebPushProvider-BNu3bPeV.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import K from"./RequiredOptionError-CAuRlc0R.js";import{defineProvider as C}from"./defineProvider-C9q-7iwt.js";import{t as E}from"./credentials-D9KXFoo0.js";import{m as A}from"./http-YFt2eXXy.js";import{g as k}from"./id-FYdbBSpb.js";import{a as T}from"./sms-CT04IxF8.js";import{u as y,f as S,b as
|
|
1
|
+
import K from"./RequiredOptionError-CAuRlc0R.js";import{defineProvider as C}from"./defineProvider-C9q-7iwt.js";import{t as E}from"./credentials-D9KXFoo0.js";import{m as A}from"./http-YFt2eXXy.js";import{g as k}from"./id-FYdbBSpb.js";import{a as T}from"./sms-CT04IxF8.js";import{u as y,f as S,b as m,s as c,r as U,d as f,e as P,g as B}from"./webcrypto-BPm3NDEm.js";const R=2419200,$=i=>{let e=i;if(typeof i=="string")try{e=JSON.parse(i)}catch{return}const t=e;if(!(!t||typeof t.endpoint!="string"||typeof t.keys?.p256dh!="string"||typeof t.keys.auth!="string"))return t},j=async(i,e)=>{const t=S(e),d=m(t.slice(1,33)),a=m(t.slice(33,65));return c().importKey("jwk",{crv:"P-256",d:i,ext:!0,key_ops:["sign"],kty:"EC",x:d,y:a},{name:"ECDSA",namedCurve:"P-256"},!1,["sign"])},x=async(i,e,t)=>{const d=m(y(JSON.stringify({alg:"ES256",typ:"JWT"}))),a=m(y(JSON.stringify({aud:i,exp:Math.floor(Date.now()/1e3)+720*60,sub:e}))),u=`${d}.${a}`,n=await c().sign({hash:"SHA-256",name:"ECDSA"},t,y(u));return`${u}.${m(new Uint8Array(n))}`},D=async(i,e)=>{const t=S(i.keys.p256dh),d=S(i.keys.auth),a=U(16),u=await c().generateKey({name:"ECDH",namedCurve:"P-256"},!0,["deriveBits"]),n=new Uint8Array(await c().exportKey("raw",u.publicKey)),s=await c().importKey("raw",t,{name:"ECDH",namedCurve:"P-256"},!1,[]),g=new Uint8Array(await c().deriveBits({name:"ECDH",public:s},u.privateKey,256)),r=f(y("WebPush: info\0"),t,n),l=await P(d,g,r,32),b=await P(a,l,y("Content-Encoding: aes128gcm\0"),16),v=await P(a,l,y("Content-Encoding: nonce\0"),12),w=await c().importKey("raw",b,{name:"AES-GCM"},!1,["encrypt"]),h=f(e,new Uint8Array([2])),o=new Uint8Array(await c().encrypt({iv:v,name:"AES-GCM"},w,h)),p=f(a,B(4096,4),new Uint8Array([n.length]),n);return f(p,o)},q=C(i=>{const e=i??{};if(!e.vapidPublicKey||!e.vapidPrivateKey||!e.vapidSubject)throw new K("web-push",["vapidPublicKey","vapidPrivateKey","vapidSubject"]);const t=e.ttl??R,d=e.urgency??"normal",a=e.timeout??3e4,u=async(n,s,g)=>{const r=$(n);if(!r)return{error:"Invalid push subscription (expected { endpoint, keys: { p256dh, auth } })",id:n,status:"failed"};const{origin:l}=new URL(r.endpoint),b=await x(l,e.vapidSubject,g),v=JSON.stringify({badge:s.badge,body:s.body,data:s.data,image:s.imageUrl,title:s.title}),w=await D(r,y(v)),h={Authorization:`vapid t=${b},k=${e.vapidPublicKey}`,"Content-Encoding":"aes128gcm","Content-Type":"application/octet-stream",TTL:String(t),Urgency:d},o=await A(r.endpoint,{body:w,headers:h,method:"POST",timeout:a});if(!o.success||!o.data)return{error:o.error instanceof Error?o.error.message:"Request failed",id:n,status:"failed"};const{status:p}=o.data;return p===404||p===410?{error:`Subscription gone (HTTP ${String(p)}) — remove this subscription`,id:n,status:"failed"}:p>=400?{error:`HTTP ${String(p)}: ${typeof o.data.body=="string"?o.data.body:""}`.trim(),id:n,status:"failed"}:{id:n,messageId:k("web-push"),status:"sent"}};return{channel:"push",features:{batchSending:!1,media:!1,richContent:!0},id:"web-push",initialize:()=>{},isAvailable:()=>!!(e.vapidPublicKey&&e.vapidPrivateKey&&e.vapidSubject),options:e,send:async n=>{const s=E(n.to),g=await j(e.vapidPrivateKey,e.vapidPublicKey),r=[];for(const l of s)r.push(await u(l,n,g));return T("push","web-push",r)},validateCredentials:()=>!!(e.vapidPublicKey&&e.vapidPrivateKey&&e.vapidSubject)}});export{q as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{c as s,h as a,t as c}from"./webcrypto-BPm3NDEm.js";const r=300,h=async(t,n,e)=>c(await a(t,n,e)),l=async(t,n,e)=>s(await a(t,n,e)),f=(t,n)=>{if(t.length!==n.length)return!1;let e=0;for(let o=0;o<t.length;o+=1)e|=(t.codePointAt(o)??0)^(n.codePointAt(o)??0);return e===0},m=(t,n)=>{if(t===void 0)return!1;const e=Number.parseInt(t,10);if(Number.isNaN(e))return!1;const o=Math.floor(Date.now()/1e3);return Math.abs(o-e)<=n};export{r as REPLAY_WINDOW_SECONDS,l as hmacBase64,h as hmacHex,m as isWithinReplayWindow,f as timingSafeEqual};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as f,a as c,u,b as m,h as d,f as p}from"./webcrypto-BPm3NDEm.js";import{timingSafeEqual as h}from"./hmacBase64-ocSXhrdQ.js";const s=e=>{try{const t=JSON.parse(new TextDecoder().decode(p(e)));return typeof t=="object"&&t!==null&&!Array.isArray(t)?t:void 0}catch{return}},x=async e=>f(await c(u(e))),y=async(e,t)=>{const r=e.split(".");if(r.length!==3)return;const[n,a,o]=r;if(s(n)?.alg!=="HS256")return;const i=m(await d(t,`${n}.${a}`,"SHA-256"));if(h(i,o))return s(a)},S=(e,t=5)=>{const r=Math.floor(Date.now()/1e3);return!(typeof e.exp=="number"&&r>e.exp+t||typeof e.nbf=="number"&&r+t<e.nbf)};export{S as isJwtTimeValid,x as sha256Hex,y as verifyHs256Jwt};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isWithinReplayWindow as o,REPLAY_WINDOW_SECONDS as c,hmacHex as d,timingSafeEqual as l}from"./hmacBase64-
|
|
1
|
+
import{isWithinReplayWindow as o,REPLAY_WINDOW_SECONDS as c,hmacHex as d,timingSafeEqual as l}from"./hmacBase64-ocSXhrdQ.js";import{getHeader as r,tryParseObject as p}from"./getHeader-BF9s-Tgp.js";const f="X-Slack-Signature",m="X-Slack-Request-Timestamp",u={parse:s=>{const e=p(s);if(e===void 0)return;const t=e.event??e,i=typeof t.channel=="string"?t.channel:void 0,n=typeof t.ts=="string"?t.ts:"";return{channel:"chat",messageId:typeof e.event_id=="string"?e.event_id:n,provider:"slack",recipient:i,timestamp:new Date,type:"delivered"}},verify:async(s,e,t)=>{if(t.trim()==="")return!1;const i=r(e,f),n=r(e,m);if(i===void 0||n===void 0||!o(n,c))return!1;const a=await d(t,`v0:${n}:${s}`,"SHA-256");return l(`v0=${a}`,i)}};export{u as slackWebhook};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{f as T,s as p,u as g}from"./webcrypto-BPm3NDEm.js";import{tryParseObject as v}from"./getHeader-BF9s-Tgp.js";const S=/-----BEGIN CERTIFICATE-----/u,E=/-----END CERTIFICATE-----/u,y=/\s+/gu,f=48,h=160,c=(t,e)=>{if(e+2>t.length)throw new RangeError("truncated DER");const r=t[e]??0,s=t[e+1]??0;if(s<128){const o=e+2;if(o+s>t.length)throw new RangeError("truncated DER");return{end:o+s,length:s,tag:r,valueOffset:o}}const n=s&127;if(n===0||n>4)throw new RangeError("unsupported DER length");let i=0;for(let o=0;o<n;o+=1)i=i*256+(t[e+2+o]??0);const a=e+2+n;if(a+i>t.length)throw new RangeError("truncated DER");return{end:a+i,length:i,tag:r,valueOffset:a}},m=t=>{const e=t.replace(S,"").replace(E,"").replaceAll(y,"");if(e.length!==0)try{return T(e)}catch{return}},C=t=>{const e=m(t);if(e!==void 0)try{const r=c(e,0);if(r.tag!==f)return;const s=c(e,r.valueOffset);if(s.tag!==f)return;let n=s.valueOffset;const i=c(e,n);i.tag===h&&(n=i.end);for(let l=0;l<5;l+=1)n=c(e,n).end;const a=c(e,n);if(a.tag!==f)return;const o=new Uint8Array(new ArrayBuffer(a.end-n));return o.set(e.subarray(n,a.end)),o}catch{return}},A=/^sns\.[a-z0-9-]{3,}\.amazonaws\.com(?:\.cn)?$/iu,I=["Message","MessageId","Subject","SubscribeURL","Timestamp","TopicArn","Type"],R=["Message","MessageId","SubscribeURL","Timestamp","Token","TopicArn","Type"],u=new Map,w=64,M=5e3,b=3600*1e3,d=t=>v(t),_=t=>{const e=t.SigningCertURL??t.SigningCertUrl;return typeof e=="string"?e:void 0},O=t=>{try{const e=new URL(t);return e.protocol==="https:"&&e.pathname.endsWith(".pem")&&A.test(e.hostname)}catch{return!1}},U=(t,e)=>{let r="";for(const s of e){const n=t[s];typeof n=="string"&&(r+=`${s}
|
|
2
|
+
${n}
|
|
3
|
+
`)}return r},k=async t=>{const e=u.get(t);if(e!==void 0)return e;const r=await globalThis.fetch(t,{signal:AbortSignal.timeout(M)});if(!r.ok)return;const s=C(await r.text());if(s!==void 0){if(u.size>=w){const n=u.keys().next().value;n!==void 0&&u.delete(n)}u.set(t,s)}return s},N=async t=>{const e=_(t),r=t.SignatureVersion;if(e===void 0||typeof t.Signature!="string"||typeof t.Type!="string"||r!=="1"&&r!=="2"||!O(e))return!1;const s=Date.parse(t.Timestamp??"");if(Number.isNaN(s)||Math.abs(Date.now()-s)>b)return!1;const n=t.Type==="Notification"?I:R,i=U(t,n);try{const a=await k(e);if(a===void 0)return!1;const o=await p().importKey("spki",a,{hash:r==="1"?"SHA-1":"SHA-256",name:"RSASSA-PKCS1-v1_5"},!1,["verify"]);return await p().verify("RSASSA-PKCS1-v1_5",o,T(t.Signature),g(i))}catch{return!1}},L={parse:t=>{const e=d(t);if(e?.Type===void 0)return;const r={type:e.Type};return e.Type==="SubscriptionConfirmation"||e.Type==="UnsubscribeConfirmation"?r.subscribeUrl=e.SubscribeURL:e.Message!==void 0&&(r.message=e.Message),{messageId:e.MessageId??"",metadata:r,provider:"sns",timestamp:e.Timestamp===void 0?new Date:new Date(e.Timestamp),type:"delivered"}},verify:async t=>{const e=d(t);return e===void 0?!1:N(e)}};export{L as snsWebhook,N as verifySnsMessage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isWithinReplayWindow as m,REPLAY_WINDOW_SECONDS as w,hmacBase64 as E,hmacHex as h,timingSafeEqual as f}from"./hmacBase64-
|
|
1
|
+
import{isWithinReplayWindow as m,REPLAY_WINDOW_SECONDS as w,hmacBase64 as E,hmacHex as h,timingSafeEqual as f}from"./hmacBase64-ocSXhrdQ.js";import{getHeader as r,tryParseObject as v}from"./getHeader-BF9s-Tgp.js";const g="webhook-id",b="webhook-timestamp",A="webhook-signature",H=new Set(["bounced","clicked","delivered","failed","interacted","queued","read","sent"]),S=i=>{const e=atob(i),t=new Uint8Array(e.length);for(let n=0;n<e.length;n+=1)t[n]=e.codePointAt(n)??0;return t},D={parse:i=>{const e=v(i);if(e===void 0)return;const t=typeof e.type=="string"?e.type:void 0,o=t!==void 0&&H.has(t)?t:"delivered";return{messageId:typeof e.id=="string"?e.id:"",provider:"standard-webhooks",recipient:typeof e.recipient=="string"?e.recipient:void 0,timestamp:new Date,type:o}},verify:async(i,e,t)=>{if(t.trim()==="")return!1;const n=r(e,g),o=r(e,b),a=r(e,A);if(n===void 0||o===void 0||a===void 0||!m(o,w))return!1;const d=t.startsWith("whsec_")?S(t.slice(6)):t,c=`${n}.${o}.${i}`,l=await E(d,c,"SHA-256"),u=await h(d,c,"SHA-256"),y=a.split(" ");for(const s of y){const p=s.includes(",")?s.split(",")[1]??"":s;if(f(l,p)||f(u,p))return!0}return!1}};export{D as standardWebhook};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{hmacBase64 as u,timingSafeEqual as l}from"./hmacBase64-
|
|
1
|
+
import{hmacBase64 as u,timingSafeEqual as l}from"./hmacBase64-ocSXhrdQ.js";import{getHeader as a}from"./getHeader-BF9s-Tgp.js";const m="X-Twilio-Signature",S={delivered:"delivered",failed:"failed",queued:"queued",sending:"queued",sent:"sent",undelivered:"failed"},f=(o,e)=>{const s=Object.keys(e).toSorted();let t=o;for(const r of s)t+=`${r}${e[r]??""}`;return t},i=o=>{const e={};return new URLSearchParams(o).forEach((t,r)=>{e[r]=t}),e},g={parse:o=>{const e=i(o),s=e.MessageStatus??e.SmsStatus??e.CallStatus;if(s===void 0)return;const t=S[s.toLowerCase()];return t===void 0?void 0:{channel:"sms",messageId:e.MessageSid??e.SmsSid??e.CallSid??"",provider:"twilio",recipient:e.To,timestamp:new Date,type:t}},verify:async(o,e,s)=>{if(s.trim()==="")return!1;const t=a(e,m),r=a(e,"x-twilio-signature-url");if(t===void 0||r===void 0)return!1;const n=i(o),d=f(r,n),c=await u(s,d,"SHA-1");return l(c,t)}};export{g as twilioWebhook};
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { C as ChannelType, a as NotificationResult, R as Result, M as MaybePromise } from "./types.d-C7l7qdMG.js";
|
|
2
|
+
import { W as WebhookHeaders } from "./types.d-BXfGenMH.js";
|
|
3
|
+
/**
|
|
4
|
+
* The normalised kind of an inbound payload, abstracting over each provider's own taxonomy:
|
|
5
|
+
*
|
|
6
|
+
* - `"message"` — a user-authored message (chat message, SMS, WhatsApp).
|
|
7
|
+
* - `"command"` — a slash / bot command (Slack slash command, Discord application command).
|
|
8
|
+
* - `"callback"` — a UI interaction (Slack block action, Discord component, Telegram callback query).
|
|
9
|
+
* - `"event"` — a provider lifecycle event that is not a direct user message (Slack Events API envelope).
|
|
10
|
+
* - `"unknown"` — a recognised payload that does not map to a more specific kind.
|
|
11
|
+
*/
|
|
12
|
+
type InboundMessageType = "callback" | "command" | "event" | "message" | "unknown";
|
|
13
|
+
/**
|
|
14
|
+
* A participant in an inbound conversation (the sender, or the bot/recipient).
|
|
15
|
+
*/
|
|
16
|
+
interface InboundParticipant {
|
|
17
|
+
/** Provider-native id (Slack user id, Discord user id, phone number, ...). */
|
|
18
|
+
id: string;
|
|
19
|
+
/** Whether the participant is a bot / automated account, when the provider reports it. */
|
|
20
|
+
isBot?: boolean;
|
|
21
|
+
/** Human-readable display name, when available. */
|
|
22
|
+
name?: string;
|
|
23
|
+
/** Handle / username, when available. */
|
|
24
|
+
username?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A file or media attachment carried by an inbound message.
|
|
28
|
+
*/
|
|
29
|
+
interface InboundAttachment {
|
|
30
|
+
/** MIME type, when reported. */
|
|
31
|
+
contentType?: string;
|
|
32
|
+
/** Original filename, when reported. */
|
|
33
|
+
filename?: string;
|
|
34
|
+
/** Provider-native attachment id, when reported. */
|
|
35
|
+
id?: string;
|
|
36
|
+
/** Size in bytes, when reported. */
|
|
37
|
+
size?: number;
|
|
38
|
+
/** URL to fetch the attachment (may require provider auth). */
|
|
39
|
+
url?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A normalised inbound message. Every channel receiver maps its provider-native payload
|
|
43
|
+
* into this shape so a single handler can process messages from any channel — the
|
|
44
|
+
* counterpart to the outbound {@link ../types.NotificationPayload NotificationPayload}.
|
|
45
|
+
*/
|
|
46
|
+
interface InboundMessage {
|
|
47
|
+
/** File / media attachments carried by the message. */
|
|
48
|
+
attachments?: InboundAttachment[];
|
|
49
|
+
/** The logical channel the message arrived on, mirroring {@link ChannelType}. */
|
|
50
|
+
channel: ChannelType;
|
|
51
|
+
/** Parsed command detail when {@link InboundMessage.type} is `"command"`. */
|
|
52
|
+
command?: {
|
|
53
|
+
/** Positional / free-text arguments following the command name. */
|
|
54
|
+
args?: string;
|
|
55
|
+
/** The command name, without any leading `/`. */
|
|
56
|
+
name: string;
|
|
57
|
+
};
|
|
58
|
+
/** Conversation / channel / room / chat id the message belongs to. */
|
|
59
|
+
conversationId?: string;
|
|
60
|
+
/** The sender. */
|
|
61
|
+
from: InboundParticipant;
|
|
62
|
+
/** Provider message / event id. */
|
|
63
|
+
id: string;
|
|
64
|
+
/** Provider extras not covered by a normalised field (team id, guild id, app id, ...). */
|
|
65
|
+
metadata?: Record<string, unknown>;
|
|
66
|
+
/** The provider id: `"slack"`, `"discord"`, `"telegram"`, `"twilio"`. */
|
|
67
|
+
provider: string;
|
|
68
|
+
/** The original, unmodified provider payload. */
|
|
69
|
+
raw: unknown;
|
|
70
|
+
/** Plain-text content, when the payload carries text. */
|
|
71
|
+
text?: string;
|
|
72
|
+
/** Thread / reply anchor, so replies can be threaded where the channel supports it. */
|
|
73
|
+
threadId?: string;
|
|
74
|
+
/** Provider event timestamp (falls back to receive time when the payload omits one). */
|
|
75
|
+
timestamp: Date;
|
|
76
|
+
/** The kind of inbound payload. */
|
|
77
|
+
type: InboundMessageType;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* A normalised reply a handler can return to be delivered in the webhook's HTTP response,
|
|
81
|
+
* where the channel supports synchronous replies (Slack slash commands, Discord
|
|
82
|
+
* interactions, Telegram webhook method calls, Twilio TwiML). For channels that require an
|
|
83
|
+
* out-of-band reply (Slack Events API), send through the matching outbound provider instead.
|
|
84
|
+
*/
|
|
85
|
+
interface InboundReply {
|
|
86
|
+
/** Provider-native rich content (Slack blocks, Discord embeds, ...). */
|
|
87
|
+
blocks?: unknown;
|
|
88
|
+
/** Provider-specific fields merged into the serialised response (escape hatch). */
|
|
89
|
+
raw?: Record<string, unknown>;
|
|
90
|
+
/** Plain-text reply body. */
|
|
91
|
+
text?: string;
|
|
92
|
+
/** Reply within the given thread, when the channel supports threading. */
|
|
93
|
+
threadId?: string;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* What {@link InboundContext.reply} accepts: a normalised {@link InboundReply} or, as a
|
|
97
|
+
* shorthand, a plain string treated as `{ text }`.
|
|
98
|
+
*/
|
|
99
|
+
type InboundReplyInput = InboundReply | string;
|
|
100
|
+
/**
|
|
101
|
+
* Sends a reply back to the originating conversation through the outbound provider configured
|
|
102
|
+
* on the receiver — the out-of-band counterpart to returning an {@link InboundReply} from the
|
|
103
|
+
* handler. Resolves to the provider's send {@link Result}; rejects when no provider was
|
|
104
|
+
* configured. The reply target (recipient, thread) is derived from the inbound message.
|
|
105
|
+
* @param reply The reply content.
|
|
106
|
+
* @returns The provider's send result.
|
|
107
|
+
*/
|
|
108
|
+
type InboundReplyFunction = (reply: InboundReplyInput) => Promise<Result<NotificationResult>>;
|
|
109
|
+
/**
|
|
110
|
+
* Context passed to an {@link InboundHandler} alongside the parsed message: the verified raw
|
|
111
|
+
* request for handlers that need provider-native fields, plus {@link InboundContext.reply} to
|
|
112
|
+
* answer in a single call.
|
|
113
|
+
*/
|
|
114
|
+
interface InboundContext {
|
|
115
|
+
/** The raw request body text, already read and signature-verified. */
|
|
116
|
+
body: string;
|
|
117
|
+
/** Case-insensitive request headers. */
|
|
118
|
+
headers: WebhookHeaders;
|
|
119
|
+
/**
|
|
120
|
+
* Sends a reply to the originating conversation through the receiver's outbound provider.
|
|
121
|
+
* Rejects if no `provider` was passed to the receiver factory.
|
|
122
|
+
*/
|
|
123
|
+
reply: InboundReplyFunction;
|
|
124
|
+
/** The originating web-standard {@link Request}. */
|
|
125
|
+
request: Request;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* What an {@link InboundHandler} may return: a normalised {@link InboundReply} (serialised
|
|
129
|
+
* by the channel), a raw web-standard {@link Response} (passed through unchanged), or
|
|
130
|
+
* nothing (the channel acknowledges the delivery with an empty success response).
|
|
131
|
+
*/
|
|
132
|
+
type InboundResponse = InboundReply | Response | void;
|
|
133
|
+
/**
|
|
134
|
+
* Handles a verified, parsed inbound message. This is the seam an AI agent plugs into:
|
|
135
|
+
* receive a normalised message, optionally return a reply.
|
|
136
|
+
* @param message The normalised inbound message.
|
|
137
|
+
* @param context The request context.
|
|
138
|
+
* @returns The reply to deliver, a raw response, or nothing.
|
|
139
|
+
*/
|
|
140
|
+
type InboundHandler = (message: InboundMessage, context: InboundContext) => MaybePromise<InboundResponse>;
|
|
141
|
+
/**
|
|
142
|
+
* The reason an inbound request was rejected before it reached the handler.
|
|
143
|
+
*/
|
|
144
|
+
type InboundErrorReason = "invalid_body" | "invalid_signature" | "missing_signature";
|
|
145
|
+
/**
|
|
146
|
+
* Options shared by every channel receiver factory.
|
|
147
|
+
*/
|
|
148
|
+
interface ReceiverOptions {
|
|
149
|
+
/**
|
|
150
|
+
* Called when a request fails verification or cannot be parsed, before the handler runs.
|
|
151
|
+
* Return a {@link Response} to override the default (`401` for signature failures,
|
|
152
|
+
* `400` for malformed bodies); return nothing to keep the default.
|
|
153
|
+
* @param reason Why the request was rejected.
|
|
154
|
+
* @param request The originating request.
|
|
155
|
+
*/
|
|
156
|
+
onError?: (reason: InboundErrorReason, request: Request) => MaybePromise<Response | void>;
|
|
157
|
+
/** Called for every verified, parsed inbound message. */
|
|
158
|
+
onMessage: InboundHandler;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* A channel receiver: the inbound counterpart to an outbound
|
|
162
|
+
* {@link ../providers/provider.Provider Provider}. It verifies, parses and dispatches
|
|
163
|
+
* inbound webhook requests through a single web-standard entry point.
|
|
164
|
+
*/
|
|
165
|
+
interface Receiver {
|
|
166
|
+
/** The logical channel this receiver handles. */
|
|
167
|
+
readonly channel: ChannelType;
|
|
168
|
+
/**
|
|
169
|
+
* Verify, parse and dispatch an inbound webhook request, returning the HTTP response to
|
|
170
|
+
* send back to the provider. Framework-agnostic: pass any web-standard {@link Request}.
|
|
171
|
+
* @param request The inbound webhook request.
|
|
172
|
+
* @returns The response to return to the provider.
|
|
173
|
+
*/
|
|
174
|
+
handle: (request: Request) => Promise<Response>;
|
|
175
|
+
/** The provider id this receiver handles. */
|
|
176
|
+
readonly provider: string;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* @deprecated Renamed to {@link Receiver}. Kept as an alias for one major; removed in the next.
|
|
180
|
+
*/
|
|
181
|
+
type InboundChannel = Receiver;
|
|
182
|
+
export { InboundMessage as I, Receiver as R, InboundReplyFunction as a, InboundReplyInput as b, InboundReply as c, InboundAttachment as d, InboundChannel as e, InboundContext as f, InboundErrorReason as g, InboundHandler as h, InboundMessageType as i, InboundParticipant as j, InboundResponse as k, ReceiverOptions as l };
|