codesift-mcp 0.13.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/dist/cli/commands.d.ts.map +1 -1
  2. package/dist/cli/commands.js +14 -1
  3. package/dist/cli/commands.js.map +1 -1
  4. package/dist/cli/hooks/pre-tool-use.d.ts +1 -0
  5. package/dist/cli/hooks/pre-tool-use.d.ts.map +1 -1
  6. package/dist/cli/hooks/pre-tool-use.js +11 -2
  7. package/dist/cli/hooks/pre-tool-use.js.map +1 -1
  8. package/dist/config.d.ts +13 -0
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/config.js +21 -0
  11. package/dist/config.js.map +1 -1
  12. package/dist/formatters-core.d.ts +7 -0
  13. package/dist/formatters-core.d.ts.map +1 -1
  14. package/dist/formatters-core.js +14 -3
  15. package/dist/formatters-core.js.map +1 -1
  16. package/dist/parser/extractors/hono-inline-analyzer.js +2 -2
  17. package/dist/parser/extractors/hono-inline-analyzer.js.map +1 -1
  18. package/dist/register-tool-groups/core/search.d.ts.map +1 -1
  19. package/dist/register-tool-groups/core/search.js +14 -0
  20. package/dist/register-tool-groups/core/search.js.map +1 -1
  21. package/dist/register-tool-groups/core/symbols.d.ts.map +1 -1
  22. package/dist/register-tool-groups/core/symbols.js +50 -5
  23. package/dist/register-tool-groups/core/symbols.js.map +1 -1
  24. package/dist/server.d.ts.map +1 -1
  25. package/dist/server.js +54 -2
  26. package/dist/server.js.map +1 -1
  27. package/dist/storage/_shared.d.ts +21 -0
  28. package/dist/storage/_shared.d.ts.map +1 -1
  29. package/dist/storage/_shared.js +36 -0
  30. package/dist/storage/_shared.js.map +1 -1
  31. package/dist/storage/index-footprint.d.ts +12 -0
  32. package/dist/storage/index-footprint.d.ts.map +1 -0
  33. package/dist/storage/index-footprint.js +41 -0
  34. package/dist/storage/index-footprint.js.map +1 -0
  35. package/dist/storage/index-store.d.ts +30 -3
  36. package/dist/storage/index-store.d.ts.map +1 -1
  37. package/dist/storage/index-store.js +122 -9
  38. package/dist/storage/index-store.js.map +1 -1
  39. package/dist/storage/registry.d.ts.map +1 -1
  40. package/dist/storage/registry.js +64 -1
  41. package/dist/storage/registry.js.map +1 -1
  42. package/dist/storage/sqlite/accessors.d.ts +34 -0
  43. package/dist/storage/sqlite/accessors.d.ts.map +1 -0
  44. package/dist/storage/sqlite/accessors.js +178 -0
  45. package/dist/storage/sqlite/accessors.js.map +1 -0
  46. package/dist/storage/sqlite/connection.d.ts +43 -0
  47. package/dist/storage/sqlite/connection.d.ts.map +1 -0
  48. package/dist/storage/sqlite/connection.js +241 -0
  49. package/dist/storage/sqlite/connection.js.map +1 -0
  50. package/dist/storage/sqlite/errors.d.ts +38 -0
  51. package/dist/storage/sqlite/errors.d.ts.map +1 -0
  52. package/dist/storage/sqlite/errors.js +150 -0
  53. package/dist/storage/sqlite/errors.js.map +1 -0
  54. package/dist/storage/sqlite/index-io.d.ts +49 -0
  55. package/dist/storage/sqlite/index-io.d.ts.map +1 -0
  56. package/dist/storage/sqlite/index-io.js +268 -0
  57. package/dist/storage/sqlite/index-io.js.map +1 -0
  58. package/dist/storage/sqlite/meta.d.ts +41 -0
  59. package/dist/storage/sqlite/meta.d.ts.map +1 -0
  60. package/dist/storage/sqlite/meta.js +28 -0
  61. package/dist/storage/sqlite/meta.js.map +1 -0
  62. package/dist/storage/sqlite/rows.d.ts +64 -0
  63. package/dist/storage/sqlite/rows.d.ts.map +1 -0
  64. package/dist/storage/sqlite/rows.js +186 -0
  65. package/dist/storage/sqlite/rows.js.map +1 -0
  66. package/dist/storage/sqlite/runtime.d.ts +6 -0
  67. package/dist/storage/sqlite/runtime.d.ts.map +1 -0
  68. package/dist/storage/sqlite/runtime.js +31 -0
  69. package/dist/storage/sqlite/runtime.js.map +1 -0
  70. package/dist/storage/sqlite/schema.d.ts +34 -0
  71. package/dist/storage/sqlite/schema.d.ts.map +1 -0
  72. package/dist/storage/sqlite/schema.js +98 -0
  73. package/dist/storage/sqlite/schema.js.map +1 -0
  74. package/dist/storage/sqlite-index-store.d.ts +40 -80
  75. package/dist/storage/sqlite-index-store.d.ts.map +1 -1
  76. package/dist/storage/sqlite-index-store.js +32 -577
  77. package/dist/storage/sqlite-index-store.js.map +1 -1
  78. package/dist/storage/telemetry/config.d.ts.map +1 -1
  79. package/dist/storage/telemetry/config.js +14 -3
  80. package/dist/storage/telemetry/config.js.map +1 -1
  81. package/dist/storage/telemetry/payload.d.ts.map +1 -1
  82. package/dist/storage/telemetry/payload.js +4 -1
  83. package/dist/storage/telemetry/payload.js.map +1 -1
  84. package/dist/storage/telemetry/retro-aggregator.d.ts +27 -0
  85. package/dist/storage/telemetry/retro-aggregator.d.ts.map +1 -0
  86. package/dist/storage/telemetry/retro-aggregator.js +179 -0
  87. package/dist/storage/telemetry/retro-aggregator.js.map +1 -0
  88. package/dist/storage/telemetry/sanitizer.d.ts +30 -0
  89. package/dist/storage/telemetry/sanitizer.d.ts.map +1 -1
  90. package/dist/storage/telemetry/sanitizer.js +23 -0
  91. package/dist/storage/telemetry/sanitizer.js.map +1 -1
  92. package/dist/storage/telemetry/uploader.d.ts.map +1 -1
  93. package/dist/storage/telemetry/uploader.js +6 -0
  94. package/dist/storage/telemetry/uploader.js.map +1 -1
  95. package/dist/storage/workspace-resolver.js +1 -1
  96. package/dist/storage/workspace-resolver.js.map +1 -1
  97. package/dist/tools/index-tools/folder-indexer.d.ts.map +1 -1
  98. package/dist/tools/index-tools/folder-indexer.js +4 -1
  99. package/dist/tools/index-tools/folder-indexer.js.map +1 -1
  100. package/dist/tools/index-tools/registry.d.ts +16 -0
  101. package/dist/tools/index-tools/registry.d.ts.map +1 -1
  102. package/dist/tools/index-tools/registry.js +79 -1
  103. package/dist/tools/index-tools/registry.js.map +1 -1
  104. package/dist/tools/index-tools.d.ts +2 -2
  105. package/dist/tools/index-tools.d.ts.map +1 -1
  106. package/dist/tools/index-tools.js +2 -2
  107. package/dist/tools/index-tools.js.map +1 -1
  108. package/dist/tools/plan-turn/stale-index.d.ts +32 -0
  109. package/dist/tools/plan-turn/stale-index.d.ts.map +1 -1
  110. package/dist/tools/plan-turn/stale-index.js +42 -5
  111. package/dist/tools/plan-turn/stale-index.js.map +1 -1
  112. package/dist/tools/search-tools/types.d.ts +20 -0
  113. package/dist/tools/search-tools/types.d.ts.map +1 -1
  114. package/dist/tools/search-tools/zero-hit.d.ts.map +1 -1
  115. package/dist/tools/search-tools/zero-hit.js +0 -0
  116. package/dist/tools/search-tools/zero-hit.js.map +1 -1
  117. package/dist/tools/status-tools.d.ts +10 -0
  118. package/dist/tools/status-tools.d.ts.map +1 -1
  119. package/dist/tools/status-tools.js +14 -4
  120. package/dist/tools/status-tools.js.map +1 -1
  121. package/dist/tools/symbol-tools.d.ts +95 -1
  122. package/dist/tools/symbol-tools.d.ts.map +1 -1
  123. package/dist/tools/symbol-tools.js +182 -26
  124. package/dist/tools/symbol-tools.js.map +1 -1
  125. package/dist/types.d.ts +6 -0
  126. package/dist/types.d.ts.map +1 -1
  127. package/dist/utils/git-head.d.ts +12 -0
  128. package/dist/utils/git-head.d.ts.map +1 -1
  129. package/dist/utils/git-head.js +25 -0
  130. package/dist/utils/git-head.js.map +1 -1
  131. package/package.json +6 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accessors.js","sourceRoot":"","sources":["../../../src/storage/sqlite/accessors.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,aAAa,EACb,eAAe,EACf,cAAc,GACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,cAAc,EAAqB,MAAM,WAAW,CAAC;AAC9D,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,WAAW,EACX,WAAW,GAGZ,MAAM,WAAW,CAAC;AAEnB;;;;;;GAMG;AAEH;;;;;;;;GAQG;AACH,SAAS,eAAe,CAAI,MAAc,EAAE,IAAa;IACvD,IAAI,CAAC;QACH,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAClC,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAc,EACd,QAAgB;IAEhB,MAAM,EAAE,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;IACrC,OAAO,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE;QAClC,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,2CAA2C,CAAC,CAAC,GAAG,CAAC,QAAQ,CAEnE,CAAC;QACd,OAAO,GAAG,EAAE,QAAQ,IAAI,SAAS,CAAC;IACpC,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,MAAc;IACzD,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;IAC1B,IAAI,MAAoC,CAAC;IACzC,IAAI,GAAG,GAAwB,IAAI,CAAC;IACpC,IAAI,CAAC;QACH,4FAA4F;QAC5F,iFAAiF;QACjF,6FAA6F;QAC7F,8FAA8F;QAC9F,qFAAqF;QACrF,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAClD,MAAM,GAAG,QAAQ,CAAC;QAClB,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,CAAC,GAAW,EAAsB,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;YAC/E,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;YAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;YAC1B,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBAC7C,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACxB,OAAO,IAAI,CAAC,CAAC,gCAAgC;YAC/C,CAAC;YAED,0FAA0F;YAC1F,4FAA4F;YAC5F,sEAAsE;YACtE,MAAM,KAAK,GACT,QAAQ,CAAC,OAAO,CAAC,oCAAoC,CAAC,CAAC,GAAG,EAC3D,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;YACtB,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,mCAAmC,CAAC,CAAC,GAAG,EAE5D,CAAC;YAEd,MAAM,OAAO,GAAiB;gBAC5B,IAAI;gBACJ,IAAI;gBACJ,KAAK;gBACL,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;gBAC3C,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;gBAC3C,YAAY,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC;gBACrC,UAAU,EAAE,KAAK,CAAC,MAAM;aACzB,CAAC;YAEF,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;YAE7C,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACxB,GAAG,GAAG,OAAO,CAAC;QAChB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,eAAe,CAAC,QAAQ,CAAC,CAAC;YAC1B,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAClC,CAAC;YAAS,CAAC;QACT,IAAI,CAAC;YAAC,MAAM,EAAE,KAAK,EAAE,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,mEAAmE;AACnE,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAc,EACd,QAAgB;IAEhB,MAAM,EAAE,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;IACrC,OAAO,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE;QAClC,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,oCAAoC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAE5D,CAAC;QACd,OAAO,GAAG,CAAC,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/C,CAAC,CAAC,CAAC;AACL,CAAC;AAED,6FAA6F;AAC7F,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,MAAc,EACd,WAAmB,EACnB,UAAwB,EACxB,SAAqB;IAErB,MAAM,EAAE,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;IAErC,IAAI,aAAa,CAAC,EAAE,EAAE,MAAM,CAAC,KAAK,SAAS,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,mDAAmD,MAAM,EAAE,CAAC,CAAC;IAC/E,CAAC;IAED,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACjB,IAAI,CAAC;QACH,EAAE,CAAC,OAAO,CAAC,oCAAoC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAElE,MAAM,MAAM,GAAG,EAAE,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;QAC7C,KAAK,MAAM,GAAG,IAAI,UAAU;YAAE,MAAM,CAAC,GAAG,CAAC,GAAI,WAAW,CAAC,GAAG,CAAa,CAAC,CAAC;QAE3E,IAAI,SAAS;YAAE,EAAE,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,GAAG,CAAC,GAAI,cAAc,CAAC,SAAS,CAAa,CAAC,CAAC;QAE1F,cAAc,CAAC,EAAE,EAAE,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrD,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,eAAe,CAAC,EAAE,CAAC,CAAC;QACpB,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAClC,CAAC;AACH,CAAC;AAED,2DAA2D;AAC3D,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,MAAc,EACd,WAAmB;IAEnB,MAAM,EAAE,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;IACrC,IAAI,aAAa,CAAC,EAAE,EAAE,MAAM,CAAC,KAAK,SAAS;QAAE,OAAO,CAAC,oBAAoB;IAEzE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACjB,IAAI,CAAC;QACH,EAAE,CAAC,OAAO,CAAC,oCAAoC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAClE,EAAE,CAAC,OAAO,CAAC,kCAAkC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAChE,cAAc,CAAC,EAAE,EAAE,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrD,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,eAAe,CAAC,EAAE,CAAC,CAAC;QACpB,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAClC,CAAC;AACH,CAAC;AAED,gFAAgF;AAChF,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,MAAc,EACd,QAAgB;IAEhB,MAAM,EAAE,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;IACrC,OAAO,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE;QAClC,MAAM,IAAI,GAAG,aAAa,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACvC,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,EAAE;aACZ,OAAO,CAAC,sCAAsC,CAAC;aAC/C,GAAG,CAAC,QAAQ,CAA2B,CAAC;QAC3C,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAc;IACjD,MAAM,EAAE,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;IACrC,OAAO,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE;QAClC,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC,GAAG,EAA8B,CAAC;QAChF,OAAO,GAAG,CAAC,YAAY,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,43 @@
1
+ import type { DatabaseSync as DatabaseSyncType } from "node:sqlite";
2
+ /**
3
+ * ROLLBACK that cannot replace the error that caused it.
4
+ *
5
+ * SQLite rolls back automatically on the faults that matter most here — `SQLITE_FULL`,
6
+ * `SQLITE_IOERR`, `SQLITE_NOMEM`, `SQLITE_INTERRUPT` — so an explicit ROLLBACK in the catch block
7
+ * then throws `cannot rollback - no transaction is active`, and THAT is what propagates. Verified
8
+ * on node v24.18.0: a write that failed with `database or disk is full` (errcode 13) arrived at
9
+ * the caller as a rollback complaint instead, which classifies as nothing at all.
10
+ *
11
+ * Losing the transaction is not the risk — SQLite already ended it. Losing the diagnosis is.
12
+ */
13
+ export declare function rollbackQuietly(db: DatabaseSyncType): void;
14
+ /**
15
+ * Open (and cache) a connection, creating the schema on first use.
16
+ *
17
+ * WAL is the reason this migration solves a problem a sharded JSON layout would not: the MCP
18
+ * server and the `codesift postindex-file` hook are separate processes writing the same
19
+ * index, and WAL gives them concurrent readers with a single writer instead of a corruption
20
+ * window. `busy_timeout` makes a writer wait its turn rather than throwing SQLITE_BUSY when
21
+ * an agent edits files faster than the previous write settles.
22
+ */
23
+ export declare function openIndexDb(dbPath: string): Promise<DatabaseSyncType>;
24
+ /** Close one cached connection (tests, and the shutdown path). */
25
+ export declare function closeIndexDb(dbPath: string): void;
26
+ export declare function closeAllIndexDbs(): void;
27
+ /**
28
+ * Open a second connection for reading only.
29
+ *
30
+ * The paged read in `index-io.ts` yields to the event loop between pages, and the cached
31
+ * connection is shared by every request in this process — so a write landing mid-traversal would
32
+ * let one `CodeIndex` be assembled out of two different database states. Holding a read
33
+ * transaction on the SHARED connection cannot fix that: any write arriving during the yield would
34
+ * hit "cannot start a transaction within a transaction".
35
+ *
36
+ * A private connection in WAL mode sees a stable snapshot from `BEGIN` to `COMMIT` regardless of
37
+ * what other connections commit meanwhile. It costs one open per cold load, which happens once
38
+ * per process per repo.
39
+ */
40
+ export declare function openReadConnection(dbPath: string): Promise<DatabaseSyncType>;
41
+ export declare function readMetaValue(db: DatabaseSyncType, key: string): string | undefined;
42
+ export declare function writeMetaValue(db: DatabaseSyncType, key: string, value: string): void;
43
+ //# sourceMappingURL=connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../../src/storage/sqlite/connection.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,IAAI,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAiBpE;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,gBAAgB,GAAG,IAAI,CAO1D;AAmBD;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAgB3E;AA+FD,kEAAkE;AAClE,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CASjD;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAgBlF;AAMD,wBAAgB,aAAa,CAAC,EAAE,EAAE,gBAAgB,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKnF;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,gBAAgB,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAIrF"}
@@ -0,0 +1,241 @@
1
+ import { mkdir } from "node:fs/promises";
2
+ import { dirname } from "node:path";
3
+ import { IndexStorageError, rethrowOperational } from "./errors.js";
4
+ import { loadSqliteCtor } from "./runtime.js";
5
+ import { MIGRATE_V1_TO_V2_SQL, SCHEMA_SQL, SCHEMA_VERSION } from "./schema.js";
6
+ /**
7
+ * Connection handling, schema creation, migration, and the `meta` key/value accessors.
8
+ *
9
+ * The open-connection cache lives HERE and nowhere else. One cache is the whole point: two
10
+ * modules each holding their own `connections` map would each hand out a different handle for the
11
+ * same path, so `closeIndexDb` would close one and leave the other open, and a write through one
12
+ * would be invisible to a `PRAGMA data_version` read through the other. Type-checking, linting and
13
+ * most tests all pass over that, which is exactly why it is called out.
14
+ */
15
+ const connections = new Map();
16
+ /**
17
+ * ROLLBACK that cannot replace the error that caused it.
18
+ *
19
+ * SQLite rolls back automatically on the faults that matter most here — `SQLITE_FULL`,
20
+ * `SQLITE_IOERR`, `SQLITE_NOMEM`, `SQLITE_INTERRUPT` — so an explicit ROLLBACK in the catch block
21
+ * then throws `cannot rollback - no transaction is active`, and THAT is what propagates. Verified
22
+ * on node v24.18.0: a write that failed with `database or disk is full` (errcode 13) arrived at
23
+ * the caller as a rollback complaint instead, which classifies as nothing at all.
24
+ *
25
+ * Losing the transaction is not the risk — SQLite already ended it. Losing the diagnosis is.
26
+ */
27
+ export function rollbackQuietly(db) {
28
+ try {
29
+ db.exec("ROLLBACK");
30
+ }
31
+ catch {
32
+ /* already rolled back by SQLite, or the handle is gone — either way the original error is
33
+ the one worth reporting */
34
+ }
35
+ }
36
+ /**
37
+ * Opens that have started but not yet reached `connections`.
38
+ *
39
+ * Without this, `openIndexDb` is a check-then-act straddling two awaits (`loadSqliteCtor`, then
40
+ * `mkdir`): two concurrent callers both miss the cache, both construct a handle, and the second
41
+ * `connections.set` overwrites the first. The orphan is not in the map, so `closeIndexDb` and
42
+ * `closeAllIndexDbs` can never reach it — a leaked descriptor and a WAL read mark for the life of
43
+ * the process, arriving on exactly the EMFILE/BUSY conditions the rest of this file works to avoid.
44
+ * Reproducible with `Promise.all([openIndexDb(p), openIndexDb(p)])`, and reachable in production
45
+ * because a save and a read can race on a cold cache.
46
+ *
47
+ * Known and unchanged: a `closeIndexDb` issued WHILE an open is in flight does not cancel it, so
48
+ * the completing open still populates the cache. That is a narrower window than the one closed
49
+ * here, and pretending otherwise in a comment is how the previous defect survived.
50
+ */
51
+ const opening = new Map();
52
+ /**
53
+ * Open (and cache) a connection, creating the schema on first use.
54
+ *
55
+ * WAL is the reason this migration solves a problem a sharded JSON layout would not: the MCP
56
+ * server and the `codesift postindex-file` hook are separate processes writing the same
57
+ * index, and WAL gives them concurrent readers with a single writer instead of a corruption
58
+ * window. `busy_timeout` makes a writer wait its turn rather than throwing SQLITE_BUSY when
59
+ * an agent edits files faster than the previous write settles.
60
+ */
61
+ export async function openIndexDb(dbPath) {
62
+ const cached = connections.get(dbPath);
63
+ if (cached)
64
+ return cached;
65
+ const inFlight = opening.get(dbPath);
66
+ if (inFlight)
67
+ return inFlight;
68
+ const attempt = openUncachedIndexDb(dbPath);
69
+ opening.set(dbPath, attempt);
70
+ try {
71
+ return await attempt;
72
+ }
73
+ finally {
74
+ // Safe to drop once settled: `openUncachedIndexDb` populates `connections` before it resolves,
75
+ // so any caller arriving after this point hits the cache instead.
76
+ opening.delete(dbPath);
77
+ }
78
+ }
79
+ async function openUncachedIndexDb(dbPath) {
80
+ const Ctor = await loadSqliteCtor();
81
+ if (!Ctor)
82
+ throw new Error("node:sqlite is unavailable (requires Node >= 22.5)");
83
+ if (dbPath !== ":memory:")
84
+ await mkdir(dirname(dbPath), { recursive: true });
85
+ let db;
86
+ try {
87
+ db = new Ctor(dbPath);
88
+ db.exec("PRAGMA journal_mode = WAL");
89
+ db.exec("PRAGMA busy_timeout = 5000");
90
+ db.exec("PRAGMA foreign_keys = ON");
91
+ db.exec(SCHEMA_SQL);
92
+ }
93
+ catch (err) {
94
+ // Close before rethrowing: the constructor can succeed and a PRAGMA still fail, and this
95
+ // handle never reaches the `connections` cache, so nothing else would ever close it. On a
96
+ // retry loop against a sick database that leaks a descriptor and a lock per attempt —
97
+ // which makes the very BUSY/EMFILE conditions being reported worse.
98
+ try {
99
+ db?.close();
100
+ }
101
+ catch {
102
+ /* already dead — the original fault is the one worth reporting */
103
+ }
104
+ // A corrupt or unopenable file fails here, before any row is read — the one place where
105
+ // "cannot open" would otherwise look exactly like "nothing indexed yet".
106
+ rethrowOperational(err, dbPath);
107
+ }
108
+ // Classified like the block above, and for a reason that is not obvious: `CREATE TABLE IF NOT
109
+ // EXISTS` can succeed on a database whose DATA pages are corrupt, because it only consults
110
+ // sqlite_schema. The first statement that actually reads a page is this one — so on real
111
+ // corruption the fault surfaces HERE, outside the guard, and reached callers unwrapped. Found by
112
+ // a test that was aiming at something else; the audits did not catch it either.
113
+ let stored;
114
+ try {
115
+ stored = readMetaValue(db, "schema_version");
116
+ }
117
+ catch (err) {
118
+ try {
119
+ db.close();
120
+ }
121
+ catch { /* not cached yet — nothing else would ever close it */ }
122
+ rethrowOperational(err, dbPath);
123
+ }
124
+ if (stored === undefined) {
125
+ writeMetaValue(db, "schema_version", String(SCHEMA_VERSION));
126
+ }
127
+ else if (Number(stored) > SCHEMA_VERSION) {
128
+ try {
129
+ db.close();
130
+ }
131
+ catch { /* a throwing close must not mask the message below */ }
132
+ // An IndexStorageError, not a plain one, and the difference is the whole point: every read
133
+ // path that routes through `workspace-scope-helper.ts` does
134
+ // `if (isIndexStorageError(err)) throw err;` and otherwise collapses the failure to
135
+ // `index = null`. As a plain Error this instruction — the one thing that tells the user how to
136
+ // fix it — was swallowed, and the tool reported an empty repo.
137
+ throw new IndexStorageError(`Index at ${dbPath} was written by a newer CodeSift (schema v${stored} > v${SCHEMA_VERSION}). Upgrade codesift-mcp.`, "SCHEMA_TOO_NEW", dbPath);
138
+ }
139
+ else if (Number(stored) < SCHEMA_VERSION) {
140
+ // One transaction: a half-applied table swap would leave `symbols_v1` as the only copy
141
+ // of the rows, under a name nothing reads.
142
+ try {
143
+ db.exec("BEGIN IMMEDIATE");
144
+ // Re-read UNDER the lock. The version above was read before `BEGIN IMMEDIATE`, so two
145
+ // processes opening the same index at once both saw `stored < 2` and both queued the
146
+ // migration; the second then re-copied a table that was already v2. Harmless — v2 has no
147
+ // PRIMARY KEY, so nothing is dropped — but it is a full-table copy of a repo-sized table
148
+ // done for nothing, and on a large index that is seconds of wall clock plus the disk churn.
149
+ // The lock is the only point at which the answer is stable, so this is where to ask.
150
+ // Fall through rather than return early: the connection still has to reach `connections`
151
+ // below, and an early return here would quietly stop caching it for the racing process.
152
+ const current = Number(readMetaValue(db, "schema_version") ?? 0);
153
+ if (current < SCHEMA_VERSION && current < 2) {
154
+ db.exec(MIGRATE_V1_TO_V2_SQL);
155
+ // The rebuild keeps every row the v1 table still HELD — it cannot bring back the ones
156
+ // v1 already discarded on id collision (73,165 across 16 indexes when this was found).
157
+ // Without this marker the upgraded database looks like any other complete v2 index, so
158
+ // a caller would read a short symbol list as a fact about the code. Only a full reindex
159
+ // from source can restore them, and `saveIndexSqlite` clears the flag when it does.
160
+ writeMetaValue(db, "lossy_v1_migration", "1");
161
+ }
162
+ if (current < SCHEMA_VERSION) {
163
+ writeMetaValue(db, "schema_version", String(SCHEMA_VERSION));
164
+ }
165
+ db.exec("COMMIT");
166
+ }
167
+ catch (err) {
168
+ rollbackQuietly(db);
169
+ try {
170
+ db.close();
171
+ }
172
+ catch { /* already dead */ }
173
+ rethrowOperational(err, dbPath);
174
+ }
175
+ }
176
+ connections.set(dbPath, db);
177
+ return db;
178
+ }
179
+ /** Close one cached connection (tests, and the shutdown path). */
180
+ export function closeIndexDb(dbPath) {
181
+ const db = connections.get(dbPath);
182
+ if (!db)
183
+ return;
184
+ connections.delete(dbPath);
185
+ try {
186
+ db.close();
187
+ }
188
+ catch {
189
+ /* already closed — nothing to protect here */
190
+ }
191
+ }
192
+ export function closeAllIndexDbs() {
193
+ for (const path of [...connections.keys()])
194
+ closeIndexDb(path);
195
+ }
196
+ /**
197
+ * Open a second connection for reading only.
198
+ *
199
+ * The paged read in `index-io.ts` yields to the event loop between pages, and the cached
200
+ * connection is shared by every request in this process — so a write landing mid-traversal would
201
+ * let one `CodeIndex` be assembled out of two different database states. Holding a read
202
+ * transaction on the SHARED connection cannot fix that: any write arriving during the yield would
203
+ * hit "cannot start a transaction within a transaction".
204
+ *
205
+ * A private connection in WAL mode sees a stable snapshot from `BEGIN` to `COMMIT` regardless of
206
+ * what other connections commit meanwhile. It costs one open per cold load, which happens once
207
+ * per process per repo.
208
+ */
209
+ export async function openReadConnection(dbPath) {
210
+ const Ctor = await loadSqliteCtor();
211
+ if (!Ctor)
212
+ throw new Error("node:sqlite is unavailable (requires Node >= 22.5)");
213
+ const db = new Ctor(dbPath);
214
+ // The constructor can succeed and the PRAGMA still fail — corruption and I/O faults often
215
+ // surface on the first statement, not at open. This handle never reaches the `connections`
216
+ // cache, and the caller's `finally` only closes what it managed to assign, so an unguarded
217
+ // throw here leaks a descriptor on every attempt. Same guard as `openIndexDb`, same reason;
218
+ // it was missing here only because this connection is deliberately not cached.
219
+ try {
220
+ db.exec("PRAGMA busy_timeout = 5000");
221
+ }
222
+ catch (err) {
223
+ try {
224
+ db.close();
225
+ }
226
+ catch { /* already unusable — the leak is what we came to prevent */ }
227
+ throw err;
228
+ }
229
+ return db;
230
+ }
231
+ // ---------------------------------------------------------------------------
232
+ // meta helpers
233
+ // ---------------------------------------------------------------------------
234
+ export function readMetaValue(db, key) {
235
+ const row = db.prepare("SELECT value FROM meta WHERE key = ?").get(key);
236
+ return row?.value;
237
+ }
238
+ export function writeMetaValue(db, key, value) {
239
+ db.prepare("INSERT INTO meta(key, value) VALUES(?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(key, value);
240
+ }
241
+ //# sourceMappingURL=connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../../../src/storage/sqlite/connection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Operational failures of the index store, as distinct from "this repo has no index".
3
+ *
4
+ * Pure classification: no database handle, no filesystem, no dependency on any other module here.
5
+ * That is deliberate — every other module in this backend rethrows through `rethrowOperational`,
6
+ * so anything this file imported would become a dependency of the whole backend.
7
+ */
8
+ /**
9
+ * A storage fault, as distinct from "this repo has no index".
10
+ *
11
+ * Both used to arrive at callers as `null`, so a locked or corrupt database was indistinguishable
12
+ * from an unindexed repo: tools reported "not indexed" (prompting a pointless full reindex over a
13
+ * database that was merely busy) or returned empty results that read as an authoritative "no
14
+ * matches". Empty-because-broken is the worst answer an index can give, because nothing about it
15
+ * looks wrong.
16
+ */
17
+ export declare class IndexStorageError extends Error {
18
+ readonly code: string;
19
+ readonly path: string;
20
+ constructor(message: string, code: string, path: string, options?: {
21
+ cause?: unknown;
22
+ });
23
+ }
24
+ /**
25
+ * Structural check, not `instanceof`.
26
+ *
27
+ * A duplicated module instance (bundler, worker/thread boundary, a test importing through a
28
+ * different specifier) makes `instanceof` false for an object that is in every observable way
29
+ * the right error — and the failure mode is silent: the fault falls into the "unexpected error"
30
+ * branch and gets reported as an unindexed repo, which is the exact bug this file exists to
31
+ * prevent.
32
+ */
33
+ export declare function isIndexStorageError(err: unknown): err is IndexStorageError;
34
+ /** The operational code for `err`, or null when it is not an operational failure. */
35
+ export declare function classifyStorageError(err: unknown): string | null;
36
+ /** Rethrow operational faults as IndexStorageError; leave everything else untouched. */
37
+ export declare function rethrowOperational(err: unknown, path: string): never;
38
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/storage/sqlite/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;GAQG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IAGxC,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM;IAHvB,YACE,OAAO,EAAE,MAAM,EACN,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,EAI9B;CACF;AA2DD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,iBAAiB,CAO1E;AAED,qFAAqF;AACrF,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAoChE;AAED,wFAAwF;AACxF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,KAAK,CAWpE"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Operational failures of the index store, as distinct from "this repo has no index".
3
+ *
4
+ * Pure classification: no database handle, no filesystem, no dependency on any other module here.
5
+ * That is deliberate — every other module in this backend rethrows through `rethrowOperational`,
6
+ * so anything this file imported would become a dependency of the whole backend.
7
+ */
8
+ /**
9
+ * A storage fault, as distinct from "this repo has no index".
10
+ *
11
+ * Both used to arrive at callers as `null`, so a locked or corrupt database was indistinguishable
12
+ * from an unindexed repo: tools reported "not indexed" (prompting a pointless full reindex over a
13
+ * database that was merely busy) or returned empty results that read as an authoritative "no
14
+ * matches". Empty-because-broken is the worst answer an index can give, because nothing about it
15
+ * looks wrong.
16
+ */
17
+ export class IndexStorageError extends Error {
18
+ code;
19
+ path;
20
+ constructor(message, code, path, options) {
21
+ super(message, options);
22
+ this.code = code;
23
+ this.path = path;
24
+ this.name = "IndexStorageError";
25
+ }
26
+ }
27
+ /**
28
+ * Codes that mean "the store is there but unusable right now", never "there is nothing here".
29
+ *
30
+ * Deliberately a tight allowlist. Classifying too broadly would convert ordinary absence into a
31
+ * thrown error on a hot path used by ~every tool, which is a worse failure than the one being
32
+ * fixed — so anything unrecognised keeps the previous null-ish behaviour.
33
+ */
34
+ const OPERATIONAL_CODES = new Set([
35
+ "SQLITE_BUSY",
36
+ "SQLITE_LOCKED",
37
+ "SQLITE_CORRUPT",
38
+ "SQLITE_NOTADB",
39
+ "SQLITE_CANTOPEN",
40
+ "SQLITE_IOERR",
41
+ "SQLITE_READONLY",
42
+ "SQLITE_PERM",
43
+ "SQLITE_FULL",
44
+ "EACCES",
45
+ "EPERM",
46
+ "EIO",
47
+ "EBUSY",
48
+ "ENOSPC",
49
+ "EMFILE",
50
+ ]);
51
+ /**
52
+ * SQLite primary result codes, by number — the path that actually fires for `node:sqlite`.
53
+ *
54
+ * The driver sets `code = "ERR_SQLITE_ERROR"` on EVERY sqlite fault and puts the real result code
55
+ * in `errcode` (numeric) with its text in `errstr`. So the string allowlist below never matched a
56
+ * sqlite fault at all; only the four message regexes did, and only by accident of wording.
57
+ * Measured on node v24.18.0:
58
+ *
59
+ * not-a-database code=ERR_SQLITE_ERROR errcode=26 errstr=file is not a database
60
+ * readonly write code=ERR_SQLITE_ERROR errcode=8 errstr=attempt to write a readonly database
61
+ * disk full code=ERR_SQLITE_ERROR errcode=13 errstr=database or disk is full
62
+ *
63
+ * `SQLITE_FULL`, `SQLITE_READONLY` and `SQLITE_PERM` are listed in the allowlist as must-classify
64
+ * and returned `null` for the entire life of that allowlist: a full disk reported as "this repo
65
+ * has no index". Numbers are the only form the driver actually gives, so they are what we read.
66
+ *
67
+ * Masked with `& 0xff` because SQLite's extended codes are `primary | (sub << 8)` — 266 is
68
+ * SQLITE_IOERR_READ, and 266 & 0xff is 10, SQLITE_IOERR. Reporting the primary code keeps the
69
+ * vocabulary the allowlist speaks; the detail survives in the message.
70
+ */
71
+ const SQLITE_RESULT_CODES = new Map([
72
+ [3, "SQLITE_PERM"],
73
+ [5, "SQLITE_BUSY"],
74
+ [6, "SQLITE_LOCKED"],
75
+ [8, "SQLITE_READONLY"],
76
+ [10, "SQLITE_IOERR"],
77
+ [11, "SQLITE_CORRUPT"],
78
+ [13, "SQLITE_FULL"],
79
+ [14, "SQLITE_CANTOPEN"],
80
+ [26, "SQLITE_NOTADB"],
81
+ ]);
82
+ /**
83
+ * Structural check, not `instanceof`.
84
+ *
85
+ * A duplicated module instance (bundler, worker/thread boundary, a test importing through a
86
+ * different specifier) makes `instanceof` false for an object that is in every observable way
87
+ * the right error — and the failure mode is silent: the fault falls into the "unexpected error"
88
+ * branch and gets reported as an unindexed repo, which is the exact bug this file exists to
89
+ * prevent.
90
+ */
91
+ export function isIndexStorageError(err) {
92
+ return (typeof err === "object" &&
93
+ err !== null &&
94
+ err.name === "IndexStorageError" &&
95
+ typeof err.code === "string");
96
+ }
97
+ /** The operational code for `err`, or null when it is not an operational failure. */
98
+ export function classifyStorageError(err) {
99
+ // Structural, not `instanceof`, for the reason spelled out on `isIndexStorageError`: a duplicated
100
+ // module instance makes `instanceof` false for an object that is in every observable way the
101
+ // right error, and the fault then falls through to "unexpected" — reported as an unindexed repo.
102
+ if (isIndexStorageError(err))
103
+ return err.code;
104
+ if (typeof err !== "object" || err === null)
105
+ return null;
106
+ // `errcode` first: it is the only field `node:sqlite` puts the real result code in.
107
+ const errcode = err.errcode;
108
+ if (typeof errcode === "number") {
109
+ const primary = SQLITE_RESULT_CODES.get(errcode & 0xff);
110
+ if (primary !== undefined)
111
+ return primary;
112
+ }
113
+ const code = err.code;
114
+ if (typeof code === "string") {
115
+ if (OPERATIONAL_CODES.has(code))
116
+ return code;
117
+ // Kept for error sources that DO name codes as strings — filesystem faults from `mkdir`, and
118
+ // other sqlite bindings (better-sqlite3 sets `code: "SQLITE_IOERR_READ"`). `node:sqlite` is
119
+ // not one of them; see SQLITE_RESULT_CODES.
120
+ for (const known of OPERATIONAL_CODES) {
121
+ if (known.startsWith("SQLITE_") && code.startsWith(`${known}_`))
122
+ return code;
123
+ }
124
+ }
125
+ // Last resort, for a build or binding that carries the reason only in prose.
126
+ const message = err.message;
127
+ if (typeof message === "string") {
128
+ if (/database disk image is malformed|file is not a database/i.test(message)) {
129
+ return "SQLITE_CORRUPT";
130
+ }
131
+ if (/database is locked|database table is locked/i.test(message))
132
+ return "SQLITE_BUSY";
133
+ if (/unable to open database/i.test(message))
134
+ return "SQLITE_CANTOPEN";
135
+ if (/disk I\/O error/i.test(message))
136
+ return "SQLITE_IOERR";
137
+ }
138
+ return null;
139
+ }
140
+ /** Rethrow operational faults as IndexStorageError; leave everything else untouched. */
141
+ export function rethrowOperational(err, path) {
142
+ const code = classifyStorageError(err);
143
+ if (code === null)
144
+ throw err;
145
+ if (isIndexStorageError(err))
146
+ throw err; // already classified — do not re-wrap
147
+ const detail = err instanceof Error ? err.message : String(err);
148
+ throw new IndexStorageError(`index storage at ${path} is unreadable (${code}): ${detail}`, code, path, { cause: err });
149
+ }
150
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/storage/sqlite/errors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import type { CodeIndex } from "../../types.js";
2
+ /**
3
+ * Whole-index read and write: the paths that touch every row.
4
+ *
5
+ * These are the operations the narrow accessors exist to avoid, kept together because they share
6
+ * the same two invariants — one transaction per logical read, and `repo` written last so a
7
+ * half-written index reads as empty rather than as a complete one.
8
+ */
9
+ /**
10
+ * Replace the entire index. Used by full (re)indexing and by JSON->SQLite migration.
11
+ *
12
+ * `sourceComplete` means every symbol in `index` came from parsing the file it belongs to during
13
+ * THIS run — the only condition under which the `lossy_v1_migration` marker can honestly be
14
+ * cleared. It defaults to false, and the default is the point: rewriting every row is NOT the
15
+ * same as re-reading every file. `index_folder` reuses cached symbols for files whose mtime has
16
+ * not moved, and `enqueueIndexMutation` folds a single-file edit into a whole-index rewrite that
17
+ * arrives here indistinguishable from a real reindex. An earlier version cleared the marker on
18
+ * any call and so erased the warning on the next keystroke after an upgrade.
19
+ *
20
+ * Defaulting to "still lossy" errs toward over-warning, which a reindex clears, rather than
21
+ * toward silence, which nothing does.
22
+ */
23
+ export declare function saveIndexSqlite(dbPath: string, index: CodeIndex, opts?: {
24
+ sourceComplete?: boolean;
25
+ }): Promise<void>;
26
+ /**
27
+ * True when this database was upgraded from the v1 schema, whose PRIMARY KEY on a non-unique
28
+ * `symbols.id` silently discarded colliding rows. The rebuild preserved everything still stored,
29
+ * but cannot recover what was already gone — so a short symbol list here is a fact about the
30
+ * migration, not about the code. Cleared by a full reindex.
31
+ */
32
+ export declare function wasLossilyMigrated(dbPath: string): Promise<boolean>;
33
+ /**
34
+ * Import a legacy JSON index, but only if this db is still empty — decided and written
35
+ * inside ONE `BEGIN IMMEDIATE` transaction.
36
+ *
37
+ * The check-then-act cannot be done in JS: `codesift postindex-file` is a fresh process per
38
+ * edit, so two of them (or one plus the server) can both observe an empty db, both read the
39
+ * same legacy JSON, and the slower writer then overwrites an incremental update the faster
40
+ * one already committed. An in-process guard cannot see the other process at all.
41
+ *
42
+ * `BEGIN IMMEDIATE` takes the write lock before the emptiness check, so the second writer
43
+ * either waits and then sees the committed import (returns false), or fails on busy_timeout —
44
+ * never silently clobbers. Returns true when this call performed the import.
45
+ */
46
+ export declare function importLegacyIndexIfEmpty(dbPath: string, index: CodeIndex): Promise<boolean>;
47
+ /** Materialise the whole index. Returns null when the db has never been written to. */
48
+ export declare function loadIndexSqlite(dbPath: string): Promise<CodeIndex | null>;
49
+ //# sourceMappingURL=index-io.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-io.d.ts","sourceRoot":"","sources":["../../../src/storage/sqlite/index-io.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAyB,MAAM,gBAAgB,CAAC;AAyBvE;;;;;;GAMG;AAEH;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,SAAS,EAChB,IAAI,CAAC,EAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAE,GAClC,OAAO,CAAC,IAAI,CAAC,CAiBf;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAGzE;AA4BD;;;;;;;;;;;;GAYG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,SAAS,GACf,OAAO,CAAC,OAAO,CAAC,CAgBlB;AAgFD,uFAAuF;AACvF,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAkF/E"}