@interactive-inc/flume 0.9.1 → 0.9.3

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/README.md CHANGED
@@ -326,7 +326,8 @@ onEvent: (item) => {
326
326
  `reconnect` on `Flume` is an options object — omit it to disable reconnects entirely.
327
327
 
328
328
  ```ts
329
- new Flume(sources, {
329
+ new Flume({
330
+ sources,
330
331
  onEvent: (item) => { ... },
331
332
  reconnect: {
332
333
  maxAttempts: 10,
package/dist/slack.js CHANGED
@@ -177,6 +177,7 @@ const FlumeSlackEnvelopeSchema = z.object({
177
177
  });
178
178
  //#endregion
179
179
  //#region lib/slack/slack-socket-mode.ts
180
+ const IDLE_CHECK_INTERVAL_MS = 15e3;
180
181
  const WS_OPEN = 1;
181
182
  /**
182
183
  * Slack Socket Mode の最小 WebSocket 実装。`apps.connections.open` で URL を取得し
@@ -191,6 +192,8 @@ var FlumeSlackSocketMode = class {
191
192
  hasConnected = false;
192
193
  pendingResolve = null;
193
194
  pendingResolved = false;
195
+ lastFrameAt = 0;
196
+ idleWatchdog = null;
194
197
  constructor(props) {
195
198
  this.props = props;
196
199
  this.log = new FlumeLogger({
@@ -249,9 +252,51 @@ var FlumeSlackSocketMode = class {
249
252
  message: "stopping socket mode"
250
253
  });
251
254
  this.isStoppedFlag = true;
255
+ this.disarmIdleWatchdog();
252
256
  this.closeSocket(this.ws);
253
257
  this.ws = null;
254
258
  }
259
+ armIdleWatchdog() {
260
+ this.disarmIdleWatchdog();
261
+ const idleLimit = this.props.idleTimeoutMs;
262
+ if (idleLimit === null || idleLimit === void 0 || idleLimit <= 0) return;
263
+ const handle = attempt(() => this.props.deps.setInterval(() => this.checkIdle(idleLimit), IDLE_CHECK_INTERVAL_MS));
264
+ if (handle instanceof Error) {
265
+ this.log.error({
266
+ action: "idle.watchdog.schedule.error",
267
+ message: safeErrorMessage({ error: handle }),
268
+ error: handle
269
+ });
270
+ return;
271
+ }
272
+ this.idleWatchdog = handle;
273
+ }
274
+ disarmIdleWatchdog() {
275
+ if (!this.idleWatchdog) return;
276
+ const handle = this.idleWatchdog;
277
+ this.idleWatchdog = null;
278
+ const result = attempt(() => this.props.deps.clearInterval(handle));
279
+ if (result instanceof Error) this.log.error({
280
+ action: "idle.watchdog.clear.error",
281
+ message: safeErrorMessage({ error: result }),
282
+ error: result
283
+ });
284
+ }
285
+ checkIdle(idleLimit) {
286
+ if (!this.hasConnected || this.isStoppedFlag) return;
287
+ const elapsed = this.props.deps.now() - this.lastFrameAt;
288
+ if (elapsed < idleLimit) return;
289
+ this.log.warn({
290
+ action: "idle.timeout",
291
+ message: `no frames for ${elapsed}ms (limit ${idleLimit}ms) — force-closing socket`,
292
+ detail: {
293
+ elapsedMs: elapsed,
294
+ limitMs: idleLimit
295
+ }
296
+ });
297
+ this.disarmIdleWatchdog();
298
+ this.closeSocket(this.ws);
299
+ }
255
300
  isConnected() {
256
301
  return this.ws !== null && this.ws.readyState === WS_OPEN;
257
302
  }
@@ -357,12 +402,14 @@ var FlumeSlackSocketMode = class {
357
402
  message: `type=${typeof json.type === "string" ? json.type : "-"} length=${raw.length}`,
358
403
  detail: { length: raw.length }
359
404
  });
405
+ this.lastFrameAt = this.props.deps.now();
360
406
  if (json.type === "hello") {
361
407
  this.log.info({
362
408
  action: "socket.hello",
363
409
  message: "connection ready"
364
410
  });
365
411
  this.hasConnected = true;
412
+ this.armIdleWatchdog();
366
413
  this.props.onConnected();
367
414
  this.completeConnect(null);
368
415
  return;
@@ -419,6 +466,7 @@ var FlumeSlackSocketMode = class {
419
466
  reason: ev.reason
420
467
  }
421
468
  });
469
+ this.disarmIdleWatchdog();
422
470
  this.ws = null;
423
471
  if (this.hasConnected) this.props.onDisconnected();
424
472
  if (!this.pendingResolved) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@interactive-inc/flume",
3
- "version": "0.9.1",
3
+ "version": "0.9.3",
4
4
  "description": "Unified notification listener for Discord, Slack, and GitHub. Raw WebSocket + fetch + Zod. No SDK dependencies.",
5
5
  "keywords": [
6
6
  "discord",