@interactive-inc/claude-funnel 0.66.0 → 0.68.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.
Files changed (97) hide show
  1. package/README.md +31 -6
  2. package/dist/bin.js +292 -383
  3. package/dist/channel.d.ts +76 -52
  4. package/dist/channel.js +82 -47
  5. package/dist/{channels-DR90RmUP.d.ts → channels-B0cifCfw.d.ts} +6 -6
  6. package/dist/claude.d.ts +11 -8
  7. package/dist/claude.js +31 -15
  8. package/dist/connector-descriptor-D4XtEsH8.d.ts +215 -0
  9. package/dist/{connector-diagnostics-recorder-COtNEmUp.js → connector-diagnostics-recorder-BmOijU1p.js} +1 -0
  10. package/dist/connectors/discord.d.ts +11 -8
  11. package/dist/connectors/discord.js +2 -2
  12. package/dist/connectors/gh.d.ts +7 -6
  13. package/dist/connectors/gh.js +2 -2
  14. package/dist/connectors/schedule.d.ts +21 -17
  15. package/dist/connectors/schedule.js +3 -3
  16. package/dist/connectors/slack.d.ts +15 -10
  17. package/dist/connectors/slack.js +3 -3
  18. package/dist/diagnostic-event-CxMM5Bl2.js +78 -0
  19. package/dist/diagnostic-log-BsAg5qZK.d.ts +186 -0
  20. package/dist/diagnostics.d.ts +2 -2
  21. package/dist/diagnostics.js +3 -2
  22. package/dist/{discord-connector-DXARcO6w.js → discord-connector-CAcWifJn.js} +6 -6
  23. package/dist/{discord-connector-schema-D1dyxhXk.d.ts → discord-connector-schema-BaBZYHxK.d.ts} +0 -1
  24. package/dist/docs.d.ts +1 -1
  25. package/dist/docs.js +1 -1
  26. package/dist/doctor.d.ts +1 -1
  27. package/dist/doctor.js +1 -1
  28. package/dist/event-log.d.ts +2 -0
  29. package/dist/event-log.js +3 -0
  30. package/dist/{file-process-guard-CiBwXjzm.d.ts → file-process-guard-D-QatrwL.d.ts} +22 -18
  31. package/dist/{file-system-DHgbuTFb.d.ts → file-system-BNtl997T.d.ts} +2 -1
  32. package/dist/{flume-source-listener-FzR5lGpL.d.ts → flume-source-listener-Ba8_1Mg-.d.ts} +9 -6
  33. package/dist/{funnel-diagnostics-Cvk6Sk4x.js → funnel-diagnostics-CAG7hJP0.js} +85 -141
  34. package/dist/{funnel-diagnostics-kbd27d7I.d.ts → funnel-diagnostics-CDrZznTB.d.ts} +18 -18
  35. package/dist/{funnel-docs-C-ge0MuB.js → funnel-docs-CI4hMkhq.js} +12 -3
  36. package/dist/{funnel-doctor-CVTNUtDn.d.ts → funnel-doctor-CY4S5cnz.d.ts} +20 -9
  37. package/dist/{funnel-doctor-XrI2GBH8.js → funnel-doctor-Csw5qjcH.js} +1 -0
  38. package/dist/{funnel-error-0t1MK1R6.js → funnel-error-DoSBCogD.js} +13 -0
  39. package/dist/{funnel-recovery-D8MkiTpk.d.ts → funnel-recovery-BwcSNNSN.d.ts} +1 -2
  40. package/dist/{funnel-recovery-DKnEutUS.js → funnel-recovery-DYbSYGWC.js} +1 -0
  41. package/dist/gateway/daemon.js +60 -139
  42. package/dist/gateway.d.ts +2 -3
  43. package/dist/gateway.js +2 -2
  44. package/dist/{gh-connector-BUGCOEWS.js → gh-connector-DMwI6zJA.js} +5 -5
  45. package/dist/{gh-connector-schema-BCHEqpUg.d.ts → gh-connector-schema-BrNsCl1T.d.ts} +0 -1
  46. package/dist/{index-BU1TtuFy.d.ts → index-fHrKn4jm.d.ts} +210 -40
  47. package/dist/index.d.ts +125 -115
  48. package/dist/index.js +242 -57
  49. package/dist/{local-config-json-schema-DexV8vX3.js → local-config-json-schema-_-YVHX4c.js} +4 -4
  50. package/dist/local-config.d.ts +2 -3
  51. package/dist/local-config.js +1 -1
  52. package/dist/logger.d.ts +17 -265
  53. package/dist/logger.js +6 -141
  54. package/dist/memory-event-log-BY8XjFzi.js +147 -0
  55. package/dist/{memory-token-prompter-D0kIdhar.d.ts → memory-token-prompter-B20tl_aU.d.ts} +2 -3
  56. package/dist/{node-file-system-BOXIHW_Q.js → node-file-system-xJx7dymE.js} +11 -3
  57. package/dist/{profiles-M24JrcOh.d.ts → profiles-CePRnv3F.d.ts} +3 -4
  58. package/dist/{profiles-ZHLONml4.js → profiles-KpN7_gBw.js} +19 -5
  59. package/dist/profiles.d.ts +1 -1
  60. package/dist/profiles.js +1 -1
  61. package/dist/recovery.d.ts +1 -1
  62. package/dist/recovery.js +1 -1
  63. package/dist/{schedule-connector-9k3gOIgl.js → schedule-connector-BXUOYgwZ.js} +63 -99
  64. package/dist/schedule-connector-schema-CNV8PgWX.js +137 -0
  65. package/dist/schedule-connector-schema-Tq5kJul-.d.ts +127 -0
  66. package/dist/{settings-reader-BnKIbmsD.d.ts → settings-reader-CyTpbAre.d.ts} +1 -2
  67. package/dist/{settings-schema-IShSMm5G.d.ts → settings-schema-B-CKLuST.d.ts} +0 -1
  68. package/dist/{settings-store-ccv291GV.js → settings-store-ZFWBhOnp.js} +12 -3
  69. package/dist/{slack-connector-CxpWagbT.js → slack-connector-CC3V2VXQ.js} +10 -9
  70. package/dist/{slack-event-processor-xFDG3US0.js → slack-event-processor-Cs_YuI3w.js} +2 -2
  71. package/dist/{slack-event-processor-BDTr62cN.d.ts → slack-event-processor-DmAlq4cV.d.ts} +0 -1
  72. package/dist/{slot-fields-D-pvMgTK.js → slot-fields-eCWizJqS.js} +4 -4
  73. package/dist/{sqlite-diagnostic-log-CwhXE9Qh.js → sqlite-diagnostic-log-Qs4cf9I_.js} +92 -100
  74. package/dist/sqlite-event-log-BYgCFMxC.d.ts +284 -0
  75. package/dist/{funnel-log-sqlite-sink-DLYkY0pZ.js → sqlite-event-log-eZuXKszJ.js} +53 -12
  76. package/dist/{yaml-render-DVEtYuF1.js → yaml-render-DxxRQT-V.js} +3 -3
  77. package/package.json +25 -10
  78. package/dist/broadcaster-DVNcw00K.d.ts +0 -138
  79. package/dist/connector-descriptor-CmyHlEiT.d.ts +0 -380
  80. package/dist/diagnostic-sql-reader-oXZnWFf_.js +0 -83
  81. package/dist/file-system-Wvzc2ePY.js +0 -9
  82. package/dist/logger-B6iyNbxM.js +0 -9
  83. package/dist/schedule-connector-schema-D7ZEKvit.d.ts +0 -49
  84. package/dist/schedule-connector-schema-DKEPZnVv.js +0 -31
  85. package/dist/settings-reader-9FcX3qS1.js +0 -11
  86. /package/dist/{connector-adapter-Dvs8N7ew.js → connector-adapter-BL2ehMej.js} +0 -0
  87. /package/dist/{connector-listener-mPGZYa8e.js → connector-listener-BcQMzY6Q.js} +0 -0
  88. /package/dist/{discord-connector-schema-B4YpWpR3.js → discord-connector-schema-saAGuPmJ.js} +0 -0
  89. /package/dist/{error-message-of-ColuYmAk.js → error-message-of-BG4ezZ_s.js} +0 -0
  90. /package/dist/{funnel-docs-Cd66sKyV.d.ts → funnel-docs-BJWawfWI.d.ts} +0 -0
  91. /package/dist/{gh-connector-schema-CAqIhzGr.js → gh-connector-schema-DmZTrzUu.js} +0 -0
  92. /package/dist/{logger-Bc_Ooypb.d.ts → logger-mMZvblTx.d.ts} +0 -0
  93. /package/dist/{loopback-fetch-CVNuN3YZ.js → loopback-fetch-HP-bo9Kv.js} +0 -0
  94. /package/dist/{node-http-client-u00atiKx.js → node-http-client-SX3xFh4h.js} +0 -0
  95. /package/dist/{node-process-runner-DxTvycoK.js → node-process-runner-D4rzPl8Q.js} +0 -0
  96. /package/dist/{resolve-connector-token-DxDG9mhf.js → resolve-connector-token-BwiJCu82.js} +0 -0
  97. /package/dist/{slack-connector-schema-Dem8to4P.js → slack-connector-schema-EGVuuqSX.js} +0 -0
package/dist/logger.d.ts CHANGED
@@ -1,263 +1,9 @@
1
- //#region lib/logger/funnel-log-entry.d.ts
2
- /**
3
- * Wrapper that `FunnelLog.emit` puts around every event before handing it
4
- * to a sink. `seq` is monotonic across the lifetime of the underlying store —
5
- * sinks persist it as the primary key so replay (and broadcaster seeding
6
- * after restart) is an indexed range scan, not a full table walk. `ts` is
7
- * epoch milliseconds. `event` is the caller-defined payload validated by the
8
- * Zod schema passed to the bus.
9
- */
10
- type FunnelLogEntry<E> = {
11
- seq: number;
12
- ts: number;
13
- event: E;
14
- };
15
- //#endregion
16
- //#region lib/logger/funnel-log-sink.d.ts
17
- /**
18
- * Relay sink. Receives records that already have a `seq` assigned by the
19
- * primary and stores or forwards them — memory ring, stdout, network push,
20
- * a second SQLite mirror, etc. Does not generate seq itself, so any number
21
- * can be attached and they all observe the same monotonic stream.
22
- *
23
- * `write` returns `void` on success or an `Error` the bus surfaces via
24
- * `onSinkError`. Throwing is also tolerated (the bus catches), but
25
- * returning is preferred so the failure path is part of the type.
26
- */
27
- type FunnelLogSink<E> = {
28
- write(record: FunnelLogEntry<E>): void | Error;
29
- close?(): void;
30
- };
31
- /**
32
- * Primary sink. Owns the canonical seq sequence for the bus. `insert` is
33
- * the atomic boundary — it assigns a seq strictly greater than every
34
- * previously assigned one, persists the record, and returns it. SQLite
35
- * implementations get atomicity for free by delegating to `INTEGER PRIMARY
36
- * KEY` so two processes sharing one database file see one monotonic
37
- * stream without bus-level coordination.
38
- *
39
- * `getMaxSeq` is the highest seq currently in the sink — used for
40
- * observability and for replay seeding by clients reading the store.
41
- */
42
- type FunnelLogPrimarySink<E> = {
43
- insert(input: {
44
- ts: number;
45
- event: E;
46
- }): FunnelLogEntry<E> | Error;
47
- getMaxSeq(): number;
48
- close?(): void;
49
- };
50
- //#endregion
1
+ import { c as EventLogValidator, d as EventLogEntry, i as MemoryEventLog, l as EventLogRelay, o as EventLog, s as EventLogProps, t as SqliteEventLog, u as EventLogStore } from "./sqlite-event-log-BYgCFMxC.js";
51
2
  //#region lib/logger/funnel-log.d.ts
52
- type Listener<E> = (record: FunnelLogEntry<E>) => void;
53
- type SinkErrorHandler<E> = (error: Error, record: FunnelLogEntry<E>, sink: FunnelLogSink<E>) => void;
54
- type FunnelLogValidator<E> = (event: unknown) => {
55
- success: true;
56
- data: E;
57
- } | {
58
- success: false;
59
- error: Error;
60
- };
61
- type Props$5<E> = {
62
- /** Validates each event before emission. Use `schema.safeParse` from any validation library, or a plain function. */validate: FunnelLogValidator<E>; /** Owns seq assignment + durability. Use `FunnelLogSqliteSink` for multi-process safety. */
63
- primary: FunnelLogPrimarySink<E>; /** Optional fanout for already-sequenced records (memory ring, stdout, network mirror). */
64
- relays?: ReadonlyArray<FunnelLogSink<E>>; /** Override for tests. Defaults to `Date.now`. */
65
- now?: () => number; /** Observer for relay failures. Default: silently swallow. */
66
- onSinkError?: SinkErrorHandler<E>;
67
- };
68
- /**
69
- * Validated event log bus. Three responsibilities and nothing else:
70
- * validate the event, delegate seq + persistence to the primary sink, and
71
- * fan the resulting record out to relays and live subscribers.
72
- *
73
- * Splitting "primary" from "relays" makes the seq invariant honest: there
74
- * is exactly one source of truth (the primary's atomic insert). Two
75
- * `FunnelLog` instances pointed at the same SQLite file therefore see
76
- * one monotonic stream without bus-level coordination. Relays mirror
77
- * already-sequenced records, so they can be added or removed without
78
- * affecting correctness.
79
- *
80
- * Failure isolation:
81
- * - Primary failure short-circuits emit and is returned to the caller.
82
- * - Relay failures never block the primary path — they surface via the
83
- * optional `onSinkError` callback so the caller can observe without
84
- * being interrupted.
85
- * - A subscriber that throws is contained; the rest of the fanout
86
- * completes normally.
87
- */
88
- declare class FunnelLog<E> {
89
- private readonly validate;
90
- private readonly primary;
91
- private readonly relays;
92
- private readonly now;
93
- private readonly onSinkError;
94
- private readonly listeners;
95
- constructor(props: Props$5<E>);
96
- emit(event: E): FunnelLogEntry<E> | Error;
97
- subscribe(listener: Listener<E>): () => void;
98
- getMaxSeq(): number;
99
- close(): void;
100
- private callPrimary;
101
- private fanOutToRelays;
102
- private callRelay;
103
- private fanOutToListeners;
104
- private callClose;
105
- }
106
- //#endregion
107
- //#region lib/logger/funnel-log-sqlite-sink.d.ts
108
- type IndexValues<I extends ReadonlyArray<string>> = Record<I[number], string | null>;
109
- /**
110
- * Constructor props. The shape narrows on `I`: when no indexes are
111
- * declared (the default), `extractIndexes` is forbidden; when indexes
112
- * are declared, both `indexes` and `extractIndexes` are required and
113
- * `extractIndexes` is type-checked against the index keys.
114
- */
115
- type Props$4<E, I extends ReadonlyArray<string>> = I extends readonly [] ? {
116
- path: string;
117
- maxRows?: number;
118
- maxAgeMs?: number;
119
- maxBytes?: number;
120
- targetBytes?: number;
121
- now?: () => number;
122
- indexes?: I;
123
- extractIndexes?: never;
124
- } : {
125
- path: string;
126
- maxRows?: number;
127
- maxAgeMs?: number;
128
- maxBytes?: number;
129
- targetBytes?: number;
130
- now?: () => number;
131
- indexes: I;
132
- extractIndexes: (event: E) => IndexValues<I>;
133
- };
134
- type QueryFilter<I extends ReadonlyArray<string>> = {
135
- /** Return only records with seq strictly greater than this. */sinceSeq?: number; /** Filter by the top-level `event.type` discriminator. */
136
- type?: string; /** Filter by indexed columns. Keys are constrained to the declared `indexes`. */
137
- where?: Partial<IndexValues<I>>; /** Maximum rows returned. Default 1000. */
138
- limit?: number;
139
- /**
140
- * Which end of the seq range to take when `limit` clips the result.
141
- * "asc" (default) returns the oldest matching rows; "desc" returns the
142
- * newest. Rows are always sorted ascending by seq before returning, so the
143
- * caller sees a chronological slice either way — "desc" just picks the tail.
144
- */
145
- order?: "asc" | "desc";
146
- };
147
- /**
148
- * SQLite-backed sink built on `bun:sqlite`. Implements both primary and
149
- * relay roles so the same instance can own seq generation for one bus and
150
- * mirror records from another (e.g. cross-process replication, restore
151
- * from a backup stream).
152
- *
153
- * Concurrency model: seq is `INTEGER PRIMARY KEY`, so SQLite assigns it
154
- * atomically via `lastInsertRowid`. Two `FunnelLog` instances pointed
155
- * at the same database file therefore see one monotonically increasing
156
- * seq stream without any bus-level coordination — the database itself is
157
- * the synchronization point.
158
- *
159
- * Schema is version-managed via `PRAGMA user_version`. Migrations are
160
- * append-only and run in a transaction on every construct so a partial
161
- * upgrade rolls back cleanly. Caller-defined `indexes` are layered on top
162
- * via `ALTER TABLE ADD COLUMN` + `CREATE INDEX IF NOT EXISTS`, so adding
163
- * a new index to an existing database is a no-downtime operation.
164
- *
165
- * Type safety: the second generic parameter `I` is the literal tuple of
166
- * index column names. `extractIndexes` and `query({ where })` are
167
- * both type-checked against this tuple, so a typo at the call site is a
168
- * compile-time error rather than a silent miss at runtime.
169
- *
170
- * Retention is bounded by `maxRows` and/or `maxAgeMs`. Both run on every
171
- * insert as a single indexed DELETE that no-ops below the cap.
172
- *
173
- * Bulk inserts use `insertMany`, which wraps the batch in one transaction
174
- * for ~10–100x throughput at the cost of one fsync per batch instead of
175
- * one per row.
176
- */
177
- declare class FunnelLogSqliteSink<E, const I extends ReadonlyArray<string> = readonly []> implements FunnelLogPrimarySink<E>, FunnelLogSink<E> {
178
- private readonly db;
179
- private readonly maxRows;
180
- private readonly maxAgeMs;
181
- private readonly maxBytes;
182
- private readonly targetBytes;
183
- private readonly now;
184
- private readonly indexes;
185
- private readonly extractIndexes;
186
- private readonly insertStmt;
187
- private readonly insertWithSeqStmt;
188
- private readonly maxSeqStmt;
189
- private readonly countStmt;
190
- private readonly trimRowsStmt;
191
- private readonly trimAgeStmt;
192
- private readonly trimOldestStmt;
193
- private insertsSinceByteCheck;
194
- constructor(props: Props$4<E, I>);
195
- insert(input: {
196
- ts: number;
197
- event: E;
198
- }): FunnelLogEntry<E> | Error;
199
- insertMany(inputs: ReadonlyArray<{
200
- ts: number;
201
- event: E;
202
- }>): FunnelLogEntry<E>[] | Error;
203
- write(record: FunnelLogEntry<E>): void | Error;
204
- getMaxSeq(): number;
205
- query(props?: QueryFilter<I>): FunnelLogEntry<E>[];
206
- /**
207
- * Current schema version. Useful for diagnostics and for tests that want
208
- * to verify migrations ran. Reads `PRAGMA user_version` once per call.
209
- */
210
- getSchemaVersion(): number;
211
- close(): void;
212
- private buildInsertParams;
213
- private appendWhereConditions;
214
- private trim;
215
- /**
216
- * Throttled byte-size enforcement. Only every BYTE_CHECK_INTERVAL inserts do
217
- * we measure the file; on overflow we estimate how many of the oldest rows to
218
- * drop to land near targetBytes (by the byte/row ratio), delete them in one
219
- * statement, then VACUUM once to return the freed pages to the filesystem (a
220
- * plain DELETE only frees pages inside the file). One DELETE + one VACUUM per
221
- * overflow keeps the expensive rewrite rare — the file must refill the whole
222
- * maxBytes→targetBytes delta before the next overflow can trigger.
223
- */
224
- private maybeTrimBytes;
225
- private byteSize;
226
- /** Drop every row and reclaim the file space. Used by `<log>.clear()`. */
227
- clear(): void;
228
- private syncIndexColumns;
229
- private migrate;
230
- }
231
- //#endregion
232
- //#region lib/logger/funnel-log-memory-sink.d.ts
233
- type Props$3 = {
234
- /** Hard cap on retained records. The oldest is evicted on overflow. 0 disables retention. */capacity?: number;
235
- };
236
- /**
237
- * In-memory ring buffer that doubles as primary or relay. As primary it
238
- * owns its own seq counter (single-process only — for multi-process
239
- * safety, use `FunnelLogSqliteSink` as primary and place this as a
240
- * relay). As relay it accepts whatever seq the primary assigned and
241
- * advances its own counter to match, so `getMaxSeq` stays meaningful.
242
- *
243
- * Useful as a test double, as a short-window replay buffer paired with a
244
- * persistent primary (covering reconnects without round-tripping disk),
245
- * or as a backing store for live subscribers.
246
- */
247
- declare class FunnelLogMemorySink<E> implements FunnelLogPrimarySink<E>, FunnelLogSink<E> {
248
- private readonly capacity;
249
- private readonly buffer;
250
- private seq;
251
- constructor(props?: Props$3);
252
- insert(input: {
253
- ts: number;
254
- event: E;
255
- }): FunnelLogEntry<E>;
256
- write(record: FunnelLogEntry<E>): void;
257
- getMaxSeq(): number;
258
- query(): ReadonlyArray<FunnelLogEntry<E>>;
259
- clear(): void;
260
- private append;
3
+ type FunnelLogValidator<E> = EventLogValidator<E>;
4
+ declare class FunnelLog<E> extends EventLog<E> {
5
+ constructor(props: EventLogProps<E>);
6
+ emit(event: E): EventLogEntry<E> | Error;
261
7
  }
262
8
  //#endregion
263
9
  //#region lib/logger/funnel-text-entry.d.ts
@@ -294,9 +40,13 @@ type FunnelTextWriter = {
294
40
  //#region lib/logger/funnel-text-log.d.ts
295
41
  type WriteErrorHandler = (error: Error, record: FunnelTextEntry) => void;
296
42
  type Props$2 = {
297
- /** Where records go. Use `FunnelTextStdoutWriter`, `FunnelTextFileWriter`, or your own. */writer: FunnelTextWriter; /** Minimum level to emit. Lower-rank records are dropped. Default: "info". */
298
- level?: FunnelTextLevel; /** Override for tests. Defaults to `Date.now`. */
299
- now?: () => number; /** Observer for writer failures. Default: silently swallow. */
43
+ /** Where records go. Use `FunnelTextStdoutWriter`, `FunnelTextFileWriter`, or your own. */
44
+ writer: FunnelTextWriter;
45
+ /** Minimum level to emit. Lower-rank records are dropped. Default: "info". */
46
+ level?: FunnelTextLevel;
47
+ /** Override for tests. Defaults to `Date.now`. */
48
+ now?: () => number;
49
+ /** Observer for writer failures. Default: silently swallow. */
300
50
  onWriteError?: WriteErrorHandler;
301
51
  };
302
52
  /**
@@ -334,7 +84,8 @@ declare class FunnelTextLog {
334
84
  //#endregion
335
85
  //#region lib/logger/funnel-text-file-writer.d.ts
336
86
  type Props$1 = {
337
- /** Filesystem path. Parent directory is created on construct. */path: string;
87
+ /** Filesystem path. Parent directory is created on construct. */
88
+ path: string;
338
89
  /**
339
90
  * Optional size cap in bytes. When the next write would push the file
340
91
  * over the cap, the existing file becomes `<path>.1` (replacing any
@@ -368,7 +119,8 @@ type Stream = {
368
119
  write(s: string): void;
369
120
  };
370
121
  type Props = {
371
- /** Override for tests. Defaults to `process.stdout`. */out?: Stream;
122
+ /** Override for tests. Defaults to `process.stdout`. */
123
+ out?: Stream;
372
124
  };
373
125
  /**
374
126
  * Writes one JSON line per record to stdout. Useful as the default writer
@@ -381,4 +133,4 @@ declare class FunnelTextStdoutWriter implements FunnelTextWriter {
381
133
  write(record: FunnelTextEntry): void;
382
134
  }
383
135
  //#endregion
384
- export { FunnelLog, FunnelLogEntry, FunnelLogMemorySink, FunnelLogPrimarySink, FunnelLogSink, FunnelLogSqliteSink, FunnelLogValidator, FunnelTextEntry, FunnelTextFileWriter, FunnelTextLevel, FunnelTextLog, FunnelTextStdoutWriter, FunnelTextWriter };
136
+ export { FunnelLog, type EventLogEntry as FunnelLogEntry, MemoryEventLog as FunnelLogMemorySink, type EventLogStore as FunnelLogPrimarySink, type EventLogRelay as FunnelLogSink, SqliteEventLog as FunnelLogSqliteSink, FunnelLogValidator, FunnelTextEntry, FunnelTextFileWriter, FunnelTextLevel, FunnelTextLog, FunnelTextStdoutWriter, FunnelTextWriter };
package/dist/logger.js CHANGED
@@ -1,149 +1,14 @@
1
- import { t as FunnelLogSqliteSink } from "./funnel-log-sqlite-sink-DLYkY0pZ.js";
1
+ import { t as SqliteEventLog } from "./sqlite-event-log-eZuXKszJ.js";
2
+ import { n as EventLog, t as MemoryEventLog } from "./memory-event-log-BY8XjFzi.js";
2
3
  import { dirname } from "node:path";
3
4
  import { appendFileSync, existsSync, mkdirSync, renameSync, statSync, unlinkSync } from "node:fs";
4
5
  //#region lib/logger/funnel-log.ts
5
- /**
6
- * Validated event log bus. Three responsibilities and nothing else:
7
- * validate the event, delegate seq + persistence to the primary sink, and
8
- * fan the resulting record out to relays and live subscribers.
9
- *
10
- * Splitting "primary" from "relays" makes the seq invariant honest: there
11
- * is exactly one source of truth (the primary's atomic insert). Two
12
- * `FunnelLog` instances pointed at the same SQLite file therefore see
13
- * one monotonic stream without bus-level coordination. Relays mirror
14
- * already-sequenced records, so they can be added or removed without
15
- * affecting correctness.
16
- *
17
- * Failure isolation:
18
- * - Primary failure short-circuits emit and is returned to the caller.
19
- * - Relay failures never block the primary path — they surface via the
20
- * optional `onSinkError` callback so the caller can observe without
21
- * being interrupted.
22
- * - A subscriber that throws is contained; the rest of the fanout
23
- * completes normally.
24
- */
25
- var FunnelLog = class {
26
- validate;
27
- primary;
28
- relays;
29
- now;
30
- onSinkError;
31
- listeners = /* @__PURE__ */ new Set();
6
+ var FunnelLog = class extends EventLog {
32
7
  constructor(props) {
33
- this.validate = props.validate;
34
- this.primary = props.primary;
35
- this.relays = props.relays ?? [];
36
- this.now = props.now ?? (() => Date.now());
37
- this.onSinkError = props.onSinkError ?? null;
8
+ super(props);
38
9
  }
39
10
  emit(event) {
40
- const parsed = this.validate(event);
41
- if (!parsed.success) return parsed.error;
42
- const result = this.callPrimary(parsed.data);
43
- if (result instanceof Error) return result;
44
- this.fanOutToRelays(result);
45
- this.fanOutToListeners(result);
46
- return result;
47
- }
48
- subscribe(listener) {
49
- this.listeners.add(listener);
50
- return () => {
51
- this.listeners.delete(listener);
52
- };
53
- }
54
- getMaxSeq() {
55
- return this.primary.getMaxSeq();
56
- }
57
- close() {
58
- this.listeners.clear();
59
- this.callClose(this.primary);
60
- for (const relay of this.relays) this.callClose(relay);
61
- }
62
- callPrimary(event) {
63
- try {
64
- return this.primary.insert({
65
- ts: this.now(),
66
- event
67
- });
68
- } catch (e) {
69
- return e instanceof Error ? e : new Error(String(e));
70
- }
71
- }
72
- fanOutToRelays(record) {
73
- for (const relay of this.relays) {
74
- const error = this.callRelay(relay, record);
75
- if (!error) continue;
76
- if (this.onSinkError) this.onSinkError(error, record, relay);
77
- }
78
- }
79
- callRelay(relay, record) {
80
- try {
81
- const outcome = relay.write(record);
82
- return outcome instanceof Error ? outcome : null;
83
- } catch (e) {
84
- return e instanceof Error ? e : new Error(String(e));
85
- }
86
- }
87
- fanOutToListeners(record) {
88
- for (const listener of this.listeners) try {
89
- listener(record);
90
- } catch {}
91
- }
92
- callClose(sink) {
93
- if (!sink.close) return;
94
- try {
95
- sink.close();
96
- } catch {}
97
- }
98
- };
99
- //#endregion
100
- //#region lib/logger/funnel-log-memory-sink.ts
101
- /**
102
- * In-memory ring buffer that doubles as primary or relay. As primary it
103
- * owns its own seq counter (single-process only — for multi-process
104
- * safety, use `FunnelLogSqliteSink` as primary and place this as a
105
- * relay). As relay it accepts whatever seq the primary assigned and
106
- * advances its own counter to match, so `getMaxSeq` stays meaningful.
107
- *
108
- * Useful as a test double, as a short-window replay buffer paired with a
109
- * persistent primary (covering reconnects without round-tripping disk),
110
- * or as a backing store for live subscribers.
111
- */
112
- var FunnelLogMemorySink = class {
113
- capacity;
114
- buffer = [];
115
- seq = 0;
116
- constructor(props = {}) {
117
- this.capacity = Math.max(0, props.capacity ?? 1e3);
118
- }
119
- insert(input) {
120
- this.seq += 1;
121
- const record = {
122
- seq: this.seq,
123
- ts: input.ts,
124
- event: input.event
125
- };
126
- this.append(record);
127
- return record;
128
- }
129
- write(record) {
130
- if (record.seq > this.seq) this.seq = record.seq;
131
- this.append(record);
132
- }
133
- getMaxSeq() {
134
- return this.seq;
135
- }
136
- query() {
137
- return this.buffer;
138
- }
139
- clear() {
140
- this.buffer.length = 0;
141
- this.seq = 0;
142
- }
143
- append(record) {
144
- if (this.capacity === 0) return;
145
- this.buffer.push(record);
146
- if (this.buffer.length > this.capacity) this.buffer.shift();
11
+ return this.append(event);
147
12
  }
148
13
  };
149
14
  //#endregion
@@ -278,4 +143,4 @@ var FunnelTextStdoutWriter = class {
278
143
  }
279
144
  };
280
145
  //#endregion
281
- export { FunnelLog, FunnelLogMemorySink, FunnelLogSqliteSink, FunnelTextFileWriter, FunnelTextLog, FunnelTextStdoutWriter };
146
+ export { FunnelLog, MemoryEventLog as FunnelLogMemorySink, SqliteEventLog as FunnelLogSqliteSink, FunnelTextFileWriter, FunnelTextLog, FunnelTextStdoutWriter };
@@ -0,0 +1,147 @@
1
+ //#region lib/event-log/event-log.ts
2
+ /**
3
+ * Validated event log. Three responsibilities and nothing else:
4
+ * validate the event, delegate seq + persistence to the primary sink, and
5
+ * fan the resulting record out to relays and live subscribers.
6
+ *
7
+ * Splitting "primary" from "relays" makes the seq invariant honest: there
8
+ * is exactly one source of truth (the primary's atomic insert). Two
9
+ * `EventLog` instances pointed at the same SQLite file therefore see
10
+ * one monotonic stream without coordinating with each other. Relays mirror
11
+ * already-sequenced records, so they can be added or removed without
12
+ * affecting correctness.
13
+ *
14
+ * Failure isolation:
15
+ * - Primary failure short-circuits append and is returned to the caller.
16
+ * - Relay failures never block the primary path — they surface via the
17
+ * optional `onSinkError` callback so the caller can observe without
18
+ * being interrupted.
19
+ * - A subscriber that throws is contained; the rest of the fanout
20
+ * completes normally.
21
+ */
22
+ var EventLog = class {
23
+ validate;
24
+ primary;
25
+ relays;
26
+ now;
27
+ onSinkError;
28
+ listeners = /* @__PURE__ */ new Set();
29
+ constructor(props) {
30
+ this.validate = props.validate;
31
+ this.primary = props.primary;
32
+ this.relays = props.relays ?? [];
33
+ this.now = props.now ?? (() => Date.now());
34
+ this.onSinkError = props.onSinkError ?? null;
35
+ }
36
+ append(event) {
37
+ const parsed = this.validate(event);
38
+ if (!parsed.success) return parsed.error;
39
+ const result = this.callPrimary(parsed.data);
40
+ if (result instanceof Error) return result;
41
+ this.fanOutToRelays(result);
42
+ this.fanOutToListeners(result);
43
+ return result;
44
+ }
45
+ subscribe(listener) {
46
+ this.listeners.add(listener);
47
+ return () => {
48
+ this.listeners.delete(listener);
49
+ };
50
+ }
51
+ getMaxSeq() {
52
+ return this.primary.getMaxSeq();
53
+ }
54
+ close() {
55
+ this.listeners.clear();
56
+ this.callClose(this.primary);
57
+ for (const relay of this.relays) this.callClose(relay);
58
+ }
59
+ callPrimary(event) {
60
+ try {
61
+ return this.primary.insert({
62
+ ts: this.now(),
63
+ event
64
+ });
65
+ } catch (e) {
66
+ return e instanceof Error ? e : new Error(String(e));
67
+ }
68
+ }
69
+ fanOutToRelays(record) {
70
+ for (const relay of this.relays) {
71
+ const error = this.callRelay(relay, record);
72
+ if (!error) continue;
73
+ if (this.onSinkError) this.onSinkError(error, record, relay);
74
+ }
75
+ }
76
+ callRelay(relay, record) {
77
+ try {
78
+ const outcome = relay.write(record);
79
+ return outcome instanceof Error ? outcome : null;
80
+ } catch (e) {
81
+ return e instanceof Error ? e : new Error(String(e));
82
+ }
83
+ }
84
+ fanOutToListeners(record) {
85
+ for (const listener of this.listeners) try {
86
+ listener(record);
87
+ } catch {}
88
+ }
89
+ callClose(sink) {
90
+ if (!sink.close) return;
91
+ try {
92
+ sink.close();
93
+ } catch {}
94
+ }
95
+ };
96
+ //#endregion
97
+ //#region lib/event-log/memory-event-log.ts
98
+ /**
99
+ * In-memory ring buffer that doubles as primary or relay. As primary it
100
+ * owns its own seq counter (single-process only — for multi-process
101
+ * safety, use `SqliteEventLog` as primary and place this as a
102
+ * relay). As relay it accepts whatever seq the primary assigned and
103
+ * advances its own counter to match, so `getMaxSeq` stays meaningful.
104
+ *
105
+ * Useful as a test double, as a short-window replay buffer paired with a
106
+ * persistent primary (covering reconnects without round-tripping disk),
107
+ * or as a backing store for live subscribers.
108
+ */
109
+ var MemoryEventLog = class {
110
+ capacity;
111
+ buffer = [];
112
+ seq = 0;
113
+ constructor(props = {}) {
114
+ this.capacity = Math.max(0, props.capacity ?? 1e3);
115
+ }
116
+ insert(input) {
117
+ this.seq += 1;
118
+ const record = {
119
+ seq: this.seq,
120
+ ts: input.ts,
121
+ event: input.event
122
+ };
123
+ this.append(record);
124
+ return record;
125
+ }
126
+ write(record) {
127
+ if (record.seq > this.seq) this.seq = record.seq;
128
+ this.append(record);
129
+ }
130
+ getMaxSeq() {
131
+ return this.seq;
132
+ }
133
+ query() {
134
+ return this.buffer;
135
+ }
136
+ clear() {
137
+ this.buffer.length = 0;
138
+ this.seq = 0;
139
+ }
140
+ append(record) {
141
+ if (this.capacity === 0) return;
142
+ this.buffer.push(record);
143
+ if (this.buffer.length > this.capacity) this.buffer.shift();
144
+ }
145
+ };
146
+ //#endregion
147
+ export { EventLog as n, MemoryEventLog as t };
@@ -1,7 +1,6 @@
1
- import { n as FunnelFileSystem } from "./file-system-DHgbuTFb.js";
2
- import { r as FunnelChannels } from "./channels-DR90RmUP.js";
1
+ import { n as FunnelFileSystem } from "./file-system-BNtl997T.js";
2
+ import { r as FunnelChannels } from "./channels-B0cifCfw.js";
3
3
  import { z } from "zod";
4
-
5
4
  //#region lib/services/local-config/local-config-schema.d.ts
6
5
  declare const connectorSpecSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
7
6
  type: z.ZodLiteral<"slack">;
@@ -1,6 +1,13 @@
1
- import { t as FunnelFileSystem } from "./file-system-Wvzc2ePY.js";
2
1
  import { basename, dirname } from "node:path";
3
2
  import { appendFileSync, chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
3
+ //#region lib/engine/fs/file-system.ts
4
+ /**
5
+ * Filesystem boundary used everywhere funnel reads or writes.
6
+ * Default is NodeFunnelFileSystem (real `node:fs`); MemoryFunnelFileSystem
7
+ * provides a sandbox for tests and embedded use.
8
+ */
9
+ var FunnelFileSystem = class {};
10
+ //#endregion
4
11
  //#region lib/engine/fs/node-file-system.ts
5
12
  const SECRET_MODE = 384;
6
13
  /**
@@ -91,7 +98,8 @@ const acquireLock = (lockPath) => {
91
98
  if (!isErrnoCode(error, "EEXIST")) throw error;
92
99
  if (performance.now() >= deadline) throw new Error(`failed to acquire file lock ${lockPath} within ${LOCK_TIMEOUT_MS}ms`);
93
100
  breakIfStale(lockPath);
94
- sleepSyncMs(Math.min(LOCK_RETRY_MAX_MS, LOCK_RETRY_BASE_MS * 2 ** Math.min(attempt, 4)));
101
+ const wait = Math.min(LOCK_RETRY_MAX_MS, LOCK_RETRY_BASE_MS * 2 ** Math.min(attempt, 4));
102
+ sleepSyncMs(wait);
95
103
  attempt = attempt + 1;
96
104
  }
97
105
  };
@@ -171,4 +179,4 @@ const atomicWrite = (path, data, mode) => {
171
179
  }
172
180
  };
173
181
  //#endregion
174
- export { NodeFunnelFileSystem as t };
182
+ export { FunnelFileSystem as n, NodeFunnelFileSystem as t };
@@ -1,7 +1,6 @@
1
- import { n as FunnelFileSystem } from "./file-system-DHgbuTFb.js";
2
- import { r as ProfileConfig } from "./settings-schema-IShSMm5G.js";
3
- import { n as FunnelIdGenerator, t as FunnelSettingsReader } from "./settings-reader-BnKIbmsD.js";
4
-
1
+ import { n as FunnelFileSystem } from "./file-system-BNtl997T.js";
2
+ import { r as ProfileConfig } from "./settings-schema-B-CKLuST.js";
3
+ import { n as FunnelIdGenerator, t as FunnelSettingsReader } from "./settings-reader-CyTpbAre.js";
5
4
  //#region lib/engine/profiles/profiles.d.ts
6
5
  type Deps = {
7
6
  store: FunnelSettingsReader;