@neat.is/core 0.4.12 → 0.4.14
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-HVF4S7J3.js → chunk-6CO7C4IU.js} +6 -3
- package/dist/{chunk-HVF4S7J3.js.map → chunk-6CO7C4IU.js.map} +1 -1
- package/dist/{chunk-3QCRUEQD.js → chunk-GHPHVXYM.js} +5 -3
- package/dist/chunk-GHPHVXYM.js.map +1 -0
- package/dist/{chunk-7JY6F7BY.js → chunk-QZRDQ7WX.js} +3 -3
- package/dist/{chunk-WDG4QEFO.js → chunk-XS4CGNRO.js} +558 -233
- package/dist/chunk-XS4CGNRO.js.map +1 -0
- package/dist/cli.cjs +801 -458
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +21 -4
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +613 -286
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +620 -293
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-VSPMP3OS.js → otel-grpc-QAISVAY5.js} +3 -3
- package/dist/server.cjs +538 -211
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +6 -2
- package/dist/chunk-3QCRUEQD.js.map +0 -1
- package/dist/chunk-WDG4QEFO.js.map +0 -1
- /package/dist/{chunk-7JY6F7BY.js.map → chunk-QZRDQ7WX.js.map} +0 -0
- /package/dist/{otel-grpc-VSPMP3OS.js.map → otel-grpc-QAISVAY5.js.map} +0 -0
package/dist/cli.cjs
CHANGED
|
@@ -58,9 +58,9 @@ function mountBearerAuth(app, opts) {
|
|
|
58
58
|
const suffixes = [...DEFAULT_UNAUTH_SUFFIXES, ...opts.extraUnauthenticatedSuffixes ?? []];
|
|
59
59
|
const publicRead = opts.publicRead === true;
|
|
60
60
|
app.addHook("preHandler", (req, reply, done) => {
|
|
61
|
-
const
|
|
61
|
+
const path49 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
62
62
|
for (const suffix of suffixes) {
|
|
63
|
-
if (
|
|
63
|
+
if (path49 === suffix || path49.endsWith(suffix)) {
|
|
64
64
|
done();
|
|
65
65
|
return;
|
|
66
66
|
}
|
|
@@ -134,7 +134,9 @@ __export(otel_grpc_exports, {
|
|
|
134
134
|
});
|
|
135
135
|
function bytesToHex(buf) {
|
|
136
136
|
if (!buf) return "";
|
|
137
|
-
|
|
137
|
+
if (Buffer.isBuffer(buf)) return buf.toString("hex");
|
|
138
|
+
if (buf instanceof Uint8Array) return Buffer.from(buf).toString("hex");
|
|
139
|
+
return "";
|
|
138
140
|
}
|
|
139
141
|
function nanosToString(n) {
|
|
140
142
|
if (n === void 0 || n === null) return "0";
|
|
@@ -186,8 +188,8 @@ function reshapeGrpcRequest(req) {
|
|
|
186
188
|
};
|
|
187
189
|
}
|
|
188
190
|
function resolveProtoRoot() {
|
|
189
|
-
const here =
|
|
190
|
-
return
|
|
191
|
+
const here = import_node_path39.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
192
|
+
return import_node_path39.default.resolve(here, "..", "proto");
|
|
191
193
|
}
|
|
192
194
|
function loadTraceService() {
|
|
193
195
|
const protoRoot = resolveProtoRoot();
|
|
@@ -255,13 +257,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
255
257
|
})
|
|
256
258
|
};
|
|
257
259
|
}
|
|
258
|
-
var import_node_url,
|
|
260
|
+
var import_node_url, import_node_path39, import_node_crypto2, grpc, protoLoader;
|
|
259
261
|
var init_otel_grpc = __esm({
|
|
260
262
|
"src/otel-grpc.ts"() {
|
|
261
263
|
"use strict";
|
|
262
264
|
init_cjs_shims();
|
|
263
265
|
import_node_url = require("url");
|
|
264
|
-
|
|
266
|
+
import_node_path39 = __toESM(require("path"), 1);
|
|
265
267
|
import_node_crypto2 = require("crypto");
|
|
266
268
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
267
269
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -371,10 +373,10 @@ function parseOtlpRequest(body) {
|
|
|
371
373
|
return out;
|
|
372
374
|
}
|
|
373
375
|
function loadProtoRoot() {
|
|
374
|
-
const here =
|
|
375
|
-
const protoRoot =
|
|
376
|
+
const here = import_node_path40.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
377
|
+
const protoRoot = import_node_path40.default.resolve(here, "..", "proto");
|
|
376
378
|
const root = new import_protobufjs.default.Root();
|
|
377
|
-
root.resolvePath = (_origin, target) =>
|
|
379
|
+
root.resolvePath = (_origin, target) => import_node_path40.default.resolve(protoRoot, target);
|
|
378
380
|
root.loadSync(
|
|
379
381
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
380
382
|
{ keepCase: true }
|
|
@@ -407,7 +409,10 @@ function encodeProtobufResponseBody() {
|
|
|
407
409
|
}
|
|
408
410
|
async function decodeProtobufBody(buf) {
|
|
409
411
|
const Type = loadProtobufDecoder();
|
|
410
|
-
const decoded = Type.decode(buf)
|
|
412
|
+
const decoded = Type.toObject(Type.decode(buf), {
|
|
413
|
+
longs: String,
|
|
414
|
+
enums: Number
|
|
415
|
+
});
|
|
411
416
|
const { reshapeGrpcRequest: reshapeGrpcRequest2 } = await Promise.resolve().then(() => (init_otel_grpc(), otel_grpc_exports));
|
|
412
417
|
return reshapeGrpcRequest2(decoded);
|
|
413
418
|
}
|
|
@@ -567,12 +572,12 @@ async function buildOtelReceiver(opts) {
|
|
|
567
572
|
};
|
|
568
573
|
return decorated;
|
|
569
574
|
}
|
|
570
|
-
var
|
|
575
|
+
var import_node_path40, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
|
|
571
576
|
var init_otel = __esm({
|
|
572
577
|
"src/otel.ts"() {
|
|
573
578
|
"use strict";
|
|
574
579
|
init_cjs_shims();
|
|
575
|
-
|
|
580
|
+
import_node_path40 = __toESM(require("path"), 1);
|
|
576
581
|
import_node_url2 = require("url");
|
|
577
582
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
578
583
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -600,8 +605,8 @@ __export(cli_exports, {
|
|
|
600
605
|
});
|
|
601
606
|
module.exports = __toCommonJS(cli_exports);
|
|
602
607
|
init_cjs_shims();
|
|
603
|
-
var
|
|
604
|
-
var
|
|
608
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
609
|
+
var import_node_fs31 = require("fs");
|
|
605
610
|
var import_node_url3 = require("url");
|
|
606
611
|
|
|
607
612
|
// src/graph.ts
|
|
@@ -1131,19 +1136,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1131
1136
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1132
1137
|
let best = { path: [start], edges: [] };
|
|
1133
1138
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1134
|
-
function step(node,
|
|
1135
|
-
if (
|
|
1136
|
-
best = { path: [...
|
|
1139
|
+
function step(node, path49, edges) {
|
|
1140
|
+
if (path49.length > best.path.length) {
|
|
1141
|
+
best = { path: [...path49], edges: [...edges] };
|
|
1137
1142
|
}
|
|
1138
|
-
if (
|
|
1143
|
+
if (path49.length - 1 >= maxDepth) return;
|
|
1139
1144
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1140
1145
|
for (const [srcId, edge] of incoming) {
|
|
1141
1146
|
if (visited.has(srcId)) continue;
|
|
1142
1147
|
visited.add(srcId);
|
|
1143
|
-
|
|
1148
|
+
path49.push(srcId);
|
|
1144
1149
|
edges.push(edge);
|
|
1145
|
-
step(srcId,
|
|
1146
|
-
|
|
1150
|
+
step(srcId, path49, edges);
|
|
1151
|
+
path49.pop();
|
|
1147
1152
|
edges.pop();
|
|
1148
1153
|
visited.delete(srcId);
|
|
1149
1154
|
}
|
|
@@ -3115,10 +3120,8 @@ async function addServiceAliases(graph, scanPath, services) {
|
|
|
3115
3120
|
await collectK8sAliases(graph, scanPath, byName);
|
|
3116
3121
|
}
|
|
3117
3122
|
|
|
3118
|
-
// src/extract/
|
|
3123
|
+
// src/extract/files.ts
|
|
3119
3124
|
init_cjs_shims();
|
|
3120
|
-
var import_node_path18 = __toESM(require("path"), 1);
|
|
3121
|
-
var import_types8 = require("@neat.is/types");
|
|
3122
3125
|
|
|
3123
3126
|
// src/extract/calls/shared.ts
|
|
3124
3127
|
init_cjs_shims();
|
|
@@ -3217,11 +3220,333 @@ function ensureFileNode(graph, serviceName, serviceNodeId, relPath) {
|
|
|
3217
3220
|
return { fileNodeId, nodesAdded, edgesAdded };
|
|
3218
3221
|
}
|
|
3219
3222
|
|
|
3223
|
+
// src/extract/files.ts
|
|
3224
|
+
var import_node_path11 = __toESM(require("path"), 1);
|
|
3225
|
+
async function addFiles(graph, services) {
|
|
3226
|
+
let nodesAdded = 0;
|
|
3227
|
+
let edgesAdded = 0;
|
|
3228
|
+
for (const service of services) {
|
|
3229
|
+
const filePaths = await walkSourceFiles(service.dir);
|
|
3230
|
+
for (const filePath of filePaths) {
|
|
3231
|
+
const relPath = toPosix2(import_node_path11.default.relative(service.dir, filePath));
|
|
3232
|
+
const { nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
3233
|
+
graph,
|
|
3234
|
+
service.pkg.name,
|
|
3235
|
+
service.node.id,
|
|
3236
|
+
relPath
|
|
3237
|
+
);
|
|
3238
|
+
nodesAdded += n;
|
|
3239
|
+
edgesAdded += e;
|
|
3240
|
+
}
|
|
3241
|
+
}
|
|
3242
|
+
return { nodesAdded, edgesAdded };
|
|
3243
|
+
}
|
|
3244
|
+
|
|
3245
|
+
// src/extract/imports.ts
|
|
3246
|
+
init_cjs_shims();
|
|
3247
|
+
var import_node_path12 = __toESM(require("path"), 1);
|
|
3248
|
+
var import_node_fs11 = require("fs");
|
|
3249
|
+
var import_tree_sitter = __toESM(require("tree-sitter"), 1);
|
|
3250
|
+
var import_tree_sitter_javascript = __toESM(require("tree-sitter-javascript"), 1);
|
|
3251
|
+
var import_tree_sitter_python = __toESM(require("tree-sitter-python"), 1);
|
|
3252
|
+
var import_types8 = require("@neat.is/types");
|
|
3253
|
+
var PARSE_CHUNK = 16384;
|
|
3254
|
+
function parseSource(parser, source) {
|
|
3255
|
+
return parser.parse(
|
|
3256
|
+
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK)
|
|
3257
|
+
);
|
|
3258
|
+
}
|
|
3259
|
+
function makeJsParser() {
|
|
3260
|
+
const p = new import_tree_sitter.default();
|
|
3261
|
+
p.setLanguage(import_tree_sitter_javascript.default);
|
|
3262
|
+
return p;
|
|
3263
|
+
}
|
|
3264
|
+
function makePyParser() {
|
|
3265
|
+
const p = new import_tree_sitter.default();
|
|
3266
|
+
p.setLanguage(import_tree_sitter_python.default);
|
|
3267
|
+
return p;
|
|
3268
|
+
}
|
|
3269
|
+
function stringLiteralText(node) {
|
|
3270
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
3271
|
+
const child = node.child(i);
|
|
3272
|
+
if (child?.type === "string_fragment") return child.text;
|
|
3273
|
+
}
|
|
3274
|
+
const raw = node.text;
|
|
3275
|
+
if (raw.length >= 2) return raw.slice(1, -1);
|
|
3276
|
+
return raw.length === 0 ? null : "";
|
|
3277
|
+
}
|
|
3278
|
+
function clipSnippet(text) {
|
|
3279
|
+
const oneLine = text.split("\n")[0] ?? text;
|
|
3280
|
+
return oneLine.length > 120 ? oneLine.slice(0, 120) : oneLine;
|
|
3281
|
+
}
|
|
3282
|
+
function collectJsImports(node, out) {
|
|
3283
|
+
if (node.type === "import_statement") {
|
|
3284
|
+
const source = node.childForFieldName("source");
|
|
3285
|
+
if (source) {
|
|
3286
|
+
const specifier = stringLiteralText(source);
|
|
3287
|
+
if (specifier) {
|
|
3288
|
+
out.push({ specifier, line: node.startPosition.row + 1, snippet: clipSnippet(node.text) });
|
|
3289
|
+
}
|
|
3290
|
+
}
|
|
3291
|
+
return;
|
|
3292
|
+
}
|
|
3293
|
+
if (node.type === "call_expression") {
|
|
3294
|
+
const fn = node.childForFieldName("function");
|
|
3295
|
+
if (fn?.type === "identifier" && fn.text === "require") {
|
|
3296
|
+
const args = node.childForFieldName("arguments");
|
|
3297
|
+
const firstArg = args?.namedChild(0);
|
|
3298
|
+
if (firstArg?.type === "string") {
|
|
3299
|
+
const specifier = stringLiteralText(firstArg);
|
|
3300
|
+
if (specifier) {
|
|
3301
|
+
out.push({ specifier, line: node.startPosition.row + 1, snippet: clipSnippet(node.text) });
|
|
3302
|
+
}
|
|
3303
|
+
}
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3306
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
3307
|
+
const child = node.namedChild(i);
|
|
3308
|
+
if (child) collectJsImports(child, out);
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
function collectPyImports(node, out) {
|
|
3312
|
+
if (node.type === "import_from_statement") {
|
|
3313
|
+
let level = 0;
|
|
3314
|
+
let modulePath = "";
|
|
3315
|
+
let pastFrom = false;
|
|
3316
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
3317
|
+
const child = node.child(i);
|
|
3318
|
+
if (!child) continue;
|
|
3319
|
+
if (!pastFrom) {
|
|
3320
|
+
if (child.type === "from") pastFrom = true;
|
|
3321
|
+
continue;
|
|
3322
|
+
}
|
|
3323
|
+
if (child.type === "import") break;
|
|
3324
|
+
if (child.type === "relative_import") {
|
|
3325
|
+
for (let j = 0; j < child.childCount; j++) {
|
|
3326
|
+
const rc = child.child(j);
|
|
3327
|
+
if (!rc) continue;
|
|
3328
|
+
if (rc.type === "import_prefix") {
|
|
3329
|
+
for (let k = 0; k < rc.childCount; k++) {
|
|
3330
|
+
if (rc.child(k)?.type === ".") level++;
|
|
3331
|
+
}
|
|
3332
|
+
} else if (rc.type === "dotted_name") modulePath = rc.text;
|
|
3333
|
+
}
|
|
3334
|
+
break;
|
|
3335
|
+
}
|
|
3336
|
+
if (child.type === "dotted_name") {
|
|
3337
|
+
modulePath = child.text;
|
|
3338
|
+
break;
|
|
3339
|
+
}
|
|
3340
|
+
}
|
|
3341
|
+
if (level > 0 || modulePath) {
|
|
3342
|
+
out.push({ modulePath, level, line: node.startPosition.row + 1, snippet: clipSnippet(node.text) });
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3345
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
3346
|
+
const child = node.namedChild(i);
|
|
3347
|
+
if (child) collectPyImports(child, out);
|
|
3348
|
+
}
|
|
3349
|
+
}
|
|
3350
|
+
async function fileExists(p) {
|
|
3351
|
+
try {
|
|
3352
|
+
await import_node_fs11.promises.access(p);
|
|
3353
|
+
return true;
|
|
3354
|
+
} catch {
|
|
3355
|
+
return false;
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
function isWithinServiceDir(candidate, serviceDir) {
|
|
3359
|
+
const rel = import_node_path12.default.relative(serviceDir, candidate);
|
|
3360
|
+
return rel !== "" && !rel.startsWith("..") && !import_node_path12.default.isAbsolute(rel);
|
|
3361
|
+
}
|
|
3362
|
+
var JS_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
3363
|
+
var JS_INDEX_FILES = JS_EXTENSIONS.map((ext) => `index${ext}`);
|
|
3364
|
+
async function firstExistingCandidate(base, serviceDir) {
|
|
3365
|
+
for (const ext of JS_EXTENSIONS) {
|
|
3366
|
+
const candidate = base + ext;
|
|
3367
|
+
if (isWithinServiceDir(candidate, serviceDir) && await fileExists(candidate)) {
|
|
3368
|
+
return toPosix2(import_node_path12.default.relative(serviceDir, candidate));
|
|
3369
|
+
}
|
|
3370
|
+
}
|
|
3371
|
+
for (const indexFile of JS_INDEX_FILES) {
|
|
3372
|
+
const candidate = import_node_path12.default.join(base, indexFile);
|
|
3373
|
+
if (isWithinServiceDir(candidate, serviceDir) && await fileExists(candidate)) {
|
|
3374
|
+
return toPosix2(import_node_path12.default.relative(serviceDir, candidate));
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
return null;
|
|
3378
|
+
}
|
|
3379
|
+
async function loadTsPathConfig(serviceDir) {
|
|
3380
|
+
const tsconfigPath = import_node_path12.default.join(serviceDir, "tsconfig.json");
|
|
3381
|
+
let raw;
|
|
3382
|
+
try {
|
|
3383
|
+
raw = await import_node_fs11.promises.readFile(tsconfigPath, "utf8");
|
|
3384
|
+
} catch {
|
|
3385
|
+
return null;
|
|
3386
|
+
}
|
|
3387
|
+
try {
|
|
3388
|
+
const parsed = JSON.parse(raw);
|
|
3389
|
+
const paths = parsed.compilerOptions?.paths;
|
|
3390
|
+
if (!paths || Object.keys(paths).length === 0) return null;
|
|
3391
|
+
const baseUrl = parsed.compilerOptions?.baseUrl;
|
|
3392
|
+
return { paths, baseDir: baseUrl ? import_node_path12.default.resolve(serviceDir, baseUrl) : serviceDir };
|
|
3393
|
+
} catch (err) {
|
|
3394
|
+
recordExtractionError("import alias resolution", tsconfigPath, err);
|
|
3395
|
+
return null;
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
async function resolveTsAlias(specifier, config, serviceDir) {
|
|
3399
|
+
for (const [pattern, targets] of Object.entries(config.paths)) {
|
|
3400
|
+
let suffix = null;
|
|
3401
|
+
if (pattern === specifier) {
|
|
3402
|
+
suffix = "";
|
|
3403
|
+
} else if (pattern.endsWith("/*")) {
|
|
3404
|
+
const prefix = pattern.slice(0, -1);
|
|
3405
|
+
if (specifier.startsWith(prefix)) suffix = specifier.slice(prefix.length);
|
|
3406
|
+
}
|
|
3407
|
+
if (suffix === null) continue;
|
|
3408
|
+
for (const target of targets) {
|
|
3409
|
+
const targetBase = target.endsWith("/*") ? target.slice(0, -2) : target.replace(/\*$/, "");
|
|
3410
|
+
const resolvedBase = import_node_path12.default.resolve(config.baseDir, targetBase, suffix);
|
|
3411
|
+
const hit = await firstExistingCandidate(resolvedBase, serviceDir);
|
|
3412
|
+
if (hit) return hit;
|
|
3413
|
+
if (isWithinServiceDir(resolvedBase, serviceDir) && await fileExists(resolvedBase)) {
|
|
3414
|
+
return toPosix2(import_node_path12.default.relative(serviceDir, resolvedBase));
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3417
|
+
}
|
|
3418
|
+
return null;
|
|
3419
|
+
}
|
|
3420
|
+
async function resolveJsImport(specifier, importerDir, serviceDir, tsPaths) {
|
|
3421
|
+
if (!specifier) return null;
|
|
3422
|
+
if (specifier.startsWith("./") || specifier.startsWith("../")) {
|
|
3423
|
+
const base = import_node_path12.default.resolve(importerDir, specifier);
|
|
3424
|
+
const ext = import_node_path12.default.extname(specifier);
|
|
3425
|
+
if (ext) {
|
|
3426
|
+
if (ext === ".js" || ext === ".jsx") {
|
|
3427
|
+
const tsExt = ext === ".jsx" ? ".tsx" : ".ts";
|
|
3428
|
+
const tsSibling = base.slice(0, -ext.length) + tsExt;
|
|
3429
|
+
if (isWithinServiceDir(tsSibling, serviceDir) && await fileExists(tsSibling)) {
|
|
3430
|
+
return toPosix2(import_node_path12.default.relative(serviceDir, tsSibling));
|
|
3431
|
+
}
|
|
3432
|
+
}
|
|
3433
|
+
if (isWithinServiceDir(base, serviceDir) && await fileExists(base)) {
|
|
3434
|
+
return toPosix2(import_node_path12.default.relative(serviceDir, base));
|
|
3435
|
+
}
|
|
3436
|
+
return null;
|
|
3437
|
+
}
|
|
3438
|
+
return firstExistingCandidate(base, serviceDir);
|
|
3439
|
+
}
|
|
3440
|
+
if (tsPaths) return resolveTsAlias(specifier, tsPaths, serviceDir);
|
|
3441
|
+
return null;
|
|
3442
|
+
}
|
|
3443
|
+
async function resolvePyImport(imp, importerPath, serviceDir) {
|
|
3444
|
+
if (!imp.modulePath) return null;
|
|
3445
|
+
const relPath = imp.modulePath.split(".").join("/");
|
|
3446
|
+
let baseDir;
|
|
3447
|
+
if (imp.level > 0) {
|
|
3448
|
+
baseDir = import_node_path12.default.dirname(importerPath);
|
|
3449
|
+
for (let i = 1; i < imp.level; i++) baseDir = import_node_path12.default.dirname(baseDir);
|
|
3450
|
+
} else {
|
|
3451
|
+
baseDir = serviceDir;
|
|
3452
|
+
}
|
|
3453
|
+
const candidates = [import_node_path12.default.join(baseDir, `${relPath}.py`), import_node_path12.default.join(baseDir, relPath, "__init__.py")];
|
|
3454
|
+
for (const candidate of candidates) {
|
|
3455
|
+
if (isWithinServiceDir(candidate, serviceDir) && await fileExists(candidate)) {
|
|
3456
|
+
return toPosix2(import_node_path12.default.relative(serviceDir, candidate));
|
|
3457
|
+
}
|
|
3458
|
+
}
|
|
3459
|
+
return null;
|
|
3460
|
+
}
|
|
3461
|
+
function emitImportEdge(graph, serviceName, importerFileId, importerRelPath, importeeRelPath, line, snippet2) {
|
|
3462
|
+
const importeeFileId = (0, import_types8.fileId)(serviceName, importeeRelPath);
|
|
3463
|
+
if (!graph.hasNode(importeeFileId)) return 0;
|
|
3464
|
+
const edgeId = (0, import_types8.extractedEdgeId)(importerFileId, importeeFileId, import_types8.EdgeType.IMPORTS);
|
|
3465
|
+
if (graph.hasEdge(edgeId)) return 0;
|
|
3466
|
+
const edge = {
|
|
3467
|
+
id: edgeId,
|
|
3468
|
+
source: importerFileId,
|
|
3469
|
+
target: importeeFileId,
|
|
3470
|
+
type: import_types8.EdgeType.IMPORTS,
|
|
3471
|
+
provenance: import_types8.Provenance.EXTRACTED,
|
|
3472
|
+
confidence: (0, import_types8.confidenceForExtracted)("structural"),
|
|
3473
|
+
evidence: { file: importerRelPath, line, snippet: snippet2 }
|
|
3474
|
+
};
|
|
3475
|
+
graph.addEdgeWithKey(edgeId, importerFileId, importeeFileId, edge);
|
|
3476
|
+
return 1;
|
|
3477
|
+
}
|
|
3478
|
+
async function addImports(graph, services) {
|
|
3479
|
+
const jsParser = makeJsParser();
|
|
3480
|
+
const pyParser = makePyParser();
|
|
3481
|
+
let edgesAdded = 0;
|
|
3482
|
+
for (const service of services) {
|
|
3483
|
+
const tsPaths = await loadTsPathConfig(service.dir);
|
|
3484
|
+
const files = await loadSourceFiles(service.dir);
|
|
3485
|
+
for (const file of files) {
|
|
3486
|
+
if (isTestPath(file.path)) continue;
|
|
3487
|
+
const relFile = toPosix2(import_node_path12.default.relative(service.dir, file.path));
|
|
3488
|
+
const importerFileId = (0, import_types8.fileId)(service.pkg.name, relFile);
|
|
3489
|
+
const isPython = import_node_path12.default.extname(file.path) === ".py";
|
|
3490
|
+
if (isPython) {
|
|
3491
|
+
let pyImports = [];
|
|
3492
|
+
try {
|
|
3493
|
+
const tree = parseSource(pyParser, file.content);
|
|
3494
|
+
collectPyImports(tree.rootNode, pyImports);
|
|
3495
|
+
} catch (err) {
|
|
3496
|
+
recordExtractionError("import extraction", file.path, err);
|
|
3497
|
+
continue;
|
|
3498
|
+
}
|
|
3499
|
+
for (const imp of pyImports) {
|
|
3500
|
+
const resolved = await resolvePyImport(imp, file.path, service.dir);
|
|
3501
|
+
if (!resolved) continue;
|
|
3502
|
+
edgesAdded += emitImportEdge(
|
|
3503
|
+
graph,
|
|
3504
|
+
service.pkg.name,
|
|
3505
|
+
importerFileId,
|
|
3506
|
+
relFile,
|
|
3507
|
+
resolved,
|
|
3508
|
+
imp.line,
|
|
3509
|
+
imp.snippet
|
|
3510
|
+
);
|
|
3511
|
+
}
|
|
3512
|
+
continue;
|
|
3513
|
+
}
|
|
3514
|
+
let jsImports = [];
|
|
3515
|
+
try {
|
|
3516
|
+
const tree = parseSource(jsParser, file.content);
|
|
3517
|
+
collectJsImports(tree.rootNode, jsImports);
|
|
3518
|
+
} catch (err) {
|
|
3519
|
+
recordExtractionError("import extraction", file.path, err);
|
|
3520
|
+
continue;
|
|
3521
|
+
}
|
|
3522
|
+
for (const imp of jsImports) {
|
|
3523
|
+
const resolved = await resolveJsImport(imp.specifier, import_node_path12.default.dirname(file.path), service.dir, tsPaths);
|
|
3524
|
+
if (!resolved) continue;
|
|
3525
|
+
edgesAdded += emitImportEdge(
|
|
3526
|
+
graph,
|
|
3527
|
+
service.pkg.name,
|
|
3528
|
+
importerFileId,
|
|
3529
|
+
relFile,
|
|
3530
|
+
resolved,
|
|
3531
|
+
imp.line,
|
|
3532
|
+
imp.snippet
|
|
3533
|
+
);
|
|
3534
|
+
}
|
|
3535
|
+
}
|
|
3536
|
+
}
|
|
3537
|
+
return { nodesAdded: 0, edgesAdded };
|
|
3538
|
+
}
|
|
3539
|
+
|
|
3540
|
+
// src/extract/databases/index.ts
|
|
3541
|
+
init_cjs_shims();
|
|
3542
|
+
var import_node_path20 = __toESM(require("path"), 1);
|
|
3543
|
+
var import_types9 = require("@neat.is/types");
|
|
3544
|
+
|
|
3220
3545
|
// src/extract/databases/db-config-yaml.ts
|
|
3221
3546
|
init_cjs_shims();
|
|
3222
|
-
var
|
|
3547
|
+
var import_node_path13 = __toESM(require("path"), 1);
|
|
3223
3548
|
async function parse(serviceDir) {
|
|
3224
|
-
const yamlPath =
|
|
3549
|
+
const yamlPath = import_node_path13.default.join(serviceDir, "db-config.yaml");
|
|
3225
3550
|
if (!await exists(yamlPath)) return [];
|
|
3226
3551
|
const raw = await readYaml(yamlPath);
|
|
3227
3552
|
return [
|
|
@@ -3239,13 +3564,13 @@ var dbConfigYamlParser = { name: "db-config.yaml", parse };
|
|
|
3239
3564
|
|
|
3240
3565
|
// src/extract/databases/dotenv.ts
|
|
3241
3566
|
init_cjs_shims();
|
|
3242
|
-
var
|
|
3243
|
-
var
|
|
3567
|
+
var import_node_fs13 = require("fs");
|
|
3568
|
+
var import_node_path15 = __toESM(require("path"), 1);
|
|
3244
3569
|
|
|
3245
3570
|
// src/extract/databases/shared.ts
|
|
3246
3571
|
init_cjs_shims();
|
|
3247
|
-
var
|
|
3248
|
-
var
|
|
3572
|
+
var import_node_fs12 = require("fs");
|
|
3573
|
+
var import_node_path14 = __toESM(require("path"), 1);
|
|
3249
3574
|
function schemeToEngine(scheme) {
|
|
3250
3575
|
const s = scheme.toLowerCase().split("+")[0];
|
|
3251
3576
|
switch (s) {
|
|
@@ -3284,14 +3609,14 @@ function parseConnectionString(url) {
|
|
|
3284
3609
|
}
|
|
3285
3610
|
async function readIfExists(filePath) {
|
|
3286
3611
|
try {
|
|
3287
|
-
return await
|
|
3612
|
+
return await import_node_fs12.promises.readFile(filePath, "utf8");
|
|
3288
3613
|
} catch {
|
|
3289
3614
|
return null;
|
|
3290
3615
|
}
|
|
3291
3616
|
}
|
|
3292
3617
|
async function findFirst(serviceDir, candidates) {
|
|
3293
3618
|
for (const rel of candidates) {
|
|
3294
|
-
const abs =
|
|
3619
|
+
const abs = import_node_path14.default.join(serviceDir, rel);
|
|
3295
3620
|
const content = await readIfExists(abs);
|
|
3296
3621
|
if (content !== null) return abs;
|
|
3297
3622
|
}
|
|
@@ -3342,15 +3667,15 @@ function parseDotenvLine(line) {
|
|
|
3342
3667
|
return { key, value };
|
|
3343
3668
|
}
|
|
3344
3669
|
async function parse2(serviceDir) {
|
|
3345
|
-
const entries = await
|
|
3670
|
+
const entries = await import_node_fs13.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
|
|
3346
3671
|
const configs = [];
|
|
3347
3672
|
const seen = /* @__PURE__ */ new Set();
|
|
3348
3673
|
for (const entry2 of entries) {
|
|
3349
3674
|
if (!entry2.isFile()) continue;
|
|
3350
3675
|
const match = isConfigFile(entry2.name);
|
|
3351
3676
|
if (!match.match || match.fileType !== "env") continue;
|
|
3352
|
-
const filePath =
|
|
3353
|
-
const content = await
|
|
3677
|
+
const filePath = import_node_path15.default.join(serviceDir, entry2.name);
|
|
3678
|
+
const content = await import_node_fs13.promises.readFile(filePath, "utf8");
|
|
3354
3679
|
for (const line of content.split("\n")) {
|
|
3355
3680
|
const parsed = parseDotenvLine(line);
|
|
3356
3681
|
if (!parsed) continue;
|
|
@@ -3369,9 +3694,9 @@ var dotenvParser = { name: ".env", parse: parse2 };
|
|
|
3369
3694
|
|
|
3370
3695
|
// src/extract/databases/prisma.ts
|
|
3371
3696
|
init_cjs_shims();
|
|
3372
|
-
var
|
|
3697
|
+
var import_node_path16 = __toESM(require("path"), 1);
|
|
3373
3698
|
async function parse3(serviceDir) {
|
|
3374
|
-
const schemaPath =
|
|
3699
|
+
const schemaPath = import_node_path16.default.join(serviceDir, "prisma", "schema.prisma");
|
|
3375
3700
|
const content = await readIfExists(schemaPath);
|
|
3376
3701
|
if (!content) return [];
|
|
3377
3702
|
const block = content.match(/datasource\s+\w+\s*\{([^}]*)\}/s);
|
|
@@ -3503,10 +3828,10 @@ var knexParser = { name: "knex", parse: parse5 };
|
|
|
3503
3828
|
|
|
3504
3829
|
// src/extract/databases/ormconfig.ts
|
|
3505
3830
|
init_cjs_shims();
|
|
3506
|
-
var
|
|
3831
|
+
var import_node_path17 = __toESM(require("path"), 1);
|
|
3507
3832
|
async function parse6(serviceDir) {
|
|
3508
3833
|
for (const candidate of ["ormconfig.json", "ormconfig.yaml", "ormconfig.yml"]) {
|
|
3509
|
-
const abs =
|
|
3834
|
+
const abs = import_node_path17.default.join(serviceDir, candidate);
|
|
3510
3835
|
if (!await exists(abs)) continue;
|
|
3511
3836
|
const raw = candidate.endsWith(".json") ? await readJson(abs) : await readYaml(abs);
|
|
3512
3837
|
const entries = Array.isArray(raw) ? raw : [raw];
|
|
@@ -3566,9 +3891,9 @@ var typeormParser = { name: "typeorm", parse: parse7 };
|
|
|
3566
3891
|
|
|
3567
3892
|
// src/extract/databases/sequelize.ts
|
|
3568
3893
|
init_cjs_shims();
|
|
3569
|
-
var
|
|
3894
|
+
var import_node_path18 = __toESM(require("path"), 1);
|
|
3570
3895
|
async function parse8(serviceDir) {
|
|
3571
|
-
const configPath =
|
|
3896
|
+
const configPath = import_node_path18.default.join(serviceDir, "config", "config.json");
|
|
3572
3897
|
if (!await exists(configPath)) return [];
|
|
3573
3898
|
const raw = await readJson(configPath);
|
|
3574
3899
|
const out = [];
|
|
@@ -3595,7 +3920,7 @@ var sequelizeParser = { name: "sequelize", parse: parse8 };
|
|
|
3595
3920
|
|
|
3596
3921
|
// src/extract/databases/docker-compose.ts
|
|
3597
3922
|
init_cjs_shims();
|
|
3598
|
-
var
|
|
3923
|
+
var import_node_path19 = __toESM(require("path"), 1);
|
|
3599
3924
|
function portFromService(svc) {
|
|
3600
3925
|
for (const raw of svc.ports ?? []) {
|
|
3601
3926
|
const str = String(raw);
|
|
@@ -3622,7 +3947,7 @@ function databaseFromEnv(svc) {
|
|
|
3622
3947
|
}
|
|
3623
3948
|
async function parse9(serviceDir) {
|
|
3624
3949
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
3625
|
-
const abs =
|
|
3950
|
+
const abs = import_node_path19.default.join(serviceDir, name);
|
|
3626
3951
|
if (!await exists(abs)) continue;
|
|
3627
3952
|
const raw = await readYaml(abs);
|
|
3628
3953
|
if (!raw?.services) return [];
|
|
@@ -3663,8 +3988,8 @@ function compatibleDriversFor(engine) {
|
|
|
3663
3988
|
}
|
|
3664
3989
|
function toDatabaseNode(config) {
|
|
3665
3990
|
return {
|
|
3666
|
-
id: (0,
|
|
3667
|
-
type:
|
|
3991
|
+
id: (0, import_types9.databaseId)(config.host),
|
|
3992
|
+
type: import_types9.NodeType.DatabaseNode,
|
|
3668
3993
|
name: config.database || config.host,
|
|
3669
3994
|
engine: config.engine,
|
|
3670
3995
|
engineVersion: config.engineVersion,
|
|
@@ -3794,7 +4119,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
3794
4119
|
discoveredVia: mergedDiscoveredVia
|
|
3795
4120
|
});
|
|
3796
4121
|
}
|
|
3797
|
-
const relConfigFile = toPosix2(
|
|
4122
|
+
const relConfigFile = toPosix2(import_node_path20.default.relative(service.dir, config.sourceFile));
|
|
3798
4123
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
3799
4124
|
graph,
|
|
3800
4125
|
service.pkg.name,
|
|
@@ -3803,14 +4128,14 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
3803
4128
|
);
|
|
3804
4129
|
nodesAdded += fn;
|
|
3805
4130
|
edgesAdded += fe;
|
|
3806
|
-
const evidenceFile = toPosix2(
|
|
4131
|
+
const evidenceFile = toPosix2(import_node_path20.default.relative(scanPath, config.sourceFile));
|
|
3807
4132
|
const edge = {
|
|
3808
|
-
id: (0, import_types4.extractedEdgeId)(fileNodeId, dbNode.id,
|
|
4133
|
+
id: (0, import_types4.extractedEdgeId)(fileNodeId, dbNode.id, import_types9.EdgeType.CONNECTS_TO),
|
|
3809
4134
|
source: fileNodeId,
|
|
3810
4135
|
target: dbNode.id,
|
|
3811
|
-
type:
|
|
3812
|
-
provenance:
|
|
3813
|
-
confidence: (0,
|
|
4136
|
+
type: import_types9.EdgeType.CONNECTS_TO,
|
|
4137
|
+
provenance: import_types9.Provenance.EXTRACTED,
|
|
4138
|
+
confidence: (0, import_types9.confidenceForExtracted)("structural"),
|
|
3814
4139
|
evidence: { file: evidenceFile }
|
|
3815
4140
|
};
|
|
3816
4141
|
if (!graph.hasEdge(edge.id)) {
|
|
@@ -3837,15 +4162,15 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
3837
4162
|
|
|
3838
4163
|
// src/extract/configs.ts
|
|
3839
4164
|
init_cjs_shims();
|
|
3840
|
-
var
|
|
3841
|
-
var
|
|
3842
|
-
var
|
|
4165
|
+
var import_node_fs14 = require("fs");
|
|
4166
|
+
var import_node_path21 = __toESM(require("path"), 1);
|
|
4167
|
+
var import_types10 = require("@neat.is/types");
|
|
3843
4168
|
async function walkConfigFiles(dir) {
|
|
3844
4169
|
const out = [];
|
|
3845
4170
|
async function walk(current) {
|
|
3846
|
-
const entries = await
|
|
4171
|
+
const entries = await import_node_fs14.promises.readdir(current, { withFileTypes: true });
|
|
3847
4172
|
for (const entry2 of entries) {
|
|
3848
|
-
const full =
|
|
4173
|
+
const full = import_node_path21.default.join(current, entry2.name);
|
|
3849
4174
|
if (entry2.isDirectory()) {
|
|
3850
4175
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
3851
4176
|
if (await isPythonVenvDir(full)) continue;
|
|
@@ -3864,19 +4189,19 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
3864
4189
|
for (const service of services) {
|
|
3865
4190
|
const configFiles = await walkConfigFiles(service.dir);
|
|
3866
4191
|
for (const file of configFiles) {
|
|
3867
|
-
const relPath =
|
|
4192
|
+
const relPath = import_node_path21.default.relative(scanPath, file);
|
|
3868
4193
|
const node = {
|
|
3869
|
-
id: (0,
|
|
3870
|
-
type:
|
|
3871
|
-
name:
|
|
4194
|
+
id: (0, import_types10.configId)(relPath),
|
|
4195
|
+
type: import_types10.NodeType.ConfigNode,
|
|
4196
|
+
name: import_node_path21.default.basename(file),
|
|
3872
4197
|
path: relPath,
|
|
3873
|
-
fileType: isConfigFile(
|
|
4198
|
+
fileType: isConfigFile(import_node_path21.default.basename(file)).fileType
|
|
3874
4199
|
};
|
|
3875
4200
|
if (!graph.hasNode(node.id)) {
|
|
3876
4201
|
graph.addNode(node.id, node);
|
|
3877
4202
|
nodesAdded++;
|
|
3878
4203
|
}
|
|
3879
|
-
const relToService = toPosix2(
|
|
4204
|
+
const relToService = toPosix2(import_node_path21.default.relative(service.dir, file));
|
|
3880
4205
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
3881
4206
|
graph,
|
|
3882
4207
|
service.pkg.name,
|
|
@@ -3886,13 +4211,13 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
3886
4211
|
nodesAdded += fn;
|
|
3887
4212
|
edgesAdded += fe;
|
|
3888
4213
|
const edge = {
|
|
3889
|
-
id: (0, import_types4.extractedEdgeId)(fileNodeId, node.id,
|
|
4214
|
+
id: (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types10.EdgeType.CONFIGURED_BY),
|
|
3890
4215
|
source: fileNodeId,
|
|
3891
4216
|
target: node.id,
|
|
3892
|
-
type:
|
|
3893
|
-
provenance:
|
|
3894
|
-
confidence: (0,
|
|
3895
|
-
evidence: { file: relPath.split(
|
|
4217
|
+
type: import_types10.EdgeType.CONFIGURED_BY,
|
|
4218
|
+
provenance: import_types10.Provenance.EXTRACTED,
|
|
4219
|
+
confidence: (0, import_types10.confidenceForExtracted)("structural"),
|
|
4220
|
+
evidence: { file: relPath.split(import_node_path21.default.sep).join("/") }
|
|
3896
4221
|
};
|
|
3897
4222
|
if (!graph.hasEdge(edge.id)) {
|
|
3898
4223
|
graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
|
|
@@ -3905,15 +4230,15 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
3905
4230
|
|
|
3906
4231
|
// src/extract/calls/index.ts
|
|
3907
4232
|
init_cjs_shims();
|
|
3908
|
-
var
|
|
4233
|
+
var import_types16 = require("@neat.is/types");
|
|
3909
4234
|
|
|
3910
4235
|
// src/extract/calls/http.ts
|
|
3911
4236
|
init_cjs_shims();
|
|
3912
|
-
var
|
|
3913
|
-
var
|
|
3914
|
-
var
|
|
3915
|
-
var
|
|
3916
|
-
var
|
|
4237
|
+
var import_node_path22 = __toESM(require("path"), 1);
|
|
4238
|
+
var import_tree_sitter2 = __toESM(require("tree-sitter"), 1);
|
|
4239
|
+
var import_tree_sitter_javascript2 = __toESM(require("tree-sitter-javascript"), 1);
|
|
4240
|
+
var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
|
|
4241
|
+
var import_types11 = require("@neat.is/types");
|
|
3917
4242
|
var STRING_LITERAL_NODE_TYPES = /* @__PURE__ */ new Set(["string_fragment", "string_content"]);
|
|
3918
4243
|
var JSX_EXTERNAL_LINK_TAGS = /* @__PURE__ */ new Set(["a", "Link", "NavLink", "ExternalLink", "Anchor"]);
|
|
3919
4244
|
function isInsideJsxExternalLink(node) {
|
|
@@ -3941,14 +4266,14 @@ function collectStringLiterals(node, out) {
|
|
|
3941
4266
|
if (child) collectStringLiterals(child, out);
|
|
3942
4267
|
}
|
|
3943
4268
|
}
|
|
3944
|
-
var
|
|
3945
|
-
function
|
|
4269
|
+
var PARSE_CHUNK2 = 16384;
|
|
4270
|
+
function parseSource2(parser, source) {
|
|
3946
4271
|
return parser.parse(
|
|
3947
|
-
(index) => index >= source.length ? "" : source.slice(index, index +
|
|
4272
|
+
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK2)
|
|
3948
4273
|
);
|
|
3949
4274
|
}
|
|
3950
4275
|
function callsFromSource(source, parser, knownHosts) {
|
|
3951
|
-
const tree =
|
|
4276
|
+
const tree = parseSource2(parser, source);
|
|
3952
4277
|
const literals = [];
|
|
3953
4278
|
collectStringLiterals(tree.rootNode, literals);
|
|
3954
4279
|
const out = [];
|
|
@@ -3962,25 +4287,25 @@ function callsFromSource(source, parser, knownHosts) {
|
|
|
3962
4287
|
}
|
|
3963
4288
|
return out;
|
|
3964
4289
|
}
|
|
3965
|
-
function
|
|
3966
|
-
const p = new
|
|
3967
|
-
p.setLanguage(
|
|
4290
|
+
function makeJsParser2() {
|
|
4291
|
+
const p = new import_tree_sitter2.default();
|
|
4292
|
+
p.setLanguage(import_tree_sitter_javascript2.default);
|
|
3968
4293
|
return p;
|
|
3969
4294
|
}
|
|
3970
|
-
function
|
|
3971
|
-
const p = new
|
|
3972
|
-
p.setLanguage(
|
|
4295
|
+
function makePyParser2() {
|
|
4296
|
+
const p = new import_tree_sitter2.default();
|
|
4297
|
+
p.setLanguage(import_tree_sitter_python2.default);
|
|
3973
4298
|
return p;
|
|
3974
4299
|
}
|
|
3975
4300
|
async function addHttpCallEdges(graph, services) {
|
|
3976
|
-
const jsParser =
|
|
3977
|
-
const pyParser =
|
|
4301
|
+
const jsParser = makeJsParser2();
|
|
4302
|
+
const pyParser = makePyParser2();
|
|
3978
4303
|
const knownHosts = /* @__PURE__ */ new Set();
|
|
3979
4304
|
const hostToNodeId = /* @__PURE__ */ new Map();
|
|
3980
4305
|
for (const service of services) {
|
|
3981
|
-
knownHosts.add(
|
|
4306
|
+
knownHosts.add(import_node_path22.default.basename(service.dir));
|
|
3982
4307
|
knownHosts.add(service.pkg.name);
|
|
3983
|
-
hostToNodeId.set(
|
|
4308
|
+
hostToNodeId.set(import_node_path22.default.basename(service.dir), service.node.id);
|
|
3984
4309
|
hostToNodeId.set(service.pkg.name, service.node.id);
|
|
3985
4310
|
}
|
|
3986
4311
|
let nodesAdded = 0;
|
|
@@ -3990,7 +4315,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
3990
4315
|
const seen = /* @__PURE__ */ new Set();
|
|
3991
4316
|
for (const file of files) {
|
|
3992
4317
|
if (isTestPath(file.path)) continue;
|
|
3993
|
-
const parser =
|
|
4318
|
+
const parser = import_node_path22.default.extname(file.path) === ".py" ? pyParser : jsParser;
|
|
3994
4319
|
let sites;
|
|
3995
4320
|
try {
|
|
3996
4321
|
sites = callsFromSource(file.content, parser, knownHosts);
|
|
@@ -3999,14 +4324,14 @@ async function addHttpCallEdges(graph, services) {
|
|
|
3999
4324
|
continue;
|
|
4000
4325
|
}
|
|
4001
4326
|
if (sites.length === 0) continue;
|
|
4002
|
-
const relFile = toPosix2(
|
|
4327
|
+
const relFile = toPosix2(import_node_path22.default.relative(service.dir, file.path));
|
|
4003
4328
|
for (const site of sites) {
|
|
4004
4329
|
const targetId = hostToNodeId.get(site.host);
|
|
4005
4330
|
if (!targetId || targetId === service.node.id) continue;
|
|
4006
4331
|
const dedupKey = `${relFile}|${targetId}`;
|
|
4007
4332
|
if (seen.has(dedupKey)) continue;
|
|
4008
4333
|
seen.add(dedupKey);
|
|
4009
|
-
const confidence = (0,
|
|
4334
|
+
const confidence = (0, import_types11.confidenceForExtracted)("hostname-shape-match");
|
|
4010
4335
|
const ev = {
|
|
4011
4336
|
file: relFile,
|
|
4012
4337
|
line: site.line,
|
|
@@ -4020,25 +4345,25 @@ async function addHttpCallEdges(graph, services) {
|
|
|
4020
4345
|
);
|
|
4021
4346
|
nodesAdded += n;
|
|
4022
4347
|
edgesAdded += e;
|
|
4023
|
-
if (!(0,
|
|
4348
|
+
if (!(0, import_types11.passesExtractedFloor)(confidence)) {
|
|
4024
4349
|
noteExtractedDropped({
|
|
4025
4350
|
source: fileNodeId,
|
|
4026
4351
|
target: targetId,
|
|
4027
|
-
type:
|
|
4352
|
+
type: import_types11.EdgeType.CALLS,
|
|
4028
4353
|
confidence,
|
|
4029
4354
|
confidenceKind: "hostname-shape-match",
|
|
4030
4355
|
evidence: ev
|
|
4031
4356
|
});
|
|
4032
4357
|
continue;
|
|
4033
4358
|
}
|
|
4034
|
-
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, targetId,
|
|
4359
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, targetId, import_types11.EdgeType.CALLS);
|
|
4035
4360
|
if (!graph.hasEdge(edgeId)) {
|
|
4036
4361
|
const edge = {
|
|
4037
4362
|
id: edgeId,
|
|
4038
4363
|
source: fileNodeId,
|
|
4039
4364
|
target: targetId,
|
|
4040
|
-
type:
|
|
4041
|
-
provenance:
|
|
4365
|
+
type: import_types11.EdgeType.CALLS,
|
|
4366
|
+
provenance: import_types11.Provenance.EXTRACTED,
|
|
4042
4367
|
confidence,
|
|
4043
4368
|
evidence: ev
|
|
4044
4369
|
};
|
|
@@ -4053,8 +4378,8 @@ async function addHttpCallEdges(graph, services) {
|
|
|
4053
4378
|
|
|
4054
4379
|
// src/extract/calls/kafka.ts
|
|
4055
4380
|
init_cjs_shims();
|
|
4056
|
-
var
|
|
4057
|
-
var
|
|
4381
|
+
var import_node_path23 = __toESM(require("path"), 1);
|
|
4382
|
+
var import_types12 = require("@neat.is/types");
|
|
4058
4383
|
var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
4059
4384
|
var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
|
|
4060
4385
|
function findAll(re, text) {
|
|
@@ -4075,7 +4400,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
4075
4400
|
seen.add(key);
|
|
4076
4401
|
const line = lineOf(file.content, topic);
|
|
4077
4402
|
out.push({
|
|
4078
|
-
infraId: (0,
|
|
4403
|
+
infraId: (0, import_types12.infraId)("kafka-topic", topic),
|
|
4079
4404
|
name: topic,
|
|
4080
4405
|
kind: "kafka-topic",
|
|
4081
4406
|
edgeType,
|
|
@@ -4084,7 +4409,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
4084
4409
|
// tier (ADR-066).
|
|
4085
4410
|
confidenceKind: "verified-call-site",
|
|
4086
4411
|
evidence: {
|
|
4087
|
-
file:
|
|
4412
|
+
file: import_node_path23.default.relative(serviceDir, file.path),
|
|
4088
4413
|
line,
|
|
4089
4414
|
snippet: snippet(file.content, line)
|
|
4090
4415
|
}
|
|
@@ -4097,8 +4422,8 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
4097
4422
|
|
|
4098
4423
|
// src/extract/calls/redis.ts
|
|
4099
4424
|
init_cjs_shims();
|
|
4100
|
-
var
|
|
4101
|
-
var
|
|
4425
|
+
var import_node_path24 = __toESM(require("path"), 1);
|
|
4426
|
+
var import_types13 = require("@neat.is/types");
|
|
4102
4427
|
var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
|
|
4103
4428
|
function redisEndpointsFromFile(file, serviceDir) {
|
|
4104
4429
|
const out = [];
|
|
@@ -4111,7 +4436,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
4111
4436
|
seen.add(host);
|
|
4112
4437
|
const line = lineOf(file.content, host);
|
|
4113
4438
|
out.push({
|
|
4114
|
-
infraId: (0,
|
|
4439
|
+
infraId: (0, import_types13.infraId)("redis", host),
|
|
4115
4440
|
name: host,
|
|
4116
4441
|
kind: "redis",
|
|
4117
4442
|
edgeType: "CALLS",
|
|
@@ -4120,7 +4445,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
4120
4445
|
// support tier (ADR-066).
|
|
4121
4446
|
confidenceKind: "url-with-structural-support",
|
|
4122
4447
|
evidence: {
|
|
4123
|
-
file:
|
|
4448
|
+
file: import_node_path24.default.relative(serviceDir, file.path),
|
|
4124
4449
|
line,
|
|
4125
4450
|
snippet: snippet(file.content, line)
|
|
4126
4451
|
}
|
|
@@ -4131,8 +4456,8 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
4131
4456
|
|
|
4132
4457
|
// src/extract/calls/aws.ts
|
|
4133
4458
|
init_cjs_shims();
|
|
4134
|
-
var
|
|
4135
|
-
var
|
|
4459
|
+
var import_node_path25 = __toESM(require("path"), 1);
|
|
4460
|
+
var import_types14 = require("@neat.is/types");
|
|
4136
4461
|
var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
4137
4462
|
var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
4138
4463
|
function hasMarker(text, markers) {
|
|
@@ -4156,7 +4481,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
4156
4481
|
seen.add(key);
|
|
4157
4482
|
const line = lineOf(file.content, name);
|
|
4158
4483
|
out.push({
|
|
4159
|
-
infraId: (0,
|
|
4484
|
+
infraId: (0, import_types14.infraId)(kind, name),
|
|
4160
4485
|
name,
|
|
4161
4486
|
kind,
|
|
4162
4487
|
edgeType: "CALLS",
|
|
@@ -4165,7 +4490,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
4165
4490
|
// (ADR-066).
|
|
4166
4491
|
confidenceKind: "verified-call-site",
|
|
4167
4492
|
evidence: {
|
|
4168
|
-
file:
|
|
4493
|
+
file: import_node_path25.default.relative(serviceDir, file.path),
|
|
4169
4494
|
line,
|
|
4170
4495
|
snippet: snippet(file.content, line)
|
|
4171
4496
|
}
|
|
@@ -4190,8 +4515,8 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
4190
4515
|
|
|
4191
4516
|
// src/extract/calls/grpc.ts
|
|
4192
4517
|
init_cjs_shims();
|
|
4193
|
-
var
|
|
4194
|
-
var
|
|
4518
|
+
var import_node_path26 = __toESM(require("path"), 1);
|
|
4519
|
+
var import_types15 = require("@neat.is/types");
|
|
4195
4520
|
var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
|
|
4196
4521
|
var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
|
|
4197
4522
|
var GRPC_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@grpc\/grpc-js['"`]|_grpc_pb['"`]/;
|
|
@@ -4240,7 +4565,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
4240
4565
|
const { kind } = classified;
|
|
4241
4566
|
const line = lineOf(file.content, m[0]);
|
|
4242
4567
|
out.push({
|
|
4243
|
-
infraId: (0,
|
|
4568
|
+
infraId: (0, import_types15.infraId)(kind, name),
|
|
4244
4569
|
name,
|
|
4245
4570
|
kind,
|
|
4246
4571
|
edgeType: "CALLS",
|
|
@@ -4249,7 +4574,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
4249
4574
|
// tier (ADR-066).
|
|
4250
4575
|
confidenceKind: "verified-call-site",
|
|
4251
4576
|
evidence: {
|
|
4252
|
-
file:
|
|
4577
|
+
file: import_node_path26.default.relative(serviceDir, file.path),
|
|
4253
4578
|
line,
|
|
4254
4579
|
snippet: snippet(file.content, line)
|
|
4255
4580
|
}
|
|
@@ -4262,11 +4587,11 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
4262
4587
|
function edgeTypeFromEndpoint(ep) {
|
|
4263
4588
|
switch (ep.edgeType) {
|
|
4264
4589
|
case "PUBLISHES_TO":
|
|
4265
|
-
return
|
|
4590
|
+
return import_types16.EdgeType.PUBLISHES_TO;
|
|
4266
4591
|
case "CONSUMES_FROM":
|
|
4267
|
-
return
|
|
4592
|
+
return import_types16.EdgeType.CONSUMES_FROM;
|
|
4268
4593
|
default:
|
|
4269
|
-
return
|
|
4594
|
+
return import_types16.EdgeType.CALLS;
|
|
4270
4595
|
}
|
|
4271
4596
|
}
|
|
4272
4597
|
function isAwsKind(kind) {
|
|
@@ -4293,7 +4618,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4293
4618
|
if (!graph.hasNode(ep.infraId)) {
|
|
4294
4619
|
const node = {
|
|
4295
4620
|
id: ep.infraId,
|
|
4296
|
-
type:
|
|
4621
|
+
type: import_types16.NodeType.InfraNode,
|
|
4297
4622
|
name: ep.name,
|
|
4298
4623
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
4299
4624
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -4305,7 +4630,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4305
4630
|
nodesAdded++;
|
|
4306
4631
|
}
|
|
4307
4632
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
4308
|
-
const confidence = (0,
|
|
4633
|
+
const confidence = (0, import_types16.confidenceForExtracted)(ep.confidenceKind);
|
|
4309
4634
|
const relFile = toPosix2(ep.evidence.file);
|
|
4310
4635
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
4311
4636
|
graph,
|
|
@@ -4315,7 +4640,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4315
4640
|
);
|
|
4316
4641
|
nodesAdded += n;
|
|
4317
4642
|
edgesAdded += e;
|
|
4318
|
-
if (!(0,
|
|
4643
|
+
if (!(0, import_types16.passesExtractedFloor)(confidence)) {
|
|
4319
4644
|
noteExtractedDropped({
|
|
4320
4645
|
source: fileNodeId,
|
|
4321
4646
|
target: ep.infraId,
|
|
@@ -4335,7 +4660,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4335
4660
|
source: fileNodeId,
|
|
4336
4661
|
target: ep.infraId,
|
|
4337
4662
|
type: edgeType,
|
|
4338
|
-
provenance:
|
|
4663
|
+
provenance: import_types16.Provenance.EXTRACTED,
|
|
4339
4664
|
confidence,
|
|
4340
4665
|
evidence: ep.evidence
|
|
4341
4666
|
};
|
|
@@ -4360,16 +4685,16 @@ init_cjs_shims();
|
|
|
4360
4685
|
|
|
4361
4686
|
// src/extract/infra/docker-compose.ts
|
|
4362
4687
|
init_cjs_shims();
|
|
4363
|
-
var
|
|
4364
|
-
var
|
|
4688
|
+
var import_node_path27 = __toESM(require("path"), 1);
|
|
4689
|
+
var import_types18 = require("@neat.is/types");
|
|
4365
4690
|
|
|
4366
4691
|
// src/extract/infra/shared.ts
|
|
4367
4692
|
init_cjs_shims();
|
|
4368
|
-
var
|
|
4693
|
+
var import_types17 = require("@neat.is/types");
|
|
4369
4694
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
4370
4695
|
return {
|
|
4371
|
-
id: (0,
|
|
4372
|
-
type:
|
|
4696
|
+
id: (0, import_types17.infraId)(kind, name),
|
|
4697
|
+
type: import_types17.NodeType.InfraNode,
|
|
4373
4698
|
name,
|
|
4374
4699
|
provider,
|
|
4375
4700
|
kind,
|
|
@@ -4398,7 +4723,7 @@ function dependsOnList(value) {
|
|
|
4398
4723
|
}
|
|
4399
4724
|
function serviceNameToServiceNode(name, services) {
|
|
4400
4725
|
for (const s of services) {
|
|
4401
|
-
if (s.node.name === name ||
|
|
4726
|
+
if (s.node.name === name || import_node_path27.default.basename(s.dir) === name) return s.node.id;
|
|
4402
4727
|
}
|
|
4403
4728
|
return null;
|
|
4404
4729
|
}
|
|
@@ -4407,7 +4732,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4407
4732
|
let edgesAdded = 0;
|
|
4408
4733
|
let composePath = null;
|
|
4409
4734
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
4410
|
-
const abs =
|
|
4735
|
+
const abs = import_node_path27.default.join(scanPath, name);
|
|
4411
4736
|
if (await exists(abs)) {
|
|
4412
4737
|
composePath = abs;
|
|
4413
4738
|
break;
|
|
@@ -4420,13 +4745,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4420
4745
|
} catch (err) {
|
|
4421
4746
|
recordExtractionError(
|
|
4422
4747
|
"infra docker-compose",
|
|
4423
|
-
|
|
4748
|
+
import_node_path27.default.relative(scanPath, composePath),
|
|
4424
4749
|
err
|
|
4425
4750
|
);
|
|
4426
4751
|
return { nodesAdded, edgesAdded };
|
|
4427
4752
|
}
|
|
4428
4753
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
4429
|
-
const evidenceFile =
|
|
4754
|
+
const evidenceFile = import_node_path27.default.relative(scanPath, composePath).split(import_node_path27.default.sep).join("/");
|
|
4430
4755
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
4431
4756
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
4432
4757
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -4448,15 +4773,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4448
4773
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
4449
4774
|
const targetId = composeNameToNodeId.get(dep);
|
|
4450
4775
|
if (!targetId) continue;
|
|
4451
|
-
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId,
|
|
4776
|
+
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types18.EdgeType.DEPENDS_ON);
|
|
4452
4777
|
if (graph.hasEdge(edgeId)) continue;
|
|
4453
4778
|
const edge = {
|
|
4454
4779
|
id: edgeId,
|
|
4455
4780
|
source: sourceId,
|
|
4456
4781
|
target: targetId,
|
|
4457
|
-
type:
|
|
4458
|
-
provenance:
|
|
4459
|
-
confidence: (0,
|
|
4782
|
+
type: import_types18.EdgeType.DEPENDS_ON,
|
|
4783
|
+
provenance: import_types18.Provenance.EXTRACTED,
|
|
4784
|
+
confidence: (0, import_types18.confidenceForExtracted)("structural"),
|
|
4460
4785
|
evidence: { file: evidenceFile }
|
|
4461
4786
|
};
|
|
4462
4787
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -4468,9 +4793,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4468
4793
|
|
|
4469
4794
|
// src/extract/infra/dockerfile.ts
|
|
4470
4795
|
init_cjs_shims();
|
|
4471
|
-
var
|
|
4472
|
-
var
|
|
4473
|
-
var
|
|
4796
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
4797
|
+
var import_node_fs15 = require("fs");
|
|
4798
|
+
var import_types19 = require("@neat.is/types");
|
|
4474
4799
|
function runtimeImage(content) {
|
|
4475
4800
|
const lines = content.split("\n");
|
|
4476
4801
|
let last = null;
|
|
@@ -4489,15 +4814,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4489
4814
|
let nodesAdded = 0;
|
|
4490
4815
|
let edgesAdded = 0;
|
|
4491
4816
|
for (const service of services) {
|
|
4492
|
-
const dockerfilePath =
|
|
4817
|
+
const dockerfilePath = import_node_path28.default.join(service.dir, "Dockerfile");
|
|
4493
4818
|
if (!await exists(dockerfilePath)) continue;
|
|
4494
4819
|
let content;
|
|
4495
4820
|
try {
|
|
4496
|
-
content = await
|
|
4821
|
+
content = await import_node_fs15.promises.readFile(dockerfilePath, "utf8");
|
|
4497
4822
|
} catch (err) {
|
|
4498
4823
|
recordExtractionError(
|
|
4499
4824
|
"infra dockerfile",
|
|
4500
|
-
|
|
4825
|
+
import_node_path28.default.relative(scanPath, dockerfilePath),
|
|
4501
4826
|
err
|
|
4502
4827
|
);
|
|
4503
4828
|
continue;
|
|
@@ -4509,7 +4834,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4509
4834
|
graph.addNode(node.id, node);
|
|
4510
4835
|
nodesAdded++;
|
|
4511
4836
|
}
|
|
4512
|
-
const relDockerfile = toPosix2(
|
|
4837
|
+
const relDockerfile = toPosix2(import_node_path28.default.relative(service.dir, dockerfilePath));
|
|
4513
4838
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
4514
4839
|
graph,
|
|
4515
4840
|
service.pkg.name,
|
|
@@ -4518,17 +4843,17 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4518
4843
|
);
|
|
4519
4844
|
nodesAdded += fn;
|
|
4520
4845
|
edgesAdded += fe;
|
|
4521
|
-
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id,
|
|
4846
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types19.EdgeType.RUNS_ON);
|
|
4522
4847
|
if (!graph.hasEdge(edgeId)) {
|
|
4523
4848
|
const edge = {
|
|
4524
4849
|
id: edgeId,
|
|
4525
4850
|
source: fileNodeId,
|
|
4526
4851
|
target: node.id,
|
|
4527
|
-
type:
|
|
4528
|
-
provenance:
|
|
4529
|
-
confidence: (0,
|
|
4852
|
+
type: import_types19.EdgeType.RUNS_ON,
|
|
4853
|
+
provenance: import_types19.Provenance.EXTRACTED,
|
|
4854
|
+
confidence: (0, import_types19.confidenceForExtracted)("structural"),
|
|
4530
4855
|
evidence: {
|
|
4531
|
-
file: toPosix2(
|
|
4856
|
+
file: toPosix2(import_node_path28.default.relative(scanPath, dockerfilePath))
|
|
4532
4857
|
}
|
|
4533
4858
|
};
|
|
4534
4859
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -4540,21 +4865,21 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4540
4865
|
|
|
4541
4866
|
// src/extract/infra/terraform.ts
|
|
4542
4867
|
init_cjs_shims();
|
|
4543
|
-
var
|
|
4544
|
-
var
|
|
4868
|
+
var import_node_fs16 = require("fs");
|
|
4869
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
4545
4870
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
4546
4871
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
4547
4872
|
if (depth > max) return [];
|
|
4548
4873
|
const out = [];
|
|
4549
|
-
const entries = await
|
|
4874
|
+
const entries = await import_node_fs16.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
4550
4875
|
for (const entry2 of entries) {
|
|
4551
4876
|
if (entry2.isDirectory()) {
|
|
4552
4877
|
if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
|
|
4553
|
-
const child =
|
|
4878
|
+
const child = import_node_path29.default.join(start, entry2.name);
|
|
4554
4879
|
if (await isPythonVenvDir(child)) continue;
|
|
4555
4880
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
4556
4881
|
} else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
|
|
4557
|
-
out.push(
|
|
4882
|
+
out.push(import_node_path29.default.join(start, entry2.name));
|
|
4558
4883
|
}
|
|
4559
4884
|
}
|
|
4560
4885
|
return out;
|
|
@@ -4563,7 +4888,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
4563
4888
|
let nodesAdded = 0;
|
|
4564
4889
|
const files = await walkTfFiles(scanPath);
|
|
4565
4890
|
for (const file of files) {
|
|
4566
|
-
const content = await
|
|
4891
|
+
const content = await import_node_fs16.promises.readFile(file, "utf8");
|
|
4567
4892
|
RESOURCE_RE.lastIndex = 0;
|
|
4568
4893
|
let m;
|
|
4569
4894
|
while ((m = RESOURCE_RE.exec(content)) !== null) {
|
|
@@ -4581,8 +4906,8 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
4581
4906
|
|
|
4582
4907
|
// src/extract/infra/k8s.ts
|
|
4583
4908
|
init_cjs_shims();
|
|
4584
|
-
var
|
|
4585
|
-
var
|
|
4909
|
+
var import_node_fs17 = require("fs");
|
|
4910
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
4586
4911
|
var import_yaml3 = require("yaml");
|
|
4587
4912
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
4588
4913
|
Service: "k8s-service",
|
|
@@ -4596,15 +4921,15 @@ var K8S_KIND_TO_INFRA_KIND = {
|
|
|
4596
4921
|
async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
4597
4922
|
if (depth > max) return [];
|
|
4598
4923
|
const out = [];
|
|
4599
|
-
const entries = await
|
|
4924
|
+
const entries = await import_node_fs17.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
4600
4925
|
for (const entry2 of entries) {
|
|
4601
4926
|
if (entry2.isDirectory()) {
|
|
4602
4927
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
4603
|
-
const child =
|
|
4928
|
+
const child = import_node_path30.default.join(start, entry2.name);
|
|
4604
4929
|
if (await isPythonVenvDir(child)) continue;
|
|
4605
4930
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
4606
|
-
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
4607
|
-
out.push(
|
|
4931
|
+
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path30.default.extname(entry2.name))) {
|
|
4932
|
+
out.push(import_node_path30.default.join(start, entry2.name));
|
|
4608
4933
|
}
|
|
4609
4934
|
}
|
|
4610
4935
|
return out;
|
|
@@ -4613,7 +4938,7 @@ async function addK8sResources(graph, scanPath) {
|
|
|
4613
4938
|
let nodesAdded = 0;
|
|
4614
4939
|
const files = await walkYamlFiles2(scanPath);
|
|
4615
4940
|
for (const file of files) {
|
|
4616
|
-
const content = await
|
|
4941
|
+
const content = await import_node_fs17.promises.readFile(file, "utf8");
|
|
4617
4942
|
let docs;
|
|
4618
4943
|
try {
|
|
4619
4944
|
docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
|
|
@@ -4648,17 +4973,17 @@ async function addInfra(graph, scanPath, services) {
|
|
|
4648
4973
|
}
|
|
4649
4974
|
|
|
4650
4975
|
// src/extract/index.ts
|
|
4651
|
-
var
|
|
4976
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
4652
4977
|
|
|
4653
4978
|
// src/extract/retire.ts
|
|
4654
4979
|
init_cjs_shims();
|
|
4655
|
-
var
|
|
4656
|
-
var
|
|
4657
|
-
var
|
|
4980
|
+
var import_node_fs18 = require("fs");
|
|
4981
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
4982
|
+
var import_types20 = require("@neat.is/types");
|
|
4658
4983
|
function dropOrphanedFileNodes(graph) {
|
|
4659
4984
|
const orphans = [];
|
|
4660
4985
|
graph.forEachNode((id, attrs) => {
|
|
4661
|
-
if (attrs.type !==
|
|
4986
|
+
if (attrs.type !== import_types20.NodeType.FileNode) return;
|
|
4662
4987
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
4663
4988
|
orphans.push(id);
|
|
4664
4989
|
}
|
|
@@ -4671,7 +4996,7 @@ function retireEdgesByFile(graph, file) {
|
|
|
4671
4996
|
const toDrop = [];
|
|
4672
4997
|
graph.forEachEdge((id, attrs) => {
|
|
4673
4998
|
const edge = attrs;
|
|
4674
|
-
if (edge.provenance !==
|
|
4999
|
+
if (edge.provenance !== import_types20.Provenance.EXTRACTED) return;
|
|
4675
5000
|
if (!edge.evidence?.file) return;
|
|
4676
5001
|
if (edge.evidence.file === normalized) toDrop.push(id);
|
|
4677
5002
|
});
|
|
@@ -4684,14 +5009,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
4684
5009
|
const bases = [scanPath, ...serviceDirs];
|
|
4685
5010
|
graph.forEachEdge((id, attrs) => {
|
|
4686
5011
|
const edge = attrs;
|
|
4687
|
-
if (edge.provenance !==
|
|
5012
|
+
if (edge.provenance !== import_types20.Provenance.EXTRACTED) return;
|
|
4688
5013
|
const evidenceFile = edge.evidence?.file;
|
|
4689
5014
|
if (!evidenceFile) return;
|
|
4690
|
-
if (
|
|
4691
|
-
if (!(0,
|
|
5015
|
+
if (import_node_path31.default.isAbsolute(evidenceFile)) {
|
|
5016
|
+
if (!(0, import_node_fs18.existsSync)(evidenceFile)) toDrop.push(id);
|
|
4692
5017
|
return;
|
|
4693
5018
|
}
|
|
4694
|
-
const found = bases.some((base) => (0,
|
|
5019
|
+
const found = bases.some((base) => (0, import_node_fs18.existsSync)(import_node_path31.default.join(base, evidenceFile)));
|
|
4695
5020
|
if (!found) toDrop.push(id);
|
|
4696
5021
|
});
|
|
4697
5022
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -4706,6 +5031,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
4706
5031
|
const services = await discoverServices(scanPath);
|
|
4707
5032
|
const phase1Nodes = addServiceNodes(graph, services);
|
|
4708
5033
|
await addServiceAliases(graph, scanPath, services);
|
|
5034
|
+
const fileEnum = await addFiles(graph, services);
|
|
5035
|
+
const importGraph = await addImports(graph, services);
|
|
4709
5036
|
const phase2 = await addDatabasesAndCompat(graph, services, scanPath);
|
|
4710
5037
|
const phase3 = await addConfigNodes(graph, services, scanPath);
|
|
4711
5038
|
const phase4 = await addCallEdges(graph, services);
|
|
@@ -4729,7 +5056,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
4729
5056
|
}
|
|
4730
5057
|
const droppedEntries = drainDroppedExtracted();
|
|
4731
5058
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
4732
|
-
const rejectedPath =
|
|
5059
|
+
const rejectedPath = import_node_path32.default.join(import_node_path32.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
4733
5060
|
try {
|
|
4734
5061
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
4735
5062
|
} catch (err) {
|
|
@@ -4739,8 +5066,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
4739
5066
|
}
|
|
4740
5067
|
}
|
|
4741
5068
|
const result = {
|
|
4742
|
-
nodesAdded: phase1Nodes + phase2.nodesAdded + phase3.nodesAdded + phase4.nodesAdded + phase5.nodesAdded,
|
|
4743
|
-
edgesAdded: phase2.edgesAdded + phase3.edgesAdded + phase4.edgesAdded + phase5.edgesAdded,
|
|
5069
|
+
nodesAdded: phase1Nodes + fileEnum.nodesAdded + importGraph.nodesAdded + phase2.nodesAdded + phase3.nodesAdded + phase4.nodesAdded + phase5.nodesAdded,
|
|
5070
|
+
edgesAdded: fileEnum.edgesAdded + importGraph.edgesAdded + phase2.edgesAdded + phase3.edgesAdded + phase4.edgesAdded + phase5.edgesAdded,
|
|
4744
5071
|
frontiersPromoted,
|
|
4745
5072
|
extractionErrors: errorEntries.length,
|
|
4746
5073
|
errorEntries,
|
|
@@ -4763,7 +5090,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
4763
5090
|
|
|
4764
5091
|
// src/divergences.ts
|
|
4765
5092
|
init_cjs_shims();
|
|
4766
|
-
var
|
|
5093
|
+
var import_types21 = require("@neat.is/types");
|
|
4767
5094
|
function bucketKey(source, target, type) {
|
|
4768
5095
|
return `${type}|${source}|${target}`;
|
|
4769
5096
|
}
|
|
@@ -4771,22 +5098,22 @@ function bucketEdges(graph) {
|
|
|
4771
5098
|
const buckets = /* @__PURE__ */ new Map();
|
|
4772
5099
|
graph.forEachEdge((id, attrs) => {
|
|
4773
5100
|
const e = attrs;
|
|
4774
|
-
const parsed = (0,
|
|
5101
|
+
const parsed = (0, import_types21.parseEdgeId)(id);
|
|
4775
5102
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
4776
5103
|
const key = bucketKey(e.source, e.target, e.type);
|
|
4777
5104
|
const cur = buckets.get(key) ?? { source: e.source, target: e.target, type: e.type };
|
|
4778
5105
|
switch (provenance) {
|
|
4779
|
-
case
|
|
5106
|
+
case import_types21.Provenance.EXTRACTED:
|
|
4780
5107
|
cur.extracted = e;
|
|
4781
5108
|
break;
|
|
4782
|
-
case
|
|
5109
|
+
case import_types21.Provenance.OBSERVED:
|
|
4783
5110
|
cur.observed = e;
|
|
4784
5111
|
break;
|
|
4785
|
-
case
|
|
5112
|
+
case import_types21.Provenance.INFERRED:
|
|
4786
5113
|
cur.inferred = e;
|
|
4787
5114
|
break;
|
|
4788
5115
|
default:
|
|
4789
|
-
if (e.provenance ===
|
|
5116
|
+
if (e.provenance === import_types21.Provenance.STALE) cur.stale = e;
|
|
4790
5117
|
}
|
|
4791
5118
|
buckets.set(key, cur);
|
|
4792
5119
|
});
|
|
@@ -4795,7 +5122,7 @@ function bucketEdges(graph) {
|
|
|
4795
5122
|
function nodeIsFrontier(graph, nodeId) {
|
|
4796
5123
|
if (!graph.hasNode(nodeId)) return false;
|
|
4797
5124
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
4798
|
-
return attrs.type ===
|
|
5125
|
+
return attrs.type === import_types21.NodeType.FrontierNode;
|
|
4799
5126
|
}
|
|
4800
5127
|
function clampConfidence(n) {
|
|
4801
5128
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -4816,7 +5143,7 @@ function gradedConfidence(edge) {
|
|
|
4816
5143
|
}
|
|
4817
5144
|
function detectMissingDivergences(graph, bucket) {
|
|
4818
5145
|
const out = [];
|
|
4819
|
-
if (bucket.type ===
|
|
5146
|
+
if (bucket.type === import_types21.EdgeType.CONTAINS) return out;
|
|
4820
5147
|
if (bucket.extracted && !bucket.observed) {
|
|
4821
5148
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
4822
5149
|
out.push({
|
|
@@ -4857,7 +5184,7 @@ function declaredHostFor(svc) {
|
|
|
4857
5184
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
4858
5185
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
4859
5186
|
const e = graph.getEdgeAttributes(edgeId);
|
|
4860
|
-
if (e.type ===
|
|
5187
|
+
if (e.type === import_types21.EdgeType.CONFIGURED_BY && e.provenance === import_types21.Provenance.EXTRACTED) {
|
|
4861
5188
|
return true;
|
|
4862
5189
|
}
|
|
4863
5190
|
}
|
|
@@ -4870,10 +5197,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
4870
5197
|
const out = [];
|
|
4871
5198
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
4872
5199
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
4873
|
-
if (edge.type !==
|
|
4874
|
-
if (edge.provenance !==
|
|
5200
|
+
if (edge.type !== import_types21.EdgeType.CONNECTS_TO) continue;
|
|
5201
|
+
if (edge.provenance !== import_types21.Provenance.OBSERVED) continue;
|
|
4875
5202
|
const target = graph.getNodeAttributes(edge.target);
|
|
4876
|
-
if (target.type !==
|
|
5203
|
+
if (target.type !== import_types21.NodeType.DatabaseNode) continue;
|
|
4877
5204
|
const observedHost = target.host?.trim();
|
|
4878
5205
|
if (!observedHost) continue;
|
|
4879
5206
|
if (observedHost === declaredHost) continue;
|
|
@@ -4895,10 +5222,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
4895
5222
|
const deps = svc.dependencies ?? {};
|
|
4896
5223
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
4897
5224
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
4898
|
-
if (edge.type !==
|
|
4899
|
-
if (edge.provenance !==
|
|
5225
|
+
if (edge.type !== import_types21.EdgeType.CONNECTS_TO) continue;
|
|
5226
|
+
if (edge.provenance !== import_types21.Provenance.OBSERVED) continue;
|
|
4900
5227
|
const target = graph.getNodeAttributes(edge.target);
|
|
4901
|
-
if (target.type !==
|
|
5228
|
+
if (target.type !== import_types21.NodeType.DatabaseNode) continue;
|
|
4902
5229
|
for (const pair of compatPairs()) {
|
|
4903
5230
|
if (pair.engine !== target.engine) continue;
|
|
4904
5231
|
const declared = deps[pair.driver];
|
|
@@ -4959,7 +5286,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
4959
5286
|
}
|
|
4960
5287
|
graph.forEachNode((nodeId, attrs) => {
|
|
4961
5288
|
const n = attrs;
|
|
4962
|
-
if (n.type !==
|
|
5289
|
+
if (n.type !== import_types21.NodeType.ServiceNode) return;
|
|
4963
5290
|
const svc = n;
|
|
4964
5291
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
4965
5292
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
@@ -4992,7 +5319,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
4992
5319
|
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
4993
5320
|
return a.target.localeCompare(b.target);
|
|
4994
5321
|
});
|
|
4995
|
-
return
|
|
5322
|
+
return import_types21.DivergenceResultSchema.parse({
|
|
4996
5323
|
divergences: filtered,
|
|
4997
5324
|
totalAffected: filtered.length,
|
|
4998
5325
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -5001,9 +5328,9 @@ function computeDivergences(graph, opts = {}) {
|
|
|
5001
5328
|
|
|
5002
5329
|
// src/persist.ts
|
|
5003
5330
|
init_cjs_shims();
|
|
5004
|
-
var
|
|
5005
|
-
var
|
|
5006
|
-
var
|
|
5331
|
+
var import_node_fs19 = require("fs");
|
|
5332
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
5333
|
+
var import_types22 = require("@neat.is/types");
|
|
5007
5334
|
var SCHEMA_VERSION = 4;
|
|
5008
5335
|
function migrateV1ToV2(payload) {
|
|
5009
5336
|
const nodes = payload.graph.nodes;
|
|
@@ -5025,12 +5352,12 @@ function migrateV2ToV3(payload) {
|
|
|
5025
5352
|
for (const edge of edges) {
|
|
5026
5353
|
const attrs = edge.attributes;
|
|
5027
5354
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
5028
|
-
attrs.provenance =
|
|
5355
|
+
attrs.provenance = import_types22.Provenance.OBSERVED;
|
|
5029
5356
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
5030
5357
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
5031
5358
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
5032
5359
|
if (type && source && target) {
|
|
5033
|
-
const newId = (0,
|
|
5360
|
+
const newId = (0, import_types22.observedEdgeId)(source, target, type);
|
|
5034
5361
|
attrs.id = newId;
|
|
5035
5362
|
if (edge.key) edge.key = newId;
|
|
5036
5363
|
}
|
|
@@ -5039,7 +5366,7 @@ function migrateV2ToV3(payload) {
|
|
|
5039
5366
|
return { ...payload, schemaVersion: 3 };
|
|
5040
5367
|
}
|
|
5041
5368
|
async function ensureDir(filePath) {
|
|
5042
|
-
await
|
|
5369
|
+
await import_node_fs19.promises.mkdir(import_node_path33.default.dirname(filePath), { recursive: true });
|
|
5043
5370
|
}
|
|
5044
5371
|
async function saveGraphToDisk(graph, outPath) {
|
|
5045
5372
|
await ensureDir(outPath);
|
|
@@ -5049,13 +5376,13 @@ async function saveGraphToDisk(graph, outPath) {
|
|
|
5049
5376
|
graph: graph.export()
|
|
5050
5377
|
};
|
|
5051
5378
|
const tmp = `${outPath}.tmp`;
|
|
5052
|
-
await
|
|
5053
|
-
await
|
|
5379
|
+
await import_node_fs19.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
|
|
5380
|
+
await import_node_fs19.promises.rename(tmp, outPath);
|
|
5054
5381
|
}
|
|
5055
5382
|
async function loadGraphFromDisk(graph, outPath) {
|
|
5056
5383
|
let raw;
|
|
5057
5384
|
try {
|
|
5058
|
-
raw = await
|
|
5385
|
+
raw = await import_node_fs19.promises.readFile(outPath, "utf8");
|
|
5059
5386
|
} catch (err) {
|
|
5060
5387
|
if (err.code === "ENOENT") return;
|
|
5061
5388
|
throw err;
|
|
@@ -5114,8 +5441,8 @@ function startPersistLoop(graph, outPath, intervalMs = 6e4) {
|
|
|
5114
5441
|
|
|
5115
5442
|
// src/gitignore.ts
|
|
5116
5443
|
init_cjs_shims();
|
|
5117
|
-
var
|
|
5118
|
-
var
|
|
5444
|
+
var import_node_fs20 = require("fs");
|
|
5445
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
5119
5446
|
var NEAT_OUT_LINE = "neat-out/";
|
|
5120
5447
|
var NEAT_HEADER = "# NEAT \u2014 machine-local snapshots and events";
|
|
5121
5448
|
function isNeatOutLine(line) {
|
|
@@ -5123,15 +5450,15 @@ function isNeatOutLine(line) {
|
|
|
5123
5450
|
return trimmed === "neat-out/" || trimmed === "neat-out";
|
|
5124
5451
|
}
|
|
5125
5452
|
async function ensureNeatOutIgnored(projectDir) {
|
|
5126
|
-
const file =
|
|
5453
|
+
const file = import_node_path34.default.join(projectDir, ".gitignore");
|
|
5127
5454
|
let existing = null;
|
|
5128
5455
|
try {
|
|
5129
|
-
existing = await
|
|
5456
|
+
existing = await import_node_fs20.promises.readFile(file, "utf8");
|
|
5130
5457
|
} catch (err) {
|
|
5131
5458
|
if (err.code !== "ENOENT") throw err;
|
|
5132
5459
|
}
|
|
5133
5460
|
if (existing === null) {
|
|
5134
|
-
await
|
|
5461
|
+
await import_node_fs20.promises.writeFile(file, `${NEAT_HEADER}
|
|
5135
5462
|
${NEAT_OUT_LINE}
|
|
5136
5463
|
`, "utf8");
|
|
5137
5464
|
return { action: "created", file };
|
|
@@ -5144,13 +5471,13 @@ ${NEAT_OUT_LINE}
|
|
|
5144
5471
|
${NEAT_HEADER}
|
|
5145
5472
|
${NEAT_OUT_LINE}
|
|
5146
5473
|
`;
|
|
5147
|
-
await
|
|
5474
|
+
await import_node_fs20.promises.writeFile(file, existing + appended, "utf8");
|
|
5148
5475
|
return { action: "added", file };
|
|
5149
5476
|
}
|
|
5150
5477
|
|
|
5151
5478
|
// src/summary.ts
|
|
5152
5479
|
init_cjs_shims();
|
|
5153
|
-
var
|
|
5480
|
+
var import_types23 = require("@neat.is/types");
|
|
5154
5481
|
function renderOtelEnvBlock() {
|
|
5155
5482
|
return [
|
|
5156
5483
|
"for prod OTel routing, set these in your deploy platform's env:",
|
|
@@ -5160,19 +5487,19 @@ function renderOtelEnvBlock() {
|
|
|
5160
5487
|
}
|
|
5161
5488
|
function findIncompatServices(nodes) {
|
|
5162
5489
|
return nodes.filter(
|
|
5163
|
-
(n) => n.type ===
|
|
5490
|
+
(n) => n.type === import_types23.NodeType.ServiceNode && Array.isArray(n.incompatibilities) && (n.incompatibilities ?? []).length > 0
|
|
5164
5491
|
);
|
|
5165
5492
|
}
|
|
5166
5493
|
function servicesWithoutObserved(nodes, edges) {
|
|
5167
5494
|
const seen = /* @__PURE__ */ new Set();
|
|
5168
5495
|
for (const e of edges) {
|
|
5169
|
-
if (e.provenance ===
|
|
5496
|
+
if (e.provenance === import_types23.Provenance.OBSERVED) {
|
|
5170
5497
|
seen.add(e.source);
|
|
5171
5498
|
seen.add(e.target);
|
|
5172
5499
|
}
|
|
5173
5500
|
}
|
|
5174
5501
|
return nodes.filter(
|
|
5175
|
-
(n) => n.type ===
|
|
5502
|
+
(n) => n.type === import_types23.NodeType.ServiceNode && !seen.has(n.id)
|
|
5176
5503
|
);
|
|
5177
5504
|
}
|
|
5178
5505
|
function formatDivergence(d) {
|
|
@@ -5246,27 +5573,27 @@ function formatIncompat(inc) {
|
|
|
5246
5573
|
|
|
5247
5574
|
// src/watch.ts
|
|
5248
5575
|
init_cjs_shims();
|
|
5249
|
-
var
|
|
5250
|
-
var
|
|
5576
|
+
var import_node_fs26 = __toESM(require("fs"), 1);
|
|
5577
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
5251
5578
|
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
5252
5579
|
|
|
5253
5580
|
// src/api.ts
|
|
5254
5581
|
init_cjs_shims();
|
|
5255
5582
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
5256
5583
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
5257
|
-
var
|
|
5584
|
+
var import_types25 = require("@neat.is/types");
|
|
5258
5585
|
|
|
5259
5586
|
// src/extend/index.ts
|
|
5260
5587
|
init_cjs_shims();
|
|
5261
|
-
var
|
|
5262
|
-
var
|
|
5588
|
+
var import_node_fs22 = require("fs");
|
|
5589
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
5263
5590
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
5264
5591
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
5265
5592
|
|
|
5266
5593
|
// src/installers/package-manager.ts
|
|
5267
5594
|
init_cjs_shims();
|
|
5268
|
-
var
|
|
5269
|
-
var
|
|
5595
|
+
var import_node_fs21 = require("fs");
|
|
5596
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
5270
5597
|
var import_node_child_process = require("child_process");
|
|
5271
5598
|
var LOCKFILE_PRIORITY = [
|
|
5272
5599
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -5281,29 +5608,29 @@ var LOCKFILE_PRIORITY = [
|
|
|
5281
5608
|
var NPM_FALLBACK_ARGS = ["install", "--no-audit", "--no-fund", "--prefer-offline"];
|
|
5282
5609
|
async function exists2(p) {
|
|
5283
5610
|
try {
|
|
5284
|
-
await
|
|
5611
|
+
await import_node_fs21.promises.access(p);
|
|
5285
5612
|
return true;
|
|
5286
5613
|
} catch {
|
|
5287
5614
|
return false;
|
|
5288
5615
|
}
|
|
5289
5616
|
}
|
|
5290
5617
|
async function detectPackageManager(serviceDir) {
|
|
5291
|
-
let dir =
|
|
5618
|
+
let dir = import_node_path35.default.resolve(serviceDir);
|
|
5292
5619
|
const stops = /* @__PURE__ */ new Set();
|
|
5293
5620
|
for (let i = 0; i < 64; i++) {
|
|
5294
5621
|
if (stops.has(dir)) break;
|
|
5295
5622
|
stops.add(dir);
|
|
5296
5623
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
5297
|
-
const lockPath =
|
|
5624
|
+
const lockPath = import_node_path35.default.join(dir, candidate.lockfile);
|
|
5298
5625
|
if (await exists2(lockPath)) {
|
|
5299
5626
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
5300
5627
|
}
|
|
5301
5628
|
}
|
|
5302
|
-
const parent =
|
|
5629
|
+
const parent = import_node_path35.default.dirname(dir);
|
|
5303
5630
|
if (parent === dir) break;
|
|
5304
5631
|
dir = parent;
|
|
5305
5632
|
}
|
|
5306
|
-
return { pm: "npm", cwd:
|
|
5633
|
+
return { pm: "npm", cwd: import_node_path35.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
5307
5634
|
}
|
|
5308
5635
|
async function runPackageManagerInstall(cmd) {
|
|
5309
5636
|
return new Promise((resolve) => {
|
|
@@ -5343,32 +5670,32 @@ ${err.message}`
|
|
|
5343
5670
|
}
|
|
5344
5671
|
|
|
5345
5672
|
// src/extend/index.ts
|
|
5346
|
-
async function
|
|
5673
|
+
async function fileExists2(p) {
|
|
5347
5674
|
try {
|
|
5348
|
-
await
|
|
5675
|
+
await import_node_fs22.promises.access(p);
|
|
5349
5676
|
return true;
|
|
5350
5677
|
} catch {
|
|
5351
5678
|
return false;
|
|
5352
5679
|
}
|
|
5353
5680
|
}
|
|
5354
5681
|
async function readPackageJson(scanPath) {
|
|
5355
|
-
const pkgPath =
|
|
5356
|
-
const raw = await
|
|
5682
|
+
const pkgPath = import_node_path36.default.join(scanPath, "package.json");
|
|
5683
|
+
const raw = await import_node_fs22.promises.readFile(pkgPath, "utf8");
|
|
5357
5684
|
return JSON.parse(raw);
|
|
5358
5685
|
}
|
|
5359
5686
|
async function findHookFiles(scanPath) {
|
|
5360
|
-
const entries = await
|
|
5687
|
+
const entries = await import_node_fs22.promises.readdir(scanPath);
|
|
5361
5688
|
return entries.filter(
|
|
5362
5689
|
(e) => (e.startsWith("instrumentation") || e.startsWith("otel-init")) && /\.(ts|js)$/.test(e)
|
|
5363
5690
|
).sort();
|
|
5364
5691
|
}
|
|
5365
5692
|
function extendLogPath() {
|
|
5366
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
5693
|
+
return process.env.NEAT_EXTEND_LOG ?? import_node_path36.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
|
|
5367
5694
|
}
|
|
5368
5695
|
async function appendExtendLog(entry2) {
|
|
5369
5696
|
const logPath = extendLogPath();
|
|
5370
|
-
await
|
|
5371
|
-
await
|
|
5697
|
+
await import_node_fs22.promises.mkdir(import_node_path36.default.dirname(logPath), { recursive: true });
|
|
5698
|
+
await import_node_fs22.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
|
|
5372
5699
|
}
|
|
5373
5700
|
function splicedContent(fileContent, snippet2) {
|
|
5374
5701
|
if (fileContent.includes("__INSTRUMENTATION_BLOCK__")) {
|
|
@@ -5426,7 +5753,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
5426
5753
|
}
|
|
5427
5754
|
async function describeProjectInstrumentation(ctx) {
|
|
5428
5755
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
5429
|
-
const envNeat = await
|
|
5756
|
+
const envNeat = await fileExists2(import_node_path36.default.join(ctx.scanPath, ".env.neat"));
|
|
5430
5757
|
const registryInstrPackages = new Set(
|
|
5431
5758
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
5432
5759
|
);
|
|
@@ -5448,31 +5775,31 @@ async function applyExtension(ctx, args, options) {
|
|
|
5448
5775
|
);
|
|
5449
5776
|
}
|
|
5450
5777
|
for (const file of hookFiles) {
|
|
5451
|
-
const content = await
|
|
5778
|
+
const content = await import_node_fs22.promises.readFile(import_node_path36.default.join(ctx.scanPath, file), "utf8");
|
|
5452
5779
|
if (content.includes(args.registration_snippet)) {
|
|
5453
5780
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
5454
5781
|
}
|
|
5455
5782
|
}
|
|
5456
5783
|
const primaryFile = hookFiles[0];
|
|
5457
|
-
const primaryPath =
|
|
5784
|
+
const primaryPath = import_node_path36.default.join(ctx.scanPath, primaryFile);
|
|
5458
5785
|
const filesTouched = [];
|
|
5459
5786
|
const depsAdded = [];
|
|
5460
|
-
const pkgPath =
|
|
5787
|
+
const pkgPath = import_node_path36.default.join(ctx.scanPath, "package.json");
|
|
5461
5788
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
5462
5789
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
5463
5790
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
5464
|
-
await
|
|
5791
|
+
await import_node_fs22.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
5465
5792
|
filesTouched.push("package.json");
|
|
5466
5793
|
depsAdded.push(`${args.instrumentation_package}@${args.version}`);
|
|
5467
5794
|
}
|
|
5468
|
-
const hookContent = await
|
|
5795
|
+
const hookContent = await import_node_fs22.promises.readFile(primaryPath, "utf8");
|
|
5469
5796
|
const patched = splicedContent(hookContent, args.registration_snippet);
|
|
5470
5797
|
if (!patched) {
|
|
5471
5798
|
throw new Error(
|
|
5472
5799
|
`Could not find instrumentation insertion point in ${primaryFile}. Expected __INSTRUMENTATION_BLOCK__, instrumentations.push(, or new NodeSDK(.`
|
|
5473
5800
|
);
|
|
5474
5801
|
}
|
|
5475
|
-
await
|
|
5802
|
+
await import_node_fs22.promises.writeFile(primaryPath, patched, "utf8");
|
|
5476
5803
|
filesTouched.push(primaryFile);
|
|
5477
5804
|
const cmd = await detectPackageManager(ctx.scanPath);
|
|
5478
5805
|
const installer = options?.runInstall ?? runPackageManagerInstall;
|
|
@@ -5503,7 +5830,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
5503
5830
|
};
|
|
5504
5831
|
}
|
|
5505
5832
|
for (const file of hookFiles) {
|
|
5506
|
-
const content = await
|
|
5833
|
+
const content = await import_node_fs22.promises.readFile(import_node_path36.default.join(ctx.scanPath, file), "utf8");
|
|
5507
5834
|
if (content.includes(args.registration_snippet)) {
|
|
5508
5835
|
return {
|
|
5509
5836
|
library: args.library,
|
|
@@ -5525,7 +5852,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
5525
5852
|
depsToAdd.push(`${args.instrumentation_package}@${args.version}`);
|
|
5526
5853
|
filesTouched.push("package.json");
|
|
5527
5854
|
}
|
|
5528
|
-
const hookContent = await
|
|
5855
|
+
const hookContent = await import_node_fs22.promises.readFile(import_node_path36.default.join(ctx.scanPath, primaryFile), "utf8");
|
|
5529
5856
|
const patched = splicedContent(hookContent, args.registration_snippet);
|
|
5530
5857
|
if (patched) {
|
|
5531
5858
|
filesTouched.push(primaryFile);
|
|
@@ -5537,31 +5864,31 @@ async function dryRunExtension(ctx, args) {
|
|
|
5537
5864
|
}
|
|
5538
5865
|
async function rollbackExtension(ctx, args) {
|
|
5539
5866
|
const logPath = extendLogPath();
|
|
5540
|
-
if (!await
|
|
5867
|
+
if (!await fileExists2(logPath)) {
|
|
5541
5868
|
return { undone: false, message: "no apply found for library" };
|
|
5542
5869
|
}
|
|
5543
|
-
const raw = await
|
|
5870
|
+
const raw = await import_node_fs22.promises.readFile(logPath, "utf8");
|
|
5544
5871
|
const entries = raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
5545
5872
|
const match = [...entries].reverse().find((e) => e.project === ctx.project && e.library === args.library);
|
|
5546
5873
|
if (!match) {
|
|
5547
5874
|
return { undone: false, message: "no apply found for library" };
|
|
5548
5875
|
}
|
|
5549
|
-
const pkgPath =
|
|
5550
|
-
if (await
|
|
5876
|
+
const pkgPath = import_node_path36.default.join(ctx.scanPath, "package.json");
|
|
5877
|
+
if (await fileExists2(pkgPath)) {
|
|
5551
5878
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
5552
5879
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
5553
5880
|
const { [match.instrumentation_package]: _removed, ...rest } = pkg.dependencies;
|
|
5554
5881
|
pkg.dependencies = rest;
|
|
5555
|
-
await
|
|
5882
|
+
await import_node_fs22.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
5556
5883
|
}
|
|
5557
5884
|
}
|
|
5558
5885
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
5559
5886
|
for (const file of hookFiles) {
|
|
5560
|
-
const filePath =
|
|
5561
|
-
const content = await
|
|
5887
|
+
const filePath = import_node_path36.default.join(ctx.scanPath, file);
|
|
5888
|
+
const content = await import_node_fs22.promises.readFile(filePath, "utf8");
|
|
5562
5889
|
if (content.includes(match.registration_snippet)) {
|
|
5563
5890
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
5564
|
-
await
|
|
5891
|
+
await import_node_fs22.promises.writeFile(filePath, filtered, "utf8");
|
|
5565
5892
|
break;
|
|
5566
5893
|
}
|
|
5567
5894
|
}
|
|
@@ -5573,7 +5900,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
5573
5900
|
|
|
5574
5901
|
// src/diff.ts
|
|
5575
5902
|
init_cjs_shims();
|
|
5576
|
-
var
|
|
5903
|
+
var import_node_fs23 = require("fs");
|
|
5577
5904
|
async function loadSnapshotForDiff(target) {
|
|
5578
5905
|
if (/^https?:\/\//i.test(target)) {
|
|
5579
5906
|
const res = await fetch(target);
|
|
@@ -5582,7 +5909,7 @@ async function loadSnapshotForDiff(target) {
|
|
|
5582
5909
|
}
|
|
5583
5910
|
return await res.json();
|
|
5584
5911
|
}
|
|
5585
|
-
const raw = await
|
|
5912
|
+
const raw = await import_node_fs23.promises.readFile(target, "utf8");
|
|
5586
5913
|
return JSON.parse(raw);
|
|
5587
5914
|
}
|
|
5588
5915
|
function indexEntries(entries) {
|
|
@@ -5650,23 +5977,23 @@ function canonicalJson(value) {
|
|
|
5650
5977
|
|
|
5651
5978
|
// src/projects.ts
|
|
5652
5979
|
init_cjs_shims();
|
|
5653
|
-
var
|
|
5980
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
5654
5981
|
function pathsForProject(project, baseDir) {
|
|
5655
5982
|
if (project === DEFAULT_PROJECT) {
|
|
5656
5983
|
return {
|
|
5657
|
-
snapshotPath:
|
|
5658
|
-
errorsPath:
|
|
5659
|
-
staleEventsPath:
|
|
5660
|
-
embeddingsCachePath:
|
|
5661
|
-
policyViolationsPath:
|
|
5984
|
+
snapshotPath: import_node_path37.default.join(baseDir, "graph.json"),
|
|
5985
|
+
errorsPath: import_node_path37.default.join(baseDir, "errors.ndjson"),
|
|
5986
|
+
staleEventsPath: import_node_path37.default.join(baseDir, "stale-events.ndjson"),
|
|
5987
|
+
embeddingsCachePath: import_node_path37.default.join(baseDir, "embeddings.json"),
|
|
5988
|
+
policyViolationsPath: import_node_path37.default.join(baseDir, "policy-violations.ndjson")
|
|
5662
5989
|
};
|
|
5663
5990
|
}
|
|
5664
5991
|
return {
|
|
5665
|
-
snapshotPath:
|
|
5666
|
-
errorsPath:
|
|
5667
|
-
staleEventsPath:
|
|
5668
|
-
embeddingsCachePath:
|
|
5669
|
-
policyViolationsPath:
|
|
5992
|
+
snapshotPath: import_node_path37.default.join(baseDir, `${project}.json`),
|
|
5993
|
+
errorsPath: import_node_path37.default.join(baseDir, `errors.${project}.ndjson`),
|
|
5994
|
+
staleEventsPath: import_node_path37.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
5995
|
+
embeddingsCachePath: import_node_path37.default.join(baseDir, `embeddings.${project}.json`),
|
|
5996
|
+
policyViolationsPath: import_node_path37.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
5670
5997
|
};
|
|
5671
5998
|
}
|
|
5672
5999
|
var Projects = class {
|
|
@@ -5702,25 +6029,25 @@ var Projects = class {
|
|
|
5702
6029
|
|
|
5703
6030
|
// src/registry.ts
|
|
5704
6031
|
init_cjs_shims();
|
|
5705
|
-
var
|
|
6032
|
+
var import_node_fs24 = require("fs");
|
|
5706
6033
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
5707
|
-
var
|
|
5708
|
-
var
|
|
6034
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
6035
|
+
var import_types24 = require("@neat.is/types");
|
|
5709
6036
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
5710
6037
|
var LOCK_RETRY_MS = 50;
|
|
5711
6038
|
function neatHome() {
|
|
5712
6039
|
const override = process.env.NEAT_HOME;
|
|
5713
|
-
if (override && override.length > 0) return
|
|
5714
|
-
return
|
|
6040
|
+
if (override && override.length > 0) return import_node_path38.default.resolve(override);
|
|
6041
|
+
return import_node_path38.default.join(import_node_os3.default.homedir(), ".neat");
|
|
5715
6042
|
}
|
|
5716
6043
|
function registryPath() {
|
|
5717
|
-
return
|
|
6044
|
+
return import_node_path38.default.join(neatHome(), "projects.json");
|
|
5718
6045
|
}
|
|
5719
6046
|
function registryLockPath() {
|
|
5720
|
-
return
|
|
6047
|
+
return import_node_path38.default.join(neatHome(), "projects.json.lock");
|
|
5721
6048
|
}
|
|
5722
6049
|
function daemonPidPath() {
|
|
5723
|
-
return
|
|
6050
|
+
return import_node_path38.default.join(neatHome(), "neatd.pid");
|
|
5724
6051
|
}
|
|
5725
6052
|
function isPidAliveDefault(pid) {
|
|
5726
6053
|
try {
|
|
@@ -5732,7 +6059,7 @@ function isPidAliveDefault(pid) {
|
|
|
5732
6059
|
}
|
|
5733
6060
|
async function readPidFile(file) {
|
|
5734
6061
|
try {
|
|
5735
|
-
const raw = await
|
|
6062
|
+
const raw = await import_node_fs24.promises.readFile(file, "utf8");
|
|
5736
6063
|
const pid = Number.parseInt(raw.trim(), 10);
|
|
5737
6064
|
return Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
5738
6065
|
} catch {
|
|
@@ -5780,32 +6107,32 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
5780
6107
|
}
|
|
5781
6108
|
}
|
|
5782
6109
|
async function normalizeProjectPath(input) {
|
|
5783
|
-
const resolved =
|
|
6110
|
+
const resolved = import_node_path38.default.resolve(input);
|
|
5784
6111
|
try {
|
|
5785
|
-
return await
|
|
6112
|
+
return await import_node_fs24.promises.realpath(resolved);
|
|
5786
6113
|
} catch {
|
|
5787
6114
|
return resolved;
|
|
5788
6115
|
}
|
|
5789
6116
|
}
|
|
5790
6117
|
async function writeAtomically(target, contents) {
|
|
5791
|
-
await
|
|
6118
|
+
await import_node_fs24.promises.mkdir(import_node_path38.default.dirname(target), { recursive: true });
|
|
5792
6119
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
5793
|
-
const fd = await
|
|
6120
|
+
const fd = await import_node_fs24.promises.open(tmp, "w");
|
|
5794
6121
|
try {
|
|
5795
6122
|
await fd.writeFile(contents, "utf8");
|
|
5796
6123
|
await fd.sync();
|
|
5797
6124
|
} finally {
|
|
5798
6125
|
await fd.close();
|
|
5799
6126
|
}
|
|
5800
|
-
await
|
|
6127
|
+
await import_node_fs24.promises.rename(tmp, target);
|
|
5801
6128
|
}
|
|
5802
6129
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
5803
6130
|
const deadline = Date.now() + timeoutMs;
|
|
5804
|
-
await
|
|
6131
|
+
await import_node_fs24.promises.mkdir(import_node_path38.default.dirname(lockPath), { recursive: true });
|
|
5805
6132
|
let probedHolder = false;
|
|
5806
6133
|
while (true) {
|
|
5807
6134
|
try {
|
|
5808
|
-
const fd = await
|
|
6135
|
+
const fd = await import_node_fs24.promises.open(lockPath, "wx");
|
|
5809
6136
|
try {
|
|
5810
6137
|
await fd.writeFile(`${process.pid}
|
|
5811
6138
|
`, "utf8");
|
|
@@ -5830,7 +6157,7 @@ async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaul
|
|
|
5830
6157
|
}
|
|
5831
6158
|
}
|
|
5832
6159
|
async function releaseLock(lockPath) {
|
|
5833
|
-
await
|
|
6160
|
+
await import_node_fs24.promises.unlink(lockPath).catch(() => {
|
|
5834
6161
|
});
|
|
5835
6162
|
}
|
|
5836
6163
|
async function withLock(fn) {
|
|
@@ -5846,7 +6173,7 @@ async function readRegistry() {
|
|
|
5846
6173
|
const file = registryPath();
|
|
5847
6174
|
let raw;
|
|
5848
6175
|
try {
|
|
5849
|
-
raw = await
|
|
6176
|
+
raw = await import_node_fs24.promises.readFile(file, "utf8");
|
|
5850
6177
|
} catch (err) {
|
|
5851
6178
|
if (err.code === "ENOENT") {
|
|
5852
6179
|
return { version: 1, projects: [] };
|
|
@@ -5854,10 +6181,10 @@ async function readRegistry() {
|
|
|
5854
6181
|
throw err;
|
|
5855
6182
|
}
|
|
5856
6183
|
const parsed = JSON.parse(raw);
|
|
5857
|
-
return
|
|
6184
|
+
return import_types24.RegistryFileSchema.parse(parsed);
|
|
5858
6185
|
}
|
|
5859
6186
|
async function writeRegistry(reg) {
|
|
5860
|
-
const validated =
|
|
6187
|
+
const validated = import_types24.RegistryFileSchema.parse(reg);
|
|
5861
6188
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
5862
6189
|
}
|
|
5863
6190
|
var ProjectNameCollisionError = class extends Error {
|
|
@@ -6120,11 +6447,11 @@ function registerRoutes(scope, ctx) {
|
|
|
6120
6447
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
6121
6448
|
const parsed = [];
|
|
6122
6449
|
for (const c of candidates) {
|
|
6123
|
-
const r =
|
|
6450
|
+
const r = import_types25.DivergenceTypeSchema.safeParse(c);
|
|
6124
6451
|
if (!r.success) {
|
|
6125
6452
|
return reply.code(400).send({
|
|
6126
6453
|
error: `unknown divergence type "${c}"`,
|
|
6127
|
-
allowed:
|
|
6454
|
+
allowed: import_types25.DivergenceTypeSchema.options
|
|
6128
6455
|
});
|
|
6129
6456
|
}
|
|
6130
6457
|
parsed.push(r.data);
|
|
@@ -6337,7 +6664,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6337
6664
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
6338
6665
|
let violations = await log.readAll();
|
|
6339
6666
|
if (req.query.severity) {
|
|
6340
|
-
const sev =
|
|
6667
|
+
const sev = import_types25.PolicySeveritySchema.safeParse(req.query.severity);
|
|
6341
6668
|
if (!sev.success) {
|
|
6342
6669
|
return reply.code(400).send({
|
|
6343
6670
|
error: "invalid severity",
|
|
@@ -6354,7 +6681,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6354
6681
|
scope.post("/policies/check", async (req, reply) => {
|
|
6355
6682
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6356
6683
|
if (!proj) return;
|
|
6357
|
-
const parsed =
|
|
6684
|
+
const parsed = import_types25.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
6358
6685
|
if (!parsed.success) {
|
|
6359
6686
|
return reply.code(400).send({
|
|
6360
6687
|
error: "invalid /policies/check body",
|
|
@@ -6597,8 +6924,8 @@ init_otel_grpc();
|
|
|
6597
6924
|
|
|
6598
6925
|
// src/search.ts
|
|
6599
6926
|
init_cjs_shims();
|
|
6600
|
-
var
|
|
6601
|
-
var
|
|
6927
|
+
var import_node_fs25 = require("fs");
|
|
6928
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
6602
6929
|
var import_node_crypto3 = require("crypto");
|
|
6603
6930
|
var DEFAULT_LIMIT = 10;
|
|
6604
6931
|
var NOMIC_DIM = 768;
|
|
@@ -6728,7 +7055,7 @@ async function pickEmbedder() {
|
|
|
6728
7055
|
}
|
|
6729
7056
|
async function readCache(cachePath) {
|
|
6730
7057
|
try {
|
|
6731
|
-
const raw = await
|
|
7058
|
+
const raw = await import_node_fs25.promises.readFile(cachePath, "utf8");
|
|
6732
7059
|
const parsed = JSON.parse(raw);
|
|
6733
7060
|
if (parsed.version !== 1) return null;
|
|
6734
7061
|
return parsed;
|
|
@@ -6737,8 +7064,8 @@ async function readCache(cachePath) {
|
|
|
6737
7064
|
}
|
|
6738
7065
|
}
|
|
6739
7066
|
async function writeCache(cachePath, cache) {
|
|
6740
|
-
await
|
|
6741
|
-
await
|
|
7067
|
+
await import_node_fs25.promises.mkdir(import_node_path41.default.dirname(cachePath), { recursive: true });
|
|
7068
|
+
await import_node_fs25.promises.writeFile(cachePath, JSON.stringify(cache));
|
|
6742
7069
|
}
|
|
6743
7070
|
var VectorIndex = class {
|
|
6744
7071
|
constructor(embedder, cachePath) {
|
|
@@ -6886,6 +7213,7 @@ async function buildSearchIndex(graph, options = {}) {
|
|
|
6886
7213
|
var ALL_PHASES = [
|
|
6887
7214
|
"services",
|
|
6888
7215
|
"aliases",
|
|
7216
|
+
"imports",
|
|
6889
7217
|
"databases",
|
|
6890
7218
|
"configs",
|
|
6891
7219
|
"calls",
|
|
@@ -6893,8 +7221,8 @@ var ALL_PHASES = [
|
|
|
6893
7221
|
];
|
|
6894
7222
|
function classifyChange(relPath) {
|
|
6895
7223
|
const phases = /* @__PURE__ */ new Set();
|
|
6896
|
-
const base =
|
|
6897
|
-
const segments = relPath.split(
|
|
7224
|
+
const base = import_node_path42.default.basename(relPath).toLowerCase();
|
|
7225
|
+
const segments = relPath.split(import_node_path42.default.sep).map((s) => s.toLowerCase());
|
|
6898
7226
|
if (base === "package.json" || base === "requirements.txt" || base === "pyproject.toml" || base === "setup.py") {
|
|
6899
7227
|
phases.add("services");
|
|
6900
7228
|
phases.add("aliases");
|
|
@@ -6909,6 +7237,7 @@ function classifyChange(relPath) {
|
|
|
6909
7237
|
phases.add("aliases");
|
|
6910
7238
|
}
|
|
6911
7239
|
if (/\.(?:js|jsx|mjs|cjs|ts|tsx|py)$/.test(base)) {
|
|
7240
|
+
phases.add("imports");
|
|
6912
7241
|
phases.add("calls");
|
|
6913
7242
|
}
|
|
6914
7243
|
if (/\.ya?ml$/.test(base) && !/^docker-compose.*\.ya?ml$/.test(base)) {
|
|
@@ -6930,6 +7259,11 @@ async function runExtractPhases(graph, scanPath, phases, project = DEFAULT_PROJE
|
|
|
6930
7259
|
if (phases.has("aliases")) {
|
|
6931
7260
|
await addServiceAliases(graph, scanPath, services);
|
|
6932
7261
|
}
|
|
7262
|
+
if (phases.has("imports")) {
|
|
7263
|
+
const r = await addImports(graph, services);
|
|
7264
|
+
nodesAdded += r.nodesAdded;
|
|
7265
|
+
edgesAdded += r.edgesAdded;
|
|
7266
|
+
}
|
|
6933
7267
|
if (phases.has("databases")) {
|
|
6934
7268
|
const r = await addDatabasesAndCompat(graph, services, scanPath);
|
|
6935
7269
|
nodesAdded += r.nodesAdded;
|
|
@@ -7002,16 +7336,16 @@ function countWatchableDirs(scanPath, limit) {
|
|
|
7002
7336
|
if (count >= limit) return;
|
|
7003
7337
|
let entries;
|
|
7004
7338
|
try {
|
|
7005
|
-
entries =
|
|
7339
|
+
entries = import_node_fs26.default.readdirSync(dir, { withFileTypes: true });
|
|
7006
7340
|
} catch {
|
|
7007
7341
|
return;
|
|
7008
7342
|
}
|
|
7009
7343
|
for (const e of entries) {
|
|
7010
7344
|
if (count >= limit) return;
|
|
7011
7345
|
if (!e.isDirectory()) continue;
|
|
7012
|
-
if (IGNORED_WATCH_PATHS.some((re) => re.test(
|
|
7346
|
+
if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path42.default.join(dir, e.name) + import_node_path42.default.sep))) continue;
|
|
7013
7347
|
count++;
|
|
7014
|
-
if (depth < 2) visit(
|
|
7348
|
+
if (depth < 2) visit(import_node_path42.default.join(dir, e.name), depth + 1);
|
|
7015
7349
|
}
|
|
7016
7350
|
};
|
|
7017
7351
|
visit(scanPath, 0);
|
|
@@ -7029,8 +7363,8 @@ async function startWatch(graph, opts) {
|
|
|
7029
7363
|
const projectName = opts.project ?? DEFAULT_PROJECT;
|
|
7030
7364
|
await loadGraphFromDisk(graph, opts.outPath);
|
|
7031
7365
|
const detachEventBus = attachGraphToEventBus(graph, { project: projectName });
|
|
7032
|
-
const policyFilePath =
|
|
7033
|
-
const policyViolationsPath =
|
|
7366
|
+
const policyFilePath = import_node_path42.default.join(opts.scanPath, "policy.json");
|
|
7367
|
+
const policyViolationsPath = import_node_path42.default.join(import_node_path42.default.dirname(opts.outPath), "policy-violations.ndjson");
|
|
7034
7368
|
let policies = [];
|
|
7035
7369
|
try {
|
|
7036
7370
|
policies = await loadPolicyFile(policyFilePath);
|
|
@@ -7081,7 +7415,7 @@ async function startWatch(graph, opts) {
|
|
|
7081
7415
|
assertBindAuthority(host, auth.authToken);
|
|
7082
7416
|
const port = opts.port ?? 8080;
|
|
7083
7417
|
const otelPort = opts.otelPort ?? 4318;
|
|
7084
|
-
const cachePath = opts.embeddingsCachePath ??
|
|
7418
|
+
const cachePath = opts.embeddingsCachePath ?? import_node_path42.default.join(import_node_path42.default.dirname(opts.outPath), "embeddings.json");
|
|
7085
7419
|
let searchIndex;
|
|
7086
7420
|
try {
|
|
7087
7421
|
searchIndex = await buildSearchIndex(graph, { cachePath });
|
|
@@ -7099,7 +7433,7 @@ async function startWatch(graph, opts) {
|
|
|
7099
7433
|
// Paths are derived from the explicit options the watch caller passes
|
|
7100
7434
|
// — pathsForProject is only used to fill in the embeddings/snapshot
|
|
7101
7435
|
// fields so the registry shape is complete.
|
|
7102
|
-
...pathsForProject(projectName,
|
|
7436
|
+
...pathsForProject(projectName, import_node_path42.default.dirname(opts.outPath)),
|
|
7103
7437
|
snapshotPath: opts.outPath,
|
|
7104
7438
|
errorsPath: opts.errorsPath,
|
|
7105
7439
|
staleEventsPath: opts.staleEventsPath
|
|
@@ -7186,9 +7520,9 @@ async function startWatch(graph, opts) {
|
|
|
7186
7520
|
};
|
|
7187
7521
|
const onPath = (absPath) => {
|
|
7188
7522
|
if (shouldIgnore(absPath)) return;
|
|
7189
|
-
const rel =
|
|
7523
|
+
const rel = import_node_path42.default.relative(opts.scanPath, absPath);
|
|
7190
7524
|
if (!rel || rel.startsWith("..")) return;
|
|
7191
|
-
pendingPaths.add(rel.split(
|
|
7525
|
+
pendingPaths.add(rel.split(import_node_path42.default.sep).join("/"));
|
|
7192
7526
|
const phases = classifyChange(rel);
|
|
7193
7527
|
if (phases.size === 0) {
|
|
7194
7528
|
for (const p of ALL_PHASES) pending.add(p);
|
|
@@ -7242,8 +7576,8 @@ async function startWatch(graph, opts) {
|
|
|
7242
7576
|
|
|
7243
7577
|
// src/deploy/detect.ts
|
|
7244
7578
|
init_cjs_shims();
|
|
7245
|
-
var
|
|
7246
|
-
var
|
|
7579
|
+
var import_node_fs27 = require("fs");
|
|
7580
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
7247
7581
|
var import_node_child_process2 = require("child_process");
|
|
7248
7582
|
var import_node_crypto4 = require("crypto");
|
|
7249
7583
|
function generateToken() {
|
|
@@ -7343,21 +7677,21 @@ async function runDeploy(opts = {}) {
|
|
|
7343
7677
|
const token = generateToken();
|
|
7344
7678
|
switch (substrate) {
|
|
7345
7679
|
case "docker-compose": {
|
|
7346
|
-
const artifactPath =
|
|
7680
|
+
const artifactPath = import_node_path43.default.join(cwd, "docker-compose.neat.yml");
|
|
7347
7681
|
const contents = emitDockerCompose(cwd);
|
|
7348
|
-
await
|
|
7682
|
+
await import_node_fs27.promises.writeFile(artifactPath, contents, "utf8");
|
|
7349
7683
|
return {
|
|
7350
7684
|
substrate,
|
|
7351
7685
|
artifactPath,
|
|
7352
7686
|
token,
|
|
7353
7687
|
contents,
|
|
7354
|
-
startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${
|
|
7688
|
+
startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${import_node_path43.default.basename(artifactPath)} up -d`
|
|
7355
7689
|
};
|
|
7356
7690
|
}
|
|
7357
7691
|
case "systemd": {
|
|
7358
|
-
const artifactPath =
|
|
7692
|
+
const artifactPath = import_node_path43.default.join(cwd, "neat.service");
|
|
7359
7693
|
const contents = emitSystemdUnit(cwd);
|
|
7360
|
-
await
|
|
7694
|
+
await import_node_fs27.promises.writeFile(artifactPath, contents, "utf8");
|
|
7361
7695
|
return {
|
|
7362
7696
|
substrate,
|
|
7363
7697
|
artifactPath,
|
|
@@ -7390,15 +7724,16 @@ init_cjs_shims();
|
|
|
7390
7724
|
|
|
7391
7725
|
// src/installers/javascript.ts
|
|
7392
7726
|
init_cjs_shims();
|
|
7393
|
-
var
|
|
7394
|
-
var
|
|
7727
|
+
var import_node_fs28 = require("fs");
|
|
7728
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
7395
7729
|
var import_semver2 = __toESM(require("semver"), 1);
|
|
7396
7730
|
|
|
7397
7731
|
// src/installers/templates.ts
|
|
7398
7732
|
init_cjs_shims();
|
|
7399
7733
|
var OTEL_INIT_HEADER = "// Generated by `neat init --apply` (ADR-069). OpenTelemetry auto-instrumentation hook.";
|
|
7400
|
-
var OTEL_INIT_STAMP = "// neat-template-version:
|
|
7734
|
+
var OTEL_INIT_STAMP = "// neat-template-version: 5 \u2014 layered file-first capture (ADR-090) + http/json protocol pin (#468).";
|
|
7401
7735
|
var OTEL_OTLP_HEADERS_JS = "if (process.env.NEAT_OTEL_TOKEN) process.env.OTEL_EXPORTER_OTLP_HEADERS ||= 'Authorization=Bearer ' + process.env.NEAT_OTEL_TOKEN";
|
|
7736
|
+
var OTEL_OTLP_PROTOCOL_JS = "process.env.OTEL_EXPORTER_OTLP_PROTOCOL ||= 'http/json'";
|
|
7402
7737
|
function neatCaptureSource(ts) {
|
|
7403
7738
|
const spanT = ts ? ": any" : "";
|
|
7404
7739
|
const strOpt = ts ? ": string | undefined" : "";
|
|
@@ -7688,6 +8023,7 @@ var OTEL_INIT_CJS = `${OTEL_INIT_HEADER}
|
|
|
7688
8023
|
${OTEL_INIT_STAMP}
|
|
7689
8024
|
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
7690
8025
|
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projects/__PROJECT__/v1/traces'
|
|
8026
|
+
${OTEL_OTLP_PROTOCOL_JS}
|
|
7691
8027
|
${OTEL_OTLP_HEADERS_JS}
|
|
7692
8028
|
|
|
7693
8029
|
const { NodeSDK } = require('@opentelemetry/sdk-node')
|
|
@@ -7706,6 +8042,7 @@ var OTEL_INIT_ESM = `${OTEL_INIT_HEADER}
|
|
|
7706
8042
|
${OTEL_INIT_STAMP}
|
|
7707
8043
|
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
7708
8044
|
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projects/__PROJECT__/v1/traces'
|
|
8045
|
+
${OTEL_OTLP_PROTOCOL_JS}
|
|
7709
8046
|
${OTEL_OTLP_HEADERS_JS}
|
|
7710
8047
|
|
|
7711
8048
|
import { NodeSDK } from '@opentelemetry/sdk-node'
|
|
@@ -7729,6 +8066,7 @@ ${OTEL_INIT_STAMP}
|
|
|
7729
8066
|
// constraining the runtime logic. The file is regenerated, never hand-edited.
|
|
7730
8067
|
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
7731
8068
|
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projects/__PROJECT__/v1/traces'
|
|
8069
|
+
${OTEL_OTLP_PROTOCOL_JS}
|
|
7732
8070
|
${OTEL_OTLP_HEADERS_JS}
|
|
7733
8071
|
|
|
7734
8072
|
import { NodeSDK } from '@opentelemetry/sdk-node'
|
|
@@ -7754,6 +8092,7 @@ function renderEnvNeat(serviceName, projectName) {
|
|
|
7754
8092
|
"# Generated by `neat init --apply` (ADR-069).",
|
|
7755
8093
|
`OTEL_SERVICE_NAME=${serviceName}`,
|
|
7756
8094
|
`OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/projects/${projectName}/v1/traces`,
|
|
8095
|
+
"OTEL_EXPORTER_OTLP_PROTOCOL=http/json",
|
|
7757
8096
|
"# Set NEAT_OTEL_TOKEN to the daemon's OTLP secret to authenticate exported spans (#410).",
|
|
7758
8097
|
"# NEAT_OTEL_TOKEN=",
|
|
7759
8098
|
""
|
|
@@ -7777,6 +8116,7 @@ export async function register() {
|
|
|
7777
8116
|
var NEXT_INSTRUMENTATION_NODE_TS = `${NEXT_INSTRUMENTATION_HEADER}
|
|
7778
8117
|
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
7779
8118
|
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projects/__PROJECT__/v1/traces'
|
|
8119
|
+
${OTEL_OTLP_PROTOCOL_JS}
|
|
7780
8120
|
${OTEL_OTLP_HEADERS_JS}
|
|
7781
8121
|
|
|
7782
8122
|
import { NodeSDK } from '@opentelemetry/sdk-node'
|
|
@@ -7789,6 +8129,7 @@ new NodeSDK({ instrumentations }).start()
|
|
|
7789
8129
|
var NEXT_INSTRUMENTATION_NODE_JS = `${NEXT_INSTRUMENTATION_HEADER}
|
|
7790
8130
|
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
7791
8131
|
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projects/__PROJECT__/v1/traces'
|
|
8132
|
+
${OTEL_OTLP_PROTOCOL_JS}
|
|
7792
8133
|
${OTEL_OTLP_HEADERS_JS}
|
|
7793
8134
|
|
|
7794
8135
|
const { NodeSDK } = require('@opentelemetry/sdk-node')
|
|
@@ -7808,6 +8149,7 @@ var FRAMEWORK_OTEL_INIT_HEADER = "// Generated by `neat init --apply` (ADR-074).
|
|
|
7808
8149
|
var FRAMEWORK_OTEL_INIT_TS_BODY = `${FRAMEWORK_OTEL_INIT_HEADER}
|
|
7809
8150
|
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
7810
8151
|
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projects/__PROJECT__/v1/traces'
|
|
8152
|
+
${OTEL_OTLP_PROTOCOL_JS}
|
|
7811
8153
|
${OTEL_OTLP_HEADERS_JS}
|
|
7812
8154
|
|
|
7813
8155
|
import { NodeSDK } from '@opentelemetry/sdk-node'
|
|
@@ -7822,6 +8164,7 @@ sdk.start()
|
|
|
7822
8164
|
var FRAMEWORK_OTEL_INIT_JS_BODY = `${FRAMEWORK_OTEL_INIT_HEADER}
|
|
7823
8165
|
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
7824
8166
|
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projects/__PROJECT__/v1/traces'
|
|
8167
|
+
${OTEL_OTLP_PROTOCOL_JS}
|
|
7825
8168
|
${OTEL_OTLP_HEADERS_JS}
|
|
7826
8169
|
|
|
7827
8170
|
const { NodeSDK } = require('@opentelemetry/sdk-node')
|
|
@@ -7929,15 +8272,15 @@ var OTEL_ENV = {
|
|
|
7929
8272
|
value: "http://localhost:4318/projects/<project>/v1/traces"
|
|
7930
8273
|
};
|
|
7931
8274
|
function serviceNodeName(pkg, serviceDir) {
|
|
7932
|
-
return pkg.name ??
|
|
8275
|
+
return pkg.name ?? import_node_path44.default.basename(serviceDir);
|
|
7933
8276
|
}
|
|
7934
8277
|
function projectToken(pkg, serviceDir, project) {
|
|
7935
8278
|
if (project && project.length > 0) return project;
|
|
7936
|
-
return pkg.name ??
|
|
8279
|
+
return pkg.name ?? import_node_path44.default.basename(serviceDir);
|
|
7937
8280
|
}
|
|
7938
8281
|
async function readJsonFile(p) {
|
|
7939
8282
|
try {
|
|
7940
|
-
const raw = await
|
|
8283
|
+
const raw = await import_node_fs28.promises.readFile(p, "utf8");
|
|
7941
8284
|
return JSON.parse(raw);
|
|
7942
8285
|
} catch {
|
|
7943
8286
|
return null;
|
|
@@ -7946,16 +8289,16 @@ async function readJsonFile(p) {
|
|
|
7946
8289
|
async function detectRuntimeKind(pkgRoot, pkg) {
|
|
7947
8290
|
const deps = allDeps(pkg);
|
|
7948
8291
|
if ("react-native" in deps || "expo" in deps) return "react-native";
|
|
7949
|
-
const appJson = await readJsonFile(
|
|
8292
|
+
const appJson = await readJsonFile(import_node_path44.default.join(pkgRoot, "app.json"));
|
|
7950
8293
|
if (appJson && typeof appJson === "object" && "expo" in appJson) {
|
|
7951
8294
|
return "react-native";
|
|
7952
8295
|
}
|
|
7953
|
-
if (await exists3(
|
|
8296
|
+
if (await exists3(import_node_path44.default.join(pkgRoot, "vite.config.js")) || await exists3(import_node_path44.default.join(pkgRoot, "vite.config.ts")) || await exists3(import_node_path44.default.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
|
|
7954
8297
|
return "browser-bundle";
|
|
7955
8298
|
}
|
|
7956
|
-
if (await exists3(
|
|
7957
|
-
if (await exists3(
|
|
7958
|
-
if (await exists3(
|
|
8299
|
+
if (await exists3(import_node_path44.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
|
|
8300
|
+
if (await exists3(import_node_path44.default.join(pkgRoot, "bun.lockb"))) return "bun";
|
|
8301
|
+
if (await exists3(import_node_path44.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path44.default.join(pkgRoot, "deno.lock"))) {
|
|
7959
8302
|
return "deno";
|
|
7960
8303
|
}
|
|
7961
8304
|
const engines = pkg.engines ?? {};
|
|
@@ -7964,7 +8307,7 @@ async function detectRuntimeKind(pkgRoot, pkg) {
|
|
|
7964
8307
|
}
|
|
7965
8308
|
async function readPackageJson2(serviceDir) {
|
|
7966
8309
|
try {
|
|
7967
|
-
const raw = await
|
|
8310
|
+
const raw = await import_node_fs28.promises.readFile(import_node_path44.default.join(serviceDir, "package.json"), "utf8");
|
|
7968
8311
|
return JSON.parse(raw);
|
|
7969
8312
|
} catch {
|
|
7970
8313
|
return null;
|
|
@@ -7972,7 +8315,7 @@ async function readPackageJson2(serviceDir) {
|
|
|
7972
8315
|
}
|
|
7973
8316
|
async function exists3(p) {
|
|
7974
8317
|
try {
|
|
7975
|
-
await
|
|
8318
|
+
await import_node_fs28.promises.stat(p);
|
|
7976
8319
|
return true;
|
|
7977
8320
|
} catch {
|
|
7978
8321
|
return false;
|
|
@@ -7980,7 +8323,7 @@ async function exists3(p) {
|
|
|
7980
8323
|
}
|
|
7981
8324
|
async function readFileMaybe(p) {
|
|
7982
8325
|
try {
|
|
7983
|
-
return await
|
|
8326
|
+
return await import_node_fs28.promises.readFile(p, "utf8");
|
|
7984
8327
|
} catch {
|
|
7985
8328
|
return null;
|
|
7986
8329
|
}
|
|
@@ -8008,7 +8351,7 @@ function needsVersionUpgrade(installed, expected) {
|
|
|
8008
8351
|
var NEXT_CONFIG_CANDIDATES = ["next.config.js", "next.config.ts", "next.config.mjs"];
|
|
8009
8352
|
async function findNextConfig(serviceDir) {
|
|
8010
8353
|
for (const name of NEXT_CONFIG_CANDIDATES) {
|
|
8011
|
-
const candidate =
|
|
8354
|
+
const candidate = import_node_path44.default.join(serviceDir, name);
|
|
8012
8355
|
if (await exists3(candidate)) return candidate;
|
|
8013
8356
|
}
|
|
8014
8357
|
return null;
|
|
@@ -8035,7 +8378,7 @@ function hasRemixDependency(pkg) {
|
|
|
8035
8378
|
}
|
|
8036
8379
|
async function findRemixEntry(serviceDir) {
|
|
8037
8380
|
for (const rel of REMIX_ENTRY_CANDIDATES) {
|
|
8038
|
-
const candidate =
|
|
8381
|
+
const candidate = import_node_path44.default.join(serviceDir, rel);
|
|
8039
8382
|
if (await exists3(candidate)) return candidate;
|
|
8040
8383
|
}
|
|
8041
8384
|
return null;
|
|
@@ -8047,14 +8390,14 @@ function hasSvelteKitDependency(pkg) {
|
|
|
8047
8390
|
}
|
|
8048
8391
|
async function findSvelteKitHooks(serviceDir) {
|
|
8049
8392
|
for (const rel of SVELTEKIT_HOOKS_CANDIDATES) {
|
|
8050
|
-
const candidate =
|
|
8393
|
+
const candidate = import_node_path44.default.join(serviceDir, rel);
|
|
8051
8394
|
if (await exists3(candidate)) return candidate;
|
|
8052
8395
|
}
|
|
8053
8396
|
return null;
|
|
8054
8397
|
}
|
|
8055
8398
|
async function findSvelteKitConfig(serviceDir) {
|
|
8056
8399
|
for (const rel of SVELTEKIT_CONFIG_CANDIDATES) {
|
|
8057
|
-
const candidate =
|
|
8400
|
+
const candidate = import_node_path44.default.join(serviceDir, rel);
|
|
8058
8401
|
if (await exists3(candidate)) return candidate;
|
|
8059
8402
|
}
|
|
8060
8403
|
return null;
|
|
@@ -8065,7 +8408,7 @@ function hasNuxtDependency(pkg) {
|
|
|
8065
8408
|
}
|
|
8066
8409
|
async function findNuxtConfig(serviceDir) {
|
|
8067
8410
|
for (const name of NUXT_CONFIG_CANDIDATES) {
|
|
8068
|
-
const candidate =
|
|
8411
|
+
const candidate = import_node_path44.default.join(serviceDir, name);
|
|
8069
8412
|
if (await exists3(candidate)) return candidate;
|
|
8070
8413
|
}
|
|
8071
8414
|
return null;
|
|
@@ -8076,7 +8419,7 @@ function hasAstroDependency(pkg) {
|
|
|
8076
8419
|
}
|
|
8077
8420
|
async function findAstroConfig(serviceDir) {
|
|
8078
8421
|
for (const name of ASTRO_CONFIG_CANDIDATES) {
|
|
8079
|
-
const candidate =
|
|
8422
|
+
const candidate = import_node_path44.default.join(serviceDir, name);
|
|
8080
8423
|
if (await exists3(candidate)) return candidate;
|
|
8081
8424
|
}
|
|
8082
8425
|
return null;
|
|
@@ -8090,7 +8433,7 @@ function parseNextMajor(range) {
|
|
|
8090
8433
|
return Number.isFinite(n) ? n : null;
|
|
8091
8434
|
}
|
|
8092
8435
|
async function isTypeScriptProject(serviceDir) {
|
|
8093
|
-
return exists3(
|
|
8436
|
+
return exists3(import_node_path44.default.join(serviceDir, "tsconfig.json"));
|
|
8094
8437
|
}
|
|
8095
8438
|
var INDEX_EXTENSIONS = [".ts", ".tsx", ".js", ".mjs", ".cjs"];
|
|
8096
8439
|
var INDEX_CANDIDATES = INDEX_EXTENSIONS.map((ext) => `index${ext}`);
|
|
@@ -8136,7 +8479,7 @@ function entryFromScript(script) {
|
|
|
8136
8479
|
}
|
|
8137
8480
|
async function resolveEntry(serviceDir, pkg) {
|
|
8138
8481
|
if (typeof pkg.main === "string" && pkg.main.length > 0) {
|
|
8139
|
-
const candidate =
|
|
8482
|
+
const candidate = import_node_path44.default.resolve(serviceDir, pkg.main);
|
|
8140
8483
|
if (await exists3(candidate)) return candidate;
|
|
8141
8484
|
}
|
|
8142
8485
|
if (pkg.bin) {
|
|
@@ -8150,36 +8493,36 @@ async function resolveEntry(serviceDir, pkg) {
|
|
|
8150
8493
|
if (typeof first === "string") binEntry = first;
|
|
8151
8494
|
}
|
|
8152
8495
|
if (binEntry) {
|
|
8153
|
-
const candidate =
|
|
8496
|
+
const candidate = import_node_path44.default.resolve(serviceDir, binEntry);
|
|
8154
8497
|
if (await exists3(candidate)) return candidate;
|
|
8155
8498
|
}
|
|
8156
8499
|
}
|
|
8157
8500
|
const startEntry = entryFromScript(pkg.scripts?.start);
|
|
8158
8501
|
if (startEntry) {
|
|
8159
|
-
const candidate =
|
|
8502
|
+
const candidate = import_node_path44.default.resolve(serviceDir, startEntry);
|
|
8160
8503
|
if (await exists3(candidate)) return candidate;
|
|
8161
8504
|
}
|
|
8162
8505
|
const devEntry = entryFromScript(pkg.scripts?.dev);
|
|
8163
8506
|
if (devEntry) {
|
|
8164
|
-
const candidate =
|
|
8507
|
+
const candidate = import_node_path44.default.resolve(serviceDir, devEntry);
|
|
8165
8508
|
if (await exists3(candidate)) return candidate;
|
|
8166
8509
|
}
|
|
8167
8510
|
for (const rel of SRC_INDEX_CANDIDATES) {
|
|
8168
|
-
const candidate =
|
|
8511
|
+
const candidate = import_node_path44.default.join(serviceDir, rel);
|
|
8169
8512
|
if (await exists3(candidate)) return candidate;
|
|
8170
8513
|
}
|
|
8171
8514
|
for (const rel of SRC_NAMED_CANDIDATES) {
|
|
8172
|
-
const candidate =
|
|
8515
|
+
const candidate = import_node_path44.default.join(serviceDir, rel);
|
|
8173
8516
|
if (await exists3(candidate)) return candidate;
|
|
8174
8517
|
}
|
|
8175
8518
|
for (const name of INDEX_CANDIDATES) {
|
|
8176
|
-
const candidate =
|
|
8519
|
+
const candidate = import_node_path44.default.join(serviceDir, name);
|
|
8177
8520
|
if (await exists3(candidate)) return candidate;
|
|
8178
8521
|
}
|
|
8179
8522
|
return null;
|
|
8180
8523
|
}
|
|
8181
8524
|
function dispatchEntry(entryFile, pkg) {
|
|
8182
|
-
const ext =
|
|
8525
|
+
const ext = import_node_path44.default.extname(entryFile).toLowerCase();
|
|
8183
8526
|
if (ext === ".ts" || ext === ".tsx") return "ts";
|
|
8184
8527
|
if (ext === ".mjs") return "esm";
|
|
8185
8528
|
if (ext === ".cjs") return "cjs";
|
|
@@ -8196,9 +8539,9 @@ function otelInitContents(flavor) {
|
|
|
8196
8539
|
return OTEL_INIT_CJS;
|
|
8197
8540
|
}
|
|
8198
8541
|
function injectionLine(flavor, entryFile, otelInitFile) {
|
|
8199
|
-
let rel =
|
|
8542
|
+
let rel = import_node_path44.default.relative(import_node_path44.default.dirname(entryFile), otelInitFile);
|
|
8200
8543
|
if (!rel.startsWith(".")) rel = `./${rel}`;
|
|
8201
|
-
rel = rel.split(
|
|
8544
|
+
rel = rel.split(import_node_path44.default.sep).join("/");
|
|
8202
8545
|
if (flavor === "cjs") return `require('${rel}')`;
|
|
8203
8546
|
if (flavor === "esm") return `import '${rel}'`;
|
|
8204
8547
|
const tsRel = rel.replace(/\.ts$/, "");
|
|
@@ -8211,23 +8554,23 @@ function lineIsOtelInjection(line) {
|
|
|
8211
8554
|
}
|
|
8212
8555
|
async function detectsSrcLayout(serviceDir) {
|
|
8213
8556
|
const [hasSrcApp, hasSrcPages, hasRootApp, hasRootPages] = await Promise.all([
|
|
8214
|
-
exists3(
|
|
8215
|
-
exists3(
|
|
8216
|
-
exists3(
|
|
8217
|
-
exists3(
|
|
8557
|
+
exists3(import_node_path44.default.join(serviceDir, "src", "app")),
|
|
8558
|
+
exists3(import_node_path44.default.join(serviceDir, "src", "pages")),
|
|
8559
|
+
exists3(import_node_path44.default.join(serviceDir, "app")),
|
|
8560
|
+
exists3(import_node_path44.default.join(serviceDir, "pages"))
|
|
8218
8561
|
]);
|
|
8219
8562
|
return (hasSrcApp || hasSrcPages) && !hasRootApp && !hasRootPages;
|
|
8220
8563
|
}
|
|
8221
8564
|
async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project) {
|
|
8222
8565
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
8223
8566
|
const srcLayout = await detectsSrcLayout(serviceDir);
|
|
8224
|
-
const baseDir = srcLayout ?
|
|
8225
|
-
const instrumentationFile =
|
|
8226
|
-
const instrumentationNodeFile =
|
|
8567
|
+
const baseDir = srcLayout ? import_node_path44.default.join(serviceDir, "src") : serviceDir;
|
|
8568
|
+
const instrumentationFile = import_node_path44.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
|
|
8569
|
+
const instrumentationNodeFile = import_node_path44.default.join(
|
|
8227
8570
|
baseDir,
|
|
8228
8571
|
useTs ? "instrumentation.node.ts" : "instrumentation.node.js"
|
|
8229
8572
|
);
|
|
8230
|
-
const envNeatFile =
|
|
8573
|
+
const envNeatFile = import_node_path44.default.join(baseDir, ".env.neat");
|
|
8231
8574
|
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
8232
8575
|
const dependencyEdits = [];
|
|
8233
8576
|
for (const sdk of SDK_PACKAGES) {
|
|
@@ -8284,7 +8627,7 @@ async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project)
|
|
|
8284
8627
|
const nextMajor = parseNextMajor(nextRange);
|
|
8285
8628
|
if (nextMajor !== null && nextMajor < 15) {
|
|
8286
8629
|
try {
|
|
8287
|
-
const raw = await
|
|
8630
|
+
const raw = await import_node_fs28.promises.readFile(nextConfigPath, "utf8");
|
|
8288
8631
|
if (!raw.includes("instrumentationHook")) {
|
|
8289
8632
|
nextConfigEdit = {
|
|
8290
8633
|
file: nextConfigPath,
|
|
@@ -8332,7 +8675,7 @@ function buildDependencyEdits(pkg, manifestPath) {
|
|
|
8332
8675
|
return edits;
|
|
8333
8676
|
}
|
|
8334
8677
|
async function queueEnvNeat(serviceDir, pkg, project, generatedFiles) {
|
|
8335
|
-
const envNeatFile =
|
|
8678
|
+
const envNeatFile = import_node_path44.default.join(serviceDir, ".env.neat");
|
|
8336
8679
|
if (!await exists3(envNeatFile)) {
|
|
8337
8680
|
generatedFiles.push({
|
|
8338
8681
|
file: envNeatFile,
|
|
@@ -8367,7 +8710,7 @@ function fileImportsOtelHook(raw, specifiers) {
|
|
|
8367
8710
|
}
|
|
8368
8711
|
async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
8369
8712
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
8370
|
-
const otelServerFile =
|
|
8713
|
+
const otelServerFile = import_node_path44.default.join(
|
|
8371
8714
|
serviceDir,
|
|
8372
8715
|
useTs ? "app/otel.server.ts" : "app/otel.server.js"
|
|
8373
8716
|
);
|
|
@@ -8388,7 +8731,7 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
|
8388
8731
|
await queueEnvNeat(serviceDir, pkg, project, generatedFiles);
|
|
8389
8732
|
const entrypointEdits = [];
|
|
8390
8733
|
try {
|
|
8391
|
-
const raw = await
|
|
8734
|
+
const raw = await import_node_fs28.promises.readFile(entryFile, "utf8");
|
|
8392
8735
|
if (!fileImportsOtelHook(raw, ["./otel.server"])) {
|
|
8393
8736
|
const lines = raw.split(/\r?\n/);
|
|
8394
8737
|
const firstReal = lines[0]?.startsWith("#!") ? lines[1] ?? "" : lines[0] ?? "";
|
|
@@ -8424,11 +8767,11 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
|
8424
8767
|
}
|
|
8425
8768
|
async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project) {
|
|
8426
8769
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
8427
|
-
const otelInitFile =
|
|
8770
|
+
const otelInitFile = import_node_path44.default.join(
|
|
8428
8771
|
serviceDir,
|
|
8429
8772
|
useTs ? "src/otel-init.ts" : "src/otel-init.js"
|
|
8430
8773
|
);
|
|
8431
|
-
const resolvedHooksFile = hooksFile ??
|
|
8774
|
+
const resolvedHooksFile = hooksFile ?? import_node_path44.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
|
|
8432
8775
|
const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
|
|
8433
8776
|
const generatedFiles = [];
|
|
8434
8777
|
const entrypointEdits = [];
|
|
@@ -8453,7 +8796,7 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project)
|
|
|
8453
8796
|
});
|
|
8454
8797
|
} else {
|
|
8455
8798
|
try {
|
|
8456
|
-
const raw = await
|
|
8799
|
+
const raw = await import_node_fs28.promises.readFile(hooksFile, "utf8");
|
|
8457
8800
|
if (!fileImportsOtelHook(raw, ["./otel-init"])) {
|
|
8458
8801
|
const lines = raw.split(/\r?\n/);
|
|
8459
8802
|
const firstReal = lines[0]?.startsWith("#!") ? lines[1] ?? "" : lines[0] ?? "";
|
|
@@ -8490,11 +8833,11 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project)
|
|
|
8490
8833
|
}
|
|
8491
8834
|
async function planNuxt(serviceDir, pkg, manifestPath, project) {
|
|
8492
8835
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
8493
|
-
const otelPluginFile =
|
|
8836
|
+
const otelPluginFile = import_node_path44.default.join(
|
|
8494
8837
|
serviceDir,
|
|
8495
8838
|
useTs ? "server/plugins/otel.ts" : "server/plugins/otel.js"
|
|
8496
8839
|
);
|
|
8497
|
-
const otelInitFile =
|
|
8840
|
+
const otelInitFile = import_node_path44.default.join(
|
|
8498
8841
|
serviceDir,
|
|
8499
8842
|
useTs ? "server/plugins/otel-init.ts" : "server/plugins/otel-init.js"
|
|
8500
8843
|
);
|
|
@@ -8545,19 +8888,19 @@ async function planNuxt(serviceDir, pkg, manifestPath, project) {
|
|
|
8545
8888
|
var ASTRO_MIDDLEWARE_CANDIDATES = ["src/middleware.ts", "src/middleware.js"];
|
|
8546
8889
|
async function findAstroMiddleware(serviceDir) {
|
|
8547
8890
|
for (const rel of ASTRO_MIDDLEWARE_CANDIDATES) {
|
|
8548
|
-
const candidate =
|
|
8891
|
+
const candidate = import_node_path44.default.join(serviceDir, rel);
|
|
8549
8892
|
if (await exists3(candidate)) return candidate;
|
|
8550
8893
|
}
|
|
8551
8894
|
return null;
|
|
8552
8895
|
}
|
|
8553
8896
|
async function planAstro(serviceDir, pkg, manifestPath, project) {
|
|
8554
8897
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
8555
|
-
const otelInitFile =
|
|
8898
|
+
const otelInitFile = import_node_path44.default.join(
|
|
8556
8899
|
serviceDir,
|
|
8557
8900
|
useTs ? "src/otel-init.ts" : "src/otel-init.js"
|
|
8558
8901
|
);
|
|
8559
8902
|
const existingMiddleware = await findAstroMiddleware(serviceDir);
|
|
8560
|
-
const middlewareFile = existingMiddleware ??
|
|
8903
|
+
const middlewareFile = existingMiddleware ?? import_node_path44.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
|
|
8561
8904
|
const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
|
|
8562
8905
|
const generatedFiles = [];
|
|
8563
8906
|
const entrypointEdits = [];
|
|
@@ -8582,7 +8925,7 @@ async function planAstro(serviceDir, pkg, manifestPath, project) {
|
|
|
8582
8925
|
});
|
|
8583
8926
|
} else {
|
|
8584
8927
|
try {
|
|
8585
|
-
const raw = await
|
|
8928
|
+
const raw = await import_node_fs28.promises.readFile(existingMiddleware, "utf8");
|
|
8586
8929
|
if (!fileImportsOtelHook(raw, ["./otel-init"])) {
|
|
8587
8930
|
const lines = raw.split(/\r?\n/);
|
|
8588
8931
|
const firstReal = lines[0]?.startsWith("#!") ? lines[1] ?? "" : lines[0] ?? "";
|
|
@@ -8653,7 +8996,7 @@ async function findFrameworkDispatch(serviceDir, pkg, manifestPath, project) {
|
|
|
8653
8996
|
}
|
|
8654
8997
|
async function plan(serviceDir, opts) {
|
|
8655
8998
|
const pkg = await readPackageJson2(serviceDir);
|
|
8656
|
-
const manifestPath =
|
|
8999
|
+
const manifestPath = import_node_path44.default.join(serviceDir, "package.json");
|
|
8657
9000
|
const project = opts?.project;
|
|
8658
9001
|
const empty = {
|
|
8659
9002
|
language: "javascript",
|
|
@@ -8688,8 +9031,8 @@ async function plan(serviceDir, opts) {
|
|
|
8688
9031
|
return { ...empty, libOnly: true };
|
|
8689
9032
|
}
|
|
8690
9033
|
const flavor = dispatchEntry(entryFile, pkg);
|
|
8691
|
-
const otelInitFile =
|
|
8692
|
-
const envNeatFile =
|
|
9034
|
+
const otelInitFile = import_node_path44.default.join(import_node_path44.default.dirname(entryFile), otelInitFilename(flavor));
|
|
9035
|
+
const envNeatFile = import_node_path44.default.join(serviceDir, ".env.neat");
|
|
8693
9036
|
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
8694
9037
|
const dependencyEdits = [];
|
|
8695
9038
|
for (const sdk of SDK_PACKAGES) {
|
|
@@ -8713,7 +9056,7 @@ async function plan(serviceDir, opts) {
|
|
|
8713
9056
|
}
|
|
8714
9057
|
const entrypointEdits = [];
|
|
8715
9058
|
try {
|
|
8716
|
-
const raw = await
|
|
9059
|
+
const raw = await import_node_fs28.promises.readFile(entryFile, "utf8");
|
|
8717
9060
|
const lines = raw.split(/\r?\n/);
|
|
8718
9061
|
const firstReal = lines[0]?.startsWith("#!") ? lines[1] ?? "" : lines[0] ?? "";
|
|
8719
9062
|
if (!lineIsOtelInjection(firstReal)) {
|
|
@@ -8758,13 +9101,13 @@ async function plan(serviceDir, opts) {
|
|
|
8758
9101
|
};
|
|
8759
9102
|
}
|
|
8760
9103
|
function isAllowedWritePath(serviceDir, target) {
|
|
8761
|
-
const rel =
|
|
9104
|
+
const rel = import_node_path44.default.relative(serviceDir, target);
|
|
8762
9105
|
if (rel.startsWith("..")) return false;
|
|
8763
|
-
const base =
|
|
9106
|
+
const base = import_node_path44.default.basename(target);
|
|
8764
9107
|
if (base === "package.json") return true;
|
|
8765
9108
|
if (base === ".env.neat") return true;
|
|
8766
9109
|
if (/^otel-init\.(?:js|cjs|mjs|ts)$/.test(base)) return true;
|
|
8767
|
-
const relPosix = rel.split(
|
|
9110
|
+
const relPosix = rel.split(import_node_path44.default.sep).join("/");
|
|
8768
9111
|
if (/^instrumentation(?:\.node)?\.(?:js|cjs|mjs|ts)$/.test(base)) {
|
|
8769
9112
|
if (relPosix === base) return true;
|
|
8770
9113
|
if (relPosix === `src/${base}`) return true;
|
|
@@ -8781,10 +9124,10 @@ function isAllowedWritePath(serviceDir, target) {
|
|
|
8781
9124
|
return false;
|
|
8782
9125
|
}
|
|
8783
9126
|
async function writeAtomic(file, contents) {
|
|
8784
|
-
await
|
|
9127
|
+
await import_node_fs28.promises.mkdir(import_node_path44.default.dirname(file), { recursive: true });
|
|
8785
9128
|
const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
8786
|
-
await
|
|
8787
|
-
await
|
|
9129
|
+
await import_node_fs28.promises.writeFile(tmp, contents, "utf8");
|
|
9130
|
+
await import_node_fs28.promises.rename(tmp, file);
|
|
8788
9131
|
}
|
|
8789
9132
|
async function apply(installPlan) {
|
|
8790
9133
|
const { serviceDir } = installPlan;
|
|
@@ -8860,7 +9203,7 @@ async function apply(installPlan) {
|
|
|
8860
9203
|
for (const target of allTargets) {
|
|
8861
9204
|
if (await exists3(target)) {
|
|
8862
9205
|
try {
|
|
8863
|
-
originals.set(target, await
|
|
9206
|
+
originals.set(target, await import_node_fs28.promises.readFile(target, "utf8"));
|
|
8864
9207
|
} catch {
|
|
8865
9208
|
}
|
|
8866
9209
|
}
|
|
@@ -8943,14 +9286,14 @@ async function rollback(installPlan, originals, createdFiles) {
|
|
|
8943
9286
|
const removed = [];
|
|
8944
9287
|
for (const [file, raw] of originals.entries()) {
|
|
8945
9288
|
try {
|
|
8946
|
-
await
|
|
9289
|
+
await import_node_fs28.promises.writeFile(file, raw, "utf8");
|
|
8947
9290
|
restored.push(file);
|
|
8948
9291
|
} catch {
|
|
8949
9292
|
}
|
|
8950
9293
|
}
|
|
8951
9294
|
for (const file of createdFiles) {
|
|
8952
9295
|
try {
|
|
8953
|
-
await
|
|
9296
|
+
await import_node_fs28.promises.unlink(file);
|
|
8954
9297
|
removed.push(file);
|
|
8955
9298
|
} catch {
|
|
8956
9299
|
}
|
|
@@ -8965,8 +9308,8 @@ async function rollback(installPlan, originals, createdFiles) {
|
|
|
8965
9308
|
...removed.map((f) => `removed: ${f}`),
|
|
8966
9309
|
""
|
|
8967
9310
|
];
|
|
8968
|
-
const rollbackPath =
|
|
8969
|
-
await
|
|
9311
|
+
const rollbackPath = import_node_path44.default.join(installPlan.serviceDir, "neat-rollback.patch");
|
|
9312
|
+
await import_node_fs28.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
|
|
8970
9313
|
}
|
|
8971
9314
|
function injectInstrumentationHook(raw) {
|
|
8972
9315
|
if (raw.includes("instrumentationHook")) return raw;
|
|
@@ -8995,8 +9338,8 @@ var javascriptInstaller = {
|
|
|
8995
9338
|
|
|
8996
9339
|
// src/installers/python.ts
|
|
8997
9340
|
init_cjs_shims();
|
|
8998
|
-
var
|
|
8999
|
-
var
|
|
9341
|
+
var import_node_fs29 = require("fs");
|
|
9342
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
9000
9343
|
var SDK_PACKAGES2 = [
|
|
9001
9344
|
{ name: "opentelemetry-distro", version: ">=0.49b0" },
|
|
9002
9345
|
{ name: "opentelemetry-exporter-otlp", version: ">=1.28.0" }
|
|
@@ -9008,7 +9351,7 @@ var OTEL_ENV2 = {
|
|
|
9008
9351
|
};
|
|
9009
9352
|
async function exists4(p) {
|
|
9010
9353
|
try {
|
|
9011
|
-
await
|
|
9354
|
+
await import_node_fs29.promises.stat(p);
|
|
9012
9355
|
return true;
|
|
9013
9356
|
} catch {
|
|
9014
9357
|
return false;
|
|
@@ -9017,7 +9360,7 @@ async function exists4(p) {
|
|
|
9017
9360
|
async function detect2(serviceDir) {
|
|
9018
9361
|
const markers = ["requirements.txt", "pyproject.toml", "setup.py"];
|
|
9019
9362
|
for (const m of markers) {
|
|
9020
|
-
if (await exists4(
|
|
9363
|
+
if (await exists4(import_node_path45.default.join(serviceDir, m))) return true;
|
|
9021
9364
|
}
|
|
9022
9365
|
return false;
|
|
9023
9366
|
}
|
|
@@ -9027,9 +9370,9 @@ function reqPackageName(line) {
|
|
|
9027
9370
|
return head.replace(/[<>=!~].*$/, "").toLowerCase();
|
|
9028
9371
|
}
|
|
9029
9372
|
async function planRequirementsTxtEdits(serviceDir) {
|
|
9030
|
-
const file =
|
|
9373
|
+
const file = import_node_path45.default.join(serviceDir, "requirements.txt");
|
|
9031
9374
|
if (!await exists4(file)) return null;
|
|
9032
|
-
const raw = await
|
|
9375
|
+
const raw = await import_node_fs29.promises.readFile(file, "utf8");
|
|
9033
9376
|
const presentNames = new Set(
|
|
9034
9377
|
raw.split(/\r?\n/).map(reqPackageName).filter((n) => n.length > 0)
|
|
9035
9378
|
);
|
|
@@ -9037,9 +9380,9 @@ async function planRequirementsTxtEdits(serviceDir) {
|
|
|
9037
9380
|
return { manifest: file, missing: [...missing] };
|
|
9038
9381
|
}
|
|
9039
9382
|
async function planProcfileEdits(serviceDir) {
|
|
9040
|
-
const procfile =
|
|
9383
|
+
const procfile = import_node_path45.default.join(serviceDir, "Procfile");
|
|
9041
9384
|
if (!await exists4(procfile)) return [];
|
|
9042
|
-
const raw = await
|
|
9385
|
+
const raw = await import_node_fs29.promises.readFile(procfile, "utf8");
|
|
9043
9386
|
const edits = [];
|
|
9044
9387
|
for (const line of raw.split(/\r?\n/)) {
|
|
9045
9388
|
if (line.length === 0) continue;
|
|
@@ -9091,8 +9434,8 @@ async function applyRequirementsTxt(manifest, edits, original) {
|
|
|
9091
9434
|
const next = `${original}${trailing}${newlines.join("\n")}
|
|
9092
9435
|
`;
|
|
9093
9436
|
const tmp = `${manifest}.${process.pid}.${Date.now()}.tmp`;
|
|
9094
|
-
await
|
|
9095
|
-
await
|
|
9437
|
+
await import_node_fs29.promises.writeFile(tmp, next, "utf8");
|
|
9438
|
+
await import_node_fs29.promises.rename(tmp, manifest);
|
|
9096
9439
|
}
|
|
9097
9440
|
async function applyProcfile(procfile, edits, original) {
|
|
9098
9441
|
let next = original;
|
|
@@ -9101,8 +9444,8 @@ async function applyProcfile(procfile, edits, original) {
|
|
|
9101
9444
|
next = next.replace(e.before, e.after);
|
|
9102
9445
|
}
|
|
9103
9446
|
const tmp = `${procfile}.${process.pid}.${Date.now()}.tmp`;
|
|
9104
|
-
await
|
|
9105
|
-
await
|
|
9447
|
+
await import_node_fs29.promises.writeFile(tmp, next, "utf8");
|
|
9448
|
+
await import_node_fs29.promises.rename(tmp, procfile);
|
|
9106
9449
|
}
|
|
9107
9450
|
async function apply2(installPlan) {
|
|
9108
9451
|
const { serviceDir } = installPlan;
|
|
@@ -9115,7 +9458,7 @@ async function apply2(installPlan) {
|
|
|
9115
9458
|
const originals = /* @__PURE__ */ new Map();
|
|
9116
9459
|
for (const file of touched) {
|
|
9117
9460
|
try {
|
|
9118
|
-
originals.set(file, await
|
|
9461
|
+
originals.set(file, await import_node_fs29.promises.readFile(file, "utf8"));
|
|
9119
9462
|
} catch {
|
|
9120
9463
|
}
|
|
9121
9464
|
}
|
|
@@ -9126,7 +9469,7 @@ async function apply2(installPlan) {
|
|
|
9126
9469
|
if (raw === void 0) {
|
|
9127
9470
|
throw new Error(`python installer: cannot read ${file} during apply`);
|
|
9128
9471
|
}
|
|
9129
|
-
const base =
|
|
9472
|
+
const base = import_node_path45.default.basename(file);
|
|
9130
9473
|
if (base === "requirements.txt") {
|
|
9131
9474
|
const edits = installPlan.dependencyEdits.filter((e) => e.file === file);
|
|
9132
9475
|
if (edits.length > 0) {
|
|
@@ -9151,7 +9494,7 @@ async function rollback2(installPlan, originals) {
|
|
|
9151
9494
|
const restored = [];
|
|
9152
9495
|
for (const [file, raw] of originals.entries()) {
|
|
9153
9496
|
try {
|
|
9154
|
-
await
|
|
9497
|
+
await import_node_fs29.promises.writeFile(file, raw, "utf8");
|
|
9155
9498
|
restored.push(file);
|
|
9156
9499
|
} catch {
|
|
9157
9500
|
}
|
|
@@ -9165,8 +9508,8 @@ async function rollback2(installPlan, originals) {
|
|
|
9165
9508
|
...restored.map((f) => `restored: ${f}`),
|
|
9166
9509
|
""
|
|
9167
9510
|
];
|
|
9168
|
-
const rollbackPath =
|
|
9169
|
-
await
|
|
9511
|
+
const rollbackPath = import_node_path45.default.join(installPlan.serviceDir, "neat-rollback.patch");
|
|
9512
|
+
await import_node_fs29.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
|
|
9170
9513
|
}
|
|
9171
9514
|
var pythonInstaller = {
|
|
9172
9515
|
name: "python",
|
|
@@ -9289,10 +9632,10 @@ function renderPatch(sections) {
|
|
|
9289
9632
|
|
|
9290
9633
|
// src/orchestrator.ts
|
|
9291
9634
|
init_cjs_shims();
|
|
9292
|
-
var
|
|
9635
|
+
var import_node_fs30 = require("fs");
|
|
9293
9636
|
var import_node_http = __toESM(require("http"), 1);
|
|
9294
9637
|
var import_node_net = __toESM(require("net"), 1);
|
|
9295
|
-
var
|
|
9638
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
9296
9639
|
var import_node_child_process3 = require("child_process");
|
|
9297
9640
|
var import_node_readline = __toESM(require("readline"), 1);
|
|
9298
9641
|
async function extractAndPersist(opts) {
|
|
@@ -9301,7 +9644,7 @@ async function extractAndPersist(opts) {
|
|
|
9301
9644
|
const graphKey = opts.projectExplicit ? opts.project : DEFAULT_PROJECT;
|
|
9302
9645
|
resetGraph(graphKey);
|
|
9303
9646
|
const graph = getGraph(graphKey);
|
|
9304
|
-
const projectPaths = pathsForProject(graphKey,
|
|
9647
|
+
const projectPaths = pathsForProject(graphKey, import_node_path46.default.join(opts.scanPath, "neat-out"));
|
|
9305
9648
|
const extraction = await extractFromDirectory(graph, opts.scanPath, {
|
|
9306
9649
|
errorsPath: projectPaths.errorsPath
|
|
9307
9650
|
});
|
|
@@ -9355,7 +9698,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
9355
9698
|
console.log(`skipping ${svc.dir}: browser bundle; browser-OTel support lands in a future release.`);
|
|
9356
9699
|
} else if (outcome.outcome === "react-native") {
|
|
9357
9700
|
reactNative++;
|
|
9358
|
-
const svcName =
|
|
9701
|
+
const svcName = import_node_path46.default.basename(svc.dir);
|
|
9359
9702
|
console.log(
|
|
9360
9703
|
`neat: ${svc.dir} detected as React Native / Expo
|
|
9361
9704
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -9366,7 +9709,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
9366
9709
|
);
|
|
9367
9710
|
} else if (outcome.outcome === "bun") {
|
|
9368
9711
|
bun++;
|
|
9369
|
-
const svcName =
|
|
9712
|
+
const svcName = import_node_path46.default.basename(svc.dir);
|
|
9370
9713
|
console.log(
|
|
9371
9714
|
`neat: ${svc.dir} detected as Bun
|
|
9372
9715
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -9377,7 +9720,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
9377
9720
|
);
|
|
9378
9721
|
} else if (outcome.outcome === "deno") {
|
|
9379
9722
|
deno++;
|
|
9380
|
-
const svcName =
|
|
9723
|
+
const svcName = import_node_path46.default.basename(svc.dir);
|
|
9381
9724
|
console.log(
|
|
9382
9725
|
`neat: ${svc.dir} detected as Deno
|
|
9383
9726
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -9388,7 +9731,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
9388
9731
|
);
|
|
9389
9732
|
} else if (outcome.outcome === "cloudflare-workers") {
|
|
9390
9733
|
cloudflareWorkers++;
|
|
9391
|
-
const svcName =
|
|
9734
|
+
const svcName = import_node_path46.default.basename(svc.dir);
|
|
9392
9735
|
console.log(
|
|
9393
9736
|
`neat: ${svc.dir} detected as Cloudflare Workers
|
|
9394
9737
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -9399,7 +9742,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
9399
9742
|
);
|
|
9400
9743
|
} else if (outcome.outcome === "electron") {
|
|
9401
9744
|
electron++;
|
|
9402
|
-
const svcName =
|
|
9745
|
+
const svcName = import_node_path46.default.basename(svc.dir);
|
|
9403
9746
|
console.log(
|
|
9404
9747
|
`neat: ${svc.dir} detected as Electron
|
|
9405
9748
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -9573,10 +9916,10 @@ function formatPortCollisionMessage(port) {
|
|
|
9573
9916
|
];
|
|
9574
9917
|
}
|
|
9575
9918
|
function spawnDaemonDetached() {
|
|
9576
|
-
const here =
|
|
9919
|
+
const here = import_node_path46.default.dirname(new URL(importMetaUrl).pathname);
|
|
9577
9920
|
const candidates = [
|
|
9578
|
-
|
|
9579
|
-
|
|
9921
|
+
import_node_path46.default.join(here, "neatd.cjs"),
|
|
9922
|
+
import_node_path46.default.join(here, "neatd.js")
|
|
9580
9923
|
];
|
|
9581
9924
|
let entry2 = null;
|
|
9582
9925
|
const fsSync = require("fs");
|
|
@@ -9634,7 +9977,7 @@ async function runOrchestrator(opts) {
|
|
|
9634
9977
|
browser: "skipped"
|
|
9635
9978
|
}
|
|
9636
9979
|
};
|
|
9637
|
-
const stat = await
|
|
9980
|
+
const stat = await import_node_fs30.promises.stat(opts.scanPath).catch(() => null);
|
|
9638
9981
|
if (!stat || !stat.isDirectory()) {
|
|
9639
9982
|
console.error(`neat: ${opts.scanPath} is not a directory`);
|
|
9640
9983
|
result.exitCode = 2;
|
|
@@ -9777,11 +10120,11 @@ function printSummary(result, graph, dashboardUrl) {
|
|
|
9777
10120
|
|
|
9778
10121
|
// src/cli-verbs.ts
|
|
9779
10122
|
init_cjs_shims();
|
|
9780
|
-
var
|
|
10123
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
9781
10124
|
|
|
9782
10125
|
// src/cli-client.ts
|
|
9783
10126
|
init_cjs_shims();
|
|
9784
|
-
var
|
|
10127
|
+
var import_types26 = require("@neat.is/types");
|
|
9785
10128
|
var HttpError = class extends Error {
|
|
9786
10129
|
constructor(status2, message, responseBody = "") {
|
|
9787
10130
|
super(message);
|
|
@@ -9806,10 +10149,10 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
9806
10149
|
const root = baseUrl.replace(/\/$/, "");
|
|
9807
10150
|
const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
|
|
9808
10151
|
return {
|
|
9809
|
-
async get(
|
|
10152
|
+
async get(path49) {
|
|
9810
10153
|
let res;
|
|
9811
10154
|
try {
|
|
9812
|
-
res = await fetch(`${root}${
|
|
10155
|
+
res = await fetch(`${root}${path49}`, {
|
|
9813
10156
|
headers: { ...authHeader }
|
|
9814
10157
|
});
|
|
9815
10158
|
} catch (err) {
|
|
@@ -9821,16 +10164,16 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
9821
10164
|
const body = await res.text().catch(() => "");
|
|
9822
10165
|
throw new HttpError(
|
|
9823
10166
|
res.status,
|
|
9824
|
-
`${res.status} ${res.statusText} on GET ${
|
|
10167
|
+
`${res.status} ${res.statusText} on GET ${path49}: ${body}`,
|
|
9825
10168
|
body
|
|
9826
10169
|
);
|
|
9827
10170
|
}
|
|
9828
10171
|
return await res.json();
|
|
9829
10172
|
},
|
|
9830
|
-
async post(
|
|
10173
|
+
async post(path49, body) {
|
|
9831
10174
|
let res;
|
|
9832
10175
|
try {
|
|
9833
|
-
res = await fetch(`${root}${
|
|
10176
|
+
res = await fetch(`${root}${path49}`, {
|
|
9834
10177
|
method: "POST",
|
|
9835
10178
|
headers: { "content-type": "application/json", ...authHeader },
|
|
9836
10179
|
body: JSON.stringify(body)
|
|
@@ -9844,7 +10187,7 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
9844
10187
|
const text = await res.text().catch(() => "");
|
|
9845
10188
|
throw new HttpError(
|
|
9846
10189
|
res.status,
|
|
9847
|
-
`${res.status} ${res.statusText} on POST ${
|
|
10190
|
+
`${res.status} ${res.statusText} on POST ${path49}: ${text}`,
|
|
9848
10191
|
text
|
|
9849
10192
|
);
|
|
9850
10193
|
}
|
|
@@ -9858,12 +10201,12 @@ function projectPath(project, suffix) {
|
|
|
9858
10201
|
}
|
|
9859
10202
|
async function runRootCause(client, input) {
|
|
9860
10203
|
const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
|
|
9861
|
-
const
|
|
10204
|
+
const path49 = projectPath(
|
|
9862
10205
|
input.project,
|
|
9863
10206
|
`/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
|
|
9864
10207
|
);
|
|
9865
10208
|
try {
|
|
9866
|
-
const result = await client.get(
|
|
10209
|
+
const result = await client.get(path49);
|
|
9867
10210
|
const arrowPath = result.traversalPath.join(" \u2190 ");
|
|
9868
10211
|
const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
|
|
9869
10212
|
const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
|
|
@@ -9889,12 +10232,12 @@ async function runRootCause(client, input) {
|
|
|
9889
10232
|
}
|
|
9890
10233
|
async function runBlastRadius(client, input) {
|
|
9891
10234
|
const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
|
|
9892
|
-
const
|
|
10235
|
+
const path49 = projectPath(
|
|
9893
10236
|
input.project,
|
|
9894
10237
|
`/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
|
|
9895
10238
|
);
|
|
9896
10239
|
try {
|
|
9897
|
-
const result = await client.get(
|
|
10240
|
+
const result = await client.get(path49);
|
|
9898
10241
|
if (result.totalAffected === 0) {
|
|
9899
10242
|
return {
|
|
9900
10243
|
summary: `${result.origin} has no downstream dependencies. Nothing else would break if it failed.`
|
|
@@ -9923,17 +10266,17 @@ async function runBlastRadius(client, input) {
|
|
|
9923
10266
|
}
|
|
9924
10267
|
}
|
|
9925
10268
|
function formatBlastEntry(n) {
|
|
9926
|
-
const tag = n.edgeProvenance ===
|
|
10269
|
+
const tag = n.edgeProvenance === import_types26.Provenance.STALE ? " [STALE \u2014 last seen too long ago]" : "";
|
|
9927
10270
|
return ` \u2022 ${n.nodeId} (distance ${n.distance}, ${n.edgeProvenance})${tag}`;
|
|
9928
10271
|
}
|
|
9929
10272
|
async function runDependencies(client, input) {
|
|
9930
10273
|
const depth = input.depth ?? 3;
|
|
9931
|
-
const
|
|
10274
|
+
const path49 = projectPath(
|
|
9932
10275
|
input.project,
|
|
9933
10276
|
`/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
|
|
9934
10277
|
);
|
|
9935
10278
|
try {
|
|
9936
|
-
const result = await client.get(
|
|
10279
|
+
const result = await client.get(path49);
|
|
9937
10280
|
if (result.total === 0) {
|
|
9938
10281
|
return {
|
|
9939
10282
|
summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
|
|
@@ -9969,9 +10312,9 @@ async function runObservedDependencies(client, input) {
|
|
|
9969
10312
|
const edges = await client.get(
|
|
9970
10313
|
projectPath(input.project, `/graph/edges/${encodeURIComponent(input.nodeId)}`)
|
|
9971
10314
|
);
|
|
9972
|
-
const observed = edges.outbound.filter((e) => e.provenance ===
|
|
10315
|
+
const observed = edges.outbound.filter((e) => e.provenance === import_types26.Provenance.OBSERVED);
|
|
9973
10316
|
if (observed.length === 0) {
|
|
9974
|
-
const hasExtracted = edges.outbound.some((e) => e.provenance ===
|
|
10317
|
+
const hasExtracted = edges.outbound.some((e) => e.provenance === import_types26.Provenance.EXTRACTED);
|
|
9975
10318
|
const note = hasExtracted ? " Static (EXTRACTED) dependencies exist but no runtime traffic has been seen \u2014 is OTel running?" : "";
|
|
9976
10319
|
return { summary: `No OBSERVED dependencies for ${input.nodeId}.${note}` };
|
|
9977
10320
|
}
|
|
@@ -9979,7 +10322,7 @@ async function runObservedDependencies(client, input) {
|
|
|
9979
10322
|
return {
|
|
9980
10323
|
summary: `${input.nodeId} has ${observed.length} runtime dependenc${observed.length === 1 ? "y" : "ies"} confirmed by OTel.`,
|
|
9981
10324
|
block: blockLines.join("\n"),
|
|
9982
|
-
provenance:
|
|
10325
|
+
provenance: import_types26.Provenance.OBSERVED
|
|
9983
10326
|
};
|
|
9984
10327
|
} catch (err) {
|
|
9985
10328
|
if (err instanceof HttpError && err.status === 404) {
|
|
@@ -10014,9 +10357,9 @@ function formatDuration(ms) {
|
|
|
10014
10357
|
return `${Math.round(h / 24)}d`;
|
|
10015
10358
|
}
|
|
10016
10359
|
async function runIncidents(client, input) {
|
|
10017
|
-
const
|
|
10360
|
+
const path49 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
|
|
10018
10361
|
try {
|
|
10019
|
-
const body = await client.get(
|
|
10362
|
+
const body = await client.get(path49);
|
|
10020
10363
|
const events = body.events;
|
|
10021
10364
|
if (events.length === 0) {
|
|
10022
10365
|
return {
|
|
@@ -10033,7 +10376,7 @@ async function runIncidents(client, input) {
|
|
|
10033
10376
|
return {
|
|
10034
10377
|
summary: `${target} has ${body.total} recorded incident${body.total === 1 ? "" : "s"}; showing the ${ordered.length} most recent.`,
|
|
10035
10378
|
block: blockLines.join("\n"),
|
|
10036
|
-
provenance:
|
|
10379
|
+
provenance: import_types26.Provenance.OBSERVED
|
|
10037
10380
|
};
|
|
10038
10381
|
} catch (err) {
|
|
10039
10382
|
if (err instanceof HttpError && err.status === 404) {
|
|
@@ -10142,7 +10485,7 @@ async function runStaleEdges(client, input) {
|
|
|
10142
10485
|
return {
|
|
10143
10486
|
summary: `${events.length} stale-edge transition${events.length === 1 ? "" : "s"} recorded${input.edgeType ? ` for ${input.edgeType}` : ""}.`,
|
|
10144
10487
|
block: blockLines.join("\n"),
|
|
10145
|
-
provenance:
|
|
10488
|
+
provenance: import_types26.Provenance.STALE
|
|
10146
10489
|
};
|
|
10147
10490
|
}
|
|
10148
10491
|
async function runPolicies(client, input) {
|
|
@@ -10306,7 +10649,7 @@ async function resolveProjectEntry(opts) {
|
|
|
10306
10649
|
const cwd = opts.cwd ?? process.cwd();
|
|
10307
10650
|
const resolvedCwd = await normalizeProjectPath(cwd);
|
|
10308
10651
|
for (const entry2 of entries) {
|
|
10309
|
-
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${
|
|
10652
|
+
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path47.default.sep}`)) {
|
|
10310
10653
|
return entry2;
|
|
10311
10654
|
}
|
|
10312
10655
|
}
|
|
@@ -10456,7 +10799,7 @@ async function runSync(opts) {
|
|
|
10456
10799
|
}
|
|
10457
10800
|
|
|
10458
10801
|
// src/cli.ts
|
|
10459
|
-
var
|
|
10802
|
+
var import_types27 = require("@neat.is/types");
|
|
10460
10803
|
function usage() {
|
|
10461
10804
|
console.log("usage: neat <command> [args] [--project <name>]");
|
|
10462
10805
|
console.log("");
|
|
@@ -10666,14 +11009,14 @@ function assignFlag(out, field, value) {
|
|
|
10666
11009
|
out[field] = value;
|
|
10667
11010
|
}
|
|
10668
11011
|
function readPackageVersion() {
|
|
10669
|
-
const here = typeof __dirname !== "undefined" ? __dirname :
|
|
11012
|
+
const here = typeof __dirname !== "undefined" ? __dirname : import_node_path48.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl));
|
|
10670
11013
|
const candidates = [
|
|
10671
|
-
|
|
10672
|
-
|
|
11014
|
+
import_node_path48.default.resolve(here, "../package.json"),
|
|
11015
|
+
import_node_path48.default.resolve(here, "../../package.json")
|
|
10673
11016
|
];
|
|
10674
11017
|
for (const candidate of candidates) {
|
|
10675
11018
|
try {
|
|
10676
|
-
const raw = (0,
|
|
11019
|
+
const raw = (0, import_node_fs31.readFileSync)(candidate, "utf8");
|
|
10677
11020
|
const parsed = JSON.parse(raw);
|
|
10678
11021
|
if (parsed.name === "@neat.is/core" && typeof parsed.version === "string") {
|
|
10679
11022
|
return parsed.version;
|
|
@@ -10737,7 +11080,7 @@ async function buildPatchSections(services, project) {
|
|
|
10737
11080
|
}
|
|
10738
11081
|
async function runInit(opts) {
|
|
10739
11082
|
const written = [];
|
|
10740
|
-
const stat = await
|
|
11083
|
+
const stat = await import_node_fs31.promises.stat(opts.scanPath).catch(() => null);
|
|
10741
11084
|
if (!stat || !stat.isDirectory()) {
|
|
10742
11085
|
console.error(`neat init: ${opts.scanPath} is not a directory`);
|
|
10743
11086
|
return { exitCode: 2, writtenFiles: written };
|
|
@@ -10746,13 +11089,13 @@ async function runInit(opts) {
|
|
|
10746
11089
|
printDiscoveryReport(opts, services);
|
|
10747
11090
|
const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
|
|
10748
11091
|
const patch = renderPatch(sections);
|
|
10749
|
-
const patchPath =
|
|
11092
|
+
const patchPath = import_node_path48.default.join(opts.scanPath, "neat.patch");
|
|
10750
11093
|
if (opts.dryRun) {
|
|
10751
|
-
await
|
|
11094
|
+
await import_node_fs31.promises.writeFile(patchPath, patch, "utf8");
|
|
10752
11095
|
written.push(patchPath);
|
|
10753
11096
|
console.log(`dry-run: patch written to ${patchPath}`);
|
|
10754
|
-
const gitignorePath =
|
|
10755
|
-
const gitignoreExists = await
|
|
11097
|
+
const gitignorePath = import_node_path48.default.join(opts.scanPath, ".gitignore");
|
|
11098
|
+
const gitignoreExists = await import_node_fs31.promises.stat(gitignorePath).then(() => true).catch(() => false);
|
|
10756
11099
|
const verb = gitignoreExists ? "append" : "create";
|
|
10757
11100
|
console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
|
|
10758
11101
|
console.log("rerun without --dry-run to register and snapshot.");
|
|
@@ -10763,9 +11106,9 @@ async function runInit(opts) {
|
|
|
10763
11106
|
const graph = getGraph(graphKey);
|
|
10764
11107
|
const projectPaths = pathsForProject(
|
|
10765
11108
|
graphKey,
|
|
10766
|
-
|
|
11109
|
+
import_node_path48.default.join(opts.scanPath, "neat-out")
|
|
10767
11110
|
);
|
|
10768
|
-
const errorsPath =
|
|
11111
|
+
const errorsPath = import_node_path48.default.join(import_node_path48.default.dirname(opts.outPath), import_node_path48.default.basename(projectPaths.errorsPath));
|
|
10769
11112
|
const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
|
|
10770
11113
|
await saveGraphToDisk(graph, opts.outPath);
|
|
10771
11114
|
written.push(opts.outPath);
|
|
@@ -10844,7 +11187,7 @@ async function runInit(opts) {
|
|
|
10844
11187
|
console.log("Run `npm install` (or your language equivalent) to refresh lockfiles.");
|
|
10845
11188
|
}
|
|
10846
11189
|
} else {
|
|
10847
|
-
await
|
|
11190
|
+
await import_node_fs31.promises.writeFile(patchPath, patch, "utf8");
|
|
10848
11191
|
written.push(patchPath);
|
|
10849
11192
|
}
|
|
10850
11193
|
}
|
|
@@ -10884,9 +11227,9 @@ var CLAUDE_SKILL_CONFIG = {
|
|
|
10884
11227
|
};
|
|
10885
11228
|
function claudeConfigPath() {
|
|
10886
11229
|
const override = process.env.NEAT_CLAUDE_CONFIG;
|
|
10887
|
-
if (override && override.length > 0) return
|
|
11230
|
+
if (override && override.length > 0) return import_node_path48.default.resolve(override);
|
|
10888
11231
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
10889
|
-
return
|
|
11232
|
+
return import_node_path48.default.join(home, ".claude.json");
|
|
10890
11233
|
}
|
|
10891
11234
|
async function runSkill(opts) {
|
|
10892
11235
|
const snippet2 = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
|
|
@@ -10898,7 +11241,7 @@ async function runSkill(opts) {
|
|
|
10898
11241
|
const target = claudeConfigPath();
|
|
10899
11242
|
let existing = {};
|
|
10900
11243
|
try {
|
|
10901
|
-
existing = JSON.parse(await
|
|
11244
|
+
existing = JSON.parse(await import_node_fs31.promises.readFile(target, "utf8"));
|
|
10902
11245
|
} catch (err) {
|
|
10903
11246
|
if (err.code !== "ENOENT") {
|
|
10904
11247
|
console.error(`neat skill: failed to read ${target} \u2014 ${err.message}`);
|
|
@@ -10910,8 +11253,8 @@ async function runSkill(opts) {
|
|
|
10910
11253
|
...existing,
|
|
10911
11254
|
mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
|
|
10912
11255
|
};
|
|
10913
|
-
await
|
|
10914
|
-
await
|
|
11256
|
+
await import_node_fs31.promises.mkdir(import_node_path48.default.dirname(target), { recursive: true });
|
|
11257
|
+
await import_node_fs31.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
10915
11258
|
console.log(`neat skill: wrote mcpServers.neat to ${target}`);
|
|
10916
11259
|
console.log("restart Claude Code to pick up the new MCP server.");
|
|
10917
11260
|
return { exitCode: 0 };
|
|
@@ -10949,12 +11292,12 @@ async function main() {
|
|
|
10949
11292
|
console.error("neat init: --apply and --dry-run are mutually exclusive");
|
|
10950
11293
|
process.exit(2);
|
|
10951
11294
|
}
|
|
10952
|
-
const scanPath =
|
|
11295
|
+
const scanPath = import_node_path48.default.resolve(target);
|
|
10953
11296
|
const projectExplicit = parsed.project !== null;
|
|
10954
|
-
const projectName = projectExplicit ? project :
|
|
11297
|
+
const projectName = projectExplicit ? project : import_node_path48.default.basename(scanPath);
|
|
10955
11298
|
const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
|
|
10956
|
-
const fallback = pathsForProject(projectKey,
|
|
10957
|
-
const outPath =
|
|
11299
|
+
const fallback = pathsForProject(projectKey, import_node_path48.default.join(scanPath, "neat-out")).snapshotPath;
|
|
11300
|
+
const outPath = import_node_path48.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
|
|
10958
11301
|
const result = await runInit({
|
|
10959
11302
|
scanPath,
|
|
10960
11303
|
outPath,
|
|
@@ -10975,21 +11318,21 @@ async function main() {
|
|
|
10975
11318
|
usage();
|
|
10976
11319
|
process.exit(2);
|
|
10977
11320
|
}
|
|
10978
|
-
const scanPath =
|
|
10979
|
-
const stat = await
|
|
11321
|
+
const scanPath = import_node_path48.default.resolve(target);
|
|
11322
|
+
const stat = await import_node_fs31.promises.stat(scanPath).catch(() => null);
|
|
10980
11323
|
if (!stat || !stat.isDirectory()) {
|
|
10981
11324
|
console.error(`neat watch: ${scanPath} is not a directory`);
|
|
10982
11325
|
process.exit(2);
|
|
10983
11326
|
}
|
|
10984
|
-
const projectPaths = pathsForProject(project,
|
|
10985
|
-
const outPath =
|
|
10986
|
-
const errorsPath =
|
|
10987
|
-
process.env.NEAT_ERRORS_PATH ??
|
|
11327
|
+
const projectPaths = pathsForProject(project, import_node_path48.default.join(scanPath, "neat-out"));
|
|
11328
|
+
const outPath = import_node_path48.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
|
|
11329
|
+
const errorsPath = import_node_path48.default.resolve(
|
|
11330
|
+
process.env.NEAT_ERRORS_PATH ?? import_node_path48.default.join(import_node_path48.default.dirname(outPath), import_node_path48.default.basename(projectPaths.errorsPath))
|
|
10988
11331
|
);
|
|
10989
|
-
const staleEventsPath =
|
|
10990
|
-
process.env.NEAT_STALE_EVENTS_PATH ??
|
|
11332
|
+
const staleEventsPath = import_node_path48.default.resolve(
|
|
11333
|
+
process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path48.default.join(import_node_path48.default.dirname(outPath), import_node_path48.default.basename(projectPaths.staleEventsPath))
|
|
10991
11334
|
);
|
|
10992
|
-
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ?
|
|
11335
|
+
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path48.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
|
|
10993
11336
|
const handle = await startWatch(getGraph(project), {
|
|
10994
11337
|
scanPath,
|
|
10995
11338
|
outPath,
|
|
@@ -11135,11 +11478,11 @@ async function main() {
|
|
|
11135
11478
|
process.exit(1);
|
|
11136
11479
|
}
|
|
11137
11480
|
async function tryOrchestrator(cmd, parsed) {
|
|
11138
|
-
const scanPath =
|
|
11139
|
-
const stat = await
|
|
11481
|
+
const scanPath = import_node_path48.default.resolve(cmd);
|
|
11482
|
+
const stat = await import_node_fs31.promises.stat(scanPath).catch(() => null);
|
|
11140
11483
|
if (!stat || !stat.isDirectory()) return null;
|
|
11141
11484
|
const projectExplicit = parsed.project !== null;
|
|
11142
|
-
const projectName = projectExplicit ? parsed.project :
|
|
11485
|
+
const projectName = projectExplicit ? parsed.project : import_node_path48.default.basename(scanPath);
|
|
11143
11486
|
const result = await runOrchestrator({
|
|
11144
11487
|
scanPath,
|
|
11145
11488
|
project: projectName,
|
|
@@ -11289,10 +11632,10 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
11289
11632
|
const parts = parsed.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
11290
11633
|
const out = [];
|
|
11291
11634
|
for (const p of parts) {
|
|
11292
|
-
const r =
|
|
11635
|
+
const r = import_types27.DivergenceTypeSchema.safeParse(p);
|
|
11293
11636
|
if (!r.success) {
|
|
11294
11637
|
console.error(
|
|
11295
|
-
`neat divergences: unknown --type "${p}". allowed: ${
|
|
11638
|
+
`neat divergences: unknown --type "${p}". allowed: ${import_types27.DivergenceTypeSchema.options.join(", ")}`
|
|
11296
11639
|
);
|
|
11297
11640
|
return 2;
|
|
11298
11641
|
}
|