@interactive-inc/flume 0.4.0 → 0.9.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/dist/index.js CHANGED
@@ -1,13 +1,146 @@
1
- import { a as FlumeParseError, c as safeNormalizeError, i as FlumeStartError, l as safeErrorMessage, n as FlumeLogger, o as createFlumeDefaultDeps, s as attempt, t as safeInvokeCallback } from "./safe-invoke-callback-EpWXwfwp.js";
2
- import { t as FlumeConnectionError } from "./connection-error-HUO3PC3G.js";
3
- import { t as FlumeHttpError } from "./http-error-CPSKoSie.js";
4
- //#region lib/flume-stopped.ts
1
+ import { a as FlumeStartError, c as safeNormalizeError, l as safeErrorMessage, n as safeInvokeCallback, o as FlumeParseError, r as FlumeLogger, s as attempt, t as FlumeSource } from "./flume-source.js";
2
+ import { t as FlumeConnectionError } from "./connection-error.js";
3
+ import { t as FlumeHttpError } from "./http-error.js";
4
+ //#region lib/deps.ts
5
5
  /**
6
- * 停止済みの終端状態。最終ステータスのスナップショットのみ観測できる
6
+ * `globalThis.WebSocket` の現在の値を返す。
7
+ * 取得は呼び出しごとに行う — `createFlumeDefaultDeps()` がモジュール初期化時ではなく
8
+ * 呼ばれた瞬間の `globalThis.WebSocket` を見るので、jsdom / happy-dom / vitest の
9
+ * `beforeEach` で `globalThis.WebSocket` を差し込むテスト戦略がそのまま機能する。
10
+ * `WebSocket` が無い環境 (Node の素の global など) では `null` を返す。
7
11
  */
8
- var FlumeStopped = class {
12
+ function resolveCurrentWebSocket() {
13
+ const candidate = attempt(() => globalThis.WebSocket);
14
+ if (candidate instanceof Error || typeof candidate !== "function") return null;
15
+ return candidate;
16
+ }
17
+ /**
18
+ * platform 既定の IO を束ねた `FlumeRuntimeDeps`。
19
+ * `FlumeTimerHandle` は不透明型 (`unknown`) のため、setTimeout / clearTimeout の戻り値・引数を
20
+ * platform 型と橋渡しする際に境界で `as unknown as` を使う (IO 境界の最終手段)。
21
+ *
22
+ * `WebSocket` を含む全 IO は呼び出しごとに `globalThis` から引く lazy lookup。
23
+ * モジュール初期化後に `globalThis.WebSocket` が差し替わる環境
24
+ * (テストの `beforeEach` パッチ、jsdom などのブラウザ環境エミュレータ) でも
25
+ * `createFlumeDefaultDeps()` が返した deps が常に最新の参照を見る。
26
+ */
27
+ function createFlumeDefaultDeps() {
28
+ return {
29
+ fetch: (url, init) => globalThis.fetch(url, init),
30
+ WebSocket: resolveCurrentWebSocket(),
31
+ now: () => Date.now(),
32
+ random: () => Math.random(),
33
+ setTimeout: (fn, ms) => globalThis.setTimeout(fn, ms),
34
+ clearTimeout: (id) => globalThis.clearTimeout(id),
35
+ setInterval: (fn, ms) => globalThis.setInterval(fn, ms),
36
+ clearInterval: (id) => globalThis.clearInterval(id)
37
+ };
38
+ }
39
+ //#endregion
40
+ //#region lib/flume-stream.ts
41
+ const DONE = {
42
+ value: void 0,
43
+ done: true
44
+ };
45
+ /**
46
+ * push (`FlumeStreamHub.publish`) を pull (`for await`) に変換する async iterator。
47
+ * consumer が待っていれば即 resolve、いなければ buffer に積み、溢れたら onOverflow に従う。
48
+ * `return()` (break / 例外) と hub.close() のどちらでも自然に done へ落ちる
49
+ */
50
+ var FlumeStream = class {
51
+ props;
52
+ items = [];
53
+ resolvers = [];
54
+ closed = false;
55
+ constructor(props) {
56
+ this.props = props;
57
+ }
58
+ push(item) {
59
+ if (this.closed) return;
60
+ const resolver = this.resolvers.shift();
61
+ if (resolver) {
62
+ resolver({
63
+ value: item,
64
+ done: false
65
+ });
66
+ return;
67
+ }
68
+ if (this.items.length >= this.props.buffer) {
69
+ if (this.props.onOverflow === "drop-newest") return;
70
+ this.items.shift();
71
+ }
72
+ this.items.push(item);
73
+ }
74
+ close() {
75
+ if (this.closed) return;
76
+ this.closed = true;
77
+ while (this.resolvers.length > 0) {
78
+ const resolver = this.resolvers.shift();
79
+ if (resolver) resolver(DONE);
80
+ }
81
+ }
82
+ next() {
83
+ const item = this.items.shift();
84
+ if (item !== void 0) return Promise.resolve({
85
+ value: item,
86
+ done: false
87
+ });
88
+ if (this.closed) return Promise.resolve(DONE);
89
+ return new Promise((resolve) => this.resolvers.push(resolve));
90
+ }
91
+ return() {
92
+ this.close();
93
+ this.props.onClose();
94
+ return Promise.resolve(DONE);
95
+ }
96
+ [Symbol.asyncIterator]() {
97
+ return this;
98
+ }
99
+ };
100
+ //#endregion
101
+ //#region lib/flume-stream-hub.ts
102
+ const DEFAULT_BUFFER = 1e3;
103
+ /**
104
+ * firehose (`onEvent` / `stream()`) の item を複数の pull consumer へ fan-out する内部ハブ。
105
+ * subscriber が居なければ publish は実質 no-op。Flume 停止時に close() で全 stream を終端する
106
+ */
107
+ var FlumeStreamHub = class {
108
+ streams = /* @__PURE__ */ new Set();
109
+ closed = false;
110
+ publish(item) {
111
+ if (this.closed) return;
112
+ for (const stream of this.streams) stream.push(item);
113
+ }
114
+ subscribe(options) {
115
+ const stream = new FlumeStream({
116
+ buffer: options?.buffer ?? DEFAULT_BUFFER,
117
+ onOverflow: options?.onOverflow ?? "drop-oldest",
118
+ onClose: () => this.streams.delete(stream)
119
+ });
120
+ if (this.closed) {
121
+ stream.close();
122
+ return stream;
123
+ }
124
+ this.streams.add(stream);
125
+ return stream;
126
+ }
127
+ close() {
128
+ if (this.closed) return;
129
+ this.closed = true;
130
+ for (const stream of this.streams) stream.close();
131
+ this.streams.clear();
132
+ }
133
+ };
134
+ //#endregion
135
+ //#region lib/flume-closed.ts
136
+ /**
137
+ * 停止済みの終端状態。最終ステータスと、停止時に source.disconnect が throw した
138
+ * エラー一覧を観測できる。`errors()` を読めば `onLog` を grep せずに「どの source が
139
+ * きれいに close したか / どれが失敗したか」を直接判定できる
140
+ */
141
+ var FlumeClosed = class {
9
142
  props;
10
- kind = "stopped";
143
+ kind = "closed";
11
144
  constructor(props) {
12
145
  this.props = props;
13
146
  Object.freeze(this);
@@ -15,31 +148,39 @@ var FlumeStopped = class {
15
148
  statuses() {
16
149
  return this.props.finalStatuses;
17
150
  }
151
+ /**
152
+ * `runClose` 中に `source.stop()` が rejected で settle した source の名前と
153
+ * 正規化済み Error の組。`onEvent` firehose の `flume.close.failed` log と 1:1 対応する。
154
+ * 全 source が clean close した場合は空配列。
155
+ */
156
+ errors() {
157
+ return this.props.closeErrors;
158
+ }
18
159
  };
19
160
  //#endregion
20
161
  //#region lib/flume-running.ts
21
162
  /**
22
- * 稼働中の Flume。stop() で FlumeStopped へ遷移する。signal が abort されると自動 stop
163
+ * 稼働中の Flume。close() で FlumeClosed へ遷移する。signal が abort されると自動 close
23
164
  * 全ての source 呼び出し・signal 操作・status 読み取りを `attempt` 経由で扱い、
24
- * `runStop` の最外殻 try/catch で想定外の throw も `FlumeStopped` の resolve に変換する
165
+ * `runClose` の最外殻 try/catch で想定外の throw も `FlumeClosed` の resolve に変換する
25
166
  */
26
167
  var FlumeRunning = class {
27
168
  props;
28
169
  kind = "running";
29
- stopPromise = null;
170
+ closePromise = null;
30
171
  onAbort;
31
172
  constructor(props) {
32
173
  this.props = props;
33
174
  this.onAbort = () => {
34
175
  this.props.log.info({
35
176
  action: "flume.abort",
36
- message: "signal aborted, stopping"
177
+ message: "signal aborted, closing"
37
178
  });
38
179
  safeInvokeCallback({
39
- fn: () => this.stop(),
180
+ fn: () => this.close(),
40
181
  onError: (error) => {
41
182
  this.props.log.error({
42
- action: "flume.abort.stop.failed",
183
+ action: "flume.abort.close.failed",
43
184
  message: safeErrorMessage({ error }),
44
185
  error
45
186
  });
@@ -59,27 +200,49 @@ var FlumeRunning = class {
59
200
  }
60
201
  }
61
202
  }
62
- stop() {
63
- if (this.stopPromise) return this.stopPromise;
64
- this.stopPromise = this.runStop();
65
- return this.stopPromise;
203
+ close() {
204
+ if (this.closePromise) return this.closePromise;
205
+ this.closePromise = this.runClose();
206
+ return this.closePromise;
66
207
  }
67
208
  statuses() {
68
209
  return this.snapshotStatuses();
69
210
  }
70
- async runStop() {
211
+ /**
212
+ * 統合 firehose を pull で受け取る async iterator。`for await (const item of running.stream())`。
213
+ * item は events + 全ログの union (`FlumeStreamItem`)。`item.kind` で判別する。
214
+ * close() / signal abort で iterator は自然に終了し、`break` すると hub から自動 unsubscribe する。
215
+ * consumer が遅れて buffer を超えたら `onOverflow` (既定 drop-oldest) に従う
216
+ */
217
+ stream(options) {
218
+ return this.props.hub.subscribe(options);
219
+ }
220
+ /**
221
+ * Host が `Flume({ signal })` で渡した AbortSignal をそのまま公開する。
222
+ * 直接の controller を持っていない呼び出し元が `running.signal?.aborted`
223
+ * で abort 状態を確認できる
224
+ */
225
+ get signal() {
226
+ return this.props.signal;
227
+ }
228
+ async runClose() {
229
+ const closeErrors = [];
71
230
  try {
72
231
  this.props.log.info({
73
- action: "flume.stop",
74
- message: `stopping ${this.props.sources.length} source(s)`
232
+ action: "flume.close",
233
+ message: `closing ${this.props.sources.length} source(s)`
75
234
  });
76
235
  const settled = await Promise.allSettled(this.props.sources.map((source) => Promise.resolve().then(() => source.stop())));
77
236
  for (const [index, result] of settled.entries()) if (result.status === "rejected") {
78
237
  const source = this.props.sources[index];
79
238
  const name = source ? this.sourceName(source) : "?";
80
239
  const error = safeNormalizeError({ value: result.reason });
240
+ closeErrors.push({
241
+ source: name,
242
+ error
243
+ });
81
244
  this.props.log.error({
82
- action: "flume.stop.failed",
245
+ action: "flume.close.failed",
83
246
  message: `${name}: ${safeErrorMessage({ error })}`,
84
247
  error,
85
248
  detail: { source: name }
@@ -98,18 +261,26 @@ var FlumeRunning = class {
98
261
  }
99
262
  }
100
263
  this.props.log.info({
101
- action: "flume.stop.complete",
102
- message: "all sources stopped"
264
+ action: "flume.close.complete",
265
+ message: "all sources closed"
266
+ });
267
+ this.props.hub.close();
268
+ return new FlumeClosed({
269
+ finalStatuses: this.snapshotStatuses(),
270
+ closeErrors
103
271
  });
104
- return new FlumeStopped({ finalStatuses: this.snapshotStatuses() });
105
272
  } catch (err) {
106
273
  const error = safeNormalizeError({ value: err });
107
274
  this.props.log.error({
108
- action: "flume.stop.unhandled",
275
+ action: "flume.close.unhandled",
109
276
  message: safeErrorMessage({ error }),
110
277
  error
111
278
  });
112
- return new FlumeStopped({ finalStatuses: this.snapshotStatuses() });
279
+ this.props.hub.close();
280
+ return new FlumeClosed({
281
+ finalStatuses: this.snapshotStatuses(),
282
+ closeErrors
283
+ });
113
284
  }
114
285
  }
115
286
  snapshotStatuses() {
@@ -143,42 +314,95 @@ var FlumeRunning = class {
143
314
  }
144
315
  };
145
316
  //#endregion
317
+ //#region lib/reconnect-config.ts
318
+ const DEFAULTS = {
319
+ maxAttempts: Infinity,
320
+ baseDelay: 1e3,
321
+ maxDelay: 3e4
322
+ };
323
+ function resolveFlumeReconnectConfig(input) {
324
+ if (input === false || input === void 0) return null;
325
+ if (input === true) return { ...DEFAULTS };
326
+ return {
327
+ ...DEFAULTS,
328
+ ...input
329
+ };
330
+ }
331
+ //#endregion
146
332
  //#region lib/flume.ts
147
333
  /**
148
- * 起動前の Flume。`start()` で `FlumeRunning` へ遷移する。
334
+ * 起動前の Flume。`open()` で `FlumeRunning` へ遷移する。
335
+ * コンストラクタは単一オブジェクト `{ sources, ...options }` を受け取る (`sources` のみ必須)。
336
+ * events も全ログも 1 本の firehose (`onEvent` push / `stream()` pull) に流れ、購読側が filter する。
149
337
  * いずれかの source 失敗時は既に成功した source を全て `stop()` してロールバックし
150
338
  * `FlumeStartError` を返す。
151
339
  * `source.start()` / `source.stop()` の sync throw も `Promise.resolve().then` 経由で
152
- * Promise rejection に正規化して `allSettled` で捕捉する (`start()` は決して reject しない)
340
+ * Promise rejection に正規化して `allSettled` で捕捉する (`open()` は決して reject しない)
153
341
  */
154
342
  var Flume = class {
155
- props;
343
+ options;
156
344
  consumed = false;
157
345
  log;
158
346
  deps;
159
- constructor(props) {
160
- this.props = props;
161
- this.deps = props.deps ?? createFlumeDefaultDeps();
347
+ sources;
348
+ sourceEventHandler;
349
+ hub = new FlumeStreamHub();
350
+ constructor(options) {
351
+ this.options = options;
352
+ this.sources = options.sources;
353
+ this.deps = options.deps ?? createFlumeDefaultDeps();
162
354
  this.log = new FlumeLogger({
163
355
  source: "flume",
164
- handler: props.onLog,
356
+ handler: this.buildLogHandler(),
165
357
  deps: this.deps
166
358
  });
359
+ this.sourceEventHandler = (event) => this.emitItem({
360
+ kind: "event",
361
+ event
362
+ });
363
+ }
364
+ /** source が受信したログを firehose へ流す handler。error は onError にも分岐する */
365
+ buildLogHandler() {
366
+ return (log) => {
367
+ this.emitItem({
368
+ kind: "log",
369
+ log
370
+ });
371
+ const onError = this.options.onError;
372
+ if (!onError || log.level !== "error") return;
373
+ try {
374
+ Promise.resolve(onError(log)).catch(() => {});
375
+ } catch {}
376
+ };
167
377
  }
168
- async start(handler) {
169
- const guard = this.guardStart();
378
+ /**
379
+ * firehose の単一 sink: pull の hub と push の onEvent の両方へ item を配る。
380
+ * onEvent への転送は this.log を経由しない (経由すると log item 経路で再帰する) ため
381
+ * 例外をここで握り潰す
382
+ */
383
+ emitItem(item) {
384
+ this.hub.publish(item);
385
+ const onEvent = this.options.onEvent;
386
+ if (!onEvent) return;
387
+ try {
388
+ Promise.resolve(onEvent(item)).catch(() => {});
389
+ } catch {}
390
+ }
391
+ async open() {
392
+ const guard = this.guardOpen();
170
393
  if (guard) return guard;
171
394
  this.consumed = true;
172
395
  this.log.info({
173
- action: "flume.start",
174
- message: `starting ${this.props.sources.length} source(s)`,
175
- detail: { count: this.props.sources.length }
396
+ action: "flume.open",
397
+ message: `opening ${this.sources.length} source(s)`,
398
+ detail: { count: this.sources.length }
176
399
  });
177
- const settled = await Promise.allSettled(this.props.sources.map((source) => this.safeStart(source, handler)));
400
+ const reconnect = resolveFlumeReconnectConfig(this.options.reconnect);
401
+ const settled = await Promise.allSettled(this.sources.map((source) => this.safeStart(source, reconnect)));
178
402
  const failures = [];
179
403
  const started = [];
180
404
  for (const [index, result] of settled.entries()) {
181
- const source = this.props.sources[index];
405
+ const source = this.sources[index];
182
406
  if (source === void 0) continue;
183
407
  const name = this.sourceName(source);
184
408
  if (result.status === "rejected") {
@@ -206,48 +430,49 @@ var Flume = class {
206
430
  });
207
431
  await this.rollback(started);
208
432
  const detail = failures.map((f) => `${f.name}: ${safeErrorMessage({ error: f.error })}`).join("; ");
209
- const error = new FlumeStartError(`Flume.start: ${failures.length} source(s) failed: ${detail}`);
433
+ const error = new FlumeStartError(`Flume.open: ${failures.length} source(s) failed: ${detail}`);
210
434
  this.log.error({
211
- action: "flume.start.failed",
435
+ action: "flume.open.failed",
212
436
  message: safeErrorMessage({ error }),
213
437
  error
214
438
  });
215
439
  return error;
216
440
  }
217
441
  if (this.isSignalAborted()) {
218
- await this.rollback(this.props.sources);
219
- const error = new FlumeStartError("Flume.start: aborted during start");
442
+ await this.rollback(this.sources);
443
+ const error = new FlumeStartError("Flume.open: aborted during open");
220
444
  this.log.warn({
221
- action: "flume.start.aborted",
445
+ action: "flume.open.aborted",
222
446
  message: safeErrorMessage({ error }),
223
447
  error
224
448
  });
225
449
  return error;
226
450
  }
227
451
  this.log.info({
228
- action: "flume.start.complete",
229
- message: "all sources started"
452
+ action: "flume.open.complete",
453
+ message: "all sources opened"
230
454
  });
231
455
  return new FlumeRunning({
232
- sources: this.props.sources,
233
- signal: this.props.signal,
234
- log: this.log
456
+ sources: this.sources,
457
+ signal: this.options.signal,
458
+ log: this.log,
459
+ hub: this.hub
235
460
  });
236
461
  }
237
- guardStart() {
462
+ guardOpen() {
238
463
  if (this.consumed) {
239
- const error = new FlumeStartError("Flume.start: already started");
464
+ const error = new FlumeStartError("Flume.open: already opened");
240
465
  this.log.warn({
241
- action: "flume.start.refused",
466
+ action: "flume.open.refused",
242
467
  message: safeErrorMessage({ error }),
243
468
  error
244
469
  });
245
470
  return error;
246
471
  }
247
472
  if (this.isSignalAborted()) {
248
- const error = new FlumeStartError("Flume.start: signal already aborted");
473
+ const error = new FlumeStartError("Flume.open: signal already aborted");
249
474
  this.log.warn({
250
- action: "flume.start.refused",
475
+ action: "flume.open.refused",
251
476
  message: safeErrorMessage({ error }),
252
477
  error
253
478
  });
@@ -256,7 +481,7 @@ var Flume = class {
256
481
  return null;
257
482
  }
258
483
  isSignalAborted() {
259
- const signal = this.props.signal;
484
+ const signal = this.options.signal;
260
485
  if (!signal) return false;
261
486
  const result = attempt(() => signal.aborted === true);
262
487
  return result instanceof Error ? true : result;
@@ -267,8 +492,16 @@ var Flume = class {
267
492
  if (typeof result !== "string") return "?";
268
493
  return result;
269
494
  }
270
- safeStart(source, handler) {
271
- return Promise.resolve().then(() => source.start(handler, { signal: this.props.signal }));
495
+ safeStart(source, reconnect) {
496
+ const name = this.sourceName(source);
497
+ const ctx = {
498
+ onEvent: this.sourceEventHandler,
499
+ log: this.log.child(name),
500
+ deps: this.deps,
501
+ reconnect,
502
+ signal: this.options.signal
503
+ };
504
+ return Promise.resolve().then(() => source.start(ctx));
272
505
  }
273
506
  async rollback(sources) {
274
507
  const settled = await Promise.allSettled(sources.map((source) => Promise.resolve().then(() => source.stop())));
@@ -286,4 +519,52 @@ var Flume = class {
286
519
  }
287
520
  };
288
521
  //#endregion
289
- export { Flume, FlumeConnectionError, FlumeHttpError, FlumeParseError, FlumeRunning, FlumeStartError, FlumeStopped, createFlumeDefaultDeps };
522
+ //#region lib/flume-confluence.ts
523
+ /**
524
+ * 複数の `Flume` を束ねて動的に増減させる上位レイヤー。各 Flume は immutable のまま、
525
+ * `add()` で新しいグループを起動し `remove()` で個別に停止する。全グループの firehose は
526
+ * `onEvent` 1 本に合流する。Flume 本体の FSM / rollback / reconnect はそのまま再利用される。
527
+ *
528
+ * id はグループの管理ハンドル (add/remove 用)。合流ストリーム自体は id を持たず、
529
+ * item の中の source 名で発信元を判別する。throw しない流儀に従い `add()` は `Error | null` を返す
530
+ */
531
+ var FlumeConfluence = class {
532
+ props;
533
+ running = /* @__PURE__ */ new Map();
534
+ constructor(props = {}) {
535
+ this.props = props;
536
+ }
537
+ /** sources を 1 グループとして起動。id 重複や起動失敗は `Error` で返す (throw しない) */
538
+ async add(id, sources) {
539
+ if (this.running.has(id)) return new FlumeStartError(`FlumeConfluence: id already added: ${id}`);
540
+ const running = await new Flume({
541
+ sources,
542
+ onEvent: this.props.onEvent,
543
+ onError: this.props.onError,
544
+ deps: this.props.deps,
545
+ reconnect: this.props.reconnect
546
+ }).open();
547
+ if (running instanceof Error) return running;
548
+ this.running.set(id, running);
549
+ return null;
550
+ }
551
+ /** 指定グループだけ close。他グループは無停止。未知の id は no-op */
552
+ async remove(id) {
553
+ const running = this.running.get(id);
554
+ if (!running) return;
555
+ this.running.delete(id);
556
+ await running.close();
557
+ }
558
+ async closeAll() {
559
+ const ids = [...this.running.keys()];
560
+ await Promise.all(ids.map((id) => this.remove(id)));
561
+ }
562
+ has(id) {
563
+ return this.running.has(id);
564
+ }
565
+ ids() {
566
+ return [...this.running.keys()];
567
+ }
568
+ };
569
+ //#endregion
570
+ export { Flume, FlumeClosed, FlumeConfluence, FlumeConnectionError, FlumeHttpError, FlumeParseError, FlumeRunning, FlumeSource, FlumeStartError, createFlumeDefaultDeps };
@@ -0,0 +1,6 @@
1
+ //#region lib/utils/is-record.ts
2
+ function isRecord(value) {
3
+ return typeof value === "object" && value !== null;
4
+ }
5
+ //#endregion
6
+ export { isRecord as t };
@@ -0,0 +1,9 @@
1
+ //#region lib/errors/parse-error.d.ts
2
+ type Options = {
3
+ cause?: unknown;
4
+ };
5
+ declare class FlumeParseError extends Error {
6
+ constructor(message: string, options?: Options);
7
+ }
8
+ //#endregion
9
+ export { FlumeParseError as t };
@@ -0,0 +1,11 @@
1
+ import { o as FlumeParseError } from "./flume-source.js";
2
+ //#region lib/utils/safe-json-parse.ts
3
+ function safeJsonParse(raw) {
4
+ try {
5
+ return JSON.parse(raw);
6
+ } catch (error) {
7
+ return new FlumeParseError(error instanceof Error ? `invalid JSON: ${error.message}` : "invalid JSON", { cause: error });
8
+ }
9
+ }
10
+ //#endregion
11
+ export { safeJsonParse as t };
@@ -1,5 +1,5 @@
1
- import { l as safeErrorMessage } from "./safe-invoke-callback-EpWXwfwp.js";
2
- import { t as FlumeHttpError } from "./http-error-CPSKoSie.js";
1
+ import { l as safeErrorMessage } from "./flume-source.js";
2
+ import { t as FlumeHttpError } from "./http-error.js";
3
3
  //#region lib/utils/safe-read-text.ts
4
4
  /**
5
5
  * `response.text()` を保護する。body 読み取り中の reject (接続切断 / 解凍失敗 / 二重消費) を
@@ -1,5 +1,5 @@
1
- import { l as safeErrorMessage, s as attempt, t as safeInvokeCallback } from "./safe-invoke-callback-EpWXwfwp.js";
2
- import { t as FlumeConnectionError } from "./connection-error-HUO3PC3G.js";
1
+ import { l as safeErrorMessage, n as safeInvokeCallback, s as attempt } from "./flume-source.js";
2
+ import { t as FlumeConnectionError } from "./connection-error.js";
3
3
  //#region lib/utils/safe-random.ts
4
4
  /**
5
5
  * `deps.random()` を保護する。throw / 範囲外値 / 非数値が返った場合は 0.5 を返す。
@@ -40,7 +40,7 @@ var FlumeReconnector = class {
40
40
  if (this.isAborted) return 0;
41
41
  if (this.currentAttempt >= this.props.maxAttempts) return -1;
42
42
  this.clearTimer();
43
- const delay = this.nextDelay();
43
+ const delay = this.computeDelay();
44
44
  const timerResult = attempt(() => this.props.deps.setTimeout(() => this.runRetry(fn), delay));
45
45
  if (timerResult instanceof Error) {
46
46
  this.props.log.error({
@@ -51,6 +51,7 @@ var FlumeReconnector = class {
51
51
  this.timer = null;
52
52
  return 0;
53
53
  }
54
+ this.currentAttempt++;
54
55
  this.timer = timerResult;
55
56
  return delay;
56
57
  }
@@ -85,28 +86,11 @@ var FlumeReconnector = class {
85
86
  });
86
87
  this.timer = null;
87
88
  }
88
- nextDelay() {
89
- const jitter = Math.min(this.props.baseDelay * 2 ** this.currentAttempt, this.props.maxDelay) * (.5 + safeRandom({ deps: this.props.deps }) * .5);
90
- this.currentAttempt++;
91
- return jitter;
89
+ computeDelay() {
90
+ return Math.min(this.props.baseDelay * 2 ** this.currentAttempt, this.props.maxDelay) * (.5 + safeRandom({ deps: this.props.deps }) * .5);
92
91
  }
93
92
  };
94
93
  //#endregion
95
- //#region lib/reconnect-config.ts
96
- const DEFAULTS = {
97
- maxAttempts: Infinity,
98
- baseDelay: 1e3,
99
- maxDelay: 3e4
100
- };
101
- function resolveFlumeReconnectConfig(input) {
102
- if (input === false || input === void 0) return null;
103
- if (input === true) return { ...DEFAULTS };
104
- return {
105
- ...DEFAULTS,
106
- ...input
107
- };
108
- }
109
- //#endregion
110
94
  //#region lib/schedule-reconnect.ts
111
95
  /**
112
96
  * 接続が落ちた際の共通再接続スケジューラ。
@@ -155,11 +139,6 @@ function scheduleFlumeReconnect(props) {
155
139
  });
156
140
  }
157
141
  //#endregion
158
- //#region lib/utils/is-record.ts
159
- function isRecord(value) {
160
- return typeof value === "object" && value !== null;
161
- }
162
- //#endregion
163
142
  //#region lib/utils/safe-stringify.ts
164
143
  /**
165
144
  * `JSON.stringify` を `string | Error` に変換するだけのラッパ。
@@ -169,4 +148,4 @@ function safeStringify(value) {
169
148
  return attempt(() => JSON.stringify(value));
170
149
  }
171
150
  //#endregion
172
- export { FlumeReconnector as a, resolveFlumeReconnectConfig as i, isRecord as n, safeRandom as o, scheduleFlumeReconnect as r, safeStringify as t };
151
+ export { safeRandom as i, scheduleFlumeReconnect as n, FlumeReconnector as r, safeStringify as t };
package/dist/slack.d.ts CHANGED
@@ -1,24 +1,16 @@
1
- import { C as FlumeSourceStartOptions, T as FlumeStatus, _ as FlumeSlackEnvelope, c as FlumeHandler, y as FlumeSlackSourceOptions } from "./types-D-tO-Mh2.js";
1
+ import { t as FlumeSource, w as FlumeSourceStartContext, x as FlumeSlackSourceOptions, y as FlumeSlackEnvelope } from "./flume-source.js";
2
2
 
3
3
  //#region lib/slack/slack-source.d.ts
4
- declare class FlumeSlackSource {
4
+ declare class FlumeSlackSource extends FlumeSource {
5
5
  private readonly options;
6
6
  readonly name: "slack";
7
7
  private socket;
8
8
  private reconnector;
9
- private handler;
10
9
  private internalController;
11
- private readonly log;
12
- private readonly deps;
13
- private readonly queue;
14
- private readonly seen;
15
- private readonly signals;
16
- private readonly statusEmitter;
17
- private readonly onSignalAbort;
10
+ private seen;
18
11
  constructor(options: FlumeSlackSourceOptions);
19
- start(handler: FlumeHandler, options?: FlumeSourceStartOptions): Promise<Error | null>;
20
- stop(): Promise<void>;
21
- status(): FlumeStatus;
12
+ protected connect(ctx: FlumeSourceStartContext): Promise<Error | null>;
13
+ protected disconnect(): void;
22
14
  private hasWebSocket;
23
15
  private connectInternal;
24
16
  private handleMessage;