@interactive-inc/flume 0.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.
@@ -0,0 +1,69 @@
1
+ import { z } from "zod/v4";
2
+ //#region lib/schema.ts
3
+ const FlumeGatewayMessageSchema = z.object({
4
+ op: z.number(),
5
+ d: z.record(z.string(), z.unknown()).nullable(),
6
+ s: z.number().nullable(),
7
+ t: z.string().nullable()
8
+ });
9
+ const FlumeSlackEnvelopeSchema = z.object({
10
+ envelope_id: z.string(),
11
+ type: z.string(),
12
+ payload: z.record(z.string(), z.unknown()),
13
+ accepts_response_payload: z.boolean().optional(),
14
+ retry_attempt: z.number().optional(),
15
+ retry_reason: z.string().optional()
16
+ });
17
+ const FlumeSlackConnectionResponseSchema = z.object({
18
+ ok: z.boolean(),
19
+ url: z.string().optional(),
20
+ error: z.string().optional()
21
+ });
22
+ const FlumeGitHubNotificationSchema = z.object({
23
+ id: z.string(),
24
+ reason: z.string(),
25
+ unread: z.boolean(),
26
+ updated_at: z.string(),
27
+ subject: z.object({
28
+ title: z.string(),
29
+ url: z.string().nullable(),
30
+ type: z.string()
31
+ }),
32
+ repository: z.object({ full_name: z.string() })
33
+ });
34
+ //#endregion
35
+ //#region lib/logger.ts
36
+ var FlumeLogger = class {
37
+ props;
38
+ constructor(props) {
39
+ this.props = props;
40
+ Object.freeze(this);
41
+ }
42
+ debug(entry) {
43
+ this.emit("debug", entry);
44
+ }
45
+ info(entry) {
46
+ this.emit("info", entry);
47
+ }
48
+ warn(entry) {
49
+ this.emit("warn", entry);
50
+ }
51
+ error(entry) {
52
+ this.emit("error", entry);
53
+ }
54
+ emit(level, input) {
55
+ if (!this.props.handler) return;
56
+ const log = {
57
+ level,
58
+ source: this.props.source,
59
+ action: input.action,
60
+ message: input.message,
61
+ timestamp: this.props.deps.now(),
62
+ error: input.error,
63
+ detail: input.detail
64
+ };
65
+ this.props.handler(log);
66
+ }
67
+ };
68
+ //#endregion
69
+ export { FlumeSlackEnvelopeSchema as a, FlumeSlackConnectionResponseSchema as i, FlumeGatewayMessageSchema as n, FlumeGitHubNotificationSchema as r, FlumeLogger as t };
@@ -0,0 +1,81 @@
1
+ //#region lib/errors/connection-error.ts
2
+ var FlumeConnectionError = class extends Error {
3
+ constructor(message) {
4
+ super(message);
5
+ this.name = "FlumeConnectionError";
6
+ Object.freeze(this);
7
+ }
8
+ };
9
+ //#endregion
10
+ //#region lib/errors/parse-error.ts
11
+ var FlumeParseError = class extends Error {
12
+ constructor(message) {
13
+ super(message);
14
+ this.name = "FlumeParseError";
15
+ Object.freeze(this);
16
+ }
17
+ };
18
+ //#endregion
19
+ //#region lib/reconnect-config.ts
20
+ const DEFAULTS = {
21
+ maxAttempts: Infinity,
22
+ baseDelay: 1e3,
23
+ maxDelay: 3e4
24
+ };
25
+ function resolveFlumeReconnectConfig(input) {
26
+ if (input === false || input === void 0) return null;
27
+ if (input === true) return { ...DEFAULTS };
28
+ return {
29
+ ...DEFAULTS,
30
+ ...input
31
+ };
32
+ }
33
+ //#endregion
34
+ //#region lib/reconnector.ts
35
+ var FlumeReconnector = class {
36
+ props;
37
+ attempt = 0;
38
+ aborted = false;
39
+ timer = null;
40
+ constructor(props) {
41
+ this.props = props;
42
+ }
43
+ schedule(fn) {
44
+ if (this.aborted) return 0;
45
+ if (this.attempt >= this.props.maxAttempts) return -1;
46
+ const delay = this.nextDelay();
47
+ this.timer = this.props.deps.setTimeout(fn, delay);
48
+ return delay;
49
+ }
50
+ reset() {
51
+ this.attempt = 0;
52
+ }
53
+ cancel() {
54
+ this.aborted = true;
55
+ if (this.timer !== null) {
56
+ this.props.deps.clearTimeout(this.timer);
57
+ this.timer = null;
58
+ }
59
+ }
60
+ nextDelay() {
61
+ const jitter = Math.min(this.props.baseDelay * 2 ** this.attempt, this.props.maxDelay) * (.5 + this.props.deps.random() * .5);
62
+ this.attempt++;
63
+ return jitter;
64
+ }
65
+ };
66
+ //#endregion
67
+ //#region lib/utils/is-record.ts
68
+ function isRecord(value) {
69
+ return typeof value === "object" && value !== null;
70
+ }
71
+ //#endregion
72
+ //#region lib/utils/safe-json-parse.ts
73
+ function safeJsonParse(raw) {
74
+ try {
75
+ return JSON.parse(raw);
76
+ } catch {
77
+ return null;
78
+ }
79
+ }
80
+ //#endregion
81
+ export { FlumeParseError as a, resolveFlumeReconnectConfig as i, isRecord as n, FlumeConnectionError as o, FlumeReconnector as r, safeJsonParse as t };
@@ -0,0 +1,400 @@
1
+ import { a as FlumeSlackEnvelopeSchema, i as FlumeSlackConnectionResponseSchema, t as FlumeLogger } from "./logger-B9E8zvgj.js";
2
+ import { a as FlumeParseError, i as resolveFlumeReconnectConfig, n as isRecord, o as FlumeConnectionError, r as FlumeReconnector, t as safeJsonParse } from "./safe-json-parse-D8t_4Vm_.js";
3
+ //#region lib/errors/http-error.ts
4
+ var FlumeHttpError = class extends Error {
5
+ status;
6
+ constructor(props) {
7
+ super(props.message);
8
+ this.name = "FlumeHttpError";
9
+ this.status = props.status;
10
+ Object.freeze(this);
11
+ }
12
+ };
13
+ //#endregion
14
+ //#region lib/slack/obtain-slack-url.ts
15
+ async function obtainSlackUrl(props) {
16
+ const log = new FlumeLogger({
17
+ source: "slack.url",
18
+ handler: props.onLog,
19
+ deps: props.deps
20
+ });
21
+ const url = "https://slack.com/api/apps.connections.open";
22
+ log.debug({
23
+ action: "http.request",
24
+ message: `POST ${url}`
25
+ });
26
+ const response = await safeFetch(props, url, log);
27
+ if (response instanceof FlumeHttpError) return response;
28
+ log.debug({
29
+ action: "http.response",
30
+ message: `POST ${response.status}`,
31
+ detail: {
32
+ status: response.status,
33
+ url
34
+ }
35
+ });
36
+ const raw = await response.json();
37
+ log.debug({
38
+ action: "http.body",
39
+ message: "apps.connections.open response",
40
+ detail: {
41
+ ok: isOk(raw),
42
+ error: errorField(raw)
43
+ }
44
+ });
45
+ const parsed = FlumeSlackConnectionResponseSchema.safeParse(raw);
46
+ if (!parsed.success) {
47
+ log.warn({
48
+ action: "parse.fail",
49
+ message: "apps.connections.open: invalid response shape",
50
+ detail: { issues: parsed.error.issues.map((i) => ({
51
+ path: i.path,
52
+ message: i.message
53
+ })) }
54
+ });
55
+ return new FlumeHttpError({
56
+ message: "apps.connections.open: invalid response shape",
57
+ status: response.status
58
+ });
59
+ }
60
+ if (!parsed.data.ok || !parsed.data.url) {
61
+ log.warn({
62
+ action: "api.fail",
63
+ message: `apps.connections.open failed: ${parsed.data.error ?? "no url"}`
64
+ });
65
+ return new FlumeHttpError({
66
+ message: `apps.connections.open failed: ${parsed.data.error ?? "no url"}`,
67
+ status: response.status
68
+ });
69
+ }
70
+ log.info({
71
+ action: "url.obtained",
72
+ message: "WSS URL obtained"
73
+ });
74
+ return parsed.data.url;
75
+ }
76
+ function isOk(raw) {
77
+ return raw?.ok;
78
+ }
79
+ function errorField(raw) {
80
+ return raw?.error;
81
+ }
82
+ async function safeFetch(props, url, log) {
83
+ try {
84
+ return await props.deps.fetch(url, {
85
+ method: "POST",
86
+ headers: { Authorization: `Bearer ${props.appToken}` }
87
+ });
88
+ } catch (error) {
89
+ const err = error instanceof Error ? error : new Error(String(error));
90
+ log.error({
91
+ action: "http.error",
92
+ message: `network error: ${err.message}`,
93
+ error: err
94
+ });
95
+ return new FlumeHttpError({
96
+ message: err.message,
97
+ status: 0
98
+ });
99
+ }
100
+ }
101
+ //#endregion
102
+ //#region lib/slack/slack-socket-mode.ts
103
+ function framePreview(raw) {
104
+ return raw.length > 200 ? `${raw.slice(0, 200)}... (${raw.length} bytes)` : raw;
105
+ }
106
+ var FlumeSlackSocketMode = class {
107
+ props;
108
+ log;
109
+ ws = null;
110
+ stopped = false;
111
+ pendingResolve = null;
112
+ pendingResolved = false;
113
+ constructor(props) {
114
+ this.props = props;
115
+ this.log = new FlumeLogger({
116
+ source: "slack.socket-mode",
117
+ handler: props.onLog,
118
+ deps: props.deps
119
+ });
120
+ }
121
+ async connect() {
122
+ this.log.info({
123
+ action: "connect.start",
124
+ message: "opening WebSocket connection"
125
+ });
126
+ const url = await obtainSlackUrl({
127
+ appToken: this.props.appToken,
128
+ onLog: this.props.onLog,
129
+ deps: this.props.deps
130
+ });
131
+ if (url instanceof FlumeHttpError) {
132
+ this.log.error({
133
+ action: "http.error",
134
+ message: url.message,
135
+ error: url
136
+ });
137
+ return url;
138
+ }
139
+ this.log.info({
140
+ action: "url.obtained",
141
+ message: "WebSocket URL obtained"
142
+ });
143
+ return this.openSocket(url);
144
+ }
145
+ disconnect() {
146
+ this.log.info({
147
+ action: "disconnect",
148
+ message: "stopping socket mode"
149
+ });
150
+ this.stopped = true;
151
+ if (this.ws) {
152
+ this.ws.close();
153
+ this.ws = null;
154
+ }
155
+ }
156
+ isConnected() {
157
+ return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
158
+ }
159
+ openSocket(url) {
160
+ this.pendingResolved = false;
161
+ return new Promise((resolve) => {
162
+ this.pendingResolve = resolve;
163
+ const socket = new this.props.deps.WebSocket(url);
164
+ this.ws = socket;
165
+ socket.addEventListener("message", (ev) => this.onMessage(String(ev.data), socket));
166
+ socket.addEventListener("close", (ev) => this.onClose(ev));
167
+ socket.addEventListener("error", () => this.onError());
168
+ });
169
+ }
170
+ completeConnect(error) {
171
+ if (this.pendingResolved || !this.pendingResolve) return;
172
+ this.pendingResolved = true;
173
+ this.pendingResolve(error);
174
+ }
175
+ onMessage(raw, socket) {
176
+ this.log.debug({
177
+ action: "ws.recv",
178
+ message: framePreview(raw)
179
+ });
180
+ const json = safeJsonParse(raw);
181
+ if (!isRecord(json)) {
182
+ this.log.error({
183
+ action: "ws.parse-error",
184
+ message: "invalid JSON",
185
+ error: new FlumeParseError(raw.slice(0, 200))
186
+ });
187
+ return;
188
+ }
189
+ if (json.type === "hello") {
190
+ this.log.info({
191
+ action: "ws.hello",
192
+ message: "connection ready"
193
+ });
194
+ this.props.onConnected();
195
+ this.completeConnect(null);
196
+ return;
197
+ }
198
+ if (json.type === "disconnect") {
199
+ const reason = typeof json.reason === "string" ? json.reason : "unknown";
200
+ this.log.info({
201
+ action: "ws.disconnect-requested",
202
+ message: `reason=${reason}`,
203
+ detail: { reason }
204
+ });
205
+ socket.close();
206
+ return;
207
+ }
208
+ if (typeof json.envelope_id === "string") {
209
+ this.log.debug({
210
+ action: "ws.ack",
211
+ message: `envelope_id=${json.envelope_id}`
212
+ });
213
+ const ack = JSON.stringify({ envelope_id: json.envelope_id });
214
+ socket.send(ack);
215
+ this.log.debug({
216
+ action: "ws.send",
217
+ message: framePreview(ack)
218
+ });
219
+ }
220
+ const envelope = FlumeSlackEnvelopeSchema.safeParse(json);
221
+ if (envelope.success) {
222
+ this.log.debug({
223
+ action: "envelope.recv",
224
+ message: `type=${envelope.data.type} envelope_id=${envelope.data.envelope_id}`,
225
+ detail: {
226
+ type: envelope.data.type,
227
+ envelopeId: envelope.data.envelope_id
228
+ }
229
+ });
230
+ this.props.onMessage(envelope.data);
231
+ return;
232
+ }
233
+ this.log.warn({
234
+ action: "envelope.parse-fail",
235
+ message: "unrecognised envelope shape, dropping",
236
+ detail: {
237
+ type: typeof json.type === "string" ? json.type : "unknown",
238
+ issues: envelope.error.issues.map((i) => ({
239
+ path: i.path,
240
+ message: i.message
241
+ }))
242
+ }
243
+ });
244
+ }
245
+ onClose(ev) {
246
+ this.log.info({
247
+ action: "ws.close",
248
+ message: `code=${ev.code} reason=${ev.reason || "none"}`
249
+ });
250
+ this.ws = null;
251
+ this.props.onDisconnected();
252
+ this.completeConnect(new FlumeConnectionError(`WebSocket closed before hello (code=${ev.code})`));
253
+ }
254
+ onError() {
255
+ this.log.error({
256
+ action: "ws.error",
257
+ message: "WebSocket error event"
258
+ });
259
+ this.completeConnect(new FlumeConnectionError("WebSocket connection error"));
260
+ }
261
+ };
262
+ //#endregion
263
+ //#region lib/slack/slack-source.ts
264
+ var FlumeSlackSource = class FlumeSlackSource {
265
+ options;
266
+ socket = null;
267
+ reconnector = null;
268
+ handler = null;
269
+ currentStatus = "disconnected";
270
+ log;
271
+ deps;
272
+ constructor(options) {
273
+ this.options = options;
274
+ this.deps = options.deps;
275
+ this.log = new FlumeLogger({
276
+ source: "slack",
277
+ handler: options.onLog,
278
+ deps: this.deps
279
+ });
280
+ const rc = resolveFlumeReconnectConfig(options.reconnect);
281
+ if (rc) this.reconnector = new FlumeReconnector({
282
+ ...rc,
283
+ deps: this.deps
284
+ });
285
+ }
286
+ async start(handler) {
287
+ if (this.options.signal?.aborted) return;
288
+ this.options.signal?.addEventListener("abort", () => this.stop(), { once: true });
289
+ this.handler = handler;
290
+ this.log.info({
291
+ action: "start",
292
+ message: "starting Slack source"
293
+ });
294
+ await this.connectInternal();
295
+ }
296
+ async stop() {
297
+ this.log.info({
298
+ action: "stop",
299
+ message: "stopping Slack source"
300
+ });
301
+ if (this.reconnector && !this.reconnector.aborted) this.log.debug({
302
+ action: "reconnect.cancel",
303
+ message: "aborting reconnector"
304
+ });
305
+ this.reconnector?.cancel();
306
+ this.socket?.disconnect();
307
+ this.socket = null;
308
+ this.handler = null;
309
+ this.setStatus("disconnected");
310
+ }
311
+ status() {
312
+ return this.currentStatus;
313
+ }
314
+ async connectInternal() {
315
+ this.setStatus("connecting");
316
+ this.socket = new FlumeSlackSocketMode({
317
+ appToken: this.options.appToken,
318
+ onLog: this.options.onLog,
319
+ deps: this.deps,
320
+ onMessage: (envelope) => this.handleMessage(envelope),
321
+ onConnected: () => {
322
+ if (this.reconnector && this.reconnector.attempt > 0) this.log.info({
323
+ action: "reconnect.reset",
324
+ message: `cleared ${this.reconnector.attempt} attempts`
325
+ });
326
+ this.reconnector?.reset();
327
+ this.setStatus("connected");
328
+ },
329
+ onDisconnected: () => {
330
+ if (!this.socket?.stopped) this.scheduleReconnect();
331
+ }
332
+ });
333
+ const error = await this.socket.connect();
334
+ if (error instanceof Error) {
335
+ this.log.error({
336
+ action: "connect.failed",
337
+ message: error.message,
338
+ error
339
+ });
340
+ this.scheduleReconnect();
341
+ }
342
+ }
343
+ handleMessage(envelope) {
344
+ const event = {
345
+ source: "slack",
346
+ type: envelope.type,
347
+ data: envelope.payload,
348
+ meta: FlumeSlackSource.extractMeta(envelope),
349
+ receivedAt: this.deps.now()
350
+ };
351
+ try {
352
+ this.handler?.(event);
353
+ } catch (err) {
354
+ this.log.error({
355
+ action: "handler.error",
356
+ message: "user handler threw",
357
+ error: err instanceof Error ? err : new Error(String(err))
358
+ });
359
+ }
360
+ }
361
+ scheduleReconnect() {
362
+ if (!this.reconnector || this.reconnector.aborted) {
363
+ this.setStatus("disconnected");
364
+ return;
365
+ }
366
+ this.setStatus("reconnecting");
367
+ const delay = this.reconnector.schedule(() => this.connectInternal());
368
+ if (delay === -1) {
369
+ this.log.error({
370
+ action: "reconnect.exhausted",
371
+ message: `gave up after ${this.reconnector.attempt} attempts`
372
+ });
373
+ this.setStatus("disconnected");
374
+ } else this.log.info({
375
+ action: "reconnect.scheduled",
376
+ message: `next attempt in ${Math.round(delay)}ms`
377
+ });
378
+ }
379
+ setStatus(next) {
380
+ if (this.currentStatus === next) return;
381
+ this.log.info({
382
+ action: "status",
383
+ message: `${this.currentStatus} → ${next}`
384
+ });
385
+ this.currentStatus = next;
386
+ this.options.onStatus?.(next);
387
+ }
388
+ static extractMeta(envelope) {
389
+ const meta = { event_type: envelope.type };
390
+ const eventPayload = isRecord(envelope.payload.event) ? envelope.payload.event : null;
391
+ if (!eventPayload) return meta;
392
+ if (typeof eventPayload.channel === "string") meta.channel_id = eventPayload.channel;
393
+ if (typeof eventPayload.user === "string") meta.user_id = eventPayload.user;
394
+ if (typeof eventPayload.thread_ts === "string") meta.thread_ts = eventPayload.thread_ts;
395
+ if (typeof eventPayload.type === "string") meta.slack_event_type = eventPayload.type;
396
+ return meta;
397
+ }
398
+ };
399
+ //#endregion
400
+ export { FlumeHttpError as i, FlumeSlackSocketMode as n, obtainSlackUrl as r, FlumeSlackSource as t };
@@ -0,0 +1,23 @@
1
+ import { g as FlumeSlackSourceOptions, h as FlumeSlackEnvelope, o as FlumeHandler, y as FlumeStatus } from "./types-BVQSU336.js";
2
+
3
+ //#region lib/slack/slack-source.d.ts
4
+ declare class FlumeSlackSource {
5
+ private readonly options;
6
+ private socket;
7
+ private reconnector;
8
+ private handler;
9
+ private currentStatus;
10
+ private readonly log;
11
+ private readonly deps;
12
+ constructor(options: FlumeSlackSourceOptions);
13
+ start(handler: FlumeHandler): Promise<void>;
14
+ stop(): Promise<void>;
15
+ status(): FlumeStatus;
16
+ private connectInternal;
17
+ private handleMessage;
18
+ private scheduleReconnect;
19
+ private setStatus;
20
+ static extractMeta(envelope: FlumeSlackEnvelope): Record<string, string>;
21
+ }
22
+ //#endregion
23
+ export { FlumeSlackSource };
package/dist/slack.js ADDED
@@ -0,0 +1,2 @@
1
+ import { t as FlumeSlackSource } from "./slack-source-CszepStG.js";
2
+ export { FlumeSlackSource };
@@ -0,0 +1,112 @@
1
+ import { z } from "zod/v4";
2
+
3
+ //#region lib/schema.d.ts
4
+ declare const FlumeGatewayMessageSchema: z.ZodObject<{
5
+ op: z.ZodNumber;
6
+ d: z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
7
+ s: z.ZodNullable<z.ZodNumber>;
8
+ t: z.ZodNullable<z.ZodString>;
9
+ }, z.core.$strip>;
10
+ declare const FlumeSlackEnvelopeSchema: z.ZodObject<{
11
+ envelope_id: z.ZodString;
12
+ type: z.ZodString;
13
+ payload: z.ZodRecord<z.ZodString, z.ZodUnknown>;
14
+ accepts_response_payload: z.ZodOptional<z.ZodBoolean>;
15
+ retry_attempt: z.ZodOptional<z.ZodNumber>;
16
+ retry_reason: z.ZodOptional<z.ZodString>;
17
+ }, z.core.$strip>;
18
+ declare const FlumeSlackConnectionResponseSchema: z.ZodObject<{
19
+ ok: z.ZodBoolean;
20
+ url: z.ZodOptional<z.ZodString>;
21
+ error: z.ZodOptional<z.ZodString>;
22
+ }, z.core.$strip>;
23
+ declare const FlumeGitHubNotificationSchema: z.ZodObject<{
24
+ id: z.ZodString;
25
+ reason: z.ZodString;
26
+ unread: z.ZodBoolean;
27
+ updated_at: z.ZodString;
28
+ subject: z.ZodObject<{
29
+ title: z.ZodString;
30
+ url: z.ZodNullable<z.ZodString>;
31
+ type: z.ZodString;
32
+ }, z.core.$strip>;
33
+ repository: z.ZodObject<{
34
+ full_name: z.ZodString;
35
+ }, z.core.$strip>;
36
+ }, z.core.$strip>;
37
+ //#endregion
38
+ //#region lib/types.d.ts
39
+ type FlumeTimerHandle = ReturnType<typeof setTimeout>;
40
+ type FlumeRuntimeDeps = {
41
+ fetch(url: string | URL, init?: RequestInit): Promise<Response>;
42
+ WebSocket: new (url: string | URL) => WebSocket;
43
+ now(): number;
44
+ random(): number;
45
+ setTimeout(fn: () => void, ms: number): FlumeTimerHandle;
46
+ clearTimeout(id: FlumeTimerHandle): void;
47
+ setInterval(fn: () => void, ms: number): FlumeTimerHandle;
48
+ clearInterval(id: FlumeTimerHandle): void;
49
+ };
50
+ type FlumeSourceName = "discord" | "slack" | "github";
51
+ type FlumeEvent = {
52
+ source: FlumeSourceName;
53
+ type: string;
54
+ data: unknown;
55
+ meta: Record<string, string>;
56
+ receivedAt: number;
57
+ };
58
+ type FlumeHandler = (event: FlumeEvent) => void | Promise<void>;
59
+ type FlumeStatus = "disconnected" | "connecting" | "connected" | "reconnecting";
60
+ type FlumeStatusHandler = (status: FlumeStatus, detail?: string) => void;
61
+ type FlumeLogLevel = "debug" | "info" | "warn" | "error";
62
+ type FlumeLog = {
63
+ level: FlumeLogLevel;
64
+ source: string;
65
+ action: string;
66
+ message: string;
67
+ error?: Error;
68
+ detail?: Record<string, unknown>;
69
+ timestamp: number;
70
+ };
71
+ type FlumeLogHandler = (log: FlumeLog) => void;
72
+ type FlumeLogInput = {
73
+ action: string;
74
+ message: string;
75
+ error?: Error;
76
+ detail?: Record<string, unknown>;
77
+ };
78
+ type FlumeReconnectOptions = {
79
+ maxAttempts?: number;
80
+ baseDelay?: number;
81
+ maxDelay?: number;
82
+ };
83
+ type FlumeReconnectConfig = {
84
+ maxAttempts: number;
85
+ baseDelay: number;
86
+ maxDelay: number;
87
+ };
88
+ type FlumeSourceOptions = {
89
+ reconnect?: boolean | FlumeReconnectOptions;
90
+ onStatus?: FlumeStatusHandler;
91
+ onLog?: FlumeLogHandler;
92
+ signal?: AbortSignal;
93
+ deps: FlumeRuntimeDeps;
94
+ };
95
+ type FlumeDiscordSourceOptions = FlumeSourceOptions & {
96
+ token: string;
97
+ intents?: number;
98
+ };
99
+ type FlumeSlackSourceOptions = FlumeSourceOptions & {
100
+ appToken: string;
101
+ botToken?: string;
102
+ };
103
+ type FlumeGitHubSourceOptions = FlumeSourceOptions & {
104
+ token: string;
105
+ pollInterval?: number;
106
+ };
107
+ type FlumeGatewayMessage = z.infer<typeof FlumeGatewayMessageSchema>;
108
+ type FlumeSlackEnvelope = z.infer<typeof FlumeSlackEnvelopeSchema>;
109
+ type FlumeSlackConnectionResponse = z.infer<typeof FlumeSlackConnectionResponseSchema>;
110
+ type FlumeGitHubNotification = z.infer<typeof FlumeGitHubNotificationSchema>;
111
+ //#endregion
112
+ export { FlumeGitHubNotificationSchema as C, FlumeGatewayMessageSchema as S, FlumeSlackEnvelopeSchema as T, FlumeSourceName as _, FlumeGitHubSourceOptions as a, FlumeStatusHandler as b, FlumeLogHandler as c, FlumeReconnectConfig as d, FlumeReconnectOptions as f, FlumeSlackSourceOptions as g, FlumeSlackEnvelope as h, FlumeGitHubNotification as i, FlumeLogInput as l, FlumeSlackConnectionResponse as m, FlumeEvent as n, FlumeHandler as o, FlumeRuntimeDeps as p, FlumeGatewayMessage as r, FlumeLog as s, FlumeDiscordSourceOptions as t, FlumeLogLevel as u, FlumeSourceOptions as v, FlumeSlackConnectionResponseSchema as w, FlumeTimerHandle as x, FlumeStatus as y };