sensemaking 0.24.4 → 0.24.6

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 (130) hide show
  1. package/README.md +16 -12
  2. package/dist/cjs/cli/status.js +14 -24
  3. package/dist/cjs/cli/status.js.map +1 -1
  4. package/dist/cjs/commands/search.js +22 -0
  5. package/dist/cjs/commands/search.js.map +1 -1
  6. package/dist/cjs/config/types.js.map +1 -1
  7. package/dist/cjs/errors.d.cts +1 -1
  8. package/dist/cjs/errors.d.ts +1 -1
  9. package/dist/cjs/errors.js.map +1 -1
  10. package/dist/cjs/lib/watch-notification.d.cts +1 -0
  11. package/dist/cjs/lib/watch-notification.d.ts +1 -0
  12. package/dist/cjs/lib/watch-notification.js +15 -0
  13. package/dist/cjs/lib/watch-notification.js.map +1 -0
  14. package/dist/cjs/lib/worker-file.d.cts +1 -0
  15. package/dist/cjs/lib/worker-file.d.ts +1 -0
  16. package/dist/cjs/lib/worker-file.js +34 -0
  17. package/dist/cjs/lib/worker-file.js.map +1 -0
  18. package/dist/cjs/output/search-error.js +38 -0
  19. package/dist/cjs/output/search-error.js.map +1 -1
  20. package/dist/cjs/scan/pool.js +2 -23
  21. package/dist/cjs/scan/pool.js.map +1 -1
  22. package/dist/cjs/store/builder.d.cts +2 -1
  23. package/dist/cjs/store/builder.d.ts +2 -1
  24. package/dist/cjs/store/builder.js +4 -4
  25. package/dist/cjs/store/builder.js.map +1 -1
  26. package/dist/cjs/store/duckdb/lexical.d.cts +1 -0
  27. package/dist/cjs/store/duckdb/lexical.d.ts +1 -0
  28. package/dist/cjs/store/duckdb/lexical.js +28 -9
  29. package/dist/cjs/store/duckdb/lexical.js.map +1 -1
  30. package/dist/cjs/store/duckdb/native.d.cts +2 -0
  31. package/dist/cjs/store/duckdb/native.d.ts +2 -0
  32. package/dist/cjs/store/duckdb/native.js +11 -1
  33. package/dist/cjs/store/duckdb/native.js.map +1 -1
  34. package/dist/cjs/store/duckdb/ordered-bm25.d.cts +17 -0
  35. package/dist/cjs/store/duckdb/ordered-bm25.d.ts +17 -0
  36. package/dist/cjs/store/duckdb/ordered-bm25.js +238 -0
  37. package/dist/cjs/store/duckdb/ordered-bm25.js.map +1 -0
  38. package/dist/cjs/store/lock-wait.d.cts +2 -2
  39. package/dist/cjs/store/lock-wait.d.ts +2 -2
  40. package/dist/cjs/store/lock-wait.js +14 -11
  41. package/dist/cjs/store/lock-wait.js.map +1 -1
  42. package/dist/cjs/store/native.d.cts +4 -0
  43. package/dist/cjs/store/native.d.ts +4 -0
  44. package/dist/cjs/store/native.js +64 -9
  45. package/dist/cjs/store/native.js.map +1 -1
  46. package/dist/cjs/store/open.js +11 -7
  47. package/dist/cjs/store/open.js.map +1 -1
  48. package/dist/cjs/store/reconcile.d.cts +5 -1
  49. package/dist/cjs/store/reconcile.d.ts +5 -1
  50. package/dist/cjs/store/reconcile.js +5 -4
  51. package/dist/cjs/store/reconcile.js.map +1 -1
  52. package/dist/cjs/store/turso/lexical.js +5 -1
  53. package/dist/cjs/store/turso/lexical.js.map +1 -1
  54. package/dist/cjs/text/segment.js +168 -3
  55. package/dist/cjs/text/segment.js.map +1 -1
  56. package/dist/cjs/watch-claim.d.cts +39 -0
  57. package/dist/cjs/watch-claim.d.ts +39 -0
  58. package/dist/cjs/watch-claim.js +446 -0
  59. package/dist/cjs/watch-claim.js.map +1 -0
  60. package/dist/cjs/watch.js +246 -147
  61. package/dist/cjs/watch.js.map +1 -1
  62. package/dist/cjs/workers/watch-heartbeat.d.cts +1 -0
  63. package/dist/cjs/workers/watch-heartbeat.d.ts +1 -0
  64. package/dist/cjs/workers/watch-heartbeat.js +308 -0
  65. package/dist/cjs/workers/watch-heartbeat.js.map +1 -0
  66. package/dist/esm/cli/status.js +6 -5
  67. package/dist/esm/cli/status.js.map +1 -1
  68. package/dist/esm/commands/search.js +21 -0
  69. package/dist/esm/commands/search.js.map +1 -1
  70. package/dist/esm/config/types.js.map +1 -1
  71. package/dist/esm/errors.d.ts +1 -1
  72. package/dist/esm/errors.js.map +1 -1
  73. package/dist/esm/lib/watch-notification.d.ts +1 -0
  74. package/dist/esm/lib/watch-notification.js +4 -0
  75. package/dist/esm/lib/watch-notification.js.map +1 -0
  76. package/dist/esm/lib/worker-file.d.ts +1 -0
  77. package/dist/esm/lib/worker-file.js +24 -0
  78. package/dist/esm/lib/worker-file.js.map +1 -0
  79. package/dist/esm/output/search-error.js +21 -0
  80. package/dist/esm/output/search-error.js.map +1 -1
  81. package/dist/esm/scan/pool.js +2 -22
  82. package/dist/esm/scan/pool.js.map +1 -1
  83. package/dist/esm/store/builder.d.ts +2 -1
  84. package/dist/esm/store/builder.js +4 -4
  85. package/dist/esm/store/builder.js.map +1 -1
  86. package/dist/esm/store/duckdb/lexical.d.ts +1 -0
  87. package/dist/esm/store/duckdb/lexical.js +16 -7
  88. package/dist/esm/store/duckdb/lexical.js.map +1 -1
  89. package/dist/esm/store/duckdb/native.d.ts +2 -0
  90. package/dist/esm/store/duckdb/native.js +5 -1
  91. package/dist/esm/store/duckdb/native.js.map +1 -1
  92. package/dist/esm/store/duckdb/ordered-bm25.d.ts +17 -0
  93. package/dist/esm/store/duckdb/ordered-bm25.js +56 -0
  94. package/dist/esm/store/duckdb/ordered-bm25.js.map +1 -0
  95. package/dist/esm/store/lock-wait.d.ts +2 -2
  96. package/dist/esm/store/lock-wait.js +17 -14
  97. package/dist/esm/store/lock-wait.js.map +1 -1
  98. package/dist/esm/store/native.d.ts +4 -0
  99. package/dist/esm/store/native.js +60 -6
  100. package/dist/esm/store/native.js.map +1 -1
  101. package/dist/esm/store/open.js +9 -6
  102. package/dist/esm/store/open.js.map +1 -1
  103. package/dist/esm/store/reconcile.d.ts +5 -1
  104. package/dist/esm/store/reconcile.js +4 -3
  105. package/dist/esm/store/reconcile.js.map +1 -1
  106. package/dist/esm/store/turso/lexical.js +5 -1
  107. package/dist/esm/store/turso/lexical.js.map +1 -1
  108. package/dist/esm/text/segment.js +130 -4
  109. package/dist/esm/text/segment.js.map +1 -1
  110. package/dist/esm/watch-claim.d.ts +39 -0
  111. package/dist/esm/watch-claim.js +176 -0
  112. package/dist/esm/watch-claim.js.map +1 -0
  113. package/dist/esm/watch.js +169 -118
  114. package/dist/esm/watch.js.map +1 -1
  115. package/dist/esm/workers/watch-heartbeat.d.ts +1 -0
  116. package/dist/esm/workers/watch-heartbeat.js +106 -0
  117. package/dist/esm/workers/watch-heartbeat.js.map +1 -0
  118. package/package.json +2 -2
  119. package/skills/sense/SKILL.md +105 -104
  120. package/skills/sense/references/search.md +47 -0
  121. package/skills/sense/references/sql.md +120 -0
  122. package/skills/sense/references/stores/duckdb.md +35 -0
  123. package/skills/sense/references/stores/sqlite.md +66 -0
  124. package/skills/sense/references/stores/turso.md +36 -0
  125. package/skills/sense-setup/SKILL.md +93 -34
  126. package/skills/sense-setup/references/embeddings.md +51 -0
  127. package/skills/sense-setup/references/store-benchmarks.md +16 -0
  128. package/skills/sense-setup/references/stores/duckdb.md +28 -0
  129. package/skills/sense-setup/references/stores/sqlite.md +21 -0
  130. package/skills/sense-setup/references/stores/turso.md +25 -0
package/dist/cjs/watch.js CHANGED
@@ -9,10 +9,11 @@ Object.defineProperty(exports, "runWatch", {
9
9
  }
10
10
  });
11
11
  var _nodefs = require("node:fs");
12
- var _indexts = require("./config/index.js");
13
12
  var _errorsts = require("./errors.js");
14
13
  var _guardedtickts = require("./lib/guarded-tick.js");
15
- var _indexts1 = require("./store/index.js");
14
+ var _watchnotificationts = require("./lib/watch-notification.js");
15
+ var _indexts = require("./store/index.js");
16
+ var _watchclaimts = require("./watch-claim.js");
16
17
  function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
17
18
  try {
18
19
  var info = gen[key](arg);
@@ -39,6 +40,12 @@ function _async_to_generator(fn) {
39
40
  });
40
41
  };
41
42
  }
43
+ function _instanceof(left, right) {
44
+ "@swc/helpers - instanceof";
45
+ if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
46
+ return !!right[Symbol.hasInstance](left);
47
+ } else return left instanceof right;
48
+ }
42
49
  function _ts_generator(thisArg, body) {
43
50
  var f, y, t, _ = {
44
51
  label: 0,
@@ -140,11 +147,9 @@ function _ts_generator(thisArg, body) {
140
147
  }
141
148
  // Watch is a cache pre-warmer, not a correctness mechanism: open() always reconciles anyway, so any fs event just triggers a debounced full reconcile.
142
149
  var DEBOUNCE_MS = 200;
143
- var HEARTBEAT_INTERVAL_MS = 5000;
144
- var STALE_HEARTBEAT_MS = 15000;
145
150
  function runWatch(_0) {
146
151
  return _async_to_generator(function(cfg) {
147
- var opts, _opts_onEvent, _opts_debounceMs, _opts_heartbeatIntervalMs, _cfg_rootDir, onEvent, debounceMs, heartbeatIntervalMs, _ref, initialStore, dbPath, initialWarnings, initialParsed, rootDir, existingHeartbeat, age, initialTotal, _tmp, stopping, running, queuedAlways, tick, startCycle, requestCycle, debounceTimer, scheduleReconcile, watcher, heartbeatTimer;
152
+ var opts, _opts_onEvent, _opts_debounceMs, _opts_heartbeatIntervalMs, _cfg_rootDir, _cfg_configDir, _opts_force, onEvent, debounceMs, reconcileIntervalMs, claimIntervalMs, rootDir, configDir, claimFailure, requestShutdown, claim, claimCloseStarted, closeClaim, _ref, initialStore, dbPath, initialWarnings, initialParsed, initialTotal, _tmp, stopping, running, queuedAlways, tick, startCycle, requestCycle, debounceTimer, scheduleReconcile, watcher, reconcileTimer, err, closeError;
148
153
  var _arguments = arguments;
149
154
  return _ts_generator(this, function(_state) {
150
155
  switch(_state.label){
@@ -152,45 +157,57 @@ function runWatch(_0) {
152
157
  opts = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
153
158
  onEvent = (_opts_onEvent = opts.onEvent) !== null && _opts_onEvent !== void 0 ? _opts_onEvent : function() {};
154
159
  debounceMs = (_opts_debounceMs = opts.debounceMs) !== null && _opts_debounceMs !== void 0 ? _opts_debounceMs : DEBOUNCE_MS;
155
- heartbeatIntervalMs = (_opts_heartbeatIntervalMs = opts.heartbeatIntervalMs) !== null && _opts_heartbeatIntervalMs !== void 0 ? _opts_heartbeatIntervalMs : HEARTBEAT_INTERVAL_MS;
156
- return [
157
- 4,
158
- (0, _indexts1.openStore)(cfg)
159
- ];
160
- case 1:
161
- _ref = _state.sent(), initialStore = _ref.store, dbPath = _ref.dbPath, initialWarnings = _ref.warnings, initialParsed = _ref.parsed;
162
- // openStore normalizes the legacy direct-API baseDir before a watcher touches the filesystem.
160
+ reconcileIntervalMs = (_opts_heartbeatIntervalMs = opts.heartbeatIntervalMs) !== null && _opts_heartbeatIntervalMs !== void 0 ? _opts_heartbeatIntervalMs : _watchclaimts.WATCH_HEARTBEAT_INTERVAL_MS;
161
+ if (!Number.isFinite(reconcileIntervalMs) || reconcileIntervalMs <= 0) throw new _errorsts.SenseError('CONFIG_INVALID', 'watch heartbeatIntervalMs must be a finite positive number');
162
+ // Reconciliation may be parked for a caller, but live ownership must renew before fixed expiry.
163
+ claimIntervalMs = Math.min(reconcileIntervalMs, _watchclaimts.WATCH_HEARTBEAT_INTERVAL_MS);
163
164
  rootDir = (_cfg_rootDir = cfg.rootDir) !== null && _cfg_rootDir !== void 0 ? _cfg_rootDir : cfg.baseDir;
165
+ configDir = (_cfg_configDir = cfg.configDir) !== null && _cfg_configDir !== void 0 ? _cfg_configDir : rootDir;
166
+ claimFailure = null;
164
167
  return [
165
168
  4,
166
- (0, _indexts1.getMeta)(initialStore, 'watch_heartbeat')
169
+ (0, _watchclaimts.startWatchClaim)(configDir, (_opts_force = opts.force) !== null && _opts_force !== void 0 ? _opts_force : false, claimIntervalMs, function(err) {
170
+ claimFailure !== null && claimFailure !== void 0 ? claimFailure : claimFailure = err;
171
+ requestShutdown === null || requestShutdown === void 0 ? void 0 : requestShutdown(err);
172
+ })
167
173
  ];
174
+ case 1:
175
+ claim = _state.sent();
176
+ claimCloseStarted = false;
177
+ closeClaim = function closeClaim() {
178
+ claimCloseStarted = true;
179
+ return claim.close();
180
+ };
181
+ _state.label = 2;
168
182
  case 2:
169
- existingHeartbeat = _state.sent();
170
- if (!(existingHeartbeat && !opts.force)) return [
171
- 3,
172
- 4
173
- ];
174
- age = Date.now() - Date.parse(existingHeartbeat);
175
- if (!(age >= 0 && age < STALE_HEARTBEAT_MS)) return [
176
- 3,
177
- 4
178
- ];
183
+ _state.trys.push([
184
+ 2,
185
+ 12,
186
+ 17,
187
+ 18
188
+ ]);
179
189
  return [
180
190
  4,
181
- initialStore.close()
191
+ (0, _indexts.openStore)(cfg)
182
192
  ];
183
193
  case 3:
184
- _state.sent();
185
- throw new _errorsts.SenseError('WATCH_ACTIVE', "another watcher appears active (heartbeat ".concat(Math.round(age / 1000), "s ago); use --force to override"));
194
+ _ref = _state.sent(), initialStore = _ref.store, dbPath = _ref.dbPath, initialWarnings = _ref.warnings, initialParsed = _ref.parsed;
195
+ initialTotal = 0;
196
+ _state.label = 4;
186
197
  case 4:
198
+ _state.trys.push([
199
+ 4,
200
+ ,
201
+ 8,
202
+ 10
203
+ ]);
187
204
  if (!(initialWarnings.length > 0 || initialParsed > 0)) return [
188
205
  3,
189
206
  6
190
207
  ];
191
208
  return [
192
209
  4,
193
- (0, _indexts1.docCount)(initialStore)
210
+ (0, _indexts.docCount)(initialStore)
194
211
  ];
195
212
  case 5:
196
213
  _tmp = _state.sent();
@@ -202,38 +219,24 @@ function runWatch(_0) {
202
219
  _tmp = 0;
203
220
  _state.label = 7;
204
221
  case 7:
222
+ // total is read before close: an event fires only once nothing is held open again.
205
223
  initialTotal = _tmp;
206
224
  return [
207
- 4,
208
- (0, _indexts1.setMeta)(initialStore, 'watch_heartbeat', new Date().toISOString())
225
+ 3,
226
+ 10
209
227
  ];
210
228
  case 8:
211
- _state.sent();
212
229
  return [
213
230
  4,
214
- (0, _indexts1.setMeta)(initialStore, 'watch_pid', String(process.pid))
231
+ initialStore.close()
215
232
  ];
216
233
  case 9:
217
234
  _state.sent();
218
235
  return [
219
- 4,
220
- initialStore.close()
236
+ 7
221
237
  ];
222
238
  case 10:
223
- _state.sent();
224
- onEvent({
225
- type: 'started',
226
- rootDir: rootDir,
227
- dbPath: dbPath
228
- });
229
- if (initialWarnings.length > 0 || initialParsed > 0) {
230
- onEvent({
231
- type: 'reconciled',
232
- parsed: initialParsed,
233
- total: initialTotal,
234
- warnings: initialWarnings
235
- });
236
- }
239
+ if (claimFailure) throw claimFailure;
237
240
  stopping = false;
238
241
  // At most one open+reconcile+close cycle runs at a time; a trigger that arrives mid-cycle is
239
242
  // coalesced into a single rerun instead of opening a second, overlapping connection.
@@ -247,13 +250,13 @@ function runWatch(_0) {
247
250
  case 0:
248
251
  _state.trys.push([
249
252
  0,
250
- 10,
253
+ 8,
251
254
  ,
252
- 11
255
+ 9
253
256
  ]);
254
257
  return [
255
258
  4,
256
- (0, _indexts1.openStore)(cfg)
259
+ (0, _indexts.openStore)(cfg)
257
260
  ];
258
261
  case 1:
259
262
  _ref = _state.sent(), store = _ref.store, parsed = _ref.parsed, warnings = _ref.warnings;
@@ -262,24 +265,12 @@ function runWatch(_0) {
262
265
  _state.trys.push([
263
266
  2,
264
267
  ,
265
- 7,
266
- 9
268
+ 5,
269
+ 7
267
270
  ]);
268
- return [
269
- 4,
270
- (0, _indexts1.setMeta)(store, 'watch_heartbeat', new Date().toISOString())
271
- ];
272
- case 3:
273
- _state.sent();
274
- return [
275
- 4,
276
- (0, _indexts1.setMeta)(store, 'watch_pid', String(process.pid))
277
- ];
278
- case 4:
279
- _state.sent();
280
271
  if (!(alwaysEmit || parsed > 0 || warnings.length > 0)) return [
281
272
  3,
282
- 6
273
+ 4
283
274
  ];
284
275
  _tmp = {
285
276
  type: 'reconciled',
@@ -287,34 +278,34 @@ function runWatch(_0) {
287
278
  };
288
279
  return [
289
280
  4,
290
- (0, _indexts1.docCount)(store)
281
+ (0, _indexts.docCount)(store)
291
282
  ];
292
- case 5:
283
+ case 3:
293
284
  onEvent.apply(void 0, [
294
285
  (_tmp.total = _state.sent(), _tmp.warnings = warnings, _tmp)
295
286
  ]);
296
- _state.label = 6;
297
- case 6:
287
+ _state.label = 4;
288
+ case 4:
298
289
  return [
299
290
  3,
300
- 9
291
+ 7
301
292
  ];
302
- case 7:
293
+ case 5:
303
294
  return [
304
295
  4,
305
296
  store.close()
306
297
  ];
307
- case 8:
298
+ case 6:
308
299
  _state.sent();
309
300
  return [
310
301
  7
311
302
  ];
312
- case 9:
303
+ case 7:
313
304
  return [
314
305
  3,
315
- 11
306
+ 9
316
307
  ];
317
- case 10:
308
+ case 8:
318
309
  err = _state.sent();
319
310
  onEvent({
320
311
  type: 'reconcile-error',
@@ -322,9 +313,9 @@ function runWatch(_0) {
322
313
  });
323
314
  return [
324
315
  3,
325
- 11
316
+ 9
326
317
  ];
327
- case 11:
318
+ case 9:
328
319
  return [
329
320
  2
330
321
  ];
@@ -358,15 +349,15 @@ function runWatch(_0) {
358
349
  requestCycle(true);
359
350
  }, debounceMs);
360
351
  };
361
- // Ignore our own state dir, or the heartbeat write would retrigger itself forever. An event with
362
- // an unresolvable filename (null, which fs.watch delivers under load) reconciles: parsing nothing costs less than missing a real edit.
352
+ // Ignore our own state files, or cache and claim writes would retrigger reconciliation forever.
353
+ // An unresolvable filename reconciles because parsing nothing costs less than missing a real edit.
363
354
  watcher = (0, _nodefs.watch)(rootDir, {
364
355
  recursive: true
365
356
  }, function(_event, filename) {
366
- if (typeof filename === 'string' && filename.startsWith(_indexts.STATE_DIR)) return;
357
+ if (!(0, _watchnotificationts.shouldReconcileWatchNotification)(filename)) return;
367
358
  scheduleReconcile();
368
359
  });
369
- heartbeatTimer = setInterval((0, _guardedtickts.guardedTick)(function() {
360
+ reconcileTimer = setInterval((0, _guardedtickts.guardedTick)(function() {
370
361
  return _async_to_generator(function() {
371
362
  return _ts_generator(this, function(_state) {
372
363
  return [
@@ -377,75 +368,183 @@ function runWatch(_0) {
377
368
  })();
378
369
  }, function() {
379
370
  return stopping;
380
- }), heartbeatIntervalMs);
371
+ }), reconcileIntervalMs);
381
372
  return [
382
- 2,
383
- new Promise(function(resolveShutdown) {
384
- var _opts_signal, _opts_signal1;
385
- // SIGINT/SIGTERM and an aborted signal all run this same path exactly once; each is
386
- // unregistered here too so a second runWatch call in the same process starts clean.
387
- var shutdown = function shutdown1() {
388
- return _async_to_generator(function() {
389
- var _opts_signal, _ref, finalStore;
390
- return _ts_generator(this, function(_state) {
391
- switch(_state.label){
392
- case 0:
393
- if (stopping) return [
394
- 2
395
- ];
396
- stopping = true;
397
- process.off('SIGINT', shutdown);
398
- process.off('SIGTERM', shutdown);
399
- (_opts_signal = opts.signal) === null || _opts_signal === void 0 ? void 0 : _opts_signal.removeEventListener('abort', shutdown);
400
- clearInterval(heartbeatTimer);
401
- if (debounceTimer) clearTimeout(debounceTimer);
402
- watcher.close();
403
- return [
404
- 4,
405
- running
406
- ];
407
- case 1:
408
- _state.sent();
409
- return [
410
- 4,
411
- (0, _indexts1.openStore)(cfg)
412
- ];
413
- case 2:
414
- _ref = _state.sent(), finalStore = _ref.store;
415
- return [
416
- 4,
417
- (0, _indexts1.setMeta)(finalStore, 'watch_heartbeat', null)
418
- ];
419
- case 3:
420
- _state.sent();
421
- return [
422
- 4,
423
- (0, _indexts1.setMeta)(finalStore, 'watch_pid', null)
424
- ];
425
- case 4:
426
- _state.sent();
427
- return [
428
- 4,
429
- finalStore.close()
430
- ];
431
- case 5:
432
- _state.sent();
433
- resolveShutdown();
434
- return [
435
- 2
436
- ];
437
- }
438
- });
439
- })();
373
+ 4,
374
+ new Promise(function(resolveShutdown, rejectShutdown) {
375
+ var _opts_signal;
376
+ var shutdownReason = null;
377
+ var shutdown = function shutdown(reason) {
378
+ shutdownReason !== null && shutdownReason !== void 0 ? shutdownReason : shutdownReason = reason !== null && reason !== void 0 ? reason : null;
379
+ if (stopping) return;
380
+ stopping = true;
381
+ var shutdownTask = function() {
382
+ return _async_to_generator(function() {
383
+ var _opts_signal, finalError, _ref, finalStore, err, err1;
384
+ return _ts_generator(this, function(_state) {
385
+ switch(_state.label){
386
+ case 0:
387
+ process.off('SIGINT', stopNormally);
388
+ process.off('SIGTERM', stopNormally);
389
+ (_opts_signal = opts.signal) === null || _opts_signal === void 0 ? void 0 : _opts_signal.removeEventListener('abort', stopNormally);
390
+ clearInterval(reconcileTimer);
391
+ if (debounceTimer) clearTimeout(debounceTimer);
392
+ watcher.close();
393
+ return [
394
+ 4,
395
+ running
396
+ ];
397
+ case 1:
398
+ _state.sent();
399
+ _state.label = 2;
400
+ case 2:
401
+ _state.trys.push([
402
+ 2,
403
+ 6,
404
+ ,
405
+ 7
406
+ ]);
407
+ if (!!shutdownReason) return [
408
+ 3,
409
+ 5
410
+ ];
411
+ return [
412
+ 4,
413
+ (0, _indexts.openStore)(cfg)
414
+ ];
415
+ case 3:
416
+ _ref = _state.sent(), finalStore = _ref.store;
417
+ return [
418
+ 4,
419
+ finalStore.close()
420
+ ];
421
+ case 4:
422
+ _state.sent();
423
+ _state.label = 5;
424
+ case 5:
425
+ return [
426
+ 3,
427
+ 7
428
+ ];
429
+ case 6:
430
+ err = _state.sent();
431
+ finalError = err;
432
+ return [
433
+ 3,
434
+ 7
435
+ ];
436
+ case 7:
437
+ _state.trys.push([
438
+ 7,
439
+ 9,
440
+ ,
441
+ 10
442
+ ]);
443
+ return [
444
+ 4,
445
+ closeClaim()
446
+ ];
447
+ case 8:
448
+ _state.sent();
449
+ return [
450
+ 3,
451
+ 10
452
+ ];
453
+ case 9:
454
+ err1 = _state.sent();
455
+ if (finalError) throw new AggregateError([
456
+ finalError,
457
+ err1
458
+ ], 'watch shutdown and claim cleanup both failed');
459
+ throw err1;
460
+ case 10:
461
+ if (finalError) throw finalError;
462
+ if (shutdownReason) throw shutdownReason;
463
+ return [
464
+ 2
465
+ ];
466
+ }
467
+ });
468
+ })();
469
+ }();
470
+ shutdownTask.then(resolveShutdown, rejectShutdown);
440
471
  };
441
- process.once('SIGINT', shutdown);
442
- process.once('SIGTERM', shutdown);
443
- if ((_opts_signal = opts.signal) === null || _opts_signal === void 0 ? void 0 : _opts_signal.aborted) shutdown();
444
- else (_opts_signal1 = opts.signal) === null || _opts_signal1 === void 0 ? void 0 : _opts_signal1.addEventListener('abort', shutdown, {
445
- once: true
446
- });
472
+ var stopNormally = function stopNormally() {
473
+ return shutdown();
474
+ };
475
+ requestShutdown = shutdown;
476
+ process.once('SIGINT', stopNormally);
477
+ process.once('SIGTERM', stopNormally);
478
+ if (claimFailure) shutdown(claimFailure);
479
+ else if ((_opts_signal = opts.signal) === null || _opts_signal === void 0 ? void 0 : _opts_signal.aborted) shutdown();
480
+ else {
481
+ var _opts_signal1;
482
+ (_opts_signal1 = opts.signal) === null || _opts_signal1 === void 0 ? void 0 : _opts_signal1.addEventListener('abort', stopNormally, {
483
+ once: true
484
+ });
485
+ try {
486
+ onEvent({
487
+ type: 'started',
488
+ rootDir: rootDir,
489
+ dbPath: dbPath
490
+ });
491
+ if (!stopping && (initialWarnings.length > 0 || initialParsed > 0)) {
492
+ onEvent({
493
+ type: 'reconciled',
494
+ parsed: initialParsed,
495
+ total: initialTotal,
496
+ warnings: initialWarnings
497
+ });
498
+ }
499
+ } catch (err) {
500
+ shutdown(_instanceof(err, Error) ? err : new Error(String(err)));
501
+ }
502
+ }
447
503
  })
448
504
  ];
505
+ case 11:
506
+ return [
507
+ 2,
508
+ _state.sent()
509
+ ];
510
+ case 12:
511
+ err = _state.sent();
512
+ if (claimCloseStarted) throw err;
513
+ _state.label = 13;
514
+ case 13:
515
+ _state.trys.push([
516
+ 13,
517
+ 15,
518
+ ,
519
+ 16
520
+ ]);
521
+ return [
522
+ 4,
523
+ closeClaim()
524
+ ];
525
+ case 14:
526
+ _state.sent();
527
+ return [
528
+ 3,
529
+ 16
530
+ ];
531
+ case 15:
532
+ closeError = _state.sent();
533
+ throw new AggregateError([
534
+ err,
535
+ closeError
536
+ ], 'watch setup and claim cleanup both failed');
537
+ case 16:
538
+ throw err;
539
+ case 17:
540
+ requestShutdown = undefined;
541
+ return [
542
+ 7
543
+ ];
544
+ case 18:
545
+ return [
546
+ 2
547
+ ];
449
548
  }
450
549
  });
451
550
  }).apply(this, arguments);
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/watch.ts"],"sourcesContent":["import { watch as fsWatch } from 'node:fs';\nimport type { ResolvedConfig } from './config/index.ts';\nimport { STATE_DIR } from './config/index.ts';\nimport { SenseError } from './errors.ts';\nimport { guardedTick } from './lib/guarded-tick.ts';\nimport { docCount, getMeta, openStore, setMeta } from './store/index.ts';\n\n// Watch is a cache pre-warmer, not a correctness mechanism: open() always reconciles anyway, so any fs event just triggers a debounced full reconcile.\nconst DEBOUNCE_MS = 200;\nconst HEARTBEAT_INTERVAL_MS = 5000;\nconst STALE_HEARTBEAT_MS = 15000;\n\nexport type WatchEvent = { type: 'started'; rootDir: string; dbPath: string } | { type: 'reconciled'; parsed: number; total: number; warnings: string[] } | { type: 'reconcile-error'; message: string };\n\nexport interface WatchOptions {\n force?: boolean;\n onEvent?: (event: WatchEvent) => void;\n // Aborting runs the same shutdown path as SIGINT/SIGTERM.\n signal?: AbortSignal;\n debounceMs?: number;\n heartbeatIntervalMs?: number;\n}\n\n// Runs in the foreground until SIGINT/SIGTERM/signal abort. duckdb/turso hand the cache file to\n// one process at a time, so every cycle opens, reconciles, writes the heartbeat/pid rows, and closes.\nexport async function runWatch(cfg: ResolvedConfig, opts: WatchOptions = {}): Promise<void> {\n const onEvent = opts.onEvent ?? (() => {});\n const debounceMs = opts.debounceMs ?? DEBOUNCE_MS;\n const heartbeatIntervalMs = opts.heartbeatIntervalMs ?? HEARTBEAT_INTERVAL_MS;\n const { store: initialStore, dbPath, warnings: initialWarnings, parsed: initialParsed } = await openStore(cfg);\n // openStore normalizes the legacy direct-API baseDir before a watcher touches the filesystem.\n const rootDir = cfg.rootDir ?? cfg.baseDir;\n const existingHeartbeat = await getMeta(initialStore, 'watch_heartbeat');\n if (existingHeartbeat && !opts.force) {\n const age = Date.now() - Date.parse(existingHeartbeat);\n if (age >= 0 && age < STALE_HEARTBEAT_MS) {\n await initialStore.close();\n throw new SenseError('WATCH_ACTIVE', `another watcher appears active (heartbeat ${Math.round(age / 1000)}s ago); use --force to override`);\n }\n }\n\n // total is read before close: an event fires only once nothing is held open again.\n const initialTotal = initialWarnings.length > 0 || initialParsed > 0 ? await docCount(initialStore) : 0;\n await setMeta(initialStore, 'watch_heartbeat', new Date().toISOString());\n await setMeta(initialStore, 'watch_pid', String(process.pid));\n await initialStore.close();\n\n onEvent({ type: 'started', rootDir, dbPath });\n if (initialWarnings.length > 0 || initialParsed > 0) {\n onEvent({ type: 'reconciled', parsed: initialParsed, total: initialTotal, warnings: initialWarnings });\n }\n\n let stopping = false;\n // At most one open+reconcile+close cycle runs at a time; a trigger that arrives mid-cycle is\n // coalesced into a single rerun instead of opening a second, overlapping connection.\n let running: Promise<void> | null = null;\n let queuedAlways: boolean | null = null;\n\n const tick = async (alwaysEmit: boolean): Promise<void> => {\n try {\n const { store, parsed, warnings } = await openStore(cfg);\n try {\n await setMeta(store, 'watch_heartbeat', new Date().toISOString());\n await setMeta(store, 'watch_pid', String(process.pid));\n if (alwaysEmit || parsed > 0 || warnings.length > 0) {\n onEvent({ type: 'reconciled', parsed, total: await docCount(store), warnings });\n }\n } finally {\n await store.close();\n }\n } catch (err) {\n onEvent({ type: 'reconcile-error', message: (err as Error).message });\n }\n };\n\n const startCycle = (alwaysEmit: boolean) => {\n running = tick(alwaysEmit).then(() => {\n running = null;\n if (queuedAlways !== null && !stopping) {\n const again = queuedAlways;\n queuedAlways = null;\n startCycle(again);\n }\n });\n };\n\n const requestCycle = (alwaysEmit: boolean) => {\n if (stopping) return;\n if (running) {\n queuedAlways = queuedAlways || alwaysEmit;\n return;\n }\n startCycle(alwaysEmit);\n };\n\n let debounceTimer: NodeJS.Timeout | null = null;\n const scheduleReconcile = () => {\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n debounceTimer = null;\n requestCycle(true);\n }, debounceMs);\n };\n\n // Ignore our own state dir, or the heartbeat write would retrigger itself forever. An event with\n // an unresolvable filename (null, which fs.watch delivers under load) reconciles: parsing nothing costs less than missing a real edit.\n const watcher = fsWatch(rootDir, { recursive: true }, (_event, filename) => {\n if (typeof filename === 'string' && filename.startsWith(STATE_DIR)) return;\n scheduleReconcile();\n });\n const heartbeatTimer = setInterval(\n guardedTick(\n async () => requestCycle(false),\n () => stopping\n ),\n heartbeatIntervalMs\n );\n\n return new Promise<void>((resolveShutdown) => {\n // SIGINT/SIGTERM and an aborted signal all run this same path exactly once; each is\n // unregistered here too so a second runWatch call in the same process starts clean.\n const shutdown = async () => {\n if (stopping) return;\n stopping = true;\n process.off('SIGINT', shutdown);\n process.off('SIGTERM', shutdown);\n opts.signal?.removeEventListener('abort', shutdown);\n clearInterval(heartbeatTimer);\n if (debounceTimer) clearTimeout(debounceTimer);\n watcher.close();\n await running;\n const { store: finalStore } = await openStore(cfg);\n await setMeta(finalStore, 'watch_heartbeat', null);\n await setMeta(finalStore, 'watch_pid', null);\n await finalStore.close();\n resolveShutdown();\n };\n process.once('SIGINT', shutdown);\n process.once('SIGTERM', shutdown);\n if (opts.signal?.aborted) shutdown();\n else opts.signal?.addEventListener('abort', shutdown, { once: true });\n });\n}\n"],"names":["runWatch","DEBOUNCE_MS","HEARTBEAT_INTERVAL_MS","STALE_HEARTBEAT_MS","cfg","opts","onEvent","debounceMs","heartbeatIntervalMs","initialStore","dbPath","initialWarnings","initialParsed","rootDir","existingHeartbeat","age","initialTotal","stopping","running","queuedAlways","tick","startCycle","requestCycle","debounceTimer","scheduleReconcile","watcher","heartbeatTimer","openStore","store","warnings","parsed","baseDir","getMeta","force","Date","now","parse","close","SenseError","Math","round","length","docCount","setMeta","toISOString","String","process","pid","type","total","alwaysEmit","err","message","then","again","clearTimeout","setTimeout","fsWatch","recursive","_event","filename","startsWith","STATE_DIR","setInterval","guardedTick","Promise","resolveShutdown","shutdown","finalStore","off","signal","removeEventListener","clearInterval","once","aborted","addEventListener"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/watch.ts"],"sourcesContent":["import { watch as fsWatch } from 'node:fs';\nimport type { ResolvedConfig } from './config/index.ts';\nimport { SenseError } from './errors.ts';\nimport { guardedTick } from './lib/guarded-tick.ts';\nimport { shouldReconcileWatchNotification } from './lib/watch-notification.ts';\nimport { docCount, openStore } from './store/index.ts';\nimport { startWatchClaim, WATCH_HEARTBEAT_INTERVAL_MS } from './watch-claim.ts';\n\n// Watch is a cache pre-warmer, not a correctness mechanism: open() always reconciles anyway, so any fs event just triggers a debounced full reconcile.\nconst DEBOUNCE_MS = 200;\n\nexport type WatchEvent = { type: 'started'; rootDir: string; dbPath: string } | { type: 'reconciled'; parsed: number; total: number; warnings: string[] } | { type: 'reconcile-error'; message: string };\n\nexport interface WatchOptions {\n force?: boolean;\n onEvent?: (event: WatchEvent) => void;\n // Aborting runs the same shutdown path as SIGINT/SIGTERM.\n signal?: AbortSignal;\n debounceMs?: number;\n heartbeatIntervalMs?: number;\n}\n\n// Runs in the foreground until SIGINT/SIGTERM/signal abort. DuckDB and Turso hand the cache file to\n// one process at a time, so every reconcile cycle opens and closes instead of holding an idle lock.\nexport async function runWatch(cfg: ResolvedConfig, opts: WatchOptions = {}): Promise<void> {\n const onEvent = opts.onEvent ?? (() => {});\n const debounceMs = opts.debounceMs ?? DEBOUNCE_MS;\n const reconcileIntervalMs = opts.heartbeatIntervalMs ?? WATCH_HEARTBEAT_INTERVAL_MS;\n if (!Number.isFinite(reconcileIntervalMs) || reconcileIntervalMs <= 0) throw new SenseError('CONFIG_INVALID', 'watch heartbeatIntervalMs must be a finite positive number');\n // Reconciliation may be parked for a caller, but live ownership must renew before fixed expiry.\n const claimIntervalMs = Math.min(reconcileIntervalMs, WATCH_HEARTBEAT_INTERVAL_MS);\n const rootDir = cfg.rootDir ?? cfg.baseDir;\n const configDir = cfg.configDir ?? rootDir;\n let claimFailure: Error | null = null;\n let requestShutdown: ((reason: Error) => void) | undefined;\n const claim = await startWatchClaim(configDir, opts.force ?? false, claimIntervalMs, (err) => {\n claimFailure ??= err;\n requestShutdown?.(err);\n });\n let claimCloseStarted = false;\n const closeClaim = (): Promise<void> => {\n claimCloseStarted = true;\n return claim.close();\n };\n try {\n const { store: initialStore, dbPath, warnings: initialWarnings, parsed: initialParsed } = await openStore(cfg);\n let initialTotal = 0;\n try {\n // total is read before close: an event fires only once nothing is held open again.\n initialTotal = initialWarnings.length > 0 || initialParsed > 0 ? await docCount(initialStore) : 0;\n } finally {\n await initialStore.close();\n }\n if (claimFailure) throw claimFailure;\n\n let stopping = false;\n // At most one open+reconcile+close cycle runs at a time; a trigger that arrives mid-cycle is\n // coalesced into a single rerun instead of opening a second, overlapping connection.\n let running: Promise<void> | null = null;\n let queuedAlways: boolean | null = null;\n\n const tick = async (alwaysEmit: boolean): Promise<void> => {\n try {\n const { store, parsed, warnings } = await openStore(cfg);\n try {\n if (alwaysEmit || parsed > 0 || warnings.length > 0) {\n onEvent({ type: 'reconciled', parsed, total: await docCount(store), warnings });\n }\n } finally {\n await store.close();\n }\n } catch (err) {\n onEvent({ type: 'reconcile-error', message: (err as Error).message });\n }\n };\n\n const startCycle = (alwaysEmit: boolean) => {\n running = tick(alwaysEmit).then(() => {\n running = null;\n if (queuedAlways !== null && !stopping) {\n const again = queuedAlways;\n queuedAlways = null;\n startCycle(again);\n }\n });\n };\n\n const requestCycle = (alwaysEmit: boolean) => {\n if (stopping) return;\n if (running) {\n queuedAlways = queuedAlways || alwaysEmit;\n return;\n }\n startCycle(alwaysEmit);\n };\n\n let debounceTimer: NodeJS.Timeout | null = null;\n const scheduleReconcile = () => {\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n debounceTimer = null;\n requestCycle(true);\n }, debounceMs);\n };\n\n // Ignore our own state files, or cache and claim writes would retrigger reconciliation forever.\n // An unresolvable filename reconciles because parsing nothing costs less than missing a real edit.\n const watcher = fsWatch(rootDir, { recursive: true }, (_event, filename) => {\n if (!shouldReconcileWatchNotification(filename)) return;\n scheduleReconcile();\n });\n const reconcileTimer = setInterval(\n guardedTick(\n async () => requestCycle(false),\n () => stopping\n ),\n reconcileIntervalMs\n );\n\n return await new Promise<void>((resolveShutdown, rejectShutdown) => {\n let shutdownReason: Error | null = null;\n const shutdown = (reason?: Error) => {\n shutdownReason ??= reason ?? null;\n if (stopping) return;\n stopping = true;\n const shutdownTask = (async () => {\n process.off('SIGINT', stopNormally);\n process.off('SIGTERM', stopNormally);\n opts.signal?.removeEventListener('abort', stopNormally);\n clearInterval(reconcileTimer);\n if (debounceTimer) clearTimeout(debounceTimer);\n watcher.close();\n await running;\n let finalError: unknown;\n try {\n // A normal stop performs one final freshness pass. Ownership loss drains existing work\n // but starts nothing new because the replacement watcher now owns that responsibility.\n if (!shutdownReason) {\n const { store: finalStore } = await openStore(cfg);\n await finalStore.close();\n }\n } catch (err) {\n finalError = err;\n }\n try {\n await closeClaim();\n } catch (err) {\n if (finalError) throw new AggregateError([finalError, err], 'watch shutdown and claim cleanup both failed');\n throw err;\n }\n if (finalError) throw finalError;\n if (shutdownReason) throw shutdownReason;\n })();\n shutdownTask.then(resolveShutdown, rejectShutdown);\n };\n const stopNormally = () => shutdown();\n requestShutdown = shutdown;\n process.once('SIGINT', stopNormally);\n process.once('SIGTERM', stopNormally);\n if (claimFailure) shutdown(claimFailure);\n else if (opts.signal?.aborted) shutdown();\n else {\n opts.signal?.addEventListener('abort', stopNormally, { once: true });\n try {\n onEvent({ type: 'started', rootDir, dbPath });\n if (!stopping && (initialWarnings.length > 0 || initialParsed > 0)) {\n onEvent({ type: 'reconciled', parsed: initialParsed, total: initialTotal, warnings: initialWarnings });\n }\n } catch (err) {\n shutdown(err instanceof Error ? err : new Error(String(err)));\n }\n }\n });\n } catch (err) {\n if (claimCloseStarted) throw err;\n try {\n await closeClaim();\n } catch (closeError) {\n throw new AggregateError([err, closeError], 'watch setup and claim cleanup both failed');\n }\n throw err;\n } finally {\n requestShutdown = undefined;\n }\n}\n"],"names":["runWatch","DEBOUNCE_MS","cfg","opts","onEvent","debounceMs","reconcileIntervalMs","claimIntervalMs","rootDir","configDir","claimFailure","requestShutdown","claim","claimCloseStarted","closeClaim","initialStore","dbPath","initialWarnings","initialParsed","initialTotal","stopping","running","queuedAlways","tick","startCycle","requestCycle","debounceTimer","scheduleReconcile","watcher","reconcileTimer","err","closeError","heartbeatIntervalMs","WATCH_HEARTBEAT_INTERVAL_MS","Number","isFinite","SenseError","Math","min","baseDir","startWatchClaim","force","close","openStore","store","warnings","parsed","length","docCount","alwaysEmit","type","total","message","then","again","clearTimeout","setTimeout","fsWatch","recursive","_event","filename","shouldReconcileWatchNotification","setInterval","guardedTick","Promise","resolveShutdown","rejectShutdown","shutdownReason","shutdown","reason","shutdownTask","finalError","finalStore","process","off","stopNormally","signal","removeEventListener","clearInterval","AggregateError","once","aborted","addEventListener","Error","String","undefined"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};