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.
- package/LICENSE +21 -0
- package/README.md +284 -0
- package/dist/cli/config.d.ts +28 -0
- package/dist/cli/config.js +48 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/exit-codes.d.ts +13 -0
- package/dist/cli/exit-codes.js +39 -0
- package/dist/cli/exit-codes.js.map +1 -0
- package/dist/cli/format.d.ts +22 -0
- package/dist/cli/format.js +148 -0
- package/dist/cli/format.js.map +1 -0
- package/dist/cli/main.d.ts +16 -0
- package/dist/cli/main.js +1518 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/client/kernel-client.d.ts +97 -0
- package/dist/client/kernel-client.js +14 -0
- package/dist/client/kernel-client.js.map +1 -0
- package/dist/client/local.d.ts +26 -0
- package/dist/client/local.js +103 -0
- package/dist/client/local.js.map +1 -0
- package/dist/client/remote-mcp.d.ts +35 -0
- package/dist/client/remote-mcp.js +174 -0
- package/dist/client/remote-mcp.js.map +1 -0
- package/dist/embed/backfill.d.ts +24 -0
- package/dist/embed/backfill.js +33 -0
- package/dist/embed/backfill.js.map +1 -0
- package/dist/embed/chunker.d.ts +31 -0
- package/dist/embed/chunker.js +121 -0
- package/dist/embed/chunker.js.map +1 -0
- package/dist/embed/cmd-hook.d.ts +21 -0
- package/dist/embed/cmd-hook.js +100 -0
- package/dist/embed/cmd-hook.js.map +1 -0
- package/dist/embed/config.d.ts +61 -0
- package/dist/embed/config.js +109 -0
- package/dist/embed/config.js.map +1 -0
- package/dist/embed/hook.d.ts +38 -0
- package/dist/embed/hook.js +56 -0
- package/dist/embed/hook.js.map +1 -0
- package/dist/embed/http-hook.d.ts +13 -0
- package/dist/embed/http-hook.js +39 -0
- package/dist/embed/http-hook.js.map +1 -0
- package/dist/embed/worker.d.ts +62 -0
- package/dist/embed/worker.js +228 -0
- package/dist/embed/worker.js.map +1 -0
- package/dist/fixtures/hwe/characters/albion-ever.md +12 -0
- package/dist/fixtures/hwe/characters/cent.md +11 -0
- package/dist/fixtures/hwe/concepts/cent-demiurge-resonance.md +10 -0
- package/dist/fixtures/hwe/concepts/phase-looper.md +10 -0
- package/dist/fixtures/hwe/entities/demiurge.md +11 -0
- package/dist/fixtures/hwe/entities/monad.md +11 -0
- package/dist/fixtures/hwe/moc/globocorp-network.md +19 -0
- package/dist/fixtures/hwe/organizations/globocorp.md +11 -0
- package/dist/fixtures/hwe/readme.md +24 -0
- package/dist/fixtures/starship/crew/aria-okonkwo.md +14 -0
- package/dist/fixtures/starship/crew/bexley-orr.md +14 -0
- package/dist/fixtures/starship/crew/dax-thorne.md +14 -0
- package/dist/fixtures/starship/crew/isolde-marsh.md +16 -0
- package/dist/fixtures/starship/crew/kestrel-vance.md +15 -0
- package/dist/fixtures/starship/crew/quill-vasquez.md +14 -0
- package/dist/fixtures/starship/crew/soren-halloway.md +16 -0
- package/dist/fixtures/starship/encounters/the-drift-choir.md +14 -0
- package/dist/fixtures/starship/encounters/the-hollow-signal.md +16 -0
- package/dist/fixtures/starship/encounters/the-wandering-buoy.md +14 -0
- package/dist/fixtures/starship/equipment/coolant-loop-b.md +13 -0
- package/dist/fixtures/starship/equipment/emergency-ration-bars.md +12 -0
- package/dist/fixtures/starship/equipment/long-range-sensors.md +13 -0
- package/dist/fixtures/starship/equipment/plasma-manifold-3.md +15 -0
- package/dist/fixtures/starship/equipment/shuttle-corvid.md +13 -0
- package/dist/fixtures/starship/logs/marsh-4419-1.md +13 -0
- package/dist/fixtures/starship/logs/okonkwo-4415-1.md +13 -0
- package/dist/fixtures/starship/logs/orr-4413-1.md +13 -0
- package/dist/fixtures/starship/logs/orr-4420-1.md +12 -0
- package/dist/fixtures/starship/logs/thorne-4413-2.md +13 -0
- package/dist/fixtures/starship/logs/vance-4419-2.md +13 -0
- package/dist/fixtures/starship/misc/the-galley-ficus.md +11 -0
- package/dist/fixtures/starship/missions/the-cinder-run.md +17 -0
- package/dist/fixtures/starship/missions/the-drift-choir.md +17 -0
- package/dist/fixtures/starship/missions/the-hollow-signal.md +17 -0
- package/dist/fixtures/starship/missions/the-silent-beacon.md +17 -0
- package/dist/fixtures/starship/moc/crew.md +19 -0
- package/dist/fixtures/starship/moc/encounters.md +14 -0
- package/dist/fixtures/starship/moc/missions.md +15 -0
- package/dist/fixtures/starship/readme.md +60 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/kernel/auth/glob.d.ts +46 -0
- package/dist/kernel/auth/glob.js +112 -0
- package/dist/kernel/auth/glob.js.map +1 -0
- package/dist/kernel/auth/scope.d.ts +53 -0
- package/dist/kernel/auth/scope.js +83 -0
- package/dist/kernel/auth/scope.js.map +1 -0
- package/dist/kernel/casefold.d.ts +37 -0
- package/dist/kernel/casefold.js +44 -0
- package/dist/kernel/casefold.js.map +1 -0
- package/dist/kernel/constants.d.ts +21 -0
- package/dist/kernel/constants.js +22 -0
- package/dist/kernel/constants.js.map +1 -0
- package/dist/kernel/context.d.ts +70 -0
- package/dist/kernel/context.js +114 -0
- package/dist/kernel/context.js.map +1 -0
- package/dist/kernel/deletion.d.ts +50 -0
- package/dist/kernel/deletion.js +83 -0
- package/dist/kernel/deletion.js.map +1 -0
- package/dist/kernel/diff.d.ts +36 -0
- package/dist/kernel/diff.js +61 -0
- package/dist/kernel/diff.js.map +1 -0
- package/dist/kernel/errors.d.ts +37 -0
- package/dist/kernel/errors.js +55 -0
- package/dist/kernel/errors.js.map +1 -0
- package/dist/kernel/frontmatter-input.d.ts +32 -0
- package/dist/kernel/frontmatter-input.js +85 -0
- package/dist/kernel/frontmatter-input.js.map +1 -0
- package/dist/kernel/graph.d.ts +34 -0
- package/dist/kernel/graph.js +559 -0
- package/dist/kernel/graph.js.map +1 -0
- package/dist/kernel/history.d.ts +74 -0
- package/dist/kernel/history.js +143 -0
- package/dist/kernel/history.js.map +1 -0
- package/dist/kernel/kernel.d.ts +162 -0
- package/dist/kernel/kernel.js +675 -0
- package/dist/kernel/kernel.js.map +1 -0
- package/dist/kernel/path-config.d.ts +101 -0
- package/dist/kernel/path-config.js +171 -0
- package/dist/kernel/path-config.js.map +1 -0
- package/dist/kernel/query/ast.d.ts +24 -0
- package/dist/kernel/query/ast.js +52 -0
- package/dist/kernel/query/ast.js.map +1 -0
- package/dist/kernel/query/cel-parse.d.ts +37 -0
- package/dist/kernel/query/cel-parse.js +88 -0
- package/dist/kernel/query/cel-parse.js.map +1 -0
- package/dist/kernel/query/degrees.d.ts +31 -0
- package/dist/kernel/query/degrees.js +135 -0
- package/dist/kernel/query/degrees.js.map +1 -0
- package/dist/kernel/query/graph-ast.d.ts +51 -0
- package/dist/kernel/query/graph-ast.js +139 -0
- package/dist/kernel/query/graph-ast.js.map +1 -0
- package/dist/kernel/query/query.d.ts +56 -0
- package/dist/kernel/query/query.js +276 -0
- package/dist/kernel/query/query.js.map +1 -0
- package/dist/kernel/validation.d.ts +64 -0
- package/dist/kernel/validation.js +183 -0
- package/dist/kernel/validation.js.map +1 -0
- package/dist/kernel/version-id.d.ts +10 -0
- package/dist/kernel/version-id.js +21 -0
- package/dist/kernel/version-id.js.map +1 -0
- package/dist/kernel/wire.d.ts +148 -0
- package/dist/kernel/wire.js +6 -0
- package/dist/kernel/wire.js.map +1 -0
- package/dist/links/backfill.d.ts +28 -0
- package/dist/links/backfill.js +33 -0
- package/dist/links/backfill.js.map +1 -0
- package/dist/links/extract.d.ts +29 -0
- package/dist/links/extract.js +233 -0
- package/dist/links/extract.js.map +1 -0
- package/dist/links/link-config.d.ts +70 -0
- package/dist/links/link-config.js +107 -0
- package/dist/links/link-config.js.map +1 -0
- package/dist/links/maintain.d.ts +37 -0
- package/dist/links/maintain.js +91 -0
- package/dist/links/maintain.js.map +1 -0
- package/dist/links/repair.d.ts +46 -0
- package/dist/links/repair.js +96 -0
- package/dist/links/repair.js.map +1 -0
- package/dist/links/resolve.d.ts +41 -0
- package/dist/links/resolve.js +121 -0
- package/dist/links/resolve.js.map +1 -0
- package/dist/links/stale.d.ts +32 -0
- package/dist/links/stale.js +73 -0
- package/dist/links/stale.js.map +1 -0
- package/dist/markdown/content-hash.d.ts +34 -0
- package/dist/markdown/content-hash.js +46 -0
- package/dist/markdown/content-hash.js.map +1 -0
- package/dist/markdown/frontmatter.d.ts +69 -0
- package/dist/markdown/frontmatter.js +147 -0
- package/dist/markdown/frontmatter.js.map +1 -0
- package/dist/markdown/hash-backfill.d.ts +22 -0
- package/dist/markdown/hash-backfill.js +43 -0
- package/dist/markdown/hash-backfill.js.map +1 -0
- package/dist/mcp/query-syntax.d.ts +20 -0
- package/dist/mcp/query-syntax.js +194 -0
- package/dist/mcp/query-syntax.js.map +1 -0
- package/dist/mcp/render.d.ts +31 -0
- package/dist/mcp/render.js +110 -0
- package/dist/mcp/render.js.map +1 -0
- package/dist/mcp/server.d.ts +60 -0
- package/dist/mcp/server.js +170 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +93 -0
- package/dist/mcp/tools.js +1218 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/rest/conditional.d.ts +37 -0
- package/dist/rest/conditional.js +58 -0
- package/dist/rest/conditional.js.map +1 -0
- package/dist/rest/negotiate.d.ts +33 -0
- package/dist/rest/negotiate.js +54 -0
- package/dist/rest/negotiate.js.map +1 -0
- package/dist/rest/routes.d.ts +37 -0
- package/dist/rest/routes.js +744 -0
- package/dist/rest/routes.js.map +1 -0
- package/dist/seed/fixture-seed.d.ts +35 -0
- package/dist/seed/fixture-seed.js +75 -0
- package/dist/seed/fixture-seed.js.map +1 -0
- package/dist/seed/starship-config.d.ts +3 -0
- package/dist/seed/starship-config.js +5 -0
- package/dist/seed/starship-config.js.map +1 -0
- package/dist/server/headers.d.ts +36 -0
- package/dist/server/headers.js +30 -0
- package/dist/server/headers.js.map +1 -0
- package/dist/server/http-error.d.ts +53 -0
- package/dist/server/http-error.js +104 -0
- package/dist/server/http-error.js.map +1 -0
- package/dist/server/serve.d.ts +56 -0
- package/dist/server/serve.js +135 -0
- package/dist/server/serve.js.map +1 -0
- package/dist/shell/audit.d.ts +32 -0
- package/dist/shell/audit.js +28 -0
- package/dist/shell/audit.js.map +1 -0
- package/dist/shell/guard.d.ts +50 -0
- package/dist/shell/guard.js +176 -0
- package/dist/shell/guard.js.map +1 -0
- package/dist/shell/keys.d.ts +36 -0
- package/dist/shell/keys.js +64 -0
- package/dist/shell/keys.js.map +1 -0
- package/dist/shell/login.d.ts +56 -0
- package/dist/shell/login.js +130 -0
- package/dist/shell/login.js.map +1 -0
- package/dist/shell/oidc.d.ts +62 -0
- package/dist/shell/oidc.js +102 -0
- package/dist/shell/oidc.js.map +1 -0
- package/dist/shell/policy.d.ts +100 -0
- package/dist/shell/policy.js +232 -0
- package/dist/shell/policy.js.map +1 -0
- package/dist/shell/proxy-policy.d.ts +34 -0
- package/dist/shell/proxy-policy.js +120 -0
- package/dist/shell/proxy-policy.js.map +1 -0
- package/dist/shell/proxy.d.ts +47 -0
- package/dist/shell/proxy.js +253 -0
- package/dist/shell/proxy.js.map +1 -0
- package/dist/shell/serve.d.ts +86 -0
- package/dist/shell/serve.js +0 -0
- package/dist/shell/serve.js.map +1 -0
- package/dist/shell/stdio.d.ts +55 -0
- package/dist/shell/stdio.js +63 -0
- package/dist/shell/stdio.js.map +1 -0
- package/dist/storage/registry.d.ts +20 -0
- package/dist/storage/registry.js +38 -0
- package/dist/storage/registry.js.map +1 -0
- package/dist/storage/search-plan.d.ts +93 -0
- package/dist/storage/search-plan.js +15 -0
- package/dist/storage/search-plan.js.map +1 -0
- package/dist/storage/types.d.ts +448 -0
- package/dist/storage/types.js +14 -0
- package/dist/storage/types.js.map +1 -0
- package/dist/storage/versions-since.d.ts +72 -0
- package/dist/storage/versions-since.js +92 -0
- package/dist/storage/versions-since.js.map +1 -0
- package/dist/storage-postgres/adapter.d.ts +17 -0
- package/dist/storage-postgres/adapter.js +782 -0
- package/dist/storage-postgres/adapter.js.map +1 -0
- package/dist/storage-postgres/compile-postgres.d.ts +22 -0
- package/dist/storage-postgres/compile-postgres.js +830 -0
- package/dist/storage-postgres/compile-postgres.js.map +1 -0
- package/dist/storage-postgres/errors.d.ts +34 -0
- package/dist/storage-postgres/errors.js +50 -0
- package/dist/storage-postgres/errors.js.map +1 -0
- package/dist/storage-postgres/migrations/0001_init.sql +113 -0
- package/dist/storage-postgres/migrations/0002_content_hash.sql +7 -0
- package/dist/storage-postgres/migrations/index.d.ts +22 -0
- package/dist/storage-postgres/migrations/index.js +70 -0
- package/dist/storage-postgres/migrations/index.js.map +1 -0
- package/dist/storage-sqlite/adapter.d.ts +2 -0
- package/dist/storage-sqlite/adapter.js +688 -0
- package/dist/storage-sqlite/adapter.js.map +1 -0
- package/dist/storage-sqlite/compile-filter.d.ts +41 -0
- package/dist/storage-sqlite/compile-filter.js +881 -0
- package/dist/storage-sqlite/compile-filter.js.map +1 -0
- package/dist/storage-sqlite/compile-sqlite.d.ts +19 -0
- package/dist/storage-sqlite/compile-sqlite.js +176 -0
- package/dist/storage-sqlite/compile-sqlite.js.map +1 -0
- package/dist/storage-sqlite/migrations/0001_init.sql +123 -0
- package/dist/storage-sqlite/migrations/0002_content_hash.sql +7 -0
- package/dist/storage-sqlite/migrations/index.d.ts +6 -0
- package/dist/storage-sqlite/migrations/index.js +41 -0
- package/dist/storage-sqlite/migrations/index.js.map +1 -0
- package/dist/storage-sqlite/vec.d.ts +40 -0
- package/dist/storage-sqlite/vec.js +62 -0
- package/dist/storage-sqlite/vec.js.map +1 -0
- package/dist/sync/cursor.d.ts +36 -0
- package/dist/sync/cursor.js +66 -0
- package/dist/sync/cursor.js.map +1 -0
- package/dist/sync/daemon.d.ts +40 -0
- package/dist/sync/daemon.js +202 -0
- package/dist/sync/daemon.js.map +1 -0
- package/dist/sync/feed.d.ts +38 -0
- package/dist/sync/feed.js +125 -0
- package/dist/sync/feed.js.map +1 -0
- package/dist/sync/fs-store.d.ts +8 -0
- package/dist/sync/fs-store.js +76 -0
- package/dist/sync/fs-store.js.map +1 -0
- package/dist/sync/intrinsics.d.ts +52 -0
- package/dist/sync/intrinsics.js +76 -0
- package/dist/sync/intrinsics.js.map +1 -0
- package/dist/sync/paths.d.ts +38 -0
- package/dist/sync/paths.js +56 -0
- package/dist/sync/paths.js.map +1 -0
- package/dist/sync/push.d.ts +48 -0
- package/dist/sync/push.js +328 -0
- package/dist/sync/push.js.map +1 -0
- package/dist/sync/reconcile.d.ts +54 -0
- package/dist/sync/reconcile.js +252 -0
- package/dist/sync/reconcile.js.map +1 -0
- package/dist/sync/run.d.ts +27 -0
- package/dist/sync/run.js +46 -0
- package/dist/sync/run.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +5 -0
- package/dist/version.js.map +1 -0
- 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
|