@neat.is/core 0.10.5-dev.20261006 → 0.10.6-dev.20261007
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-DVCGDMTZ.js → chunk-KTJK2PAH.js} +1443 -603
- package/dist/chunk-KTJK2PAH.js.map +1 -0
- package/dist/{chunk-Q2SL2SVH.js → chunk-VL7ANDPI.js} +35 -218
- package/dist/chunk-VL7ANDPI.js.map +1 -0
- package/dist/cli.cjs +1766 -1105
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/index.cjs +1596 -938
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -1
- package/dist/index.d.ts +43 -1
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +1609 -951
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +1307 -637
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +3 -2
- package/dist/chunk-DVCGDMTZ.js.map +0 -1
- package/dist/chunk-Q2SL2SVH.js.map +0 -1
package/dist/server.cjs
CHANGED
|
@@ -63,8 +63,8 @@ function mountBearerAuth(app, opts) {
|
|
|
63
63
|
]);
|
|
64
64
|
const publicRead = opts.publicRead === true;
|
|
65
65
|
app.addHook("preHandler", (req, reply, done) => {
|
|
66
|
-
const
|
|
67
|
-
if (exactUnauthPaths.has(
|
|
66
|
+
const path81 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
67
|
+
if (exactUnauthPaths.has(path81) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path81)) {
|
|
68
68
|
done();
|
|
69
69
|
return;
|
|
70
70
|
}
|
|
@@ -201,8 +201,8 @@ function reshapeGrpcRequest(req) {
|
|
|
201
201
|
};
|
|
202
202
|
}
|
|
203
203
|
function resolveProtoRoot() {
|
|
204
|
-
const here =
|
|
205
|
-
return
|
|
204
|
+
const here = import_node_path57.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
205
|
+
return import_node_path57.default.resolve(here, "..", "proto");
|
|
206
206
|
}
|
|
207
207
|
function loadTraceService() {
|
|
208
208
|
const protoRoot = resolveProtoRoot();
|
|
@@ -270,13 +270,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
270
270
|
})
|
|
271
271
|
};
|
|
272
272
|
}
|
|
273
|
-
var import_node_url,
|
|
273
|
+
var import_node_url, import_node_path57, import_node_crypto2, grpc, protoLoader;
|
|
274
274
|
var init_otel_grpc = __esm({
|
|
275
275
|
"src/otel-grpc.ts"() {
|
|
276
276
|
"use strict";
|
|
277
277
|
init_cjs_shims();
|
|
278
278
|
import_node_url = require("url");
|
|
279
|
-
|
|
279
|
+
import_node_path57 = __toESM(require("path"), 1);
|
|
280
280
|
import_node_crypto2 = require("crypto");
|
|
281
281
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
282
282
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -422,8 +422,8 @@ function websocketChannelPathOf(attrs) {
|
|
|
422
422
|
const v = attrs[key];
|
|
423
423
|
if (typeof v === "string" && v.length > 0) {
|
|
424
424
|
const q = v.indexOf("?");
|
|
425
|
-
const
|
|
426
|
-
if (
|
|
425
|
+
const path81 = q === -1 ? v : v.slice(0, q);
|
|
426
|
+
if (path81.length > 0) return path81;
|
|
427
427
|
}
|
|
428
428
|
}
|
|
429
429
|
return void 0;
|
|
@@ -485,10 +485,10 @@ function parseOtlpRequest(body) {
|
|
|
485
485
|
return out;
|
|
486
486
|
}
|
|
487
487
|
function loadProtoRoot() {
|
|
488
|
-
const here =
|
|
489
|
-
const protoRoot =
|
|
488
|
+
const here = import_node_path58.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
489
|
+
const protoRoot = import_node_path58.default.resolve(here, "..", "proto");
|
|
490
490
|
const root = new import_protobufjs.default.Root();
|
|
491
|
-
root.resolvePath = (_origin, target) =>
|
|
491
|
+
root.resolvePath = (_origin, target) => import_node_path58.default.resolve(protoRoot, target);
|
|
492
492
|
root.loadSync(
|
|
493
493
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
494
494
|
{ keepCase: true }
|
|
@@ -750,12 +750,12 @@ async function buildOtelReceiver(opts) {
|
|
|
750
750
|
};
|
|
751
751
|
return decorated;
|
|
752
752
|
}
|
|
753
|
-
var
|
|
753
|
+
var import_node_path58, import_node_url2, import_node_zlib, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
|
|
754
754
|
var init_otel = __esm({
|
|
755
755
|
"src/otel.ts"() {
|
|
756
756
|
"use strict";
|
|
757
757
|
init_cjs_shims();
|
|
758
|
-
|
|
758
|
+
import_node_path58 = __toESM(require("path"), 1);
|
|
759
759
|
import_node_url2 = require("url");
|
|
760
760
|
import_node_zlib = __toESM(require("zlib"), 1);
|
|
761
761
|
import_fastify = __toESM(require("fastify"), 1);
|
|
@@ -772,7 +772,7 @@ var init_otel = __esm({
|
|
|
772
772
|
|
|
773
773
|
// src/server.ts
|
|
774
774
|
init_cjs_shims();
|
|
775
|
-
var
|
|
775
|
+
var import_node_path80 = __toESM(require("path"), 1);
|
|
776
776
|
|
|
777
777
|
// src/graph.ts
|
|
778
778
|
init_cjs_shims();
|
|
@@ -795,8 +795,9 @@ function getGraph(project = DEFAULT_PROJECT) {
|
|
|
795
795
|
// src/api.ts
|
|
796
796
|
init_cjs_shims();
|
|
797
797
|
var import_fastify2 = __toESM(require("fastify"), 1);
|
|
798
|
+
var import_node_crypto6 = require("crypto");
|
|
798
799
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
799
|
-
var
|
|
800
|
+
var import_types103 = require("@neat.is/types");
|
|
800
801
|
|
|
801
802
|
// src/extend/index.ts
|
|
802
803
|
init_cjs_shims();
|
|
@@ -1514,14 +1515,37 @@ init_cjs_shims();
|
|
|
1514
1515
|
|
|
1515
1516
|
// src/ingest.ts
|
|
1516
1517
|
init_cjs_shims();
|
|
1517
|
-
var
|
|
1518
|
-
var
|
|
1519
|
-
var sourceMapJs = __toESM(require("source-map-js"), 1);
|
|
1518
|
+
var import_node_fs11 = require("fs");
|
|
1519
|
+
var import_node_path12 = __toESM(require("path"), 1);
|
|
1520
1520
|
|
|
1521
|
-
// src/
|
|
1521
|
+
// src/durable-incident.ts
|
|
1522
1522
|
init_cjs_shims();
|
|
1523
1523
|
var import_node_fs4 = require("fs");
|
|
1524
1524
|
var import_node_path4 = __toESM(require("path"), 1);
|
|
1525
|
+
async function appendRuntimeIncident(errorsPath, project, event, options = {}) {
|
|
1526
|
+
const token = options.token ?? process.env.NEAT_INCIDENT_DURABLE_TOKEN;
|
|
1527
|
+
if (token !== void 0) {
|
|
1528
|
+
if (Buffer.byteLength(token, "utf8") < 32) throw new Error("durable incident sink unavailable");
|
|
1529
|
+
const response = await (options.fetchImpl ?? fetch)("http://127.0.0.1:8081/incident-append", {
|
|
1530
|
+
method: "POST",
|
|
1531
|
+
headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
|
|
1532
|
+
body: JSON.stringify({ project, event }),
|
|
1533
|
+
signal: AbortSignal.timeout(3e4)
|
|
1534
|
+
});
|
|
1535
|
+
if (response.status !== 204) throw new Error("durable incident sink unavailable");
|
|
1536
|
+
return;
|
|
1537
|
+
}
|
|
1538
|
+
await import_node_fs4.promises.mkdir(import_node_path4.default.dirname(errorsPath), { recursive: true });
|
|
1539
|
+
await import_node_fs4.promises.appendFile(errorsPath, JSON.stringify(event) + "\n", "utf8");
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
// src/ingest.ts
|
|
1543
|
+
var sourceMapJs = __toESM(require("source-map-js"), 1);
|
|
1544
|
+
|
|
1545
|
+
// src/policy.ts
|
|
1546
|
+
init_cjs_shims();
|
|
1547
|
+
var import_node_fs5 = require("fs");
|
|
1548
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
1525
1549
|
var import_types2 = require("@neat.is/types");
|
|
1526
1550
|
|
|
1527
1551
|
// src/events.ts
|
|
@@ -1990,7 +2014,7 @@ function matchPolicyToNode(graph, policy, nodeId, node) {
|
|
|
1990
2014
|
async function loadPolicyFile(policyPath) {
|
|
1991
2015
|
let raw;
|
|
1992
2016
|
try {
|
|
1993
|
-
raw = await
|
|
2017
|
+
raw = await import_node_fs5.promises.readFile(policyPath, "utf8");
|
|
1994
2018
|
} catch (err) {
|
|
1995
2019
|
if (err.code === "ENOENT") return [];
|
|
1996
2020
|
throw err;
|
|
@@ -2011,8 +2035,8 @@ var PolicyViolationsLog = class {
|
|
|
2011
2035
|
if (!this.seen) await this.hydrate();
|
|
2012
2036
|
if (this.seen.has(v.id)) return false;
|
|
2013
2037
|
this.seen.add(v.id);
|
|
2014
|
-
await
|
|
2015
|
-
await
|
|
2038
|
+
await import_node_fs5.promises.mkdir(import_node_path5.default.dirname(this.path), { recursive: true });
|
|
2039
|
+
await import_node_fs5.promises.appendFile(this.path, JSON.stringify(v) + "\n", "utf8");
|
|
2016
2040
|
emitNeatEvent({
|
|
2017
2041
|
type: "policy-violation",
|
|
2018
2042
|
project: this.project,
|
|
@@ -2022,7 +2046,7 @@ var PolicyViolationsLog = class {
|
|
|
2022
2046
|
}
|
|
2023
2047
|
async readAll() {
|
|
2024
2048
|
try {
|
|
2025
|
-
const raw = await
|
|
2049
|
+
const raw = await import_node_fs5.promises.readFile(this.path, "utf8");
|
|
2026
2050
|
return raw.split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
2027
2051
|
} catch (err) {
|
|
2028
2052
|
if (err.code === "ENOENT") return [];
|
|
@@ -2041,7 +2065,7 @@ var import_types7 = require("@neat.is/types");
|
|
|
2041
2065
|
|
|
2042
2066
|
// src/extract/routes.ts
|
|
2043
2067
|
init_cjs_shims();
|
|
2044
|
-
var
|
|
2068
|
+
var import_node_path11 = __toESM(require("path"), 1);
|
|
2045
2069
|
var import_tree_sitter2 = __toESM(require("tree-sitter"), 1);
|
|
2046
2070
|
var import_tree_sitter_javascript2 = __toESM(require("tree-sitter-javascript"), 1);
|
|
2047
2071
|
var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
|
|
@@ -2053,8 +2077,8 @@ var import_types6 = require("@neat.is/types");
|
|
|
2053
2077
|
|
|
2054
2078
|
// src/extract/shared.ts
|
|
2055
2079
|
init_cjs_shims();
|
|
2056
|
-
var
|
|
2057
|
-
var
|
|
2080
|
+
var import_node_fs6 = require("fs");
|
|
2081
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
2058
2082
|
var import_yaml = require("yaml");
|
|
2059
2083
|
var import_types3 = require("@neat.is/types");
|
|
2060
2084
|
var SERVICE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs", ".cjs", ".ts", ".tsx", ".py", ".go", ".rb", ".php", ".cs", ".java", ".kt", ".rs", ".cpp", ".cc", ".cxx", ".c++", ".hpp", ".hh", ".hxx", ".h++"]);
|
|
@@ -2098,7 +2122,7 @@ async function isPythonVenvDir(dir) {
|
|
|
2098
2122
|
const cached = PYVENV_MARKER_CACHE.get(dir);
|
|
2099
2123
|
if (cached !== void 0) return cached;
|
|
2100
2124
|
try {
|
|
2101
|
-
const stat = await
|
|
2125
|
+
const stat = await import_node_fs6.promises.stat(import_node_path6.default.join(dir, "pyvenv.cfg"));
|
|
2102
2126
|
const ok = stat.isFile();
|
|
2103
2127
|
PYVENV_MARKER_CACHE.set(dir, ok);
|
|
2104
2128
|
return ok;
|
|
@@ -2108,7 +2132,7 @@ async function isPythonVenvDir(dir) {
|
|
|
2108
2132
|
}
|
|
2109
2133
|
}
|
|
2110
2134
|
function isConfigFile(name) {
|
|
2111
|
-
const ext =
|
|
2135
|
+
const ext = import_node_path6.default.extname(name);
|
|
2112
2136
|
if (CONFIG_FILE_EXTENSIONS.has(ext)) return { match: true, fileType: ext.slice(1) };
|
|
2113
2137
|
if (JSON_CONFIG_FILENAMES.has(name)) return { match: true, fileType: "json" };
|
|
2114
2138
|
if (name === ".env" || name.startsWith(".env.")) {
|
|
@@ -2225,16 +2249,16 @@ function cleanVersion(raw) {
|
|
|
2225
2249
|
return raw.replace(/^[\^~><=v\s]+/, "").trim() || void 0;
|
|
2226
2250
|
}
|
|
2227
2251
|
async function readJson(filePath) {
|
|
2228
|
-
const raw = await
|
|
2252
|
+
const raw = await import_node_fs6.promises.readFile(filePath, "utf8");
|
|
2229
2253
|
return JSON.parse(raw);
|
|
2230
2254
|
}
|
|
2231
2255
|
async function readYaml(filePath) {
|
|
2232
|
-
const raw = await
|
|
2256
|
+
const raw = await import_node_fs6.promises.readFile(filePath, "utf8");
|
|
2233
2257
|
return (0, import_yaml.parse)(raw);
|
|
2234
2258
|
}
|
|
2235
2259
|
async function exists2(p) {
|
|
2236
2260
|
try {
|
|
2237
|
-
await
|
|
2261
|
+
await import_node_fs6.promises.access(p);
|
|
2238
2262
|
return true;
|
|
2239
2263
|
} catch {
|
|
2240
2264
|
return false;
|
|
@@ -2243,8 +2267,8 @@ async function exists2(p) {
|
|
|
2243
2267
|
|
|
2244
2268
|
// src/extract/errors.ts
|
|
2245
2269
|
init_cjs_shims();
|
|
2246
|
-
var
|
|
2247
|
-
var
|
|
2270
|
+
var import_node_fs7 = require("fs");
|
|
2271
|
+
var import_node_path7 = __toESM(require("path"), 1);
|
|
2248
2272
|
var sink = [];
|
|
2249
2273
|
function recordExtractionError(producer, file, err) {
|
|
2250
2274
|
const e = err instanceof Error ? err : new Error(String(err));
|
|
@@ -2263,14 +2287,14 @@ function drainExtractionErrors() {
|
|
|
2263
2287
|
}
|
|
2264
2288
|
async function writeExtractionErrors(errors, errorsPath) {
|
|
2265
2289
|
if (errors.length === 0) return;
|
|
2266
|
-
await
|
|
2290
|
+
await import_node_fs7.promises.mkdir(import_node_path7.default.dirname(errorsPath), { recursive: true });
|
|
2267
2291
|
const lines = errors.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
2268
|
-
await
|
|
2292
|
+
await import_node_fs7.promises.appendFile(errorsPath, lines, "utf8");
|
|
2269
2293
|
}
|
|
2270
2294
|
function extractionHealthPathFor(errorsPath) {
|
|
2271
|
-
const dir =
|
|
2272
|
-
const suffix =
|
|
2273
|
-
return
|
|
2295
|
+
const dir = import_node_path7.default.dirname(errorsPath);
|
|
2296
|
+
const suffix = import_node_path7.default.basename(errorsPath).replace(/^errors/, "").replace(/\.ndjson$/, "");
|
|
2297
|
+
return import_node_path7.default.join(dir, `extraction-health${suffix}.json`);
|
|
2274
2298
|
}
|
|
2275
2299
|
var HEALTH_FILE_SAMPLE = 100;
|
|
2276
2300
|
async function writeExtractionHealth(errors, healthPath, now = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
@@ -2282,13 +2306,13 @@ async function writeExtractionHealth(errors, healthPath, now = (/* @__PURE__ */
|
|
|
2282
2306
|
files: errors.slice(0, HEALTH_FILE_SAMPLE).map((e) => e.file),
|
|
2283
2307
|
updatedAt: now
|
|
2284
2308
|
};
|
|
2285
|
-
await
|
|
2286
|
-
await
|
|
2309
|
+
await import_node_fs7.promises.mkdir(import_node_path7.default.dirname(healthPath), { recursive: true });
|
|
2310
|
+
await import_node_fs7.promises.writeFile(healthPath, JSON.stringify(coverage), "utf8");
|
|
2287
2311
|
}
|
|
2288
2312
|
async function readExtractionHealth(healthPath) {
|
|
2289
2313
|
let raw;
|
|
2290
2314
|
try {
|
|
2291
|
-
raw = await
|
|
2315
|
+
raw = await import_node_fs7.promises.readFile(healthPath, "utf8");
|
|
2292
2316
|
} catch {
|
|
2293
2317
|
return void 0;
|
|
2294
2318
|
}
|
|
@@ -2324,21 +2348,21 @@ function isRejectedLogEnabled() {
|
|
|
2324
2348
|
}
|
|
2325
2349
|
async function writeRejectedExtracted(drops, rejectedPath) {
|
|
2326
2350
|
if (drops.length === 0) return;
|
|
2327
|
-
await
|
|
2351
|
+
await import_node_fs7.promises.mkdir(import_node_path7.default.dirname(rejectedPath), { recursive: true });
|
|
2328
2352
|
const lines = drops.map((d) => JSON.stringify({ ...d, ts: (/* @__PURE__ */ new Date()).toISOString() })).join("\n") + "\n";
|
|
2329
|
-
await
|
|
2353
|
+
await import_node_fs7.promises.appendFile(rejectedPath, lines, "utf8");
|
|
2330
2354
|
}
|
|
2331
2355
|
|
|
2332
2356
|
// src/extract/calls/shared.ts
|
|
2333
2357
|
init_cjs_shims();
|
|
2334
|
-
var
|
|
2335
|
-
var
|
|
2358
|
+
var import_node_fs9 = require("fs");
|
|
2359
|
+
var import_node_path9 = __toESM(require("path"), 1);
|
|
2336
2360
|
var import_types4 = require("@neat.is/types");
|
|
2337
2361
|
|
|
2338
2362
|
// src/extract/gitignore.ts
|
|
2339
2363
|
init_cjs_shims();
|
|
2340
|
-
var
|
|
2341
|
-
var
|
|
2364
|
+
var import_node_fs8 = require("fs");
|
|
2365
|
+
var import_node_path8 = __toESM(require("path"), 1);
|
|
2342
2366
|
var import_ignore = __toESM(require("ignore"), 1);
|
|
2343
2367
|
var LAYER_CACHE = /* @__PURE__ */ new Map();
|
|
2344
2368
|
async function layerFor(dir) {
|
|
@@ -2346,7 +2370,7 @@ async function layerFor(dir) {
|
|
|
2346
2370
|
if (cached !== void 0) return cached;
|
|
2347
2371
|
let layer = null;
|
|
2348
2372
|
try {
|
|
2349
|
-
const raw = await
|
|
2373
|
+
const raw = await import_node_fs8.promises.readFile(import_node_path8.default.join(dir, ".gitignore"), "utf8");
|
|
2350
2374
|
layer = { dir, ig: (0, import_ignore.default)().add(raw) };
|
|
2351
2375
|
} catch {
|
|
2352
2376
|
layer = null;
|
|
@@ -2356,7 +2380,7 @@ async function layerFor(dir) {
|
|
|
2356
2380
|
}
|
|
2357
2381
|
async function isRepoRoot(dir) {
|
|
2358
2382
|
try {
|
|
2359
|
-
await
|
|
2383
|
+
await import_node_fs8.promises.stat(import_node_path8.default.join(dir, ".git"));
|
|
2360
2384
|
return true;
|
|
2361
2385
|
} catch {
|
|
2362
2386
|
return false;
|
|
@@ -2364,11 +2388,11 @@ async function isRepoRoot(dir) {
|
|
|
2364
2388
|
}
|
|
2365
2389
|
async function loadIgnoreChain(startDir) {
|
|
2366
2390
|
const dirs = [];
|
|
2367
|
-
let current =
|
|
2391
|
+
let current = import_node_path8.default.resolve(startDir);
|
|
2368
2392
|
for (; ; ) {
|
|
2369
2393
|
dirs.push(current);
|
|
2370
2394
|
if (await isRepoRoot(current)) break;
|
|
2371
|
-
const parent =
|
|
2395
|
+
const parent = import_node_path8.default.dirname(current);
|
|
2372
2396
|
if (parent === current) break;
|
|
2373
2397
|
current = parent;
|
|
2374
2398
|
}
|
|
@@ -2381,7 +2405,7 @@ async function loadIgnoreChain(startDir) {
|
|
|
2381
2405
|
return layers;
|
|
2382
2406
|
}
|
|
2383
2407
|
async function extendIgnoreChain(chain, dir) {
|
|
2384
|
-
const layer = await layerFor(
|
|
2408
|
+
const layer = await layerFor(import_node_path8.default.resolve(dir));
|
|
2385
2409
|
return layer ? [...chain, layer] : chain;
|
|
2386
2410
|
}
|
|
2387
2411
|
function isIgnored(chain, fullPath, isDir) {
|
|
@@ -2389,14 +2413,14 @@ function isIgnored(chain, fullPath, isDir) {
|
|
|
2389
2413
|
}
|
|
2390
2414
|
function decide(chain, fullPath, isDir) {
|
|
2391
2415
|
if (chain.length === 0) return { ignored: false, subtree: chain };
|
|
2392
|
-
const abs =
|
|
2416
|
+
const abs = import_node_path8.default.resolve(fullPath);
|
|
2393
2417
|
let ignored = false;
|
|
2394
2418
|
let unignoredAt = -1;
|
|
2395
2419
|
for (let i = 0; i < chain.length; i++) {
|
|
2396
2420
|
const layer = chain[i];
|
|
2397
|
-
const rel =
|
|
2398
|
-
if (!rel || rel.startsWith("..") ||
|
|
2399
|
-
const candidate = rel.split(
|
|
2421
|
+
const rel = import_node_path8.default.relative(layer.dir, abs);
|
|
2422
|
+
if (!rel || rel.startsWith("..") || import_node_path8.default.isAbsolute(rel)) continue;
|
|
2423
|
+
const candidate = rel.split(import_node_path8.default.sep).join("/") + (isDir ? "/" : "");
|
|
2400
2424
|
const result = layer.ig.test(candidate);
|
|
2401
2425
|
if (result.ignored) ignored = true;
|
|
2402
2426
|
else if (result.unignored) {
|
|
@@ -2415,7 +2439,7 @@ function buildServiceHostIndex(services) {
|
|
|
2415
2439
|
const knownHosts = /* @__PURE__ */ new Set();
|
|
2416
2440
|
const hostToNodeId = /* @__PURE__ */ new Map();
|
|
2417
2441
|
for (const service of services) {
|
|
2418
|
-
const base =
|
|
2442
|
+
const base = import_node_path9.default.basename(service.dir);
|
|
2419
2443
|
knownHosts.add(base);
|
|
2420
2444
|
knownHosts.add(service.pkg.name);
|
|
2421
2445
|
hostToNodeId.set(base, service.node.id);
|
|
@@ -2424,29 +2448,29 @@ function buildServiceHostIndex(services) {
|
|
|
2424
2448
|
return { knownHosts, hostToNodeId };
|
|
2425
2449
|
}
|
|
2426
2450
|
async function walkSourceFiles(dir, excludeDirs = []) {
|
|
2427
|
-
const excluded = new Set(excludeDirs.map((d) =>
|
|
2451
|
+
const excluded = new Set(excludeDirs.map((d) => import_node_path9.default.resolve(d)));
|
|
2428
2452
|
const out = [];
|
|
2429
2453
|
const rootChain = await loadIgnoreChain(dir);
|
|
2430
2454
|
async function walk11(current, inherited) {
|
|
2431
|
-
const entries = await
|
|
2455
|
+
const entries = await import_node_fs9.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
2432
2456
|
const chain = entries.some((e) => e.isFile() && e.name === ".gitignore") ? await extendIgnoreChain(inherited, current) : inherited;
|
|
2433
2457
|
for (const entry of entries) {
|
|
2434
|
-
const full =
|
|
2458
|
+
const full = import_node_path9.default.join(current, entry.name);
|
|
2435
2459
|
if (entry.isDirectory()) {
|
|
2436
2460
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
2437
|
-
if (excluded.has(
|
|
2461
|
+
if (excluded.has(import_node_path9.default.resolve(full))) continue;
|
|
2438
2462
|
const verdict = decide(chain, full, true);
|
|
2439
2463
|
if (verdict.ignored) continue;
|
|
2440
2464
|
if (await isPythonVenvDir(full)) continue;
|
|
2441
2465
|
await walk11(full, verdict.subtree);
|
|
2442
|
-
} else if (entry.isFile() && SERVICE_FILE_EXTENSIONS.has(
|
|
2466
|
+
} else if (entry.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path9.default.extname(entry.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
|
|
2443
2467
|
// would attribute our instrumentation imports to the user's service.
|
|
2444
2468
|
!isNeatAuthoredSourceFile(entry.name) && !isIgnored(chain, full, false)) {
|
|
2445
2469
|
out.push(full);
|
|
2446
2470
|
}
|
|
2447
2471
|
}
|
|
2448
2472
|
}
|
|
2449
|
-
await walk11(dir, rootChain.filter((l) =>
|
|
2473
|
+
await walk11(dir, rootChain.filter((l) => import_node_path9.default.resolve(l.dir) !== import_node_path9.default.resolve(dir)));
|
|
2450
2474
|
return out;
|
|
2451
2475
|
}
|
|
2452
2476
|
var MINIFIED_LINE_CHARS = 5e3;
|
|
@@ -2474,7 +2498,7 @@ async function loadSourceFiles(dir, excludeDirs = []) {
|
|
|
2474
2498
|
const out = [];
|
|
2475
2499
|
for (const p of paths) {
|
|
2476
2500
|
try {
|
|
2477
|
-
const content = await
|
|
2501
|
+
const content = await import_node_fs9.promises.readFile(p, "utf8");
|
|
2478
2502
|
const verdict = isMinifiedSource(p, content);
|
|
2479
2503
|
if (verdict.minified) {
|
|
2480
2504
|
noteSkippedFile({ path: p, reason: "minified", detail: verdict.detail });
|
|
@@ -2502,7 +2526,7 @@ function toPosix(p) {
|
|
|
2502
2526
|
return p.split("\\").join("/");
|
|
2503
2527
|
}
|
|
2504
2528
|
function languageForPath(relPath) {
|
|
2505
|
-
switch (
|
|
2529
|
+
switch (import_node_path9.default.extname(relPath).toLowerCase()) {
|
|
2506
2530
|
case ".py":
|
|
2507
2531
|
return "python";
|
|
2508
2532
|
case ".go":
|
|
@@ -2586,8 +2610,8 @@ function ensureFileNode(graph, serviceName, serviceNodeId, relPath) {
|
|
|
2586
2610
|
|
|
2587
2611
|
// src/extract/imports.ts
|
|
2588
2612
|
init_cjs_shims();
|
|
2589
|
-
var
|
|
2590
|
-
var
|
|
2613
|
+
var import_node_path10 = __toESM(require("path"), 1);
|
|
2614
|
+
var import_node_fs10 = require("fs");
|
|
2591
2615
|
var import_tree_sitter = __toESM(require("tree-sitter"), 1);
|
|
2592
2616
|
var import_tree_sitter_javascript = __toESM(require("tree-sitter-javascript"), 1);
|
|
2593
2617
|
var import_tree_sitter_python = __toESM(require("tree-sitter-python"), 1);
|
|
@@ -2732,15 +2756,15 @@ function collectPyImports(node, out) {
|
|
|
2732
2756
|
}
|
|
2733
2757
|
async function fileExists2(p) {
|
|
2734
2758
|
try {
|
|
2735
|
-
await
|
|
2759
|
+
await import_node_fs10.promises.access(p);
|
|
2736
2760
|
return true;
|
|
2737
2761
|
} catch {
|
|
2738
2762
|
return false;
|
|
2739
2763
|
}
|
|
2740
2764
|
}
|
|
2741
2765
|
function isWithinServiceDir(candidate, serviceDir) {
|
|
2742
|
-
const rel =
|
|
2743
|
-
return rel !== "" && !rel.startsWith("..") && !
|
|
2766
|
+
const rel = import_node_path10.default.relative(serviceDir, candidate);
|
|
2767
|
+
return rel !== "" && !rel.startsWith("..") && !import_node_path10.default.isAbsolute(rel);
|
|
2744
2768
|
}
|
|
2745
2769
|
var JS_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
2746
2770
|
var JS_INDEX_FILES = JS_EXTENSIONS.map((ext) => `index${ext}`);
|
|
@@ -2748,22 +2772,22 @@ async function firstExistingCandidate(base, serviceDir) {
|
|
|
2748
2772
|
for (const ext of JS_EXTENSIONS) {
|
|
2749
2773
|
const candidate = base + ext;
|
|
2750
2774
|
if (isWithinServiceDir(candidate, serviceDir) && await fileExists2(candidate)) {
|
|
2751
|
-
return toPosix(
|
|
2775
|
+
return toPosix(import_node_path10.default.relative(serviceDir, candidate));
|
|
2752
2776
|
}
|
|
2753
2777
|
}
|
|
2754
2778
|
for (const indexFile of JS_INDEX_FILES) {
|
|
2755
|
-
const candidate =
|
|
2779
|
+
const candidate = import_node_path10.default.join(base, indexFile);
|
|
2756
2780
|
if (isWithinServiceDir(candidate, serviceDir) && await fileExists2(candidate)) {
|
|
2757
|
-
return toPosix(
|
|
2781
|
+
return toPosix(import_node_path10.default.relative(serviceDir, candidate));
|
|
2758
2782
|
}
|
|
2759
2783
|
}
|
|
2760
2784
|
return null;
|
|
2761
2785
|
}
|
|
2762
2786
|
async function loadTsPathConfig(serviceDir) {
|
|
2763
|
-
const tsconfigPath =
|
|
2787
|
+
const tsconfigPath = import_node_path10.default.join(serviceDir, "tsconfig.json");
|
|
2764
2788
|
let raw;
|
|
2765
2789
|
try {
|
|
2766
|
-
raw = await
|
|
2790
|
+
raw = await import_node_fs10.promises.readFile(tsconfigPath, "utf8");
|
|
2767
2791
|
} catch {
|
|
2768
2792
|
return null;
|
|
2769
2793
|
}
|
|
@@ -2772,7 +2796,7 @@ async function loadTsPathConfig(serviceDir) {
|
|
|
2772
2796
|
const paths = parsed.compilerOptions?.paths;
|
|
2773
2797
|
if (!paths || Object.keys(paths).length === 0) return null;
|
|
2774
2798
|
const baseUrl = parsed.compilerOptions?.baseUrl;
|
|
2775
|
-
return { paths, baseDir: baseUrl ?
|
|
2799
|
+
return { paths, baseDir: baseUrl ? import_node_path10.default.resolve(serviceDir, baseUrl) : serviceDir };
|
|
2776
2800
|
} catch (err) {
|
|
2777
2801
|
recordExtractionError("import alias resolution", tsconfigPath, err);
|
|
2778
2802
|
return null;
|
|
@@ -2790,11 +2814,11 @@ async function resolveTsAlias(specifier, config, serviceDir) {
|
|
|
2790
2814
|
if (suffix === null) continue;
|
|
2791
2815
|
for (const target of targets) {
|
|
2792
2816
|
const targetBase = target.endsWith("/*") ? target.slice(0, -2) : target.replace(/\*$/, "");
|
|
2793
|
-
const resolvedBase =
|
|
2817
|
+
const resolvedBase = import_node_path10.default.resolve(config.baseDir, targetBase, suffix);
|
|
2794
2818
|
const hit = await firstExistingCandidate(resolvedBase, serviceDir);
|
|
2795
2819
|
if (hit) return hit;
|
|
2796
2820
|
if (isWithinServiceDir(resolvedBase, serviceDir) && await fileExists2(resolvedBase)) {
|
|
2797
|
-
return toPosix(
|
|
2821
|
+
return toPosix(import_node_path10.default.relative(serviceDir, resolvedBase));
|
|
2798
2822
|
}
|
|
2799
2823
|
}
|
|
2800
2824
|
}
|
|
@@ -2803,18 +2827,18 @@ async function resolveTsAlias(specifier, config, serviceDir) {
|
|
|
2803
2827
|
async function resolveJsImport(specifier, importerDir, serviceDir, tsPaths) {
|
|
2804
2828
|
if (!specifier) return null;
|
|
2805
2829
|
if (specifier.startsWith("./") || specifier.startsWith("../")) {
|
|
2806
|
-
const base =
|
|
2807
|
-
const ext =
|
|
2830
|
+
const base = import_node_path10.default.resolve(importerDir, specifier);
|
|
2831
|
+
const ext = import_node_path10.default.extname(specifier);
|
|
2808
2832
|
if (ext) {
|
|
2809
2833
|
if (ext === ".js" || ext === ".jsx") {
|
|
2810
2834
|
const tsExt = ext === ".jsx" ? ".tsx" : ".ts";
|
|
2811
2835
|
const tsSibling = base.slice(0, -ext.length) + tsExt;
|
|
2812
2836
|
if (isWithinServiceDir(tsSibling, serviceDir) && await fileExists2(tsSibling)) {
|
|
2813
|
-
return toPosix(
|
|
2837
|
+
return toPosix(import_node_path10.default.relative(serviceDir, tsSibling));
|
|
2814
2838
|
}
|
|
2815
2839
|
}
|
|
2816
2840
|
if (isWithinServiceDir(base, serviceDir) && await fileExists2(base)) {
|
|
2817
|
-
return toPosix(
|
|
2841
|
+
return toPosix(import_node_path10.default.relative(serviceDir, base));
|
|
2818
2842
|
}
|
|
2819
2843
|
if (!JS_EXTENSIONS.includes(ext)) {
|
|
2820
2844
|
return firstExistingCandidate(base, serviceDir);
|
|
@@ -2829,30 +2853,30 @@ async function resolveJsImport(specifier, importerDir, serviceDir, tsPaths) {
|
|
|
2829
2853
|
async function resolvePyImport(imp, importerPath, serviceDir) {
|
|
2830
2854
|
let baseDir;
|
|
2831
2855
|
if (imp.level > 0) {
|
|
2832
|
-
baseDir =
|
|
2833
|
-
for (let i = 1; i < imp.level; i++) baseDir =
|
|
2856
|
+
baseDir = import_node_path10.default.dirname(importerPath);
|
|
2857
|
+
for (let i = 1; i < imp.level; i++) baseDir = import_node_path10.default.dirname(baseDir);
|
|
2834
2858
|
} else {
|
|
2835
2859
|
baseDir = serviceDir;
|
|
2836
2860
|
}
|
|
2837
|
-
const moduleBase = imp.modulePath ?
|
|
2861
|
+
const moduleBase = imp.modulePath ? import_node_path10.default.join(baseDir, imp.modulePath.split(".").join("/")) : baseDir;
|
|
2838
2862
|
const resolved = /* @__PURE__ */ new Set();
|
|
2839
2863
|
let needModuleFile = imp.names.length === 0;
|
|
2840
2864
|
for (const name of imp.names) {
|
|
2841
|
-
const submoduleFile =
|
|
2842
|
-
const subpackageInit =
|
|
2865
|
+
const submoduleFile = import_node_path10.default.join(moduleBase, `${name}.py`);
|
|
2866
|
+
const subpackageInit = import_node_path10.default.join(moduleBase, name, "__init__.py");
|
|
2843
2867
|
if (isWithinServiceDir(submoduleFile, serviceDir) && await fileExists2(submoduleFile)) {
|
|
2844
|
-
resolved.add(toPosix(
|
|
2868
|
+
resolved.add(toPosix(import_node_path10.default.relative(serviceDir, submoduleFile)));
|
|
2845
2869
|
} else if (isWithinServiceDir(subpackageInit, serviceDir) && await fileExists2(subpackageInit)) {
|
|
2846
|
-
resolved.add(toPosix(
|
|
2870
|
+
resolved.add(toPosix(import_node_path10.default.relative(serviceDir, subpackageInit)));
|
|
2847
2871
|
} else {
|
|
2848
2872
|
needModuleFile = true;
|
|
2849
2873
|
}
|
|
2850
2874
|
}
|
|
2851
2875
|
if (needModuleFile) {
|
|
2852
|
-
const moduleFileCandidates = imp.modulePath ? [`${moduleBase}.py`,
|
|
2876
|
+
const moduleFileCandidates = imp.modulePath ? [`${moduleBase}.py`, import_node_path10.default.join(moduleBase, "__init__.py")] : [import_node_path10.default.join(moduleBase, "__init__.py")];
|
|
2853
2877
|
for (const candidate of moduleFileCandidates) {
|
|
2854
2878
|
if (isWithinServiceDir(candidate, serviceDir) && await fileExists2(candidate)) {
|
|
2855
|
-
resolved.add(toPosix(
|
|
2879
|
+
resolved.add(toPosix(import_node_path10.default.relative(serviceDir, candidate)));
|
|
2856
2880
|
break;
|
|
2857
2881
|
}
|
|
2858
2882
|
}
|
|
@@ -2862,11 +2886,11 @@ async function resolvePyImport(imp, importerPath, serviceDir) {
|
|
|
2862
2886
|
async function resolveGoImport(specifier, modulePath, serviceDir) {
|
|
2863
2887
|
if (specifier !== modulePath && !specifier.startsWith(`${modulePath}/`)) return null;
|
|
2864
2888
|
const suffix = specifier === modulePath ? "" : specifier.slice(modulePath.length + 1);
|
|
2865
|
-
const dir =
|
|
2866
|
-
const entries = await
|
|
2867
|
-
const candidates = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".go") && !entry.name.endsWith("_test.go")).map((entry) =>
|
|
2889
|
+
const dir = import_node_path10.default.join(serviceDir, suffix);
|
|
2890
|
+
const entries = await import_node_fs10.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
2891
|
+
const candidates = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".go") && !entry.name.endsWith("_test.go")).map((entry) => import_node_path10.default.join(dir, entry.name));
|
|
2868
2892
|
if (candidates.length !== 1) return null;
|
|
2869
|
-
return toPosix(
|
|
2893
|
+
return toPosix(import_node_path10.default.relative(serviceDir, candidates[0]));
|
|
2870
2894
|
}
|
|
2871
2895
|
function emitImportEdge(graph, serviceName, importerFileId, importerRelPath, importeeRelPath, line, snippet2) {
|
|
2872
2896
|
const importeeFileId = (0, import_types5.fileId)(serviceName, importeeRelPath);
|
|
@@ -2896,12 +2920,12 @@ async function addImports(graph, services) {
|
|
|
2896
2920
|
const files = await loadSourceFiles(service.dir, service.excludeDirs);
|
|
2897
2921
|
for (const file of files) {
|
|
2898
2922
|
if (isTestPath(file.path)) continue;
|
|
2899
|
-
const relFile = toPosix(
|
|
2923
|
+
const relFile = toPosix(import_node_path10.default.relative(service.dir, file.path));
|
|
2900
2924
|
const importerFileId = (0, import_types5.fileId)(service.pkg.name, relFile);
|
|
2901
|
-
const isPython =
|
|
2902
|
-
const isGo =
|
|
2903
|
-
if (
|
|
2904
|
-
if (
|
|
2925
|
+
const isPython = import_node_path10.default.extname(file.path) === ".py";
|
|
2926
|
+
const isGo = import_node_path10.default.extname(file.path) === ".go";
|
|
2927
|
+
if (import_node_path10.default.extname(file.path) === ".rb") continue;
|
|
2928
|
+
if (import_node_path10.default.extname(file.path) === ".php") continue;
|
|
2905
2929
|
if (isGo) {
|
|
2906
2930
|
let goImports = [];
|
|
2907
2931
|
try {
|
|
@@ -2911,7 +2935,7 @@ async function addImports(graph, services) {
|
|
|
2911
2935
|
recordExtractionError("import extraction", file.path, err);
|
|
2912
2936
|
continue;
|
|
2913
2937
|
}
|
|
2914
|
-
const goMod = await
|
|
2938
|
+
const goMod = await import_node_fs10.promises.readFile(import_node_path10.default.join(service.dir, "go.mod"), "utf8").catch(() => "");
|
|
2915
2939
|
const modulePath = goMod.match(/^\s*module\s+(\S+)\s*$/m)?.[1];
|
|
2916
2940
|
if (!modulePath) continue;
|
|
2917
2941
|
for (const imp of goImports) {
|
|
@@ -2955,7 +2979,7 @@ async function addImports(graph, services) {
|
|
|
2955
2979
|
continue;
|
|
2956
2980
|
}
|
|
2957
2981
|
for (const imp of jsImports) {
|
|
2958
|
-
const resolved = await resolveJsImport(imp.specifier,
|
|
2982
|
+
const resolved = await resolveJsImport(imp.specifier, import_node_path10.default.dirname(file.path), service.dir, tsPaths);
|
|
2959
2983
|
if (!resolved) continue;
|
|
2960
2984
|
edgesAdded += emitImportEdge(
|
|
2961
2985
|
graph,
|
|
@@ -3080,8 +3104,8 @@ function chiRoutesFromSource(source, parser) {
|
|
|
3080
3104
|
chiWalk(tree.rootNode, "", out);
|
|
3081
3105
|
return out;
|
|
3082
3106
|
}
|
|
3083
|
-
function stripChiRegex(
|
|
3084
|
-
return
|
|
3107
|
+
function stripChiRegex(path81) {
|
|
3108
|
+
return path81.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
|
|
3085
3109
|
}
|
|
3086
3110
|
function chiWalk(node, prefix, out) {
|
|
3087
3111
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
@@ -3421,7 +3445,7 @@ function isNextPagesApiFile(relFile) {
|
|
|
3421
3445
|
if (pagesIdx === -1 || segs[pagesIdx + 1] !== "api") return false;
|
|
3422
3446
|
const base = segs[segs.length - 1] ?? "";
|
|
3423
3447
|
if (/^_(app|document|middleware)\./.test(base)) return false;
|
|
3424
|
-
return JS_ROUTE_EXTENSIONS.has(
|
|
3448
|
+
return JS_ROUTE_EXTENSIONS.has(import_node_path11.default.extname(base));
|
|
3425
3449
|
}
|
|
3426
3450
|
function nextSegment(seg) {
|
|
3427
3451
|
if (seg.startsWith("(") && seg.endsWith(")")) return null;
|
|
@@ -3753,9 +3777,9 @@ function rubyRocketRoute(args) {
|
|
|
3753
3777
|
if (!pair || pair.type !== "pair") continue;
|
|
3754
3778
|
const k = pair.childForFieldName("key");
|
|
3755
3779
|
if (k?.type !== "string") continue;
|
|
3756
|
-
const
|
|
3757
|
-
if (
|
|
3758
|
-
return { path:
|
|
3780
|
+
const path81 = rubyLiteral(k);
|
|
3781
|
+
if (path81 === null) continue;
|
|
3782
|
+
return { path: path81, target: rubyLiteral(pair.childForFieldName("value")) };
|
|
3759
3783
|
}
|
|
3760
3784
|
return null;
|
|
3761
3785
|
}
|
|
@@ -3845,23 +3869,23 @@ function railsInferHandler(rawPath) {
|
|
|
3845
3869
|
if (segs.length === 1) return `${segs[0]}#index`;
|
|
3846
3870
|
return `${segs.slice(0, -1).join("/")}#${segs[segs.length - 1]}`;
|
|
3847
3871
|
}
|
|
3848
|
-
function railsResource(call, ctx, out,
|
|
3872
|
+
function railsResource(call, ctx, out, plural2) {
|
|
3849
3873
|
if (ctx.depth >= 2) return;
|
|
3850
3874
|
const args = rubyArgs(call);
|
|
3851
3875
|
const name = rubyLiteral(rubyPositional(args)[0]);
|
|
3852
3876
|
if (!name) return;
|
|
3853
3877
|
const base = `${ctx.path}/${name}`;
|
|
3854
|
-
const controller = ctx.module + (
|
|
3878
|
+
const controller = ctx.module + (plural2 ? name : railsPluralize(name));
|
|
3855
3879
|
const allow = railsActionFilter(args);
|
|
3856
3880
|
const line = call.startPosition.row + 1;
|
|
3857
|
-
for (const row of
|
|
3881
|
+
for (const row of plural2 ? RAILS_PLURAL_ROWS : RAILS_SINGULAR_ROWS) {
|
|
3858
3882
|
if (!allow(row.action)) continue;
|
|
3859
3883
|
for (const m of row.methods) railsEmit(out, m, base + row.suffix, `${controller}#${row.action}`, line);
|
|
3860
3884
|
}
|
|
3861
3885
|
const block = call.childForFieldName("block");
|
|
3862
3886
|
if (!block) return;
|
|
3863
|
-
const memberBase =
|
|
3864
|
-
const nestedPath =
|
|
3887
|
+
const memberBase = plural2 ? `${base}/:id` : base;
|
|
3888
|
+
const nestedPath = plural2 ? `${base}/:${railsSingularize(name)}_id` : base;
|
|
3865
3889
|
for (const child of rubyBlockCalls(block)) {
|
|
3866
3890
|
const cm = child.childForFieldName("method")?.text;
|
|
3867
3891
|
if (cm === "member") railsBlockVerbs(child, memberBase, controller, out);
|
|
@@ -4603,12 +4627,12 @@ async function expressMountPrefixes(files, serviceDir, tsPaths) {
|
|
|
4603
4627
|
const jsParser = makeJsParser2();
|
|
4604
4628
|
const fileInfo = /* @__PURE__ */ new Map();
|
|
4605
4629
|
for (const f of files) {
|
|
4606
|
-
if (!JS_ROUTE_EXTENSIONS.has(
|
|
4630
|
+
if (!JS_ROUTE_EXTENSIONS.has(import_node_path11.default.extname(f.path))) continue;
|
|
4607
4631
|
if (isTestPath(f.path)) continue;
|
|
4608
|
-
const rel = toPosix(
|
|
4632
|
+
const rel = toPosix(import_node_path11.default.relative(serviceDir, f.path));
|
|
4609
4633
|
try {
|
|
4610
4634
|
const tree = parseSource2(jsParser, f.content);
|
|
4611
|
-
fileInfo.set(rel, analyzeExpressFile(tree.rootNode,
|
|
4635
|
+
fileInfo.set(rel, analyzeExpressFile(tree.rootNode, import_node_path11.default.dirname(f.path)));
|
|
4612
4636
|
} catch {
|
|
4613
4637
|
}
|
|
4614
4638
|
}
|
|
@@ -4707,14 +4731,14 @@ async function addRoutes(graph, services) {
|
|
|
4707
4731
|
const mountPrefixes = hasExpress ? await expressMountPrefixes(files, service.dir, await loadTsPathConfig(service.dir)) : /* @__PURE__ */ new Map();
|
|
4708
4732
|
for (const file of files) {
|
|
4709
4733
|
if (isTestPath(file.path)) continue;
|
|
4710
|
-
const ext =
|
|
4734
|
+
const ext = import_node_path11.default.extname(file.path);
|
|
4711
4735
|
const isPy = ext === ".py";
|
|
4712
4736
|
const isGo = ext === ".go";
|
|
4713
4737
|
const isRb = ext === ".rb";
|
|
4714
4738
|
const isPhp = ext === ".php";
|
|
4715
4739
|
const isRs = ext === ".rs";
|
|
4716
4740
|
if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy && !isGo && !isRb && !isPhp && !isRs) continue;
|
|
4717
|
-
const relFile = toPosix(
|
|
4741
|
+
const relFile = toPosix(import_node_path11.default.relative(service.dir, file.path));
|
|
4718
4742
|
let routes;
|
|
4719
4743
|
try {
|
|
4720
4744
|
if (isPhp) {
|
|
@@ -5128,20 +5152,20 @@ var CODE_LINENO_ATTR = "code.lineno";
|
|
|
5128
5152
|
var CODE_FUNCTION_NAME_ATTR = "code.function.name";
|
|
5129
5153
|
var CODE_FUNCTION_ATTR = "code.function";
|
|
5130
5154
|
function codeFilepathOf(attrs) {
|
|
5131
|
-
const
|
|
5132
|
-
if (typeof
|
|
5155
|
+
const stable2 = attrs[CODE_FILE_PATH_ATTR];
|
|
5156
|
+
if (typeof stable2 === "string" && stable2.length > 0) return stable2;
|
|
5133
5157
|
const prior = attrs[CODE_FILEPATH_ATTR];
|
|
5134
5158
|
return typeof prior === "string" && prior.length > 0 ? prior : void 0;
|
|
5135
5159
|
}
|
|
5136
5160
|
function codeLinenoOf(attrs) {
|
|
5137
|
-
const
|
|
5138
|
-
if (typeof
|
|
5161
|
+
const stable2 = attrs[CODE_LINE_NUMBER_ATTR];
|
|
5162
|
+
if (typeof stable2 === "number" && Number.isFinite(stable2)) return stable2;
|
|
5139
5163
|
const prior = attrs[CODE_LINENO_ATTR];
|
|
5140
5164
|
return typeof prior === "number" && Number.isFinite(prior) ? prior : void 0;
|
|
5141
5165
|
}
|
|
5142
5166
|
function codeFunctionOf(attrs) {
|
|
5143
|
-
const
|
|
5144
|
-
if (typeof
|
|
5167
|
+
const stable2 = attrs[CODE_FUNCTION_NAME_ATTR];
|
|
5168
|
+
if (typeof stable2 === "string" && stable2.length > 0) return stable2;
|
|
5145
5169
|
const prior = attrs[CODE_FUNCTION_ATTR];
|
|
5146
5170
|
return typeof prior === "string" && prior.length > 0 ? prior : void 0;
|
|
5147
5171
|
}
|
|
@@ -5171,7 +5195,7 @@ function languageForExt(relPath) {
|
|
|
5171
5195
|
function relPathForRuntimeFile(filepath, serviceNode, scanPath) {
|
|
5172
5196
|
let p = toPosix2(filepath).replace(/^file:\/\//, "");
|
|
5173
5197
|
if (scanPath && scanPath.length > 0) {
|
|
5174
|
-
const absRoot = toPosix2(
|
|
5198
|
+
const absRoot = toPosix2(import_node_path12.default.resolve(scanPath, serviceNode?.repoPath ?? ""));
|
|
5175
5199
|
const anchor = absRoot.endsWith("/") ? absRoot : `${absRoot}/`;
|
|
5176
5200
|
if (p.startsWith(anchor)) return p.slice(anchor.length);
|
|
5177
5201
|
}
|
|
@@ -5199,10 +5223,10 @@ function resolveDistToSrc(absFilepath, line) {
|
|
|
5199
5223
|
entry = null;
|
|
5200
5224
|
const mapPath = `${absFilepath}.map`;
|
|
5201
5225
|
try {
|
|
5202
|
-
if ((0,
|
|
5203
|
-
const raw = JSON.parse((0,
|
|
5226
|
+
if ((0, import_node_fs11.existsSync)(mapPath)) {
|
|
5227
|
+
const raw = JSON.parse((0, import_node_fs11.readFileSync)(mapPath, "utf8"));
|
|
5204
5228
|
const consumer = new sourceMapJs.SourceMapConsumer(raw);
|
|
5205
|
-
entry = { consumer, dir:
|
|
5229
|
+
entry = { consumer, dir: import_node_path12.default.dirname(mapPath) };
|
|
5206
5230
|
}
|
|
5207
5231
|
} catch {
|
|
5208
5232
|
entry = null;
|
|
@@ -5222,7 +5246,7 @@ function resolveDistToSrc(absFilepath, line) {
|
|
|
5222
5246
|
}
|
|
5223
5247
|
if (!pos || !pos.source) return null;
|
|
5224
5248
|
const root = entry.consumer.sourceRoot ?? "";
|
|
5225
|
-
const resolved =
|
|
5249
|
+
const resolved = import_node_path12.default.resolve(entry.dir, root, pos.source);
|
|
5226
5250
|
return { filepath: resolved, ...pos.line ? { line: pos.line } : {} };
|
|
5227
5251
|
} catch {
|
|
5228
5252
|
return null;
|
|
@@ -5766,8 +5790,7 @@ function emitIncidentEvent(project, ev) {
|
|
|
5766
5790
|
});
|
|
5767
5791
|
}
|
|
5768
5792
|
async function appendErrorEvent(ctx, ev) {
|
|
5769
|
-
await
|
|
5770
|
-
await import_node_fs10.promises.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
5793
|
+
await appendRuntimeIncident(ctx.errorsPath, ctx.project ?? DEFAULT_PROJECT, ev);
|
|
5771
5794
|
emitIncidentEvent(ctx.project ?? DEFAULT_PROJECT, ev);
|
|
5772
5795
|
}
|
|
5773
5796
|
async function appendConnectorIncident(errorsPath, input, project) {
|
|
@@ -5782,8 +5805,7 @@ async function appendConnectorIncident(errorsPath, input, project) {
|
|
|
5782
5805
|
...input.attributes && Object.keys(input.attributes).length > 0 ? { attributes: input.attributes } : {},
|
|
5783
5806
|
affectedNode: input.affectedNode
|
|
5784
5807
|
};
|
|
5785
|
-
await
|
|
5786
|
-
await import_node_fs10.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
5808
|
+
await appendRuntimeIncident(errorsPath, project ?? DEFAULT_PROJECT, ev);
|
|
5787
5809
|
if (project) emitIncidentEvent(project, ev);
|
|
5788
5810
|
}
|
|
5789
5811
|
function landIncidentCallSite(span, callSite, trusted, graph) {
|
|
@@ -6374,13 +6396,13 @@ async function markStaleEdges(graph, options = {}) {
|
|
|
6374
6396
|
return { count: events.length, events };
|
|
6375
6397
|
}
|
|
6376
6398
|
async function appendStaleEvents(staleEventsPath, events) {
|
|
6377
|
-
await
|
|
6399
|
+
await import_node_fs11.promises.mkdir(import_node_path12.default.dirname(staleEventsPath), { recursive: true });
|
|
6378
6400
|
const lines = events.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
6379
|
-
await
|
|
6401
|
+
await import_node_fs11.promises.appendFile(staleEventsPath, lines, "utf8");
|
|
6380
6402
|
}
|
|
6381
6403
|
async function readStaleEvents(staleEventsPath) {
|
|
6382
6404
|
try {
|
|
6383
|
-
const raw = await
|
|
6405
|
+
const raw = await import_node_fs11.promises.readFile(staleEventsPath, "utf8");
|
|
6384
6406
|
return raw.split("\n").filter((line) => line.length > 0).map((line) => JSON.parse(line));
|
|
6385
6407
|
} catch (err) {
|
|
6386
6408
|
if (err.code === "ENOENT") return [];
|
|
@@ -6416,7 +6438,7 @@ function startStalenessLoop(graph, options = {}) {
|
|
|
6416
6438
|
var INCIDENT_READ_MAX_BYTES = 32 * 1024 * 1024;
|
|
6417
6439
|
var INCIDENT_READ_MAX_EVENTS = 5e3;
|
|
6418
6440
|
async function readErrorFileTail(errorsPath, maxBytes) {
|
|
6419
|
-
const handle = await
|
|
6441
|
+
const handle = await import_node_fs11.promises.open(errorsPath, "r");
|
|
6420
6442
|
try {
|
|
6421
6443
|
const { size } = await handle.stat();
|
|
6422
6444
|
if (size <= maxBytes) {
|
|
@@ -6452,6 +6474,23 @@ async function readErrorEvents(errorsPath, opts) {
|
|
|
6452
6474
|
const cap = opts?.limit !== void 0 && opts.limit > 0 ? Math.min(opts.limit, INCIDENT_READ_MAX_EVENTS) : INCIDENT_READ_MAX_EVENTS;
|
|
6453
6475
|
return deduped.length > cap ? deduped.slice(deduped.length - cap) : deduped;
|
|
6454
6476
|
}
|
|
6477
|
+
async function readErrorEventById(errorsPath, id) {
|
|
6478
|
+
let raw;
|
|
6479
|
+
try {
|
|
6480
|
+
raw = await readErrorFileTail(errorsPath, INCIDENT_READ_MAX_BYTES);
|
|
6481
|
+
} catch (err) {
|
|
6482
|
+
if (err.code === "ENOENT") return void 0;
|
|
6483
|
+
throw err;
|
|
6484
|
+
}
|
|
6485
|
+
const lines = raw.split("\n");
|
|
6486
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
6487
|
+
const line = lines[i];
|
|
6488
|
+
if (!line) continue;
|
|
6489
|
+
const event = JSON.parse(line);
|
|
6490
|
+
if (event.id === id) return redactPersistedAttributes(event);
|
|
6491
|
+
}
|
|
6492
|
+
return void 0;
|
|
6493
|
+
}
|
|
6455
6494
|
function isSynthesizedHttpIncident(ev) {
|
|
6456
6495
|
if (ev.exceptionType || ev.exceptionStacktrace) return false;
|
|
6457
6496
|
if (ev.errorType) return false;
|
|
@@ -6659,19 +6698,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
6659
6698
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
6660
6699
|
let best = { path: [start], edges: [] };
|
|
6661
6700
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
6662
|
-
function step(node,
|
|
6663
|
-
if (
|
|
6664
|
-
best = { path: [...
|
|
6701
|
+
function step(node, path81, edges) {
|
|
6702
|
+
if (path81.length > best.path.length) {
|
|
6703
|
+
best = { path: [...path81], edges: [...edges] };
|
|
6665
6704
|
}
|
|
6666
|
-
if (
|
|
6705
|
+
if (path81.length - 1 >= maxDepth) return;
|
|
6667
6706
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
6668
6707
|
for (const [srcId, edge] of incoming) {
|
|
6669
6708
|
if (visited.has(srcId)) continue;
|
|
6670
6709
|
visited.add(srcId);
|
|
6671
|
-
|
|
6710
|
+
path81.push(srcId);
|
|
6672
6711
|
edges.push(edge);
|
|
6673
|
-
step(srcId,
|
|
6674
|
-
|
|
6712
|
+
step(srcId, path81, edges);
|
|
6713
|
+
path81.pop();
|
|
6675
6714
|
edges.pop();
|
|
6676
6715
|
visited.delete(srcId);
|
|
6677
6716
|
}
|
|
@@ -6886,20 +6925,20 @@ function dominantFailingCall(graph, serviceId17, visited) {
|
|
|
6886
6925
|
return best;
|
|
6887
6926
|
}
|
|
6888
6927
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
6889
|
-
const
|
|
6928
|
+
const path81 = [originServiceId];
|
|
6890
6929
|
const edges = [];
|
|
6891
6930
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
6892
6931
|
let current = originServiceId;
|
|
6893
6932
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
6894
6933
|
const hop = dominantFailingCall(graph, current, visited);
|
|
6895
6934
|
if (!hop) break;
|
|
6896
|
-
|
|
6935
|
+
path81.push(hop.nextService);
|
|
6897
6936
|
edges.push(hop.edge);
|
|
6898
6937
|
visited.add(hop.nextService);
|
|
6899
6938
|
current = hop.nextService;
|
|
6900
6939
|
}
|
|
6901
6940
|
if (edges.length === 0) return null;
|
|
6902
|
-
return { path:
|
|
6941
|
+
return { path: path81, edges, culprit: current };
|
|
6903
6942
|
}
|
|
6904
6943
|
function isStaleCallEdge(e) {
|
|
6905
6944
|
return e.type === import_types8.EdgeType.CALLS && e.provenance === import_types8.Provenance.STALE;
|
|
@@ -6935,26 +6974,26 @@ function dominantStaleCall(graph, serviceId17, visited) {
|
|
|
6935
6974
|
return best;
|
|
6936
6975
|
}
|
|
6937
6976
|
function followStaleCallChain(graph, originServiceId, maxDepth) {
|
|
6938
|
-
const
|
|
6977
|
+
const path81 = [originServiceId];
|
|
6939
6978
|
const edges = [];
|
|
6940
6979
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
6941
6980
|
let current = originServiceId;
|
|
6942
6981
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
6943
6982
|
const hop = dominantStaleCall(graph, current, visited);
|
|
6944
6983
|
if (!hop) break;
|
|
6945
|
-
|
|
6984
|
+
path81.push(hop.nextService);
|
|
6946
6985
|
edges.push(hop.edge);
|
|
6947
6986
|
visited.add(hop.nextService);
|
|
6948
6987
|
current = hop.nextService;
|
|
6949
6988
|
}
|
|
6950
6989
|
if (edges.length === 0) return null;
|
|
6951
|
-
return { path:
|
|
6990
|
+
return { path: path81, edges, culprit: current };
|
|
6952
6991
|
}
|
|
6953
6992
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
6954
6993
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
6955
6994
|
if (!chain) return null;
|
|
6956
6995
|
const culprit = chain.culprit;
|
|
6957
|
-
const
|
|
6996
|
+
const path81 = [...chain.path];
|
|
6958
6997
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
6959
6998
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
6960
6999
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -6962,14 +7001,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
6962
7001
|
if (loc) {
|
|
6963
7002
|
let rootCauseNode = culprit;
|
|
6964
7003
|
if (loc.fileNode) {
|
|
6965
|
-
|
|
7004
|
+
path81.push(loc.fileNode);
|
|
6966
7005
|
edgeProvenances.push(import_types8.Provenance.OBSERVED);
|
|
6967
7006
|
rootCauseNode = loc.fileNode;
|
|
6968
7007
|
}
|
|
6969
7008
|
return import_types8.RootCauseResultSchema.parse({
|
|
6970
7009
|
rootCauseNode,
|
|
6971
7010
|
rootCauseReason: loc.rootCauseReason,
|
|
6972
|
-
traversalPath:
|
|
7011
|
+
traversalPath: path81,
|
|
6973
7012
|
edgeProvenances,
|
|
6974
7013
|
confidence,
|
|
6975
7014
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -6981,7 +7020,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
6981
7020
|
return import_types8.RootCauseResultSchema.parse({
|
|
6982
7021
|
rootCauseNode: culprit,
|
|
6983
7022
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
6984
|
-
traversalPath:
|
|
7023
|
+
traversalPath: path81,
|
|
6985
7024
|
edgeProvenances,
|
|
6986
7025
|
confidence,
|
|
6987
7026
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -7634,10 +7673,10 @@ function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
|
|
|
7634
7673
|
traversalPath = staleChain.path;
|
|
7635
7674
|
edgeProvenances = staleChain.edges.map((e) => e.provenance);
|
|
7636
7675
|
} else if (top.node !== seedNode) {
|
|
7637
|
-
const
|
|
7638
|
-
if (
|
|
7639
|
-
traversalPath =
|
|
7640
|
-
edgeProvenances =
|
|
7676
|
+
const path81 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
|
|
7677
|
+
if (path81) {
|
|
7678
|
+
traversalPath = path81.nodes;
|
|
7679
|
+
edgeProvenances = path81.edges.map((e) => e.provenance);
|
|
7641
7680
|
} else {
|
|
7642
7681
|
traversalPath = [errorNodeId, top.node];
|
|
7643
7682
|
edgeProvenances = [top.provenance ?? import_types8.Provenance.OBSERVED];
|
|
@@ -8399,16 +8438,16 @@ init_cjs_shims();
|
|
|
8399
8438
|
|
|
8400
8439
|
// src/extract/services.ts
|
|
8401
8440
|
init_cjs_shims();
|
|
8402
|
-
var
|
|
8403
|
-
var
|
|
8441
|
+
var import_node_fs22 = require("fs");
|
|
8442
|
+
var import_node_path24 = __toESM(require("path"), 1);
|
|
8404
8443
|
var import_ignore2 = __toESM(require("ignore"), 1);
|
|
8405
8444
|
var import_minimatch2 = require("minimatch");
|
|
8406
8445
|
var import_types19 = require("@neat.is/types");
|
|
8407
8446
|
|
|
8408
8447
|
// src/extract/python.ts
|
|
8409
8448
|
init_cjs_shims();
|
|
8410
|
-
var
|
|
8411
|
-
var
|
|
8449
|
+
var import_node_fs12 = require("fs");
|
|
8450
|
+
var import_node_path13 = __toESM(require("path"), 1);
|
|
8412
8451
|
var import_smol_toml = require("smol-toml");
|
|
8413
8452
|
var REQUIREMENT_LINE = /^\s*([A-Za-z0-9_.-]+)(?:\[[^\]]*\])?\s*(?:(==)\s*([A-Za-z0-9_.+-]+))?/;
|
|
8414
8453
|
function parseRequirementsTxt(content) {
|
|
@@ -8441,25 +8480,25 @@ function depsFromPyProject(pyproject) {
|
|
|
8441
8480
|
return out;
|
|
8442
8481
|
}
|
|
8443
8482
|
async function discoverPythonService(serviceDir) {
|
|
8444
|
-
const pyprojectPath =
|
|
8445
|
-
const requirementsPath =
|
|
8446
|
-
const setupPath =
|
|
8483
|
+
const pyprojectPath = import_node_path13.default.join(serviceDir, "pyproject.toml");
|
|
8484
|
+
const requirementsPath = import_node_path13.default.join(serviceDir, "requirements.txt");
|
|
8485
|
+
const setupPath = import_node_path13.default.join(serviceDir, "setup.py");
|
|
8447
8486
|
const hasPyproject = await exists2(pyprojectPath);
|
|
8448
8487
|
const hasRequirements = await exists2(requirementsPath);
|
|
8449
8488
|
const hasSetup = await exists2(setupPath);
|
|
8450
8489
|
if (!hasPyproject && !hasRequirements && !hasSetup) return null;
|
|
8451
|
-
let name =
|
|
8490
|
+
let name = import_node_path13.default.basename(serviceDir);
|
|
8452
8491
|
let version;
|
|
8453
8492
|
const dependencies = {};
|
|
8454
8493
|
if (hasPyproject) {
|
|
8455
|
-
const raw = await
|
|
8494
|
+
const raw = await import_node_fs12.promises.readFile(pyprojectPath, "utf8");
|
|
8456
8495
|
const pyproject = (0, import_smol_toml.parse)(raw);
|
|
8457
8496
|
name = pyproject.project?.name ?? pyproject.tool?.poetry?.name ?? name;
|
|
8458
8497
|
version = pyproject.project?.version ?? pyproject.tool?.poetry?.version ?? void 0;
|
|
8459
8498
|
Object.assign(dependencies, depsFromPyProject(pyproject));
|
|
8460
8499
|
}
|
|
8461
8500
|
if (hasRequirements) {
|
|
8462
|
-
const raw = await
|
|
8501
|
+
const raw = await import_node_fs12.promises.readFile(requirementsPath, "utf8");
|
|
8463
8502
|
Object.assign(dependencies, parseRequirementsTxt(raw));
|
|
8464
8503
|
}
|
|
8465
8504
|
return { name, version, dependencies };
|
|
@@ -8474,8 +8513,8 @@ function pythonToPackage(service) {
|
|
|
8474
8513
|
|
|
8475
8514
|
// src/extract/go.ts
|
|
8476
8515
|
init_cjs_shims();
|
|
8477
|
-
var
|
|
8478
|
-
var
|
|
8516
|
+
var import_node_fs13 = require("fs");
|
|
8517
|
+
var import_node_path14 = __toESM(require("path"), 1);
|
|
8479
8518
|
var import_types10 = require("@neat.is/types");
|
|
8480
8519
|
function parseGoMod(source) {
|
|
8481
8520
|
const module2 = source.match(/^\s*module\s+(\S+)\s*$/m)?.[1];
|
|
@@ -8502,7 +8541,7 @@ function goFramework(deps) {
|
|
|
8502
8541
|
async function discoverGoService(scanPath, dir) {
|
|
8503
8542
|
let raw;
|
|
8504
8543
|
try {
|
|
8505
|
-
raw = await
|
|
8544
|
+
raw = await import_node_fs13.promises.readFile(import_node_path14.default.join(dir, "go.mod"), "utf8");
|
|
8506
8545
|
} catch {
|
|
8507
8546
|
return null;
|
|
8508
8547
|
}
|
|
@@ -8517,7 +8556,7 @@ async function discoverGoService(scanPath, dir) {
|
|
|
8517
8556
|
name,
|
|
8518
8557
|
language: "go",
|
|
8519
8558
|
dependencies: mod.dependencies,
|
|
8520
|
-
repoPath:
|
|
8559
|
+
repoPath: import_node_path14.default.relative(scanPath, dir),
|
|
8521
8560
|
...framework ? { framework } : {}
|
|
8522
8561
|
};
|
|
8523
8562
|
return { pkg, dir, node };
|
|
@@ -8525,8 +8564,8 @@ async function discoverGoService(scanPath, dir) {
|
|
|
8525
8564
|
|
|
8526
8565
|
// src/extract/ruby.ts
|
|
8527
8566
|
init_cjs_shims();
|
|
8528
|
-
var
|
|
8529
|
-
var
|
|
8567
|
+
var import_node_fs14 = require("fs");
|
|
8568
|
+
var import_node_path15 = __toESM(require("path"), 1);
|
|
8530
8569
|
var import_types11 = require("@neat.is/types");
|
|
8531
8570
|
function parseGemfile(source) {
|
|
8532
8571
|
const dependencies = {};
|
|
@@ -8543,12 +8582,12 @@ function parseGemfile(source) {
|
|
|
8543
8582
|
async function discoverRubyService(scanPath, dir) {
|
|
8544
8583
|
let raw;
|
|
8545
8584
|
try {
|
|
8546
|
-
raw = await
|
|
8585
|
+
raw = await import_node_fs14.promises.readFile(import_node_path15.default.join(dir, "Gemfile"), "utf8");
|
|
8547
8586
|
} catch {
|
|
8548
8587
|
return null;
|
|
8549
8588
|
}
|
|
8550
8589
|
const gemfile = parseGemfile(raw);
|
|
8551
|
-
const name =
|
|
8590
|
+
const name = import_node_path15.default.basename(dir);
|
|
8552
8591
|
const pkg = { name, dependencies: gemfile.dependencies };
|
|
8553
8592
|
const node = {
|
|
8554
8593
|
id: (0, import_types11.serviceId)(name),
|
|
@@ -8556,7 +8595,7 @@ async function discoverRubyService(scanPath, dir) {
|
|
|
8556
8595
|
name,
|
|
8557
8596
|
language: "ruby",
|
|
8558
8597
|
dependencies: gemfile.dependencies,
|
|
8559
|
-
repoPath:
|
|
8598
|
+
repoPath: import_node_path15.default.relative(scanPath, dir),
|
|
8560
8599
|
...gemfile.dependencies["rails"] !== void 0 ? { framework: "rails" } : {}
|
|
8561
8600
|
};
|
|
8562
8601
|
return { pkg, dir, node };
|
|
@@ -8564,7 +8603,7 @@ async function discoverRubyService(scanPath, dir) {
|
|
|
8564
8603
|
|
|
8565
8604
|
// src/extract/php.ts
|
|
8566
8605
|
init_cjs_shims();
|
|
8567
|
-
var
|
|
8606
|
+
var import_node_path16 = __toESM(require("path"), 1);
|
|
8568
8607
|
var import_types12 = require("@neat.is/types");
|
|
8569
8608
|
function parseComposerJson(composer) {
|
|
8570
8609
|
return {
|
|
@@ -8577,12 +8616,12 @@ function parseComposerJson(composer) {
|
|
|
8577
8616
|
async function discoverPhpService(scanPath, dir) {
|
|
8578
8617
|
let composer;
|
|
8579
8618
|
try {
|
|
8580
|
-
composer = await readJson(
|
|
8619
|
+
composer = await readJson(import_node_path16.default.join(dir, "composer.json"));
|
|
8581
8620
|
} catch {
|
|
8582
8621
|
return null;
|
|
8583
8622
|
}
|
|
8584
8623
|
const { dependencies } = parseComposerJson(composer);
|
|
8585
|
-
const name =
|
|
8624
|
+
const name = import_node_path16.default.basename(dir);
|
|
8586
8625
|
const pkg = { name, dependencies };
|
|
8587
8626
|
const node = {
|
|
8588
8627
|
id: (0, import_types12.serviceId)(name),
|
|
@@ -8590,7 +8629,7 @@ async function discoverPhpService(scanPath, dir) {
|
|
|
8590
8629
|
name,
|
|
8591
8630
|
language: "php",
|
|
8592
8631
|
dependencies,
|
|
8593
|
-
repoPath:
|
|
8632
|
+
repoPath: import_node_path16.default.relative(scanPath, dir),
|
|
8594
8633
|
...dependencies["laravel/framework"] !== void 0 ? { framework: "laravel" } : {}
|
|
8595
8634
|
};
|
|
8596
8635
|
return { pkg, dir, node };
|
|
@@ -8598,23 +8637,23 @@ async function discoverPhpService(scanPath, dir) {
|
|
|
8598
8637
|
|
|
8599
8638
|
// src/extract/csharp.ts
|
|
8600
8639
|
init_cjs_shims();
|
|
8601
|
-
var
|
|
8602
|
-
var
|
|
8640
|
+
var import_node_fs15 = require("fs");
|
|
8641
|
+
var import_node_path17 = __toESM(require("path"), 1);
|
|
8603
8642
|
var import_types13 = require("@neat.is/types");
|
|
8604
8643
|
var CSHARP_PROJECT_EXTS = /* @__PURE__ */ new Set([".csproj", ".sln"]);
|
|
8605
8644
|
async function projectFiles(dir) {
|
|
8606
|
-
const entries = await
|
|
8645
|
+
const entries = await import_node_fs15.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
8607
8646
|
const out = [];
|
|
8608
8647
|
for (const entry of entries) {
|
|
8609
8648
|
if (!entry.isFile()) continue;
|
|
8610
|
-
if (CSHARP_PROJECT_EXTS.has(
|
|
8649
|
+
if (CSHARP_PROJECT_EXTS.has(import_node_path17.default.extname(entry.name).toLowerCase())) out.push(entry.name);
|
|
8611
8650
|
}
|
|
8612
8651
|
return out.sort();
|
|
8613
8652
|
}
|
|
8614
8653
|
var NESTED_PROJECT_DIR = "src";
|
|
8615
8654
|
async function resolveProjectDir(dir) {
|
|
8616
8655
|
if ((await projectFiles(dir)).length > 0) return dir;
|
|
8617
|
-
const nested =
|
|
8656
|
+
const nested = import_node_path17.default.join(dir, NESTED_PROJECT_DIR);
|
|
8618
8657
|
if ((await projectFiles(nested)).length > 0) return nested;
|
|
8619
8658
|
return null;
|
|
8620
8659
|
}
|
|
@@ -8639,10 +8678,10 @@ async function discoverCsharpService(scanPath, dir) {
|
|
|
8639
8678
|
if (projectDir === null) return null;
|
|
8640
8679
|
const projects = await projectFiles(projectDir);
|
|
8641
8680
|
const csproj = projects.find((p) => p.toLowerCase().endsWith(".csproj"));
|
|
8642
|
-
const name = csproj ?
|
|
8681
|
+
const name = csproj ? import_node_path17.default.basename(csproj, import_node_path17.default.extname(csproj)) : import_node_path17.default.basename(dir);
|
|
8643
8682
|
let dependencies = {};
|
|
8644
8683
|
if (csproj) {
|
|
8645
|
-
const raw = await
|
|
8684
|
+
const raw = await import_node_fs15.promises.readFile(import_node_path17.default.join(projectDir, csproj), "utf8").catch(() => "");
|
|
8646
8685
|
dependencies = parseCsproj(raw).dependencies;
|
|
8647
8686
|
}
|
|
8648
8687
|
const pkg = { name, dependencies };
|
|
@@ -8654,20 +8693,20 @@ async function discoverCsharpService(scanPath, dir) {
|
|
|
8654
8693
|
dependencies,
|
|
8655
8694
|
// Anchor on the directory that holds the project and its `.cs` files, so file
|
|
8656
8695
|
// attribution reaches the source whether it sits at the root or under `src/`.
|
|
8657
|
-
repoPath:
|
|
8696
|
+
repoPath: import_node_path17.default.relative(scanPath, projectDir)
|
|
8658
8697
|
};
|
|
8659
8698
|
return { pkg, dir: projectDir, node };
|
|
8660
8699
|
}
|
|
8661
8700
|
|
|
8662
8701
|
// src/extract/java.ts
|
|
8663
8702
|
init_cjs_shims();
|
|
8664
|
-
var
|
|
8665
|
-
var
|
|
8703
|
+
var import_node_fs16 = require("fs");
|
|
8704
|
+
var import_node_path18 = __toESM(require("path"), 1);
|
|
8666
8705
|
var import_types14 = require("@neat.is/types");
|
|
8667
8706
|
var JAVA_MANIFESTS = ["pom.xml", "build.gradle", "build.gradle.kts"];
|
|
8668
8707
|
async function hasJavaManifest(dir) {
|
|
8669
8708
|
for (const manifest of JAVA_MANIFESTS) {
|
|
8670
|
-
if (await exists2(
|
|
8709
|
+
if (await exists2(import_node_path18.default.join(dir, manifest))) return true;
|
|
8671
8710
|
}
|
|
8672
8711
|
return false;
|
|
8673
8712
|
}
|
|
@@ -8702,19 +8741,19 @@ function parseGradle(source) {
|
|
|
8702
8741
|
return { dependencies };
|
|
8703
8742
|
}
|
|
8704
8743
|
async function discoverJavaService(scanPath, dir) {
|
|
8705
|
-
const pomPath =
|
|
8706
|
-
const gradlePath =
|
|
8707
|
-
const gradleKtsPath =
|
|
8744
|
+
const pomPath = import_node_path18.default.join(dir, "pom.xml");
|
|
8745
|
+
const gradlePath = import_node_path18.default.join(dir, "build.gradle");
|
|
8746
|
+
const gradleKtsPath = import_node_path18.default.join(dir, "build.gradle.kts");
|
|
8708
8747
|
let manifest = null;
|
|
8709
8748
|
if (await exists2(pomPath)) {
|
|
8710
|
-
manifest = parsePomXml(await
|
|
8749
|
+
manifest = parsePomXml(await import_node_fs16.promises.readFile(pomPath, "utf8").catch(() => ""));
|
|
8711
8750
|
} else if (await exists2(gradlePath)) {
|
|
8712
|
-
manifest = parseGradle(await
|
|
8751
|
+
manifest = parseGradle(await import_node_fs16.promises.readFile(gradlePath, "utf8").catch(() => ""));
|
|
8713
8752
|
} else if (await exists2(gradleKtsPath)) {
|
|
8714
|
-
manifest = parseGradle(await
|
|
8753
|
+
manifest = parseGradle(await import_node_fs16.promises.readFile(gradleKtsPath, "utf8").catch(() => ""));
|
|
8715
8754
|
}
|
|
8716
8755
|
if (!manifest) return null;
|
|
8717
|
-
const name = manifest.name?.trim() ||
|
|
8756
|
+
const name = manifest.name?.trim() || import_node_path18.default.basename(dir);
|
|
8718
8757
|
const dependencies = manifest.dependencies;
|
|
8719
8758
|
const pkg = { name, dependencies };
|
|
8720
8759
|
const node = {
|
|
@@ -8723,15 +8762,15 @@ async function discoverJavaService(scanPath, dir) {
|
|
|
8723
8762
|
name,
|
|
8724
8763
|
language: "java",
|
|
8725
8764
|
dependencies,
|
|
8726
|
-
repoPath:
|
|
8765
|
+
repoPath: import_node_path18.default.relative(scanPath, dir)
|
|
8727
8766
|
};
|
|
8728
8767
|
return { pkg, dir, node };
|
|
8729
8768
|
}
|
|
8730
8769
|
|
|
8731
8770
|
// src/extract/kotlin.ts
|
|
8732
8771
|
init_cjs_shims();
|
|
8733
|
-
var
|
|
8734
|
-
var
|
|
8772
|
+
var import_node_fs17 = require("fs");
|
|
8773
|
+
var import_node_path19 = __toESM(require("path"), 1);
|
|
8735
8774
|
var import_types15 = require("@neat.is/types");
|
|
8736
8775
|
var KOTLIN_SOURCE_EXT = ".kt";
|
|
8737
8776
|
var JAVA_SOURCE_EXT = ".java";
|
|
@@ -8741,15 +8780,15 @@ async function dominantJvmSourceLanguage(dir) {
|
|
|
8741
8780
|
let java = 0;
|
|
8742
8781
|
async function recurse(current, depth) {
|
|
8743
8782
|
if (depth > SOURCE_PROBE_MAX_DEPTH) return;
|
|
8744
|
-
const entries = await
|
|
8783
|
+
const entries = await import_node_fs17.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
8745
8784
|
for (const entry of entries) {
|
|
8746
8785
|
if (entry.isDirectory()) {
|
|
8747
8786
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
8748
|
-
await recurse(
|
|
8787
|
+
await recurse(import_node_path19.default.join(current, entry.name), depth + 1);
|
|
8749
8788
|
continue;
|
|
8750
8789
|
}
|
|
8751
8790
|
if (!entry.isFile()) continue;
|
|
8752
|
-
const ext =
|
|
8791
|
+
const ext = import_node_path19.default.extname(entry.name).toLowerCase();
|
|
8753
8792
|
if (ext === KOTLIN_SOURCE_EXT) kt++;
|
|
8754
8793
|
else if (ext === JAVA_SOURCE_EXT) java++;
|
|
8755
8794
|
}
|
|
@@ -8760,17 +8799,17 @@ async function dominantJvmSourceLanguage(dir) {
|
|
|
8760
8799
|
return void 0;
|
|
8761
8800
|
}
|
|
8762
8801
|
async function readJvmManifest(dir) {
|
|
8763
|
-
const pomPath =
|
|
8764
|
-
const gradlePath =
|
|
8765
|
-
const gradleKtsPath =
|
|
8802
|
+
const pomPath = import_node_path19.default.join(dir, JAVA_MANIFESTS[0]);
|
|
8803
|
+
const gradlePath = import_node_path19.default.join(dir, JAVA_MANIFESTS[1]);
|
|
8804
|
+
const gradleKtsPath = import_node_path19.default.join(dir, JAVA_MANIFESTS[2]);
|
|
8766
8805
|
if (await exists2(pomPath)) {
|
|
8767
|
-
return parsePomXml(await
|
|
8806
|
+
return parsePomXml(await import_node_fs17.promises.readFile(pomPath, "utf8").catch(() => ""));
|
|
8768
8807
|
}
|
|
8769
8808
|
if (await exists2(gradlePath)) {
|
|
8770
|
-
return parseGradle(await
|
|
8809
|
+
return parseGradle(await import_node_fs17.promises.readFile(gradlePath, "utf8").catch(() => ""));
|
|
8771
8810
|
}
|
|
8772
8811
|
if (await exists2(gradleKtsPath)) {
|
|
8773
|
-
return parseGradle(await
|
|
8812
|
+
return parseGradle(await import_node_fs17.promises.readFile(gradleKtsPath, "utf8").catch(() => ""));
|
|
8774
8813
|
}
|
|
8775
8814
|
return null;
|
|
8776
8815
|
}
|
|
@@ -8779,7 +8818,7 @@ async function discoverKotlinService(scanPath, dir) {
|
|
|
8779
8818
|
if (await dominantJvmSourceLanguage(dir) !== "kotlin") return null;
|
|
8780
8819
|
const manifest = await readJvmManifest(dir);
|
|
8781
8820
|
if (!manifest) return null;
|
|
8782
|
-
const name = manifest.name?.trim() ||
|
|
8821
|
+
const name = manifest.name?.trim() || import_node_path19.default.basename(dir);
|
|
8783
8822
|
const dependencies = manifest.dependencies;
|
|
8784
8823
|
const pkg = { name, dependencies };
|
|
8785
8824
|
const node = {
|
|
@@ -8788,20 +8827,20 @@ async function discoverKotlinService(scanPath, dir) {
|
|
|
8788
8827
|
name,
|
|
8789
8828
|
language: "kotlin",
|
|
8790
8829
|
dependencies,
|
|
8791
|
-
repoPath:
|
|
8830
|
+
repoPath: import_node_path19.default.relative(scanPath, dir)
|
|
8792
8831
|
};
|
|
8793
8832
|
return { pkg, dir, node };
|
|
8794
8833
|
}
|
|
8795
8834
|
|
|
8796
8835
|
// src/extract/rust.ts
|
|
8797
8836
|
init_cjs_shims();
|
|
8798
|
-
var
|
|
8799
|
-
var
|
|
8837
|
+
var import_node_fs18 = require("fs");
|
|
8838
|
+
var import_node_path20 = __toESM(require("path"), 1);
|
|
8800
8839
|
var import_smol_toml2 = require("smol-toml");
|
|
8801
8840
|
var import_types16 = require("@neat.is/types");
|
|
8802
8841
|
var CARGO_MANIFEST = "Cargo.toml";
|
|
8803
8842
|
async function hasRustManifest(dir) {
|
|
8804
|
-
return exists2(
|
|
8843
|
+
return exists2(import_node_path20.default.join(dir, CARGO_MANIFEST));
|
|
8805
8844
|
}
|
|
8806
8845
|
function parseCargoToml(source) {
|
|
8807
8846
|
const dependencies = {};
|
|
@@ -8826,10 +8865,10 @@ function parseCargoToml(source) {
|
|
|
8826
8865
|
return { name, dependencies };
|
|
8827
8866
|
}
|
|
8828
8867
|
async function discoverRustService(scanPath, dir) {
|
|
8829
|
-
const cargoPath =
|
|
8868
|
+
const cargoPath = import_node_path20.default.join(dir, CARGO_MANIFEST);
|
|
8830
8869
|
if (!await exists2(cargoPath)) return null;
|
|
8831
|
-
const manifest = parseCargoToml(await
|
|
8832
|
-
const name = manifest.name?.trim() ||
|
|
8870
|
+
const manifest = parseCargoToml(await import_node_fs18.promises.readFile(cargoPath, "utf8").catch(() => ""));
|
|
8871
|
+
const name = manifest.name?.trim() || import_node_path20.default.basename(dir);
|
|
8833
8872
|
const dependencies = manifest.dependencies;
|
|
8834
8873
|
const pkg = { name, dependencies };
|
|
8835
8874
|
const node = {
|
|
@@ -8838,19 +8877,19 @@ async function discoverRustService(scanPath, dir) {
|
|
|
8838
8877
|
name,
|
|
8839
8878
|
language: "rust",
|
|
8840
8879
|
dependencies,
|
|
8841
|
-
repoPath:
|
|
8880
|
+
repoPath: import_node_path20.default.relative(scanPath, dir)
|
|
8842
8881
|
};
|
|
8843
8882
|
return { pkg, dir, node };
|
|
8844
8883
|
}
|
|
8845
8884
|
|
|
8846
8885
|
// src/extract/cpp.ts
|
|
8847
8886
|
init_cjs_shims();
|
|
8848
|
-
var
|
|
8849
|
-
var
|
|
8887
|
+
var import_node_fs19 = require("fs");
|
|
8888
|
+
var import_node_path21 = __toESM(require("path"), 1);
|
|
8850
8889
|
var import_types17 = require("@neat.is/types");
|
|
8851
8890
|
var CMAKE_MANIFEST = "CMakeLists.txt";
|
|
8852
8891
|
async function hasCppManifest(dir) {
|
|
8853
|
-
return exists2(
|
|
8892
|
+
return exists2(import_node_path21.default.join(dir, CMAKE_MANIFEST));
|
|
8854
8893
|
}
|
|
8855
8894
|
function parseCMakeProjectName(source) {
|
|
8856
8895
|
const withoutComments = source.replace(/#.*$/gm, "");
|
|
@@ -8858,10 +8897,10 @@ function parseCMakeProjectName(source) {
|
|
|
8858
8897
|
return m?.[1];
|
|
8859
8898
|
}
|
|
8860
8899
|
async function discoverCppService(scanPath, dir) {
|
|
8861
|
-
const cmakePath =
|
|
8900
|
+
const cmakePath = import_node_path21.default.join(dir, CMAKE_MANIFEST);
|
|
8862
8901
|
if (!await exists2(cmakePath)) return null;
|
|
8863
|
-
const source = await
|
|
8864
|
-
const name = parseCMakeProjectName(source)?.trim() ||
|
|
8902
|
+
const source = await import_node_fs19.promises.readFile(cmakePath, "utf8").catch(() => "");
|
|
8903
|
+
const name = parseCMakeProjectName(source)?.trim() || import_node_path21.default.basename(dir);
|
|
8865
8904
|
const dependencies = {};
|
|
8866
8905
|
const pkg = { name, dependencies };
|
|
8867
8906
|
const node = {
|
|
@@ -8870,15 +8909,15 @@ async function discoverCppService(scanPath, dir) {
|
|
|
8870
8909
|
name,
|
|
8871
8910
|
language: "cpp",
|
|
8872
8911
|
dependencies,
|
|
8873
|
-
repoPath:
|
|
8912
|
+
repoPath: import_node_path21.default.relative(scanPath, dir)
|
|
8874
8913
|
};
|
|
8875
8914
|
return { pkg, dir, node };
|
|
8876
8915
|
}
|
|
8877
8916
|
|
|
8878
8917
|
// src/extract/dockerfile-service.ts
|
|
8879
8918
|
init_cjs_shims();
|
|
8880
|
-
var
|
|
8881
|
-
var
|
|
8919
|
+
var import_node_fs20 = require("fs");
|
|
8920
|
+
var import_node_path22 = __toESM(require("path"), 1);
|
|
8882
8921
|
var import_types18 = require("@neat.is/types");
|
|
8883
8922
|
function languageForSourceExt(ext) {
|
|
8884
8923
|
switch (ext) {
|
|
@@ -8920,12 +8959,12 @@ function languageForSourceExt(ext) {
|
|
|
8920
8959
|
}
|
|
8921
8960
|
var LANGUAGE_PRECEDENCE = ["typescript", "javascript", "python", "go", "ruby", "php", "csharp", "java", "kotlin", "rust", "cpp"];
|
|
8922
8961
|
async function inferServiceLanguage(dir) {
|
|
8923
|
-
const entries = await
|
|
8962
|
+
const entries = await import_node_fs20.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
8924
8963
|
const counts = /* @__PURE__ */ new Map();
|
|
8925
8964
|
for (const entry of entries) {
|
|
8926
8965
|
if (!entry.isFile()) continue;
|
|
8927
8966
|
if (isNeatAuthoredSourceFile(entry.name)) continue;
|
|
8928
|
-
const ext =
|
|
8967
|
+
const ext = import_node_path22.default.extname(entry.name).toLowerCase();
|
|
8929
8968
|
if (!SERVICE_FILE_EXTENSIONS.has(ext)) continue;
|
|
8930
8969
|
const lang = languageForSourceExt(ext);
|
|
8931
8970
|
if (!lang) continue;
|
|
@@ -8944,7 +8983,7 @@ async function inferServiceLanguage(dir) {
|
|
|
8944
8983
|
return best;
|
|
8945
8984
|
}
|
|
8946
8985
|
async function hasLanguageManifest(dir) {
|
|
8947
|
-
return await exists2(
|
|
8986
|
+
return await exists2(import_node_path22.default.join(dir, "package.json")) || await exists2(import_node_path22.default.join(dir, "pyproject.toml")) || await exists2(import_node_path22.default.join(dir, "requirements.txt")) || await exists2(import_node_path22.default.join(dir, "setup.py")) || await exists2(import_node_path22.default.join(dir, "go.mod")) || await exists2(import_node_path22.default.join(dir, "Gemfile")) || await exists2(import_node_path22.default.join(dir, "composer.json")) || // C#'s marker is a glob (`*.csproj` / `*.sln`), so it reads the directory
|
|
8948
8987
|
// rather than probing a fixed filename (ADR-196).
|
|
8949
8988
|
await hasCsharpProject(dir) || // Java (and its JVM sibling Kotlin, ADR-199) key on the same Maven / Gradle
|
|
8950
8989
|
// build manifest (ADR-197), so this one check stands the Dockerfile fallback
|
|
@@ -8954,15 +8993,15 @@ async function hasLanguageManifest(dir) {
|
|
|
8954
8993
|
await hasCppManifest(dir);
|
|
8955
8994
|
}
|
|
8956
8995
|
async function hasDockerfileDeclaredService(dir) {
|
|
8957
|
-
if (!await exists2(
|
|
8996
|
+
if (!await exists2(import_node_path22.default.join(dir, "Dockerfile"))) return false;
|
|
8958
8997
|
return await inferServiceLanguage(dir) !== null;
|
|
8959
8998
|
}
|
|
8960
8999
|
async function discoverDockerfileService(scanPath, dir) {
|
|
8961
|
-
if (!await exists2(
|
|
9000
|
+
if (!await exists2(import_node_path22.default.join(dir, "Dockerfile"))) return null;
|
|
8962
9001
|
if (await hasLanguageManifest(dir)) return null;
|
|
8963
9002
|
const language = await inferServiceLanguage(dir);
|
|
8964
9003
|
if (!language) return null;
|
|
8965
|
-
const name =
|
|
9004
|
+
const name = import_node_path22.default.basename(dir);
|
|
8966
9005
|
const pkg = { name, dependencies: {} };
|
|
8967
9006
|
const node = {
|
|
8968
9007
|
id: (0, import_types18.serviceId)(name),
|
|
@@ -8970,24 +9009,24 @@ async function discoverDockerfileService(scanPath, dir) {
|
|
|
8970
9009
|
name,
|
|
8971
9010
|
language,
|
|
8972
9011
|
dependencies: {},
|
|
8973
|
-
repoPath:
|
|
9012
|
+
repoPath: import_node_path22.default.relative(scanPath, dir)
|
|
8974
9013
|
};
|
|
8975
9014
|
return { pkg, dir, node };
|
|
8976
9015
|
}
|
|
8977
9016
|
|
|
8978
9017
|
// src/extract/owners.ts
|
|
8979
9018
|
init_cjs_shims();
|
|
8980
|
-
var
|
|
8981
|
-
var
|
|
9019
|
+
var import_node_fs21 = require("fs");
|
|
9020
|
+
var import_node_path23 = __toESM(require("path"), 1);
|
|
8982
9021
|
var import_minimatch = require("minimatch");
|
|
8983
9022
|
async function loadCodeowners(scanPath) {
|
|
8984
9023
|
const candidates = [
|
|
8985
|
-
|
|
8986
|
-
|
|
9024
|
+
import_node_path23.default.join(scanPath, "CODEOWNERS"),
|
|
9025
|
+
import_node_path23.default.join(scanPath, ".github", "CODEOWNERS")
|
|
8987
9026
|
];
|
|
8988
9027
|
for (const file of candidates) {
|
|
8989
9028
|
if (await exists2(file)) {
|
|
8990
|
-
const raw = await
|
|
9029
|
+
const raw = await import_node_fs21.promises.readFile(file, "utf8");
|
|
8991
9030
|
return parseCodeowners(raw);
|
|
8992
9031
|
}
|
|
8993
9032
|
}
|
|
@@ -9005,7 +9044,7 @@ function parseCodeowners(raw) {
|
|
|
9005
9044
|
return { rules };
|
|
9006
9045
|
}
|
|
9007
9046
|
function matchOwner(file, repoPath) {
|
|
9008
|
-
const normalized = repoPath.split(
|
|
9047
|
+
const normalized = repoPath.split(import_node_path23.default.sep).join("/");
|
|
9009
9048
|
for (const rule of file.rules) {
|
|
9010
9049
|
if (matchesPattern(rule.pattern, normalized)) return rule.owners;
|
|
9011
9050
|
}
|
|
@@ -9021,7 +9060,7 @@ function matchesPattern(rawPattern, repoPath) {
|
|
|
9021
9060
|
return false;
|
|
9022
9061
|
}
|
|
9023
9062
|
async function readPackageJsonAuthor(serviceDir) {
|
|
9024
|
-
const pkgPath =
|
|
9063
|
+
const pkgPath = import_node_path23.default.join(serviceDir, "package.json");
|
|
9025
9064
|
if (!await exists2(pkgPath)) return null;
|
|
9026
9065
|
try {
|
|
9027
9066
|
const pkg = await readJson(pkgPath);
|
|
@@ -9058,36 +9097,36 @@ function workspaceGlobs(pkg) {
|
|
|
9058
9097
|
return null;
|
|
9059
9098
|
}
|
|
9060
9099
|
async function hasPythonManifest(dir) {
|
|
9061
|
-
return await exists2(
|
|
9100
|
+
return await exists2(import_node_path24.default.join(dir, "pyproject.toml")) || await exists2(import_node_path24.default.join(dir, "requirements.txt")) || await exists2(import_node_path24.default.join(dir, "setup.py"));
|
|
9062
9101
|
}
|
|
9063
9102
|
async function hasGoManifest(dir) {
|
|
9064
|
-
return exists2(
|
|
9103
|
+
return exists2(import_node_path24.default.join(dir, "go.mod"));
|
|
9065
9104
|
}
|
|
9066
9105
|
async function hasRubyManifest(dir) {
|
|
9067
|
-
return exists2(
|
|
9106
|
+
return exists2(import_node_path24.default.join(dir, "Gemfile"));
|
|
9068
9107
|
}
|
|
9069
9108
|
async function hasPhpManifest(dir) {
|
|
9070
|
-
return exists2(
|
|
9109
|
+
return exists2(import_node_path24.default.join(dir, "composer.json"));
|
|
9071
9110
|
}
|
|
9072
9111
|
async function hasCsharpManifest(dir) {
|
|
9073
9112
|
return hasCsharpProject(dir);
|
|
9074
9113
|
}
|
|
9075
9114
|
async function loadGitignore(scanPath) {
|
|
9076
|
-
const gitignorePath =
|
|
9115
|
+
const gitignorePath = import_node_path24.default.join(scanPath, ".gitignore");
|
|
9077
9116
|
if (!await exists2(gitignorePath)) return null;
|
|
9078
|
-
const raw = await
|
|
9117
|
+
const raw = await import_node_fs22.promises.readFile(gitignorePath, "utf8");
|
|
9079
9118
|
return (0, import_ignore2.default)().add(raw);
|
|
9080
9119
|
}
|
|
9081
9120
|
async function walkDirs(start, scanPath, options, visit) {
|
|
9082
9121
|
async function recurse(current, depth) {
|
|
9083
9122
|
if (depth > options.maxDepth) return;
|
|
9084
|
-
const entries = await
|
|
9123
|
+
const entries = await import_node_fs22.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
9085
9124
|
for (const entry of entries) {
|
|
9086
9125
|
if (!entry.isDirectory()) continue;
|
|
9087
9126
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
9088
|
-
const child =
|
|
9127
|
+
const child = import_node_path24.default.join(current, entry.name);
|
|
9089
9128
|
if (options.ig) {
|
|
9090
|
-
const rel =
|
|
9129
|
+
const rel = import_node_path24.default.relative(scanPath, child).split(import_node_path24.default.sep).join("/");
|
|
9091
9130
|
if (rel && options.ig.ignores(rel + "/")) continue;
|
|
9092
9131
|
}
|
|
9093
9132
|
if (await isPythonVenvDir(child)) continue;
|
|
@@ -9103,8 +9142,8 @@ async function expandWorkspaceGlobs(scanPath, globs) {
|
|
|
9103
9142
|
for (const raw of globs) {
|
|
9104
9143
|
const pattern = raw.replace(/^\.\//, "");
|
|
9105
9144
|
if (!pattern.includes("*")) {
|
|
9106
|
-
const candidate =
|
|
9107
|
-
if (await exists2(
|
|
9145
|
+
const candidate = import_node_path24.default.join(scanPath, pattern);
|
|
9146
|
+
if (await exists2(import_node_path24.default.join(candidate, "package.json"))) found.add(candidate);
|
|
9108
9147
|
continue;
|
|
9109
9148
|
}
|
|
9110
9149
|
const segments = pattern.split("/");
|
|
@@ -9113,13 +9152,13 @@ async function expandWorkspaceGlobs(scanPath, globs) {
|
|
|
9113
9152
|
if (seg.includes("*")) break;
|
|
9114
9153
|
staticSegments.push(seg);
|
|
9115
9154
|
}
|
|
9116
|
-
const start =
|
|
9155
|
+
const start = import_node_path24.default.join(scanPath, ...staticSegments);
|
|
9117
9156
|
if (!await exists2(start)) continue;
|
|
9118
9157
|
const hasDoubleStar = pattern.includes("**");
|
|
9119
9158
|
const walkDepth = hasDoubleStar ? scanDepth : Math.max(0, segments.length - staticSegments.length - 1);
|
|
9120
9159
|
await walkDirs(start, scanPath, { maxDepth: walkDepth, ig: null }, async (dir) => {
|
|
9121
|
-
const rel =
|
|
9122
|
-
if ((0, import_minimatch2.minimatch)(rel, pattern) && await exists2(
|
|
9160
|
+
const rel = import_node_path24.default.relative(scanPath, dir).split(import_node_path24.default.sep).join("/");
|
|
9161
|
+
if ((0, import_minimatch2.minimatch)(rel, pattern) && await exists2(import_node_path24.default.join(dir, "package.json"))) {
|
|
9123
9162
|
found.add(dir);
|
|
9124
9163
|
}
|
|
9125
9164
|
});
|
|
@@ -9142,18 +9181,18 @@ function detectJsFramework(pkg) {
|
|
|
9142
9181
|
async function detectJsServiceLanguage(dir, pkg) {
|
|
9143
9182
|
const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
9144
9183
|
if (deps["typescript"] !== void 0) return "typescript";
|
|
9145
|
-
const entries = await
|
|
9184
|
+
const entries = await import_node_fs22.promises.readdir(dir).catch(() => []);
|
|
9146
9185
|
if (entries.some((name) => /^tsconfig(\..+)?\.json$/.test(name))) return "typescript";
|
|
9147
9186
|
return "javascript";
|
|
9148
9187
|
}
|
|
9149
9188
|
async function discoverNodeService(scanPath, dir) {
|
|
9150
|
-
const pkgPath =
|
|
9189
|
+
const pkgPath = import_node_path24.default.join(dir, "package.json");
|
|
9151
9190
|
if (!await exists2(pkgPath)) return null;
|
|
9152
9191
|
let pkg;
|
|
9153
9192
|
try {
|
|
9154
9193
|
pkg = await readJson(pkgPath);
|
|
9155
9194
|
} catch (err) {
|
|
9156
|
-
recordExtractionError("services",
|
|
9195
|
+
recordExtractionError("services", import_node_path24.default.relative(scanPath, pkgPath), err);
|
|
9157
9196
|
return null;
|
|
9158
9197
|
}
|
|
9159
9198
|
if (!pkg.name) return null;
|
|
@@ -9166,7 +9205,7 @@ async function discoverNodeService(scanPath, dir) {
|
|
|
9166
9205
|
language,
|
|
9167
9206
|
version: pkg.version,
|
|
9168
9207
|
dependencies: pkg.dependencies ?? {},
|
|
9169
|
-
repoPath:
|
|
9208
|
+
repoPath: import_node_path24.default.relative(scanPath, dir),
|
|
9170
9209
|
...pkg.engines?.node ? { nodeEngine: pkg.engines.node } : {},
|
|
9171
9210
|
...framework ? { framework } : {}
|
|
9172
9211
|
};
|
|
@@ -9183,12 +9222,12 @@ async function discoverPyService(scanPath, dir) {
|
|
|
9183
9222
|
language: "python",
|
|
9184
9223
|
version: py.version,
|
|
9185
9224
|
dependencies: py.dependencies,
|
|
9186
|
-
repoPath:
|
|
9225
|
+
repoPath: import_node_path24.default.relative(scanPath, dir)
|
|
9187
9226
|
};
|
|
9188
9227
|
return { pkg, dir, node };
|
|
9189
9228
|
}
|
|
9190
9229
|
async function discoverServices(scanPath) {
|
|
9191
|
-
const rootPkgPath =
|
|
9230
|
+
const rootPkgPath = import_node_path24.default.join(scanPath, "package.json");
|
|
9192
9231
|
let rootPkg = null;
|
|
9193
9232
|
if (await exists2(rootPkgPath)) {
|
|
9194
9233
|
try {
|
|
@@ -9196,7 +9235,7 @@ async function discoverServices(scanPath) {
|
|
|
9196
9235
|
} catch (err) {
|
|
9197
9236
|
recordExtractionError(
|
|
9198
9237
|
"services workspaces",
|
|
9199
|
-
|
|
9238
|
+
import_node_path24.default.relative(scanPath, rootPkgPath),
|
|
9200
9239
|
err
|
|
9201
9240
|
);
|
|
9202
9241
|
}
|
|
@@ -9217,7 +9256,7 @@ async function discoverServices(scanPath) {
|
|
|
9217
9256
|
scanPath,
|
|
9218
9257
|
{ maxDepth: parseScanDepth(), ig },
|
|
9219
9258
|
async (dir) => {
|
|
9220
|
-
if (await exists2(
|
|
9259
|
+
if (await exists2(import_node_path24.default.join(dir, "package.json"))) {
|
|
9221
9260
|
candidateDirs.push(dir);
|
|
9222
9261
|
} else if (await hasPythonManifest(dir) || await hasGoManifest(dir) || await hasRubyManifest(dir) || await hasPhpManifest(dir) || await hasCsharpManifest(dir) || await hasJavaManifest(dir) || await hasRustManifest(dir) || await hasCppManifest(dir)) {
|
|
9223
9262
|
candidateDirs.push(dir);
|
|
@@ -9245,8 +9284,8 @@ async function discoverServices(scanPath) {
|
|
|
9245
9284
|
seenDirs.add(service.dir);
|
|
9246
9285
|
const existingDir = seen.get(service.node.name);
|
|
9247
9286
|
if (existingDir !== void 0) {
|
|
9248
|
-
const a =
|
|
9249
|
-
const b =
|
|
9287
|
+
const a = import_node_path24.default.relative(scanPath, existingDir) || ".";
|
|
9288
|
+
const b = import_node_path24.default.relative(scanPath, dir) || ".";
|
|
9250
9289
|
console.warn(
|
|
9251
9290
|
`[neat] duplicate package name "${service.node.name}" \u2014 keeping ${a}, ignoring ${b}`
|
|
9252
9291
|
);
|
|
@@ -9260,14 +9299,14 @@ async function discoverServices(scanPath) {
|
|
|
9260
9299
|
const owner = await computeServiceOwner(codeowners, service.node.repoPath, service.dir);
|
|
9261
9300
|
if (owner !== void 0) service.node.owner = owner;
|
|
9262
9301
|
}
|
|
9263
|
-
const resolvedDirs = out.map((s) =>
|
|
9302
|
+
const resolvedDirs = out.map((s) => import_node_path24.default.resolve(s.dir));
|
|
9264
9303
|
for (let i = 0; i < out.length; i++) {
|
|
9265
9304
|
const self = resolvedDirs[i];
|
|
9266
9305
|
const nested = [];
|
|
9267
9306
|
for (let j = 0; j < out.length; j++) {
|
|
9268
9307
|
if (i === j) continue;
|
|
9269
9308
|
const other = resolvedDirs[j];
|
|
9270
|
-
if (other.startsWith(self +
|
|
9309
|
+
if (other.startsWith(self + import_node_path24.default.sep)) nested.push(other);
|
|
9271
9310
|
}
|
|
9272
9311
|
out[i].excludeDirs = nested;
|
|
9273
9312
|
}
|
|
@@ -9294,8 +9333,8 @@ function addServiceNodes(graph, services) {
|
|
|
9294
9333
|
|
|
9295
9334
|
// src/extract/aliases.ts
|
|
9296
9335
|
init_cjs_shims();
|
|
9297
|
-
var
|
|
9298
|
-
var
|
|
9336
|
+
var import_node_path25 = __toESM(require("path"), 1);
|
|
9337
|
+
var import_node_fs23 = require("fs");
|
|
9299
9338
|
var import_yaml2 = require("yaml");
|
|
9300
9339
|
var import_types20 = require("@neat.is/types");
|
|
9301
9340
|
var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
@@ -9322,14 +9361,14 @@ function indexServicesByName(services) {
|
|
|
9322
9361
|
const map = /* @__PURE__ */ new Map();
|
|
9323
9362
|
for (const s of services) {
|
|
9324
9363
|
map.set(s.node.name, s.node.id);
|
|
9325
|
-
map.set(
|
|
9364
|
+
map.set(import_node_path25.default.basename(s.dir), s.node.id);
|
|
9326
9365
|
}
|
|
9327
9366
|
return map;
|
|
9328
9367
|
}
|
|
9329
9368
|
async function collectComposeAliases(graph, scanPath, serviceIndex) {
|
|
9330
9369
|
let composePath = null;
|
|
9331
9370
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
9332
|
-
const abs =
|
|
9371
|
+
const abs = import_node_path25.default.join(scanPath, name);
|
|
9333
9372
|
if (await exists2(abs)) {
|
|
9334
9373
|
composePath = abs;
|
|
9335
9374
|
break;
|
|
@@ -9342,7 +9381,7 @@ async function collectComposeAliases(graph, scanPath, serviceIndex) {
|
|
|
9342
9381
|
} catch (err) {
|
|
9343
9382
|
recordExtractionError(
|
|
9344
9383
|
"aliases compose",
|
|
9345
|
-
|
|
9384
|
+
import_node_path25.default.relative(scanPath, composePath),
|
|
9346
9385
|
err
|
|
9347
9386
|
);
|
|
9348
9387
|
return;
|
|
@@ -9385,11 +9424,11 @@ function parseDockerfileLabels(content) {
|
|
|
9385
9424
|
}
|
|
9386
9425
|
async function collectDockerfileAliases(graph, services) {
|
|
9387
9426
|
for (const service of services) {
|
|
9388
|
-
const dockerfilePath =
|
|
9427
|
+
const dockerfilePath = import_node_path25.default.join(service.dir, "Dockerfile");
|
|
9389
9428
|
if (!await exists2(dockerfilePath)) continue;
|
|
9390
9429
|
let content;
|
|
9391
9430
|
try {
|
|
9392
|
-
content = await
|
|
9431
|
+
content = await import_node_fs23.promises.readFile(dockerfilePath, "utf8");
|
|
9393
9432
|
} catch (err) {
|
|
9394
9433
|
recordExtractionError("aliases dockerfile", dockerfilePath, err);
|
|
9395
9434
|
continue;
|
|
@@ -9401,15 +9440,15 @@ async function collectDockerfileAliases(graph, services) {
|
|
|
9401
9440
|
async function walkYamlFiles(start, depth = 0, max = 5) {
|
|
9402
9441
|
if (depth > max) return [];
|
|
9403
9442
|
const out = [];
|
|
9404
|
-
const entries = await
|
|
9443
|
+
const entries = await import_node_fs23.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
9405
9444
|
for (const entry of entries) {
|
|
9406
9445
|
if (entry.isDirectory()) {
|
|
9407
9446
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
9408
|
-
const child =
|
|
9447
|
+
const child = import_node_path25.default.join(start, entry.name);
|
|
9409
9448
|
if (await isPythonVenvDir(child)) continue;
|
|
9410
9449
|
out.push(...await walkYamlFiles(child, depth + 1, max));
|
|
9411
|
-
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
9412
|
-
out.push(
|
|
9450
|
+
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path25.default.extname(entry.name))) {
|
|
9451
|
+
out.push(import_node_path25.default.join(start, entry.name));
|
|
9413
9452
|
}
|
|
9414
9453
|
}
|
|
9415
9454
|
return out;
|
|
@@ -9436,7 +9475,7 @@ function k8sServiceTarget(doc, byName) {
|
|
|
9436
9475
|
async function collectK8sAliases(graph, scanPath, serviceIndex) {
|
|
9437
9476
|
const files = await walkYamlFiles(scanPath);
|
|
9438
9477
|
for (const file of files) {
|
|
9439
|
-
const content = await
|
|
9478
|
+
const content = await import_node_fs23.promises.readFile(file, "utf8");
|
|
9440
9479
|
let docs;
|
|
9441
9480
|
try {
|
|
9442
9481
|
docs = (0, import_yaml2.parseAllDocuments)(content).map((d) => d.toJSON());
|
|
@@ -9461,14 +9500,14 @@ async function addServiceAliases(graph, scanPath, services) {
|
|
|
9461
9500
|
|
|
9462
9501
|
// src/extract/files.ts
|
|
9463
9502
|
init_cjs_shims();
|
|
9464
|
-
var
|
|
9503
|
+
var import_node_path26 = __toESM(require("path"), 1);
|
|
9465
9504
|
async function addFiles(graph, services) {
|
|
9466
9505
|
let nodesAdded = 0;
|
|
9467
9506
|
let edgesAdded = 0;
|
|
9468
9507
|
for (const service of services) {
|
|
9469
9508
|
const filePaths = await walkSourceFiles(service.dir, service.excludeDirs);
|
|
9470
9509
|
for (const filePath of filePaths) {
|
|
9471
|
-
const relPath = toPosix(
|
|
9510
|
+
const relPath = toPosix(import_node_path26.default.relative(service.dir, filePath));
|
|
9472
9511
|
const { nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
9473
9512
|
graph,
|
|
9474
9513
|
service.pkg.name,
|
|
@@ -9484,7 +9523,7 @@ async function addFiles(graph, services) {
|
|
|
9484
9523
|
|
|
9485
9524
|
// src/extract/symbols.ts
|
|
9486
9525
|
init_cjs_shims();
|
|
9487
|
-
var
|
|
9526
|
+
var import_node_path27 = __toESM(require("path"), 1);
|
|
9488
9527
|
var import_tree_sitter3 = __toESM(require("tree-sitter"), 1);
|
|
9489
9528
|
var import_tree_sitter_javascript3 = __toESM(require("tree-sitter-javascript"), 1);
|
|
9490
9529
|
var import_tree_sitter_typescript = __toESM(require("tree-sitter-typescript"), 1);
|
|
@@ -10236,10 +10275,10 @@ async function addSymbols(graph, services) {
|
|
|
10236
10275
|
for (const service of services) {
|
|
10237
10276
|
const files = await loadSourceFiles(service.dir, service.excludeDirs);
|
|
10238
10277
|
for (const file of files) {
|
|
10239
|
-
const ext =
|
|
10278
|
+
const ext = import_node_path27.default.extname(file.path);
|
|
10240
10279
|
const parser = parserForExt5(ext);
|
|
10241
10280
|
if (!parser) continue;
|
|
10242
|
-
const relPath = toPosix(
|
|
10281
|
+
const relPath = toPosix(import_node_path27.default.relative(service.dir, file.path));
|
|
10243
10282
|
let defs;
|
|
10244
10283
|
try {
|
|
10245
10284
|
const tree = parseSource3(parser, file.content);
|
|
@@ -10299,7 +10338,7 @@ async function addSymbols(graph, services) {
|
|
|
10299
10338
|
|
|
10300
10339
|
// src/extract/symbol-edges.ts
|
|
10301
10340
|
init_cjs_shims();
|
|
10302
|
-
var
|
|
10341
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
10303
10342
|
var import_tree_sitter4 = __toESM(require("tree-sitter"), 1);
|
|
10304
10343
|
var import_types22 = require("@neat.is/types");
|
|
10305
10344
|
function extendsInfo(classHeritage) {
|
|
@@ -10406,10 +10445,10 @@ async function addSymbolEdges(graph, services) {
|
|
|
10406
10445
|
const tsPaths = await loadTsPathConfig(service.dir);
|
|
10407
10446
|
const files = await loadSourceFiles(service.dir, service.excludeDirs);
|
|
10408
10447
|
for (const file of files) {
|
|
10409
|
-
const parser = parserForExt5(
|
|
10448
|
+
const parser = parserForExt5(import_node_path28.default.extname(file.path));
|
|
10410
10449
|
if (!parser) continue;
|
|
10411
|
-
const relPath = toPosix(
|
|
10412
|
-
const fileDir =
|
|
10450
|
+
const relPath = toPosix(import_node_path28.default.relative(service.dir, file.path));
|
|
10451
|
+
const fileDir = import_node_path28.default.dirname(file.path);
|
|
10413
10452
|
let root;
|
|
10414
10453
|
try {
|
|
10415
10454
|
root = parseSource3(parser, file.content).rootNode;
|
|
@@ -10544,7 +10583,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
10544
10583
|
|
|
10545
10584
|
// src/extract/actions.ts
|
|
10546
10585
|
init_cjs_shims();
|
|
10547
|
-
var
|
|
10586
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
10548
10587
|
var import_tree_sitter5 = __toESM(require("tree-sitter"), 1);
|
|
10549
10588
|
var import_types23 = require("@neat.is/types");
|
|
10550
10589
|
function stringLiteralText2(node) {
|
|
@@ -10691,9 +10730,9 @@ async function addServerActions(graph, services) {
|
|
|
10691
10730
|
const files = await loadSourceFiles(service.dir, service.excludeDirs);
|
|
10692
10731
|
for (const file of files) {
|
|
10693
10732
|
if (isTestPath(file.path)) continue;
|
|
10694
|
-
const parser = parserForExt5(
|
|
10733
|
+
const parser = parserForExt5(import_node_path29.default.extname(file.path));
|
|
10695
10734
|
if (!parser) continue;
|
|
10696
|
-
const relPath = toPosix(
|
|
10735
|
+
const relPath = toPosix(import_node_path29.default.relative(service.dir, file.path));
|
|
10697
10736
|
let root;
|
|
10698
10737
|
try {
|
|
10699
10738
|
root = parseSource3(parser, file.content).rootNode;
|
|
@@ -10754,10 +10793,10 @@ async function addServerActions(graph, services) {
|
|
|
10754
10793
|
}
|
|
10755
10794
|
for (const file of files) {
|
|
10756
10795
|
if (isTestPath(file.path)) continue;
|
|
10757
|
-
const parser = parserForExt5(
|
|
10796
|
+
const parser = parserForExt5(import_node_path29.default.extname(file.path));
|
|
10758
10797
|
if (!parser) continue;
|
|
10759
|
-
const relPath = toPosix(
|
|
10760
|
-
const fileDir =
|
|
10798
|
+
const relPath = toPosix(import_node_path29.default.relative(service.dir, file.path));
|
|
10799
|
+
const fileDir = import_node_path29.default.dirname(file.path);
|
|
10761
10800
|
let root;
|
|
10762
10801
|
try {
|
|
10763
10802
|
root = parseSource3(parser, file.content).rootNode;
|
|
@@ -10810,14 +10849,14 @@ async function addServerActions(graph, services) {
|
|
|
10810
10849
|
|
|
10811
10850
|
// src/extract/databases/index.ts
|
|
10812
10851
|
init_cjs_shims();
|
|
10813
|
-
var
|
|
10852
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
10814
10853
|
var import_types24 = require("@neat.is/types");
|
|
10815
10854
|
|
|
10816
10855
|
// src/extract/databases/db-config-yaml.ts
|
|
10817
10856
|
init_cjs_shims();
|
|
10818
|
-
var
|
|
10857
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
10819
10858
|
async function parse(serviceDir) {
|
|
10820
|
-
const yamlPath =
|
|
10859
|
+
const yamlPath = import_node_path30.default.join(serviceDir, "db-config.yaml");
|
|
10821
10860
|
if (!await exists2(yamlPath)) return [];
|
|
10822
10861
|
const raw = await readYaml(yamlPath);
|
|
10823
10862
|
return [
|
|
@@ -10835,13 +10874,13 @@ var dbConfigYamlParser = { name: "db-config.yaml", parse };
|
|
|
10835
10874
|
|
|
10836
10875
|
// src/extract/databases/dotenv.ts
|
|
10837
10876
|
init_cjs_shims();
|
|
10838
|
-
var
|
|
10839
|
-
var
|
|
10877
|
+
var import_node_fs25 = require("fs");
|
|
10878
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
10840
10879
|
|
|
10841
10880
|
// src/extract/databases/shared.ts
|
|
10842
10881
|
init_cjs_shims();
|
|
10843
|
-
var
|
|
10844
|
-
var
|
|
10882
|
+
var import_node_fs24 = require("fs");
|
|
10883
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
10845
10884
|
function schemeToEngine(scheme) {
|
|
10846
10885
|
const s = scheme.toLowerCase().split("+")[0];
|
|
10847
10886
|
switch (s) {
|
|
@@ -10880,18 +10919,18 @@ function parseConnectionString(url) {
|
|
|
10880
10919
|
}
|
|
10881
10920
|
async function readIfExists(filePath) {
|
|
10882
10921
|
try {
|
|
10883
|
-
return await
|
|
10922
|
+
return await import_node_fs24.promises.readFile(filePath, "utf8");
|
|
10884
10923
|
} catch {
|
|
10885
10924
|
return null;
|
|
10886
10925
|
}
|
|
10887
10926
|
}
|
|
10888
10927
|
async function resolveEnvVar(serviceDir, name) {
|
|
10889
|
-
const entries = await
|
|
10928
|
+
const entries = await import_node_fs24.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
|
|
10890
10929
|
const envNames = entries.filter((e) => e.isFile() && (e.name === ".env" || e.name.startsWith(".env."))).map((e) => e.name);
|
|
10891
10930
|
const rank = (n) => n === ".env.local" ? 0 : n === ".env" ? 1 : 2;
|
|
10892
10931
|
envNames.sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
|
|
10893
10932
|
for (const fileName of envNames) {
|
|
10894
|
-
const content = await readIfExists(
|
|
10933
|
+
const content = await readIfExists(import_node_path31.default.join(serviceDir, fileName));
|
|
10895
10934
|
if (!content) continue;
|
|
10896
10935
|
for (const line of content.split("\n")) {
|
|
10897
10936
|
const trimmed = line.trim();
|
|
@@ -10910,7 +10949,7 @@ async function resolveEnvVar(serviceDir, name) {
|
|
|
10910
10949
|
}
|
|
10911
10950
|
async function findFirst(serviceDir, candidates) {
|
|
10912
10951
|
for (const rel of candidates) {
|
|
10913
|
-
const abs =
|
|
10952
|
+
const abs = import_node_path31.default.join(serviceDir, rel);
|
|
10914
10953
|
const content = await readIfExists(abs);
|
|
10915
10954
|
if (content !== null) return abs;
|
|
10916
10955
|
}
|
|
@@ -10968,15 +11007,15 @@ function parseDotenvLine(line) {
|
|
|
10968
11007
|
return { key, value };
|
|
10969
11008
|
}
|
|
10970
11009
|
async function parse2(serviceDir) {
|
|
10971
|
-
const entries = await
|
|
11010
|
+
const entries = await import_node_fs25.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
|
|
10972
11011
|
const configs = [];
|
|
10973
11012
|
const seen = /* @__PURE__ */ new Set();
|
|
10974
11013
|
for (const entry of entries) {
|
|
10975
11014
|
if (!entry.isFile()) continue;
|
|
10976
11015
|
const match = isConfigFile(entry.name);
|
|
10977
11016
|
if (!match.match || match.fileType !== "env") continue;
|
|
10978
|
-
const filePath =
|
|
10979
|
-
const content = await
|
|
11017
|
+
const filePath = import_node_path32.default.join(serviceDir, entry.name);
|
|
11018
|
+
const content = await import_node_fs25.promises.readFile(filePath, "utf8");
|
|
10980
11019
|
for (const line of content.split("\n")) {
|
|
10981
11020
|
const parsed = parseDotenvLine(line);
|
|
10982
11021
|
if (!parsed) continue;
|
|
@@ -10995,9 +11034,9 @@ var dotenvParser = { name: ".env", parse: parse2 };
|
|
|
10995
11034
|
|
|
10996
11035
|
// src/extract/databases/prisma.ts
|
|
10997
11036
|
init_cjs_shims();
|
|
10998
|
-
var
|
|
11037
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
10999
11038
|
async function parse3(serviceDir) {
|
|
11000
|
-
const schemaPath =
|
|
11039
|
+
const schemaPath = import_node_path33.default.join(serviceDir, "prisma", "schema.prisma");
|
|
11001
11040
|
const content = await readIfExists(schemaPath);
|
|
11002
11041
|
if (!content) return [];
|
|
11003
11042
|
const block = content.match(/datasource\s+\w+\s*\{([^}]*)\}/s);
|
|
@@ -11155,10 +11194,10 @@ var knexParser = { name: "knex", parse: parse5 };
|
|
|
11155
11194
|
|
|
11156
11195
|
// src/extract/databases/ormconfig.ts
|
|
11157
11196
|
init_cjs_shims();
|
|
11158
|
-
var
|
|
11197
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
11159
11198
|
async function parse6(serviceDir) {
|
|
11160
11199
|
for (const candidate of ["ormconfig.json", "ormconfig.yaml", "ormconfig.yml"]) {
|
|
11161
|
-
const abs =
|
|
11200
|
+
const abs = import_node_path34.default.join(serviceDir, candidate);
|
|
11162
11201
|
if (!await exists2(abs)) continue;
|
|
11163
11202
|
const raw = candidate.endsWith(".json") ? await readJson(abs) : await readYaml(abs);
|
|
11164
11203
|
const entries = Array.isArray(raw) ? raw : [raw];
|
|
@@ -11218,9 +11257,9 @@ var typeormParser = { name: "typeorm", parse: parse7 };
|
|
|
11218
11257
|
|
|
11219
11258
|
// src/extract/databases/sequelize.ts
|
|
11220
11259
|
init_cjs_shims();
|
|
11221
|
-
var
|
|
11260
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
11222
11261
|
async function parse8(serviceDir) {
|
|
11223
|
-
const configPath =
|
|
11262
|
+
const configPath = import_node_path35.default.join(serviceDir, "config", "config.json");
|
|
11224
11263
|
if (!await exists2(configPath)) return [];
|
|
11225
11264
|
const raw = await readJson(configPath);
|
|
11226
11265
|
const out = [];
|
|
@@ -11247,8 +11286,8 @@ var sequelizeParser = { name: "sequelize", parse: parse8 };
|
|
|
11247
11286
|
|
|
11248
11287
|
// src/extract/databases/csharp.ts
|
|
11249
11288
|
init_cjs_shims();
|
|
11250
|
-
var
|
|
11251
|
-
var
|
|
11289
|
+
var import_node_fs26 = require("fs");
|
|
11290
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
11252
11291
|
var CS_EXT = ".cs";
|
|
11253
11292
|
var NPGSQL_GATE = /\bUseNpgsql\b|\bNpgsql\b/;
|
|
11254
11293
|
var REDIS_GATE = /\bConnectionMultiplexer\b|\bStackExchange\.Redis\b|\bConfigurationOptions\.Parse\b|\bAddStackExchangeRedisCache\b/;
|
|
@@ -11295,12 +11334,12 @@ function parseRedisEndpoint(raw) {
|
|
|
11295
11334
|
return { host, port, database: "", engine: "redis", engineVersion: "unknown" };
|
|
11296
11335
|
}
|
|
11297
11336
|
async function resolveEnvUpTree(startDir, name) {
|
|
11298
|
-
let dir =
|
|
11337
|
+
let dir = import_node_path36.default.resolve(startDir);
|
|
11299
11338
|
for (let depth = 0; depth < 12; depth++) {
|
|
11300
11339
|
const value = await resolveEnvVar(dir, name);
|
|
11301
11340
|
if (value !== null) return value;
|
|
11302
|
-
const atRepoRoot = await
|
|
11303
|
-
const parent =
|
|
11341
|
+
const atRepoRoot = await import_node_fs26.promises.access(import_node_path36.default.join(dir, ".git")).then(() => true).catch(() => false);
|
|
11342
|
+
const parent = import_node_path36.default.dirname(dir);
|
|
11304
11343
|
if (atRepoRoot || parent === dir) break;
|
|
11305
11344
|
dir = parent;
|
|
11306
11345
|
}
|
|
@@ -11355,12 +11394,12 @@ async function resolveConfigs(literals, keys, serviceDir, looksLike, parse11) {
|
|
|
11355
11394
|
}
|
|
11356
11395
|
async function parse9(serviceDir) {
|
|
11357
11396
|
const files = (await walkSourceFiles(serviceDir).catch(() => [])).filter(
|
|
11358
|
-
(f) =>
|
|
11397
|
+
(f) => import_node_path36.default.extname(f) === CS_EXT
|
|
11359
11398
|
);
|
|
11360
11399
|
if (files.length === 0) return [];
|
|
11361
11400
|
const sources = [];
|
|
11362
11401
|
for (const file of files) {
|
|
11363
|
-
const content = await
|
|
11402
|
+
const content = await import_node_fs26.promises.readFile(file, "utf8").catch(() => null);
|
|
11364
11403
|
if (content !== null) sources.push({ file, content });
|
|
11365
11404
|
}
|
|
11366
11405
|
let pgGateFile = null;
|
|
@@ -11401,7 +11440,7 @@ var csharpParser = { name: "csharp", parse: parse9 };
|
|
|
11401
11440
|
|
|
11402
11441
|
// src/extract/databases/docker-compose.ts
|
|
11403
11442
|
init_cjs_shims();
|
|
11404
|
-
var
|
|
11443
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
11405
11444
|
function portFromService(svc) {
|
|
11406
11445
|
for (const raw of svc.ports ?? []) {
|
|
11407
11446
|
const str = String(raw);
|
|
@@ -11428,7 +11467,7 @@ function databaseFromEnv(svc) {
|
|
|
11428
11467
|
}
|
|
11429
11468
|
async function parse10(serviceDir) {
|
|
11430
11469
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
11431
|
-
const abs =
|
|
11470
|
+
const abs = import_node_path37.default.join(serviceDir, name);
|
|
11432
11471
|
if (!await exists2(abs)) continue;
|
|
11433
11472
|
const raw = await readYaml(abs);
|
|
11434
11473
|
if (!raw?.services) return [];
|
|
@@ -11601,7 +11640,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
11601
11640
|
discoveredVia: mergedDiscoveredVia
|
|
11602
11641
|
});
|
|
11603
11642
|
}
|
|
11604
|
-
const relConfigFile = toPosix(
|
|
11643
|
+
const relConfigFile = toPosix(import_node_path38.default.relative(service.dir, config.sourceFile));
|
|
11605
11644
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
11606
11645
|
graph,
|
|
11607
11646
|
service.pkg.name,
|
|
@@ -11610,7 +11649,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
11610
11649
|
);
|
|
11611
11650
|
nodesAdded += fn;
|
|
11612
11651
|
edgesAdded += fe;
|
|
11613
|
-
const evidenceFile = toPosix(
|
|
11652
|
+
const evidenceFile = toPosix(import_node_path38.default.relative(scanPath, config.sourceFile));
|
|
11614
11653
|
const edge = {
|
|
11615
11654
|
id: (0, import_types3.extractedEdgeId)(fileNodeId, dbNode.id, import_types24.EdgeType.CONNECTS_TO),
|
|
11616
11655
|
source: fileNodeId,
|
|
@@ -11631,15 +11670,15 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
11631
11670
|
if (allConfigs.length === 1) {
|
|
11632
11671
|
const primary = allConfigs[0];
|
|
11633
11672
|
service.node.dbConnectionTarget = primary.port ? `${primary.host}:${primary.port}` : primary.host;
|
|
11634
|
-
const relPath =
|
|
11673
|
+
const relPath = import_node_path38.default.relative(scanPath, primary.sourceFile);
|
|
11635
11674
|
const cfgId = (0, import_types24.configId)(relPath);
|
|
11636
11675
|
if (!graph.hasNode(cfgId)) {
|
|
11637
11676
|
const cfgNode = {
|
|
11638
11677
|
id: cfgId,
|
|
11639
11678
|
type: import_types24.NodeType.ConfigNode,
|
|
11640
|
-
name:
|
|
11679
|
+
name: import_node_path38.default.basename(primary.sourceFile),
|
|
11641
11680
|
path: relPath,
|
|
11642
|
-
fileType: isConfigFile(
|
|
11681
|
+
fileType: isConfigFile(import_node_path38.default.basename(primary.sourceFile)).fileType || "config"
|
|
11643
11682
|
};
|
|
11644
11683
|
graph.addNode(cfgId, cfgNode);
|
|
11645
11684
|
nodesAdded++;
|
|
@@ -11680,19 +11719,19 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
11680
11719
|
|
|
11681
11720
|
// src/extract/configs.ts
|
|
11682
11721
|
init_cjs_shims();
|
|
11683
|
-
var
|
|
11684
|
-
var
|
|
11722
|
+
var import_node_fs27 = require("fs");
|
|
11723
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
11685
11724
|
var import_types25 = require("@neat.is/types");
|
|
11686
11725
|
async function walkConfigFiles(dir, excludeDirs = []) {
|
|
11687
|
-
const excluded = new Set(excludeDirs.map((d) =>
|
|
11726
|
+
const excluded = new Set(excludeDirs.map((d) => import_node_path39.default.resolve(d)));
|
|
11688
11727
|
const out = [];
|
|
11689
11728
|
async function walk11(current) {
|
|
11690
|
-
const entries = await
|
|
11729
|
+
const entries = await import_node_fs27.promises.readdir(current, { withFileTypes: true });
|
|
11691
11730
|
for (const entry of entries) {
|
|
11692
|
-
const full =
|
|
11731
|
+
const full = import_node_path39.default.join(current, entry.name);
|
|
11693
11732
|
if (entry.isDirectory()) {
|
|
11694
11733
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
11695
|
-
if (excluded.has(
|
|
11734
|
+
if (excluded.has(import_node_path39.default.resolve(full))) continue;
|
|
11696
11735
|
if (await isPythonVenvDir(full)) continue;
|
|
11697
11736
|
await walk11(full);
|
|
11698
11737
|
} else if (entry.isFile() && isConfigFile(entry.name).match) {
|
|
@@ -11709,19 +11748,19 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
11709
11748
|
for (const service of services) {
|
|
11710
11749
|
const configFiles = await walkConfigFiles(service.dir, service.excludeDirs);
|
|
11711
11750
|
for (const file of configFiles) {
|
|
11712
|
-
const relPath =
|
|
11751
|
+
const relPath = import_node_path39.default.relative(scanPath, file);
|
|
11713
11752
|
const node = {
|
|
11714
11753
|
id: (0, import_types25.configId)(relPath),
|
|
11715
11754
|
type: import_types25.NodeType.ConfigNode,
|
|
11716
|
-
name:
|
|
11755
|
+
name: import_node_path39.default.basename(file),
|
|
11717
11756
|
path: relPath,
|
|
11718
|
-
fileType: isConfigFile(
|
|
11757
|
+
fileType: isConfigFile(import_node_path39.default.basename(file)).fileType
|
|
11719
11758
|
};
|
|
11720
11759
|
if (!graph.hasNode(node.id)) {
|
|
11721
11760
|
graph.addNode(node.id, node);
|
|
11722
11761
|
nodesAdded++;
|
|
11723
11762
|
}
|
|
11724
|
-
const relToService = toPosix(
|
|
11763
|
+
const relToService = toPosix(import_node_path39.default.relative(service.dir, file));
|
|
11725
11764
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
11726
11765
|
graph,
|
|
11727
11766
|
service.pkg.name,
|
|
@@ -11737,7 +11776,7 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
11737
11776
|
type: import_types25.EdgeType.CONFIGURED_BY,
|
|
11738
11777
|
provenance: import_types25.Provenance.EXTRACTED,
|
|
11739
11778
|
confidence: (0, import_types25.confidenceForExtracted)("structural"),
|
|
11740
|
-
evidence: { file: relPath.split(
|
|
11779
|
+
evidence: { file: relPath.split(import_node_path39.default.sep).join("/") }
|
|
11741
11780
|
};
|
|
11742
11781
|
if (!graph.hasEdge(edge.id)) {
|
|
11743
11782
|
graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
|
|
@@ -11750,8 +11789,8 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
11750
11789
|
|
|
11751
11790
|
// src/extract/proto.ts
|
|
11752
11791
|
init_cjs_shims();
|
|
11753
|
-
var
|
|
11754
|
-
var
|
|
11792
|
+
var import_node_fs28 = require("fs");
|
|
11793
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
11755
11794
|
var import_types26 = require("@neat.is/types");
|
|
11756
11795
|
var PROTO_EXTENSION = ".proto";
|
|
11757
11796
|
function packageOf(content) {
|
|
@@ -11778,10 +11817,10 @@ function grpcMethodsFromProto(content, fqPackage) {
|
|
|
11778
11817
|
}
|
|
11779
11818
|
const body = content.slice(bodyStart, i - 1);
|
|
11780
11819
|
const rpcRe = /\brpc\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(/g;
|
|
11781
|
-
let
|
|
11782
|
-
while ((
|
|
11783
|
-
const rpcMethod =
|
|
11784
|
-
const line = lineAt(content, bodyStart +
|
|
11820
|
+
let rm2;
|
|
11821
|
+
while ((rm2 = rpcRe.exec(body)) !== null) {
|
|
11822
|
+
const rpcMethod = rm2[1];
|
|
11823
|
+
const line = lineAt(content, bodyStart + rm2.index);
|
|
11785
11824
|
out.push({ rpcService, rpcMethod, line });
|
|
11786
11825
|
}
|
|
11787
11826
|
serviceRe.lastIndex = i;
|
|
@@ -11789,18 +11828,18 @@ function grpcMethodsFromProto(content, fqPackage) {
|
|
|
11789
11828
|
return out;
|
|
11790
11829
|
}
|
|
11791
11830
|
async function walkProtoFiles(dir, excludeDirs = []) {
|
|
11792
|
-
const excluded = new Set(excludeDirs.map((d) =>
|
|
11831
|
+
const excluded = new Set(excludeDirs.map((d) => import_node_path40.default.resolve(d)));
|
|
11793
11832
|
const out = [];
|
|
11794
11833
|
async function walk11(current) {
|
|
11795
|
-
const entries = await
|
|
11834
|
+
const entries = await import_node_fs28.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
11796
11835
|
for (const entry of entries) {
|
|
11797
|
-
const full =
|
|
11836
|
+
const full = import_node_path40.default.join(current, entry.name);
|
|
11798
11837
|
if (entry.isDirectory()) {
|
|
11799
11838
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
11800
|
-
if (excluded.has(
|
|
11839
|
+
if (excluded.has(import_node_path40.default.resolve(full))) continue;
|
|
11801
11840
|
if (await isPythonVenvDir(full)) continue;
|
|
11802
11841
|
await walk11(full);
|
|
11803
|
-
} else if (entry.isFile() &&
|
|
11842
|
+
} else if (entry.isFile() && import_node_path40.default.extname(entry.name) === PROTO_EXTENSION) {
|
|
11804
11843
|
out.push(full);
|
|
11805
11844
|
}
|
|
11806
11845
|
}
|
|
@@ -11815,10 +11854,10 @@ async function addGrpcMethods(graph, services) {
|
|
|
11815
11854
|
const protoPaths = await walkProtoFiles(service.dir, service.excludeDirs);
|
|
11816
11855
|
for (const protoPath of protoPaths) {
|
|
11817
11856
|
if (isTestPath(protoPath)) continue;
|
|
11818
|
-
const relFile = toPosix(
|
|
11857
|
+
const relFile = toPosix(import_node_path40.default.relative(service.dir, protoPath));
|
|
11819
11858
|
let content;
|
|
11820
11859
|
try {
|
|
11821
|
-
content = await
|
|
11860
|
+
content = await import_node_fs28.promises.readFile(protoPath, "utf8");
|
|
11822
11861
|
} catch (err) {
|
|
11823
11862
|
recordExtractionError("proto extraction", protoPath, err);
|
|
11824
11863
|
continue;
|
|
@@ -11877,7 +11916,7 @@ var import_types46 = require("@neat.is/types");
|
|
|
11877
11916
|
|
|
11878
11917
|
// src/extract/calls/http.ts
|
|
11879
11918
|
init_cjs_shims();
|
|
11880
|
-
var
|
|
11919
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
11881
11920
|
var import_tree_sitter6 = __toESM(require("tree-sitter"), 1);
|
|
11882
11921
|
var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
|
|
11883
11922
|
var import_tree_sitter_typescript2 = __toESM(require("tree-sitter-typescript"), 1);
|
|
@@ -11960,7 +11999,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
11960
11999
|
const seen = /* @__PURE__ */ new Set();
|
|
11961
12000
|
for (const file of files) {
|
|
11962
12001
|
if (isTestPath(file.path)) continue;
|
|
11963
|
-
const parser = parserForExt(
|
|
12002
|
+
const parser = parserForExt(import_node_path41.default.extname(file.path), parserCache);
|
|
11964
12003
|
let sites;
|
|
11965
12004
|
try {
|
|
11966
12005
|
sites = callsFromSource(file.content, parser, knownHosts);
|
|
@@ -11969,7 +12008,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
11969
12008
|
continue;
|
|
11970
12009
|
}
|
|
11971
12010
|
if (sites.length === 0) continue;
|
|
11972
|
-
const relFile = toPosix(
|
|
12011
|
+
const relFile = toPosix(import_node_path41.default.relative(service.dir, file.path));
|
|
11973
12012
|
for (const site of sites) {
|
|
11974
12013
|
const targetId = hostToNodeId.get(site.host);
|
|
11975
12014
|
if (!targetId || targetId === service.node.id) continue;
|
|
@@ -12023,7 +12062,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
12023
12062
|
|
|
12024
12063
|
// src/extract/calls/route-match.ts
|
|
12025
12064
|
init_cjs_shims();
|
|
12026
|
-
var
|
|
12065
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
12027
12066
|
var import_tree_sitter7 = __toESM(require("tree-sitter"), 1);
|
|
12028
12067
|
var import_tree_sitter_javascript5 = __toESM(require("tree-sitter-javascript"), 1);
|
|
12029
12068
|
var import_types28 = require("@neat.is/types");
|
|
@@ -12301,7 +12340,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
12301
12340
|
const seen = /* @__PURE__ */ new Set();
|
|
12302
12341
|
for (const file of files) {
|
|
12303
12342
|
if (isTestPath(file.path)) continue;
|
|
12304
|
-
if (!JS_CLIENT_EXTENSIONS.has(
|
|
12343
|
+
if (!JS_CLIENT_EXTENSIONS.has(import_node_path42.default.extname(file.path))) continue;
|
|
12305
12344
|
let sites;
|
|
12306
12345
|
try {
|
|
12307
12346
|
sites = clientCallSitesFromSource(file.content, jsParser, knownHosts);
|
|
@@ -12310,7 +12349,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
12310
12349
|
continue;
|
|
12311
12350
|
}
|
|
12312
12351
|
if (sites.length === 0) continue;
|
|
12313
|
-
const relFile = toPosix(
|
|
12352
|
+
const relFile = toPosix(import_node_path42.default.relative(service.dir, file.path));
|
|
12314
12353
|
for (const site of sites) {
|
|
12315
12354
|
if (site.host === null) {
|
|
12316
12355
|
const dedupKey2 = `${relFile}|unresolved-host`;
|
|
@@ -12403,7 +12442,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
12403
12442
|
|
|
12404
12443
|
// src/extract/calls/kafka.ts
|
|
12405
12444
|
init_cjs_shims();
|
|
12406
|
-
var
|
|
12445
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
12407
12446
|
var import_types29 = require("@neat.is/types");
|
|
12408
12447
|
var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
12409
12448
|
var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
|
|
@@ -12491,13 +12530,13 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
12491
12530
|
// call sites — verified-call-site tier (ADR-066).
|
|
12492
12531
|
confidenceKind: "verified-call-site",
|
|
12493
12532
|
evidence: {
|
|
12494
|
-
file:
|
|
12533
|
+
file: import_node_path43.default.relative(serviceDir, file.path),
|
|
12495
12534
|
line,
|
|
12496
12535
|
snippet: snippet(file.content, line)
|
|
12497
12536
|
}
|
|
12498
12537
|
});
|
|
12499
12538
|
};
|
|
12500
|
-
if (
|
|
12539
|
+
if (import_node_path43.default.extname(file.path) === ".go") {
|
|
12501
12540
|
goSaramaEndpoints(file.content, make);
|
|
12502
12541
|
} else {
|
|
12503
12542
|
for (const { topic } of findAll(PRODUCER_TOPIC_RE, file.content)) make(topic, "PUBLISHES_TO");
|
|
@@ -12508,7 +12547,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
12508
12547
|
|
|
12509
12548
|
// src/extract/calls/redis.ts
|
|
12510
12549
|
init_cjs_shims();
|
|
12511
|
-
var
|
|
12550
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
12512
12551
|
var import_types30 = require("@neat.is/types");
|
|
12513
12552
|
var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
|
|
12514
12553
|
function redisEndpointsFromFile(file, serviceDir) {
|
|
@@ -12531,7 +12570,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
12531
12570
|
// support tier (ADR-066).
|
|
12532
12571
|
confidenceKind: "url-with-structural-support",
|
|
12533
12572
|
evidence: {
|
|
12534
|
-
file:
|
|
12573
|
+
file: import_node_path44.default.relative(serviceDir, file.path),
|
|
12535
12574
|
line,
|
|
12536
12575
|
snippet: snippet(file.content, line)
|
|
12537
12576
|
}
|
|
@@ -12542,7 +12581,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
12542
12581
|
|
|
12543
12582
|
// src/extract/calls/aws.ts
|
|
12544
12583
|
init_cjs_shims();
|
|
12545
|
-
var
|
|
12584
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
12546
12585
|
var import_types31 = require("@neat.is/types");
|
|
12547
12586
|
var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
12548
12587
|
var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
@@ -12576,7 +12615,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
12576
12615
|
// (ADR-066).
|
|
12577
12616
|
confidenceKind: "verified-call-site",
|
|
12578
12617
|
evidence: {
|
|
12579
|
-
file:
|
|
12618
|
+
file: import_node_path45.default.relative(serviceDir, file.path),
|
|
12580
12619
|
line,
|
|
12581
12620
|
snippet: snippet(file.content, line)
|
|
12582
12621
|
}
|
|
@@ -12601,7 +12640,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
12601
12640
|
|
|
12602
12641
|
// src/extract/calls/grpc.ts
|
|
12603
12642
|
init_cjs_shims();
|
|
12604
|
-
var
|
|
12643
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
12605
12644
|
var import_types32 = require("@neat.is/types");
|
|
12606
12645
|
var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
|
|
12607
12646
|
var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
|
|
@@ -12660,7 +12699,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
12660
12699
|
// tier (ADR-066).
|
|
12661
12700
|
confidenceKind: "verified-call-site",
|
|
12662
12701
|
evidence: {
|
|
12663
|
-
file:
|
|
12702
|
+
file: import_node_path46.default.relative(serviceDir, file.path),
|
|
12664
12703
|
line,
|
|
12665
12704
|
snippet: snippet(file.content, line)
|
|
12666
12705
|
}
|
|
@@ -12671,7 +12710,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
12671
12710
|
|
|
12672
12711
|
// src/extract/calls/supabase.ts
|
|
12673
12712
|
init_cjs_shims();
|
|
12674
|
-
var
|
|
12713
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
12675
12714
|
var import_types33 = require("@neat.is/types");
|
|
12676
12715
|
var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
|
|
12677
12716
|
var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
|
|
@@ -12730,7 +12769,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
12730
12769
|
// tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
|
|
12731
12770
|
confidenceKind: "verified-call-site",
|
|
12732
12771
|
evidence: {
|
|
12733
|
-
file:
|
|
12772
|
+
file: import_node_path47.default.relative(serviceDir, file.path),
|
|
12734
12773
|
line,
|
|
12735
12774
|
snippet: snippet(file.content, line)
|
|
12736
12775
|
}
|
|
@@ -12757,7 +12796,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
12757
12796
|
edgeType: "CALLS",
|
|
12758
12797
|
confidenceKind: "verified-call-site",
|
|
12759
12798
|
evidence: {
|
|
12760
|
-
file:
|
|
12799
|
+
file: import_node_path47.default.relative(serviceDir, file.path),
|
|
12761
12800
|
line,
|
|
12762
12801
|
snippet: snippet(file.content, line)
|
|
12763
12802
|
}
|
|
@@ -12769,7 +12808,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
12769
12808
|
|
|
12770
12809
|
// src/extract/calls/firestore.ts
|
|
12771
12810
|
init_cjs_shims();
|
|
12772
|
-
var
|
|
12811
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
12773
12812
|
var import_tree_sitter8 = __toESM(require("tree-sitter"), 1);
|
|
12774
12813
|
var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"), 1);
|
|
12775
12814
|
var import_types34 = require("@neat.is/types");
|
|
@@ -12942,7 +12981,7 @@ function firestoreEndpointsFromFile(file, serviceDir, imported) {
|
|
|
12942
12981
|
if (!hasClient && !hasAdmin && importedNames.size === 0) return [];
|
|
12943
12982
|
const ownSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
|
|
12944
12983
|
const fileSdk = !hasClient && !hasAdmin ? imported?.sdk ?? null : ownSdk;
|
|
12945
|
-
const tree = parseSource3(parserForExt2(
|
|
12984
|
+
const tree = parseSource3(parserForExt2(import_node_path48.default.extname(file.path)), file.content);
|
|
12946
12985
|
const clientVars = firestoreClientVars(tree.rootNode);
|
|
12947
12986
|
for (const n of importedNames) clientVars.add(n);
|
|
12948
12987
|
const collLine = /* @__PURE__ */ new Map();
|
|
@@ -13047,7 +13086,7 @@ function firestoreEndpointsFromFile(file, serviceDir, imported) {
|
|
|
13047
13086
|
...columnSet.size > 0 ? { columns: [...columnSet] } : {},
|
|
13048
13087
|
...sdkWrites ? { sdkWrites } : {},
|
|
13049
13088
|
evidence: {
|
|
13050
|
-
file:
|
|
13089
|
+
file: import_node_path48.default.relative(serviceDir, file.path),
|
|
13051
13090
|
line,
|
|
13052
13091
|
snippet: snippet(file.content, line)
|
|
13053
13092
|
}
|
|
@@ -13107,7 +13146,7 @@ function sdkOf(content) {
|
|
|
13107
13146
|
return hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
|
|
13108
13147
|
}
|
|
13109
13148
|
function parseOrNull(file) {
|
|
13110
|
-
const ext =
|
|
13149
|
+
const ext = import_node_path48.default.extname(file.path);
|
|
13111
13150
|
if (!(ext in GRAMMAR_BY_EXT)) return null;
|
|
13112
13151
|
try {
|
|
13113
13152
|
return parseSource3(parserForExt2(ext), file.content).rootNode;
|
|
@@ -13123,7 +13162,7 @@ async function firestoreCrossFileEndpoints(files, serviceDir) {
|
|
|
13123
13162
|
if (!root) continue;
|
|
13124
13163
|
const names = exportedFirestoreClients(root);
|
|
13125
13164
|
if (names.size === 0) continue;
|
|
13126
|
-
registry.set(toPosix(
|
|
13165
|
+
registry.set(toPosix(import_node_path48.default.relative(serviceDir, f.path)), { names, sdk: sdkOf(f.content) });
|
|
13127
13166
|
}
|
|
13128
13167
|
if (registry.size === 0) return [];
|
|
13129
13168
|
const out = [];
|
|
@@ -13136,7 +13175,7 @@ async function firestoreCrossFileEndpoints(files, serviceDir) {
|
|
|
13136
13175
|
const names = /* @__PURE__ */ new Set();
|
|
13137
13176
|
let sdk = null;
|
|
13138
13177
|
for (const b of bindings) {
|
|
13139
|
-
const rel = await resolveJsImport(b.specifier,
|
|
13178
|
+
const rel = await resolveJsImport(b.specifier, import_node_path48.default.dirname(f.path), serviceDir, null);
|
|
13140
13179
|
if (!rel) continue;
|
|
13141
13180
|
const entry = registry.get(rel);
|
|
13142
13181
|
if (!entry || !entry.names.has(b.imported)) continue;
|
|
@@ -13151,7 +13190,7 @@ async function firestoreCrossFileEndpoints(files, serviceDir) {
|
|
|
13151
13190
|
|
|
13152
13191
|
// src/extract/calls/mongoose.ts
|
|
13153
13192
|
init_cjs_shims();
|
|
13154
|
-
var
|
|
13193
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
13155
13194
|
var import_types35 = require("@neat.is/types");
|
|
13156
13195
|
var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
|
|
13157
13196
|
var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
|
|
@@ -13302,7 +13341,7 @@ function endpoint(r, file, serviceDir, matchText) {
|
|
|
13302
13341
|
kind: r.kind,
|
|
13303
13342
|
edgeType: "CALLS",
|
|
13304
13343
|
confidenceKind: "verified-call-site",
|
|
13305
|
-
evidence: { file:
|
|
13344
|
+
evidence: { file: import_node_path49.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
|
|
13306
13345
|
};
|
|
13307
13346
|
}
|
|
13308
13347
|
function mongooseEndpointsFromFile(file, serviceDir) {
|
|
@@ -13392,7 +13431,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
|
13392
13431
|
const registry = /* @__PURE__ */ new Map();
|
|
13393
13432
|
for (const f of mongooseFiles) {
|
|
13394
13433
|
const fx = fileExportsOf(f.content, pluralizeOn);
|
|
13395
|
-
if (fx) registry.set(toPosix(
|
|
13434
|
+
if (fx) registry.set(toPosix(import_node_path49.default.relative(serviceDir, f.path)), fx);
|
|
13396
13435
|
}
|
|
13397
13436
|
if (registry.size === 0) return [];
|
|
13398
13437
|
const out = [];
|
|
@@ -13403,7 +13442,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
|
13403
13442
|
const directColl = /* @__PURE__ */ new Map();
|
|
13404
13443
|
const nsExports = /* @__PURE__ */ new Map();
|
|
13405
13444
|
for (const b of bindings) {
|
|
13406
|
-
const resolvedRel = await resolveJsImport(b.specifier,
|
|
13445
|
+
const resolvedRel = await resolveJsImport(b.specifier, import_node_path49.default.dirname(f.path), serviceDir, null);
|
|
13407
13446
|
if (!resolvedRel) continue;
|
|
13408
13447
|
const fx = registry.get(resolvedRel);
|
|
13409
13448
|
if (!fx) continue;
|
|
@@ -13447,7 +13486,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
|
13447
13486
|
|
|
13448
13487
|
// src/extract/calls/sqlalchemy.ts
|
|
13449
13488
|
init_cjs_shims();
|
|
13450
|
-
var
|
|
13489
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
13451
13490
|
var import_tree_sitter9 = __toESM(require("tree-sitter"), 1);
|
|
13452
13491
|
var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
|
|
13453
13492
|
var import_types36 = require("@neat.is/types");
|
|
@@ -13586,7 +13625,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
|
|
|
13586
13625
|
childTable,
|
|
13587
13626
|
parentTable,
|
|
13588
13627
|
evidence: {
|
|
13589
|
-
file:
|
|
13628
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
13590
13629
|
line,
|
|
13591
13630
|
snippet: snippet(file.content, line)
|
|
13592
13631
|
}
|
|
@@ -13611,7 +13650,7 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
|
|
|
13611
13650
|
confidenceKind: "verified-call-site",
|
|
13612
13651
|
...columns && columns.length > 0 ? { columns } : {},
|
|
13613
13652
|
evidence: {
|
|
13614
|
-
file:
|
|
13653
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
13615
13654
|
line,
|
|
13616
13655
|
snippet: snippet(file.content, line)
|
|
13617
13656
|
}
|
|
@@ -13718,7 +13757,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
|
|
|
13718
13757
|
edgeType: "CALLS",
|
|
13719
13758
|
confidenceKind: "verified-call-site",
|
|
13720
13759
|
evidence: {
|
|
13721
|
-
file:
|
|
13760
|
+
file: import_node_path50.default.relative(serviceDir, file.path),
|
|
13722
13761
|
line,
|
|
13723
13762
|
snippet: snippet(file.content, line)
|
|
13724
13763
|
}
|
|
@@ -13730,7 +13769,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
|
|
|
13730
13769
|
|
|
13731
13770
|
// src/extract/calls/socket.ts
|
|
13732
13771
|
init_cjs_shims();
|
|
13733
|
-
var
|
|
13772
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
13734
13773
|
var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
|
|
13735
13774
|
var import_tree_sitter_python6 = __toESM(require("tree-sitter-python"), 1);
|
|
13736
13775
|
var import_types37 = require("@neat.is/types");
|
|
@@ -13806,7 +13845,7 @@ function socketEndpointsFromFile(file, serviceDir) {
|
|
|
13806
13845
|
// A verified `socket.socket(...)` + `.connect((host, port))` call site.
|
|
13807
13846
|
confidenceKind: "verified-call-site",
|
|
13808
13847
|
evidence: {
|
|
13809
|
-
file:
|
|
13848
|
+
file: import_node_path51.default.relative(serviceDir, file.path),
|
|
13810
13849
|
line,
|
|
13811
13850
|
snippet: snippet(file.content, line)
|
|
13812
13851
|
}
|
|
@@ -13817,7 +13856,7 @@ function socketEndpointsFromFile(file, serviceDir) {
|
|
|
13817
13856
|
|
|
13818
13857
|
// src/extract/calls/django-orm.ts
|
|
13819
13858
|
init_cjs_shims();
|
|
13820
|
-
var
|
|
13859
|
+
var import_node_path52 = __toESM(require("path"), 1);
|
|
13821
13860
|
var import_tree_sitter11 = __toESM(require("tree-sitter"), 1);
|
|
13822
13861
|
var import_tree_sitter_python7 = __toESM(require("tree-sitter-python"), 1);
|
|
13823
13862
|
var import_types38 = require("@neat.is/types");
|
|
@@ -13887,7 +13926,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
13887
13926
|
const tree = parseSource8(makePyParser5(), file.content);
|
|
13888
13927
|
const out = [];
|
|
13889
13928
|
const seen = /* @__PURE__ */ new Set();
|
|
13890
|
-
const defaultAppLabel =
|
|
13929
|
+
const defaultAppLabel = import_node_path52.default.basename(import_node_path52.default.dirname(file.path));
|
|
13891
13930
|
walk5(tree.rootNode, (node) => {
|
|
13892
13931
|
if (node.type !== "class_definition") return;
|
|
13893
13932
|
if (!extendsDjangoModel(node)) return;
|
|
@@ -13905,7 +13944,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
13905
13944
|
kind: "sql-table",
|
|
13906
13945
|
edgeType: "CALLS",
|
|
13907
13946
|
confidenceKind: "verified-call-site",
|
|
13908
|
-
evidence: { file:
|
|
13947
|
+
evidence: { file: import_node_path52.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
|
|
13909
13948
|
});
|
|
13910
13949
|
});
|
|
13911
13950
|
return out;
|
|
@@ -13913,7 +13952,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
13913
13952
|
|
|
13914
13953
|
// src/extract/calls/drizzle.ts
|
|
13915
13954
|
init_cjs_shims();
|
|
13916
|
-
var
|
|
13955
|
+
var import_node_path53 = __toESM(require("path"), 1);
|
|
13917
13956
|
var import_tree_sitter12 = __toESM(require("tree-sitter"), 1);
|
|
13918
13957
|
var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"), 1);
|
|
13919
13958
|
var import_types39 = require("@neat.is/types");
|
|
@@ -13991,7 +14030,7 @@ function columnsFromObject(obj) {
|
|
|
13991
14030
|
}
|
|
13992
14031
|
function drizzleEndpointsFromFile(file, serviceDir) {
|
|
13993
14032
|
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
13994
|
-
const tree = parseSource3(parserForExt3(
|
|
14033
|
+
const tree = parseSource3(parserForExt3(import_node_path53.default.extname(file.path)), file.content);
|
|
13995
14034
|
const out = [];
|
|
13996
14035
|
const seen = /* @__PURE__ */ new Set();
|
|
13997
14036
|
const walk11 = (node) => {
|
|
@@ -14014,7 +14053,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
|
|
|
14014
14053
|
confidenceKind: "structural",
|
|
14015
14054
|
columns,
|
|
14016
14055
|
evidence: {
|
|
14017
|
-
file:
|
|
14056
|
+
file: import_node_path53.default.relative(serviceDir, file.path),
|
|
14018
14057
|
line,
|
|
14019
14058
|
snippet: snippet(file.content, line)
|
|
14020
14059
|
}
|
|
@@ -14080,7 +14119,7 @@ function referencesTargetVar(call) {
|
|
|
14080
14119
|
}
|
|
14081
14120
|
function drizzleForeignKeys(file, serviceDir) {
|
|
14082
14121
|
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
14083
|
-
const tree = parseSource3(parserForExt3(
|
|
14122
|
+
const tree = parseSource3(parserForExt3(import_node_path53.default.extname(file.path)), file.content);
|
|
14084
14123
|
const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
|
|
14085
14124
|
const out = [];
|
|
14086
14125
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -14099,7 +14138,7 @@ function drizzleForeignKeys(file, serviceDir) {
|
|
|
14099
14138
|
childTable: table.tableName,
|
|
14100
14139
|
parentTable,
|
|
14101
14140
|
evidence: {
|
|
14102
|
-
file:
|
|
14141
|
+
file: import_node_path53.default.relative(serviceDir, file.path),
|
|
14103
14142
|
line,
|
|
14104
14143
|
snippet: snippet(file.content, line)
|
|
14105
14144
|
}
|
|
@@ -14116,7 +14155,7 @@ function drizzleForeignKeys(file, serviceDir) {
|
|
|
14116
14155
|
|
|
14117
14156
|
// src/extract/calls/prisma.ts
|
|
14118
14157
|
init_cjs_shims();
|
|
14119
|
-
var
|
|
14158
|
+
var import_node_path54 = __toESM(require("path"), 1);
|
|
14120
14159
|
var import_types40 = require("@neat.is/types");
|
|
14121
14160
|
var SCALAR_TYPES = /* @__PURE__ */ new Set([
|
|
14122
14161
|
"Int",
|
|
@@ -14172,7 +14211,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
|
|
|
14172
14211
|
confidenceKind: "structural",
|
|
14173
14212
|
columns: b.columns,
|
|
14174
14213
|
evidence: {
|
|
14175
|
-
file:
|
|
14214
|
+
file: import_node_path54.default.relative(serviceDir, file.path),
|
|
14176
14215
|
line: b.startLine,
|
|
14177
14216
|
snippet: snippet(content, b.startLine)
|
|
14178
14217
|
}
|
|
@@ -14233,7 +14272,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
|
|
|
14233
14272
|
}
|
|
14234
14273
|
async function prismaColumnEndpoints(serviceDir) {
|
|
14235
14274
|
const schemaPath = await findFirst(serviceDir, [
|
|
14236
|
-
|
|
14275
|
+
import_node_path54.default.join("prisma", "schema.prisma"),
|
|
14237
14276
|
"schema.prisma"
|
|
14238
14277
|
]);
|
|
14239
14278
|
if (!schemaPath) return [];
|
|
@@ -14308,7 +14347,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
|
|
|
14308
14347
|
childTable: current.table,
|
|
14309
14348
|
parentTable,
|
|
14310
14349
|
evidence: {
|
|
14311
|
-
file:
|
|
14350
|
+
file: import_node_path54.default.relative(serviceDir, file.path),
|
|
14312
14351
|
line: lineNo,
|
|
14313
14352
|
snippet: snippet(content, lineNo)
|
|
14314
14353
|
}
|
|
@@ -14323,7 +14362,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
|
|
|
14323
14362
|
}
|
|
14324
14363
|
async function prismaForeignKeys(serviceDir) {
|
|
14325
14364
|
const schemaPath = await findFirst(serviceDir, [
|
|
14326
|
-
|
|
14365
|
+
import_node_path54.default.join("prisma", "schema.prisma"),
|
|
14327
14366
|
"schema.prisma"
|
|
14328
14367
|
]);
|
|
14329
14368
|
if (!schemaPath) return [];
|
|
@@ -14334,7 +14373,7 @@ async function prismaForeignKeys(serviceDir) {
|
|
|
14334
14373
|
|
|
14335
14374
|
// src/extract/calls/activerecord.ts
|
|
14336
14375
|
init_cjs_shims();
|
|
14337
|
-
var
|
|
14376
|
+
var import_node_path55 = __toESM(require("path"), 1);
|
|
14338
14377
|
var import_tree_sitter13 = __toESM(require("tree-sitter"), 1);
|
|
14339
14378
|
var import_tree_sitter_ruby3 = __toESM(require("tree-sitter-ruby"), 1);
|
|
14340
14379
|
var import_types41 = require("@neat.is/types");
|
|
@@ -14542,7 +14581,7 @@ function railsSchemaEndpointsFromFile(file, serviceDir) {
|
|
|
14542
14581
|
confidenceKind: "structural",
|
|
14543
14582
|
...table.columns.length > 0 ? { columns: table.columns } : {},
|
|
14544
14583
|
evidence: {
|
|
14545
|
-
file:
|
|
14584
|
+
file: import_node_path55.default.relative(serviceDir, file.path),
|
|
14546
14585
|
line: table.line,
|
|
14547
14586
|
snippet: snippet(file.content, table.line)
|
|
14548
14587
|
}
|
|
@@ -14564,7 +14603,7 @@ function railsSchemaForeignKeys(file, serviceDir) {
|
|
|
14564
14603
|
childTable,
|
|
14565
14604
|
parentTable,
|
|
14566
14605
|
evidence: {
|
|
14567
|
-
file:
|
|
14606
|
+
file: import_node_path55.default.relative(serviceDir, file.path),
|
|
14568
14607
|
line,
|
|
14569
14608
|
snippet: snippet(file.content, line)
|
|
14570
14609
|
}
|
|
@@ -14647,7 +14686,7 @@ function railsModelEndpointsFromFile(file, serviceDir) {
|
|
|
14647
14686
|
edgeType: "CALLS",
|
|
14648
14687
|
confidenceKind: "verified-call-site",
|
|
14649
14688
|
evidence: {
|
|
14650
|
-
file:
|
|
14689
|
+
file: import_node_path55.default.relative(serviceDir, file.path),
|
|
14651
14690
|
line,
|
|
14652
14691
|
snippet: snippet(file.content, line)
|
|
14653
14692
|
}
|
|
@@ -14684,7 +14723,7 @@ function railsModelForeignKeys(file, serviceDir) {
|
|
|
14684
14723
|
childTable,
|
|
14685
14724
|
parentTable,
|
|
14686
14725
|
evidence: {
|
|
14687
|
-
file:
|
|
14726
|
+
file: import_node_path55.default.relative(serviceDir, file.path),
|
|
14688
14727
|
line,
|
|
14689
14728
|
snippet: snippet(file.content, line)
|
|
14690
14729
|
}
|
|
@@ -14696,7 +14735,7 @@ function railsModelForeignKeys(file, serviceDir) {
|
|
|
14696
14735
|
|
|
14697
14736
|
// src/extract/calls/eloquent.ts
|
|
14698
14737
|
init_cjs_shims();
|
|
14699
|
-
var
|
|
14738
|
+
var import_node_path56 = __toESM(require("path"), 1);
|
|
14700
14739
|
var import_tree_sitter14 = __toESM(require("tree-sitter"), 1);
|
|
14701
14740
|
var import_tree_sitter_php3 = __toESM(require("tree-sitter-php"), 1);
|
|
14702
14741
|
var import_types42 = require("@neat.is/types");
|
|
@@ -14953,7 +14992,7 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
|
|
|
14953
14992
|
confidenceKind: "structural",
|
|
14954
14993
|
...columns.length > 0 ? { columns } : {},
|
|
14955
14994
|
evidence: {
|
|
14956
|
-
file:
|
|
14995
|
+
file: import_node_path56.default.relative(serviceDir, file.path),
|
|
14957
14996
|
line: bp.line,
|
|
14958
14997
|
snippet: snippet(file.content, bp.line)
|
|
14959
14998
|
}
|
|
@@ -14975,7 +15014,7 @@ function laravelMigrationForeignKeys(file, serviceDir) {
|
|
|
14975
15014
|
childTable,
|
|
14976
15015
|
parentTable,
|
|
14977
15016
|
evidence: {
|
|
14978
|
-
file:
|
|
15017
|
+
file: import_node_path56.default.relative(serviceDir, file.path),
|
|
14979
15018
|
line,
|
|
14980
15019
|
snippet: snippet(file.content, line)
|
|
14981
15020
|
}
|
|
@@ -15095,7 +15134,7 @@ function laravelModelEndpointsFromFile(file, serviceDir) {
|
|
|
15095
15134
|
edgeType: "CALLS",
|
|
15096
15135
|
confidenceKind: "verified-call-site",
|
|
15097
15136
|
evidence: {
|
|
15098
|
-
file:
|
|
15137
|
+
file: import_node_path56.default.relative(serviceDir, file.path),
|
|
15099
15138
|
line,
|
|
15100
15139
|
snippet: snippet(file.content, line)
|
|
15101
15140
|
}
|
|
@@ -15131,7 +15170,7 @@ function laravelModelForeignKeys(file, serviceDir) {
|
|
|
15131
15170
|
childTable,
|
|
15132
15171
|
parentTable,
|
|
15133
15172
|
evidence: {
|
|
15134
|
-
file:
|
|
15173
|
+
file: import_node_path56.default.relative(serviceDir, file.path),
|
|
15135
15174
|
line,
|
|
15136
15175
|
snippet: snippet(file.content, line)
|
|
15137
15176
|
}
|
|
@@ -15143,7 +15182,7 @@ function laravelModelForeignKeys(file, serviceDir) {
|
|
|
15143
15182
|
|
|
15144
15183
|
// src/extract/calls/go.ts
|
|
15145
15184
|
init_cjs_shims();
|
|
15146
|
-
var
|
|
15185
|
+
var import_node_path59 = __toESM(require("path"), 1);
|
|
15147
15186
|
var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
|
|
15148
15187
|
var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
|
|
15149
15188
|
var import_types43 = require("@neat.is/types");
|
|
@@ -15218,7 +15257,7 @@ function firstStringLiteralArg(argsNode) {
|
|
|
15218
15257
|
return null;
|
|
15219
15258
|
}
|
|
15220
15259
|
function goSqlEndpointsFromFile(file, serviceDir) {
|
|
15221
|
-
if (
|
|
15260
|
+
if (import_node_path59.default.extname(file.path) !== ".go") return [];
|
|
15222
15261
|
if (!file.content.includes(DATABASE_SQL_IMPORT) && !file.content.includes(SQLX_IMPORT)) return [];
|
|
15223
15262
|
const tree = parseSource11(makeGoParser3(), file.content);
|
|
15224
15263
|
const importsDatabaseSql = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([DATABASE_SQL_IMPORT]));
|
|
@@ -15247,7 +15286,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
15247
15286
|
confidenceKind: "verified-call-site",
|
|
15248
15287
|
...columns.length > 0 ? { columns } : {},
|
|
15249
15288
|
evidence: {
|
|
15250
|
-
file: toPosix(
|
|
15289
|
+
file: toPosix(import_node_path59.default.relative(serviceDir, file.path)),
|
|
15251
15290
|
line,
|
|
15252
15291
|
snippet: snippet(file.content, line)
|
|
15253
15292
|
}
|
|
@@ -15258,7 +15297,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
15258
15297
|
|
|
15259
15298
|
// src/extract/calls/gorm.ts
|
|
15260
15299
|
init_cjs_shims();
|
|
15261
|
-
var
|
|
15300
|
+
var import_node_path60 = __toESM(require("path"), 1);
|
|
15262
15301
|
var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
|
|
15263
15302
|
var import_tree_sitter_go5 = __toESM(require("tree-sitter-go"), 1);
|
|
15264
15303
|
var import_types44 = require("@neat.is/types");
|
|
@@ -15691,7 +15730,7 @@ function collectColumns(struct, structs, seen, prefix, out, emitted) {
|
|
|
15691
15730
|
seen.delete(struct.name);
|
|
15692
15731
|
}
|
|
15693
15732
|
function gormEndpointsFromFile(file, serviceDir) {
|
|
15694
|
-
if (
|
|
15733
|
+
if (import_node_path60.default.extname(file.path) !== ".go") return [];
|
|
15695
15734
|
if (!GORM_IMPORT_RE.test(file.content)) return [];
|
|
15696
15735
|
const tree = parseSource12(makeGoParser4(), file.content);
|
|
15697
15736
|
const { structs, models, tableFor } = analyze(tree);
|
|
@@ -15713,7 +15752,7 @@ function gormEndpointsFromFile(file, serviceDir) {
|
|
|
15713
15752
|
confidenceKind: "structural",
|
|
15714
15753
|
...columns.length > 0 ? { columns } : {},
|
|
15715
15754
|
evidence: {
|
|
15716
|
-
file: toPosix(
|
|
15755
|
+
file: toPosix(import_node_path60.default.relative(serviceDir, file.path)),
|
|
15717
15756
|
line: struct.line,
|
|
15718
15757
|
snippet: snippet(file.content, struct.line)
|
|
15719
15758
|
}
|
|
@@ -15722,7 +15761,7 @@ function gormEndpointsFromFile(file, serviceDir) {
|
|
|
15722
15761
|
return out;
|
|
15723
15762
|
}
|
|
15724
15763
|
function gormForeignKeys(file, serviceDir) {
|
|
15725
|
-
if (
|
|
15764
|
+
if (import_node_path60.default.extname(file.path) !== ".go") return [];
|
|
15726
15765
|
if (!GORM_IMPORT_RE.test(file.content)) return [];
|
|
15727
15766
|
const tree = parseSource12(makeGoParser4(), file.content);
|
|
15728
15767
|
const { structs, models, tableFor } = analyze(tree);
|
|
@@ -15737,7 +15776,7 @@ function gormForeignKeys(file, serviceDir) {
|
|
|
15737
15776
|
childTable,
|
|
15738
15777
|
parentTable,
|
|
15739
15778
|
evidence: {
|
|
15740
|
-
file: toPosix(
|
|
15779
|
+
file: toPosix(import_node_path60.default.relative(serviceDir, file.path)),
|
|
15741
15780
|
line,
|
|
15742
15781
|
snippet: snippet(file.content, line)
|
|
15743
15782
|
}
|
|
@@ -15774,7 +15813,7 @@ function gormForeignKeys(file, serviceDir) {
|
|
|
15774
15813
|
|
|
15775
15814
|
// src/extract/calls/efcore.ts
|
|
15776
15815
|
init_cjs_shims();
|
|
15777
|
-
var
|
|
15816
|
+
var import_node_path61 = __toESM(require("path"), 1);
|
|
15778
15817
|
var import_tree_sitter17 = __toESM(require("tree-sitter"), 1);
|
|
15779
15818
|
var import_tree_sitter_c_sharp2 = __toESM(require("tree-sitter-c-sharp"), 1);
|
|
15780
15819
|
var import_types45 = require("@neat.is/types");
|
|
@@ -15853,7 +15892,7 @@ function tableFromToTable(call) {
|
|
|
15853
15892
|
return value ? csStringLiteral(value) : null;
|
|
15854
15893
|
}
|
|
15855
15894
|
function efcoreEndpointsFromFile(file, serviceDir) {
|
|
15856
|
-
if (
|
|
15895
|
+
if (import_node_path61.default.extname(file.path) !== ".cs") return [];
|
|
15857
15896
|
if (!EFCORE_GATE.test(file.content)) return [];
|
|
15858
15897
|
const tree = parseSource13(makeCsParser(), file.content);
|
|
15859
15898
|
const out = [];
|
|
@@ -15868,7 +15907,7 @@ function efcoreEndpointsFromFile(file, serviceDir) {
|
|
|
15868
15907
|
edgeType: "CALLS",
|
|
15869
15908
|
confidenceKind: "structural",
|
|
15870
15909
|
evidence: {
|
|
15871
|
-
file: toPosix(
|
|
15910
|
+
file: toPosix(import_node_path61.default.relative(serviceDir, file.path)),
|
|
15872
15911
|
line,
|
|
15873
15912
|
snippet: snippet(file.content, line)
|
|
15874
15913
|
}
|
|
@@ -16123,7 +16162,7 @@ init_cjs_shims();
|
|
|
16123
16162
|
|
|
16124
16163
|
// src/extract/infra/docker-compose.ts
|
|
16125
16164
|
init_cjs_shims();
|
|
16126
|
-
var
|
|
16165
|
+
var import_node_path62 = __toESM(require("path"), 1);
|
|
16127
16166
|
var import_types49 = require("@neat.is/types");
|
|
16128
16167
|
|
|
16129
16168
|
// src/extract/infra/shared.ts
|
|
@@ -16194,7 +16233,7 @@ function dependsOnList(value) {
|
|
|
16194
16233
|
}
|
|
16195
16234
|
function serviceNameToServiceNode(name, services) {
|
|
16196
16235
|
for (const s of services) {
|
|
16197
|
-
if (s.node.name === name ||
|
|
16236
|
+
if (s.node.name === name || import_node_path62.default.basename(s.dir) === name) return s.node.id;
|
|
16198
16237
|
}
|
|
16199
16238
|
return null;
|
|
16200
16239
|
}
|
|
@@ -16203,7 +16242,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
16203
16242
|
let edgesAdded = 0;
|
|
16204
16243
|
let composePath = null;
|
|
16205
16244
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
16206
|
-
const abs =
|
|
16245
|
+
const abs = import_node_path62.default.join(scanPath, name);
|
|
16207
16246
|
if (await exists2(abs)) {
|
|
16208
16247
|
composePath = abs;
|
|
16209
16248
|
break;
|
|
@@ -16216,13 +16255,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
16216
16255
|
} catch (err) {
|
|
16217
16256
|
recordExtractionError(
|
|
16218
16257
|
"infra docker-compose",
|
|
16219
|
-
|
|
16258
|
+
import_node_path62.default.relative(scanPath, composePath),
|
|
16220
16259
|
err
|
|
16221
16260
|
);
|
|
16222
16261
|
return { nodesAdded, edgesAdded };
|
|
16223
16262
|
}
|
|
16224
16263
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
16225
|
-
const evidenceFile =
|
|
16264
|
+
const evidenceFile = import_node_path62.default.relative(scanPath, composePath).split(import_node_path62.default.sep).join("/");
|
|
16226
16265
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
16227
16266
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
16228
16267
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -16264,8 +16303,8 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
16264
16303
|
|
|
16265
16304
|
// src/extract/infra/dockerfile.ts
|
|
16266
16305
|
init_cjs_shims();
|
|
16267
|
-
var
|
|
16268
|
-
var
|
|
16306
|
+
var import_node_path63 = __toESM(require("path"), 1);
|
|
16307
|
+
var import_node_fs29 = require("fs");
|
|
16269
16308
|
var import_types50 = require("@neat.is/types");
|
|
16270
16309
|
function readDockerfile(content) {
|
|
16271
16310
|
let image = null;
|
|
@@ -16295,15 +16334,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
16295
16334
|
let nodesAdded = 0;
|
|
16296
16335
|
let edgesAdded = 0;
|
|
16297
16336
|
for (const service of services) {
|
|
16298
|
-
const dockerfilePath =
|
|
16337
|
+
const dockerfilePath = import_node_path63.default.join(service.dir, "Dockerfile");
|
|
16299
16338
|
if (!await exists2(dockerfilePath)) continue;
|
|
16300
16339
|
let content;
|
|
16301
16340
|
try {
|
|
16302
|
-
content = await
|
|
16341
|
+
content = await import_node_fs29.promises.readFile(dockerfilePath, "utf8");
|
|
16303
16342
|
} catch (err) {
|
|
16304
16343
|
recordExtractionError(
|
|
16305
16344
|
"infra dockerfile",
|
|
16306
|
-
|
|
16345
|
+
import_node_path63.default.relative(scanPath, dockerfilePath),
|
|
16307
16346
|
err
|
|
16308
16347
|
);
|
|
16309
16348
|
continue;
|
|
@@ -16315,8 +16354,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
16315
16354
|
graph.addNode(node.id, node);
|
|
16316
16355
|
nodesAdded++;
|
|
16317
16356
|
}
|
|
16318
|
-
const relDockerfile = toPosix(
|
|
16319
|
-
const evidenceFile = toPosix(
|
|
16357
|
+
const relDockerfile = toPosix(import_node_path63.default.relative(service.dir, dockerfilePath));
|
|
16358
|
+
const evidenceFile = toPosix(import_node_path63.default.relative(scanPath, dockerfilePath));
|
|
16320
16359
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
16321
16360
|
graph,
|
|
16322
16361
|
service.pkg.name,
|
|
@@ -16368,23 +16407,23 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
16368
16407
|
|
|
16369
16408
|
// src/extract/infra/terraform.ts
|
|
16370
16409
|
init_cjs_shims();
|
|
16371
|
-
var
|
|
16372
|
-
var
|
|
16410
|
+
var import_node_fs30 = require("fs");
|
|
16411
|
+
var import_node_path64 = __toESM(require("path"), 1);
|
|
16373
16412
|
var import_types51 = require("@neat.is/types");
|
|
16374
16413
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
16375
16414
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
16376
16415
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
16377
16416
|
if (depth > max) return [];
|
|
16378
16417
|
const out = [];
|
|
16379
|
-
const entries = await
|
|
16418
|
+
const entries = await import_node_fs30.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
16380
16419
|
for (const entry of entries) {
|
|
16381
16420
|
if (entry.isDirectory()) {
|
|
16382
16421
|
if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
|
|
16383
|
-
const child =
|
|
16422
|
+
const child = import_node_path64.default.join(start, entry.name);
|
|
16384
16423
|
if (await isPythonVenvDir(child)) continue;
|
|
16385
16424
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
16386
16425
|
} else if (entry.isFile() && entry.name.endsWith(".tf")) {
|
|
16387
|
-
out.push(
|
|
16426
|
+
out.push(import_node_path64.default.join(start, entry.name));
|
|
16388
16427
|
}
|
|
16389
16428
|
}
|
|
16390
16429
|
return out;
|
|
@@ -16415,8 +16454,8 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
16415
16454
|
let edgesAdded = 0;
|
|
16416
16455
|
const files = await walkTfFiles(scanPath);
|
|
16417
16456
|
for (const file of files) {
|
|
16418
|
-
const content = await
|
|
16419
|
-
const evidenceFile = toPosix(
|
|
16457
|
+
const content = await import_node_fs30.promises.readFile(file, "utf8");
|
|
16458
|
+
const evidenceFile = toPosix(import_node_path64.default.relative(scanPath, file));
|
|
16420
16459
|
const resources = [];
|
|
16421
16460
|
const byKey = /* @__PURE__ */ new Map();
|
|
16422
16461
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -16473,8 +16512,8 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
16473
16512
|
|
|
16474
16513
|
// src/extract/infra/k8s.ts
|
|
16475
16514
|
init_cjs_shims();
|
|
16476
|
-
var
|
|
16477
|
-
var
|
|
16515
|
+
var import_node_fs31 = require("fs");
|
|
16516
|
+
var import_node_path65 = __toESM(require("path"), 1);
|
|
16478
16517
|
var import_yaml3 = require("yaml");
|
|
16479
16518
|
var import_types52 = require("@neat.is/types");
|
|
16480
16519
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
@@ -16490,15 +16529,15 @@ var WORKLOAD_KINDS = /* @__PURE__ */ new Set(["Deployment", "StatefulSet", "Daem
|
|
|
16490
16529
|
async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
16491
16530
|
if (depth > max) return [];
|
|
16492
16531
|
const out = [];
|
|
16493
|
-
const entries = await
|
|
16532
|
+
const entries = await import_node_fs31.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
16494
16533
|
for (const entry of entries) {
|
|
16495
16534
|
if (entry.isDirectory()) {
|
|
16496
16535
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
16497
|
-
const child =
|
|
16536
|
+
const child = import_node_path65.default.join(start, entry.name);
|
|
16498
16537
|
if (await isPythonVenvDir(child)) continue;
|
|
16499
16538
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
16500
|
-
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
16501
|
-
out.push(
|
|
16539
|
+
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path65.default.extname(entry.name))) {
|
|
16540
|
+
out.push(import_node_path65.default.join(start, entry.name));
|
|
16502
16541
|
}
|
|
16503
16542
|
}
|
|
16504
16543
|
return out;
|
|
@@ -16508,14 +16547,14 @@ async function addK8sResources(graph, scanPath) {
|
|
|
16508
16547
|
let edgesAdded = 0;
|
|
16509
16548
|
const files = await walkYamlFiles2(scanPath);
|
|
16510
16549
|
for (const file of files) {
|
|
16511
|
-
const content = await
|
|
16550
|
+
const content = await import_node_fs31.promises.readFile(file, "utf8");
|
|
16512
16551
|
let docs;
|
|
16513
16552
|
try {
|
|
16514
16553
|
docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
|
|
16515
16554
|
} catch {
|
|
16516
16555
|
continue;
|
|
16517
16556
|
}
|
|
16518
|
-
const relFile = toPosix(
|
|
16557
|
+
const relFile = toPosix(import_node_path65.default.relative(scanPath, file));
|
|
16519
16558
|
for (const doc of docs) {
|
|
16520
16559
|
if (!doc?.kind || !doc.metadata?.name) continue;
|
|
16521
16560
|
const infraKind = K8S_KIND_TO_INFRA_KIND[doc.kind];
|
|
@@ -16564,16 +16603,16 @@ async function addK8sResources(graph, scanPath) {
|
|
|
16564
16603
|
|
|
16565
16604
|
// src/extract/infra/cloudflare.ts
|
|
16566
16605
|
init_cjs_shims();
|
|
16567
|
-
var
|
|
16568
|
-
var
|
|
16606
|
+
var import_node_fs32 = require("fs");
|
|
16607
|
+
var import_node_path66 = __toESM(require("path"), 1);
|
|
16569
16608
|
var import_smol_toml3 = require("smol-toml");
|
|
16570
16609
|
var import_types53 = require("@neat.is/types");
|
|
16571
16610
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
16572
16611
|
async function readWranglerConfig(dir) {
|
|
16573
16612
|
for (const filename of WRANGLER_FILENAMES) {
|
|
16574
|
-
const abs =
|
|
16613
|
+
const abs = import_node_path66.default.join(dir, filename);
|
|
16575
16614
|
if (!await exists2(abs)) continue;
|
|
16576
|
-
const raw = await
|
|
16615
|
+
const raw = await import_node_fs32.promises.readFile(abs, "utf8");
|
|
16577
16616
|
const config = filename === "wrangler.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
16578
16617
|
return { config, relFile: filename, raw };
|
|
16579
16618
|
}
|
|
@@ -16634,11 +16673,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
16634
16673
|
try {
|
|
16635
16674
|
read = await readWranglerConfig(service.dir);
|
|
16636
16675
|
} catch (err) {
|
|
16637
|
-
recordExtractionError("infra cloudflare",
|
|
16676
|
+
recordExtractionError("infra cloudflare", import_node_path66.default.relative(scanPath, service.dir), err);
|
|
16638
16677
|
continue;
|
|
16639
16678
|
}
|
|
16640
16679
|
if (!read || !read.config.name) continue;
|
|
16641
|
-
const evidenceFile = toPosix(
|
|
16680
|
+
const evidenceFile = toPosix(import_node_path66.default.relative(scanPath, import_node_path66.default.join(service.dir, read.relFile)));
|
|
16642
16681
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
16643
16682
|
}
|
|
16644
16683
|
for (const worker of discovered) {
|
|
@@ -16650,7 +16689,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
16650
16689
|
}
|
|
16651
16690
|
let anchorId = service.node.id;
|
|
16652
16691
|
if (config.main) {
|
|
16653
|
-
const entryRelPath = toPosix(
|
|
16692
|
+
const entryRelPath = toPosix(import_node_path66.default.normalize(config.main));
|
|
16654
16693
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
16655
16694
|
graph,
|
|
16656
16695
|
service.pkg.name,
|
|
@@ -16797,24 +16836,24 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
16797
16836
|
|
|
16798
16837
|
// src/extract/infra/vercel.ts
|
|
16799
16838
|
init_cjs_shims();
|
|
16800
|
-
var
|
|
16801
|
-
var
|
|
16839
|
+
var import_node_fs33 = require("fs");
|
|
16840
|
+
var import_node_path67 = __toESM(require("path"), 1);
|
|
16802
16841
|
var import_types54 = require("@neat.is/types");
|
|
16803
16842
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
16804
16843
|
async function readVercelConfig(dir) {
|
|
16805
16844
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
16806
|
-
const abs =
|
|
16845
|
+
const abs = import_node_path67.default.join(dir, filename);
|
|
16807
16846
|
if (!await exists2(abs)) continue;
|
|
16808
|
-
const raw = await
|
|
16847
|
+
const raw = await import_node_fs33.promises.readFile(abs, "utf8");
|
|
16809
16848
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
16810
16849
|
return { config, relFile: filename, raw };
|
|
16811
16850
|
}
|
|
16812
16851
|
return null;
|
|
16813
16852
|
}
|
|
16814
16853
|
async function readLinkedProjectName(dir) {
|
|
16815
|
-
const abs =
|
|
16854
|
+
const abs = import_node_path67.default.join(dir, ".vercel", "project.json");
|
|
16816
16855
|
if (!await exists2(abs)) return void 0;
|
|
16817
|
-
const parsed = JSON.parse(await
|
|
16856
|
+
const parsed = JSON.parse(await import_node_fs33.promises.readFile(abs, "utf8"));
|
|
16818
16857
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
16819
16858
|
}
|
|
16820
16859
|
function routeSource(route) {
|
|
@@ -16830,7 +16869,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
16830
16869
|
read = await readVercelConfig(service.dir);
|
|
16831
16870
|
projectName = await readLinkedProjectName(service.dir);
|
|
16832
16871
|
} catch (err) {
|
|
16833
|
-
recordExtractionError("infra vercel",
|
|
16872
|
+
recordExtractionError("infra vercel", import_node_path67.default.relative(scanPath, service.dir), err);
|
|
16834
16873
|
continue;
|
|
16835
16874
|
}
|
|
16836
16875
|
if (!read && !projectName) continue;
|
|
@@ -16846,7 +16885,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
16846
16885
|
const anchorId = service.node.id;
|
|
16847
16886
|
if (!read) continue;
|
|
16848
16887
|
const { config, relFile, raw } = read;
|
|
16849
|
-
const evidenceFile = toPosix(
|
|
16888
|
+
const evidenceFile = toPosix(import_node_path67.default.relative(scanPath, import_node_path67.default.join(service.dir, relFile)));
|
|
16850
16889
|
const add = (edgeType, kind, name) => {
|
|
16851
16890
|
if (!name) return;
|
|
16852
16891
|
const result = emitPlatformResourceEdge(
|
|
@@ -16875,16 +16914,16 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
16875
16914
|
|
|
16876
16915
|
// src/extract/infra/railway.ts
|
|
16877
16916
|
init_cjs_shims();
|
|
16878
|
-
var
|
|
16879
|
-
var
|
|
16917
|
+
var import_node_fs34 = require("fs");
|
|
16918
|
+
var import_node_path68 = __toESM(require("path"), 1);
|
|
16880
16919
|
var import_smol_toml4 = require("smol-toml");
|
|
16881
16920
|
var import_types55 = require("@neat.is/types");
|
|
16882
16921
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
16883
16922
|
async function readRailwayConfig(dir) {
|
|
16884
16923
|
for (const filename of RAILWAY_FILENAMES) {
|
|
16885
|
-
const abs =
|
|
16924
|
+
const abs = import_node_path68.default.join(dir, filename);
|
|
16886
16925
|
if (!await exists2(abs)) continue;
|
|
16887
|
-
const raw = await
|
|
16926
|
+
const raw = await import_node_fs34.promises.readFile(abs, "utf8");
|
|
16888
16927
|
const config = filename === "railway.toml" ? (0, import_smol_toml4.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
16889
16928
|
return { config, relFile: filename, raw };
|
|
16890
16929
|
}
|
|
@@ -16898,7 +16937,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
16898
16937
|
try {
|
|
16899
16938
|
read = await readRailwayConfig(service.dir);
|
|
16900
16939
|
} catch (err) {
|
|
16901
|
-
recordExtractionError("infra railway",
|
|
16940
|
+
recordExtractionError("infra railway", import_node_path68.default.relative(scanPath, service.dir), err);
|
|
16902
16941
|
continue;
|
|
16903
16942
|
}
|
|
16904
16943
|
if (!read) continue;
|
|
@@ -16908,7 +16947,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
16908
16947
|
}
|
|
16909
16948
|
const anchorId = service.node.id;
|
|
16910
16949
|
const { config, relFile, raw } = read;
|
|
16911
|
-
const evidenceFile = toPosix(
|
|
16950
|
+
const evidenceFile = toPosix(import_node_path68.default.relative(scanPath, import_node_path68.default.join(service.dir, relFile)));
|
|
16912
16951
|
const add = (edgeType, kind, name) => {
|
|
16913
16952
|
if (!name) return;
|
|
16914
16953
|
const result = emitPlatformResourceEdge(
|
|
@@ -16933,15 +16972,15 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
16933
16972
|
|
|
16934
16973
|
// src/extract/infra/supabase.ts
|
|
16935
16974
|
init_cjs_shims();
|
|
16936
|
-
var
|
|
16937
|
-
var
|
|
16975
|
+
var import_node_fs35 = require("fs");
|
|
16976
|
+
var import_node_path69 = __toESM(require("path"), 1);
|
|
16938
16977
|
var import_smol_toml5 = require("smol-toml");
|
|
16939
16978
|
var import_types56 = require("@neat.is/types");
|
|
16940
16979
|
async function readSupabaseConfig(dir) {
|
|
16941
|
-
const relFile =
|
|
16942
|
-
const abs =
|
|
16980
|
+
const relFile = import_node_path69.default.join("supabase", "config.toml");
|
|
16981
|
+
const abs = import_node_path69.default.join(dir, relFile);
|
|
16943
16982
|
if (!await exists2(abs)) return null;
|
|
16944
|
-
const raw = await
|
|
16983
|
+
const raw = await import_node_fs35.promises.readFile(abs, "utf8");
|
|
16945
16984
|
const config = (0, import_smol_toml5.parse)(raw);
|
|
16946
16985
|
return { config, relFile, raw };
|
|
16947
16986
|
}
|
|
@@ -16953,7 +16992,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
16953
16992
|
try {
|
|
16954
16993
|
read = await readSupabaseConfig(service.dir);
|
|
16955
16994
|
} catch (err) {
|
|
16956
|
-
recordExtractionError("infra supabase",
|
|
16995
|
+
recordExtractionError("infra supabase", import_node_path69.default.relative(scanPath, service.dir), err);
|
|
16957
16996
|
continue;
|
|
16958
16997
|
}
|
|
16959
16998
|
if (!read) continue;
|
|
@@ -16968,7 +17007,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
16968
17007
|
});
|
|
16969
17008
|
}
|
|
16970
17009
|
const anchorId = service.node.id;
|
|
16971
|
-
const evidenceFile = toPosix(
|
|
17010
|
+
const evidenceFile = toPosix(import_node_path69.default.relative(scanPath, import_node_path69.default.join(service.dir, relFile)));
|
|
16972
17011
|
const add = (edgeType, kind, name) => {
|
|
16973
17012
|
if (!name) return;
|
|
16974
17013
|
const result = emitPlatformResourceEdge(
|
|
@@ -17010,7 +17049,7 @@ async function addInfra(graph, scanPath, services) {
|
|
|
17010
17049
|
|
|
17011
17050
|
// src/extract/zod-shapes.ts
|
|
17012
17051
|
init_cjs_shims();
|
|
17013
|
-
var
|
|
17052
|
+
var import_node_path70 = __toESM(require("path"), 1);
|
|
17014
17053
|
var import_tree_sitter18 = __toESM(require("tree-sitter"), 1);
|
|
17015
17054
|
var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
|
|
17016
17055
|
var import_types57 = require("@neat.is/types");
|
|
@@ -17105,7 +17144,7 @@ function topLevelSchemas(root) {
|
|
|
17105
17144
|
}
|
|
17106
17145
|
function zodShapesFromFile(file, serviceDir) {
|
|
17107
17146
|
if (!ZOD_IMPORT_RE.test(file.content)) return [];
|
|
17108
|
-
const tree = parseSource3(parserForExt4(
|
|
17147
|
+
const tree = parseSource3(parserForExt4(import_node_path70.default.extname(file.path)), file.content);
|
|
17109
17148
|
const out = [];
|
|
17110
17149
|
const seen = /* @__PURE__ */ new Set();
|
|
17111
17150
|
for (const { name, call } of topLevelSchemas(tree.rootNode)) {
|
|
@@ -17123,7 +17162,7 @@ function zodShapesFromFile(file, serviceDir) {
|
|
|
17123
17162
|
name,
|
|
17124
17163
|
fields,
|
|
17125
17164
|
evidence: {
|
|
17126
|
-
file:
|
|
17165
|
+
file: import_node_path70.default.relative(serviceDir, file.path),
|
|
17127
17166
|
line,
|
|
17128
17167
|
snippet: snippet(file.content, line)
|
|
17129
17168
|
}
|
|
@@ -17391,12 +17430,12 @@ async function addFirestoreRules(graph, services) {
|
|
|
17391
17430
|
}
|
|
17392
17431
|
|
|
17393
17432
|
// src/extract/index.ts
|
|
17394
|
-
var
|
|
17433
|
+
var import_node_path72 = __toESM(require("path"), 1);
|
|
17395
17434
|
|
|
17396
17435
|
// src/extract/retire.ts
|
|
17397
17436
|
init_cjs_shims();
|
|
17398
|
-
var
|
|
17399
|
-
var
|
|
17437
|
+
var import_node_fs36 = require("fs");
|
|
17438
|
+
var import_node_path71 = __toESM(require("path"), 1);
|
|
17400
17439
|
var import_types59 = require("@neat.is/types");
|
|
17401
17440
|
function dropOrphanedFileNodes(graph, source) {
|
|
17402
17441
|
const orphans = [];
|
|
@@ -17430,11 +17469,11 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = [], so
|
|
|
17430
17469
|
if (outOfScope(graph, edge, source)) return;
|
|
17431
17470
|
const evidenceFile = edge.evidence?.file;
|
|
17432
17471
|
if (!evidenceFile) return;
|
|
17433
|
-
if (
|
|
17434
|
-
if (!(0,
|
|
17472
|
+
if (import_node_path71.default.isAbsolute(evidenceFile)) {
|
|
17473
|
+
if (!(0, import_node_fs36.existsSync)(evidenceFile)) toDrop.push(id);
|
|
17435
17474
|
return;
|
|
17436
17475
|
}
|
|
17437
|
-
const found = bases.some((base) => (0,
|
|
17476
|
+
const found = bases.some((base) => (0, import_node_fs36.existsSync)(import_node_path71.default.join(base, evidenceFile)));
|
|
17438
17477
|
if (!found) toDrop.push(id);
|
|
17439
17478
|
});
|
|
17440
17479
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -17443,7 +17482,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = [], so
|
|
|
17443
17482
|
}
|
|
17444
17483
|
|
|
17445
17484
|
// src/extract/index.ts
|
|
17446
|
-
|
|
17485
|
+
var passQueue = Promise.resolve();
|
|
17486
|
+
function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
17487
|
+
const pass = passQueue.then(() => runExtractionPass(graph, scanPath, opts));
|
|
17488
|
+
passQueue = pass.catch(() => {
|
|
17489
|
+
});
|
|
17490
|
+
return pass;
|
|
17491
|
+
}
|
|
17492
|
+
async function runExtractionPass(graph, scanPath, opts) {
|
|
17447
17493
|
await ensureCompatLoaded();
|
|
17448
17494
|
drainExtractionErrors();
|
|
17449
17495
|
drainSkippedFiles();
|
|
@@ -17495,7 +17541,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
17495
17541
|
const droppedEntries = drainDroppedExtracted();
|
|
17496
17542
|
const skippedEntries = drainSkippedFiles();
|
|
17497
17543
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
17498
|
-
const rejectedPath =
|
|
17544
|
+
const rejectedPath = import_node_path72.default.join(import_node_path72.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
17499
17545
|
try {
|
|
17500
17546
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
17501
17547
|
} catch (err) {
|
|
@@ -17517,7 +17563,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
17517
17563
|
skippedFiles: skippedEntries.length,
|
|
17518
17564
|
skippedEntries
|
|
17519
17565
|
};
|
|
17520
|
-
emitNeatEvent({
|
|
17566
|
+
if (opts.announce !== false) emitNeatEvent({
|
|
17521
17567
|
type: "extraction-complete",
|
|
17522
17568
|
project: opts.project ?? DEFAULT_PROJECT,
|
|
17523
17569
|
payload: {
|
|
@@ -18435,7 +18481,7 @@ async function askGraph(graph, question, opts = {}) {
|
|
|
18435
18481
|
|
|
18436
18482
|
// src/diff.ts
|
|
18437
18483
|
init_cjs_shims();
|
|
18438
|
-
var
|
|
18484
|
+
var import_node_fs37 = require("fs");
|
|
18439
18485
|
async function loadSnapshotForDiff(target) {
|
|
18440
18486
|
if (/^https?:\/\//i.test(target)) {
|
|
18441
18487
|
const res = await fetch(target);
|
|
@@ -18444,7 +18490,7 @@ async function loadSnapshotForDiff(target) {
|
|
|
18444
18490
|
}
|
|
18445
18491
|
return await res.json();
|
|
18446
18492
|
}
|
|
18447
|
-
const raw = await
|
|
18493
|
+
const raw = await import_node_fs37.promises.readFile(target, "utf8");
|
|
18448
18494
|
return JSON.parse(raw);
|
|
18449
18495
|
}
|
|
18450
18496
|
function indexEntries(entries) {
|
|
@@ -18512,8 +18558,8 @@ function canonicalJson(value) {
|
|
|
18512
18558
|
|
|
18513
18559
|
// src/persist.ts
|
|
18514
18560
|
init_cjs_shims();
|
|
18515
|
-
var
|
|
18516
|
-
var
|
|
18561
|
+
var import_node_fs38 = require("fs");
|
|
18562
|
+
var import_node_path73 = __toESM(require("path"), 1);
|
|
18517
18563
|
var import_types62 = require("@neat.is/types");
|
|
18518
18564
|
var SCHEMA_VERSION = 7;
|
|
18519
18565
|
function migrateV1ToV2(payload) {
|
|
@@ -18568,7 +18614,7 @@ function migrateV2ToV3(payload) {
|
|
|
18568
18614
|
return { ...payload, schemaVersion: 3 };
|
|
18569
18615
|
}
|
|
18570
18616
|
async function ensureDir(filePath) {
|
|
18571
|
-
await
|
|
18617
|
+
await import_node_fs38.promises.mkdir(import_node_path73.default.dirname(filePath), { recursive: true });
|
|
18572
18618
|
}
|
|
18573
18619
|
var WRITE_FLUSH_CHARS = 4 * 1024 * 1024;
|
|
18574
18620
|
function jsonOrUndefined(value) {
|
|
@@ -18614,7 +18660,7 @@ async function saveGraphToDisk(graph, outPath) {
|
|
|
18614
18660
|
graph: graph.export()
|
|
18615
18661
|
};
|
|
18616
18662
|
const tmp = `${outPath}.tmp`;
|
|
18617
|
-
const handle = await
|
|
18663
|
+
const handle = await import_node_fs38.promises.open(tmp, "w");
|
|
18618
18664
|
try {
|
|
18619
18665
|
let buffered = "";
|
|
18620
18666
|
for (const chunk of serializeGraphChunks(payload)) {
|
|
@@ -18628,7 +18674,7 @@ async function saveGraphToDisk(graph, outPath) {
|
|
|
18628
18674
|
} finally {
|
|
18629
18675
|
await handle.close();
|
|
18630
18676
|
}
|
|
18631
|
-
await
|
|
18677
|
+
await import_node_fs38.promises.rename(tmp, outPath);
|
|
18632
18678
|
}
|
|
18633
18679
|
function migrateSnapshot(input) {
|
|
18634
18680
|
let payload = input;
|
|
@@ -18660,7 +18706,7 @@ function migrateSnapshot(input) {
|
|
|
18660
18706
|
async function loadGraphFromDisk(graph, outPath) {
|
|
18661
18707
|
let raw;
|
|
18662
18708
|
try {
|
|
18663
|
-
raw = await
|
|
18709
|
+
raw = await import_node_fs38.promises.readFile(outPath, "utf8");
|
|
18664
18710
|
} catch (err) {
|
|
18665
18711
|
if (err.code === "ENOENT") return;
|
|
18666
18712
|
throw err;
|
|
@@ -18711,23 +18757,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
18711
18757
|
|
|
18712
18758
|
// src/projects.ts
|
|
18713
18759
|
init_cjs_shims();
|
|
18714
|
-
var
|
|
18760
|
+
var import_node_path74 = __toESM(require("path"), 1);
|
|
18715
18761
|
function pathsForProject(project, baseDir) {
|
|
18716
18762
|
if (project === DEFAULT_PROJECT) {
|
|
18717
18763
|
return {
|
|
18718
|
-
snapshotPath:
|
|
18719
|
-
errorsPath:
|
|
18720
|
-
staleEventsPath:
|
|
18721
|
-
embeddingsCachePath:
|
|
18722
|
-
policyViolationsPath:
|
|
18764
|
+
snapshotPath: import_node_path74.default.join(baseDir, "graph.json"),
|
|
18765
|
+
errorsPath: import_node_path74.default.join(baseDir, "errors.ndjson"),
|
|
18766
|
+
staleEventsPath: import_node_path74.default.join(baseDir, "stale-events.ndjson"),
|
|
18767
|
+
embeddingsCachePath: import_node_path74.default.join(baseDir, "embeddings.json"),
|
|
18768
|
+
policyViolationsPath: import_node_path74.default.join(baseDir, "policy-violations.ndjson")
|
|
18723
18769
|
};
|
|
18724
18770
|
}
|
|
18725
18771
|
return {
|
|
18726
|
-
snapshotPath:
|
|
18727
|
-
errorsPath:
|
|
18728
|
-
staleEventsPath:
|
|
18729
|
-
embeddingsCachePath:
|
|
18730
|
-
policyViolationsPath:
|
|
18772
|
+
snapshotPath: import_node_path74.default.join(baseDir, `${project}.json`),
|
|
18773
|
+
errorsPath: import_node_path74.default.join(baseDir, `errors.${project}.ndjson`),
|
|
18774
|
+
staleEventsPath: import_node_path74.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
18775
|
+
embeddingsCachePath: import_node_path74.default.join(baseDir, `embeddings.${project}.json`),
|
|
18776
|
+
policyViolationsPath: import_node_path74.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
18731
18777
|
};
|
|
18732
18778
|
}
|
|
18733
18779
|
var Projects = class {
|
|
@@ -18767,20 +18813,20 @@ function parseExtraProjects(raw) {
|
|
|
18767
18813
|
|
|
18768
18814
|
// src/registry.ts
|
|
18769
18815
|
init_cjs_shims();
|
|
18770
|
-
var
|
|
18816
|
+
var import_node_fs39 = require("fs");
|
|
18771
18817
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
18772
|
-
var
|
|
18818
|
+
var import_node_path75 = __toESM(require("path"), 1);
|
|
18773
18819
|
var import_types63 = require("@neat.is/types");
|
|
18774
18820
|
function neatHome() {
|
|
18775
18821
|
const override = process.env.NEAT_HOME;
|
|
18776
|
-
if (override && override.length > 0) return
|
|
18777
|
-
return
|
|
18822
|
+
if (override && override.length > 0) return import_node_path75.default.resolve(override);
|
|
18823
|
+
return import_node_path75.default.join(import_node_os3.default.homedir(), ".neat");
|
|
18778
18824
|
}
|
|
18779
18825
|
function registryPath() {
|
|
18780
|
-
return
|
|
18826
|
+
return import_node_path75.default.join(neatHome(), "projects.json");
|
|
18781
18827
|
}
|
|
18782
18828
|
function daemonsDir() {
|
|
18783
|
-
return
|
|
18829
|
+
return import_node_path75.default.join(neatHome(), "daemons");
|
|
18784
18830
|
}
|
|
18785
18831
|
function isFiniteInt(v) {
|
|
18786
18832
|
return typeof v === "number" && Number.isFinite(v);
|
|
@@ -18813,7 +18859,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
18813
18859
|
const dir = daemonsDir();
|
|
18814
18860
|
let names;
|
|
18815
18861
|
try {
|
|
18816
|
-
names = await
|
|
18862
|
+
names = await import_node_fs39.promises.readdir(dir);
|
|
18817
18863
|
} catch (err) {
|
|
18818
18864
|
if (err.code === "ENOENT") return [];
|
|
18819
18865
|
throw err;
|
|
@@ -18821,10 +18867,10 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
18821
18867
|
const out = [];
|
|
18822
18868
|
for (const name of names) {
|
|
18823
18869
|
if (!name.endsWith(".json")) continue;
|
|
18824
|
-
const file =
|
|
18870
|
+
const file = import_node_path75.default.join(dir, name);
|
|
18825
18871
|
let raw;
|
|
18826
18872
|
try {
|
|
18827
|
-
raw = await
|
|
18873
|
+
raw = await import_node_fs39.promises.readFile(file, "utf8");
|
|
18828
18874
|
} catch {
|
|
18829
18875
|
continue;
|
|
18830
18876
|
}
|
|
@@ -18852,7 +18898,7 @@ async function readRegistry() {
|
|
|
18852
18898
|
const file = registryPath();
|
|
18853
18899
|
let raw;
|
|
18854
18900
|
try {
|
|
18855
|
-
raw = await
|
|
18901
|
+
raw = await import_node_fs39.promises.readFile(file, "utf8");
|
|
18856
18902
|
} catch (err) {
|
|
18857
18903
|
if (err.code === "ENOENT") {
|
|
18858
18904
|
return { version: 1, projects: [] };
|
|
@@ -18875,8 +18921,64 @@ var DEFAULT_PRUNE_TTL_MS = 7 * DAY_MS2;
|
|
|
18875
18921
|
|
|
18876
18922
|
// src/streaming.ts
|
|
18877
18923
|
init_cjs_shims();
|
|
18924
|
+
var import_node_fs40 = require("fs");
|
|
18925
|
+
var import_zod = require("zod");
|
|
18926
|
+
var import_types64 = require("@neat.is/types");
|
|
18878
18927
|
var SSE_HEARTBEAT_MS = 3e4;
|
|
18879
18928
|
var SSE_BACKPRESSURE_CAP = 1e3;
|
|
18929
|
+
var INCIDENT_TRIGGER_STREAM_SCOPE = "incident-only-v1";
|
|
18930
|
+
var IncidentTriggerSchema = import_types64.IncidentEventPayloadSchema.extend({
|
|
18931
|
+
incidentId: import_zod.z.string().regex(/^[A-Za-z0-9:_-]{1,100}$/),
|
|
18932
|
+
affectedNode: import_zod.z.string().min(1).max(512),
|
|
18933
|
+
service: import_zod.z.string().min(1).max(256),
|
|
18934
|
+
at: import_zod.z.string().datetime()
|
|
18935
|
+
});
|
|
18936
|
+
var MAX_REPLAY_BYTES = 16 * 1024 * 1024;
|
|
18937
|
+
var MAX_REPLAY_EVENTS = 1e4;
|
|
18938
|
+
function isExtractionFailure(row) {
|
|
18939
|
+
const keys = Object.keys(row);
|
|
18940
|
+
return keys.every((key) => ["source", "producer", "file", "error", "stack", "ts"].includes(key)) && typeof row.producer === "string" && typeof row.file === "string" && typeof row.error === "string" && typeof row.ts === "string" && (row.stack === void 0 || typeof row.stack === "string") && !Number.isNaN(Date.parse(row.ts));
|
|
18941
|
+
}
|
|
18942
|
+
function loadIncidentReplay(errorsPath, cursor) {
|
|
18943
|
+
if (cursor !== void 0 && !/^[A-Za-z0-9:_-]{1,100}$/.test(cursor)) throw new Error("incident replay unavailable");
|
|
18944
|
+
try {
|
|
18945
|
+
if ((0, import_node_fs40.statSync)(errorsPath).size > MAX_REPLAY_BYTES) throw new Error("incident replay unavailable");
|
|
18946
|
+
const raw = (0, import_node_fs40.readFileSync)(errorsPath, "utf8");
|
|
18947
|
+
if (raw && !raw.endsWith("\n")) throw new Error("incident replay unavailable");
|
|
18948
|
+
const seen = /* @__PURE__ */ new Map();
|
|
18949
|
+
const events = [];
|
|
18950
|
+
for (const line of raw.split("\n")) {
|
|
18951
|
+
if (!line) continue;
|
|
18952
|
+
if (events.length >= MAX_REPLAY_EVENTS) throw new Error("incident replay unavailable");
|
|
18953
|
+
const row = JSON.parse(line);
|
|
18954
|
+
if (row !== null && typeof row === "object" && !Array.isArray(row) && "source" in row && row.source === "extract") {
|
|
18955
|
+
if (!isExtractionFailure(row)) throw new Error("incident replay unavailable");
|
|
18956
|
+
continue;
|
|
18957
|
+
}
|
|
18958
|
+
const event = import_types64.ErrorEventSchema.parse(row);
|
|
18959
|
+
const trigger = IncidentTriggerSchema.parse({
|
|
18960
|
+
incidentId: event.id,
|
|
18961
|
+
affectedNode: event.affectedNode,
|
|
18962
|
+
service: event.service,
|
|
18963
|
+
incidentKind: (0, import_types64.incidentKindOf)(event),
|
|
18964
|
+
at: event.timestamp
|
|
18965
|
+
});
|
|
18966
|
+
const prior = seen.get(trigger.incidentId), encoded = JSON.stringify(trigger);
|
|
18967
|
+
if (prior !== void 0) {
|
|
18968
|
+
if (prior !== encoded) throw new Error("incident replay unavailable");
|
|
18969
|
+
continue;
|
|
18970
|
+
}
|
|
18971
|
+
seen.set(trigger.incidentId, encoded);
|
|
18972
|
+
events.push(trigger);
|
|
18973
|
+
}
|
|
18974
|
+
if (cursor === void 0) return events;
|
|
18975
|
+
const at = events.findIndex((event) => event.incidentId === cursor);
|
|
18976
|
+
if (at < 0) throw new Error("incident replay unavailable");
|
|
18977
|
+
return events.slice(at + 1);
|
|
18978
|
+
} catch {
|
|
18979
|
+
throw new Error("incident replay unavailable");
|
|
18980
|
+
}
|
|
18981
|
+
}
|
|
18880
18982
|
function handleSse(req, reply, opts) {
|
|
18881
18983
|
const heartbeatMs = opts.heartbeatMs ?? SSE_HEARTBEAT_MS;
|
|
18882
18984
|
const backpressureCap = opts.backpressureCap ?? SSE_BACKPRESSURE_CAP;
|
|
@@ -18884,8 +18986,20 @@ function handleSse(req, reply, opts) {
|
|
|
18884
18986
|
reply.raw.setHeader("Cache-Control", "no-cache, no-transform");
|
|
18885
18987
|
reply.raw.setHeader("Connection", "keep-alive");
|
|
18886
18988
|
reply.raw.setHeader("X-Accel-Buffering", "no");
|
|
18989
|
+
if (opts.incidentOnly) {
|
|
18990
|
+
reply.raw.setHeader("X-NEAT-Event-Scope", INCIDENT_TRIGGER_STREAM_SCOPE);
|
|
18991
|
+
reply.raw.setHeader("X-NEAT-Project", opts.project);
|
|
18992
|
+
if (opts.incidentReplay) reply.raw.setHeader("X-NEAT-Replay-Complete", "1");
|
|
18993
|
+
}
|
|
18887
18994
|
reply.raw.flushHeaders?.();
|
|
18888
18995
|
reply.raw.write(":open\n\n");
|
|
18996
|
+
for (const trigger of opts.incidentReplay ?? []) {
|
|
18997
|
+
reply.raw.write(`id: ${trigger.incidentId}
|
|
18998
|
+
event: incident
|
|
18999
|
+
data: ${JSON.stringify(trigger)}
|
|
19000
|
+
|
|
19001
|
+
`);
|
|
19002
|
+
}
|
|
18889
19003
|
let pending = 0;
|
|
18890
19004
|
let dropped = false;
|
|
18891
19005
|
const closeConnection = () => {
|
|
@@ -18913,6 +19027,17 @@ data: ${JSON.stringify({ reason: "backpressure" })}
|
|
|
18913
19027
|
};
|
|
18914
19028
|
const listener = (envelope) => {
|
|
18915
19029
|
if (envelope.project !== opts.project) return;
|
|
19030
|
+
if (opts.incidentOnly) {
|
|
19031
|
+
if (envelope.type !== "incident") return;
|
|
19032
|
+
const trigger = IncidentTriggerSchema.safeParse(envelope.payload);
|
|
19033
|
+
if (!trigger.success) return;
|
|
19034
|
+
writeFrame(`id: ${trigger.data.incidentId}
|
|
19035
|
+
event: incident
|
|
19036
|
+
data: ${JSON.stringify(trigger.data)}
|
|
19037
|
+
|
|
19038
|
+
`);
|
|
19039
|
+
return;
|
|
19040
|
+
}
|
|
18916
19041
|
writeFrame(`event: ${envelope.type}
|
|
18917
19042
|
data: ${JSON.stringify(envelope.payload)}
|
|
18918
19043
|
|
|
@@ -18932,11 +19057,501 @@ data: ${JSON.stringify(envelope.payload)}
|
|
|
18932
19057
|
// src/api.ts
|
|
18933
19058
|
init_auth();
|
|
18934
19059
|
|
|
18935
|
-
// src/
|
|
19060
|
+
// src/pr-verdict.ts
|
|
19061
|
+
init_cjs_shims();
|
|
19062
|
+
var import_node_crypto3 = require("crypto");
|
|
19063
|
+
var import_node_fs42 = require("fs");
|
|
19064
|
+
var import_node_os5 = __toESM(require("os"), 1);
|
|
19065
|
+
var import_node_path77 = __toESM(require("path"), 1);
|
|
19066
|
+
|
|
19067
|
+
// ../action/src/graph.mjs
|
|
19068
|
+
init_cjs_shims();
|
|
19069
|
+
var import_node_fs41 = require("fs");
|
|
19070
|
+
var MARKER = "<!-- neat-action:graph-impact -->";
|
|
19071
|
+
var shortId = (id) => String(id).split(":").slice(-1)[0];
|
|
19072
|
+
var fence = (s) => "`" + s + "`";
|
|
19073
|
+
function graphFromExport(raw) {
|
|
19074
|
+
const g = raw.graph ?? raw;
|
|
19075
|
+
const nodes = new Map((g.nodes ?? []).map((n) => [n.key, n.attributes ?? {}]));
|
|
19076
|
+
const edges = (g.edges ?? []).map((e) => ({
|
|
19077
|
+
key: e.key,
|
|
19078
|
+
source: e.source,
|
|
19079
|
+
target: e.target,
|
|
19080
|
+
...e.attributes ?? {}
|
|
19081
|
+
}));
|
|
19082
|
+
return { nodes, edges };
|
|
19083
|
+
}
|
|
19084
|
+
var namesByType = (entries, type) => entries.filter(([, a]) => a.type === type).map(([k, a]) => a.name ?? shortId(k));
|
|
19085
|
+
function diffGraphs(base, head) {
|
|
19086
|
+
const added = [...head.nodes].filter(([k]) => !base.nodes.has(k));
|
|
19087
|
+
const removed = [...base.nodes].filter(([k]) => !head.nodes.has(k));
|
|
19088
|
+
return {
|
|
19089
|
+
routesAdded: namesByType(added, "RouteNode"),
|
|
19090
|
+
routesRemoved: namesByType(removed, "RouteNode"),
|
|
19091
|
+
tablesAdded: namesByType(added, "InfraNode"),
|
|
19092
|
+
tablesRemoved: namesByType(removed, "InfraNode")
|
|
19093
|
+
};
|
|
19094
|
+
}
|
|
19095
|
+
function changedFileNodeIds(graph, changedPaths) {
|
|
19096
|
+
const ids = [];
|
|
19097
|
+
for (const [key, attrs] of graph.nodes) {
|
|
19098
|
+
if (attrs.type !== "FileNode" || !attrs.path) continue;
|
|
19099
|
+
if (changedPaths.some((c) => c === attrs.path || c.endsWith("/" + attrs.path))) ids.push(key);
|
|
19100
|
+
}
|
|
19101
|
+
return ids;
|
|
19102
|
+
}
|
|
19103
|
+
function changedNodeIds(base, head) {
|
|
19104
|
+
if (!base) return [...head.nodes.keys()];
|
|
19105
|
+
return [
|
|
19106
|
+
...[...head.nodes.keys()].filter((k) => !base.nodes.has(k)),
|
|
19107
|
+
...[...base.nodes.keys()].filter((k) => !head.nodes.has(k))
|
|
19108
|
+
];
|
|
19109
|
+
}
|
|
19110
|
+
function formatDivergences(data, changedIds) {
|
|
19111
|
+
const findings = Array.isArray(data) ? data : data?.divergences ?? data?.findings ?? data?.items ?? [];
|
|
19112
|
+
const changed = new Set(changedIds);
|
|
19113
|
+
return findings.filter((f) => changed.has(f.source) || changed.has(f.target) || changed.has(f.nodeId)).slice(0, 10).map((f) => {
|
|
19114
|
+
const kind = f.kind || f.type || "divergence";
|
|
19115
|
+
const s = shortId(f.source ?? f.nodeId ?? "");
|
|
19116
|
+
const t = f.target ? " \u2192 `" + shortId(f.target) + "`" : "";
|
|
19117
|
+
const why = f.reason || f.message || "";
|
|
19118
|
+
return `${kind}: \`${s}\`${t}${why ? " \u2014 " + why : ""}`;
|
|
19119
|
+
});
|
|
19120
|
+
}
|
|
19121
|
+
function observedBreakFrom(node, data) {
|
|
19122
|
+
const observed = Boolean(data?.observed);
|
|
19123
|
+
const dependentCount = Number(data?.inboundObservedCount ?? 0);
|
|
19124
|
+
const callCount = Array.isArray(data?.dependencies) ? data.dependencies.length : 0;
|
|
19125
|
+
if (!observed && dependentCount === 0 && callCount === 0) return null;
|
|
19126
|
+
const inboundVolume = typeof data.inboundVolume === "number" ? data.inboundVolume : void 0;
|
|
19127
|
+
const window = typeof data.window === "string" ? data.window : void 0;
|
|
19128
|
+
const inboundLastObserved = typeof data.inboundLastObserved === "string" ? data.inboundLastObserved : void 0;
|
|
19129
|
+
return {
|
|
19130
|
+
id: node.id,
|
|
19131
|
+
type: node.type,
|
|
19132
|
+
label: node.label,
|
|
19133
|
+
change: node.change,
|
|
19134
|
+
dependentCount,
|
|
19135
|
+
callCount,
|
|
19136
|
+
...inboundVolume !== void 0 ? { inboundVolume } : {},
|
|
19137
|
+
...window !== void 0 ? { window } : {},
|
|
19138
|
+
...inboundLastObserved !== void 0 ? { inboundLastObserved } : {}
|
|
19139
|
+
};
|
|
19140
|
+
}
|
|
19141
|
+
function blastRadius(graph, nodeId, maxDepth = 10) {
|
|
19142
|
+
const inbound = /* @__PURE__ */ new Map();
|
|
19143
|
+
for (const e of graph.edges) {
|
|
19144
|
+
if (!inbound.has(e.target)) inbound.set(e.target, []);
|
|
19145
|
+
inbound.get(e.target).push({ source: e.source, type: e.type });
|
|
19146
|
+
}
|
|
19147
|
+
const seen = /* @__PURE__ */ new Set([nodeId]);
|
|
19148
|
+
const dependents = [];
|
|
19149
|
+
let frontier = [nodeId];
|
|
19150
|
+
for (let d = 0; d < maxDepth && frontier.length; d++) {
|
|
19151
|
+
const next = [];
|
|
19152
|
+
for (const n of frontier) {
|
|
19153
|
+
for (const { source } of inbound.get(n) ?? []) {
|
|
19154
|
+
if (seen.has(source)) continue;
|
|
19155
|
+
seen.add(source);
|
|
19156
|
+
dependents.push(source);
|
|
19157
|
+
next.push(source);
|
|
19158
|
+
}
|
|
19159
|
+
}
|
|
19160
|
+
frontier = next;
|
|
19161
|
+
}
|
|
19162
|
+
return dependents;
|
|
19163
|
+
}
|
|
19164
|
+
var stable = (obj) => JSON.stringify(obj, Object.keys(obj ?? {}).sort());
|
|
19165
|
+
function changedNodesForObservedScan(base, head, { limit = 25 } = {}) {
|
|
19166
|
+
const out = [];
|
|
19167
|
+
for (const [key, attrs] of base.nodes) {
|
|
19168
|
+
if (!head.nodes.has(key)) {
|
|
19169
|
+
out.push({ id: key, type: attrs.type, label: attrs.name ?? shortId(key), change: "removed" });
|
|
19170
|
+
}
|
|
19171
|
+
}
|
|
19172
|
+
for (const [key, attrs] of head.nodes) {
|
|
19173
|
+
const b = base.nodes.get(key);
|
|
19174
|
+
if (b && stable(b) !== stable(attrs)) {
|
|
19175
|
+
out.push({ id: key, type: attrs.type, label: attrs.name ?? shortId(key), change: "changed" });
|
|
19176
|
+
}
|
|
19177
|
+
}
|
|
19178
|
+
return out.slice(0, limit);
|
|
19179
|
+
}
|
|
19180
|
+
var plural = (n) => n === 1 ? "" : "s";
|
|
19181
|
+
function formatAge(iso) {
|
|
19182
|
+
const t = Date.parse(iso);
|
|
19183
|
+
if (!Number.isFinite(t)) return null;
|
|
19184
|
+
const ms = Date.now() - t;
|
|
19185
|
+
if (ms < 0) return "just now";
|
|
19186
|
+
const s = Math.round(ms / 1e3);
|
|
19187
|
+
if (s < 60) return `${s}s ago`;
|
|
19188
|
+
const m = Math.round(s / 60);
|
|
19189
|
+
if (m < 60) return `${m}m ago`;
|
|
19190
|
+
const h = Math.round(m / 60);
|
|
19191
|
+
if (h < 48) return `${h}h ago`;
|
|
19192
|
+
return `${Math.round(h / 24)}d ago`;
|
|
19193
|
+
}
|
|
19194
|
+
function windowPhrase(window) {
|
|
19195
|
+
if (window === "7d") return "in 7d";
|
|
19196
|
+
if (window === "lifetime") return "(lifetime)";
|
|
19197
|
+
return "";
|
|
19198
|
+
}
|
|
19199
|
+
function observedTraffic(b) {
|
|
19200
|
+
if (typeof b.inboundVolume !== "number") return null;
|
|
19201
|
+
const win = windowPhrase(b.window);
|
|
19202
|
+
const served = `served ${b.inboundVolume.toLocaleString("en-US")}\xD7${win ? " " + win : ""}`;
|
|
19203
|
+
const age = b.inboundLastObserved ? formatAge(b.inboundLastObserved) : null;
|
|
19204
|
+
return age ? `${served}, last seen ${age}` : served;
|
|
19205
|
+
}
|
|
19206
|
+
var FUSION_NOTE = "<sub>Fused view \u2014 the static graph (EXTRACTED) weighted against production traffic (OBSERVED) from the connected NEAT host. Observed-break checks ran live against that host.</sub>";
|
|
19207
|
+
var TONE_COPY = {
|
|
19208
|
+
loud: {
|
|
19209
|
+
green: "## \u2705 NEAT \u2014 all clear",
|
|
19210
|
+
yellow: "## \u26A0\uFE0F NEAT \u2014 a few minor problems",
|
|
19211
|
+
red: "## \u{1F6D1} NEAT \u2014 FUCK NO. Here are the problems:"
|
|
19212
|
+
},
|
|
19213
|
+
professional: {
|
|
19214
|
+
green: "## \u2705 NEAT \u2014 all clear",
|
|
19215
|
+
yellow: "## \u26A0\uFE0F NEAT \u2014 Minor issues to review",
|
|
19216
|
+
red: "## \u{1F6D1} NEAT \u2014 Critical: production will break"
|
|
19217
|
+
}
|
|
19218
|
+
};
|
|
19219
|
+
function breakHeadline(b) {
|
|
19220
|
+
const verb = b.change === "removed" ? "deletes" : "changes";
|
|
19221
|
+
const what = fence(b.label) + (b.type === "InfraNode" ? " (data store)" : "");
|
|
19222
|
+
const consequence = b.type === "RouteNode" ? "Live callers 404." : b.type === "InfraNode" ? "Live readers and writers fail." : "Live callers break.";
|
|
19223
|
+
const traffic = observedTraffic(b);
|
|
19224
|
+
if (traffic) {
|
|
19225
|
+
return `**\u{1F534} Production will break** \u2014 this PR ${verb} ${what}, ${traffic} (OBSERVED). ${consequence}`;
|
|
19226
|
+
}
|
|
19227
|
+
if (b.dependentCount > 0) {
|
|
19228
|
+
return `**\u{1F534} Production will break** \u2014 this PR ${verb} ${what}, ${b.dependentCount} observed dependent${plural(b.dependentCount)} (OBSERVED). ${consequence}`;
|
|
19229
|
+
}
|
|
19230
|
+
return `**\u{1F534} Production will break** \u2014 this PR ${verb} ${what}, seen live in production making ${b.callCount} observed call${plural(b.callCount)} (OBSERVED). Runtime behavior changes.`;
|
|
19231
|
+
}
|
|
19232
|
+
function breakDetail(b) {
|
|
19233
|
+
return "- " + fence(b.label) + ` \u2014 ${b.change} ${b.type ?? "node"}; ${b.dependentCount} observed inbound dependent${plural(b.dependentCount)}, ${b.callCount} observed outbound call${plural(b.callCount)}. Provenance: OBSERVED (live traffic on the connected host).`;
|
|
19234
|
+
}
|
|
19235
|
+
function renderVerdict({
|
|
19236
|
+
graph,
|
|
19237
|
+
delta,
|
|
19238
|
+
changedFiles = [],
|
|
19239
|
+
divergences = [],
|
|
19240
|
+
observedBreaks = [],
|
|
19241
|
+
tone = "loud",
|
|
19242
|
+
sniperDispatchUrl = "",
|
|
19243
|
+
graphDiffUrl = ""
|
|
19244
|
+
}) {
|
|
19245
|
+
const copy = TONE_COPY[tone] ?? TONE_COPY.loud;
|
|
19246
|
+
const brokenRoutes = new Set(
|
|
19247
|
+
observedBreaks.filter((b) => b.type === "RouteNode").map((b) => b.label)
|
|
19248
|
+
);
|
|
19249
|
+
const brokenTables = new Set(
|
|
19250
|
+
observedBreaks.filter((b) => b.type === "InfraNode").map((b) => b.label)
|
|
19251
|
+
);
|
|
19252
|
+
const staticConcerns = [];
|
|
19253
|
+
for (const r of delta.routesRemoved ?? []) {
|
|
19254
|
+
if (brokenRoutes.has(r)) continue;
|
|
19255
|
+
staticConcerns.push({
|
|
19256
|
+
headline: `**\u{1F7E0} Removed route** \u2014 ${fence(r)} no longer exists; confirm nothing in-repo still calls it.`,
|
|
19257
|
+
detail: `- Route ${fence(r)} removed. The connected host observed no production traffic on it \u2014 static-only concern.`
|
|
19258
|
+
});
|
|
19259
|
+
}
|
|
19260
|
+
for (const t of delta.tablesRemoved ?? []) {
|
|
19261
|
+
if (brokenTables.has(t)) continue;
|
|
19262
|
+
staticConcerns.push({
|
|
19263
|
+
headline: `**\u{1F7E0} Removed reference** \u2014 ${fence(t)} no longer exists; confirm nothing in-repo still reads it.`,
|
|
19264
|
+
detail: `- Table/infra ${fence(t)} removed. The connected host observed no production traffic on it \u2014 static-only concern.`
|
|
19265
|
+
});
|
|
19266
|
+
}
|
|
19267
|
+
const findings = [
|
|
19268
|
+
...observedBreaks.map((b) => ({ headline: breakHeadline(b), detail: breakDetail(b) })),
|
|
19269
|
+
...divergences.map((d) => ({
|
|
19270
|
+
headline: `**\u{1F7E0} Divergence** \u2014 ${d}`,
|
|
19271
|
+
detail: `- ${d}`
|
|
19272
|
+
})),
|
|
19273
|
+
...staticConcerns
|
|
19274
|
+
];
|
|
19275
|
+
const tier = observedBreaks.length > 0 ? "red" : findings.length > 0 ? "yellow" : "green";
|
|
19276
|
+
const actionLinks = [];
|
|
19277
|
+
if (sniperDispatchUrl)
|
|
19278
|
+
actionLinks.push(`**[\u26A1 Dispatch Sniper to fix these \u2192](${sniperDispatchUrl})**`);
|
|
19279
|
+
if (graphDiffUrl) actionLinks.push(`[Full graph diff \u2192](${graphDiffUrl})`);
|
|
19280
|
+
const L = [MARKER, ""];
|
|
19281
|
+
if (tier === "green") {
|
|
19282
|
+
L.push(
|
|
19283
|
+
copy.green,
|
|
19284
|
+
"",
|
|
19285
|
+
"Nothing this PR touches is observed in production, and it introduces no new divergences.",
|
|
19286
|
+
"",
|
|
19287
|
+
FUSION_NOTE
|
|
19288
|
+
);
|
|
19289
|
+
return { marker: MARKER, body: L.join("\n"), tier };
|
|
19290
|
+
}
|
|
19291
|
+
if (tier === "yellow") {
|
|
19292
|
+
L.push(
|
|
19293
|
+
copy.yellow,
|
|
19294
|
+
"",
|
|
19295
|
+
`${findings.length} minor problem${plural(findings.length)} \u2014 read ${findings.length === 1 ? "it" : "them"} below.`,
|
|
19296
|
+
"",
|
|
19297
|
+
"<details><summary>Read them \u2192</summary>",
|
|
19298
|
+
""
|
|
19299
|
+
);
|
|
19300
|
+
findings.forEach((f, i) => L.push(`${i + 1}. ${f.headline}`));
|
|
19301
|
+
L.push("", ...findings.map((f) => f.detail));
|
|
19302
|
+
L.push("", "</details>");
|
|
19303
|
+
if (actionLinks.length) L.push("", ...actionLinks);
|
|
19304
|
+
L.push("", FUSION_NOTE);
|
|
19305
|
+
return { marker: MARKER, body: L.join("\n"), tier };
|
|
19306
|
+
}
|
|
19307
|
+
L.push(copy.red, "");
|
|
19308
|
+
findings.forEach((f, i) => L.push(`${i + 1}. ${f.headline}`));
|
|
19309
|
+
L.push("", "<details><summary>Full detail \u2192</summary>", "");
|
|
19310
|
+
L.push(...findings.map((f) => f.detail));
|
|
19311
|
+
const anyDelta = (delta.routesAdded?.length ?? 0) + (delta.routesRemoved?.length ?? 0) + (delta.tablesAdded?.length ?? 0) + (delta.tablesRemoved?.length ?? 0);
|
|
19312
|
+
if (anyDelta) {
|
|
19313
|
+
L.push("", "**Graph delta**");
|
|
19314
|
+
if (delta.routesAdded?.length) L.push("- \u2795 routes: " + delta.routesAdded.map(fence).join(", "));
|
|
19315
|
+
if (delta.routesRemoved?.length)
|
|
19316
|
+
L.push("- \u2796 routes: " + delta.routesRemoved.map(fence).join(", "));
|
|
19317
|
+
if (delta.tablesAdded?.length) L.push("- \u2795 tables: " + delta.tablesAdded.map(fence).join(", "));
|
|
19318
|
+
if (delta.tablesRemoved?.length)
|
|
19319
|
+
L.push("- \u2796 tables: " + delta.tablesRemoved.map(fence).join(", "));
|
|
19320
|
+
}
|
|
19321
|
+
const filesInGraph = (changedFiles ?? []).filter((f) => graph?.nodes?.has(f));
|
|
19322
|
+
if (filesInGraph.length) {
|
|
19323
|
+
L.push("", "**Blast radius (in-repo)**");
|
|
19324
|
+
for (const f of filesInGraph) {
|
|
19325
|
+
const deps = blastRadius(graph, f).filter((d) => d.startsWith("file:")).map(shortId);
|
|
19326
|
+
L.push(
|
|
19327
|
+
"- " + fence(shortId(f)) + " \u2192 depended on by " + (deps.length ? deps.map(fence).join(", ") : "_nothing else in-repo_")
|
|
19328
|
+
);
|
|
19329
|
+
}
|
|
19330
|
+
}
|
|
19331
|
+
L.push("", "</details>");
|
|
19332
|
+
if (actionLinks.length) L.push("", ...actionLinks);
|
|
19333
|
+
L.push("", FUSION_NOTE);
|
|
19334
|
+
return { marker: MARKER, body: L.join("\n"), tier };
|
|
19335
|
+
}
|
|
19336
|
+
|
|
19337
|
+
// src/pr-verdict.ts
|
|
19338
|
+
var import_types65 = require("@neat.is/types");
|
|
19339
|
+
|
|
19340
|
+
// src/connectors/hosted-repos.ts
|
|
18936
19341
|
init_cjs_shims();
|
|
18937
19342
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
18938
|
-
var
|
|
18939
|
-
var
|
|
19343
|
+
var import_node_path76 = __toESM(require("path"), 1);
|
|
19344
|
+
var import_promises = require("fs/promises");
|
|
19345
|
+
var DEFAULT_SYNC_INTERVAL_MS = 5 * 6e4;
|
|
19346
|
+
var CLONE_TIMEOUT_MS = 5 * 6e4;
|
|
19347
|
+
function scrubToken(s) {
|
|
19348
|
+
return s.replace(/x-access-token:[^@\s]+@/g, "x-access-token:***@");
|
|
19349
|
+
}
|
|
19350
|
+
var defaultCloneCommit = async (cloneUrl, sha, destDir) => {
|
|
19351
|
+
const [{ default: git }, httpMod, fs42] = await Promise.all([
|
|
19352
|
+
import("isomorphic-git"),
|
|
19353
|
+
import("isomorphic-git/http/node"),
|
|
19354
|
+
import("fs")
|
|
19355
|
+
]);
|
|
19356
|
+
const http = httpMod.default ?? httpMod;
|
|
19357
|
+
const parsed = new URL(cloneUrl);
|
|
19358
|
+
const password = parsed.password || parsed.username;
|
|
19359
|
+
const username = parsed.password ? parsed.username : "x-access-token";
|
|
19360
|
+
const cleanUrl = `${parsed.protocol}//${parsed.host}${parsed.pathname}`;
|
|
19361
|
+
const clone = (async () => {
|
|
19362
|
+
await git.init({ fs: fs42, dir: destDir });
|
|
19363
|
+
await git.addRemote({ fs: fs42, dir: destDir, remote: "origin", url: cleanUrl });
|
|
19364
|
+
await git.fetch({
|
|
19365
|
+
fs: fs42,
|
|
19366
|
+
http,
|
|
19367
|
+
dir: destDir,
|
|
19368
|
+
remote: "origin",
|
|
19369
|
+
// A full SHA is fetched as itself; the remote doesn't have to advertise a ref that points at it.
|
|
19370
|
+
ref: sha,
|
|
19371
|
+
remoteRef: sha,
|
|
19372
|
+
singleBranch: true,
|
|
19373
|
+
depth: 1,
|
|
19374
|
+
tags: false,
|
|
19375
|
+
...password ? { onAuth: () => ({ username, password }) } : {}
|
|
19376
|
+
});
|
|
19377
|
+
await git.checkout({ fs: fs42, dir: destDir, ref: sha, force: true });
|
|
19378
|
+
})();
|
|
19379
|
+
clone.catch(() => {
|
|
19380
|
+
});
|
|
19381
|
+
let timer;
|
|
19382
|
+
const timeout = new Promise((_, reject) => {
|
|
19383
|
+
timer = setTimeout(() => reject(new Error(`clone timed out after ${CLONE_TIMEOUT_MS}ms`)), CLONE_TIMEOUT_MS);
|
|
19384
|
+
});
|
|
19385
|
+
try {
|
|
19386
|
+
await Promise.race([clone, timeout]);
|
|
19387
|
+
} finally {
|
|
19388
|
+
if (timer) clearTimeout(timer);
|
|
19389
|
+
}
|
|
19390
|
+
};
|
|
19391
|
+
function scrubCloneToken(s) {
|
|
19392
|
+
return scrubToken(s);
|
|
19393
|
+
}
|
|
19394
|
+
|
|
19395
|
+
// src/pr-verdict.ts
|
|
19396
|
+
var DEFAULT_PR_VERDICT_TIMEOUT_MS = 24e4;
|
|
19397
|
+
var MAX_CHANGED_FILES = 3e3;
|
|
19398
|
+
var PrVerdictError = class extends Error {
|
|
19399
|
+
constructor(message, status2, stage, details) {
|
|
19400
|
+
super(message);
|
|
19401
|
+
this.status = status2;
|
|
19402
|
+
this.stage = stage;
|
|
19403
|
+
this.details = details;
|
|
19404
|
+
this.name = "PrVerdictError";
|
|
19405
|
+
}
|
|
19406
|
+
status;
|
|
19407
|
+
stage;
|
|
19408
|
+
details;
|
|
19409
|
+
};
|
|
19410
|
+
function parsePrVerdictRequest(body) {
|
|
19411
|
+
const parsed = import_types65.PrVerdictBodySchema.safeParse(body);
|
|
19412
|
+
if (!parsed.success) {
|
|
19413
|
+
throw new PrVerdictError("invalid pr-verdict body", 400, void 0, parsed.error.issues);
|
|
19414
|
+
}
|
|
19415
|
+
const { changedFiles, tone, ...rest } = parsed.data;
|
|
19416
|
+
return {
|
|
19417
|
+
...rest,
|
|
19418
|
+
...changedFiles ? { changedFiles: changedFiles.slice(0, MAX_CHANGED_FILES) } : {},
|
|
19419
|
+
...tone ? { tone } : {}
|
|
19420
|
+
};
|
|
19421
|
+
}
|
|
19422
|
+
function createPrVerdictRunner(deps = {}) {
|
|
19423
|
+
let busy = false;
|
|
19424
|
+
return async (req, ctx) => {
|
|
19425
|
+
if (busy) throw new PrVerdictError("a verdict is already being computed on this daemon", 429);
|
|
19426
|
+
busy = true;
|
|
19427
|
+
const timeoutMs = deps.timeoutMs ?? DEFAULT_PR_VERDICT_TIMEOUT_MS;
|
|
19428
|
+
const deadline = (deps.now ?? Date.now)() + timeoutMs;
|
|
19429
|
+
const work = computePrVerdict(req, ctx, deps, deadline).finally(() => {
|
|
19430
|
+
busy = false;
|
|
19431
|
+
});
|
|
19432
|
+
let timer;
|
|
19433
|
+
const timeout = new Promise((_, reject) => {
|
|
19434
|
+
timer = setTimeout(
|
|
19435
|
+
() => reject(new PrVerdictError(`verdict did not finish within ${timeoutMs}ms`, 504)),
|
|
19436
|
+
timeoutMs
|
|
19437
|
+
);
|
|
19438
|
+
});
|
|
19439
|
+
try {
|
|
19440
|
+
return await Promise.race([work, timeout]);
|
|
19441
|
+
} finally {
|
|
19442
|
+
if (timer) clearTimeout(timer);
|
|
19443
|
+
work.catch(() => {
|
|
19444
|
+
});
|
|
19445
|
+
}
|
|
19446
|
+
};
|
|
19447
|
+
}
|
|
19448
|
+
async function computePrVerdict(req, ctx, deps, deadline) {
|
|
19449
|
+
const now = deps.now ?? Date.now;
|
|
19450
|
+
const startedAt = now();
|
|
19451
|
+
const cloneCommit = deps.cloneCommit ?? defaultCloneCommit;
|
|
19452
|
+
const extract = deps.extract ?? extractFromDirectory;
|
|
19453
|
+
const root = await import_node_fs42.promises.mkdtemp(import_node_path77.default.join(deps.tmpRoot ?? import_node_os5.default.tmpdir(), "neat-pr-"));
|
|
19454
|
+
const baseDir = import_node_path77.default.join(root, "base");
|
|
19455
|
+
const headDir = import_node_path77.default.join(root, "head");
|
|
19456
|
+
const stage = async (name, run) => {
|
|
19457
|
+
const left = deadline - now();
|
|
19458
|
+
if (left <= 0) throw new PrVerdictError(`verdict ran out of time before ${name}`, 504, name);
|
|
19459
|
+
const work = run();
|
|
19460
|
+
work.catch(() => {
|
|
19461
|
+
});
|
|
19462
|
+
let timer;
|
|
19463
|
+
const expired = new Promise((_, reject) => {
|
|
19464
|
+
timer = setTimeout(() => reject(new PrVerdictError(`verdict ran out of time during ${name}`, 504, name)), left);
|
|
19465
|
+
});
|
|
19466
|
+
try {
|
|
19467
|
+
return await Promise.race([work, expired]);
|
|
19468
|
+
} catch (err) {
|
|
19469
|
+
if (err instanceof PrVerdictError) throw err;
|
|
19470
|
+
const why = scrubCloneToken(err.message ?? String(err)).slice(0, 300);
|
|
19471
|
+
throw new PrVerdictError(`${name} failed \u2014 ${why}`, 422, name);
|
|
19472
|
+
} finally {
|
|
19473
|
+
if (timer) clearTimeout(timer);
|
|
19474
|
+
}
|
|
19475
|
+
};
|
|
19476
|
+
const extractOpts = { source: `${req.owner}/${req.name}`, announce: false };
|
|
19477
|
+
try {
|
|
19478
|
+
await import_node_fs42.promises.mkdir(baseDir);
|
|
19479
|
+
await import_node_fs42.promises.mkdir(headDir);
|
|
19480
|
+
await stage("clone-base", () => cloneCommit(req.cloneUrl, req.baseSha, baseDir));
|
|
19481
|
+
await stage("clone-head", () => cloneCommit(req.cloneUrl, req.headSha, headDir));
|
|
19482
|
+
const baseGraph = makeGraph();
|
|
19483
|
+
const headGraph = makeGraph();
|
|
19484
|
+
await stage("extract-base", () => extract(baseGraph, baseDir, extractOpts));
|
|
19485
|
+
await stage("extract-head", () => extract(headGraph, headDir, extractOpts));
|
|
19486
|
+
const base = graphFromExport(baseGraph.export());
|
|
19487
|
+
const head = graphFromExport(headGraph.export());
|
|
19488
|
+
const delta = diffGraphs(base, head);
|
|
19489
|
+
const changedPaths = req.changedFiles ?? await changedPathsBetween(baseDir, headDir);
|
|
19490
|
+
const changedFiles = changedFileNodeIds(head, changedPaths);
|
|
19491
|
+
const divergences = formatDivergences(
|
|
19492
|
+
computeDivergences(ctx.liveGraph, { incidents: ctx.incidents ?? [] }),
|
|
19493
|
+
changedNodeIds(base, head)
|
|
19494
|
+
);
|
|
19495
|
+
const observedBreaks = [];
|
|
19496
|
+
for (const node of changedNodesForObservedScan(base, head)) {
|
|
19497
|
+
if (!ctx.liveGraph.hasNode(node.id)) continue;
|
|
19498
|
+
const found = observedBreakFrom(node, getObservedDependencies(ctx.liveGraph, node.id));
|
|
19499
|
+
if (found) observedBreaks.push(found);
|
|
19500
|
+
}
|
|
19501
|
+
const { marker, body } = renderVerdict({
|
|
19502
|
+
graph: head,
|
|
19503
|
+
delta,
|
|
19504
|
+
changedFiles,
|
|
19505
|
+
divergences,
|
|
19506
|
+
observedBreaks,
|
|
19507
|
+
tone: req.tone ?? "loud"
|
|
19508
|
+
});
|
|
19509
|
+
return {
|
|
19510
|
+
marker,
|
|
19511
|
+
body,
|
|
19512
|
+
base: { sha: req.baseSha, nodes: baseGraph.order, edges: baseGraph.size },
|
|
19513
|
+
head: { sha: req.headSha, nodes: headGraph.order, edges: headGraph.size },
|
|
19514
|
+
changedFiles: changedFiles.length,
|
|
19515
|
+
observedBreaks: observedBreaks.length,
|
|
19516
|
+
divergences: divergences.length,
|
|
19517
|
+
durationMs: now() - startedAt
|
|
19518
|
+
};
|
|
19519
|
+
} finally {
|
|
19520
|
+
await import_node_fs42.promises.rm(root, { recursive: true, force: true }).catch(() => {
|
|
19521
|
+
});
|
|
19522
|
+
}
|
|
19523
|
+
}
|
|
19524
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "neat-out"]);
|
|
19525
|
+
async function hashTree(dir) {
|
|
19526
|
+
const out = /* @__PURE__ */ new Map();
|
|
19527
|
+
const walk11 = async (rel) => {
|
|
19528
|
+
const entries = await import_node_fs42.promises.readdir(import_node_path77.default.join(dir, rel), { withFileTypes: true }).catch(() => []);
|
|
19529
|
+
for (const entry of entries) {
|
|
19530
|
+
const child = rel ? `${rel}/${entry.name}` : entry.name;
|
|
19531
|
+
if (entry.isDirectory()) {
|
|
19532
|
+
if (!SKIP_DIRS.has(entry.name)) await walk11(child);
|
|
19533
|
+
} else if (entry.isFile()) {
|
|
19534
|
+
const bytes = await import_node_fs42.promises.readFile(import_node_path77.default.join(dir, child)).catch(() => null);
|
|
19535
|
+
if (bytes) out.set(child, (0, import_node_crypto3.createHash)("sha1").update(bytes).digest("hex"));
|
|
19536
|
+
}
|
|
19537
|
+
}
|
|
19538
|
+
};
|
|
19539
|
+
await walk11("");
|
|
19540
|
+
return out;
|
|
19541
|
+
}
|
|
19542
|
+
async function changedPathsBetween(baseDir, headDir) {
|
|
19543
|
+
const [base, head] = await Promise.all([hashTree(baseDir), hashTree(headDir)]);
|
|
19544
|
+
const changed = [];
|
|
19545
|
+
for (const [file, hash] of head) if (base.get(file) !== hash) changed.push(file);
|
|
19546
|
+
for (const file of base.keys()) if (!head.has(file)) changed.push(file);
|
|
19547
|
+
return changed.sort().slice(0, MAX_CHANGED_FILES);
|
|
19548
|
+
}
|
|
19549
|
+
|
|
19550
|
+
// src/connectors-config.ts
|
|
19551
|
+
init_cjs_shims();
|
|
19552
|
+
var import_node_os6 = __toESM(require("os"), 1);
|
|
19553
|
+
var import_node_path78 = __toESM(require("path"), 1);
|
|
19554
|
+
var import_node_fs43 = require("fs");
|
|
18940
19555
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
18941
19556
|
var EnvRefUnsetError = class extends Error {
|
|
18942
19557
|
ref;
|
|
@@ -18950,17 +19565,17 @@ var EnvRefUnsetError = class extends Error {
|
|
|
18950
19565
|
};
|
|
18951
19566
|
function neatHome2() {
|
|
18952
19567
|
const override = process.env.NEAT_HOME;
|
|
18953
|
-
if (override && override.length > 0) return
|
|
18954
|
-
return
|
|
19568
|
+
if (override && override.length > 0) return import_node_path78.default.resolve(override);
|
|
19569
|
+
return import_node_path78.default.join(import_node_os6.default.homedir(), ".neat");
|
|
18955
19570
|
}
|
|
18956
19571
|
function connectorsConfigPath(home = neatHome2()) {
|
|
18957
|
-
return
|
|
19572
|
+
return import_node_path78.default.join(home, "connectors.json");
|
|
18958
19573
|
}
|
|
18959
19574
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
18960
19575
|
async function warnIfModeLooserThan0600(file) {
|
|
18961
19576
|
if (process.platform === "win32") return;
|
|
18962
19577
|
try {
|
|
18963
|
-
const stat = await
|
|
19578
|
+
const stat = await import_node_fs43.promises.stat(file);
|
|
18964
19579
|
if ((stat.mode & MODE_MASK_LOOSER_THAN_0600) !== 0) {
|
|
18965
19580
|
const mode = (stat.mode & 511).toString(8).padStart(3, "0");
|
|
18966
19581
|
console.warn(
|
|
@@ -18974,7 +19589,7 @@ async function readConnectorsConfig(home = neatHome2()) {
|
|
|
18974
19589
|
const file = connectorsConfigPath(home);
|
|
18975
19590
|
let raw;
|
|
18976
19591
|
try {
|
|
18977
|
-
raw = await
|
|
19592
|
+
raw = await import_node_fs43.promises.readFile(file, "utf8");
|
|
18978
19593
|
} catch (err) {
|
|
18979
19594
|
if (err.code === "ENOENT") {
|
|
18980
19595
|
return { version: CONNECTORS_CONFIG_VERSION, connectors: [] };
|
|
@@ -19141,15 +19756,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
|
|
|
19141
19756
|
|
|
19142
19757
|
// src/connectors/index.ts
|
|
19143
19758
|
init_cjs_shims();
|
|
19144
|
-
var
|
|
19759
|
+
var import_types66 = require("@neat.is/types");
|
|
19145
19760
|
var NO_ENV = "unknown";
|
|
19146
19761
|
function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
19147
19762
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
19148
19763
|
const sites = [];
|
|
19149
19764
|
for (const edgeId of graph.inboundEdges(targetNodeId)) {
|
|
19150
19765
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
19151
|
-
if (edge.provenance !==
|
|
19152
|
-
const parsed = (0,
|
|
19766
|
+
if (edge.provenance !== import_types66.Provenance.EXTRACTED) continue;
|
|
19767
|
+
const parsed = (0, import_types66.parseFileId)(edge.source);
|
|
19153
19768
|
if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
|
|
19154
19769
|
const site = { relPath: edge.evidence.file };
|
|
19155
19770
|
if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
|
|
@@ -19160,7 +19775,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
|
19160
19775
|
function routeCallSiteFor(graph, targetNodeId) {
|
|
19161
19776
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
19162
19777
|
const attrs = graph.getNodeAttributes(targetNodeId);
|
|
19163
|
-
if (attrs.type !==
|
|
19778
|
+
if (attrs.type !== import_types66.NodeType.RouteNode || !attrs.path) return void 0;
|
|
19164
19779
|
const site = { relPath: attrs.path };
|
|
19165
19780
|
if (attrs.line !== void 0) site.line = attrs.line;
|
|
19166
19781
|
return site;
|
|
@@ -19246,7 +19861,7 @@ init_cjs_shims();
|
|
|
19246
19861
|
|
|
19247
19862
|
// src/connectors/gcp-auth.ts
|
|
19248
19863
|
init_cjs_shims();
|
|
19249
|
-
var
|
|
19864
|
+
var import_node_crypto4 = require("crypto");
|
|
19250
19865
|
|
|
19251
19866
|
// src/connectors/junction.ts
|
|
19252
19867
|
init_cjs_shims();
|
|
@@ -19558,7 +20173,7 @@ function buildServiceAccountAssertion(key, scope, tokenUri, nowMs) {
|
|
|
19558
20173
|
exp: iat + ASSERTION_LIFETIME_S
|
|
19559
20174
|
};
|
|
19560
20175
|
const signingInput = `${base64url(JSON.stringify(header))}.${base64url(JSON.stringify(claims))}`;
|
|
19561
|
-
const signature = (0,
|
|
20176
|
+
const signature = (0, import_node_crypto4.createSign)("RSA-SHA256").update(signingInput).end().sign(key.private_key);
|
|
19562
20177
|
return `${signingInput}.${base64url(signature)}`;
|
|
19563
20178
|
}
|
|
19564
20179
|
async function mintGcpAccessToken(key, scope, opts = {}) {
|
|
@@ -19728,10 +20343,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
19728
20343
|
// src/connectors/supabase/map.ts
|
|
19729
20344
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
19730
20345
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
19731
|
-
function targetFromRestPath(
|
|
19732
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
20346
|
+
function targetFromRestPath(path81) {
|
|
20347
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path81);
|
|
19733
20348
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
19734
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
20349
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path81);
|
|
19735
20350
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
19736
20351
|
return null;
|
|
19737
20352
|
}
|
|
@@ -19842,23 +20457,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
19842
20457
|
|
|
19843
20458
|
// src/connectors/supabase/resolve.ts
|
|
19844
20459
|
init_cjs_shims();
|
|
19845
|
-
var
|
|
20460
|
+
var import_types68 = require("@neat.is/types");
|
|
19846
20461
|
function createSupabaseResolveTarget(graph, config) {
|
|
19847
20462
|
return (signal, _ctx) => {
|
|
19848
20463
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
19849
20464
|
return null;
|
|
19850
20465
|
}
|
|
19851
|
-
const subResourceId = (0,
|
|
20466
|
+
const subResourceId = (0, import_types68.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
19852
20467
|
if (graph.hasNode(subResourceId)) {
|
|
19853
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
20468
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types68.EdgeType.CALLS };
|
|
19854
20469
|
}
|
|
19855
|
-
const bareResourceId = (0,
|
|
20470
|
+
const bareResourceId = (0, import_types68.infraId)(signal.targetKind, signal.targetName);
|
|
19856
20471
|
if (graph.hasNode(bareResourceId)) {
|
|
19857
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
20472
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types68.EdgeType.CALLS };
|
|
19858
20473
|
}
|
|
19859
|
-
const projectLevelId = (0,
|
|
20474
|
+
const projectLevelId = (0, import_types68.infraId)("supabase", config.nodeRef);
|
|
19860
20475
|
if (graph.hasNode(projectLevelId)) {
|
|
19861
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
20476
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types68.EdgeType.CALLS };
|
|
19862
20477
|
}
|
|
19863
20478
|
return null;
|
|
19864
20479
|
};
|
|
@@ -19951,7 +20566,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
19951
20566
|
|
|
19952
20567
|
// src/connectors/railway/index.ts
|
|
19953
20568
|
init_cjs_shims();
|
|
19954
|
-
var
|
|
20569
|
+
var import_types72 = require("@neat.is/types");
|
|
19955
20570
|
|
|
19956
20571
|
// src/connectors/railway/client.ts
|
|
19957
20572
|
init_cjs_shims();
|
|
@@ -20102,7 +20717,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
20102
20717
|
const out = [];
|
|
20103
20718
|
graph.forEachNode((_id, attrs) => {
|
|
20104
20719
|
const node = attrs;
|
|
20105
|
-
if (node.type !==
|
|
20720
|
+
if (node.type !== import_types72.NodeType.RouteNode) return;
|
|
20106
20721
|
const route = attrs;
|
|
20107
20722
|
if (route.service !== serviceName) return;
|
|
20108
20723
|
out.push({
|
|
@@ -20206,12 +20821,12 @@ function createRailwayResolveTarget(config) {
|
|
|
20206
20821
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
20207
20822
|
if (!serviceName) return null;
|
|
20208
20823
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
20209
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
20824
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types72.EdgeType.CALLS };
|
|
20210
20825
|
}
|
|
20211
20826
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
20212
20827
|
const peerName = config.serviceNameById[signal.targetName];
|
|
20213
20828
|
if (!peerName) return null;
|
|
20214
|
-
return { targetNodeId: (0,
|
|
20829
|
+
return { targetNodeId: (0, import_types72.serviceId)(peerName), serviceName, edgeType: import_types72.EdgeType.CONNECTS_TO };
|
|
20215
20830
|
}
|
|
20216
20831
|
return null;
|
|
20217
20832
|
};
|
|
@@ -20335,9 +20950,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
20335
20950
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
20336
20951
|
if (secondSep === -1) return null;
|
|
20337
20952
|
const method = rest.slice(0, secondSep);
|
|
20338
|
-
const
|
|
20339
|
-
if (!resourceName || !method || !
|
|
20340
|
-
return { resourceName, method, path:
|
|
20953
|
+
const path81 = rest.slice(secondSep + 1);
|
|
20954
|
+
if (!resourceName || !method || !path81) return null;
|
|
20955
|
+
return { resourceName, method, path: path81 };
|
|
20341
20956
|
}
|
|
20342
20957
|
function resourceNameFor(type, labels) {
|
|
20343
20958
|
if (!labels) return null;
|
|
@@ -20375,14 +20990,14 @@ function mapLogEntryToSignal(entry) {
|
|
|
20375
20990
|
if (!req) return null;
|
|
20376
20991
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
20377
20992
|
const method = req.requestMethod.toUpperCase();
|
|
20378
|
-
const
|
|
20379
|
-
if (
|
|
20993
|
+
const path81 = pathFromRequestUrl(req.requestUrl);
|
|
20994
|
+
if (path81 === null) return null;
|
|
20380
20995
|
const timestamp = entry.timestamp;
|
|
20381
20996
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
20382
20997
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
|
|
20383
20998
|
return {
|
|
20384
20999
|
targetKind: resourceType,
|
|
20385
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
21000
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path81 }),
|
|
20386
21001
|
callCount: 1,
|
|
20387
21002
|
errorCount: isError ? 1 : 0,
|
|
20388
21003
|
lastObservedIso: timestamp
|
|
@@ -20399,26 +21014,26 @@ function mapLogEntriesToSignals(entries) {
|
|
|
20399
21014
|
|
|
20400
21015
|
// src/connectors/firebase/resolve.ts
|
|
20401
21016
|
init_cjs_shims();
|
|
20402
|
-
var
|
|
21017
|
+
var import_types74 = require("@neat.is/types");
|
|
20403
21018
|
|
|
20404
21019
|
// src/connectors/infer-service.ts
|
|
20405
21020
|
init_cjs_shims();
|
|
20406
|
-
var
|
|
21021
|
+
var import_types73 = require("@neat.is/types");
|
|
20407
21022
|
function normalizeName(name) {
|
|
20408
21023
|
return name.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
20409
21024
|
}
|
|
20410
|
-
function inferServiceName(graph, resourceName, method,
|
|
21025
|
+
function inferServiceName(graph, resourceName, method, path81) {
|
|
20411
21026
|
const wantedMethod = method.toUpperCase();
|
|
20412
|
-
const wantedPath = normalizePathTemplate(
|
|
21027
|
+
const wantedPath = normalizePathTemplate(path81);
|
|
20413
21028
|
const services = [];
|
|
20414
21029
|
const owners = /* @__PURE__ */ new Set();
|
|
20415
21030
|
graph.forEachNode((_id, attrs) => {
|
|
20416
21031
|
const node = attrs;
|
|
20417
|
-
if (node.type ===
|
|
21032
|
+
if (node.type === import_types73.NodeType.ServiceNode && typeof node.name === "string") {
|
|
20418
21033
|
services.push(node.name);
|
|
20419
21034
|
return;
|
|
20420
21035
|
}
|
|
20421
|
-
if (node.type !==
|
|
21036
|
+
if (node.type !== import_types73.NodeType.RouteNode) return;
|
|
20422
21037
|
const route = attrs;
|
|
20423
21038
|
if (!route.pathTemplate || normalizePathTemplate(route.pathTemplate) !== wantedPath) return;
|
|
20424
21039
|
const routeMethod = route.method.toUpperCase();
|
|
@@ -20445,7 +21060,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
20445
21060
|
const entries = [];
|
|
20446
21061
|
graph.forEachNode((_id, attrs) => {
|
|
20447
21062
|
const node = attrs;
|
|
20448
|
-
if (node.type !==
|
|
21063
|
+
if (node.type !== import_types74.NodeType.RouteNode) return;
|
|
20449
21064
|
const route = attrs;
|
|
20450
21065
|
if (route.service !== serviceName) return;
|
|
20451
21066
|
entries.push({
|
|
@@ -20477,7 +21092,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
20477
21092
|
return {
|
|
20478
21093
|
targetNodeId: match.routeNodeId,
|
|
20479
21094
|
serviceName,
|
|
20480
|
-
edgeType:
|
|
21095
|
+
edgeType: import_types74.EdgeType.CALLS
|
|
20481
21096
|
};
|
|
20482
21097
|
};
|
|
20483
21098
|
}
|
|
@@ -20510,11 +21125,11 @@ init_cjs_shims();
|
|
|
20510
21125
|
|
|
20511
21126
|
// src/connectors/cloudflare/connector.ts
|
|
20512
21127
|
init_cjs_shims();
|
|
20513
|
-
var
|
|
21128
|
+
var import_types76 = require("@neat.is/types");
|
|
20514
21129
|
|
|
20515
21130
|
// src/connectors/cloudflare/client.ts
|
|
20516
21131
|
init_cjs_shims();
|
|
20517
|
-
var
|
|
21132
|
+
var import_node_crypto5 = require("crypto");
|
|
20518
21133
|
var DEFAULT_BASE_URL = "https://api.cloudflare.com/client/v4";
|
|
20519
21134
|
var DEFAULT_EVENT_LIMIT = 1e3;
|
|
20520
21135
|
async function queryWorkerInvocations(ctx, config, window, fetchImpl = fetch) {
|
|
@@ -20527,7 +21142,7 @@ async function queryWorkerInvocations(ctx, config, window, fetchImpl = fetch) {
|
|
|
20527
21142
|
const body = {
|
|
20528
21143
|
// Cloudflare's schema requires an identifier per query even for an
|
|
20529
21144
|
// ad-hoc, unsaved one — a fresh id per tick, never reused.
|
|
20530
|
-
queryId: `neat-connector-${(0,
|
|
21145
|
+
queryId: `neat-connector-${(0, import_node_crypto5.randomUUID)()}`,
|
|
20531
21146
|
timeframe: { from: window.fromMs, to: window.toMs },
|
|
20532
21147
|
view: "events",
|
|
20533
21148
|
limit: config.eventLimit ?? DEFAULT_EVENT_LIMIT,
|
|
@@ -20626,7 +21241,7 @@ function mapEventToSignal(event) {
|
|
|
20626
21241
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
20627
21242
|
const statusCode = metadata?.statusCode;
|
|
20628
21243
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
20629
|
-
const
|
|
21244
|
+
const path81 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
20630
21245
|
return {
|
|
20631
21246
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
20632
21247
|
targetName: scriptName,
|
|
@@ -20634,7 +21249,7 @@ function mapEventToSignal(event) {
|
|
|
20634
21249
|
errorCount: isError ? 1 : 0,
|
|
20635
21250
|
lastObservedIso: observedAt.toISOString(),
|
|
20636
21251
|
method,
|
|
20637
|
-
...
|
|
21252
|
+
...path81 ? { path: path81 } : {},
|
|
20638
21253
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
20639
21254
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
20640
21255
|
};
|
|
@@ -20674,19 +21289,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
20674
21289
|
graph.forEachNode((id, attrs) => {
|
|
20675
21290
|
if (found) return;
|
|
20676
21291
|
const a = attrs;
|
|
20677
|
-
if (a.type ===
|
|
21292
|
+
if (a.type === import_types76.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
20678
21293
|
found = id;
|
|
20679
21294
|
}
|
|
20680
21295
|
});
|
|
20681
21296
|
return found;
|
|
20682
21297
|
}
|
|
20683
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
20684
|
-
const normalizedPath = normalizePathTemplate(
|
|
21298
|
+
function findMatchingRouteNode(graph, serviceName, method, path81) {
|
|
21299
|
+
const normalizedPath = normalizePathTemplate(path81);
|
|
20685
21300
|
let found = null;
|
|
20686
21301
|
graph.forEachNode((id, attrs) => {
|
|
20687
21302
|
if (found) return;
|
|
20688
21303
|
const a = attrs;
|
|
20689
|
-
if (a.type !==
|
|
21304
|
+
if (a.type !== import_types76.NodeType.RouteNode || a.service !== serviceName) return;
|
|
20690
21305
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
20691
21306
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
20692
21307
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -20698,18 +21313,18 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
20698
21313
|
return (signal) => {
|
|
20699
21314
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
20700
21315
|
const scriptName = signal.targetName;
|
|
20701
|
-
const { method, path:
|
|
21316
|
+
const { method, path: path81 } = signal;
|
|
20702
21317
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
20703
|
-
if (!method || !
|
|
20704
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
21318
|
+
if (!method || !path81) return wholeFileId;
|
|
21319
|
+
return findMatchingRouteNode(graph, serviceName, method, path81) ?? wholeFileId;
|
|
20705
21320
|
};
|
|
20706
21321
|
const mapping = config.workers?.[scriptName];
|
|
20707
21322
|
if (mapping) {
|
|
20708
|
-
const wholeFileId = (0,
|
|
21323
|
+
const wholeFileId = (0, import_types76.fileId)(mapping.service, mapping.entryFile);
|
|
20709
21324
|
return {
|
|
20710
21325
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
20711
21326
|
serviceName: mapping.service,
|
|
20712
|
-
edgeType:
|
|
21327
|
+
edgeType: import_types76.EdgeType.CALLS
|
|
20713
21328
|
};
|
|
20714
21329
|
}
|
|
20715
21330
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -20718,13 +21333,13 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
20718
21333
|
return {
|
|
20719
21334
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
20720
21335
|
serviceName: fileNode.service,
|
|
20721
|
-
edgeType:
|
|
21336
|
+
edgeType: import_types76.EdgeType.CALLS
|
|
20722
21337
|
};
|
|
20723
21338
|
}
|
|
20724
21339
|
return {
|
|
20725
|
-
targetNodeId: (0,
|
|
21340
|
+
targetNodeId: (0, import_types76.infraId)("cloudflare-worker", scriptName),
|
|
20726
21341
|
serviceName: scriptName,
|
|
20727
|
-
edgeType:
|
|
21342
|
+
edgeType: import_types76.EdgeType.CALLS,
|
|
20728
21343
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
20729
21344
|
};
|
|
20730
21345
|
};
|
|
@@ -20920,14 +21535,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
|
|
|
20920
21535
|
|
|
20921
21536
|
// src/connectors/neon/resolve.ts
|
|
20922
21537
|
init_cjs_shims();
|
|
20923
|
-
var
|
|
21538
|
+
var import_types80 = require("@neat.is/types");
|
|
20924
21539
|
function createNeonResolveTarget(config) {
|
|
20925
21540
|
return (signal) => {
|
|
20926
21541
|
if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
20927
21542
|
return {
|
|
20928
|
-
targetNodeId: (0,
|
|
21543
|
+
targetNodeId: (0, import_types80.infraId)("sql-table", signal.targetName),
|
|
20929
21544
|
serviceName: config.serviceName,
|
|
20930
|
-
edgeType:
|
|
21545
|
+
edgeType: import_types80.EdgeType.CALLS,
|
|
20931
21546
|
ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
|
|
20932
21547
|
};
|
|
20933
21548
|
};
|
|
@@ -21053,9 +21668,9 @@ function parseCloudRunTargetName(targetName) {
|
|
|
21053
21668
|
const secondSep = rest.indexOf(FIELD_SEP2);
|
|
21054
21669
|
if (secondSep === -1) return null;
|
|
21055
21670
|
const method = rest.slice(0, secondSep);
|
|
21056
|
-
const
|
|
21057
|
-
if (!serviceName || !method || !
|
|
21058
|
-
return { serviceName, method, path:
|
|
21671
|
+
const path81 = rest.slice(secondSep + 1);
|
|
21672
|
+
if (!serviceName || !method || !path81) return null;
|
|
21673
|
+
return { serviceName, method, path: path81 };
|
|
21059
21674
|
}
|
|
21060
21675
|
|
|
21061
21676
|
// src/connectors/cloud-run/map.ts
|
|
@@ -21084,14 +21699,14 @@ function mapLogEntryToSignal2(entry) {
|
|
|
21084
21699
|
if (!req) return null;
|
|
21085
21700
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
21086
21701
|
const method = req.requestMethod.toUpperCase();
|
|
21087
|
-
const
|
|
21088
|
-
if (
|
|
21702
|
+
const path81 = pathFromRequestUrl2(req.requestUrl);
|
|
21703
|
+
if (path81 === null) return null;
|
|
21089
21704
|
const timestamp = entry.timestamp;
|
|
21090
21705
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
21091
21706
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
|
|
21092
21707
|
return {
|
|
21093
21708
|
targetKind: CLOUD_RUN_TARGET_KIND,
|
|
21094
|
-
targetName: packCloudRunTargetName({ serviceName, method, path:
|
|
21709
|
+
targetName: packCloudRunTargetName({ serviceName, method, path: path81 }),
|
|
21095
21710
|
callCount: 1,
|
|
21096
21711
|
errorCount: isError ? 1 : 0,
|
|
21097
21712
|
lastObservedIso: timestamp
|
|
@@ -21108,14 +21723,14 @@ function mapLogEntriesToSignals2(entries) {
|
|
|
21108
21723
|
|
|
21109
21724
|
// src/connectors/cloud-run/resolve.ts
|
|
21110
21725
|
init_cjs_shims();
|
|
21111
|
-
var
|
|
21726
|
+
var import_types84 = require("@neat.is/types");
|
|
21112
21727
|
var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
|
|
21113
21728
|
function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
|
|
21114
21729
|
let found = null;
|
|
21115
21730
|
graph.forEachNode((_id, attrs) => {
|
|
21116
21731
|
if (found) return;
|
|
21117
21732
|
const node = attrs;
|
|
21118
|
-
if (node.type !==
|
|
21733
|
+
if (node.type !== import_types84.NodeType.RouteNode) return;
|
|
21119
21734
|
const route = attrs;
|
|
21120
21735
|
if (route.service !== serviceName || !route.pathTemplate) return;
|
|
21121
21736
|
if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
|
|
@@ -21130,23 +21745,23 @@ function createCloudRunResolveTarget(graph, config) {
|
|
|
21130
21745
|
if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
|
|
21131
21746
|
const identity = parseCloudRunTargetName(signal.targetName);
|
|
21132
21747
|
if (!identity) return null;
|
|
21133
|
-
const { serviceName: gcpServiceName, method, path:
|
|
21134
|
-
const mappedService = config.serviceMap?.[gcpServiceName] ?? (config.inferServices ? inferServiceName(graph, gcpServiceName, method,
|
|
21748
|
+
const { serviceName: gcpServiceName, method, path: path81 } = identity;
|
|
21749
|
+
const mappedService = config.serviceMap?.[gcpServiceName] ?? (config.inferServices ? inferServiceName(graph, gcpServiceName, method, path81) ?? void 0 : void 0);
|
|
21135
21750
|
if (mappedService) {
|
|
21136
21751
|
const routeNodeId = findMatchingRouteNode2(
|
|
21137
21752
|
graph,
|
|
21138
21753
|
mappedService,
|
|
21139
21754
|
method,
|
|
21140
|
-
normalizePathTemplate(
|
|
21755
|
+
normalizePathTemplate(path81)
|
|
21141
21756
|
);
|
|
21142
21757
|
if (routeNodeId) {
|
|
21143
|
-
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType:
|
|
21758
|
+
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types84.EdgeType.CALLS };
|
|
21144
21759
|
}
|
|
21145
21760
|
}
|
|
21146
21761
|
return {
|
|
21147
|
-
targetNodeId: (0,
|
|
21762
|
+
targetNodeId: (0, import_types84.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
|
|
21148
21763
|
serviceName: mappedService ?? gcpServiceName,
|
|
21149
|
-
edgeType:
|
|
21764
|
+
edgeType: import_types84.EdgeType.CALLS,
|
|
21150
21765
|
ensureInfraNode: {
|
|
21151
21766
|
kind: CLOUD_RUN_SERVICE_INFRA_KIND,
|
|
21152
21767
|
name: gcpServiceName,
|
|
@@ -21273,9 +21888,9 @@ function parseGcpLbTargetName(targetName) {
|
|
|
21273
21888
|
const secondSep = rest.indexOf(FIELD_SEP3);
|
|
21274
21889
|
if (secondSep === -1) return null;
|
|
21275
21890
|
const method = rest.slice(0, secondSep);
|
|
21276
|
-
const
|
|
21277
|
-
if (!backendServiceName || !method || !
|
|
21278
|
-
return { backendServiceName, method, path:
|
|
21891
|
+
const path81 = rest.slice(secondSep + 1);
|
|
21892
|
+
if (!backendServiceName || !method || !path81) return null;
|
|
21893
|
+
return { backendServiceName, method, path: path81 };
|
|
21279
21894
|
}
|
|
21280
21895
|
|
|
21281
21896
|
// src/connectors/gcp-lb/map.ts
|
|
@@ -21304,14 +21919,14 @@ function mapLogEntryToSignal3(entry) {
|
|
|
21304
21919
|
if (!req) return null;
|
|
21305
21920
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
21306
21921
|
const method = req.requestMethod.toUpperCase();
|
|
21307
|
-
const
|
|
21308
|
-
if (
|
|
21922
|
+
const path81 = pathFromRequestUrl3(req.requestUrl);
|
|
21923
|
+
if (path81 === null) return null;
|
|
21309
21924
|
const timestamp = entry.timestamp;
|
|
21310
21925
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
21311
21926
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD5;
|
|
21312
21927
|
return {
|
|
21313
21928
|
targetKind: GCP_LB_TARGET_KIND,
|
|
21314
|
-
targetName: packGcpLbTargetName({ backendServiceName, method, path:
|
|
21929
|
+
targetName: packGcpLbTargetName({ backendServiceName, method, path: path81 }),
|
|
21315
21930
|
callCount: 1,
|
|
21316
21931
|
errorCount: isError ? 1 : 0,
|
|
21317
21932
|
lastObservedIso: timestamp
|
|
@@ -21328,14 +21943,14 @@ function mapLogEntriesToSignals3(entries) {
|
|
|
21328
21943
|
|
|
21329
21944
|
// src/connectors/gcp-lb/resolve.ts
|
|
21330
21945
|
init_cjs_shims();
|
|
21331
|
-
var
|
|
21946
|
+
var import_types88 = require("@neat.is/types");
|
|
21332
21947
|
var GCP_LB_BACKEND_INFRA_KIND = "gcp-lb-backend";
|
|
21333
21948
|
function findMatchingRouteNode3(graph, serviceName, method, normalizedPath) {
|
|
21334
21949
|
let found = null;
|
|
21335
21950
|
graph.forEachNode((_id, attrs) => {
|
|
21336
21951
|
if (found) return;
|
|
21337
21952
|
const node = attrs;
|
|
21338
|
-
if (node.type !==
|
|
21953
|
+
if (node.type !== import_types88.NodeType.RouteNode) return;
|
|
21339
21954
|
const route = attrs;
|
|
21340
21955
|
if (route.service !== serviceName || !route.pathTemplate) return;
|
|
21341
21956
|
if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
|
|
@@ -21350,23 +21965,23 @@ function createGcpLbResolveTarget(graph, config) {
|
|
|
21350
21965
|
if (signal.targetKind !== GCP_LB_TARGET_KIND) return null;
|
|
21351
21966
|
const identity = parseGcpLbTargetName(signal.targetName);
|
|
21352
21967
|
if (!identity) return null;
|
|
21353
|
-
const { backendServiceName, method, path:
|
|
21354
|
-
const mappedService = config.backendServiceMap?.[backendServiceName] ?? (config.inferServices ? inferServiceName(graph, backendServiceName, method,
|
|
21968
|
+
const { backendServiceName, method, path: path81 } = identity;
|
|
21969
|
+
const mappedService = config.backendServiceMap?.[backendServiceName] ?? (config.inferServices ? inferServiceName(graph, backendServiceName, method, path81) ?? void 0 : void 0);
|
|
21355
21970
|
if (mappedService) {
|
|
21356
21971
|
const routeNodeId = findMatchingRouteNode3(
|
|
21357
21972
|
graph,
|
|
21358
21973
|
mappedService,
|
|
21359
21974
|
method,
|
|
21360
|
-
normalizePathTemplate(
|
|
21975
|
+
normalizePathTemplate(path81)
|
|
21361
21976
|
);
|
|
21362
21977
|
if (routeNodeId) {
|
|
21363
|
-
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType:
|
|
21978
|
+
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types88.EdgeType.CALLS };
|
|
21364
21979
|
}
|
|
21365
21980
|
}
|
|
21366
21981
|
return {
|
|
21367
|
-
targetNodeId: (0,
|
|
21982
|
+
targetNodeId: (0, import_types88.infraId)(GCP_LB_BACKEND_INFRA_KIND, backendServiceName),
|
|
21368
21983
|
serviceName: mappedService ?? backendServiceName,
|
|
21369
|
-
edgeType:
|
|
21984
|
+
edgeType: import_types88.EdgeType.CALLS,
|
|
21370
21985
|
ensureInfraNode: {
|
|
21371
21986
|
kind: GCP_LB_BACKEND_INFRA_KIND,
|
|
21372
21987
|
name: backendServiceName,
|
|
@@ -21407,7 +22022,7 @@ function createGcpLbConnector(graph, config = {}) {
|
|
|
21407
22022
|
|
|
21408
22023
|
// src/connectors/render/index.ts
|
|
21409
22024
|
init_cjs_shims();
|
|
21410
|
-
var
|
|
22025
|
+
var import_types91 = require("@neat.is/types");
|
|
21411
22026
|
|
|
21412
22027
|
// src/connectors/render/types.ts
|
|
21413
22028
|
init_cjs_shims();
|
|
@@ -21485,7 +22100,7 @@ function buildRenderRouteIndex(graph, serviceName) {
|
|
|
21485
22100
|
const out = [];
|
|
21486
22101
|
graph.forEachNode((_id, attrs) => {
|
|
21487
22102
|
const node = attrs;
|
|
21488
|
-
if (node.type !==
|
|
22103
|
+
if (node.type !== import_types91.NodeType.RouteNode) return;
|
|
21489
22104
|
const route = attrs;
|
|
21490
22105
|
if (route.service !== serviceName) return;
|
|
21491
22106
|
out.push({
|
|
@@ -21570,7 +22185,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
|
|
|
21570
22185
|
function createRenderResolveTarget(config) {
|
|
21571
22186
|
return (signal) => {
|
|
21572
22187
|
if (signal.targetKind === ROUTE_TARGET_KIND2) {
|
|
21573
|
-
return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType:
|
|
22188
|
+
return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types91.EdgeType.CALLS };
|
|
21574
22189
|
}
|
|
21575
22190
|
return null;
|
|
21576
22191
|
};
|
|
@@ -21708,21 +22323,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
|
|
|
21708
22323
|
|
|
21709
22324
|
// src/connectors/planetscale/resolve.ts
|
|
21710
22325
|
init_cjs_shims();
|
|
21711
|
-
var
|
|
22326
|
+
var import_types95 = require("@neat.is/types");
|
|
21712
22327
|
var PLANETSCALE_DATABASE_KIND = "planetscale-database";
|
|
21713
22328
|
function createPlanetscaleResolveTarget(graph, config) {
|
|
21714
22329
|
const databaseName = `${config.organization}/${config.database}`;
|
|
21715
22330
|
return (signal, _ctx) => {
|
|
21716
22331
|
if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
21717
|
-
const tableId = (0,
|
|
22332
|
+
const tableId = (0, import_types95.infraId)("sql-table", signal.targetName);
|
|
21718
22333
|
if (graph.hasNode(tableId)) {
|
|
21719
|
-
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType:
|
|
22334
|
+
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types95.EdgeType.CALLS };
|
|
21720
22335
|
}
|
|
21721
|
-
const providerId = (0,
|
|
22336
|
+
const providerId = (0, import_types95.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
|
|
21722
22337
|
return {
|
|
21723
22338
|
targetNodeId: providerId,
|
|
21724
22339
|
serviceName: config.serviceName,
|
|
21725
|
-
edgeType:
|
|
22340
|
+
edgeType: import_types95.EdgeType.CALLS,
|
|
21726
22341
|
ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
|
|
21727
22342
|
};
|
|
21728
22343
|
};
|
|
@@ -21987,7 +22602,7 @@ function mapBuildsToSignals(builds, serviceName) {
|
|
|
21987
22602
|
|
|
21988
22603
|
// src/connectors/eas/resolve.ts
|
|
21989
22604
|
init_cjs_shims();
|
|
21990
|
-
var
|
|
22605
|
+
var import_types100 = require("@neat.is/types");
|
|
21991
22606
|
var NO_ENV2 = "unknown";
|
|
21992
22607
|
var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
|
|
21993
22608
|
var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
|
|
@@ -22003,8 +22618,8 @@ function configBasenamesForPhase(phase) {
|
|
|
22003
22618
|
function configNodeService(graph, configNodeId) {
|
|
22004
22619
|
for (const edgeId of graph.inboundEdges(configNodeId)) {
|
|
22005
22620
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
22006
|
-
if (edge.type !==
|
|
22007
|
-
const parsed = (0,
|
|
22621
|
+
if (edge.type !== import_types100.EdgeType.CONFIGURED_BY) continue;
|
|
22622
|
+
const parsed = (0, import_types100.parseFileId)(edge.source);
|
|
22008
22623
|
if (parsed) return parsed.service;
|
|
22009
22624
|
}
|
|
22010
22625
|
return null;
|
|
@@ -22015,7 +22630,7 @@ function findConfigNode(graph, basenames, serviceName) {
|
|
|
22015
22630
|
graph.forEachNode((id, attrs) => {
|
|
22016
22631
|
if (scoped) return;
|
|
22017
22632
|
const node = attrs;
|
|
22018
|
-
if (node.type !==
|
|
22633
|
+
if (node.type !== import_types100.NodeType.ConfigNode) return;
|
|
22019
22634
|
if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
|
|
22020
22635
|
if (anyMatch === null) anyMatch = id;
|
|
22021
22636
|
if (configNodeService(graph, id) === serviceName) scoped = id;
|
|
@@ -22032,13 +22647,13 @@ function createEasResolveTarget(graph) {
|
|
|
22032
22647
|
if (basenames.length > 0) {
|
|
22033
22648
|
const configNodeId = findConfigNode(graph, basenames, serviceName);
|
|
22034
22649
|
if (configNodeId) {
|
|
22035
|
-
return { targetNodeId: configNodeId, serviceName, edgeType:
|
|
22650
|
+
return { targetNodeId: configNodeId, serviceName, edgeType: import_types100.EdgeType.CALLS };
|
|
22036
22651
|
}
|
|
22037
22652
|
}
|
|
22038
22653
|
return {
|
|
22039
22654
|
targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
|
|
22040
22655
|
serviceName,
|
|
22041
|
-
edgeType:
|
|
22656
|
+
edgeType: import_types100.EdgeType.CALLS
|
|
22042
22657
|
};
|
|
22043
22658
|
};
|
|
22044
22659
|
}
|
|
@@ -22682,6 +23297,35 @@ function registerRoutes(scope, ctx) {
|
|
|
22682
23297
|
if (!proj) return;
|
|
22683
23298
|
handleSse(req, reply, { project: proj.name });
|
|
22684
23299
|
});
|
|
23300
|
+
scope.get(
|
|
23301
|
+
"/incident-triggers",
|
|
23302
|
+
{ config: BEARER_DELEGATED },
|
|
23303
|
+
(req, reply) => {
|
|
23304
|
+
const token = ctx.incidentStreamToken;
|
|
23305
|
+
if (!token || Buffer.byteLength(token, "utf8") < 32 || token === ctx.generalAuthToken || !ctx.incidentReplayDurable) {
|
|
23306
|
+
return reply.code(503).send({ error: "incident trigger stream unavailable" });
|
|
23307
|
+
}
|
|
23308
|
+
const header = req.headers.authorization;
|
|
23309
|
+
const supplied = typeof header === "string" && header.startsWith("Bearer ") ? Buffer.from(header.slice("Bearer ".length), "utf8") : void 0;
|
|
23310
|
+
const expected = Buffer.from(token, "utf8");
|
|
23311
|
+
if (!supplied || supplied.length !== expected.length || !(0, import_node_crypto6.timingSafeEqual)(supplied, expected)) {
|
|
23312
|
+
return reply.code(401).send({ error: "unauthorized" });
|
|
23313
|
+
}
|
|
23314
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
23315
|
+
if (!proj) return;
|
|
23316
|
+
const errorsPath = errorsPathFor(proj);
|
|
23317
|
+
if (!errorsPath) return reply.code(503).send({ error: "incident replay unavailable" });
|
|
23318
|
+
let replay;
|
|
23319
|
+
try {
|
|
23320
|
+
const cursor = req.headers["last-event-id"];
|
|
23321
|
+
if (Array.isArray(cursor)) throw new Error("invalid cursor");
|
|
23322
|
+
replay = loadIncidentReplay(errorsPath, cursor);
|
|
23323
|
+
} catch {
|
|
23324
|
+
return reply.code(409).send({ error: "incident replay unavailable" });
|
|
23325
|
+
}
|
|
23326
|
+
handleSse(req, reply, { project: proj.name, incidentOnly: true, incidentReplay: replay });
|
|
23327
|
+
}
|
|
23328
|
+
);
|
|
22685
23329
|
if (ctx.scope === "project") {
|
|
22686
23330
|
scope.get("/health", async (req, reply) => {
|
|
22687
23331
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
@@ -22771,11 +23415,11 @@ function registerRoutes(scope, ctx) {
|
|
|
22771
23415
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
22772
23416
|
const parsed = [];
|
|
22773
23417
|
for (const c of candidates) {
|
|
22774
|
-
const r =
|
|
23418
|
+
const r = import_types103.DivergenceTypeSchema.safeParse(c);
|
|
22775
23419
|
if (!r.success) {
|
|
22776
23420
|
return reply.code(400).send({
|
|
22777
23421
|
error: `unknown divergence type "${c}"`,
|
|
22778
|
-
allowed:
|
|
23422
|
+
allowed: import_types103.DivergenceTypeSchema.options
|
|
22779
23423
|
});
|
|
22780
23424
|
}
|
|
22781
23425
|
parsed.push(r.data);
|
|
@@ -22967,7 +23611,7 @@ function registerRoutes(scope, ctx) {
|
|
|
22967
23611
|
const incidents = epath ? await readErrorEvents(epath) : [];
|
|
22968
23612
|
let errorEvent;
|
|
22969
23613
|
if (req.query.errorId) {
|
|
22970
|
-
errorEvent = incidents.find((e) => e.id === req.query.errorId);
|
|
23614
|
+
errorEvent = incidents.find((e) => e.id === req.query.errorId) ?? (epath ? await readErrorEventById(epath, req.query.errorId) : void 0);
|
|
22971
23615
|
if (!errorEvent) {
|
|
22972
23616
|
return reply.code(404).send({ error: "error event not found", id: req.query.errorId });
|
|
22973
23617
|
}
|
|
@@ -23153,6 +23797,26 @@ function registerRoutes(scope, ctx) {
|
|
|
23153
23797
|
}
|
|
23154
23798
|
return reply.code(202).send({ project: proj.name, ...syncNow() });
|
|
23155
23799
|
});
|
|
23800
|
+
scope.post("/pr-verdict", async (req, reply) => {
|
|
23801
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
23802
|
+
if (!proj) return;
|
|
23803
|
+
try {
|
|
23804
|
+
const request = parsePrVerdictRequest(req.body);
|
|
23805
|
+
const epath = errorsPathFor(proj);
|
|
23806
|
+
const incidents = epath ? await readErrorEvents(epath) : [];
|
|
23807
|
+
const verdict = await ctx.prVerdict(request, { liveGraph: proj.graph, incidents });
|
|
23808
|
+
return { project: proj.name, ...verdict };
|
|
23809
|
+
} catch (err) {
|
|
23810
|
+
if (!(err instanceof PrVerdictError)) throw err;
|
|
23811
|
+
if (err.status === 429) void reply.header("retry-after", "30");
|
|
23812
|
+
return reply.code(err.status).send({
|
|
23813
|
+
error: err.message,
|
|
23814
|
+
project: proj.name,
|
|
23815
|
+
...err.stage ? { stage: err.stage } : {},
|
|
23816
|
+
...err.details !== void 0 ? { details: err.details } : {}
|
|
23817
|
+
});
|
|
23818
|
+
}
|
|
23819
|
+
});
|
|
23156
23820
|
scope.post("/graph/scan", async (req, reply) => {
|
|
23157
23821
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
23158
23822
|
if (!proj) return;
|
|
@@ -23192,7 +23856,7 @@ function registerRoutes(scope, ctx) {
|
|
|
23192
23856
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
23193
23857
|
let violations = await log.readAll();
|
|
23194
23858
|
if (req.query.severity) {
|
|
23195
|
-
const sev =
|
|
23859
|
+
const sev = import_types103.PolicySeveritySchema.safeParse(req.query.severity);
|
|
23196
23860
|
if (!sev.success) {
|
|
23197
23861
|
return reply.code(400).send({
|
|
23198
23862
|
error: "invalid severity",
|
|
@@ -23231,7 +23895,7 @@ function registerRoutes(scope, ctx) {
|
|
|
23231
23895
|
scope.post("/policies/check", async (req, reply) => {
|
|
23232
23896
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
23233
23897
|
if (!proj) return;
|
|
23234
|
-
const parsed =
|
|
23898
|
+
const parsed = import_types103.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
23235
23899
|
if (!parsed.success) {
|
|
23236
23900
|
return reply.code(400).send({
|
|
23237
23901
|
error: "invalid /policies/check body",
|
|
@@ -23440,6 +24104,8 @@ async function buildApi(opts) {
|
|
|
23440
24104
|
const authToken = opts.authToken ?? env.authToken;
|
|
23441
24105
|
const trustProxy = opts.trustProxy ?? env.trustProxy;
|
|
23442
24106
|
const publicRead = opts.publicRead ?? env.publicRead;
|
|
24107
|
+
const incidentStreamToken = opts.incidentStreamToken ?? process.env.NEAT_INCIDENT_STREAM_TOKEN;
|
|
24108
|
+
const incidentReplayDurable = opts.incidentReplayDurable ?? (process.env.NEAT_INCIDENT_REPLAY_DURABLE === "1" && Buffer.byteLength(process.env.NEAT_INCIDENT_DURABLE_TOKEN ?? "", "utf8") >= 32);
|
|
23443
24109
|
mountBearerAuth(app, {
|
|
23444
24110
|
token: authToken,
|
|
23445
24111
|
trustProxy,
|
|
@@ -23475,6 +24141,9 @@ async function buildApi(opts) {
|
|
|
23475
24141
|
const routeCtx = {
|
|
23476
24142
|
registry,
|
|
23477
24143
|
startedAt,
|
|
24144
|
+
incidentStreamToken,
|
|
24145
|
+
incidentReplayDurable,
|
|
24146
|
+
generalAuthToken: authToken,
|
|
23478
24147
|
scope: "root",
|
|
23479
24148
|
errorsPathFor,
|
|
23480
24149
|
staleEventsPathFor,
|
|
@@ -23483,6 +24152,7 @@ async function buildApi(opts) {
|
|
|
23483
24152
|
singleProject: opts.singleProject?.name,
|
|
23484
24153
|
connectorsHome: opts.connectorsHome,
|
|
23485
24154
|
repoSync: opts.repoSync,
|
|
24155
|
+
prVerdict: opts.prVerdict ?? createPrVerdictRunner(),
|
|
23486
24156
|
runPoll: opts.runPoll ?? runConnectorPoll
|
|
23487
24157
|
};
|
|
23488
24158
|
app.get("/health", async () => {
|
|
@@ -23587,9 +24257,9 @@ init_otel_grpc();
|
|
|
23587
24257
|
|
|
23588
24258
|
// src/search.ts
|
|
23589
24259
|
init_cjs_shims();
|
|
23590
|
-
var
|
|
23591
|
-
var
|
|
23592
|
-
var
|
|
24260
|
+
var import_node_fs44 = require("fs");
|
|
24261
|
+
var import_node_path79 = __toESM(require("path"), 1);
|
|
24262
|
+
var import_node_crypto7 = require("crypto");
|
|
23593
24263
|
var DEFAULT_LIMIT = 10;
|
|
23594
24264
|
var NOMIC_DIM = 768;
|
|
23595
24265
|
var MINI_LM_DIM = 384;
|
|
@@ -23653,7 +24323,7 @@ function embedText(node) {
|
|
|
23653
24323
|
return parts.join(" ");
|
|
23654
24324
|
}
|
|
23655
24325
|
function attrsHash(node) {
|
|
23656
|
-
return (0,
|
|
24326
|
+
return (0, import_node_crypto7.createHash)("sha1").update(embedText(node)).digest("hex").slice(0, 16);
|
|
23657
24327
|
}
|
|
23658
24328
|
function cosine(a, b) {
|
|
23659
24329
|
if (a.length !== b.length) return 0;
|
|
@@ -23742,7 +24412,7 @@ async function pickEmbedder() {
|
|
|
23742
24412
|
}
|
|
23743
24413
|
async function readCache(cachePath) {
|
|
23744
24414
|
try {
|
|
23745
|
-
const raw = await
|
|
24415
|
+
const raw = await import_node_fs44.promises.readFile(cachePath, "utf8");
|
|
23746
24416
|
const parsed = JSON.parse(raw);
|
|
23747
24417
|
if (parsed.version !== 1) return null;
|
|
23748
24418
|
return parsed;
|
|
@@ -23751,8 +24421,8 @@ async function readCache(cachePath) {
|
|
|
23751
24421
|
}
|
|
23752
24422
|
}
|
|
23753
24423
|
async function writeCache(cachePath, cache) {
|
|
23754
|
-
await
|
|
23755
|
-
await
|
|
24424
|
+
await import_node_fs44.promises.mkdir(import_node_path79.default.dirname(cachePath), { recursive: true });
|
|
24425
|
+
await import_node_fs44.promises.writeFile(cachePath, JSON.stringify(cache));
|
|
23756
24426
|
}
|
|
23757
24427
|
var VectorIndex = class {
|
|
23758
24428
|
constructor(embedder, cachePath) {
|
|
@@ -23966,14 +24636,14 @@ async function bootProject(registry, name, scanPath, baseDir) {
|
|
|
23966
24636
|
async function main() {
|
|
23967
24637
|
const baseDirEnv = process.env.NEAT_OUT_DIR;
|
|
23968
24638
|
const legacyOutPath = process.env.NEAT_OUT_PATH;
|
|
23969
|
-
const baseDir = baseDirEnv ?
|
|
23970
|
-
const defaultScanPath =
|
|
24639
|
+
const baseDir = baseDirEnv ? import_node_path80.default.resolve(baseDirEnv) : legacyOutPath ? import_node_path80.default.resolve(import_node_path80.default.dirname(legacyOutPath)) : import_node_path80.default.resolve("./neat-out");
|
|
24640
|
+
const defaultScanPath = import_node_path80.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
|
|
23971
24641
|
const registry = new Projects();
|
|
23972
24642
|
await bootProject(registry, DEFAULT_PROJECT, defaultScanPath, baseDir);
|
|
23973
24643
|
for (const name of parseExtraProjects(process.env.NEAT_PROJECTS)) {
|
|
23974
24644
|
const envKey = `NEAT_PROJECT_SCAN_PATH_${name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
|
|
23975
24645
|
const projectScan = process.env[envKey];
|
|
23976
|
-
await bootProject(registry, name, projectScan ?
|
|
24646
|
+
await bootProject(registry, name, projectScan ? import_node_path80.default.resolve(projectScan) : void 0, baseDir);
|
|
23977
24647
|
}
|
|
23978
24648
|
const host = process.env.HOST ?? "0.0.0.0";
|
|
23979
24649
|
const port = Number(process.env.PORT ?? 8080);
|