@neat.is/core 0.5.3 → 0.5.4-dev.20260721
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-I72HTUOG.js → chunk-4RU3AAOI.js} +2 -2
- package/dist/{chunk-CFDPIMRP.js → chunk-I4NZ7PSN.js} +3 -2
- package/dist/chunk-I4NZ7PSN.js.map +1 -0
- package/dist/{chunk-GJHEZC5K.js → chunk-PEFX3DBR.js} +422 -111
- package/dist/chunk-PEFX3DBR.js.map +1 -0
- package/dist/{chunk-X2AMX3QZ.js → chunk-VR73QNJD.js} +263 -10
- package/dist/chunk-VR73QNJD.js.map +1 -0
- package/dist/cli.cjs +1094 -448
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +95 -19
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +798 -282
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +807 -291
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-IDMIH6ZY.js → otel-grpc-TEGOXE6T.js} +3 -3
- package/dist/server.cjs +498 -172
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-CFDPIMRP.js.map +0 -1
- package/dist/chunk-GJHEZC5K.js.map +0 -1
- package/dist/chunk-X2AMX3QZ.js.map +0 -1
- /package/dist/{chunk-I72HTUOG.js.map → chunk-4RU3AAOI.js.map} +0 -0
- /package/dist/{otel-grpc-IDMIH6ZY.js.map → otel-grpc-TEGOXE6T.js.map} +0 -0
package/dist/cli.cjs
CHANGED
|
@@ -61,8 +61,8 @@ function mountBearerAuth(app, opts) {
|
|
|
61
61
|
]);
|
|
62
62
|
const publicRead = opts.publicRead === true;
|
|
63
63
|
app.addHook("preHandler", (req, reply, done) => {
|
|
64
|
-
const
|
|
65
|
-
if (exactUnauthPaths.has(
|
|
64
|
+
const path63 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
65
|
+
if (exactUnauthPaths.has(path63) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path63)) {
|
|
66
66
|
done();
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
@@ -194,8 +194,8 @@ function reshapeGrpcRequest(req) {
|
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
196
|
function resolveProtoRoot() {
|
|
197
|
-
const here =
|
|
198
|
-
return
|
|
197
|
+
const here = import_node_path50.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
198
|
+
return import_node_path50.default.resolve(here, "..", "proto");
|
|
199
199
|
}
|
|
200
200
|
function loadTraceService() {
|
|
201
201
|
const protoRoot = resolveProtoRoot();
|
|
@@ -263,13 +263,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
263
263
|
})
|
|
264
264
|
};
|
|
265
265
|
}
|
|
266
|
-
var import_node_url2,
|
|
266
|
+
var import_node_url2, import_node_path50, import_node_crypto2, grpc, protoLoader;
|
|
267
267
|
var init_otel_grpc = __esm({
|
|
268
268
|
"src/otel-grpc.ts"() {
|
|
269
269
|
"use strict";
|
|
270
270
|
init_cjs_shims();
|
|
271
271
|
import_node_url2 = require("url");
|
|
272
|
-
|
|
272
|
+
import_node_path50 = __toESM(require("path"), 1);
|
|
273
273
|
import_node_crypto2 = require("crypto");
|
|
274
274
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
275
275
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -362,8 +362,8 @@ function websocketChannelPathOf(attrs) {
|
|
|
362
362
|
const v = attrs[key];
|
|
363
363
|
if (typeof v === "string" && v.length > 0) {
|
|
364
364
|
const q = v.indexOf("?");
|
|
365
|
-
const
|
|
366
|
-
if (
|
|
365
|
+
const path63 = q === -1 ? v : v.slice(0, q);
|
|
366
|
+
if (path63.length > 0) return path63;
|
|
367
367
|
}
|
|
368
368
|
}
|
|
369
369
|
return void 0;
|
|
@@ -398,6 +398,7 @@ function parseOtlpRequest(body) {
|
|
|
398
398
|
attributes: attrs,
|
|
399
399
|
dbSystem: typeof attrs["db.system"] === "string" ? attrs["db.system"] : void 0,
|
|
400
400
|
dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
|
|
401
|
+
dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
|
|
401
402
|
messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
|
|
402
403
|
messagingDestination: messagingDestinationOf(attrs),
|
|
403
404
|
graphqlOperationName: typeof attrs["graphql.operation.name"] === "string" && attrs["graphql.operation.name"].length > 0 ? attrs["graphql.operation.name"] : void 0,
|
|
@@ -417,10 +418,10 @@ function parseOtlpRequest(body) {
|
|
|
417
418
|
return out;
|
|
418
419
|
}
|
|
419
420
|
function loadProtoRoot() {
|
|
420
|
-
const here =
|
|
421
|
-
const protoRoot =
|
|
421
|
+
const here = import_node_path51.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl));
|
|
422
|
+
const protoRoot = import_node_path51.default.resolve(here, "..", "proto");
|
|
422
423
|
const root = new import_protobufjs.default.Root();
|
|
423
|
-
root.resolvePath = (_origin, target) =>
|
|
424
|
+
root.resolvePath = (_origin, target) => import_node_path51.default.resolve(protoRoot, target);
|
|
424
425
|
root.loadSync(
|
|
425
426
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
426
427
|
{ keepCase: true }
|
|
@@ -648,12 +649,12 @@ async function listenSteppingOtlp(app, requestedPort, host) {
|
|
|
648
649
|
}
|
|
649
650
|
}
|
|
650
651
|
}
|
|
651
|
-
var
|
|
652
|
+
var import_node_path51, import_node_url3, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
|
|
652
653
|
var init_otel = __esm({
|
|
653
654
|
"src/otel.ts"() {
|
|
654
655
|
"use strict";
|
|
655
656
|
init_cjs_shims();
|
|
656
|
-
|
|
657
|
+
import_node_path51 = __toESM(require("path"), 1);
|
|
657
658
|
import_node_url3 = require("url");
|
|
658
659
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
659
660
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -690,7 +691,7 @@ __export(cli_exports, {
|
|
|
690
691
|
});
|
|
691
692
|
module.exports = __toCommonJS(cli_exports);
|
|
692
693
|
init_cjs_shims();
|
|
693
|
-
var
|
|
694
|
+
var import_node_path62 = __toESM(require("path"), 1);
|
|
694
695
|
var import_node_fs41 = require("fs");
|
|
695
696
|
|
|
696
697
|
// src/banner.ts
|
|
@@ -1256,19 +1257,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1256
1257
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1257
1258
|
let best = { path: [start], edges: [] };
|
|
1258
1259
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1259
|
-
function step(node,
|
|
1260
|
-
if (
|
|
1261
|
-
best = { path: [...
|
|
1260
|
+
function step(node, path63, edges) {
|
|
1261
|
+
if (path63.length > best.path.length) {
|
|
1262
|
+
best = { path: [...path63], edges: [...edges] };
|
|
1262
1263
|
}
|
|
1263
|
-
if (
|
|
1264
|
+
if (path63.length - 1 >= maxDepth) return;
|
|
1264
1265
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1265
1266
|
for (const [srcId, edge] of incoming) {
|
|
1266
1267
|
if (visited.has(srcId)) continue;
|
|
1267
1268
|
visited.add(srcId);
|
|
1268
|
-
|
|
1269
|
+
path63.push(srcId);
|
|
1269
1270
|
edges.push(edge);
|
|
1270
|
-
step(srcId,
|
|
1271
|
-
|
|
1271
|
+
step(srcId, path63, edges);
|
|
1272
|
+
path63.pop();
|
|
1272
1273
|
edges.pop();
|
|
1273
1274
|
visited.delete(srcId);
|
|
1274
1275
|
}
|
|
@@ -1462,26 +1463,26 @@ function dominantFailingCall(graph, serviceId6, visited) {
|
|
|
1462
1463
|
return best;
|
|
1463
1464
|
}
|
|
1464
1465
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
1465
|
-
const
|
|
1466
|
+
const path63 = [originServiceId];
|
|
1466
1467
|
const edges = [];
|
|
1467
1468
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1468
1469
|
let current = originServiceId;
|
|
1469
1470
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1470
1471
|
const hop = dominantFailingCall(graph, current, visited);
|
|
1471
1472
|
if (!hop) break;
|
|
1472
|
-
|
|
1473
|
+
path63.push(hop.nextService);
|
|
1473
1474
|
edges.push(hop.edge);
|
|
1474
1475
|
visited.add(hop.nextService);
|
|
1475
1476
|
current = hop.nextService;
|
|
1476
1477
|
}
|
|
1477
1478
|
if (edges.length === 0) return null;
|
|
1478
|
-
return { path:
|
|
1479
|
+
return { path: path63, edges, culprit: current };
|
|
1479
1480
|
}
|
|
1480
1481
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
1481
1482
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
1482
1483
|
if (!chain) return null;
|
|
1483
1484
|
const culprit = chain.culprit;
|
|
1484
|
-
const
|
|
1485
|
+
const path63 = [...chain.path];
|
|
1485
1486
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
1486
1487
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
1487
1488
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -1489,14 +1490,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1489
1490
|
if (loc) {
|
|
1490
1491
|
let rootCauseNode = culprit;
|
|
1491
1492
|
if (loc.fileNode) {
|
|
1492
|
-
|
|
1493
|
+
path63.push(loc.fileNode);
|
|
1493
1494
|
edgeProvenances.push(import_types.Provenance.OBSERVED);
|
|
1494
1495
|
rootCauseNode = loc.fileNode;
|
|
1495
1496
|
}
|
|
1496
1497
|
return import_types.RootCauseResultSchema.parse({
|
|
1497
1498
|
rootCauseNode,
|
|
1498
1499
|
rootCauseReason: loc.rootCauseReason,
|
|
1499
|
-
traversalPath:
|
|
1500
|
+
traversalPath: path63,
|
|
1500
1501
|
edgeProvenances,
|
|
1501
1502
|
confidence,
|
|
1502
1503
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -1508,7 +1509,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1508
1509
|
return import_types.RootCauseResultSchema.parse({
|
|
1509
1510
|
rootCauseNode: culprit,
|
|
1510
1511
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
1511
|
-
traversalPath:
|
|
1512
|
+
traversalPath: path63,
|
|
1512
1513
|
edgeProvenances,
|
|
1513
1514
|
confidence,
|
|
1514
1515
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -2585,6 +2586,19 @@ function ensureServiceNode(graph, serviceName, env) {
|
|
|
2585
2586
|
graph.addNode(id, node);
|
|
2586
2587
|
return id;
|
|
2587
2588
|
}
|
|
2589
|
+
function ensureInfraNode(graph, kind, name, provider) {
|
|
2590
|
+
const id = (0, import_types3.infraId)(kind, name);
|
|
2591
|
+
if (graph.hasNode(id)) return id;
|
|
2592
|
+
const node = {
|
|
2593
|
+
id,
|
|
2594
|
+
type: import_types3.NodeType.InfraNode,
|
|
2595
|
+
name,
|
|
2596
|
+
provider,
|
|
2597
|
+
kind
|
|
2598
|
+
};
|
|
2599
|
+
graph.addNode(id, node);
|
|
2600
|
+
return id;
|
|
2601
|
+
}
|
|
2588
2602
|
function ensureDatabaseNode(graph, host, engine) {
|
|
2589
2603
|
const id = (0, import_types3.databaseId)(host);
|
|
2590
2604
|
if (graph.hasNode(id)) return id;
|
|
@@ -2922,6 +2936,18 @@ async function handleSpan(ctx, span) {
|
|
|
2922
2936
|
callSiteEvidence
|
|
2923
2937
|
);
|
|
2924
2938
|
if (result) affectedNode = targetId;
|
|
2939
|
+
if (span.dbSystem === "mongodb" && span.dbCollection) {
|
|
2940
|
+
const collectionId = ensureInfraNode(ctx.graph, "mongodb-collection", span.dbCollection, "self");
|
|
2941
|
+
upsertObservedEdge(
|
|
2942
|
+
ctx.graph,
|
|
2943
|
+
import_types3.EdgeType.CALLS,
|
|
2944
|
+
observedSource(),
|
|
2945
|
+
collectionId,
|
|
2946
|
+
ts,
|
|
2947
|
+
isError,
|
|
2948
|
+
callSiteEvidence
|
|
2949
|
+
);
|
|
2950
|
+
}
|
|
2925
2951
|
}
|
|
2926
2952
|
} else if (span.messagingSystem && span.messagingDestination && spanMintsMessagingEdge(span.kind)) {
|
|
2927
2953
|
const targetId = ensureMessagingDestinationNode(
|
|
@@ -5781,7 +5807,7 @@ async function addGrpcMethods(graph, services) {
|
|
|
5781
5807
|
|
|
5782
5808
|
// src/extract/calls/index.ts
|
|
5783
5809
|
init_cjs_shims();
|
|
5784
|
-
var
|
|
5810
|
+
var import_types21 = require("@neat.is/types");
|
|
5785
5811
|
|
|
5786
5812
|
// src/extract/calls/http.ts
|
|
5787
5813
|
init_cjs_shims();
|
|
@@ -6494,15 +6520,311 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
6494
6520
|
return out;
|
|
6495
6521
|
}
|
|
6496
6522
|
|
|
6523
|
+
// src/extract/calls/mongoose.ts
|
|
6524
|
+
init_cjs_shims();
|
|
6525
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
6526
|
+
var import_types20 = require("@neat.is/types");
|
|
6527
|
+
var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
|
|
6528
|
+
var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
|
|
6529
|
+
var PLURALIZE_DISABLED_RE = /\bpluralize\s*\(\s*(?:null|false)\s*\)/;
|
|
6530
|
+
var UNCOUNTABLES = /* @__PURE__ */ new Set([
|
|
6531
|
+
"advice",
|
|
6532
|
+
"energy",
|
|
6533
|
+
"excretion",
|
|
6534
|
+
"digestion",
|
|
6535
|
+
"cooperation",
|
|
6536
|
+
"health",
|
|
6537
|
+
"justice",
|
|
6538
|
+
"labour",
|
|
6539
|
+
"machinery",
|
|
6540
|
+
"equipment",
|
|
6541
|
+
"information",
|
|
6542
|
+
"pollution",
|
|
6543
|
+
"sewage",
|
|
6544
|
+
"paper",
|
|
6545
|
+
"money",
|
|
6546
|
+
"species",
|
|
6547
|
+
"series",
|
|
6548
|
+
"rain",
|
|
6549
|
+
"rice",
|
|
6550
|
+
"fish",
|
|
6551
|
+
"sheep",
|
|
6552
|
+
"moose",
|
|
6553
|
+
"deer",
|
|
6554
|
+
"news",
|
|
6555
|
+
"expertise",
|
|
6556
|
+
"status",
|
|
6557
|
+
"media"
|
|
6558
|
+
]);
|
|
6559
|
+
var PLURALIZE_RULES = [
|
|
6560
|
+
[/human$/gi, "humans"],
|
|
6561
|
+
[/(m)an$/gi, "$1en"],
|
|
6562
|
+
[/(pe)rson$/gi, "$1ople"],
|
|
6563
|
+
[/(child)$/gi, "$1ren"],
|
|
6564
|
+
[/^(ox)$/gi, "$1en"],
|
|
6565
|
+
[/(ax|test)is$/gi, "$1es"],
|
|
6566
|
+
[/(octop|vir)us$/gi, "$1i"],
|
|
6567
|
+
[/(alias|status)$/gi, "$1es"],
|
|
6568
|
+
[/(bu)s$/gi, "$1ses"],
|
|
6569
|
+
[/(buffal|tomat|potat)o$/gi, "$1oes"],
|
|
6570
|
+
[/([ti])um$/gi, "$1a"],
|
|
6571
|
+
[/sis$/gi, "ses"],
|
|
6572
|
+
[/(?:([^f])fe|([lr])f)$/gi, "$1$2ves"],
|
|
6573
|
+
[/(hive)$/gi, "$1s"],
|
|
6574
|
+
[/([^aeiouy]|qu)y$/gi, "$1ies"],
|
|
6575
|
+
[/(x|ch|ss|sh)$/gi, "$1es"],
|
|
6576
|
+
[/(matr|vert|ind)ix|ex$/gi, "$1ices"],
|
|
6577
|
+
[/([m|l])ouse$/gi, "$1ice"],
|
|
6578
|
+
[/(kn|w|l)ife$/gi, "$1ives"],
|
|
6579
|
+
[/(quiz)$/gi, "$1zes"],
|
|
6580
|
+
[/s$/gi, "s"],
|
|
6581
|
+
[/([^a-z])$/, "$1"],
|
|
6582
|
+
[/$/gi, "s"]
|
|
6583
|
+
];
|
|
6584
|
+
function pluralizeCollection(name) {
|
|
6585
|
+
const str = name.toLowerCase();
|
|
6586
|
+
if (UNCOUNTABLES.has(str)) return str;
|
|
6587
|
+
for (const [re, repl] of PLURALIZE_RULES) {
|
|
6588
|
+
if (str.match(re)) return str.replace(re, repl);
|
|
6589
|
+
}
|
|
6590
|
+
return str;
|
|
6591
|
+
}
|
|
6592
|
+
var MODEL_METHODS = [
|
|
6593
|
+
"find",
|
|
6594
|
+
"findOne",
|
|
6595
|
+
"findById",
|
|
6596
|
+
"findByIdAndUpdate",
|
|
6597
|
+
"findByIdAndDelete",
|
|
6598
|
+
"findOneAndUpdate",
|
|
6599
|
+
"findOneAndDelete",
|
|
6600
|
+
"findOneAndReplace",
|
|
6601
|
+
"countDocuments",
|
|
6602
|
+
"estimatedDocumentCount",
|
|
6603
|
+
"distinct",
|
|
6604
|
+
"aggregate",
|
|
6605
|
+
"exists",
|
|
6606
|
+
"where",
|
|
6607
|
+
"populate",
|
|
6608
|
+
"watch",
|
|
6609
|
+
"hydrate",
|
|
6610
|
+
"create",
|
|
6611
|
+
"insertMany",
|
|
6612
|
+
"insertOne",
|
|
6613
|
+
"updateOne",
|
|
6614
|
+
"updateMany",
|
|
6615
|
+
"replaceOne",
|
|
6616
|
+
"deleteOne",
|
|
6617
|
+
"deleteMany",
|
|
6618
|
+
"bulkWrite",
|
|
6619
|
+
"bulkSave",
|
|
6620
|
+
"save",
|
|
6621
|
+
// pre-v7, removed in current mongoose but present in older codebases:
|
|
6622
|
+
"count",
|
|
6623
|
+
"update",
|
|
6624
|
+
"remove",
|
|
6625
|
+
"findOneAndRemove",
|
|
6626
|
+
"findByIdAndRemove"
|
|
6627
|
+
];
|
|
6628
|
+
var MODEL_METHODS_ALT = MODEL_METHODS.join("|");
|
|
6629
|
+
function schemaCollectionVars(content) {
|
|
6630
|
+
const out = /* @__PURE__ */ new Map();
|
|
6631
|
+
const re = /(?:const|let|var)\s+(\w+)\s*=\s*new\s+(?:mongoose\s*\.\s*)?Schema\s*\(/g;
|
|
6632
|
+
let m;
|
|
6633
|
+
while ((m = re.exec(content)) !== null) {
|
|
6634
|
+
const windowText = content.slice(m.index, m.index + 2e3);
|
|
6635
|
+
const opt = /\bcollection\s*:\s*['"`]([\w.-]+)['"`]/.exec(windowText);
|
|
6636
|
+
if (opt) out.set(m[1], opt[1]);
|
|
6637
|
+
}
|
|
6638
|
+
return out;
|
|
6639
|
+
}
|
|
6640
|
+
function resolveModel(modelName, rest, schemaColl, pluralizeOn) {
|
|
6641
|
+
const trimmed = rest.trim();
|
|
6642
|
+
const literalThird = /,\s*['"`]([\w.-]+)['"`]\s*$/.exec(trimmed);
|
|
6643
|
+
if (literalThird) return { name: literalThird[1], kind: "mongodb-collection" };
|
|
6644
|
+
const firstIdent = /^([A-Za-z_$][\w$]*)/.exec(trimmed)?.[1];
|
|
6645
|
+
if (firstIdent && schemaColl.has(firstIdent)) {
|
|
6646
|
+
return { name: schemaColl.get(firstIdent), kind: "mongodb-collection" };
|
|
6647
|
+
}
|
|
6648
|
+
if (/,\s*[A-Za-z_$][\w$]*\s*$/.test(trimmed)) {
|
|
6649
|
+
return { name: modelName, kind: "mongodb-model" };
|
|
6650
|
+
}
|
|
6651
|
+
return { name: pluralizeOn ? pluralizeCollection(modelName) : modelName, kind: "mongodb-collection" };
|
|
6652
|
+
}
|
|
6653
|
+
function collectModelDefs(content, pluralizeOn) {
|
|
6654
|
+
const schemaColl = schemaCollectionVars(content);
|
|
6655
|
+
const out = [];
|
|
6656
|
+
const re = /(?:(?:const|let|var)\s+(\w+)\s*=\s*)?(?:await\s+)?(?:\b\w+\s*\.\s*)?\bmodel\s*\(\s*['"`]([\w$]+)['"`]\s*(?:,\s*([\s\S]{0,300}?))?\)/g;
|
|
6657
|
+
let m;
|
|
6658
|
+
while ((m = re.exec(content)) !== null) {
|
|
6659
|
+
out.push({
|
|
6660
|
+
modelName: m[2],
|
|
6661
|
+
varName: m[1] ?? null,
|
|
6662
|
+
resolved: resolveModel(m[2], m[3] ?? "", schemaColl, pluralizeOn),
|
|
6663
|
+
matchText: m[0]
|
|
6664
|
+
});
|
|
6665
|
+
}
|
|
6666
|
+
return out;
|
|
6667
|
+
}
|
|
6668
|
+
function endpoint(r, file, serviceDir, matchText) {
|
|
6669
|
+
const line = lineOf(file.content, matchText);
|
|
6670
|
+
return {
|
|
6671
|
+
infraId: (0, import_types20.infraId)(r.kind, r.name),
|
|
6672
|
+
name: r.name,
|
|
6673
|
+
kind: r.kind,
|
|
6674
|
+
edgeType: "CALLS",
|
|
6675
|
+
confidenceKind: "verified-call-site",
|
|
6676
|
+
evidence: { file: import_node_path32.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
|
|
6677
|
+
};
|
|
6678
|
+
}
|
|
6679
|
+
function mongooseEndpointsFromFile(file, serviceDir) {
|
|
6680
|
+
const hasMongoose = MONGOOSE_IMPORT_RE.test(file.content);
|
|
6681
|
+
const hasMongodb = MONGODB_IMPORT_RE.test(file.content);
|
|
6682
|
+
if (!hasMongoose && !hasMongodb) return [];
|
|
6683
|
+
const content = file.content;
|
|
6684
|
+
const out = [];
|
|
6685
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6686
|
+
const push = (r, matchText) => {
|
|
6687
|
+
const key = `${r.kind}/${r.name}`;
|
|
6688
|
+
if (seen.has(key)) return;
|
|
6689
|
+
seen.add(key);
|
|
6690
|
+
out.push(endpoint(r, file, serviceDir, matchText));
|
|
6691
|
+
};
|
|
6692
|
+
if (hasMongoose) {
|
|
6693
|
+
const pluralizeOn = !PLURALIZE_DISABLED_RE.test(content);
|
|
6694
|
+
for (const def of collectModelDefs(content, pluralizeOn)) push(def.resolved, def.matchText);
|
|
6695
|
+
}
|
|
6696
|
+
const collRe = /\.\s*collection\s*\(\s*['"`]([\w.$-]+)['"`]\s*\)/g;
|
|
6697
|
+
let c;
|
|
6698
|
+
while ((c = collRe.exec(content)) !== null) {
|
|
6699
|
+
push({ name: c[1], kind: "mongodb-collection" }, c[0]);
|
|
6700
|
+
}
|
|
6701
|
+
return out;
|
|
6702
|
+
}
|
|
6703
|
+
function parseDestructure(inner, specifier, out) {
|
|
6704
|
+
for (const raw of inner.split(",")) {
|
|
6705
|
+
const part = raw.trim();
|
|
6706
|
+
if (!part) continue;
|
|
6707
|
+
const asM = /^(\w+)\s+as\s+(\w+)$/.exec(part);
|
|
6708
|
+
const colonM = /^(\w+)\s*:\s*(\w+)$/.exec(part);
|
|
6709
|
+
if (asM) out.push({ local: asM[2], kind: "named", exportName: asM[1], specifier });
|
|
6710
|
+
else if (colonM) out.push({ local: colonM[2], kind: "named", exportName: colonM[1], specifier });
|
|
6711
|
+
else {
|
|
6712
|
+
const id = /^(\w+)$/.exec(part);
|
|
6713
|
+
if (id) out.push({ local: id[1], kind: "named", exportName: id[1], specifier });
|
|
6714
|
+
}
|
|
6715
|
+
}
|
|
6716
|
+
}
|
|
6717
|
+
function parseImportBindings(content) {
|
|
6718
|
+
const out = [];
|
|
6719
|
+
const esm = /import\s+([^;'"`\n]+?)\s+from\s*['"`]([^'"`]+)['"`]/g;
|
|
6720
|
+
let m;
|
|
6721
|
+
while ((m = esm.exec(content)) !== null) {
|
|
6722
|
+
const clause = m[1].trim();
|
|
6723
|
+
const spec = m[2];
|
|
6724
|
+
const ns = /^\*\s+as\s+(\w+)$/.exec(clause);
|
|
6725
|
+
if (ns) {
|
|
6726
|
+
out.push({ local: ns[1], kind: "namespace", specifier: spec });
|
|
6727
|
+
continue;
|
|
6728
|
+
}
|
|
6729
|
+
const named = /\{([^}]*)\}/.exec(clause);
|
|
6730
|
+
if (named) parseDestructure(named[1], spec, out);
|
|
6731
|
+
const def = /^(\w+)\s*(?:,|$)/.exec(clause);
|
|
6732
|
+
if (def && !clause.startsWith("{")) out.push({ local: def[1], kind: "default", specifier: spec });
|
|
6733
|
+
}
|
|
6734
|
+
const cjs = /(?:const|let|var)\s+(\{[^}]*\}|\w+)\s*=\s*require\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
|
|
6735
|
+
while ((m = cjs.exec(content)) !== null) {
|
|
6736
|
+
const lhs = m[1];
|
|
6737
|
+
const spec = m[2];
|
|
6738
|
+
if (lhs.startsWith("{")) parseDestructure(lhs.slice(1, -1), spec, out);
|
|
6739
|
+
else out.push({ local: lhs, kind: "whole", specifier: spec });
|
|
6740
|
+
}
|
|
6741
|
+
return out;
|
|
6742
|
+
}
|
|
6743
|
+
function fileExportsOf(content, pluralizeOn) {
|
|
6744
|
+
const defs = collectModelDefs(content, pluralizeOn).filter((d) => d.resolved.kind === "mongodb-collection");
|
|
6745
|
+
if (defs.length === 0) return null;
|
|
6746
|
+
const byVar = /* @__PURE__ */ new Map();
|
|
6747
|
+
for (const d of defs) if (d.varName) byVar.set(d.varName, d.resolved.name);
|
|
6748
|
+
const byName = new Map(byVar);
|
|
6749
|
+
let def;
|
|
6750
|
+
const named = /(?:module\.exports|export\s+default)\s*=\s*(\w+)\b/.exec(content);
|
|
6751
|
+
if (named && byVar.has(named[1])) def = byVar.get(named[1]);
|
|
6752
|
+
if (!def) {
|
|
6753
|
+
const inline = /(?:module\.exports|export\s+default)\s*=\s*(?:await\s+)?(?:\w+\s*\.\s*)?model\s*\(\s*['"`]([\w$]+)['"`]/.exec(content);
|
|
6754
|
+
if (inline) def = pluralizeOn ? pluralizeCollection(inline[1]) : inline[1];
|
|
6755
|
+
}
|
|
6756
|
+
if (!def && byVar.size === 1) def = [...byVar.values()][0];
|
|
6757
|
+
return { byName, ...def ? { default: def } : {} };
|
|
6758
|
+
}
|
|
6759
|
+
async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
6760
|
+
const mongooseFiles = files.filter((f) => MONGOOSE_IMPORT_RE.test(f.content));
|
|
6761
|
+
if (mongooseFiles.length === 0) return [];
|
|
6762
|
+
const pluralizeOn = !files.some((f) => PLURALIZE_DISABLED_RE.test(f.content));
|
|
6763
|
+
const registry = /* @__PURE__ */ new Map();
|
|
6764
|
+
for (const f of mongooseFiles) {
|
|
6765
|
+
const fx = fileExportsOf(f.content, pluralizeOn);
|
|
6766
|
+
if (fx) registry.set(toPosix2(import_node_path32.default.relative(serviceDir, f.path)), fx);
|
|
6767
|
+
}
|
|
6768
|
+
if (registry.size === 0) return [];
|
|
6769
|
+
const out = [];
|
|
6770
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6771
|
+
for (const f of files) {
|
|
6772
|
+
const bindings = parseImportBindings(f.content);
|
|
6773
|
+
if (bindings.length === 0) continue;
|
|
6774
|
+
const directColl = /* @__PURE__ */ new Map();
|
|
6775
|
+
const nsExports = /* @__PURE__ */ new Map();
|
|
6776
|
+
for (const b of bindings) {
|
|
6777
|
+
const resolvedRel = await resolveJsImport(b.specifier, import_node_path32.default.dirname(f.path), serviceDir, null);
|
|
6778
|
+
if (!resolvedRel) continue;
|
|
6779
|
+
const fx = registry.get(resolvedRel);
|
|
6780
|
+
if (!fx) continue;
|
|
6781
|
+
if (b.kind === "named" && b.exportName) {
|
|
6782
|
+
const coll = fx.byName.get(b.exportName);
|
|
6783
|
+
if (coll) directColl.set(b.local, coll);
|
|
6784
|
+
} else if (b.kind === "default") {
|
|
6785
|
+
if (fx.default) directColl.set(b.local, fx.default);
|
|
6786
|
+
} else {
|
|
6787
|
+
if (fx.default) directColl.set(b.local, fx.default);
|
|
6788
|
+
nsExports.set(b.local, fx);
|
|
6789
|
+
}
|
|
6790
|
+
}
|
|
6791
|
+
if (directColl.size === 0 && nsExports.size === 0) continue;
|
|
6792
|
+
const localDefs = new Set(
|
|
6793
|
+
collectModelDefs(f.content, pluralizeOn).map((d) => d.varName).filter((v) => v !== null)
|
|
6794
|
+
);
|
|
6795
|
+
const emit = (collection, matchText) => {
|
|
6796
|
+
const key = `${f.path}::${collection}`;
|
|
6797
|
+
if (seen.has(key)) return;
|
|
6798
|
+
seen.add(key);
|
|
6799
|
+
out.push(endpoint({ name: collection, kind: "mongodb-collection" }, f, serviceDir, matchText));
|
|
6800
|
+
};
|
|
6801
|
+
for (const [local, collection] of directColl) {
|
|
6802
|
+
if (localDefs.has(local)) continue;
|
|
6803
|
+
const qre = new RegExp(`\\b${local}\\s*\\.\\s*(?:${MODEL_METHODS_ALT})\\s*\\(`, "g");
|
|
6804
|
+
let qm;
|
|
6805
|
+
while ((qm = qre.exec(f.content)) !== null) emit(collection, qm[0]);
|
|
6806
|
+
}
|
|
6807
|
+
for (const [local, fx] of nsExports) {
|
|
6808
|
+
const qre = new RegExp(`\\b${local}\\s*\\.\\s*(\\w+)\\s*\\.\\s*(?:${MODEL_METHODS_ALT})\\s*\\(`, "g");
|
|
6809
|
+
let qm;
|
|
6810
|
+
while ((qm = qre.exec(f.content)) !== null) {
|
|
6811
|
+
const coll = fx.byName.get(qm[1]);
|
|
6812
|
+
if (coll) emit(coll, qm[0]);
|
|
6813
|
+
}
|
|
6814
|
+
}
|
|
6815
|
+
}
|
|
6816
|
+
return out;
|
|
6817
|
+
}
|
|
6818
|
+
|
|
6497
6819
|
// src/extract/calls/index.ts
|
|
6498
6820
|
function edgeTypeFromEndpoint(ep) {
|
|
6499
6821
|
switch (ep.edgeType) {
|
|
6500
6822
|
case "PUBLISHES_TO":
|
|
6501
|
-
return
|
|
6823
|
+
return import_types21.EdgeType.PUBLISHES_TO;
|
|
6502
6824
|
case "CONSUMES_FROM":
|
|
6503
|
-
return
|
|
6825
|
+
return import_types21.EdgeType.CONSUMES_FROM;
|
|
6504
6826
|
default:
|
|
6505
|
-
return
|
|
6827
|
+
return import_types21.EdgeType.CALLS;
|
|
6506
6828
|
}
|
|
6507
6829
|
}
|
|
6508
6830
|
function isAwsKind(kind) {
|
|
@@ -6514,23 +6836,27 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6514
6836
|
for (const service of services) {
|
|
6515
6837
|
const files = await loadSourceFiles(service.dir);
|
|
6516
6838
|
const endpoints = [];
|
|
6839
|
+
const maskedFiles = [];
|
|
6517
6840
|
for (const file of files) {
|
|
6518
6841
|
if (isTestPath(file.path)) continue;
|
|
6519
6842
|
const masked = maskCommentsInSource(file.content);
|
|
6520
6843
|
const maskedFile = { path: file.path, content: masked };
|
|
6844
|
+
maskedFiles.push(maskedFile);
|
|
6521
6845
|
endpoints.push(...kafkaEndpointsFromFile(maskedFile, service.dir));
|
|
6522
6846
|
endpoints.push(...redisEndpointsFromFile(maskedFile, service.dir));
|
|
6523
6847
|
endpoints.push(...awsEndpointsFromFile(maskedFile, service.dir));
|
|
6524
6848
|
endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir));
|
|
6525
6849
|
endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir));
|
|
6850
|
+
endpoints.push(...mongooseEndpointsFromFile(maskedFile, service.dir));
|
|
6526
6851
|
}
|
|
6852
|
+
endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
|
|
6527
6853
|
if (endpoints.length === 0) continue;
|
|
6528
6854
|
const seenEdges = /* @__PURE__ */ new Set();
|
|
6529
6855
|
for (const ep of endpoints) {
|
|
6530
6856
|
if (!graph.hasNode(ep.infraId)) {
|
|
6531
6857
|
const node = {
|
|
6532
6858
|
id: ep.infraId,
|
|
6533
|
-
type:
|
|
6859
|
+
type: import_types21.NodeType.InfraNode,
|
|
6534
6860
|
name: ep.name,
|
|
6535
6861
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
6536
6862
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -6542,7 +6868,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6542
6868
|
nodesAdded++;
|
|
6543
6869
|
}
|
|
6544
6870
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
6545
|
-
const confidence = (0,
|
|
6871
|
+
const confidence = (0, import_types21.confidenceForExtracted)(ep.confidenceKind);
|
|
6546
6872
|
const relFile = toPosix2(ep.evidence.file);
|
|
6547
6873
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
6548
6874
|
graph,
|
|
@@ -6552,7 +6878,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6552
6878
|
);
|
|
6553
6879
|
nodesAdded += n;
|
|
6554
6880
|
edgesAdded += e;
|
|
6555
|
-
if (!(0,
|
|
6881
|
+
if (!(0, import_types21.passesExtractedFloor)(confidence)) {
|
|
6556
6882
|
noteExtractedDropped({
|
|
6557
6883
|
source: fileNodeId,
|
|
6558
6884
|
target: ep.infraId,
|
|
@@ -6572,7 +6898,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6572
6898
|
source: fileNodeId,
|
|
6573
6899
|
target: ep.infraId,
|
|
6574
6900
|
type: edgeType,
|
|
6575
|
-
provenance:
|
|
6901
|
+
provenance: import_types21.Provenance.EXTRACTED,
|
|
6576
6902
|
confidence,
|
|
6577
6903
|
evidence: ep.evidence
|
|
6578
6904
|
};
|
|
@@ -6598,16 +6924,16 @@ init_cjs_shims();
|
|
|
6598
6924
|
|
|
6599
6925
|
// src/extract/infra/docker-compose.ts
|
|
6600
6926
|
init_cjs_shims();
|
|
6601
|
-
var
|
|
6602
|
-
var
|
|
6927
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
6928
|
+
var import_types23 = require("@neat.is/types");
|
|
6603
6929
|
|
|
6604
6930
|
// src/extract/infra/shared.ts
|
|
6605
6931
|
init_cjs_shims();
|
|
6606
|
-
var
|
|
6932
|
+
var import_types22 = require("@neat.is/types");
|
|
6607
6933
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
6608
6934
|
return {
|
|
6609
|
-
id: (0,
|
|
6610
|
-
type:
|
|
6935
|
+
id: (0, import_types22.infraId)(kind, name),
|
|
6936
|
+
type: import_types22.NodeType.InfraNode,
|
|
6611
6937
|
name,
|
|
6612
6938
|
provider,
|
|
6613
6939
|
kind,
|
|
@@ -6651,8 +6977,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
|
|
|
6651
6977
|
source: anchorId,
|
|
6652
6978
|
target: node.id,
|
|
6653
6979
|
type: edgeType,
|
|
6654
|
-
provenance:
|
|
6655
|
-
confidence: (0,
|
|
6980
|
+
provenance: import_types22.Provenance.EXTRACTED,
|
|
6981
|
+
confidence: (0, import_types22.confidenceForExtracted)("structural"),
|
|
6656
6982
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
6657
6983
|
};
|
|
6658
6984
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6669,7 +6995,7 @@ function dependsOnList(value) {
|
|
|
6669
6995
|
}
|
|
6670
6996
|
function serviceNameToServiceNode(name, services) {
|
|
6671
6997
|
for (const s of services) {
|
|
6672
|
-
if (s.node.name === name ||
|
|
6998
|
+
if (s.node.name === name || import_node_path33.default.basename(s.dir) === name) return s.node.id;
|
|
6673
6999
|
}
|
|
6674
7000
|
return null;
|
|
6675
7001
|
}
|
|
@@ -6678,7 +7004,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6678
7004
|
let edgesAdded = 0;
|
|
6679
7005
|
let composePath = null;
|
|
6680
7006
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
6681
|
-
const abs =
|
|
7007
|
+
const abs = import_node_path33.default.join(scanPath, name);
|
|
6682
7008
|
if (await exists(abs)) {
|
|
6683
7009
|
composePath = abs;
|
|
6684
7010
|
break;
|
|
@@ -6691,13 +7017,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6691
7017
|
} catch (err) {
|
|
6692
7018
|
recordExtractionError(
|
|
6693
7019
|
"infra docker-compose",
|
|
6694
|
-
|
|
7020
|
+
import_node_path33.default.relative(scanPath, composePath),
|
|
6695
7021
|
err
|
|
6696
7022
|
);
|
|
6697
7023
|
return { nodesAdded, edgesAdded };
|
|
6698
7024
|
}
|
|
6699
7025
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
6700
|
-
const evidenceFile =
|
|
7026
|
+
const evidenceFile = import_node_path33.default.relative(scanPath, composePath).split(import_node_path33.default.sep).join("/");
|
|
6701
7027
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
6702
7028
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
6703
7029
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -6719,15 +7045,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6719
7045
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
6720
7046
|
const targetId = composeNameToNodeId.get(dep);
|
|
6721
7047
|
if (!targetId) continue;
|
|
6722
|
-
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId,
|
|
7048
|
+
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types23.EdgeType.DEPENDS_ON);
|
|
6723
7049
|
if (graph.hasEdge(edgeId)) continue;
|
|
6724
7050
|
const edge = {
|
|
6725
7051
|
id: edgeId,
|
|
6726
7052
|
source: sourceId,
|
|
6727
7053
|
target: targetId,
|
|
6728
|
-
type:
|
|
6729
|
-
provenance:
|
|
6730
|
-
confidence: (0,
|
|
7054
|
+
type: import_types23.EdgeType.DEPENDS_ON,
|
|
7055
|
+
provenance: import_types23.Provenance.EXTRACTED,
|
|
7056
|
+
confidence: (0, import_types23.confidenceForExtracted)("structural"),
|
|
6731
7057
|
evidence: { file: evidenceFile }
|
|
6732
7058
|
};
|
|
6733
7059
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6739,9 +7065,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6739
7065
|
|
|
6740
7066
|
// src/extract/infra/dockerfile.ts
|
|
6741
7067
|
init_cjs_shims();
|
|
6742
|
-
var
|
|
7068
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
6743
7069
|
var import_node_fs17 = require("fs");
|
|
6744
|
-
var
|
|
7070
|
+
var import_types24 = require("@neat.is/types");
|
|
6745
7071
|
function readDockerfile(content) {
|
|
6746
7072
|
let image = null;
|
|
6747
7073
|
const ports = [];
|
|
@@ -6770,7 +7096,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6770
7096
|
let nodesAdded = 0;
|
|
6771
7097
|
let edgesAdded = 0;
|
|
6772
7098
|
for (const service of services) {
|
|
6773
|
-
const dockerfilePath =
|
|
7099
|
+
const dockerfilePath = import_node_path34.default.join(service.dir, "Dockerfile");
|
|
6774
7100
|
if (!await exists(dockerfilePath)) continue;
|
|
6775
7101
|
let content;
|
|
6776
7102
|
try {
|
|
@@ -6778,7 +7104,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6778
7104
|
} catch (err) {
|
|
6779
7105
|
recordExtractionError(
|
|
6780
7106
|
"infra dockerfile",
|
|
6781
|
-
|
|
7107
|
+
import_node_path34.default.relative(scanPath, dockerfilePath),
|
|
6782
7108
|
err
|
|
6783
7109
|
);
|
|
6784
7110
|
continue;
|
|
@@ -6790,8 +7116,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6790
7116
|
graph.addNode(node.id, node);
|
|
6791
7117
|
nodesAdded++;
|
|
6792
7118
|
}
|
|
6793
|
-
const relDockerfile = toPosix2(
|
|
6794
|
-
const evidenceFile = toPosix2(
|
|
7119
|
+
const relDockerfile = toPosix2(import_node_path34.default.relative(service.dir, dockerfilePath));
|
|
7120
|
+
const evidenceFile = toPosix2(import_node_path34.default.relative(scanPath, dockerfilePath));
|
|
6795
7121
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
6796
7122
|
graph,
|
|
6797
7123
|
service.pkg.name,
|
|
@@ -6800,15 +7126,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6800
7126
|
);
|
|
6801
7127
|
nodesAdded += fn;
|
|
6802
7128
|
edgesAdded += fe;
|
|
6803
|
-
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id,
|
|
7129
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types24.EdgeType.RUNS_ON);
|
|
6804
7130
|
if (!graph.hasEdge(edgeId)) {
|
|
6805
7131
|
const edge = {
|
|
6806
7132
|
id: edgeId,
|
|
6807
7133
|
source: fileNodeId,
|
|
6808
7134
|
target: node.id,
|
|
6809
|
-
type:
|
|
6810
|
-
provenance:
|
|
6811
|
-
confidence: (0,
|
|
7135
|
+
type: import_types24.EdgeType.RUNS_ON,
|
|
7136
|
+
provenance: import_types24.Provenance.EXTRACTED,
|
|
7137
|
+
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
6812
7138
|
evidence: {
|
|
6813
7139
|
file: evidenceFile,
|
|
6814
7140
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -6823,15 +7149,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6823
7149
|
graph.addNode(portNode.id, portNode);
|
|
6824
7150
|
nodesAdded++;
|
|
6825
7151
|
}
|
|
6826
|
-
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id,
|
|
7152
|
+
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id, import_types24.EdgeType.CONNECTS_TO);
|
|
6827
7153
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
6828
7154
|
const portEdge = {
|
|
6829
7155
|
id: portEdgeId,
|
|
6830
7156
|
source: fileNodeId,
|
|
6831
7157
|
target: portNode.id,
|
|
6832
|
-
type:
|
|
6833
|
-
provenance:
|
|
6834
|
-
confidence: (0,
|
|
7158
|
+
type: import_types24.EdgeType.CONNECTS_TO,
|
|
7159
|
+
provenance: import_types24.Provenance.EXTRACTED,
|
|
7160
|
+
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
6835
7161
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
6836
7162
|
};
|
|
6837
7163
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -6844,8 +7170,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6844
7170
|
// src/extract/infra/terraform.ts
|
|
6845
7171
|
init_cjs_shims();
|
|
6846
7172
|
var import_node_fs18 = require("fs");
|
|
6847
|
-
var
|
|
6848
|
-
var
|
|
7173
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
7174
|
+
var import_types25 = require("@neat.is/types");
|
|
6849
7175
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
6850
7176
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
6851
7177
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
@@ -6855,11 +7181,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
6855
7181
|
for (const entry2 of entries) {
|
|
6856
7182
|
if (entry2.isDirectory()) {
|
|
6857
7183
|
if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
|
|
6858
|
-
const child =
|
|
7184
|
+
const child = import_node_path35.default.join(start, entry2.name);
|
|
6859
7185
|
if (await isPythonVenvDir(child)) continue;
|
|
6860
7186
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
6861
7187
|
} else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
|
|
6862
|
-
out.push(
|
|
7188
|
+
out.push(import_node_path35.default.join(start, entry2.name));
|
|
6863
7189
|
}
|
|
6864
7190
|
}
|
|
6865
7191
|
return out;
|
|
@@ -6891,7 +7217,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6891
7217
|
const files = await walkTfFiles(scanPath);
|
|
6892
7218
|
for (const file of files) {
|
|
6893
7219
|
const content = await import_node_fs18.promises.readFile(file, "utf8");
|
|
6894
|
-
const evidenceFile = toPosix2(
|
|
7220
|
+
const evidenceFile = toPosix2(import_node_path35.default.relative(scanPath, file));
|
|
6895
7221
|
const resources = [];
|
|
6896
7222
|
const byKey = /* @__PURE__ */ new Map();
|
|
6897
7223
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -6926,16 +7252,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6926
7252
|
if (!target) continue;
|
|
6927
7253
|
if (seen.has(target.nodeId)) continue;
|
|
6928
7254
|
seen.add(target.nodeId);
|
|
6929
|
-
const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId,
|
|
7255
|
+
const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId, import_types25.EdgeType.DEPENDS_ON);
|
|
6930
7256
|
if (graph.hasEdge(edgeId)) continue;
|
|
6931
7257
|
const line = lineAt2(content, resource.bodyOffset + ref.index);
|
|
6932
7258
|
const edge = {
|
|
6933
7259
|
id: edgeId,
|
|
6934
7260
|
source: resource.nodeId,
|
|
6935
7261
|
target: target.nodeId,
|
|
6936
|
-
type:
|
|
6937
|
-
provenance:
|
|
6938
|
-
confidence: (0,
|
|
7262
|
+
type: import_types25.EdgeType.DEPENDS_ON,
|
|
7263
|
+
provenance: import_types25.Provenance.EXTRACTED,
|
|
7264
|
+
confidence: (0, import_types25.confidenceForExtracted)("structural"),
|
|
6939
7265
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
6940
7266
|
};
|
|
6941
7267
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6949,7 +7275,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6949
7275
|
// src/extract/infra/k8s.ts
|
|
6950
7276
|
init_cjs_shims();
|
|
6951
7277
|
var import_node_fs19 = require("fs");
|
|
6952
|
-
var
|
|
7278
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
6953
7279
|
var import_yaml3 = require("yaml");
|
|
6954
7280
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
6955
7281
|
Service: "k8s-service",
|
|
@@ -6967,11 +7293,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
6967
7293
|
for (const entry2 of entries) {
|
|
6968
7294
|
if (entry2.isDirectory()) {
|
|
6969
7295
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
6970
|
-
const child =
|
|
7296
|
+
const child = import_node_path36.default.join(start, entry2.name);
|
|
6971
7297
|
if (await isPythonVenvDir(child)) continue;
|
|
6972
7298
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
6973
|
-
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
6974
|
-
out.push(
|
|
7299
|
+
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path36.default.extname(entry2.name))) {
|
|
7300
|
+
out.push(import_node_path36.default.join(start, entry2.name));
|
|
6975
7301
|
}
|
|
6976
7302
|
}
|
|
6977
7303
|
return out;
|
|
@@ -7005,13 +7331,13 @@ async function addK8sResources(graph, scanPath) {
|
|
|
7005
7331
|
// src/extract/infra/cloudflare.ts
|
|
7006
7332
|
init_cjs_shims();
|
|
7007
7333
|
var import_node_fs20 = require("fs");
|
|
7008
|
-
var
|
|
7334
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
7009
7335
|
var import_smol_toml2 = require("smol-toml");
|
|
7010
|
-
var
|
|
7336
|
+
var import_types26 = require("@neat.is/types");
|
|
7011
7337
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
7012
7338
|
async function readWranglerConfig(dir) {
|
|
7013
7339
|
for (const filename of WRANGLER_FILENAMES) {
|
|
7014
|
-
const abs =
|
|
7340
|
+
const abs = import_node_path37.default.join(dir, filename);
|
|
7015
7341
|
if (!await exists(abs)) continue;
|
|
7016
7342
|
const raw = await import_node_fs20.promises.readFile(abs, "utf8");
|
|
7017
7343
|
const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7055,8 +7381,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
|
|
|
7055
7381
|
source: anchorId,
|
|
7056
7382
|
target: node.id,
|
|
7057
7383
|
type: edgeType,
|
|
7058
|
-
provenance:
|
|
7059
|
-
confidence: (0,
|
|
7384
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7385
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7060
7386
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
7061
7387
|
};
|
|
7062
7388
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7074,11 +7400,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7074
7400
|
try {
|
|
7075
7401
|
read = await readWranglerConfig(service.dir);
|
|
7076
7402
|
} catch (err) {
|
|
7077
|
-
recordExtractionError("infra cloudflare",
|
|
7403
|
+
recordExtractionError("infra cloudflare", import_node_path37.default.relative(scanPath, service.dir), err);
|
|
7078
7404
|
continue;
|
|
7079
7405
|
}
|
|
7080
7406
|
if (!read || !read.config.name) continue;
|
|
7081
|
-
const evidenceFile = toPosix2(
|
|
7407
|
+
const evidenceFile = toPosix2(import_node_path37.default.relative(scanPath, import_node_path37.default.join(service.dir, read.relFile)));
|
|
7082
7408
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
7083
7409
|
}
|
|
7084
7410
|
for (const worker of discovered) {
|
|
@@ -7090,7 +7416,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7090
7416
|
}
|
|
7091
7417
|
let anchorId = service.node.id;
|
|
7092
7418
|
if (config.main) {
|
|
7093
|
-
const entryRelPath = toPosix2(
|
|
7419
|
+
const entryRelPath = toPosix2(import_node_path37.default.normalize(config.main));
|
|
7094
7420
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
7095
7421
|
graph,
|
|
7096
7422
|
service.pkg.name,
|
|
@@ -7117,15 +7443,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7117
7443
|
nodesAdded++;
|
|
7118
7444
|
}
|
|
7119
7445
|
if (runtimeNode.id !== anchorId) {
|
|
7120
|
-
const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id,
|
|
7446
|
+
const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id, import_types26.EdgeType.RUNS_ON);
|
|
7121
7447
|
if (!graph.hasEdge(runsOnId)) {
|
|
7122
7448
|
const edge = {
|
|
7123
7449
|
id: runsOnId,
|
|
7124
7450
|
source: anchorId,
|
|
7125
7451
|
target: runtimeNode.id,
|
|
7126
|
-
type:
|
|
7127
|
-
provenance:
|
|
7128
|
-
confidence: (0,
|
|
7452
|
+
type: import_types26.EdgeType.RUNS_ON,
|
|
7453
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7454
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7129
7455
|
evidence: {
|
|
7130
7456
|
file: evidenceFile,
|
|
7131
7457
|
...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
|
|
@@ -7139,7 +7465,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7139
7465
|
const result = addResourceEdge(
|
|
7140
7466
|
graph,
|
|
7141
7467
|
anchorId,
|
|
7142
|
-
|
|
7468
|
+
import_types26.EdgeType.CONNECTS_TO,
|
|
7143
7469
|
"cloudflare-route",
|
|
7144
7470
|
route,
|
|
7145
7471
|
evidenceFile,
|
|
@@ -7163,7 +7489,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7163
7489
|
const result = addResourceEdge(
|
|
7164
7490
|
graph,
|
|
7165
7491
|
anchorId,
|
|
7166
|
-
|
|
7492
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7167
7493
|
group.kind,
|
|
7168
7494
|
name,
|
|
7169
7495
|
evidenceFile,
|
|
@@ -7177,7 +7503,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7177
7503
|
const result = addResourceEdge(
|
|
7178
7504
|
graph,
|
|
7179
7505
|
anchorId,
|
|
7180
|
-
|
|
7506
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7181
7507
|
"cloudflare-cron",
|
|
7182
7508
|
cron,
|
|
7183
7509
|
evidenceFile,
|
|
@@ -7190,7 +7516,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7190
7516
|
const result = addResourceEdge(
|
|
7191
7517
|
graph,
|
|
7192
7518
|
anchorId,
|
|
7193
|
-
|
|
7519
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7194
7520
|
"cloudflare-env-var",
|
|
7195
7521
|
varName,
|
|
7196
7522
|
evidenceFile,
|
|
@@ -7203,15 +7529,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7203
7529
|
if (!svc.service) continue;
|
|
7204
7530
|
const target = workerIndex.get(svc.service);
|
|
7205
7531
|
if (target && target.anchorId !== anchorId) {
|
|
7206
|
-
const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId,
|
|
7532
|
+
const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId, import_types26.EdgeType.CALLS);
|
|
7207
7533
|
if (!graph.hasEdge(edgeId)) {
|
|
7208
7534
|
const edge = {
|
|
7209
7535
|
id: edgeId,
|
|
7210
7536
|
source: anchorId,
|
|
7211
7537
|
target: target.anchorId,
|
|
7212
|
-
type:
|
|
7213
|
-
provenance:
|
|
7214
|
-
confidence: (0,
|
|
7538
|
+
type: import_types26.EdgeType.CALLS,
|
|
7539
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7540
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7215
7541
|
evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
|
|
7216
7542
|
};
|
|
7217
7543
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7222,7 +7548,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7222
7548
|
const result = addResourceEdge(
|
|
7223
7549
|
graph,
|
|
7224
7550
|
anchorId,
|
|
7225
|
-
|
|
7551
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7226
7552
|
"cloudflare-service-binding",
|
|
7227
7553
|
svc.service,
|
|
7228
7554
|
evidenceFile,
|
|
@@ -7238,12 +7564,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7238
7564
|
// src/extract/infra/vercel.ts
|
|
7239
7565
|
init_cjs_shims();
|
|
7240
7566
|
var import_node_fs21 = require("fs");
|
|
7241
|
-
var
|
|
7242
|
-
var
|
|
7567
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
7568
|
+
var import_types27 = require("@neat.is/types");
|
|
7243
7569
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
7244
7570
|
async function readVercelConfig(dir) {
|
|
7245
7571
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
7246
|
-
const abs =
|
|
7572
|
+
const abs = import_node_path38.default.join(dir, filename);
|
|
7247
7573
|
if (!await exists(abs)) continue;
|
|
7248
7574
|
const raw = await import_node_fs21.promises.readFile(abs, "utf8");
|
|
7249
7575
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7252,7 +7578,7 @@ async function readVercelConfig(dir) {
|
|
|
7252
7578
|
return null;
|
|
7253
7579
|
}
|
|
7254
7580
|
async function readLinkedProjectName(dir) {
|
|
7255
|
-
const abs =
|
|
7581
|
+
const abs = import_node_path38.default.join(dir, ".vercel", "project.json");
|
|
7256
7582
|
if (!await exists(abs)) return void 0;
|
|
7257
7583
|
const parsed = JSON.parse(await import_node_fs21.promises.readFile(abs, "utf8"));
|
|
7258
7584
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
@@ -7270,7 +7596,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7270
7596
|
read = await readVercelConfig(service.dir);
|
|
7271
7597
|
projectName = await readLinkedProjectName(service.dir);
|
|
7272
7598
|
} catch (err) {
|
|
7273
|
-
recordExtractionError("infra vercel",
|
|
7599
|
+
recordExtractionError("infra vercel", import_node_path38.default.relative(scanPath, service.dir), err);
|
|
7274
7600
|
continue;
|
|
7275
7601
|
}
|
|
7276
7602
|
if (!read && !projectName) continue;
|
|
@@ -7286,7 +7612,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7286
7612
|
const anchorId = service.node.id;
|
|
7287
7613
|
if (!read) continue;
|
|
7288
7614
|
const { config, relFile, raw } = read;
|
|
7289
|
-
const evidenceFile = toPosix2(
|
|
7615
|
+
const evidenceFile = toPosix2(import_node_path38.default.relative(scanPath, import_node_path38.default.join(service.dir, relFile)));
|
|
7290
7616
|
const add = (edgeType, kind, name) => {
|
|
7291
7617
|
if (!name) return;
|
|
7292
7618
|
const result = emitPlatformResourceEdge(
|
|
@@ -7302,12 +7628,12 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7302
7628
|
nodesAdded += result.nodesAdded;
|
|
7303
7629
|
edgesAdded += result.edgesAdded;
|
|
7304
7630
|
};
|
|
7305
|
-
add(
|
|
7306
|
-
for (const cron of config.crons ?? []) add(
|
|
7307
|
-
for (const varName of Object.keys(config.env ?? {})) add(
|
|
7308
|
-
for (const varName of Object.keys(config.build?.env ?? {})) add(
|
|
7631
|
+
add(import_types27.EdgeType.RUNS_ON, "vercel", "vercel");
|
|
7632
|
+
for (const cron of config.crons ?? []) add(import_types27.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
|
|
7633
|
+
for (const varName of Object.keys(config.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
7634
|
+
for (const varName of Object.keys(config.build?.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
7309
7635
|
for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
|
|
7310
|
-
add(
|
|
7636
|
+
add(import_types27.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
|
|
7311
7637
|
}
|
|
7312
7638
|
}
|
|
7313
7639
|
return { nodesAdded, edgesAdded };
|
|
@@ -7316,13 +7642,13 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7316
7642
|
// src/extract/infra/railway.ts
|
|
7317
7643
|
init_cjs_shims();
|
|
7318
7644
|
var import_node_fs22 = require("fs");
|
|
7319
|
-
var
|
|
7645
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
7320
7646
|
var import_smol_toml3 = require("smol-toml");
|
|
7321
|
-
var
|
|
7647
|
+
var import_types28 = require("@neat.is/types");
|
|
7322
7648
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
7323
7649
|
async function readRailwayConfig(dir) {
|
|
7324
7650
|
for (const filename of RAILWAY_FILENAMES) {
|
|
7325
|
-
const abs =
|
|
7651
|
+
const abs = import_node_path39.default.join(dir, filename);
|
|
7326
7652
|
if (!await exists(abs)) continue;
|
|
7327
7653
|
const raw = await import_node_fs22.promises.readFile(abs, "utf8");
|
|
7328
7654
|
const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7338,7 +7664,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7338
7664
|
try {
|
|
7339
7665
|
read = await readRailwayConfig(service.dir);
|
|
7340
7666
|
} catch (err) {
|
|
7341
|
-
recordExtractionError("infra railway",
|
|
7667
|
+
recordExtractionError("infra railway", import_node_path39.default.relative(scanPath, service.dir), err);
|
|
7342
7668
|
continue;
|
|
7343
7669
|
}
|
|
7344
7670
|
if (!read) continue;
|
|
@@ -7348,7 +7674,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7348
7674
|
}
|
|
7349
7675
|
const anchorId = service.node.id;
|
|
7350
7676
|
const { config, relFile, raw } = read;
|
|
7351
|
-
const evidenceFile = toPosix2(
|
|
7677
|
+
const evidenceFile = toPosix2(import_node_path39.default.relative(scanPath, import_node_path39.default.join(service.dir, relFile)));
|
|
7352
7678
|
const add = (edgeType, kind, name) => {
|
|
7353
7679
|
if (!name) return;
|
|
7354
7680
|
const result = emitPlatformResourceEdge(
|
|
@@ -7364,9 +7690,9 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7364
7690
|
nodesAdded += result.nodesAdded;
|
|
7365
7691
|
edgesAdded += result.edgesAdded;
|
|
7366
7692
|
};
|
|
7367
|
-
add(
|
|
7368
|
-
add(
|
|
7369
|
-
add(
|
|
7693
|
+
add(import_types28.EdgeType.RUNS_ON, "railway", "railway");
|
|
7694
|
+
add(import_types28.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
|
|
7695
|
+
add(import_types28.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
|
|
7370
7696
|
}
|
|
7371
7697
|
return { nodesAdded, edgesAdded };
|
|
7372
7698
|
}
|
|
@@ -7374,12 +7700,12 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7374
7700
|
// src/extract/infra/supabase.ts
|
|
7375
7701
|
init_cjs_shims();
|
|
7376
7702
|
var import_node_fs23 = require("fs");
|
|
7377
|
-
var
|
|
7703
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
7378
7704
|
var import_smol_toml4 = require("smol-toml");
|
|
7379
|
-
var
|
|
7705
|
+
var import_types29 = require("@neat.is/types");
|
|
7380
7706
|
async function readSupabaseConfig(dir) {
|
|
7381
|
-
const relFile =
|
|
7382
|
-
const abs =
|
|
7707
|
+
const relFile = import_node_path40.default.join("supabase", "config.toml");
|
|
7708
|
+
const abs = import_node_path40.default.join(dir, relFile);
|
|
7383
7709
|
if (!await exists(abs)) return null;
|
|
7384
7710
|
const raw = await import_node_fs23.promises.readFile(abs, "utf8");
|
|
7385
7711
|
const config = (0, import_smol_toml4.parse)(raw);
|
|
@@ -7393,7 +7719,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7393
7719
|
try {
|
|
7394
7720
|
read = await readSupabaseConfig(service.dir);
|
|
7395
7721
|
} catch (err) {
|
|
7396
|
-
recordExtractionError("infra supabase",
|
|
7722
|
+
recordExtractionError("infra supabase", import_node_path40.default.relative(scanPath, service.dir), err);
|
|
7397
7723
|
continue;
|
|
7398
7724
|
}
|
|
7399
7725
|
if (!read) continue;
|
|
@@ -7408,7 +7734,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7408
7734
|
});
|
|
7409
7735
|
}
|
|
7410
7736
|
const anchorId = service.node.id;
|
|
7411
|
-
const evidenceFile = toPosix2(
|
|
7737
|
+
const evidenceFile = toPosix2(import_node_path40.default.relative(scanPath, import_node_path40.default.join(service.dir, relFile)));
|
|
7412
7738
|
const add = (edgeType, kind, name) => {
|
|
7413
7739
|
if (!name) return;
|
|
7414
7740
|
const result = emitPlatformResourceEdge(
|
|
@@ -7424,10 +7750,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7424
7750
|
nodesAdded += result.nodesAdded;
|
|
7425
7751
|
edgesAdded += result.edgesAdded;
|
|
7426
7752
|
};
|
|
7427
|
-
add(
|
|
7428
|
-
for (const fn of Object.keys(config.functions ?? {})) add(
|
|
7429
|
-
if (config.storage) add(
|
|
7430
|
-
if (config.auth) add(
|
|
7753
|
+
add(import_types29.EdgeType.RUNS_ON, "supabase", "supabase");
|
|
7754
|
+
for (const fn of Object.keys(config.functions ?? {})) add(import_types29.EdgeType.DEPENDS_ON, "supabase-function", fn);
|
|
7755
|
+
if (config.storage) add(import_types29.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
|
|
7756
|
+
if (config.auth) add(import_types29.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
|
|
7431
7757
|
}
|
|
7432
7758
|
return { nodesAdded, edgesAdded };
|
|
7433
7759
|
}
|
|
@@ -7449,17 +7775,17 @@ async function addInfra(graph, scanPath, services) {
|
|
|
7449
7775
|
}
|
|
7450
7776
|
|
|
7451
7777
|
// src/extract/index.ts
|
|
7452
|
-
var
|
|
7778
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
7453
7779
|
|
|
7454
7780
|
// src/extract/retire.ts
|
|
7455
7781
|
init_cjs_shims();
|
|
7456
7782
|
var import_node_fs24 = require("fs");
|
|
7457
|
-
var
|
|
7458
|
-
var
|
|
7783
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
7784
|
+
var import_types30 = require("@neat.is/types");
|
|
7459
7785
|
function dropOrphanedFileNodes(graph) {
|
|
7460
7786
|
const orphans = [];
|
|
7461
7787
|
graph.forEachNode((id, attrs) => {
|
|
7462
|
-
if (attrs.type !==
|
|
7788
|
+
if (attrs.type !== import_types30.NodeType.FileNode) return;
|
|
7463
7789
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
7464
7790
|
orphans.push(id);
|
|
7465
7791
|
}
|
|
@@ -7472,7 +7798,7 @@ function retireEdgesByFile(graph, file) {
|
|
|
7472
7798
|
const toDrop = [];
|
|
7473
7799
|
graph.forEachEdge((id, attrs) => {
|
|
7474
7800
|
const edge = attrs;
|
|
7475
|
-
if (edge.provenance !==
|
|
7801
|
+
if (edge.provenance !== import_types30.Provenance.EXTRACTED) return;
|
|
7476
7802
|
if (!edge.evidence?.file) return;
|
|
7477
7803
|
if (edge.evidence.file === normalized) toDrop.push(id);
|
|
7478
7804
|
});
|
|
@@ -7485,14 +7811,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
7485
7811
|
const bases = [scanPath, ...serviceDirs];
|
|
7486
7812
|
graph.forEachEdge((id, attrs) => {
|
|
7487
7813
|
const edge = attrs;
|
|
7488
|
-
if (edge.provenance !==
|
|
7814
|
+
if (edge.provenance !== import_types30.Provenance.EXTRACTED) return;
|
|
7489
7815
|
const evidenceFile = edge.evidence?.file;
|
|
7490
7816
|
if (!evidenceFile) return;
|
|
7491
|
-
if (
|
|
7817
|
+
if (import_node_path41.default.isAbsolute(evidenceFile)) {
|
|
7492
7818
|
if (!(0, import_node_fs24.existsSync)(evidenceFile)) toDrop.push(id);
|
|
7493
7819
|
return;
|
|
7494
7820
|
}
|
|
7495
|
-
const found = bases.some((base) => (0, import_node_fs24.existsSync)(
|
|
7821
|
+
const found = bases.some((base) => (0, import_node_fs24.existsSync)(import_node_path41.default.join(base, evidenceFile)));
|
|
7496
7822
|
if (!found) toDrop.push(id);
|
|
7497
7823
|
});
|
|
7498
7824
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -7534,7 +7860,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
7534
7860
|
}
|
|
7535
7861
|
const droppedEntries = drainDroppedExtracted();
|
|
7536
7862
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
7537
|
-
const rejectedPath =
|
|
7863
|
+
const rejectedPath = import_node_path42.default.join(import_node_path42.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
7538
7864
|
try {
|
|
7539
7865
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
7540
7866
|
} catch (err) {
|
|
@@ -7568,39 +7894,39 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
7568
7894
|
|
|
7569
7895
|
// src/divergences.ts
|
|
7570
7896
|
init_cjs_shims();
|
|
7571
|
-
var
|
|
7897
|
+
var import_types31 = require("@neat.is/types");
|
|
7572
7898
|
function bucketKey(source, target, type) {
|
|
7573
7899
|
return `${type}|${source}|${target}`;
|
|
7574
7900
|
}
|
|
7575
7901
|
function bucketSourceFor(graph, edge) {
|
|
7576
|
-
if (edge.type !==
|
|
7577
|
-
const parsed = (0,
|
|
7902
|
+
if (edge.type !== import_types31.EdgeType.CONNECTS_TO) return edge.source;
|
|
7903
|
+
const parsed = (0, import_types31.parseFileId)(edge.source);
|
|
7578
7904
|
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
7579
7905
|
const target = graph.getNodeAttributes(edge.target);
|
|
7580
|
-
if (target.type !==
|
|
7581
|
-
return (0,
|
|
7906
|
+
if (target.type !== import_types31.NodeType.DatabaseNode) return edge.source;
|
|
7907
|
+
return (0, import_types31.serviceId)(parsed.service);
|
|
7582
7908
|
}
|
|
7583
7909
|
function bucketEdges(graph) {
|
|
7584
7910
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
7585
7911
|
graph.forEachEdge((id, attrs) => {
|
|
7586
7912
|
const e = attrs;
|
|
7587
|
-
const parsed = (0,
|
|
7913
|
+
const parsed = (0, import_types31.parseEdgeId)(id);
|
|
7588
7914
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
7589
7915
|
const source = bucketSourceFor(graph, e);
|
|
7590
7916
|
const key = bucketKey(source, e.target, e.type);
|
|
7591
7917
|
const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
|
|
7592
7918
|
switch (provenance) {
|
|
7593
|
-
case
|
|
7919
|
+
case import_types31.Provenance.EXTRACTED:
|
|
7594
7920
|
cur.extracted = e;
|
|
7595
7921
|
break;
|
|
7596
|
-
case
|
|
7922
|
+
case import_types31.Provenance.OBSERVED:
|
|
7597
7923
|
cur.observed = e;
|
|
7598
7924
|
break;
|
|
7599
|
-
case
|
|
7925
|
+
case import_types31.Provenance.INFERRED:
|
|
7600
7926
|
cur.inferred = e;
|
|
7601
7927
|
break;
|
|
7602
7928
|
default:
|
|
7603
|
-
if (e.provenance ===
|
|
7929
|
+
if (e.provenance === import_types31.Provenance.STALE) cur.stale = e;
|
|
7604
7930
|
}
|
|
7605
7931
|
buckets2.set(key, cur);
|
|
7606
7932
|
});
|
|
@@ -7609,12 +7935,12 @@ function bucketEdges(graph) {
|
|
|
7609
7935
|
function nodeIsFrontier(graph, nodeId) {
|
|
7610
7936
|
if (!graph.hasNode(nodeId)) return false;
|
|
7611
7937
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
7612
|
-
return attrs.type ===
|
|
7938
|
+
return attrs.type === import_types31.NodeType.FrontierNode;
|
|
7613
7939
|
}
|
|
7614
7940
|
function nodeIsWebsocketChannel(graph, nodeId) {
|
|
7615
7941
|
if (!graph.hasNode(nodeId)) return false;
|
|
7616
7942
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
7617
|
-
return attrs.type ===
|
|
7943
|
+
return attrs.type === import_types31.NodeType.WebSocketChannelNode;
|
|
7618
7944
|
}
|
|
7619
7945
|
function clampConfidence(n) {
|
|
7620
7946
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -7634,14 +7960,14 @@ function gradedConfidence(edge) {
|
|
|
7634
7960
|
return clampConfidence(confidenceForEdge(edge));
|
|
7635
7961
|
}
|
|
7636
7962
|
var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7963
|
+
import_types31.EdgeType.CALLS,
|
|
7964
|
+
import_types31.EdgeType.CONNECTS_TO,
|
|
7965
|
+
import_types31.EdgeType.PUBLISHES_TO,
|
|
7966
|
+
import_types31.EdgeType.CONSUMES_FROM
|
|
7641
7967
|
]);
|
|
7642
7968
|
function detectMissingDivergences(graph, bucket) {
|
|
7643
7969
|
const out = [];
|
|
7644
|
-
if (bucket.type ===
|
|
7970
|
+
if (bucket.type === import_types31.EdgeType.CONTAINS) return out;
|
|
7645
7971
|
if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
|
|
7646
7972
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
7647
7973
|
out.push({
|
|
@@ -7682,7 +8008,7 @@ function declaredHostFor(svc) {
|
|
|
7682
8008
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
7683
8009
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
7684
8010
|
const e = graph.getEdgeAttributes(edgeId);
|
|
7685
|
-
if (e.type ===
|
|
8011
|
+
if (e.type === import_types31.EdgeType.CONFIGURED_BY && e.provenance === import_types31.Provenance.EXTRACTED) {
|
|
7686
8012
|
return true;
|
|
7687
8013
|
}
|
|
7688
8014
|
}
|
|
@@ -7695,10 +8021,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
7695
8021
|
const out = [];
|
|
7696
8022
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
7697
8023
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
7698
|
-
if (edge.type !==
|
|
7699
|
-
if (edge.provenance !==
|
|
8024
|
+
if (edge.type !== import_types31.EdgeType.CONNECTS_TO) continue;
|
|
8025
|
+
if (edge.provenance !== import_types31.Provenance.OBSERVED) continue;
|
|
7700
8026
|
const target = graph.getNodeAttributes(edge.target);
|
|
7701
|
-
if (target.type !==
|
|
8027
|
+
if (target.type !== import_types31.NodeType.DatabaseNode) continue;
|
|
7702
8028
|
const observedHost = target.host?.trim();
|
|
7703
8029
|
if (!observedHost) continue;
|
|
7704
8030
|
if (observedHost === declaredHost) continue;
|
|
@@ -7720,10 +8046,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
7720
8046
|
const deps = svc.dependencies ?? {};
|
|
7721
8047
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
7722
8048
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
7723
|
-
if (edge.type !==
|
|
7724
|
-
if (edge.provenance !==
|
|
8049
|
+
if (edge.type !== import_types31.EdgeType.CONNECTS_TO) continue;
|
|
8050
|
+
if (edge.provenance !== import_types31.Provenance.OBSERVED) continue;
|
|
7725
8051
|
const target = graph.getNodeAttributes(edge.target);
|
|
7726
|
-
if (target.type !==
|
|
8052
|
+
if (target.type !== import_types31.NodeType.DatabaseNode) continue;
|
|
7727
8053
|
for (const pair of compatPairs()) {
|
|
7728
8054
|
if (pair.engine !== target.engine) continue;
|
|
7729
8055
|
const declared = deps[pair.driver];
|
|
@@ -7782,7 +8108,7 @@ function suppressHostMismatchHalves(all) {
|
|
|
7782
8108
|
for (const d of all) {
|
|
7783
8109
|
if (d.type !== "host-mismatch") continue;
|
|
7784
8110
|
observedHalf.add(`${d.source}->${d.target}`);
|
|
7785
|
-
declaredHalf.add((0,
|
|
8111
|
+
declaredHalf.add((0, import_types31.databaseId)(d.extractedHost));
|
|
7786
8112
|
}
|
|
7787
8113
|
if (observedHalf.size === 0) return all;
|
|
7788
8114
|
return all.filter((d) => {
|
|
@@ -7801,7 +8127,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
7801
8127
|
}
|
|
7802
8128
|
graph.forEachNode((nodeId, attrs) => {
|
|
7803
8129
|
const n = attrs;
|
|
7804
|
-
if (n.type !==
|
|
8130
|
+
if (n.type !== import_types31.NodeType.ServiceNode) return;
|
|
7805
8131
|
const svc = n;
|
|
7806
8132
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
7807
8133
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
@@ -7835,7 +8161,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
7835
8161
|
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
7836
8162
|
return a.target.localeCompare(b.target);
|
|
7837
8163
|
});
|
|
7838
|
-
return
|
|
8164
|
+
return import_types31.DivergenceResultSchema.parse({
|
|
7839
8165
|
divergences: filtered,
|
|
7840
8166
|
totalAffected: filtered.length,
|
|
7841
8167
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -7845,8 +8171,8 @@ function computeDivergences(graph, opts = {}) {
|
|
|
7845
8171
|
// src/persist.ts
|
|
7846
8172
|
init_cjs_shims();
|
|
7847
8173
|
var import_node_fs25 = require("fs");
|
|
7848
|
-
var
|
|
7849
|
-
var
|
|
8174
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
8175
|
+
var import_types32 = require("@neat.is/types");
|
|
7850
8176
|
var SCHEMA_VERSION = 4;
|
|
7851
8177
|
function migrateV1ToV2(payload) {
|
|
7852
8178
|
const nodes = payload.graph.nodes;
|
|
@@ -7868,12 +8194,12 @@ function migrateV2ToV3(payload) {
|
|
|
7868
8194
|
for (const edge of edges) {
|
|
7869
8195
|
const attrs = edge.attributes;
|
|
7870
8196
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
7871
|
-
attrs.provenance =
|
|
8197
|
+
attrs.provenance = import_types32.Provenance.OBSERVED;
|
|
7872
8198
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
7873
8199
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
7874
8200
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
7875
8201
|
if (type && source && target) {
|
|
7876
|
-
const newId = (0,
|
|
8202
|
+
const newId = (0, import_types32.observedEdgeId)(source, target, type);
|
|
7877
8203
|
attrs.id = newId;
|
|
7878
8204
|
if (edge.key) edge.key = newId;
|
|
7879
8205
|
}
|
|
@@ -7882,7 +8208,7 @@ function migrateV2ToV3(payload) {
|
|
|
7882
8208
|
return { ...payload, schemaVersion: 3 };
|
|
7883
8209
|
}
|
|
7884
8210
|
async function ensureDir(filePath) {
|
|
7885
|
-
await import_node_fs25.promises.mkdir(
|
|
8211
|
+
await import_node_fs25.promises.mkdir(import_node_path43.default.dirname(filePath), { recursive: true });
|
|
7886
8212
|
}
|
|
7887
8213
|
async function saveGraphToDisk(graph, outPath) {
|
|
7888
8214
|
await ensureDir(outPath);
|
|
@@ -7964,7 +8290,7 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
7964
8290
|
// src/gitignore.ts
|
|
7965
8291
|
init_cjs_shims();
|
|
7966
8292
|
var import_node_fs26 = require("fs");
|
|
7967
|
-
var
|
|
8293
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
7968
8294
|
var NEAT_OUT_LINE = "neat-out/";
|
|
7969
8295
|
var NEAT_HEADER = "# NEAT \u2014 machine-local snapshots and events";
|
|
7970
8296
|
function isNeatOutLine(line) {
|
|
@@ -7972,7 +8298,7 @@ function isNeatOutLine(line) {
|
|
|
7972
8298
|
return trimmed === "neat-out/" || trimmed === "neat-out";
|
|
7973
8299
|
}
|
|
7974
8300
|
async function ensureNeatOutIgnored(projectDir) {
|
|
7975
|
-
const file =
|
|
8301
|
+
const file = import_node_path44.default.join(projectDir, ".gitignore");
|
|
7976
8302
|
let existing = null;
|
|
7977
8303
|
try {
|
|
7978
8304
|
existing = await import_node_fs26.promises.readFile(file, "utf8");
|
|
@@ -7999,7 +8325,7 @@ ${NEAT_OUT_LINE}
|
|
|
7999
8325
|
|
|
8000
8326
|
// src/summary.ts
|
|
8001
8327
|
init_cjs_shims();
|
|
8002
|
-
var
|
|
8328
|
+
var import_types33 = require("@neat.is/types");
|
|
8003
8329
|
function renderOtelEnvBlock() {
|
|
8004
8330
|
return [
|
|
8005
8331
|
"for prod OTel routing, set these in your deploy platform's env:",
|
|
@@ -8009,19 +8335,19 @@ function renderOtelEnvBlock() {
|
|
|
8009
8335
|
}
|
|
8010
8336
|
function findIncompatServices(nodes) {
|
|
8011
8337
|
return nodes.filter(
|
|
8012
|
-
(n) => n.type ===
|
|
8338
|
+
(n) => n.type === import_types33.NodeType.ServiceNode && Array.isArray(n.incompatibilities) && (n.incompatibilities ?? []).length > 0
|
|
8013
8339
|
);
|
|
8014
8340
|
}
|
|
8015
8341
|
function servicesWithoutObserved(nodes, edges) {
|
|
8016
8342
|
const seen = /* @__PURE__ */ new Set();
|
|
8017
8343
|
for (const e of edges) {
|
|
8018
|
-
if (e.provenance ===
|
|
8344
|
+
if (e.provenance === import_types33.Provenance.OBSERVED) {
|
|
8019
8345
|
seen.add(e.source);
|
|
8020
8346
|
seen.add(e.target);
|
|
8021
8347
|
}
|
|
8022
8348
|
}
|
|
8023
8349
|
return nodes.filter(
|
|
8024
|
-
(n) => n.type ===
|
|
8350
|
+
(n) => n.type === import_types33.NodeType.ServiceNode && !seen.has(n.id)
|
|
8025
8351
|
);
|
|
8026
8352
|
}
|
|
8027
8353
|
function formatDivergence(d) {
|
|
@@ -8096,26 +8422,26 @@ function formatIncompat(inc) {
|
|
|
8096
8422
|
// src/watch.ts
|
|
8097
8423
|
init_cjs_shims();
|
|
8098
8424
|
var import_node_fs35 = __toESM(require("fs"), 1);
|
|
8099
|
-
var
|
|
8425
|
+
var import_node_path55 = __toESM(require("path"), 1);
|
|
8100
8426
|
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
8101
8427
|
|
|
8102
8428
|
// src/api.ts
|
|
8103
8429
|
init_cjs_shims();
|
|
8104
8430
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
8105
8431
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
8106
|
-
var
|
|
8432
|
+
var import_types35 = require("@neat.is/types");
|
|
8107
8433
|
|
|
8108
8434
|
// src/extend/index.ts
|
|
8109
8435
|
init_cjs_shims();
|
|
8110
8436
|
var import_node_fs28 = require("fs");
|
|
8111
|
-
var
|
|
8437
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
8112
8438
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
8113
8439
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
8114
8440
|
|
|
8115
8441
|
// src/installers/package-manager.ts
|
|
8116
8442
|
init_cjs_shims();
|
|
8117
8443
|
var import_node_fs27 = require("fs");
|
|
8118
|
-
var
|
|
8444
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
8119
8445
|
var import_node_child_process = require("child_process");
|
|
8120
8446
|
var LOCKFILE_PRIORITY = [
|
|
8121
8447
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -8137,22 +8463,22 @@ async function exists2(p) {
|
|
|
8137
8463
|
}
|
|
8138
8464
|
}
|
|
8139
8465
|
async function detectPackageManager(serviceDir) {
|
|
8140
|
-
let dir =
|
|
8466
|
+
let dir = import_node_path45.default.resolve(serviceDir);
|
|
8141
8467
|
const stops = /* @__PURE__ */ new Set();
|
|
8142
8468
|
for (let i = 0; i < 64; i++) {
|
|
8143
8469
|
if (stops.has(dir)) break;
|
|
8144
8470
|
stops.add(dir);
|
|
8145
8471
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
8146
|
-
const lockPath =
|
|
8472
|
+
const lockPath = import_node_path45.default.join(dir, candidate.lockfile);
|
|
8147
8473
|
if (await exists2(lockPath)) {
|
|
8148
8474
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
8149
8475
|
}
|
|
8150
8476
|
}
|
|
8151
|
-
const parent =
|
|
8477
|
+
const parent = import_node_path45.default.dirname(dir);
|
|
8152
8478
|
if (parent === dir) break;
|
|
8153
8479
|
dir = parent;
|
|
8154
8480
|
}
|
|
8155
|
-
return { pm: "npm", cwd:
|
|
8481
|
+
return { pm: "npm", cwd: import_node_path45.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
8156
8482
|
}
|
|
8157
8483
|
async function runPackageManagerInstall(cmd) {
|
|
8158
8484
|
return new Promise((resolve) => {
|
|
@@ -8201,7 +8527,7 @@ async function fileExists2(p) {
|
|
|
8201
8527
|
}
|
|
8202
8528
|
}
|
|
8203
8529
|
async function readPackageJson(scanPath) {
|
|
8204
|
-
const pkgPath =
|
|
8530
|
+
const pkgPath = import_node_path46.default.join(scanPath, "package.json");
|
|
8205
8531
|
const raw = await import_node_fs28.promises.readFile(pkgPath, "utf8");
|
|
8206
8532
|
return JSON.parse(raw);
|
|
8207
8533
|
}
|
|
@@ -8220,11 +8546,11 @@ async function findHookFiles(scanPath) {
|
|
|
8220
8546
|
for (const entry2 of entries) {
|
|
8221
8547
|
if (entry2.isDirectory()) {
|
|
8222
8548
|
if (entry2.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry2.name)) continue;
|
|
8223
|
-
await walk3(
|
|
8549
|
+
await walk3(import_node_path46.default.join(dir, entry2.name));
|
|
8224
8550
|
} else if (entry2.isFile()) {
|
|
8225
8551
|
if ((entry2.name.startsWith("instrumentation") || entry2.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry2.name)) {
|
|
8226
|
-
const rel =
|
|
8227
|
-
found.push(rel.split(
|
|
8552
|
+
const rel = import_node_path46.default.relative(scanPath, import_node_path46.default.join(dir, entry2.name));
|
|
8553
|
+
found.push(rel.split(import_node_path46.default.sep).join("/"));
|
|
8228
8554
|
}
|
|
8229
8555
|
}
|
|
8230
8556
|
}
|
|
@@ -8235,7 +8561,7 @@ async function findHookFiles(scanPath) {
|
|
|
8235
8561
|
async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
8236
8562
|
let fallback = null;
|
|
8237
8563
|
for (const file of hookFiles) {
|
|
8238
|
-
const content = await import_node_fs28.promises.readFile(
|
|
8564
|
+
const content = await import_node_fs28.promises.readFile(import_node_path46.default.join(scanPath, file), "utf8");
|
|
8239
8565
|
const patched = splicedContent(content, snippet2);
|
|
8240
8566
|
if (patched !== null) return { file, content, patched };
|
|
8241
8567
|
if (fallback === null) fallback = { file, content };
|
|
@@ -8243,11 +8569,11 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
|
8243
8569
|
return { file: fallback.file, content: fallback.content, patched: null };
|
|
8244
8570
|
}
|
|
8245
8571
|
function extendLogPath() {
|
|
8246
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
8572
|
+
return process.env.NEAT_EXTEND_LOG ?? import_node_path46.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
|
|
8247
8573
|
}
|
|
8248
8574
|
async function appendExtendLog(entry2) {
|
|
8249
8575
|
const logPath = extendLogPath();
|
|
8250
|
-
await import_node_fs28.promises.mkdir(
|
|
8576
|
+
await import_node_fs28.promises.mkdir(import_node_path46.default.dirname(logPath), { recursive: true });
|
|
8251
8577
|
await import_node_fs28.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
|
|
8252
8578
|
}
|
|
8253
8579
|
function splicedContent(fileContent, snippet2) {
|
|
@@ -8306,7 +8632,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
8306
8632
|
}
|
|
8307
8633
|
async function describeProjectInstrumentation(ctx) {
|
|
8308
8634
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
8309
|
-
const envNeat = await fileExists2(
|
|
8635
|
+
const envNeat = await fileExists2(import_node_path46.default.join(ctx.scanPath, ".env.neat"));
|
|
8310
8636
|
const registryInstrPackages = new Set(
|
|
8311
8637
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
8312
8638
|
);
|
|
@@ -8328,7 +8654,7 @@ async function applyExtension(ctx, args, options) {
|
|
|
8328
8654
|
);
|
|
8329
8655
|
}
|
|
8330
8656
|
for (const file of hookFiles) {
|
|
8331
|
-
const content = await import_node_fs28.promises.readFile(
|
|
8657
|
+
const content = await import_node_fs28.promises.readFile(import_node_path46.default.join(ctx.scanPath, file), "utf8");
|
|
8332
8658
|
if (content.includes(args.registration_snippet)) {
|
|
8333
8659
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
8334
8660
|
}
|
|
@@ -8340,10 +8666,10 @@ async function applyExtension(ctx, args, options) {
|
|
|
8340
8666
|
);
|
|
8341
8667
|
}
|
|
8342
8668
|
const primaryFile = primary.file;
|
|
8343
|
-
const primaryPath =
|
|
8669
|
+
const primaryPath = import_node_path46.default.join(ctx.scanPath, primaryFile);
|
|
8344
8670
|
const filesTouched = [];
|
|
8345
8671
|
const depsAdded = [];
|
|
8346
|
-
const pkgPath =
|
|
8672
|
+
const pkgPath = import_node_path46.default.join(ctx.scanPath, "package.json");
|
|
8347
8673
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
8348
8674
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
8349
8675
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
@@ -8382,7 +8708,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
8382
8708
|
};
|
|
8383
8709
|
}
|
|
8384
8710
|
for (const file of hookFiles) {
|
|
8385
|
-
const content = await import_node_fs28.promises.readFile(
|
|
8711
|
+
const content = await import_node_fs28.promises.readFile(import_node_path46.default.join(ctx.scanPath, file), "utf8");
|
|
8386
8712
|
if (content.includes(args.registration_snippet)) {
|
|
8387
8713
|
return {
|
|
8388
8714
|
library: args.library,
|
|
@@ -8423,7 +8749,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
8423
8749
|
if (!match) {
|
|
8424
8750
|
return { undone: false, message: "no apply found for library" };
|
|
8425
8751
|
}
|
|
8426
|
-
const pkgPath =
|
|
8752
|
+
const pkgPath = import_node_path46.default.join(ctx.scanPath, "package.json");
|
|
8427
8753
|
if (await fileExists2(pkgPath)) {
|
|
8428
8754
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
8429
8755
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
@@ -8434,7 +8760,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
8434
8760
|
}
|
|
8435
8761
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
8436
8762
|
for (const file of hookFiles) {
|
|
8437
|
-
const filePath =
|
|
8763
|
+
const filePath = import_node_path46.default.join(ctx.scanPath, file);
|
|
8438
8764
|
const content = await import_node_fs28.promises.readFile(filePath, "utf8");
|
|
8439
8765
|
if (content.includes(match.registration_snippet)) {
|
|
8440
8766
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
@@ -8572,23 +8898,23 @@ function canonicalJson(value) {
|
|
|
8572
8898
|
|
|
8573
8899
|
// src/projects.ts
|
|
8574
8900
|
init_cjs_shims();
|
|
8575
|
-
var
|
|
8901
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
8576
8902
|
function pathsForProject(project, baseDir) {
|
|
8577
8903
|
if (project === DEFAULT_PROJECT) {
|
|
8578
8904
|
return {
|
|
8579
|
-
snapshotPath:
|
|
8580
|
-
errorsPath:
|
|
8581
|
-
staleEventsPath:
|
|
8582
|
-
embeddingsCachePath:
|
|
8583
|
-
policyViolationsPath:
|
|
8905
|
+
snapshotPath: import_node_path47.default.join(baseDir, "graph.json"),
|
|
8906
|
+
errorsPath: import_node_path47.default.join(baseDir, "errors.ndjson"),
|
|
8907
|
+
staleEventsPath: import_node_path47.default.join(baseDir, "stale-events.ndjson"),
|
|
8908
|
+
embeddingsCachePath: import_node_path47.default.join(baseDir, "embeddings.json"),
|
|
8909
|
+
policyViolationsPath: import_node_path47.default.join(baseDir, "policy-violations.ndjson")
|
|
8584
8910
|
};
|
|
8585
8911
|
}
|
|
8586
8912
|
return {
|
|
8587
|
-
snapshotPath:
|
|
8588
|
-
errorsPath:
|
|
8589
|
-
staleEventsPath:
|
|
8590
|
-
embeddingsCachePath:
|
|
8591
|
-
policyViolationsPath:
|
|
8913
|
+
snapshotPath: import_node_path47.default.join(baseDir, `${project}.json`),
|
|
8914
|
+
errorsPath: import_node_path47.default.join(baseDir, `errors.${project}.ndjson`),
|
|
8915
|
+
staleEventsPath: import_node_path47.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
8916
|
+
embeddingsCachePath: import_node_path47.default.join(baseDir, `embeddings.${project}.json`),
|
|
8917
|
+
policyViolationsPath: import_node_path47.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
8592
8918
|
};
|
|
8593
8919
|
}
|
|
8594
8920
|
var Projects = class {
|
|
@@ -8626,26 +8952,26 @@ var Projects = class {
|
|
|
8626
8952
|
init_cjs_shims();
|
|
8627
8953
|
var import_node_fs30 = require("fs");
|
|
8628
8954
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
8629
|
-
var
|
|
8630
|
-
var
|
|
8955
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
8956
|
+
var import_types34 = require("@neat.is/types");
|
|
8631
8957
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
8632
8958
|
var LOCK_RETRY_MS = 50;
|
|
8633
8959
|
function neatHome() {
|
|
8634
8960
|
const override = process.env.NEAT_HOME;
|
|
8635
|
-
if (override && override.length > 0) return
|
|
8636
|
-
return
|
|
8961
|
+
if (override && override.length > 0) return import_node_path48.default.resolve(override);
|
|
8962
|
+
return import_node_path48.default.join(import_node_os3.default.homedir(), ".neat");
|
|
8637
8963
|
}
|
|
8638
8964
|
function registryPath() {
|
|
8639
|
-
return
|
|
8965
|
+
return import_node_path48.default.join(neatHome(), "projects.json");
|
|
8640
8966
|
}
|
|
8641
8967
|
function registryLockPath() {
|
|
8642
|
-
return
|
|
8968
|
+
return import_node_path48.default.join(neatHome(), "projects.json.lock");
|
|
8643
8969
|
}
|
|
8644
8970
|
function daemonPidPath() {
|
|
8645
|
-
return
|
|
8971
|
+
return import_node_path48.default.join(neatHome(), "neatd.pid");
|
|
8646
8972
|
}
|
|
8647
8973
|
function daemonsDir() {
|
|
8648
|
-
return
|
|
8974
|
+
return import_node_path48.default.join(neatHome(), "daemons");
|
|
8649
8975
|
}
|
|
8650
8976
|
function isFiniteInt(v) {
|
|
8651
8977
|
return typeof v === "number" && Number.isFinite(v);
|
|
@@ -8686,7 +9012,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
8686
9012
|
const out = [];
|
|
8687
9013
|
for (const name of names) {
|
|
8688
9014
|
if (!name.endsWith(".json")) continue;
|
|
8689
|
-
const file =
|
|
9015
|
+
const file = import_node_path48.default.join(dir, name);
|
|
8690
9016
|
let raw;
|
|
8691
9017
|
try {
|
|
8692
9018
|
raw = await import_node_fs30.promises.readFile(file, "utf8");
|
|
@@ -8807,7 +9133,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
8807
9133
|
}
|
|
8808
9134
|
}
|
|
8809
9135
|
async function normalizeProjectPath(input) {
|
|
8810
|
-
const resolved =
|
|
9136
|
+
const resolved = import_node_path48.default.resolve(input);
|
|
8811
9137
|
try {
|
|
8812
9138
|
return await import_node_fs30.promises.realpath(resolved);
|
|
8813
9139
|
} catch {
|
|
@@ -8815,7 +9141,7 @@ async function normalizeProjectPath(input) {
|
|
|
8815
9141
|
}
|
|
8816
9142
|
}
|
|
8817
9143
|
async function writeAtomically(target, contents) {
|
|
8818
|
-
await import_node_fs30.promises.mkdir(
|
|
9144
|
+
await import_node_fs30.promises.mkdir(import_node_path48.default.dirname(target), { recursive: true });
|
|
8819
9145
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
8820
9146
|
const fd = await import_node_fs30.promises.open(tmp, "w");
|
|
8821
9147
|
try {
|
|
@@ -8828,7 +9154,7 @@ async function writeAtomically(target, contents) {
|
|
|
8828
9154
|
}
|
|
8829
9155
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
8830
9156
|
const deadline = Date.now() + timeoutMs;
|
|
8831
|
-
await import_node_fs30.promises.mkdir(
|
|
9157
|
+
await import_node_fs30.promises.mkdir(import_node_path48.default.dirname(lockPath), { recursive: true });
|
|
8832
9158
|
let probedHolder = false;
|
|
8833
9159
|
while (true) {
|
|
8834
9160
|
try {
|
|
@@ -8881,10 +9207,10 @@ async function readRegistry() {
|
|
|
8881
9207
|
throw err;
|
|
8882
9208
|
}
|
|
8883
9209
|
const parsed = JSON.parse(raw);
|
|
8884
|
-
return
|
|
9210
|
+
return import_types34.RegistryFileSchema.parse(parsed);
|
|
8885
9211
|
}
|
|
8886
9212
|
async function writeRegistry(reg) {
|
|
8887
|
-
const validated =
|
|
9213
|
+
const validated = import_types34.RegistryFileSchema.parse(reg);
|
|
8888
9214
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
8889
9215
|
}
|
|
8890
9216
|
var ProjectNameCollisionError = class extends Error {
|
|
@@ -9070,7 +9396,7 @@ init_auth();
|
|
|
9070
9396
|
// src/connectors-config.ts
|
|
9071
9397
|
init_cjs_shims();
|
|
9072
9398
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
9073
|
-
var
|
|
9399
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
9074
9400
|
var import_node_fs31 = require("fs");
|
|
9075
9401
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
9076
9402
|
var EnvRefUnsetError = class extends Error {
|
|
@@ -9085,11 +9411,11 @@ var EnvRefUnsetError = class extends Error {
|
|
|
9085
9411
|
};
|
|
9086
9412
|
function neatHome2() {
|
|
9087
9413
|
const override = process.env.NEAT_HOME;
|
|
9088
|
-
if (override && override.length > 0) return
|
|
9089
|
-
return
|
|
9414
|
+
if (override && override.length > 0) return import_node_path49.default.resolve(override);
|
|
9415
|
+
return import_node_path49.default.join(import_node_os4.default.homedir(), ".neat");
|
|
9090
9416
|
}
|
|
9091
9417
|
function connectorsConfigPath(home = neatHome2()) {
|
|
9092
|
-
return
|
|
9418
|
+
return import_node_path49.default.join(home, "connectors.json");
|
|
9093
9419
|
}
|
|
9094
9420
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
9095
9421
|
async function warnIfModeLooserThan0600(file) {
|
|
@@ -9220,7 +9546,7 @@ function connectorMatchesProject(entry2, project) {
|
|
|
9220
9546
|
var CONNECTORS_LOCK_TIMEOUT_MS = 5e3;
|
|
9221
9547
|
var CONNECTORS_LOCK_RETRY_MS = 50;
|
|
9222
9548
|
function connectorsConfigLockPath(home = neatHome2()) {
|
|
9223
|
-
return
|
|
9549
|
+
return import_node_path49.default.join(home, "connectors.json.lock");
|
|
9224
9550
|
}
|
|
9225
9551
|
function isEnvRef(value) {
|
|
9226
9552
|
return value.length > 1 && value.startsWith("$");
|
|
@@ -9233,7 +9559,7 @@ function redactCredentialRef(ref) {
|
|
|
9233
9559
|
return out;
|
|
9234
9560
|
}
|
|
9235
9561
|
async function writeConfigAtomically0600(file, contents) {
|
|
9236
|
-
await import_node_fs31.promises.mkdir(
|
|
9562
|
+
await import_node_fs31.promises.mkdir(import_node_path49.default.dirname(file), { recursive: true });
|
|
9237
9563
|
const tmp = `${file}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
9238
9564
|
const fd = await import_node_fs31.promises.open(tmp, "w", 384);
|
|
9239
9565
|
try {
|
|
@@ -9247,7 +9573,7 @@ async function writeConfigAtomically0600(file, contents) {
|
|
|
9247
9573
|
}
|
|
9248
9574
|
async function acquireConnectorsLock(lockPath, timeoutMs = CONNECTORS_LOCK_TIMEOUT_MS) {
|
|
9249
9575
|
const deadline = Date.now() + timeoutMs;
|
|
9250
|
-
await import_node_fs31.promises.mkdir(
|
|
9576
|
+
await import_node_fs31.promises.mkdir(import_node_path49.default.dirname(lockPath), { recursive: true });
|
|
9251
9577
|
for (; ; ) {
|
|
9252
9578
|
try {
|
|
9253
9579
|
const fd = await import_node_fs31.promises.open(lockPath, "wx");
|
|
@@ -9530,11 +9856,11 @@ function registerRoutes(scope, ctx) {
|
|
|
9530
9856
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
9531
9857
|
const parsed = [];
|
|
9532
9858
|
for (const c of candidates) {
|
|
9533
|
-
const r =
|
|
9859
|
+
const r = import_types35.DivergenceTypeSchema.safeParse(c);
|
|
9534
9860
|
if (!r.success) {
|
|
9535
9861
|
return reply.code(400).send({
|
|
9536
9862
|
error: `unknown divergence type "${c}"`,
|
|
9537
|
-
allowed:
|
|
9863
|
+
allowed: import_types35.DivergenceTypeSchema.options
|
|
9538
9864
|
});
|
|
9539
9865
|
}
|
|
9540
9866
|
parsed.push(r.data);
|
|
@@ -9805,7 +10131,7 @@ function registerRoutes(scope, ctx) {
|
|
|
9805
10131
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
9806
10132
|
let violations = await log.readAll();
|
|
9807
10133
|
if (req.query.severity) {
|
|
9808
|
-
const sev =
|
|
10134
|
+
const sev = import_types35.PolicySeveritySchema.safeParse(req.query.severity);
|
|
9809
10135
|
if (!sev.success) {
|
|
9810
10136
|
return reply.code(400).send({
|
|
9811
10137
|
error: "invalid severity",
|
|
@@ -9844,7 +10170,7 @@ function registerRoutes(scope, ctx) {
|
|
|
9844
10170
|
scope.post("/policies/check", async (req, reply) => {
|
|
9845
10171
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
9846
10172
|
if (!proj) return;
|
|
9847
|
-
const parsed =
|
|
10173
|
+
const parsed = import_types35.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
9848
10174
|
if (!parsed.success) {
|
|
9849
10175
|
return reply.code(400).send({
|
|
9850
10176
|
error: "invalid /policies/check body",
|
|
@@ -10110,13 +10436,13 @@ init_otel();
|
|
|
10110
10436
|
// src/daemon.ts
|
|
10111
10437
|
init_cjs_shims();
|
|
10112
10438
|
var import_node_fs33 = require("fs");
|
|
10113
|
-
var
|
|
10439
|
+
var import_node_path53 = __toESM(require("path"), 1);
|
|
10114
10440
|
var import_node_module = require("module");
|
|
10115
10441
|
init_otel();
|
|
10116
10442
|
|
|
10117
10443
|
// src/connectors/index.ts
|
|
10118
10444
|
init_cjs_shims();
|
|
10119
|
-
var
|
|
10445
|
+
var import_types36 = require("@neat.is/types");
|
|
10120
10446
|
|
|
10121
10447
|
// src/connectors/registry.ts
|
|
10122
10448
|
init_cjs_shims();
|
|
@@ -10194,7 +10520,12 @@ var JUNCTION_DEFAULT_RATE_LIMITS = {
|
|
|
10194
10520
|
supabase: { capacity: 30, refillMs: 1e4 },
|
|
10195
10521
|
// Not a documented API limit at all — a self-imposed ceiling on the raw
|
|
10196
10522
|
// pg_stat_statements connection (see module header above).
|
|
10197
|
-
"supabase-postgres": { capacity: 20, refillMs: 3e3 }
|
|
10523
|
+
"supabase-postgres": { capacity: 20, refillMs: 3e3 },
|
|
10524
|
+
// Push provider (ADR-146): the Drains REST API is touched only by `neat
|
|
10525
|
+
// connector add/remove/test` (provision/deprovision/validate), never a poll
|
|
10526
|
+
// loop, so this bucket is exercised a handful of times per command. Kept
|
|
10527
|
+
// conservative pending a documented Drains-API rate limit.
|
|
10528
|
+
vercel: { capacity: 20, refillMs: 5e3 }
|
|
10198
10529
|
};
|
|
10199
10530
|
var JUNCTION_GENERIC_RATE_LIMIT = { capacity: 20, refillMs: 5e3 };
|
|
10200
10531
|
function defaultRateLimitFor(provider) {
|
|
@@ -10486,10 +10817,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
10486
10817
|
// src/connectors/supabase/map.ts
|
|
10487
10818
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
10488
10819
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
10489
|
-
function targetFromRestPath(
|
|
10490
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
10820
|
+
function targetFromRestPath(path63) {
|
|
10821
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path63);
|
|
10491
10822
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
10492
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
10823
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path63);
|
|
10493
10824
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
10494
10825
|
return null;
|
|
10495
10826
|
}
|
|
@@ -10598,23 +10929,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
10598
10929
|
|
|
10599
10930
|
// src/connectors/supabase/resolve.ts
|
|
10600
10931
|
init_cjs_shims();
|
|
10601
|
-
var
|
|
10932
|
+
var import_types38 = require("@neat.is/types");
|
|
10602
10933
|
function createSupabaseResolveTarget(graph, config) {
|
|
10603
10934
|
return (signal, _ctx) => {
|
|
10604
10935
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
10605
10936
|
return null;
|
|
10606
10937
|
}
|
|
10607
|
-
const subResourceId = (0,
|
|
10938
|
+
const subResourceId = (0, import_types38.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
10608
10939
|
if (graph.hasNode(subResourceId)) {
|
|
10609
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
10940
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
|
|
10610
10941
|
}
|
|
10611
|
-
const bareResourceId = (0,
|
|
10942
|
+
const bareResourceId = (0, import_types38.infraId)(signal.targetKind, signal.targetName);
|
|
10612
10943
|
if (graph.hasNode(bareResourceId)) {
|
|
10613
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
10944
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
|
|
10614
10945
|
}
|
|
10615
|
-
const projectLevelId = (0,
|
|
10946
|
+
const projectLevelId = (0, import_types38.infraId)("supabase", config.nodeRef);
|
|
10616
10947
|
if (graph.hasNode(projectLevelId)) {
|
|
10617
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
10948
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
|
|
10618
10949
|
}
|
|
10619
10950
|
return null;
|
|
10620
10951
|
};
|
|
@@ -10707,7 +11038,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
10707
11038
|
|
|
10708
11039
|
// src/connectors/railway/index.ts
|
|
10709
11040
|
init_cjs_shims();
|
|
10710
|
-
var
|
|
11041
|
+
var import_types42 = require("@neat.is/types");
|
|
10711
11042
|
|
|
10712
11043
|
// src/connectors/railway/client.ts
|
|
10713
11044
|
init_cjs_shims();
|
|
@@ -10833,7 +11164,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
10833
11164
|
const out = [];
|
|
10834
11165
|
graph.forEachNode((_id, attrs) => {
|
|
10835
11166
|
const node = attrs;
|
|
10836
|
-
if (node.type !==
|
|
11167
|
+
if (node.type !== import_types42.NodeType.RouteNode) return;
|
|
10837
11168
|
const route = attrs;
|
|
10838
11169
|
if (route.service !== serviceName) return;
|
|
10839
11170
|
out.push({
|
|
@@ -10937,12 +11268,12 @@ function createRailwayResolveTarget(config) {
|
|
|
10937
11268
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
10938
11269
|
if (!serviceName) return null;
|
|
10939
11270
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
10940
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
11271
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types42.EdgeType.CALLS };
|
|
10941
11272
|
}
|
|
10942
11273
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
10943
11274
|
const peerName = config.serviceNameById[signal.targetName];
|
|
10944
11275
|
if (!peerName) return null;
|
|
10945
|
-
return { targetNodeId: (0,
|
|
11276
|
+
return { targetNodeId: (0, import_types42.serviceId)(peerName), serviceName, edgeType: import_types42.EdgeType.CONNECTS_TO };
|
|
10946
11277
|
}
|
|
10947
11278
|
return null;
|
|
10948
11279
|
};
|
|
@@ -11066,9 +11397,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
11066
11397
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
11067
11398
|
if (secondSep === -1) return null;
|
|
11068
11399
|
const method = rest.slice(0, secondSep);
|
|
11069
|
-
const
|
|
11070
|
-
if (!resourceName || !method || !
|
|
11071
|
-
return { resourceName, method, path:
|
|
11400
|
+
const path63 = rest.slice(secondSep + 1);
|
|
11401
|
+
if (!resourceName || !method || !path63) return null;
|
|
11402
|
+
return { resourceName, method, path: path63 };
|
|
11072
11403
|
}
|
|
11073
11404
|
function resourceNameFor(type, labels) {
|
|
11074
11405
|
if (!labels) return null;
|
|
@@ -11106,14 +11437,14 @@ function mapLogEntryToSignal(entry2) {
|
|
|
11106
11437
|
if (!req) return null;
|
|
11107
11438
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
11108
11439
|
const method = req.requestMethod.toUpperCase();
|
|
11109
|
-
const
|
|
11110
|
-
if (
|
|
11440
|
+
const path63 = pathFromRequestUrl(req.requestUrl);
|
|
11441
|
+
if (path63 === null) return null;
|
|
11111
11442
|
const timestamp = entry2.timestamp;
|
|
11112
11443
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
11113
11444
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
|
|
11114
11445
|
return {
|
|
11115
11446
|
targetKind: resourceType,
|
|
11116
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
11447
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path63 }),
|
|
11117
11448
|
callCount: 1,
|
|
11118
11449
|
errorCount: isError ? 1 : 0,
|
|
11119
11450
|
lastObservedIso: timestamp
|
|
@@ -11130,7 +11461,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
11130
11461
|
|
|
11131
11462
|
// src/connectors/firebase/resolve.ts
|
|
11132
11463
|
init_cjs_shims();
|
|
11133
|
-
var
|
|
11464
|
+
var import_types43 = require("@neat.is/types");
|
|
11134
11465
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
11135
11466
|
switch (resourceType) {
|
|
11136
11467
|
case "cloud_function":
|
|
@@ -11145,7 +11476,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
11145
11476
|
const entries = [];
|
|
11146
11477
|
graph.forEachNode((_id, attrs) => {
|
|
11147
11478
|
const node = attrs;
|
|
11148
|
-
if (node.type !==
|
|
11479
|
+
if (node.type !== import_types43.NodeType.RouteNode) return;
|
|
11149
11480
|
const route = attrs;
|
|
11150
11481
|
if (route.service !== serviceName) return;
|
|
11151
11482
|
entries.push({
|
|
@@ -11177,7 +11508,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
11177
11508
|
return {
|
|
11178
11509
|
targetNodeId: match.routeNodeId,
|
|
11179
11510
|
serviceName,
|
|
11180
|
-
edgeType:
|
|
11511
|
+
edgeType: import_types43.EdgeType.CALLS
|
|
11181
11512
|
};
|
|
11182
11513
|
};
|
|
11183
11514
|
}
|
|
@@ -11204,7 +11535,7 @@ init_cjs_shims();
|
|
|
11204
11535
|
|
|
11205
11536
|
// src/connectors/cloudflare/connector.ts
|
|
11206
11537
|
init_cjs_shims();
|
|
11207
|
-
var
|
|
11538
|
+
var import_types45 = require("@neat.is/types");
|
|
11208
11539
|
|
|
11209
11540
|
// src/connectors/cloudflare/client.ts
|
|
11210
11541
|
init_cjs_shims();
|
|
@@ -11320,7 +11651,7 @@ function mapEventToSignal(event) {
|
|
|
11320
11651
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
11321
11652
|
const statusCode = metadata?.statusCode;
|
|
11322
11653
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
11323
|
-
const
|
|
11654
|
+
const path63 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
11324
11655
|
return {
|
|
11325
11656
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
11326
11657
|
targetName: scriptName,
|
|
@@ -11328,7 +11659,7 @@ function mapEventToSignal(event) {
|
|
|
11328
11659
|
errorCount: isError ? 1 : 0,
|
|
11329
11660
|
lastObservedIso: observedAt.toISOString(),
|
|
11330
11661
|
method,
|
|
11331
|
-
...
|
|
11662
|
+
...path63 ? { path: path63 } : {},
|
|
11332
11663
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
11333
11664
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
11334
11665
|
};
|
|
@@ -11368,19 +11699,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
11368
11699
|
graph.forEachNode((id, attrs) => {
|
|
11369
11700
|
if (found) return;
|
|
11370
11701
|
const a = attrs;
|
|
11371
|
-
if (a.type ===
|
|
11702
|
+
if (a.type === import_types45.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
11372
11703
|
found = id;
|
|
11373
11704
|
}
|
|
11374
11705
|
});
|
|
11375
11706
|
return found;
|
|
11376
11707
|
}
|
|
11377
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
11378
|
-
const normalizedPath = normalizePathTemplate(
|
|
11708
|
+
function findMatchingRouteNode(graph, serviceName, method, path63) {
|
|
11709
|
+
const normalizedPath = normalizePathTemplate(path63);
|
|
11379
11710
|
let found = null;
|
|
11380
11711
|
graph.forEachNode((id, attrs) => {
|
|
11381
11712
|
if (found) return;
|
|
11382
11713
|
const a = attrs;
|
|
11383
|
-
if (a.type !==
|
|
11714
|
+
if (a.type !== import_types45.NodeType.RouteNode || a.service !== serviceName) return;
|
|
11384
11715
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
11385
11716
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
11386
11717
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -11392,18 +11723,18 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
11392
11723
|
return (signal) => {
|
|
11393
11724
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
11394
11725
|
const scriptName = signal.targetName;
|
|
11395
|
-
const { method, path:
|
|
11726
|
+
const { method, path: path63 } = signal;
|
|
11396
11727
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
11397
|
-
if (!method || !
|
|
11398
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
11728
|
+
if (!method || !path63) return wholeFileId;
|
|
11729
|
+
return findMatchingRouteNode(graph, serviceName, method, path63) ?? wholeFileId;
|
|
11399
11730
|
};
|
|
11400
11731
|
const mapping = config.workers?.[scriptName];
|
|
11401
11732
|
if (mapping) {
|
|
11402
|
-
const wholeFileId = (0,
|
|
11733
|
+
const wholeFileId = (0, import_types45.fileId)(mapping.service, mapping.entryFile);
|
|
11403
11734
|
return {
|
|
11404
11735
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
11405
11736
|
serviceName: mapping.service,
|
|
11406
|
-
edgeType:
|
|
11737
|
+
edgeType: import_types45.EdgeType.CALLS
|
|
11407
11738
|
};
|
|
11408
11739
|
}
|
|
11409
11740
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -11412,18 +11743,124 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
11412
11743
|
return {
|
|
11413
11744
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
11414
11745
|
serviceName: fileNode.service,
|
|
11415
|
-
edgeType:
|
|
11746
|
+
edgeType: import_types45.EdgeType.CALLS
|
|
11416
11747
|
};
|
|
11417
11748
|
}
|
|
11418
11749
|
return {
|
|
11419
|
-
targetNodeId: (0,
|
|
11750
|
+
targetNodeId: (0, import_types45.infraId)("cloudflare-worker", scriptName),
|
|
11420
11751
|
serviceName: scriptName,
|
|
11421
|
-
edgeType:
|
|
11752
|
+
edgeType: import_types45.EdgeType.CALLS,
|
|
11422
11753
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
11423
11754
|
};
|
|
11424
11755
|
};
|
|
11425
11756
|
}
|
|
11426
11757
|
|
|
11758
|
+
// src/connectors/vercel/index.ts
|
|
11759
|
+
init_cjs_shims();
|
|
11760
|
+
|
|
11761
|
+
// src/connectors/vercel/client.ts
|
|
11762
|
+
init_cjs_shims();
|
|
11763
|
+
var DEFAULT_API_BASE_URL = "https://api.vercel.com";
|
|
11764
|
+
var DEFAULT_DRAIN_NAME = "neat-otlp";
|
|
11765
|
+
var TRACE_SCHEMAS = { trace: { version: "v1" } };
|
|
11766
|
+
function apiBase(config) {
|
|
11767
|
+
return config.apiBaseUrl ?? DEFAULT_API_BASE_URL;
|
|
11768
|
+
}
|
|
11769
|
+
function teamQuery(config) {
|
|
11770
|
+
return `?teamId=${encodeURIComponent(config.teamId)}`;
|
|
11771
|
+
}
|
|
11772
|
+
function drainDelivery(config, otelToken) {
|
|
11773
|
+
return {
|
|
11774
|
+
type: "http",
|
|
11775
|
+
endpoint: config.endpoint,
|
|
11776
|
+
encoding: "json",
|
|
11777
|
+
headers: bearerAuthHeader(otelToken),
|
|
11778
|
+
...config.secret ? { secret: config.secret } : {}
|
|
11779
|
+
};
|
|
11780
|
+
}
|
|
11781
|
+
async function describeError(res) {
|
|
11782
|
+
try {
|
|
11783
|
+
const data = await res.json();
|
|
11784
|
+
const message = data?.error?.message;
|
|
11785
|
+
return typeof message === "string" && message.length > 0 ? ` \u2014 ${message}` : "";
|
|
11786
|
+
} catch {
|
|
11787
|
+
return "";
|
|
11788
|
+
}
|
|
11789
|
+
}
|
|
11790
|
+
async function createVercelDrain(config, credentials, fetchImpl = fetch) {
|
|
11791
|
+
const projectIds = config.projectIds ?? [];
|
|
11792
|
+
const body = {
|
|
11793
|
+
name: config.drainName ?? DEFAULT_DRAIN_NAME,
|
|
11794
|
+
projects: projectIds.length > 0 ? "some" : "all",
|
|
11795
|
+
...projectIds.length > 0 ? { projectIds } : {},
|
|
11796
|
+
schemas: TRACE_SCHEMAS,
|
|
11797
|
+
delivery: drainDelivery(config, credentials.otelToken),
|
|
11798
|
+
source: { kind: "self-served" }
|
|
11799
|
+
};
|
|
11800
|
+
const res = await junctionFetch(
|
|
11801
|
+
`${apiBase(config)}/v1/drains${teamQuery(config)}`,
|
|
11802
|
+
{
|
|
11803
|
+
method: "POST",
|
|
11804
|
+
headers: { "Content-Type": "application/json", ...bearerAuthHeader(credentials.token) },
|
|
11805
|
+
body: JSON.stringify(body)
|
|
11806
|
+
},
|
|
11807
|
+
{ provider: "vercel", accountKey: config.teamId, fetchImpl }
|
|
11808
|
+
);
|
|
11809
|
+
if (!res.ok) {
|
|
11810
|
+
throw new Error(
|
|
11811
|
+
`vercel connector: create drain failed (${res.status} ${res.statusText}${await describeError(res)})`
|
|
11812
|
+
);
|
|
11813
|
+
}
|
|
11814
|
+
const payload = await res.json().catch(() => null);
|
|
11815
|
+
if (!payload || typeof payload.id !== "string" || payload.id.length === 0) {
|
|
11816
|
+
throw new Error(
|
|
11817
|
+
"vercel connector: create drain returned no drain id \u2014 the Drains API response shape may have changed"
|
|
11818
|
+
);
|
|
11819
|
+
}
|
|
11820
|
+
return {
|
|
11821
|
+
id: payload.id,
|
|
11822
|
+
...payload.status ? { status: payload.status } : {},
|
|
11823
|
+
...payload.disabledReason ? { disabledReason: payload.disabledReason } : {}
|
|
11824
|
+
};
|
|
11825
|
+
}
|
|
11826
|
+
async function deleteVercelDrain(config, drainId, credentials, fetchImpl = fetch) {
|
|
11827
|
+
const res = await junctionFetch(
|
|
11828
|
+
`${apiBase(config)}/v1/drains/${encodeURIComponent(drainId)}${teamQuery(config)}`,
|
|
11829
|
+
{ method: "DELETE", headers: { ...bearerAuthHeader(credentials.token) } },
|
|
11830
|
+
{ provider: "vercel", accountKey: config.teamId, fetchImpl }
|
|
11831
|
+
);
|
|
11832
|
+
if (res.ok || res.status === 404) return;
|
|
11833
|
+
throw new Error(
|
|
11834
|
+
`vercel connector: delete drain failed (${res.status} ${res.statusText}${await describeError(res)})`
|
|
11835
|
+
);
|
|
11836
|
+
}
|
|
11837
|
+
async function testVercelDrainDelivery(config, credentials, fetchImpl = fetch) {
|
|
11838
|
+
const res = await junctionFetch(
|
|
11839
|
+
`${apiBase(config)}/v1/drains/test${teamQuery(config)}`,
|
|
11840
|
+
{
|
|
11841
|
+
method: "POST",
|
|
11842
|
+
headers: { "Content-Type": "application/json", ...bearerAuthHeader(credentials.token) },
|
|
11843
|
+
body: JSON.stringify({ schemas: TRACE_SCHEMAS, delivery: drainDelivery(config, credentials.otelToken) })
|
|
11844
|
+
},
|
|
11845
|
+
{ provider: "vercel", accountKey: config.teamId, fetchImpl }
|
|
11846
|
+
);
|
|
11847
|
+
if (res.status === 401 || res.status === 403) {
|
|
11848
|
+
return { status: "failure", error: `vercel rejected the API token (HTTP ${res.status})` };
|
|
11849
|
+
}
|
|
11850
|
+
if (!res.ok) {
|
|
11851
|
+
return {
|
|
11852
|
+
status: "failure",
|
|
11853
|
+
error: `vercel drain validation failed (${res.status} ${res.statusText}${await describeError(res)})`
|
|
11854
|
+
};
|
|
11855
|
+
}
|
|
11856
|
+
const payload = await res.json().catch(() => null);
|
|
11857
|
+
return {
|
|
11858
|
+
...payload?.status ? { status: payload.status } : {},
|
|
11859
|
+
...payload?.error ? { error: payload.error } : {},
|
|
11860
|
+
...payload?.endpoint ? { endpoint: payload.endpoint } : {}
|
|
11861
|
+
};
|
|
11862
|
+
}
|
|
11863
|
+
|
|
11427
11864
|
// src/connectors/registry.ts
|
|
11428
11865
|
var CLOUDFLARE_API_BASE_URL = "https://api.cloudflare.com/client/v4";
|
|
11429
11866
|
async function authProbe(input) {
|
|
@@ -11575,8 +12012,96 @@ var PROVIDER_DISPATCH = {
|
|
|
11575
12012
|
}
|
|
11576
12013
|
}
|
|
11577
12014
|
};
|
|
11578
|
-
function
|
|
11579
|
-
return
|
|
12015
|
+
function vercelCredsFrom(credentials) {
|
|
12016
|
+
return { token: String(credentials.token ?? ""), otelToken: String(credentials.otelToken ?? "") };
|
|
12017
|
+
}
|
|
12018
|
+
function vercelConfigFromOptions(options) {
|
|
12019
|
+
const raw = options.projectIds;
|
|
12020
|
+
let projectIds;
|
|
12021
|
+
if (Array.isArray(raw)) projectIds = raw.filter((p) => typeof p === "string");
|
|
12022
|
+
else if (typeof raw === "string" && raw.trim().length > 0) {
|
|
12023
|
+
projectIds = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
12024
|
+
}
|
|
12025
|
+
return {
|
|
12026
|
+
teamId: String(options.teamId ?? ""),
|
|
12027
|
+
endpoint: String(options.endpoint ?? ""),
|
|
12028
|
+
...projectIds && projectIds.length > 0 ? { projectIds } : {},
|
|
12029
|
+
...typeof options.drainId === "string" ? { drainId: options.drainId } : {},
|
|
12030
|
+
...typeof options.drainName === "string" ? { drainName: options.drainName } : {},
|
|
12031
|
+
...typeof options.apiBaseUrl === "string" ? { apiBaseUrl: options.apiBaseUrl } : {},
|
|
12032
|
+
...typeof options.secret === "string" ? { secret: options.secret } : {}
|
|
12033
|
+
};
|
|
12034
|
+
}
|
|
12035
|
+
var PUSH_PROVIDER_DISPATCH = {
|
|
12036
|
+
vercel: {
|
|
12037
|
+
provider: "vercel",
|
|
12038
|
+
// The Vercel access token is the "primary" secret a single `--token`
|
|
12039
|
+
// populates; `otelToken` (the daemon's OTLP bearer) is the second field.
|
|
12040
|
+
primaryCredentialKey: "token",
|
|
12041
|
+
requiredCredentialFields: ["token", "otelToken"],
|
|
12042
|
+
// teamId scopes every Drains call; endpoint is where the drain delivers.
|
|
12043
|
+
// projectIds is optional (absent → the drain covers the whole team).
|
|
12044
|
+
requiredOptionFields: ["teamId", "endpoint"],
|
|
12045
|
+
// POST /v1/drains/test — authenticates the token and pings the endpoint
|
|
12046
|
+
// with a sample event, so `success` means the credential is live *and* the
|
|
12047
|
+
// daemon's OTLP endpoint is reachable and accepted the drain's bearer.
|
|
12048
|
+
async validate({ credentials, options, fetchImpl }) {
|
|
12049
|
+
const result = await testVercelDrainDelivery(
|
|
12050
|
+
vercelConfigFromOptions(options),
|
|
12051
|
+
vercelCredsFrom(credentials),
|
|
12052
|
+
fetchImpl
|
|
12053
|
+
);
|
|
12054
|
+
if (result.status === "success") return { ok: true };
|
|
12055
|
+
return {
|
|
12056
|
+
ok: false,
|
|
12057
|
+
reason: result.error ?? `vercel drain delivery test returned "${result.status ?? "no status"}"`
|
|
12058
|
+
};
|
|
12059
|
+
},
|
|
12060
|
+
// POST /v1/drains — creates the trace drain, returns its id to store in
|
|
12061
|
+
// `options.drainId`. A created-but-not-enabled drain is surfaced as a note,
|
|
12062
|
+
// not a failure (the entry still points at a real drain).
|
|
12063
|
+
async provision({ credentials, options, fetchImpl }) {
|
|
12064
|
+
try {
|
|
12065
|
+
const created = await createVercelDrain(
|
|
12066
|
+
vercelConfigFromOptions(options),
|
|
12067
|
+
vercelCredsFrom(credentials),
|
|
12068
|
+
fetchImpl
|
|
12069
|
+
);
|
|
12070
|
+
const note = created.status && created.status !== "enabled" ? `the drain was created but its status is "${created.status}"${created.disabledReason ? ` (${created.disabledReason})` : ""} \u2014 check the Vercel dashboard` : void 0;
|
|
12071
|
+
return { ok: true, options: { drainId: created.id }, ...note ? { note } : {} };
|
|
12072
|
+
} catch (err) {
|
|
12073
|
+
return { ok: false, reason: err.message };
|
|
12074
|
+
}
|
|
12075
|
+
},
|
|
12076
|
+
// DELETE /v1/drains/{id} — idempotent (deleteVercelDrain treats 404 as
|
|
12077
|
+
// success). No recorded drainId → nothing to delete, still a success.
|
|
12078
|
+
async deprovision({ credentials, options, fetchImpl }) {
|
|
12079
|
+
const drainId = typeof options.drainId === "string" ? options.drainId : "";
|
|
12080
|
+
if (!drainId) {
|
|
12081
|
+
return { ok: true, note: "no drain id was recorded \u2014 nothing to delete on the Vercel side" };
|
|
12082
|
+
}
|
|
12083
|
+
try {
|
|
12084
|
+
await deleteVercelDrain(
|
|
12085
|
+
vercelConfigFromOptions(options),
|
|
12086
|
+
drainId,
|
|
12087
|
+
vercelCredsFrom(credentials),
|
|
12088
|
+
fetchImpl
|
|
12089
|
+
);
|
|
12090
|
+
return { ok: true };
|
|
12091
|
+
} catch (err) {
|
|
12092
|
+
return { ok: false, reason: err.message };
|
|
12093
|
+
}
|
|
12094
|
+
}
|
|
12095
|
+
}
|
|
12096
|
+
};
|
|
12097
|
+
function isPushProvider(provider) {
|
|
12098
|
+
return provider in PUSH_PROVIDER_DISPATCH;
|
|
12099
|
+
}
|
|
12100
|
+
function getProviderFieldSchema(provider) {
|
|
12101
|
+
return PROVIDER_DISPATCH[provider] ?? PUSH_PROVIDER_DISPATCH[provider];
|
|
12102
|
+
}
|
|
12103
|
+
function knownProviderNames() {
|
|
12104
|
+
return [...Object.keys(PROVIDER_DISPATCH), ...Object.keys(PUSH_PROVIDER_DISPATCH)].sort();
|
|
11580
12105
|
}
|
|
11581
12106
|
function resolveEntryCredentials(dispatch, entry2, env) {
|
|
11582
12107
|
let credentials;
|
|
@@ -11598,7 +12123,7 @@ function resolveEntryCredentials(dispatch, entry2, env) {
|
|
|
11598
12123
|
return { ok: true, credentials };
|
|
11599
12124
|
}
|
|
11600
12125
|
async function validateConnectorEntry(entry2, env = process.env, fetchImpl) {
|
|
11601
|
-
const dispatch = PROVIDER_DISPATCH[entry2.provider];
|
|
12126
|
+
const dispatch = PROVIDER_DISPATCH[entry2.provider] ?? PUSH_PROVIDER_DISPATCH[entry2.provider];
|
|
11602
12127
|
if (!dispatch) {
|
|
11603
12128
|
return { status: "unknown-provider", reason: `unknown provider "${entry2.provider}"` };
|
|
11604
12129
|
}
|
|
@@ -11622,6 +12147,54 @@ async function validateConnectorEntry(entry2, env = process.env, fetchImpl) {
|
|
|
11622
12147
|
});
|
|
11623
12148
|
return result.ok ? { status: "ok" } : { status: "rejected", reason: result.reason };
|
|
11624
12149
|
}
|
|
12150
|
+
function resolvePushEntry(entry2, env) {
|
|
12151
|
+
const dispatch = PUSH_PROVIDER_DISPATCH[entry2.provider];
|
|
12152
|
+
if (!dispatch) {
|
|
12153
|
+
return PROVIDER_DISPATCH[entry2.provider] ? {
|
|
12154
|
+
ok: false,
|
|
12155
|
+
outcome: {
|
|
12156
|
+
status: "not-push",
|
|
12157
|
+
reason: `provider "${entry2.provider}" is polled, not provisioned \u2014 there is no drain to manage`
|
|
12158
|
+
}
|
|
12159
|
+
} : { ok: false, outcome: { status: "unknown-provider", reason: `unknown provider "${entry2.provider}"` } };
|
|
12160
|
+
}
|
|
12161
|
+
const creds = resolveEntryCredentials(dispatch, entry2, env);
|
|
12162
|
+
if (!creds.ok) {
|
|
12163
|
+
const status2 = creds.kind === "unset-env" ? "unset-env" : creds.kind === "missing-field" ? "missing-field" : "failed";
|
|
12164
|
+
return { ok: false, outcome: { status: status2, reason: creds.reason } };
|
|
12165
|
+
}
|
|
12166
|
+
const options = entry2.options ?? {};
|
|
12167
|
+
const missingOpts = dispatch.requiredOptionFields.filter((k) => !(k in options));
|
|
12168
|
+
if (missingOpts.length > 0) {
|
|
12169
|
+
return {
|
|
12170
|
+
ok: false,
|
|
12171
|
+
outcome: { status: "missing-field", reason: `options missing required field(s): ${missingOpts.join(", ")}` }
|
|
12172
|
+
};
|
|
12173
|
+
}
|
|
12174
|
+
return { ok: true, dispatch, credentials: creds.credentials, options };
|
|
12175
|
+
}
|
|
12176
|
+
async function provisionConnector(entry2, env = process.env, fetchImpl) {
|
|
12177
|
+
const resolved = resolvePushEntry(entry2, env);
|
|
12178
|
+
if (!resolved.ok) return resolved.outcome;
|
|
12179
|
+
const result = await resolved.dispatch.provision({
|
|
12180
|
+
credentials: resolved.credentials,
|
|
12181
|
+
options: resolved.options,
|
|
12182
|
+
...fetchImpl ? { fetchImpl } : {}
|
|
12183
|
+
});
|
|
12184
|
+
if (!result.ok) return { status: "failed", reason: result.reason };
|
|
12185
|
+
return { status: "ok", ...result.options ? { options: result.options } : {}, ...result.note ? { note: result.note } : {} };
|
|
12186
|
+
}
|
|
12187
|
+
async function deprovisionConnector(entry2, env = process.env, fetchImpl) {
|
|
12188
|
+
const resolved = resolvePushEntry(entry2, env);
|
|
12189
|
+
if (!resolved.ok) return resolved.outcome;
|
|
12190
|
+
const result = await resolved.dispatch.deprovision({
|
|
12191
|
+
credentials: resolved.credentials,
|
|
12192
|
+
options: resolved.options,
|
|
12193
|
+
...fetchImpl ? { fetchImpl } : {}
|
|
12194
|
+
});
|
|
12195
|
+
if (!result.ok) return { status: "failed", reason: result.reason };
|
|
12196
|
+
return { status: "ok", ...result.note ? { note: result.note } : {} };
|
|
12197
|
+
}
|
|
11625
12198
|
|
|
11626
12199
|
// src/daemon.ts
|
|
11627
12200
|
init_auth();
|
|
@@ -11629,28 +12202,28 @@ init_auth();
|
|
|
11629
12202
|
// src/unrouted.ts
|
|
11630
12203
|
init_cjs_shims();
|
|
11631
12204
|
var import_node_fs32 = require("fs");
|
|
11632
|
-
var
|
|
12205
|
+
var import_node_path52 = __toESM(require("path"), 1);
|
|
11633
12206
|
|
|
11634
12207
|
// src/daemon.ts
|
|
11635
|
-
var
|
|
12208
|
+
var import_types48 = require("@neat.is/types");
|
|
11636
12209
|
function daemonJsonPath(scanPath) {
|
|
11637
|
-
return
|
|
12210
|
+
return import_node_path53.default.join(scanPath, "neat-out", "daemon.json");
|
|
11638
12211
|
}
|
|
11639
12212
|
function daemonsDiscoveryDir(home) {
|
|
11640
12213
|
const base = home && home.length > 0 ? home : neatHomeFromEnv();
|
|
11641
|
-
return
|
|
12214
|
+
return import_node_path53.default.join(base, "daemons");
|
|
11642
12215
|
}
|
|
11643
12216
|
function daemonDiscoveryPath(project, home) {
|
|
11644
|
-
return
|
|
12217
|
+
return import_node_path53.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
|
|
11645
12218
|
}
|
|
11646
12219
|
function sanitizeDiscoveryName(project) {
|
|
11647
12220
|
return project.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
11648
12221
|
}
|
|
11649
12222
|
function neatHomeFromEnv() {
|
|
11650
12223
|
const env = process.env.NEAT_HOME;
|
|
11651
|
-
if (env && env.length > 0) return
|
|
12224
|
+
if (env && env.length > 0) return import_node_path53.default.resolve(env);
|
|
11652
12225
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
11653
|
-
return
|
|
12226
|
+
return import_node_path53.default.join(home, ".neat");
|
|
11654
12227
|
}
|
|
11655
12228
|
async function readDaemonRecord(scanPath) {
|
|
11656
12229
|
try {
|
|
@@ -11721,7 +12294,7 @@ init_otel_grpc();
|
|
|
11721
12294
|
// src/search.ts
|
|
11722
12295
|
init_cjs_shims();
|
|
11723
12296
|
var import_node_fs34 = require("fs");
|
|
11724
|
-
var
|
|
12297
|
+
var import_node_path54 = __toESM(require("path"), 1);
|
|
11725
12298
|
var import_node_crypto4 = require("crypto");
|
|
11726
12299
|
var DEFAULT_LIMIT = 10;
|
|
11727
12300
|
var NOMIC_DIM = 768;
|
|
@@ -11884,7 +12457,7 @@ async function readCache(cachePath) {
|
|
|
11884
12457
|
}
|
|
11885
12458
|
}
|
|
11886
12459
|
async function writeCache(cachePath, cache) {
|
|
11887
|
-
await import_node_fs34.promises.mkdir(
|
|
12460
|
+
await import_node_fs34.promises.mkdir(import_node_path54.default.dirname(cachePath), { recursive: true });
|
|
11888
12461
|
await import_node_fs34.promises.writeFile(cachePath, JSON.stringify(cache));
|
|
11889
12462
|
}
|
|
11890
12463
|
var VectorIndex = class {
|
|
@@ -12042,8 +12615,8 @@ var ALL_PHASES = [
|
|
|
12042
12615
|
];
|
|
12043
12616
|
function classifyChange(relPath) {
|
|
12044
12617
|
const phases = /* @__PURE__ */ new Set();
|
|
12045
|
-
const base =
|
|
12046
|
-
const segments = relPath.split(
|
|
12618
|
+
const base = import_node_path55.default.basename(relPath).toLowerCase();
|
|
12619
|
+
const segments = relPath.split(import_node_path55.default.sep).map((s) => s.toLowerCase());
|
|
12047
12620
|
if (base === "package.json" || base === "requirements.txt" || base === "pyproject.toml" || base === "setup.py") {
|
|
12048
12621
|
phases.add("services");
|
|
12049
12622
|
phases.add("aliases");
|
|
@@ -12171,9 +12744,9 @@ function countWatchableDirs(scanPath, limit) {
|
|
|
12171
12744
|
for (const e of entries) {
|
|
12172
12745
|
if (count >= limit) return;
|
|
12173
12746
|
if (!e.isDirectory()) continue;
|
|
12174
|
-
if (IGNORED_WATCH_PATHS.some((re) => re.test(
|
|
12747
|
+
if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path55.default.join(dir, e.name) + import_node_path55.default.sep))) continue;
|
|
12175
12748
|
count++;
|
|
12176
|
-
if (depth < 2) visit(
|
|
12749
|
+
if (depth < 2) visit(import_node_path55.default.join(dir, e.name), depth + 1);
|
|
12177
12750
|
}
|
|
12178
12751
|
};
|
|
12179
12752
|
visit(scanPath, 0);
|
|
@@ -12191,8 +12764,8 @@ async function startWatch(graph, opts) {
|
|
|
12191
12764
|
const projectName = opts.project ?? DEFAULT_PROJECT;
|
|
12192
12765
|
await loadGraphFromDisk(graph, opts.outPath);
|
|
12193
12766
|
const detachEventBus = attachGraphToEventBus(graph, { project: projectName });
|
|
12194
|
-
const policyFilePath =
|
|
12195
|
-
const policyViolationsPath =
|
|
12767
|
+
const policyFilePath = import_node_path55.default.join(opts.scanPath, "policy.json");
|
|
12768
|
+
const policyViolationsPath = import_node_path55.default.join(import_node_path55.default.dirname(opts.outPath), "policy-violations.ndjson");
|
|
12196
12769
|
let policies = [];
|
|
12197
12770
|
try {
|
|
12198
12771
|
policies = await loadPolicyFile(policyFilePath);
|
|
@@ -12243,7 +12816,7 @@ async function startWatch(graph, opts) {
|
|
|
12243
12816
|
assertBindAuthority(host, auth.authToken);
|
|
12244
12817
|
const port = opts.port ?? 8080;
|
|
12245
12818
|
const otelPort = opts.otelPort ?? 4318;
|
|
12246
|
-
const cachePath = opts.embeddingsCachePath ??
|
|
12819
|
+
const cachePath = opts.embeddingsCachePath ?? import_node_path55.default.join(import_node_path55.default.dirname(opts.outPath), "embeddings.json");
|
|
12247
12820
|
let searchIndex;
|
|
12248
12821
|
try {
|
|
12249
12822
|
searchIndex = await buildSearchIndex(graph, { cachePath });
|
|
@@ -12261,7 +12834,7 @@ async function startWatch(graph, opts) {
|
|
|
12261
12834
|
// Paths are derived from the explicit options the watch caller passes
|
|
12262
12835
|
// — pathsForProject is only used to fill in the embeddings/snapshot
|
|
12263
12836
|
// fields so the registry shape is complete.
|
|
12264
|
-
...pathsForProject(projectName,
|
|
12837
|
+
...pathsForProject(projectName, import_node_path55.default.dirname(opts.outPath)),
|
|
12265
12838
|
snapshotPath: opts.outPath,
|
|
12266
12839
|
errorsPath: opts.errorsPath,
|
|
12267
12840
|
staleEventsPath: opts.staleEventsPath
|
|
@@ -12379,9 +12952,9 @@ async function startWatch(graph, opts) {
|
|
|
12379
12952
|
};
|
|
12380
12953
|
const onPath = (absPath) => {
|
|
12381
12954
|
if (shouldIgnore(absPath)) return;
|
|
12382
|
-
const rel =
|
|
12955
|
+
const rel = import_node_path55.default.relative(opts.scanPath, absPath);
|
|
12383
12956
|
if (!rel || rel.startsWith("..")) return;
|
|
12384
|
-
pendingPaths.add(rel.split(
|
|
12957
|
+
pendingPaths.add(rel.split(import_node_path55.default.sep).join("/"));
|
|
12385
12958
|
const phases = classifyChange(rel);
|
|
12386
12959
|
if (phases.size === 0) {
|
|
12387
12960
|
for (const p of ALL_PHASES) pending.add(p);
|
|
@@ -12438,7 +13011,7 @@ async function startWatch(graph, opts) {
|
|
|
12438
13011
|
// src/deploy/detect.ts
|
|
12439
13012
|
init_cjs_shims();
|
|
12440
13013
|
var import_node_fs36 = require("fs");
|
|
12441
|
-
var
|
|
13014
|
+
var import_node_path56 = __toESM(require("path"), 1);
|
|
12442
13015
|
var import_node_child_process2 = require("child_process");
|
|
12443
13016
|
var import_node_crypto5 = require("crypto");
|
|
12444
13017
|
function generateToken() {
|
|
@@ -12538,7 +13111,7 @@ async function runDeploy(opts = {}) {
|
|
|
12538
13111
|
const token = generateToken();
|
|
12539
13112
|
switch (substrate) {
|
|
12540
13113
|
case "docker-compose": {
|
|
12541
|
-
const artifactPath =
|
|
13114
|
+
const artifactPath = import_node_path56.default.join(cwd, "docker-compose.neat.yml");
|
|
12542
13115
|
const contents = emitDockerCompose(cwd);
|
|
12543
13116
|
await import_node_fs36.promises.writeFile(artifactPath, contents, "utf8");
|
|
12544
13117
|
return {
|
|
@@ -12546,11 +13119,11 @@ async function runDeploy(opts = {}) {
|
|
|
12546
13119
|
artifactPath,
|
|
12547
13120
|
token,
|
|
12548
13121
|
contents,
|
|
12549
|
-
startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${
|
|
13122
|
+
startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${import_node_path56.default.basename(artifactPath)} up -d`
|
|
12550
13123
|
};
|
|
12551
13124
|
}
|
|
12552
13125
|
case "systemd": {
|
|
12553
|
-
const artifactPath =
|
|
13126
|
+
const artifactPath = import_node_path56.default.join(cwd, "neat.service");
|
|
12554
13127
|
const contents = emitSystemdUnit(cwd);
|
|
12555
13128
|
await import_node_fs36.promises.writeFile(artifactPath, contents, "utf8");
|
|
12556
13129
|
return {
|
|
@@ -12586,7 +13159,7 @@ init_cjs_shims();
|
|
|
12586
13159
|
// src/installers/javascript.ts
|
|
12587
13160
|
init_cjs_shims();
|
|
12588
13161
|
var import_node_fs37 = require("fs");
|
|
12589
|
-
var
|
|
13162
|
+
var import_node_path57 = __toESM(require("path"), 1);
|
|
12590
13163
|
var import_semver2 = __toESM(require("semver"), 1);
|
|
12591
13164
|
|
|
12592
13165
|
// src/installers/templates.ts
|
|
@@ -13217,11 +13790,11 @@ var OTEL_ENV = {
|
|
|
13217
13790
|
value: "http://localhost:4318/projects/<project>/v1/traces"
|
|
13218
13791
|
};
|
|
13219
13792
|
function serviceNodeName(pkg, serviceDir) {
|
|
13220
|
-
return pkg.name ??
|
|
13793
|
+
return pkg.name ?? import_node_path57.default.basename(serviceDir);
|
|
13221
13794
|
}
|
|
13222
13795
|
function projectToken(pkg, serviceDir, project) {
|
|
13223
13796
|
if (project && project.length > 0) return project;
|
|
13224
|
-
return pkg.name ??
|
|
13797
|
+
return pkg.name ?? import_node_path57.default.basename(serviceDir);
|
|
13225
13798
|
}
|
|
13226
13799
|
async function readJsonFile(p) {
|
|
13227
13800
|
try {
|
|
@@ -13234,16 +13807,16 @@ async function readJsonFile(p) {
|
|
|
13234
13807
|
async function detectRuntimeKind(pkgRoot, pkg) {
|
|
13235
13808
|
const deps = allDeps(pkg);
|
|
13236
13809
|
if ("react-native" in deps || "expo" in deps) return "react-native";
|
|
13237
|
-
const appJson = await readJsonFile(
|
|
13810
|
+
const appJson = await readJsonFile(import_node_path57.default.join(pkgRoot, "app.json"));
|
|
13238
13811
|
if (appJson && typeof appJson === "object" && "expo" in appJson) {
|
|
13239
13812
|
return "react-native";
|
|
13240
13813
|
}
|
|
13241
|
-
if (await exists3(
|
|
13814
|
+
if (await exists3(import_node_path57.default.join(pkgRoot, "vite.config.js")) || await exists3(import_node_path57.default.join(pkgRoot, "vite.config.ts")) || await exists3(import_node_path57.default.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
|
|
13242
13815
|
return "browser-bundle";
|
|
13243
13816
|
}
|
|
13244
|
-
if (await exists3(
|
|
13245
|
-
if (await exists3(
|
|
13246
|
-
if (await exists3(
|
|
13817
|
+
if (await exists3(import_node_path57.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
|
|
13818
|
+
if (await exists3(import_node_path57.default.join(pkgRoot, "bun.lockb"))) return "bun";
|
|
13819
|
+
if (await exists3(import_node_path57.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path57.default.join(pkgRoot, "deno.lock"))) {
|
|
13247
13820
|
return "deno";
|
|
13248
13821
|
}
|
|
13249
13822
|
const engines = pkg.engines ?? {};
|
|
@@ -13252,7 +13825,7 @@ async function detectRuntimeKind(pkgRoot, pkg) {
|
|
|
13252
13825
|
}
|
|
13253
13826
|
async function readPackageJson2(serviceDir) {
|
|
13254
13827
|
try {
|
|
13255
|
-
const raw = await import_node_fs37.promises.readFile(
|
|
13828
|
+
const raw = await import_node_fs37.promises.readFile(import_node_path57.default.join(serviceDir, "package.json"), "utf8");
|
|
13256
13829
|
return JSON.parse(raw);
|
|
13257
13830
|
} catch {
|
|
13258
13831
|
return null;
|
|
@@ -13296,7 +13869,7 @@ function needsVersionUpgrade(installed, expected) {
|
|
|
13296
13869
|
var NEXT_CONFIG_CANDIDATES = ["next.config.js", "next.config.ts", "next.config.mjs"];
|
|
13297
13870
|
async function findNextConfig(serviceDir) {
|
|
13298
13871
|
for (const name of NEXT_CONFIG_CANDIDATES) {
|
|
13299
|
-
const candidate =
|
|
13872
|
+
const candidate = import_node_path57.default.join(serviceDir, name);
|
|
13300
13873
|
if (await exists3(candidate)) return candidate;
|
|
13301
13874
|
}
|
|
13302
13875
|
return null;
|
|
@@ -13365,7 +13938,7 @@ function hasRemixDependency(pkg) {
|
|
|
13365
13938
|
}
|
|
13366
13939
|
async function findRemixEntry(serviceDir) {
|
|
13367
13940
|
for (const rel of REMIX_ENTRY_CANDIDATES) {
|
|
13368
|
-
const candidate =
|
|
13941
|
+
const candidate = import_node_path57.default.join(serviceDir, rel);
|
|
13369
13942
|
if (await exists3(candidate)) return candidate;
|
|
13370
13943
|
}
|
|
13371
13944
|
return null;
|
|
@@ -13377,14 +13950,14 @@ function hasSvelteKitDependency(pkg) {
|
|
|
13377
13950
|
}
|
|
13378
13951
|
async function findSvelteKitHooks(serviceDir) {
|
|
13379
13952
|
for (const rel of SVELTEKIT_HOOKS_CANDIDATES) {
|
|
13380
|
-
const candidate =
|
|
13953
|
+
const candidate = import_node_path57.default.join(serviceDir, rel);
|
|
13381
13954
|
if (await exists3(candidate)) return candidate;
|
|
13382
13955
|
}
|
|
13383
13956
|
return null;
|
|
13384
13957
|
}
|
|
13385
13958
|
async function findSvelteKitConfig(serviceDir) {
|
|
13386
13959
|
for (const rel of SVELTEKIT_CONFIG_CANDIDATES) {
|
|
13387
|
-
const candidate =
|
|
13960
|
+
const candidate = import_node_path57.default.join(serviceDir, rel);
|
|
13388
13961
|
if (await exists3(candidate)) return candidate;
|
|
13389
13962
|
}
|
|
13390
13963
|
return null;
|
|
@@ -13395,7 +13968,7 @@ function hasNuxtDependency(pkg) {
|
|
|
13395
13968
|
}
|
|
13396
13969
|
async function findNuxtConfig(serviceDir) {
|
|
13397
13970
|
for (const name of NUXT_CONFIG_CANDIDATES) {
|
|
13398
|
-
const candidate =
|
|
13971
|
+
const candidate = import_node_path57.default.join(serviceDir, name);
|
|
13399
13972
|
if (await exists3(candidate)) return candidate;
|
|
13400
13973
|
}
|
|
13401
13974
|
return null;
|
|
@@ -13406,7 +13979,7 @@ function hasAstroDependency(pkg) {
|
|
|
13406
13979
|
}
|
|
13407
13980
|
async function findAstroConfig(serviceDir) {
|
|
13408
13981
|
for (const name of ASTRO_CONFIG_CANDIDATES) {
|
|
13409
|
-
const candidate =
|
|
13982
|
+
const candidate = import_node_path57.default.join(serviceDir, name);
|
|
13410
13983
|
if (await exists3(candidate)) return candidate;
|
|
13411
13984
|
}
|
|
13412
13985
|
return null;
|
|
@@ -13420,7 +13993,7 @@ function parseNextMajor(range) {
|
|
|
13420
13993
|
return Number.isFinite(n) ? n : null;
|
|
13421
13994
|
}
|
|
13422
13995
|
async function isTypeScriptProject(serviceDir) {
|
|
13423
|
-
return exists3(
|
|
13996
|
+
return exists3(import_node_path57.default.join(serviceDir, "tsconfig.json"));
|
|
13424
13997
|
}
|
|
13425
13998
|
var INDEX_EXTENSIONS = [".ts", ".tsx", ".js", ".mjs", ".cjs"];
|
|
13426
13999
|
var INDEX_CANDIDATES = INDEX_EXTENSIONS.map((ext) => `index${ext}`);
|
|
@@ -13469,7 +14042,7 @@ function entryFromScript(script) {
|
|
|
13469
14042
|
}
|
|
13470
14043
|
async function resolveEntry(serviceDir, pkg) {
|
|
13471
14044
|
if (typeof pkg.main === "string" && pkg.main.length > 0) {
|
|
13472
|
-
const candidate =
|
|
14045
|
+
const candidate = import_node_path57.default.resolve(serviceDir, pkg.main);
|
|
13473
14046
|
if (await exists3(candidate)) return candidate;
|
|
13474
14047
|
}
|
|
13475
14048
|
if (pkg.bin) {
|
|
@@ -13483,40 +14056,40 @@ async function resolveEntry(serviceDir, pkg) {
|
|
|
13483
14056
|
if (typeof first === "string") binEntry = first;
|
|
13484
14057
|
}
|
|
13485
14058
|
if (binEntry) {
|
|
13486
|
-
const candidate =
|
|
14059
|
+
const candidate = import_node_path57.default.resolve(serviceDir, binEntry);
|
|
13487
14060
|
if (await exists3(candidate)) return candidate;
|
|
13488
14061
|
}
|
|
13489
14062
|
}
|
|
13490
14063
|
const startEntry = entryFromScript(pkg.scripts?.start);
|
|
13491
14064
|
if (startEntry) {
|
|
13492
|
-
const candidate =
|
|
14065
|
+
const candidate = import_node_path57.default.resolve(serviceDir, startEntry);
|
|
13493
14066
|
if (await exists3(candidate)) return candidate;
|
|
13494
14067
|
}
|
|
13495
14068
|
const devEntry = entryFromScript(pkg.scripts?.dev);
|
|
13496
14069
|
if (devEntry) {
|
|
13497
|
-
const candidate =
|
|
14070
|
+
const candidate = import_node_path57.default.resolve(serviceDir, devEntry);
|
|
13498
14071
|
if (await exists3(candidate)) return candidate;
|
|
13499
14072
|
}
|
|
13500
14073
|
for (const rel of SRC_INDEX_CANDIDATES) {
|
|
13501
|
-
const candidate =
|
|
14074
|
+
const candidate = import_node_path57.default.join(serviceDir, rel);
|
|
13502
14075
|
if (await exists3(candidate)) return candidate;
|
|
13503
14076
|
}
|
|
13504
14077
|
for (const rel of SRC_NAMED_CANDIDATES) {
|
|
13505
|
-
const candidate =
|
|
14078
|
+
const candidate = import_node_path57.default.join(serviceDir, rel);
|
|
13506
14079
|
if (await exists3(candidate)) return candidate;
|
|
13507
14080
|
}
|
|
13508
14081
|
for (const rel of ROOT_NAMED_CANDIDATES) {
|
|
13509
|
-
const candidate =
|
|
14082
|
+
const candidate = import_node_path57.default.join(serviceDir, rel);
|
|
13510
14083
|
if (await exists3(candidate)) return candidate;
|
|
13511
14084
|
}
|
|
13512
14085
|
for (const name of INDEX_CANDIDATES) {
|
|
13513
|
-
const candidate =
|
|
14086
|
+
const candidate = import_node_path57.default.join(serviceDir, name);
|
|
13514
14087
|
if (await exists3(candidate)) return candidate;
|
|
13515
14088
|
}
|
|
13516
14089
|
return null;
|
|
13517
14090
|
}
|
|
13518
14091
|
function dispatchEntry(entryFile, pkg) {
|
|
13519
|
-
const ext =
|
|
14092
|
+
const ext = import_node_path57.default.extname(entryFile).toLowerCase();
|
|
13520
14093
|
if (ext === ".ts" || ext === ".tsx") return "ts";
|
|
13521
14094
|
if (ext === ".mjs") return "esm";
|
|
13522
14095
|
if (ext === ".cjs") return "cjs";
|
|
@@ -13533,9 +14106,9 @@ function otelInitContents(flavor) {
|
|
|
13533
14106
|
return OTEL_INIT_CJS;
|
|
13534
14107
|
}
|
|
13535
14108
|
function injectionLine(flavor, entryFile, otelInitFile) {
|
|
13536
|
-
let rel =
|
|
14109
|
+
let rel = import_node_path57.default.relative(import_node_path57.default.dirname(entryFile), otelInitFile);
|
|
13537
14110
|
if (!rel.startsWith(".")) rel = `./${rel}`;
|
|
13538
|
-
rel = rel.split(
|
|
14111
|
+
rel = rel.split(import_node_path57.default.sep).join("/");
|
|
13539
14112
|
if (flavor === "cjs") return `require('${rel}')`;
|
|
13540
14113
|
if (flavor === "esm") return `import '${rel}'`;
|
|
13541
14114
|
const tsRel = rel.replace(/\.ts$/, "");
|
|
@@ -13548,27 +14121,27 @@ function lineIsOtelInjection(line) {
|
|
|
13548
14121
|
}
|
|
13549
14122
|
async function detectsSrcLayout(serviceDir) {
|
|
13550
14123
|
const [hasSrcApp, hasSrcPages, hasRootApp, hasRootPages] = await Promise.all([
|
|
13551
|
-
exists3(
|
|
13552
|
-
exists3(
|
|
13553
|
-
exists3(
|
|
13554
|
-
exists3(
|
|
14124
|
+
exists3(import_node_path57.default.join(serviceDir, "src", "app")),
|
|
14125
|
+
exists3(import_node_path57.default.join(serviceDir, "src", "pages")),
|
|
14126
|
+
exists3(import_node_path57.default.join(serviceDir, "app")),
|
|
14127
|
+
exists3(import_node_path57.default.join(serviceDir, "pages"))
|
|
13555
14128
|
]);
|
|
13556
14129
|
return (hasSrcApp || hasSrcPages) && !hasRootApp && !hasRootPages;
|
|
13557
14130
|
}
|
|
13558
14131
|
async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project) {
|
|
13559
14132
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
13560
14133
|
const srcLayout = await detectsSrcLayout(serviceDir);
|
|
13561
|
-
const baseDir = srcLayout ?
|
|
13562
|
-
const instrumentationFile =
|
|
13563
|
-
const instrumentationNodeFile =
|
|
14134
|
+
const baseDir = srcLayout ? import_node_path57.default.join(serviceDir, "src") : serviceDir;
|
|
14135
|
+
const instrumentationFile = import_node_path57.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
|
|
14136
|
+
const instrumentationNodeFile = import_node_path57.default.join(
|
|
13564
14137
|
baseDir,
|
|
13565
14138
|
useTs ? "instrumentation.node.ts" : "instrumentation.node.js"
|
|
13566
14139
|
);
|
|
13567
|
-
const instrumentationEdgeFile =
|
|
14140
|
+
const instrumentationEdgeFile = import_node_path57.default.join(
|
|
13568
14141
|
baseDir,
|
|
13569
14142
|
useTs ? "instrumentation.edge.ts" : "instrumentation.edge.js"
|
|
13570
14143
|
);
|
|
13571
|
-
const envNeatFile =
|
|
14144
|
+
const envNeatFile = import_node_path57.default.join(baseDir, ".env.neat");
|
|
13572
14145
|
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
13573
14146
|
const dependencyEdits = [];
|
|
13574
14147
|
for (const sdk of SDK_PACKAGES) {
|
|
@@ -13693,7 +14266,7 @@ function buildDependencyEdits(pkg, manifestPath) {
|
|
|
13693
14266
|
return edits;
|
|
13694
14267
|
}
|
|
13695
14268
|
async function queueEnvNeat(serviceDir, pkg, project, generatedFiles) {
|
|
13696
|
-
const envNeatFile =
|
|
14269
|
+
const envNeatFile = import_node_path57.default.join(serviceDir, ".env.neat");
|
|
13697
14270
|
if (!await exists3(envNeatFile)) {
|
|
13698
14271
|
generatedFiles.push({
|
|
13699
14272
|
file: envNeatFile,
|
|
@@ -13728,7 +14301,7 @@ function fileImportsOtelHook(raw, specifiers) {
|
|
|
13728
14301
|
}
|
|
13729
14302
|
async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
13730
14303
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
13731
|
-
const otelServerFile =
|
|
14304
|
+
const otelServerFile = import_node_path57.default.join(
|
|
13732
14305
|
serviceDir,
|
|
13733
14306
|
useTs ? "app/otel.server.ts" : "app/otel.server.js"
|
|
13734
14307
|
);
|
|
@@ -13785,11 +14358,11 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
|
13785
14358
|
}
|
|
13786
14359
|
async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project) {
|
|
13787
14360
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
13788
|
-
const otelInitFile =
|
|
14361
|
+
const otelInitFile = import_node_path57.default.join(
|
|
13789
14362
|
serviceDir,
|
|
13790
14363
|
useTs ? "src/otel-init.ts" : "src/otel-init.js"
|
|
13791
14364
|
);
|
|
13792
|
-
const resolvedHooksFile = hooksFile ??
|
|
14365
|
+
const resolvedHooksFile = hooksFile ?? import_node_path57.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
|
|
13793
14366
|
const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
|
|
13794
14367
|
const generatedFiles = [];
|
|
13795
14368
|
const entrypointEdits = [];
|
|
@@ -13851,11 +14424,11 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project)
|
|
|
13851
14424
|
}
|
|
13852
14425
|
async function planNuxt(serviceDir, pkg, manifestPath, project) {
|
|
13853
14426
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
13854
|
-
const otelPluginFile =
|
|
14427
|
+
const otelPluginFile = import_node_path57.default.join(
|
|
13855
14428
|
serviceDir,
|
|
13856
14429
|
useTs ? "server/plugins/otel.ts" : "server/plugins/otel.js"
|
|
13857
14430
|
);
|
|
13858
|
-
const otelInitFile =
|
|
14431
|
+
const otelInitFile = import_node_path57.default.join(
|
|
13859
14432
|
serviceDir,
|
|
13860
14433
|
useTs ? "server/plugins/otel-init.ts" : "server/plugins/otel-init.js"
|
|
13861
14434
|
);
|
|
@@ -13906,19 +14479,19 @@ async function planNuxt(serviceDir, pkg, manifestPath, project) {
|
|
|
13906
14479
|
var ASTRO_MIDDLEWARE_CANDIDATES = ["src/middleware.ts", "src/middleware.js"];
|
|
13907
14480
|
async function findAstroMiddleware(serviceDir) {
|
|
13908
14481
|
for (const rel of ASTRO_MIDDLEWARE_CANDIDATES) {
|
|
13909
|
-
const candidate =
|
|
14482
|
+
const candidate = import_node_path57.default.join(serviceDir, rel);
|
|
13910
14483
|
if (await exists3(candidate)) return candidate;
|
|
13911
14484
|
}
|
|
13912
14485
|
return null;
|
|
13913
14486
|
}
|
|
13914
14487
|
async function planAstro(serviceDir, pkg, manifestPath, project) {
|
|
13915
14488
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
13916
|
-
const otelInitFile =
|
|
14489
|
+
const otelInitFile = import_node_path57.default.join(
|
|
13917
14490
|
serviceDir,
|
|
13918
14491
|
useTs ? "src/otel-init.ts" : "src/otel-init.js"
|
|
13919
14492
|
);
|
|
13920
14493
|
const existingMiddleware = await findAstroMiddleware(serviceDir);
|
|
13921
|
-
const middlewareFile = existingMiddleware ??
|
|
14494
|
+
const middlewareFile = existingMiddleware ?? import_node_path57.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
|
|
13922
14495
|
const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
|
|
13923
14496
|
const generatedFiles = [];
|
|
13924
14497
|
const entrypointEdits = [];
|
|
@@ -14014,7 +14587,7 @@ async function findFrameworkDispatch(serviceDir, pkg, manifestPath, project) {
|
|
|
14014
14587
|
}
|
|
14015
14588
|
async function plan(serviceDir, opts) {
|
|
14016
14589
|
const pkg = await readPackageJson2(serviceDir);
|
|
14017
|
-
const manifestPath =
|
|
14590
|
+
const manifestPath = import_node_path57.default.join(serviceDir, "package.json");
|
|
14018
14591
|
const project = opts?.project;
|
|
14019
14592
|
const empty = {
|
|
14020
14593
|
language: "javascript",
|
|
@@ -14049,8 +14622,8 @@ async function plan(serviceDir, opts) {
|
|
|
14049
14622
|
return { ...empty, libOnly: true };
|
|
14050
14623
|
}
|
|
14051
14624
|
const flavor = dispatchEntry(entryFile, pkg);
|
|
14052
|
-
const otelInitFile =
|
|
14053
|
-
const envNeatFile =
|
|
14625
|
+
const otelInitFile = import_node_path57.default.join(import_node_path57.default.dirname(entryFile), otelInitFilename(flavor));
|
|
14626
|
+
const envNeatFile = import_node_path57.default.join(serviceDir, ".env.neat");
|
|
14054
14627
|
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
14055
14628
|
const dependencyEdits = [];
|
|
14056
14629
|
for (const sdk of SDK_PACKAGES) {
|
|
@@ -14119,13 +14692,13 @@ async function plan(serviceDir, opts) {
|
|
|
14119
14692
|
};
|
|
14120
14693
|
}
|
|
14121
14694
|
function isAllowedWritePath(serviceDir, target) {
|
|
14122
|
-
const rel =
|
|
14695
|
+
const rel = import_node_path57.default.relative(serviceDir, target);
|
|
14123
14696
|
if (rel.startsWith("..")) return false;
|
|
14124
|
-
const base =
|
|
14697
|
+
const base = import_node_path57.default.basename(target);
|
|
14125
14698
|
if (base === "package.json") return true;
|
|
14126
14699
|
if (base === ".env.neat") return true;
|
|
14127
14700
|
if (/^otel-init\.(?:js|cjs|mjs|ts)$/.test(base)) return true;
|
|
14128
|
-
const relPosix = rel.split(
|
|
14701
|
+
const relPosix = rel.split(import_node_path57.default.sep).join("/");
|
|
14129
14702
|
if (/^instrumentation(?:\.(?:node|edge))?\.(?:js|cjs|mjs|ts)$/.test(base)) {
|
|
14130
14703
|
if (relPosix === base) return true;
|
|
14131
14704
|
if (relPosix === `src/${base}`) return true;
|
|
@@ -14142,7 +14715,7 @@ function isAllowedWritePath(serviceDir, target) {
|
|
|
14142
14715
|
return false;
|
|
14143
14716
|
}
|
|
14144
14717
|
async function writeAtomic(file, contents) {
|
|
14145
|
-
await import_node_fs37.promises.mkdir(
|
|
14718
|
+
await import_node_fs37.promises.mkdir(import_node_path57.default.dirname(file), { recursive: true });
|
|
14146
14719
|
const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
14147
14720
|
await import_node_fs37.promises.writeFile(tmp, contents, "utf8");
|
|
14148
14721
|
await import_node_fs37.promises.rename(tmp, file);
|
|
@@ -14326,7 +14899,7 @@ async function rollback(installPlan, originals, createdFiles) {
|
|
|
14326
14899
|
...removed.map((f) => `removed: ${f}`),
|
|
14327
14900
|
""
|
|
14328
14901
|
];
|
|
14329
|
-
const rollbackPath =
|
|
14902
|
+
const rollbackPath = import_node_path57.default.join(installPlan.serviceDir, "neat-rollback.patch");
|
|
14330
14903
|
await import_node_fs37.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
|
|
14331
14904
|
}
|
|
14332
14905
|
function injectInstrumentationHook(raw) {
|
|
@@ -14357,7 +14930,7 @@ var javascriptInstaller = {
|
|
|
14357
14930
|
// src/installers/python.ts
|
|
14358
14931
|
init_cjs_shims();
|
|
14359
14932
|
var import_node_fs38 = require("fs");
|
|
14360
|
-
var
|
|
14933
|
+
var import_node_path58 = __toESM(require("path"), 1);
|
|
14361
14934
|
var SDK_PACKAGES2 = [
|
|
14362
14935
|
{ name: "opentelemetry-distro", version: ">=0.49b0" },
|
|
14363
14936
|
{ name: "opentelemetry-exporter-otlp", version: ">=1.28.0" }
|
|
@@ -14378,7 +14951,7 @@ async function exists4(p) {
|
|
|
14378
14951
|
async function detect2(serviceDir) {
|
|
14379
14952
|
const markers = ["requirements.txt", "pyproject.toml", "setup.py"];
|
|
14380
14953
|
for (const m of markers) {
|
|
14381
|
-
if (await exists4(
|
|
14954
|
+
if (await exists4(import_node_path58.default.join(serviceDir, m))) return true;
|
|
14382
14955
|
}
|
|
14383
14956
|
return false;
|
|
14384
14957
|
}
|
|
@@ -14388,7 +14961,7 @@ function reqPackageName(line) {
|
|
|
14388
14961
|
return head.replace(/[<>=!~].*$/, "").toLowerCase();
|
|
14389
14962
|
}
|
|
14390
14963
|
async function planRequirementsTxtEdits(serviceDir) {
|
|
14391
|
-
const file =
|
|
14964
|
+
const file = import_node_path58.default.join(serviceDir, "requirements.txt");
|
|
14392
14965
|
if (!await exists4(file)) return null;
|
|
14393
14966
|
const raw = await import_node_fs38.promises.readFile(file, "utf8");
|
|
14394
14967
|
const presentNames = new Set(
|
|
@@ -14398,7 +14971,7 @@ async function planRequirementsTxtEdits(serviceDir) {
|
|
|
14398
14971
|
return { manifest: file, missing: [...missing] };
|
|
14399
14972
|
}
|
|
14400
14973
|
async function planProcfileEdits(serviceDir) {
|
|
14401
|
-
const procfile =
|
|
14974
|
+
const procfile = import_node_path58.default.join(serviceDir, "Procfile");
|
|
14402
14975
|
if (!await exists4(procfile)) return [];
|
|
14403
14976
|
const raw = await import_node_fs38.promises.readFile(procfile, "utf8");
|
|
14404
14977
|
const edits = [];
|
|
@@ -14487,7 +15060,7 @@ async function apply2(installPlan) {
|
|
|
14487
15060
|
if (raw === void 0) {
|
|
14488
15061
|
throw new Error(`python installer: cannot read ${file} during apply`);
|
|
14489
15062
|
}
|
|
14490
|
-
const base =
|
|
15063
|
+
const base = import_node_path58.default.basename(file);
|
|
14491
15064
|
if (base === "requirements.txt") {
|
|
14492
15065
|
const edits = installPlan.dependencyEdits.filter((e) => e.file === file);
|
|
14493
15066
|
if (edits.length > 0) {
|
|
@@ -14526,7 +15099,7 @@ async function rollback2(installPlan, originals) {
|
|
|
14526
15099
|
...restored.map((f) => `restored: ${f}`),
|
|
14527
15100
|
""
|
|
14528
15101
|
];
|
|
14529
|
-
const rollbackPath =
|
|
15102
|
+
const rollbackPath = import_node_path58.default.join(installPlan.serviceDir, "neat-rollback.patch");
|
|
14530
15103
|
await import_node_fs38.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
|
|
14531
15104
|
}
|
|
14532
15105
|
var pythonInstaller = {
|
|
@@ -14653,7 +15226,7 @@ init_cjs_shims();
|
|
|
14653
15226
|
var import_node_fs39 = require("fs");
|
|
14654
15227
|
var import_node_http = __toESM(require("http"), 1);
|
|
14655
15228
|
var import_node_net = __toESM(require("net"), 1);
|
|
14656
|
-
var
|
|
15229
|
+
var import_node_path59 = __toESM(require("path"), 1);
|
|
14657
15230
|
var import_node_url4 = require("url");
|
|
14658
15231
|
var import_node_child_process3 = require("child_process");
|
|
14659
15232
|
var import_node_readline = __toESM(require("readline"), 1);
|
|
@@ -14663,7 +15236,7 @@ async function extractAndPersist(opts) {
|
|
|
14663
15236
|
const graphKey = opts.projectExplicit ? opts.project : DEFAULT_PROJECT;
|
|
14664
15237
|
resetGraph(graphKey);
|
|
14665
15238
|
const graph = getGraph(graphKey);
|
|
14666
|
-
const projectPaths = pathsForProject(graphKey,
|
|
15239
|
+
const projectPaths = pathsForProject(graphKey, import_node_path59.default.join(opts.scanPath, "neat-out"));
|
|
14667
15240
|
const extraction = await extractFromDirectory(graph, opts.scanPath, {
|
|
14668
15241
|
errorsPath: projectPaths.errorsPath
|
|
14669
15242
|
});
|
|
@@ -14715,7 +15288,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
14715
15288
|
libOnly++;
|
|
14716
15289
|
const appDeps = svc.pkg ? appFrameworkDependencies(svc.pkg) : [];
|
|
14717
15290
|
if (appDeps.length > 0) {
|
|
14718
|
-
const svcName =
|
|
15291
|
+
const svcName = import_node_path59.default.basename(svc.dir);
|
|
14719
15292
|
const list = appDeps.join(", ");
|
|
14720
15293
|
console.warn(
|
|
14721
15294
|
`neat: runtime layer won't engage for ${svcName}: no entry point found.
|
|
@@ -14728,7 +15301,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
14728
15301
|
console.log(`skipping ${svc.dir}: browser bundle; browser-OTel support lands in a future release.`);
|
|
14729
15302
|
} else if (outcome.outcome === "react-native") {
|
|
14730
15303
|
reactNative++;
|
|
14731
|
-
const svcName =
|
|
15304
|
+
const svcName = import_node_path59.default.basename(svc.dir);
|
|
14732
15305
|
console.log(
|
|
14733
15306
|
`neat: ${svc.dir} detected as React Native / Expo
|
|
14734
15307
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -14739,7 +15312,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
14739
15312
|
);
|
|
14740
15313
|
} else if (outcome.outcome === "bun") {
|
|
14741
15314
|
bun++;
|
|
14742
|
-
const svcName =
|
|
15315
|
+
const svcName = import_node_path59.default.basename(svc.dir);
|
|
14743
15316
|
console.log(
|
|
14744
15317
|
`neat: ${svc.dir} detected as Bun
|
|
14745
15318
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -14750,7 +15323,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
14750
15323
|
);
|
|
14751
15324
|
} else if (outcome.outcome === "deno") {
|
|
14752
15325
|
deno++;
|
|
14753
|
-
const svcName =
|
|
15326
|
+
const svcName = import_node_path59.default.basename(svc.dir);
|
|
14754
15327
|
console.log(
|
|
14755
15328
|
`neat: ${svc.dir} detected as Deno
|
|
14756
15329
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -14761,7 +15334,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
14761
15334
|
);
|
|
14762
15335
|
} else if (outcome.outcome === "cloudflare-workers") {
|
|
14763
15336
|
cloudflareWorkers++;
|
|
14764
|
-
const svcName =
|
|
15337
|
+
const svcName = import_node_path59.default.basename(svc.dir);
|
|
14765
15338
|
console.log(
|
|
14766
15339
|
`neat: ${svc.dir} detected as Cloudflare Workers
|
|
14767
15340
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -14772,7 +15345,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
14772
15345
|
);
|
|
14773
15346
|
} else if (outcome.outcome === "electron") {
|
|
14774
15347
|
electron++;
|
|
14775
|
-
const svcName =
|
|
15348
|
+
const svcName = import_node_path59.default.basename(svc.dir);
|
|
14776
15349
|
console.log(
|
|
14777
15350
|
`neat: ${svc.dir} detected as Electron
|
|
14778
15351
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -14785,7 +15358,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
14785
15358
|
if (svc.pkg && (outcome.outcome === "instrumented" || outcome.outcome === "already-instrumented")) {
|
|
14786
15359
|
const gaps = uninstrumentedLibraries(svc.pkg);
|
|
14787
15360
|
if (gaps.length > 0) {
|
|
14788
|
-
const svcName =
|
|
15361
|
+
const svcName = import_node_path59.default.basename(svc.dir);
|
|
14789
15362
|
const list = gaps.join(", ");
|
|
14790
15363
|
const subject = gaps.length === 1 ? "this library" : "these libraries";
|
|
14791
15364
|
const aux = gaps.length === 1 ? "isn't" : "aren't";
|
|
@@ -14991,8 +15564,8 @@ async function persistedPortsFor(scanPath) {
|
|
|
14991
15564
|
return { rest: record.ports.rest, otlp: record.ports.otlp, web: record.ports.web };
|
|
14992
15565
|
}
|
|
14993
15566
|
async function acquireSpawnLock(scanPath) {
|
|
14994
|
-
const lockPath =
|
|
14995
|
-
await import_node_fs39.promises.mkdir(
|
|
15567
|
+
const lockPath = import_node_path59.default.join(scanPath, "neat-out", "daemon.spawn.lock");
|
|
15568
|
+
await import_node_fs39.promises.mkdir(import_node_path59.default.dirname(lockPath), { recursive: true });
|
|
14996
15569
|
const STALE_LOCK_MS = 6e4;
|
|
14997
15570
|
try {
|
|
14998
15571
|
const fd = await import_node_fs39.promises.open(lockPath, "wx");
|
|
@@ -15037,13 +15610,13 @@ async function healthIsForProject(restPort, project) {
|
|
|
15037
15610
|
return false;
|
|
15038
15611
|
}
|
|
15039
15612
|
function daemonLogPath(projectPath2) {
|
|
15040
|
-
return
|
|
15613
|
+
return import_node_path59.default.join(projectPath2, "neat-out", "daemon.log");
|
|
15041
15614
|
}
|
|
15042
15615
|
function spawnDaemonDetached(spec) {
|
|
15043
|
-
const here =
|
|
15616
|
+
const here = import_node_path59.default.dirname((0, import_node_url4.fileURLToPath)(importMetaUrl));
|
|
15044
15617
|
const candidates = [
|
|
15045
|
-
|
|
15046
|
-
|
|
15618
|
+
import_node_path59.default.join(here, "neatd.cjs"),
|
|
15619
|
+
import_node_path59.default.join(here, "neatd.js")
|
|
15047
15620
|
];
|
|
15048
15621
|
let entry2 = null;
|
|
15049
15622
|
const fsSync = require("fs");
|
|
@@ -15073,7 +15646,7 @@ function spawnDaemonDetached(spec) {
|
|
|
15073
15646
|
let logFd = null;
|
|
15074
15647
|
if (spec) {
|
|
15075
15648
|
const logPath = daemonLogPath(spec.projectPath);
|
|
15076
|
-
fsSync.mkdirSync(
|
|
15649
|
+
fsSync.mkdirSync(import_node_path59.default.dirname(logPath), { recursive: true });
|
|
15077
15650
|
logFd = fsSync.openSync(logPath, "a");
|
|
15078
15651
|
}
|
|
15079
15652
|
const child = (0, import_node_child_process3.spawn)(process.execPath, [entry2, "start"], {
|
|
@@ -15272,7 +15845,7 @@ async function runOrchestrator(opts) {
|
|
|
15272
15845
|
result.steps.browser = openBrowser(dashboardUrl);
|
|
15273
15846
|
}
|
|
15274
15847
|
const daemonRunning = result.steps.daemon === "spawned" || result.steps.daemon === "already-running";
|
|
15275
|
-
const daemonLog = daemonRunning ?
|
|
15848
|
+
const daemonLog = daemonRunning ? import_node_path59.default.relative(opts.scanPath, daemonLogPath(opts.scanPath)) : null;
|
|
15276
15849
|
printSummary(result, graph, dashboardUrl, daemonLog);
|
|
15277
15850
|
return result;
|
|
15278
15851
|
}
|
|
@@ -15352,6 +15925,8 @@ function parseConnectorArgs(args) {
|
|
|
15352
15925
|
positional: [],
|
|
15353
15926
|
skipValidate: false,
|
|
15354
15927
|
plaintext: false,
|
|
15928
|
+
help: false,
|
|
15929
|
+
json: false,
|
|
15355
15930
|
fields: {}
|
|
15356
15931
|
};
|
|
15357
15932
|
const positional = [];
|
|
@@ -15365,6 +15940,14 @@ function parseConnectorArgs(args) {
|
|
|
15365
15940
|
out.plaintext = true;
|
|
15366
15941
|
continue;
|
|
15367
15942
|
}
|
|
15943
|
+
if (arg === "--help" || arg === "-h") {
|
|
15944
|
+
out.help = true;
|
|
15945
|
+
continue;
|
|
15946
|
+
}
|
|
15947
|
+
if (arg === "--json") {
|
|
15948
|
+
out.json = true;
|
|
15949
|
+
continue;
|
|
15950
|
+
}
|
|
15368
15951
|
if (arg.startsWith("--")) {
|
|
15369
15952
|
let flag = arg;
|
|
15370
15953
|
let value;
|
|
@@ -15441,19 +16024,20 @@ function plaintextFields(ref) {
|
|
|
15441
16024
|
async function connectorAdd(args, deps) {
|
|
15442
16025
|
let provider = args.positional[0];
|
|
15443
16026
|
if (!provider && deps.interactive) {
|
|
15444
|
-
provider = (await deps.prompt(`Provider (${
|
|
16027
|
+
provider = (await deps.prompt(`Provider (${knownProviderNames().join(", ")}):`)).trim();
|
|
15445
16028
|
}
|
|
15446
16029
|
if (!provider) {
|
|
15447
16030
|
deps.err("neat connector add: a provider is required (e.g. `neat connector add supabase`)");
|
|
15448
16031
|
return 2;
|
|
15449
16032
|
}
|
|
15450
|
-
const dispatch =
|
|
16033
|
+
const dispatch = getProviderFieldSchema(provider);
|
|
15451
16034
|
if (!dispatch) {
|
|
15452
16035
|
deps.err(
|
|
15453
|
-
`neat connector add: unknown provider "${provider}". known providers: ${
|
|
16036
|
+
`neat connector add: unknown provider "${provider}". known providers: ${knownProviderNames().join(", ")}`
|
|
15454
16037
|
);
|
|
15455
16038
|
return 2;
|
|
15456
16039
|
}
|
|
16040
|
+
const push = isPushProvider(provider);
|
|
15457
16041
|
let project = args.project;
|
|
15458
16042
|
if (project === void 0 && deps.interactive) {
|
|
15459
16043
|
const answer = (await deps.prompt("Project (blank = the project the daemon is bootstrapping):")).trim();
|
|
@@ -15492,7 +16076,7 @@ async function connectorAdd(args, deps) {
|
|
|
15492
16076
|
credential,
|
|
15493
16077
|
...Object.keys(options).length > 0 ? { options } : {}
|
|
15494
16078
|
};
|
|
15495
|
-
if (!args.skipValidate) {
|
|
16079
|
+
if (!push && !args.skipValidate) {
|
|
15496
16080
|
const outcome = await validateConnectorEntry(entry2, deps.env, deps.fetchImpl);
|
|
15497
16081
|
const code = reportPreWriteValidation(outcome, deps);
|
|
15498
16082
|
if (code !== 0) return code;
|
|
@@ -15503,14 +16087,32 @@ async function connectorAdd(args, deps) {
|
|
|
15503
16087
|
`neat connector add: storing a literal secret at rest for ${plaintext.join(", ")} \u2014 prefer an env-var reference like \`$PROVIDER_TOKEN\` (pass --plaintext to silence this).`
|
|
15504
16088
|
);
|
|
15505
16089
|
}
|
|
16090
|
+
if (push) {
|
|
16091
|
+
if (args.skipValidate) {
|
|
16092
|
+
deps.out("note: --skip-validate has no effect for a push provider \u2014 provisioning the drain is the add.");
|
|
16093
|
+
}
|
|
16094
|
+
const outcome = await provisionConnector(entry2, deps.env, deps.fetchImpl);
|
|
16095
|
+
if (outcome.status !== "ok") {
|
|
16096
|
+
deps.err(
|
|
16097
|
+
`neat connector add: could not provision the ${provider} connector \u2014 ${outcome.reason}. Nothing was written.`
|
|
16098
|
+
);
|
|
16099
|
+
return 1;
|
|
16100
|
+
}
|
|
16101
|
+
if (outcome.options) entry2.options = { ...entry2.options ?? {}, ...outcome.options };
|
|
16102
|
+
if (outcome.note) deps.out(`note: ${outcome.note}`);
|
|
16103
|
+
}
|
|
15506
16104
|
const { replaced } = await upsertConnectorEntry(entry2, deps.home);
|
|
15507
16105
|
const verb = replaced ? "updated" : "added";
|
|
15508
16106
|
const where = project ? `project "${project}"` : "the bootstrapping project";
|
|
15509
16107
|
deps.out(`${verb} connector "${id}" (${provider}) for ${where}.`);
|
|
15510
|
-
if (
|
|
15511
|
-
deps.out("
|
|
16108
|
+
if (push) {
|
|
16109
|
+
deps.out("The drain is live \u2014 traces arrive at the daemon's OTLP receiver. No poll or restart needed.");
|
|
16110
|
+
} else {
|
|
16111
|
+
if (args.skipValidate) {
|
|
16112
|
+
deps.out("skipped validation (--skip-validate) \u2014 the credential is checked at the next daemon poll.");
|
|
16113
|
+
}
|
|
16114
|
+
deps.out("Restart the project's daemon (or start it) to begin polling this connector.");
|
|
15512
16115
|
}
|
|
15513
|
-
deps.out("Restart the project's daemon (or start it) to begin polling this connector.");
|
|
15514
16116
|
return 0;
|
|
15515
16117
|
}
|
|
15516
16118
|
function reportPreWriteValidation(outcome, deps) {
|
|
@@ -15536,10 +16138,26 @@ function reportPreWriteValidation(outcome, deps) {
|
|
|
15536
16138
|
return 2;
|
|
15537
16139
|
}
|
|
15538
16140
|
}
|
|
15539
|
-
async function connectorList(deps, filterProject) {
|
|
16141
|
+
async function connectorList(deps, filterProject, json = false) {
|
|
15540
16142
|
const config = await readConnectorsConfig(deps.home);
|
|
15541
16143
|
let entries = config.connectors;
|
|
15542
16144
|
if (filterProject) entries = entries.filter((e) => e.project === filterProject);
|
|
16145
|
+
if (json) {
|
|
16146
|
+
const rows = entries.map((e) => ({
|
|
16147
|
+
id: e.id,
|
|
16148
|
+
provider: e.provider,
|
|
16149
|
+
project: e.project ?? null,
|
|
16150
|
+
kind: isPushProvider(e.provider) ? "push" : "pull",
|
|
16151
|
+
credential: describeCredential(e.credential, deps.env).map((c) => ({
|
|
16152
|
+
...c.field ? { field: c.field } : {},
|
|
16153
|
+
ref: c.display,
|
|
16154
|
+
...c.status ? { status: c.status } : { kind: c.kind }
|
|
16155
|
+
})),
|
|
16156
|
+
...e.options ? { options: e.options } : {}
|
|
16157
|
+
}));
|
|
16158
|
+
deps.out(JSON.stringify(rows, null, 2));
|
|
16159
|
+
return 0;
|
|
16160
|
+
}
|
|
15543
16161
|
if (entries.length === 0) {
|
|
15544
16162
|
deps.out(
|
|
15545
16163
|
filterProject ? `no connectors configured for project "${filterProject}".` : "no connectors configured. run `neat connector add <provider>` to add one."
|
|
@@ -15562,6 +16180,22 @@ async function connectorRemove(deps, id) {
|
|
|
15562
16180
|
deps.err("neat connector remove: missing <id>. run `neat connector list` to see configured ids.");
|
|
15563
16181
|
return 2;
|
|
15564
16182
|
}
|
|
16183
|
+
const config = await readConnectorsConfig(deps.home);
|
|
16184
|
+
const entry2 = config.connectors.find((c) => c.id === id);
|
|
16185
|
+
if (!entry2) {
|
|
16186
|
+
deps.err(`neat connector remove: no connector with id "${id}". run \`neat connector list\` to see configured ids.`);
|
|
16187
|
+
return 1;
|
|
16188
|
+
}
|
|
16189
|
+
if (isPushProvider(entry2.provider)) {
|
|
16190
|
+
const outcome = await deprovisionConnector(entry2, deps.env, deps.fetchImpl);
|
|
16191
|
+
if (outcome.status !== "ok") {
|
|
16192
|
+
deps.err(
|
|
16193
|
+
`neat connector remove: could not delete the ${entry2.provider} drain \u2014 ${outcome.reason}. The entry was kept so you can retry (or delete the drain in the Vercel dashboard, then re-run).`
|
|
16194
|
+
);
|
|
16195
|
+
return 1;
|
|
16196
|
+
}
|
|
16197
|
+
if (outcome.note) deps.out(`note: ${outcome.note}`);
|
|
16198
|
+
}
|
|
15565
16199
|
const removed = await removeConnectorEntry(id, deps.home);
|
|
15566
16200
|
if (!removed) {
|
|
15567
16201
|
deps.err(`neat connector remove: no connector with id "${id}". run \`neat connector list\` to see configured ids.`);
|
|
@@ -15602,13 +16236,20 @@ async function connectorTest(deps, id) {
|
|
|
15602
16236
|
}
|
|
15603
16237
|
function printConnectorUsage(write) {
|
|
15604
16238
|
write("usage: neat connector <add|list|remove|test> [args]");
|
|
16239
|
+
write(` providers: ${knownProviderNames().join(", ")}`);
|
|
16240
|
+
write(" pull (polled): supabase, railway, firebase, cloudflare");
|
|
16241
|
+
write(" push (drains): vercel \u2014 provisions a Vercel trace Drain that forwards");
|
|
16242
|
+
write(" traces to the daemon's OTLP receiver; no app instrumentation");
|
|
15605
16243
|
write(" add <provider> add a connector; validates the credential first (--skip-validate to skip)");
|
|
15606
16244
|
write(" flags: --project <name> --credential/--token <$VAR|value> --id <id>");
|
|
15607
16245
|
write(" --<option> <value> (provider-specific) --plaintext --skip-validate");
|
|
16246
|
+
write(" vercel: neat connector add vercel --token $VERCEL_TOKEN \\");
|
|
16247
|
+
write(" --otel-token $NEAT_OTEL_TOKEN --team-id <teamId> \\");
|
|
16248
|
+
write(" --endpoint https://<public-host>/v1/traces [--project-ids <id,id>]");
|
|
15608
16249
|
write(" list list configured connectors (credentials shown redacted)");
|
|
15609
16250
|
write(" flags: --project <name>");
|
|
15610
|
-
write(" remove <id> remove a connector by id");
|
|
15611
|
-
write(" test <id> re-run
|
|
16251
|
+
write(" remove <id> remove a connector by id (a push provider also has its drain deleted)");
|
|
16252
|
+
write(" test <id> re-run validation for an existing connector (a push provider re-tests its drain)");
|
|
15612
16253
|
}
|
|
15613
16254
|
async function runConnectorCommand(rawArgs, deps = {}) {
|
|
15614
16255
|
const resolved = resolveDeps(deps);
|
|
@@ -15618,11 +16259,15 @@ async function runConnectorCommand(rawArgs, deps = {}) {
|
|
|
15618
16259
|
return 2;
|
|
15619
16260
|
}
|
|
15620
16261
|
const a = parsed.value;
|
|
16262
|
+
if (a.help) {
|
|
16263
|
+
printConnectorUsage(resolved.out);
|
|
16264
|
+
return 0;
|
|
16265
|
+
}
|
|
15621
16266
|
switch (a.subcommand) {
|
|
15622
16267
|
case "add":
|
|
15623
16268
|
return connectorAdd(a, resolved);
|
|
15624
16269
|
case "list":
|
|
15625
|
-
return connectorList(resolved, a.project);
|
|
16270
|
+
return connectorList(resolved, a.project, a.json);
|
|
15626
16271
|
case "remove":
|
|
15627
16272
|
return connectorRemove(resolved, a.positional[0]);
|
|
15628
16273
|
case "test":
|
|
@@ -15640,7 +16285,7 @@ async function runConnectorCommand(rawArgs, deps = {}) {
|
|
|
15640
16285
|
|
|
15641
16286
|
// src/hooks-cli.ts
|
|
15642
16287
|
init_cjs_shims();
|
|
15643
|
-
var
|
|
16288
|
+
var import_node_path60 = __toESM(require("path"), 1);
|
|
15644
16289
|
var import_node_os5 = __toESM(require("os"), 1);
|
|
15645
16290
|
var import_node_fs40 = require("fs");
|
|
15646
16291
|
var import_node_url5 = require("url");
|
|
@@ -15649,14 +16294,14 @@ var GUIDE_FILENAME = "GRAPH_FIRST.md";
|
|
|
15649
16294
|
var GUIDE_INSTALL_NAME = "neat-graph-first.md";
|
|
15650
16295
|
var HOOK_MATCHER = "Grep|Glob|Bash";
|
|
15651
16296
|
function moduleDir() {
|
|
15652
|
-
return typeof __dirname !== "undefined" ? __dirname :
|
|
16297
|
+
return typeof __dirname !== "undefined" ? __dirname : import_node_path60.default.dirname((0, import_node_url5.fileURLToPath)(importMetaUrl));
|
|
15653
16298
|
}
|
|
15654
16299
|
async function readSkillAsset(rel) {
|
|
15655
16300
|
const here = moduleDir();
|
|
15656
16301
|
const candidates = [
|
|
15657
|
-
|
|
15658
|
-
|
|
15659
|
-
|
|
16302
|
+
import_node_path60.default.resolve(here, "../../claude-skill", rel),
|
|
16303
|
+
import_node_path60.default.resolve(here, "../../../claude-skill", rel),
|
|
16304
|
+
import_node_path60.default.resolve(here, "../claude-skill", rel)
|
|
15660
16305
|
];
|
|
15661
16306
|
for (const candidate of candidates) {
|
|
15662
16307
|
try {
|
|
@@ -15670,17 +16315,17 @@ async function readSkillAsset(rel) {
|
|
|
15670
16315
|
}
|
|
15671
16316
|
function neatHome3() {
|
|
15672
16317
|
const override = process.env.NEAT_HOME;
|
|
15673
|
-
if (override && override.length > 0) return
|
|
15674
|
-
return
|
|
16318
|
+
if (override && override.length > 0) return import_node_path60.default.resolve(override);
|
|
16319
|
+
return import_node_path60.default.join(import_node_os5.default.homedir(), ".neat");
|
|
15675
16320
|
}
|
|
15676
16321
|
function claudeSettingsPath() {
|
|
15677
16322
|
const override = process.env.NEAT_CLAUDE_SETTINGS;
|
|
15678
|
-
if (override && override.length > 0) return
|
|
16323
|
+
if (override && override.length > 0) return import_node_path60.default.resolve(override);
|
|
15679
16324
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? import_node_os5.default.homedir();
|
|
15680
|
-
return
|
|
16325
|
+
return import_node_path60.default.join(home, ".claude", "settings.json");
|
|
15681
16326
|
}
|
|
15682
16327
|
function installedHookPath() {
|
|
15683
|
-
return
|
|
16328
|
+
return import_node_path60.default.join(neatHome3(), "hooks", HOOK_FILENAME);
|
|
15684
16329
|
}
|
|
15685
16330
|
function isNeatSearchEntry(entry2) {
|
|
15686
16331
|
return (entry2.hooks ?? []).some(
|
|
@@ -15713,9 +16358,9 @@ async function runHooks(opts) {
|
|
|
15713
16358
|
const hookScript = await readSkillAsset(`hooks/${HOOK_FILENAME}`);
|
|
15714
16359
|
const guide = await readSkillAsset(GUIDE_FILENAME);
|
|
15715
16360
|
const scriptPath = installedHookPath();
|
|
15716
|
-
await import_node_fs40.promises.mkdir(
|
|
16361
|
+
await import_node_fs40.promises.mkdir(import_node_path60.default.dirname(scriptPath), { recursive: true });
|
|
15717
16362
|
await import_node_fs40.promises.writeFile(scriptPath, hookScript, { mode: 493 });
|
|
15718
|
-
const guidePath =
|
|
16363
|
+
const guidePath = import_node_path60.default.join(neatHome3(), GUIDE_INSTALL_NAME);
|
|
15719
16364
|
await import_node_fs40.promises.writeFile(guidePath, guide, "utf8");
|
|
15720
16365
|
const settingsFile = claudeSettingsPath();
|
|
15721
16366
|
let settings = {};
|
|
@@ -15742,7 +16387,7 @@ async function runHooks(opts) {
|
|
|
15742
16387
|
...settings,
|
|
15743
16388
|
hooks: { ...hooks, PreToolUse: preToolUse }
|
|
15744
16389
|
};
|
|
15745
|
-
await import_node_fs40.promises.mkdir(
|
|
16390
|
+
await import_node_fs40.promises.mkdir(import_node_path60.default.dirname(settingsFile), { recursive: true });
|
|
15746
16391
|
await import_node_fs40.promises.writeFile(settingsFile, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
15747
16392
|
console.log(`neat hooks: installed the search-nudge hook`);
|
|
15748
16393
|
console.log(` script: ${scriptPath}`);
|
|
@@ -15814,11 +16459,11 @@ async function runHooksCommand(args) {
|
|
|
15814
16459
|
|
|
15815
16460
|
// src/cli-verbs.ts
|
|
15816
16461
|
init_cjs_shims();
|
|
15817
|
-
var
|
|
16462
|
+
var import_node_path61 = __toESM(require("path"), 1);
|
|
15818
16463
|
|
|
15819
16464
|
// src/cli-client.ts
|
|
15820
16465
|
init_cjs_shims();
|
|
15821
|
-
var
|
|
16466
|
+
var import_types49 = require("@neat.is/types");
|
|
15822
16467
|
var HttpError = class extends Error {
|
|
15823
16468
|
constructor(status2, message, responseBody = "") {
|
|
15824
16469
|
super(message);
|
|
@@ -15843,10 +16488,10 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
15843
16488
|
const root = baseUrl.replace(/\/$/, "");
|
|
15844
16489
|
const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
|
|
15845
16490
|
return {
|
|
15846
|
-
async get(
|
|
16491
|
+
async get(path63) {
|
|
15847
16492
|
let res;
|
|
15848
16493
|
try {
|
|
15849
|
-
res = await fetch(`${root}${
|
|
16494
|
+
res = await fetch(`${root}${path63}`, {
|
|
15850
16495
|
headers: { ...authHeader }
|
|
15851
16496
|
});
|
|
15852
16497
|
} catch (err) {
|
|
@@ -15858,16 +16503,16 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
15858
16503
|
const body = await res.text().catch(() => "");
|
|
15859
16504
|
throw new HttpError(
|
|
15860
16505
|
res.status,
|
|
15861
|
-
`${res.status} ${res.statusText} on GET ${
|
|
16506
|
+
`${res.status} ${res.statusText} on GET ${path63}: ${body}`,
|
|
15862
16507
|
body
|
|
15863
16508
|
);
|
|
15864
16509
|
}
|
|
15865
16510
|
return await res.json();
|
|
15866
16511
|
},
|
|
15867
|
-
async post(
|
|
16512
|
+
async post(path63, body) {
|
|
15868
16513
|
let res;
|
|
15869
16514
|
try {
|
|
15870
|
-
res = await fetch(`${root}${
|
|
16515
|
+
res = await fetch(`${root}${path63}`, {
|
|
15871
16516
|
method: "POST",
|
|
15872
16517
|
headers: { "content-type": "application/json", ...authHeader },
|
|
15873
16518
|
body: JSON.stringify(body)
|
|
@@ -15881,7 +16526,7 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
15881
16526
|
const text = await res.text().catch(() => "");
|
|
15882
16527
|
throw new HttpError(
|
|
15883
16528
|
res.status,
|
|
15884
|
-
`${res.status} ${res.statusText} on POST ${
|
|
16529
|
+
`${res.status} ${res.statusText} on POST ${path63}: ${text}`,
|
|
15885
16530
|
text
|
|
15886
16531
|
);
|
|
15887
16532
|
}
|
|
@@ -15895,12 +16540,12 @@ function projectPath(project, suffix) {
|
|
|
15895
16540
|
}
|
|
15896
16541
|
async function runRootCause(client, input) {
|
|
15897
16542
|
const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
|
|
15898
|
-
const
|
|
16543
|
+
const path63 = projectPath(
|
|
15899
16544
|
input.project,
|
|
15900
16545
|
`/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
|
|
15901
16546
|
);
|
|
15902
16547
|
try {
|
|
15903
|
-
const result = await client.get(
|
|
16548
|
+
const result = await client.get(path63);
|
|
15904
16549
|
const arrowPath = result.traversalPath.join(" \u2190 ");
|
|
15905
16550
|
const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
|
|
15906
16551
|
const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
|
|
@@ -15926,12 +16571,12 @@ async function runRootCause(client, input) {
|
|
|
15926
16571
|
}
|
|
15927
16572
|
async function runBlastRadius(client, input) {
|
|
15928
16573
|
const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
|
|
15929
|
-
const
|
|
16574
|
+
const path63 = projectPath(
|
|
15930
16575
|
input.project,
|
|
15931
16576
|
`/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
|
|
15932
16577
|
);
|
|
15933
16578
|
try {
|
|
15934
|
-
const result = await client.get(
|
|
16579
|
+
const result = await client.get(path63);
|
|
15935
16580
|
if (result.totalAffected === 0) {
|
|
15936
16581
|
return {
|
|
15937
16582
|
summary: `${result.origin} has no dependents. Nothing else would break if it failed.`
|
|
@@ -15960,17 +16605,17 @@ async function runBlastRadius(client, input) {
|
|
|
15960
16605
|
}
|
|
15961
16606
|
}
|
|
15962
16607
|
function formatBlastEntry(n) {
|
|
15963
|
-
const tag = n.edgeProvenance ===
|
|
16608
|
+
const tag = n.edgeProvenance === import_types49.Provenance.STALE ? " [STALE \u2014 last seen too long ago]" : "";
|
|
15964
16609
|
return ` \u2022 ${n.nodeId} (distance ${n.distance}, ${n.edgeProvenance})${tag}`;
|
|
15965
16610
|
}
|
|
15966
16611
|
async function runDependencies(client, input) {
|
|
15967
16612
|
const depth = input.depth ?? 3;
|
|
15968
|
-
const
|
|
16613
|
+
const path63 = projectPath(
|
|
15969
16614
|
input.project,
|
|
15970
16615
|
`/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
|
|
15971
16616
|
);
|
|
15972
16617
|
try {
|
|
15973
|
-
const result = await client.get(
|
|
16618
|
+
const result = await client.get(path63);
|
|
15974
16619
|
if (result.total === 0) {
|
|
15975
16620
|
return {
|
|
15976
16621
|
summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
|
|
@@ -16017,7 +16662,7 @@ async function runObservedDependencies(client, input) {
|
|
|
16017
16662
|
if (result.observed) {
|
|
16018
16663
|
return {
|
|
16019
16664
|
summary: `${input.nodeId} makes no outbound runtime calls, but OTel has observed it receiving traffic on ${result.inboundObservedCount} inbound call path${result.inboundObservedCount === 1 ? "" : "s"} \u2014 it's a pure receiver.`,
|
|
16020
|
-
provenance:
|
|
16665
|
+
provenance: import_types49.Provenance.OBSERVED
|
|
16021
16666
|
};
|
|
16022
16667
|
}
|
|
16023
16668
|
const note = result.hasExtractedOutbound ? " Static (EXTRACTED) dependencies exist but no runtime traffic has been seen \u2014 is OTel running?" : "";
|
|
@@ -16027,7 +16672,7 @@ async function runObservedDependencies(client, input) {
|
|
|
16027
16672
|
return {
|
|
16028
16673
|
summary: `${input.nodeId} has ${result.dependencies.length} runtime dependenc${result.dependencies.length === 1 ? "y" : "ies"} confirmed by OTel.`,
|
|
16029
16674
|
block: blockLines.join("\n"),
|
|
16030
|
-
provenance:
|
|
16675
|
+
provenance: import_types49.Provenance.OBSERVED
|
|
16031
16676
|
};
|
|
16032
16677
|
} catch (err) {
|
|
16033
16678
|
if (err instanceof HttpError && err.status === 404) {
|
|
@@ -16062,9 +16707,9 @@ function formatDuration(ms) {
|
|
|
16062
16707
|
return `${Math.round(h / 24)}d`;
|
|
16063
16708
|
}
|
|
16064
16709
|
async function runIncidents(client, input) {
|
|
16065
|
-
const
|
|
16710
|
+
const path63 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
|
|
16066
16711
|
try {
|
|
16067
|
-
const body = await client.get(
|
|
16712
|
+
const body = await client.get(path63);
|
|
16068
16713
|
const events = body.events;
|
|
16069
16714
|
if (events.length === 0) {
|
|
16070
16715
|
return {
|
|
@@ -16081,7 +16726,7 @@ async function runIncidents(client, input) {
|
|
|
16081
16726
|
return {
|
|
16082
16727
|
summary: `${target} has ${body.total} recorded incident${body.total === 1 ? "" : "s"}; showing the ${ordered.length} most recent.`,
|
|
16083
16728
|
block: blockLines.join("\n"),
|
|
16084
|
-
provenance:
|
|
16729
|
+
provenance: import_types49.Provenance.OBSERVED
|
|
16085
16730
|
};
|
|
16086
16731
|
} catch (err) {
|
|
16087
16732
|
if (err instanceof HttpError && err.status === 404) {
|
|
@@ -16190,7 +16835,7 @@ async function runStaleEdges(client, input) {
|
|
|
16190
16835
|
return {
|
|
16191
16836
|
summary: `${events.length} stale-edge transition${events.length === 1 ? "" : "s"} recorded${input.edgeType ? ` for ${input.edgeType}` : ""}.`,
|
|
16192
16837
|
block: blockLines.join("\n"),
|
|
16193
|
-
provenance:
|
|
16838
|
+
provenance: import_types49.Provenance.STALE
|
|
16194
16839
|
};
|
|
16195
16840
|
}
|
|
16196
16841
|
async function runPolicies(client, input) {
|
|
@@ -16354,7 +16999,7 @@ async function resolveProjectEntry(opts) {
|
|
|
16354
16999
|
const cwd = opts.cwd ?? process.cwd();
|
|
16355
17000
|
const resolvedCwd = await normalizeProjectPath(cwd);
|
|
16356
17001
|
for (const entry2 of entries) {
|
|
16357
|
-
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${
|
|
17002
|
+
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path61.default.sep}`)) {
|
|
16358
17003
|
return entry2;
|
|
16359
17004
|
}
|
|
16360
17005
|
}
|
|
@@ -16507,7 +17152,7 @@ async function runSync(opts) {
|
|
|
16507
17152
|
}
|
|
16508
17153
|
|
|
16509
17154
|
// src/cli.ts
|
|
16510
|
-
var
|
|
17155
|
+
var import_types50 = require("@neat.is/types");
|
|
16511
17156
|
function isNpxInvocation() {
|
|
16512
17157
|
if (process.env.npm_command === "exec") return true;
|
|
16513
17158
|
const execpath = process.env.npm_execpath ?? "";
|
|
@@ -16576,8 +17221,9 @@ function usage2() {
|
|
|
16576
17221
|
console.log(" --dry-run run extraction in-memory; do not write");
|
|
16577
17222
|
console.log(" --no-instrument skip the SDK install apply step");
|
|
16578
17223
|
console.log(" --json emit the delta summary as JSON");
|
|
16579
|
-
console.log(" connector Configure
|
|
16580
|
-
console.log(" firebase, cloudflare).
|
|
17224
|
+
console.log(" connector Configure OBSERVED connectors \u2014 pull (supabase, railway,");
|
|
17225
|
+
console.log(" firebase, cloudflare) and push (vercel, via a trace Drain).");
|
|
17226
|
+
console.log(" Subcommands:");
|
|
16581
17227
|
console.log(" add <provider> add a connector; validates the credential");
|
|
16582
17228
|
console.log(" against the provider first (--skip-validate to skip)");
|
|
16583
17229
|
console.log(" flags: --project <name>, --credential/--token <$VAR|value>,");
|
|
@@ -16817,12 +17463,12 @@ async function runInit(opts) {
|
|
|
16817
17463
|
printDiscoveryReport(opts, services);
|
|
16818
17464
|
const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
|
|
16819
17465
|
const patch = renderPatch(sections);
|
|
16820
|
-
const patchPath =
|
|
17466
|
+
const patchPath = import_node_path62.default.join(opts.scanPath, "neat.patch");
|
|
16821
17467
|
if (opts.dryRun) {
|
|
16822
17468
|
await import_node_fs41.promises.writeFile(patchPath, patch, "utf8");
|
|
16823
17469
|
written.push(patchPath);
|
|
16824
17470
|
console.log(`dry-run: patch written to ${patchPath}`);
|
|
16825
|
-
const gitignorePath =
|
|
17471
|
+
const gitignorePath = import_node_path62.default.join(opts.scanPath, ".gitignore");
|
|
16826
17472
|
const gitignoreExists = await import_node_fs41.promises.stat(gitignorePath).then(() => true).catch(() => false);
|
|
16827
17473
|
const verb = gitignoreExists ? "append" : "create";
|
|
16828
17474
|
console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
|
|
@@ -16834,9 +17480,9 @@ async function runInit(opts) {
|
|
|
16834
17480
|
const graph = getGraph(graphKey);
|
|
16835
17481
|
const projectPaths = pathsForProject(
|
|
16836
17482
|
graphKey,
|
|
16837
|
-
|
|
17483
|
+
import_node_path62.default.join(opts.scanPath, "neat-out")
|
|
16838
17484
|
);
|
|
16839
|
-
const errorsPath =
|
|
17485
|
+
const errorsPath = import_node_path62.default.join(import_node_path62.default.dirname(opts.outPath), import_node_path62.default.basename(projectPaths.errorsPath));
|
|
16840
17486
|
const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
|
|
16841
17487
|
await saveGraphToDisk(graph, opts.outPath);
|
|
16842
17488
|
written.push(opts.outPath);
|
|
@@ -16955,9 +17601,9 @@ var CLAUDE_SKILL_CONFIG = {
|
|
|
16955
17601
|
};
|
|
16956
17602
|
function claudeConfigPath() {
|
|
16957
17603
|
const override = process.env.NEAT_CLAUDE_CONFIG;
|
|
16958
|
-
if (override && override.length > 0) return
|
|
17604
|
+
if (override && override.length > 0) return import_node_path62.default.resolve(override);
|
|
16959
17605
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
16960
|
-
return
|
|
17606
|
+
return import_node_path62.default.join(home, ".claude.json");
|
|
16961
17607
|
}
|
|
16962
17608
|
async function runSkill(opts) {
|
|
16963
17609
|
const snippet2 = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
|
|
@@ -16981,7 +17627,7 @@ async function runSkill(opts) {
|
|
|
16981
17627
|
...existing,
|
|
16982
17628
|
mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
|
|
16983
17629
|
};
|
|
16984
|
-
await import_node_fs41.promises.mkdir(
|
|
17630
|
+
await import_node_fs41.promises.mkdir(import_node_path62.default.dirname(target), { recursive: true });
|
|
16985
17631
|
await import_node_fs41.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
16986
17632
|
console.log(`neat skill: wrote mcpServers.neat to ${target}`);
|
|
16987
17633
|
console.log("restart Claude Code to pick up the new MCP server.");
|
|
@@ -17047,12 +17693,12 @@ async function main() {
|
|
|
17047
17693
|
console.error("neat init: --apply and --dry-run are mutually exclusive");
|
|
17048
17694
|
process.exit(2);
|
|
17049
17695
|
}
|
|
17050
|
-
const scanPath =
|
|
17696
|
+
const scanPath = import_node_path62.default.resolve(target);
|
|
17051
17697
|
const projectExplicit = parsed.project !== null;
|
|
17052
|
-
const projectName = projectExplicit ? project :
|
|
17698
|
+
const projectName = projectExplicit ? project : import_node_path62.default.basename(scanPath);
|
|
17053
17699
|
const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
|
|
17054
|
-
const fallback = pathsForProject(projectKey,
|
|
17055
|
-
const outPath =
|
|
17700
|
+
const fallback = pathsForProject(projectKey, import_node_path62.default.join(scanPath, "neat-out")).snapshotPath;
|
|
17701
|
+
const outPath = import_node_path62.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
|
|
17056
17702
|
const result = await runInit({
|
|
17057
17703
|
scanPath,
|
|
17058
17704
|
outPath,
|
|
@@ -17073,21 +17719,21 @@ async function main() {
|
|
|
17073
17719
|
usage2();
|
|
17074
17720
|
process.exit(2);
|
|
17075
17721
|
}
|
|
17076
|
-
const scanPath =
|
|
17722
|
+
const scanPath = import_node_path62.default.resolve(target);
|
|
17077
17723
|
const stat = await import_node_fs41.promises.stat(scanPath).catch(() => null);
|
|
17078
17724
|
if (!stat || !stat.isDirectory()) {
|
|
17079
17725
|
console.error(`neat watch: ${scanPath} is not a directory`);
|
|
17080
17726
|
process.exit(2);
|
|
17081
17727
|
}
|
|
17082
|
-
const projectPaths = pathsForProject(project,
|
|
17083
|
-
const outPath =
|
|
17084
|
-
const errorsPath =
|
|
17085
|
-
process.env.NEAT_ERRORS_PATH ??
|
|
17728
|
+
const projectPaths = pathsForProject(project, import_node_path62.default.join(scanPath, "neat-out"));
|
|
17729
|
+
const outPath = import_node_path62.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
|
|
17730
|
+
const errorsPath = import_node_path62.default.resolve(
|
|
17731
|
+
process.env.NEAT_ERRORS_PATH ?? import_node_path62.default.join(import_node_path62.default.dirname(outPath), import_node_path62.default.basename(projectPaths.errorsPath))
|
|
17086
17732
|
);
|
|
17087
|
-
const staleEventsPath =
|
|
17088
|
-
process.env.NEAT_STALE_EVENTS_PATH ??
|
|
17733
|
+
const staleEventsPath = import_node_path62.default.resolve(
|
|
17734
|
+
process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path62.default.join(import_node_path62.default.dirname(outPath), import_node_path62.default.basename(projectPaths.staleEventsPath))
|
|
17089
17735
|
);
|
|
17090
|
-
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ?
|
|
17736
|
+
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path62.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
|
|
17091
17737
|
const handle = await startWatch(getGraph(project), {
|
|
17092
17738
|
scanPath,
|
|
17093
17739
|
outPath,
|
|
@@ -17270,11 +17916,11 @@ async function main() {
|
|
|
17270
17916
|
process.exit(1);
|
|
17271
17917
|
}
|
|
17272
17918
|
async function tryOrchestrator(cmd, parsed) {
|
|
17273
|
-
const scanPath =
|
|
17919
|
+
const scanPath = import_node_path62.default.resolve(cmd);
|
|
17274
17920
|
const stat = await import_node_fs41.promises.stat(scanPath).catch(() => null);
|
|
17275
17921
|
if (!stat || !stat.isDirectory()) return null;
|
|
17276
17922
|
const projectExplicit = parsed.project !== null;
|
|
17277
|
-
const projectName = projectExplicit ? parsed.project :
|
|
17923
|
+
const projectName = projectExplicit ? parsed.project : import_node_path62.default.basename(scanPath);
|
|
17278
17924
|
const result = await runOrchestrator({
|
|
17279
17925
|
scanPath,
|
|
17280
17926
|
project: projectName,
|
|
@@ -17463,10 +18109,10 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
17463
18109
|
const parts = parsed.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
17464
18110
|
const out = [];
|
|
17465
18111
|
for (const p of parts) {
|
|
17466
|
-
const r =
|
|
18112
|
+
const r = import_types50.DivergenceTypeSchema.safeParse(p);
|
|
17467
18113
|
if (!r.success) {
|
|
17468
18114
|
console.error(
|
|
17469
|
-
`neat divergences: unknown --type "${p}". allowed: ${
|
|
18115
|
+
`neat divergences: unknown --type "${p}". allowed: ${import_types50.DivergenceTypeSchema.options.join(", ")}`
|
|
17470
18116
|
);
|
|
17471
18117
|
return 2;
|
|
17472
18118
|
}
|