notex-companion 0.1.0 → 0.2.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/dist/cli.d.ts +3 -3
- package/dist/cli.js +31 -4
- package/dist/client.d.ts +2 -0
- package/dist/client.js +371 -0
- package/dist/context.d.ts +1 -1
- package/dist/cors.d.ts +3 -3
- package/dist/footer.d.ts +1 -1
- package/dist/graph.d.ts +5 -5
- package/dist/http.d.ts +2 -2
- package/dist/index.js +32 -4
- package/dist/net.d.ts +1 -1
- package/dist/ops.d.ts +28 -9
- package/dist/scoring.d.ts +3 -3
- package/dist/serve.d.ts +3 -3
- package/dist/traversal.d.ts +6 -6
- package/package.json +6 -2
package/dist/cli.d.ts
CHANGED
|
@@ -3,8 +3,8 @@ export declare class CliUsageError extends Error {
|
|
|
3
3
|
}
|
|
4
4
|
export type ServeArgs = {
|
|
5
5
|
port: number | undefined;
|
|
6
|
-
origins: string
|
|
6
|
+
origins: Array<string>;
|
|
7
7
|
rotateToken: boolean;
|
|
8
8
|
};
|
|
9
|
-
export declare function parseServeArgs(args: string
|
|
10
|
-
export declare function main(argv?: string
|
|
9
|
+
export declare function parseServeArgs(args: Array<string>): ServeArgs;
|
|
10
|
+
export declare function main(argv?: Array<string>): void;
|
package/dist/cli.js
CHANGED
|
@@ -142,7 +142,7 @@ function loadGraph(checkoutPath) {
|
|
|
142
142
|
const scoreIndex = doc.nodes.map((n) => ({
|
|
143
143
|
id: n.id,
|
|
144
144
|
labelTokens: new Set([...shatter(n.label), ...shatter(n.norm_label ?? "")]),
|
|
145
|
-
pathTokens: new Set(shatter(n.source_file
|
|
145
|
+
pathTokens: new Set(shatter(n.source_file)),
|
|
146
146
|
labelLower: String(n.label).toLowerCase()
|
|
147
147
|
}));
|
|
148
148
|
const project = (n) => ({
|
|
@@ -286,7 +286,7 @@ function traverse(adjacency, allEdges, seedIds, depth, maxNodes) {
|
|
|
286
286
|
for (const id of frontier) {
|
|
287
287
|
for (const { other, edge } of adjacency.get(id) ?? []) {
|
|
288
288
|
if (!kept.has(other))
|
|
289
|
-
candidates.push({ other, weight: edge.weight
|
|
289
|
+
candidates.push({ other, weight: edge.weight });
|
|
290
290
|
}
|
|
291
291
|
}
|
|
292
292
|
candidates.sort((a, b) => b.weight - a.weight);
|
|
@@ -324,8 +324,8 @@ function traverse(adjacency, allEdges, seedIds, depth, maxNodes) {
|
|
|
324
324
|
}
|
|
325
325
|
|
|
326
326
|
// src/ops.ts
|
|
327
|
-
var API_VERSION = "0.
|
|
328
|
-
var CAPABILITIES = ["search", "query", "path", "node"];
|
|
327
|
+
var API_VERSION = "0.2.0";
|
|
328
|
+
var CAPABILITIES = ["search", "query", "path", "node", "browse"];
|
|
329
329
|
var MAX_NODES_CEILING = 1000;
|
|
330
330
|
var MAX_DEPTH_CEILING = 3;
|
|
331
331
|
var DEFAULT_DEPTH = 1;
|
|
@@ -333,6 +333,8 @@ var DEFAULT_MAX_NODES = 60;
|
|
|
333
333
|
var DEFAULT_SEED_COUNT = 5;
|
|
334
334
|
var DEFAULT_SEARCH_LIMIT = 20;
|
|
335
335
|
var MAX_SEARCH_LIMIT = 100;
|
|
336
|
+
var DEFAULT_BROWSE_GROUP_LIMIT = 8;
|
|
337
|
+
var MAX_BROWSE_GROUP_LIMIT = 50;
|
|
336
338
|
function status(index) {
|
|
337
339
|
return {
|
|
338
340
|
graph: index.stamp,
|
|
@@ -349,6 +351,21 @@ function search(index, req) {
|
|
|
349
351
|
results: scored.map((s) => ({ ...index.project(index.nodesById.get(s.id)), score: s.score }))
|
|
350
352
|
};
|
|
351
353
|
}
|
|
354
|
+
function browse(index, req) {
|
|
355
|
+
const limit = Math.max(0, Math.min(req.limit ?? DEFAULT_BROWSE_GROUP_LIMIT, MAX_BROWSE_GROUP_LIMIT));
|
|
356
|
+
const byType = new Map;
|
|
357
|
+
for (const raw of index.nodesById.values()) {
|
|
358
|
+
const projected = index.project(raw);
|
|
359
|
+
if (!byType.has(projected.fileType))
|
|
360
|
+
byType.set(projected.fileType, []);
|
|
361
|
+
byType.get(projected.fileType).push(projected);
|
|
362
|
+
}
|
|
363
|
+
const groups = [...byType.entries()].map(([fileType, nodes]) => {
|
|
364
|
+
const sorted = [...nodes].sort((a, b) => a.label.localeCompare(b.label));
|
|
365
|
+
return { fileType, total: sorted.length, nodes: sorted.slice(0, limit) };
|
|
366
|
+
}).sort((a, b) => b.total - a.total || a.fileType.localeCompare(b.fileType));
|
|
367
|
+
return { graph: index.stamp, groups };
|
|
368
|
+
}
|
|
352
369
|
function query(index, req) {
|
|
353
370
|
const depth = Math.min(req.depth ?? DEFAULT_DEPTH, MAX_DEPTH_CEILING);
|
|
354
371
|
const maxNodes = Math.min(req.maxNodes ?? DEFAULT_MAX_NODES, MAX_NODES_CEILING);
|
|
@@ -517,6 +534,8 @@ async function dispatch(req, url, opts) {
|
|
|
517
534
|
if (method === "GET" && pathname.startsWith("/v1/node/")) {
|
|
518
535
|
return node(index, { id: decodeNodeId(pathname.slice("/v1/node/".length)) });
|
|
519
536
|
}
|
|
537
|
+
if (method === "GET" && pathname === "/v1/browse")
|
|
538
|
+
return browse(index, parseBrowseRequest(url.searchParams));
|
|
520
539
|
throw new OpError("not_found", `No such route: ${method} ${pathname}`);
|
|
521
540
|
}
|
|
522
541
|
function decodeNodeId(raw) {
|
|
@@ -610,6 +629,14 @@ function parseQueryRequest(body) {
|
|
|
610
629
|
function parsePathRequest(body) {
|
|
611
630
|
return { from: requireString(body, "from"), to: requireString(body, "to"), maxDepth: optionalCount(body, "maxDepth") };
|
|
612
631
|
}
|
|
632
|
+
function parseBrowseRequest(searchParams) {
|
|
633
|
+
const raw = searchParams.get("limit");
|
|
634
|
+
if (raw === null)
|
|
635
|
+
return {};
|
|
636
|
+
if (!/^\d+$/.test(raw))
|
|
637
|
+
throw new OpError("invalid_request", `"limit" must be a non-negative integer`);
|
|
638
|
+
return { limit: Number(raw) };
|
|
639
|
+
}
|
|
613
640
|
function errorBody(err) {
|
|
614
641
|
const detail = err.detail instanceof Error ? { name: err.detail.name, message: err.detail.message } : err.detail;
|
|
615
642
|
return { error: { code: err.code, message: err.message, ...detail !== undefined ? { detail } : {} } };
|
package/dist/client.d.ts
ADDED
package/dist/client.js
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
var ERROR_CODES = {
|
|
3
|
+
unauthorized: "unauthorized",
|
|
4
|
+
notFound: "not_found",
|
|
5
|
+
graphUnreadable: "graph_unreadable",
|
|
6
|
+
invalidRequest: "invalid_request",
|
|
7
|
+
graphLoading: "graph_loading"
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
class OpError extends Error {
|
|
11
|
+
code;
|
|
12
|
+
detail;
|
|
13
|
+
constructor(code, message, detail) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = "OpError";
|
|
16
|
+
this.code = code;
|
|
17
|
+
this.detail = detail;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
// src/context.ts
|
|
21
|
+
function buildContext(question, stamp, nodes, edges, opts = {}) {
|
|
22
|
+
const lines = [];
|
|
23
|
+
const builtDate = stamp.builtAt.slice(0, 10);
|
|
24
|
+
lines.push(`# ${question}`, "");
|
|
25
|
+
lines.push(`built ${builtDate} · ${nodes.length} nodes`, "");
|
|
26
|
+
const groups = new Map;
|
|
27
|
+
for (const n of nodes) {
|
|
28
|
+
const key = n.community?.name ? n.community.name : "Ungrouped";
|
|
29
|
+
if (!groups.has(key))
|
|
30
|
+
groups.set(key, []);
|
|
31
|
+
groups.get(key).push(n);
|
|
32
|
+
}
|
|
33
|
+
const groupNames = [...groups.keys()].sort((a, b) => a === "Ungrouped" ? 1 : b === "Ungrouped" ? -1 : a.localeCompare(b));
|
|
34
|
+
for (const name of groupNames) {
|
|
35
|
+
lines.push(`## ${name}`);
|
|
36
|
+
const members = [...groups.get(name)].sort((a, b) => a.label.localeCompare(b.label) || a.id.localeCompare(b.id));
|
|
37
|
+
for (const n of members)
|
|
38
|
+
lines.push(`- ${n.label} — ${n.sourceFile}:${n.sourceLocation}`);
|
|
39
|
+
lines.push("");
|
|
40
|
+
}
|
|
41
|
+
if (edges.length > 0) {
|
|
42
|
+
const labelById = new Map(nodes.map((n) => [n.id, n.label]));
|
|
43
|
+
lines.push("## Relations");
|
|
44
|
+
for (const e of edges) {
|
|
45
|
+
const tag = e.confidence === "EXTRACTED" ? "" : ` [${e.confidence}]`;
|
|
46
|
+
lines.push(`- ${labelById.get(e.source) ?? e.source} —${e.relation}→ ${labelById.get(e.target) ?? e.target}${tag}`);
|
|
47
|
+
}
|
|
48
|
+
lines.push("");
|
|
49
|
+
}
|
|
50
|
+
if (opts.truncated) {
|
|
51
|
+
lines.push(`> Retrieval was truncated (${opts.truncated.reason}); ${opts.truncated.omittedCount} related nodes were omitted.`, "");
|
|
52
|
+
}
|
|
53
|
+
if (opts.degraded) {
|
|
54
|
+
lines.push("> Matched literally — no vocabulary expansion was applied to the question.", "");
|
|
55
|
+
}
|
|
56
|
+
while (lines.length > 0 && lines[lines.length - 1] === "")
|
|
57
|
+
lines.pop();
|
|
58
|
+
return lines.join(`
|
|
59
|
+
`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/footer.ts
|
|
63
|
+
function isoDate(date) {
|
|
64
|
+
return date.toISOString().slice(0, 10);
|
|
65
|
+
}
|
|
66
|
+
function buildFooter(stamp, sources, opts = {}) {
|
|
67
|
+
const draftDate = opts.draftDate ?? isoDate(new Date);
|
|
68
|
+
const builtDate = stamp.builtAt.slice(0, 10);
|
|
69
|
+
const graphLine = stamp.headSha ? `Graph built ${builtDate} (${stamp.graphHash}) at commit ${stamp.headSha.slice(0, 7)}.` : `Graph built ${builtDate} (${stamp.graphHash}).`;
|
|
70
|
+
const noteClauses = [];
|
|
71
|
+
if (opts.truncated) {
|
|
72
|
+
noteClauses.push(`Retrieval was truncated (${opts.truncated.reason}); some related code may be missing.`);
|
|
73
|
+
}
|
|
74
|
+
if (opts.degraded) {
|
|
75
|
+
noteClauses.push("Retrieval matched literally; vocabulary was not expanded.");
|
|
76
|
+
}
|
|
77
|
+
const sortedSources = [...new Set(sources.map((s) => `${s.file}:${s.location}`))].sort();
|
|
78
|
+
const lines = [
|
|
79
|
+
"---",
|
|
80
|
+
`Drafted from the code graph on ${draftDate}.`,
|
|
81
|
+
graphLine,
|
|
82
|
+
...noteClauses.length > 0 ? [noteClauses.join(" ")] : [],
|
|
83
|
+
"Sources:",
|
|
84
|
+
...sortedSources.map((s) => `- ${s}`)
|
|
85
|
+
];
|
|
86
|
+
return `
|
|
87
|
+
` + lines.join(`
|
|
88
|
+
`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/scoring.ts
|
|
92
|
+
var STOPWORDS = new Set(("a an the and or but if of to in on for with from by at as is are was were " + "do does did how what where when which who why can could should would we " + "our it its this that these those there here about into over under not no " + "you your i me my be been being have has had will shall may might must " + "get got make made use used using does happen happens work works").split(" "));
|
|
93
|
+
function shatter(text) {
|
|
94
|
+
return String(text).replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^A-Za-z0-9]+/).map((t) => t.toLowerCase()).filter(Boolean);
|
|
95
|
+
}
|
|
96
|
+
function terms(question) {
|
|
97
|
+
return [...new Set(shatter(question).filter((t) => t.length >= 3 && !STOPWORDS.has(t)))];
|
|
98
|
+
}
|
|
99
|
+
function scoreNodes(index, queryTerms) {
|
|
100
|
+
const scored = [];
|
|
101
|
+
for (const entry of index) {
|
|
102
|
+
let score = 0;
|
|
103
|
+
let exact = false;
|
|
104
|
+
for (const term of queryTerms) {
|
|
105
|
+
let best = 0;
|
|
106
|
+
if (entry.labelLower === term) {
|
|
107
|
+
best = 6;
|
|
108
|
+
exact = true;
|
|
109
|
+
} else if (entry.labelTokens.has(term)) {
|
|
110
|
+
best = 3;
|
|
111
|
+
exact = true;
|
|
112
|
+
} else {
|
|
113
|
+
for (const tok of entry.labelTokens) {
|
|
114
|
+
if (tok.length >= 4 && (tok.startsWith(term) || term.startsWith(tok))) {
|
|
115
|
+
best = Math.max(best, 1.5);
|
|
116
|
+
} else if (term.length >= 4 && tok.includes(term)) {
|
|
117
|
+
best = Math.max(best, 1);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (best === 0) {
|
|
122
|
+
if (entry.pathTokens.has(term)) {
|
|
123
|
+
best = 1.2;
|
|
124
|
+
} else {
|
|
125
|
+
for (const tok of entry.pathTokens) {
|
|
126
|
+
if (tok.length >= 4 && term.length >= 4 && tok.startsWith(term))
|
|
127
|
+
best = 0.6;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
score += best;
|
|
132
|
+
}
|
|
133
|
+
if (score > 0)
|
|
134
|
+
scored.push({ id: entry.id, score: Math.round(score * 100) / 100, exact });
|
|
135
|
+
}
|
|
136
|
+
return scored.sort((a, b) => b.score - a.score || a.id.localeCompare(b.id));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// src/traversal.ts
|
|
140
|
+
function traverse(adjacency, allEdges, seedIds, depth, maxNodes) {
|
|
141
|
+
const kept = new Set;
|
|
142
|
+
const overflow = new Set;
|
|
143
|
+
let frontier = [];
|
|
144
|
+
for (const s of seedIds) {
|
|
145
|
+
if (kept.size < maxNodes) {
|
|
146
|
+
kept.add(s);
|
|
147
|
+
frontier.push(s);
|
|
148
|
+
} else {
|
|
149
|
+
overflow.add(s);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
let d = 0;
|
|
153
|
+
for (;d < depth; d++) {
|
|
154
|
+
const candidates = [];
|
|
155
|
+
for (const id of frontier) {
|
|
156
|
+
for (const { other, edge } of adjacency.get(id) ?? []) {
|
|
157
|
+
if (!kept.has(other))
|
|
158
|
+
candidates.push({ other, weight: edge.weight });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
candidates.sort((a, b) => b.weight - a.weight);
|
|
162
|
+
const next = [];
|
|
163
|
+
for (const c of candidates) {
|
|
164
|
+
if (kept.has(c.other))
|
|
165
|
+
continue;
|
|
166
|
+
if (kept.size >= maxNodes) {
|
|
167
|
+
overflow.add(c.other);
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
kept.add(c.other);
|
|
171
|
+
next.push(c.other);
|
|
172
|
+
}
|
|
173
|
+
frontier = next;
|
|
174
|
+
if (frontier.length === 0)
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
let truncated = null;
|
|
178
|
+
if (overflow.size > 0) {
|
|
179
|
+
truncated = { reason: "maxNodes", omittedCount: overflow.size };
|
|
180
|
+
} else if (d === depth && frontier.length > 0) {
|
|
181
|
+
const beyond = new Set;
|
|
182
|
+
for (const id of frontier) {
|
|
183
|
+
for (const { other } of adjacency.get(id) ?? []) {
|
|
184
|
+
if (!kept.has(other))
|
|
185
|
+
beyond.add(other);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (beyond.size > 0)
|
|
189
|
+
truncated = { reason: "depth", omittedCount: beyond.size };
|
|
190
|
+
}
|
|
191
|
+
const edges = allEdges.filter((e) => kept.has(e.source) && kept.has(e.target));
|
|
192
|
+
return { nodeIds: [...kept], edges, truncated };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// src/ops.ts
|
|
196
|
+
var API_VERSION = "0.2.0";
|
|
197
|
+
var CAPABILITIES = ["search", "query", "path", "node", "browse"];
|
|
198
|
+
var MAX_NODES_CEILING = 1000;
|
|
199
|
+
var MAX_DEPTH_CEILING = 3;
|
|
200
|
+
var DEFAULT_DEPTH = 1;
|
|
201
|
+
var DEFAULT_MAX_NODES = 60;
|
|
202
|
+
var DEFAULT_SEED_COUNT = 5;
|
|
203
|
+
var DEFAULT_SEARCH_LIMIT = 20;
|
|
204
|
+
var MAX_SEARCH_LIMIT = 100;
|
|
205
|
+
var DEFAULT_BROWSE_GROUP_LIMIT = 8;
|
|
206
|
+
var MAX_BROWSE_GROUP_LIMIT = 50;
|
|
207
|
+
function status(index) {
|
|
208
|
+
return {
|
|
209
|
+
graph: index.stamp,
|
|
210
|
+
apiVersion: API_VERSION,
|
|
211
|
+
capabilities: [...CAPABILITIES],
|
|
212
|
+
limits: { maxNodes: MAX_NODES_CEILING, maxDepth: MAX_DEPTH_CEILING }
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
function search(index, req) {
|
|
216
|
+
const limit = Math.max(0, Math.min(req.limit ?? DEFAULT_SEARCH_LIMIT, MAX_SEARCH_LIMIT));
|
|
217
|
+
const scored = scoreNodes(index.scoreIndex, terms(req.q)).slice(0, limit);
|
|
218
|
+
return {
|
|
219
|
+
graph: index.stamp,
|
|
220
|
+
results: scored.map((s) => ({ ...index.project(index.nodesById.get(s.id)), score: s.score }))
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
function browse(index, req) {
|
|
224
|
+
const limit = Math.max(0, Math.min(req.limit ?? DEFAULT_BROWSE_GROUP_LIMIT, MAX_BROWSE_GROUP_LIMIT));
|
|
225
|
+
const byType = new Map;
|
|
226
|
+
for (const raw of index.nodesById.values()) {
|
|
227
|
+
const projected = index.project(raw);
|
|
228
|
+
if (!byType.has(projected.fileType))
|
|
229
|
+
byType.set(projected.fileType, []);
|
|
230
|
+
byType.get(projected.fileType).push(projected);
|
|
231
|
+
}
|
|
232
|
+
const groups = [...byType.entries()].map(([fileType, nodes]) => {
|
|
233
|
+
const sorted = [...nodes].sort((a, b) => a.label.localeCompare(b.label));
|
|
234
|
+
return { fileType, total: sorted.length, nodes: sorted.slice(0, limit) };
|
|
235
|
+
}).sort((a, b) => b.total - a.total || a.fileType.localeCompare(b.fileType));
|
|
236
|
+
return { graph: index.stamp, groups };
|
|
237
|
+
}
|
|
238
|
+
function query(index, req) {
|
|
239
|
+
const depth = Math.min(req.depth ?? DEFAULT_DEPTH, MAX_DEPTH_CEILING);
|
|
240
|
+
const maxNodes = Math.min(req.maxNodes ?? DEFAULT_MAX_NODES, MAX_NODES_CEILING);
|
|
241
|
+
const seedCount = Math.max(0, req.seeds ?? DEFAULT_SEED_COUNT);
|
|
242
|
+
const include = req.include ?? ["subgraph"];
|
|
243
|
+
const queryTerms = req.terms?.length ? req.terms.map((t) => t.toLowerCase()) : terms(req.question);
|
|
244
|
+
const degraded = req.terms?.length ? undefined : { expansion: "none" };
|
|
245
|
+
const scored = scoreNodes(index.scoreIndex, queryTerms);
|
|
246
|
+
const scoredById = new Map(scored.map((s) => [s.id, s]));
|
|
247
|
+
const seedIds = scored.slice(0, seedCount).map((s) => s.id);
|
|
248
|
+
const topScore = scored[0]?.score ?? 0;
|
|
249
|
+
const clearsFloor = seedIds.some((id) => scoredById.get(id)?.exact);
|
|
250
|
+
const lowConfidence = clearsFloor ? undefined : { topScore };
|
|
251
|
+
if (seedIds.length === 0) {
|
|
252
|
+
const empty = {
|
|
253
|
+
graph: index.stamp,
|
|
254
|
+
...degraded ? { degraded } : {},
|
|
255
|
+
...lowConfidence ? { lowConfidence } : {},
|
|
256
|
+
subgraph: { nodes: [], edges: [], seeds: [] }
|
|
257
|
+
};
|
|
258
|
+
if (include.includes("context")) {
|
|
259
|
+
empty.context = { markdown: buildContext(req.question, index.stamp, [], [], { degraded }), sources: [] };
|
|
260
|
+
}
|
|
261
|
+
if (include.includes("footer")) {
|
|
262
|
+
empty.footer = buildFooter(index.stamp, [], { degraded });
|
|
263
|
+
}
|
|
264
|
+
return empty;
|
|
265
|
+
}
|
|
266
|
+
const { nodeIds, edges: rawEdges, truncated } = traverse(index.adjacency, index.edges, seedIds, depth, maxNodes);
|
|
267
|
+
const nodes = nodeIds.map((id) => index.project(index.nodesById.get(id)));
|
|
268
|
+
const edges = rawEdges.map((e) => index.projectEdge(e));
|
|
269
|
+
const response = {
|
|
270
|
+
graph: index.stamp,
|
|
271
|
+
...degraded ? { degraded } : {},
|
|
272
|
+
...truncated ? { truncated } : {},
|
|
273
|
+
...lowConfidence ? { lowConfidence } : {},
|
|
274
|
+
subgraph: { nodes, edges, seeds: seedIds }
|
|
275
|
+
};
|
|
276
|
+
if (include.includes("context")) {
|
|
277
|
+
response.context = {
|
|
278
|
+
markdown: buildContext(req.question, index.stamp, nodes, edges, { truncated: truncated ?? undefined, degraded }),
|
|
279
|
+
sources: sourcesFrom(nodes)
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
if (include.includes("footer")) {
|
|
283
|
+
response.footer = buildFooter(index.stamp, sourcesFrom(nodes), { truncated: truncated ?? undefined, degraded });
|
|
284
|
+
}
|
|
285
|
+
return response;
|
|
286
|
+
}
|
|
287
|
+
function sourcesFrom(nodes) {
|
|
288
|
+
const keys = [...new Set(nodes.map((n) => `${n.sourceFile}:${n.sourceLocation}`))].sort();
|
|
289
|
+
return keys.map((k) => {
|
|
290
|
+
const i = k.lastIndexOf(":");
|
|
291
|
+
return { file: k.slice(0, i), location: k.slice(i + 1) };
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
function path(index, req) {
|
|
295
|
+
const { from, to, maxDepth } = req;
|
|
296
|
+
if (!index.nodesById.has(from))
|
|
297
|
+
throw new OpError("not_found", `Unknown node id: ${from}`);
|
|
298
|
+
if (!index.nodesById.has(to))
|
|
299
|
+
throw new OpError("not_found", `Unknown node id: ${to}`);
|
|
300
|
+
if (from === to) {
|
|
301
|
+
return {
|
|
302
|
+
graph: index.stamp,
|
|
303
|
+
found: true,
|
|
304
|
+
nodes: [index.project(index.nodesById.get(from))],
|
|
305
|
+
edges: []
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
const cameFrom = new Map;
|
|
309
|
+
const depthOf = new Map([[from, 0]]);
|
|
310
|
+
const queue = [from];
|
|
311
|
+
let qi = 0;
|
|
312
|
+
let found = false;
|
|
313
|
+
outer:
|
|
314
|
+
while (qi < queue.length) {
|
|
315
|
+
const id = queue[qi++];
|
|
316
|
+
const d = depthOf.get(id);
|
|
317
|
+
if (maxDepth !== undefined && d >= maxDepth)
|
|
318
|
+
continue;
|
|
319
|
+
for (const { other, edge } of index.adjacency.get(id) ?? []) {
|
|
320
|
+
if (depthOf.has(other))
|
|
321
|
+
continue;
|
|
322
|
+
depthOf.set(other, d + 1);
|
|
323
|
+
cameFrom.set(other, { prev: id, edge });
|
|
324
|
+
if (other === to) {
|
|
325
|
+
found = true;
|
|
326
|
+
break outer;
|
|
327
|
+
}
|
|
328
|
+
queue.push(other);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
if (!found)
|
|
332
|
+
return { graph: index.stamp, found: false, nodes: [], edges: [] };
|
|
333
|
+
const nodeIds = [to];
|
|
334
|
+
const rawEdges = [];
|
|
335
|
+
let cur = to;
|
|
336
|
+
while (cur !== from) {
|
|
337
|
+
const step = cameFrom.get(cur);
|
|
338
|
+
rawEdges.push(step.edge);
|
|
339
|
+
cur = step.prev;
|
|
340
|
+
nodeIds.push(cur);
|
|
341
|
+
}
|
|
342
|
+
nodeIds.reverse();
|
|
343
|
+
rawEdges.reverse();
|
|
344
|
+
return {
|
|
345
|
+
graph: index.stamp,
|
|
346
|
+
found: true,
|
|
347
|
+
nodes: nodeIds.map((id) => index.project(index.nodesById.get(id))),
|
|
348
|
+
edges: rawEdges.map((e) => index.projectEdge(e))
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
function node(index, req) {
|
|
352
|
+
const raw = index.nodesById.get(req.id);
|
|
353
|
+
if (!raw)
|
|
354
|
+
throw new OpError("not_found", `Unknown node id: ${req.id}`);
|
|
355
|
+
const neighbours = (index.adjacency.get(req.id) ?? []).map(({ other, edge }) => ({
|
|
356
|
+
node: index.project(index.nodesById.get(other)),
|
|
357
|
+
edge: index.projectEdge(edge)
|
|
358
|
+
}));
|
|
359
|
+
return { graph: index.stamp, node: index.project(raw), neighbours };
|
|
360
|
+
}
|
|
361
|
+
export {
|
|
362
|
+
status,
|
|
363
|
+
search,
|
|
364
|
+
query,
|
|
365
|
+
path,
|
|
366
|
+
node,
|
|
367
|
+
browse,
|
|
368
|
+
OpError,
|
|
369
|
+
ERROR_CODES,
|
|
370
|
+
API_VERSION
|
|
371
|
+
};
|
package/dist/context.d.ts
CHANGED
|
@@ -3,4 +3,4 @@ export type BuildContextOptions = {
|
|
|
3
3
|
truncated?: Truncated;
|
|
4
4
|
degraded?: Degraded;
|
|
5
5
|
};
|
|
6
|
-
export declare function buildContext(question: string, stamp: GraphStamp, nodes: GraphNode
|
|
6
|
+
export declare function buildContext(question: string, stamp: GraphStamp, nodes: Array<GraphNode>, edges: Array<GraphEdge>, opts?: BuildContextOptions): string;
|
package/dist/cors.d.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* `--origin` is repeatable, defaulting to the production Notex origin (companion-api.md §5);
|
|
3
3
|
* `http://localhost:3000` is added on top of that only outside production.
|
|
4
4
|
*/
|
|
5
|
-
export declare function resolveOrigins(configured: string
|
|
5
|
+
export declare function resolveOrigins(configured: Array<string>, nodeEnv: string | undefined, productionOrigin?: string): Array<string>;
|
|
6
6
|
/** Headers for a real (non-preflight) response. `{}` when the origin isn't an exact allowlist match. */
|
|
7
|
-
export declare function corsHeaders(origins: string
|
|
7
|
+
export declare function corsHeaders(origins: Array<string>, requestOrigin: string | undefined): Record<string, string>;
|
|
8
8
|
/** Full preflight header set (companion-api.md §5). `{}` when the origin isn't an exact allowlist match. */
|
|
9
|
-
export declare function preflightHeaders(origins: string
|
|
9
|
+
export declare function preflightHeaders(origins: Array<string>, requestOrigin: string | undefined): Record<string, string>;
|
package/dist/footer.d.ts
CHANGED
|
@@ -9,4 +9,4 @@ export type BuildFooterOptions = {
|
|
|
9
9
|
truncated?: Truncated;
|
|
10
10
|
degraded?: Degraded;
|
|
11
11
|
};
|
|
12
|
-
export declare function buildFooter(stamp: GraphStamp, sources: FooterSource
|
|
12
|
+
export declare function buildFooter(stamp: GraphStamp, sources: Array<FooterSource>, opts?: BuildFooterOptions): string;
|
package/dist/graph.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { ScoreIndexEntry } from "./scoring.js";
|
|
2
|
+
import type { GraphEdge, GraphNode, GraphStamp } from "./types.js";
|
|
3
3
|
type RawNode = {
|
|
4
4
|
id: string;
|
|
5
5
|
label: string;
|
|
@@ -26,10 +26,10 @@ export type AdjacencyEntry = {
|
|
|
26
26
|
export type GraphIndex = {
|
|
27
27
|
stamp: GraphStamp;
|
|
28
28
|
nodesById: Map<string, RawNode>;
|
|
29
|
-
edges: RawEdge
|
|
29
|
+
edges: Array<RawEdge>;
|
|
30
30
|
/** Undirected — the graph is `"directed": false` (companion-api.md §2.2). */
|
|
31
|
-
adjacency: Map<string, AdjacencyEntry
|
|
32
|
-
scoreIndex: ScoreIndexEntry
|
|
31
|
+
adjacency: Map<string, Array<AdjacencyEntry>>;
|
|
32
|
+
scoreIndex: Array<ScoreIndexEntry>;
|
|
33
33
|
project: (n: RawNode) => GraphNode;
|
|
34
34
|
projectEdge: (e: RawEdge) => GraphEdge;
|
|
35
35
|
};
|
package/dist/http.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { GraphIndex } from "./graph.js";
|
|
2
1
|
import { OpError } from "./types.js";
|
|
2
|
+
import type { GraphIndex } from "./graph.js";
|
|
3
3
|
/**
|
|
4
4
|
* The graph is loaded once into memory (companion-api.md §3); `loading` covers the window
|
|
5
5
|
* before that finishes, `error` covers a permanently unreadable graph.json (never a crash).
|
|
@@ -15,7 +15,7 @@ export type GraphState = {
|
|
|
15
15
|
};
|
|
16
16
|
export type HandlerOptions = {
|
|
17
17
|
token: string;
|
|
18
|
-
origins: string
|
|
18
|
+
origins: Array<string>;
|
|
19
19
|
getGraphState: () => GraphState;
|
|
20
20
|
};
|
|
21
21
|
export declare function createHandler(opts: HandlerOptions): (req: Request) => Promise<Response>;
|
package/dist/index.js
CHANGED
|
@@ -155,7 +155,7 @@ function traverse(adjacency, allEdges, seedIds, depth, maxNodes) {
|
|
|
155
155
|
for (const id of frontier) {
|
|
156
156
|
for (const { other, edge } of adjacency.get(id) ?? []) {
|
|
157
157
|
if (!kept.has(other))
|
|
158
|
-
candidates.push({ other, weight: edge.weight
|
|
158
|
+
candidates.push({ other, weight: edge.weight });
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
161
|
candidates.sort((a, b) => b.weight - a.weight);
|
|
@@ -193,8 +193,8 @@ function traverse(adjacency, allEdges, seedIds, depth, maxNodes) {
|
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
// src/ops.ts
|
|
196
|
-
var API_VERSION = "0.
|
|
197
|
-
var CAPABILITIES = ["search", "query", "path", "node"];
|
|
196
|
+
var API_VERSION = "0.2.0";
|
|
197
|
+
var CAPABILITIES = ["search", "query", "path", "node", "browse"];
|
|
198
198
|
var MAX_NODES_CEILING = 1000;
|
|
199
199
|
var MAX_DEPTH_CEILING = 3;
|
|
200
200
|
var DEFAULT_DEPTH = 1;
|
|
@@ -202,6 +202,8 @@ var DEFAULT_MAX_NODES = 60;
|
|
|
202
202
|
var DEFAULT_SEED_COUNT = 5;
|
|
203
203
|
var DEFAULT_SEARCH_LIMIT = 20;
|
|
204
204
|
var MAX_SEARCH_LIMIT = 100;
|
|
205
|
+
var DEFAULT_BROWSE_GROUP_LIMIT = 8;
|
|
206
|
+
var MAX_BROWSE_GROUP_LIMIT = 50;
|
|
205
207
|
function status(index) {
|
|
206
208
|
return {
|
|
207
209
|
graph: index.stamp,
|
|
@@ -218,6 +220,21 @@ function search(index, req) {
|
|
|
218
220
|
results: scored.map((s) => ({ ...index.project(index.nodesById.get(s.id)), score: s.score }))
|
|
219
221
|
};
|
|
220
222
|
}
|
|
223
|
+
function browse(index, req) {
|
|
224
|
+
const limit = Math.max(0, Math.min(req.limit ?? DEFAULT_BROWSE_GROUP_LIMIT, MAX_BROWSE_GROUP_LIMIT));
|
|
225
|
+
const byType = new Map;
|
|
226
|
+
for (const raw of index.nodesById.values()) {
|
|
227
|
+
const projected = index.project(raw);
|
|
228
|
+
if (!byType.has(projected.fileType))
|
|
229
|
+
byType.set(projected.fileType, []);
|
|
230
|
+
byType.get(projected.fileType).push(projected);
|
|
231
|
+
}
|
|
232
|
+
const groups = [...byType.entries()].map(([fileType, nodes]) => {
|
|
233
|
+
const sorted = [...nodes].sort((a, b) => a.label.localeCompare(b.label));
|
|
234
|
+
return { fileType, total: sorted.length, nodes: sorted.slice(0, limit) };
|
|
235
|
+
}).sort((a, b) => b.total - a.total || a.fileType.localeCompare(b.fileType));
|
|
236
|
+
return { graph: index.stamp, groups };
|
|
237
|
+
}
|
|
221
238
|
function query(index, req) {
|
|
222
239
|
const depth = Math.min(req.depth ?? DEFAULT_DEPTH, MAX_DEPTH_CEILING);
|
|
223
240
|
const maxNodes = Math.min(req.maxNodes ?? DEFAULT_MAX_NODES, MAX_NODES_CEILING);
|
|
@@ -409,7 +426,7 @@ function loadGraph(checkoutPath) {
|
|
|
409
426
|
const scoreIndex = doc.nodes.map((n) => ({
|
|
410
427
|
id: n.id,
|
|
411
428
|
labelTokens: new Set([...shatter(n.label), ...shatter(n.norm_label ?? "")]),
|
|
412
|
-
pathTokens: new Set(shatter(n.source_file
|
|
429
|
+
pathTokens: new Set(shatter(n.source_file)),
|
|
413
430
|
labelLower: String(n.label).toLowerCase()
|
|
414
431
|
}));
|
|
415
432
|
const project = (n) => ({
|
|
@@ -506,6 +523,8 @@ async function dispatch(req, url, opts) {
|
|
|
506
523
|
if (method === "GET" && pathname.startsWith("/v1/node/")) {
|
|
507
524
|
return node(index, { id: decodeNodeId(pathname.slice("/v1/node/".length)) });
|
|
508
525
|
}
|
|
526
|
+
if (method === "GET" && pathname === "/v1/browse")
|
|
527
|
+
return browse(index, parseBrowseRequest(url.searchParams));
|
|
509
528
|
throw new OpError("not_found", `No such route: ${method} ${pathname}`);
|
|
510
529
|
}
|
|
511
530
|
function decodeNodeId(raw) {
|
|
@@ -599,6 +618,14 @@ function parseQueryRequest(body) {
|
|
|
599
618
|
function parsePathRequest(body) {
|
|
600
619
|
return { from: requireString(body, "from"), to: requireString(body, "to"), maxDepth: optionalCount(body, "maxDepth") };
|
|
601
620
|
}
|
|
621
|
+
function parseBrowseRequest(searchParams) {
|
|
622
|
+
const raw = searchParams.get("limit");
|
|
623
|
+
if (raw === null)
|
|
624
|
+
return {};
|
|
625
|
+
if (!/^\d+$/.test(raw))
|
|
626
|
+
throw new OpError("invalid_request", `"limit" must be a non-negative integer`);
|
|
627
|
+
return { limit: Number(raw) };
|
|
628
|
+
}
|
|
602
629
|
function errorBody(err) {
|
|
603
630
|
const detail = err.detail instanceof Error ? { name: err.detail.name, message: err.detail.message } : err.detail;
|
|
604
631
|
return { error: { code: err.code, message: err.message, ...detail !== undefined ? { detail } : {} } };
|
|
@@ -763,6 +790,7 @@ export {
|
|
|
763
790
|
node,
|
|
764
791
|
loadGraph,
|
|
765
792
|
createHandler,
|
|
793
|
+
browse,
|
|
766
794
|
OpError,
|
|
767
795
|
ERROR_CODES,
|
|
768
796
|
API_VERSION
|
package/dist/net.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export type FetchHandler = (req: Request) => Promise<Response>;
|
|
|
2
2
|
export type MinimalServer = {
|
|
3
3
|
readonly hostname: string;
|
|
4
4
|
readonly port: number;
|
|
5
|
-
stop(closeActiveConnections?: boolean)
|
|
5
|
+
stop: (closeActiveConnections?: boolean) => void;
|
|
6
6
|
};
|
|
7
7
|
export type StartServerOptions = {
|
|
8
8
|
hostname: string;
|
package/dist/ops.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import type { GraphEdge, GraphNode, OpResponse } from "./types.js";
|
|
1
2
|
import type { GraphIndex } from "./graph.js";
|
|
2
|
-
import { type GraphEdge, type GraphNode, type OpResponse } from "./types.js";
|
|
3
3
|
/** Also the version /v1/ping reports (companion-api.md §4.1) — the two must never drift apart. */
|
|
4
|
-
export declare const API_VERSION = "0.
|
|
4
|
+
export declare const API_VERSION = "0.2.0";
|
|
5
5
|
export type StatusResult = {
|
|
6
6
|
apiVersion: string;
|
|
7
|
-
capabilities: string
|
|
7
|
+
capabilities: Array<string>;
|
|
8
8
|
limits: {
|
|
9
9
|
maxNodes: number;
|
|
10
10
|
maxDepth: number;
|
|
@@ -21,9 +21,28 @@ export type SearchResult = {
|
|
|
21
21
|
}>;
|
|
22
22
|
};
|
|
23
23
|
export declare function search(index: GraphIndex, req: SearchRequest): OpResponse<SearchResult>;
|
|
24
|
+
export type BrowseRequest = {
|
|
25
|
+
limit?: number;
|
|
26
|
+
};
|
|
27
|
+
export type BrowseGroup = {
|
|
28
|
+
fileType: string;
|
|
29
|
+
total: number;
|
|
30
|
+
nodes: Array<GraphNode>;
|
|
31
|
+
};
|
|
32
|
+
export type BrowseResult = {
|
|
33
|
+
groups: Array<BrowseGroup>;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Lets the UI show what's in the graph before the user types anything (TBR-82) — `search`
|
|
37
|
+
* and `query` both require query terms and return nothing for an empty string. Groups by
|
|
38
|
+
* `fileType`, never `community`: community ids/names reshuffle across rebuilds (TBR-48),
|
|
39
|
+
* so graph-gui.md §4.1 rules out a community browser as a navigation structure that would
|
|
40
|
+
* silently change under the user. `fileType` is stable graphify-derived data instead.
|
|
41
|
+
*/
|
|
42
|
+
export declare function browse(index: GraphIndex, req: BrowseRequest): OpResponse<BrowseResult>;
|
|
24
43
|
export type QueryRequest = {
|
|
25
44
|
question: string;
|
|
26
|
-
terms?: string
|
|
45
|
+
terms?: Array<string>;
|
|
27
46
|
depth?: number;
|
|
28
47
|
maxNodes?: number;
|
|
29
48
|
seeds?: number;
|
|
@@ -31,9 +50,9 @@ export type QueryRequest = {
|
|
|
31
50
|
};
|
|
32
51
|
export type QueryResult = {
|
|
33
52
|
subgraph: {
|
|
34
|
-
nodes: GraphNode
|
|
35
|
-
edges: GraphEdge
|
|
36
|
-
seeds: string
|
|
53
|
+
nodes: Array<GraphNode>;
|
|
54
|
+
edges: Array<GraphEdge>;
|
|
55
|
+
seeds: Array<string>;
|
|
37
56
|
};
|
|
38
57
|
context?: {
|
|
39
58
|
markdown: string;
|
|
@@ -57,8 +76,8 @@ export type PathRequest = {
|
|
|
57
76
|
};
|
|
58
77
|
export type PathResult = {
|
|
59
78
|
found: boolean;
|
|
60
|
-
nodes: GraphNode
|
|
61
|
-
edges: GraphEdge
|
|
79
|
+
nodes: Array<GraphNode>;
|
|
80
|
+
edges: Array<GraphEdge>;
|
|
62
81
|
};
|
|
63
82
|
/** Fully deterministic, undirected, no scoring (companion-api.md §4.5) — plain BFS. */
|
|
64
83
|
export declare function path(index: GraphIndex, req: PathRequest): OpResponse<PathResult>;
|
package/dist/scoring.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** camelCase / snake_case / path segments → discrete lowercase tokens. */
|
|
2
|
-
export declare function shatter(text: string): string
|
|
3
|
-
export declare function terms(question: string): string
|
|
2
|
+
export declare function shatter(text: string): Array<string>;
|
|
3
|
+
export declare function terms(question: string): Array<string>;
|
|
4
4
|
export type ScoreIndexEntry = {
|
|
5
5
|
id: string;
|
|
6
6
|
labelTokens: Set<string>;
|
|
@@ -14,4 +14,4 @@ export type ScoredNode = {
|
|
|
14
14
|
exact: boolean;
|
|
15
15
|
};
|
|
16
16
|
/** label tokens weigh full; path tokens weigh less — a path match is weaker evidence. */
|
|
17
|
-
export declare function scoreNodes(index: ScoreIndexEntry
|
|
17
|
+
export declare function scoreNodes(index: Array<ScoreIndexEntry>, queryTerms: Array<string>): Array<ScoredNode>;
|
package/dist/serve.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { MinimalServer } from "./net.js";
|
|
2
|
+
import type { GraphState } from "./http.js";
|
|
3
3
|
export type ServeOptions = {
|
|
4
4
|
/** Absolute path of the checkout to serve — where graphify-out/graph.json and .notex/ live. */
|
|
5
5
|
checkoutPath: string;
|
|
@@ -11,7 +11,7 @@ export type ServeOptions = {
|
|
|
11
11
|
*/
|
|
12
12
|
port?: number;
|
|
13
13
|
/** Additional allowed origins beyond the dev default (companion-api.md §5). */
|
|
14
|
-
origins?: string
|
|
14
|
+
origins?: Array<string>;
|
|
15
15
|
rotateToken?: boolean;
|
|
16
16
|
nodeEnv?: string;
|
|
17
17
|
/** Fires once the graph finishes loading (or fails to) — the CLI uses this for startup output. */
|
package/dist/traversal.d.ts
CHANGED
|
@@ -4,13 +4,13 @@ export type TraversalEdge = {
|
|
|
4
4
|
target: string;
|
|
5
5
|
weight: number;
|
|
6
6
|
};
|
|
7
|
-
export type AdjacencyMap<
|
|
7
|
+
export type AdjacencyMap<TEdge> = Map<string, Array<{
|
|
8
8
|
other: string;
|
|
9
|
-
edge:
|
|
9
|
+
edge: TEdge;
|
|
10
10
|
}>>;
|
|
11
|
-
export type TraversalResult<
|
|
12
|
-
nodeIds: string
|
|
13
|
-
edges:
|
|
11
|
+
export type TraversalResult<TEdge> = {
|
|
12
|
+
nodeIds: Array<string>;
|
|
13
|
+
edges: Array<TEdge>;
|
|
14
14
|
truncated: Truncated | null;
|
|
15
15
|
};
|
|
16
|
-
export declare function traverse<
|
|
16
|
+
export declare function traverse<TEdge extends TraversalEdge>(adjacency: AdjacencyMap<TEdge>, allEdges: Array<TEdge>, seedIds: Array<string>, depth: number, maxNodes: number): TraversalResult<TEdge>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "notex-companion",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Local retrieval companion for Notex — reads a checkout's graphify-out/graph.json and serves deterministic search/query/path/node lookups over loopback HTTP and (stub) MCP stdio. No LLM, no graph building, no network beyond 127.0.0.1.",
|
|
5
5
|
"keywords": ["notex", "graphify", "mcp", "code-graph"],
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,6 +21,10 @@
|
|
|
21
21
|
"types": "./dist/index.d.ts",
|
|
22
22
|
"default": "./dist/index.js"
|
|
23
23
|
},
|
|
24
|
+
"./client": {
|
|
25
|
+
"types": "./dist/client.d.ts",
|
|
26
|
+
"default": "./dist/client.js"
|
|
27
|
+
},
|
|
24
28
|
"./package.json": "./package.json"
|
|
25
29
|
},
|
|
26
30
|
"files": ["dist", "README.md"],
|
|
@@ -32,7 +36,7 @@
|
|
|
32
36
|
"typecheck": "tsc --noEmit",
|
|
33
37
|
"build": "rm -rf dist && bun run build:cli && bun run build:lib && bun run build:types && chmod +x dist/cli.js",
|
|
34
38
|
"build:cli": "bun build ./src/bin.ts --target=node --format=esm --outfile=dist/cli.js --banner=\"#!/usr/bin/env node\"",
|
|
35
|
-
"build:lib": "bun build ./src/index.ts --target=node --format=esm --outfile=dist/index.js",
|
|
39
|
+
"build:lib": "bun build ./src/index.ts --target=node --format=esm --outfile=dist/index.js && bun build ./src/client.ts --target=browser --format=esm --outfile=dist/client.js",
|
|
36
40
|
"build:types": "tsc -p tsconfig.build.json && node scripts/fix-dts-extensions.mjs",
|
|
37
41
|
"prepublishOnly": "bun run typecheck && bun test && bun run build"
|
|
38
42
|
},
|