eminiar 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +2 -2
- package/dist/cli.js +1355 -206
- package/dist/hook.js +23 -6
- package/dist/server.js +1415 -266
- package/dist/ui/app.global.js +30 -30
- 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,56 +38240,59 @@ 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");
|
|
38257
|
+
const found = [];
|
|
37113
38258
|
for (let i = 0; i < lines.length; i++) {
|
|
37114
38259
|
const line = lines[i];
|
|
37115
|
-
if (!line.includes("http")) continue;
|
|
37116
|
-
const comment = COMMENT_RE.test(line);
|
|
38260
|
+
if (!line.includes("http") || PLACEHOLDER_RE.test(line)) continue;
|
|
37117
38261
|
for (const m of line.matchAll(URL_RE)) {
|
|
37118
38262
|
const host = m[1].toLowerCase();
|
|
37119
|
-
|
|
37120
|
-
|
|
37121
|
-
|
|
37122
|
-
|
|
37123
|
-
|
|
37124
|
-
|
|
37125
|
-
|
|
37126
|
-
|
|
37127
|
-
|
|
37128
|
-
|
|
37129
|
-
|
|
37130
|
-
|
|
37131
|
-
|
|
37132
|
-
|
|
37133
|
-
|
|
37134
|
-
|
|
37135
|
-
|
|
37136
|
-
|
|
37137
|
-
|
|
37138
|
-
|
|
37139
|
-
|
|
37140
|
-
|
|
37141
|
-
|
|
37142
|
-
|
|
37143
|
-
|
|
37144
|
-
|
|
37145
|
-
|
|
37146
|
-
|
|
37147
|
-
|
|
38263
|
+
if (isApiHostCandidate(host)) found.push({ host, path: m[2] ?? "/", line, i });
|
|
38264
|
+
}
|
|
38265
|
+
}
|
|
38266
|
+
if (new Set(found.map((f) => f.host)).size >= REFERENCE_TABLE_HOSTS) continue;
|
|
38267
|
+
for (const { host, path: path3, line, i } of found) {
|
|
38268
|
+
const comment = COMMENT_RE.test(line);
|
|
38269
|
+
const spec = SPEC_URL_RE.test(path3);
|
|
38270
|
+
if (!spec && isDocLink(host, path3)) {
|
|
38271
|
+
if (comment && !isEnv) continue;
|
|
38272
|
+
const link = docLinks.get(host) ?? { host, urls: [], samples: [], project };
|
|
38273
|
+
const full = `https://${host}${path3.replace(/[#?].*$/, "")}`;
|
|
38274
|
+
if (!link.urls.includes(full) && link.urls.length < 3) link.urls.push(full);
|
|
38275
|
+
if (link.samples.length < 3) link.samples.push(`${rel}:${i + 1}`);
|
|
38276
|
+
docLinks.set(host, link);
|
|
38277
|
+
continue;
|
|
38278
|
+
}
|
|
38279
|
+
const callLike = !comment && (spec || isEnv || CALL_RE.test(line) || API_PATH_RE.test(path3) || API_HOST_RE.test(host));
|
|
38280
|
+
const cur = hits.get(host) ?? {
|
|
38281
|
+
host,
|
|
38282
|
+
calls: 0,
|
|
38283
|
+
mentions: 0,
|
|
38284
|
+
samples: [],
|
|
38285
|
+
specUrls: [],
|
|
38286
|
+
project
|
|
38287
|
+
};
|
|
38288
|
+
if (callLike) cur.calls++;
|
|
38289
|
+
else cur.mentions++;
|
|
38290
|
+
if (callLike && cur.samples.length < 3) cur.samples.push(`${rel}:${i + 1}`);
|
|
38291
|
+
if (spec) {
|
|
38292
|
+
const full = `https://${host}${path3}`;
|
|
38293
|
+
if (!cur.specUrls.includes(full) && cur.specUrls.length < 4) cur.specUrls.push(full);
|
|
37148
38294
|
}
|
|
38295
|
+
hits.set(host, cur);
|
|
37149
38296
|
}
|
|
37150
38297
|
}
|
|
37151
38298
|
return { hits, docLinks };
|
|
@@ -37194,7 +38341,7 @@ function fileSignals(dir, project) {
|
|
|
37194
38341
|
function localCandidates(root, dir, project, isRegistered) {
|
|
37195
38342
|
const out = [];
|
|
37196
38343
|
const prefix = project ? `${slugify3(basename(dir))}-` : "";
|
|
37197
|
-
const rel = (abs) =>
|
|
38344
|
+
const rel = (abs) => relative7(root, abs).split("\\").join("/");
|
|
37198
38345
|
const push = (c) => {
|
|
37199
38346
|
if (!c.files) return;
|
|
37200
38347
|
out.push({ ...c, project, registered: isRegistered(c) });
|
|
@@ -37224,7 +38371,7 @@ function localCandidates(root, dir, project, isRegistered) {
|
|
|
37224
38371
|
why: "project readme and top-level guides"
|
|
37225
38372
|
});
|
|
37226
38373
|
for (const d of ["docs", "doc", "documentation"]) {
|
|
37227
|
-
const abs =
|
|
38374
|
+
const abs = join25(dir, d);
|
|
37228
38375
|
if (!existsSync20(abs)) continue;
|
|
37229
38376
|
const files = glob(abs, ["**/*.md", "**/*.mdx", "**/*.rst", "**/*.txt"]).filter(
|
|
37230
38377
|
(f) => !SKIP_EXT.has(f.slice(f.lastIndexOf(".")))
|
|
@@ -37248,7 +38395,7 @@ function localCandidates(root, dir, project, isRegistered) {
|
|
|
37248
38395
|
"docs/decisions",
|
|
37249
38396
|
"decisions"
|
|
37250
38397
|
]) {
|
|
37251
|
-
const abs =
|
|
38398
|
+
const abs = join25(dir, d);
|
|
37252
38399
|
if (!existsSync20(abs)) continue;
|
|
37253
38400
|
if (out.some((c) => c.path && rel(abs).startsWith(`${c.path}/`))) break;
|
|
37254
38401
|
const files = glob(abs, ["**/*.md"]);
|
|
@@ -37308,7 +38455,7 @@ function localCandidates(root, dir, project, isRegistered) {
|
|
|
37308
38455
|
push({
|
|
37309
38456
|
name: `${prefix}api-specs`,
|
|
37310
38457
|
path: rel(dir) || ".",
|
|
37311
|
-
glob: api.map((f) =>
|
|
38458
|
+
glob: api.map((f) => relative7(dir, f).split("\\").join("/")),
|
|
37312
38459
|
files: api.length,
|
|
37313
38460
|
bytes: sizeOf(api),
|
|
37314
38461
|
why: "OpenAPI / Postman / GraphQL / protobuf specs"
|
|
@@ -37382,7 +38529,7 @@ function discoverDocs(ctx, opts = {}) {
|
|
|
37382
38529
|
const apiHits = /* @__PURE__ */ new Map();
|
|
37383
38530
|
const docLinks = /* @__PURE__ */ new Map();
|
|
37384
38531
|
for (const dir of projects) {
|
|
37385
|
-
const project = dir === root ? "" :
|
|
38532
|
+
const project = dir === root ? "" : relative7(root, dir).split("\\").join("/");
|
|
37386
38533
|
const own2 = dir === root && projects.length > 1 ? void 0 : dir;
|
|
37387
38534
|
local.push(...localCandidates(root, dir, project, isRegistered));
|
|
37388
38535
|
signals.push(...npmSignals(dir, includeDev, project));
|
|
@@ -37392,7 +38539,7 @@ function discoverDocs(ctx, opts = {}) {
|
|
|
37392
38539
|
signals.push(...fileSignals(dir, project));
|
|
37393
38540
|
const api = apiSignals(own2 ?? dir, project);
|
|
37394
38541
|
const inSubproject = (samples) => own2 === void 0 && projects.some(
|
|
37395
|
-
(p) => p !== root && samples.some((s) => s.startsWith(`${
|
|
38542
|
+
(p) => p !== root && samples.some((s) => s.startsWith(`${relative7(root, p)}/`))
|
|
37396
38543
|
);
|
|
37397
38544
|
for (const hit of api.hits.values()) {
|
|
37398
38545
|
if (inSubproject(hit.samples)) continue;
|
|
@@ -37582,12 +38729,12 @@ function discoverDocs(ctx, opts = {}) {
|
|
|
37582
38729
|
);
|
|
37583
38730
|
return {
|
|
37584
38731
|
root,
|
|
37585
|
-
projects: projects.map((p) => p === root ? "." :
|
|
38732
|
+
projects: projects.map((p) => p === root ? "." : relative7(root, p)),
|
|
37586
38733
|
local,
|
|
37587
38734
|
providers
|
|
37588
38735
|
};
|
|
37589
38736
|
}
|
|
37590
|
-
var import_fast_glob4, IGNORE, GENERIC_ENV, SKIP_EXT, VISIBLE_DOT_DIRS, CODE_GLOBS, NOT_API_HOSTS, NOT_API_SUFFIX, TWO_LEVEL_TLDS, URL_RE, CALL_RE, API_PATH_RE, API_HOST_RE, SPEC_URL_RE, TEST_PATH_RE, DOC_HOST_RE, DOC_HOST_SUFFIX, DOC_PATH_RE, COMMENT_RE;
|
|
38737
|
+
var import_fast_glob4, IGNORE, GENERIC_ENV, SKIP_EXT, VISIBLE_DOT_DIRS, CODE_GLOBS, NOT_API_HOSTS, NOT_API_SUFFIX, TWO_LEVEL_TLDS, URL_RE, CALL_RE, API_PATH_RE, API_HOST_RE, SPEC_URL_RE, TEST_PATH_RE, DOC_HOST_RE, DOC_HOST_SUFFIX, DOC_PATH_RE, COMMENT_RE, PLACEHOLDER_RE, REFERENCE_TABLE_HOSTS;
|
|
37591
38738
|
var init_discover = __esm({
|
|
37592
38739
|
"src/docs/discover.ts"() {
|
|
37593
38740
|
"use strict";
|
|
@@ -37878,6 +39025,8 @@ var init_discover = __esm({
|
|
|
37878
39025
|
DOC_HOST_SUFFIX = /\.(readthedocs\.io|gitbook\.io|github\.io|gitlab\.io|netlify\.app|vercel\.app|mintlify\.app|readme\.io|redoc\.ly|stoplight\.io)$/i;
|
|
37879
39026
|
DOC_PATH_RE = /^\/(docs?|documentation|developers?|guides?|learn|reference|manual|wiki|help|en|latest|stable|v\d+(\.\d+)*\/docs)(\/|$)/i;
|
|
37880
39027
|
COMMENT_RE = /^\s*(\/\/|#|\*|\/\*|<!--|--|;|'''|""")/;
|
|
39028
|
+
PLACEHOLDER_RE = /\bplaceholder\b/i;
|
|
39029
|
+
REFERENCE_TABLE_HOSTS = 15;
|
|
37881
39030
|
}
|
|
37882
39031
|
});
|
|
37883
39032
|
|
|
@@ -39854,14 +41003,14 @@ var init_em_search = __esm({
|
|
|
39854
41003
|
});
|
|
39855
41004
|
|
|
39856
41005
|
// src/ui/api.ts
|
|
39857
|
-
import { existsSync as existsSync21, readdirSync as
|
|
39858
|
-
import { join as
|
|
41006
|
+
import { existsSync as existsSync21, readdirSync as readdirSync8 } from "fs";
|
|
41007
|
+
import { join as join26, relative as relative8 } from "path";
|
|
39859
41008
|
function listProjects() {
|
|
39860
41009
|
const dir = paths().projects;
|
|
39861
41010
|
const out = [];
|
|
39862
|
-
for (const id of
|
|
41011
|
+
for (const id of readdirSync8(dir, { withFileTypes: true })) {
|
|
39863
41012
|
if (!id.isDirectory()) continue;
|
|
39864
|
-
const db =
|
|
41013
|
+
const db = join26(dir, id.name, "eminiar.db");
|
|
39865
41014
|
try {
|
|
39866
41015
|
const a = new SqliteAdapter(db, { vectors: false });
|
|
39867
41016
|
out.push({ id: id.name, root: a.sessionLatest()?.project_root ?? null });
|
|
@@ -39887,8 +41036,8 @@ function statsPayload(ctx) {
|
|
|
39887
41036
|
const db = getStorage(ctx);
|
|
39888
41037
|
const sessionId = getCurrentSession(ctx);
|
|
39889
41038
|
const g = globalDb();
|
|
39890
|
-
const onboardingDir =
|
|
39891
|
-
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)) : [];
|
|
39892
41041
|
return {
|
|
39893
41042
|
project: ctx.root,
|
|
39894
41043
|
projectId: ctx.id,
|
|
@@ -40080,7 +41229,7 @@ function docsPayload(ctx) {
|
|
|
40080
41229
|
const known = new Set(m.sources.map((s) => docsTag(s.name)));
|
|
40081
41230
|
const orphans2 = [...stats.values()].filter((s) => !known.has(s.source)).map((s) => ({ source: s.source, documents: s.documents, chunks: s.chunks }));
|
|
40082
41231
|
return {
|
|
40083
|
-
manifest:
|
|
41232
|
+
manifest: relative8(ctx.root, manifestPath(ctx)),
|
|
40084
41233
|
sources,
|
|
40085
41234
|
orphans: orphans2,
|
|
40086
41235
|
totals: {
|
|
@@ -40398,8 +41547,8 @@ var init_namepath = __esm({
|
|
|
40398
41547
|
});
|
|
40399
41548
|
|
|
40400
41549
|
// src/code/symbols.ts
|
|
40401
|
-
import { existsSync as existsSync22, readFileSync as
|
|
40402
|
-
import { join as
|
|
41550
|
+
import { existsSync as existsSync22, readFileSync as readFileSync20 } from "fs";
|
|
41551
|
+
import { join as join27 } from "path";
|
|
40403
41552
|
function isExported2(n) {
|
|
40404
41553
|
if (!n.meta) return false;
|
|
40405
41554
|
try {
|
|
@@ -40435,8 +41584,8 @@ var init_symbols2 = __esm({
|
|
|
40435
41584
|
});
|
|
40436
41585
|
|
|
40437
41586
|
// src/ui/graph-api.ts
|
|
40438
|
-
import { readFileSync as
|
|
40439
|
-
import { relative as
|
|
41587
|
+
import { readFileSync as readFileSync21, realpathSync as realpathSync2, statSync as statSync9 } from "fs";
|
|
41588
|
+
import { relative as relative9, resolve as resolve7 } from "path";
|
|
40440
41589
|
function metaOf2(row) {
|
|
40441
41590
|
if (!row?.meta) return {};
|
|
40442
41591
|
try {
|
|
@@ -40497,17 +41646,17 @@ function treePayload(ctx) {
|
|
|
40497
41646
|
}
|
|
40498
41647
|
function filePayload(ctx, rel) {
|
|
40499
41648
|
const clean = rel.replace(/^\.\//, "");
|
|
40500
|
-
const abs =
|
|
40501
|
-
if (
|
|
41649
|
+
const abs = resolve7(ctx.root, clean);
|
|
41650
|
+
if (relative9(ctx.root, abs).startsWith("..")) throw new Error("path outside the project");
|
|
40502
41651
|
let real = abs;
|
|
40503
41652
|
try {
|
|
40504
41653
|
real = realpathSync2(abs);
|
|
40505
41654
|
} catch {
|
|
40506
41655
|
return { error: "not found", path: clean };
|
|
40507
41656
|
}
|
|
40508
|
-
if (
|
|
41657
|
+
if (relative9(realpathSync2(ctx.root), real).startsWith(".."))
|
|
40509
41658
|
throw new Error("path outside the project");
|
|
40510
|
-
const st =
|
|
41659
|
+
const st = statSync9(real);
|
|
40511
41660
|
if (!st.isFile()) return { error: "not a file", path: clean };
|
|
40512
41661
|
const db = getStorage(ctx);
|
|
40513
41662
|
const row = db.fileIndexGet(clean);
|
|
@@ -40521,7 +41670,7 @@ function filePayload(ctx, rel) {
|
|
|
40521
41670
|
}));
|
|
40522
41671
|
if (st.size > MAX_CODE_BYTES)
|
|
40523
41672
|
return { path: clean, tooLarge: true, size: st.size, status: row?.status ?? null, symbols };
|
|
40524
|
-
const content =
|
|
41673
|
+
const content = readFileSync21(real, "utf8");
|
|
40525
41674
|
return {
|
|
40526
41675
|
path: clean,
|
|
40527
41676
|
size: st.size,
|
|
@@ -41243,14 +42392,14 @@ function buildHistory(ctx, db, opts = {}) {
|
|
|
41243
42392
|
};
|
|
41244
42393
|
});
|
|
41245
42394
|
const bySha = new Map(commits.map((c) => [c.sha, c]));
|
|
41246
|
-
const
|
|
42395
|
+
const resolve8 = (prefix) => {
|
|
41247
42396
|
if (bySha.has(prefix)) return bySha.get(prefix);
|
|
41248
42397
|
for (const c of commits) if (c.sha.startsWith(prefix)) return c;
|
|
41249
42398
|
return void 0;
|
|
41250
42399
|
};
|
|
41251
42400
|
for (const c of commits) {
|
|
41252
42401
|
if (c.kind !== "revert" || !c.revertOf) continue;
|
|
41253
|
-
const target =
|
|
42402
|
+
const target = resolve8(c.revertOf);
|
|
41254
42403
|
if (target) {
|
|
41255
42404
|
target.revertedBy = c.sha;
|
|
41256
42405
|
c.origins.push(target.sha);
|
|
@@ -42099,9 +43248,9 @@ __export(server_exports, {
|
|
|
42099
43248
|
uiDir: () => uiDir
|
|
42100
43249
|
});
|
|
42101
43250
|
import { spawn as spawn2 } from "child_process";
|
|
42102
|
-
import { existsSync as existsSync23, readFileSync as
|
|
43251
|
+
import { existsSync as existsSync23, readFileSync as readFileSync22, statSync as statSync10 } from "fs";
|
|
42103
43252
|
import { createServer } from "http";
|
|
42104
|
-
import { extname as extname3, join as
|
|
43253
|
+
import { extname as extname3, join as join28, normalize as normalize6 } from "path";
|
|
42105
43254
|
import { URL as URL2 } from "url";
|
|
42106
43255
|
function json2(res, status, body) {
|
|
42107
43256
|
res.writeHead(status, {
|
|
@@ -42123,7 +43272,7 @@ function strList(v) {
|
|
|
42123
43272
|
return void 0;
|
|
42124
43273
|
}
|
|
42125
43274
|
function uiDir() {
|
|
42126
|
-
return process.env.EMINIAR_UI_DIR ||
|
|
43275
|
+
return process.env.EMINIAR_UI_DIR || join28(pluginRoot(), "dist", "ui");
|
|
42127
43276
|
}
|
|
42128
43277
|
function originAllowed(req, port) {
|
|
42129
43278
|
const host = req.headers.host ?? "";
|
|
@@ -42147,25 +43296,25 @@ function originAllowed(req, port) {
|
|
|
42147
43296
|
function serveStatic(res, p) {
|
|
42148
43297
|
const dir = uiDir();
|
|
42149
43298
|
const rel = p === "/" ? "index.html" : p.replace(/^\/+/, "");
|
|
42150
|
-
const file3 =
|
|
42151
|
-
if (!file3.startsWith(
|
|
42152
|
-
if (!existsSync23(file3) || !
|
|
43299
|
+
const file3 = normalize6(join28(dir, rel));
|
|
43300
|
+
if (!file3.startsWith(normalize6(dir))) return false;
|
|
43301
|
+
if (!existsSync23(file3) || !statSync10(file3).isFile()) return false;
|
|
42153
43302
|
const type = MIME[extname3(file3).toLowerCase()] ?? "application/octet-stream";
|
|
42154
43303
|
if (rel === "index.html") {
|
|
42155
43304
|
const stamp = (name) => {
|
|
42156
43305
|
try {
|
|
42157
|
-
return `/${name}?v=${Math.floor(
|
|
43306
|
+
return `/${name}?v=${Math.floor(statSync10(join28(dir, name)).mtimeMs)}`;
|
|
42158
43307
|
} catch {
|
|
42159
43308
|
return `/${name}`;
|
|
42160
43309
|
}
|
|
42161
43310
|
};
|
|
42162
|
-
const html =
|
|
43311
|
+
const html = readFileSync22(file3, "utf8").replace('"/app.global.js"', `"${stamp("app.global.js")}"`).replace('"/styles.css"', `"${stamp("styles.css")}"`);
|
|
42163
43312
|
res.writeHead(200, { "content-type": type, "cache-control": "no-store" });
|
|
42164
43313
|
res.end(html);
|
|
42165
43314
|
return true;
|
|
42166
43315
|
}
|
|
42167
43316
|
res.writeHead(200, { "content-type": type, "cache-control": "no-cache" });
|
|
42168
|
-
res.end(
|
|
43317
|
+
res.end(readFileSync22(file3));
|
|
42169
43318
|
return true;
|
|
42170
43319
|
}
|
|
42171
43320
|
async function serve(opts) {
|
|
@@ -42506,9 +43655,9 @@ async function serve(opts) {
|
|
|
42506
43655
|
json2(res, 500, { error: err.message });
|
|
42507
43656
|
}
|
|
42508
43657
|
});
|
|
42509
|
-
await new Promise((
|
|
43658
|
+
await new Promise((resolve8, reject) => {
|
|
42510
43659
|
server.once("error", reject);
|
|
42511
|
-
server.listen(opts.port, "127.0.0.1",
|
|
43660
|
+
server.listen(opts.port, "127.0.0.1", resolve8);
|
|
42512
43661
|
});
|
|
42513
43662
|
const url2 = `http://127.0.0.1:${opts.port}/`;
|
|
42514
43663
|
console.log(`eminiar dashboard at ${url2} (Ctrl-C to stop)`);
|
|
@@ -42549,7 +43698,7 @@ var statusline_exports = {};
|
|
|
42549
43698
|
__export(statusline_exports, {
|
|
42550
43699
|
renderStatusline: () => renderStatusline
|
|
42551
43700
|
});
|
|
42552
|
-
import { existsSync as existsSync24, readFileSync as
|
|
43701
|
+
import { existsSync as existsSync24, readFileSync as readFileSync23, writeFileSync as writeFileSync9 } from "fs";
|
|
42553
43702
|
function renderStatusline(raw) {
|
|
42554
43703
|
let input2 = {};
|
|
42555
43704
|
try {
|
|
@@ -42559,7 +43708,7 @@ function renderStatusline(raw) {
|
|
|
42559
43708
|
const cache3 = paths().statuslineCache;
|
|
42560
43709
|
try {
|
|
42561
43710
|
if (existsSync24(cache3)) {
|
|
42562
|
-
const c = JSON.parse(
|
|
43711
|
+
const c = JSON.parse(readFileSync23(cache3, "utf8"));
|
|
42563
43712
|
if (Date.now() - c.at < 5e3 && c.key === (input2.session_id ?? "")) return c.line;
|
|
42564
43713
|
}
|
|
42565
43714
|
} catch {
|