eminiar 0.5.2 → 0.6.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/.claude-plugin/plugin.json +1 -1
- package/README.md +2 -2
- package/dist/cli.js +1363 -190
- package/dist/hook.js +23 -6
- package/dist/server.js +1379 -235
- package/dist/ui/app.global.js +31 -31
- package/eminiar.config.schema.json +4 -2
- package/package.json +2 -1
- package/vendor/grammars/php.wasm +0 -0
package/dist/cli.js
CHANGED
|
@@ -4602,7 +4602,7 @@ function isRef(value) {
|
|
|
4602
4602
|
function cloneIssues(issues) {
|
|
4603
4603
|
return issues.map((iss) => iss.path ? { ...iss, path: iss.path.slice() } : { ...iss });
|
|
4604
4604
|
}
|
|
4605
|
-
function isRecursive(inst, stack,
|
|
4605
|
+
function isRecursive(inst, stack, resolve8) {
|
|
4606
4606
|
const cached5 = recursive.get(inst);
|
|
4607
4607
|
if (cached5 !== void 0)
|
|
4608
4608
|
return cached5 ? PROVEN : NONE;
|
|
@@ -4612,7 +4612,7 @@ function isRecursive(inst, stack, resolve7) {
|
|
|
4612
4612
|
let result = NONE;
|
|
4613
4613
|
const check3 = (child) => {
|
|
4614
4614
|
if (result !== PROVEN && child?._zod) {
|
|
4615
|
-
const answer = isRecursive(child, stack,
|
|
4615
|
+
const answer = isRecursive(child, stack, resolve8);
|
|
4616
4616
|
if (answer > result)
|
|
4617
4617
|
result = answer;
|
|
4618
4618
|
}
|
|
@@ -4623,7 +4623,7 @@ function isRecursive(inst, stack, resolve7) {
|
|
|
4623
4623
|
const desc = Object.getOwnPropertyDescriptor(sh, key);
|
|
4624
4624
|
if (spread && !desc.enumerable)
|
|
4625
4625
|
continue;
|
|
4626
|
-
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack,
|
|
4626
|
+
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve8) : NONE;
|
|
4627
4627
|
if (child > answer)
|
|
4628
4628
|
answer = child;
|
|
4629
4629
|
}
|
|
@@ -4687,7 +4687,7 @@ function isRecursive(inst, stack, resolve7) {
|
|
|
4687
4687
|
break;
|
|
4688
4688
|
// `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
|
|
4689
4689
|
case "lazy": {
|
|
4690
|
-
const inner = def._cachedInner ?? (
|
|
4690
|
+
const inner = def._cachedInner ?? (resolve8 ? inst._zod.innerType : void 0);
|
|
4691
4691
|
merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
|
|
4692
4692
|
break;
|
|
4693
4693
|
}
|
|
@@ -20776,7 +20776,8 @@ var init_config = __esm({
|
|
|
20776
20776
|
"**/*.mjs",
|
|
20777
20777
|
"**/*.cjs",
|
|
20778
20778
|
"**/*.py",
|
|
20779
|
-
"**/*.go"
|
|
20779
|
+
"**/*.go",
|
|
20780
|
+
"**/*.php"
|
|
20780
20781
|
]),
|
|
20781
20782
|
exclude: external_exports.array(external_exports.string()).default([
|
|
20782
20783
|
"**/node_modules/**",
|
|
@@ -20790,7 +20791,8 @@ var init_config = __esm({
|
|
|
20790
20791
|
"**/__pycache__/**",
|
|
20791
20792
|
"**/site-packages/**",
|
|
20792
20793
|
"**/vendor/**",
|
|
20793
|
-
"**/*.min.js"
|
|
20794
|
+
"**/*.min.js",
|
|
20795
|
+
"**/*.blade.php"
|
|
20794
20796
|
])
|
|
20795
20797
|
}).prefault({}),
|
|
20796
20798
|
git: external_exports.object({
|
|
@@ -22397,12 +22399,18 @@ var init_adapter = __esm({
|
|
|
22397
22399
|
const ids = this.db.prepare("SELECT id FROM graph_nodes WHERE source = ? AND path = ?").all(source, path3).map((r) => r.id);
|
|
22398
22400
|
const fileId2 = `file:${path3}`;
|
|
22399
22401
|
const all = ids.includes(fileId2) ? ids : [...ids, fileId2];
|
|
22402
|
+
this.db.prepare(
|
|
22403
|
+
"DELETE FROM graph_edges WHERE source = ? AND kind = 'handles' AND dst IN (SELECT id FROM graph_nodes WHERE source = ? AND path = ? AND kind = 'route')"
|
|
22404
|
+
).run(source, source, path3);
|
|
22405
|
+
const foreignRoute = "AND NOT (kind = 'handles' AND dst IN (SELECT id FROM graph_nodes WHERE kind = 'route' AND path IS NOT ?))";
|
|
22400
22406
|
for (let i = 0; i < all.length; i += 400) {
|
|
22401
22407
|
const slice = all.slice(i, i + 400);
|
|
22402
22408
|
const ph = slice.map(() => "?").join(",");
|
|
22403
|
-
this.db.prepare(`DELETE FROM graph_edges WHERE source = ? AND src IN (${ph})`).run(source, ...slice);
|
|
22409
|
+
this.db.prepare(`DELETE FROM graph_edges WHERE source = ? AND src IN (${ph}) ${foreignRoute}`).run(source, ...slice, path3);
|
|
22404
22410
|
}
|
|
22405
|
-
this.db.prepare(
|
|
22411
|
+
this.db.prepare(
|
|
22412
|
+
`DELETE FROM graph_edges WHERE source = ? AND src LIKE ? ESCAPE '\\' ${foreignRoute}`
|
|
22413
|
+
).run(source, `sym:${path3.replace(/[%_\\]/g, "\\$&")}#%`, path3);
|
|
22406
22414
|
if (opts.keepFile) {
|
|
22407
22415
|
this.db.prepare("DELETE FROM graph_nodes WHERE source = ? AND path = ? AND kind != 'file'").run(source, path3);
|
|
22408
22416
|
} else {
|
|
@@ -23192,13 +23200,13 @@ import { spawn } from "child_process";
|
|
|
23192
23200
|
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
|
|
23193
23201
|
import { join as join13 } from "path";
|
|
23194
23202
|
function mcpHandshake(bin, ms = 15e3) {
|
|
23195
|
-
return new Promise((
|
|
23203
|
+
return new Promise((resolve8) => {
|
|
23196
23204
|
const child = spawn(bin, [], { stdio: ["pipe", "pipe", "pipe"] });
|
|
23197
23205
|
let buf = "";
|
|
23198
23206
|
let err = "";
|
|
23199
23207
|
const timer = setTimeout(() => {
|
|
23200
23208
|
child.kill();
|
|
23201
|
-
|
|
23209
|
+
resolve8(`no reply in ${ms / 1e3}s${err ? `: ${err.trim().slice(-200)}` : ""}`);
|
|
23202
23210
|
}, ms);
|
|
23203
23211
|
child.stdout.on("data", (d) => {
|
|
23204
23212
|
buf += d.toString();
|
|
@@ -23206,7 +23214,7 @@ function mcpHandshake(bin, ms = 15e3) {
|
|
|
23206
23214
|
clearTimeout(timer);
|
|
23207
23215
|
child.kill();
|
|
23208
23216
|
const m = /"serverInfo":\{"name":"([^"]+)","version":"([^"]+)"/.exec(buf);
|
|
23209
|
-
|
|
23217
|
+
resolve8(m ? `${m[1]} v${m[2]}` : "ok");
|
|
23210
23218
|
}
|
|
23211
23219
|
});
|
|
23212
23220
|
child.stderr.on("data", (d) => {
|
|
@@ -23214,7 +23222,7 @@ function mcpHandshake(bin, ms = 15e3) {
|
|
|
23214
23222
|
});
|
|
23215
23223
|
child.on("error", (e) => {
|
|
23216
23224
|
clearTimeout(timer);
|
|
23217
|
-
|
|
23225
|
+
resolve8(`spawn failed: ${e.message}`);
|
|
23218
23226
|
});
|
|
23219
23227
|
child.stdin.write(
|
|
23220
23228
|
`${JSON.stringify({ jsonrpc: "2.0", id: 1, method: "initialize", params: { protocolVersion: "2025-06-18", capabilities: {}, clientInfo: { name: "doctor", version: "0" } } })}
|
|
@@ -27473,15 +27481,15 @@ var require_pattern = __commonJS({
|
|
|
27473
27481
|
exports.removeDuplicateSlashes = removeDuplicateSlashes;
|
|
27474
27482
|
function partitionAbsoluteAndRelative(patterns) {
|
|
27475
27483
|
const absolute = [];
|
|
27476
|
-
const
|
|
27484
|
+
const relative10 = [];
|
|
27477
27485
|
for (const pattern2 of patterns) {
|
|
27478
27486
|
if (isAbsolute(pattern2)) {
|
|
27479
27487
|
absolute.push(pattern2);
|
|
27480
27488
|
} else {
|
|
27481
|
-
|
|
27489
|
+
relative10.push(pattern2);
|
|
27482
27490
|
}
|
|
27483
27491
|
}
|
|
27484
|
-
return [absolute,
|
|
27492
|
+
return [absolute, relative10];
|
|
27485
27493
|
}
|
|
27486
27494
|
exports.partitionAbsoluteAndRelative = partitionAbsoluteAndRelative;
|
|
27487
27495
|
function isAbsolute(pattern2) {
|
|
@@ -27912,11 +27920,11 @@ var require_out = __commonJS({
|
|
|
27912
27920
|
async.read(path3, getSettings(optionsOrSettingsOrCallback), callback);
|
|
27913
27921
|
}
|
|
27914
27922
|
exports.stat = stat;
|
|
27915
|
-
function
|
|
27923
|
+
function statSync11(path3, optionsOrSettings) {
|
|
27916
27924
|
const settings = getSettings(optionsOrSettings);
|
|
27917
27925
|
return sync.read(path3, settings);
|
|
27918
27926
|
}
|
|
27919
|
-
exports.statSync =
|
|
27927
|
+
exports.statSync = statSync11;
|
|
27920
27928
|
function getSettings(settingsOrOptions = {}) {
|
|
27921
27929
|
if (settingsOrOptions instanceof settings_1.default) {
|
|
27922
27930
|
return settingsOrOptions;
|
|
@@ -28597,41 +28605,41 @@ var require_queue = __commonJS({
|
|
|
28597
28605
|
queue.drained = drained;
|
|
28598
28606
|
return queue;
|
|
28599
28607
|
function push(value) {
|
|
28600
|
-
var p = new Promise(function(
|
|
28608
|
+
var p = new Promise(function(resolve8, reject) {
|
|
28601
28609
|
pushCb(value, function(err, result) {
|
|
28602
28610
|
if (err) {
|
|
28603
28611
|
reject(err);
|
|
28604
28612
|
return;
|
|
28605
28613
|
}
|
|
28606
|
-
|
|
28614
|
+
resolve8(result);
|
|
28607
28615
|
});
|
|
28608
28616
|
});
|
|
28609
28617
|
p.catch(noop);
|
|
28610
28618
|
return p;
|
|
28611
28619
|
}
|
|
28612
28620
|
function unshift(value) {
|
|
28613
|
-
var p = new Promise(function(
|
|
28621
|
+
var p = new Promise(function(resolve8, reject) {
|
|
28614
28622
|
unshiftCb(value, function(err, result) {
|
|
28615
28623
|
if (err) {
|
|
28616
28624
|
reject(err);
|
|
28617
28625
|
return;
|
|
28618
28626
|
}
|
|
28619
|
-
|
|
28627
|
+
resolve8(result);
|
|
28620
28628
|
});
|
|
28621
28629
|
});
|
|
28622
28630
|
p.catch(noop);
|
|
28623
28631
|
return p;
|
|
28624
28632
|
}
|
|
28625
28633
|
function drained() {
|
|
28626
|
-
var p = new Promise(function(
|
|
28634
|
+
var p = new Promise(function(resolve8) {
|
|
28627
28635
|
process.nextTick(function() {
|
|
28628
28636
|
if (queue.idle()) {
|
|
28629
|
-
|
|
28637
|
+
resolve8();
|
|
28630
28638
|
} else {
|
|
28631
28639
|
var previousDrain = queue.drain;
|
|
28632
28640
|
queue.drain = function() {
|
|
28633
28641
|
if (typeof previousDrain === "function") previousDrain();
|
|
28634
|
-
|
|
28642
|
+
resolve8();
|
|
28635
28643
|
queue.drain = previousDrain;
|
|
28636
28644
|
};
|
|
28637
28645
|
}
|
|
@@ -29128,9 +29136,9 @@ var require_stream3 = __commonJS({
|
|
|
29128
29136
|
});
|
|
29129
29137
|
}
|
|
29130
29138
|
_getStat(filepath) {
|
|
29131
|
-
return new Promise((
|
|
29139
|
+
return new Promise((resolve8, reject) => {
|
|
29132
29140
|
this._stat(filepath, this._fsStatSettings, (error62, stats) => {
|
|
29133
|
-
return error62 === null ?
|
|
29141
|
+
return error62 === null ? resolve8(stats) : reject(error62);
|
|
29134
29142
|
});
|
|
29135
29143
|
});
|
|
29136
29144
|
}
|
|
@@ -29155,10 +29163,10 @@ var require_async5 = __commonJS({
|
|
|
29155
29163
|
this._readerStream = new stream_1.default(this._settings);
|
|
29156
29164
|
}
|
|
29157
29165
|
dynamic(root, options) {
|
|
29158
|
-
return new Promise((
|
|
29166
|
+
return new Promise((resolve8, reject) => {
|
|
29159
29167
|
this._walkAsync(root, options, (error62, entries) => {
|
|
29160
29168
|
if (error62 === null) {
|
|
29161
|
-
|
|
29169
|
+
resolve8(entries);
|
|
29162
29170
|
} else {
|
|
29163
29171
|
reject(error62);
|
|
29164
29172
|
}
|
|
@@ -29168,10 +29176,10 @@ var require_async5 = __commonJS({
|
|
|
29168
29176
|
async static(patterns, options) {
|
|
29169
29177
|
const entries = [];
|
|
29170
29178
|
const stream = this._readerStream.static(patterns, options);
|
|
29171
|
-
return new Promise((
|
|
29179
|
+
return new Promise((resolve8, reject) => {
|
|
29172
29180
|
stream.once("error", reject);
|
|
29173
29181
|
stream.on("data", (entry) => entries.push(entry));
|
|
29174
|
-
stream.once("end", () =>
|
|
29182
|
+
stream.once("end", () => resolve8(entries));
|
|
29175
29183
|
});
|
|
29176
29184
|
}
|
|
29177
29185
|
};
|
|
@@ -31063,6 +31071,7 @@ var init_metrics = __esm({
|
|
|
31063
31071
|
"class",
|
|
31064
31072
|
"method",
|
|
31065
31073
|
"interface",
|
|
31074
|
+
"trait",
|
|
31066
31075
|
"type",
|
|
31067
31076
|
"enum",
|
|
31068
31077
|
"field",
|
|
@@ -31661,6 +31670,40 @@ function parseRestShorthand(rest2, actionName) {
|
|
|
31661
31670
|
}
|
|
31662
31671
|
return [{ method: "ALL", path: joinRoutePath(rest2) }];
|
|
31663
31672
|
}
|
|
31673
|
+
function phpRoutePath(...parts) {
|
|
31674
|
+
return normalizeRoutePath(
|
|
31675
|
+
joinRoutePath(...parts).replace(/\{(\w+)\s*:[^{}]*(?:\{[^{}]*\}[^{}]*)*\}/g, "{$1}").replace(/\{(\w+)<[^>]*>[^}]*\}/g, "{$1}")
|
|
31676
|
+
);
|
|
31677
|
+
}
|
|
31678
|
+
function resourceParam(segment) {
|
|
31679
|
+
return segment.replace(/-/g, "_").replace(/ies$/, "y").replace(/(s|x|z|ch|sh)es$/, "$1").replace(/([^s])s$/, "$1");
|
|
31680
|
+
}
|
|
31681
|
+
function laravelResource(name, api = false, only, except) {
|
|
31682
|
+
const segs = name.split(".").filter(Boolean);
|
|
31683
|
+
const last = segs.pop();
|
|
31684
|
+
if (!last) return [];
|
|
31685
|
+
const base = joinRoutePath(...segs.map((s) => `${s}/:${resourceParam(s)}`), last);
|
|
31686
|
+
const param = `:${resourceParam(last)}`;
|
|
31687
|
+
return RESOURCE_ACTIONS.filter(
|
|
31688
|
+
([, , action]) => !(api && (action === "create" || action === "edit")) && (!only || only.includes(action)) && !except?.includes(action)
|
|
31689
|
+
).map(([method, suffix, action]) => ({
|
|
31690
|
+
method,
|
|
31691
|
+
path: `${base === "/" ? "" : base}${suffix.replace("{p}", param)}` || "/",
|
|
31692
|
+
action
|
|
31693
|
+
}));
|
|
31694
|
+
}
|
|
31695
|
+
function parseRouteAnnotations(doc) {
|
|
31696
|
+
const out = [];
|
|
31697
|
+
for (const m of doc.matchAll(/@Route\(([^)]*)\)/g)) {
|
|
31698
|
+
const body = m[1] ?? "";
|
|
31699
|
+
const path3 = /^\s*"([^"]*)"/.exec(body)?.[1] ?? /\bpath\s*=\s*"([^"]*)"/.exec(body)?.[1];
|
|
31700
|
+
if (path3 === void 0) continue;
|
|
31701
|
+
const list2 = /\bmethods\s*=\s*(\{[^}]*\}|"[^"]*")/.exec(body)?.[1] ?? "";
|
|
31702
|
+
const methods = [...list2.matchAll(/"([^"]+)"/g)].map((x) => httpMethod(x[1])).filter((x) => !!x);
|
|
31703
|
+
for (const method of methods.length ? methods : ["ALL"]) out.push({ method, path: path3 });
|
|
31704
|
+
}
|
|
31705
|
+
return out;
|
|
31706
|
+
}
|
|
31664
31707
|
function frameworkOf(importedPackages) {
|
|
31665
31708
|
for (const p of importedPackages) {
|
|
31666
31709
|
const f = FRAMEWORK_PKGS[p];
|
|
@@ -31705,7 +31748,7 @@ function fileRoutes(rel, exportedNames, defaultExport) {
|
|
|
31705
31748
|
}
|
|
31706
31749
|
return out;
|
|
31707
31750
|
}
|
|
31708
|
-
var ROUTE_METHODS, JS_METHOD_ALIASES, REST_ALIASES, FRAMEWORK_PKGS, routeId, NEXT_APP_ROUTE, NEXT_PAGES_API, HTTP_EXPORTS;
|
|
31751
|
+
var ROUTE_METHODS, JS_METHOD_ALIASES, REST_ALIASES, RESOURCE_ACTIONS, FRAMEWORK_PKGS, routeId, NEXT_APP_ROUTE, NEXT_PAGES_API, HTTP_EXPORTS;
|
|
31709
31752
|
var init_routes = __esm({
|
|
31710
31753
|
"src/graph/routes.ts"() {
|
|
31711
31754
|
"use strict";
|
|
@@ -31729,6 +31772,16 @@ var init_routes = __esm({
|
|
|
31729
31772
|
["PATCH", "/:id", "patch"],
|
|
31730
31773
|
["DELETE", "/:id", "remove"]
|
|
31731
31774
|
];
|
|
31775
|
+
RESOURCE_ACTIONS = [
|
|
31776
|
+
["GET", "", "index"],
|
|
31777
|
+
["GET", "/create", "create"],
|
|
31778
|
+
["POST", "", "store"],
|
|
31779
|
+
["GET", "/{p}", "show"],
|
|
31780
|
+
["GET", "/{p}/edit", "edit"],
|
|
31781
|
+
["PUT", "/{p}", "update"],
|
|
31782
|
+
["PATCH", "/{p}", "update"],
|
|
31783
|
+
["DELETE", "/{p}", "destroy"]
|
|
31784
|
+
];
|
|
31732
31785
|
FRAMEWORK_PKGS = {
|
|
31733
31786
|
express: "express",
|
|
31734
31787
|
fastify: "fastify",
|
|
@@ -31759,7 +31812,18 @@ var init_routes = __esm({
|
|
|
31759
31812
|
"github.com/labstack/echo": "echo",
|
|
31760
31813
|
"github.com/gorilla/mux": "gorilla",
|
|
31761
31814
|
"github.com/gofiber/fiber": "fiber",
|
|
31762
|
-
"github.com/julienschmidt/httprouter": "httprouter"
|
|
31815
|
+
"github.com/julienschmidt/httprouter": "httprouter",
|
|
31816
|
+
// PHP (top-level namespace of a `use` the project cannot resolve)
|
|
31817
|
+
Illuminate: "laravel",
|
|
31818
|
+
Symfony: "symfony",
|
|
31819
|
+
Slim: "slim",
|
|
31820
|
+
CodeIgniter: "codeigniter",
|
|
31821
|
+
// PHP (composer.json `require`, when no `use` in the file names the framework)
|
|
31822
|
+
"laravel/lumen-framework": "lumen",
|
|
31823
|
+
"laravel/framework": "laravel",
|
|
31824
|
+
"symfony/framework-bundle": "symfony",
|
|
31825
|
+
"slim/slim": "slim",
|
|
31826
|
+
"codeigniter4/framework": "codeigniter"
|
|
31763
31827
|
};
|
|
31764
31828
|
routeId = (method, path3) => `route:${method} ${path3}`;
|
|
31765
31829
|
NEXT_APP_ROUTE = /(?:^|\/)app\/(.*?)(?:\/)?route\.(?:ts|tsx|js|jsx|mjs)$/;
|
|
@@ -31789,6 +31853,8 @@ function grammarFor(path3) {
|
|
|
31789
31853
|
return "python";
|
|
31790
31854
|
case ".go":
|
|
31791
31855
|
return "go";
|
|
31856
|
+
case ".php":
|
|
31857
|
+
return "php";
|
|
31792
31858
|
default:
|
|
31793
31859
|
return void 0;
|
|
31794
31860
|
}
|
|
@@ -32293,28 +32359,1095 @@ var init_go = __esm({
|
|
|
32293
32359
|
}
|
|
32294
32360
|
});
|
|
32295
32361
|
|
|
32296
|
-
// src/graph/languages/
|
|
32297
|
-
import {
|
|
32362
|
+
// src/graph/languages/php.ts
|
|
32363
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync12, statSync as statSync2 } from "fs";
|
|
32298
32364
|
import { dirname as dirname5, join as join17, normalize as normalize2, relative as relative3, resolve as resolve4 } from "path";
|
|
32365
|
+
function readComposer(dir) {
|
|
32366
|
+
const file3 = join17(dir, "composer.json");
|
|
32367
|
+
if (!isFile(file3)) return null;
|
|
32368
|
+
try {
|
|
32369
|
+
const json3 = JSON.parse(readFileSync12(file3, "utf8"));
|
|
32370
|
+
const info = { dir, psr4: [], psr0: [], classmap: [], files: [], require: [] };
|
|
32371
|
+
for (const key of ["autoload", "autoload-dev"]) {
|
|
32372
|
+
const section = json3[key];
|
|
32373
|
+
for (const [prefix, dirs] of Object.entries(section?.["psr-4"] ?? {})) {
|
|
32374
|
+
info.psr4.push({
|
|
32375
|
+
prefix: prefix && !prefix.endsWith("\\") ? `${prefix}\\` : prefix,
|
|
32376
|
+
dirs: asList(dirs)
|
|
32377
|
+
});
|
|
32378
|
+
}
|
|
32379
|
+
for (const [prefix, dirs] of Object.entries(section?.["psr-0"] ?? {}))
|
|
32380
|
+
info.psr0.push({ prefix, dirs: asList(dirs) });
|
|
32381
|
+
info.classmap.push(...section?.classmap ?? []);
|
|
32382
|
+
info.files.push(...section?.files ?? []);
|
|
32383
|
+
}
|
|
32384
|
+
info.require = Object.keys(json3.require ?? {});
|
|
32385
|
+
info.psr4.sort((a, b) => b.prefix.length - a.prefix.length);
|
|
32386
|
+
info.psr0.sort((a, b) => b.prefix.length - a.prefix.length);
|
|
32387
|
+
const empty2 = !info.psr4.length && !info.psr0.length && !info.classmap.length && !info.files.length && !info.require.length;
|
|
32388
|
+
return empty2 ? null : info;
|
|
32389
|
+
} catch {
|
|
32390
|
+
return null;
|
|
32391
|
+
}
|
|
32392
|
+
}
|
|
32393
|
+
function composerAutoload(root, fromAbs) {
|
|
32394
|
+
let dir = dirname5(fromAbs);
|
|
32395
|
+
while (dir.startsWith(root)) {
|
|
32396
|
+
let hit = composerCache.get(dir);
|
|
32397
|
+
if (hit === void 0) {
|
|
32398
|
+
hit = readComposer(dir);
|
|
32399
|
+
composerCache.set(dir, hit);
|
|
32400
|
+
}
|
|
32401
|
+
if (hit) return hit;
|
|
32402
|
+
if (dir === root) break;
|
|
32403
|
+
dir = dirname5(dir);
|
|
32404
|
+
}
|
|
32405
|
+
return void 0;
|
|
32406
|
+
}
|
|
32407
|
+
function phpFilesUnder(abs, budget) {
|
|
32408
|
+
if (isFile(abs)) return abs.endsWith(".php") ? [abs] : [];
|
|
32409
|
+
let entries;
|
|
32410
|
+
try {
|
|
32411
|
+
entries = readdirSync6(abs, { recursive: true, encoding: "utf8" });
|
|
32412
|
+
} catch {
|
|
32413
|
+
return [];
|
|
32414
|
+
}
|
|
32415
|
+
const out = [];
|
|
32416
|
+
for (const e of entries) {
|
|
32417
|
+
if (budget.left <= 0) break;
|
|
32418
|
+
if (!e.endsWith(".php") || e.split(/[\\/]/).includes("vendor")) continue;
|
|
32419
|
+
out.push(join17(abs, e));
|
|
32420
|
+
budget.left--;
|
|
32421
|
+
}
|
|
32422
|
+
return out;
|
|
32423
|
+
}
|
|
32424
|
+
function classmapClasses(root, auto) {
|
|
32425
|
+
if (auto.classes) return auto.classes;
|
|
32426
|
+
const classes = /* @__PURE__ */ new Map();
|
|
32427
|
+
const budget = { left: CLASSMAP_MAX_FILES };
|
|
32428
|
+
for (const entry of auto.classmap) {
|
|
32429
|
+
for (const f of phpFilesUnder(join17(auto.dir, entry), budget)) {
|
|
32430
|
+
if (relative3(root, f).startsWith("..")) continue;
|
|
32431
|
+
let code;
|
|
32432
|
+
try {
|
|
32433
|
+
code = readFileSync12(f, "utf8");
|
|
32434
|
+
} catch {
|
|
32435
|
+
continue;
|
|
32436
|
+
}
|
|
32437
|
+
const ns = /^\s*namespace\s+([\w\\]+)\s*[;{]/m.exec(code)?.[1];
|
|
32438
|
+
for (const m of code.matchAll(
|
|
32439
|
+
/^\s*(?:(?:abstract|final|readonly)\s+)*(?:class|interface|trait|enum)\s+(\w+)/gm
|
|
32440
|
+
)) {
|
|
32441
|
+
const fq = ns ? `${ns}\\${m[1]}` : m[1];
|
|
32442
|
+
if (!classes.has(fq)) classes.set(fq, toRel(root, f));
|
|
32443
|
+
}
|
|
32444
|
+
}
|
|
32445
|
+
}
|
|
32446
|
+
auto.classes = classes;
|
|
32447
|
+
return classes;
|
|
32448
|
+
}
|
|
32449
|
+
function autoloadedFunctions(root, auto) {
|
|
32450
|
+
if (auto.functions) return auto.functions;
|
|
32451
|
+
const functions = /* @__PURE__ */ new Map();
|
|
32452
|
+
for (const entry of auto.files) {
|
|
32453
|
+
const f = join17(auto.dir, entry);
|
|
32454
|
+
if (!isFile(f) || relative3(root, f).startsWith("..")) continue;
|
|
32455
|
+
let code;
|
|
32456
|
+
try {
|
|
32457
|
+
code = readFileSync12(f, "utf8");
|
|
32458
|
+
} catch {
|
|
32459
|
+
continue;
|
|
32460
|
+
}
|
|
32461
|
+
if (/^\s*namespace\s+[\w\\]+\s*[;{]/m.test(code)) continue;
|
|
32462
|
+
for (const m of code.matchAll(/^\s*function\s+&?\s*(\w+)\s*\(/gm))
|
|
32463
|
+
if (!functions.has(m[1])) functions.set(m[1], toRel(root, f));
|
|
32464
|
+
}
|
|
32465
|
+
auto.functions = functions;
|
|
32466
|
+
return functions;
|
|
32467
|
+
}
|
|
32468
|
+
function resolvePhpClass(root, fromRel, fqcn) {
|
|
32469
|
+
const name = fqcn.replace(/^\\+/, "");
|
|
32470
|
+
if (!name) return void 0;
|
|
32471
|
+
const candidates2 = [];
|
|
32472
|
+
const auto = composerAutoload(root, resolve4(root, fromRel));
|
|
32473
|
+
if (auto?.psr4.length || auto?.psr0.length) {
|
|
32474
|
+
for (const { prefix, dirs } of auto.psr4) {
|
|
32475
|
+
if (!name.startsWith(prefix)) continue;
|
|
32476
|
+
const rest2 = name.slice(prefix.length).split("\\");
|
|
32477
|
+
for (const d of dirs) candidates2.push(join17(auto.dir, d, ...rest2));
|
|
32478
|
+
}
|
|
32479
|
+
for (const { prefix, dirs } of auto.psr0) {
|
|
32480
|
+
if (!name.startsWith(prefix)) continue;
|
|
32481
|
+
const cut = name.lastIndexOf("\\");
|
|
32482
|
+
const ns = cut === -1 ? [] : name.slice(0, cut).split("\\");
|
|
32483
|
+
const cls = name.slice(cut + 1).split("_");
|
|
32484
|
+
for (const d of dirs) candidates2.push(join17(auto.dir, d, ...ns, ...cls));
|
|
32485
|
+
}
|
|
32486
|
+
} else {
|
|
32487
|
+
const parts = name.split("\\");
|
|
32488
|
+
candidates2.push(resolve4(root, ...parts), resolve4(root, "src", ...parts));
|
|
32489
|
+
}
|
|
32490
|
+
for (const c of candidates2) {
|
|
32491
|
+
const f = `${c}.php`;
|
|
32492
|
+
if (isFile(f) && !relative3(root, f).startsWith("..")) return toRel(root, f);
|
|
32493
|
+
}
|
|
32494
|
+
return auto?.classmap.length ? classmapClasses(root, auto).get(name) : void 0;
|
|
32495
|
+
}
|
|
32496
|
+
function phpString(n) {
|
|
32497
|
+
if (!n || n.type !== "string" && n.type !== "encapsed_string") return void 0;
|
|
32498
|
+
if (n.namedChildren.some((c) => c && c.type !== "string_content" && c.type !== "escape_sequence"))
|
|
32499
|
+
return void 0;
|
|
32500
|
+
return literalText(n)?.replace(/\\(\\|')/g, "$1");
|
|
32501
|
+
}
|
|
32502
|
+
function argsOf(call) {
|
|
32503
|
+
const list2 = call.childForFieldName("arguments") ?? call.childForFieldName("parameters");
|
|
32504
|
+
return (list2?.namedChildren ?? []).filter((a) => a?.type === "argument").map((a) => a.namedChildren[a.namedChildren.length - 1]).filter((a) => !!a);
|
|
32505
|
+
}
|
|
32506
|
+
function positionalArgs(call) {
|
|
32507
|
+
const list2 = call.childForFieldName("arguments") ?? call.childForFieldName("parameters");
|
|
32508
|
+
return (list2?.namedChildren ?? []).filter((a) => a?.type === "argument" && !a.childForFieldName("name")).map((a) => a.namedChildren[0]).filter((a) => !!a);
|
|
32509
|
+
}
|
|
32510
|
+
function namedArg(call, name) {
|
|
32511
|
+
const list2 = call.childForFieldName("arguments") ?? call.childForFieldName("parameters");
|
|
32512
|
+
for (const a of list2?.namedChildren ?? []) {
|
|
32513
|
+
if (a?.type === "argument" && a.childForFieldName("name")?.text === name)
|
|
32514
|
+
return a.namedChildren[a.namedChildren.length - 1] ?? void 0;
|
|
32515
|
+
}
|
|
32516
|
+
return void 0;
|
|
32517
|
+
}
|
|
32518
|
+
function stringsOf(n) {
|
|
32519
|
+
if (!n) return [];
|
|
32520
|
+
if (n.type !== "array_creation_expression") {
|
|
32521
|
+
const s = phpString(n);
|
|
32522
|
+
return s === void 0 ? [] : [s];
|
|
32523
|
+
}
|
|
32524
|
+
return n.namedChildren.flatMap((el) => {
|
|
32525
|
+
const s = el?.namedChildren.length === 1 ? phpString(el.namedChildren[0]) : void 0;
|
|
32526
|
+
return s === void 0 ? [] : [s];
|
|
32527
|
+
});
|
|
32528
|
+
}
|
|
32529
|
+
function arrayElements(arr2) {
|
|
32530
|
+
const out = [];
|
|
32531
|
+
for (const el of arr2.namedChildren) {
|
|
32532
|
+
if (el?.type !== "array_element_initializer") continue;
|
|
32533
|
+
const kids = el.namedChildren.filter((c) => !!c);
|
|
32534
|
+
if (kids.length === 1) out.push({ value: kids[0] });
|
|
32535
|
+
else if (kids.length === 2) out.push({ key: phpString(kids[0]), value: kids[1] });
|
|
32536
|
+
}
|
|
32537
|
+
return out;
|
|
32538
|
+
}
|
|
32539
|
+
function handlerShaped(a) {
|
|
32540
|
+
if (!a) return false;
|
|
32541
|
+
switch (a.type) {
|
|
32542
|
+
case "anonymous_function":
|
|
32543
|
+
case "arrow_function":
|
|
32544
|
+
case "class_constant_access_expression":
|
|
32545
|
+
return true;
|
|
32546
|
+
case "string":
|
|
32547
|
+
case "encapsed_string":
|
|
32548
|
+
return CONTROLLER_STRING.test(phpString(a) ?? "");
|
|
32549
|
+
case "array_creation_expression":
|
|
32550
|
+
return arrayElements(a).some((e) => e.key ? e.key === "uses" : handlerShaped(e.value));
|
|
32551
|
+
default:
|
|
32552
|
+
return false;
|
|
32553
|
+
}
|
|
32554
|
+
}
|
|
32555
|
+
async function parsePhp(root, rel, code) {
|
|
32556
|
+
const p = await getParser("php");
|
|
32557
|
+
const tree = p.parse(code);
|
|
32558
|
+
if (!tree) return { nodes: [], edges: [], exports: [] };
|
|
32559
|
+
const fid = fileId(rel);
|
|
32560
|
+
const fileNode = {
|
|
32561
|
+
id: fid,
|
|
32562
|
+
kind: "file",
|
|
32563
|
+
name: rel,
|
|
32564
|
+
path: rel,
|
|
32565
|
+
source: "tree-sitter"
|
|
32566
|
+
};
|
|
32567
|
+
const nodes = [fileNode];
|
|
32568
|
+
const edges = [];
|
|
32569
|
+
const exportsList = [];
|
|
32570
|
+
const scope = /* @__PURE__ */ new Map();
|
|
32571
|
+
let classUses = /* @__PURE__ */ new Map();
|
|
32572
|
+
const usesByBlock = /* @__PURE__ */ new Map();
|
|
32573
|
+
const declared = /* @__PURE__ */ new Set();
|
|
32574
|
+
const localSyms = /* @__PURE__ */ new Map();
|
|
32575
|
+
const varTypes = /* @__PURE__ */ new Map();
|
|
32576
|
+
const propTypes = /* @__PURE__ */ new Map();
|
|
32577
|
+
const parents = /* @__PURE__ */ new Map();
|
|
32578
|
+
const imports = /* @__PURE__ */ new Map();
|
|
32579
|
+
const resolvedClasses = /* @__PURE__ */ new Map();
|
|
32580
|
+
const importedPkgs = /* @__PURE__ */ new Set();
|
|
32581
|
+
const refs = new RefCollector();
|
|
32582
|
+
let complexity = 0;
|
|
32583
|
+
let namespace = "";
|
|
32584
|
+
const namespaces = [];
|
|
32585
|
+
const pushEdge = (e) => edges.push({ confidence: 1, weight: 1, ...e });
|
|
32586
|
+
const auto = composerAutoload(root, resolve4(root, rel));
|
|
32587
|
+
const stmts = [];
|
|
32588
|
+
let block = 0;
|
|
32589
|
+
for (const stmt of tree.rootNode.namedChildren) {
|
|
32590
|
+
if (!stmt) continue;
|
|
32591
|
+
if (stmt.type === "namespace_definition") {
|
|
32592
|
+
block++;
|
|
32593
|
+
namespace = stmt.childForFieldName("name")?.text ?? "";
|
|
32594
|
+
if (namespace) addNamespace(stmt, namespace);
|
|
32595
|
+
for (const s of stmt.childForFieldName("body")?.namedChildren ?? [])
|
|
32596
|
+
if (s) stmts.push([s, namespace, block]);
|
|
32597
|
+
} else stmts.push([stmt, namespace, block]);
|
|
32598
|
+
}
|
|
32599
|
+
const enter = (ns, b) => {
|
|
32600
|
+
namespace = ns;
|
|
32601
|
+
let uses = usesByBlock.get(b);
|
|
32602
|
+
if (!uses) {
|
|
32603
|
+
uses = /* @__PURE__ */ new Map();
|
|
32604
|
+
usesByBlock.set(b, uses);
|
|
32605
|
+
}
|
|
32606
|
+
classUses = uses;
|
|
32607
|
+
};
|
|
32608
|
+
for (const [stmt] of stmts) {
|
|
32609
|
+
const name = CLASS_KINDS[stmt.type] ? stmt.childForFieldName("name")?.text : void 0;
|
|
32610
|
+
if (name) declared.add(name);
|
|
32611
|
+
}
|
|
32612
|
+
for (const [stmt, ns, b] of stmts) {
|
|
32613
|
+
enter(ns, b);
|
|
32614
|
+
if (stmt.type === "namespace_use_declaration") handleUse(stmt);
|
|
32615
|
+
}
|
|
32616
|
+
for (const [stmt, ns, b] of stmts) {
|
|
32617
|
+
enter(ns, b);
|
|
32618
|
+
if (stmt.type !== "namespace_use_declaration") collectDecls(stmt);
|
|
32619
|
+
}
|
|
32620
|
+
for (const [stmt, ns, b] of stmts) {
|
|
32621
|
+
enter(ns, b);
|
|
32622
|
+
if (stmt.type !== "namespace_use_declaration") walkRefs(stmt);
|
|
32623
|
+
}
|
|
32624
|
+
handleIncludes(tree.rootNode);
|
|
32625
|
+
edges.push(...imports.values());
|
|
32626
|
+
edges.push(...refs.edges());
|
|
32627
|
+
const errors = tree.rootNode.hasError ? tree.rootNode.descendantsOfType("ERROR").length : 0;
|
|
32628
|
+
fileNode.meta = {
|
|
32629
|
+
loc: tree.rootNode.endPosition.row + 1,
|
|
32630
|
+
complexity,
|
|
32631
|
+
...namespaces.length ? { php_namespace: namespaces[0] } : {},
|
|
32632
|
+
...errors ? { parse_errors: errors } : {}
|
|
32633
|
+
};
|
|
32634
|
+
return { nodes, edges, exports: exportsList, errors };
|
|
32635
|
+
function addNamespace(n, name) {
|
|
32636
|
+
if (namespaces.includes(name)) return;
|
|
32637
|
+
namespaces.push(name);
|
|
32638
|
+
const id = symId(rel, name);
|
|
32639
|
+
nodes.push({
|
|
32640
|
+
id,
|
|
32641
|
+
kind: "namespace",
|
|
32642
|
+
name,
|
|
32643
|
+
path: rel,
|
|
32644
|
+
startLine: n.startPosition.row + 1,
|
|
32645
|
+
endLine: n.endPosition.row + 1,
|
|
32646
|
+
source: "tree-sitter"
|
|
32647
|
+
});
|
|
32648
|
+
pushEdge({ src: fid, dst: id, kind: "defines", source: "tree-sitter" });
|
|
32649
|
+
}
|
|
32650
|
+
function addImport(dst, name, spec, line, conf) {
|
|
32651
|
+
const cur = imports.get(dst);
|
|
32652
|
+
if (cur?.meta) {
|
|
32653
|
+
const names = cur.meta.names;
|
|
32654
|
+
if (!names.includes(name)) names.push(name);
|
|
32655
|
+
return;
|
|
32656
|
+
}
|
|
32657
|
+
imports.set(dst, {
|
|
32658
|
+
src: fid,
|
|
32659
|
+
dst,
|
|
32660
|
+
kind: "imports",
|
|
32661
|
+
source: "tree-sitter",
|
|
32662
|
+
confidence: conf,
|
|
32663
|
+
weight: 1,
|
|
32664
|
+
meta: { names: [name], spec, line }
|
|
32665
|
+
});
|
|
32666
|
+
}
|
|
32667
|
+
function handleUse(decl) {
|
|
32668
|
+
const group = decl.childForFieldName("body");
|
|
32669
|
+
const prefix = group ? decl.namedChildren.find((c) => c?.type === "namespace_name")?.text : void 0;
|
|
32670
|
+
const declKind = decl.children.find((c) => c?.type === "function" || c?.type === "const")?.type;
|
|
32671
|
+
for (const c of (group ?? decl).namedChildren) {
|
|
32672
|
+
if (c?.type !== "namespace_use_clause") continue;
|
|
32673
|
+
const alias = c.childForFieldName("alias");
|
|
32674
|
+
const target = c.namedChildren.find(
|
|
32675
|
+
(x) => x && x.id !== alias?.id && (x.type === "qualified_name" || x.type === "name")
|
|
32676
|
+
);
|
|
32677
|
+
if (!target) continue;
|
|
32678
|
+
const fqn = (prefix ? `${prefix}\\${target.text}` : target.text).replace(/^\\+/, "");
|
|
32679
|
+
const local = alias?.text ?? lastSegment(fqn);
|
|
32680
|
+
const kind = c.children.find((x) => x?.type === "function" || x?.type === "const")?.type ?? declKind;
|
|
32681
|
+
const line = c.startPosition.row + 1;
|
|
32682
|
+
const label = alias ? `${fqn} as ${alias.text}` : fqn;
|
|
32683
|
+
const file3 = kind ? void 0 : classFile(fqn);
|
|
32684
|
+
if (file3) {
|
|
32685
|
+
if (file3 !== rel) addImport(fileId(file3), label, fqn, line, 0.95);
|
|
32686
|
+
} else {
|
|
32687
|
+
const top = fqn.split("\\")[0];
|
|
32688
|
+
importedPkgs.add(top);
|
|
32689
|
+
if (!imports.has(pkgId(top)))
|
|
32690
|
+
nodes.push({ id: pkgId(top), kind: "package", name: top, source: "tree-sitter" });
|
|
32691
|
+
addImport(pkgId(top), label, fqn, line, 1);
|
|
32692
|
+
}
|
|
32693
|
+
if (kind) scope.set(local, { id: pkgId(fqn.split("\\")[0]), confidence: 0.6 });
|
|
32694
|
+
else classUses.set(local, fqn);
|
|
32695
|
+
}
|
|
32696
|
+
}
|
|
32697
|
+
function handleIncludes(rootNode) {
|
|
32698
|
+
const here = dirname5(resolve4(root, rel));
|
|
32699
|
+
for (const inc of rootNode.descendantsOfType([
|
|
32700
|
+
"require_expression",
|
|
32701
|
+
"require_once_expression",
|
|
32702
|
+
"include_expression",
|
|
32703
|
+
"include_once_expression"
|
|
32704
|
+
])) {
|
|
32705
|
+
const arg = inc.namedChildren[0];
|
|
32706
|
+
if (!arg) continue;
|
|
32707
|
+
const candidates2 = [];
|
|
32708
|
+
const plain = phpString(arg);
|
|
32709
|
+
if (plain !== void 0) candidates2.push(resolve4(here, plain), resolve4(root, plain));
|
|
32710
|
+
else if (arg.type === "binary_expression") {
|
|
32711
|
+
const left = arg.childForFieldName("left")?.text.replace(/\s+/g, "") ?? "";
|
|
32712
|
+
const right = phpString(arg.childForFieldName("right"));
|
|
32713
|
+
const up = /^dirname\(__(?:DIR|FILE)__(?:,(\d+))?\)$/.exec(left);
|
|
32714
|
+
let base;
|
|
32715
|
+
if (left === "__DIR__" || left === "dirname(__FILE__)") base = here;
|
|
32716
|
+
else if (up) {
|
|
32717
|
+
base = here;
|
|
32718
|
+
for (let i = left.includes("__FILE__") ? 1 : 0; i < Number(up[1] ?? 1); i++)
|
|
32719
|
+
base = dirname5(base);
|
|
32720
|
+
}
|
|
32721
|
+
if (base && right !== void 0) candidates2.push(join17(base, right));
|
|
32722
|
+
}
|
|
32723
|
+
const hit = candidates2.find((c) => isFile(c) && !relative3(root, c).startsWith(".."));
|
|
32724
|
+
if (!hit) continue;
|
|
32725
|
+
const file3 = toRel(root, hit);
|
|
32726
|
+
if (file3 !== rel) addImport(fileId(file3), "*", arg.text, inc.startPosition.row + 1, 0.9);
|
|
32727
|
+
}
|
|
32728
|
+
}
|
|
32729
|
+
function classFile(fqcn) {
|
|
32730
|
+
let hit = resolvedClasses.get(fqcn);
|
|
32731
|
+
if (hit === void 0) {
|
|
32732
|
+
hit = resolvePhpClass(root, rel, fqcn) ?? null;
|
|
32733
|
+
resolvedClasses.set(fqcn, hit);
|
|
32734
|
+
}
|
|
32735
|
+
return hit ?? void 0;
|
|
32736
|
+
}
|
|
32737
|
+
function fqcnOf(name) {
|
|
32738
|
+
if (name.startsWith("\\")) return name.slice(1);
|
|
32739
|
+
const [head, ...rest2] = name.split("\\");
|
|
32740
|
+
const used = classUses.get(head);
|
|
32741
|
+
if (used) return [used, ...rest2].join("\\");
|
|
32742
|
+
return namespace ? `${namespace}\\${name}` : name;
|
|
32743
|
+
}
|
|
32744
|
+
function classRef(name) {
|
|
32745
|
+
if (!name || RELATIVE.has(name.toLowerCase())) return void 0;
|
|
32746
|
+
if (!name.includes("\\") && !classUses.has(name) && declared.has(name))
|
|
32747
|
+
return { id: symId(rel, name), confidence: 0.9 };
|
|
32748
|
+
const fqcn = fqcnOf(name);
|
|
32749
|
+
const file3 = classFile(fqcn);
|
|
32750
|
+
if (file3) return { id: symId(file3, lastSegment(fqcn)), confidence: file3 === rel ? 0.9 : 0.85 };
|
|
32751
|
+
const used = classUses.get(name.replace(/^\\/, "").split("\\")[0]);
|
|
32752
|
+
if (used && !name.startsWith("\\")) return { id: pkgId(used.split("\\")[0]), confidence: 0.6 };
|
|
32753
|
+
return void 0;
|
|
32754
|
+
}
|
|
32755
|
+
function typeRef(n) {
|
|
32756
|
+
if (!n) return void 0;
|
|
32757
|
+
const named = n.type === "named_type" ? n : n.descendantsOfType("named_type")[0];
|
|
32758
|
+
const t = named ? classRef(named.text) : void 0;
|
|
32759
|
+
return t?.id.startsWith("sym:") ? t.id : void 0;
|
|
32760
|
+
}
|
|
32761
|
+
function collectDecls(n) {
|
|
32762
|
+
if (CLASS_KINDS[n.type]) {
|
|
32763
|
+
collectClass(n);
|
|
32764
|
+
return;
|
|
32765
|
+
}
|
|
32766
|
+
switch (n.type) {
|
|
32767
|
+
case "function_definition": {
|
|
32768
|
+
const name = n.childForFieldName("name")?.text;
|
|
32769
|
+
if (name) addFunction(name, n);
|
|
32770
|
+
return;
|
|
32771
|
+
}
|
|
32772
|
+
case "if_statement": {
|
|
32773
|
+
for (const f of n.descendantsOfType("function_definition")) {
|
|
32774
|
+
const name = f.childForFieldName("name")?.text;
|
|
32775
|
+
if (name && !insideDecl(f, n)) addFunction(name, f);
|
|
32776
|
+
}
|
|
32777
|
+
return;
|
|
32778
|
+
}
|
|
32779
|
+
case "const_declaration": {
|
|
32780
|
+
for (const el of n.namedChildren) {
|
|
32781
|
+
const name = el?.type === "const_element" ? el.namedChildren[0]?.text : void 0;
|
|
32782
|
+
if (name) addSym(name, "variable", el, true);
|
|
32783
|
+
}
|
|
32784
|
+
return;
|
|
32785
|
+
}
|
|
32786
|
+
case "expression_statement": {
|
|
32787
|
+
const call = n.namedChildren[0];
|
|
32788
|
+
if (call?.type !== "function_call_expression") return;
|
|
32789
|
+
if (call.childForFieldName("function")?.text !== "define") return;
|
|
32790
|
+
const name = phpString(argsOf(call)[0]);
|
|
32791
|
+
if (name && /^\w+$/.test(name)) addSym(name, "variable", call, true);
|
|
32792
|
+
return;
|
|
32793
|
+
}
|
|
32794
|
+
default:
|
|
32795
|
+
return;
|
|
32796
|
+
}
|
|
32797
|
+
}
|
|
32798
|
+
function insideDecl(n, top) {
|
|
32799
|
+
for (let cur = n.parent; cur && cur.id !== top.id; cur = cur.parent) {
|
|
32800
|
+
if (cur.type === "function_definition" || cur.type === "method_declaration") return true;
|
|
32801
|
+
if (CLASS_KINDS[cur.type]) return true;
|
|
32802
|
+
}
|
|
32803
|
+
return false;
|
|
32804
|
+
}
|
|
32805
|
+
function addFunction(name, n) {
|
|
32806
|
+
addSym(name, "function", n, true);
|
|
32807
|
+
scope.set(name, { id: symId(rel, name), confidence: 0.9 });
|
|
32808
|
+
}
|
|
32809
|
+
function collectClass(n) {
|
|
32810
|
+
const name = n.childForFieldName("name")?.text;
|
|
32811
|
+
const kind = CLASS_KINDS[n.type];
|
|
32812
|
+
if (!name || !kind) return;
|
|
32813
|
+
addSym(name, kind, n, true);
|
|
32814
|
+
const self = symId(rel, name);
|
|
32815
|
+
for (const h of n.namedChildren) {
|
|
32816
|
+
if (h?.type !== "base_clause" && h?.type !== "class_interface_clause") continue;
|
|
32817
|
+
for (const b of h.namedChildren) {
|
|
32818
|
+
if (b?.type !== "name" && b?.type !== "qualified_name") continue;
|
|
32819
|
+
const target = heritage(self, b.text, h.type === "base_clause" ? "extends" : "implements");
|
|
32820
|
+
if (target && h.type === "base_clause" && kind === "class") parents.set(name, target);
|
|
32821
|
+
}
|
|
32822
|
+
}
|
|
32823
|
+
const body = n.childForFieldName("body");
|
|
32824
|
+
for (const m of body?.namedChildren ?? []) {
|
|
32825
|
+
if (!m) continue;
|
|
32826
|
+
switch (m.type) {
|
|
32827
|
+
case "method_declaration": {
|
|
32828
|
+
const mname = m.childForFieldName("name")?.text;
|
|
32829
|
+
if (!mname) break;
|
|
32830
|
+
addSym(`${name}.${mname}`, "method", m, isPublic(m));
|
|
32831
|
+
const params = m.childForFieldName("parameters")?.namedChildren ?? [];
|
|
32832
|
+
for (const prm of params) {
|
|
32833
|
+
if (prm?.type !== "property_promotion_parameter") continue;
|
|
32834
|
+
const pname = prm.childForFieldName("name")?.text.replace(/^\$/, "");
|
|
32835
|
+
if (!pname) continue;
|
|
32836
|
+
addSym(`${name}.${pname}`, "field", prm, isPublic(prm));
|
|
32837
|
+
const t = typeRef(prm.childForFieldName("type"));
|
|
32838
|
+
if (t) propTypes.set(`${name}.${pname}`, t);
|
|
32839
|
+
}
|
|
32840
|
+
break;
|
|
32841
|
+
}
|
|
32842
|
+
case "property_declaration": {
|
|
32843
|
+
const t = typeRef(m.childForFieldName("type"));
|
|
32844
|
+
for (const el of m.namedChildren) {
|
|
32845
|
+
if (el?.type !== "property_element") continue;
|
|
32846
|
+
const pname = el.childForFieldName("name")?.text.replace(/^\$/, "");
|
|
32847
|
+
if (!pname) continue;
|
|
32848
|
+
addSym(`${name}.${pname}`, "field", el, isPublic(m));
|
|
32849
|
+
if (t) propTypes.set(`${name}.${pname}`, t);
|
|
32850
|
+
}
|
|
32851
|
+
break;
|
|
32852
|
+
}
|
|
32853
|
+
case "const_declaration": {
|
|
32854
|
+
for (const el of m.namedChildren) {
|
|
32855
|
+
const cname = el?.type === "const_element" ? el.namedChildren[0]?.text : void 0;
|
|
32856
|
+
if (cname) addSym(`${name}.${cname}`, "field", el, isPublic(m));
|
|
32857
|
+
}
|
|
32858
|
+
break;
|
|
32859
|
+
}
|
|
32860
|
+
case "enum_case": {
|
|
32861
|
+
const cname = m.childForFieldName("name")?.text;
|
|
32862
|
+
if (cname) addSym(`${name}.${cname}`, "field", m, true);
|
|
32863
|
+
break;
|
|
32864
|
+
}
|
|
32865
|
+
case "use_declaration": {
|
|
32866
|
+
for (const t of m.namedChildren) {
|
|
32867
|
+
if (t?.type === "name" || t?.type === "qualified_name")
|
|
32868
|
+
heritage(self, t.text, "extends", { trait: true });
|
|
32869
|
+
}
|
|
32870
|
+
break;
|
|
32871
|
+
}
|
|
32872
|
+
default:
|
|
32873
|
+
break;
|
|
32874
|
+
}
|
|
32875
|
+
}
|
|
32876
|
+
attributeRoutes(n, name);
|
|
32877
|
+
}
|
|
32878
|
+
function heritage(src, name, kind, meta4) {
|
|
32879
|
+
const ref = classRef(name) ?? (name.startsWith("\\") && !name.slice(1).includes("\\") ? void 0 : { id: symId(rel, lastSegment(name)), confidence: 0.5 });
|
|
32880
|
+
if (!ref) return void 0;
|
|
32881
|
+
pushEdge({
|
|
32882
|
+
src,
|
|
32883
|
+
dst: ref.id,
|
|
32884
|
+
kind,
|
|
32885
|
+
source: "tree-sitter",
|
|
32886
|
+
confidence: ref.confidence,
|
|
32887
|
+
...meta4 ? { meta: meta4 } : {}
|
|
32888
|
+
});
|
|
32889
|
+
return ref.id.startsWith("sym:") ? ref.id : void 0;
|
|
32890
|
+
}
|
|
32891
|
+
function addSym(qualified, kind, n, exported) {
|
|
32892
|
+
const id = symId(rel, qualified);
|
|
32893
|
+
const c = kind === "function" || kind === "method" ? complexityOf(n, BRANCHES2, BOOL_OPS2) : 0;
|
|
32894
|
+
complexity += c;
|
|
32895
|
+
nodes.push({
|
|
32896
|
+
id,
|
|
32897
|
+
kind,
|
|
32898
|
+
name: qualified,
|
|
32899
|
+
path: rel,
|
|
32900
|
+
startLine: n.startPosition.row + 1,
|
|
32901
|
+
endLine: n.endPosition.row + 1,
|
|
32902
|
+
source: "tree-sitter",
|
|
32903
|
+
meta: {
|
|
32904
|
+
...exported ? { exported: true } : {},
|
|
32905
|
+
loc: n.endPosition.row - n.startPosition.row + 1,
|
|
32906
|
+
...c ? { complexity: c } : {}
|
|
32907
|
+
}
|
|
32908
|
+
});
|
|
32909
|
+
pushEdge({ src: fid, dst: id, kind: "defines", source: "tree-sitter" });
|
|
32910
|
+
if (exported) {
|
|
32911
|
+
exportsList.push(qualified);
|
|
32912
|
+
pushEdge({ src: fid, dst: id, kind: "exports", source: "tree-sitter" });
|
|
32913
|
+
}
|
|
32914
|
+
localSyms.set(qualified, id);
|
|
32915
|
+
}
|
|
32916
|
+
function enclosing(n) {
|
|
32917
|
+
let cur = n.parent;
|
|
32918
|
+
while (cur) {
|
|
32919
|
+
if (cur.type === "method_declaration") {
|
|
32920
|
+
const name = cur.childForFieldName("name")?.text;
|
|
32921
|
+
const owner = cur.parent?.parent?.childForFieldName("name")?.text;
|
|
32922
|
+
const q = owner && name ? `${owner}.${name}` : void 0;
|
|
32923
|
+
if (q && localSyms.has(q)) return q;
|
|
32924
|
+
}
|
|
32925
|
+
if (cur.type === "function_definition") {
|
|
32926
|
+
const name = cur.childForFieldName("name")?.text;
|
|
32927
|
+
if (name && localSyms.has(name)) return name;
|
|
32928
|
+
}
|
|
32929
|
+
cur = cur.parent;
|
|
32930
|
+
}
|
|
32931
|
+
return void 0;
|
|
32932
|
+
}
|
|
32933
|
+
function objectType(obj, caller) {
|
|
32934
|
+
const cls = classOf(caller);
|
|
32935
|
+
if (obj.type === "variable_name") {
|
|
32936
|
+
if (obj.text === "$this") return cls ? { id: symId(rel, cls), confidence: 0.9 } : void 0;
|
|
32937
|
+
const t = varTypes.get(`${caller ?? ""}\0${obj.text}`);
|
|
32938
|
+
return t ? { id: t, confidence: 0.8 } : void 0;
|
|
32939
|
+
}
|
|
32940
|
+
if (obj.type === "member_access_expression" || obj.type === "nullsafe_member_access_expression") {
|
|
32941
|
+
const inner = obj.childForFieldName("object");
|
|
32942
|
+
const prop = obj.childForFieldName("name")?.text;
|
|
32943
|
+
if (inner?.text !== "$this" || !cls || !prop) return void 0;
|
|
32944
|
+
const t = propTypes.get(`${cls}.${prop}`);
|
|
32945
|
+
return t ? { id: t, confidence: 0.8 } : void 0;
|
|
32946
|
+
}
|
|
32947
|
+
return void 0;
|
|
32948
|
+
}
|
|
32949
|
+
function scopeType(s, caller) {
|
|
32950
|
+
const cls = classOf(caller);
|
|
32951
|
+
if (s.type === "relative_scope" || RELATIVE.has(s.text.toLowerCase())) {
|
|
32952
|
+
const word = s.text.toLowerCase();
|
|
32953
|
+
if (!cls) return void 0;
|
|
32954
|
+
if (word === "parent") {
|
|
32955
|
+
const p2 = parents.get(cls);
|
|
32956
|
+
return p2 ? { id: p2, confidence: 0.8 } : void 0;
|
|
32957
|
+
}
|
|
32958
|
+
return { id: symId(rel, cls), confidence: 0.9 };
|
|
32959
|
+
}
|
|
32960
|
+
if (s.type === "name" || s.type === "qualified_name") return classRef(s.text);
|
|
32961
|
+
return void 0;
|
|
32962
|
+
}
|
|
32963
|
+
function callTarget(call, caller) {
|
|
32964
|
+
switch (call.type) {
|
|
32965
|
+
case "function_call_expression": {
|
|
32966
|
+
const fn = call.childForFieldName("function");
|
|
32967
|
+
if (fn?.type !== "name") return void 0;
|
|
32968
|
+
const local = scope.get(fn.text);
|
|
32969
|
+
if (local || !auto?.files.length) return local;
|
|
32970
|
+
const file3 = autoloadedFunctions(root, auto).get(fn.text);
|
|
32971
|
+
return file3 && file3 !== rel ? { id: symId(file3, fn.text), confidence: 0.8 } : void 0;
|
|
32972
|
+
}
|
|
32973
|
+
case "member_call_expression":
|
|
32974
|
+
case "nullsafe_member_call_expression": {
|
|
32975
|
+
const obj = call.childForFieldName("object");
|
|
32976
|
+
const m = call.childForFieldName("name");
|
|
32977
|
+
if (!obj || m?.type !== "name") return void 0;
|
|
32978
|
+
const t = objectType(obj, caller);
|
|
32979
|
+
return t ? { id: `${t.id}.${m.text}`, confidence: t.confidence } : void 0;
|
|
32980
|
+
}
|
|
32981
|
+
case "scoped_call_expression": {
|
|
32982
|
+
const s = call.childForFieldName("scope");
|
|
32983
|
+
const m = call.childForFieldName("name");
|
|
32984
|
+
if (!s || m?.type !== "name") return void 0;
|
|
32985
|
+
const t = scopeType(s, caller);
|
|
32986
|
+
if (t?.id.startsWith("pkg:")) return t;
|
|
32987
|
+
return t ? { id: `${t.id}.${m.text}`, confidence: t.confidence } : void 0;
|
|
32988
|
+
}
|
|
32989
|
+
case "object_creation_expression": {
|
|
32990
|
+
const cls = call.namedChildren.find(
|
|
32991
|
+
(c) => c?.type === "name" || c?.type === "qualified_name"
|
|
32992
|
+
);
|
|
32993
|
+
return cls ? classRef(cls.text) : void 0;
|
|
32994
|
+
}
|
|
32995
|
+
default:
|
|
32996
|
+
return void 0;
|
|
32997
|
+
}
|
|
32998
|
+
}
|
|
32999
|
+
function noteVarTypes(n) {
|
|
33000
|
+
for (const prm of n.descendantsOfType("simple_parameter")) {
|
|
33001
|
+
const t = typeRef(prm.childForFieldName("type"));
|
|
33002
|
+
const v = prm.childForFieldName("name")?.text;
|
|
33003
|
+
if (t && v) varTypes.set(`${enclosing(prm) ?? ""}\0${v}`, t);
|
|
33004
|
+
}
|
|
33005
|
+
for (const a of n.descendantsOfType("assignment_expression")) {
|
|
33006
|
+
const left = a.childForFieldName("left");
|
|
33007
|
+
const right = a.childForFieldName("right");
|
|
33008
|
+
if (left?.type !== "variable_name" || right?.type !== "object_creation_expression") continue;
|
|
33009
|
+
const cls = right.namedChildren.find(
|
|
33010
|
+
(c) => c?.type === "name" || c?.type === "qualified_name"
|
|
33011
|
+
);
|
|
33012
|
+
const t = cls ? classRef(cls.text) : void 0;
|
|
33013
|
+
if (t?.id.startsWith("sym:")) varTypes.set(`${enclosing(a) ?? ""}\0${left.text}`, t.id);
|
|
33014
|
+
}
|
|
33015
|
+
}
|
|
33016
|
+
function addRoute(method, path3, framework, line, handlers, confidence, extra = {}) {
|
|
33017
|
+
const id = routeId(method, path3);
|
|
33018
|
+
if (!nodes.some((n) => n.id === id)) {
|
|
33019
|
+
nodes.push({
|
|
33020
|
+
id,
|
|
33021
|
+
kind: "route",
|
|
33022
|
+
name: `${method} ${path3}`,
|
|
33023
|
+
path: rel,
|
|
33024
|
+
startLine: line,
|
|
33025
|
+
endLine: line,
|
|
33026
|
+
source: "tree-sitter",
|
|
33027
|
+
meta: { method, route: path3, framework, ...extra }
|
|
33028
|
+
});
|
|
33029
|
+
pushEdge({ src: fid, dst: id, kind: "defines", source: "tree-sitter" });
|
|
33030
|
+
}
|
|
33031
|
+
for (const h of handlers)
|
|
33032
|
+
pushEdge({ src: h, dst: id, kind: "handles", source: "tree-sitter", confidence });
|
|
33033
|
+
}
|
|
33034
|
+
function controllerRef(name, literal2 = false) {
|
|
33035
|
+
if (!literal2) {
|
|
33036
|
+
const direct = classRef(name);
|
|
33037
|
+
if (direct?.id.startsWith("sym:")) return direct;
|
|
33038
|
+
}
|
|
33039
|
+
const fq = name.replace(/^\\+/, "");
|
|
33040
|
+
const candidates2 = CONTROLLER_NAMESPACES.map((ns) => `${ns}\\${fq}`);
|
|
33041
|
+
if (literal2) candidates2.push(fq);
|
|
33042
|
+
for (const c of candidates2) {
|
|
33043
|
+
const file3 = classFile(c);
|
|
33044
|
+
if (file3) return { id: symId(file3, lastSegment(fq)), confidence: 0.85 };
|
|
33045
|
+
}
|
|
33046
|
+
return void 0;
|
|
33047
|
+
}
|
|
33048
|
+
function classConstant(n) {
|
|
33049
|
+
if (n.type !== "class_constant_access_expression") return void 0;
|
|
33050
|
+
const [cls, member] = n.namedChildren;
|
|
33051
|
+
if (!cls || member?.text !== "class") return void 0;
|
|
33052
|
+
const t = controllerRef(cls.text);
|
|
33053
|
+
return t?.id.startsWith("sym:") ? t : void 0;
|
|
33054
|
+
}
|
|
33055
|
+
function handlersOf(arg, caller, controller) {
|
|
33056
|
+
const handlers = [];
|
|
33057
|
+
let confidence = 0.9;
|
|
33058
|
+
const self = caller ? symId(rel, caller) : fid;
|
|
33059
|
+
const visit2 = (a) => {
|
|
33060
|
+
if (!a) return;
|
|
33061
|
+
if (a.type === "anonymous_function" || a.type === "arrow_function") {
|
|
33062
|
+
handlers.push(self);
|
|
33063
|
+
confidence = Math.min(confidence, 0.7);
|
|
33064
|
+
} else if (a.type === "class_constant_access_expression") {
|
|
33065
|
+
const t = classConstant(a);
|
|
33066
|
+
if (t) handlers.push(t.id);
|
|
33067
|
+
} else if (a.type === "string" || a.type === "encapsed_string") {
|
|
33068
|
+
const s = phpString(a) ?? "";
|
|
33069
|
+
const m = CONTROLLER_STRING.exec(s);
|
|
33070
|
+
const t = m ? controllerRef(m[1], true) : void 0;
|
|
33071
|
+
if (t && m) handlers.push(`${t.id}.${m[2]}`);
|
|
33072
|
+
else if (controller && /^\w+$/.test(s)) handlers.push(`${controller.id}.${s}`);
|
|
33073
|
+
} else if (a.type === "array_creation_expression") {
|
|
33074
|
+
const els = arrayElements(a);
|
|
33075
|
+
const [first, second] = els;
|
|
33076
|
+
const method = second && !second.key ? phpString(second.value) : void 0;
|
|
33077
|
+
const cls = first && !first.key ? classConstant(first.value) : void 0;
|
|
33078
|
+
if (els.length === 2 && cls && method) handlers.push(`${cls.id}.${method}`);
|
|
33079
|
+
else for (const el of els) if (!el.key || el.key === "uses") visit2(el.value);
|
|
33080
|
+
}
|
|
33081
|
+
};
|
|
33082
|
+
visit2(arg);
|
|
33083
|
+
if (!handlers.length) {
|
|
33084
|
+
handlers.push(self);
|
|
33085
|
+
confidence = 0.5;
|
|
33086
|
+
}
|
|
33087
|
+
return { handlers: [...new Set(handlers)], confidence };
|
|
33088
|
+
}
|
|
33089
|
+
function groupContext(n) {
|
|
33090
|
+
const prefixes = [];
|
|
33091
|
+
let controller;
|
|
33092
|
+
for (let cur = n.parent; cur; cur = cur.parent) {
|
|
33093
|
+
if (cur.type !== "anonymous_function" && cur.type !== "arrow_function") continue;
|
|
33094
|
+
const call = cur.parent?.type === "argument" ? cur.parent.parent?.parent : void 0;
|
|
33095
|
+
if (!call || !isCall(call) || call.childForFieldName("name")?.text !== "group") continue;
|
|
33096
|
+
const own2 = [];
|
|
33097
|
+
const first = argsOf(call)[0];
|
|
33098
|
+
const direct = first && first.id !== cur.id ? phpString(first) : void 0;
|
|
33099
|
+
if (direct !== void 0) own2.push(direct);
|
|
33100
|
+
else if (first?.type === "array_creation_expression") {
|
|
33101
|
+
const prefix = arrayElements(first).find((e) => e.key === "prefix");
|
|
33102
|
+
const s = prefix ? phpString(prefix.value) : void 0;
|
|
33103
|
+
if (s !== void 0) own2.push(s);
|
|
33104
|
+
}
|
|
33105
|
+
for (let link = call.childForFieldName("object"); link && isCall(link); ) {
|
|
33106
|
+
const name = link.childForFieldName("name")?.text;
|
|
33107
|
+
const a0 = argsOf(link)[0];
|
|
33108
|
+
if (name === "prefix") {
|
|
33109
|
+
const s = phpString(a0);
|
|
33110
|
+
if (s !== void 0) own2.unshift(s);
|
|
33111
|
+
} else if (name === "controller" && a0 && !controller) {
|
|
33112
|
+
const s = phpString(a0);
|
|
33113
|
+
controller = classConstant(a0) ?? (s ? controllerRef(s, true) : void 0);
|
|
33114
|
+
}
|
|
33115
|
+
link = link.childForFieldName("object");
|
|
33116
|
+
}
|
|
33117
|
+
prefixes.unshift(...own2);
|
|
33118
|
+
}
|
|
33119
|
+
return { prefixes, controller };
|
|
33120
|
+
}
|
|
33121
|
+
function laravelBase() {
|
|
33122
|
+
return /(^|\/)routes\/api\.php$/.test(rel) ? "/api" : void 0;
|
|
33123
|
+
}
|
|
33124
|
+
function routeFromCall(call, caller) {
|
|
33125
|
+
let facade = false;
|
|
33126
|
+
const chained = [];
|
|
33127
|
+
for (let link = call; link && isCall(link); ) {
|
|
33128
|
+
if (link.type === "scoped_call_expression") {
|
|
33129
|
+
facade = lastSegment(link.childForFieldName("scope")?.text ?? "") === "Route";
|
|
33130
|
+
break;
|
|
33131
|
+
}
|
|
33132
|
+
link = link.childForFieldName("object");
|
|
33133
|
+
if (link && isCall(link) && link.childForFieldName("name")?.text === "prefix") {
|
|
33134
|
+
const s = phpString(argsOf(link)[0]);
|
|
33135
|
+
if (s !== void 0) chained.unshift(s);
|
|
33136
|
+
}
|
|
33137
|
+
}
|
|
33138
|
+
const obj = call.type === "member_call_expression" ? call.childForFieldName("object") : null;
|
|
33139
|
+
const member = !facade && obj?.type === "variable_name" && obj.text !== "$this";
|
|
33140
|
+
if (!facade && !member) return;
|
|
33141
|
+
const verb = call.childForFieldName("name")?.text ?? "";
|
|
33142
|
+
const lower = verb.toLowerCase();
|
|
33143
|
+
const args = argsOf(call);
|
|
33144
|
+
if (facade && (verb === "resource" || verb === "apiResource")) {
|
|
33145
|
+
const name = phpString(args[0]);
|
|
33146
|
+
if (name) resourceRoutes(call, verb === "apiResource", name, args, caller, chained);
|
|
33147
|
+
return;
|
|
33148
|
+
}
|
|
33149
|
+
if (facade && (verb === "resources" || verb === "apiResources")) {
|
|
33150
|
+
if (args[0]?.type !== "array_creation_expression") return;
|
|
33151
|
+
for (const el of arrayElements(args[0])) {
|
|
33152
|
+
if (el.key)
|
|
33153
|
+
resourceRoutes(
|
|
33154
|
+
call,
|
|
33155
|
+
verb === "apiResources",
|
|
33156
|
+
el.key,
|
|
33157
|
+
[void 0, el.value, args[1]],
|
|
33158
|
+
caller,
|
|
33159
|
+
chained
|
|
33160
|
+
);
|
|
33161
|
+
}
|
|
33162
|
+
return;
|
|
33163
|
+
}
|
|
33164
|
+
let methods;
|
|
33165
|
+
let rest2;
|
|
33166
|
+
if (facade && lower === "view") {
|
|
33167
|
+
methods = ["GET"];
|
|
33168
|
+
rest2 = args.slice(0, 1);
|
|
33169
|
+
} else if (facade && (lower === "redirect" || lower === "permanentredirect")) {
|
|
33170
|
+
methods = ["ALL"];
|
|
33171
|
+
rest2 = args.slice(0, 1);
|
|
33172
|
+
} else if (lower === "match" || lower === "map") {
|
|
33173
|
+
methods = stringsOf(args[0]).map((m) => httpMethod(m)).filter((m) => !!m);
|
|
33174
|
+
rest2 = args.slice(1);
|
|
33175
|
+
} else if (lower === "any") {
|
|
33176
|
+
methods = ["ALL"];
|
|
33177
|
+
rest2 = args;
|
|
33178
|
+
} else if (ROUTE_METHODS.has(lower) && lower !== "all") {
|
|
33179
|
+
methods = [lower.toUpperCase()];
|
|
33180
|
+
rest2 = args;
|
|
33181
|
+
} else return;
|
|
33182
|
+
const raw = phpString(rest2[0]);
|
|
33183
|
+
if (raw === void 0 || !methods.length) return;
|
|
33184
|
+
if (member && (!handlerShaped(rest2[1]) || !raw.startsWith("/") && !ROUTER_VARS.has(obj.text)))
|
|
33185
|
+
return;
|
|
33186
|
+
const ctx = groupContext(call);
|
|
33187
|
+
const { handlers, confidence } = handlersOf(rest2[1], caller, ctx.controller);
|
|
33188
|
+
const path3 = phpRoutePath(facade ? laravelBase() : void 0, ...ctx.prefixes, ...chained, raw);
|
|
33189
|
+
const framework = facade ? "laravel" : frameworkOf(importedPkgs) ?? frameworkOf(auto?.require ?? []) ?? "http";
|
|
33190
|
+
for (const m of methods)
|
|
33191
|
+
addRoute(m, path3, framework, call.startPosition.row + 1, handlers, confidence);
|
|
33192
|
+
}
|
|
33193
|
+
function resourceRoutes(call, api, name, args, caller, chained) {
|
|
33194
|
+
let only;
|
|
33195
|
+
let except;
|
|
33196
|
+
if (args[2]?.type === "array_creation_expression") {
|
|
33197
|
+
for (const opt of arrayElements(args[2])) {
|
|
33198
|
+
if (opt.key === "only") only = stringsOf(opt.value);
|
|
33199
|
+
if (opt.key === "except") except = stringsOf(opt.value);
|
|
33200
|
+
}
|
|
33201
|
+
}
|
|
33202
|
+
for (let cur = call; cur.parent?.type === "member_call_expression"; cur = cur.parent) {
|
|
33203
|
+
if (cur.parent.childForFieldName("object")?.id !== cur.id) break;
|
|
33204
|
+
const m = cur.parent.childForFieldName("name")?.text;
|
|
33205
|
+
if (m === "only") only = stringsOf(argsOf(cur.parent)[0]);
|
|
33206
|
+
if (m === "except") except = stringsOf(argsOf(cur.parent)[0]);
|
|
33207
|
+
}
|
|
33208
|
+
const ctx = groupContext(call);
|
|
33209
|
+
const literal2 = phpString(args[1]);
|
|
33210
|
+
const cls = args[1] ? classConstant(args[1]) ?? (literal2 ? controllerRef(literal2, true) : void 0) : void 0;
|
|
33211
|
+
const self = caller ? symId(rel, caller) : fid;
|
|
33212
|
+
for (const spec of laravelResource(name, api, only, except)) {
|
|
33213
|
+
addRoute(
|
|
33214
|
+
spec.method,
|
|
33215
|
+
phpRoutePath(laravelBase(), ...ctx.prefixes, ...chained, spec.path),
|
|
33216
|
+
"laravel",
|
|
33217
|
+
call.startPosition.row + 1,
|
|
33218
|
+
[cls ? `${cls.id}.${spec.action}` : self],
|
|
33219
|
+
cls ? 0.9 : 0.5
|
|
33220
|
+
);
|
|
33221
|
+
}
|
|
33222
|
+
}
|
|
33223
|
+
function attributesOf(n) {
|
|
33224
|
+
return (n.childForFieldName("attributes")?.descendantsOfType("attribute") ?? []).map(
|
|
33225
|
+
(attr) => ({ attr, fq: fqcnOf(attr.namedChildren[0]?.text ?? "") })
|
|
33226
|
+
);
|
|
33227
|
+
}
|
|
33228
|
+
function routeSpecs(n) {
|
|
33229
|
+
const out = [];
|
|
33230
|
+
for (const { attr, fq } of attributesOf(n)) {
|
|
33231
|
+
const short = lastSegment(fq);
|
|
33232
|
+
if (fq.startsWith(SPATIE_ATTRIBUTES)) {
|
|
33233
|
+
const verb = short === "Any" ? "ALL" : httpMethod(short);
|
|
33234
|
+
const pos = positionalArgs(attr);
|
|
33235
|
+
const methods2 = short === "Route" ? stringsOf(namedArg(attr, "method") ?? pos.shift()).map((m) => httpMethod(m)).filter((m) => !!m) : verb ? [verb] : [];
|
|
33236
|
+
const path4 = phpString(namedArg(attr, "uri") ?? pos[0]);
|
|
33237
|
+
if (path4 === void 0) continue;
|
|
33238
|
+
for (const method of methods2) out.push({ method, path: path4, spatie: true });
|
|
33239
|
+
continue;
|
|
33240
|
+
}
|
|
33241
|
+
if (short !== "Route") continue;
|
|
33242
|
+
const path3 = phpString(namedArg(attr, "path")) ?? phpString(positionalArgs(attr)[0]);
|
|
33243
|
+
if (path3 === void 0) continue;
|
|
33244
|
+
const methods = stringsOf(namedArg(attr, "methods")).map((m) => httpMethod(m)).filter((m) => !!m);
|
|
33245
|
+
for (const method of methods.length ? methods : ["ALL"]) out.push({ method, path: path3 });
|
|
33246
|
+
}
|
|
33247
|
+
const doc = n.previousNamedSibling;
|
|
33248
|
+
if (doc?.type === "comment" && doc.text.includes("@Route"))
|
|
33249
|
+
out.push(...parseRouteAnnotations(doc.text));
|
|
33250
|
+
return out;
|
|
33251
|
+
}
|
|
33252
|
+
function attributeRoutes(cls, className) {
|
|
33253
|
+
const classSpecs = routeSpecs(cls);
|
|
33254
|
+
let prefix = classSpecs[0]?.path;
|
|
33255
|
+
const framework = frameworkOf(importedPkgs) ?? "symfony";
|
|
33256
|
+
const line = cls.startPosition.row + 1;
|
|
33257
|
+
for (const { attr, fq } of attributesOf(cls)) {
|
|
33258
|
+
if (!fq.startsWith(SPATIE_ATTRIBUTES)) continue;
|
|
33259
|
+
const short = lastSegment(fq);
|
|
33260
|
+
const pos = positionalArgs(attr);
|
|
33261
|
+
if (short === "Prefix") prefix = phpString(namedArg(attr, "prefix") ?? pos[0]) ?? prefix;
|
|
33262
|
+
if (short !== "Resource") continue;
|
|
33263
|
+
const name = phpString(namedArg(attr, "resource") ?? pos[0]);
|
|
33264
|
+
if (!name) continue;
|
|
33265
|
+
const api = (namedArg(attr, "apiResource") ?? pos[1])?.text === "true";
|
|
33266
|
+
const only = namedArg(attr, "only");
|
|
33267
|
+
const except = namedArg(attr, "except");
|
|
33268
|
+
for (const spec of laravelResource(
|
|
33269
|
+
name,
|
|
33270
|
+
api,
|
|
33271
|
+
only ? stringsOf(only) : void 0,
|
|
33272
|
+
except ? stringsOf(except) : void 0
|
|
33273
|
+
))
|
|
33274
|
+
addRoute(
|
|
33275
|
+
spec.method,
|
|
33276
|
+
phpRoutePath(prefix, spec.path),
|
|
33277
|
+
"laravel",
|
|
33278
|
+
line,
|
|
33279
|
+
[symId(rel, `${className}.${spec.action}`)],
|
|
33280
|
+
0.95
|
|
33281
|
+
);
|
|
33282
|
+
}
|
|
33283
|
+
for (const m of cls.childForFieldName("body")?.namedChildren ?? []) {
|
|
33284
|
+
if (m?.type !== "method_declaration") continue;
|
|
33285
|
+
const mname = m.childForFieldName("name")?.text;
|
|
33286
|
+
if (!mname) continue;
|
|
33287
|
+
const specs = routeSpecs(m);
|
|
33288
|
+
if (!specs.length && mname === "__invoke" && classSpecs.length) {
|
|
33289
|
+
for (const s of classSpecs)
|
|
33290
|
+
addRoute(
|
|
33291
|
+
s.method,
|
|
33292
|
+
phpRoutePath(s.path),
|
|
33293
|
+
framework,
|
|
33294
|
+
line,
|
|
33295
|
+
[symId(rel, `${className}.${mname}`)],
|
|
33296
|
+
0.95
|
|
33297
|
+
);
|
|
33298
|
+
continue;
|
|
33299
|
+
}
|
|
33300
|
+
for (const s of specs) {
|
|
33301
|
+
addRoute(
|
|
33302
|
+
s.method,
|
|
33303
|
+
phpRoutePath(prefix, s.path),
|
|
33304
|
+
s.spatie ? "laravel" : framework,
|
|
33305
|
+
m.startPosition.row + 1,
|
|
33306
|
+
[symId(rel, `${className}.${mname}`)],
|
|
33307
|
+
0.95
|
|
33308
|
+
);
|
|
33309
|
+
}
|
|
33310
|
+
}
|
|
33311
|
+
}
|
|
33312
|
+
function walkRefs(n) {
|
|
33313
|
+
noteVarTypes(n);
|
|
33314
|
+
for (const call of n.descendantsOfType(CALLS)) {
|
|
33315
|
+
const caller = enclosing(call);
|
|
33316
|
+
routeFromCall(call, caller);
|
|
33317
|
+
const target = callTarget(call, caller);
|
|
33318
|
+
if (!target) continue;
|
|
33319
|
+
refs.push(
|
|
33320
|
+
caller ? symId(rel, caller) : fid,
|
|
33321
|
+
target.id,
|
|
33322
|
+
"calls",
|
|
33323
|
+
target.confidence,
|
|
33324
|
+
call.startPosition.row + 1
|
|
33325
|
+
);
|
|
33326
|
+
}
|
|
33327
|
+
const named = [];
|
|
33328
|
+
for (const t of n.descendantsOfType("named_type")) named.push(t);
|
|
33329
|
+
for (const a of n.descendantsOfType([
|
|
33330
|
+
"class_constant_access_expression",
|
|
33331
|
+
"scoped_property_access_expression"
|
|
33332
|
+
])) {
|
|
33333
|
+
const s = a.namedChildren[0];
|
|
33334
|
+
if (s && (s.type === "name" || s.type === "qualified_name")) named.push(s);
|
|
33335
|
+
}
|
|
33336
|
+
for (const b of n.descendantsOfType("binary_expression")) {
|
|
33337
|
+
const right = b.childForFieldName("right");
|
|
33338
|
+
if (b.childForFieldName("operator")?.type === "instanceof" && right) named.push(right);
|
|
33339
|
+
}
|
|
33340
|
+
for (const t of named) {
|
|
33341
|
+
const target = classRef(t.text);
|
|
33342
|
+
if (!target) continue;
|
|
33343
|
+
const caller = enclosing(t);
|
|
33344
|
+
refs.push(
|
|
33345
|
+
caller ? symId(rel, caller) : fid,
|
|
33346
|
+
target.id,
|
|
33347
|
+
"references",
|
|
33348
|
+
target.confidence * 0.9,
|
|
33349
|
+
t.startPosition.row + 1
|
|
33350
|
+
);
|
|
33351
|
+
}
|
|
33352
|
+
for (const me of n.descendantsOfType([
|
|
33353
|
+
"member_access_expression",
|
|
33354
|
+
"nullsafe_member_access_expression"
|
|
33355
|
+
])) {
|
|
33356
|
+
if (me.childForFieldName("object")?.text !== "$this") continue;
|
|
33357
|
+
const caller = enclosing(me);
|
|
33358
|
+
const cls = classOf(caller);
|
|
33359
|
+
const prop = me.childForFieldName("name")?.text;
|
|
33360
|
+
if (!cls || !prop || !localSyms.has(`${cls}.${prop}`)) continue;
|
|
33361
|
+
refs.push(
|
|
33362
|
+
symId(rel, caller),
|
|
33363
|
+
symId(rel, `${cls}.${prop}`),
|
|
33364
|
+
"references",
|
|
33365
|
+
0.81,
|
|
33366
|
+
me.startPosition.row + 1
|
|
33367
|
+
);
|
|
33368
|
+
}
|
|
33369
|
+
}
|
|
33370
|
+
}
|
|
33371
|
+
var BRANCHES2, BOOL_OPS2, CALLS, CLASS_KINDS, RELATIVE, ROUTER_VARS, CONTROLLER_NAMESPACES, SPATIE_ATTRIBUTES, isFile, toRel, CLASSMAP_MAX_FILES, composerCache, asList, CONTROLLER_STRING, isPublic, classOf, lastSegment, isCall;
|
|
33372
|
+
var init_php = __esm({
|
|
33373
|
+
"src/graph/languages/php.ts"() {
|
|
33374
|
+
"use strict";
|
|
33375
|
+
init_esm_shims();
|
|
33376
|
+
init_ids();
|
|
33377
|
+
init_routes();
|
|
33378
|
+
init_parser();
|
|
33379
|
+
BRANCHES2 = [
|
|
33380
|
+
"if_statement",
|
|
33381
|
+
"else_if_clause",
|
|
33382
|
+
"for_statement",
|
|
33383
|
+
"foreach_statement",
|
|
33384
|
+
"while_statement",
|
|
33385
|
+
"do_statement",
|
|
33386
|
+
"case_statement",
|
|
33387
|
+
"catch_clause",
|
|
33388
|
+
"conditional_expression",
|
|
33389
|
+
"match_conditional_expression",
|
|
33390
|
+
"binary_expression"
|
|
33391
|
+
];
|
|
33392
|
+
BOOL_OPS2 = /* @__PURE__ */ new Set(["&&", "||", "and", "or", "??"]);
|
|
33393
|
+
CALLS = [
|
|
33394
|
+
"function_call_expression",
|
|
33395
|
+
"member_call_expression",
|
|
33396
|
+
"nullsafe_member_call_expression",
|
|
33397
|
+
"scoped_call_expression",
|
|
33398
|
+
"object_creation_expression"
|
|
33399
|
+
];
|
|
33400
|
+
CLASS_KINDS = {
|
|
33401
|
+
class_declaration: "class",
|
|
33402
|
+
interface_declaration: "interface",
|
|
33403
|
+
trait_declaration: "trait",
|
|
33404
|
+
enum_declaration: "enum"
|
|
33405
|
+
};
|
|
33406
|
+
RELATIVE = /* @__PURE__ */ new Set(["self", "static", "parent"]);
|
|
33407
|
+
ROUTER_VARS = /* @__PURE__ */ new Set(["$app", "$router", "$routes", "$route", "$group", "$r"]);
|
|
33408
|
+
CONTROLLER_NAMESPACES = ["App\\Http\\Controllers", "App\\Controllers"];
|
|
33409
|
+
SPATIE_ATTRIBUTES = "Spatie\\RouteAttributes\\Attributes\\";
|
|
33410
|
+
isFile = (p) => {
|
|
33411
|
+
try {
|
|
33412
|
+
return statSync2(p).isFile();
|
|
33413
|
+
} catch {
|
|
33414
|
+
return false;
|
|
33415
|
+
}
|
|
33416
|
+
};
|
|
33417
|
+
toRel = (root, abs) => normalize2(relative3(root, abs)).split("\\").join("/");
|
|
33418
|
+
CLASSMAP_MAX_FILES = 5e3;
|
|
33419
|
+
composerCache = /* @__PURE__ */ new Map();
|
|
33420
|
+
asList = (v) => Array.isArray(v) ? v : [v];
|
|
33421
|
+
CONTROLLER_STRING = /^\\?([\w\\]+?)(?:@|::?)(\w+)$/;
|
|
33422
|
+
isPublic = (m) => !m.namedChildren.some((c) => c?.type === "visibility_modifier" && c.text !== "public");
|
|
33423
|
+
classOf = (caller) => caller?.includes(".") ? caller.split(".")[0] : void 0;
|
|
33424
|
+
lastSegment = (name) => name.split("\\").pop() ?? name;
|
|
33425
|
+
isCall = (n) => n.type === "member_call_expression" || n.type === "scoped_call_expression";
|
|
33426
|
+
}
|
|
33427
|
+
});
|
|
33428
|
+
|
|
33429
|
+
// src/graph/languages/python.ts
|
|
33430
|
+
import { existsSync as existsSync15, statSync as statSync3 } from "fs";
|
|
33431
|
+
import { dirname as dirname6, join as join18, normalize as normalize3, relative as relative4, resolve as resolve5 } from "path";
|
|
32299
33432
|
function resolvePython(root, fromRel, module, level) {
|
|
32300
33433
|
const parts = module ? module.split(".") : [];
|
|
32301
33434
|
const candidates2 = [];
|
|
32302
33435
|
if (level > 0) {
|
|
32303
|
-
let base =
|
|
32304
|
-
for (let i = 1; i < level; i++) base =
|
|
32305
|
-
candidates2.push(
|
|
33436
|
+
let base = dirname6(resolve5(root, fromRel));
|
|
33437
|
+
for (let i = 1; i < level; i++) base = dirname6(base);
|
|
33438
|
+
candidates2.push(join18(base, ...parts));
|
|
32306
33439
|
} else {
|
|
32307
|
-
candidates2.push(
|
|
32308
|
-
let dir =
|
|
33440
|
+
candidates2.push(resolve5(root, ...parts), resolve5(root, "src", ...parts));
|
|
33441
|
+
let dir = dirname6(resolve5(root, fromRel));
|
|
32309
33442
|
while (dir.startsWith(root) && dir !== root) {
|
|
32310
|
-
candidates2.push(
|
|
32311
|
-
dir =
|
|
33443
|
+
candidates2.push(join18(dir, ...parts));
|
|
33444
|
+
dir = dirname6(dir);
|
|
32312
33445
|
}
|
|
32313
33446
|
}
|
|
32314
33447
|
for (const c of candidates2) {
|
|
32315
|
-
for (const f of [`${c}.py`,
|
|
32316
|
-
if (
|
|
32317
|
-
return
|
|
33448
|
+
for (const f of [`${c}.py`, join18(c, "__init__.py"), `${c}.pyi`]) {
|
|
33449
|
+
if (isFile2(f) && !relative4(root, f).startsWith(".."))
|
|
33450
|
+
return normalize3(relative4(root, f)).split("\\").join("/");
|
|
32318
33451
|
}
|
|
32319
33452
|
}
|
|
32320
33453
|
return void 0;
|
|
@@ -32351,7 +33484,7 @@ async function parsePython(root, rel, code) {
|
|
|
32351
33484
|
walkRefs(stmt);
|
|
32352
33485
|
}
|
|
32353
33486
|
edges.push(...refs.edges());
|
|
32354
|
-
if (!existsSync15(
|
|
33487
|
+
if (!existsSync15(join18(root, rel))) {
|
|
32355
33488
|
}
|
|
32356
33489
|
const errors = tree.rootNode.hasError ? tree.rootNode.descendantsOfType("ERROR").length : 0;
|
|
32357
33490
|
fileNode.meta = {
|
|
@@ -32504,7 +33637,7 @@ async function parsePython(root, rel, code) {
|
|
|
32504
33637
|
}
|
|
32505
33638
|
function addSym(qualified, kind, n, exported) {
|
|
32506
33639
|
const id = symId(rel, qualified);
|
|
32507
|
-
const c = kind === "function" || kind === "method" ? complexityOf(n,
|
|
33640
|
+
const c = kind === "function" || kind === "method" ? complexityOf(n, BRANCHES3, BOOL_OPS3) : 0;
|
|
32508
33641
|
complexity += c;
|
|
32509
33642
|
nodes.push({
|
|
32510
33643
|
id,
|
|
@@ -32647,7 +33780,7 @@ async function parsePython(root, rel, code) {
|
|
|
32647
33780
|
}
|
|
32648
33781
|
}
|
|
32649
33782
|
}
|
|
32650
|
-
var
|
|
33783
|
+
var BRANCHES3, BOOL_OPS3, isFile2, topModule, REF_SKIP_PARENT;
|
|
32651
33784
|
var init_python = __esm({
|
|
32652
33785
|
"src/graph/languages/python.ts"() {
|
|
32653
33786
|
"use strict";
|
|
@@ -32655,7 +33788,7 @@ var init_python = __esm({
|
|
|
32655
33788
|
init_ids();
|
|
32656
33789
|
init_routes();
|
|
32657
33790
|
init_parser();
|
|
32658
|
-
|
|
33791
|
+
BRANCHES3 = [
|
|
32659
33792
|
"if_statement",
|
|
32660
33793
|
"elif_clause",
|
|
32661
33794
|
"for_statement",
|
|
@@ -32666,10 +33799,10 @@ var init_python = __esm({
|
|
|
32666
33799
|
"case_clause",
|
|
32667
33800
|
"for_in_clause"
|
|
32668
33801
|
];
|
|
32669
|
-
|
|
32670
|
-
|
|
33802
|
+
BOOL_OPS3 = /* @__PURE__ */ new Set(["and", "or"]);
|
|
33803
|
+
isFile2 = (p) => {
|
|
32671
33804
|
try {
|
|
32672
|
-
return
|
|
33805
|
+
return statSync3(p).isFile();
|
|
32673
33806
|
} catch {
|
|
32674
33807
|
return false;
|
|
32675
33808
|
}
|
|
@@ -32698,21 +33831,21 @@ var init_python = __esm({
|
|
|
32698
33831
|
});
|
|
32699
33832
|
|
|
32700
33833
|
// src/graph/resolve.ts
|
|
32701
|
-
import { existsSync as existsSync16, readdirSync as
|
|
32702
|
-
import { dirname as
|
|
33834
|
+
import { existsSync as existsSync16, readdirSync as readdirSync7, readFileSync as readFileSync13, statSync as statSync4 } from "fs";
|
|
33835
|
+
import { dirname as dirname7, join as join19, normalize as normalize4, relative as relative5, resolve as resolve6 } from "path";
|
|
32703
33836
|
function loadTsPaths(root) {
|
|
32704
33837
|
const hit = tsPathsCache.get(root);
|
|
32705
33838
|
if (hit !== void 0) return hit;
|
|
32706
33839
|
let out = null;
|
|
32707
33840
|
try {
|
|
32708
|
-
const file3 =
|
|
33841
|
+
const file3 = join19(root, "tsconfig.json");
|
|
32709
33842
|
if (existsSync16(file3)) {
|
|
32710
|
-
const raw =
|
|
33843
|
+
const raw = readFileSync13(file3, "utf8").replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/.*$/gm, "").replace(/,(\s*[}\]])/g, "$1");
|
|
32711
33844
|
const cfg = JSON.parse(raw);
|
|
32712
33845
|
const co = cfg.compilerOptions ?? {};
|
|
32713
33846
|
if (co.paths) {
|
|
32714
33847
|
out = {
|
|
32715
|
-
baseUrl:
|
|
33848
|
+
baseUrl: resolve6(root, co.baseUrl ?? "."),
|
|
32716
33849
|
paths: Object.entries(co.paths).map(([k, v]) => {
|
|
32717
33850
|
const star = k.indexOf("*");
|
|
32718
33851
|
return star === -1 ? { prefix: k, suffix: "\0", targets: v } : { prefix: k.slice(0, star), suffix: k.slice(star + 1), targets: v };
|
|
@@ -32729,7 +33862,7 @@ function loadTsPaths(root) {
|
|
|
32729
33862
|
function probe(abs) {
|
|
32730
33863
|
const tryFile = (p) => {
|
|
32731
33864
|
try {
|
|
32732
|
-
return
|
|
33865
|
+
return statSync4(p).isFile() ? p : void 0;
|
|
32733
33866
|
} catch {
|
|
32734
33867
|
return void 0;
|
|
32735
33868
|
}
|
|
@@ -32745,16 +33878,16 @@ function probe(abs) {
|
|
|
32745
33878
|
if (tryFile(abs + ext)) return abs + ext;
|
|
32746
33879
|
}
|
|
32747
33880
|
for (const ext of EXTS) {
|
|
32748
|
-
const idx =
|
|
33881
|
+
const idx = join19(abs, `index${ext}`);
|
|
32749
33882
|
if (tryFile(idx)) return idx;
|
|
32750
33883
|
}
|
|
32751
33884
|
return void 0;
|
|
32752
33885
|
}
|
|
32753
33886
|
function readPackageJson(dir) {
|
|
32754
33887
|
try {
|
|
32755
|
-
const file3 =
|
|
33888
|
+
const file3 = join19(dir, "package.json");
|
|
32756
33889
|
if (!existsSync16(file3)) return void 0;
|
|
32757
|
-
return JSON.parse(
|
|
33890
|
+
return JSON.parse(readFileSync13(file3, "utf8"));
|
|
32758
33891
|
} catch {
|
|
32759
33892
|
return void 0;
|
|
32760
33893
|
}
|
|
@@ -32770,9 +33903,9 @@ function loadWorkspaces(root) {
|
|
|
32770
33903
|
if (Array.isArray(ws)) globs.push(...ws);
|
|
32771
33904
|
else if (ws?.packages) globs.push(...ws.packages);
|
|
32772
33905
|
try {
|
|
32773
|
-
const yaml =
|
|
33906
|
+
const yaml = join19(root, "pnpm-workspace.yaml");
|
|
32774
33907
|
if (existsSync16(yaml)) {
|
|
32775
|
-
for (const m of
|
|
33908
|
+
for (const m of readFileSync13(yaml, "utf8").matchAll(/^\s*-\s*['"]?([^'"\n#]+?)['"]?\s*$/gm))
|
|
32776
33909
|
globs.push(m[1].trim());
|
|
32777
33910
|
}
|
|
32778
33911
|
} catch {
|
|
@@ -32781,12 +33914,12 @@ function loadWorkspaces(root) {
|
|
|
32781
33914
|
if (g.startsWith("!")) continue;
|
|
32782
33915
|
const star = g.indexOf("*");
|
|
32783
33916
|
const dirs = [];
|
|
32784
|
-
if (star === -1) dirs.push(
|
|
33917
|
+
if (star === -1) dirs.push(resolve6(root, g));
|
|
32785
33918
|
else {
|
|
32786
|
-
const base =
|
|
33919
|
+
const base = resolve6(root, g.slice(0, star));
|
|
32787
33920
|
try {
|
|
32788
|
-
for (const d of
|
|
32789
|
-
if (d.isDirectory()) dirs.push(
|
|
33921
|
+
for (const d of readdirSync7(base, { withFileTypes: true })) {
|
|
33922
|
+
if (d.isDirectory()) dirs.push(join19(base, d.name));
|
|
32790
33923
|
}
|
|
32791
33924
|
} catch {
|
|
32792
33925
|
}
|
|
@@ -32840,16 +33973,16 @@ function resolvePackageEntry(dir, sub) {
|
|
|
32840
33973
|
} else candidates2.push(sub, `src/${sub}`, `lib/${sub}`);
|
|
32841
33974
|
for (const c of candidates2) {
|
|
32842
33975
|
if (c.includes("*")) continue;
|
|
32843
|
-
const hit = probe(
|
|
33976
|
+
const hit = probe(resolve6(dir, c));
|
|
32844
33977
|
if (hit) return hit;
|
|
32845
33978
|
}
|
|
32846
33979
|
return void 0;
|
|
32847
33980
|
}
|
|
32848
33981
|
function resolveImport(root, fromRel, spec) {
|
|
32849
33982
|
if (spec.startsWith(".") || spec.startsWith("/")) {
|
|
32850
|
-
const abs = spec.startsWith("/") ? spec :
|
|
33983
|
+
const abs = spec.startsWith("/") ? spec : resolve6(root, dirname7(fromRel), spec);
|
|
32851
33984
|
const hit = probe(abs);
|
|
32852
|
-
return hit ?
|
|
33985
|
+
return hit ? normalize4(relative5(root, hit)) : void 0;
|
|
32853
33986
|
}
|
|
32854
33987
|
const tp = loadTsPaths(root);
|
|
32855
33988
|
if (tp) {
|
|
@@ -32858,8 +33991,8 @@ function resolveImport(root, fromRel, spec) {
|
|
|
32858
33991
|
if (exact ? spec === p.prefix : spec.startsWith(p.prefix) && spec.endsWith(p.suffix)) {
|
|
32859
33992
|
const star = exact ? "" : spec.slice(p.prefix.length, spec.length - p.suffix.length);
|
|
32860
33993
|
for (const t of p.targets) {
|
|
32861
|
-
const hit = probe(
|
|
32862
|
-
if (hit) return
|
|
33994
|
+
const hit = probe(resolve6(tp.baseUrl, t.replace("*", star)));
|
|
33995
|
+
if (hit) return normalize4(relative5(root, hit));
|
|
32863
33996
|
}
|
|
32864
33997
|
}
|
|
32865
33998
|
}
|
|
@@ -32868,7 +34001,7 @@ function resolveImport(root, fromRel, spec) {
|
|
|
32868
34001
|
const dir = loadWorkspaces(root).get(name);
|
|
32869
34002
|
if (dir) {
|
|
32870
34003
|
const hit = resolvePackageEntry(dir, spec.slice(name.length + 1));
|
|
32871
|
-
if (hit && !
|
|
34004
|
+
if (hit && !relative5(root, hit).startsWith("..")) return normalize4(relative5(root, hit));
|
|
32872
34005
|
}
|
|
32873
34006
|
return void 0;
|
|
32874
34007
|
}
|
|
@@ -32932,6 +34065,7 @@ async function parseFile(root, rel, code) {
|
|
|
32932
34065
|
if (!grammar) return { nodes: [], edges: [], exports: [] };
|
|
32933
34066
|
if (grammar === "python") return parsePython(root, rel, code);
|
|
32934
34067
|
if (grammar === "go") return parseGo(root, rel, code);
|
|
34068
|
+
if (grammar === "php") return parsePhp(root, rel, code);
|
|
32935
34069
|
const p = await getParser(grammar);
|
|
32936
34070
|
const tree = p.parse(code);
|
|
32937
34071
|
if (!tree) return { nodes: [], edges: [], exports: [] };
|
|
@@ -33524,6 +34658,7 @@ var init_treesitter = __esm({
|
|
|
33524
34658
|
init_ids();
|
|
33525
34659
|
init_go();
|
|
33526
34660
|
init_parser();
|
|
34661
|
+
init_php();
|
|
33527
34662
|
init_python();
|
|
33528
34663
|
init_resolve();
|
|
33529
34664
|
init_routes();
|
|
@@ -33789,6 +34924,11 @@ function inherited(db, classId, member, depth) {
|
|
|
33789
34924
|
}
|
|
33790
34925
|
return void 0;
|
|
33791
34926
|
}
|
|
34927
|
+
function ownerAncestor(db, path3, name) {
|
|
34928
|
+
const dot = name.indexOf(".");
|
|
34929
|
+
const owner = symId(path3, name.slice(0, dot));
|
|
34930
|
+
return db.graphGetNode(owner) ? inherited(db, owner, name.slice(dot + 1), 0) : void 0;
|
|
34931
|
+
}
|
|
33792
34932
|
function defaultExportOf(db, path3) {
|
|
33793
34933
|
const f = db.graphGetNode(fileId(path3));
|
|
33794
34934
|
if (!f?.meta) return void 0;
|
|
@@ -33837,6 +34977,10 @@ function linkGraph(db) {
|
|
|
33837
34977
|
direct = true;
|
|
33838
34978
|
} else target = goSibling(db, path3, head);
|
|
33839
34979
|
}
|
|
34980
|
+
if (!target && name.includes(".")) {
|
|
34981
|
+
target = ownerAncestor(db, path3, name);
|
|
34982
|
+
direct = true;
|
|
34983
|
+
}
|
|
33840
34984
|
if (target && name.includes(".") && !direct) {
|
|
33841
34985
|
const rest3 = name.slice(name.indexOf(".") + 1);
|
|
33842
34986
|
const member = `${target}.${rest3}`;
|
|
@@ -33875,7 +35019,7 @@ function linkGraph(db) {
|
|
|
33875
35019
|
const name = meta4.unresolved;
|
|
33876
35020
|
if (!name) continue;
|
|
33877
35021
|
const id = symId(e.dst.slice(5), name);
|
|
33878
|
-
const target = db.graphGetNode(id) ? id : viaReexports(db, e.dst.slice(5), name.split(".")[0], 0, /* @__PURE__ */ new Set());
|
|
35022
|
+
const target = db.graphGetNode(id) ? id : viaReexports(db, e.dst.slice(5), name.split(".")[0], 0, /* @__PURE__ */ new Set()) ?? (name.includes(".") ? ownerAncestor(db, e.dst.slice(5), name) : void 0);
|
|
33879
35023
|
if (!target || target === e.src) continue;
|
|
33880
35024
|
const { unresolved: _u, ...rest2 } = meta4;
|
|
33881
35025
|
replace(db, e, {
|
|
@@ -33918,8 +35062,8 @@ var init_link = __esm({
|
|
|
33918
35062
|
});
|
|
33919
35063
|
|
|
33920
35064
|
// src/graph/reindex.ts
|
|
33921
|
-
import { readFileSync as
|
|
33922
|
-
import { join as
|
|
35065
|
+
import { readFileSync as readFileSync14, statSync as statSync5 } from "fs";
|
|
35066
|
+
import { join as join20 } from "path";
|
|
33923
35067
|
function listCodeFiles(ctx) {
|
|
33924
35068
|
return import_fast_glob2.default.sync(ctx.config.graph.include, {
|
|
33925
35069
|
cwd: ctx.root,
|
|
@@ -33930,8 +35074,8 @@ function listCodeFiles(ctx) {
|
|
|
33930
35074
|
});
|
|
33931
35075
|
}
|
|
33932
35076
|
async function indexOne(ctx, db, rel, force) {
|
|
33933
|
-
const abs =
|
|
33934
|
-
const st =
|
|
35077
|
+
const abs = join20(ctx.root, rel);
|
|
35078
|
+
const st = statSync5(abs);
|
|
33935
35079
|
const backend = getCodeBackend(ctx);
|
|
33936
35080
|
const language = backend.languageOf(rel) ?? null;
|
|
33937
35081
|
if (st.size > MAX_CODE_BYTES) {
|
|
@@ -33950,7 +35094,7 @@ async function indexOne(ctx, db, rel, force) {
|
|
|
33950
35094
|
}
|
|
33951
35095
|
return "skipped";
|
|
33952
35096
|
}
|
|
33953
|
-
const code =
|
|
35097
|
+
const code = readFileSync14(abs, "utf8");
|
|
33954
35098
|
const hash2 = hashText(code);
|
|
33955
35099
|
const prev = db.fileIndexGet(rel);
|
|
33956
35100
|
if (!force && prev?.hash === hash2 && prev.status !== "skipped") return "unchanged";
|
|
@@ -34031,7 +35175,7 @@ async function reindexCode(ctx, db, opts = {}) {
|
|
|
34031
35175
|
summary.unsupported++;
|
|
34032
35176
|
if (db.fileIndexGet(rel)?.status === "unsupported") continue;
|
|
34033
35177
|
try {
|
|
34034
|
-
const st =
|
|
35178
|
+
const st = statSync5(join20(ctx.root, rel));
|
|
34035
35179
|
db.fileIndexSet({
|
|
34036
35180
|
path: rel,
|
|
34037
35181
|
hash: "",
|
|
@@ -34047,7 +35191,7 @@ async function reindexCode(ctx, db, opts = {}) {
|
|
|
34047
35191
|
}
|
|
34048
35192
|
for (const rel of targets) {
|
|
34049
35193
|
try {
|
|
34050
|
-
if (!statSafe(
|
|
35194
|
+
if (!statSafe(join20(ctx.root, rel))) {
|
|
34051
35195
|
if (db.fileIndexGet(rel)) {
|
|
34052
35196
|
removeOne(db, rel);
|
|
34053
35197
|
summary.removed++;
|
|
@@ -34059,7 +35203,7 @@ async function reindexCode(ctx, db, opts = {}) {
|
|
|
34059
35203
|
const error62 = err.message;
|
|
34060
35204
|
summary.errors.push({ path: rel, error: error62 });
|
|
34061
35205
|
try {
|
|
34062
|
-
const st =
|
|
35206
|
+
const st = statSync5(join20(ctx.root, rel));
|
|
34063
35207
|
db.fileIndexSet({
|
|
34064
35208
|
path: rel,
|
|
34065
35209
|
hash: "",
|
|
@@ -34122,7 +35266,7 @@ async function reindexCode(ctx, db, opts = {}) {
|
|
|
34122
35266
|
}
|
|
34123
35267
|
function statSafe(p) {
|
|
34124
35268
|
try {
|
|
34125
|
-
return
|
|
35269
|
+
return statSync5(p).isFile();
|
|
34126
35270
|
} catch {
|
|
34127
35271
|
return false;
|
|
34128
35272
|
}
|
|
@@ -34162,10 +35306,10 @@ function indexFreshness(ctx, db) {
|
|
|
34162
35306
|
const dirty = [];
|
|
34163
35307
|
for (const r of rows.slice(0, 400)) {
|
|
34164
35308
|
try {
|
|
34165
|
-
const abs =
|
|
34166
|
-
const st =
|
|
35309
|
+
const abs = join20(ctx.root, r.path);
|
|
35310
|
+
const st = statSync5(abs);
|
|
34167
35311
|
if (r.mtime !== null && Math.abs(st.mtimeMs - r.mtime) < 1 && st.size === r.size) continue;
|
|
34168
|
-
if (hashText(
|
|
35312
|
+
if (hashText(readFileSync14(abs, "utf8")) !== r.hash) dirty.push(r.path);
|
|
34169
35313
|
} catch {
|
|
34170
35314
|
dirty.push(r.path);
|
|
34171
35315
|
}
|
|
@@ -34199,22 +35343,22 @@ var init_reindex = __esm({
|
|
|
34199
35343
|
});
|
|
34200
35344
|
|
|
34201
35345
|
// src/session/current.ts
|
|
34202
|
-
import { readFileSync as
|
|
34203
|
-
import { join as
|
|
35346
|
+
import { readFileSync as readFileSync15, statSync as statSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
35347
|
+
import { join as join21 } from "path";
|
|
34204
35348
|
function file2(ctx) {
|
|
34205
|
-
return
|
|
35349
|
+
return join21(projectDir(ctx.id), "current-session");
|
|
34206
35350
|
}
|
|
34207
35351
|
function getCurrentSession(ctx) {
|
|
34208
35352
|
if (process.env.EMINIAR_SESSION_ID) return process.env.EMINIAR_SESSION_ID;
|
|
34209
35353
|
const f = file2(ctx);
|
|
34210
35354
|
let mtimeMs;
|
|
34211
35355
|
try {
|
|
34212
|
-
mtimeMs =
|
|
35356
|
+
mtimeMs = statSync6(f).mtimeMs;
|
|
34213
35357
|
} catch {
|
|
34214
35358
|
return "unknown";
|
|
34215
35359
|
}
|
|
34216
35360
|
if (cached4 && cached4.file === f && cached4.mtimeMs === mtimeMs) return cached4.id;
|
|
34217
|
-
const id =
|
|
35361
|
+
const id = readFileSync15(f, "utf8").trim() || "unknown";
|
|
34218
35362
|
cached4 = { file: f, mtimeMs, id };
|
|
34219
35363
|
return id;
|
|
34220
35364
|
}
|
|
@@ -34383,13 +35527,13 @@ __export(store_exports, {
|
|
|
34383
35527
|
import {
|
|
34384
35528
|
existsSync as existsSync17,
|
|
34385
35529
|
mkdirSync as mkdirSync7,
|
|
34386
|
-
readFileSync as
|
|
35530
|
+
readFileSync as readFileSync16,
|
|
34387
35531
|
renameSync,
|
|
34388
|
-
statSync as
|
|
35532
|
+
statSync as statSync7,
|
|
34389
35533
|
unlinkSync,
|
|
34390
35534
|
writeFileSync as writeFileSync7
|
|
34391
35535
|
} from "fs";
|
|
34392
|
-
import { dirname as
|
|
35536
|
+
import { dirname as dirname8, join as join22, relative as relative6, sep } from "path";
|
|
34393
35537
|
function isStatus(s) {
|
|
34394
35538
|
return typeof s === "string" && MEMORY_STATUSES.includes(s);
|
|
34395
35539
|
}
|
|
@@ -34403,16 +35547,16 @@ function memoriesRel(ctx) {
|
|
|
34403
35547
|
return ctx.config.memories.dir.replace(/^\.\//, "").replace(/\/+$/, "");
|
|
34404
35548
|
}
|
|
34405
35549
|
function memoriesDir(ctx) {
|
|
34406
|
-
return
|
|
35550
|
+
return join22(ctx.root, memoriesRel(ctx));
|
|
34407
35551
|
}
|
|
34408
35552
|
function relOf(ctx, name) {
|
|
34409
35553
|
return `${memoriesRel(ctx)}/${name}.md`;
|
|
34410
35554
|
}
|
|
34411
35555
|
function absOf(ctx, name) {
|
|
34412
|
-
return
|
|
35556
|
+
return join22(ctx.root, relOf(ctx, name));
|
|
34413
35557
|
}
|
|
34414
35558
|
function nameOfFile(ctx, abs) {
|
|
34415
|
-
const rel =
|
|
35559
|
+
const rel = relative6(memoriesDir(ctx), abs).split(sep).join("/");
|
|
34416
35560
|
if (!rel.endsWith(".md")) return void 0;
|
|
34417
35561
|
const name = rel.slice(0, -3);
|
|
34418
35562
|
return isValidName(name) ? name : void 0;
|
|
@@ -34451,7 +35595,7 @@ function normalizeRelated(ids) {
|
|
|
34451
35595
|
}
|
|
34452
35596
|
function writeMemoryFile(ctx, name, meta4, body) {
|
|
34453
35597
|
const abs = absOf(ctx, name);
|
|
34454
|
-
mkdirSync7(
|
|
35598
|
+
mkdirSync7(dirname8(abs), { recursive: true });
|
|
34455
35599
|
writeFileSync7(abs, serializeMemoryFile(meta4, `
|
|
34456
35600
|
${body.trim()}
|
|
34457
35601
|
`));
|
|
@@ -34496,12 +35640,12 @@ function syncFile(ctx, db, name, opts = {}) {
|
|
|
34496
35640
|
const abs = absOf(ctx, name);
|
|
34497
35641
|
const rel = relOf(ctx, name);
|
|
34498
35642
|
if (!existsSync17(abs)) return void 0;
|
|
34499
|
-
let st =
|
|
35643
|
+
let st = statSync7(abs);
|
|
34500
35644
|
const row = db.memoryGetByName(name);
|
|
34501
35645
|
const mtime = Math.floor(st.mtimeMs);
|
|
34502
35646
|
if (!opts.force && row && row.file_mtime === mtime && row.file_size === st.size)
|
|
34503
35647
|
return { changed: false, row };
|
|
34504
|
-
let text3 =
|
|
35648
|
+
let text3 = readFileSync16(abs, "utf8");
|
|
34505
35649
|
let hash2 = hashText(text3);
|
|
34506
35650
|
if (!opts.force && row && row.file_hash === hash2) {
|
|
34507
35651
|
db.memoryUpsertByName({
|
|
@@ -34540,7 +35684,7 @@ function syncFile(ctx, db, name, opts = {}) {
|
|
|
34540
35684
|
${parsed.body.replace(/^\s*\n/, "")}`
|
|
34541
35685
|
);
|
|
34542
35686
|
writeFileSync7(abs, text3);
|
|
34543
|
-
st =
|
|
35687
|
+
st = statSync7(abs);
|
|
34544
35688
|
hash2 = hashText(text3);
|
|
34545
35689
|
}
|
|
34546
35690
|
const body = parsed.body.trim();
|
|
@@ -34578,7 +35722,7 @@ tags: ${tags.join(", ")}` : ""}`,
|
|
|
34578
35722
|
function setMeta(ctx, db, name, patch) {
|
|
34579
35723
|
const abs = absOf(ctx, name);
|
|
34580
35724
|
if (!existsSync17(abs)) return false;
|
|
34581
|
-
const parsed = parseMemoryFile(
|
|
35725
|
+
const parsed = parseMemoryFile(readFileSync16(abs, "utf8"));
|
|
34582
35726
|
const meta4 = { ...parsed.meta };
|
|
34583
35727
|
for (const [k, v] of Object.entries(patch)) {
|
|
34584
35728
|
if (v === void 0) delete meta4[k];
|
|
@@ -34627,7 +35771,7 @@ function reconcile(ctx, db) {
|
|
|
34627
35771
|
for (const f of files) {
|
|
34628
35772
|
const name = nameOfFile(ctx, f);
|
|
34629
35773
|
if (!name) {
|
|
34630
|
-
out.skipped.push(
|
|
35774
|
+
out.skipped.push(relative6(ctx.root, f));
|
|
34631
35775
|
continue;
|
|
34632
35776
|
}
|
|
34633
35777
|
seen.add(name);
|
|
@@ -34659,7 +35803,7 @@ function write(ctx, db, input2) {
|
|
|
34659
35803
|
let extra = {};
|
|
34660
35804
|
if (exists) {
|
|
34661
35805
|
try {
|
|
34662
|
-
extra = parseMemoryFile(
|
|
35806
|
+
extra = parseMemoryFile(readFileSync16(abs, "utf8")).meta;
|
|
34663
35807
|
} catch {
|
|
34664
35808
|
}
|
|
34665
35809
|
}
|
|
@@ -34686,7 +35830,7 @@ function write(ctx, db, input2) {
|
|
|
34686
35830
|
function read(ctx, db, name) {
|
|
34687
35831
|
const synced = syncFile(ctx, db, name);
|
|
34688
35832
|
if (!synced) return void 0;
|
|
34689
|
-
const parsed = parseMemoryFile(
|
|
35833
|
+
const parsed = parseMemoryFile(readFileSync16(absOf(ctx, name), "utf8"));
|
|
34690
35834
|
return {
|
|
34691
35835
|
name,
|
|
34692
35836
|
kind: synced.row.kind,
|
|
@@ -34713,7 +35857,7 @@ function edit(ctx, db, name, opts) {
|
|
|
34713
35857
|
const abs = absOf(ctx, name);
|
|
34714
35858
|
if (!existsSync17(abs)) throw new Error(`memory "${name}" does not exist`);
|
|
34715
35859
|
syncFile(ctx, db, name);
|
|
34716
|
-
const parsed = parseMemoryFile(
|
|
35860
|
+
const parsed = parseMemoryFile(readFileSync16(abs, "utf8"));
|
|
34717
35861
|
const re = new RegExp(opts.regex ? opts.find : escapeRe(opts.find), "g");
|
|
34718
35862
|
const count = (parsed.body.match(re) ?? []).length;
|
|
34719
35863
|
if (!count) return 0;
|
|
@@ -34731,9 +35875,9 @@ function rename(ctx, db, from, to) {
|
|
|
34731
35875
|
const absTo = absOf(ctx, to);
|
|
34732
35876
|
if (!existsSync17(absFrom)) throw new Error(`memory "${from}" does not exist`);
|
|
34733
35877
|
if (existsSync17(absTo) || db.memoryGetByName(to)) throw new Error(`memory "${to}" already exists`);
|
|
34734
|
-
mkdirSync7(
|
|
35878
|
+
mkdirSync7(dirname8(absTo), { recursive: true });
|
|
34735
35879
|
renameSync(absFrom, absTo);
|
|
34736
|
-
const parsed = parseMemoryFile(
|
|
35880
|
+
const parsed = parseMemoryFile(readFileSync16(absTo, "utf8"));
|
|
34737
35881
|
writeFileSync7(
|
|
34738
35882
|
absTo,
|
|
34739
35883
|
serializeMemoryFile(
|
|
@@ -34749,7 +35893,7 @@ function rename(ctx, db, from, to) {
|
|
|
34749
35893
|
if (!row.name || row.name === to) continue;
|
|
34750
35894
|
const abs = absOf(ctx, row.name);
|
|
34751
35895
|
if (!existsSync17(abs)) continue;
|
|
34752
|
-
const text3 =
|
|
35896
|
+
const text3 = readFileSync16(abs, "utf8");
|
|
34753
35897
|
if (!ref.test(text3)) continue;
|
|
34754
35898
|
ref.lastIndex = 0;
|
|
34755
35899
|
writeFileSync7(abs, text3.replace(ref, `mem:${to}`));
|
|
@@ -34768,11 +35912,11 @@ function remove(ctx, db, name) {
|
|
|
34768
35912
|
function gitignoreHint(ctx) {
|
|
34769
35913
|
if (hinted.has(ctx.root)) return void 0;
|
|
34770
35914
|
hinted.add(ctx.root);
|
|
34771
|
-
const file3 =
|
|
35915
|
+
const file3 = join22(ctx.root, ".gitignore");
|
|
34772
35916
|
if (!existsSync17(file3)) return void 0;
|
|
34773
35917
|
const dir = memoriesRel(ctx);
|
|
34774
35918
|
const top = dir.split("/")[0];
|
|
34775
|
-
const ignored =
|
|
35919
|
+
const ignored = readFileSync16(file3, "utf8").split(/\r?\n/).map((l) => l.trim().replace(/^\//, "").replace(/\/$/, "")).some((l) => l === top || l === dir || l === `${top}/**` || l === `${dir}/**`);
|
|
34776
35920
|
return ignored ? `note: .gitignore ignores ${dir}, so memories stay local to this machine (remove that line to share them with the team)` : void 0;
|
|
34777
35921
|
}
|
|
34778
35922
|
var import_fast_glob3, MEMORY_KINDS, SEGMENT, NAME_RE, hinted;
|
|
@@ -34846,7 +35990,7 @@ var init_em_graph = __esm({
|
|
|
34846
35990
|
});
|
|
34847
35991
|
emGraphQuery = defineTool({
|
|
34848
35992
|
name: "em_graph_query",
|
|
34849
|
-
description: 'Look up nodes in the project knowledge graph (files, functions, classes, routes, packages, people, concepts, memories) by name/path, run a preset (hotspots, coupled, orphans, dead_code, routes, cycles, clusters, stats), or run a structured pattern: start at nodes matching `from`, walk `edge.kinds` in `edge.direction` for `edge.depth` hops, keep what matches `to` \u2014 e.g. {from:{kind:"route"}, edge:{kinds:["handles"], direction:"in"}} lists every route handler, {from:{name:"^parseFile$"}, edge:{kinds:["calls"], direction:"in", depth:2}} its callers two levels up. Built from tree-sitter (TS/JS, Python, Go), git history, memories and em_graph_add.',
|
|
35993
|
+
description: 'Look up nodes in the project knowledge graph (files, functions, classes, routes, packages, people, concepts, memories) by name/path, run a preset (hotspots, coupled, orphans, dead_code, routes, cycles, clusters, stats), or run a structured pattern: start at nodes matching `from`, walk `edge.kinds` in `edge.direction` for `edge.depth` hops, keep what matches `to` \u2014 e.g. {from:{kind:"route"}, edge:{kinds:["handles"], direction:"in"}} lists every route handler, {from:{name:"^parseFile$"}, edge:{kinds:["calls"], direction:"in", depth:2}} its callers two levels up. Built from tree-sitter (TS/JS, Python, Go, PHP), git history, memories and em_graph_add.',
|
|
34850
35994
|
schema: {
|
|
34851
35995
|
query: external_exports.string().optional().describe("Substring of a name, path or id"),
|
|
34852
35996
|
kind: external_exports.string().optional().describe(
|
|
@@ -35400,12 +36544,12 @@ var init_em_graph_changes = __esm({
|
|
|
35400
36544
|
});
|
|
35401
36545
|
|
|
35402
36546
|
// src/graph/architecture.ts
|
|
35403
|
-
import { existsSync as existsSync18, readFileSync as
|
|
35404
|
-
import { join as
|
|
36547
|
+
import { existsSync as existsSync18, readFileSync as readFileSync17 } from "fs";
|
|
36548
|
+
import { join as join23 } from "path";
|
|
35405
36549
|
function pkgJson(root) {
|
|
35406
36550
|
try {
|
|
35407
|
-
const f =
|
|
35408
|
-
return existsSync18(f) ? JSON.parse(
|
|
36551
|
+
const f = join23(root, "package.json");
|
|
36552
|
+
return existsSync18(f) ? JSON.parse(readFileSync17(f, "utf8")) : {};
|
|
35409
36553
|
} catch {
|
|
35410
36554
|
return {};
|
|
35411
36555
|
}
|
|
@@ -36757,8 +37901,8 @@ var init_catalog = __esm({
|
|
|
36757
37901
|
});
|
|
36758
37902
|
|
|
36759
37903
|
// src/docs/manifest.ts
|
|
36760
|
-
import { existsSync as existsSync19, mkdirSync as mkdirSync8, readFileSync as
|
|
36761
|
-
import { dirname as
|
|
37904
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync8, readFileSync as readFileSync18, writeFileSync as writeFileSync8 } from "fs";
|
|
37905
|
+
import { dirname as dirname9, join as join24 } from "path";
|
|
36762
37906
|
function docsTag(name) {
|
|
36763
37907
|
return `${DOCS_PREFIX}${name}`;
|
|
36764
37908
|
}
|
|
@@ -36770,7 +37914,7 @@ function slugify3(input2) {
|
|
|
36770
37914
|
return (s || "source").slice(0, 64);
|
|
36771
37915
|
}
|
|
36772
37916
|
function manifestPathAt(root, rel = ".eminiar/docs.json") {
|
|
36773
|
-
return
|
|
37917
|
+
return join24(root, rel);
|
|
36774
37918
|
}
|
|
36775
37919
|
function manifestPath(ctx) {
|
|
36776
37920
|
return manifestPathAt(ctx.root, ctx.config.docs.manifest);
|
|
@@ -36778,7 +37922,7 @@ function manifestPath(ctx) {
|
|
|
36778
37922
|
function empty() {
|
|
36779
37923
|
return { version: 1, sources: [] };
|
|
36780
37924
|
}
|
|
36781
|
-
function
|
|
37925
|
+
function normalize5(raw) {
|
|
36782
37926
|
if (!raw || typeof raw !== "object") return empty();
|
|
36783
37927
|
const sources = Array.isArray(raw.sources) ? raw.sources : [];
|
|
36784
37928
|
const out = [];
|
|
@@ -36800,7 +37944,7 @@ function normalize4(raw) {
|
|
|
36800
37944
|
function readManifestAt(file3) {
|
|
36801
37945
|
if (!existsSync19(file3)) return empty();
|
|
36802
37946
|
try {
|
|
36803
|
-
return
|
|
37947
|
+
return normalize5(JSON.parse(readFileSync18(file3, "utf8")));
|
|
36804
37948
|
} catch {
|
|
36805
37949
|
return empty();
|
|
36806
37950
|
}
|
|
@@ -36810,7 +37954,7 @@ function readManifest(ctx) {
|
|
|
36810
37954
|
}
|
|
36811
37955
|
function writeManifest(ctx, m) {
|
|
36812
37956
|
const file3 = manifestPath(ctx);
|
|
36813
|
-
mkdirSync8(
|
|
37957
|
+
mkdirSync8(dirname9(file3), { recursive: true });
|
|
36814
37958
|
const sorted = {
|
|
36815
37959
|
version: 1,
|
|
36816
37960
|
sources: [...m.sources].sort((a, b) => a.name.localeCompare(b.name))
|
|
@@ -36911,11 +38055,11 @@ var init_manifest = __esm({
|
|
|
36911
38055
|
});
|
|
36912
38056
|
|
|
36913
38057
|
// src/docs/discover.ts
|
|
36914
|
-
import { existsSync as existsSync20, readFileSync as
|
|
36915
|
-
import { basename, join as
|
|
38058
|
+
import { existsSync as existsSync20, readFileSync as readFileSync19, statSync as statSync8 } from "fs";
|
|
38059
|
+
import { basename, join as join25, relative as relative7 } from "path";
|
|
36916
38060
|
function readText(file3) {
|
|
36917
38061
|
try {
|
|
36918
|
-
return
|
|
38062
|
+
return readFileSync19(file3, "utf8");
|
|
36919
38063
|
} catch {
|
|
36920
38064
|
return void 0;
|
|
36921
38065
|
}
|
|
@@ -36933,14 +38077,14 @@ function sizeOf(files) {
|
|
|
36933
38077
|
let n = 0;
|
|
36934
38078
|
for (const f of files) {
|
|
36935
38079
|
try {
|
|
36936
|
-
n +=
|
|
38080
|
+
n += statSync8(f).size;
|
|
36937
38081
|
} catch {
|
|
36938
38082
|
}
|
|
36939
38083
|
}
|
|
36940
38084
|
return n;
|
|
36941
38085
|
}
|
|
36942
38086
|
function hiddenPath(cwd, file3) {
|
|
36943
|
-
const parts =
|
|
38087
|
+
const parts = relative7(cwd, file3).split("/");
|
|
36944
38088
|
return parts.slice(0, -1).some((p, i) => {
|
|
36945
38089
|
if (!p.startsWith(".")) return false;
|
|
36946
38090
|
if (p === ".claude") return !["agents", "skills", "commands"].includes(parts[i + 1] ?? "");
|
|
@@ -36969,8 +38113,8 @@ function findProjects(root, opts) {
|
|
|
36969
38113
|
const found = /* @__PURE__ */ new Set();
|
|
36970
38114
|
for (const m of markers) {
|
|
36971
38115
|
for (const f of glob(root, [`*/${m}`, `*/*/${m}`], 3)) {
|
|
36972
|
-
const dir =
|
|
36973
|
-
const rel =
|
|
38116
|
+
const dir = join25(f, "..");
|
|
38117
|
+
const rel = relative7(root, dir);
|
|
36974
38118
|
if (!rel || rel.split("/").some((p) => p.startsWith(".") || p === "node_modules")) continue;
|
|
36975
38119
|
found.add(dir);
|
|
36976
38120
|
}
|
|
@@ -36978,7 +38122,7 @@ function findProjects(root, opts) {
|
|
|
36978
38122
|
const max = opts.maxProjects ?? 40;
|
|
36979
38123
|
const seenNames = /* @__PURE__ */ new Set();
|
|
36980
38124
|
const nameOf = (dir) => {
|
|
36981
|
-
const pkg = readJson3(
|
|
38125
|
+
const pkg = readJson3(join25(dir, "package.json"));
|
|
36982
38126
|
return typeof pkg?.name === "string" ? pkg.name : void 0;
|
|
36983
38127
|
};
|
|
36984
38128
|
const rootName = nameOf(root);
|
|
@@ -36996,7 +38140,7 @@ function findProjects(root, opts) {
|
|
|
36996
38140
|
return out;
|
|
36997
38141
|
}
|
|
36998
38142
|
function npmSignals(dir, includeDev, project) {
|
|
36999
|
-
const pkg = readJson3(
|
|
38143
|
+
const pkg = readJson3(join25(dir, "package.json"));
|
|
37000
38144
|
if (!pkg) return [];
|
|
37001
38145
|
const sections = ["dependencies", ...includeDev ? ["devDependencies"] : []];
|
|
37002
38146
|
const out = [];
|
|
@@ -37013,13 +38157,13 @@ function npmSignals(dir, includeDev, project) {
|
|
|
37013
38157
|
}
|
|
37014
38158
|
function pythonSignals(dir, project) {
|
|
37015
38159
|
const out = [];
|
|
37016
|
-
const py = readText(
|
|
38160
|
+
const py = readText(join25(dir, "pyproject.toml"));
|
|
37017
38161
|
if (py) {
|
|
37018
38162
|
const block = /dependencies\s*=\s*\[([\s\S]*?)\]/.exec(py)?.[1] ?? "";
|
|
37019
38163
|
for (const m of block.matchAll(/["']([A-Za-z0-9_.-]+)/g))
|
|
37020
38164
|
out.push({ kind: "pypi", key: m[1].toLowerCase(), project });
|
|
37021
38165
|
}
|
|
37022
|
-
const req = readText(
|
|
38166
|
+
const req = readText(join25(dir, "requirements.txt"));
|
|
37023
38167
|
if (req) {
|
|
37024
38168
|
for (const line of req.split("\n")) {
|
|
37025
38169
|
const name = /^\s*([A-Za-z0-9_.-]+)/.exec(line)?.[1];
|
|
@@ -37030,7 +38174,7 @@ function pythonSignals(dir, project) {
|
|
|
37030
38174
|
return out;
|
|
37031
38175
|
}
|
|
37032
38176
|
function goSignals(dir, project) {
|
|
37033
|
-
const mod = readText(
|
|
38177
|
+
const mod = readText(join25(dir, "go.mod"));
|
|
37034
38178
|
if (!mod) return [];
|
|
37035
38179
|
const out = [];
|
|
37036
38180
|
const block = /require\s*\(([\s\S]*?)\)/g;
|
|
@@ -37096,18 +38240,18 @@ function isDocLink(host, path3) {
|
|
|
37096
38240
|
function apiSignals(dir, project) {
|
|
37097
38241
|
const hits = /* @__PURE__ */ new Map();
|
|
37098
38242
|
const docLinks = /* @__PURE__ */ new Map();
|
|
37099
|
-
const files = glob(dir, CODE_GLOBS, 8).filter((f) => !/\.min\.(js|mjs|cjs)$/.test(f)).filter((f) => !TEST_PATH_RE.test(
|
|
38243
|
+
const files = glob(dir, CODE_GLOBS, 8).filter((f) => !/\.min\.(js|mjs|cjs)$/.test(f)).filter((f) => !TEST_PATH_RE.test(relative7(dir, f))).slice(0, 4e3);
|
|
37100
38244
|
for (const file3 of files) {
|
|
37101
38245
|
let size = 0;
|
|
37102
38246
|
try {
|
|
37103
|
-
size =
|
|
38247
|
+
size = statSync8(file3).size;
|
|
37104
38248
|
} catch {
|
|
37105
38249
|
continue;
|
|
37106
38250
|
}
|
|
37107
38251
|
if (size > 400 * 1024) continue;
|
|
37108
38252
|
const text3 = readText(file3);
|
|
37109
38253
|
if (!text3 || text3.includes("\0")) continue;
|
|
37110
|
-
const rel =
|
|
38254
|
+
const rel = relative7(dir, file3).split("\\").join("/");
|
|
37111
38255
|
const isEnv = basename(file3).startsWith(".env");
|
|
37112
38256
|
const lines = text3.split("\n");
|
|
37113
38257
|
const found = [];
|
|
@@ -37197,7 +38341,7 @@ function fileSignals(dir, project) {
|
|
|
37197
38341
|
function localCandidates(root, dir, project, isRegistered) {
|
|
37198
38342
|
const out = [];
|
|
37199
38343
|
const prefix = project ? `${slugify3(basename(dir))}-` : "";
|
|
37200
|
-
const rel = (abs) =>
|
|
38344
|
+
const rel = (abs) => relative7(root, abs).split("\\").join("/");
|
|
37201
38345
|
const push = (c) => {
|
|
37202
38346
|
if (!c.files) return;
|
|
37203
38347
|
out.push({ ...c, project, registered: isRegistered(c) });
|
|
@@ -37227,7 +38371,7 @@ function localCandidates(root, dir, project, isRegistered) {
|
|
|
37227
38371
|
why: "project readme and top-level guides"
|
|
37228
38372
|
});
|
|
37229
38373
|
for (const d of ["docs", "doc", "documentation"]) {
|
|
37230
|
-
const abs =
|
|
38374
|
+
const abs = join25(dir, d);
|
|
37231
38375
|
if (!existsSync20(abs)) continue;
|
|
37232
38376
|
const files = glob(abs, ["**/*.md", "**/*.mdx", "**/*.rst", "**/*.txt"]).filter(
|
|
37233
38377
|
(f) => !SKIP_EXT.has(f.slice(f.lastIndexOf(".")))
|
|
@@ -37251,7 +38395,7 @@ function localCandidates(root, dir, project, isRegistered) {
|
|
|
37251
38395
|
"docs/decisions",
|
|
37252
38396
|
"decisions"
|
|
37253
38397
|
]) {
|
|
37254
|
-
const abs =
|
|
38398
|
+
const abs = join25(dir, d);
|
|
37255
38399
|
if (!existsSync20(abs)) continue;
|
|
37256
38400
|
if (out.some((c) => c.path && rel(abs).startsWith(`${c.path}/`))) break;
|
|
37257
38401
|
const files = glob(abs, ["**/*.md"]);
|
|
@@ -37311,7 +38455,7 @@ function localCandidates(root, dir, project, isRegistered) {
|
|
|
37311
38455
|
push({
|
|
37312
38456
|
name: `${prefix}api-specs`,
|
|
37313
38457
|
path: rel(dir) || ".",
|
|
37314
|
-
glob: api.map((f) =>
|
|
38458
|
+
glob: api.map((f) => relative7(dir, f).split("\\").join("/")),
|
|
37315
38459
|
files: api.length,
|
|
37316
38460
|
bytes: sizeOf(api),
|
|
37317
38461
|
why: "OpenAPI / Postman / GraphQL / protobuf specs"
|
|
@@ -37385,7 +38529,7 @@ function discoverDocs(ctx, opts = {}) {
|
|
|
37385
38529
|
const apiHits = /* @__PURE__ */ new Map();
|
|
37386
38530
|
const docLinks = /* @__PURE__ */ new Map();
|
|
37387
38531
|
for (const dir of projects) {
|
|
37388
|
-
const project = dir === root ? "" :
|
|
38532
|
+
const project = dir === root ? "" : relative7(root, dir).split("\\").join("/");
|
|
37389
38533
|
const own2 = dir === root && projects.length > 1 ? void 0 : dir;
|
|
37390
38534
|
local.push(...localCandidates(root, dir, project, isRegistered));
|
|
37391
38535
|
signals.push(...npmSignals(dir, includeDev, project));
|
|
@@ -37395,7 +38539,7 @@ function discoverDocs(ctx, opts = {}) {
|
|
|
37395
38539
|
signals.push(...fileSignals(dir, project));
|
|
37396
38540
|
const api = apiSignals(own2 ?? dir, project);
|
|
37397
38541
|
const inSubproject = (samples) => own2 === void 0 && projects.some(
|
|
37398
|
-
(p) => p !== root && samples.some((s) => s.startsWith(`${
|
|
38542
|
+
(p) => p !== root && samples.some((s) => s.startsWith(`${relative7(root, p)}/`))
|
|
37399
38543
|
);
|
|
37400
38544
|
for (const hit of api.hits.values()) {
|
|
37401
38545
|
if (inSubproject(hit.samples)) continue;
|
|
@@ -37585,7 +38729,7 @@ function discoverDocs(ctx, opts = {}) {
|
|
|
37585
38729
|
);
|
|
37586
38730
|
return {
|
|
37587
38731
|
root,
|
|
37588
|
-
projects: projects.map((p) => p === root ? "." :
|
|
38732
|
+
projects: projects.map((p) => p === root ? "." : relative7(root, p)),
|
|
37589
38733
|
local,
|
|
37590
38734
|
providers
|
|
37591
38735
|
};
|
|
@@ -39859,14 +41003,14 @@ var init_em_search = __esm({
|
|
|
39859
41003
|
});
|
|
39860
41004
|
|
|
39861
41005
|
// src/ui/api.ts
|
|
39862
|
-
import { existsSync as existsSync21, readdirSync as
|
|
39863
|
-
import { join as
|
|
41006
|
+
import { existsSync as existsSync21, readdirSync as readdirSync8 } from "fs";
|
|
41007
|
+
import { join as join26, relative as relative8 } from "path";
|
|
39864
41008
|
function listProjects() {
|
|
39865
41009
|
const dir = paths().projects;
|
|
39866
41010
|
const out = [];
|
|
39867
|
-
for (const id of
|
|
41011
|
+
for (const id of readdirSync8(dir, { withFileTypes: true })) {
|
|
39868
41012
|
if (!id.isDirectory()) continue;
|
|
39869
|
-
const db =
|
|
41013
|
+
const db = join26(dir, id.name, "eminiar.db");
|
|
39870
41014
|
try {
|
|
39871
41015
|
const a = new SqliteAdapter(db, { vectors: false });
|
|
39872
41016
|
out.push({ id: id.name, root: a.sessionLatest()?.project_root ?? null });
|
|
@@ -39892,8 +41036,8 @@ function statsPayload(ctx) {
|
|
|
39892
41036
|
const db = getStorage(ctx);
|
|
39893
41037
|
const sessionId = getCurrentSession(ctx);
|
|
39894
41038
|
const g = globalDb();
|
|
39895
|
-
const onboardingDir =
|
|
39896
|
-
const onboarding = existsSync21(onboardingDir) ?
|
|
41039
|
+
const onboardingDir = join26(memoriesDir(ctx), "onboarding");
|
|
41040
|
+
const onboarding = existsSync21(onboardingDir) ? readdirSync8(onboardingDir).filter((f) => f.endsWith(".md")).map((f) => f.slice(0, -3)) : [];
|
|
39897
41041
|
return {
|
|
39898
41042
|
project: ctx.root,
|
|
39899
41043
|
projectId: ctx.id,
|
|
@@ -40085,7 +41229,7 @@ function docsPayload(ctx) {
|
|
|
40085
41229
|
const known = new Set(m.sources.map((s) => docsTag(s.name)));
|
|
40086
41230
|
const orphans2 = [...stats.values()].filter((s) => !known.has(s.source)).map((s) => ({ source: s.source, documents: s.documents, chunks: s.chunks }));
|
|
40087
41231
|
return {
|
|
40088
|
-
manifest:
|
|
41232
|
+
manifest: relative8(ctx.root, manifestPath(ctx)),
|
|
40089
41233
|
sources,
|
|
40090
41234
|
orphans: orphans2,
|
|
40091
41235
|
totals: {
|
|
@@ -40146,32 +41290,58 @@ function graphMap(ctx, opts = {}) {
|
|
|
40146
41290
|
let files = db.graphFileNodes();
|
|
40147
41291
|
if (opts.pathFilter)
|
|
40148
41292
|
files = files.filter((f) => (f.path ?? f.name).includes(opts.pathFilter));
|
|
40149
|
-
const
|
|
40150
|
-
const
|
|
40151
|
-
const
|
|
40152
|
-
|
|
41293
|
+
const fileSet = new Set(files.map((f) => f.id));
|
|
41294
|
+
const kinds = opts.kinds ?? (opts.coChanges === false ? ["imports"] : ["imports", "co_changes"]);
|
|
41295
|
+
const lift = (id) => {
|
|
41296
|
+
const kind = parseId(id).kind;
|
|
41297
|
+
if (kind === "file" || kind === "sym") {
|
|
41298
|
+
const rel = fileOf(id);
|
|
41299
|
+
const f = rel === void 0 ? void 0 : fileId(rel);
|
|
41300
|
+
return f && fileSet.has(f) ? f : void 0;
|
|
41301
|
+
}
|
|
41302
|
+
return kind === "pkg" ? void 0 : id;
|
|
41303
|
+
};
|
|
41304
|
+
const merged = /* @__PURE__ */ new Map();
|
|
41305
|
+
for (const kind of kinds) {
|
|
41306
|
+
for (const e of db.graphEdgesByKind(kind, MAP_EDGES_PER_KIND)) {
|
|
41307
|
+
if (kind === "co_changes" && e.weight < 0.3) continue;
|
|
41308
|
+
const src = lift(e.src);
|
|
41309
|
+
const dst = lift(e.dst);
|
|
41310
|
+
if (!src || !dst || src === dst) continue;
|
|
41311
|
+
if (!fileSet.has(src) && !fileSet.has(dst)) continue;
|
|
41312
|
+
const key = kind === "co_changes" ? `${[src, dst].sort().join("|")}|co` : `${src}|${dst}|${kind}`;
|
|
41313
|
+
const prev = merged.get(key);
|
|
41314
|
+
if (prev)
|
|
41315
|
+
prev.weight = kind === "co_changes" ? Math.max(prev.weight, e.weight) : prev.weight + 1;
|
|
41316
|
+
else merged.set(key, { src, dst, kind, weight: kind === "co_changes" ? e.weight : 1 });
|
|
41317
|
+
}
|
|
41318
|
+
}
|
|
40153
41319
|
const degree = /* @__PURE__ */ new Map();
|
|
40154
|
-
for (const e of
|
|
41320
|
+
for (const e of merged.values()) {
|
|
40155
41321
|
degree.set(e.src, (degree.get(e.src) ?? 0) + 1);
|
|
40156
41322
|
degree.set(e.dst, (degree.get(e.dst) ?? 0) + 1);
|
|
40157
41323
|
}
|
|
40158
41324
|
const keep = new Set(
|
|
40159
41325
|
[...files].sort((a, b) => (degree.get(b.id) ?? 0) - (degree.get(a.id) ?? 0)).slice(0, limit).map((f) => f.id)
|
|
40160
41326
|
);
|
|
40161
|
-
const seenEdge = /* @__PURE__ */ new Set();
|
|
40162
41327
|
const edges = [];
|
|
40163
|
-
|
|
40164
|
-
|
|
40165
|
-
|
|
40166
|
-
const
|
|
40167
|
-
if (
|
|
40168
|
-
|
|
40169
|
-
|
|
40170
|
-
|
|
41328
|
+
const extra = /* @__PURE__ */ new Set();
|
|
41329
|
+
for (const e of merged.values()) {
|
|
41330
|
+
const srcOk = keep.has(e.src) || !fileSet.has(e.src) && keep.has(e.dst);
|
|
41331
|
+
const dstOk = keep.has(e.dst) || !fileSet.has(e.dst) && keep.has(e.src);
|
|
41332
|
+
if (!srcOk || !dstOk) continue;
|
|
41333
|
+
if (!keep.has(e.src)) extra.add(e.src);
|
|
41334
|
+
if (!keep.has(e.dst)) extra.add(e.dst);
|
|
41335
|
+
edges.push(e);
|
|
41336
|
+
}
|
|
41337
|
+
const extraRows = new Map(db.graphGetNodes([...extra]).map((n) => [n.id, n]));
|
|
40171
41338
|
return {
|
|
40172
41339
|
mode: "map",
|
|
40173
41340
|
total: files.length,
|
|
40174
|
-
nodes:
|
|
41341
|
+
nodes: [
|
|
41342
|
+
...files.filter((f) => keep.has(f.id)).map((f) => visNode(f, f.id)),
|
|
41343
|
+
...[...extra].map((id) => visNode(extraRows.get(id), id))
|
|
41344
|
+
],
|
|
40175
41345
|
edges
|
|
40176
41346
|
};
|
|
40177
41347
|
}
|
|
@@ -40333,7 +41503,7 @@ async function runAction(ctx, input2) {
|
|
|
40333
41503
|
return done(false, `unknown action ${String(input2.action)}`);
|
|
40334
41504
|
}
|
|
40335
41505
|
}
|
|
40336
|
-
var memoryKinds;
|
|
41506
|
+
var memoryKinds, MAP_EDGES_PER_KIND;
|
|
40337
41507
|
var init_api2 = __esm({
|
|
40338
41508
|
"src/ui/api.ts"() {
|
|
40339
41509
|
"use strict";
|
|
@@ -40348,6 +41518,7 @@ var init_api2 = __esm({
|
|
|
40348
41518
|
init_sync();
|
|
40349
41519
|
init_queue();
|
|
40350
41520
|
init_format2();
|
|
41521
|
+
init_ids();
|
|
40351
41522
|
init_query2();
|
|
40352
41523
|
init_doctor2();
|
|
40353
41524
|
init_store();
|
|
@@ -40362,6 +41533,7 @@ var init_api2 = __esm({
|
|
|
40362
41533
|
init_em_purge();
|
|
40363
41534
|
init_em_search();
|
|
40364
41535
|
memoryKinds = [...MEMORY_KINDS];
|
|
41536
|
+
MAP_EDGES_PER_KIND = 5e4;
|
|
40365
41537
|
}
|
|
40366
41538
|
});
|
|
40367
41539
|
|
|
@@ -40403,8 +41575,8 @@ var init_namepath = __esm({
|
|
|
40403
41575
|
});
|
|
40404
41576
|
|
|
40405
41577
|
// src/code/symbols.ts
|
|
40406
|
-
import { existsSync as existsSync22, readFileSync as
|
|
40407
|
-
import { join as
|
|
41578
|
+
import { existsSync as existsSync22, readFileSync as readFileSync20 } from "fs";
|
|
41579
|
+
import { join as join27 } from "path";
|
|
40408
41580
|
function isExported2(n) {
|
|
40409
41581
|
if (!n.meta) return false;
|
|
40410
41582
|
try {
|
|
@@ -40440,8 +41612,8 @@ var init_symbols2 = __esm({
|
|
|
40440
41612
|
});
|
|
40441
41613
|
|
|
40442
41614
|
// src/ui/graph-api.ts
|
|
40443
|
-
import { readFileSync as
|
|
40444
|
-
import { relative as
|
|
41615
|
+
import { readFileSync as readFileSync21, realpathSync as realpathSync2, statSync as statSync9 } from "fs";
|
|
41616
|
+
import { relative as relative9, resolve as resolve7 } from "path";
|
|
40445
41617
|
function metaOf2(row) {
|
|
40446
41618
|
if (!row?.meta) return {};
|
|
40447
41619
|
try {
|
|
@@ -40502,17 +41674,17 @@ function treePayload(ctx) {
|
|
|
40502
41674
|
}
|
|
40503
41675
|
function filePayload(ctx, rel) {
|
|
40504
41676
|
const clean = rel.replace(/^\.\//, "");
|
|
40505
|
-
const abs =
|
|
40506
|
-
if (
|
|
41677
|
+
const abs = resolve7(ctx.root, clean);
|
|
41678
|
+
if (relative9(ctx.root, abs).startsWith("..")) throw new Error("path outside the project");
|
|
40507
41679
|
let real = abs;
|
|
40508
41680
|
try {
|
|
40509
41681
|
real = realpathSync2(abs);
|
|
40510
41682
|
} catch {
|
|
40511
41683
|
return { error: "not found", path: clean };
|
|
40512
41684
|
}
|
|
40513
|
-
if (
|
|
41685
|
+
if (relative9(realpathSync2(ctx.root), real).startsWith(".."))
|
|
40514
41686
|
throw new Error("path outside the project");
|
|
40515
|
-
const st =
|
|
41687
|
+
const st = statSync9(real);
|
|
40516
41688
|
if (!st.isFile()) return { error: "not a file", path: clean };
|
|
40517
41689
|
const db = getStorage(ctx);
|
|
40518
41690
|
const row = db.fileIndexGet(clean);
|
|
@@ -40526,7 +41698,7 @@ function filePayload(ctx, rel) {
|
|
|
40526
41698
|
}));
|
|
40527
41699
|
if (st.size > MAX_CODE_BYTES)
|
|
40528
41700
|
return { path: clean, tooLarge: true, size: st.size, status: row?.status ?? null, symbols };
|
|
40529
|
-
const content =
|
|
41701
|
+
const content = readFileSync21(real, "utf8");
|
|
40530
41702
|
return {
|
|
40531
41703
|
path: clean,
|
|
40532
41704
|
size: st.size,
|
|
@@ -41248,14 +42420,14 @@ function buildHistory(ctx, db, opts = {}) {
|
|
|
41248
42420
|
};
|
|
41249
42421
|
});
|
|
41250
42422
|
const bySha = new Map(commits.map((c) => [c.sha, c]));
|
|
41251
|
-
const
|
|
42423
|
+
const resolve8 = (prefix) => {
|
|
41252
42424
|
if (bySha.has(prefix)) return bySha.get(prefix);
|
|
41253
42425
|
for (const c of commits) if (c.sha.startsWith(prefix)) return c;
|
|
41254
42426
|
return void 0;
|
|
41255
42427
|
};
|
|
41256
42428
|
for (const c of commits) {
|
|
41257
42429
|
if (c.kind !== "revert" || !c.revertOf) continue;
|
|
41258
|
-
const target =
|
|
42430
|
+
const target = resolve8(c.revertOf);
|
|
41259
42431
|
if (target) {
|
|
41260
42432
|
target.revertedBy = c.sha;
|
|
41261
42433
|
c.origins.push(target.sha);
|
|
@@ -42104,9 +43276,9 @@ __export(server_exports, {
|
|
|
42104
43276
|
uiDir: () => uiDir
|
|
42105
43277
|
});
|
|
42106
43278
|
import { spawn as spawn2 } from "child_process";
|
|
42107
|
-
import { existsSync as existsSync23, readFileSync as
|
|
43279
|
+
import { existsSync as existsSync23, readFileSync as readFileSync22, statSync as statSync10 } from "fs";
|
|
42108
43280
|
import { createServer } from "http";
|
|
42109
|
-
import { extname as extname3, join as
|
|
43281
|
+
import { extname as extname3, join as join28, normalize as normalize6 } from "path";
|
|
42110
43282
|
import { URL as URL2 } from "url";
|
|
42111
43283
|
function json2(res, status, body) {
|
|
42112
43284
|
res.writeHead(status, {
|
|
@@ -42128,7 +43300,7 @@ function strList(v) {
|
|
|
42128
43300
|
return void 0;
|
|
42129
43301
|
}
|
|
42130
43302
|
function uiDir() {
|
|
42131
|
-
return process.env.EMINIAR_UI_DIR ||
|
|
43303
|
+
return process.env.EMINIAR_UI_DIR || join28(pluginRoot(), "dist", "ui");
|
|
42132
43304
|
}
|
|
42133
43305
|
function originAllowed(req, port) {
|
|
42134
43306
|
const host = req.headers.host ?? "";
|
|
@@ -42152,25 +43324,25 @@ function originAllowed(req, port) {
|
|
|
42152
43324
|
function serveStatic(res, p) {
|
|
42153
43325
|
const dir = uiDir();
|
|
42154
43326
|
const rel = p === "/" ? "index.html" : p.replace(/^\/+/, "");
|
|
42155
|
-
const file3 =
|
|
42156
|
-
if (!file3.startsWith(
|
|
42157
|
-
if (!existsSync23(file3) || !
|
|
43327
|
+
const file3 = normalize6(join28(dir, rel));
|
|
43328
|
+
if (!file3.startsWith(normalize6(dir))) return false;
|
|
43329
|
+
if (!existsSync23(file3) || !statSync10(file3).isFile()) return false;
|
|
42158
43330
|
const type = MIME[extname3(file3).toLowerCase()] ?? "application/octet-stream";
|
|
42159
43331
|
if (rel === "index.html") {
|
|
42160
43332
|
const stamp = (name) => {
|
|
42161
43333
|
try {
|
|
42162
|
-
return `/${name}?v=${Math.floor(
|
|
43334
|
+
return `/${name}?v=${Math.floor(statSync10(join28(dir, name)).mtimeMs)}`;
|
|
42163
43335
|
} catch {
|
|
42164
43336
|
return `/${name}`;
|
|
42165
43337
|
}
|
|
42166
43338
|
};
|
|
42167
|
-
const html =
|
|
43339
|
+
const html = readFileSync22(file3, "utf8").replace('"/app.global.js"', `"${stamp("app.global.js")}"`).replace('"/styles.css"', `"${stamp("styles.css")}"`);
|
|
42168
43340
|
res.writeHead(200, { "content-type": type, "cache-control": "no-store" });
|
|
42169
43341
|
res.end(html);
|
|
42170
43342
|
return true;
|
|
42171
43343
|
}
|
|
42172
43344
|
res.writeHead(200, { "content-type": type, "cache-control": "no-cache" });
|
|
42173
|
-
res.end(
|
|
43345
|
+
res.end(readFileSync22(file3));
|
|
42174
43346
|
return true;
|
|
42175
43347
|
}
|
|
42176
43348
|
async function serve(opts) {
|
|
@@ -42332,6 +43504,7 @@ async function serve(opts) {
|
|
|
42332
43504
|
200,
|
|
42333
43505
|
graphMap(ctx(), {
|
|
42334
43506
|
limit: num2("limit", 150),
|
|
43507
|
+
kinds: q.has("kinds") ? strList(q.get("kinds")) : void 0,
|
|
42335
43508
|
coChanges: q.get("cochanges") !== "0",
|
|
42336
43509
|
pathFilter: str2("path")
|
|
42337
43510
|
})
|
|
@@ -42511,9 +43684,9 @@ async function serve(opts) {
|
|
|
42511
43684
|
json2(res, 500, { error: err.message });
|
|
42512
43685
|
}
|
|
42513
43686
|
});
|
|
42514
|
-
await new Promise((
|
|
43687
|
+
await new Promise((resolve8, reject) => {
|
|
42515
43688
|
server.once("error", reject);
|
|
42516
|
-
server.listen(opts.port, "127.0.0.1",
|
|
43689
|
+
server.listen(opts.port, "127.0.0.1", resolve8);
|
|
42517
43690
|
});
|
|
42518
43691
|
const url2 = `http://127.0.0.1:${opts.port}/`;
|
|
42519
43692
|
console.log(`eminiar dashboard at ${url2} (Ctrl-C to stop)`);
|
|
@@ -42554,7 +43727,7 @@ var statusline_exports = {};
|
|
|
42554
43727
|
__export(statusline_exports, {
|
|
42555
43728
|
renderStatusline: () => renderStatusline
|
|
42556
43729
|
});
|
|
42557
|
-
import { existsSync as existsSync24, readFileSync as
|
|
43730
|
+
import { existsSync as existsSync24, readFileSync as readFileSync23, writeFileSync as writeFileSync9 } from "fs";
|
|
42558
43731
|
function renderStatusline(raw) {
|
|
42559
43732
|
let input2 = {};
|
|
42560
43733
|
try {
|
|
@@ -42564,7 +43737,7 @@ function renderStatusline(raw) {
|
|
|
42564
43737
|
const cache3 = paths().statuslineCache;
|
|
42565
43738
|
try {
|
|
42566
43739
|
if (existsSync24(cache3)) {
|
|
42567
|
-
const c = JSON.parse(
|
|
43740
|
+
const c = JSON.parse(readFileSync23(cache3, "utf8"));
|
|
42568
43741
|
if (Date.now() - c.at < 5e3 && c.key === (input2.session_id ?? "")) return c.line;
|
|
42569
43742
|
}
|
|
42570
43743
|
} catch {
|