mrplex 0.0.1

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 (319) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +284 -0
  3. package/dist/cli/config.d.ts +28 -0
  4. package/dist/cli/config.js +48 -0
  5. package/dist/cli/config.js.map +1 -0
  6. package/dist/cli/exit-codes.d.ts +13 -0
  7. package/dist/cli/exit-codes.js +39 -0
  8. package/dist/cli/exit-codes.js.map +1 -0
  9. package/dist/cli/format.d.ts +22 -0
  10. package/dist/cli/format.js +148 -0
  11. package/dist/cli/format.js.map +1 -0
  12. package/dist/cli/main.d.ts +16 -0
  13. package/dist/cli/main.js +1518 -0
  14. package/dist/cli/main.js.map +1 -0
  15. package/dist/client/kernel-client.d.ts +97 -0
  16. package/dist/client/kernel-client.js +14 -0
  17. package/dist/client/kernel-client.js.map +1 -0
  18. package/dist/client/local.d.ts +26 -0
  19. package/dist/client/local.js +103 -0
  20. package/dist/client/local.js.map +1 -0
  21. package/dist/client/remote-mcp.d.ts +35 -0
  22. package/dist/client/remote-mcp.js +174 -0
  23. package/dist/client/remote-mcp.js.map +1 -0
  24. package/dist/embed/backfill.d.ts +24 -0
  25. package/dist/embed/backfill.js +33 -0
  26. package/dist/embed/backfill.js.map +1 -0
  27. package/dist/embed/chunker.d.ts +31 -0
  28. package/dist/embed/chunker.js +121 -0
  29. package/dist/embed/chunker.js.map +1 -0
  30. package/dist/embed/cmd-hook.d.ts +21 -0
  31. package/dist/embed/cmd-hook.js +100 -0
  32. package/dist/embed/cmd-hook.js.map +1 -0
  33. package/dist/embed/config.d.ts +61 -0
  34. package/dist/embed/config.js +109 -0
  35. package/dist/embed/config.js.map +1 -0
  36. package/dist/embed/hook.d.ts +38 -0
  37. package/dist/embed/hook.js +56 -0
  38. package/dist/embed/hook.js.map +1 -0
  39. package/dist/embed/http-hook.d.ts +13 -0
  40. package/dist/embed/http-hook.js +39 -0
  41. package/dist/embed/http-hook.js.map +1 -0
  42. package/dist/embed/worker.d.ts +62 -0
  43. package/dist/embed/worker.js +228 -0
  44. package/dist/embed/worker.js.map +1 -0
  45. package/dist/fixtures/hwe/characters/albion-ever.md +12 -0
  46. package/dist/fixtures/hwe/characters/cent.md +11 -0
  47. package/dist/fixtures/hwe/concepts/cent-demiurge-resonance.md +10 -0
  48. package/dist/fixtures/hwe/concepts/phase-looper.md +10 -0
  49. package/dist/fixtures/hwe/entities/demiurge.md +11 -0
  50. package/dist/fixtures/hwe/entities/monad.md +11 -0
  51. package/dist/fixtures/hwe/moc/globocorp-network.md +19 -0
  52. package/dist/fixtures/hwe/organizations/globocorp.md +11 -0
  53. package/dist/fixtures/hwe/readme.md +24 -0
  54. package/dist/fixtures/starship/crew/aria-okonkwo.md +14 -0
  55. package/dist/fixtures/starship/crew/bexley-orr.md +14 -0
  56. package/dist/fixtures/starship/crew/dax-thorne.md +14 -0
  57. package/dist/fixtures/starship/crew/isolde-marsh.md +16 -0
  58. package/dist/fixtures/starship/crew/kestrel-vance.md +15 -0
  59. package/dist/fixtures/starship/crew/quill-vasquez.md +14 -0
  60. package/dist/fixtures/starship/crew/soren-halloway.md +16 -0
  61. package/dist/fixtures/starship/encounters/the-drift-choir.md +14 -0
  62. package/dist/fixtures/starship/encounters/the-hollow-signal.md +16 -0
  63. package/dist/fixtures/starship/encounters/the-wandering-buoy.md +14 -0
  64. package/dist/fixtures/starship/equipment/coolant-loop-b.md +13 -0
  65. package/dist/fixtures/starship/equipment/emergency-ration-bars.md +12 -0
  66. package/dist/fixtures/starship/equipment/long-range-sensors.md +13 -0
  67. package/dist/fixtures/starship/equipment/plasma-manifold-3.md +15 -0
  68. package/dist/fixtures/starship/equipment/shuttle-corvid.md +13 -0
  69. package/dist/fixtures/starship/logs/marsh-4419-1.md +13 -0
  70. package/dist/fixtures/starship/logs/okonkwo-4415-1.md +13 -0
  71. package/dist/fixtures/starship/logs/orr-4413-1.md +13 -0
  72. package/dist/fixtures/starship/logs/orr-4420-1.md +12 -0
  73. package/dist/fixtures/starship/logs/thorne-4413-2.md +13 -0
  74. package/dist/fixtures/starship/logs/vance-4419-2.md +13 -0
  75. package/dist/fixtures/starship/misc/the-galley-ficus.md +11 -0
  76. package/dist/fixtures/starship/missions/the-cinder-run.md +17 -0
  77. package/dist/fixtures/starship/missions/the-drift-choir.md +17 -0
  78. package/dist/fixtures/starship/missions/the-hollow-signal.md +17 -0
  79. package/dist/fixtures/starship/missions/the-silent-beacon.md +17 -0
  80. package/dist/fixtures/starship/moc/crew.md +19 -0
  81. package/dist/fixtures/starship/moc/encounters.md +14 -0
  82. package/dist/fixtures/starship/moc/missions.md +15 -0
  83. package/dist/fixtures/starship/readme.md +60 -0
  84. package/dist/index.d.ts +1 -0
  85. package/dist/index.js +3 -0
  86. package/dist/index.js.map +1 -0
  87. package/dist/kernel/auth/glob.d.ts +46 -0
  88. package/dist/kernel/auth/glob.js +112 -0
  89. package/dist/kernel/auth/glob.js.map +1 -0
  90. package/dist/kernel/auth/scope.d.ts +53 -0
  91. package/dist/kernel/auth/scope.js +83 -0
  92. package/dist/kernel/auth/scope.js.map +1 -0
  93. package/dist/kernel/casefold.d.ts +37 -0
  94. package/dist/kernel/casefold.js +44 -0
  95. package/dist/kernel/casefold.js.map +1 -0
  96. package/dist/kernel/constants.d.ts +21 -0
  97. package/dist/kernel/constants.js +22 -0
  98. package/dist/kernel/constants.js.map +1 -0
  99. package/dist/kernel/context.d.ts +70 -0
  100. package/dist/kernel/context.js +114 -0
  101. package/dist/kernel/context.js.map +1 -0
  102. package/dist/kernel/deletion.d.ts +50 -0
  103. package/dist/kernel/deletion.js +83 -0
  104. package/dist/kernel/deletion.js.map +1 -0
  105. package/dist/kernel/diff.d.ts +36 -0
  106. package/dist/kernel/diff.js +61 -0
  107. package/dist/kernel/diff.js.map +1 -0
  108. package/dist/kernel/errors.d.ts +37 -0
  109. package/dist/kernel/errors.js +55 -0
  110. package/dist/kernel/errors.js.map +1 -0
  111. package/dist/kernel/frontmatter-input.d.ts +32 -0
  112. package/dist/kernel/frontmatter-input.js +85 -0
  113. package/dist/kernel/frontmatter-input.js.map +1 -0
  114. package/dist/kernel/graph.d.ts +34 -0
  115. package/dist/kernel/graph.js +559 -0
  116. package/dist/kernel/graph.js.map +1 -0
  117. package/dist/kernel/history.d.ts +74 -0
  118. package/dist/kernel/history.js +143 -0
  119. package/dist/kernel/history.js.map +1 -0
  120. package/dist/kernel/kernel.d.ts +162 -0
  121. package/dist/kernel/kernel.js +675 -0
  122. package/dist/kernel/kernel.js.map +1 -0
  123. package/dist/kernel/path-config.d.ts +101 -0
  124. package/dist/kernel/path-config.js +171 -0
  125. package/dist/kernel/path-config.js.map +1 -0
  126. package/dist/kernel/query/ast.d.ts +24 -0
  127. package/dist/kernel/query/ast.js +52 -0
  128. package/dist/kernel/query/ast.js.map +1 -0
  129. package/dist/kernel/query/cel-parse.d.ts +37 -0
  130. package/dist/kernel/query/cel-parse.js +88 -0
  131. package/dist/kernel/query/cel-parse.js.map +1 -0
  132. package/dist/kernel/query/degrees.d.ts +31 -0
  133. package/dist/kernel/query/degrees.js +135 -0
  134. package/dist/kernel/query/degrees.js.map +1 -0
  135. package/dist/kernel/query/graph-ast.d.ts +51 -0
  136. package/dist/kernel/query/graph-ast.js +139 -0
  137. package/dist/kernel/query/graph-ast.js.map +1 -0
  138. package/dist/kernel/query/query.d.ts +56 -0
  139. package/dist/kernel/query/query.js +276 -0
  140. package/dist/kernel/query/query.js.map +1 -0
  141. package/dist/kernel/validation.d.ts +64 -0
  142. package/dist/kernel/validation.js +183 -0
  143. package/dist/kernel/validation.js.map +1 -0
  144. package/dist/kernel/version-id.d.ts +10 -0
  145. package/dist/kernel/version-id.js +21 -0
  146. package/dist/kernel/version-id.js.map +1 -0
  147. package/dist/kernel/wire.d.ts +148 -0
  148. package/dist/kernel/wire.js +6 -0
  149. package/dist/kernel/wire.js.map +1 -0
  150. package/dist/links/backfill.d.ts +28 -0
  151. package/dist/links/backfill.js +33 -0
  152. package/dist/links/backfill.js.map +1 -0
  153. package/dist/links/extract.d.ts +29 -0
  154. package/dist/links/extract.js +233 -0
  155. package/dist/links/extract.js.map +1 -0
  156. package/dist/links/link-config.d.ts +70 -0
  157. package/dist/links/link-config.js +107 -0
  158. package/dist/links/link-config.js.map +1 -0
  159. package/dist/links/maintain.d.ts +37 -0
  160. package/dist/links/maintain.js +91 -0
  161. package/dist/links/maintain.js.map +1 -0
  162. package/dist/links/repair.d.ts +46 -0
  163. package/dist/links/repair.js +96 -0
  164. package/dist/links/repair.js.map +1 -0
  165. package/dist/links/resolve.d.ts +41 -0
  166. package/dist/links/resolve.js +121 -0
  167. package/dist/links/resolve.js.map +1 -0
  168. package/dist/links/stale.d.ts +32 -0
  169. package/dist/links/stale.js +73 -0
  170. package/dist/links/stale.js.map +1 -0
  171. package/dist/markdown/content-hash.d.ts +34 -0
  172. package/dist/markdown/content-hash.js +46 -0
  173. package/dist/markdown/content-hash.js.map +1 -0
  174. package/dist/markdown/frontmatter.d.ts +69 -0
  175. package/dist/markdown/frontmatter.js +147 -0
  176. package/dist/markdown/frontmatter.js.map +1 -0
  177. package/dist/markdown/hash-backfill.d.ts +22 -0
  178. package/dist/markdown/hash-backfill.js +43 -0
  179. package/dist/markdown/hash-backfill.js.map +1 -0
  180. package/dist/mcp/query-syntax.d.ts +20 -0
  181. package/dist/mcp/query-syntax.js +194 -0
  182. package/dist/mcp/query-syntax.js.map +1 -0
  183. package/dist/mcp/render.d.ts +31 -0
  184. package/dist/mcp/render.js +110 -0
  185. package/dist/mcp/render.js.map +1 -0
  186. package/dist/mcp/server.d.ts +60 -0
  187. package/dist/mcp/server.js +170 -0
  188. package/dist/mcp/server.js.map +1 -0
  189. package/dist/mcp/tools.d.ts +93 -0
  190. package/dist/mcp/tools.js +1218 -0
  191. package/dist/mcp/tools.js.map +1 -0
  192. package/dist/rest/conditional.d.ts +37 -0
  193. package/dist/rest/conditional.js +58 -0
  194. package/dist/rest/conditional.js.map +1 -0
  195. package/dist/rest/negotiate.d.ts +33 -0
  196. package/dist/rest/negotiate.js +54 -0
  197. package/dist/rest/negotiate.js.map +1 -0
  198. package/dist/rest/routes.d.ts +37 -0
  199. package/dist/rest/routes.js +744 -0
  200. package/dist/rest/routes.js.map +1 -0
  201. package/dist/seed/fixture-seed.d.ts +35 -0
  202. package/dist/seed/fixture-seed.js +75 -0
  203. package/dist/seed/fixture-seed.js.map +1 -0
  204. package/dist/seed/starship-config.d.ts +3 -0
  205. package/dist/seed/starship-config.js +5 -0
  206. package/dist/seed/starship-config.js.map +1 -0
  207. package/dist/server/headers.d.ts +36 -0
  208. package/dist/server/headers.js +30 -0
  209. package/dist/server/headers.js.map +1 -0
  210. package/dist/server/http-error.d.ts +53 -0
  211. package/dist/server/http-error.js +104 -0
  212. package/dist/server/http-error.js.map +1 -0
  213. package/dist/server/serve.d.ts +56 -0
  214. package/dist/server/serve.js +135 -0
  215. package/dist/server/serve.js.map +1 -0
  216. package/dist/shell/audit.d.ts +32 -0
  217. package/dist/shell/audit.js +28 -0
  218. package/dist/shell/audit.js.map +1 -0
  219. package/dist/shell/guard.d.ts +50 -0
  220. package/dist/shell/guard.js +176 -0
  221. package/dist/shell/guard.js.map +1 -0
  222. package/dist/shell/keys.d.ts +36 -0
  223. package/dist/shell/keys.js +64 -0
  224. package/dist/shell/keys.js.map +1 -0
  225. package/dist/shell/login.d.ts +56 -0
  226. package/dist/shell/login.js +130 -0
  227. package/dist/shell/login.js.map +1 -0
  228. package/dist/shell/oidc.d.ts +62 -0
  229. package/dist/shell/oidc.js +102 -0
  230. package/dist/shell/oidc.js.map +1 -0
  231. package/dist/shell/policy.d.ts +100 -0
  232. package/dist/shell/policy.js +232 -0
  233. package/dist/shell/policy.js.map +1 -0
  234. package/dist/shell/proxy-policy.d.ts +34 -0
  235. package/dist/shell/proxy-policy.js +120 -0
  236. package/dist/shell/proxy-policy.js.map +1 -0
  237. package/dist/shell/proxy.d.ts +47 -0
  238. package/dist/shell/proxy.js +253 -0
  239. package/dist/shell/proxy.js.map +1 -0
  240. package/dist/shell/serve.d.ts +86 -0
  241. package/dist/shell/serve.js +0 -0
  242. package/dist/shell/serve.js.map +1 -0
  243. package/dist/shell/stdio.d.ts +55 -0
  244. package/dist/shell/stdio.js +63 -0
  245. package/dist/shell/stdio.js.map +1 -0
  246. package/dist/storage/registry.d.ts +20 -0
  247. package/dist/storage/registry.js +38 -0
  248. package/dist/storage/registry.js.map +1 -0
  249. package/dist/storage/search-plan.d.ts +93 -0
  250. package/dist/storage/search-plan.js +15 -0
  251. package/dist/storage/search-plan.js.map +1 -0
  252. package/dist/storage/types.d.ts +448 -0
  253. package/dist/storage/types.js +14 -0
  254. package/dist/storage/types.js.map +1 -0
  255. package/dist/storage/versions-since.d.ts +72 -0
  256. package/dist/storage/versions-since.js +92 -0
  257. package/dist/storage/versions-since.js.map +1 -0
  258. package/dist/storage-postgres/adapter.d.ts +17 -0
  259. package/dist/storage-postgres/adapter.js +782 -0
  260. package/dist/storage-postgres/adapter.js.map +1 -0
  261. package/dist/storage-postgres/compile-postgres.d.ts +22 -0
  262. package/dist/storage-postgres/compile-postgres.js +830 -0
  263. package/dist/storage-postgres/compile-postgres.js.map +1 -0
  264. package/dist/storage-postgres/errors.d.ts +34 -0
  265. package/dist/storage-postgres/errors.js +50 -0
  266. package/dist/storage-postgres/errors.js.map +1 -0
  267. package/dist/storage-postgres/migrations/0001_init.sql +113 -0
  268. package/dist/storage-postgres/migrations/0002_content_hash.sql +7 -0
  269. package/dist/storage-postgres/migrations/index.d.ts +22 -0
  270. package/dist/storage-postgres/migrations/index.js +70 -0
  271. package/dist/storage-postgres/migrations/index.js.map +1 -0
  272. package/dist/storage-sqlite/adapter.d.ts +2 -0
  273. package/dist/storage-sqlite/adapter.js +688 -0
  274. package/dist/storage-sqlite/adapter.js.map +1 -0
  275. package/dist/storage-sqlite/compile-filter.d.ts +41 -0
  276. package/dist/storage-sqlite/compile-filter.js +881 -0
  277. package/dist/storage-sqlite/compile-filter.js.map +1 -0
  278. package/dist/storage-sqlite/compile-sqlite.d.ts +19 -0
  279. package/dist/storage-sqlite/compile-sqlite.js +176 -0
  280. package/dist/storage-sqlite/compile-sqlite.js.map +1 -0
  281. package/dist/storage-sqlite/migrations/0001_init.sql +123 -0
  282. package/dist/storage-sqlite/migrations/0002_content_hash.sql +7 -0
  283. package/dist/storage-sqlite/migrations/index.d.ts +6 -0
  284. package/dist/storage-sqlite/migrations/index.js +41 -0
  285. package/dist/storage-sqlite/migrations/index.js.map +1 -0
  286. package/dist/storage-sqlite/vec.d.ts +40 -0
  287. package/dist/storage-sqlite/vec.js +62 -0
  288. package/dist/storage-sqlite/vec.js.map +1 -0
  289. package/dist/sync/cursor.d.ts +36 -0
  290. package/dist/sync/cursor.js +66 -0
  291. package/dist/sync/cursor.js.map +1 -0
  292. package/dist/sync/daemon.d.ts +40 -0
  293. package/dist/sync/daemon.js +202 -0
  294. package/dist/sync/daemon.js.map +1 -0
  295. package/dist/sync/feed.d.ts +38 -0
  296. package/dist/sync/feed.js +125 -0
  297. package/dist/sync/feed.js.map +1 -0
  298. package/dist/sync/fs-store.d.ts +8 -0
  299. package/dist/sync/fs-store.js +76 -0
  300. package/dist/sync/fs-store.js.map +1 -0
  301. package/dist/sync/intrinsics.d.ts +52 -0
  302. package/dist/sync/intrinsics.js +76 -0
  303. package/dist/sync/intrinsics.js.map +1 -0
  304. package/dist/sync/paths.d.ts +38 -0
  305. package/dist/sync/paths.js +56 -0
  306. package/dist/sync/paths.js.map +1 -0
  307. package/dist/sync/push.d.ts +48 -0
  308. package/dist/sync/push.js +328 -0
  309. package/dist/sync/push.js.map +1 -0
  310. package/dist/sync/reconcile.d.ts +54 -0
  311. package/dist/sync/reconcile.js +252 -0
  312. package/dist/sync/reconcile.js.map +1 -0
  313. package/dist/sync/run.d.ts +27 -0
  314. package/dist/sync/run.js +46 -0
  315. package/dist/sync/run.js.map +1 -0
  316. package/dist/version.d.ts +2 -0
  317. package/dist/version.js +5 -0
  318. package/dist/version.js.map +1 -0
  319. package/package.json +80 -0
@@ -0,0 +1,448 @@
1
+ /**
2
+ * Storage layer types — adapter-agnostic. Kernel operations run on top of these.
3
+ *
4
+ * Integer ids are internal (design §3.3) and never cross the wire — the kernel
5
+ * translates rows into Version envelopes with opaque `version_id` strings and
6
+ * slug-based references.
7
+ *
8
+ * All Storage methods are async. SQLite adapter methods complete
9
+ * synchronously and resolve on the next microtask; Postgres adapter
10
+ * methods await real I/O. The uniform signature keeps kernel code
11
+ * dialect-agnostic (design §7.2, m5-plan WS1).
12
+ */
13
+ import type { SearchPlan } from "./search-plan.js";
14
+ export type RepoRow = {
15
+ id: number;
16
+ slug: string;
17
+ path_config: string | null;
18
+ /** Per-repo link-extraction override JSON (§11.2). Null = inherit. */
19
+ link_config: string | null;
20
+ created_at: string;
21
+ };
22
+ export type DocumentRow = {
23
+ id: number;
24
+ repo_id: number;
25
+ };
26
+ export type FrontmatterJson = Record<string, unknown>;
27
+ export type VersionRow = {
28
+ id: number;
29
+ document_id: number;
30
+ repo_id: number;
31
+ prev_id: number | null;
32
+ next_id: number | null;
33
+ path: string;
34
+ frontmatter_raw: string;
35
+ frontmatter: FrontmatterJson;
36
+ body: string;
37
+ author: string;
38
+ created_at: string;
39
+ /**
40
+ * SHA-256 (bare hex) of canonical content (sync/history plan §2). Derived and
41
+ * server-owned — computed in-tx by `version_insert`, never part of the insert
42
+ * input. Null only on pre-backfill rows written before migration 0002.
43
+ */
44
+ content_hash: string | null;
45
+ };
46
+ export type VersionInsertInput = {
47
+ document_id: number;
48
+ repo_id: number;
49
+ prev_id: number | null;
50
+ path: string;
51
+ frontmatter_raw: string;
52
+ frontmatter: FrontmatterJson;
53
+ body: string;
54
+ author: string;
55
+ created_at: string;
56
+ };
57
+ export type HistoryOptions = {
58
+ limit?: number;
59
+ before?: string;
60
+ };
61
+ /**
62
+ * Options for the scoped history walk (sync/history plan §3.5). A forward
63
+ * id-ordered walk over the versions of documents selected by a path glob,
64
+ * bounded by version-id cursors. `path_regexes` are anchored regex sources
65
+ * (the gitignore-glob compilation used everywhere); empty = every document in
66
+ * the repo. `ever: false` anchors on the *live* set (docs whose CURRENT path
67
+ * matches); `ever: true` includes any document that *ever* had a matching path
68
+ * (whole chains). `after_id`/`until_id` are version-id bounds (exclusive lower,
69
+ * inclusive upper). `order` picks id ascending (oldest-first) or descending.
70
+ */
71
+ export type VersionsListOptions = {
72
+ repo_id: number;
73
+ path_regexes: readonly string[];
74
+ ever: boolean;
75
+ after_id?: number;
76
+ until_id?: number;
77
+ order: "asc" | "desc";
78
+ limit: number;
79
+ };
80
+ /**
81
+ * Options for the gap-aware forward feed walk (sync/history plan §3.2–3.3).
82
+ * `after_id` is the resume cursor (0 = from the beginning); `repo_id` filters
83
+ * the output without affecting gap detection (gaps are global). `now_ms` and
84
+ * `window_ms` drive the safety window — the adapter supplies wall-clock now;
85
+ * `window_ms` is the caller's tolerance for treating a gap's successor as
86
+ * "settled long enough that the hole is burned, not pending."
87
+ */
88
+ export type VersionsSinceOptions = {
89
+ after_id: number;
90
+ repo_id?: number;
91
+ limit: number;
92
+ now_ms: number;
93
+ window_ms: number;
94
+ };
95
+ /**
96
+ * Result of `versions_since`: the settled rows in `(after_id, next_id]`
97
+ * (repo-filtered, ascending by id) and the resume cursor `next_id`. Every id
98
+ * ≤ `next_id` is final and gap-free; `next_id === after_id` means nothing is
99
+ * safe to deliver yet (caught up, or stalled at a hot gap — the client just
100
+ * polls again).
101
+ */
102
+ export type VersionsSinceResult = {
103
+ rows: VersionRow[];
104
+ next_id: number;
105
+ };
106
+ /**
107
+ * Chunks (design §3.2, §5.3). One row per chunk of a version's body.
108
+ * `embedding` is null while the row is pending embedding (rare in
109
+ * practice — chunk rows are only inserted alongside their vectors).
110
+ * Content-hash dedup key is (model, text_hash). Embeddings are
111
+ * exchanged with the storage layer as Float32Array — the adapter owns
112
+ * the on-disk representation (byte layout is private to each engine).
113
+ */
114
+ export type ChunkRow = {
115
+ version_id: number;
116
+ ix: number;
117
+ text: string;
118
+ text_hash: string;
119
+ model: string;
120
+ embedding: Float32Array | null;
121
+ };
122
+ export type ChunkUpsertInput = {
123
+ ix: number;
124
+ text: string;
125
+ text_hash: string;
126
+ model: string;
127
+ /**
128
+ * Query vector. Callers passing fresh vectors use `readonly number[]`
129
+ * (that's what the embed hook returns); callers reusing a dedup-hit
130
+ * vector pass the Float32Array they got back from chunks_by_hash so
131
+ * the adapter round-trips it without re-encoding.
132
+ */
133
+ embedding: readonly number[] | Float32Array;
134
+ };
135
+ export type VectorSearchHit = {
136
+ version_id: number;
137
+ chunk_ix: number;
138
+ score: number;
139
+ };
140
+ /**
141
+ * Links derived index (design §11.2). One row per outbound STATIC edge
142
+ * from a source document's CURRENT version, doc-keyed. The kernel extracts
143
+ * + resolves edges and hands the adapter the resolved shape below;
144
+ * extraction/resolution logic (markdown parsing, path normalization) lives
145
+ * in src/links, never in the storage layer.
146
+ */
147
+ /**
148
+ * A resolved outbound edge, ready to persist. `target_id` is the bound
149
+ * document identity or null when dangling (the named path has no live
150
+ * document yet). `target_norm` is the folded, anchor-stripped resolution
151
+ * key used to rebind danglers case-insensitively (§3.5.1).
152
+ */
153
+ export type LinkEdgeInput = {
154
+ ord: number;
155
+ field: string;
156
+ target_raw: string;
157
+ target_norm: string;
158
+ target_id: number | null;
159
+ };
160
+ /** A stored link edge row (read shape — backfill, links.stale, tests). */
161
+ export type LinkRow = {
162
+ repo_id: number;
163
+ source_id: number;
164
+ ord: number;
165
+ field: string;
166
+ target_raw: string;
167
+ target_norm: string;
168
+ target_id: number | null;
169
+ };
170
+ /**
171
+ * A distinct resolved adjacency triple for the graph read surface
172
+ * (docs/graph-plan.md WS1). `ord`, `target_raw`, and `target_norm` never
173
+ * leave storage on this path: adjacency reads collapse multiple occurrences
174
+ * of the same `(source, target, field)` to one row (`SELECT DISTINCT`) and
175
+ * exclude dangling edges (`target_id IS NULL`). Both endpoints are document
176
+ * ids; the kernel maps them to paths and applies scope.
177
+ */
178
+ export type AdjacentLink = {
179
+ source_id: number;
180
+ target_id: number;
181
+ field: string;
182
+ };
183
+ /**
184
+ * Embedding backlog (design §3.2, §5.3). One row per version awaiting
185
+ * (or having failed) embedding. `attempts` counts failed tries; a fresh
186
+ * enqueue resets `attempts` and `next_retry_at` so a superseding write
187
+ * doesn't inherit an old backoff.
188
+ */
189
+ export type BacklogRow = {
190
+ version_id: number;
191
+ attempts: number;
192
+ last_error: string | null;
193
+ next_retry_at: string | null;
194
+ };
195
+ export type BacklogStatus = {
196
+ pending: number;
197
+ due: number;
198
+ failing: number;
199
+ oldest_next_retry_at: string | null;
200
+ recent_errors: readonly {
201
+ version_id: number;
202
+ last_error: string;
203
+ }[];
204
+ models: readonly {
205
+ model: string;
206
+ chunk_count: number;
207
+ }[];
208
+ };
209
+ export type Storage = {
210
+ close(): Promise<void>;
211
+ migrate(): Promise<void>;
212
+ /**
213
+ * Serializable transaction. Nested tx flattens into the outer via
214
+ * savepoints. Contract: never await foreign I/O inside `fn` — the
215
+ * tx holds locks and the adapter's REPEATABLE READ retry loop (PG)
216
+ * must be able to replay the whole body. Kernel tx bodies only call
217
+ * storage.
218
+ */
219
+ tx<T>(fn: () => Promise<T>): Promise<T>;
220
+ repos_list(): Promise<RepoRow[]>;
221
+ repos_create(input: {
222
+ slug: string;
223
+ created_at: string;
224
+ }): Promise<RepoRow>;
225
+ repos_rename(id: number, new_slug: string): Promise<RepoRow>;
226
+ repos_set_path_config(id: number, path_config: string | null): Promise<RepoRow>;
227
+ repos_set_link_config(id: number, link_config: string | null): Promise<RepoRow>;
228
+ repos_by_slug(slug: string): Promise<RepoRow | null>;
229
+ repos_by_id(id: number): Promise<RepoRow | null>;
230
+ documents_create(repo_id: number): Promise<DocumentRow>;
231
+ /**
232
+ * Insert a new version and advance the chain atomically (design §7.2.2 #1):
233
+ * new row goes in; `prev.next_id` is updated to the new row's id; both happen
234
+ * in one tx. The two partial unique indexes on `versions` enforce the
235
+ * "one current per document" and "one live per (repo, path)" invariants
236
+ * at the storage layer (design §7.2.2 #2).
237
+ */
238
+ version_insert(input: VersionInsertInput): Promise<VersionRow>;
239
+ version_by_id(id: number): Promise<VersionRow | null>;
240
+ version_current(repo_id: number, path: string): Promise<VersionRow | null>;
241
+ /**
242
+ * Current-version rows for a batch of live paths in `repo_id`. Lookup is by
243
+ * `path_norm` (same folding as `version_current`); paths with no live row are
244
+ * simply absent.
245
+ */
246
+ versions_current_by_paths(repo_id: number, paths: readonly string[]): Promise<VersionRow[]>;
247
+ version_history(document_id: number, opts?: HistoryOptions): Promise<VersionRow[]>;
248
+ /**
249
+ * Scoped, document-spanning history walk (sync/history plan §3.5). Selects
250
+ * documents by path glob (via `path_regexes`), then returns their version
251
+ * rows interleaved by id, bounded by `after_id`/`until_id` cursors. See
252
+ * `VersionsListOptions` for the `ever` (live vs. whole-corpus) distinction.
253
+ */
254
+ versions_list(opts: VersionsListOptions): Promise<VersionRow[]>;
255
+ /**
256
+ * The gap-aware forward feed walk (sync/history plan §3.2–3.3): the longest
257
+ * safe contiguous run of version rows after `opts.after_id`, filtered to
258
+ * `opts.repo_id` when given. Returns settled rows only (never crosses a hot
259
+ * gap) plus the resume cursor `next_id`. See `versions-since.ts` for the
260
+ * safety-window logic both adapters share.
261
+ */
262
+ versions_since(opts: VersionsSinceOptions): Promise<VersionsSinceResult>;
263
+ /**
264
+ * Batch id → path lookup (sync/history plan §3.3): the feed derives each
265
+ * ref's `prev_path` (both ends of a move/delete) from its `prev_id` without
266
+ * hydrating whole prev rows. Ids not present are simply absent from the map.
267
+ */
268
+ versions_paths_by_ids(ids: readonly number[]): Promise<Map<number, string>>;
269
+ /**
270
+ * The safe head `R` (sync/history plan §3.4): the raw version id the feed
271
+ * would hand out as `next_since` at the live tip — everything ≤ R is visible
272
+ * and final. Computed with the same safety-window machinery as the feed, but
273
+ * anchored at the tip rather than a cursor, so `history.index` scans through
274
+ * a settled boundary while `history.since(R)` covers the rest with no gaps.
275
+ * Returns 0 when the log is empty.
276
+ */
277
+ versions_safe_head(now_ms: number, window_ms: number): Promise<number>;
278
+ /**
279
+ * One keyset page of the live set for `history.index` (§3.4): live rows
280
+ * (`next_id IS NULL`) in `repo_id` whose current version id is in
281
+ * `(after_id, through_id]`, ascending by id, capped at `limit`. Lightweight
282
+ * tuples only — the kernel applies system/hidden exclusion and scope.
283
+ */
284
+ versions_live_index(opts: {
285
+ repo_id: number;
286
+ through_id: number;
287
+ after_id: number;
288
+ limit: number;
289
+ }): Promise<{
290
+ id: number;
291
+ path: string;
292
+ content_hash: string | null;
293
+ }[]>;
294
+ /**
295
+ * Content-hash backfill (sync/history plan §2.6). Fetch one batch of rows
296
+ * with `content_hash IS NULL` (id-ascending, id > `after_id`, capped at
297
+ * `limit`), optionally scoped to `repo_id`. Returns the raw fields the shared
298
+ * hash function needs; the kernel computes hashes and writes them via
299
+ * `versions_set_content_hash`. Keyset by id so batches don't re-scan.
300
+ */
301
+ versions_missing_content_hash(opts: {
302
+ repo_id?: number;
303
+ after_id: number;
304
+ limit: number;
305
+ }): Promise<{
306
+ id: number;
307
+ frontmatter_raw: string;
308
+ body: string;
309
+ }[]>;
310
+ /** Set `content_hash` for a batch of version ids (backfill writer, §2.6). */
311
+ versions_set_content_hash(updates: readonly {
312
+ id: number;
313
+ content_hash: string;
314
+ }[]): Promise<void>;
315
+ /**
316
+ * All currently-live versions in a repo (i.e. rows where next_id IS NULL).
317
+ * Used by `repos.set_path_config` to produce the advisory PathWarning[]
318
+ * scan (§3.5.3). Riding the partial-index on (repo_id, path) where
319
+ * next_id is null, so O(live-set) per repo.
320
+ */
321
+ versions_live_by_repo(repo_id: number): Promise<VersionRow[]>;
322
+ fts_index(version_id: number, body: string): Promise<void>;
323
+ /**
324
+ * Replace document `source_id`'s outbound edges wholesale (delete +
325
+ * insert) in one shot. `repo_id` scopes the rows (links are repo-local).
326
+ * Called on every create/put that advances the doc's current version.
327
+ * Passing an empty `edges` clears the doc's outbound rows.
328
+ */
329
+ links_replace(repo_id: number, source_id: number, edges: readonly LinkEdgeInput[]): Promise<void>;
330
+ /** Clear a document's outbound edges (on delete). Inbound rows stay put. */
331
+ links_clear(source_id: number): Promise<void>;
332
+ /**
333
+ * Bind dangling edges in `repo_id` whose folded target matches
334
+ * `target_norm` to `document_id`. Called when a document appears at a
335
+ * path (create / move-in / restore) so waiting danglers resolve — the
336
+ * identity-bound counterpart to "backlinks survive renames" (§11.2).
337
+ * Bind-only: already-bound edges are never touched, and edges are never
338
+ * unbound (a move produces zero inbound churn; a delete leaves inbound
339
+ * rows bound and lets visibility filtering hide them — §11.2). A source's
340
+ * edge is never bound to the source itself (self-links are noise and are
341
+ * excluded, `source_id <> document_id`). Returns the number of edges
342
+ * newly bound.
343
+ */
344
+ links_resolve_dangling(repo_id: number, target_norm: string, document_id: number): Promise<number>;
345
+ /** A document's outbound edges (backfill, links.stale, tests). */
346
+ links_by_source(source_id: number): Promise<LinkRow[]>;
347
+ /** Every link row in a repo, ordered by (source_id, ord) (tests, verify). */
348
+ links_by_repo(repo_id: number): Promise<LinkRow[]>;
349
+ /**
350
+ * Outbound adjacency for a batch of source documents: distinct
351
+ * `(source_id, target_id, field)` triples where `source_id` is in the
352
+ * batch. Dangling edges (`target_id IS NULL`) are excluded in SQL. Used by
353
+ * the graph read surface (docs/graph-plan.md WS1); the kernel applies
354
+ * scope/filter visibility to the endpoints. `ord` never leaves storage.
355
+ */
356
+ links_adjacent_out(repo_id: number, source_ids: readonly number[]): Promise<AdjacentLink[]>;
357
+ /**
358
+ * Inbound adjacency for a batch of target documents: distinct
359
+ * `(source_id, target_id, field)` triples where `target_id` is in the
360
+ * batch (resolved edges only). The counterpart to links_adjacent_out.
361
+ */
362
+ links_adjacent_in(repo_id: number, target_ids: readonly number[]): Promise<AdjacentLink[]>;
363
+ /**
364
+ * Current-version rows for a batch of document ids (graph read surface).
365
+ * Only live rows (`next_id IS NULL`) in `repo_id`; ids not currently live
366
+ * are simply absent. The kernel needs the paths + frontmatter to project
367
+ * documents and to apply scope to graph endpoints.
368
+ */
369
+ versions_current_by_documents(repo_id: number, document_ids: readonly number[]): Promise<VersionRow[]>;
370
+ /**
371
+ * Document ids of live versions in `repo_id` whose path matches ANY of the
372
+ * given anchored regex sources (`^…$`, the gitignore-glob compilation used
373
+ * everywhere). Path matching happens in SQL (SQLite `regexp()` UDF, Postgres
374
+ * `~`) so the graph root-resolution path never materializes the whole repo.
375
+ * An empty `path_regexes` returns [].
376
+ */
377
+ versions_live_document_ids_matching(repo_id: number, path_regexes: readonly string[]): Promise<number[]>;
378
+ /**
379
+ * Composed query — the kernel orchestrator (§5) hands over a structured
380
+ * `SearchPlan` (repo ids + parsed CEL AST + scope regex sources + sigil
381
+ * exclusions + optional text + candidate whitelist + limit); the adapter
382
+ * compiles the plan into engine-specific SQL. Ordering per §5.1:
383
+ * text-score if `text` is present, else `$created_at DESC`.
384
+ *
385
+ * m5-plan WS2 pushed compilation behind the adapter so the kernel emits
386
+ * no SQL strings.
387
+ */
388
+ versions_search(plan: SearchPlan): Promise<VersionRow[]>;
389
+ /**
390
+ * Chunks + vectors (design §3.2, §5.3, §7.2.2). Written by the
391
+ * backlog worker; read by kernel.query's `semantic` branch.
392
+ *
393
+ * `chunks_upsert` replaces all chunks for `version_id` in one tx.
394
+ * All vectors in the input must share `model` and the same
395
+ * dimensionality; the adapter refuses mixed-dim writes (m4-plan §1,
396
+ * §5.3 "refuse mixed-dim writes to the chunks table").
397
+ */
398
+ chunks_upsert(version_id: number, model: string, chunks: readonly ChunkUpsertInput[]): Promise<void>;
399
+ /**
400
+ * Content-hash dedup lookup (§5.3). Returns one row per hash present
401
+ * in the input list for the given model, with its stored vector so
402
+ * the worker can reuse it without calling the hook.
403
+ */
404
+ chunks_by_hash(model: string, text_hashes: readonly string[]): Promise<{
405
+ text_hash: string;
406
+ embedding: Float32Array;
407
+ }[]>;
408
+ chunks_by_version(version_id: number): Promise<ChunkRow[]>;
409
+ /**
410
+ * Distinct (model, chunk_count) pairs across the chunks table — for
411
+ * `embed status`. `chunk_count` counts rows, not distinct hashes.
412
+ */
413
+ chunks_model_summary(): Promise<{
414
+ model: string;
415
+ chunk_count: number;
416
+ }[]>;
417
+ /**
418
+ * Brute-force k-NN over current-version chunks with vectors matching
419
+ * `model`. §7.2.1 pins v1 at brute-force — indexed ANN (HNSW/IVFFlat
420
+ * for pgvector) is a fast-follow.
421
+ *
422
+ * `k` limits distinct-version results, not chunk hits. The adapter
423
+ * owns the version-collapse (best chunk per version) AND the vector
424
+ * serialization (float32 vs float64, LE vs BE, etc.). Kernel callers
425
+ * pass a plain JS number array — no dialect-specific encoding leaks
426
+ * into the query layer.
427
+ */
428
+ vector_search(repo_ids: readonly number[], model: string, embedding: readonly number[], k: number): Promise<VectorSearchHit[]>;
429
+ backlog_enqueue(version_id: number): Promise<void>;
430
+ /** Rows due now (next_retry_at IS NULL or <= now), oldest first. */
431
+ backlog_dequeue(now: string, limit: number): Promise<BacklogRow[]>;
432
+ backlog_retain(input: {
433
+ version_id: number;
434
+ attempts: number;
435
+ last_error: string;
436
+ next_retry_at: string;
437
+ }): Promise<void>;
438
+ backlog_delete(version_id: number): Promise<void>;
439
+ backlog_status(now: string): Promise<BacklogStatus>;
440
+ };
441
+ export type OpenConfig = {
442
+ /** Database url — sqlite:./path.db or postgres://… */
443
+ database: string;
444
+ };
445
+ export type StorageAdapter = {
446
+ scheme: string;
447
+ open(config: OpenConfig): Promise<Storage>;
448
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Storage layer types — adapter-agnostic. Kernel operations run on top of these.
3
+ *
4
+ * Integer ids are internal (design §3.3) and never cross the wire — the kernel
5
+ * translates rows into Version envelopes with opaque `version_id` strings and
6
+ * slug-based references.
7
+ *
8
+ * All Storage methods are async. SQLite adapter methods complete
9
+ * synchronously and resolve on the next microtask; Postgres adapter
10
+ * methods await real I/O. The uniform signature keeps kernel code
11
+ * dialect-agnostic (design §7.2, m5-plan WS1).
12
+ */
13
+ export {};
14
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/storage/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * The gap-aware forward walk — the brain of the change feed (sync/history
3
+ * plan §3.2). Pure logic shared by both storage adapters so SQLite and
4
+ * Postgres compute an identical safe frontier.
5
+ *
6
+ * `versions.id` is an exact cursor iff writes are serialized. Always true on
7
+ * SQLite (single writer). On Postgres, concurrent writers create
8
+ * commit-visibility skew and `nextval` burns ids on rollback, so gaps are
9
+ * routine — each is either *pending* (in-flight, will appear) or *burned*
10
+ * (never will), indistinguishable except by age.
11
+ *
12
+ * The feed therefore returns the longest safe contiguous run after the cursor,
13
+ * not "everything since." Gap rule, keyed on the **successor's** age: if id 100
14
+ * is missing and its successor 101 has been visible longer than the window,
15
+ * 100 is burned → cross it; if 101 is younger than the window, 100 may be a
16
+ * sibling still committing → truncate the page before 101. The page ends at
17
+ * the first hot gap, the scan boundary (treated as a safe tip), or the output
18
+ * limit — whichever comes first.
19
+ *
20
+ * Gaps are only meaningful on the **global** id sequence: a repo filter
21
+ * naturally skips ids taken by other repos, so the walk runs over global rows
22
+ * (id + repo + age) and the repo filter is applied to the output only.
23
+ */
24
+ /**
25
+ * Upper bound on the lightweight global scan per poll. Large enough that a
26
+ * repo-filtered `limit` page is almost always satisfied in one scan, bounded
27
+ * so a cold `after_id=0` poll over a huge log stays cheap. Truncating the scan
28
+ * never crosses a gap — it only under-delivers, and the next poll continues
29
+ * from the returned cursor.
30
+ */
31
+ export declare const GLOBAL_SCAN_CAP = 10000;
32
+ /**
33
+ * How many recent rows to inspect when computing the safe head `R`. Only the
34
+ * newest rows can hold an unsettled (hot) gap — anything older than the safety
35
+ * window is burned-and-settled — so a small tail suffices regardless of log
36
+ * size. Comfortably exceeds any plausible in-flight concurrent-write count.
37
+ */
38
+ export declare const SAFE_HEAD_TAIL = 1000;
39
+ /** A lightweight row for the frontier walk — no body/frontmatter needed. */
40
+ export type FrontierRow = {
41
+ id: number;
42
+ repo_id: number;
43
+ created_at_ms: number;
44
+ };
45
+ export type FrontierResult = {
46
+ /**
47
+ * Inclusive upper-bound id for the page: every id in `(after_id, upper_id]`
48
+ * is settled and final. Also the resume cursor (`next_since`). Equals
49
+ * `after_id` when nothing is safe to deliver yet, so a caught-up or
50
+ * hot-gap-stalled poll simply doesn't advance.
51
+ */
52
+ upper_id: number;
53
+ };
54
+ /**
55
+ * Walk `light` (global rows with id > after_id, ascending) and return the
56
+ * inclusive `upper_id` through which the feed is gap-free. Rows matching
57
+ * `repo_id` (or all rows when it is undefined) are counted toward `limit`; the
58
+ * page caps at the id of the `limit`-th match when a further match exists
59
+ * within the safe region, otherwise at the safe frontier so an empty tail is
60
+ * skipped and the cursor keeps advancing.
61
+ */
62
+ export declare function safeFrontier(light: readonly FrontierRow[], after_id: number, repo_id: number | undefined, limit: number, now_ms: number, window_ms: number): FrontierResult;
63
+ /**
64
+ * The safe head `R` (§3.4) from a bounded tail of recent rows. `tail` is the
65
+ * last rows by id, ascending (id + age; repo irrelevant here). We anchor the
66
+ * walk at `tail[0].id - 1` so the tail's first row isn't mistaken for a leading
67
+ * gap: everything below the tail is older than any realistic safety window, so
68
+ * its gaps are burned and already settled. Within the tail, the walk crosses
69
+ * burned gaps and stops before the first hot one — exactly the id the feed
70
+ * would hand out as `next_since` at the tip. Empty tail → 0.
71
+ */
72
+ export declare function safeHeadFromTail(tail: readonly FrontierRow[], now_ms: number, window_ms: number): number;
@@ -0,0 +1,92 @@
1
+ /**
2
+ * The gap-aware forward walk — the brain of the change feed (sync/history
3
+ * plan §3.2). Pure logic shared by both storage adapters so SQLite and
4
+ * Postgres compute an identical safe frontier.
5
+ *
6
+ * `versions.id` is an exact cursor iff writes are serialized. Always true on
7
+ * SQLite (single writer). On Postgres, concurrent writers create
8
+ * commit-visibility skew and `nextval` burns ids on rollback, so gaps are
9
+ * routine — each is either *pending* (in-flight, will appear) or *burned*
10
+ * (never will), indistinguishable except by age.
11
+ *
12
+ * The feed therefore returns the longest safe contiguous run after the cursor,
13
+ * not "everything since." Gap rule, keyed on the **successor's** age: if id 100
14
+ * is missing and its successor 101 has been visible longer than the window,
15
+ * 100 is burned → cross it; if 101 is younger than the window, 100 may be a
16
+ * sibling still committing → truncate the page before 101. The page ends at
17
+ * the first hot gap, the scan boundary (treated as a safe tip), or the output
18
+ * limit — whichever comes first.
19
+ *
20
+ * Gaps are only meaningful on the **global** id sequence: a repo filter
21
+ * naturally skips ids taken by other repos, so the walk runs over global rows
22
+ * (id + repo + age) and the repo filter is applied to the output only.
23
+ */
24
+ /**
25
+ * Upper bound on the lightweight global scan per poll. Large enough that a
26
+ * repo-filtered `limit` page is almost always satisfied in one scan, bounded
27
+ * so a cold `after_id=0` poll over a huge log stays cheap. Truncating the scan
28
+ * never crosses a gap — it only under-delivers, and the next poll continues
29
+ * from the returned cursor.
30
+ */
31
+ export const GLOBAL_SCAN_CAP = 10000;
32
+ /**
33
+ * How many recent rows to inspect when computing the safe head `R`. Only the
34
+ * newest rows can hold an unsettled (hot) gap — anything older than the safety
35
+ * window is burned-and-settled — so a small tail suffices regardless of log
36
+ * size. Comfortably exceeds any plausible in-flight concurrent-write count.
37
+ */
38
+ export const SAFE_HEAD_TAIL = 1000;
39
+ /**
40
+ * Walk `light` (global rows with id > after_id, ascending) and return the
41
+ * inclusive `upper_id` through which the feed is gap-free. Rows matching
42
+ * `repo_id` (or all rows when it is undefined) are counted toward `limit`; the
43
+ * page caps at the id of the `limit`-th match when a further match exists
44
+ * within the safe region, otherwise at the safe frontier so an empty tail is
45
+ * skipped and the cursor keeps advancing.
46
+ */
47
+ export function safeFrontier(light, after_id, repo_id, limit, now_ms, window_ms) {
48
+ let expected = after_id + 1;
49
+ let frontier = after_id; // last globally-contiguous, settled id
50
+ let cutoff = after_id; // id of the last matching row we admit
51
+ let matched = 0;
52
+ let capped = false;
53
+ for (const r of light) {
54
+ if (r.id > expected) {
55
+ // A gap precedes r; r is the successor. Young successor → the missing
56
+ // ids may still be committing → stop before r (hot gap). Old successor
57
+ // → the gap is burned → cross it.
58
+ if (now_ms - r.created_at_ms < window_ms)
59
+ break;
60
+ }
61
+ const matches = repo_id === undefined || r.repo_id === repo_id;
62
+ if (matches && matched === limit) {
63
+ // A (limit+1)-th deliverable row sits within the safe region: cap the
64
+ // page at the limit-th match so the client fetches the rest next poll.
65
+ capped = true;
66
+ break;
67
+ }
68
+ frontier = r.id;
69
+ expected = r.id + 1;
70
+ if (matches) {
71
+ matched++;
72
+ cutoff = r.id;
73
+ }
74
+ }
75
+ return { upper_id: capped ? cutoff : frontier };
76
+ }
77
+ /**
78
+ * The safe head `R` (§3.4) from a bounded tail of recent rows. `tail` is the
79
+ * last rows by id, ascending (id + age; repo irrelevant here). We anchor the
80
+ * walk at `tail[0].id - 1` so the tail's first row isn't mistaken for a leading
81
+ * gap: everything below the tail is older than any realistic safety window, so
82
+ * its gaps are burned and already settled. Within the tail, the walk crosses
83
+ * burned gaps and stops before the first hot one — exactly the id the feed
84
+ * would hand out as `next_since` at the tip. Empty tail → 0.
85
+ */
86
+ export function safeHeadFromTail(tail, now_ms, window_ms) {
87
+ if (tail.length === 0)
88
+ return 0;
89
+ const anchor = tail[0].id - 1;
90
+ return safeFrontier(tail, anchor, undefined, tail.length, now_ms, window_ms).upper_id;
91
+ }
92
+ //# sourceMappingURL=versions-since.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"versions-since.js","sourceRoot":"","sources":["../../src/storage/versions-since.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,CAAC;AAErC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC;AAmBnC;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,KAA6B,EAC7B,QAAgB,EAChB,OAA2B,EAC3B,KAAa,EACb,MAAc,EACd,SAAiB;IAEjB,IAAI,QAAQ,GAAG,QAAQ,GAAG,CAAC,CAAC;IAC5B,IAAI,QAAQ,GAAG,QAAQ,CAAC,CAAC,uCAAuC;IAChE,IAAI,MAAM,GAAG,QAAQ,CAAC,CAAC,uCAAuC;IAC9D,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,MAAM,GAAG,KAAK,CAAC;IAEnB,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,CAAC,EAAE,GAAG,QAAQ,EAAE,CAAC;YACpB,sEAAsE;YACtE,uEAAuE;YACvE,kCAAkC;YAClC,IAAI,MAAM,GAAG,CAAC,CAAC,aAAa,GAAG,SAAS;gBAAE,MAAM;QAClD,CAAC;QACD,MAAM,OAAO,GAAG,OAAO,KAAK,SAAS,IAAI,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC;QAC/D,IAAI,OAAO,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;YACjC,sEAAsE;YACtE,uEAAuE;YACvE,MAAM,GAAG,IAAI,CAAC;YACd,MAAM;QACR,CAAC;QACD,QAAQ,GAAG,CAAC,CAAC,EAAE,CAAC;QAChB,QAAQ,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QACpB,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,EAAE,CAAC;YACV,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;QAChB,CAAC;IACH,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;AAClD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAC9B,IAA4B,EAC5B,MAAc,EACd,SAAiB;IAEjB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAChC,MAAM,MAAM,GAAI,IAAI,CAAC,CAAC,CAAiB,CAAC,EAAE,GAAG,CAAC,CAAC;IAC/C,OAAO,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC;AACxF,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Postgres adapter (m5-plan WS4).
3
+ *
4
+ * Contract:
5
+ * - Kernel calls are async; storage owns SQL emission.
6
+ * - `tx()` wraps `begin isolation level repeatable read` and reuses the
7
+ * same PoolClient inside via AsyncLocalStorage. Nested tx uses
8
+ * savepoints. On 40001/40P01 the whole tx body retries with jittered
9
+ * backoff up to 3 times.
10
+ * - int8 parser: PG returns bigints as strings by default; a custom
11
+ * parser converts them to JS numbers with a SafeInteger guard so id
12
+ * drift is loud, not silent.
13
+ * - Vectors serialize as pgvector's `'[…]'` literal; on read they
14
+ * arrive as a string and parse back to Float32Array.
15
+ */
16
+ import type { StorageAdapter } from "../storage/types.js";
17
+ export declare const postgresAdapter: StorageAdapter;