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,135 @@
1
+ /**
2
+ * `$degrees` binding for the graph read surface (docs/graph-plan.md §2.1,
3
+ * decision 5). `$degrees` is a scalar CEL intrinsic legal ONLY inside a
4
+ * `graph` call's `filter`: the minimum hops from the nearest root at which a
5
+ * document was reached.
6
+ *
7
+ * BFS visits every document at its minimal distance first, and the filter can
8
+ * only *prune* (visibility is monotone), so every candidate in round `r`
9
+ * shares `$degrees = r`. We exploit that: compile the filter once per round
10
+ * with `$degrees` inlined as an integer constant, then reuse the existing
11
+ * query-compilation machinery unchanged. This module does the inlining by
12
+ * rewriting the parsed CEL AST — the parser mangles `$degrees` to
13
+ * `__mrplex_i_degrees` (cel-parse.ts), and here we replace that ident with an
14
+ * `int64` constant node. Neither dialect compiler needs to know `$degrees`
15
+ * exists.
16
+ *
17
+ * A `$degrees` reference outside a `graph` filter reaches a dialect compiler
18
+ * as an unknown intrinsic and fails `filter_invalid` there — which is the
19
+ * desired only-in-graph behavior (decision 14).
20
+ */
21
+ import { ConstantSchema, ExprSchema } from "@bufbuild/cel-spec/cel/expr/syntax_pb.js";
22
+ import { clone, create } from "@bufbuild/protobuf";
23
+ import { INTRINSIC_PREFIX } from "./cel-parse.js";
24
+ /** The mangled ident the parser produces for `$degrees`. */
25
+ export const DEGREES_MANGLED = `${INTRINSIC_PREFIX}degrees`;
26
+ /**
27
+ * Return a copy of `ast` with every `$degrees` reference replaced by the
28
+ * integer literal `value`. Pure — the input AST is never mutated, so the
29
+ * per-round callers can re-bind the same parsed filter for each round.
30
+ */
31
+ export function bindDegrees(ast, value) {
32
+ const copy = clone(ExprSchema, ast);
33
+ rewrite(copy, value);
34
+ return copy;
35
+ }
36
+ function rewrite(expr, value) {
37
+ const kind = expr.exprKind;
38
+ switch (kind.case) {
39
+ case "identExpr":
40
+ if (kind.value.name === DEGREES_MANGLED) {
41
+ // Replace the ident node in place with an int64 constant.
42
+ expr.exprKind = {
43
+ case: "constExpr",
44
+ value: create(ConstantSchema, {
45
+ constantKind: { case: "int64Value", value: BigInt(value) },
46
+ }),
47
+ };
48
+ }
49
+ return;
50
+ case "selectExpr":
51
+ if (kind.value.operand)
52
+ rewrite(kind.value.operand, value);
53
+ return;
54
+ case "callExpr": {
55
+ if (kind.value.target)
56
+ rewrite(kind.value.target, value);
57
+ for (const arg of kind.value.args)
58
+ rewrite(arg, value);
59
+ return;
60
+ }
61
+ case "comprehensionExpr": {
62
+ const c = kind.value;
63
+ if (c.iterRange)
64
+ rewrite(c.iterRange, value);
65
+ if (c.accuInit)
66
+ rewrite(c.accuInit, value);
67
+ if (c.loopCondition)
68
+ rewrite(c.loopCondition, value);
69
+ if (c.loopStep)
70
+ rewrite(c.loopStep, value);
71
+ if (c.result)
72
+ rewrite(c.result, value);
73
+ return;
74
+ }
75
+ case "listExpr":
76
+ for (const el of kind.value.elements)
77
+ rewrite(el, value);
78
+ return;
79
+ case "structExpr":
80
+ for (const entry of kind.value.entries) {
81
+ if (entry.value)
82
+ rewrite(entry.value, value);
83
+ }
84
+ return;
85
+ default:
86
+ return;
87
+ }
88
+ }
89
+ /** True if the parsed filter references `$degrees` anywhere. */
90
+ export function referencesDegrees(expr) {
91
+ let found = false;
92
+ const walk = (e) => {
93
+ if (found)
94
+ return;
95
+ const kind = e.exprKind;
96
+ switch (kind.case) {
97
+ case "identExpr":
98
+ if (kind.value.name === DEGREES_MANGLED)
99
+ found = true;
100
+ return;
101
+ case "selectExpr":
102
+ if (kind.value.operand)
103
+ walk(kind.value.operand);
104
+ return;
105
+ case "callExpr":
106
+ if (kind.value.target)
107
+ walk(kind.value.target);
108
+ for (const arg of kind.value.args)
109
+ walk(arg);
110
+ return;
111
+ case "comprehensionExpr": {
112
+ const c = kind.value;
113
+ for (const sub of [c.iterRange, c.accuInit, c.loopCondition, c.loopStep, c.result]) {
114
+ if (sub)
115
+ walk(sub);
116
+ }
117
+ return;
118
+ }
119
+ case "listExpr":
120
+ for (const el of kind.value.elements)
121
+ walk(el);
122
+ return;
123
+ case "structExpr":
124
+ for (const entry of kind.value.entries)
125
+ if (entry.value)
126
+ walk(entry.value);
127
+ return;
128
+ default:
129
+ return;
130
+ }
131
+ };
132
+ walk(expr);
133
+ return found;
134
+ }
135
+ //# sourceMappingURL=degrees.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"degrees.js","sourceRoot":"","sources":["../../../src/kernel/query/degrees.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Graph-predicate recognizers over the CEL AST — design §11.2 "CEL surface"
3
+ * (links-plan.md WS4). Shared by both dialect compilers so `$in_static` etc.
4
+ * are detected identically; each compiler emits its own SQL.
5
+ *
6
+ * The parser mangles `$name` → `__mrplex_i_name` (cel-parse.ts), so the four
7
+ * possession-language intrinsics arrive as ordinary function calls:
8
+ *
9
+ * $in_static("g") → call __mrplex_i_in_static("g")
10
+ * $has_static("g","field") → call __mrplex_i_has_static("g","field")
11
+ * $backlinks_static() → call __mrplex_i_backlinks_static() (a collection)
12
+ * $links_static() → call __mrplex_i_links_static() (a collection)
13
+ *
14
+ * Collections are only meaningful as the target of `.size()` or as the
15
+ * iterRange of `.exists()`/`.all()`; a bare collection call as a boolean is
16
+ * rejected by the dialect compiler (it never reaches a boolean context).
17
+ *
18
+ * Phasing (§11.2): Phase 1 ships the **bare names** ($in/$has/$backlinks()/
19
+ * $links()) AND their `_static` forms — both resolve against the static
20
+ * index today. The bare name is defined as the future union $x_static ∪
21
+ * $x_dyn; with no dynamic edges yet, that union IS the static set, so
22
+ * `$in` == `$in_static` for now. When Phase 2 adds embedded queries, `$in`
23
+ * transparently widens to include them while `$in_static` stays pinned to
24
+ * the index. Only the `_dyn` variants are unimplemented (they need
25
+ * embedded queries) and are rejected with an actionable error.
26
+ */
27
+ import type { CelExpr } from "./ast.js";
28
+ /** Direction of a graph edge relative to the current (outer) document. */
29
+ export type GraphDirection = "in" | "has" | "backlinks" | "links";
30
+ /** A boolean membership predicate: $in_static(glob) / $has_static(glob, field?). */
31
+ export type GraphMembership = {
32
+ direction: "in" | "has";
33
+ glob: string;
34
+ field?: string;
35
+ };
36
+ /** A collection: $backlinks_static() / $links_static(). */
37
+ export type GraphCollection = {
38
+ direction: "backlinks" | "links";
39
+ };
40
+ /**
41
+ * Throw if `fn` is a reserved (`_dyn`) graph name. Called by the dialect
42
+ * compilers before their generic "unsupported function" fallthrough so the
43
+ * message is actionable ("use $in / $in_static").
44
+ */
45
+ export declare function assertNotReservedGraphName(fn: string): void;
46
+ export declare function asGraphMembership(expr: CelExpr): GraphMembership | null;
47
+ /**
48
+ * If `expr` is a collection call ($backlinks / $links, or their _static
49
+ * forms), return its structured form; otherwise null. Throws on args.
50
+ */
51
+ export declare function asGraphCollection(expr: CelExpr): GraphCollection | null;
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Graph-predicate recognizers over the CEL AST — design §11.2 "CEL surface"
3
+ * (links-plan.md WS4). Shared by both dialect compilers so `$in_static` etc.
4
+ * are detected identically; each compiler emits its own SQL.
5
+ *
6
+ * The parser mangles `$name` → `__mrplex_i_name` (cel-parse.ts), so the four
7
+ * possession-language intrinsics arrive as ordinary function calls:
8
+ *
9
+ * $in_static("g") → call __mrplex_i_in_static("g")
10
+ * $has_static("g","field") → call __mrplex_i_has_static("g","field")
11
+ * $backlinks_static() → call __mrplex_i_backlinks_static() (a collection)
12
+ * $links_static() → call __mrplex_i_links_static() (a collection)
13
+ *
14
+ * Collections are only meaningful as the target of `.size()` or as the
15
+ * iterRange of `.exists()`/`.all()`; a bare collection call as a boolean is
16
+ * rejected by the dialect compiler (it never reaches a boolean context).
17
+ *
18
+ * Phasing (§11.2): Phase 1 ships the **bare names** ($in/$has/$backlinks()/
19
+ * $links()) AND their `_static` forms — both resolve against the static
20
+ * index today. The bare name is defined as the future union $x_static ∪
21
+ * $x_dyn; with no dynamic edges yet, that union IS the static set, so
22
+ * `$in` == `$in_static` for now. When Phase 2 adds embedded queries, `$in`
23
+ * transparently widens to include them while `$in_static` stays pinned to
24
+ * the index. Only the `_dyn` variants are unimplemented (they need
25
+ * embedded queries) and are rejected with an actionable error.
26
+ */
27
+ import { KernelError } from "../errors.js";
28
+ import { INTRINSIC_PREFIX } from "./cel-parse.js";
29
+ const P = INTRINSIC_PREFIX;
30
+ // Bare names AND their `_static` forms both map to the static index today.
31
+ // The bare name is the DESIGNED-FOR-THE-FUTURE union $x_static ∪ $x_dyn
32
+ // (§11.2); Phase 1 has no dynamic edges, so the union is exactly the static
33
+ // set — so `$in` == `$in_static` for now, and callers who want to *stay*
34
+ // static-only forever write `$in_static`. When Phase 2 lands embedded
35
+ // queries, `$in` transparently widens to include them; `$in_static` does
36
+ // not. Only `_dyn` is unimplemented (it needs embedded queries).
37
+ const MEMBERSHIP_FNS = {
38
+ [`${P}in`]: "in",
39
+ [`${P}in_static`]: "in",
40
+ [`${P}has`]: "has",
41
+ [`${P}has_static`]: "has",
42
+ };
43
+ const COLLECTION_FNS = {
44
+ [`${P}backlinks`]: "backlinks",
45
+ [`${P}backlinks_static`]: "backlinks",
46
+ [`${P}links`]: "links",
47
+ [`${P}links_static`]: "links",
48
+ };
49
+ // `_dyn` variants — reserved until Phase 2 ships embedded queries. Recognized
50
+ // only to produce a clear error, never compiled.
51
+ const RESERVED_FNS = new Set([
52
+ `${P}in_dyn`,
53
+ `${P}has_dyn`,
54
+ `${P}backlinks_dyn`,
55
+ `${P}links_dyn`,
56
+ ]);
57
+ function reservedName(fn) {
58
+ // __mrplex_i_in_dyn → $in_dyn
59
+ return `$${fn.slice(P.length)}`;
60
+ }
61
+ /**
62
+ * Throw if `fn` is a reserved (`_dyn`) graph name. Called by the dialect
63
+ * compilers before their generic "unsupported function" fallthrough so the
64
+ * message is actionable ("use $in / $in_static").
65
+ */
66
+ export function assertNotReservedGraphName(fn) {
67
+ if (RESERVED_FNS.has(fn)) {
68
+ const name = reservedName(fn);
69
+ const base = name.replace(/_dyn$/, "");
70
+ throw new KernelError("filter_invalid", {
71
+ reason: `${name}() needs Phase 2 (embedded queries); use ${base}() (static today, static∪dynamic later) or ${base}_static() to stay static-only`,
72
+ });
73
+ }
74
+ }
75
+ /** A string-constant argument's value, or undefined if not a string const. */
76
+ function stringArg(expr) {
77
+ if (!expr || expr.exprKind.case !== "constExpr")
78
+ return undefined;
79
+ const k = expr.exprKind.value.constantKind;
80
+ return k?.case === "stringValue" ? k.value : undefined;
81
+ }
82
+ /**
83
+ * If `expr` is a membership call ($in_static / $has_static), return its
84
+ * structured form; otherwise null. Throws filter_invalid on a malformed
85
+ * call (wrong arity, non-string args) so mistakes surface clearly.
86
+ */
87
+ /** Human name for a mangled graph fn (`__mrplex_i_in` → `$in`). */
88
+ function displayName(fn) {
89
+ return `$${fn.slice(P.length)}`;
90
+ }
91
+ export function asGraphMembership(expr) {
92
+ if (expr.exprKind.case !== "callExpr")
93
+ return null;
94
+ const call = expr.exprKind.value;
95
+ const direction = MEMBERSHIP_FNS[call.function];
96
+ if (!direction)
97
+ return null;
98
+ const name = displayName(call.function);
99
+ if (call.target !== undefined) {
100
+ throw new KernelError("filter_invalid", {
101
+ reason: `${name} is a free function, not a method`,
102
+ });
103
+ }
104
+ const glob = stringArg(call.args[0]);
105
+ if (glob === undefined || call.args.length < 1 || call.args.length > 2) {
106
+ throw new KernelError("filter_invalid", {
107
+ reason: `${name}(path-or-glob [, field]) takes a string glob and an optional string field`,
108
+ });
109
+ }
110
+ let field;
111
+ if (call.args.length === 2) {
112
+ field = stringArg(call.args[1]);
113
+ if (field === undefined) {
114
+ throw new KernelError("filter_invalid", {
115
+ reason: `${name} field argument must be a string`,
116
+ });
117
+ }
118
+ }
119
+ return field === undefined ? { direction, glob } : { direction, glob, field };
120
+ }
121
+ /**
122
+ * If `expr` is a collection call ($backlinks / $links, or their _static
123
+ * forms), return its structured form; otherwise null. Throws on args.
124
+ */
125
+ export function asGraphCollection(expr) {
126
+ if (expr.exprKind.case !== "callExpr")
127
+ return null;
128
+ const call = expr.exprKind.value;
129
+ const direction = COLLECTION_FNS[call.function];
130
+ if (!direction)
131
+ return null;
132
+ if (call.target !== undefined || call.args.length !== 0) {
133
+ throw new KernelError("filter_invalid", {
134
+ reason: `${displayName(call.function)}() takes no arguments`,
135
+ });
136
+ }
137
+ return { direction };
138
+ }
139
+ //# sourceMappingURL=graph-ast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-ast.js","sourceRoot":"","sources":["../../../src/kernel/query/graph-ast.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * kernel.query orchestration — design §5.
3
+ *
4
+ * The kernel resolves repo access, parses CEL eagerly (so
5
+ * `filter_invalid` surfaces before storage), and builds a structured
6
+ * `SearchPlan` the adapter compiles into its dialect's SQL (m5-plan
7
+ * WS2). Semantic mode composes vector_search → SearchPlan with a
8
+ * candidate-id whitelist.
9
+ */
10
+ import type { RepoRow, Storage, VersionRow } from "../../storage/types.js";
11
+ import { type ClaimMatcher } from "../auth/scope.js";
12
+ import type { PathConfig } from "../path-config.js";
13
+ import type { QueryHit, Version } from "../wire.js";
14
+ export type QuerySpec = {
15
+ /** Slug, glob, or list. Omitted = every repo the caller can address. */
16
+ repo?: string | string[];
17
+ filter?: string;
18
+ text?: string;
19
+ semantic?: string;
20
+ limit?: number;
21
+ include_hidden?: boolean;
22
+ include_system?: boolean;
23
+ /**
24
+ * Fields to project onto each hit (docs/query-select-plan.md). Names bare
25
+ * frontmatter keys (`title`) or `$`-intrinsics (`$path`, `$body`, …).
26
+ * Defaults to `["$path"]` — the identity you almost always want and the
27
+ * cheapest thing to return.
28
+ */
29
+ select?: string[];
30
+ };
31
+ export type QueryDeps = {
32
+ storage: Storage;
33
+ serverPathConfig: PathConfig;
34
+ toVersionWire: (row: VersionRow, repoSlug: string) => Version;
35
+ /**
36
+ * Optional semantic-query embed hook. When absent and `spec.semantic` is set,
37
+ * runQuery throws `semantic_unavailable` (m4-plan §5 decision 4).
38
+ */
39
+ queryEmbed?: (semantic: string) => Promise<{
40
+ vector: number[];
41
+ model: string;
42
+ dim: number;
43
+ }>;
44
+ };
45
+ /** M2 default when the spec omits limit. */
46
+ export declare const DEFAULT_QUERY_LIMIT = 50;
47
+ /**
48
+ * `claims` is null when the call supplies no scope (full visibility) or a
49
+ * normalized `ClaimMatcher[]` narrowing what's visible (noauth plan §1).
50
+ */
51
+ export declare function runQuery(claims: ClaimMatcher[] | null, spec: QuerySpec, deps: QueryDeps): Promise<QueryHit[]>;
52
+ /**
53
+ * Compute a repo's effective config — exported for callers that want to
54
+ * apply per-repo sigils outside the main query loop (e.g., surfaces).
55
+ */
56
+ export declare function repoEffectiveConfig(repo: RepoRow, serverConfig: PathConfig): PathConfig;
@@ -0,0 +1,276 @@
1
+ /**
2
+ * kernel.query orchestration — design §5.
3
+ *
4
+ * The kernel resolves repo access, parses CEL eagerly (so
5
+ * `filter_invalid` surfaces before storage), and builds a structured
6
+ * `SearchPlan` the adapter compiles into its dialect's SQL (m5-plan
7
+ * WS2). Semantic mode composes vector_search → SearchPlan with a
8
+ * candidate-id whitelist.
9
+ */
10
+ import { slugMatchesPattern } from "../auth/glob.js";
11
+ import { claimsGrantRepo, claimsToScopeGroups } from "../auth/scope.js";
12
+ import { KernelError } from "../errors.js";
13
+ import { effectivePathConfig, parseRepoOverride } from "../path-config.js";
14
+ import { parseCel } from "./cel-parse.js";
15
+ const KNOWN_SPEC_FIELDS = new Set([
16
+ "repo",
17
+ "filter",
18
+ "text",
19
+ "semantic",
20
+ "limit",
21
+ "include_hidden",
22
+ "include_system",
23
+ "select",
24
+ ]);
25
+ /**
26
+ * Projectable `$`-intrinsics (docs/query-select-plan.md §3): the filter
27
+ * compilers' filterable columns (`$path`, `$updated_at`, `$body`) plus the
28
+ * version-identity fields that are projectable but not filterable. Each maps
29
+ * a `$name` to the value read off the already-built `Version` wire object.
30
+ * `select` validates against this registry and rejects unknown `$names`.
31
+ */
32
+ const VERSION_INTRINSIC_PROJECTORS = {
33
+ $path: (v) => v.path,
34
+ $repo: (v) => v.repo,
35
+ $version_id: (v) => v.version_id,
36
+ $prev_version_id: (v) => v.prev_version_id,
37
+ $next_version_id: (v) => v.next_version_id,
38
+ $updated_at: (v) => v.created_at,
39
+ $author: (v) => v.author,
40
+ $body: (v) => v.body,
41
+ $content_hash: (v) => v.content_hash,
42
+ };
43
+ /** All `$`-intrinsics valid in `select` (includes semantic-mode-only fields). */
44
+ const SELECTABLE_INTRINSICS = new Set([
45
+ ...Object.keys(VERSION_INTRINSIC_PROJECTORS),
46
+ "$semantic_score",
47
+ ]);
48
+ /** Identity default — the cheapest useful projection (§2). */
49
+ const DEFAULT_SELECT = ["$path"];
50
+ /** M2 default when the spec omits limit. */
51
+ export const DEFAULT_QUERY_LIMIT = 50;
52
+ /**
53
+ * `claims` is null when the call supplies no scope (full visibility) or a
54
+ * normalized `ClaimMatcher[]` narrowing what's visible (noauth plan §1).
55
+ */
56
+ export async function runQuery(claims, spec, deps) {
57
+ validateSpec(spec);
58
+ const select = spec.select ?? [...DEFAULT_SELECT];
59
+ // 1. Resolve repos the caller can address.
60
+ const reposById = new Map();
61
+ for (const row of await deps.storage.repos_list())
62
+ reposById.set(row.id, row);
63
+ const targetRepos = filterReposByAccessAndSpec(claims, spec, reposById, deps.serverPathConfig);
64
+ if (targetRepos.length === 0)
65
+ return [];
66
+ // 2. Parse CEL filter eagerly — `filter_invalid` before storage.
67
+ let filterAst;
68
+ if (spec.filter !== undefined) {
69
+ const ast = parseCel(spec.filter);
70
+ if (!ast.expr) {
71
+ throw new KernelError("filter_invalid", { reason: "empty filter" });
72
+ }
73
+ filterAst = ast.expr;
74
+ }
75
+ // 3. Per-repo sigil exclusion (§3.5.5).
76
+ const sigils = buildPerRepoSigilExclusion(targetRepos, deps.serverPathConfig, spec.include_hidden ?? false, spec.include_system ?? false);
77
+ // 4. Scope filter — absent claims see everything; claims contribute groups.
78
+ const scope = buildScope(claims, targetRepos);
79
+ const userLimit = spec.limit ?? DEFAULT_QUERY_LIMIT;
80
+ if (userLimit <= 0)
81
+ return [];
82
+ // 5. Semantic branch (M4): vector_search → candidate whitelist → SearchPlan.
83
+ if (spec.semantic !== undefined) {
84
+ if (!deps.queryEmbed) {
85
+ throw new KernelError("semantic_unavailable", {
86
+ reason: "no embedding hook configured on this server",
87
+ });
88
+ }
89
+ let embed;
90
+ try {
91
+ embed = await deps.queryEmbed(spec.semantic);
92
+ }
93
+ catch (err) {
94
+ throw new KernelError("semantic_unavailable", {
95
+ reason: `embedding hook failed at query time: ${err instanceof Error ? err.message : String(err)}`,
96
+ });
97
+ }
98
+ if (embed.vector.length !== embed.dim) {
99
+ throw new KernelError("semantic_unavailable", {
100
+ reason: `embedding hook returned vector.length ${embed.vector.length} != dim ${embed.dim}`,
101
+ });
102
+ }
103
+ const semanticK = Math.min(userLimit * 4, 200);
104
+ const hits = await deps.storage.vector_search(targetRepos.map((r) => r.id), embed.model, embed.vector, semanticK);
105
+ if (hits.length === 0)
106
+ return [];
107
+ const candidateIds = hits.map((h) => h.version_id);
108
+ const distanceById = new Map(hits.map((h) => [h.version_id, h.score]));
109
+ const plan = {
110
+ repo_ids: targetRepos.map((r) => r.id),
111
+ limit: candidateIds.length,
112
+ text: spec.text,
113
+ filter_ast: filterAst,
114
+ sigils,
115
+ scope,
116
+ candidate_ids: candidateIds,
117
+ };
118
+ const rows = await deps.storage.versions_search(plan);
119
+ rows.sort((a, b) => (distanceById.get(a.id) ?? 1) - (distanceById.get(b.id) ?? 1));
120
+ return rows.slice(0, userLimit).map((row) => {
121
+ const repoSlug = reposById.get(row.repo_id).slug;
122
+ const distance = distanceById.get(row.id);
123
+ return projectHit(deps.toVersionWire(row, repoSlug), select, {
124
+ semanticScore: distance === undefined ? undefined : cosineDistanceToSimilarity(distance),
125
+ });
126
+ });
127
+ }
128
+ // 6. Non-semantic path.
129
+ const plan = {
130
+ repo_ids: targetRepos.map((r) => r.id),
131
+ limit: userLimit,
132
+ text: spec.text,
133
+ filter_ast: filterAst,
134
+ sigils,
135
+ scope,
136
+ };
137
+ const rows = await deps.storage.versions_search(plan);
138
+ return rows.map((row) => {
139
+ const repoSlug = reposById.get(row.repo_id).slug;
140
+ return projectHit(deps.toVersionWire(row, repoSlug), select);
141
+ });
142
+ }
143
+ /** Cosine similarity from storage cosine distance (0 = identical, 2 = opposite). */
144
+ function cosineDistanceToSimilarity(distance) {
145
+ return 1 - distance;
146
+ }
147
+ function projectHit(v, select, ctx = {}) {
148
+ const hit = {};
149
+ for (const name of select) {
150
+ if (name === "$semantic_score") {
151
+ if (ctx.semanticScore !== undefined) {
152
+ hit.$semantic_score = ctx.semanticScore;
153
+ }
154
+ continue;
155
+ }
156
+ if (name.startsWith("$")) {
157
+ hit[name] = VERSION_INTRINSIC_PROJECTORS[name](v);
158
+ }
159
+ else if (v.frontmatter[name] !== undefined) {
160
+ hit[name] = v.frontmatter[name];
161
+ }
162
+ }
163
+ return hit;
164
+ }
165
+ // -----------------------------------------------------------------------------
166
+ // Validation
167
+ // -----------------------------------------------------------------------------
168
+ function validateSpec(spec) {
169
+ for (const key of Object.keys(spec)) {
170
+ if (!KNOWN_SPEC_FIELDS.has(key)) {
171
+ throw new KernelError("filter_invalid", {
172
+ reason: `unknown QuerySpec field: ${JSON.stringify(key)}`,
173
+ });
174
+ }
175
+ }
176
+ if (spec.semantic !== undefined) {
177
+ if (typeof spec.semantic !== "string" || spec.semantic.trim().length === 0) {
178
+ throw new KernelError("filter_invalid", {
179
+ reason: "the `semantic` mode requires a non-empty query string",
180
+ });
181
+ }
182
+ }
183
+ if (spec.limit !== undefined) {
184
+ if (typeof spec.limit !== "number" || !Number.isSafeInteger(spec.limit) || spec.limit < 0) {
185
+ throw new KernelError("filter_invalid", {
186
+ reason: `invalid limit: ${JSON.stringify(spec.limit)}`,
187
+ });
188
+ }
189
+ }
190
+ if (spec.select !== undefined) {
191
+ if (!Array.isArray(spec.select) || spec.select.some((s) => typeof s !== "string")) {
192
+ throw new KernelError("filter_invalid", { reason: "select must be an array of strings" });
193
+ }
194
+ for (const name of spec.select) {
195
+ if (name.startsWith("$") && !SELECTABLE_INTRINSICS.has(name)) {
196
+ // Derive the "expected" list from the registry so the message can't
197
+ // drift from the code (mirrors the filter compiler's intrinsic error).
198
+ const expected = [...SELECTABLE_INTRINSICS].sort().join(", ");
199
+ throw new KernelError("filter_invalid", {
200
+ reason: `unknown intrinsic ${name} in select (expected ${expected})`,
201
+ });
202
+ }
203
+ }
204
+ }
205
+ }
206
+ // -----------------------------------------------------------------------------
207
+ // Repo resolution
208
+ // -----------------------------------------------------------------------------
209
+ function filterReposByAccessAndSpec(claims, spec, reposById, serverConfig) {
210
+ const patterns = spec.repo === undefined ? undefined : Array.isArray(spec.repo) ? spec.repo : [spec.repo];
211
+ const systemSigils = serverConfig.system_sigils;
212
+ return [...reposById.values()]
213
+ .filter((repo) => {
214
+ if (claims !== null && !claimsGrantRepo(claims, repo.slug))
215
+ return false;
216
+ if (systemSigils.some((sigil) => repo.slug.startsWith(sigil)))
217
+ return false;
218
+ if (patterns === undefined)
219
+ return true;
220
+ return patterns.some((pattern) => slugMatchesPattern(pattern, repo.slug));
221
+ })
222
+ .sort((a, b) => a.slug.localeCompare(b.slug));
223
+ }
224
+ // -----------------------------------------------------------------------------
225
+ // Per-repo sigil exclusion (§5.1, §3.5.5)
226
+ // -----------------------------------------------------------------------------
227
+ function buildPerRepoSigilExclusion(repos, serverConfig, includeHidden, includeSystem) {
228
+ if (repos.length === 0)
229
+ return [];
230
+ const groups = new Map();
231
+ for (const repo of repos) {
232
+ const cfg = effectivePathConfig(serverConfig, parseRepoOverride(repo.path_config));
233
+ const sigils = [];
234
+ if (!includeHidden)
235
+ sigils.push(...cfg.hidden_sigils);
236
+ if (!includeSystem)
237
+ sigils.push(...cfg.system_sigils);
238
+ const key = JSON.stringify(sigils);
239
+ const existing = groups.get(key);
240
+ if (existing) {
241
+ existing.repoIds.push(repo.id);
242
+ }
243
+ else {
244
+ groups.set(key, { sigils, repoIds: [repo.id] });
245
+ }
246
+ }
247
+ const out = [];
248
+ for (const { sigils, repoIds } of groups.values()) {
249
+ if (sigils.length === 0)
250
+ continue;
251
+ out.push({ repo_ids: repoIds, sigils });
252
+ }
253
+ return out;
254
+ }
255
+ // -----------------------------------------------------------------------------
256
+ // Scope → SearchPlan.scope (§8.2)
257
+ // -----------------------------------------------------------------------------
258
+ function buildScope(claims, targetRepos) {
259
+ // Absent scope = full visibility. The repo-access filter already limited
260
+ // targetRepos, so an empty groups list here means the claims grant no path
261
+ // in any addressable repo — deny.
262
+ if (claims === null)
263
+ return { kind: "allow_all" };
264
+ const groups = claimsToScopeGroups(claims, targetRepos);
265
+ if (groups.length === 0)
266
+ return { kind: "deny_all" };
267
+ return { kind: "groups", groups };
268
+ }
269
+ /**
270
+ * Compute a repo's effective config — exported for callers that want to
271
+ * apply per-repo sigils outside the main query loop (e.g., surfaces).
272
+ */
273
+ export function repoEffectiveConfig(repo, serverConfig) {
274
+ return effectivePathConfig(serverConfig, parseRepoOverride(repo.path_config));
275
+ }
276
+ //# sourceMappingURL=query.js.map