project-librarian 0.3.0 → 0.4.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.
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.indexJavaScriptLike = indexJavaScriptLike;
37
+ const path = __importStar(require("node:path"));
38
+ const ts = __importStar(require("typescript"));
39
+ const shared_1 = require("./shared");
40
+ const httpMethods = new Set(["all", "delete", "get", "patch", "post", "put"]);
41
+ function scriptKindForPath(relativePath) {
42
+ const extension = path.extname(relativePath).toLowerCase();
43
+ if (extension === ".tsx")
44
+ return ts.ScriptKind.TSX;
45
+ if (extension === ".jsx")
46
+ return ts.ScriptKind.JSX;
47
+ if ([".ts", ".mts", ".cts"].includes(extension))
48
+ return ts.ScriptKind.TS;
49
+ return ts.ScriptKind.JS;
50
+ }
51
+ function tsLine(sourceFile, node) {
52
+ return sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1;
53
+ }
54
+ function nodeName(node, sourceFile) {
55
+ if (ts.isIdentifier(node))
56
+ return node.text;
57
+ if (ts.isStringLiteral(node) || ts.isNumericLiteral(node))
58
+ return node.text;
59
+ if (ts.isPrivateIdentifier(node))
60
+ return node.text;
61
+ return (0, shared_1.oneLine)(node.getText(sourceFile));
62
+ }
63
+ function propertyNameText(name, sourceFile) {
64
+ if (!name)
65
+ return "";
66
+ return nodeName(name, sourceFile);
67
+ }
68
+ function callTarget(expression, sourceFile) {
69
+ if (ts.isIdentifier(expression))
70
+ return expression.text;
71
+ if (ts.isPropertyAccessExpression(expression))
72
+ return (0, shared_1.oneLine)(expression.getText(sourceFile));
73
+ if (ts.isElementAccessExpression(expression))
74
+ return (0, shared_1.oneLine)(expression.getText(sourceFile));
75
+ return (0, shared_1.oneLine)(expression.getText(sourceFile));
76
+ }
77
+ function importBindingText(importClause, sourceFile) {
78
+ if (!importClause)
79
+ return "";
80
+ const names = [];
81
+ if (importClause.name)
82
+ names.push(importClause.name.text);
83
+ const namedBindings = importClause.namedBindings;
84
+ if (namedBindings && ts.isNamespaceImport(namedBindings))
85
+ names.push(`* as ${namedBindings.name.text}`);
86
+ if (namedBindings && ts.isNamedImports(namedBindings)) {
87
+ for (const element of namedBindings.elements)
88
+ names.push(element.name.text);
89
+ }
90
+ return names.join(", ") || (0, shared_1.oneLine)(importClause.getText(sourceFile));
91
+ }
92
+ function stringArg(node) {
93
+ if (!node)
94
+ return "";
95
+ return ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node) ? node.text : "";
96
+ }
97
+ function handlerArg(node, sourceFile) {
98
+ if (!node)
99
+ return "";
100
+ return callTarget(node, sourceFile);
101
+ }
102
+ function routeFromCall(node, sourceFile) {
103
+ if (!ts.isPropertyAccessExpression(node.expression))
104
+ return null;
105
+ const method = node.expression.name.text.toLowerCase();
106
+ if (!httpMethods.has(method))
107
+ return null;
108
+ const receiver = node.expression.expression;
109
+ if (!ts.isIdentifier(receiver) || !["app", "router", "server"].includes(receiver.text))
110
+ return null;
111
+ const route = stringArg(node.arguments[0]);
112
+ if (!route)
113
+ return null;
114
+ return {
115
+ handler: handlerArg(node.arguments[1], sourceFile),
116
+ method: method.toUpperCase(),
117
+ route,
118
+ };
119
+ }
120
+ function routeFromDecorator(node, sourceFile) {
121
+ const decorators = ts.canHaveDecorators(node) ? ts.getDecorators(node) ?? [] : [];
122
+ const routes = [];
123
+ for (const decorator of decorators) {
124
+ const expression = decorator.expression;
125
+ if (!ts.isCallExpression(expression))
126
+ continue;
127
+ const callee = expression.expression;
128
+ if (!ts.isIdentifier(callee))
129
+ continue;
130
+ const method = callee.text.toLowerCase();
131
+ if (!httpMethods.has(method))
132
+ continue;
133
+ routes.push({ method: method.toUpperCase(), route: stringArg(expression.arguments[0]) || "/" });
134
+ }
135
+ return routes;
136
+ }
137
+ function signatureFor(node, sourceFile) {
138
+ return (0, shared_1.oneLine)(node.getText(sourceFile));
139
+ }
140
+ function indexJavaScriptLike(file, statements) {
141
+ const sourceFile = ts.createSourceFile(file.path, file.text, ts.ScriptTarget.Latest, true, scriptKindForPath(file.path));
142
+ function visit(node, context) {
143
+ let nextContext = context;
144
+ if (ts.isFunctionDeclaration(node)) {
145
+ const name = node.name?.text ?? "";
146
+ (0, shared_1.insertSymbol)(statements, name, "function", file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
147
+ if (name)
148
+ nextContext = name;
149
+ }
150
+ else if (ts.isClassDeclaration(node)) {
151
+ const name = node.name?.text ?? "";
152
+ (0, shared_1.insertSymbol)(statements, name, "class", file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
153
+ if (name)
154
+ nextContext = name;
155
+ }
156
+ else if (ts.isInterfaceDeclaration(node)) {
157
+ (0, shared_1.insertSymbol)(statements, node.name.text, "interface", file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
158
+ }
159
+ else if (ts.isTypeAliasDeclaration(node)) {
160
+ (0, shared_1.insertSymbol)(statements, node.name.text, "type", file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
161
+ }
162
+ else if (ts.isEnumDeclaration(node)) {
163
+ (0, shared_1.insertSymbol)(statements, node.name.text, "enum", file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
164
+ }
165
+ else if (ts.isMethodDeclaration(node)) {
166
+ const name = propertyNameText(node.name, sourceFile);
167
+ (0, shared_1.insertSymbol)(statements, name, "method", file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
168
+ for (const route of routeFromDecorator(node, sourceFile)) {
169
+ statements.insertRoute.run(route.method, route.route, file.path, tsLine(sourceFile, node), name);
170
+ (0, shared_1.insertEdge)(statements, "route_to_handler", "route", `${route.method} ${route.route}`, "symbol", name, file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
171
+ }
172
+ if (name)
173
+ nextContext = name;
174
+ }
175
+ else if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name)) {
176
+ const symbolKind = node.initializer && (ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer)) ? "function" : "variable";
177
+ (0, shared_1.insertSymbol)(statements, node.name.text, symbolKind, file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
178
+ if (symbolKind === "function")
179
+ nextContext = node.name.text;
180
+ }
181
+ else if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
182
+ const imported = importBindingText(node.importClause, sourceFile);
183
+ statements.insertImport.run(file.path, node.moduleSpecifier.text, imported, tsLine(sourceFile, node), signatureFor(node, sourceFile));
184
+ (0, shared_1.insertEdge)(statements, "import", "file", file.path, "module", node.moduleSpecifier.text, file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
185
+ }
186
+ else if (ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
187
+ const exported = node.exportClause ? (0, shared_1.oneLine)(node.exportClause.getText(sourceFile)) : "";
188
+ statements.insertImport.run(file.path, node.moduleSpecifier.text, exported, tsLine(sourceFile, node), signatureFor(node, sourceFile));
189
+ (0, shared_1.insertEdge)(statements, "export", "file", file.path, "module", node.moduleSpecifier.text, file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
190
+ }
191
+ else if (ts.isCallExpression(node)) {
192
+ const route = routeFromCall(node, sourceFile);
193
+ if (route) {
194
+ statements.insertRoute.run(route.method, route.route, file.path, tsLine(sourceFile, node), route.handler);
195
+ (0, shared_1.insertEdge)(statements, "route_to_handler", "route", `${route.method} ${route.route}`, "symbol", route.handler, file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
196
+ }
197
+ if (ts.isIdentifier(node.expression) && node.expression.text === "require") {
198
+ const moduleName = stringArg(node.arguments[0]);
199
+ if (moduleName) {
200
+ statements.insertImport.run(file.path, moduleName, "", tsLine(sourceFile, node), signatureFor(node, sourceFile));
201
+ (0, shared_1.insertEdge)(statements, "import", "file", file.path, "module", moduleName, file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
202
+ }
203
+ }
204
+ else {
205
+ (0, shared_1.insertEdge)(statements, "call", context ? "symbol" : "file", context || file.path, "symbol", callTarget(node.expression, sourceFile), file, tsLine(sourceFile, node), signatureFor(node, sourceFile));
206
+ }
207
+ }
208
+ ts.forEachChild(node, (child) => visit(child, nextContext));
209
+ }
210
+ visit(sourceFile, "");
211
+ }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.planIndexUpdate = planIndexUpdate;
4
+ function planIndexUpdate(currentFiles, indexedHashes) {
5
+ const currentByPath = new Map(currentFiles.map((file) => [file.path, file]));
6
+ const deletedPaths = Array.from(indexedHashes.keys()).filter((filePath) => !currentByPath.has(filePath));
7
+ const reindexedFiles = currentFiles.filter((file) => indexedHashes.get(file.path) !== file.hash);
8
+ return {
9
+ addedFiles: reindexedFiles.filter((file) => !indexedHashes.has(file.path)),
10
+ changedFiles: reindexedFiles.filter((file) => indexedHashes.has(file.path)),
11
+ currentByPath,
12
+ deletedPaths,
13
+ reindexedFiles,
14
+ unchangedFiles: currentFiles.length - reindexedFiles.length,
15
+ };
16
+ }
@@ -0,0 +1,164 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.inspectCodeIndexHealth = inspectCodeIndexHealth;
37
+ exports.formatCodeIndexHealthRemediation = formatCodeIndexHealthRemediation;
38
+ const fs = __importStar(require("node:fs"));
39
+ function safeDiscoverCodeFiles(discoverCodeFiles, scopes) {
40
+ try {
41
+ return discoverCodeFiles(scopes).length;
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ }
47
+ function safeScalar(database, sql, param) {
48
+ try {
49
+ const rows = typeof param === "string" ? database.prepare(sql).all(param) : database.prepare(sql).all();
50
+ const value = rows[0]?.value;
51
+ return typeof value === "string" || typeof value === "number" ? String(value) : "";
52
+ }
53
+ catch {
54
+ return "";
55
+ }
56
+ }
57
+ function tableNames(database) {
58
+ try {
59
+ return database.prepare("SELECT name AS value FROM sqlite_schema WHERE type IN ('table', 'view') ORDER BY name")
60
+ .all()
61
+ .map((row) => String(row.value))
62
+ .filter(Boolean);
63
+ }
64
+ catch {
65
+ return [];
66
+ }
67
+ }
68
+ function readScopes(scopesJson, scopesText, fallback) {
69
+ if (scopesJson) {
70
+ try {
71
+ const parsed = JSON.parse(scopesJson);
72
+ if (Array.isArray(parsed) && parsed.every((scope) => typeof scope === "string"))
73
+ return parsed;
74
+ }
75
+ catch {
76
+ // Fall back to legacy comma-separated metadata below.
77
+ }
78
+ }
79
+ const scopes = scopesText.split(",").map((scope) => scope.trim()).filter(Boolean);
80
+ return scopes.length > 0 ? scopes : fallback;
81
+ }
82
+ function rebuildCommand(indexableFiles, smallRepoThreshold) {
83
+ const parts = ["project-librarian", "--code-index", "--code-index-full"];
84
+ if (indexableFiles !== null && indexableFiles < smallRepoThreshold)
85
+ parts.push("--acknowledge-small-repo");
86
+ return parts.join(" ");
87
+ }
88
+ function baseHealth(options, status, message, indexableFiles) {
89
+ return {
90
+ database_path: options.relativePath,
91
+ expected_schema_version: options.expectedSchemaVersion,
92
+ found_schema_version: "",
93
+ indexed_files: null,
94
+ indexable_files: indexableFiles,
95
+ message,
96
+ parser_mode: "",
97
+ recommended_rebuild_command: rebuildCommand(indexableFiles, options.smallRepoThreshold),
98
+ scopes: options.defaultScopes,
99
+ status,
100
+ tables: [],
101
+ updated_at: "",
102
+ };
103
+ }
104
+ function inspectCodeIndexHealth(options) {
105
+ if (!fs.existsSync(options.absolutePath)) {
106
+ const indexableFiles = safeDiscoverCodeFiles(options.discoverCodeFiles, options.defaultScopes);
107
+ return baseHealth(options, "missing", `missing code evidence index: ${options.relativePath}`, indexableFiles);
108
+ }
109
+ let database;
110
+ try {
111
+ database = options.openDatabase(options.absolutePath);
112
+ }
113
+ catch (error) {
114
+ const message = error instanceof Error ? error.message : String(error);
115
+ const indexableFiles = safeDiscoverCodeFiles(options.discoverCodeFiles, options.defaultScopes);
116
+ return baseHealth(options, "unreadable", `code evidence index is not readable: ${message}`, indexableFiles);
117
+ }
118
+ try {
119
+ database.exec("PRAGMA query_only = ON");
120
+ const tables = tableNames(database);
121
+ const foundSchemaVersion = safeScalar(database, "SELECT value FROM meta WHERE key = ?", "schema_version");
122
+ const updatedAt = safeScalar(database, "SELECT value FROM meta WHERE key = ?", "updated_at");
123
+ const parserMode = safeScalar(database, "SELECT value FROM meta WHERE key = ?", "parser_mode");
124
+ const scopes = readScopes(safeScalar(database, "SELECT value FROM meta WHERE key = ?", "scopes_json"), safeScalar(database, "SELECT value FROM meta WHERE key = ?", "scopes"), options.defaultScopes);
125
+ const indexedFilesText = tables.includes("files") ? safeScalar(database, "SELECT count(*) AS value FROM files") : "";
126
+ const indexedFiles = indexedFilesText ? Number(indexedFilesText) : null;
127
+ const indexableFiles = safeDiscoverCodeFiles(options.discoverCodeFiles, scopes.length > 0 ? scopes : options.defaultScopes);
128
+ const compatible = foundSchemaVersion === options.expectedSchemaVersion;
129
+ return {
130
+ database_path: options.relativePath,
131
+ expected_schema_version: options.expectedSchemaVersion,
132
+ found_schema_version: foundSchemaVersion,
133
+ indexed_files: indexedFiles !== null && Number.isFinite(indexedFiles) ? indexedFiles : null,
134
+ indexable_files: indexableFiles,
135
+ message: compatible
136
+ ? `code evidence index is compatible: schema ${foundSchemaVersion}`
137
+ : `code evidence index schema version ${foundSchemaVersion || "(missing)"} is incompatible with ${options.expectedSchemaVersion}`,
138
+ parser_mode: parserMode,
139
+ recommended_rebuild_command: rebuildCommand(indexableFiles, options.smallRepoThreshold),
140
+ scopes,
141
+ status: compatible ? "compatible" : "incompatible_schema",
142
+ tables,
143
+ updated_at: updatedAt,
144
+ };
145
+ }
146
+ catch (error) {
147
+ const message = error instanceof Error ? error.message : String(error);
148
+ const indexableFiles = safeDiscoverCodeFiles(options.discoverCodeFiles, options.defaultScopes);
149
+ return baseHealth(options, "unreadable", `code evidence index is not readable: ${message}`, indexableFiles);
150
+ }
151
+ finally {
152
+ database.close();
153
+ }
154
+ }
155
+ function formatCodeIndexHealthRemediation(health) {
156
+ return [
157
+ health.message,
158
+ `database: ${health.database_path}`,
159
+ `status: ${health.status}`,
160
+ `expected_schema_version: ${health.expected_schema_version}`,
161
+ `found_schema_version: ${health.found_schema_version || "(missing)"}`,
162
+ `rebuild: ${health.recommended_rebuild_command}`,
163
+ ].join("\n");
164
+ }