@treeseed/sdk 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -494
- package/dist/{src/cli-tools.d.ts → cli-tools.d.ts} +1 -1
- package/dist/cli-tools.js +5 -3
- package/dist/{src/content-store.d.ts → content-store.d.ts} +3 -2
- package/dist/content-store.js +52 -20
- package/dist/{src/d1-store.d.ts → d1-store.d.ts} +62 -1
- package/dist/d1-store.js +625 -65
- package/dist/field-aliases.d.ts +11 -0
- package/dist/field-aliases.js +41 -0
- package/dist/graph/build.d.ts +19 -0
- package/dist/graph/build.js +949 -0
- package/dist/graph/dsl.d.ts +2 -0
- package/dist/graph/dsl.js +243 -0
- package/dist/graph/query.d.ts +47 -0
- package/dist/graph/query.js +447 -0
- package/dist/graph/ranking.d.ts +3 -0
- package/dist/graph/ranking.js +483 -0
- package/dist/graph/schema.d.ts +142 -0
- package/dist/graph/schema.js +133 -0
- package/dist/graph.d.ts +52 -0
- package/dist/graph.js +133 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +90 -2
- package/dist/model-registry.d.ts +8 -0
- package/dist/model-registry.js +351 -25
- package/dist/operations/providers/default.d.ts +10 -0
- package/dist/operations/providers/default.js +514 -0
- package/dist/operations/runtime.d.ts +7 -0
- package/dist/operations/runtime.js +60 -0
- package/dist/operations/services/config-runtime.d.ts +269 -0
- package/dist/operations/services/config-runtime.js +1397 -0
- package/dist/operations/services/d1-migration.d.ts +6 -0
- package/dist/operations/services/d1-migration.js +89 -0
- package/dist/operations/services/deploy.d.ts +371 -0
- package/dist/operations/services/deploy.js +981 -0
- package/dist/operations/services/git-workflow.d.ts +49 -0
- package/dist/operations/services/git-workflow.js +218 -0
- package/dist/operations/services/github-automation.d.ts +156 -0
- package/dist/operations/services/github-automation.js +256 -0
- package/dist/operations/services/local-dev.d.ts +9 -0
- package/dist/operations/services/local-dev.js +106 -0
- package/dist/operations/services/mailpit-runtime.d.ts +4 -0
- package/dist/operations/services/mailpit-runtime.js +59 -0
- package/dist/operations/services/railway-deploy.d.ts +53 -0
- package/dist/operations/services/railway-deploy.js +123 -0
- package/dist/operations/services/runtime-paths.d.ts +19 -0
- package/dist/operations/services/runtime-paths.js +54 -0
- package/dist/operations/services/runtime-tools.d.ts +117 -0
- package/dist/operations/services/runtime-tools.js +358 -0
- package/dist/operations/services/save-deploy-preflight.d.ts +34 -0
- package/dist/operations/services/save-deploy-preflight.js +76 -0
- package/dist/operations/services/template-registry.d.ts +88 -0
- package/dist/operations/services/template-registry.js +407 -0
- package/dist/operations/services/watch-dev.d.ts +21 -0
- package/dist/operations/services/watch-dev.js +284 -0
- package/dist/operations/services/workspace-preflight.d.ts +40 -0
- package/dist/operations/services/workspace-preflight.js +165 -0
- package/dist/operations/services/workspace-save.d.ts +42 -0
- package/dist/operations/services/workspace-save.js +235 -0
- package/dist/operations/services/workspace-tools.d.ts +16 -0
- package/dist/operations/services/workspace-tools.js +270 -0
- package/dist/operations-registry.d.ts +5 -0
- package/dist/operations-registry.js +68 -0
- package/dist/operations-types.d.ts +71 -0
- package/dist/operations-types.js +17 -0
- package/dist/operations.d.ts +6 -0
- package/dist/operations.js +16 -0
- package/dist/platform/books-data.d.ts +1 -0
- package/dist/platform/books-data.js +1 -0
- package/dist/platform/contracts.d.ts +158 -0
- package/dist/platform/contracts.js +0 -0
- package/dist/platform/deploy/config.d.ts +4 -0
- package/dist/platform/deploy/config.js +222 -0
- package/dist/platform/deploy-config.d.ts +1 -0
- package/dist/platform/deploy-config.js +1 -0
- package/dist/platform/env.yaml +394 -0
- package/dist/platform/environment.d.ts +130 -0
- package/dist/platform/environment.js +331 -0
- package/dist/platform/plugin.d.ts +2 -0
- package/dist/platform/plugin.js +4 -0
- package/dist/platform/plugins/constants.d.ts +22 -0
- package/dist/platform/plugins/constants.js +29 -0
- package/dist/platform/plugins/plugin.d.ts +51 -0
- package/dist/platform/plugins/plugin.js +6 -0
- package/dist/platform/plugins/runtime.d.ts +35 -0
- package/dist/platform/plugins/runtime.js +142 -0
- package/dist/platform/plugins.d.ts +5 -0
- package/dist/platform/plugins.js +16 -0
- package/dist/platform/site-config-schema.js +1 -0
- package/dist/platform/tenant/config.d.ts +9 -0
- package/dist/platform/tenant/config.js +154 -0
- package/dist/platform/tenant/runtime-config.d.ts +4 -0
- package/dist/platform/tenant/runtime-config.js +20 -0
- package/dist/platform/tenant-config.d.ts +1 -0
- package/dist/platform/tenant-config.js +1 -0
- package/dist/platform/utils/books-data.d.ts +29 -0
- package/dist/platform/utils/books-data.js +82 -0
- package/dist/platform/utils/site-config-schema.js +321 -0
- package/dist/remote.d.ts +175 -0
- package/dist/remote.js +202 -0
- package/dist/runtime.js +50 -3
- package/dist/scripts/aggregate-book.js +121 -0
- package/dist/scripts/build-dist.js +57 -13
- package/dist/scripts/build-tenant-worker.js +36 -0
- package/dist/scripts/cleanup-markdown.js +373 -0
- package/dist/scripts/cli-test-fixtures.js +48 -0
- package/dist/scripts/config-treeseed.js +95 -0
- package/dist/scripts/ensure-mailpit.js +29 -0
- package/dist/scripts/local-dev.js +129 -0
- package/dist/scripts/logs-mailpit.js +2 -0
- package/dist/scripts/patch-starlight-content-path.js +172 -0
- package/dist/scripts/release-verify.js +34 -5
- package/dist/scripts/run-fixture-astro-command.js +18 -0
- package/dist/scripts/scaffold-site.js +65 -0
- package/dist/scripts/stop-mailpit.js +5 -0
- package/dist/scripts/sync-dev-vars.js +6 -0
- package/dist/scripts/sync-template.js +20 -0
- package/dist/scripts/template-catalog.test.js +100 -0
- package/dist/scripts/template-command.js +31 -0
- package/dist/scripts/tenant-astro-command.js +3 -0
- package/dist/scripts/tenant-build.js +16 -0
- package/dist/scripts/tenant-check.js +7 -0
- package/dist/scripts/tenant-d1-migrate-local.js +11 -0
- package/dist/scripts/tenant-deploy.js +180 -0
- package/dist/scripts/tenant-destroy.js +104 -0
- package/dist/scripts/tenant-dev.js +171 -0
- package/dist/scripts/tenant-lint.js +4 -0
- package/dist/scripts/tenant-test.js +4 -0
- package/dist/scripts/test-cloudflare-local.js +212 -0
- package/dist/scripts/test-scaffold.js +314 -0
- package/dist/scripts/test-smoke.js +71 -13
- package/dist/scripts/treeseed-assert-release-tag-version.js +21 -0
- package/dist/scripts/treeseed-build-dist.js +134 -0
- package/dist/scripts/treeseed-publish-package.js +19 -0
- package/dist/scripts/treeseed-release-verify.js +131 -0
- package/dist/scripts/treeseed-run-ts.js +45 -0
- package/dist/scripts/validate-templates.js +6 -0
- package/dist/scripts/verify-driver.js +29 -0
- package/dist/scripts/workflow-commands.test.js +39 -0
- package/dist/scripts/workspace-close.js +24 -0
- package/dist/scripts/workspace-command-e2e.js +718 -0
- package/dist/scripts/workspace-lint.js +9 -0
- package/dist/scripts/workspace-preflight.js +22 -0
- package/dist/scripts/workspace-publish-changed-packages.js +16 -0
- package/dist/scripts/workspace-release-verify.js +81 -0
- package/dist/scripts/workspace-release.js +42 -0
- package/dist/scripts/workspace-save.js +124 -0
- package/dist/scripts/workspace-start-warning.js +3 -0
- package/dist/scripts/workspace-start.js +71 -0
- package/dist/scripts/workspace-test-unit.js +4 -0
- package/dist/scripts/workspace-test.js +11 -0
- package/dist/sdk-fields.d.ts +11 -0
- package/dist/sdk-fields.js +169 -0
- package/dist/sdk-filters.d.ts +4 -0
- package/dist/sdk-filters.js +12 -15
- package/dist/sdk-types.d.ts +796 -0
- package/dist/sdk-types.js +7 -1
- package/dist/sdk-version.d.ts +2 -0
- package/dist/sdk-version.js +42 -0
- package/dist/sdk.d.ts +215 -0
- package/dist/sdk.js +235 -11
- package/dist/stores/cursor-store.js +9 -3
- package/dist/stores/lease-store.js +8 -2
- package/dist/{src/stores → stores}/message-store.d.ts +1 -1
- package/dist/stores/message-store.js +27 -3
- package/dist/stores/operational-store.d.ts +24 -0
- package/dist/stores/operational-store.js +279 -0
- package/dist/stores/run-store.js +8 -1
- package/dist/stores/subscription-store.js +7 -5
- package/dist/template-catalog.d.ts +13 -0
- package/dist/template-catalog.js +141 -0
- package/dist/treeseed/services/compose.yml +7 -0
- package/dist/treeseed/template-catalog/catalog.fixture.json +55 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/astro.config.d.ts +2 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/astro.config.ts +3 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/package.json +32 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/src/config.yaml +40 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content/empty/.gitkeep +1 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content/knowledge/handbook/index.mdx +11 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content/pages/welcome.mdx +11 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content.config.d.ts +1 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content.config.ts +3 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/src/env.yaml +1 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/src/manifest.yaml +19 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/treeseed.site.yaml +26 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/tsconfig.json +9 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template.config.json +90 -0
- package/dist/verification.d.ts +20 -0
- package/dist/verification.js +98 -0
- package/dist/workflow/operations.d.ts +396 -0
- package/dist/workflow/operations.js +841 -0
- package/dist/workflow-state.d.ts +56 -0
- package/dist/workflow-state.js +195 -0
- package/dist/workflow-support.d.ts +9 -0
- package/dist/workflow-support.js +176 -0
- package/dist/workflow.d.ts +111 -0
- package/dist/workflow.js +97 -0
- package/package.json +97 -5
- package/scripts/verify-driver.mjs +29 -0
- package/dist/scripts/.ts-run-1775616845195-odh4xzphk3l.js +0 -22
- package/dist/scripts/.ts-run-1775616848931-9386s6kwrl.js +0 -126
- package/dist/scripts/assert-release-tag-version.d.ts +0 -1
- package/dist/scripts/build-dist.d.ts +0 -1
- package/dist/scripts/package-tools.d.ts +0 -15
- package/dist/scripts/publish-package.d.ts +0 -1
- package/dist/scripts/release-verify.d.ts +0 -1
- package/dist/scripts/test-smoke.d.ts +0 -1
- package/dist/src/index.d.ts +0 -6
- package/dist/src/model-registry.d.ts +0 -4
- package/dist/src/sdk-filters.d.ts +0 -4
- package/dist/src/sdk-types.d.ts +0 -285
- package/dist/src/sdk.d.ts +0 -109
- package/dist/test/test-fixture.d.ts +0 -1
- package/dist/test/utils/envelopes.test.d.ts +0 -1
- package/dist/test/utils/sdk.test.d.ts +0 -1
- package/dist/vitest.config.d.ts +0 -2
- /package/dist/{src/frontmatter.d.ts → frontmatter.d.ts} +0 -0
- /package/dist/{src/git-runtime.d.ts → git-runtime.d.ts} +0 -0
- /package/dist/{src/runtime.d.ts → runtime.d.ts} +0 -0
- /package/dist/{src/stores → stores}/cursor-store.d.ts +0 -0
- /package/dist/{src/stores → stores}/envelopes.d.ts +0 -0
- /package/dist/{src/stores → stores}/helpers.d.ts +0 -0
- /package/dist/{src/stores → stores}/lease-store.d.ts +0 -0
- /package/dist/{src/stores → stores}/run-store.d.ts +0 -0
- /package/dist/{src/stores → stores}/subscription-store.d.ts +0 -0
- /package/dist/{src/types → types}/agents.d.ts +0 -0
- /package/dist/{src/types → types}/cloudflare.d.ts +0 -0
- /package/dist/{src/wrangler-d1.d.ts → wrangler-d1.d.ts} +0 -0
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { parseGraphDsl } from "./dsl.js";
|
|
3
|
+
import { DEFAULT_GRAPH_RANKING_PROVIDER } from "./ranking.js";
|
|
4
|
+
import { normalizeText } from "./schema.js";
|
|
5
|
+
const RELATION_TO_EDGE_TYPE = {
|
|
6
|
+
related: "RELATES_TO",
|
|
7
|
+
depends_on: "DEPENDS_ON",
|
|
8
|
+
implements: "IMPLEMENTS",
|
|
9
|
+
references: "REFERENCES",
|
|
10
|
+
parent: "PARENT_SECTION",
|
|
11
|
+
child: "CHILD_SECTION",
|
|
12
|
+
supersedes: "SUPERSEDES"
|
|
13
|
+
};
|
|
14
|
+
function matchesNodeOptions(node, options) {
|
|
15
|
+
if (!node) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
if (options?.models?.length && (!node.sourceModel || !options.models.includes(node.sourceModel))) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
if (options?.nodeTypes?.length && !options.nodeTypes.includes(node.nodeType)) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
function dedupeResults(results) {
|
|
27
|
+
return [...new Map(results.map((result) => [result.node.id, result])).values()];
|
|
28
|
+
}
|
|
29
|
+
function tokenEstimate(text) {
|
|
30
|
+
return Math.max(1, Math.ceil(text.trim().length / 4));
|
|
31
|
+
}
|
|
32
|
+
function normalizeScopePath(value) {
|
|
33
|
+
return value.replace(/\\/gu, "/").replace(/\/+/gu, "/").replace(/\/$/u, "") || "/";
|
|
34
|
+
}
|
|
35
|
+
function stripExt(value) {
|
|
36
|
+
return value.replace(/\.(md|mdx)$/iu, "");
|
|
37
|
+
}
|
|
38
|
+
function nodeContentPath(node) {
|
|
39
|
+
const relativePath = typeof node.data?.relativePath === "string" ? node.data.relativePath.replace(/\.(md|mdx)$/iu, "") : typeof node.slug === "string" ? node.slug.replace(/#.*$/u, "") : "";
|
|
40
|
+
const modelPrefix = node.sourceModel ? `/${node.sourceModel}` : "";
|
|
41
|
+
if (!relativePath && modelPrefix) {
|
|
42
|
+
return modelPrefix;
|
|
43
|
+
}
|
|
44
|
+
return normalizeScopePath(`${modelPrefix}/${relativePath}`);
|
|
45
|
+
}
|
|
46
|
+
function nodeMatchesScope(node, scopePaths) {
|
|
47
|
+
if (!scopePaths?.length) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
const contentPath = nodeContentPath(node);
|
|
51
|
+
return scopePaths.some((scopePath) => contentPath === normalizeScopePath(scopePath) || contentPath.startsWith(`${normalizeScopePath(scopePath)}/`));
|
|
52
|
+
}
|
|
53
|
+
function nodeMetadataType(node) {
|
|
54
|
+
const frontmatter = node.data?.frontmatter;
|
|
55
|
+
const frontmatterType = typeof frontmatter?.type === "string" ? frontmatter.type : null;
|
|
56
|
+
return normalizeText(frontmatterType ?? node.entityType ?? node.sourceModel ?? node.nodeType);
|
|
57
|
+
}
|
|
58
|
+
function nodeMatchesWhere(node, filters) {
|
|
59
|
+
if (!filters?.length) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
return filters.every((filter) => {
|
|
63
|
+
const values = filter.field === "type" ? [nodeMetadataType(node)] : filter.field === "status" ? [normalizeText(node.status ?? "")] : filter.field === "audience" ? (node.audience ?? []).map(normalizeText) : filter.field === "tag" ? (node.tags ?? []).map(normalizeText) : filter.field === "domain" ? [normalizeText(node.domain ?? "")] : [];
|
|
64
|
+
const expected = Array.isArray(filter.value) ? filter.value.map(normalizeText) : [normalizeText(filter.value)];
|
|
65
|
+
return filter.op === "eq" ? expected.some((entry) => values.includes(entry)) : expected.every((entry) => values.includes(entry));
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function excerptForView(node, view) {
|
|
69
|
+
const text = node.text ?? "";
|
|
70
|
+
switch (view) {
|
|
71
|
+
case "list":
|
|
72
|
+
return "";
|
|
73
|
+
case "map":
|
|
74
|
+
return `${node.title ?? node.id}`;
|
|
75
|
+
case "brief":
|
|
76
|
+
return text.slice(0, 480).trim();
|
|
77
|
+
case "full":
|
|
78
|
+
default:
|
|
79
|
+
return text;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
class GraphQueryEngine {
|
|
83
|
+
constructor(state, onQuery, rankingProvider = DEFAULT_GRAPH_RANKING_PROVIDER) {
|
|
84
|
+
this.state = state;
|
|
85
|
+
this.onQuery = onQuery;
|
|
86
|
+
this.rankingProvider = rankingProvider;
|
|
87
|
+
for (const node of state.nodes) {
|
|
88
|
+
this.nodesById.set(node.id, node);
|
|
89
|
+
if (node.nodeType === "Section" && node.fileId) {
|
|
90
|
+
const current = this.fileSections.get(node.fileId) ?? [];
|
|
91
|
+
current.push(node);
|
|
92
|
+
this.fileSections.set(node.fileId, current);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
for (const edge of state.edges) {
|
|
96
|
+
this.edgesById.set(edge.id, edge);
|
|
97
|
+
const outgoing = this.outgoing.get(edge.sourceId) ?? [];
|
|
98
|
+
outgoing.push(edge);
|
|
99
|
+
this.outgoing.set(edge.sourceId, outgoing);
|
|
100
|
+
const incoming = this.incoming.get(edge.targetId) ?? [];
|
|
101
|
+
incoming.push(edge);
|
|
102
|
+
this.incoming.set(edge.targetId, incoming);
|
|
103
|
+
}
|
|
104
|
+
for (const sections of this.fileSections.values()) {
|
|
105
|
+
sections.sort((left, right) => Number(left.data?.startOffset ?? 0) - Number(right.data?.startOffset ?? 0));
|
|
106
|
+
}
|
|
107
|
+
this.rankingIndex = this.rankingProvider.buildIndex({
|
|
108
|
+
nodes: state.nodes,
|
|
109
|
+
edges: state.edges
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
state;
|
|
113
|
+
onQuery;
|
|
114
|
+
nodesById = /* @__PURE__ */ new Map();
|
|
115
|
+
edgesById = /* @__PURE__ */ new Map();
|
|
116
|
+
outgoing = /* @__PURE__ */ new Map();
|
|
117
|
+
incoming = /* @__PURE__ */ new Map();
|
|
118
|
+
fileSections = /* @__PURE__ */ new Map();
|
|
119
|
+
rankingProvider;
|
|
120
|
+
rankingIndex;
|
|
121
|
+
filterSearchResults(results, options) {
|
|
122
|
+
return dedupeResults(
|
|
123
|
+
results.filter((result) => matchesNodeOptions(result.node, options)).slice(0, options?.limit ?? 20)
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
eligibleNodeIds(request) {
|
|
127
|
+
return statefulIds(
|
|
128
|
+
this.nodesById.values(),
|
|
129
|
+
(node) => matchesNodeOptions(node, request.options) && nodeMatchesScope(node, request.scopePaths) && nodeMatchesWhere(node, request.where)
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
searchFiles(query, options) {
|
|
133
|
+
this.onQuery?.("searchFiles", query);
|
|
134
|
+
return this.filterSearchResults(this.rankingIndex.search({ query, scope: "files", options }), options);
|
|
135
|
+
}
|
|
136
|
+
searchSections(query, options) {
|
|
137
|
+
this.onQuery?.("searchSections", query);
|
|
138
|
+
return this.filterSearchResults(this.rankingIndex.search({ query, scope: "sections", options }), options);
|
|
139
|
+
}
|
|
140
|
+
searchEntities(query, options) {
|
|
141
|
+
this.onQuery?.("searchEntities", query);
|
|
142
|
+
return this.filterSearchResults(this.rankingIndex.search({ query, scope: "entities", options }), options);
|
|
143
|
+
}
|
|
144
|
+
getNode(id) {
|
|
145
|
+
this.onQuery?.("getNode", id);
|
|
146
|
+
return this.nodesById.get(id) ?? null;
|
|
147
|
+
}
|
|
148
|
+
getNeighbors(id, options) {
|
|
149
|
+
this.onQuery?.("getNeighbors", id);
|
|
150
|
+
const edges = [
|
|
151
|
+
...options?.direction !== "incoming" ? this.outgoing.get(id) ?? [] : [],
|
|
152
|
+
...options?.direction !== "outgoing" ? this.incoming.get(id) ?? [] : []
|
|
153
|
+
].filter((edge) => !options?.edgeTypes?.length || options.edgeTypes.includes(edge.type));
|
|
154
|
+
const nodes = dedupeResults(
|
|
155
|
+
edges.map((edge) => this.nodesById.get(edge.sourceId === id ? edge.targetId : edge.sourceId)).filter((node) => matchesNodeOptions(node, options)).map((node) => ({ node, score: 1, reason: "neighbor" }))
|
|
156
|
+
).map((result) => result.node);
|
|
157
|
+
return {
|
|
158
|
+
node: this.nodesById.get(id) ?? null,
|
|
159
|
+
nodes: nodes.slice(0, options?.limit ?? nodes.length),
|
|
160
|
+
edges: edges.slice(0, options?.limit ?? edges.length)
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
followReferences(seedId, options) {
|
|
164
|
+
this.onQuery?.("followReferences", seedId);
|
|
165
|
+
const maxDepth = options?.depth ?? 2;
|
|
166
|
+
const visited = /* @__PURE__ */ new Set([seedId]);
|
|
167
|
+
const queue = [{ id: seedId, depth: 0 }];
|
|
168
|
+
const nodes = [];
|
|
169
|
+
const edges = [];
|
|
170
|
+
while (queue.length > 0) {
|
|
171
|
+
const current = queue.shift();
|
|
172
|
+
const node = this.nodesById.get(current.id);
|
|
173
|
+
if (node && matchesNodeOptions(node, options)) {
|
|
174
|
+
nodes.push(node);
|
|
175
|
+
}
|
|
176
|
+
if (current.depth >= maxDepth) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
for (const edge of this.outgoing.get(current.id) ?? []) {
|
|
180
|
+
if (options?.edgeTypes?.length && !options.edgeTypes.includes(edge.type)) {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
edges.push(edge);
|
|
184
|
+
const target = edge.targetId;
|
|
185
|
+
if (!visited.has(target) && matchesNodeOptions(this.nodesById.get(target), options)) {
|
|
186
|
+
visited.add(target);
|
|
187
|
+
queue.push({ id: target, depth: current.depth + 1 });
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
seedId,
|
|
193
|
+
nodes: dedupeResults(nodes.map((node) => ({ node, score: 1, reason: "traversal" }))).map((result) => result.node).slice(0, options?.limit ?? nodes.length),
|
|
194
|
+
edges: [...new Map(edges.map((edge) => [edge.id, edge])).values()].slice(0, options?.limit ?? edges.length)
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
getBacklinks(id, options) {
|
|
198
|
+
this.onQuery?.("getBacklinks", id);
|
|
199
|
+
const edges = (this.incoming.get(id) ?? []).filter((edge) => !options?.edgeTypes?.length || options.edgeTypes.includes(edge.type));
|
|
200
|
+
const nodes = edges.map((edge) => this.nodesById.get(edge.sourceId)).filter((node) => matchesNodeOptions(node, options));
|
|
201
|
+
return {
|
|
202
|
+
node: this.nodesById.get(id) ?? null,
|
|
203
|
+
nodes: nodes.slice(0, options?.limit ?? nodes.length),
|
|
204
|
+
edges: edges.slice(0, options?.limit ?? edges.length)
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
getRelated(id, options) {
|
|
208
|
+
this.onQuery?.("getRelated", id);
|
|
209
|
+
const result = this.queryGraph({
|
|
210
|
+
seedIds: [id],
|
|
211
|
+
options: {
|
|
212
|
+
...options,
|
|
213
|
+
depth: options?.depth ?? 1,
|
|
214
|
+
limit: options?.limit ?? 20,
|
|
215
|
+
maxNodes: options?.maxNodes ?? options?.limit ?? 20
|
|
216
|
+
},
|
|
217
|
+
relations: ["related", "references", "depends_on"]
|
|
218
|
+
});
|
|
219
|
+
return result.nodes.filter((entry) => entry.node.id !== id);
|
|
220
|
+
}
|
|
221
|
+
getSubgraph(seedIds, options) {
|
|
222
|
+
this.onQuery?.("getSubgraph", seedIds.join(","));
|
|
223
|
+
const aggregatedNodes = /* @__PURE__ */ new Map();
|
|
224
|
+
const aggregatedEdges = /* @__PURE__ */ new Map();
|
|
225
|
+
for (const seedId of seedIds) {
|
|
226
|
+
const traversal = this.followReferences(seedId, options);
|
|
227
|
+
for (const node of traversal.nodes) aggregatedNodes.set(node.id, node);
|
|
228
|
+
for (const edge of traversal.edges) aggregatedEdges.set(edge.id, edge);
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
seedId: seedIds.join(","),
|
|
232
|
+
nodes: [...aggregatedNodes.values()],
|
|
233
|
+
edges: [...aggregatedEdges.values()]
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
resolveSeeds(request) {
|
|
237
|
+
this.onQuery?.("resolveSeeds", request.query ?? request.seedIds?.join(",") ?? "");
|
|
238
|
+
const explicitSeeds = request.seeds ?? [];
|
|
239
|
+
const idSeeds = (request.seedIds ?? []).map((id, index) => ({ id: `seed-id:${index}`, kind: "id", value: id }));
|
|
240
|
+
const querySeeds = request.query ? [{ id: "seed-query:0", kind: "query", value: request.query, scope: request.scope }] : [];
|
|
241
|
+
const seeds = [...explicitSeeds, ...idSeeds, ...querySeeds];
|
|
242
|
+
const matches = [];
|
|
243
|
+
const resolvedNodeIds = /* @__PURE__ */ new Set();
|
|
244
|
+
for (const seed of seeds) {
|
|
245
|
+
if (seed.kind === "id") {
|
|
246
|
+
const node = this.getNode(seed.value);
|
|
247
|
+
if (node && nodeMatchesScope(node, request.scopePaths) && nodeMatchesWhere(node, request.where)) {
|
|
248
|
+
matches.push({ node, score: 100, reason: "seed-id" });
|
|
249
|
+
resolvedNodeIds.add(node.id);
|
|
250
|
+
}
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
if (seed.kind === "path") {
|
|
254
|
+
const node = this.resolveReference(seed.value) ?? [...this.nodesById.values()].find((entry) => nodeContentPath(entry) === normalizeScopePath(seed.value)) ?? null;
|
|
255
|
+
if (node && nodeMatchesScope(node, request.scopePaths) && nodeMatchesWhere(node, request.where)) {
|
|
256
|
+
matches.push({ node, score: 90, reason: "seed-path" });
|
|
257
|
+
resolvedNodeIds.add(node.id);
|
|
258
|
+
}
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
if (seed.kind === "tag") {
|
|
262
|
+
for (const node of this.nodesById.values()) {
|
|
263
|
+
if ((node.tags ?? []).map(normalizeText).includes(normalizeText(seed.value)) && nodeMatchesScope(node, request.scopePaths) && nodeMatchesWhere(node, request.where)) {
|
|
264
|
+
matches.push({ node, score: 85, reason: "seed-tag" });
|
|
265
|
+
resolvedNodeIds.add(node.id);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
if (seed.kind === "type") {
|
|
271
|
+
for (const node of this.nodesById.values()) {
|
|
272
|
+
if (nodeMetadataType(node) === normalizeText(seed.value) && nodeMatchesScope(node, request.scopePaths) && nodeMatchesWhere(node, request.where)) {
|
|
273
|
+
matches.push({ node, score: 85, reason: "seed-type" });
|
|
274
|
+
resolvedNodeIds.add(node.id);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
const scoped = seed.scope === "files" ? this.searchFiles(seed.value, request.options) : seed.scope === "entities" ? this.searchEntities(seed.value, request.options) : seed.scope === "sections" ? this.searchSections(seed.value, request.options) : this.filterSearchResults(this.rankingIndex.search({ query: seed.value, scope: "all", options: request.options, request }), request.options);
|
|
280
|
+
for (const match of scoped.filter((entry) => nodeMatchesScope(entry.node, request.scopePaths) && nodeMatchesWhere(entry.node, request.where)).slice(0, request.options?.limit ?? 10)) {
|
|
281
|
+
matches.push({ ...match, reason: `${match.reason}:seed-query` });
|
|
282
|
+
resolvedNodeIds.add(match.node.id);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return { seeds, matches: dedupeResults(matches), resolvedNodeIds: [...resolvedNodeIds] };
|
|
286
|
+
}
|
|
287
|
+
queryGraph(request) {
|
|
288
|
+
this.onQuery?.("queryGraph", request.query ?? request.seedIds?.join(",") ?? "");
|
|
289
|
+
const resolved = this.resolveSeeds(request);
|
|
290
|
+
const allowedEdgeTypes = request.relations?.length ? request.relations.map((relation) => RELATION_TO_EDGE_TYPE[relation]) : request.options?.edgeTypes;
|
|
291
|
+
const eligibleNodeIds = this.eligibleNodeIds(request);
|
|
292
|
+
for (const seedId of resolved.resolvedNodeIds) {
|
|
293
|
+
eligibleNodeIds.add(seedId);
|
|
294
|
+
}
|
|
295
|
+
const ranked = this.rankingIndex.rankQuery({
|
|
296
|
+
request,
|
|
297
|
+
seedIds: resolved.resolvedNodeIds.filter((id) => eligibleNodeIds.has(id)),
|
|
298
|
+
seedMatches: resolved.matches.filter((match) => eligibleNodeIds.has(match.node.id)),
|
|
299
|
+
allowedNodeIds: [...eligibleNodeIds],
|
|
300
|
+
allowedEdgeTypes
|
|
301
|
+
});
|
|
302
|
+
const nodes = ranked.nodes.map((entry) => ({
|
|
303
|
+
node: this.nodesById.get(entry.nodeId),
|
|
304
|
+
score: entry.score,
|
|
305
|
+
depth: entry.depth,
|
|
306
|
+
reasons: entry.reasons,
|
|
307
|
+
diagnostics: entry.diagnostics
|
|
308
|
+
})).filter((entry) => Boolean(entry.node) && nodeMatchesScope(entry.node, request.scopePaths) && nodeMatchesWhere(entry.node, request.where));
|
|
309
|
+
const includedNodeIds = new Set(nodes.map((entry) => entry.node.id));
|
|
310
|
+
const edges = ranked.edgeIds.map((id) => this.edgesById.get(id)).filter((edge) => Boolean(edge)).filter((edge) => includedNodeIds.has(edge.sourceId) || includedNodeIds.has(edge.targetId));
|
|
311
|
+
return {
|
|
312
|
+
seedIds: resolved.resolvedNodeIds,
|
|
313
|
+
nodes,
|
|
314
|
+
edges,
|
|
315
|
+
providerId: ranked.providerId,
|
|
316
|
+
diagnostics: ranked.diagnostics
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
buildContextPack(request) {
|
|
320
|
+
this.onQuery?.("buildContextPack", request.query ?? request.seedIds?.join(",") ?? "");
|
|
321
|
+
const graphResult = this.queryGraph(request);
|
|
322
|
+
const maxTokens = request.budget?.maxTokens ?? 1800;
|
|
323
|
+
const includeMode = request.budget?.includeMode ?? (request.view === "list" || request.view === "map" ? "mixed" : request.view === "full" ? "mixed" : "mixed");
|
|
324
|
+
const view = request.view ?? "brief";
|
|
325
|
+
const includedNodeIds = /* @__PURE__ */ new Set();
|
|
326
|
+
const nodes = [];
|
|
327
|
+
let totalTokenEstimate = 0;
|
|
328
|
+
for (const entry of graphResult.nodes) {
|
|
329
|
+
if (includeMode === "files" && entry.node.nodeType !== "File") continue;
|
|
330
|
+
if (includeMode === "sections" && entry.node.nodeType !== "Section") continue;
|
|
331
|
+
if (includeMode === "mixed" && !["File", "Section"].includes(entry.node.nodeType)) continue;
|
|
332
|
+
if (includeMode === "mixed" && entry.node.nodeType === "File" && entry.node.fileId && includedNodeIds.has(entry.node.fileId)) continue;
|
|
333
|
+
const text = excerptForView(entry.node, view);
|
|
334
|
+
if (!text.trim() && !["list", "map"].includes(view)) continue;
|
|
335
|
+
const estimate = tokenEstimate(text);
|
|
336
|
+
if (totalTokenEstimate + estimate > maxTokens && nodes.length > 0) continue;
|
|
337
|
+
totalTokenEstimate += estimate;
|
|
338
|
+
includedNodeIds.add(entry.node.id);
|
|
339
|
+
nodes.push({
|
|
340
|
+
node: entry.node,
|
|
341
|
+
score: entry.score,
|
|
342
|
+
depth: entry.depth,
|
|
343
|
+
text,
|
|
344
|
+
tokenEstimate: estimate,
|
|
345
|
+
reasons: entry.reasons,
|
|
346
|
+
provenance: {
|
|
347
|
+
seedIds: graphResult.seedIds,
|
|
348
|
+
viaEdgeTypes: graphResult.edges.filter((edge) => edge.sourceId === entry.node.id || edge.targetId === entry.node.id).map((edge) => edge.type)
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
return {
|
|
353
|
+
seedIds: graphResult.seedIds,
|
|
354
|
+
totalTokenEstimate,
|
|
355
|
+
includedNodeIds: [...includedNodeIds],
|
|
356
|
+
nodes,
|
|
357
|
+
edges: graphResult.edges
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
parseDsl(source) {
|
|
361
|
+
return parseGraphDsl(source);
|
|
362
|
+
}
|
|
363
|
+
explainReferenceChain(fromId, toId) {
|
|
364
|
+
this.onQuery?.("explainReferenceChain", `${fromId}:${toId}`);
|
|
365
|
+
const queue = [{ id: fromId, path: [fromId], edges: [] }];
|
|
366
|
+
const visited = /* @__PURE__ */ new Set([fromId]);
|
|
367
|
+
while (queue.length > 0) {
|
|
368
|
+
const current = queue.shift();
|
|
369
|
+
if (current.id === toId) {
|
|
370
|
+
return {
|
|
371
|
+
fromId,
|
|
372
|
+
toId,
|
|
373
|
+
nodes: current.path.map((id) => this.nodesById.get(id)).filter((node) => Boolean(node)),
|
|
374
|
+
edges: current.edges.map((id) => this.edgesById.get(id)).filter((edge) => Boolean(edge))
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
for (const edge of this.outgoing.get(current.id) ?? []) {
|
|
378
|
+
if (visited.has(edge.targetId)) continue;
|
|
379
|
+
visited.add(edge.targetId);
|
|
380
|
+
queue.push({
|
|
381
|
+
id: edge.targetId,
|
|
382
|
+
path: [...current.path, edge.targetId],
|
|
383
|
+
edges: [...current.edges, edge.id]
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
resolveReference(reference, options) {
|
|
390
|
+
this.onQuery?.("resolveReference", reference);
|
|
391
|
+
const trimmed = reference.trim();
|
|
392
|
+
const [pathPart, hashPart = ""] = trimmed.split("#");
|
|
393
|
+
const directNode = this.nodesById.get(trimmed);
|
|
394
|
+
if (directNode && matchesNodeOptions(directNode, { models: options?.models })) {
|
|
395
|
+
return directNode;
|
|
396
|
+
}
|
|
397
|
+
for (const node of this.nodesById.values()) {
|
|
398
|
+
if (options?.models?.length && (!node.sourceModel || !options.models.includes(node.sourceModel))) continue;
|
|
399
|
+
if (node.nodeType !== "File" && node.nodeType !== "Section" && node.nodeType !== "Entity" && !node.entityType) continue;
|
|
400
|
+
if (node.id === `entity:${pathPart}` || node.slug === pathPart || `${node.sourceModel}/${node.slug}` === pathPart) {
|
|
401
|
+
return node;
|
|
402
|
+
}
|
|
403
|
+
if (node.path && (stripExt(node.path) === stripExt(pathPart) || stripExt(path.relative(process.cwd(), node.path)) === stripExt(pathPart))) {
|
|
404
|
+
if (hashPart) {
|
|
405
|
+
const section = (this.fileSections.get(node.id) ?? []).find((entry) => entry.headingPath === hashPart || entry.slug?.endsWith(`#${hashPart}`));
|
|
406
|
+
if (section) return section;
|
|
407
|
+
}
|
|
408
|
+
return node;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
if (options?.fromNodeId) {
|
|
412
|
+
const source = this.nodesById.get(options.fromNodeId);
|
|
413
|
+
if (source?.path) {
|
|
414
|
+
const absolute = path.resolve(path.dirname(source.path), pathPart);
|
|
415
|
+
for (const node of this.nodesById.values()) {
|
|
416
|
+
if (node.nodeType !== "File" || !node.path) continue;
|
|
417
|
+
if (stripExt(node.path) === stripExt(absolute)) {
|
|
418
|
+
if (hashPart) {
|
|
419
|
+
const section = (this.fileSections.get(node.id) ?? []).find((entry) => entry.headingPath === hashPart || entry.heading === hashPart);
|
|
420
|
+
if (section) return section;
|
|
421
|
+
}
|
|
422
|
+
return node;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
serializeIndexes() {
|
|
430
|
+
return {
|
|
431
|
+
providerId: this.rankingProvider.id,
|
|
432
|
+
ranking: this.rankingIndex.serialize?.() ?? null
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
function statefulIds(nodes, predicate) {
|
|
437
|
+
const ids = /* @__PURE__ */ new Set();
|
|
438
|
+
for (const node of nodes) {
|
|
439
|
+
if (predicate(node)) {
|
|
440
|
+
ids.add(node.id);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
return ids;
|
|
444
|
+
}
|
|
445
|
+
export {
|
|
446
|
+
GraphQueryEngine
|
|
447
|
+
};
|