@massu/core 2.3.1 → 2.4.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/CHANGELOG.md CHANGED
@@ -10,6 +10,72 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
10
10
  > redistributed under the **Apache License 2.0**. Full license + NOTICE:
11
11
  > `packages/core/assets/embedder/MODEL-LICENSE`.
12
12
 
13
+ ## [2.4.0] - 2026-07-31
14
+
15
+ ### Fixed
16
+
17
+ - **Hooks silently lost telemetry whenever two sessions wrote at once — `db-driver.ts` never
18
+ armed `PRAGMA busy_timeout`, so SQLite's default of 0ms made a blocked writer fail
19
+ INSTANTLY.** Both engine paths (`openNodeSqlite`, `openBetterSqlite3`) opened a connection
20
+ and handed it out without a busy-timeout, so a writer that met a competing writer's lock
21
+ got `SQLITE_BUSY` ("database is locked") with no wait at all. Every hook catches that,
22
+ appends to `.massu/hook-failures.jsonl`, and **exits 0** — so the observation, cost, or
23
+ session row was gone with no failing signal anywhere. Measured before the fix across two
24
+ active workspaces: **2,169 lost rows in one and 156 in the other**, still accruing. In one
25
+ of them the throw landed in `session-start`'s `createSession`, which also skipped
26
+ `pullTeamPromotions` further down the same `try` — so team/enterprise promotions silently
27
+ stopped pulling on any session that lost the race. Concurrency is the NORMAL state whenever
28
+ more than one agent session shares a repo, not an edge case.
29
+
30
+ `PRAGMA busy_timeout` is set — rather than the ctor `timeout` option — because it is
31
+ engine-agnostic across `node:sqlite` and better-sqlite3, connection-local, and needs no
32
+ lock of its own. It is applied in `db-driver.ts`, the CR-69 sole open chokepoint, so
33
+ **every** opener (hooks, MCP server, CLI) inherits it. Default **5000ms**, overridable via
34
+ `MASSU_DB_BUSY_TIMEOUT_MS`; `0` is legal and restores the old fail-instantly behaviour,
35
+ which is how the tests prove they can still go RED. A malformed value falls back to the
36
+ default rather than disabling the wait (fail-safe, not fail-open).
37
+
38
+ Semver MINOR, not PATCH: `resolveBusyTimeoutMs()`, `DEFAULT_BUSY_TIMEOUT_MS` and
39
+ `DB_BUSY_TIMEOUT_ENV` are new exports. This release also carries the unpublished 2.3.2.
40
+
41
+ Proof it can fail (CR-72, planted in the real tree, restored byte-identical by sha256):
42
+ removing the pragma from `openNodeSqlite` turns the behavioural test RED
43
+ (`db-busy-timeout.test.ts` — a `worker_threads` lock-holder, because the plan audit proved
44
+ a same-thread holder is impossible: SQLite's busy-wait blocks the event loop, so the
45
+ release timer never fires); removing it from `openBetterSqlite3` turns drift-guard clause
46
+ (e) RED; hard-coding the value turns clause (e2) RED.
47
+ New gate registered in `scripts/lib/gate-registry.json` (416 → 417).
48
+ Incident: `docs/incidents/2026-07-23-hook-db-busy-timeout-silent-loss.md`.
49
+ (plan-2026-07-23-hook-latency-silent-loss-fixes, P1)
50
+
51
+ ## [2.3.2] - 2026-07-24
52
+
53
+ ### Fixed
54
+
55
+ - **`sync-public.sh` self-exempts its bulk `git rm -rf .` / `git clean -fd` from the machine
56
+ deletion circuit breaker.** The public-mirror sync wipes the mirror with `git rm -rf .` +
57
+ `git clean -fd` — hundreds of unlinks under `$HOME`, whose bulk-delete signature the breaker
58
+ SIGSTOPs — freezing the git child mid-sync on every run. Both bulk deletes now route through a
59
+ `cb_exempt_git` helper that registers each git child pid plus a start-time identity token in
60
+ `~/.claude/cb-exempt-pids.txt` for the duration of that one command, scoped to only the pids this
61
+ script adds, fail-open (the sync never depends on the breaker), with an `EXIT`/`INT`/`TERM`
62
+ cleanup trap that de-registers them even on interrupt. The start-time token pairs with the
63
+ breaker's identity-verifying exempt list so a reused pid cannot inherit a stale exemption.
64
+ Drift-guarded (`sync-public-cb-exempt-drift-guard.test.ts`, registered as a DEFEAT-proven G-6
65
+ anti-vacuity guard).
66
+
67
+ ### Changed
68
+
69
+ - **Synced `packages/core/CHANGELOG.md` with the previously-missing `[2.3.1]` entry.** The 2.3.1
70
+ release added `[2.3.1]` to the root and website CHANGELOGs but omitted
71
+ `packages/core/CHANGELOG.md`, leaving the three copies out of sync. Parity restored.
72
+ - **Trimmed `CLAUDE.md` back under its size budget**, relocating the removed detail into
73
+ `.claude/reference/patterns-quickref.md` so `.claude/CLAUDE.md` stays under its 55,000-character
74
+ cap.
75
+
76
+ No runtime behaviour of `@massu/core` changes in this release; it is repo-tooling and documentation
77
+ only. Published because CR-64 forbids commits sitting on top of a published, tagged release.
78
+
13
79
  ## [2.3.1] - 2026-07-23
14
80
 
15
81
  ### Fixed
package/dist/cli.js CHANGED
@@ -560,6 +560,12 @@ import { join as join2 } from "path";
560
560
  function resolveDbEngine() {
561
561
  return process.env[DB_ENGINE_ENV] === "better-sqlite3" ? "better-sqlite3" : DEFAULT_DB_ENGINE;
562
562
  }
563
+ function resolveBusyTimeoutMs() {
564
+ const raw = process.env[DB_BUSY_TIMEOUT_ENV];
565
+ if (raw === void 0 || raw === "") return DEFAULT_BUSY_TIMEOUT_MS;
566
+ const n = Number(raw);
567
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : DEFAULT_BUSY_TIMEOUT_MS;
568
+ }
563
569
  function nodeSqliteCtor() {
564
570
  if (!_nodeCtor) _nodeCtor = req2("node:sqlite").DatabaseSync;
565
571
  return _nodeCtor;
@@ -581,6 +587,7 @@ function openNodeSqlite(dbPath, opts) {
581
587
  enableForeignKeyConstraints: true,
582
588
  allowExtension: false
583
589
  });
590
+ raw.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
584
591
  let savepointSeq = 0;
585
592
  const wrapStmt = (sql) => {
586
593
  const st = raw.prepare(sql);
@@ -664,7 +671,9 @@ function openNodeSqlite(dbPath, opts) {
664
671
  return handle;
665
672
  }
666
673
  function openBetterSqlite3(dbPath, opts) {
667
- return openDatabase(dbPath, opts);
674
+ const db = openDatabase(dbPath, opts);
675
+ db.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
676
+ return db;
668
677
  }
669
678
  function openDatabase2(dbPath, opts = {}) {
670
679
  return resolveDbEngine() === "better-sqlite3" ? openBetterSqlite3(dbPath, opts) : openNodeSqlite(dbPath, opts);
@@ -708,7 +717,7 @@ function probeMemoryDbUsable2(opts = {}) {
708
717
  }
709
718
  return { ok: true };
710
719
  }
711
- var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, _nodeCtor, NODE_SQLITE_REMEDY;
720
+ var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, DEFAULT_BUSY_TIMEOUT_MS, DB_BUSY_TIMEOUT_ENV, _nodeCtor, NODE_SQLITE_REMEDY;
712
721
  var init_db_driver = __esm({
713
722
  "src/db-driver.ts"() {
714
723
  "use strict";
@@ -717,6 +726,8 @@ var init_db_driver = __esm({
717
726
  req2 = createRequire2(import.meta.url);
718
727
  DEFAULT_DB_ENGINE = "node-sqlite";
719
728
  DB_ENGINE_ENV = "MASSU_DB_ENGINE";
729
+ DEFAULT_BUSY_TIMEOUT_MS = 5e3;
730
+ DB_BUSY_TIMEOUT_ENV = "MASSU_DB_BUSY_TIMEOUT_MS";
720
731
  _nodeCtor = null;
721
732
  NODE_SQLITE_REMEDY = "Node's built-in node:sqlite failed to open the database. Ensure Node >= 22.16.0 (`node --version`) and that this Node build includes SQLite, then restart your MCP client / Claude Code.";
722
733
  }
@@ -12946,23 +12957,23 @@ function introspectPython(detection, projectRoot) {
12946
12957
  for (const path of candidates) {
12947
12958
  const body = readSafe(path);
12948
12959
  if (body === null) continue;
12949
- const authRegex = /\bDepends\s*\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)/gu;
12960
+ const authRegex = new RegExp(PY_AUTH_DEP_PATTERN, "gu");
12950
12961
  forEachMatch(authRegex, body, (m3) => {
12951
12962
  const name = m3[1];
12952
12963
  if (!authDeps.has(name)) authDeps.set(name, path);
12953
12964
  });
12954
- const djangoAuthRegex = /^@\s*([a-z_][a-z0-9_]*(?:_required|_login))\b/gmu;
12965
+ const djangoAuthRegex = new RegExp(PY_DJANGO_AUTH_PATTERN, "gmu");
12955
12966
  forEachMatch(djangoAuthRegex, body, (m3) => {
12956
12967
  const name = m3[1];
12957
12968
  if (!authDeps.has(name)) authDeps.set(name, path);
12958
12969
  });
12959
- const prefixRegex = /\bAPIRouter\s*\(\s*[^)]*?prefix\s*=\s*["']([^"']+)["']/gu;
12970
+ const prefixRegex = new RegExp(PY_API_PREFIX_PATTERN, "gu");
12960
12971
  forEachMatch(prefixRegex, body, (m3) => {
12961
12972
  const fullPrefix = m3[1];
12962
12973
  const base = extractPrefixBase(fullPrefix);
12963
12974
  if (base && !prefixBases.has(base)) prefixBases.set(base, path);
12964
12975
  });
12965
- const asyncRegex = /^(@pytest\.mark\.asyncio(?:\s*\([^)]*\))?)/gmu;
12976
+ const asyncRegex = new RegExp(PY_TEST_ASYNC_PATTERN, "gmu");
12966
12977
  forEachMatch(asyncRegex, body, (m3) => {
12967
12978
  const pat = m3[1].trim();
12968
12979
  if (!testAsyncPatterns.has(pat)) testAsyncPatterns.set(pat, path);
@@ -13167,10 +13178,14 @@ function relativeTo(projectRoot, absPath) {
13167
13178
  }
13168
13179
  return basename4(absPath);
13169
13180
  }
13170
- var MAX_FILE_BYTES, MAX_SAMPLES_PER_ADAPTER, MAX_DIR_DEPTH;
13181
+ var PY_AUTH_DEP_PATTERN, PY_DJANGO_AUTH_PATTERN, PY_API_PREFIX_PATTERN, PY_TEST_ASYNC_PATTERN, MAX_FILE_BYTES, MAX_SAMPLES_PER_ADAPTER, MAX_DIR_DEPTH;
13171
13182
  var init_regex_fallback = __esm({
13172
13183
  "src/detect/regex-fallback.ts"() {
13173
13184
  "use strict";
13185
+ PY_AUTH_DEP_PATTERN = String.raw`\bDepends\s*\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)`;
13186
+ PY_DJANGO_AUTH_PATTERN = String.raw`^@\s*([a-z_][a-z0-9_]*(?:_required|_login))\b`;
13187
+ PY_API_PREFIX_PATTERN = String.raw`\bAPIRouter\s*\(\s*[^)]*?prefix\s*=\s*["']([^"']+)["']`;
13188
+ PY_TEST_ASYNC_PATTERN = String.raw`^(@pytest\.mark\.asyncio(?:\s*\([^)]*\))?)`;
13174
13189
  MAX_FILE_BYTES = 256 * 1024;
13175
13190
  MAX_SAMPLES_PER_ADAPTER = 3;
13176
13191
  MAX_DIR_DEPTH = 6;
@@ -1187,6 +1187,12 @@ import { join as join2 } from "path";
1187
1187
  function resolveDbEngine() {
1188
1188
  return process.env[DB_ENGINE_ENV] === "better-sqlite3" ? "better-sqlite3" : DEFAULT_DB_ENGINE;
1189
1189
  }
1190
+ function resolveBusyTimeoutMs() {
1191
+ const raw = process.env[DB_BUSY_TIMEOUT_ENV];
1192
+ if (raw === void 0 || raw === "") return DEFAULT_BUSY_TIMEOUT_MS;
1193
+ const n = Number(raw);
1194
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : DEFAULT_BUSY_TIMEOUT_MS;
1195
+ }
1190
1196
  function nodeSqliteCtor() {
1191
1197
  if (!_nodeCtor) _nodeCtor = req2("node:sqlite").DatabaseSync;
1192
1198
  return _nodeCtor;
@@ -1208,6 +1214,7 @@ function openNodeSqlite(dbPath, opts) {
1208
1214
  enableForeignKeyConstraints: true,
1209
1215
  allowExtension: false
1210
1216
  });
1217
+ raw.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
1211
1218
  let savepointSeq = 0;
1212
1219
  const wrapStmt = (sql) => {
1213
1220
  const st = raw.prepare(sql);
@@ -1291,12 +1298,14 @@ function openNodeSqlite(dbPath, opts) {
1291
1298
  return handle;
1292
1299
  }
1293
1300
  function openBetterSqlite3(dbPath, opts) {
1294
- return openDatabase(dbPath, opts);
1301
+ const db = openDatabase(dbPath, opts);
1302
+ db.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
1303
+ return db;
1295
1304
  }
1296
1305
  function openDatabase2(dbPath, opts = {}) {
1297
1306
  return resolveDbEngine() === "better-sqlite3" ? openBetterSqlite3(dbPath, opts) : openNodeSqlite(dbPath, opts);
1298
1307
  }
1299
- var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, _nodeCtor;
1308
+ var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, DEFAULT_BUSY_TIMEOUT_MS, DB_BUSY_TIMEOUT_ENV, _nodeCtor;
1300
1309
  var init_db_driver = __esm({
1301
1310
  "src/db-driver.ts"() {
1302
1311
  "use strict";
@@ -1305,6 +1314,8 @@ var init_db_driver = __esm({
1305
1314
  req2 = createRequire2(import.meta.url);
1306
1315
  DEFAULT_DB_ENGINE = "node-sqlite";
1307
1316
  DB_ENGINE_ENV = "MASSU_DB_ENGINE";
1317
+ DEFAULT_BUSY_TIMEOUT_MS = 5e3;
1318
+ DB_BUSY_TIMEOUT_ENV = "MASSU_DB_BUSY_TIMEOUT_MS";
1308
1319
  _nodeCtor = null;
1309
1320
  }
1310
1321
  });
@@ -4132,3 +4143,6 @@ function readStdin() {
4132
4143
  });
4133
4144
  }
4134
4145
  main();
4146
+ export {
4147
+ MAX_FULL_DIFF_BYTES
4148
+ };
@@ -1187,6 +1187,12 @@ import { join as join2 } from "path";
1187
1187
  function resolveDbEngine() {
1188
1188
  return process.env[DB_ENGINE_ENV] === "better-sqlite3" ? "better-sqlite3" : DEFAULT_DB_ENGINE;
1189
1189
  }
1190
+ function resolveBusyTimeoutMs() {
1191
+ const raw = process.env[DB_BUSY_TIMEOUT_ENV];
1192
+ if (raw === void 0 || raw === "") return DEFAULT_BUSY_TIMEOUT_MS;
1193
+ const n = Number(raw);
1194
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : DEFAULT_BUSY_TIMEOUT_MS;
1195
+ }
1190
1196
  function nodeSqliteCtor() {
1191
1197
  if (!_nodeCtor) _nodeCtor = req2("node:sqlite").DatabaseSync;
1192
1198
  return _nodeCtor;
@@ -1208,6 +1214,7 @@ function openNodeSqlite(dbPath, opts) {
1208
1214
  enableForeignKeyConstraints: true,
1209
1215
  allowExtension: false
1210
1216
  });
1217
+ raw.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
1211
1218
  let savepointSeq = 0;
1212
1219
  const wrapStmt = (sql) => {
1213
1220
  const st = raw.prepare(sql);
@@ -1291,12 +1298,14 @@ function openNodeSqlite(dbPath, opts) {
1291
1298
  return handle;
1292
1299
  }
1293
1300
  function openBetterSqlite3(dbPath, opts) {
1294
- return openDatabase(dbPath, opts);
1301
+ const db = openDatabase(dbPath, opts);
1302
+ db.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
1303
+ return db;
1295
1304
  }
1296
1305
  function openDatabase2(dbPath, opts = {}) {
1297
1306
  return resolveDbEngine() === "better-sqlite3" ? openBetterSqlite3(dbPath, opts) : openNodeSqlite(dbPath, opts);
1298
1307
  }
1299
- var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, _nodeCtor;
1308
+ var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, DEFAULT_BUSY_TIMEOUT_MS, DB_BUSY_TIMEOUT_ENV, _nodeCtor;
1300
1309
  var init_db_driver = __esm({
1301
1310
  "src/db-driver.ts"() {
1302
1311
  "use strict";
@@ -1305,6 +1314,8 @@ var init_db_driver = __esm({
1305
1314
  req2 = createRequire2(import.meta.url);
1306
1315
  DEFAULT_DB_ENGINE = "node-sqlite";
1307
1316
  DB_ENGINE_ENV = "MASSU_DB_ENGINE";
1317
+ DEFAULT_BUSY_TIMEOUT_MS = 5e3;
1318
+ DB_BUSY_TIMEOUT_ENV = "MASSU_DB_BUSY_TIMEOUT_MS";
1308
1319
  _nodeCtor = null;
1309
1320
  }
1310
1321
  });
@@ -431,6 +431,12 @@ import { join as join2 } from "path";
431
431
  function resolveDbEngine() {
432
432
  return process.env[DB_ENGINE_ENV] === "better-sqlite3" ? "better-sqlite3" : DEFAULT_DB_ENGINE;
433
433
  }
434
+ function resolveBusyTimeoutMs() {
435
+ const raw = process.env[DB_BUSY_TIMEOUT_ENV];
436
+ if (raw === void 0 || raw === "") return DEFAULT_BUSY_TIMEOUT_MS;
437
+ const n = Number(raw);
438
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : DEFAULT_BUSY_TIMEOUT_MS;
439
+ }
434
440
  function nodeSqliteCtor() {
435
441
  if (!_nodeCtor) _nodeCtor = req2("node:sqlite").DatabaseSync;
436
442
  return _nodeCtor;
@@ -452,6 +458,7 @@ function openNodeSqlite(dbPath, opts) {
452
458
  enableForeignKeyConstraints: true,
453
459
  allowExtension: false
454
460
  });
461
+ raw.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
455
462
  let savepointSeq = 0;
456
463
  const wrapStmt = (sql) => {
457
464
  const st = raw.prepare(sql);
@@ -535,12 +542,14 @@ function openNodeSqlite(dbPath, opts) {
535
542
  return handle;
536
543
  }
537
544
  function openBetterSqlite3(dbPath, opts) {
538
- return openDatabase(dbPath, opts);
545
+ const db = openDatabase(dbPath, opts);
546
+ db.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
547
+ return db;
539
548
  }
540
549
  function openDatabase2(dbPath, opts = {}) {
541
550
  return resolveDbEngine() === "better-sqlite3" ? openBetterSqlite3(dbPath, opts) : openNodeSqlite(dbPath, opts);
542
551
  }
543
- var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, _nodeCtor;
552
+ var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, DEFAULT_BUSY_TIMEOUT_MS, DB_BUSY_TIMEOUT_ENV, _nodeCtor;
544
553
  var init_db_driver = __esm({
545
554
  "src/db-driver.ts"() {
546
555
  "use strict";
@@ -549,6 +558,8 @@ var init_db_driver = __esm({
549
558
  req2 = createRequire2(import.meta.url);
550
559
  DEFAULT_DB_ENGINE = "node-sqlite";
551
560
  DB_ENGINE_ENV = "MASSU_DB_ENGINE";
561
+ DEFAULT_BUSY_TIMEOUT_MS = 5e3;
562
+ DB_BUSY_TIMEOUT_ENV = "MASSU_DB_BUSY_TIMEOUT_MS";
552
563
  _nodeCtor = null;
553
564
  }
554
565
  });
@@ -1193,6 +1193,12 @@ import { join as join2 } from "path";
1193
1193
  function resolveDbEngine() {
1194
1194
  return process.env[DB_ENGINE_ENV] === "better-sqlite3" ? "better-sqlite3" : DEFAULT_DB_ENGINE;
1195
1195
  }
1196
+ function resolveBusyTimeoutMs() {
1197
+ const raw = process.env[DB_BUSY_TIMEOUT_ENV];
1198
+ if (raw === void 0 || raw === "") return DEFAULT_BUSY_TIMEOUT_MS;
1199
+ const n = Number(raw);
1200
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : DEFAULT_BUSY_TIMEOUT_MS;
1201
+ }
1196
1202
  function nodeSqliteCtor() {
1197
1203
  if (!_nodeCtor) _nodeCtor = req2("node:sqlite").DatabaseSync;
1198
1204
  return _nodeCtor;
@@ -1214,6 +1220,7 @@ function openNodeSqlite(dbPath, opts) {
1214
1220
  enableForeignKeyConstraints: true,
1215
1221
  allowExtension: false
1216
1222
  });
1223
+ raw.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
1217
1224
  let savepointSeq = 0;
1218
1225
  const wrapStmt = (sql) => {
1219
1226
  const st = raw.prepare(sql);
@@ -1297,12 +1304,14 @@ function openNodeSqlite(dbPath, opts) {
1297
1304
  return handle;
1298
1305
  }
1299
1306
  function openBetterSqlite3(dbPath, opts) {
1300
- return openDatabase(dbPath, opts);
1307
+ const db = openDatabase(dbPath, opts);
1308
+ db.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
1309
+ return db;
1301
1310
  }
1302
1311
  function openDatabase2(dbPath, opts = {}) {
1303
1312
  return resolveDbEngine() === "better-sqlite3" ? openBetterSqlite3(dbPath, opts) : openNodeSqlite(dbPath, opts);
1304
1313
  }
1305
- var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, _nodeCtor;
1314
+ var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, DEFAULT_BUSY_TIMEOUT_MS, DB_BUSY_TIMEOUT_ENV, _nodeCtor;
1306
1315
  var init_db_driver = __esm({
1307
1316
  "src/db-driver.ts"() {
1308
1317
  "use strict";
@@ -1311,6 +1320,8 @@ var init_db_driver = __esm({
1311
1320
  req2 = createRequire2(import.meta.url);
1312
1321
  DEFAULT_DB_ENGINE = "node-sqlite";
1313
1322
  DB_ENGINE_ENV = "MASSU_DB_ENGINE";
1323
+ DEFAULT_BUSY_TIMEOUT_MS = 5e3;
1324
+ DB_BUSY_TIMEOUT_ENV = "MASSU_DB_BUSY_TIMEOUT_MS";
1314
1325
  _nodeCtor = null;
1315
1326
  }
1316
1327
  });
@@ -1187,6 +1187,12 @@ import { join as join2 } from "path";
1187
1187
  function resolveDbEngine() {
1188
1188
  return process.env[DB_ENGINE_ENV] === "better-sqlite3" ? "better-sqlite3" : DEFAULT_DB_ENGINE;
1189
1189
  }
1190
+ function resolveBusyTimeoutMs() {
1191
+ const raw = process.env[DB_BUSY_TIMEOUT_ENV];
1192
+ if (raw === void 0 || raw === "") return DEFAULT_BUSY_TIMEOUT_MS;
1193
+ const n = Number(raw);
1194
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : DEFAULT_BUSY_TIMEOUT_MS;
1195
+ }
1190
1196
  function nodeSqliteCtor() {
1191
1197
  if (!_nodeCtor) _nodeCtor = req2("node:sqlite").DatabaseSync;
1192
1198
  return _nodeCtor;
@@ -1208,6 +1214,7 @@ function openNodeSqlite(dbPath, opts) {
1208
1214
  enableForeignKeyConstraints: true,
1209
1215
  allowExtension: false
1210
1216
  });
1217
+ raw.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
1211
1218
  let savepointSeq = 0;
1212
1219
  const wrapStmt = (sql) => {
1213
1220
  const st = raw.prepare(sql);
@@ -1291,12 +1298,14 @@ function openNodeSqlite(dbPath, opts) {
1291
1298
  return handle;
1292
1299
  }
1293
1300
  function openBetterSqlite3(dbPath, opts) {
1294
- return openDatabase(dbPath, opts);
1301
+ const db = openDatabase(dbPath, opts);
1302
+ db.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
1303
+ return db;
1295
1304
  }
1296
1305
  function openDatabase2(dbPath, opts = {}) {
1297
1306
  return resolveDbEngine() === "better-sqlite3" ? openBetterSqlite3(dbPath, opts) : openNodeSqlite(dbPath, opts);
1298
1307
  }
1299
- var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, _nodeCtor;
1308
+ var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, DEFAULT_BUSY_TIMEOUT_MS, DB_BUSY_TIMEOUT_ENV, _nodeCtor;
1300
1309
  var init_db_driver = __esm({
1301
1310
  "src/db-driver.ts"() {
1302
1311
  "use strict";
@@ -1305,6 +1314,8 @@ var init_db_driver = __esm({
1305
1314
  req2 = createRequire2(import.meta.url);
1306
1315
  DEFAULT_DB_ENGINE = "node-sqlite";
1307
1316
  DB_ENGINE_ENV = "MASSU_DB_ENGINE";
1317
+ DEFAULT_BUSY_TIMEOUT_MS = 5e3;
1318
+ DB_BUSY_TIMEOUT_ENV = "MASSU_DB_BUSY_TIMEOUT_MS";
1308
1319
  _nodeCtor = null;
1309
1320
  }
1310
1321
  });
@@ -431,6 +431,12 @@ import { join as join2 } from "path";
431
431
  function resolveDbEngine() {
432
432
  return process.env[DB_ENGINE_ENV] === "better-sqlite3" ? "better-sqlite3" : DEFAULT_DB_ENGINE;
433
433
  }
434
+ function resolveBusyTimeoutMs() {
435
+ const raw = process.env[DB_BUSY_TIMEOUT_ENV];
436
+ if (raw === void 0 || raw === "") return DEFAULT_BUSY_TIMEOUT_MS;
437
+ const n = Number(raw);
438
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : DEFAULT_BUSY_TIMEOUT_MS;
439
+ }
434
440
  function nodeSqliteCtor() {
435
441
  if (!_nodeCtor) _nodeCtor = req2("node:sqlite").DatabaseSync;
436
442
  return _nodeCtor;
@@ -452,6 +458,7 @@ function openNodeSqlite(dbPath, opts) {
452
458
  enableForeignKeyConstraints: true,
453
459
  allowExtension: false
454
460
  });
461
+ raw.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
455
462
  let savepointSeq = 0;
456
463
  const wrapStmt = (sql) => {
457
464
  const st = raw.prepare(sql);
@@ -535,12 +542,14 @@ function openNodeSqlite(dbPath, opts) {
535
542
  return handle;
536
543
  }
537
544
  function openBetterSqlite3(dbPath, opts) {
538
- return openDatabase(dbPath, opts);
545
+ const db = openDatabase(dbPath, opts);
546
+ db.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
547
+ return db;
539
548
  }
540
549
  function openDatabase2(dbPath, opts = {}) {
541
550
  return resolveDbEngine() === "better-sqlite3" ? openBetterSqlite3(dbPath, opts) : openNodeSqlite(dbPath, opts);
542
551
  }
543
- var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, _nodeCtor;
552
+ var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, DEFAULT_BUSY_TIMEOUT_MS, DB_BUSY_TIMEOUT_ENV, _nodeCtor;
544
553
  var init_db_driver = __esm({
545
554
  "src/db-driver.ts"() {
546
555
  "use strict";
@@ -549,6 +558,8 @@ var init_db_driver = __esm({
549
558
  req2 = createRequire2(import.meta.url);
550
559
  DEFAULT_DB_ENGINE = "node-sqlite";
551
560
  DB_ENGINE_ENV = "MASSU_DB_ENGINE";
561
+ DEFAULT_BUSY_TIMEOUT_MS = 5e3;
562
+ DB_BUSY_TIMEOUT_ENV = "MASSU_DB_BUSY_TIMEOUT_MS";
552
563
  _nodeCtor = null;
553
564
  }
554
565
  });
@@ -431,6 +431,12 @@ import { join as join2 } from "path";
431
431
  function resolveDbEngine() {
432
432
  return process.env[DB_ENGINE_ENV] === "better-sqlite3" ? "better-sqlite3" : DEFAULT_DB_ENGINE;
433
433
  }
434
+ function resolveBusyTimeoutMs() {
435
+ const raw = process.env[DB_BUSY_TIMEOUT_ENV];
436
+ if (raw === void 0 || raw === "") return DEFAULT_BUSY_TIMEOUT_MS;
437
+ const n = Number(raw);
438
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : DEFAULT_BUSY_TIMEOUT_MS;
439
+ }
434
440
  function nodeSqliteCtor() {
435
441
  if (!_nodeCtor) _nodeCtor = req2("node:sqlite").DatabaseSync;
436
442
  return _nodeCtor;
@@ -452,6 +458,7 @@ function openNodeSqlite(dbPath, opts) {
452
458
  enableForeignKeyConstraints: true,
453
459
  allowExtension: false
454
460
  });
461
+ raw.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
455
462
  let savepointSeq = 0;
456
463
  const wrapStmt = (sql) => {
457
464
  const st = raw.prepare(sql);
@@ -535,12 +542,14 @@ function openNodeSqlite(dbPath, opts) {
535
542
  return handle;
536
543
  }
537
544
  function openBetterSqlite3(dbPath, opts) {
538
- return openDatabase(dbPath, opts);
545
+ const db = openDatabase(dbPath, opts);
546
+ db.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
547
+ return db;
539
548
  }
540
549
  function openDatabase2(dbPath, opts = {}) {
541
550
  return resolveDbEngine() === "better-sqlite3" ? openBetterSqlite3(dbPath, opts) : openNodeSqlite(dbPath, opts);
542
551
  }
543
- var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, _nodeCtor;
552
+ var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, DEFAULT_BUSY_TIMEOUT_MS, DB_BUSY_TIMEOUT_ENV, _nodeCtor;
544
553
  var init_db_driver = __esm({
545
554
  "src/db-driver.ts"() {
546
555
  "use strict";
@@ -549,6 +558,8 @@ var init_db_driver = __esm({
549
558
  req2 = createRequire2(import.meta.url);
550
559
  DEFAULT_DB_ENGINE = "node-sqlite";
551
560
  DB_ENGINE_ENV = "MASSU_DB_ENGINE";
561
+ DEFAULT_BUSY_TIMEOUT_MS = 5e3;
562
+ DB_BUSY_TIMEOUT_ENV = "MASSU_DB_BUSY_TIMEOUT_MS";
552
563
  _nodeCtor = null;
553
564
  }
554
565
  });
@@ -431,6 +431,12 @@ import { join as join2 } from "path";
431
431
  function resolveDbEngine() {
432
432
  return process.env[DB_ENGINE_ENV] === "better-sqlite3" ? "better-sqlite3" : DEFAULT_DB_ENGINE;
433
433
  }
434
+ function resolveBusyTimeoutMs() {
435
+ const raw = process.env[DB_BUSY_TIMEOUT_ENV];
436
+ if (raw === void 0 || raw === "") return DEFAULT_BUSY_TIMEOUT_MS;
437
+ const n = Number(raw);
438
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : DEFAULT_BUSY_TIMEOUT_MS;
439
+ }
434
440
  function nodeSqliteCtor() {
435
441
  if (!_nodeCtor) _nodeCtor = req2("node:sqlite").DatabaseSync;
436
442
  return _nodeCtor;
@@ -452,6 +458,7 @@ function openNodeSqlite(dbPath, opts) {
452
458
  enableForeignKeyConstraints: true,
453
459
  allowExtension: false
454
460
  });
461
+ raw.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
455
462
  let savepointSeq = 0;
456
463
  const wrapStmt = (sql) => {
457
464
  const st = raw.prepare(sql);
@@ -535,12 +542,14 @@ function openNodeSqlite(dbPath, opts) {
535
542
  return handle;
536
543
  }
537
544
  function openBetterSqlite3(dbPath, opts) {
538
- return openDatabase(dbPath, opts);
545
+ const db = openDatabase(dbPath, opts);
546
+ db.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
547
+ return db;
539
548
  }
540
549
  function openDatabase2(dbPath, opts = {}) {
541
550
  return resolveDbEngine() === "better-sqlite3" ? openBetterSqlite3(dbPath, opts) : openNodeSqlite(dbPath, opts);
542
551
  }
543
- var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, _nodeCtor;
552
+ var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, DEFAULT_BUSY_TIMEOUT_MS, DB_BUSY_TIMEOUT_ENV, _nodeCtor;
544
553
  var init_db_driver = __esm({
545
554
  "src/db-driver.ts"() {
546
555
  "use strict";
@@ -549,6 +558,8 @@ var init_db_driver = __esm({
549
558
  req2 = createRequire2(import.meta.url);
550
559
  DEFAULT_DB_ENGINE = "node-sqlite";
551
560
  DB_ENGINE_ENV = "MASSU_DB_ENGINE";
561
+ DEFAULT_BUSY_TIMEOUT_MS = 5e3;
562
+ DB_BUSY_TIMEOUT_ENV = "MASSU_DB_BUSY_TIMEOUT_MS";
552
563
  _nodeCtor = null;
553
564
  }
554
565
  });
@@ -437,6 +437,12 @@ import { join as join2 } from "path";
437
437
  function resolveDbEngine() {
438
438
  return process.env[DB_ENGINE_ENV] === "better-sqlite3" ? "better-sqlite3" : DEFAULT_DB_ENGINE;
439
439
  }
440
+ function resolveBusyTimeoutMs() {
441
+ const raw = process.env[DB_BUSY_TIMEOUT_ENV];
442
+ if (raw === void 0 || raw === "") return DEFAULT_BUSY_TIMEOUT_MS;
443
+ const n = Number(raw);
444
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : DEFAULT_BUSY_TIMEOUT_MS;
445
+ }
440
446
  function nodeSqliteCtor() {
441
447
  if (!_nodeCtor) _nodeCtor = req2("node:sqlite").DatabaseSync;
442
448
  return _nodeCtor;
@@ -458,6 +464,7 @@ function openNodeSqlite(dbPath, opts) {
458
464
  enableForeignKeyConstraints: true,
459
465
  allowExtension: false
460
466
  });
467
+ raw.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
461
468
  let savepointSeq = 0;
462
469
  const wrapStmt = (sql) => {
463
470
  const st = raw.prepare(sql);
@@ -541,12 +548,14 @@ function openNodeSqlite(dbPath, opts) {
541
548
  return handle;
542
549
  }
543
550
  function openBetterSqlite3(dbPath, opts) {
544
- return openDatabase(dbPath, opts);
551
+ const db = openDatabase(dbPath, opts);
552
+ db.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
553
+ return db;
545
554
  }
546
555
  function openDatabase2(dbPath, opts = {}) {
547
556
  return resolveDbEngine() === "better-sqlite3" ? openBetterSqlite3(dbPath, opts) : openNodeSqlite(dbPath, opts);
548
557
  }
549
- var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, _nodeCtor;
558
+ var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, DEFAULT_BUSY_TIMEOUT_MS, DB_BUSY_TIMEOUT_ENV, _nodeCtor;
550
559
  var init_db_driver = __esm({
551
560
  "src/db-driver.ts"() {
552
561
  "use strict";
@@ -555,6 +564,8 @@ var init_db_driver = __esm({
555
564
  req2 = createRequire2(import.meta.url);
556
565
  DEFAULT_DB_ENGINE = "node-sqlite";
557
566
  DB_ENGINE_ENV = "MASSU_DB_ENGINE";
567
+ DEFAULT_BUSY_TIMEOUT_MS = 5e3;
568
+ DB_BUSY_TIMEOUT_ENV = "MASSU_DB_BUSY_TIMEOUT_MS";
558
569
  _nodeCtor = null;
559
570
  }
560
571
  });
@@ -431,6 +431,12 @@ import { join as join2 } from "path";
431
431
  function resolveDbEngine() {
432
432
  return process.env[DB_ENGINE_ENV] === "better-sqlite3" ? "better-sqlite3" : DEFAULT_DB_ENGINE;
433
433
  }
434
+ function resolveBusyTimeoutMs() {
435
+ const raw = process.env[DB_BUSY_TIMEOUT_ENV];
436
+ if (raw === void 0 || raw === "") return DEFAULT_BUSY_TIMEOUT_MS;
437
+ const n = Number(raw);
438
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : DEFAULT_BUSY_TIMEOUT_MS;
439
+ }
434
440
  function nodeSqliteCtor() {
435
441
  if (!_nodeCtor) _nodeCtor = req2("node:sqlite").DatabaseSync;
436
442
  return _nodeCtor;
@@ -452,6 +458,7 @@ function openNodeSqlite(dbPath, opts) {
452
458
  enableForeignKeyConstraints: true,
453
459
  allowExtension: false
454
460
  });
461
+ raw.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
455
462
  let savepointSeq = 0;
456
463
  const wrapStmt = (sql) => {
457
464
  const st = raw.prepare(sql);
@@ -535,12 +542,14 @@ function openNodeSqlite(dbPath, opts) {
535
542
  return handle;
536
543
  }
537
544
  function openBetterSqlite3(dbPath, opts) {
538
- return openDatabase(dbPath, opts);
545
+ const db = openDatabase(dbPath, opts);
546
+ db.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
547
+ return db;
539
548
  }
540
549
  function openDatabase2(dbPath, opts = {}) {
541
550
  return resolveDbEngine() === "better-sqlite3" ? openBetterSqlite3(dbPath, opts) : openNodeSqlite(dbPath, opts);
542
551
  }
543
- var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, _nodeCtor;
552
+ var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, DEFAULT_BUSY_TIMEOUT_MS, DB_BUSY_TIMEOUT_ENV, _nodeCtor;
544
553
  var init_db_driver = __esm({
545
554
  "src/db-driver.ts"() {
546
555
  "use strict";
@@ -549,6 +558,8 @@ var init_db_driver = __esm({
549
558
  req2 = createRequire2(import.meta.url);
550
559
  DEFAULT_DB_ENGINE = "node-sqlite";
551
560
  DB_ENGINE_ENV = "MASSU_DB_ENGINE";
561
+ DEFAULT_BUSY_TIMEOUT_MS = 5e3;
562
+ DB_BUSY_TIMEOUT_ENV = "MASSU_DB_BUSY_TIMEOUT_MS";
552
563
  _nodeCtor = null;
553
564
  }
554
565
  });
@@ -431,6 +431,12 @@ import { join as join2 } from "path";
431
431
  function resolveDbEngine() {
432
432
  return process.env[DB_ENGINE_ENV] === "better-sqlite3" ? "better-sqlite3" : DEFAULT_DB_ENGINE;
433
433
  }
434
+ function resolveBusyTimeoutMs() {
435
+ const raw = process.env[DB_BUSY_TIMEOUT_ENV];
436
+ if (raw === void 0 || raw === "") return DEFAULT_BUSY_TIMEOUT_MS;
437
+ const n = Number(raw);
438
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : DEFAULT_BUSY_TIMEOUT_MS;
439
+ }
434
440
  function nodeSqliteCtor() {
435
441
  if (!_nodeCtor) _nodeCtor = req2("node:sqlite").DatabaseSync;
436
442
  return _nodeCtor;
@@ -452,6 +458,7 @@ function openNodeSqlite(dbPath, opts) {
452
458
  enableForeignKeyConstraints: true,
453
459
  allowExtension: false
454
460
  });
461
+ raw.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
455
462
  let savepointSeq = 0;
456
463
  const wrapStmt = (sql) => {
457
464
  const st = raw.prepare(sql);
@@ -535,12 +542,14 @@ function openNodeSqlite(dbPath, opts) {
535
542
  return handle;
536
543
  }
537
544
  function openBetterSqlite3(dbPath, opts) {
538
- return openDatabase(dbPath, opts);
545
+ const db = openDatabase(dbPath, opts);
546
+ db.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
547
+ return db;
539
548
  }
540
549
  function openDatabase2(dbPath, opts = {}) {
541
550
  return resolveDbEngine() === "better-sqlite3" ? openBetterSqlite3(dbPath, opts) : openNodeSqlite(dbPath, opts);
542
551
  }
543
- var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, _nodeCtor;
552
+ var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, DEFAULT_BUSY_TIMEOUT_MS, DB_BUSY_TIMEOUT_ENV, _nodeCtor;
544
553
  var init_db_driver = __esm({
545
554
  "src/db-driver.ts"() {
546
555
  "use strict";
@@ -549,6 +558,8 @@ var init_db_driver = __esm({
549
558
  req2 = createRequire2(import.meta.url);
550
559
  DEFAULT_DB_ENGINE = "node-sqlite";
551
560
  DB_ENGINE_ENV = "MASSU_DB_ENGINE";
561
+ DEFAULT_BUSY_TIMEOUT_MS = 5e3;
562
+ DB_BUSY_TIMEOUT_ENV = "MASSU_DB_BUSY_TIMEOUT_MS";
552
563
  _nodeCtor = null;
553
564
  }
554
565
  });
@@ -431,6 +431,12 @@ import { join as join2 } from "path";
431
431
  function resolveDbEngine() {
432
432
  return process.env[DB_ENGINE_ENV] === "better-sqlite3" ? "better-sqlite3" : DEFAULT_DB_ENGINE;
433
433
  }
434
+ function resolveBusyTimeoutMs() {
435
+ const raw = process.env[DB_BUSY_TIMEOUT_ENV];
436
+ if (raw === void 0 || raw === "") return DEFAULT_BUSY_TIMEOUT_MS;
437
+ const n = Number(raw);
438
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : DEFAULT_BUSY_TIMEOUT_MS;
439
+ }
434
440
  function nodeSqliteCtor() {
435
441
  if (!_nodeCtor) _nodeCtor = req2("node:sqlite").DatabaseSync;
436
442
  return _nodeCtor;
@@ -452,6 +458,7 @@ function openNodeSqlite(dbPath, opts) {
452
458
  enableForeignKeyConstraints: true,
453
459
  allowExtension: false
454
460
  });
461
+ raw.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
455
462
  let savepointSeq = 0;
456
463
  const wrapStmt = (sql) => {
457
464
  const st = raw.prepare(sql);
@@ -535,12 +542,14 @@ function openNodeSqlite(dbPath, opts) {
535
542
  return handle;
536
543
  }
537
544
  function openBetterSqlite3(dbPath, opts) {
538
- return openDatabase(dbPath, opts);
545
+ const db = openDatabase(dbPath, opts);
546
+ db.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
547
+ return db;
539
548
  }
540
549
  function openDatabase2(dbPath, opts = {}) {
541
550
  return resolveDbEngine() === "better-sqlite3" ? openBetterSqlite3(dbPath, opts) : openNodeSqlite(dbPath, opts);
542
551
  }
543
- var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, _nodeCtor;
552
+ var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, DEFAULT_BUSY_TIMEOUT_MS, DB_BUSY_TIMEOUT_ENV, _nodeCtor;
544
553
  var init_db_driver = __esm({
545
554
  "src/db-driver.ts"() {
546
555
  "use strict";
@@ -549,6 +558,8 @@ var init_db_driver = __esm({
549
558
  req2 = createRequire2(import.meta.url);
550
559
  DEFAULT_DB_ENGINE = "node-sqlite";
551
560
  DB_ENGINE_ENV = "MASSU_DB_ENGINE";
561
+ DEFAULT_BUSY_TIMEOUT_MS = 5e3;
562
+ DB_BUSY_TIMEOUT_ENV = "MASSU_DB_BUSY_TIMEOUT_MS";
552
563
  _nodeCtor = null;
553
564
  }
554
565
  });
@@ -431,6 +431,12 @@ import { join as join2 } from "path";
431
431
  function resolveDbEngine() {
432
432
  return process.env[DB_ENGINE_ENV] === "better-sqlite3" ? "better-sqlite3" : DEFAULT_DB_ENGINE;
433
433
  }
434
+ function resolveBusyTimeoutMs() {
435
+ const raw = process.env[DB_BUSY_TIMEOUT_ENV];
436
+ if (raw === void 0 || raw === "") return DEFAULT_BUSY_TIMEOUT_MS;
437
+ const n = Number(raw);
438
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : DEFAULT_BUSY_TIMEOUT_MS;
439
+ }
434
440
  function nodeSqliteCtor() {
435
441
  if (!_nodeCtor) _nodeCtor = req2("node:sqlite").DatabaseSync;
436
442
  return _nodeCtor;
@@ -452,6 +458,7 @@ function openNodeSqlite(dbPath, opts) {
452
458
  enableForeignKeyConstraints: true,
453
459
  allowExtension: false
454
460
  });
461
+ raw.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
455
462
  let savepointSeq = 0;
456
463
  const wrapStmt = (sql) => {
457
464
  const st = raw.prepare(sql);
@@ -535,12 +542,14 @@ function openNodeSqlite(dbPath, opts) {
535
542
  return handle;
536
543
  }
537
544
  function openBetterSqlite3(dbPath, opts) {
538
- return openDatabase(dbPath, opts);
545
+ const db = openDatabase(dbPath, opts);
546
+ db.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
547
+ return db;
539
548
  }
540
549
  function openDatabase2(dbPath, opts = {}) {
541
550
  return resolveDbEngine() === "better-sqlite3" ? openBetterSqlite3(dbPath, opts) : openNodeSqlite(dbPath, opts);
542
551
  }
543
- var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, _nodeCtor;
552
+ var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, DEFAULT_BUSY_TIMEOUT_MS, DB_BUSY_TIMEOUT_ENV, _nodeCtor;
544
553
  var init_db_driver = __esm({
545
554
  "src/db-driver.ts"() {
546
555
  "use strict";
@@ -549,6 +558,8 @@ var init_db_driver = __esm({
549
558
  req2 = createRequire2(import.meta.url);
550
559
  DEFAULT_DB_ENGINE = "node-sqlite";
551
560
  DB_ENGINE_ENV = "MASSU_DB_ENGINE";
561
+ DEFAULT_BUSY_TIMEOUT_MS = 5e3;
562
+ DB_BUSY_TIMEOUT_ENV = "MASSU_DB_BUSY_TIMEOUT_MS";
552
563
  _nodeCtor = null;
553
564
  }
554
565
  });
@@ -1187,6 +1187,12 @@ import { join as join2 } from "path";
1187
1187
  function resolveDbEngine() {
1188
1188
  return process.env[DB_ENGINE_ENV] === "better-sqlite3" ? "better-sqlite3" : DEFAULT_DB_ENGINE;
1189
1189
  }
1190
+ function resolveBusyTimeoutMs() {
1191
+ const raw = process.env[DB_BUSY_TIMEOUT_ENV];
1192
+ if (raw === void 0 || raw === "") return DEFAULT_BUSY_TIMEOUT_MS;
1193
+ const n = Number(raw);
1194
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : DEFAULT_BUSY_TIMEOUT_MS;
1195
+ }
1190
1196
  function nodeSqliteCtor() {
1191
1197
  if (!_nodeCtor) _nodeCtor = req2("node:sqlite").DatabaseSync;
1192
1198
  return _nodeCtor;
@@ -1208,6 +1214,7 @@ function openNodeSqlite(dbPath, opts) {
1208
1214
  enableForeignKeyConstraints: true,
1209
1215
  allowExtension: false
1210
1216
  });
1217
+ raw.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
1211
1218
  let savepointSeq = 0;
1212
1219
  const wrapStmt = (sql) => {
1213
1220
  const st = raw.prepare(sql);
@@ -1291,12 +1298,14 @@ function openNodeSqlite(dbPath, opts) {
1291
1298
  return handle;
1292
1299
  }
1293
1300
  function openBetterSqlite3(dbPath, opts) {
1294
- return openDatabase(dbPath, opts);
1301
+ const db = openDatabase(dbPath, opts);
1302
+ db.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
1303
+ return db;
1295
1304
  }
1296
1305
  function openDatabase2(dbPath, opts = {}) {
1297
1306
  return resolveDbEngine() === "better-sqlite3" ? openBetterSqlite3(dbPath, opts) : openNodeSqlite(dbPath, opts);
1298
1307
  }
1299
- var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, _nodeCtor;
1308
+ var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, DEFAULT_BUSY_TIMEOUT_MS, DB_BUSY_TIMEOUT_ENV, _nodeCtor;
1300
1309
  var init_db_driver = __esm({
1301
1310
  "src/db-driver.ts"() {
1302
1311
  "use strict";
@@ -1305,6 +1314,8 @@ var init_db_driver = __esm({
1305
1314
  req2 = createRequire2(import.meta.url);
1306
1315
  DEFAULT_DB_ENGINE = "node-sqlite";
1307
1316
  DB_ENGINE_ENV = "MASSU_DB_ENGINE";
1317
+ DEFAULT_BUSY_TIMEOUT_MS = 5e3;
1318
+ DB_BUSY_TIMEOUT_ENV = "MASSU_DB_BUSY_TIMEOUT_MS";
1308
1319
  _nodeCtor = null;
1309
1320
  }
1310
1321
  });
@@ -431,6 +431,12 @@ import { join as join2 } from "path";
431
431
  function resolveDbEngine() {
432
432
  return process.env[DB_ENGINE_ENV] === "better-sqlite3" ? "better-sqlite3" : DEFAULT_DB_ENGINE;
433
433
  }
434
+ function resolveBusyTimeoutMs() {
435
+ const raw = process.env[DB_BUSY_TIMEOUT_ENV];
436
+ if (raw === void 0 || raw === "") return DEFAULT_BUSY_TIMEOUT_MS;
437
+ const n = Number(raw);
438
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : DEFAULT_BUSY_TIMEOUT_MS;
439
+ }
434
440
  function nodeSqliteCtor() {
435
441
  if (!_nodeCtor) _nodeCtor = req2("node:sqlite").DatabaseSync;
436
442
  return _nodeCtor;
@@ -452,6 +458,7 @@ function openNodeSqlite(dbPath, opts) {
452
458
  enableForeignKeyConstraints: true,
453
459
  allowExtension: false
454
460
  });
461
+ raw.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
455
462
  let savepointSeq = 0;
456
463
  const wrapStmt = (sql) => {
457
464
  const st = raw.prepare(sql);
@@ -535,12 +542,14 @@ function openNodeSqlite(dbPath, opts) {
535
542
  return handle;
536
543
  }
537
544
  function openBetterSqlite3(dbPath, opts) {
538
- return openDatabase(dbPath, opts);
545
+ const db = openDatabase(dbPath, opts);
546
+ db.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
547
+ return db;
539
548
  }
540
549
  function openDatabase2(dbPath, opts = {}) {
541
550
  return resolveDbEngine() === "better-sqlite3" ? openBetterSqlite3(dbPath, opts) : openNodeSqlite(dbPath, opts);
542
551
  }
543
- var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, _nodeCtor;
552
+ var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, DEFAULT_BUSY_TIMEOUT_MS, DB_BUSY_TIMEOUT_ENV, _nodeCtor;
544
553
  var init_db_driver = __esm({
545
554
  "src/db-driver.ts"() {
546
555
  "use strict";
@@ -549,6 +558,8 @@ var init_db_driver = __esm({
549
558
  req2 = createRequire2(import.meta.url);
550
559
  DEFAULT_DB_ENGINE = "node-sqlite";
551
560
  DB_ENGINE_ENV = "MASSU_DB_ENGINE";
561
+ DEFAULT_BUSY_TIMEOUT_MS = 5e3;
562
+ DB_BUSY_TIMEOUT_ENV = "MASSU_DB_BUSY_TIMEOUT_MS";
552
563
  _nodeCtor = null;
553
564
  }
554
565
  });
@@ -431,6 +431,12 @@ import { join as join2 } from "path";
431
431
  function resolveDbEngine() {
432
432
  return process.env[DB_ENGINE_ENV] === "better-sqlite3" ? "better-sqlite3" : DEFAULT_DB_ENGINE;
433
433
  }
434
+ function resolveBusyTimeoutMs() {
435
+ const raw = process.env[DB_BUSY_TIMEOUT_ENV];
436
+ if (raw === void 0 || raw === "") return DEFAULT_BUSY_TIMEOUT_MS;
437
+ const n = Number(raw);
438
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : DEFAULT_BUSY_TIMEOUT_MS;
439
+ }
434
440
  function nodeSqliteCtor() {
435
441
  if (!_nodeCtor) _nodeCtor = req2("node:sqlite").DatabaseSync;
436
442
  return _nodeCtor;
@@ -452,6 +458,7 @@ function openNodeSqlite(dbPath, opts) {
452
458
  enableForeignKeyConstraints: true,
453
459
  allowExtension: false
454
460
  });
461
+ raw.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
455
462
  let savepointSeq = 0;
456
463
  const wrapStmt = (sql) => {
457
464
  const st = raw.prepare(sql);
@@ -535,12 +542,14 @@ function openNodeSqlite(dbPath, opts) {
535
542
  return handle;
536
543
  }
537
544
  function openBetterSqlite3(dbPath, opts) {
538
- return openDatabase(dbPath, opts);
545
+ const db = openDatabase(dbPath, opts);
546
+ db.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
547
+ return db;
539
548
  }
540
549
  function openDatabase2(dbPath, opts = {}) {
541
550
  return resolveDbEngine() === "better-sqlite3" ? openBetterSqlite3(dbPath, opts) : openNodeSqlite(dbPath, opts);
542
551
  }
543
- var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, _nodeCtor;
552
+ var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, DEFAULT_BUSY_TIMEOUT_MS, DB_BUSY_TIMEOUT_ENV, _nodeCtor;
544
553
  var init_db_driver = __esm({
545
554
  "src/db-driver.ts"() {
546
555
  "use strict";
@@ -549,6 +558,8 @@ var init_db_driver = __esm({
549
558
  req2 = createRequire2(import.meta.url);
550
559
  DEFAULT_DB_ENGINE = "node-sqlite";
551
560
  DB_ENGINE_ENV = "MASSU_DB_ENGINE";
561
+ DEFAULT_BUSY_TIMEOUT_MS = 5e3;
562
+ DB_BUSY_TIMEOUT_ENV = "MASSU_DB_BUSY_TIMEOUT_MS";
552
563
  _nodeCtor = null;
553
564
  }
554
565
  });
@@ -454,6 +454,12 @@ import { join as join2 } from "path";
454
454
  function resolveDbEngine() {
455
455
  return process.env[DB_ENGINE_ENV] === "better-sqlite3" ? "better-sqlite3" : DEFAULT_DB_ENGINE;
456
456
  }
457
+ function resolveBusyTimeoutMs() {
458
+ const raw = process.env[DB_BUSY_TIMEOUT_ENV];
459
+ if (raw === void 0 || raw === "") return DEFAULT_BUSY_TIMEOUT_MS;
460
+ const n = Number(raw);
461
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : DEFAULT_BUSY_TIMEOUT_MS;
462
+ }
457
463
  function nodeSqliteCtor() {
458
464
  if (!_nodeCtor) _nodeCtor = req2("node:sqlite").DatabaseSync;
459
465
  return _nodeCtor;
@@ -475,6 +481,7 @@ function openNodeSqlite(dbPath, opts) {
475
481
  enableForeignKeyConstraints: true,
476
482
  allowExtension: false
477
483
  });
484
+ raw.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
478
485
  let savepointSeq = 0;
479
486
  const wrapStmt = (sql) => {
480
487
  const st = raw.prepare(sql);
@@ -558,12 +565,14 @@ function openNodeSqlite(dbPath, opts) {
558
565
  return handle;
559
566
  }
560
567
  function openBetterSqlite3(dbPath, opts) {
561
- return openDatabase(dbPath, opts);
568
+ const db = openDatabase(dbPath, opts);
569
+ db.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
570
+ return db;
562
571
  }
563
572
  function openDatabase2(dbPath, opts = {}) {
564
573
  return resolveDbEngine() === "better-sqlite3" ? openBetterSqlite3(dbPath, opts) : openNodeSqlite(dbPath, opts);
565
574
  }
566
- var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, _nodeCtor;
575
+ var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, DEFAULT_BUSY_TIMEOUT_MS, DB_BUSY_TIMEOUT_ENV, _nodeCtor;
567
576
  var init_db_driver = __esm({
568
577
  "src/db-driver.ts"() {
569
578
  "use strict";
@@ -572,6 +581,8 @@ var init_db_driver = __esm({
572
581
  req2 = createRequire2(import.meta.url);
573
582
  DEFAULT_DB_ENGINE = "node-sqlite";
574
583
  DB_ENGINE_ENV = "MASSU_DB_ENGINE";
584
+ DEFAULT_BUSY_TIMEOUT_MS = 5e3;
585
+ DB_BUSY_TIMEOUT_ENV = "MASSU_DB_BUSY_TIMEOUT_MS";
575
586
  _nodeCtor = null;
576
587
  }
577
588
  });
@@ -12646,23 +12657,23 @@ function introspectPython(detection, projectRoot) {
12646
12657
  for (const path of candidates) {
12647
12658
  const body = readSafe(path);
12648
12659
  if (body === null) continue;
12649
- const authRegex = /\bDepends\s*\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)/gu;
12660
+ const authRegex = new RegExp(PY_AUTH_DEP_PATTERN, "gu");
12650
12661
  forEachMatch(authRegex, body, (m) => {
12651
12662
  const name = m[1];
12652
12663
  if (!authDeps.has(name)) authDeps.set(name, path);
12653
12664
  });
12654
- const djangoAuthRegex = /^@\s*([a-z_][a-z0-9_]*(?:_required|_login))\b/gmu;
12665
+ const djangoAuthRegex = new RegExp(PY_DJANGO_AUTH_PATTERN, "gmu");
12655
12666
  forEachMatch(djangoAuthRegex, body, (m) => {
12656
12667
  const name = m[1];
12657
12668
  if (!authDeps.has(name)) authDeps.set(name, path);
12658
12669
  });
12659
- const prefixRegex = /\bAPIRouter\s*\(\s*[^)]*?prefix\s*=\s*["']([^"']+)["']/gu;
12670
+ const prefixRegex = new RegExp(PY_API_PREFIX_PATTERN, "gu");
12660
12671
  forEachMatch(prefixRegex, body, (m) => {
12661
12672
  const fullPrefix = m[1];
12662
12673
  const base = extractPrefixBase(fullPrefix);
12663
12674
  if (base && !prefixBases.has(base)) prefixBases.set(base, path);
12664
12675
  });
12665
- const asyncRegex = /^(@pytest\.mark\.asyncio(?:\s*\([^)]*\))?)/gmu;
12676
+ const asyncRegex = new RegExp(PY_TEST_ASYNC_PATTERN, "gmu");
12666
12677
  forEachMatch(asyncRegex, body, (m) => {
12667
12678
  const pat = m[1].trim();
12668
12679
  if (!testAsyncPatterns.has(pat)) testAsyncPatterns.set(pat, path);
@@ -12867,10 +12878,14 @@ function relativeTo(projectRoot, absPath) {
12867
12878
  }
12868
12879
  return basename4(absPath);
12869
12880
  }
12870
- var MAX_FILE_BYTES, MAX_SAMPLES_PER_ADAPTER, MAX_DIR_DEPTH;
12881
+ var PY_AUTH_DEP_PATTERN, PY_DJANGO_AUTH_PATTERN, PY_API_PREFIX_PATTERN, PY_TEST_ASYNC_PATTERN, MAX_FILE_BYTES, MAX_SAMPLES_PER_ADAPTER, MAX_DIR_DEPTH;
12871
12882
  var init_regex_fallback = __esm({
12872
12883
  "src/detect/regex-fallback.ts"() {
12873
12884
  "use strict";
12885
+ PY_AUTH_DEP_PATTERN = String.raw`\bDepends\s*\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)`;
12886
+ PY_DJANGO_AUTH_PATTERN = String.raw`^@\s*([a-z_][a-z0-9_]*(?:_required|_login))\b`;
12887
+ PY_API_PREFIX_PATTERN = String.raw`\bAPIRouter\s*\(\s*[^)]*?prefix\s*=\s*["']([^"']+)["']`;
12888
+ PY_TEST_ASYNC_PATTERN = String.raw`^(@pytest\.mark\.asyncio(?:\s*\([^)]*\))?)`;
12874
12889
  MAX_FILE_BYTES = 256 * 1024;
12875
12890
  MAX_SAMPLES_PER_ADAPTER = 3;
12876
12891
  MAX_DIR_DEPTH = 6;
@@ -431,6 +431,12 @@ import { join as join2 } from "path";
431
431
  function resolveDbEngine() {
432
432
  return process.env[DB_ENGINE_ENV] === "better-sqlite3" ? "better-sqlite3" : DEFAULT_DB_ENGINE;
433
433
  }
434
+ function resolveBusyTimeoutMs() {
435
+ const raw = process.env[DB_BUSY_TIMEOUT_ENV];
436
+ if (raw === void 0 || raw === "") return DEFAULT_BUSY_TIMEOUT_MS;
437
+ const n = Number(raw);
438
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : DEFAULT_BUSY_TIMEOUT_MS;
439
+ }
434
440
  function nodeSqliteCtor() {
435
441
  if (!_nodeCtor) _nodeCtor = req2("node:sqlite").DatabaseSync;
436
442
  return _nodeCtor;
@@ -452,6 +458,7 @@ function openNodeSqlite(dbPath, opts) {
452
458
  enableForeignKeyConstraints: true,
453
459
  allowExtension: false
454
460
  });
461
+ raw.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
455
462
  let savepointSeq = 0;
456
463
  const wrapStmt = (sql) => {
457
464
  const st = raw.prepare(sql);
@@ -535,12 +542,14 @@ function openNodeSqlite(dbPath, opts) {
535
542
  return handle;
536
543
  }
537
544
  function openBetterSqlite3(dbPath, opts) {
538
- return openDatabase(dbPath, opts);
545
+ const db = openDatabase(dbPath, opts);
546
+ db.exec(`PRAGMA busy_timeout = ${resolveBusyTimeoutMs()}`);
547
+ return db;
539
548
  }
540
549
  function openDatabase2(dbPath, opts = {}) {
541
550
  return resolveDbEngine() === "better-sqlite3" ? openBetterSqlite3(dbPath, opts) : openNodeSqlite(dbPath, opts);
542
551
  }
543
- var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, _nodeCtor;
552
+ var req2, DEFAULT_DB_ENGINE, DB_ENGINE_ENV, DEFAULT_BUSY_TIMEOUT_MS, DB_BUSY_TIMEOUT_ENV, _nodeCtor;
544
553
  var init_db_driver = __esm({
545
554
  "src/db-driver.ts"() {
546
555
  "use strict";
@@ -549,6 +558,8 @@ var init_db_driver = __esm({
549
558
  req2 = createRequire2(import.meta.url);
550
559
  DEFAULT_DB_ENGINE = "node-sqlite";
551
560
  DB_ENGINE_ENV = "MASSU_DB_ENGINE";
561
+ DEFAULT_BUSY_TIMEOUT_MS = 5e3;
562
+ DB_BUSY_TIMEOUT_ENV = "MASSU_DB_BUSY_TIMEOUT_MS";
552
563
  _nodeCtor = null;
553
564
  }
554
565
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@massu/core",
3
- "version": "2.3.1",
3
+ "version": "2.4.0",
4
4
  "type": "module",
5
5
  "description": "AI Engineering Governance MCP Server - Session memory, knowledge system, feature registry, code intelligence, rule enforcement, tiered tooling (12 free / 73 total), 56 workflow commands, 11 agents, 20+ patterns",
6
6
  "main": "./dist/cli.js",
@@ -28,8 +28,8 @@
28
28
  "build:adapter-types": "tsc -p tsconfig.adapter-types.json",
29
29
  "build:adapter-subpath": "tsx scripts/bundle-adapters.ts --subpath-only",
30
30
  "build:bundle-adapters": "tsx scripts/bundle-adapters.ts",
31
- "build:cli": "esbuild --bundle --platform=node --format=esm --outfile=dist/cli.js src/cli.ts --external:better-sqlite3 --external:yaml --external:zod --external:chokidar --external:proper-lockfile --external:fsevents --external:web-tree-sitter --external:tweetnacl --external:tar --external:smol-toml --external:vscode-languageserver-protocol --external:onnxruntime-web --external:@massu/adapter-rails --external:@massu/adapter-spring --banner:js='#!/usr/bin/env node\nimport{createRequire as __cr}from\"module\";const require=__cr(import.meta.url);'",
32
- "build:hooks": "esbuild --bundle --platform=node --format=esm --outdir=dist/hooks src/hooks/*.ts --external:better-sqlite3 --external:yaml --external:zod --external:chokidar --external:proper-lockfile --external:fsevents --external:web-tree-sitter --external:tweetnacl --external:tar --external:smol-toml --external:vscode-languageserver-protocol --external:onnxruntime-web --external:@massu/adapter-rails --external:@massu/adapter-spring --banner:js='import{createRequire as __cr}from\"module\";const require=__cr(import.meta.url);'",
31
+ "build:cli": "node scripts/build-bundles.mjs --cli",
32
+ "build:hooks": "node scripts/build-bundles.mjs --hooks",
33
33
  "prepublishOnly": "bash ../../scripts/prepublish-check.sh && node ../../scripts/bundle-pubkey.mjs && node ../../scripts/bundle-license-pubkey.mjs && node ../../scripts/bundle-promotion-pubkey.mjs && node ../../scripts/bundle-audit-export-pubkey.mjs && cp ../../CHANGELOG.md ./CHANGELOG.md && npm run build && bash ../../scripts/npm-publish-guard.sh",
34
34
  "bench:watch": "tsx test/perf/watch-benchmark.ts",
35
35
  "build:owned-sidecars": "node scripts/gen-owned-sidecars.mjs"