@neat.is/core 0.5.3 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-X2AMX3QZ.js → chunk-5PVQJLPR.js} +272 -13
- package/dist/chunk-5PVQJLPR.js.map +1 -0
- package/dist/{chunk-CFDPIMRP.js → chunk-BZ3AJVAC.js} +9 -3
- package/dist/chunk-BZ3AJVAC.js.map +1 -0
- package/dist/{chunk-GJHEZC5K.js → chunk-CS4GHQO3.js} +458 -111
- package/dist/chunk-CS4GHQO3.js.map +1 -0
- package/dist/{chunk-I72HTUOG.js → chunk-MVINCLQM.js} +2 -2
- package/dist/cli.cjs +1190 -458
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +140 -25
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +849 -286
- 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 +858 -295
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-IDMIH6ZY.js → otel-grpc-XS45HBET.js} +3 -3
- package/dist/server.cjs +540 -173
- 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-MVINCLQM.js.map} +0 -0
- /package/dist/{otel-grpc-IDMIH6ZY.js.map → otel-grpc-XS45HBET.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);
|
|
@@ -342,6 +342,11 @@ function pickEnv(spanAttrs, resourceAttrs) {
|
|
|
342
342
|
}
|
|
343
343
|
return ENV_FALLBACK;
|
|
344
344
|
}
|
|
345
|
+
function normalizeDbSystem(attrs) {
|
|
346
|
+
const raw = attrs["db.system"];
|
|
347
|
+
if (typeof raw !== "string") return void 0;
|
|
348
|
+
return raw === "mongoose" ? "mongodb" : raw;
|
|
349
|
+
}
|
|
345
350
|
function messagingDestinationOf(attrs) {
|
|
346
351
|
for (const key of ["messaging.destination.name", "messaging.destination"]) {
|
|
347
352
|
const v = attrs[key];
|
|
@@ -362,8 +367,8 @@ function websocketChannelPathOf(attrs) {
|
|
|
362
367
|
const v = attrs[key];
|
|
363
368
|
if (typeof v === "string" && v.length > 0) {
|
|
364
369
|
const q = v.indexOf("?");
|
|
365
|
-
const
|
|
366
|
-
if (
|
|
370
|
+
const path63 = q === -1 ? v : v.slice(0, q);
|
|
371
|
+
if (path63.length > 0) return path63;
|
|
367
372
|
}
|
|
368
373
|
}
|
|
369
374
|
return void 0;
|
|
@@ -396,8 +401,9 @@ function parseOtlpRequest(body) {
|
|
|
396
401
|
durationNanos: durationNanos(span.startTimeUnixNano, span.endTimeUnixNano),
|
|
397
402
|
env: pickEnv(attrs, resourceAttrs),
|
|
398
403
|
attributes: attrs,
|
|
399
|
-
dbSystem:
|
|
404
|
+
dbSystem: normalizeDbSystem(attrs),
|
|
400
405
|
dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
|
|
406
|
+
dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
|
|
401
407
|
messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
|
|
402
408
|
messagingDestination: messagingDestinationOf(attrs),
|
|
403
409
|
graphqlOperationName: typeof attrs["graphql.operation.name"] === "string" && attrs["graphql.operation.name"].length > 0 ? attrs["graphql.operation.name"] : void 0,
|
|
@@ -417,10 +423,10 @@ function parseOtlpRequest(body) {
|
|
|
417
423
|
return out;
|
|
418
424
|
}
|
|
419
425
|
function loadProtoRoot() {
|
|
420
|
-
const here =
|
|
421
|
-
const protoRoot =
|
|
426
|
+
const here = import_node_path51.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl));
|
|
427
|
+
const protoRoot = import_node_path51.default.resolve(here, "..", "proto");
|
|
422
428
|
const root = new import_protobufjs.default.Root();
|
|
423
|
-
root.resolvePath = (_origin, target) =>
|
|
429
|
+
root.resolvePath = (_origin, target) => import_node_path51.default.resolve(protoRoot, target);
|
|
424
430
|
root.loadSync(
|
|
425
431
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
426
432
|
{ keepCase: true }
|
|
@@ -648,12 +654,12 @@ async function listenSteppingOtlp(app, requestedPort, host) {
|
|
|
648
654
|
}
|
|
649
655
|
}
|
|
650
656
|
}
|
|
651
|
-
var
|
|
657
|
+
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
658
|
var init_otel = __esm({
|
|
653
659
|
"src/otel.ts"() {
|
|
654
660
|
"use strict";
|
|
655
661
|
init_cjs_shims();
|
|
656
|
-
|
|
662
|
+
import_node_path51 = __toESM(require("path"), 1);
|
|
657
663
|
import_node_url3 = require("url");
|
|
658
664
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
659
665
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -690,7 +696,7 @@ __export(cli_exports, {
|
|
|
690
696
|
});
|
|
691
697
|
module.exports = __toCommonJS(cli_exports);
|
|
692
698
|
init_cjs_shims();
|
|
693
|
-
var
|
|
699
|
+
var import_node_path62 = __toESM(require("path"), 1);
|
|
694
700
|
var import_node_fs41 = require("fs");
|
|
695
701
|
|
|
696
702
|
// src/banner.ts
|
|
@@ -1256,19 +1262,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1256
1262
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1257
1263
|
let best = { path: [start], edges: [] };
|
|
1258
1264
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1259
|
-
function step(node,
|
|
1260
|
-
if (
|
|
1261
|
-
best = { path: [...
|
|
1265
|
+
function step(node, path63, edges) {
|
|
1266
|
+
if (path63.length > best.path.length) {
|
|
1267
|
+
best = { path: [...path63], edges: [...edges] };
|
|
1262
1268
|
}
|
|
1263
|
-
if (
|
|
1269
|
+
if (path63.length - 1 >= maxDepth) return;
|
|
1264
1270
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1265
1271
|
for (const [srcId, edge] of incoming) {
|
|
1266
1272
|
if (visited.has(srcId)) continue;
|
|
1267
1273
|
visited.add(srcId);
|
|
1268
|
-
|
|
1274
|
+
path63.push(srcId);
|
|
1269
1275
|
edges.push(edge);
|
|
1270
|
-
step(srcId,
|
|
1271
|
-
|
|
1276
|
+
step(srcId, path63, edges);
|
|
1277
|
+
path63.pop();
|
|
1272
1278
|
edges.pop();
|
|
1273
1279
|
visited.delete(srcId);
|
|
1274
1280
|
}
|
|
@@ -1462,26 +1468,26 @@ function dominantFailingCall(graph, serviceId6, visited) {
|
|
|
1462
1468
|
return best;
|
|
1463
1469
|
}
|
|
1464
1470
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
1465
|
-
const
|
|
1471
|
+
const path63 = [originServiceId];
|
|
1466
1472
|
const edges = [];
|
|
1467
1473
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1468
1474
|
let current = originServiceId;
|
|
1469
1475
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1470
1476
|
const hop = dominantFailingCall(graph, current, visited);
|
|
1471
1477
|
if (!hop) break;
|
|
1472
|
-
|
|
1478
|
+
path63.push(hop.nextService);
|
|
1473
1479
|
edges.push(hop.edge);
|
|
1474
1480
|
visited.add(hop.nextService);
|
|
1475
1481
|
current = hop.nextService;
|
|
1476
1482
|
}
|
|
1477
1483
|
if (edges.length === 0) return null;
|
|
1478
|
-
return { path:
|
|
1484
|
+
return { path: path63, edges, culprit: current };
|
|
1479
1485
|
}
|
|
1480
1486
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
1481
1487
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
1482
1488
|
if (!chain) return null;
|
|
1483
1489
|
const culprit = chain.culprit;
|
|
1484
|
-
const
|
|
1490
|
+
const path63 = [...chain.path];
|
|
1485
1491
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
1486
1492
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
1487
1493
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -1489,14 +1495,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1489
1495
|
if (loc) {
|
|
1490
1496
|
let rootCauseNode = culprit;
|
|
1491
1497
|
if (loc.fileNode) {
|
|
1492
|
-
|
|
1498
|
+
path63.push(loc.fileNode);
|
|
1493
1499
|
edgeProvenances.push(import_types.Provenance.OBSERVED);
|
|
1494
1500
|
rootCauseNode = loc.fileNode;
|
|
1495
1501
|
}
|
|
1496
1502
|
return import_types.RootCauseResultSchema.parse({
|
|
1497
1503
|
rootCauseNode,
|
|
1498
1504
|
rootCauseReason: loc.rootCauseReason,
|
|
1499
|
-
traversalPath:
|
|
1505
|
+
traversalPath: path63,
|
|
1500
1506
|
edgeProvenances,
|
|
1501
1507
|
confidence,
|
|
1502
1508
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -1508,7 +1514,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1508
1514
|
return import_types.RootCauseResultSchema.parse({
|
|
1509
1515
|
rootCauseNode: culprit,
|
|
1510
1516
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
1511
|
-
traversalPath:
|
|
1517
|
+
traversalPath: path63,
|
|
1512
1518
|
edgeProvenances,
|
|
1513
1519
|
confidence,
|
|
1514
1520
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -2585,6 +2591,19 @@ function ensureServiceNode(graph, serviceName, env) {
|
|
|
2585
2591
|
graph.addNode(id, node);
|
|
2586
2592
|
return id;
|
|
2587
2593
|
}
|
|
2594
|
+
function ensureInfraNode(graph, kind, name, provider) {
|
|
2595
|
+
const id = (0, import_types3.infraId)(kind, name);
|
|
2596
|
+
if (graph.hasNode(id)) return id;
|
|
2597
|
+
const node = {
|
|
2598
|
+
id,
|
|
2599
|
+
type: import_types3.NodeType.InfraNode,
|
|
2600
|
+
name,
|
|
2601
|
+
provider,
|
|
2602
|
+
kind
|
|
2603
|
+
};
|
|
2604
|
+
graph.addNode(id, node);
|
|
2605
|
+
return id;
|
|
2606
|
+
}
|
|
2588
2607
|
function ensureDatabaseNode(graph, host, engine) {
|
|
2589
2608
|
const id = (0, import_types3.databaseId)(host);
|
|
2590
2609
|
if (graph.hasNode(id)) return id;
|
|
@@ -2922,6 +2941,18 @@ async function handleSpan(ctx, span) {
|
|
|
2922
2941
|
callSiteEvidence
|
|
2923
2942
|
);
|
|
2924
2943
|
if (result) affectedNode = targetId;
|
|
2944
|
+
if (span.dbSystem === "mongodb" && span.dbCollection) {
|
|
2945
|
+
const collectionId = ensureInfraNode(ctx.graph, "mongodb-collection", span.dbCollection, "self");
|
|
2946
|
+
upsertObservedEdge(
|
|
2947
|
+
ctx.graph,
|
|
2948
|
+
import_types3.EdgeType.CALLS,
|
|
2949
|
+
observedSource(),
|
|
2950
|
+
collectionId,
|
|
2951
|
+
ts,
|
|
2952
|
+
isError,
|
|
2953
|
+
callSiteEvidence
|
|
2954
|
+
);
|
|
2955
|
+
}
|
|
2925
2956
|
}
|
|
2926
2957
|
} else if (span.messagingSystem && span.messagingDestination && spanMintsMessagingEdge(span.kind)) {
|
|
2927
2958
|
const targetId = ensureMessagingDestinationNode(
|
|
@@ -5781,7 +5812,7 @@ async function addGrpcMethods(graph, services) {
|
|
|
5781
5812
|
|
|
5782
5813
|
// src/extract/calls/index.ts
|
|
5783
5814
|
init_cjs_shims();
|
|
5784
|
-
var
|
|
5815
|
+
var import_types21 = require("@neat.is/types");
|
|
5785
5816
|
|
|
5786
5817
|
// src/extract/calls/http.ts
|
|
5787
5818
|
init_cjs_shims();
|
|
@@ -6494,15 +6525,311 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
6494
6525
|
return out;
|
|
6495
6526
|
}
|
|
6496
6527
|
|
|
6528
|
+
// src/extract/calls/mongoose.ts
|
|
6529
|
+
init_cjs_shims();
|
|
6530
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
6531
|
+
var import_types20 = require("@neat.is/types");
|
|
6532
|
+
var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
|
|
6533
|
+
var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
|
|
6534
|
+
var PLURALIZE_DISABLED_RE = /\bpluralize\s*\(\s*(?:null|false)\s*\)/;
|
|
6535
|
+
var UNCOUNTABLES = /* @__PURE__ */ new Set([
|
|
6536
|
+
"advice",
|
|
6537
|
+
"energy",
|
|
6538
|
+
"excretion",
|
|
6539
|
+
"digestion",
|
|
6540
|
+
"cooperation",
|
|
6541
|
+
"health",
|
|
6542
|
+
"justice",
|
|
6543
|
+
"labour",
|
|
6544
|
+
"machinery",
|
|
6545
|
+
"equipment",
|
|
6546
|
+
"information",
|
|
6547
|
+
"pollution",
|
|
6548
|
+
"sewage",
|
|
6549
|
+
"paper",
|
|
6550
|
+
"money",
|
|
6551
|
+
"species",
|
|
6552
|
+
"series",
|
|
6553
|
+
"rain",
|
|
6554
|
+
"rice",
|
|
6555
|
+
"fish",
|
|
6556
|
+
"sheep",
|
|
6557
|
+
"moose",
|
|
6558
|
+
"deer",
|
|
6559
|
+
"news",
|
|
6560
|
+
"expertise",
|
|
6561
|
+
"status",
|
|
6562
|
+
"media"
|
|
6563
|
+
]);
|
|
6564
|
+
var PLURALIZE_RULES = [
|
|
6565
|
+
[/human$/gi, "humans"],
|
|
6566
|
+
[/(m)an$/gi, "$1en"],
|
|
6567
|
+
[/(pe)rson$/gi, "$1ople"],
|
|
6568
|
+
[/(child)$/gi, "$1ren"],
|
|
6569
|
+
[/^(ox)$/gi, "$1en"],
|
|
6570
|
+
[/(ax|test)is$/gi, "$1es"],
|
|
6571
|
+
[/(octop|vir)us$/gi, "$1i"],
|
|
6572
|
+
[/(alias|status)$/gi, "$1es"],
|
|
6573
|
+
[/(bu)s$/gi, "$1ses"],
|
|
6574
|
+
[/(buffal|tomat|potat)o$/gi, "$1oes"],
|
|
6575
|
+
[/([ti])um$/gi, "$1a"],
|
|
6576
|
+
[/sis$/gi, "ses"],
|
|
6577
|
+
[/(?:([^f])fe|([lr])f)$/gi, "$1$2ves"],
|
|
6578
|
+
[/(hive)$/gi, "$1s"],
|
|
6579
|
+
[/([^aeiouy]|qu)y$/gi, "$1ies"],
|
|
6580
|
+
[/(x|ch|ss|sh)$/gi, "$1es"],
|
|
6581
|
+
[/(matr|vert|ind)ix|ex$/gi, "$1ices"],
|
|
6582
|
+
[/([m|l])ouse$/gi, "$1ice"],
|
|
6583
|
+
[/(kn|w|l)ife$/gi, "$1ives"],
|
|
6584
|
+
[/(quiz)$/gi, "$1zes"],
|
|
6585
|
+
[/s$/gi, "s"],
|
|
6586
|
+
[/([^a-z])$/, "$1"],
|
|
6587
|
+
[/$/gi, "s"]
|
|
6588
|
+
];
|
|
6589
|
+
function pluralizeCollection(name) {
|
|
6590
|
+
const str = name.toLowerCase();
|
|
6591
|
+
if (UNCOUNTABLES.has(str)) return str;
|
|
6592
|
+
for (const [re, repl] of PLURALIZE_RULES) {
|
|
6593
|
+
if (str.match(re)) return str.replace(re, repl);
|
|
6594
|
+
}
|
|
6595
|
+
return str;
|
|
6596
|
+
}
|
|
6597
|
+
var MODEL_METHODS = [
|
|
6598
|
+
"find",
|
|
6599
|
+
"findOne",
|
|
6600
|
+
"findById",
|
|
6601
|
+
"findByIdAndUpdate",
|
|
6602
|
+
"findByIdAndDelete",
|
|
6603
|
+
"findOneAndUpdate",
|
|
6604
|
+
"findOneAndDelete",
|
|
6605
|
+
"findOneAndReplace",
|
|
6606
|
+
"countDocuments",
|
|
6607
|
+
"estimatedDocumentCount",
|
|
6608
|
+
"distinct",
|
|
6609
|
+
"aggregate",
|
|
6610
|
+
"exists",
|
|
6611
|
+
"where",
|
|
6612
|
+
"populate",
|
|
6613
|
+
"watch",
|
|
6614
|
+
"hydrate",
|
|
6615
|
+
"create",
|
|
6616
|
+
"insertMany",
|
|
6617
|
+
"insertOne",
|
|
6618
|
+
"updateOne",
|
|
6619
|
+
"updateMany",
|
|
6620
|
+
"replaceOne",
|
|
6621
|
+
"deleteOne",
|
|
6622
|
+
"deleteMany",
|
|
6623
|
+
"bulkWrite",
|
|
6624
|
+
"bulkSave",
|
|
6625
|
+
"save",
|
|
6626
|
+
// pre-v7, removed in current mongoose but present in older codebases:
|
|
6627
|
+
"count",
|
|
6628
|
+
"update",
|
|
6629
|
+
"remove",
|
|
6630
|
+
"findOneAndRemove",
|
|
6631
|
+
"findByIdAndRemove"
|
|
6632
|
+
];
|
|
6633
|
+
var MODEL_METHODS_ALT = MODEL_METHODS.join("|");
|
|
6634
|
+
function schemaCollectionVars(content) {
|
|
6635
|
+
const out = /* @__PURE__ */ new Map();
|
|
6636
|
+
const re = /(?:const|let|var)\s+(\w+)\s*=\s*new\s+(?:mongoose\s*\.\s*)?Schema\s*\(/g;
|
|
6637
|
+
let m;
|
|
6638
|
+
while ((m = re.exec(content)) !== null) {
|
|
6639
|
+
const windowText = content.slice(m.index, m.index + 2e3);
|
|
6640
|
+
const opt = /\bcollection\s*:\s*['"`]([\w.-]+)['"`]/.exec(windowText);
|
|
6641
|
+
if (opt) out.set(m[1], opt[1]);
|
|
6642
|
+
}
|
|
6643
|
+
return out;
|
|
6644
|
+
}
|
|
6645
|
+
function resolveModel(modelName, rest, schemaColl, pluralizeOn) {
|
|
6646
|
+
const trimmed = rest.trim();
|
|
6647
|
+
const literalThird = /,\s*['"`]([\w.-]+)['"`]\s*$/.exec(trimmed);
|
|
6648
|
+
if (literalThird) return { name: literalThird[1], kind: "mongodb-collection" };
|
|
6649
|
+
const firstIdent = /^([A-Za-z_$][\w$]*)/.exec(trimmed)?.[1];
|
|
6650
|
+
if (firstIdent && schemaColl.has(firstIdent)) {
|
|
6651
|
+
return { name: schemaColl.get(firstIdent), kind: "mongodb-collection" };
|
|
6652
|
+
}
|
|
6653
|
+
if (/,\s*[A-Za-z_$][\w$]*\s*$/.test(trimmed)) {
|
|
6654
|
+
return { name: modelName, kind: "mongodb-model" };
|
|
6655
|
+
}
|
|
6656
|
+
return { name: pluralizeOn ? pluralizeCollection(modelName) : modelName, kind: "mongodb-collection" };
|
|
6657
|
+
}
|
|
6658
|
+
function collectModelDefs(content, pluralizeOn) {
|
|
6659
|
+
const schemaColl = schemaCollectionVars(content);
|
|
6660
|
+
const out = [];
|
|
6661
|
+
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;
|
|
6662
|
+
let m;
|
|
6663
|
+
while ((m = re.exec(content)) !== null) {
|
|
6664
|
+
out.push({
|
|
6665
|
+
modelName: m[2],
|
|
6666
|
+
varName: m[1] ?? null,
|
|
6667
|
+
resolved: resolveModel(m[2], m[3] ?? "", schemaColl, pluralizeOn),
|
|
6668
|
+
matchText: m[0]
|
|
6669
|
+
});
|
|
6670
|
+
}
|
|
6671
|
+
return out;
|
|
6672
|
+
}
|
|
6673
|
+
function endpoint(r, file, serviceDir, matchText) {
|
|
6674
|
+
const line = lineOf(file.content, matchText);
|
|
6675
|
+
return {
|
|
6676
|
+
infraId: (0, import_types20.infraId)(r.kind, r.name),
|
|
6677
|
+
name: r.name,
|
|
6678
|
+
kind: r.kind,
|
|
6679
|
+
edgeType: "CALLS",
|
|
6680
|
+
confidenceKind: "verified-call-site",
|
|
6681
|
+
evidence: { file: import_node_path32.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
|
|
6682
|
+
};
|
|
6683
|
+
}
|
|
6684
|
+
function mongooseEndpointsFromFile(file, serviceDir) {
|
|
6685
|
+
const hasMongoose = MONGOOSE_IMPORT_RE.test(file.content);
|
|
6686
|
+
const hasMongodb = MONGODB_IMPORT_RE.test(file.content);
|
|
6687
|
+
if (!hasMongoose && !hasMongodb) return [];
|
|
6688
|
+
const content = file.content;
|
|
6689
|
+
const out = [];
|
|
6690
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6691
|
+
const push = (r, matchText) => {
|
|
6692
|
+
const key = `${r.kind}/${r.name}`;
|
|
6693
|
+
if (seen.has(key)) return;
|
|
6694
|
+
seen.add(key);
|
|
6695
|
+
out.push(endpoint(r, file, serviceDir, matchText));
|
|
6696
|
+
};
|
|
6697
|
+
if (hasMongoose) {
|
|
6698
|
+
const pluralizeOn = !PLURALIZE_DISABLED_RE.test(content);
|
|
6699
|
+
for (const def of collectModelDefs(content, pluralizeOn)) push(def.resolved, def.matchText);
|
|
6700
|
+
}
|
|
6701
|
+
const collRe = /\.\s*collection\s*\(\s*['"`]([\w.$-]+)['"`]\s*\)/g;
|
|
6702
|
+
let c;
|
|
6703
|
+
while ((c = collRe.exec(content)) !== null) {
|
|
6704
|
+
push({ name: c[1], kind: "mongodb-collection" }, c[0]);
|
|
6705
|
+
}
|
|
6706
|
+
return out;
|
|
6707
|
+
}
|
|
6708
|
+
function parseDestructure(inner, specifier, out) {
|
|
6709
|
+
for (const raw of inner.split(",")) {
|
|
6710
|
+
const part = raw.trim();
|
|
6711
|
+
if (!part) continue;
|
|
6712
|
+
const asM = /^(\w+)\s+as\s+(\w+)$/.exec(part);
|
|
6713
|
+
const colonM = /^(\w+)\s*:\s*(\w+)$/.exec(part);
|
|
6714
|
+
if (asM) out.push({ local: asM[2], kind: "named", exportName: asM[1], specifier });
|
|
6715
|
+
else if (colonM) out.push({ local: colonM[2], kind: "named", exportName: colonM[1], specifier });
|
|
6716
|
+
else {
|
|
6717
|
+
const id = /^(\w+)$/.exec(part);
|
|
6718
|
+
if (id) out.push({ local: id[1], kind: "named", exportName: id[1], specifier });
|
|
6719
|
+
}
|
|
6720
|
+
}
|
|
6721
|
+
}
|
|
6722
|
+
function parseImportBindings(content) {
|
|
6723
|
+
const out = [];
|
|
6724
|
+
const esm = /import\s+([^;'"`\n]+?)\s+from\s*['"`]([^'"`]+)['"`]/g;
|
|
6725
|
+
let m;
|
|
6726
|
+
while ((m = esm.exec(content)) !== null) {
|
|
6727
|
+
const clause = m[1].trim();
|
|
6728
|
+
const spec = m[2];
|
|
6729
|
+
const ns = /^\*\s+as\s+(\w+)$/.exec(clause);
|
|
6730
|
+
if (ns) {
|
|
6731
|
+
out.push({ local: ns[1], kind: "namespace", specifier: spec });
|
|
6732
|
+
continue;
|
|
6733
|
+
}
|
|
6734
|
+
const named = /\{([^}]*)\}/.exec(clause);
|
|
6735
|
+
if (named) parseDestructure(named[1], spec, out);
|
|
6736
|
+
const def = /^(\w+)\s*(?:,|$)/.exec(clause);
|
|
6737
|
+
if (def && !clause.startsWith("{")) out.push({ local: def[1], kind: "default", specifier: spec });
|
|
6738
|
+
}
|
|
6739
|
+
const cjs = /(?:const|let|var)\s+(\{[^}]*\}|\w+)\s*=\s*require\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
|
|
6740
|
+
while ((m = cjs.exec(content)) !== null) {
|
|
6741
|
+
const lhs = m[1];
|
|
6742
|
+
const spec = m[2];
|
|
6743
|
+
if (lhs.startsWith("{")) parseDestructure(lhs.slice(1, -1), spec, out);
|
|
6744
|
+
else out.push({ local: lhs, kind: "whole", specifier: spec });
|
|
6745
|
+
}
|
|
6746
|
+
return out;
|
|
6747
|
+
}
|
|
6748
|
+
function fileExportsOf(content, pluralizeOn) {
|
|
6749
|
+
const defs = collectModelDefs(content, pluralizeOn).filter((d) => d.resolved.kind === "mongodb-collection");
|
|
6750
|
+
if (defs.length === 0) return null;
|
|
6751
|
+
const byVar = /* @__PURE__ */ new Map();
|
|
6752
|
+
for (const d of defs) if (d.varName) byVar.set(d.varName, d.resolved.name);
|
|
6753
|
+
const byName = new Map(byVar);
|
|
6754
|
+
let def;
|
|
6755
|
+
const named = /(?:module\.exports|export\s+default)\s*=\s*(\w+)\b/.exec(content);
|
|
6756
|
+
if (named && byVar.has(named[1])) def = byVar.get(named[1]);
|
|
6757
|
+
if (!def) {
|
|
6758
|
+
const inline = /(?:module\.exports|export\s+default)\s*=\s*(?:await\s+)?(?:\w+\s*\.\s*)?model\s*\(\s*['"`]([\w$]+)['"`]/.exec(content);
|
|
6759
|
+
if (inline) def = pluralizeOn ? pluralizeCollection(inline[1]) : inline[1];
|
|
6760
|
+
}
|
|
6761
|
+
if (!def && byVar.size === 1) def = [...byVar.values()][0];
|
|
6762
|
+
return { byName, ...def ? { default: def } : {} };
|
|
6763
|
+
}
|
|
6764
|
+
async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
6765
|
+
const mongooseFiles = files.filter((f) => MONGOOSE_IMPORT_RE.test(f.content));
|
|
6766
|
+
if (mongooseFiles.length === 0) return [];
|
|
6767
|
+
const pluralizeOn = !files.some((f) => PLURALIZE_DISABLED_RE.test(f.content));
|
|
6768
|
+
const registry = /* @__PURE__ */ new Map();
|
|
6769
|
+
for (const f of mongooseFiles) {
|
|
6770
|
+
const fx = fileExportsOf(f.content, pluralizeOn);
|
|
6771
|
+
if (fx) registry.set(toPosix2(import_node_path32.default.relative(serviceDir, f.path)), fx);
|
|
6772
|
+
}
|
|
6773
|
+
if (registry.size === 0) return [];
|
|
6774
|
+
const out = [];
|
|
6775
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6776
|
+
for (const f of files) {
|
|
6777
|
+
const bindings = parseImportBindings(f.content);
|
|
6778
|
+
if (bindings.length === 0) continue;
|
|
6779
|
+
const directColl = /* @__PURE__ */ new Map();
|
|
6780
|
+
const nsExports = /* @__PURE__ */ new Map();
|
|
6781
|
+
for (const b of bindings) {
|
|
6782
|
+
const resolvedRel = await resolveJsImport(b.specifier, import_node_path32.default.dirname(f.path), serviceDir, null);
|
|
6783
|
+
if (!resolvedRel) continue;
|
|
6784
|
+
const fx = registry.get(resolvedRel);
|
|
6785
|
+
if (!fx) continue;
|
|
6786
|
+
if (b.kind === "named" && b.exportName) {
|
|
6787
|
+
const coll = fx.byName.get(b.exportName);
|
|
6788
|
+
if (coll) directColl.set(b.local, coll);
|
|
6789
|
+
} else if (b.kind === "default") {
|
|
6790
|
+
if (fx.default) directColl.set(b.local, fx.default);
|
|
6791
|
+
} else {
|
|
6792
|
+
if (fx.default) directColl.set(b.local, fx.default);
|
|
6793
|
+
nsExports.set(b.local, fx);
|
|
6794
|
+
}
|
|
6795
|
+
}
|
|
6796
|
+
if (directColl.size === 0 && nsExports.size === 0) continue;
|
|
6797
|
+
const localDefs = new Set(
|
|
6798
|
+
collectModelDefs(f.content, pluralizeOn).map((d) => d.varName).filter((v) => v !== null)
|
|
6799
|
+
);
|
|
6800
|
+
const emit = (collection, matchText) => {
|
|
6801
|
+
const key = `${f.path}::${collection}`;
|
|
6802
|
+
if (seen.has(key)) return;
|
|
6803
|
+
seen.add(key);
|
|
6804
|
+
out.push(endpoint({ name: collection, kind: "mongodb-collection" }, f, serviceDir, matchText));
|
|
6805
|
+
};
|
|
6806
|
+
for (const [local, collection] of directColl) {
|
|
6807
|
+
if (localDefs.has(local)) continue;
|
|
6808
|
+
const qre = new RegExp(`\\b${local}\\s*\\.\\s*(?:${MODEL_METHODS_ALT})\\s*\\(`, "g");
|
|
6809
|
+
let qm;
|
|
6810
|
+
while ((qm = qre.exec(f.content)) !== null) emit(collection, qm[0]);
|
|
6811
|
+
}
|
|
6812
|
+
for (const [local, fx] of nsExports) {
|
|
6813
|
+
const qre = new RegExp(`\\b${local}\\s*\\.\\s*(\\w+)\\s*\\.\\s*(?:${MODEL_METHODS_ALT})\\s*\\(`, "g");
|
|
6814
|
+
let qm;
|
|
6815
|
+
while ((qm = qre.exec(f.content)) !== null) {
|
|
6816
|
+
const coll = fx.byName.get(qm[1]);
|
|
6817
|
+
if (coll) emit(coll, qm[0]);
|
|
6818
|
+
}
|
|
6819
|
+
}
|
|
6820
|
+
}
|
|
6821
|
+
return out;
|
|
6822
|
+
}
|
|
6823
|
+
|
|
6497
6824
|
// src/extract/calls/index.ts
|
|
6498
6825
|
function edgeTypeFromEndpoint(ep) {
|
|
6499
6826
|
switch (ep.edgeType) {
|
|
6500
6827
|
case "PUBLISHES_TO":
|
|
6501
|
-
return
|
|
6828
|
+
return import_types21.EdgeType.PUBLISHES_TO;
|
|
6502
6829
|
case "CONSUMES_FROM":
|
|
6503
|
-
return
|
|
6830
|
+
return import_types21.EdgeType.CONSUMES_FROM;
|
|
6504
6831
|
default:
|
|
6505
|
-
return
|
|
6832
|
+
return import_types21.EdgeType.CALLS;
|
|
6506
6833
|
}
|
|
6507
6834
|
}
|
|
6508
6835
|
function isAwsKind(kind) {
|
|
@@ -6514,23 +6841,27 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6514
6841
|
for (const service of services) {
|
|
6515
6842
|
const files = await loadSourceFiles(service.dir);
|
|
6516
6843
|
const endpoints = [];
|
|
6844
|
+
const maskedFiles = [];
|
|
6517
6845
|
for (const file of files) {
|
|
6518
6846
|
if (isTestPath(file.path)) continue;
|
|
6519
6847
|
const masked = maskCommentsInSource(file.content);
|
|
6520
6848
|
const maskedFile = { path: file.path, content: masked };
|
|
6849
|
+
maskedFiles.push(maskedFile);
|
|
6521
6850
|
endpoints.push(...kafkaEndpointsFromFile(maskedFile, service.dir));
|
|
6522
6851
|
endpoints.push(...redisEndpointsFromFile(maskedFile, service.dir));
|
|
6523
6852
|
endpoints.push(...awsEndpointsFromFile(maskedFile, service.dir));
|
|
6524
6853
|
endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir));
|
|
6525
6854
|
endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir));
|
|
6855
|
+
endpoints.push(...mongooseEndpointsFromFile(maskedFile, service.dir));
|
|
6526
6856
|
}
|
|
6857
|
+
endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
|
|
6527
6858
|
if (endpoints.length === 0) continue;
|
|
6528
6859
|
const seenEdges = /* @__PURE__ */ new Set();
|
|
6529
6860
|
for (const ep of endpoints) {
|
|
6530
6861
|
if (!graph.hasNode(ep.infraId)) {
|
|
6531
6862
|
const node = {
|
|
6532
6863
|
id: ep.infraId,
|
|
6533
|
-
type:
|
|
6864
|
+
type: import_types21.NodeType.InfraNode,
|
|
6534
6865
|
name: ep.name,
|
|
6535
6866
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
6536
6867
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -6542,7 +6873,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6542
6873
|
nodesAdded++;
|
|
6543
6874
|
}
|
|
6544
6875
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
6545
|
-
const confidence = (0,
|
|
6876
|
+
const confidence = (0, import_types21.confidenceForExtracted)(ep.confidenceKind);
|
|
6546
6877
|
const relFile = toPosix2(ep.evidence.file);
|
|
6547
6878
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
6548
6879
|
graph,
|
|
@@ -6552,7 +6883,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6552
6883
|
);
|
|
6553
6884
|
nodesAdded += n;
|
|
6554
6885
|
edgesAdded += e;
|
|
6555
|
-
if (!(0,
|
|
6886
|
+
if (!(0, import_types21.passesExtractedFloor)(confidence)) {
|
|
6556
6887
|
noteExtractedDropped({
|
|
6557
6888
|
source: fileNodeId,
|
|
6558
6889
|
target: ep.infraId,
|
|
@@ -6572,7 +6903,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6572
6903
|
source: fileNodeId,
|
|
6573
6904
|
target: ep.infraId,
|
|
6574
6905
|
type: edgeType,
|
|
6575
|
-
provenance:
|
|
6906
|
+
provenance: import_types21.Provenance.EXTRACTED,
|
|
6576
6907
|
confidence,
|
|
6577
6908
|
evidence: ep.evidence
|
|
6578
6909
|
};
|
|
@@ -6598,16 +6929,16 @@ init_cjs_shims();
|
|
|
6598
6929
|
|
|
6599
6930
|
// src/extract/infra/docker-compose.ts
|
|
6600
6931
|
init_cjs_shims();
|
|
6601
|
-
var
|
|
6602
|
-
var
|
|
6932
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
6933
|
+
var import_types23 = require("@neat.is/types");
|
|
6603
6934
|
|
|
6604
6935
|
// src/extract/infra/shared.ts
|
|
6605
6936
|
init_cjs_shims();
|
|
6606
|
-
var
|
|
6937
|
+
var import_types22 = require("@neat.is/types");
|
|
6607
6938
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
6608
6939
|
return {
|
|
6609
|
-
id: (0,
|
|
6610
|
-
type:
|
|
6940
|
+
id: (0, import_types22.infraId)(kind, name),
|
|
6941
|
+
type: import_types22.NodeType.InfraNode,
|
|
6611
6942
|
name,
|
|
6612
6943
|
provider,
|
|
6613
6944
|
kind,
|
|
@@ -6651,8 +6982,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
|
|
|
6651
6982
|
source: anchorId,
|
|
6652
6983
|
target: node.id,
|
|
6653
6984
|
type: edgeType,
|
|
6654
|
-
provenance:
|
|
6655
|
-
confidence: (0,
|
|
6985
|
+
provenance: import_types22.Provenance.EXTRACTED,
|
|
6986
|
+
confidence: (0, import_types22.confidenceForExtracted)("structural"),
|
|
6656
6987
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
6657
6988
|
};
|
|
6658
6989
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6669,7 +7000,7 @@ function dependsOnList(value) {
|
|
|
6669
7000
|
}
|
|
6670
7001
|
function serviceNameToServiceNode(name, services) {
|
|
6671
7002
|
for (const s of services) {
|
|
6672
|
-
if (s.node.name === name ||
|
|
7003
|
+
if (s.node.name === name || import_node_path33.default.basename(s.dir) === name) return s.node.id;
|
|
6673
7004
|
}
|
|
6674
7005
|
return null;
|
|
6675
7006
|
}
|
|
@@ -6678,7 +7009,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6678
7009
|
let edgesAdded = 0;
|
|
6679
7010
|
let composePath = null;
|
|
6680
7011
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
6681
|
-
const abs =
|
|
7012
|
+
const abs = import_node_path33.default.join(scanPath, name);
|
|
6682
7013
|
if (await exists(abs)) {
|
|
6683
7014
|
composePath = abs;
|
|
6684
7015
|
break;
|
|
@@ -6691,13 +7022,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6691
7022
|
} catch (err) {
|
|
6692
7023
|
recordExtractionError(
|
|
6693
7024
|
"infra docker-compose",
|
|
6694
|
-
|
|
7025
|
+
import_node_path33.default.relative(scanPath, composePath),
|
|
6695
7026
|
err
|
|
6696
7027
|
);
|
|
6697
7028
|
return { nodesAdded, edgesAdded };
|
|
6698
7029
|
}
|
|
6699
7030
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
6700
|
-
const evidenceFile =
|
|
7031
|
+
const evidenceFile = import_node_path33.default.relative(scanPath, composePath).split(import_node_path33.default.sep).join("/");
|
|
6701
7032
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
6702
7033
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
6703
7034
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -6719,15 +7050,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6719
7050
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
6720
7051
|
const targetId = composeNameToNodeId.get(dep);
|
|
6721
7052
|
if (!targetId) continue;
|
|
6722
|
-
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId,
|
|
7053
|
+
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types23.EdgeType.DEPENDS_ON);
|
|
6723
7054
|
if (graph.hasEdge(edgeId)) continue;
|
|
6724
7055
|
const edge = {
|
|
6725
7056
|
id: edgeId,
|
|
6726
7057
|
source: sourceId,
|
|
6727
7058
|
target: targetId,
|
|
6728
|
-
type:
|
|
6729
|
-
provenance:
|
|
6730
|
-
confidence: (0,
|
|
7059
|
+
type: import_types23.EdgeType.DEPENDS_ON,
|
|
7060
|
+
provenance: import_types23.Provenance.EXTRACTED,
|
|
7061
|
+
confidence: (0, import_types23.confidenceForExtracted)("structural"),
|
|
6731
7062
|
evidence: { file: evidenceFile }
|
|
6732
7063
|
};
|
|
6733
7064
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6739,9 +7070,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6739
7070
|
|
|
6740
7071
|
// src/extract/infra/dockerfile.ts
|
|
6741
7072
|
init_cjs_shims();
|
|
6742
|
-
var
|
|
7073
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
6743
7074
|
var import_node_fs17 = require("fs");
|
|
6744
|
-
var
|
|
7075
|
+
var import_types24 = require("@neat.is/types");
|
|
6745
7076
|
function readDockerfile(content) {
|
|
6746
7077
|
let image = null;
|
|
6747
7078
|
const ports = [];
|
|
@@ -6770,7 +7101,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6770
7101
|
let nodesAdded = 0;
|
|
6771
7102
|
let edgesAdded = 0;
|
|
6772
7103
|
for (const service of services) {
|
|
6773
|
-
const dockerfilePath =
|
|
7104
|
+
const dockerfilePath = import_node_path34.default.join(service.dir, "Dockerfile");
|
|
6774
7105
|
if (!await exists(dockerfilePath)) continue;
|
|
6775
7106
|
let content;
|
|
6776
7107
|
try {
|
|
@@ -6778,7 +7109,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6778
7109
|
} catch (err) {
|
|
6779
7110
|
recordExtractionError(
|
|
6780
7111
|
"infra dockerfile",
|
|
6781
|
-
|
|
7112
|
+
import_node_path34.default.relative(scanPath, dockerfilePath),
|
|
6782
7113
|
err
|
|
6783
7114
|
);
|
|
6784
7115
|
continue;
|
|
@@ -6790,8 +7121,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6790
7121
|
graph.addNode(node.id, node);
|
|
6791
7122
|
nodesAdded++;
|
|
6792
7123
|
}
|
|
6793
|
-
const relDockerfile = toPosix2(
|
|
6794
|
-
const evidenceFile = toPosix2(
|
|
7124
|
+
const relDockerfile = toPosix2(import_node_path34.default.relative(service.dir, dockerfilePath));
|
|
7125
|
+
const evidenceFile = toPosix2(import_node_path34.default.relative(scanPath, dockerfilePath));
|
|
6795
7126
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
6796
7127
|
graph,
|
|
6797
7128
|
service.pkg.name,
|
|
@@ -6800,15 +7131,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6800
7131
|
);
|
|
6801
7132
|
nodesAdded += fn;
|
|
6802
7133
|
edgesAdded += fe;
|
|
6803
|
-
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id,
|
|
7134
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types24.EdgeType.RUNS_ON);
|
|
6804
7135
|
if (!graph.hasEdge(edgeId)) {
|
|
6805
7136
|
const edge = {
|
|
6806
7137
|
id: edgeId,
|
|
6807
7138
|
source: fileNodeId,
|
|
6808
7139
|
target: node.id,
|
|
6809
|
-
type:
|
|
6810
|
-
provenance:
|
|
6811
|
-
confidence: (0,
|
|
7140
|
+
type: import_types24.EdgeType.RUNS_ON,
|
|
7141
|
+
provenance: import_types24.Provenance.EXTRACTED,
|
|
7142
|
+
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
6812
7143
|
evidence: {
|
|
6813
7144
|
file: evidenceFile,
|
|
6814
7145
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -6823,15 +7154,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6823
7154
|
graph.addNode(portNode.id, portNode);
|
|
6824
7155
|
nodesAdded++;
|
|
6825
7156
|
}
|
|
6826
|
-
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id,
|
|
7157
|
+
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id, import_types24.EdgeType.CONNECTS_TO);
|
|
6827
7158
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
6828
7159
|
const portEdge = {
|
|
6829
7160
|
id: portEdgeId,
|
|
6830
7161
|
source: fileNodeId,
|
|
6831
7162
|
target: portNode.id,
|
|
6832
|
-
type:
|
|
6833
|
-
provenance:
|
|
6834
|
-
confidence: (0,
|
|
7163
|
+
type: import_types24.EdgeType.CONNECTS_TO,
|
|
7164
|
+
provenance: import_types24.Provenance.EXTRACTED,
|
|
7165
|
+
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
6835
7166
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
6836
7167
|
};
|
|
6837
7168
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -6844,8 +7175,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6844
7175
|
// src/extract/infra/terraform.ts
|
|
6845
7176
|
init_cjs_shims();
|
|
6846
7177
|
var import_node_fs18 = require("fs");
|
|
6847
|
-
var
|
|
6848
|
-
var
|
|
7178
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
7179
|
+
var import_types25 = require("@neat.is/types");
|
|
6849
7180
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
6850
7181
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
6851
7182
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
@@ -6855,11 +7186,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
6855
7186
|
for (const entry2 of entries) {
|
|
6856
7187
|
if (entry2.isDirectory()) {
|
|
6857
7188
|
if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
|
|
6858
|
-
const child =
|
|
7189
|
+
const child = import_node_path35.default.join(start, entry2.name);
|
|
6859
7190
|
if (await isPythonVenvDir(child)) continue;
|
|
6860
7191
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
6861
7192
|
} else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
|
|
6862
|
-
out.push(
|
|
7193
|
+
out.push(import_node_path35.default.join(start, entry2.name));
|
|
6863
7194
|
}
|
|
6864
7195
|
}
|
|
6865
7196
|
return out;
|
|
@@ -6891,7 +7222,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6891
7222
|
const files = await walkTfFiles(scanPath);
|
|
6892
7223
|
for (const file of files) {
|
|
6893
7224
|
const content = await import_node_fs18.promises.readFile(file, "utf8");
|
|
6894
|
-
const evidenceFile = toPosix2(
|
|
7225
|
+
const evidenceFile = toPosix2(import_node_path35.default.relative(scanPath, file));
|
|
6895
7226
|
const resources = [];
|
|
6896
7227
|
const byKey = /* @__PURE__ */ new Map();
|
|
6897
7228
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -6926,16 +7257,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6926
7257
|
if (!target) continue;
|
|
6927
7258
|
if (seen.has(target.nodeId)) continue;
|
|
6928
7259
|
seen.add(target.nodeId);
|
|
6929
|
-
const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId,
|
|
7260
|
+
const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId, import_types25.EdgeType.DEPENDS_ON);
|
|
6930
7261
|
if (graph.hasEdge(edgeId)) continue;
|
|
6931
7262
|
const line = lineAt2(content, resource.bodyOffset + ref.index);
|
|
6932
7263
|
const edge = {
|
|
6933
7264
|
id: edgeId,
|
|
6934
7265
|
source: resource.nodeId,
|
|
6935
7266
|
target: target.nodeId,
|
|
6936
|
-
type:
|
|
6937
|
-
provenance:
|
|
6938
|
-
confidence: (0,
|
|
7267
|
+
type: import_types25.EdgeType.DEPENDS_ON,
|
|
7268
|
+
provenance: import_types25.Provenance.EXTRACTED,
|
|
7269
|
+
confidence: (0, import_types25.confidenceForExtracted)("structural"),
|
|
6939
7270
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
6940
7271
|
};
|
|
6941
7272
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6949,7 +7280,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6949
7280
|
// src/extract/infra/k8s.ts
|
|
6950
7281
|
init_cjs_shims();
|
|
6951
7282
|
var import_node_fs19 = require("fs");
|
|
6952
|
-
var
|
|
7283
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
6953
7284
|
var import_yaml3 = require("yaml");
|
|
6954
7285
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
6955
7286
|
Service: "k8s-service",
|
|
@@ -6967,11 +7298,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
6967
7298
|
for (const entry2 of entries) {
|
|
6968
7299
|
if (entry2.isDirectory()) {
|
|
6969
7300
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
6970
|
-
const child =
|
|
7301
|
+
const child = import_node_path36.default.join(start, entry2.name);
|
|
6971
7302
|
if (await isPythonVenvDir(child)) continue;
|
|
6972
7303
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
6973
|
-
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
6974
|
-
out.push(
|
|
7304
|
+
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path36.default.extname(entry2.name))) {
|
|
7305
|
+
out.push(import_node_path36.default.join(start, entry2.name));
|
|
6975
7306
|
}
|
|
6976
7307
|
}
|
|
6977
7308
|
return out;
|
|
@@ -7005,13 +7336,13 @@ async function addK8sResources(graph, scanPath) {
|
|
|
7005
7336
|
// src/extract/infra/cloudflare.ts
|
|
7006
7337
|
init_cjs_shims();
|
|
7007
7338
|
var import_node_fs20 = require("fs");
|
|
7008
|
-
var
|
|
7339
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
7009
7340
|
var import_smol_toml2 = require("smol-toml");
|
|
7010
|
-
var
|
|
7341
|
+
var import_types26 = require("@neat.is/types");
|
|
7011
7342
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
7012
7343
|
async function readWranglerConfig(dir) {
|
|
7013
7344
|
for (const filename of WRANGLER_FILENAMES) {
|
|
7014
|
-
const abs =
|
|
7345
|
+
const abs = import_node_path37.default.join(dir, filename);
|
|
7015
7346
|
if (!await exists(abs)) continue;
|
|
7016
7347
|
const raw = await import_node_fs20.promises.readFile(abs, "utf8");
|
|
7017
7348
|
const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7055,8 +7386,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
|
|
|
7055
7386
|
source: anchorId,
|
|
7056
7387
|
target: node.id,
|
|
7057
7388
|
type: edgeType,
|
|
7058
|
-
provenance:
|
|
7059
|
-
confidence: (0,
|
|
7389
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7390
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7060
7391
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
7061
7392
|
};
|
|
7062
7393
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7074,11 +7405,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7074
7405
|
try {
|
|
7075
7406
|
read = await readWranglerConfig(service.dir);
|
|
7076
7407
|
} catch (err) {
|
|
7077
|
-
recordExtractionError("infra cloudflare",
|
|
7408
|
+
recordExtractionError("infra cloudflare", import_node_path37.default.relative(scanPath, service.dir), err);
|
|
7078
7409
|
continue;
|
|
7079
7410
|
}
|
|
7080
7411
|
if (!read || !read.config.name) continue;
|
|
7081
|
-
const evidenceFile = toPosix2(
|
|
7412
|
+
const evidenceFile = toPosix2(import_node_path37.default.relative(scanPath, import_node_path37.default.join(service.dir, read.relFile)));
|
|
7082
7413
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
7083
7414
|
}
|
|
7084
7415
|
for (const worker of discovered) {
|
|
@@ -7090,7 +7421,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7090
7421
|
}
|
|
7091
7422
|
let anchorId = service.node.id;
|
|
7092
7423
|
if (config.main) {
|
|
7093
|
-
const entryRelPath = toPosix2(
|
|
7424
|
+
const entryRelPath = toPosix2(import_node_path37.default.normalize(config.main));
|
|
7094
7425
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
7095
7426
|
graph,
|
|
7096
7427
|
service.pkg.name,
|
|
@@ -7117,15 +7448,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7117
7448
|
nodesAdded++;
|
|
7118
7449
|
}
|
|
7119
7450
|
if (runtimeNode.id !== anchorId) {
|
|
7120
|
-
const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id,
|
|
7451
|
+
const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id, import_types26.EdgeType.RUNS_ON);
|
|
7121
7452
|
if (!graph.hasEdge(runsOnId)) {
|
|
7122
7453
|
const edge = {
|
|
7123
7454
|
id: runsOnId,
|
|
7124
7455
|
source: anchorId,
|
|
7125
7456
|
target: runtimeNode.id,
|
|
7126
|
-
type:
|
|
7127
|
-
provenance:
|
|
7128
|
-
confidence: (0,
|
|
7457
|
+
type: import_types26.EdgeType.RUNS_ON,
|
|
7458
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7459
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7129
7460
|
evidence: {
|
|
7130
7461
|
file: evidenceFile,
|
|
7131
7462
|
...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
|
|
@@ -7139,7 +7470,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7139
7470
|
const result = addResourceEdge(
|
|
7140
7471
|
graph,
|
|
7141
7472
|
anchorId,
|
|
7142
|
-
|
|
7473
|
+
import_types26.EdgeType.CONNECTS_TO,
|
|
7143
7474
|
"cloudflare-route",
|
|
7144
7475
|
route,
|
|
7145
7476
|
evidenceFile,
|
|
@@ -7163,7 +7494,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7163
7494
|
const result = addResourceEdge(
|
|
7164
7495
|
graph,
|
|
7165
7496
|
anchorId,
|
|
7166
|
-
|
|
7497
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7167
7498
|
group.kind,
|
|
7168
7499
|
name,
|
|
7169
7500
|
evidenceFile,
|
|
@@ -7177,7 +7508,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7177
7508
|
const result = addResourceEdge(
|
|
7178
7509
|
graph,
|
|
7179
7510
|
anchorId,
|
|
7180
|
-
|
|
7511
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7181
7512
|
"cloudflare-cron",
|
|
7182
7513
|
cron,
|
|
7183
7514
|
evidenceFile,
|
|
@@ -7190,7 +7521,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7190
7521
|
const result = addResourceEdge(
|
|
7191
7522
|
graph,
|
|
7192
7523
|
anchorId,
|
|
7193
|
-
|
|
7524
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7194
7525
|
"cloudflare-env-var",
|
|
7195
7526
|
varName,
|
|
7196
7527
|
evidenceFile,
|
|
@@ -7203,15 +7534,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7203
7534
|
if (!svc.service) continue;
|
|
7204
7535
|
const target = workerIndex.get(svc.service);
|
|
7205
7536
|
if (target && target.anchorId !== anchorId) {
|
|
7206
|
-
const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId,
|
|
7537
|
+
const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId, import_types26.EdgeType.CALLS);
|
|
7207
7538
|
if (!graph.hasEdge(edgeId)) {
|
|
7208
7539
|
const edge = {
|
|
7209
7540
|
id: edgeId,
|
|
7210
7541
|
source: anchorId,
|
|
7211
7542
|
target: target.anchorId,
|
|
7212
|
-
type:
|
|
7213
|
-
provenance:
|
|
7214
|
-
confidence: (0,
|
|
7543
|
+
type: import_types26.EdgeType.CALLS,
|
|
7544
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7545
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7215
7546
|
evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
|
|
7216
7547
|
};
|
|
7217
7548
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7222,7 +7553,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7222
7553
|
const result = addResourceEdge(
|
|
7223
7554
|
graph,
|
|
7224
7555
|
anchorId,
|
|
7225
|
-
|
|
7556
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7226
7557
|
"cloudflare-service-binding",
|
|
7227
7558
|
svc.service,
|
|
7228
7559
|
evidenceFile,
|
|
@@ -7238,12 +7569,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7238
7569
|
// src/extract/infra/vercel.ts
|
|
7239
7570
|
init_cjs_shims();
|
|
7240
7571
|
var import_node_fs21 = require("fs");
|
|
7241
|
-
var
|
|
7242
|
-
var
|
|
7572
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
7573
|
+
var import_types27 = require("@neat.is/types");
|
|
7243
7574
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
7244
7575
|
async function readVercelConfig(dir) {
|
|
7245
7576
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
7246
|
-
const abs =
|
|
7577
|
+
const abs = import_node_path38.default.join(dir, filename);
|
|
7247
7578
|
if (!await exists(abs)) continue;
|
|
7248
7579
|
const raw = await import_node_fs21.promises.readFile(abs, "utf8");
|
|
7249
7580
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7252,7 +7583,7 @@ async function readVercelConfig(dir) {
|
|
|
7252
7583
|
return null;
|
|
7253
7584
|
}
|
|
7254
7585
|
async function readLinkedProjectName(dir) {
|
|
7255
|
-
const abs =
|
|
7586
|
+
const abs = import_node_path38.default.join(dir, ".vercel", "project.json");
|
|
7256
7587
|
if (!await exists(abs)) return void 0;
|
|
7257
7588
|
const parsed = JSON.parse(await import_node_fs21.promises.readFile(abs, "utf8"));
|
|
7258
7589
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
@@ -7270,7 +7601,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7270
7601
|
read = await readVercelConfig(service.dir);
|
|
7271
7602
|
projectName = await readLinkedProjectName(service.dir);
|
|
7272
7603
|
} catch (err) {
|
|
7273
|
-
recordExtractionError("infra vercel",
|
|
7604
|
+
recordExtractionError("infra vercel", import_node_path38.default.relative(scanPath, service.dir), err);
|
|
7274
7605
|
continue;
|
|
7275
7606
|
}
|
|
7276
7607
|
if (!read && !projectName) continue;
|
|
@@ -7286,7 +7617,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7286
7617
|
const anchorId = service.node.id;
|
|
7287
7618
|
if (!read) continue;
|
|
7288
7619
|
const { config, relFile, raw } = read;
|
|
7289
|
-
const evidenceFile = toPosix2(
|
|
7620
|
+
const evidenceFile = toPosix2(import_node_path38.default.relative(scanPath, import_node_path38.default.join(service.dir, relFile)));
|
|
7290
7621
|
const add = (edgeType, kind, name) => {
|
|
7291
7622
|
if (!name) return;
|
|
7292
7623
|
const result = emitPlatformResourceEdge(
|
|
@@ -7302,12 +7633,12 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7302
7633
|
nodesAdded += result.nodesAdded;
|
|
7303
7634
|
edgesAdded += result.edgesAdded;
|
|
7304
7635
|
};
|
|
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(
|
|
7636
|
+
add(import_types27.EdgeType.RUNS_ON, "vercel", "vercel");
|
|
7637
|
+
for (const cron of config.crons ?? []) add(import_types27.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
|
|
7638
|
+
for (const varName of Object.keys(config.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
7639
|
+
for (const varName of Object.keys(config.build?.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
7309
7640
|
for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
|
|
7310
|
-
add(
|
|
7641
|
+
add(import_types27.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
|
|
7311
7642
|
}
|
|
7312
7643
|
}
|
|
7313
7644
|
return { nodesAdded, edgesAdded };
|
|
@@ -7316,13 +7647,13 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7316
7647
|
// src/extract/infra/railway.ts
|
|
7317
7648
|
init_cjs_shims();
|
|
7318
7649
|
var import_node_fs22 = require("fs");
|
|
7319
|
-
var
|
|
7650
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
7320
7651
|
var import_smol_toml3 = require("smol-toml");
|
|
7321
|
-
var
|
|
7652
|
+
var import_types28 = require("@neat.is/types");
|
|
7322
7653
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
7323
7654
|
async function readRailwayConfig(dir) {
|
|
7324
7655
|
for (const filename of RAILWAY_FILENAMES) {
|
|
7325
|
-
const abs =
|
|
7656
|
+
const abs = import_node_path39.default.join(dir, filename);
|
|
7326
7657
|
if (!await exists(abs)) continue;
|
|
7327
7658
|
const raw = await import_node_fs22.promises.readFile(abs, "utf8");
|
|
7328
7659
|
const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7338,7 +7669,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7338
7669
|
try {
|
|
7339
7670
|
read = await readRailwayConfig(service.dir);
|
|
7340
7671
|
} catch (err) {
|
|
7341
|
-
recordExtractionError("infra railway",
|
|
7672
|
+
recordExtractionError("infra railway", import_node_path39.default.relative(scanPath, service.dir), err);
|
|
7342
7673
|
continue;
|
|
7343
7674
|
}
|
|
7344
7675
|
if (!read) continue;
|
|
@@ -7348,7 +7679,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7348
7679
|
}
|
|
7349
7680
|
const anchorId = service.node.id;
|
|
7350
7681
|
const { config, relFile, raw } = read;
|
|
7351
|
-
const evidenceFile = toPosix2(
|
|
7682
|
+
const evidenceFile = toPosix2(import_node_path39.default.relative(scanPath, import_node_path39.default.join(service.dir, relFile)));
|
|
7352
7683
|
const add = (edgeType, kind, name) => {
|
|
7353
7684
|
if (!name) return;
|
|
7354
7685
|
const result = emitPlatformResourceEdge(
|
|
@@ -7364,9 +7695,9 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7364
7695
|
nodesAdded += result.nodesAdded;
|
|
7365
7696
|
edgesAdded += result.edgesAdded;
|
|
7366
7697
|
};
|
|
7367
|
-
add(
|
|
7368
|
-
add(
|
|
7369
|
-
add(
|
|
7698
|
+
add(import_types28.EdgeType.RUNS_ON, "railway", "railway");
|
|
7699
|
+
add(import_types28.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
|
|
7700
|
+
add(import_types28.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
|
|
7370
7701
|
}
|
|
7371
7702
|
return { nodesAdded, edgesAdded };
|
|
7372
7703
|
}
|
|
@@ -7374,12 +7705,12 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7374
7705
|
// src/extract/infra/supabase.ts
|
|
7375
7706
|
init_cjs_shims();
|
|
7376
7707
|
var import_node_fs23 = require("fs");
|
|
7377
|
-
var
|
|
7708
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
7378
7709
|
var import_smol_toml4 = require("smol-toml");
|
|
7379
|
-
var
|
|
7710
|
+
var import_types29 = require("@neat.is/types");
|
|
7380
7711
|
async function readSupabaseConfig(dir) {
|
|
7381
|
-
const relFile =
|
|
7382
|
-
const abs =
|
|
7712
|
+
const relFile = import_node_path40.default.join("supabase", "config.toml");
|
|
7713
|
+
const abs = import_node_path40.default.join(dir, relFile);
|
|
7383
7714
|
if (!await exists(abs)) return null;
|
|
7384
7715
|
const raw = await import_node_fs23.promises.readFile(abs, "utf8");
|
|
7385
7716
|
const config = (0, import_smol_toml4.parse)(raw);
|
|
@@ -7393,7 +7724,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7393
7724
|
try {
|
|
7394
7725
|
read = await readSupabaseConfig(service.dir);
|
|
7395
7726
|
} catch (err) {
|
|
7396
|
-
recordExtractionError("infra supabase",
|
|
7727
|
+
recordExtractionError("infra supabase", import_node_path40.default.relative(scanPath, service.dir), err);
|
|
7397
7728
|
continue;
|
|
7398
7729
|
}
|
|
7399
7730
|
if (!read) continue;
|
|
@@ -7408,7 +7739,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7408
7739
|
});
|
|
7409
7740
|
}
|
|
7410
7741
|
const anchorId = service.node.id;
|
|
7411
|
-
const evidenceFile = toPosix2(
|
|
7742
|
+
const evidenceFile = toPosix2(import_node_path40.default.relative(scanPath, import_node_path40.default.join(service.dir, relFile)));
|
|
7412
7743
|
const add = (edgeType, kind, name) => {
|
|
7413
7744
|
if (!name) return;
|
|
7414
7745
|
const result = emitPlatformResourceEdge(
|
|
@@ -7424,10 +7755,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7424
7755
|
nodesAdded += result.nodesAdded;
|
|
7425
7756
|
edgesAdded += result.edgesAdded;
|
|
7426
7757
|
};
|
|
7427
|
-
add(
|
|
7428
|
-
for (const fn of Object.keys(config.functions ?? {})) add(
|
|
7429
|
-
if (config.storage) add(
|
|
7430
|
-
if (config.auth) add(
|
|
7758
|
+
add(import_types29.EdgeType.RUNS_ON, "supabase", "supabase");
|
|
7759
|
+
for (const fn of Object.keys(config.functions ?? {})) add(import_types29.EdgeType.DEPENDS_ON, "supabase-function", fn);
|
|
7760
|
+
if (config.storage) add(import_types29.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
|
|
7761
|
+
if (config.auth) add(import_types29.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
|
|
7431
7762
|
}
|
|
7432
7763
|
return { nodesAdded, edgesAdded };
|
|
7433
7764
|
}
|
|
@@ -7449,17 +7780,17 @@ async function addInfra(graph, scanPath, services) {
|
|
|
7449
7780
|
}
|
|
7450
7781
|
|
|
7451
7782
|
// src/extract/index.ts
|
|
7452
|
-
var
|
|
7783
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
7453
7784
|
|
|
7454
7785
|
// src/extract/retire.ts
|
|
7455
7786
|
init_cjs_shims();
|
|
7456
7787
|
var import_node_fs24 = require("fs");
|
|
7457
|
-
var
|
|
7458
|
-
var
|
|
7788
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
7789
|
+
var import_types30 = require("@neat.is/types");
|
|
7459
7790
|
function dropOrphanedFileNodes(graph) {
|
|
7460
7791
|
const orphans = [];
|
|
7461
7792
|
graph.forEachNode((id, attrs) => {
|
|
7462
|
-
if (attrs.type !==
|
|
7793
|
+
if (attrs.type !== import_types30.NodeType.FileNode) return;
|
|
7463
7794
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
7464
7795
|
orphans.push(id);
|
|
7465
7796
|
}
|
|
@@ -7472,7 +7803,7 @@ function retireEdgesByFile(graph, file) {
|
|
|
7472
7803
|
const toDrop = [];
|
|
7473
7804
|
graph.forEachEdge((id, attrs) => {
|
|
7474
7805
|
const edge = attrs;
|
|
7475
|
-
if (edge.provenance !==
|
|
7806
|
+
if (edge.provenance !== import_types30.Provenance.EXTRACTED) return;
|
|
7476
7807
|
if (!edge.evidence?.file) return;
|
|
7477
7808
|
if (edge.evidence.file === normalized) toDrop.push(id);
|
|
7478
7809
|
});
|
|
@@ -7485,14 +7816,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
7485
7816
|
const bases = [scanPath, ...serviceDirs];
|
|
7486
7817
|
graph.forEachEdge((id, attrs) => {
|
|
7487
7818
|
const edge = attrs;
|
|
7488
|
-
if (edge.provenance !==
|
|
7819
|
+
if (edge.provenance !== import_types30.Provenance.EXTRACTED) return;
|
|
7489
7820
|
const evidenceFile = edge.evidence?.file;
|
|
7490
7821
|
if (!evidenceFile) return;
|
|
7491
|
-
if (
|
|
7822
|
+
if (import_node_path41.default.isAbsolute(evidenceFile)) {
|
|
7492
7823
|
if (!(0, import_node_fs24.existsSync)(evidenceFile)) toDrop.push(id);
|
|
7493
7824
|
return;
|
|
7494
7825
|
}
|
|
7495
|
-
const found = bases.some((base) => (0, import_node_fs24.existsSync)(
|
|
7826
|
+
const found = bases.some((base) => (0, import_node_fs24.existsSync)(import_node_path41.default.join(base, evidenceFile)));
|
|
7496
7827
|
if (!found) toDrop.push(id);
|
|
7497
7828
|
});
|
|
7498
7829
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -7534,7 +7865,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
7534
7865
|
}
|
|
7535
7866
|
const droppedEntries = drainDroppedExtracted();
|
|
7536
7867
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
7537
|
-
const rejectedPath =
|
|
7868
|
+
const rejectedPath = import_node_path42.default.join(import_node_path42.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
7538
7869
|
try {
|
|
7539
7870
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
7540
7871
|
} catch (err) {
|
|
@@ -7568,39 +7899,39 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
7568
7899
|
|
|
7569
7900
|
// src/divergences.ts
|
|
7570
7901
|
init_cjs_shims();
|
|
7571
|
-
var
|
|
7902
|
+
var import_types31 = require("@neat.is/types");
|
|
7572
7903
|
function bucketKey(source, target, type) {
|
|
7573
7904
|
return `${type}|${source}|${target}`;
|
|
7574
7905
|
}
|
|
7575
7906
|
function bucketSourceFor(graph, edge) {
|
|
7576
|
-
if (edge.type !==
|
|
7577
|
-
const parsed = (0,
|
|
7907
|
+
if (edge.type !== import_types31.EdgeType.CONNECTS_TO) return edge.source;
|
|
7908
|
+
const parsed = (0, import_types31.parseFileId)(edge.source);
|
|
7578
7909
|
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
7579
7910
|
const target = graph.getNodeAttributes(edge.target);
|
|
7580
|
-
if (target.type !==
|
|
7581
|
-
return (0,
|
|
7911
|
+
if (target.type !== import_types31.NodeType.DatabaseNode) return edge.source;
|
|
7912
|
+
return (0, import_types31.serviceId)(parsed.service);
|
|
7582
7913
|
}
|
|
7583
7914
|
function bucketEdges(graph) {
|
|
7584
7915
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
7585
7916
|
graph.forEachEdge((id, attrs) => {
|
|
7586
7917
|
const e = attrs;
|
|
7587
|
-
const parsed = (0,
|
|
7918
|
+
const parsed = (0, import_types31.parseEdgeId)(id);
|
|
7588
7919
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
7589
7920
|
const source = bucketSourceFor(graph, e);
|
|
7590
7921
|
const key = bucketKey(source, e.target, e.type);
|
|
7591
7922
|
const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
|
|
7592
7923
|
switch (provenance) {
|
|
7593
|
-
case
|
|
7924
|
+
case import_types31.Provenance.EXTRACTED:
|
|
7594
7925
|
cur.extracted = e;
|
|
7595
7926
|
break;
|
|
7596
|
-
case
|
|
7927
|
+
case import_types31.Provenance.OBSERVED:
|
|
7597
7928
|
cur.observed = e;
|
|
7598
7929
|
break;
|
|
7599
|
-
case
|
|
7930
|
+
case import_types31.Provenance.INFERRED:
|
|
7600
7931
|
cur.inferred = e;
|
|
7601
7932
|
break;
|
|
7602
7933
|
default:
|
|
7603
|
-
if (e.provenance ===
|
|
7934
|
+
if (e.provenance === import_types31.Provenance.STALE) cur.stale = e;
|
|
7604
7935
|
}
|
|
7605
7936
|
buckets2.set(key, cur);
|
|
7606
7937
|
});
|
|
@@ -7609,12 +7940,12 @@ function bucketEdges(graph) {
|
|
|
7609
7940
|
function nodeIsFrontier(graph, nodeId) {
|
|
7610
7941
|
if (!graph.hasNode(nodeId)) return false;
|
|
7611
7942
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
7612
|
-
return attrs.type ===
|
|
7943
|
+
return attrs.type === import_types31.NodeType.FrontierNode;
|
|
7613
7944
|
}
|
|
7614
7945
|
function nodeIsWebsocketChannel(graph, nodeId) {
|
|
7615
7946
|
if (!graph.hasNode(nodeId)) return false;
|
|
7616
7947
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
7617
|
-
return attrs.type ===
|
|
7948
|
+
return attrs.type === import_types31.NodeType.WebSocketChannelNode;
|
|
7618
7949
|
}
|
|
7619
7950
|
function clampConfidence(n) {
|
|
7620
7951
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -7634,14 +7965,14 @@ function gradedConfidence(edge) {
|
|
|
7634
7965
|
return clampConfidence(confidenceForEdge(edge));
|
|
7635
7966
|
}
|
|
7636
7967
|
var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7968
|
+
import_types31.EdgeType.CALLS,
|
|
7969
|
+
import_types31.EdgeType.CONNECTS_TO,
|
|
7970
|
+
import_types31.EdgeType.PUBLISHES_TO,
|
|
7971
|
+
import_types31.EdgeType.CONSUMES_FROM
|
|
7641
7972
|
]);
|
|
7642
7973
|
function detectMissingDivergences(graph, bucket) {
|
|
7643
7974
|
const out = [];
|
|
7644
|
-
if (bucket.type ===
|
|
7975
|
+
if (bucket.type === import_types31.EdgeType.CONTAINS) return out;
|
|
7645
7976
|
if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
|
|
7646
7977
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
7647
7978
|
out.push({
|
|
@@ -7682,7 +8013,7 @@ function declaredHostFor(svc) {
|
|
|
7682
8013
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
7683
8014
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
7684
8015
|
const e = graph.getEdgeAttributes(edgeId);
|
|
7685
|
-
if (e.type ===
|
|
8016
|
+
if (e.type === import_types31.EdgeType.CONFIGURED_BY && e.provenance === import_types31.Provenance.EXTRACTED) {
|
|
7686
8017
|
return true;
|
|
7687
8018
|
}
|
|
7688
8019
|
}
|
|
@@ -7695,10 +8026,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
7695
8026
|
const out = [];
|
|
7696
8027
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
7697
8028
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
7698
|
-
if (edge.type !==
|
|
7699
|
-
if (edge.provenance !==
|
|
8029
|
+
if (edge.type !== import_types31.EdgeType.CONNECTS_TO) continue;
|
|
8030
|
+
if (edge.provenance !== import_types31.Provenance.OBSERVED) continue;
|
|
7700
8031
|
const target = graph.getNodeAttributes(edge.target);
|
|
7701
|
-
if (target.type !==
|
|
8032
|
+
if (target.type !== import_types31.NodeType.DatabaseNode) continue;
|
|
7702
8033
|
const observedHost = target.host?.trim();
|
|
7703
8034
|
if (!observedHost) continue;
|
|
7704
8035
|
if (observedHost === declaredHost) continue;
|
|
@@ -7720,10 +8051,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
7720
8051
|
const deps = svc.dependencies ?? {};
|
|
7721
8052
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
7722
8053
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
7723
|
-
if (edge.type !==
|
|
7724
|
-
if (edge.provenance !==
|
|
8054
|
+
if (edge.type !== import_types31.EdgeType.CONNECTS_TO) continue;
|
|
8055
|
+
if (edge.provenance !== import_types31.Provenance.OBSERVED) continue;
|
|
7725
8056
|
const target = graph.getNodeAttributes(edge.target);
|
|
7726
|
-
if (target.type !==
|
|
8057
|
+
if (target.type !== import_types31.NodeType.DatabaseNode) continue;
|
|
7727
8058
|
for (const pair of compatPairs()) {
|
|
7728
8059
|
if (pair.engine !== target.engine) continue;
|
|
7729
8060
|
const declared = deps[pair.driver];
|
|
@@ -7782,7 +8113,7 @@ function suppressHostMismatchHalves(all) {
|
|
|
7782
8113
|
for (const d of all) {
|
|
7783
8114
|
if (d.type !== "host-mismatch") continue;
|
|
7784
8115
|
observedHalf.add(`${d.source}->${d.target}`);
|
|
7785
|
-
declaredHalf.add((0,
|
|
8116
|
+
declaredHalf.add((0, import_types31.databaseId)(d.extractedHost));
|
|
7786
8117
|
}
|
|
7787
8118
|
if (observedHalf.size === 0) return all;
|
|
7788
8119
|
return all.filter((d) => {
|
|
@@ -7801,7 +8132,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
7801
8132
|
}
|
|
7802
8133
|
graph.forEachNode((nodeId, attrs) => {
|
|
7803
8134
|
const n = attrs;
|
|
7804
|
-
if (n.type !==
|
|
8135
|
+
if (n.type !== import_types31.NodeType.ServiceNode) return;
|
|
7805
8136
|
const svc = n;
|
|
7806
8137
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
7807
8138
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
@@ -7835,7 +8166,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
7835
8166
|
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
7836
8167
|
return a.target.localeCompare(b.target);
|
|
7837
8168
|
});
|
|
7838
|
-
return
|
|
8169
|
+
return import_types31.DivergenceResultSchema.parse({
|
|
7839
8170
|
divergences: filtered,
|
|
7840
8171
|
totalAffected: filtered.length,
|
|
7841
8172
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -7845,8 +8176,8 @@ function computeDivergences(graph, opts = {}) {
|
|
|
7845
8176
|
// src/persist.ts
|
|
7846
8177
|
init_cjs_shims();
|
|
7847
8178
|
var import_node_fs25 = require("fs");
|
|
7848
|
-
var
|
|
7849
|
-
var
|
|
8179
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
8180
|
+
var import_types32 = require("@neat.is/types");
|
|
7850
8181
|
var SCHEMA_VERSION = 4;
|
|
7851
8182
|
function migrateV1ToV2(payload) {
|
|
7852
8183
|
const nodes = payload.graph.nodes;
|
|
@@ -7868,12 +8199,12 @@ function migrateV2ToV3(payload) {
|
|
|
7868
8199
|
for (const edge of edges) {
|
|
7869
8200
|
const attrs = edge.attributes;
|
|
7870
8201
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
7871
|
-
attrs.provenance =
|
|
8202
|
+
attrs.provenance = import_types32.Provenance.OBSERVED;
|
|
7872
8203
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
7873
8204
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
7874
8205
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
7875
8206
|
if (type && source && target) {
|
|
7876
|
-
const newId = (0,
|
|
8207
|
+
const newId = (0, import_types32.observedEdgeId)(source, target, type);
|
|
7877
8208
|
attrs.id = newId;
|
|
7878
8209
|
if (edge.key) edge.key = newId;
|
|
7879
8210
|
}
|
|
@@ -7882,7 +8213,7 @@ function migrateV2ToV3(payload) {
|
|
|
7882
8213
|
return { ...payload, schemaVersion: 3 };
|
|
7883
8214
|
}
|
|
7884
8215
|
async function ensureDir(filePath) {
|
|
7885
|
-
await import_node_fs25.promises.mkdir(
|
|
8216
|
+
await import_node_fs25.promises.mkdir(import_node_path43.default.dirname(filePath), { recursive: true });
|
|
7886
8217
|
}
|
|
7887
8218
|
async function saveGraphToDisk(graph, outPath) {
|
|
7888
8219
|
await ensureDir(outPath);
|
|
@@ -7964,7 +8295,7 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
7964
8295
|
// src/gitignore.ts
|
|
7965
8296
|
init_cjs_shims();
|
|
7966
8297
|
var import_node_fs26 = require("fs");
|
|
7967
|
-
var
|
|
8298
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
7968
8299
|
var NEAT_OUT_LINE = "neat-out/";
|
|
7969
8300
|
var NEAT_HEADER = "# NEAT \u2014 machine-local snapshots and events";
|
|
7970
8301
|
function isNeatOutLine(line) {
|
|
@@ -7972,7 +8303,7 @@ function isNeatOutLine(line) {
|
|
|
7972
8303
|
return trimmed === "neat-out/" || trimmed === "neat-out";
|
|
7973
8304
|
}
|
|
7974
8305
|
async function ensureNeatOutIgnored(projectDir) {
|
|
7975
|
-
const file =
|
|
8306
|
+
const file = import_node_path44.default.join(projectDir, ".gitignore");
|
|
7976
8307
|
let existing = null;
|
|
7977
8308
|
try {
|
|
7978
8309
|
existing = await import_node_fs26.promises.readFile(file, "utf8");
|
|
@@ -7999,7 +8330,7 @@ ${NEAT_OUT_LINE}
|
|
|
7999
8330
|
|
|
8000
8331
|
// src/summary.ts
|
|
8001
8332
|
init_cjs_shims();
|
|
8002
|
-
var
|
|
8333
|
+
var import_types33 = require("@neat.is/types");
|
|
8003
8334
|
function renderOtelEnvBlock() {
|
|
8004
8335
|
return [
|
|
8005
8336
|
"for prod OTel routing, set these in your deploy platform's env:",
|
|
@@ -8009,19 +8340,19 @@ function renderOtelEnvBlock() {
|
|
|
8009
8340
|
}
|
|
8010
8341
|
function findIncompatServices(nodes) {
|
|
8011
8342
|
return nodes.filter(
|
|
8012
|
-
(n) => n.type ===
|
|
8343
|
+
(n) => n.type === import_types33.NodeType.ServiceNode && Array.isArray(n.incompatibilities) && (n.incompatibilities ?? []).length > 0
|
|
8013
8344
|
);
|
|
8014
8345
|
}
|
|
8015
8346
|
function servicesWithoutObserved(nodes, edges) {
|
|
8016
8347
|
const seen = /* @__PURE__ */ new Set();
|
|
8017
8348
|
for (const e of edges) {
|
|
8018
|
-
if (e.provenance ===
|
|
8349
|
+
if (e.provenance === import_types33.Provenance.OBSERVED) {
|
|
8019
8350
|
seen.add(e.source);
|
|
8020
8351
|
seen.add(e.target);
|
|
8021
8352
|
}
|
|
8022
8353
|
}
|
|
8023
8354
|
return nodes.filter(
|
|
8024
|
-
(n) => n.type ===
|
|
8355
|
+
(n) => n.type === import_types33.NodeType.ServiceNode && !seen.has(n.id)
|
|
8025
8356
|
);
|
|
8026
8357
|
}
|
|
8027
8358
|
function formatDivergence(d) {
|
|
@@ -8096,26 +8427,26 @@ function formatIncompat(inc) {
|
|
|
8096
8427
|
// src/watch.ts
|
|
8097
8428
|
init_cjs_shims();
|
|
8098
8429
|
var import_node_fs35 = __toESM(require("fs"), 1);
|
|
8099
|
-
var
|
|
8430
|
+
var import_node_path55 = __toESM(require("path"), 1);
|
|
8100
8431
|
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
8101
8432
|
|
|
8102
8433
|
// src/api.ts
|
|
8103
8434
|
init_cjs_shims();
|
|
8104
8435
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
8105
8436
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
8106
|
-
var
|
|
8437
|
+
var import_types35 = require("@neat.is/types");
|
|
8107
8438
|
|
|
8108
8439
|
// src/extend/index.ts
|
|
8109
8440
|
init_cjs_shims();
|
|
8110
8441
|
var import_node_fs28 = require("fs");
|
|
8111
|
-
var
|
|
8442
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
8112
8443
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
8113
8444
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
8114
8445
|
|
|
8115
8446
|
// src/installers/package-manager.ts
|
|
8116
8447
|
init_cjs_shims();
|
|
8117
8448
|
var import_node_fs27 = require("fs");
|
|
8118
|
-
var
|
|
8449
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
8119
8450
|
var import_node_child_process = require("child_process");
|
|
8120
8451
|
var LOCKFILE_PRIORITY = [
|
|
8121
8452
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -8137,22 +8468,22 @@ async function exists2(p) {
|
|
|
8137
8468
|
}
|
|
8138
8469
|
}
|
|
8139
8470
|
async function detectPackageManager(serviceDir) {
|
|
8140
|
-
let dir =
|
|
8471
|
+
let dir = import_node_path45.default.resolve(serviceDir);
|
|
8141
8472
|
const stops = /* @__PURE__ */ new Set();
|
|
8142
8473
|
for (let i = 0; i < 64; i++) {
|
|
8143
8474
|
if (stops.has(dir)) break;
|
|
8144
8475
|
stops.add(dir);
|
|
8145
8476
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
8146
|
-
const lockPath =
|
|
8477
|
+
const lockPath = import_node_path45.default.join(dir, candidate.lockfile);
|
|
8147
8478
|
if (await exists2(lockPath)) {
|
|
8148
8479
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
8149
8480
|
}
|
|
8150
8481
|
}
|
|
8151
|
-
const parent =
|
|
8482
|
+
const parent = import_node_path45.default.dirname(dir);
|
|
8152
8483
|
if (parent === dir) break;
|
|
8153
8484
|
dir = parent;
|
|
8154
8485
|
}
|
|
8155
|
-
return { pm: "npm", cwd:
|
|
8486
|
+
return { pm: "npm", cwd: import_node_path45.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
8156
8487
|
}
|
|
8157
8488
|
async function runPackageManagerInstall(cmd) {
|
|
8158
8489
|
return new Promise((resolve) => {
|
|
@@ -8201,7 +8532,7 @@ async function fileExists2(p) {
|
|
|
8201
8532
|
}
|
|
8202
8533
|
}
|
|
8203
8534
|
async function readPackageJson(scanPath) {
|
|
8204
|
-
const pkgPath =
|
|
8535
|
+
const pkgPath = import_node_path46.default.join(scanPath, "package.json");
|
|
8205
8536
|
const raw = await import_node_fs28.promises.readFile(pkgPath, "utf8");
|
|
8206
8537
|
return JSON.parse(raw);
|
|
8207
8538
|
}
|
|
@@ -8220,11 +8551,11 @@ async function findHookFiles(scanPath) {
|
|
|
8220
8551
|
for (const entry2 of entries) {
|
|
8221
8552
|
if (entry2.isDirectory()) {
|
|
8222
8553
|
if (entry2.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry2.name)) continue;
|
|
8223
|
-
await walk3(
|
|
8554
|
+
await walk3(import_node_path46.default.join(dir, entry2.name));
|
|
8224
8555
|
} else if (entry2.isFile()) {
|
|
8225
8556
|
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(
|
|
8557
|
+
const rel = import_node_path46.default.relative(scanPath, import_node_path46.default.join(dir, entry2.name));
|
|
8558
|
+
found.push(rel.split(import_node_path46.default.sep).join("/"));
|
|
8228
8559
|
}
|
|
8229
8560
|
}
|
|
8230
8561
|
}
|
|
@@ -8235,7 +8566,7 @@ async function findHookFiles(scanPath) {
|
|
|
8235
8566
|
async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
8236
8567
|
let fallback = null;
|
|
8237
8568
|
for (const file of hookFiles) {
|
|
8238
|
-
const content = await import_node_fs28.promises.readFile(
|
|
8569
|
+
const content = await import_node_fs28.promises.readFile(import_node_path46.default.join(scanPath, file), "utf8");
|
|
8239
8570
|
const patched = splicedContent(content, snippet2);
|
|
8240
8571
|
if (patched !== null) return { file, content, patched };
|
|
8241
8572
|
if (fallback === null) fallback = { file, content };
|
|
@@ -8243,11 +8574,11 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
|
8243
8574
|
return { file: fallback.file, content: fallback.content, patched: null };
|
|
8244
8575
|
}
|
|
8245
8576
|
function extendLogPath() {
|
|
8246
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
8577
|
+
return process.env.NEAT_EXTEND_LOG ?? import_node_path46.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
|
|
8247
8578
|
}
|
|
8248
8579
|
async function appendExtendLog(entry2) {
|
|
8249
8580
|
const logPath = extendLogPath();
|
|
8250
|
-
await import_node_fs28.promises.mkdir(
|
|
8581
|
+
await import_node_fs28.promises.mkdir(import_node_path46.default.dirname(logPath), { recursive: true });
|
|
8251
8582
|
await import_node_fs28.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
|
|
8252
8583
|
}
|
|
8253
8584
|
function splicedContent(fileContent, snippet2) {
|
|
@@ -8306,7 +8637,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
8306
8637
|
}
|
|
8307
8638
|
async function describeProjectInstrumentation(ctx) {
|
|
8308
8639
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
8309
|
-
const envNeat = await fileExists2(
|
|
8640
|
+
const envNeat = await fileExists2(import_node_path46.default.join(ctx.scanPath, ".env.neat"));
|
|
8310
8641
|
const registryInstrPackages = new Set(
|
|
8311
8642
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
8312
8643
|
);
|
|
@@ -8328,7 +8659,7 @@ async function applyExtension(ctx, args, options) {
|
|
|
8328
8659
|
);
|
|
8329
8660
|
}
|
|
8330
8661
|
for (const file of hookFiles) {
|
|
8331
|
-
const content = await import_node_fs28.promises.readFile(
|
|
8662
|
+
const content = await import_node_fs28.promises.readFile(import_node_path46.default.join(ctx.scanPath, file), "utf8");
|
|
8332
8663
|
if (content.includes(args.registration_snippet)) {
|
|
8333
8664
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
8334
8665
|
}
|
|
@@ -8340,10 +8671,10 @@ async function applyExtension(ctx, args, options) {
|
|
|
8340
8671
|
);
|
|
8341
8672
|
}
|
|
8342
8673
|
const primaryFile = primary.file;
|
|
8343
|
-
const primaryPath =
|
|
8674
|
+
const primaryPath = import_node_path46.default.join(ctx.scanPath, primaryFile);
|
|
8344
8675
|
const filesTouched = [];
|
|
8345
8676
|
const depsAdded = [];
|
|
8346
|
-
const pkgPath =
|
|
8677
|
+
const pkgPath = import_node_path46.default.join(ctx.scanPath, "package.json");
|
|
8347
8678
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
8348
8679
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
8349
8680
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
@@ -8382,7 +8713,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
8382
8713
|
};
|
|
8383
8714
|
}
|
|
8384
8715
|
for (const file of hookFiles) {
|
|
8385
|
-
const content = await import_node_fs28.promises.readFile(
|
|
8716
|
+
const content = await import_node_fs28.promises.readFile(import_node_path46.default.join(ctx.scanPath, file), "utf8");
|
|
8386
8717
|
if (content.includes(args.registration_snippet)) {
|
|
8387
8718
|
return {
|
|
8388
8719
|
library: args.library,
|
|
@@ -8423,7 +8754,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
8423
8754
|
if (!match) {
|
|
8424
8755
|
return { undone: false, message: "no apply found for library" };
|
|
8425
8756
|
}
|
|
8426
|
-
const pkgPath =
|
|
8757
|
+
const pkgPath = import_node_path46.default.join(ctx.scanPath, "package.json");
|
|
8427
8758
|
if (await fileExists2(pkgPath)) {
|
|
8428
8759
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
8429
8760
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
@@ -8434,7 +8765,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
8434
8765
|
}
|
|
8435
8766
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
8436
8767
|
for (const file of hookFiles) {
|
|
8437
|
-
const filePath =
|
|
8768
|
+
const filePath = import_node_path46.default.join(ctx.scanPath, file);
|
|
8438
8769
|
const content = await import_node_fs28.promises.readFile(filePath, "utf8");
|
|
8439
8770
|
if (content.includes(match.registration_snippet)) {
|
|
8440
8771
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
@@ -8572,23 +8903,23 @@ function canonicalJson(value) {
|
|
|
8572
8903
|
|
|
8573
8904
|
// src/projects.ts
|
|
8574
8905
|
init_cjs_shims();
|
|
8575
|
-
var
|
|
8906
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
8576
8907
|
function pathsForProject(project, baseDir) {
|
|
8577
8908
|
if (project === DEFAULT_PROJECT) {
|
|
8578
8909
|
return {
|
|
8579
|
-
snapshotPath:
|
|
8580
|
-
errorsPath:
|
|
8581
|
-
staleEventsPath:
|
|
8582
|
-
embeddingsCachePath:
|
|
8583
|
-
policyViolationsPath:
|
|
8910
|
+
snapshotPath: import_node_path47.default.join(baseDir, "graph.json"),
|
|
8911
|
+
errorsPath: import_node_path47.default.join(baseDir, "errors.ndjson"),
|
|
8912
|
+
staleEventsPath: import_node_path47.default.join(baseDir, "stale-events.ndjson"),
|
|
8913
|
+
embeddingsCachePath: import_node_path47.default.join(baseDir, "embeddings.json"),
|
|
8914
|
+
policyViolationsPath: import_node_path47.default.join(baseDir, "policy-violations.ndjson")
|
|
8584
8915
|
};
|
|
8585
8916
|
}
|
|
8586
8917
|
return {
|
|
8587
|
-
snapshotPath:
|
|
8588
|
-
errorsPath:
|
|
8589
|
-
staleEventsPath:
|
|
8590
|
-
embeddingsCachePath:
|
|
8591
|
-
policyViolationsPath:
|
|
8918
|
+
snapshotPath: import_node_path47.default.join(baseDir, `${project}.json`),
|
|
8919
|
+
errorsPath: import_node_path47.default.join(baseDir, `errors.${project}.ndjson`),
|
|
8920
|
+
staleEventsPath: import_node_path47.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
8921
|
+
embeddingsCachePath: import_node_path47.default.join(baseDir, `embeddings.${project}.json`),
|
|
8922
|
+
policyViolationsPath: import_node_path47.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
8592
8923
|
};
|
|
8593
8924
|
}
|
|
8594
8925
|
var Projects = class {
|
|
@@ -8626,26 +8957,26 @@ var Projects = class {
|
|
|
8626
8957
|
init_cjs_shims();
|
|
8627
8958
|
var import_node_fs30 = require("fs");
|
|
8628
8959
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
8629
|
-
var
|
|
8630
|
-
var
|
|
8960
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
8961
|
+
var import_types34 = require("@neat.is/types");
|
|
8631
8962
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
8632
8963
|
var LOCK_RETRY_MS = 50;
|
|
8633
8964
|
function neatHome() {
|
|
8634
8965
|
const override = process.env.NEAT_HOME;
|
|
8635
|
-
if (override && override.length > 0) return
|
|
8636
|
-
return
|
|
8966
|
+
if (override && override.length > 0) return import_node_path48.default.resolve(override);
|
|
8967
|
+
return import_node_path48.default.join(import_node_os3.default.homedir(), ".neat");
|
|
8637
8968
|
}
|
|
8638
8969
|
function registryPath() {
|
|
8639
|
-
return
|
|
8970
|
+
return import_node_path48.default.join(neatHome(), "projects.json");
|
|
8640
8971
|
}
|
|
8641
8972
|
function registryLockPath() {
|
|
8642
|
-
return
|
|
8973
|
+
return import_node_path48.default.join(neatHome(), "projects.json.lock");
|
|
8643
8974
|
}
|
|
8644
8975
|
function daemonPidPath() {
|
|
8645
|
-
return
|
|
8976
|
+
return import_node_path48.default.join(neatHome(), "neatd.pid");
|
|
8646
8977
|
}
|
|
8647
8978
|
function daemonsDir() {
|
|
8648
|
-
return
|
|
8979
|
+
return import_node_path48.default.join(neatHome(), "daemons");
|
|
8649
8980
|
}
|
|
8650
8981
|
function isFiniteInt(v) {
|
|
8651
8982
|
return typeof v === "number" && Number.isFinite(v);
|
|
@@ -8686,7 +9017,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
8686
9017
|
const out = [];
|
|
8687
9018
|
for (const name of names) {
|
|
8688
9019
|
if (!name.endsWith(".json")) continue;
|
|
8689
|
-
const file =
|
|
9020
|
+
const file = import_node_path48.default.join(dir, name);
|
|
8690
9021
|
let raw;
|
|
8691
9022
|
try {
|
|
8692
9023
|
raw = await import_node_fs30.promises.readFile(file, "utf8");
|
|
@@ -8807,7 +9138,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
8807
9138
|
}
|
|
8808
9139
|
}
|
|
8809
9140
|
async function normalizeProjectPath(input) {
|
|
8810
|
-
const resolved =
|
|
9141
|
+
const resolved = import_node_path48.default.resolve(input);
|
|
8811
9142
|
try {
|
|
8812
9143
|
return await import_node_fs30.promises.realpath(resolved);
|
|
8813
9144
|
} catch {
|
|
@@ -8815,7 +9146,7 @@ async function normalizeProjectPath(input) {
|
|
|
8815
9146
|
}
|
|
8816
9147
|
}
|
|
8817
9148
|
async function writeAtomically(target, contents) {
|
|
8818
|
-
await import_node_fs30.promises.mkdir(
|
|
9149
|
+
await import_node_fs30.promises.mkdir(import_node_path48.default.dirname(target), { recursive: true });
|
|
8819
9150
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
8820
9151
|
const fd = await import_node_fs30.promises.open(tmp, "w");
|
|
8821
9152
|
try {
|
|
@@ -8828,7 +9159,7 @@ async function writeAtomically(target, contents) {
|
|
|
8828
9159
|
}
|
|
8829
9160
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
8830
9161
|
const deadline = Date.now() + timeoutMs;
|
|
8831
|
-
await import_node_fs30.promises.mkdir(
|
|
9162
|
+
await import_node_fs30.promises.mkdir(import_node_path48.default.dirname(lockPath), { recursive: true });
|
|
8832
9163
|
let probedHolder = false;
|
|
8833
9164
|
while (true) {
|
|
8834
9165
|
try {
|
|
@@ -8881,10 +9212,10 @@ async function readRegistry() {
|
|
|
8881
9212
|
throw err;
|
|
8882
9213
|
}
|
|
8883
9214
|
const parsed = JSON.parse(raw);
|
|
8884
|
-
return
|
|
9215
|
+
return import_types34.RegistryFileSchema.parse(parsed);
|
|
8885
9216
|
}
|
|
8886
9217
|
async function writeRegistry(reg) {
|
|
8887
|
-
const validated =
|
|
9218
|
+
const validated = import_types34.RegistryFileSchema.parse(reg);
|
|
8888
9219
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
8889
9220
|
}
|
|
8890
9221
|
var ProjectNameCollisionError = class extends Error {
|
|
@@ -9070,7 +9401,7 @@ init_auth();
|
|
|
9070
9401
|
// src/connectors-config.ts
|
|
9071
9402
|
init_cjs_shims();
|
|
9072
9403
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
9073
|
-
var
|
|
9404
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
9074
9405
|
var import_node_fs31 = require("fs");
|
|
9075
9406
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
9076
9407
|
var EnvRefUnsetError = class extends Error {
|
|
@@ -9085,11 +9416,11 @@ var EnvRefUnsetError = class extends Error {
|
|
|
9085
9416
|
};
|
|
9086
9417
|
function neatHome2() {
|
|
9087
9418
|
const override = process.env.NEAT_HOME;
|
|
9088
|
-
if (override && override.length > 0) return
|
|
9089
|
-
return
|
|
9419
|
+
if (override && override.length > 0) return import_node_path49.default.resolve(override);
|
|
9420
|
+
return import_node_path49.default.join(import_node_os4.default.homedir(), ".neat");
|
|
9090
9421
|
}
|
|
9091
9422
|
function connectorsConfigPath(home = neatHome2()) {
|
|
9092
|
-
return
|
|
9423
|
+
return import_node_path49.default.join(home, "connectors.json");
|
|
9093
9424
|
}
|
|
9094
9425
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
9095
9426
|
async function warnIfModeLooserThan0600(file) {
|
|
@@ -9220,7 +9551,7 @@ function connectorMatchesProject(entry2, project) {
|
|
|
9220
9551
|
var CONNECTORS_LOCK_TIMEOUT_MS = 5e3;
|
|
9221
9552
|
var CONNECTORS_LOCK_RETRY_MS = 50;
|
|
9222
9553
|
function connectorsConfigLockPath(home = neatHome2()) {
|
|
9223
|
-
return
|
|
9554
|
+
return import_node_path49.default.join(home, "connectors.json.lock");
|
|
9224
9555
|
}
|
|
9225
9556
|
function isEnvRef(value) {
|
|
9226
9557
|
return value.length > 1 && value.startsWith("$");
|
|
@@ -9233,7 +9564,7 @@ function redactCredentialRef(ref) {
|
|
|
9233
9564
|
return out;
|
|
9234
9565
|
}
|
|
9235
9566
|
async function writeConfigAtomically0600(file, contents) {
|
|
9236
|
-
await import_node_fs31.promises.mkdir(
|
|
9567
|
+
await import_node_fs31.promises.mkdir(import_node_path49.default.dirname(file), { recursive: true });
|
|
9237
9568
|
const tmp = `${file}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
9238
9569
|
const fd = await import_node_fs31.promises.open(tmp, "w", 384);
|
|
9239
9570
|
try {
|
|
@@ -9247,7 +9578,7 @@ async function writeConfigAtomically0600(file, contents) {
|
|
|
9247
9578
|
}
|
|
9248
9579
|
async function acquireConnectorsLock(lockPath, timeoutMs = CONNECTORS_LOCK_TIMEOUT_MS) {
|
|
9249
9580
|
const deadline = Date.now() + timeoutMs;
|
|
9250
|
-
await import_node_fs31.promises.mkdir(
|
|
9581
|
+
await import_node_fs31.promises.mkdir(import_node_path49.default.dirname(lockPath), { recursive: true });
|
|
9251
9582
|
for (; ; ) {
|
|
9252
9583
|
try {
|
|
9253
9584
|
const fd = await import_node_fs31.promises.open(lockPath, "wx");
|
|
@@ -9530,11 +9861,11 @@ function registerRoutes(scope, ctx) {
|
|
|
9530
9861
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
9531
9862
|
const parsed = [];
|
|
9532
9863
|
for (const c of candidates) {
|
|
9533
|
-
const r =
|
|
9864
|
+
const r = import_types35.DivergenceTypeSchema.safeParse(c);
|
|
9534
9865
|
if (!r.success) {
|
|
9535
9866
|
return reply.code(400).send({
|
|
9536
9867
|
error: `unknown divergence type "${c}"`,
|
|
9537
|
-
allowed:
|
|
9868
|
+
allowed: import_types35.DivergenceTypeSchema.options
|
|
9538
9869
|
});
|
|
9539
9870
|
}
|
|
9540
9871
|
parsed.push(r.data);
|
|
@@ -9805,7 +10136,7 @@ function registerRoutes(scope, ctx) {
|
|
|
9805
10136
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
9806
10137
|
let violations = await log.readAll();
|
|
9807
10138
|
if (req.query.severity) {
|
|
9808
|
-
const sev =
|
|
10139
|
+
const sev = import_types35.PolicySeveritySchema.safeParse(req.query.severity);
|
|
9809
10140
|
if (!sev.success) {
|
|
9810
10141
|
return reply.code(400).send({
|
|
9811
10142
|
error: "invalid severity",
|
|
@@ -9844,7 +10175,7 @@ function registerRoutes(scope, ctx) {
|
|
|
9844
10175
|
scope.post("/policies/check", async (req, reply) => {
|
|
9845
10176
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
9846
10177
|
if (!proj) return;
|
|
9847
|
-
const parsed =
|
|
10178
|
+
const parsed = import_types35.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
9848
10179
|
if (!parsed.success) {
|
|
9849
10180
|
return reply.code(400).send({
|
|
9850
10181
|
error: "invalid /policies/check body",
|
|
@@ -9916,6 +10247,42 @@ function registerRoutes(scope, ctx) {
|
|
|
9916
10247
|
return reply.code(500).send({ error: err.message });
|
|
9917
10248
|
}
|
|
9918
10249
|
});
|
|
10250
|
+
scope.get("/instrumentation", async (req, reply) => {
|
|
10251
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
10252
|
+
if (!proj) return;
|
|
10253
|
+
if (!proj.scanPath) {
|
|
10254
|
+
return { engaged: null };
|
|
10255
|
+
}
|
|
10256
|
+
try {
|
|
10257
|
+
const state = await describeProjectInstrumentation({ project: proj.name, scanPath: proj.scanPath });
|
|
10258
|
+
const uninstrumented = await listUninstrumented({ project: proj.name, scanPath: proj.scanPath });
|
|
10259
|
+
if (state.hookFiles.length === 0) {
|
|
10260
|
+
return {
|
|
10261
|
+
engaged: false,
|
|
10262
|
+
diagnosis: {
|
|
10263
|
+
reason: "No instrumented entry point found \u2014 NEAT hasn't written an OTel init hook for this project.",
|
|
10264
|
+
fixCommand: "neat init",
|
|
10265
|
+
detail: "Run `neat init` so NEAT writes the instrumentation hook next to your entry point, then run your app."
|
|
10266
|
+
}
|
|
10267
|
+
};
|
|
10268
|
+
}
|
|
10269
|
+
if (uninstrumented.length > 0) {
|
|
10270
|
+
const names = uninstrumented.map((u) => u.library);
|
|
10271
|
+
const more = names.length > 1 ? ` (and ${names.length - 1} more)` : "";
|
|
10272
|
+
return {
|
|
10273
|
+
engaged: false,
|
|
10274
|
+
diagnosis: {
|
|
10275
|
+
reason: `\`${names[0]}\`${more} isn't in the auto-instrumentation set, so spans from it won't reach the graph.`,
|
|
10276
|
+
fixCommand: "neat extend",
|
|
10277
|
+
detail: `Uninstrumented on your hot path: ${names.join(", ")}. Run \`neat extend\` to wire the missing instrumentation.`
|
|
10278
|
+
}
|
|
10279
|
+
};
|
|
10280
|
+
}
|
|
10281
|
+
return { engaged: true };
|
|
10282
|
+
} catch {
|
|
10283
|
+
return { engaged: null };
|
|
10284
|
+
}
|
|
10285
|
+
});
|
|
9919
10286
|
scope.post("/extend/apply", async (req, reply) => {
|
|
9920
10287
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
9921
10288
|
if (!proj) return;
|
|
@@ -10110,13 +10477,13 @@ init_otel();
|
|
|
10110
10477
|
// src/daemon.ts
|
|
10111
10478
|
init_cjs_shims();
|
|
10112
10479
|
var import_node_fs33 = require("fs");
|
|
10113
|
-
var
|
|
10480
|
+
var import_node_path53 = __toESM(require("path"), 1);
|
|
10114
10481
|
var import_node_module = require("module");
|
|
10115
10482
|
init_otel();
|
|
10116
10483
|
|
|
10117
10484
|
// src/connectors/index.ts
|
|
10118
10485
|
init_cjs_shims();
|
|
10119
|
-
var
|
|
10486
|
+
var import_types36 = require("@neat.is/types");
|
|
10120
10487
|
|
|
10121
10488
|
// src/connectors/registry.ts
|
|
10122
10489
|
init_cjs_shims();
|
|
@@ -10194,7 +10561,12 @@ var JUNCTION_DEFAULT_RATE_LIMITS = {
|
|
|
10194
10561
|
supabase: { capacity: 30, refillMs: 1e4 },
|
|
10195
10562
|
// Not a documented API limit at all — a self-imposed ceiling on the raw
|
|
10196
10563
|
// pg_stat_statements connection (see module header above).
|
|
10197
|
-
"supabase-postgres": { capacity: 20, refillMs: 3e3 }
|
|
10564
|
+
"supabase-postgres": { capacity: 20, refillMs: 3e3 },
|
|
10565
|
+
// Push provider (ADR-146): the Drains REST API is touched only by `neat
|
|
10566
|
+
// connector add/remove/test` (provision/deprovision/validate), never a poll
|
|
10567
|
+
// loop, so this bucket is exercised a handful of times per command. Kept
|
|
10568
|
+
// conservative pending a documented Drains-API rate limit.
|
|
10569
|
+
vercel: { capacity: 20, refillMs: 5e3 }
|
|
10198
10570
|
};
|
|
10199
10571
|
var JUNCTION_GENERIC_RATE_LIMIT = { capacity: 20, refillMs: 5e3 };
|
|
10200
10572
|
function defaultRateLimitFor(provider) {
|
|
@@ -10486,10 +10858,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
10486
10858
|
// src/connectors/supabase/map.ts
|
|
10487
10859
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
10488
10860
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
10489
|
-
function targetFromRestPath(
|
|
10490
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
10861
|
+
function targetFromRestPath(path63) {
|
|
10862
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path63);
|
|
10491
10863
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
10492
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
10864
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path63);
|
|
10493
10865
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
10494
10866
|
return null;
|
|
10495
10867
|
}
|
|
@@ -10598,23 +10970,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
10598
10970
|
|
|
10599
10971
|
// src/connectors/supabase/resolve.ts
|
|
10600
10972
|
init_cjs_shims();
|
|
10601
|
-
var
|
|
10973
|
+
var import_types38 = require("@neat.is/types");
|
|
10602
10974
|
function createSupabaseResolveTarget(graph, config) {
|
|
10603
10975
|
return (signal, _ctx) => {
|
|
10604
10976
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
10605
10977
|
return null;
|
|
10606
10978
|
}
|
|
10607
|
-
const subResourceId = (0,
|
|
10979
|
+
const subResourceId = (0, import_types38.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
10608
10980
|
if (graph.hasNode(subResourceId)) {
|
|
10609
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
10981
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
|
|
10610
10982
|
}
|
|
10611
|
-
const bareResourceId = (0,
|
|
10983
|
+
const bareResourceId = (0, import_types38.infraId)(signal.targetKind, signal.targetName);
|
|
10612
10984
|
if (graph.hasNode(bareResourceId)) {
|
|
10613
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
10985
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
|
|
10614
10986
|
}
|
|
10615
|
-
const projectLevelId = (0,
|
|
10987
|
+
const projectLevelId = (0, import_types38.infraId)("supabase", config.nodeRef);
|
|
10616
10988
|
if (graph.hasNode(projectLevelId)) {
|
|
10617
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
10989
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
|
|
10618
10990
|
}
|
|
10619
10991
|
return null;
|
|
10620
10992
|
};
|
|
@@ -10707,7 +11079,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
10707
11079
|
|
|
10708
11080
|
// src/connectors/railway/index.ts
|
|
10709
11081
|
init_cjs_shims();
|
|
10710
|
-
var
|
|
11082
|
+
var import_types42 = require("@neat.is/types");
|
|
10711
11083
|
|
|
10712
11084
|
// src/connectors/railway/client.ts
|
|
10713
11085
|
init_cjs_shims();
|
|
@@ -10833,7 +11205,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
10833
11205
|
const out = [];
|
|
10834
11206
|
graph.forEachNode((_id, attrs) => {
|
|
10835
11207
|
const node = attrs;
|
|
10836
|
-
if (node.type !==
|
|
11208
|
+
if (node.type !== import_types42.NodeType.RouteNode) return;
|
|
10837
11209
|
const route = attrs;
|
|
10838
11210
|
if (route.service !== serviceName) return;
|
|
10839
11211
|
out.push({
|
|
@@ -10937,12 +11309,12 @@ function createRailwayResolveTarget(config) {
|
|
|
10937
11309
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
10938
11310
|
if (!serviceName) return null;
|
|
10939
11311
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
10940
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
11312
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types42.EdgeType.CALLS };
|
|
10941
11313
|
}
|
|
10942
11314
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
10943
11315
|
const peerName = config.serviceNameById[signal.targetName];
|
|
10944
11316
|
if (!peerName) return null;
|
|
10945
|
-
return { targetNodeId: (0,
|
|
11317
|
+
return { targetNodeId: (0, import_types42.serviceId)(peerName), serviceName, edgeType: import_types42.EdgeType.CONNECTS_TO };
|
|
10946
11318
|
}
|
|
10947
11319
|
return null;
|
|
10948
11320
|
};
|
|
@@ -11066,9 +11438,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
11066
11438
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
11067
11439
|
if (secondSep === -1) return null;
|
|
11068
11440
|
const method = rest.slice(0, secondSep);
|
|
11069
|
-
const
|
|
11070
|
-
if (!resourceName || !method || !
|
|
11071
|
-
return { resourceName, method, path:
|
|
11441
|
+
const path63 = rest.slice(secondSep + 1);
|
|
11442
|
+
if (!resourceName || !method || !path63) return null;
|
|
11443
|
+
return { resourceName, method, path: path63 };
|
|
11072
11444
|
}
|
|
11073
11445
|
function resourceNameFor(type, labels) {
|
|
11074
11446
|
if (!labels) return null;
|
|
@@ -11106,14 +11478,14 @@ function mapLogEntryToSignal(entry2) {
|
|
|
11106
11478
|
if (!req) return null;
|
|
11107
11479
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
11108
11480
|
const method = req.requestMethod.toUpperCase();
|
|
11109
|
-
const
|
|
11110
|
-
if (
|
|
11481
|
+
const path63 = pathFromRequestUrl(req.requestUrl);
|
|
11482
|
+
if (path63 === null) return null;
|
|
11111
11483
|
const timestamp = entry2.timestamp;
|
|
11112
11484
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
11113
11485
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
|
|
11114
11486
|
return {
|
|
11115
11487
|
targetKind: resourceType,
|
|
11116
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
11488
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path63 }),
|
|
11117
11489
|
callCount: 1,
|
|
11118
11490
|
errorCount: isError ? 1 : 0,
|
|
11119
11491
|
lastObservedIso: timestamp
|
|
@@ -11130,7 +11502,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
11130
11502
|
|
|
11131
11503
|
// src/connectors/firebase/resolve.ts
|
|
11132
11504
|
init_cjs_shims();
|
|
11133
|
-
var
|
|
11505
|
+
var import_types43 = require("@neat.is/types");
|
|
11134
11506
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
11135
11507
|
switch (resourceType) {
|
|
11136
11508
|
case "cloud_function":
|
|
@@ -11145,7 +11517,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
11145
11517
|
const entries = [];
|
|
11146
11518
|
graph.forEachNode((_id, attrs) => {
|
|
11147
11519
|
const node = attrs;
|
|
11148
|
-
if (node.type !==
|
|
11520
|
+
if (node.type !== import_types43.NodeType.RouteNode) return;
|
|
11149
11521
|
const route = attrs;
|
|
11150
11522
|
if (route.service !== serviceName) return;
|
|
11151
11523
|
entries.push({
|
|
@@ -11177,7 +11549,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
11177
11549
|
return {
|
|
11178
11550
|
targetNodeId: match.routeNodeId,
|
|
11179
11551
|
serviceName,
|
|
11180
|
-
edgeType:
|
|
11552
|
+
edgeType: import_types43.EdgeType.CALLS
|
|
11181
11553
|
};
|
|
11182
11554
|
};
|
|
11183
11555
|
}
|
|
@@ -11204,7 +11576,7 @@ init_cjs_shims();
|
|
|
11204
11576
|
|
|
11205
11577
|
// src/connectors/cloudflare/connector.ts
|
|
11206
11578
|
init_cjs_shims();
|
|
11207
|
-
var
|
|
11579
|
+
var import_types45 = require("@neat.is/types");
|
|
11208
11580
|
|
|
11209
11581
|
// src/connectors/cloudflare/client.ts
|
|
11210
11582
|
init_cjs_shims();
|
|
@@ -11320,7 +11692,7 @@ function mapEventToSignal(event) {
|
|
|
11320
11692
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
11321
11693
|
const statusCode = metadata?.statusCode;
|
|
11322
11694
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
11323
|
-
const
|
|
11695
|
+
const path63 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
11324
11696
|
return {
|
|
11325
11697
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
11326
11698
|
targetName: scriptName,
|
|
@@ -11328,7 +11700,7 @@ function mapEventToSignal(event) {
|
|
|
11328
11700
|
errorCount: isError ? 1 : 0,
|
|
11329
11701
|
lastObservedIso: observedAt.toISOString(),
|
|
11330
11702
|
method,
|
|
11331
|
-
...
|
|
11703
|
+
...path63 ? { path: path63 } : {},
|
|
11332
11704
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
11333
11705
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
11334
11706
|
};
|
|
@@ -11368,19 +11740,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
11368
11740
|
graph.forEachNode((id, attrs) => {
|
|
11369
11741
|
if (found) return;
|
|
11370
11742
|
const a = attrs;
|
|
11371
|
-
if (a.type ===
|
|
11743
|
+
if (a.type === import_types45.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
11372
11744
|
found = id;
|
|
11373
11745
|
}
|
|
11374
11746
|
});
|
|
11375
11747
|
return found;
|
|
11376
11748
|
}
|
|
11377
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
11378
|
-
const normalizedPath = normalizePathTemplate(
|
|
11749
|
+
function findMatchingRouteNode(graph, serviceName, method, path63) {
|
|
11750
|
+
const normalizedPath = normalizePathTemplate(path63);
|
|
11379
11751
|
let found = null;
|
|
11380
11752
|
graph.forEachNode((id, attrs) => {
|
|
11381
11753
|
if (found) return;
|
|
11382
11754
|
const a = attrs;
|
|
11383
|
-
if (a.type !==
|
|
11755
|
+
if (a.type !== import_types45.NodeType.RouteNode || a.service !== serviceName) return;
|
|
11384
11756
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
11385
11757
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
11386
11758
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -11392,18 +11764,18 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
11392
11764
|
return (signal) => {
|
|
11393
11765
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
11394
11766
|
const scriptName = signal.targetName;
|
|
11395
|
-
const { method, path:
|
|
11767
|
+
const { method, path: path63 } = signal;
|
|
11396
11768
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
11397
|
-
if (!method || !
|
|
11398
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
11769
|
+
if (!method || !path63) return wholeFileId;
|
|
11770
|
+
return findMatchingRouteNode(graph, serviceName, method, path63) ?? wholeFileId;
|
|
11399
11771
|
};
|
|
11400
11772
|
const mapping = config.workers?.[scriptName];
|
|
11401
11773
|
if (mapping) {
|
|
11402
|
-
const wholeFileId = (0,
|
|
11774
|
+
const wholeFileId = (0, import_types45.fileId)(mapping.service, mapping.entryFile);
|
|
11403
11775
|
return {
|
|
11404
11776
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
11405
11777
|
serviceName: mapping.service,
|
|
11406
|
-
edgeType:
|
|
11778
|
+
edgeType: import_types45.EdgeType.CALLS
|
|
11407
11779
|
};
|
|
11408
11780
|
}
|
|
11409
11781
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -11412,18 +11784,124 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
11412
11784
|
return {
|
|
11413
11785
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
11414
11786
|
serviceName: fileNode.service,
|
|
11415
|
-
edgeType:
|
|
11787
|
+
edgeType: import_types45.EdgeType.CALLS
|
|
11416
11788
|
};
|
|
11417
11789
|
}
|
|
11418
11790
|
return {
|
|
11419
|
-
targetNodeId: (0,
|
|
11791
|
+
targetNodeId: (0, import_types45.infraId)("cloudflare-worker", scriptName),
|
|
11420
11792
|
serviceName: scriptName,
|
|
11421
|
-
edgeType:
|
|
11793
|
+
edgeType: import_types45.EdgeType.CALLS,
|
|
11422
11794
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
11423
11795
|
};
|
|
11424
11796
|
};
|
|
11425
11797
|
}
|
|
11426
11798
|
|
|
11799
|
+
// src/connectors/vercel/index.ts
|
|
11800
|
+
init_cjs_shims();
|
|
11801
|
+
|
|
11802
|
+
// src/connectors/vercel/client.ts
|
|
11803
|
+
init_cjs_shims();
|
|
11804
|
+
var DEFAULT_API_BASE_URL = "https://api.vercel.com";
|
|
11805
|
+
var DEFAULT_DRAIN_NAME = "neat-otlp";
|
|
11806
|
+
var TRACE_SCHEMAS = { trace: { version: "v1" } };
|
|
11807
|
+
function apiBase(config) {
|
|
11808
|
+
return config.apiBaseUrl ?? DEFAULT_API_BASE_URL;
|
|
11809
|
+
}
|
|
11810
|
+
function teamQuery(config) {
|
|
11811
|
+
return `?teamId=${encodeURIComponent(config.teamId)}`;
|
|
11812
|
+
}
|
|
11813
|
+
function drainDelivery(config, otelToken) {
|
|
11814
|
+
return {
|
|
11815
|
+
type: "http",
|
|
11816
|
+
endpoint: config.endpoint,
|
|
11817
|
+
encoding: "json",
|
|
11818
|
+
headers: bearerAuthHeader(otelToken),
|
|
11819
|
+
...config.secret ? { secret: config.secret } : {}
|
|
11820
|
+
};
|
|
11821
|
+
}
|
|
11822
|
+
async function describeError(res) {
|
|
11823
|
+
try {
|
|
11824
|
+
const data = await res.json();
|
|
11825
|
+
const message = data?.error?.message;
|
|
11826
|
+
return typeof message === "string" && message.length > 0 ? ` \u2014 ${message}` : "";
|
|
11827
|
+
} catch {
|
|
11828
|
+
return "";
|
|
11829
|
+
}
|
|
11830
|
+
}
|
|
11831
|
+
async function createVercelDrain(config, credentials, fetchImpl = fetch) {
|
|
11832
|
+
const projectIds = config.projectIds ?? [];
|
|
11833
|
+
const body = {
|
|
11834
|
+
name: config.drainName ?? DEFAULT_DRAIN_NAME,
|
|
11835
|
+
projects: projectIds.length > 0 ? "some" : "all",
|
|
11836
|
+
...projectIds.length > 0 ? { projectIds } : {},
|
|
11837
|
+
schemas: TRACE_SCHEMAS,
|
|
11838
|
+
delivery: drainDelivery(config, credentials.otelToken),
|
|
11839
|
+
source: { kind: "self-served" }
|
|
11840
|
+
};
|
|
11841
|
+
const res = await junctionFetch(
|
|
11842
|
+
`${apiBase(config)}/v1/drains${teamQuery(config)}`,
|
|
11843
|
+
{
|
|
11844
|
+
method: "POST",
|
|
11845
|
+
headers: { "Content-Type": "application/json", ...bearerAuthHeader(credentials.token) },
|
|
11846
|
+
body: JSON.stringify(body)
|
|
11847
|
+
},
|
|
11848
|
+
{ provider: "vercel", accountKey: config.teamId, fetchImpl }
|
|
11849
|
+
);
|
|
11850
|
+
if (!res.ok) {
|
|
11851
|
+
throw new Error(
|
|
11852
|
+
`vercel connector: create drain failed (${res.status} ${res.statusText}${await describeError(res)})`
|
|
11853
|
+
);
|
|
11854
|
+
}
|
|
11855
|
+
const payload = await res.json().catch(() => null);
|
|
11856
|
+
if (!payload || typeof payload.id !== "string" || payload.id.length === 0) {
|
|
11857
|
+
throw new Error(
|
|
11858
|
+
"vercel connector: create drain returned no drain id \u2014 the Drains API response shape may have changed"
|
|
11859
|
+
);
|
|
11860
|
+
}
|
|
11861
|
+
return {
|
|
11862
|
+
id: payload.id,
|
|
11863
|
+
...payload.status ? { status: payload.status } : {},
|
|
11864
|
+
...payload.disabledReason ? { disabledReason: payload.disabledReason } : {}
|
|
11865
|
+
};
|
|
11866
|
+
}
|
|
11867
|
+
async function deleteVercelDrain(config, drainId, credentials, fetchImpl = fetch) {
|
|
11868
|
+
const res = await junctionFetch(
|
|
11869
|
+
`${apiBase(config)}/v1/drains/${encodeURIComponent(drainId)}${teamQuery(config)}`,
|
|
11870
|
+
{ method: "DELETE", headers: { ...bearerAuthHeader(credentials.token) } },
|
|
11871
|
+
{ provider: "vercel", accountKey: config.teamId, fetchImpl }
|
|
11872
|
+
);
|
|
11873
|
+
if (res.ok || res.status === 404) return;
|
|
11874
|
+
throw new Error(
|
|
11875
|
+
`vercel connector: delete drain failed (${res.status} ${res.statusText}${await describeError(res)})`
|
|
11876
|
+
);
|
|
11877
|
+
}
|
|
11878
|
+
async function testVercelDrainDelivery(config, credentials, fetchImpl = fetch) {
|
|
11879
|
+
const res = await junctionFetch(
|
|
11880
|
+
`${apiBase(config)}/v1/drains/test${teamQuery(config)}`,
|
|
11881
|
+
{
|
|
11882
|
+
method: "POST",
|
|
11883
|
+
headers: { "Content-Type": "application/json", ...bearerAuthHeader(credentials.token) },
|
|
11884
|
+
body: JSON.stringify({ schemas: TRACE_SCHEMAS, delivery: drainDelivery(config, credentials.otelToken) })
|
|
11885
|
+
},
|
|
11886
|
+
{ provider: "vercel", accountKey: config.teamId, fetchImpl }
|
|
11887
|
+
);
|
|
11888
|
+
if (res.status === 401 || res.status === 403) {
|
|
11889
|
+
return { status: "failure", error: `vercel rejected the API token (HTTP ${res.status})` };
|
|
11890
|
+
}
|
|
11891
|
+
if (!res.ok) {
|
|
11892
|
+
return {
|
|
11893
|
+
status: "failure",
|
|
11894
|
+
error: `vercel drain validation failed (${res.status} ${res.statusText}${await describeError(res)})`
|
|
11895
|
+
};
|
|
11896
|
+
}
|
|
11897
|
+
const payload = await res.json().catch(() => null);
|
|
11898
|
+
return {
|
|
11899
|
+
...payload?.status ? { status: payload.status } : {},
|
|
11900
|
+
...payload?.error ? { error: payload.error } : {},
|
|
11901
|
+
...payload?.endpoint ? { endpoint: payload.endpoint } : {}
|
|
11902
|
+
};
|
|
11903
|
+
}
|
|
11904
|
+
|
|
11427
11905
|
// src/connectors/registry.ts
|
|
11428
11906
|
var CLOUDFLARE_API_BASE_URL = "https://api.cloudflare.com/client/v4";
|
|
11429
11907
|
async function authProbe(input) {
|
|
@@ -11560,23 +12038,117 @@ var PROVIDER_DISPATCH = {
|
|
|
11560
12038
|
resolveTarget: createCloudflareResolveTarget(config, graph)
|
|
11561
12039
|
};
|
|
11562
12040
|
},
|
|
11563
|
-
// GET /
|
|
11564
|
-
//
|
|
12041
|
+
// GET /accounts/{accountId}/tokens/verify — the *account-scoped* token-verify
|
|
12042
|
+
// endpoint. A Workers connector token is scoped to the account, and the
|
|
12043
|
+
// user-level `GET /user/tokens/verify` returns 401 "Invalid API Token" for
|
|
12044
|
+
// such a token even though it authenticates fine against the account's own
|
|
12045
|
+
// resources (confirmed live). Probing the account-scoped verify endpoint —
|
|
12046
|
+
// `accountId` is already required for this provider — returns 200
|
|
12047
|
+
// `{status:"active"}` for a working token and 401 for a bad one, so a valid
|
|
12048
|
+
// Workers token is no longer falsely rejected at `neat connector add`.
|
|
11565
12049
|
validate({ credentials, options, fetchImpl }) {
|
|
11566
12050
|
const cfg = options;
|
|
11567
12051
|
const baseUrl = cfg.baseUrl ?? CLOUDFLARE_API_BASE_URL;
|
|
11568
12052
|
return authProbe({
|
|
11569
12053
|
provider: "cloudflare",
|
|
11570
12054
|
accountKey: cfg.accountId ?? "validate",
|
|
11571
|
-
url: `${baseUrl}/
|
|
12055
|
+
url: `${baseUrl}/accounts/${cfg.accountId ?? ""}/tokens/verify`,
|
|
11572
12056
|
token: String(credentials.apiToken ?? ""),
|
|
11573
12057
|
...fetchImpl ? { fetchImpl } : {}
|
|
11574
12058
|
});
|
|
11575
12059
|
}
|
|
11576
12060
|
}
|
|
11577
12061
|
};
|
|
11578
|
-
function
|
|
11579
|
-
return
|
|
12062
|
+
function vercelCredsFrom(credentials) {
|
|
12063
|
+
return { token: String(credentials.token ?? ""), otelToken: String(credentials.otelToken ?? "") };
|
|
12064
|
+
}
|
|
12065
|
+
function vercelConfigFromOptions(options) {
|
|
12066
|
+
const raw = options.projectIds;
|
|
12067
|
+
let projectIds;
|
|
12068
|
+
if (Array.isArray(raw)) projectIds = raw.filter((p) => typeof p === "string");
|
|
12069
|
+
else if (typeof raw === "string" && raw.trim().length > 0) {
|
|
12070
|
+
projectIds = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
12071
|
+
}
|
|
12072
|
+
return {
|
|
12073
|
+
teamId: String(options.teamId ?? ""),
|
|
12074
|
+
endpoint: String(options.endpoint ?? ""),
|
|
12075
|
+
...projectIds && projectIds.length > 0 ? { projectIds } : {},
|
|
12076
|
+
...typeof options.drainId === "string" ? { drainId: options.drainId } : {},
|
|
12077
|
+
...typeof options.drainName === "string" ? { drainName: options.drainName } : {},
|
|
12078
|
+
...typeof options.apiBaseUrl === "string" ? { apiBaseUrl: options.apiBaseUrl } : {},
|
|
12079
|
+
...typeof options.secret === "string" ? { secret: options.secret } : {}
|
|
12080
|
+
};
|
|
12081
|
+
}
|
|
12082
|
+
var PUSH_PROVIDER_DISPATCH = {
|
|
12083
|
+
vercel: {
|
|
12084
|
+
provider: "vercel",
|
|
12085
|
+
// The Vercel access token is the "primary" secret a single `--token`
|
|
12086
|
+
// populates; `otelToken` (the daemon's OTLP bearer) is the second field.
|
|
12087
|
+
primaryCredentialKey: "token",
|
|
12088
|
+
requiredCredentialFields: ["token", "otelToken"],
|
|
12089
|
+
// teamId scopes every Drains call; endpoint is where the drain delivers.
|
|
12090
|
+
// projectIds is optional (absent → the drain covers the whole team).
|
|
12091
|
+
requiredOptionFields: ["teamId", "endpoint"],
|
|
12092
|
+
// POST /v1/drains/test — authenticates the token and pings the endpoint
|
|
12093
|
+
// with a sample event, so `success` means the credential is live *and* the
|
|
12094
|
+
// daemon's OTLP endpoint is reachable and accepted the drain's bearer.
|
|
12095
|
+
async validate({ credentials, options, fetchImpl }) {
|
|
12096
|
+
const result = await testVercelDrainDelivery(
|
|
12097
|
+
vercelConfigFromOptions(options),
|
|
12098
|
+
vercelCredsFrom(credentials),
|
|
12099
|
+
fetchImpl
|
|
12100
|
+
);
|
|
12101
|
+
if (result.status === "success") return { ok: true };
|
|
12102
|
+
return {
|
|
12103
|
+
ok: false,
|
|
12104
|
+
reason: result.error ?? `vercel drain delivery test returned "${result.status ?? "no status"}"`
|
|
12105
|
+
};
|
|
12106
|
+
},
|
|
12107
|
+
// POST /v1/drains — creates the trace drain, returns its id to store in
|
|
12108
|
+
// `options.drainId`. A created-but-not-enabled drain is surfaced as a note,
|
|
12109
|
+
// not a failure (the entry still points at a real drain).
|
|
12110
|
+
async provision({ credentials, options, fetchImpl }) {
|
|
12111
|
+
try {
|
|
12112
|
+
const created = await createVercelDrain(
|
|
12113
|
+
vercelConfigFromOptions(options),
|
|
12114
|
+
vercelCredsFrom(credentials),
|
|
12115
|
+
fetchImpl
|
|
12116
|
+
);
|
|
12117
|
+
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;
|
|
12118
|
+
return { ok: true, options: { drainId: created.id }, ...note ? { note } : {} };
|
|
12119
|
+
} catch (err) {
|
|
12120
|
+
return { ok: false, reason: err.message };
|
|
12121
|
+
}
|
|
12122
|
+
},
|
|
12123
|
+
// DELETE /v1/drains/{id} — idempotent (deleteVercelDrain treats 404 as
|
|
12124
|
+
// success). No recorded drainId → nothing to delete, still a success.
|
|
12125
|
+
async deprovision({ credentials, options, fetchImpl }) {
|
|
12126
|
+
const drainId = typeof options.drainId === "string" ? options.drainId : "";
|
|
12127
|
+
if (!drainId) {
|
|
12128
|
+
return { ok: true, note: "no drain id was recorded \u2014 nothing to delete on the Vercel side" };
|
|
12129
|
+
}
|
|
12130
|
+
try {
|
|
12131
|
+
await deleteVercelDrain(
|
|
12132
|
+
vercelConfigFromOptions(options),
|
|
12133
|
+
drainId,
|
|
12134
|
+
vercelCredsFrom(credentials),
|
|
12135
|
+
fetchImpl
|
|
12136
|
+
);
|
|
12137
|
+
return { ok: true };
|
|
12138
|
+
} catch (err) {
|
|
12139
|
+
return { ok: false, reason: err.message };
|
|
12140
|
+
}
|
|
12141
|
+
}
|
|
12142
|
+
}
|
|
12143
|
+
};
|
|
12144
|
+
function isPushProvider(provider) {
|
|
12145
|
+
return provider in PUSH_PROVIDER_DISPATCH;
|
|
12146
|
+
}
|
|
12147
|
+
function getProviderFieldSchema(provider) {
|
|
12148
|
+
return PROVIDER_DISPATCH[provider] ?? PUSH_PROVIDER_DISPATCH[provider];
|
|
12149
|
+
}
|
|
12150
|
+
function knownProviderNames() {
|
|
12151
|
+
return [...Object.keys(PROVIDER_DISPATCH), ...Object.keys(PUSH_PROVIDER_DISPATCH)].sort();
|
|
11580
12152
|
}
|
|
11581
12153
|
function resolveEntryCredentials(dispatch, entry2, env) {
|
|
11582
12154
|
let credentials;
|
|
@@ -11598,7 +12170,7 @@ function resolveEntryCredentials(dispatch, entry2, env) {
|
|
|
11598
12170
|
return { ok: true, credentials };
|
|
11599
12171
|
}
|
|
11600
12172
|
async function validateConnectorEntry(entry2, env = process.env, fetchImpl) {
|
|
11601
|
-
const dispatch = PROVIDER_DISPATCH[entry2.provider];
|
|
12173
|
+
const dispatch = PROVIDER_DISPATCH[entry2.provider] ?? PUSH_PROVIDER_DISPATCH[entry2.provider];
|
|
11602
12174
|
if (!dispatch) {
|
|
11603
12175
|
return { status: "unknown-provider", reason: `unknown provider "${entry2.provider}"` };
|
|
11604
12176
|
}
|
|
@@ -11622,6 +12194,54 @@ async function validateConnectorEntry(entry2, env = process.env, fetchImpl) {
|
|
|
11622
12194
|
});
|
|
11623
12195
|
return result.ok ? { status: "ok" } : { status: "rejected", reason: result.reason };
|
|
11624
12196
|
}
|
|
12197
|
+
function resolvePushEntry(entry2, env) {
|
|
12198
|
+
const dispatch = PUSH_PROVIDER_DISPATCH[entry2.provider];
|
|
12199
|
+
if (!dispatch) {
|
|
12200
|
+
return PROVIDER_DISPATCH[entry2.provider] ? {
|
|
12201
|
+
ok: false,
|
|
12202
|
+
outcome: {
|
|
12203
|
+
status: "not-push",
|
|
12204
|
+
reason: `provider "${entry2.provider}" is polled, not provisioned \u2014 there is no drain to manage`
|
|
12205
|
+
}
|
|
12206
|
+
} : { ok: false, outcome: { status: "unknown-provider", reason: `unknown provider "${entry2.provider}"` } };
|
|
12207
|
+
}
|
|
12208
|
+
const creds = resolveEntryCredentials(dispatch, entry2, env);
|
|
12209
|
+
if (!creds.ok) {
|
|
12210
|
+
const status2 = creds.kind === "unset-env" ? "unset-env" : creds.kind === "missing-field" ? "missing-field" : "failed";
|
|
12211
|
+
return { ok: false, outcome: { status: status2, reason: creds.reason } };
|
|
12212
|
+
}
|
|
12213
|
+
const options = entry2.options ?? {};
|
|
12214
|
+
const missingOpts = dispatch.requiredOptionFields.filter((k) => !(k in options));
|
|
12215
|
+
if (missingOpts.length > 0) {
|
|
12216
|
+
return {
|
|
12217
|
+
ok: false,
|
|
12218
|
+
outcome: { status: "missing-field", reason: `options missing required field(s): ${missingOpts.join(", ")}` }
|
|
12219
|
+
};
|
|
12220
|
+
}
|
|
12221
|
+
return { ok: true, dispatch, credentials: creds.credentials, options };
|
|
12222
|
+
}
|
|
12223
|
+
async function provisionConnector(entry2, env = process.env, fetchImpl) {
|
|
12224
|
+
const resolved = resolvePushEntry(entry2, env);
|
|
12225
|
+
if (!resolved.ok) return resolved.outcome;
|
|
12226
|
+
const result = await resolved.dispatch.provision({
|
|
12227
|
+
credentials: resolved.credentials,
|
|
12228
|
+
options: resolved.options,
|
|
12229
|
+
...fetchImpl ? { fetchImpl } : {}
|
|
12230
|
+
});
|
|
12231
|
+
if (!result.ok) return { status: "failed", reason: result.reason };
|
|
12232
|
+
return { status: "ok", ...result.options ? { options: result.options } : {}, ...result.note ? { note: result.note } : {} };
|
|
12233
|
+
}
|
|
12234
|
+
async function deprovisionConnector(entry2, env = process.env, fetchImpl) {
|
|
12235
|
+
const resolved = resolvePushEntry(entry2, env);
|
|
12236
|
+
if (!resolved.ok) return resolved.outcome;
|
|
12237
|
+
const result = await resolved.dispatch.deprovision({
|
|
12238
|
+
credentials: resolved.credentials,
|
|
12239
|
+
options: resolved.options,
|
|
12240
|
+
...fetchImpl ? { fetchImpl } : {}
|
|
12241
|
+
});
|
|
12242
|
+
if (!result.ok) return { status: "failed", reason: result.reason };
|
|
12243
|
+
return { status: "ok", ...result.note ? { note: result.note } : {} };
|
|
12244
|
+
}
|
|
11625
12245
|
|
|
11626
12246
|
// src/daemon.ts
|
|
11627
12247
|
init_auth();
|
|
@@ -11629,28 +12249,28 @@ init_auth();
|
|
|
11629
12249
|
// src/unrouted.ts
|
|
11630
12250
|
init_cjs_shims();
|
|
11631
12251
|
var import_node_fs32 = require("fs");
|
|
11632
|
-
var
|
|
12252
|
+
var import_node_path52 = __toESM(require("path"), 1);
|
|
11633
12253
|
|
|
11634
12254
|
// src/daemon.ts
|
|
11635
|
-
var
|
|
12255
|
+
var import_types48 = require("@neat.is/types");
|
|
11636
12256
|
function daemonJsonPath(scanPath) {
|
|
11637
|
-
return
|
|
12257
|
+
return import_node_path53.default.join(scanPath, "neat-out", "daemon.json");
|
|
11638
12258
|
}
|
|
11639
12259
|
function daemonsDiscoveryDir(home) {
|
|
11640
12260
|
const base = home && home.length > 0 ? home : neatHomeFromEnv();
|
|
11641
|
-
return
|
|
12261
|
+
return import_node_path53.default.join(base, "daemons");
|
|
11642
12262
|
}
|
|
11643
12263
|
function daemonDiscoveryPath(project, home) {
|
|
11644
|
-
return
|
|
12264
|
+
return import_node_path53.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
|
|
11645
12265
|
}
|
|
11646
12266
|
function sanitizeDiscoveryName(project) {
|
|
11647
12267
|
return project.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
11648
12268
|
}
|
|
11649
12269
|
function neatHomeFromEnv() {
|
|
11650
12270
|
const env = process.env.NEAT_HOME;
|
|
11651
|
-
if (env && env.length > 0) return
|
|
12271
|
+
if (env && env.length > 0) return import_node_path53.default.resolve(env);
|
|
11652
12272
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
11653
|
-
return
|
|
12273
|
+
return import_node_path53.default.join(home, ".neat");
|
|
11654
12274
|
}
|
|
11655
12275
|
async function readDaemonRecord(scanPath) {
|
|
11656
12276
|
try {
|
|
@@ -11721,7 +12341,7 @@ init_otel_grpc();
|
|
|
11721
12341
|
// src/search.ts
|
|
11722
12342
|
init_cjs_shims();
|
|
11723
12343
|
var import_node_fs34 = require("fs");
|
|
11724
|
-
var
|
|
12344
|
+
var import_node_path54 = __toESM(require("path"), 1);
|
|
11725
12345
|
var import_node_crypto4 = require("crypto");
|
|
11726
12346
|
var DEFAULT_LIMIT = 10;
|
|
11727
12347
|
var NOMIC_DIM = 768;
|
|
@@ -11884,7 +12504,7 @@ async function readCache(cachePath) {
|
|
|
11884
12504
|
}
|
|
11885
12505
|
}
|
|
11886
12506
|
async function writeCache(cachePath, cache) {
|
|
11887
|
-
await import_node_fs34.promises.mkdir(
|
|
12507
|
+
await import_node_fs34.promises.mkdir(import_node_path54.default.dirname(cachePath), { recursive: true });
|
|
11888
12508
|
await import_node_fs34.promises.writeFile(cachePath, JSON.stringify(cache));
|
|
11889
12509
|
}
|
|
11890
12510
|
var VectorIndex = class {
|
|
@@ -12042,8 +12662,8 @@ var ALL_PHASES = [
|
|
|
12042
12662
|
];
|
|
12043
12663
|
function classifyChange(relPath) {
|
|
12044
12664
|
const phases = /* @__PURE__ */ new Set();
|
|
12045
|
-
const base =
|
|
12046
|
-
const segments = relPath.split(
|
|
12665
|
+
const base = import_node_path55.default.basename(relPath).toLowerCase();
|
|
12666
|
+
const segments = relPath.split(import_node_path55.default.sep).map((s) => s.toLowerCase());
|
|
12047
12667
|
if (base === "package.json" || base === "requirements.txt" || base === "pyproject.toml" || base === "setup.py") {
|
|
12048
12668
|
phases.add("services");
|
|
12049
12669
|
phases.add("aliases");
|
|
@@ -12171,9 +12791,9 @@ function countWatchableDirs(scanPath, limit) {
|
|
|
12171
12791
|
for (const e of entries) {
|
|
12172
12792
|
if (count >= limit) return;
|
|
12173
12793
|
if (!e.isDirectory()) continue;
|
|
12174
|
-
if (IGNORED_WATCH_PATHS.some((re) => re.test(
|
|
12794
|
+
if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path55.default.join(dir, e.name) + import_node_path55.default.sep))) continue;
|
|
12175
12795
|
count++;
|
|
12176
|
-
if (depth < 2) visit(
|
|
12796
|
+
if (depth < 2) visit(import_node_path55.default.join(dir, e.name), depth + 1);
|
|
12177
12797
|
}
|
|
12178
12798
|
};
|
|
12179
12799
|
visit(scanPath, 0);
|
|
@@ -12191,8 +12811,8 @@ async function startWatch(graph, opts) {
|
|
|
12191
12811
|
const projectName = opts.project ?? DEFAULT_PROJECT;
|
|
12192
12812
|
await loadGraphFromDisk(graph, opts.outPath);
|
|
12193
12813
|
const detachEventBus = attachGraphToEventBus(graph, { project: projectName });
|
|
12194
|
-
const policyFilePath =
|
|
12195
|
-
const policyViolationsPath =
|
|
12814
|
+
const policyFilePath = import_node_path55.default.join(opts.scanPath, "policy.json");
|
|
12815
|
+
const policyViolationsPath = import_node_path55.default.join(import_node_path55.default.dirname(opts.outPath), "policy-violations.ndjson");
|
|
12196
12816
|
let policies = [];
|
|
12197
12817
|
try {
|
|
12198
12818
|
policies = await loadPolicyFile(policyFilePath);
|
|
@@ -12243,7 +12863,7 @@ async function startWatch(graph, opts) {
|
|
|
12243
12863
|
assertBindAuthority(host, auth.authToken);
|
|
12244
12864
|
const port = opts.port ?? 8080;
|
|
12245
12865
|
const otelPort = opts.otelPort ?? 4318;
|
|
12246
|
-
const cachePath = opts.embeddingsCachePath ??
|
|
12866
|
+
const cachePath = opts.embeddingsCachePath ?? import_node_path55.default.join(import_node_path55.default.dirname(opts.outPath), "embeddings.json");
|
|
12247
12867
|
let searchIndex;
|
|
12248
12868
|
try {
|
|
12249
12869
|
searchIndex = await buildSearchIndex(graph, { cachePath });
|
|
@@ -12261,7 +12881,7 @@ async function startWatch(graph, opts) {
|
|
|
12261
12881
|
// Paths are derived from the explicit options the watch caller passes
|
|
12262
12882
|
// — pathsForProject is only used to fill in the embeddings/snapshot
|
|
12263
12883
|
// fields so the registry shape is complete.
|
|
12264
|
-
...pathsForProject(projectName,
|
|
12884
|
+
...pathsForProject(projectName, import_node_path55.default.dirname(opts.outPath)),
|
|
12265
12885
|
snapshotPath: opts.outPath,
|
|
12266
12886
|
errorsPath: opts.errorsPath,
|
|
12267
12887
|
staleEventsPath: opts.staleEventsPath
|
|
@@ -12379,9 +12999,9 @@ async function startWatch(graph, opts) {
|
|
|
12379
12999
|
};
|
|
12380
13000
|
const onPath = (absPath) => {
|
|
12381
13001
|
if (shouldIgnore(absPath)) return;
|
|
12382
|
-
const rel =
|
|
13002
|
+
const rel = import_node_path55.default.relative(opts.scanPath, absPath);
|
|
12383
13003
|
if (!rel || rel.startsWith("..")) return;
|
|
12384
|
-
pendingPaths.add(rel.split(
|
|
13004
|
+
pendingPaths.add(rel.split(import_node_path55.default.sep).join("/"));
|
|
12385
13005
|
const phases = classifyChange(rel);
|
|
12386
13006
|
if (phases.size === 0) {
|
|
12387
13007
|
for (const p of ALL_PHASES) pending.add(p);
|
|
@@ -12438,7 +13058,7 @@ async function startWatch(graph, opts) {
|
|
|
12438
13058
|
// src/deploy/detect.ts
|
|
12439
13059
|
init_cjs_shims();
|
|
12440
13060
|
var import_node_fs36 = require("fs");
|
|
12441
|
-
var
|
|
13061
|
+
var import_node_path56 = __toESM(require("path"), 1);
|
|
12442
13062
|
var import_node_child_process2 = require("child_process");
|
|
12443
13063
|
var import_node_crypto5 = require("crypto");
|
|
12444
13064
|
function generateToken() {
|
|
@@ -12538,7 +13158,7 @@ async function runDeploy(opts = {}) {
|
|
|
12538
13158
|
const token = generateToken();
|
|
12539
13159
|
switch (substrate) {
|
|
12540
13160
|
case "docker-compose": {
|
|
12541
|
-
const artifactPath =
|
|
13161
|
+
const artifactPath = import_node_path56.default.join(cwd, "docker-compose.neat.yml");
|
|
12542
13162
|
const contents = emitDockerCompose(cwd);
|
|
12543
13163
|
await import_node_fs36.promises.writeFile(artifactPath, contents, "utf8");
|
|
12544
13164
|
return {
|
|
@@ -12546,11 +13166,11 @@ async function runDeploy(opts = {}) {
|
|
|
12546
13166
|
artifactPath,
|
|
12547
13167
|
token,
|
|
12548
13168
|
contents,
|
|
12549
|
-
startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${
|
|
13169
|
+
startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${import_node_path56.default.basename(artifactPath)} up -d`
|
|
12550
13170
|
};
|
|
12551
13171
|
}
|
|
12552
13172
|
case "systemd": {
|
|
12553
|
-
const artifactPath =
|
|
13173
|
+
const artifactPath = import_node_path56.default.join(cwd, "neat.service");
|
|
12554
13174
|
const contents = emitSystemdUnit(cwd);
|
|
12555
13175
|
await import_node_fs36.promises.writeFile(artifactPath, contents, "utf8");
|
|
12556
13176
|
return {
|
|
@@ -12586,7 +13206,7 @@ init_cjs_shims();
|
|
|
12586
13206
|
// src/installers/javascript.ts
|
|
12587
13207
|
init_cjs_shims();
|
|
12588
13208
|
var import_node_fs37 = require("fs");
|
|
12589
|
-
var
|
|
13209
|
+
var import_node_path57 = __toESM(require("path"), 1);
|
|
12590
13210
|
var import_semver2 = __toESM(require("semver"), 1);
|
|
12591
13211
|
|
|
12592
13212
|
// src/installers/templates.ts
|
|
@@ -12964,9 +13584,12 @@ ${OTEL_ENDPOINT_RESOLVER_ESM}
|
|
|
12964
13584
|
${OTEL_OTLP_PROTOCOL_JS}
|
|
12965
13585
|
${OTEL_OTLP_HEADERS_JS}
|
|
12966
13586
|
|
|
12967
|
-
|
|
12968
|
-
|
|
12969
|
-
|
|
13587
|
+
// Keep dependency loading inside the guard. Static ESM imports are evaluated
|
|
13588
|
+
// before any try/catch and would crash the host app when an install failed.
|
|
13589
|
+
try {
|
|
13590
|
+
const { NodeSDK } = await import('@opentelemetry/sdk-node')
|
|
13591
|
+
const { getNodeAutoInstrumentations } = await import('@opentelemetry/auto-instrumentations-node')
|
|
13592
|
+
const { trace, context } = await import('@opentelemetry/api')
|
|
12970
13593
|
|
|
12971
13594
|
${CALLSITE_PROCESSOR_JS}
|
|
12972
13595
|
|
|
@@ -12975,6 +13598,14 @@ __INSTRUMENTATION_BLOCK__
|
|
|
12975
13598
|
const sdk = new NodeSDK({ instrumentations })
|
|
12976
13599
|
sdk.start()
|
|
12977
13600
|
${neatWireCaptureSource(false)}
|
|
13601
|
+
} catch (__neatOtelErr) {
|
|
13602
|
+
const __neatMsg = String((__neatOtelErr && __neatOtelErr.message) || __neatOtelErr)
|
|
13603
|
+
if (/Cannot find (?:module|package)|MODULE_NOT_FOUND|ERR_MODULE_NOT_FOUND/.test(__neatMsg)) {
|
|
13604
|
+
console.warn('[neat] OpenTelemetry is not active: its packages are not installed, so this app is running without OBSERVED tracing. Run your package manager install and restart to enable it.')
|
|
13605
|
+
} else {
|
|
13606
|
+
console.warn('[neat] OpenTelemetry failed to start; the app is running without OBSERVED tracing: ' + __neatMsg)
|
|
13607
|
+
}
|
|
13608
|
+
}
|
|
12978
13609
|
`;
|
|
12979
13610
|
var OTEL_INIT_TS = `${OTEL_INIT_HEADER}
|
|
12980
13611
|
${OTEL_INIT_STAMP}
|
|
@@ -12989,9 +13620,12 @@ ${OTEL_ENDPOINT_RESOLVER_ESM}
|
|
|
12989
13620
|
${OTEL_OTLP_PROTOCOL_JS}
|
|
12990
13621
|
${OTEL_OTLP_HEADERS_JS}
|
|
12991
13622
|
|
|
12992
|
-
import
|
|
12993
|
-
|
|
12994
|
-
|
|
13623
|
+
// Top-level await preserves the static import's ordering: the host entry does
|
|
13624
|
+
// not run until instrumentation has started (or safely degraded).
|
|
13625
|
+
try {
|
|
13626
|
+
const { NodeSDK } = await import('@opentelemetry/sdk-node')
|
|
13627
|
+
const { getNodeAutoInstrumentations } = await import('@opentelemetry/auto-instrumentations-node')
|
|
13628
|
+
const { trace, context } = await import('@opentelemetry/api')
|
|
12995
13629
|
|
|
12996
13630
|
${CALLSITE_PROCESSOR_TS}
|
|
12997
13631
|
|
|
@@ -13000,6 +13634,14 @@ __INSTRUMENTATION_BLOCK__
|
|
|
13000
13634
|
const sdk = new NodeSDK({ instrumentations })
|
|
13001
13635
|
sdk.start()
|
|
13002
13636
|
${neatWireCaptureSource(true)}
|
|
13637
|
+
} catch (__neatOtelErr) {
|
|
13638
|
+
const __neatMsg = String((__neatOtelErr && __neatOtelErr.message) || __neatOtelErr)
|
|
13639
|
+
if (/Cannot find (?:module|package)|MODULE_NOT_FOUND|ERR_MODULE_NOT_FOUND/.test(__neatMsg)) {
|
|
13640
|
+
console.warn('[neat] OpenTelemetry is not active: its packages are not installed, so this app is running without OBSERVED tracing. Run your package manager install and restart to enable it.')
|
|
13641
|
+
} else {
|
|
13642
|
+
console.warn('[neat] OpenTelemetry failed to start; the app is running without OBSERVED tracing: ' + __neatMsg)
|
|
13643
|
+
}
|
|
13644
|
+
}
|
|
13003
13645
|
`;
|
|
13004
13646
|
function renderNodeOtelInit(template, serviceName, projectName, registrations = []) {
|
|
13005
13647
|
const block = registrations.length === 0 ? "" : `
|
|
@@ -13217,11 +13859,11 @@ var OTEL_ENV = {
|
|
|
13217
13859
|
value: "http://localhost:4318/projects/<project>/v1/traces"
|
|
13218
13860
|
};
|
|
13219
13861
|
function serviceNodeName(pkg, serviceDir) {
|
|
13220
|
-
return pkg.name ??
|
|
13862
|
+
return pkg.name ?? import_node_path57.default.basename(serviceDir);
|
|
13221
13863
|
}
|
|
13222
13864
|
function projectToken(pkg, serviceDir, project) {
|
|
13223
13865
|
if (project && project.length > 0) return project;
|
|
13224
|
-
return pkg.name ??
|
|
13866
|
+
return pkg.name ?? import_node_path57.default.basename(serviceDir);
|
|
13225
13867
|
}
|
|
13226
13868
|
async function readJsonFile(p) {
|
|
13227
13869
|
try {
|
|
@@ -13234,16 +13876,16 @@ async function readJsonFile(p) {
|
|
|
13234
13876
|
async function detectRuntimeKind(pkgRoot, pkg) {
|
|
13235
13877
|
const deps = allDeps(pkg);
|
|
13236
13878
|
if ("react-native" in deps || "expo" in deps) return "react-native";
|
|
13237
|
-
const appJson = await readJsonFile(
|
|
13879
|
+
const appJson = await readJsonFile(import_node_path57.default.join(pkgRoot, "app.json"));
|
|
13238
13880
|
if (appJson && typeof appJson === "object" && "expo" in appJson) {
|
|
13239
13881
|
return "react-native";
|
|
13240
13882
|
}
|
|
13241
|
-
if (await exists3(
|
|
13883
|
+
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
13884
|
return "browser-bundle";
|
|
13243
13885
|
}
|
|
13244
|
-
if (await exists3(
|
|
13245
|
-
if (await exists3(
|
|
13246
|
-
if (await exists3(
|
|
13886
|
+
if (await exists3(import_node_path57.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
|
|
13887
|
+
if (await exists3(import_node_path57.default.join(pkgRoot, "bun.lockb"))) return "bun";
|
|
13888
|
+
if (await exists3(import_node_path57.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path57.default.join(pkgRoot, "deno.lock"))) {
|
|
13247
13889
|
return "deno";
|
|
13248
13890
|
}
|
|
13249
13891
|
const engines = pkg.engines ?? {};
|
|
@@ -13252,7 +13894,7 @@ async function detectRuntimeKind(pkgRoot, pkg) {
|
|
|
13252
13894
|
}
|
|
13253
13895
|
async function readPackageJson2(serviceDir) {
|
|
13254
13896
|
try {
|
|
13255
|
-
const raw = await import_node_fs37.promises.readFile(
|
|
13897
|
+
const raw = await import_node_fs37.promises.readFile(import_node_path57.default.join(serviceDir, "package.json"), "utf8");
|
|
13256
13898
|
return JSON.parse(raw);
|
|
13257
13899
|
} catch {
|
|
13258
13900
|
return null;
|
|
@@ -13296,7 +13938,7 @@ function needsVersionUpgrade(installed, expected) {
|
|
|
13296
13938
|
var NEXT_CONFIG_CANDIDATES = ["next.config.js", "next.config.ts", "next.config.mjs"];
|
|
13297
13939
|
async function findNextConfig(serviceDir) {
|
|
13298
13940
|
for (const name of NEXT_CONFIG_CANDIDATES) {
|
|
13299
|
-
const candidate =
|
|
13941
|
+
const candidate = import_node_path57.default.join(serviceDir, name);
|
|
13300
13942
|
if (await exists3(candidate)) return candidate;
|
|
13301
13943
|
}
|
|
13302
13944
|
return null;
|
|
@@ -13365,7 +14007,7 @@ function hasRemixDependency(pkg) {
|
|
|
13365
14007
|
}
|
|
13366
14008
|
async function findRemixEntry(serviceDir) {
|
|
13367
14009
|
for (const rel of REMIX_ENTRY_CANDIDATES) {
|
|
13368
|
-
const candidate =
|
|
14010
|
+
const candidate = import_node_path57.default.join(serviceDir, rel);
|
|
13369
14011
|
if (await exists3(candidate)) return candidate;
|
|
13370
14012
|
}
|
|
13371
14013
|
return null;
|
|
@@ -13377,14 +14019,14 @@ function hasSvelteKitDependency(pkg) {
|
|
|
13377
14019
|
}
|
|
13378
14020
|
async function findSvelteKitHooks(serviceDir) {
|
|
13379
14021
|
for (const rel of SVELTEKIT_HOOKS_CANDIDATES) {
|
|
13380
|
-
const candidate =
|
|
14022
|
+
const candidate = import_node_path57.default.join(serviceDir, rel);
|
|
13381
14023
|
if (await exists3(candidate)) return candidate;
|
|
13382
14024
|
}
|
|
13383
14025
|
return null;
|
|
13384
14026
|
}
|
|
13385
14027
|
async function findSvelteKitConfig(serviceDir) {
|
|
13386
14028
|
for (const rel of SVELTEKIT_CONFIG_CANDIDATES) {
|
|
13387
|
-
const candidate =
|
|
14029
|
+
const candidate = import_node_path57.default.join(serviceDir, rel);
|
|
13388
14030
|
if (await exists3(candidate)) return candidate;
|
|
13389
14031
|
}
|
|
13390
14032
|
return null;
|
|
@@ -13395,7 +14037,7 @@ function hasNuxtDependency(pkg) {
|
|
|
13395
14037
|
}
|
|
13396
14038
|
async function findNuxtConfig(serviceDir) {
|
|
13397
14039
|
for (const name of NUXT_CONFIG_CANDIDATES) {
|
|
13398
|
-
const candidate =
|
|
14040
|
+
const candidate = import_node_path57.default.join(serviceDir, name);
|
|
13399
14041
|
if (await exists3(candidate)) return candidate;
|
|
13400
14042
|
}
|
|
13401
14043
|
return null;
|
|
@@ -13406,7 +14048,7 @@ function hasAstroDependency(pkg) {
|
|
|
13406
14048
|
}
|
|
13407
14049
|
async function findAstroConfig(serviceDir) {
|
|
13408
14050
|
for (const name of ASTRO_CONFIG_CANDIDATES) {
|
|
13409
|
-
const candidate =
|
|
14051
|
+
const candidate = import_node_path57.default.join(serviceDir, name);
|
|
13410
14052
|
if (await exists3(candidate)) return candidate;
|
|
13411
14053
|
}
|
|
13412
14054
|
return null;
|
|
@@ -13420,7 +14062,7 @@ function parseNextMajor(range) {
|
|
|
13420
14062
|
return Number.isFinite(n) ? n : null;
|
|
13421
14063
|
}
|
|
13422
14064
|
async function isTypeScriptProject(serviceDir) {
|
|
13423
|
-
return exists3(
|
|
14065
|
+
return exists3(import_node_path57.default.join(serviceDir, "tsconfig.json"));
|
|
13424
14066
|
}
|
|
13425
14067
|
var INDEX_EXTENSIONS = [".ts", ".tsx", ".js", ".mjs", ".cjs"];
|
|
13426
14068
|
var INDEX_CANDIDATES = INDEX_EXTENSIONS.map((ext) => `index${ext}`);
|
|
@@ -13469,7 +14111,7 @@ function entryFromScript(script) {
|
|
|
13469
14111
|
}
|
|
13470
14112
|
async function resolveEntry(serviceDir, pkg) {
|
|
13471
14113
|
if (typeof pkg.main === "string" && pkg.main.length > 0) {
|
|
13472
|
-
const candidate =
|
|
14114
|
+
const candidate = import_node_path57.default.resolve(serviceDir, pkg.main);
|
|
13473
14115
|
if (await exists3(candidate)) return candidate;
|
|
13474
14116
|
}
|
|
13475
14117
|
if (pkg.bin) {
|
|
@@ -13483,40 +14125,40 @@ async function resolveEntry(serviceDir, pkg) {
|
|
|
13483
14125
|
if (typeof first === "string") binEntry = first;
|
|
13484
14126
|
}
|
|
13485
14127
|
if (binEntry) {
|
|
13486
|
-
const candidate =
|
|
14128
|
+
const candidate = import_node_path57.default.resolve(serviceDir, binEntry);
|
|
13487
14129
|
if (await exists3(candidate)) return candidate;
|
|
13488
14130
|
}
|
|
13489
14131
|
}
|
|
13490
14132
|
const startEntry = entryFromScript(pkg.scripts?.start);
|
|
13491
14133
|
if (startEntry) {
|
|
13492
|
-
const candidate =
|
|
14134
|
+
const candidate = import_node_path57.default.resolve(serviceDir, startEntry);
|
|
13493
14135
|
if (await exists3(candidate)) return candidate;
|
|
13494
14136
|
}
|
|
13495
14137
|
const devEntry = entryFromScript(pkg.scripts?.dev);
|
|
13496
14138
|
if (devEntry) {
|
|
13497
|
-
const candidate =
|
|
14139
|
+
const candidate = import_node_path57.default.resolve(serviceDir, devEntry);
|
|
13498
14140
|
if (await exists3(candidate)) return candidate;
|
|
13499
14141
|
}
|
|
13500
14142
|
for (const rel of SRC_INDEX_CANDIDATES) {
|
|
13501
|
-
const candidate =
|
|
14143
|
+
const candidate = import_node_path57.default.join(serviceDir, rel);
|
|
13502
14144
|
if (await exists3(candidate)) return candidate;
|
|
13503
14145
|
}
|
|
13504
14146
|
for (const rel of SRC_NAMED_CANDIDATES) {
|
|
13505
|
-
const candidate =
|
|
14147
|
+
const candidate = import_node_path57.default.join(serviceDir, rel);
|
|
13506
14148
|
if (await exists3(candidate)) return candidate;
|
|
13507
14149
|
}
|
|
13508
14150
|
for (const rel of ROOT_NAMED_CANDIDATES) {
|
|
13509
|
-
const candidate =
|
|
14151
|
+
const candidate = import_node_path57.default.join(serviceDir, rel);
|
|
13510
14152
|
if (await exists3(candidate)) return candidate;
|
|
13511
14153
|
}
|
|
13512
14154
|
for (const name of INDEX_CANDIDATES) {
|
|
13513
|
-
const candidate =
|
|
14155
|
+
const candidate = import_node_path57.default.join(serviceDir, name);
|
|
13514
14156
|
if (await exists3(candidate)) return candidate;
|
|
13515
14157
|
}
|
|
13516
14158
|
return null;
|
|
13517
14159
|
}
|
|
13518
14160
|
function dispatchEntry(entryFile, pkg) {
|
|
13519
|
-
const ext =
|
|
14161
|
+
const ext = import_node_path57.default.extname(entryFile).toLowerCase();
|
|
13520
14162
|
if (ext === ".ts" || ext === ".tsx") return "ts";
|
|
13521
14163
|
if (ext === ".mjs") return "esm";
|
|
13522
14164
|
if (ext === ".cjs") return "cjs";
|
|
@@ -13533,9 +14175,9 @@ function otelInitContents(flavor) {
|
|
|
13533
14175
|
return OTEL_INIT_CJS;
|
|
13534
14176
|
}
|
|
13535
14177
|
function injectionLine(flavor, entryFile, otelInitFile) {
|
|
13536
|
-
let rel =
|
|
14178
|
+
let rel = import_node_path57.default.relative(import_node_path57.default.dirname(entryFile), otelInitFile);
|
|
13537
14179
|
if (!rel.startsWith(".")) rel = `./${rel}`;
|
|
13538
|
-
rel = rel.split(
|
|
14180
|
+
rel = rel.split(import_node_path57.default.sep).join("/");
|
|
13539
14181
|
if (flavor === "cjs") return `require('${rel}')`;
|
|
13540
14182
|
if (flavor === "esm") return `import '${rel}'`;
|
|
13541
14183
|
const tsRel = rel.replace(/\.ts$/, "");
|
|
@@ -13548,27 +14190,27 @@ function lineIsOtelInjection(line) {
|
|
|
13548
14190
|
}
|
|
13549
14191
|
async function detectsSrcLayout(serviceDir) {
|
|
13550
14192
|
const [hasSrcApp, hasSrcPages, hasRootApp, hasRootPages] = await Promise.all([
|
|
13551
|
-
exists3(
|
|
13552
|
-
exists3(
|
|
13553
|
-
exists3(
|
|
13554
|
-
exists3(
|
|
14193
|
+
exists3(import_node_path57.default.join(serviceDir, "src", "app")),
|
|
14194
|
+
exists3(import_node_path57.default.join(serviceDir, "src", "pages")),
|
|
14195
|
+
exists3(import_node_path57.default.join(serviceDir, "app")),
|
|
14196
|
+
exists3(import_node_path57.default.join(serviceDir, "pages"))
|
|
13555
14197
|
]);
|
|
13556
14198
|
return (hasSrcApp || hasSrcPages) && !hasRootApp && !hasRootPages;
|
|
13557
14199
|
}
|
|
13558
14200
|
async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project) {
|
|
13559
14201
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
13560
14202
|
const srcLayout = await detectsSrcLayout(serviceDir);
|
|
13561
|
-
const baseDir = srcLayout ?
|
|
13562
|
-
const instrumentationFile =
|
|
13563
|
-
const instrumentationNodeFile =
|
|
14203
|
+
const baseDir = srcLayout ? import_node_path57.default.join(serviceDir, "src") : serviceDir;
|
|
14204
|
+
const instrumentationFile = import_node_path57.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
|
|
14205
|
+
const instrumentationNodeFile = import_node_path57.default.join(
|
|
13564
14206
|
baseDir,
|
|
13565
14207
|
useTs ? "instrumentation.node.ts" : "instrumentation.node.js"
|
|
13566
14208
|
);
|
|
13567
|
-
const instrumentationEdgeFile =
|
|
14209
|
+
const instrumentationEdgeFile = import_node_path57.default.join(
|
|
13568
14210
|
baseDir,
|
|
13569
14211
|
useTs ? "instrumentation.edge.ts" : "instrumentation.edge.js"
|
|
13570
14212
|
);
|
|
13571
|
-
const envNeatFile =
|
|
14213
|
+
const envNeatFile = import_node_path57.default.join(baseDir, ".env.neat");
|
|
13572
14214
|
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
13573
14215
|
const dependencyEdits = [];
|
|
13574
14216
|
for (const sdk of SDK_PACKAGES) {
|
|
@@ -13693,7 +14335,7 @@ function buildDependencyEdits(pkg, manifestPath) {
|
|
|
13693
14335
|
return edits;
|
|
13694
14336
|
}
|
|
13695
14337
|
async function queueEnvNeat(serviceDir, pkg, project, generatedFiles) {
|
|
13696
|
-
const envNeatFile =
|
|
14338
|
+
const envNeatFile = import_node_path57.default.join(serviceDir, ".env.neat");
|
|
13697
14339
|
if (!await exists3(envNeatFile)) {
|
|
13698
14340
|
generatedFiles.push({
|
|
13699
14341
|
file: envNeatFile,
|
|
@@ -13728,7 +14370,7 @@ function fileImportsOtelHook(raw, specifiers) {
|
|
|
13728
14370
|
}
|
|
13729
14371
|
async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
13730
14372
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
13731
|
-
const otelServerFile =
|
|
14373
|
+
const otelServerFile = import_node_path57.default.join(
|
|
13732
14374
|
serviceDir,
|
|
13733
14375
|
useTs ? "app/otel.server.ts" : "app/otel.server.js"
|
|
13734
14376
|
);
|
|
@@ -13785,11 +14427,11 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
|
13785
14427
|
}
|
|
13786
14428
|
async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project) {
|
|
13787
14429
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
13788
|
-
const otelInitFile =
|
|
14430
|
+
const otelInitFile = import_node_path57.default.join(
|
|
13789
14431
|
serviceDir,
|
|
13790
14432
|
useTs ? "src/otel-init.ts" : "src/otel-init.js"
|
|
13791
14433
|
);
|
|
13792
|
-
const resolvedHooksFile = hooksFile ??
|
|
14434
|
+
const resolvedHooksFile = hooksFile ?? import_node_path57.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
|
|
13793
14435
|
const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
|
|
13794
14436
|
const generatedFiles = [];
|
|
13795
14437
|
const entrypointEdits = [];
|
|
@@ -13851,11 +14493,11 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project)
|
|
|
13851
14493
|
}
|
|
13852
14494
|
async function planNuxt(serviceDir, pkg, manifestPath, project) {
|
|
13853
14495
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
13854
|
-
const otelPluginFile =
|
|
14496
|
+
const otelPluginFile = import_node_path57.default.join(
|
|
13855
14497
|
serviceDir,
|
|
13856
14498
|
useTs ? "server/plugins/otel.ts" : "server/plugins/otel.js"
|
|
13857
14499
|
);
|
|
13858
|
-
const otelInitFile =
|
|
14500
|
+
const otelInitFile = import_node_path57.default.join(
|
|
13859
14501
|
serviceDir,
|
|
13860
14502
|
useTs ? "server/plugins/otel-init.ts" : "server/plugins/otel-init.js"
|
|
13861
14503
|
);
|
|
@@ -13906,19 +14548,19 @@ async function planNuxt(serviceDir, pkg, manifestPath, project) {
|
|
|
13906
14548
|
var ASTRO_MIDDLEWARE_CANDIDATES = ["src/middleware.ts", "src/middleware.js"];
|
|
13907
14549
|
async function findAstroMiddleware(serviceDir) {
|
|
13908
14550
|
for (const rel of ASTRO_MIDDLEWARE_CANDIDATES) {
|
|
13909
|
-
const candidate =
|
|
14551
|
+
const candidate = import_node_path57.default.join(serviceDir, rel);
|
|
13910
14552
|
if (await exists3(candidate)) return candidate;
|
|
13911
14553
|
}
|
|
13912
14554
|
return null;
|
|
13913
14555
|
}
|
|
13914
14556
|
async function planAstro(serviceDir, pkg, manifestPath, project) {
|
|
13915
14557
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
13916
|
-
const otelInitFile =
|
|
14558
|
+
const otelInitFile = import_node_path57.default.join(
|
|
13917
14559
|
serviceDir,
|
|
13918
14560
|
useTs ? "src/otel-init.ts" : "src/otel-init.js"
|
|
13919
14561
|
);
|
|
13920
14562
|
const existingMiddleware = await findAstroMiddleware(serviceDir);
|
|
13921
|
-
const middlewareFile = existingMiddleware ??
|
|
14563
|
+
const middlewareFile = existingMiddleware ?? import_node_path57.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
|
|
13922
14564
|
const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
|
|
13923
14565
|
const generatedFiles = [];
|
|
13924
14566
|
const entrypointEdits = [];
|
|
@@ -14014,7 +14656,7 @@ async function findFrameworkDispatch(serviceDir, pkg, manifestPath, project) {
|
|
|
14014
14656
|
}
|
|
14015
14657
|
async function plan(serviceDir, opts) {
|
|
14016
14658
|
const pkg = await readPackageJson2(serviceDir);
|
|
14017
|
-
const manifestPath =
|
|
14659
|
+
const manifestPath = import_node_path57.default.join(serviceDir, "package.json");
|
|
14018
14660
|
const project = opts?.project;
|
|
14019
14661
|
const empty = {
|
|
14020
14662
|
language: "javascript",
|
|
@@ -14049,8 +14691,8 @@ async function plan(serviceDir, opts) {
|
|
|
14049
14691
|
return { ...empty, libOnly: true };
|
|
14050
14692
|
}
|
|
14051
14693
|
const flavor = dispatchEntry(entryFile, pkg);
|
|
14052
|
-
const otelInitFile =
|
|
14053
|
-
const envNeatFile =
|
|
14694
|
+
const otelInitFile = import_node_path57.default.join(import_node_path57.default.dirname(entryFile), otelInitFilename(flavor));
|
|
14695
|
+
const envNeatFile = import_node_path57.default.join(serviceDir, ".env.neat");
|
|
14054
14696
|
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
14055
14697
|
const dependencyEdits = [];
|
|
14056
14698
|
for (const sdk of SDK_PACKAGES) {
|
|
@@ -14119,13 +14761,13 @@ async function plan(serviceDir, opts) {
|
|
|
14119
14761
|
};
|
|
14120
14762
|
}
|
|
14121
14763
|
function isAllowedWritePath(serviceDir, target) {
|
|
14122
|
-
const rel =
|
|
14764
|
+
const rel = import_node_path57.default.relative(serviceDir, target);
|
|
14123
14765
|
if (rel.startsWith("..")) return false;
|
|
14124
|
-
const base =
|
|
14766
|
+
const base = import_node_path57.default.basename(target);
|
|
14125
14767
|
if (base === "package.json") return true;
|
|
14126
14768
|
if (base === ".env.neat") return true;
|
|
14127
14769
|
if (/^otel-init\.(?:js|cjs|mjs|ts)$/.test(base)) return true;
|
|
14128
|
-
const relPosix = rel.split(
|
|
14770
|
+
const relPosix = rel.split(import_node_path57.default.sep).join("/");
|
|
14129
14771
|
if (/^instrumentation(?:\.(?:node|edge))?\.(?:js|cjs|mjs|ts)$/.test(base)) {
|
|
14130
14772
|
if (relPosix === base) return true;
|
|
14131
14773
|
if (relPosix === `src/${base}`) return true;
|
|
@@ -14142,7 +14784,7 @@ function isAllowedWritePath(serviceDir, target) {
|
|
|
14142
14784
|
return false;
|
|
14143
14785
|
}
|
|
14144
14786
|
async function writeAtomic(file, contents) {
|
|
14145
|
-
await import_node_fs37.promises.mkdir(
|
|
14787
|
+
await import_node_fs37.promises.mkdir(import_node_path57.default.dirname(file), { recursive: true });
|
|
14146
14788
|
const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
14147
14789
|
await import_node_fs37.promises.writeFile(tmp, contents, "utf8");
|
|
14148
14790
|
await import_node_fs37.promises.rename(tmp, file);
|
|
@@ -14326,7 +14968,7 @@ async function rollback(installPlan, originals, createdFiles) {
|
|
|
14326
14968
|
...removed.map((f) => `removed: ${f}`),
|
|
14327
14969
|
""
|
|
14328
14970
|
];
|
|
14329
|
-
const rollbackPath =
|
|
14971
|
+
const rollbackPath = import_node_path57.default.join(installPlan.serviceDir, "neat-rollback.patch");
|
|
14330
14972
|
await import_node_fs37.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
|
|
14331
14973
|
}
|
|
14332
14974
|
function injectInstrumentationHook(raw) {
|
|
@@ -14357,7 +14999,7 @@ var javascriptInstaller = {
|
|
|
14357
14999
|
// src/installers/python.ts
|
|
14358
15000
|
init_cjs_shims();
|
|
14359
15001
|
var import_node_fs38 = require("fs");
|
|
14360
|
-
var
|
|
15002
|
+
var import_node_path58 = __toESM(require("path"), 1);
|
|
14361
15003
|
var SDK_PACKAGES2 = [
|
|
14362
15004
|
{ name: "opentelemetry-distro", version: ">=0.49b0" },
|
|
14363
15005
|
{ name: "opentelemetry-exporter-otlp", version: ">=1.28.0" }
|
|
@@ -14378,7 +15020,7 @@ async function exists4(p) {
|
|
|
14378
15020
|
async function detect2(serviceDir) {
|
|
14379
15021
|
const markers = ["requirements.txt", "pyproject.toml", "setup.py"];
|
|
14380
15022
|
for (const m of markers) {
|
|
14381
|
-
if (await exists4(
|
|
15023
|
+
if (await exists4(import_node_path58.default.join(serviceDir, m))) return true;
|
|
14382
15024
|
}
|
|
14383
15025
|
return false;
|
|
14384
15026
|
}
|
|
@@ -14388,7 +15030,7 @@ function reqPackageName(line) {
|
|
|
14388
15030
|
return head.replace(/[<>=!~].*$/, "").toLowerCase();
|
|
14389
15031
|
}
|
|
14390
15032
|
async function planRequirementsTxtEdits(serviceDir) {
|
|
14391
|
-
const file =
|
|
15033
|
+
const file = import_node_path58.default.join(serviceDir, "requirements.txt");
|
|
14392
15034
|
if (!await exists4(file)) return null;
|
|
14393
15035
|
const raw = await import_node_fs38.promises.readFile(file, "utf8");
|
|
14394
15036
|
const presentNames = new Set(
|
|
@@ -14398,7 +15040,7 @@ async function planRequirementsTxtEdits(serviceDir) {
|
|
|
14398
15040
|
return { manifest: file, missing: [...missing] };
|
|
14399
15041
|
}
|
|
14400
15042
|
async function planProcfileEdits(serviceDir) {
|
|
14401
|
-
const procfile =
|
|
15043
|
+
const procfile = import_node_path58.default.join(serviceDir, "Procfile");
|
|
14402
15044
|
if (!await exists4(procfile)) return [];
|
|
14403
15045
|
const raw = await import_node_fs38.promises.readFile(procfile, "utf8");
|
|
14404
15046
|
const edits = [];
|
|
@@ -14487,7 +15129,7 @@ async function apply2(installPlan) {
|
|
|
14487
15129
|
if (raw === void 0) {
|
|
14488
15130
|
throw new Error(`python installer: cannot read ${file} during apply`);
|
|
14489
15131
|
}
|
|
14490
|
-
const base =
|
|
15132
|
+
const base = import_node_path58.default.basename(file);
|
|
14491
15133
|
if (base === "requirements.txt") {
|
|
14492
15134
|
const edits = installPlan.dependencyEdits.filter((e) => e.file === file);
|
|
14493
15135
|
if (edits.length > 0) {
|
|
@@ -14526,7 +15168,7 @@ async function rollback2(installPlan, originals) {
|
|
|
14526
15168
|
...restored.map((f) => `restored: ${f}`),
|
|
14527
15169
|
""
|
|
14528
15170
|
];
|
|
14529
|
-
const rollbackPath =
|
|
15171
|
+
const rollbackPath = import_node_path58.default.join(installPlan.serviceDir, "neat-rollback.patch");
|
|
14530
15172
|
await import_node_fs38.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
|
|
14531
15173
|
}
|
|
14532
15174
|
var pythonInstaller = {
|
|
@@ -14653,7 +15295,7 @@ init_cjs_shims();
|
|
|
14653
15295
|
var import_node_fs39 = require("fs");
|
|
14654
15296
|
var import_node_http = __toESM(require("http"), 1);
|
|
14655
15297
|
var import_node_net = __toESM(require("net"), 1);
|
|
14656
|
-
var
|
|
15298
|
+
var import_node_path59 = __toESM(require("path"), 1);
|
|
14657
15299
|
var import_node_url4 = require("url");
|
|
14658
15300
|
var import_node_child_process3 = require("child_process");
|
|
14659
15301
|
var import_node_readline = __toESM(require("readline"), 1);
|
|
@@ -14663,7 +15305,7 @@ async function extractAndPersist(opts) {
|
|
|
14663
15305
|
const graphKey = opts.projectExplicit ? opts.project : DEFAULT_PROJECT;
|
|
14664
15306
|
resetGraph(graphKey);
|
|
14665
15307
|
const graph = getGraph(graphKey);
|
|
14666
|
-
const projectPaths = pathsForProject(graphKey,
|
|
15308
|
+
const projectPaths = pathsForProject(graphKey, import_node_path59.default.join(opts.scanPath, "neat-out"));
|
|
14667
15309
|
const extraction = await extractFromDirectory(graph, opts.scanPath, {
|
|
14668
15310
|
errorsPath: projectPaths.errorsPath
|
|
14669
15311
|
});
|
|
@@ -14715,7 +15357,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
14715
15357
|
libOnly++;
|
|
14716
15358
|
const appDeps = svc.pkg ? appFrameworkDependencies(svc.pkg) : [];
|
|
14717
15359
|
if (appDeps.length > 0) {
|
|
14718
|
-
const svcName =
|
|
15360
|
+
const svcName = import_node_path59.default.basename(svc.dir);
|
|
14719
15361
|
const list = appDeps.join(", ");
|
|
14720
15362
|
console.warn(
|
|
14721
15363
|
`neat: runtime layer won't engage for ${svcName}: no entry point found.
|
|
@@ -14728,7 +15370,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
14728
15370
|
console.log(`skipping ${svc.dir}: browser bundle; browser-OTel support lands in a future release.`);
|
|
14729
15371
|
} else if (outcome.outcome === "react-native") {
|
|
14730
15372
|
reactNative++;
|
|
14731
|
-
const svcName =
|
|
15373
|
+
const svcName = import_node_path59.default.basename(svc.dir);
|
|
14732
15374
|
console.log(
|
|
14733
15375
|
`neat: ${svc.dir} detected as React Native / Expo
|
|
14734
15376
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -14739,7 +15381,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
14739
15381
|
);
|
|
14740
15382
|
} else if (outcome.outcome === "bun") {
|
|
14741
15383
|
bun++;
|
|
14742
|
-
const svcName =
|
|
15384
|
+
const svcName = import_node_path59.default.basename(svc.dir);
|
|
14743
15385
|
console.log(
|
|
14744
15386
|
`neat: ${svc.dir} detected as Bun
|
|
14745
15387
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -14750,7 +15392,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
14750
15392
|
);
|
|
14751
15393
|
} else if (outcome.outcome === "deno") {
|
|
14752
15394
|
deno++;
|
|
14753
|
-
const svcName =
|
|
15395
|
+
const svcName = import_node_path59.default.basename(svc.dir);
|
|
14754
15396
|
console.log(
|
|
14755
15397
|
`neat: ${svc.dir} detected as Deno
|
|
14756
15398
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -14761,7 +15403,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
14761
15403
|
);
|
|
14762
15404
|
} else if (outcome.outcome === "cloudflare-workers") {
|
|
14763
15405
|
cloudflareWorkers++;
|
|
14764
|
-
const svcName =
|
|
15406
|
+
const svcName = import_node_path59.default.basename(svc.dir);
|
|
14765
15407
|
console.log(
|
|
14766
15408
|
`neat: ${svc.dir} detected as Cloudflare Workers
|
|
14767
15409
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -14772,7 +15414,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
14772
15414
|
);
|
|
14773
15415
|
} else if (outcome.outcome === "electron") {
|
|
14774
15416
|
electron++;
|
|
14775
|
-
const svcName =
|
|
15417
|
+
const svcName = import_node_path59.default.basename(svc.dir);
|
|
14776
15418
|
console.log(
|
|
14777
15419
|
`neat: ${svc.dir} detected as Electron
|
|
14778
15420
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -14785,7 +15427,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
14785
15427
|
if (svc.pkg && (outcome.outcome === "instrumented" || outcome.outcome === "already-instrumented")) {
|
|
14786
15428
|
const gaps = uninstrumentedLibraries(svc.pkg);
|
|
14787
15429
|
if (gaps.length > 0) {
|
|
14788
|
-
const svcName =
|
|
15430
|
+
const svcName = import_node_path59.default.basename(svc.dir);
|
|
14789
15431
|
const list = gaps.join(", ");
|
|
14790
15432
|
const subject = gaps.length === 1 ? "this library" : "these libraries";
|
|
14791
15433
|
const aux = gaps.length === 1 ? "isn't" : "aren't";
|
|
@@ -14991,8 +15633,8 @@ async function persistedPortsFor(scanPath) {
|
|
|
14991
15633
|
return { rest: record.ports.rest, otlp: record.ports.otlp, web: record.ports.web };
|
|
14992
15634
|
}
|
|
14993
15635
|
async function acquireSpawnLock(scanPath) {
|
|
14994
|
-
const lockPath =
|
|
14995
|
-
await import_node_fs39.promises.mkdir(
|
|
15636
|
+
const lockPath = import_node_path59.default.join(scanPath, "neat-out", "daemon.spawn.lock");
|
|
15637
|
+
await import_node_fs39.promises.mkdir(import_node_path59.default.dirname(lockPath), { recursive: true });
|
|
14996
15638
|
const STALE_LOCK_MS = 6e4;
|
|
14997
15639
|
try {
|
|
14998
15640
|
const fd = await import_node_fs39.promises.open(lockPath, "wx");
|
|
@@ -15037,13 +15679,13 @@ async function healthIsForProject(restPort, project) {
|
|
|
15037
15679
|
return false;
|
|
15038
15680
|
}
|
|
15039
15681
|
function daemonLogPath(projectPath2) {
|
|
15040
|
-
return
|
|
15682
|
+
return import_node_path59.default.join(projectPath2, "neat-out", "daemon.log");
|
|
15041
15683
|
}
|
|
15042
15684
|
function spawnDaemonDetached(spec) {
|
|
15043
|
-
const here =
|
|
15685
|
+
const here = import_node_path59.default.dirname((0, import_node_url4.fileURLToPath)(importMetaUrl));
|
|
15044
15686
|
const candidates = [
|
|
15045
|
-
|
|
15046
|
-
|
|
15687
|
+
import_node_path59.default.join(here, "neatd.cjs"),
|
|
15688
|
+
import_node_path59.default.join(here, "neatd.js")
|
|
15047
15689
|
];
|
|
15048
15690
|
let entry2 = null;
|
|
15049
15691
|
const fsSync = require("fs");
|
|
@@ -15073,7 +15715,7 @@ function spawnDaemonDetached(spec) {
|
|
|
15073
15715
|
let logFd = null;
|
|
15074
15716
|
if (spec) {
|
|
15075
15717
|
const logPath = daemonLogPath(spec.projectPath);
|
|
15076
|
-
fsSync.mkdirSync(
|
|
15718
|
+
fsSync.mkdirSync(import_node_path59.default.dirname(logPath), { recursive: true });
|
|
15077
15719
|
logFd = fsSync.openSync(logPath, "a");
|
|
15078
15720
|
}
|
|
15079
15721
|
const child = (0, import_node_child_process3.spawn)(process.execPath, [entry2, "start"], {
|
|
@@ -15272,7 +15914,7 @@ async function runOrchestrator(opts) {
|
|
|
15272
15914
|
result.steps.browser = openBrowser(dashboardUrl);
|
|
15273
15915
|
}
|
|
15274
15916
|
const daemonRunning = result.steps.daemon === "spawned" || result.steps.daemon === "already-running";
|
|
15275
|
-
const daemonLog = daemonRunning ?
|
|
15917
|
+
const daemonLog = daemonRunning ? import_node_path59.default.relative(opts.scanPath, daemonLogPath(opts.scanPath)) : null;
|
|
15276
15918
|
printSummary(result, graph, dashboardUrl, daemonLog);
|
|
15277
15919
|
return result;
|
|
15278
15920
|
}
|
|
@@ -15298,9 +15940,26 @@ function printSummary(result, graph, dashboardUrl, daemonLog) {
|
|
|
15298
15940
|
} else {
|
|
15299
15941
|
console.log("running locally \u2014 open the dashboard, no token needed");
|
|
15300
15942
|
}
|
|
15943
|
+
const failedInstalls = (result.steps.apply.packageManagerInstalls ?? []).filter(
|
|
15944
|
+
(i) => i.exitCode !== 0
|
|
15945
|
+
);
|
|
15301
15946
|
if (daemonLog !== null) {
|
|
15302
15947
|
console.log("");
|
|
15303
15948
|
console.log(`daemon running in the background (logs: ${daemonLog})`);
|
|
15949
|
+
}
|
|
15950
|
+
if (failedInstalls.length > 0) {
|
|
15951
|
+
if (daemonLog === null) console.log("");
|
|
15952
|
+
console.log(
|
|
15953
|
+
"instrumentation is wired into your manifests, but it is NOT yet active:"
|
|
15954
|
+
);
|
|
15955
|
+
console.log(
|
|
15956
|
+
"the dependency install did not complete, so the OTel SDK never landed and"
|
|
15957
|
+
);
|
|
15958
|
+
console.log("OBSERVED edges will stay empty until you finish the install. Fix it:");
|
|
15959
|
+
for (const i of failedInstalls) {
|
|
15960
|
+
console.log(` run \`${i.pm} install\` in ${i.cwd}`);
|
|
15961
|
+
}
|
|
15962
|
+
} else if (daemonLog !== null) {
|
|
15304
15963
|
console.log(
|
|
15305
15964
|
"next: run your app or your test suite \u2014 OBSERVED edges fill in as it executes,"
|
|
15306
15965
|
);
|
|
@@ -15352,6 +16011,8 @@ function parseConnectorArgs(args) {
|
|
|
15352
16011
|
positional: [],
|
|
15353
16012
|
skipValidate: false,
|
|
15354
16013
|
plaintext: false,
|
|
16014
|
+
help: false,
|
|
16015
|
+
json: false,
|
|
15355
16016
|
fields: {}
|
|
15356
16017
|
};
|
|
15357
16018
|
const positional = [];
|
|
@@ -15365,6 +16026,14 @@ function parseConnectorArgs(args) {
|
|
|
15365
16026
|
out.plaintext = true;
|
|
15366
16027
|
continue;
|
|
15367
16028
|
}
|
|
16029
|
+
if (arg === "--help" || arg === "-h") {
|
|
16030
|
+
out.help = true;
|
|
16031
|
+
continue;
|
|
16032
|
+
}
|
|
16033
|
+
if (arg === "--json") {
|
|
16034
|
+
out.json = true;
|
|
16035
|
+
continue;
|
|
16036
|
+
}
|
|
15368
16037
|
if (arg.startsWith("--")) {
|
|
15369
16038
|
let flag = arg;
|
|
15370
16039
|
let value;
|
|
@@ -15441,19 +16110,20 @@ function plaintextFields(ref) {
|
|
|
15441
16110
|
async function connectorAdd(args, deps) {
|
|
15442
16111
|
let provider = args.positional[0];
|
|
15443
16112
|
if (!provider && deps.interactive) {
|
|
15444
|
-
provider = (await deps.prompt(`Provider (${
|
|
16113
|
+
provider = (await deps.prompt(`Provider (${knownProviderNames().join(", ")}):`)).trim();
|
|
15445
16114
|
}
|
|
15446
16115
|
if (!provider) {
|
|
15447
16116
|
deps.err("neat connector add: a provider is required (e.g. `neat connector add supabase`)");
|
|
15448
16117
|
return 2;
|
|
15449
16118
|
}
|
|
15450
|
-
const dispatch =
|
|
16119
|
+
const dispatch = getProviderFieldSchema(provider);
|
|
15451
16120
|
if (!dispatch) {
|
|
15452
16121
|
deps.err(
|
|
15453
|
-
`neat connector add: unknown provider "${provider}". known providers: ${
|
|
16122
|
+
`neat connector add: unknown provider "${provider}". known providers: ${knownProviderNames().join(", ")}`
|
|
15454
16123
|
);
|
|
15455
16124
|
return 2;
|
|
15456
16125
|
}
|
|
16126
|
+
const push = isPushProvider(provider);
|
|
15457
16127
|
let project = args.project;
|
|
15458
16128
|
if (project === void 0 && deps.interactive) {
|
|
15459
16129
|
const answer = (await deps.prompt("Project (blank = the project the daemon is bootstrapping):")).trim();
|
|
@@ -15492,7 +16162,7 @@ async function connectorAdd(args, deps) {
|
|
|
15492
16162
|
credential,
|
|
15493
16163
|
...Object.keys(options).length > 0 ? { options } : {}
|
|
15494
16164
|
};
|
|
15495
|
-
if (!args.skipValidate) {
|
|
16165
|
+
if (!push && !args.skipValidate) {
|
|
15496
16166
|
const outcome = await validateConnectorEntry(entry2, deps.env, deps.fetchImpl);
|
|
15497
16167
|
const code = reportPreWriteValidation(outcome, deps);
|
|
15498
16168
|
if (code !== 0) return code;
|
|
@@ -15503,14 +16173,32 @@ async function connectorAdd(args, deps) {
|
|
|
15503
16173
|
`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
16174
|
);
|
|
15505
16175
|
}
|
|
16176
|
+
if (push) {
|
|
16177
|
+
if (args.skipValidate) {
|
|
16178
|
+
deps.out("note: --skip-validate has no effect for a push provider \u2014 provisioning the drain is the add.");
|
|
16179
|
+
}
|
|
16180
|
+
const outcome = await provisionConnector(entry2, deps.env, deps.fetchImpl);
|
|
16181
|
+
if (outcome.status !== "ok") {
|
|
16182
|
+
deps.err(
|
|
16183
|
+
`neat connector add: could not provision the ${provider} connector \u2014 ${outcome.reason}. Nothing was written.`
|
|
16184
|
+
);
|
|
16185
|
+
return 1;
|
|
16186
|
+
}
|
|
16187
|
+
if (outcome.options) entry2.options = { ...entry2.options ?? {}, ...outcome.options };
|
|
16188
|
+
if (outcome.note) deps.out(`note: ${outcome.note}`);
|
|
16189
|
+
}
|
|
15506
16190
|
const { replaced } = await upsertConnectorEntry(entry2, deps.home);
|
|
15507
16191
|
const verb = replaced ? "updated" : "added";
|
|
15508
16192
|
const where = project ? `project "${project}"` : "the bootstrapping project";
|
|
15509
16193
|
deps.out(`${verb} connector "${id}" (${provider}) for ${where}.`);
|
|
15510
|
-
if (
|
|
15511
|
-
deps.out("
|
|
16194
|
+
if (push) {
|
|
16195
|
+
deps.out("The drain is live \u2014 traces arrive at the daemon's OTLP receiver. No poll or restart needed.");
|
|
16196
|
+
} else {
|
|
16197
|
+
if (args.skipValidate) {
|
|
16198
|
+
deps.out("skipped validation (--skip-validate) \u2014 the credential is checked at the next daemon poll.");
|
|
16199
|
+
}
|
|
16200
|
+
deps.out("Restart the project's daemon (or start it) to begin polling this connector.");
|
|
15512
16201
|
}
|
|
15513
|
-
deps.out("Restart the project's daemon (or start it) to begin polling this connector.");
|
|
15514
16202
|
return 0;
|
|
15515
16203
|
}
|
|
15516
16204
|
function reportPreWriteValidation(outcome, deps) {
|
|
@@ -15536,10 +16224,26 @@ function reportPreWriteValidation(outcome, deps) {
|
|
|
15536
16224
|
return 2;
|
|
15537
16225
|
}
|
|
15538
16226
|
}
|
|
15539
|
-
async function connectorList(deps, filterProject) {
|
|
16227
|
+
async function connectorList(deps, filterProject, json = false) {
|
|
15540
16228
|
const config = await readConnectorsConfig(deps.home);
|
|
15541
16229
|
let entries = config.connectors;
|
|
15542
16230
|
if (filterProject) entries = entries.filter((e) => e.project === filterProject);
|
|
16231
|
+
if (json) {
|
|
16232
|
+
const rows = entries.map((e) => ({
|
|
16233
|
+
id: e.id,
|
|
16234
|
+
provider: e.provider,
|
|
16235
|
+
project: e.project ?? null,
|
|
16236
|
+
kind: isPushProvider(e.provider) ? "push" : "pull",
|
|
16237
|
+
credential: describeCredential(e.credential, deps.env).map((c) => ({
|
|
16238
|
+
...c.field ? { field: c.field } : {},
|
|
16239
|
+
ref: c.display,
|
|
16240
|
+
...c.status ? { status: c.status } : { kind: c.kind }
|
|
16241
|
+
})),
|
|
16242
|
+
...e.options ? { options: e.options } : {}
|
|
16243
|
+
}));
|
|
16244
|
+
deps.out(JSON.stringify(rows, null, 2));
|
|
16245
|
+
return 0;
|
|
16246
|
+
}
|
|
15543
16247
|
if (entries.length === 0) {
|
|
15544
16248
|
deps.out(
|
|
15545
16249
|
filterProject ? `no connectors configured for project "${filterProject}".` : "no connectors configured. run `neat connector add <provider>` to add one."
|
|
@@ -15562,6 +16266,22 @@ async function connectorRemove(deps, id) {
|
|
|
15562
16266
|
deps.err("neat connector remove: missing <id>. run `neat connector list` to see configured ids.");
|
|
15563
16267
|
return 2;
|
|
15564
16268
|
}
|
|
16269
|
+
const config = await readConnectorsConfig(deps.home);
|
|
16270
|
+
const entry2 = config.connectors.find((c) => c.id === id);
|
|
16271
|
+
if (!entry2) {
|
|
16272
|
+
deps.err(`neat connector remove: no connector with id "${id}". run \`neat connector list\` to see configured ids.`);
|
|
16273
|
+
return 1;
|
|
16274
|
+
}
|
|
16275
|
+
if (isPushProvider(entry2.provider)) {
|
|
16276
|
+
const outcome = await deprovisionConnector(entry2, deps.env, deps.fetchImpl);
|
|
16277
|
+
if (outcome.status !== "ok") {
|
|
16278
|
+
deps.err(
|
|
16279
|
+
`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).`
|
|
16280
|
+
);
|
|
16281
|
+
return 1;
|
|
16282
|
+
}
|
|
16283
|
+
if (outcome.note) deps.out(`note: ${outcome.note}`);
|
|
16284
|
+
}
|
|
15565
16285
|
const removed = await removeConnectorEntry(id, deps.home);
|
|
15566
16286
|
if (!removed) {
|
|
15567
16287
|
deps.err(`neat connector remove: no connector with id "${id}". run \`neat connector list\` to see configured ids.`);
|
|
@@ -15602,13 +16322,20 @@ async function connectorTest(deps, id) {
|
|
|
15602
16322
|
}
|
|
15603
16323
|
function printConnectorUsage(write) {
|
|
15604
16324
|
write("usage: neat connector <add|list|remove|test> [args]");
|
|
16325
|
+
write(` providers: ${knownProviderNames().join(", ")}`);
|
|
16326
|
+
write(" pull (polled): supabase, railway, firebase, cloudflare");
|
|
16327
|
+
write(" push (drains): vercel \u2014 provisions a Vercel trace Drain that forwards");
|
|
16328
|
+
write(" traces to the daemon's OTLP receiver; no app instrumentation");
|
|
15605
16329
|
write(" add <provider> add a connector; validates the credential first (--skip-validate to skip)");
|
|
15606
16330
|
write(" flags: --project <name> --credential/--token <$VAR|value> --id <id>");
|
|
15607
16331
|
write(" --<option> <value> (provider-specific) --plaintext --skip-validate");
|
|
16332
|
+
write(" vercel: neat connector add vercel --token $VERCEL_TOKEN \\");
|
|
16333
|
+
write(" --otel-token $NEAT_OTEL_TOKEN --team-id <teamId> \\");
|
|
16334
|
+
write(" --endpoint https://<public-host>/v1/traces [--project-ids <id,id>]");
|
|
15608
16335
|
write(" list list configured connectors (credentials shown redacted)");
|
|
15609
16336
|
write(" flags: --project <name>");
|
|
15610
|
-
write(" remove <id> remove a connector by id");
|
|
15611
|
-
write(" test <id> re-run
|
|
16337
|
+
write(" remove <id> remove a connector by id (a push provider also has its drain deleted)");
|
|
16338
|
+
write(" test <id> re-run validation for an existing connector (a push provider re-tests its drain)");
|
|
15612
16339
|
}
|
|
15613
16340
|
async function runConnectorCommand(rawArgs, deps = {}) {
|
|
15614
16341
|
const resolved = resolveDeps(deps);
|
|
@@ -15618,11 +16345,15 @@ async function runConnectorCommand(rawArgs, deps = {}) {
|
|
|
15618
16345
|
return 2;
|
|
15619
16346
|
}
|
|
15620
16347
|
const a = parsed.value;
|
|
16348
|
+
if (a.help) {
|
|
16349
|
+
printConnectorUsage(resolved.out);
|
|
16350
|
+
return 0;
|
|
16351
|
+
}
|
|
15621
16352
|
switch (a.subcommand) {
|
|
15622
16353
|
case "add":
|
|
15623
16354
|
return connectorAdd(a, resolved);
|
|
15624
16355
|
case "list":
|
|
15625
|
-
return connectorList(resolved, a.project);
|
|
16356
|
+
return connectorList(resolved, a.project, a.json);
|
|
15626
16357
|
case "remove":
|
|
15627
16358
|
return connectorRemove(resolved, a.positional[0]);
|
|
15628
16359
|
case "test":
|
|
@@ -15640,7 +16371,7 @@ async function runConnectorCommand(rawArgs, deps = {}) {
|
|
|
15640
16371
|
|
|
15641
16372
|
// src/hooks-cli.ts
|
|
15642
16373
|
init_cjs_shims();
|
|
15643
|
-
var
|
|
16374
|
+
var import_node_path60 = __toESM(require("path"), 1);
|
|
15644
16375
|
var import_node_os5 = __toESM(require("os"), 1);
|
|
15645
16376
|
var import_node_fs40 = require("fs");
|
|
15646
16377
|
var import_node_url5 = require("url");
|
|
@@ -15649,14 +16380,14 @@ var GUIDE_FILENAME = "GRAPH_FIRST.md";
|
|
|
15649
16380
|
var GUIDE_INSTALL_NAME = "neat-graph-first.md";
|
|
15650
16381
|
var HOOK_MATCHER = "Grep|Glob|Bash";
|
|
15651
16382
|
function moduleDir() {
|
|
15652
|
-
return typeof __dirname !== "undefined" ? __dirname :
|
|
16383
|
+
return typeof __dirname !== "undefined" ? __dirname : import_node_path60.default.dirname((0, import_node_url5.fileURLToPath)(importMetaUrl));
|
|
15653
16384
|
}
|
|
15654
16385
|
async function readSkillAsset(rel) {
|
|
15655
16386
|
const here = moduleDir();
|
|
15656
16387
|
const candidates = [
|
|
15657
|
-
|
|
15658
|
-
|
|
15659
|
-
|
|
16388
|
+
import_node_path60.default.resolve(here, "../../claude-skill", rel),
|
|
16389
|
+
import_node_path60.default.resolve(here, "../../../claude-skill", rel),
|
|
16390
|
+
import_node_path60.default.resolve(here, "../claude-skill", rel)
|
|
15660
16391
|
];
|
|
15661
16392
|
for (const candidate of candidates) {
|
|
15662
16393
|
try {
|
|
@@ -15670,17 +16401,17 @@ async function readSkillAsset(rel) {
|
|
|
15670
16401
|
}
|
|
15671
16402
|
function neatHome3() {
|
|
15672
16403
|
const override = process.env.NEAT_HOME;
|
|
15673
|
-
if (override && override.length > 0) return
|
|
15674
|
-
return
|
|
16404
|
+
if (override && override.length > 0) return import_node_path60.default.resolve(override);
|
|
16405
|
+
return import_node_path60.default.join(import_node_os5.default.homedir(), ".neat");
|
|
15675
16406
|
}
|
|
15676
16407
|
function claudeSettingsPath() {
|
|
15677
16408
|
const override = process.env.NEAT_CLAUDE_SETTINGS;
|
|
15678
|
-
if (override && override.length > 0) return
|
|
16409
|
+
if (override && override.length > 0) return import_node_path60.default.resolve(override);
|
|
15679
16410
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? import_node_os5.default.homedir();
|
|
15680
|
-
return
|
|
16411
|
+
return import_node_path60.default.join(home, ".claude", "settings.json");
|
|
15681
16412
|
}
|
|
15682
16413
|
function installedHookPath() {
|
|
15683
|
-
return
|
|
16414
|
+
return import_node_path60.default.join(neatHome3(), "hooks", HOOK_FILENAME);
|
|
15684
16415
|
}
|
|
15685
16416
|
function isNeatSearchEntry(entry2) {
|
|
15686
16417
|
return (entry2.hooks ?? []).some(
|
|
@@ -15713,9 +16444,9 @@ async function runHooks(opts) {
|
|
|
15713
16444
|
const hookScript = await readSkillAsset(`hooks/${HOOK_FILENAME}`);
|
|
15714
16445
|
const guide = await readSkillAsset(GUIDE_FILENAME);
|
|
15715
16446
|
const scriptPath = installedHookPath();
|
|
15716
|
-
await import_node_fs40.promises.mkdir(
|
|
16447
|
+
await import_node_fs40.promises.mkdir(import_node_path60.default.dirname(scriptPath), { recursive: true });
|
|
15717
16448
|
await import_node_fs40.promises.writeFile(scriptPath, hookScript, { mode: 493 });
|
|
15718
|
-
const guidePath =
|
|
16449
|
+
const guidePath = import_node_path60.default.join(neatHome3(), GUIDE_INSTALL_NAME);
|
|
15719
16450
|
await import_node_fs40.promises.writeFile(guidePath, guide, "utf8");
|
|
15720
16451
|
const settingsFile = claudeSettingsPath();
|
|
15721
16452
|
let settings = {};
|
|
@@ -15742,7 +16473,7 @@ async function runHooks(opts) {
|
|
|
15742
16473
|
...settings,
|
|
15743
16474
|
hooks: { ...hooks, PreToolUse: preToolUse }
|
|
15744
16475
|
};
|
|
15745
|
-
await import_node_fs40.promises.mkdir(
|
|
16476
|
+
await import_node_fs40.promises.mkdir(import_node_path60.default.dirname(settingsFile), { recursive: true });
|
|
15746
16477
|
await import_node_fs40.promises.writeFile(settingsFile, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
15747
16478
|
console.log(`neat hooks: installed the search-nudge hook`);
|
|
15748
16479
|
console.log(` script: ${scriptPath}`);
|
|
@@ -15814,11 +16545,11 @@ async function runHooksCommand(args) {
|
|
|
15814
16545
|
|
|
15815
16546
|
// src/cli-verbs.ts
|
|
15816
16547
|
init_cjs_shims();
|
|
15817
|
-
var
|
|
16548
|
+
var import_node_path61 = __toESM(require("path"), 1);
|
|
15818
16549
|
|
|
15819
16550
|
// src/cli-client.ts
|
|
15820
16551
|
init_cjs_shims();
|
|
15821
|
-
var
|
|
16552
|
+
var import_types49 = require("@neat.is/types");
|
|
15822
16553
|
var HttpError = class extends Error {
|
|
15823
16554
|
constructor(status2, message, responseBody = "") {
|
|
15824
16555
|
super(message);
|
|
@@ -15843,10 +16574,10 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
15843
16574
|
const root = baseUrl.replace(/\/$/, "");
|
|
15844
16575
|
const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
|
|
15845
16576
|
return {
|
|
15846
|
-
async get(
|
|
16577
|
+
async get(path63) {
|
|
15847
16578
|
let res;
|
|
15848
16579
|
try {
|
|
15849
|
-
res = await fetch(`${root}${
|
|
16580
|
+
res = await fetch(`${root}${path63}`, {
|
|
15850
16581
|
headers: { ...authHeader }
|
|
15851
16582
|
});
|
|
15852
16583
|
} catch (err) {
|
|
@@ -15858,16 +16589,16 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
15858
16589
|
const body = await res.text().catch(() => "");
|
|
15859
16590
|
throw new HttpError(
|
|
15860
16591
|
res.status,
|
|
15861
|
-
`${res.status} ${res.statusText} on GET ${
|
|
16592
|
+
`${res.status} ${res.statusText} on GET ${path63}: ${body}`,
|
|
15862
16593
|
body
|
|
15863
16594
|
);
|
|
15864
16595
|
}
|
|
15865
16596
|
return await res.json();
|
|
15866
16597
|
},
|
|
15867
|
-
async post(
|
|
16598
|
+
async post(path63, body) {
|
|
15868
16599
|
let res;
|
|
15869
16600
|
try {
|
|
15870
|
-
res = await fetch(`${root}${
|
|
16601
|
+
res = await fetch(`${root}${path63}`, {
|
|
15871
16602
|
method: "POST",
|
|
15872
16603
|
headers: { "content-type": "application/json", ...authHeader },
|
|
15873
16604
|
body: JSON.stringify(body)
|
|
@@ -15881,7 +16612,7 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
15881
16612
|
const text = await res.text().catch(() => "");
|
|
15882
16613
|
throw new HttpError(
|
|
15883
16614
|
res.status,
|
|
15884
|
-
`${res.status} ${res.statusText} on POST ${
|
|
16615
|
+
`${res.status} ${res.statusText} on POST ${path63}: ${text}`,
|
|
15885
16616
|
text
|
|
15886
16617
|
);
|
|
15887
16618
|
}
|
|
@@ -15895,12 +16626,12 @@ function projectPath(project, suffix) {
|
|
|
15895
16626
|
}
|
|
15896
16627
|
async function runRootCause(client, input) {
|
|
15897
16628
|
const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
|
|
15898
|
-
const
|
|
16629
|
+
const path63 = projectPath(
|
|
15899
16630
|
input.project,
|
|
15900
16631
|
`/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
|
|
15901
16632
|
);
|
|
15902
16633
|
try {
|
|
15903
|
-
const result = await client.get(
|
|
16634
|
+
const result = await client.get(path63);
|
|
15904
16635
|
const arrowPath = result.traversalPath.join(" \u2190 ");
|
|
15905
16636
|
const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
|
|
15906
16637
|
const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
|
|
@@ -15926,12 +16657,12 @@ async function runRootCause(client, input) {
|
|
|
15926
16657
|
}
|
|
15927
16658
|
async function runBlastRadius(client, input) {
|
|
15928
16659
|
const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
|
|
15929
|
-
const
|
|
16660
|
+
const path63 = projectPath(
|
|
15930
16661
|
input.project,
|
|
15931
16662
|
`/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
|
|
15932
16663
|
);
|
|
15933
16664
|
try {
|
|
15934
|
-
const result = await client.get(
|
|
16665
|
+
const result = await client.get(path63);
|
|
15935
16666
|
if (result.totalAffected === 0) {
|
|
15936
16667
|
return {
|
|
15937
16668
|
summary: `${result.origin} has no dependents. Nothing else would break if it failed.`
|
|
@@ -15960,17 +16691,17 @@ async function runBlastRadius(client, input) {
|
|
|
15960
16691
|
}
|
|
15961
16692
|
}
|
|
15962
16693
|
function formatBlastEntry(n) {
|
|
15963
|
-
const tag = n.edgeProvenance ===
|
|
16694
|
+
const tag = n.edgeProvenance === import_types49.Provenance.STALE ? " [STALE \u2014 last seen too long ago]" : "";
|
|
15964
16695
|
return ` \u2022 ${n.nodeId} (distance ${n.distance}, ${n.edgeProvenance})${tag}`;
|
|
15965
16696
|
}
|
|
15966
16697
|
async function runDependencies(client, input) {
|
|
15967
16698
|
const depth = input.depth ?? 3;
|
|
15968
|
-
const
|
|
16699
|
+
const path63 = projectPath(
|
|
15969
16700
|
input.project,
|
|
15970
16701
|
`/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
|
|
15971
16702
|
);
|
|
15972
16703
|
try {
|
|
15973
|
-
const result = await client.get(
|
|
16704
|
+
const result = await client.get(path63);
|
|
15974
16705
|
if (result.total === 0) {
|
|
15975
16706
|
return {
|
|
15976
16707
|
summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
|
|
@@ -16017,7 +16748,7 @@ async function runObservedDependencies(client, input) {
|
|
|
16017
16748
|
if (result.observed) {
|
|
16018
16749
|
return {
|
|
16019
16750
|
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:
|
|
16751
|
+
provenance: import_types49.Provenance.OBSERVED
|
|
16021
16752
|
};
|
|
16022
16753
|
}
|
|
16023
16754
|
const note = result.hasExtractedOutbound ? " Static (EXTRACTED) dependencies exist but no runtime traffic has been seen \u2014 is OTel running?" : "";
|
|
@@ -16027,7 +16758,7 @@ async function runObservedDependencies(client, input) {
|
|
|
16027
16758
|
return {
|
|
16028
16759
|
summary: `${input.nodeId} has ${result.dependencies.length} runtime dependenc${result.dependencies.length === 1 ? "y" : "ies"} confirmed by OTel.`,
|
|
16029
16760
|
block: blockLines.join("\n"),
|
|
16030
|
-
provenance:
|
|
16761
|
+
provenance: import_types49.Provenance.OBSERVED
|
|
16031
16762
|
};
|
|
16032
16763
|
} catch (err) {
|
|
16033
16764
|
if (err instanceof HttpError && err.status === 404) {
|
|
@@ -16062,9 +16793,9 @@ function formatDuration(ms) {
|
|
|
16062
16793
|
return `${Math.round(h / 24)}d`;
|
|
16063
16794
|
}
|
|
16064
16795
|
async function runIncidents(client, input) {
|
|
16065
|
-
const
|
|
16796
|
+
const path63 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
|
|
16066
16797
|
try {
|
|
16067
|
-
const body = await client.get(
|
|
16798
|
+
const body = await client.get(path63);
|
|
16068
16799
|
const events = body.events;
|
|
16069
16800
|
if (events.length === 0) {
|
|
16070
16801
|
return {
|
|
@@ -16081,7 +16812,7 @@ async function runIncidents(client, input) {
|
|
|
16081
16812
|
return {
|
|
16082
16813
|
summary: `${target} has ${body.total} recorded incident${body.total === 1 ? "" : "s"}; showing the ${ordered.length} most recent.`,
|
|
16083
16814
|
block: blockLines.join("\n"),
|
|
16084
|
-
provenance:
|
|
16815
|
+
provenance: import_types49.Provenance.OBSERVED
|
|
16085
16816
|
};
|
|
16086
16817
|
} catch (err) {
|
|
16087
16818
|
if (err instanceof HttpError && err.status === 404) {
|
|
@@ -16190,7 +16921,7 @@ async function runStaleEdges(client, input) {
|
|
|
16190
16921
|
return {
|
|
16191
16922
|
summary: `${events.length} stale-edge transition${events.length === 1 ? "" : "s"} recorded${input.edgeType ? ` for ${input.edgeType}` : ""}.`,
|
|
16192
16923
|
block: blockLines.join("\n"),
|
|
16193
|
-
provenance:
|
|
16924
|
+
provenance: import_types49.Provenance.STALE
|
|
16194
16925
|
};
|
|
16195
16926
|
}
|
|
16196
16927
|
async function runPolicies(client, input) {
|
|
@@ -16354,7 +17085,7 @@ async function resolveProjectEntry(opts) {
|
|
|
16354
17085
|
const cwd = opts.cwd ?? process.cwd();
|
|
16355
17086
|
const resolvedCwd = await normalizeProjectPath(cwd);
|
|
16356
17087
|
for (const entry2 of entries) {
|
|
16357
|
-
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${
|
|
17088
|
+
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path61.default.sep}`)) {
|
|
16358
17089
|
return entry2;
|
|
16359
17090
|
}
|
|
16360
17091
|
}
|
|
@@ -16507,7 +17238,7 @@ async function runSync(opts) {
|
|
|
16507
17238
|
}
|
|
16508
17239
|
|
|
16509
17240
|
// src/cli.ts
|
|
16510
|
-
var
|
|
17241
|
+
var import_types50 = require("@neat.is/types");
|
|
16511
17242
|
function isNpxInvocation() {
|
|
16512
17243
|
if (process.env.npm_command === "exec") return true;
|
|
16513
17244
|
const execpath = process.env.npm_execpath ?? "";
|
|
@@ -16576,8 +17307,9 @@ function usage2() {
|
|
|
16576
17307
|
console.log(" --dry-run run extraction in-memory; do not write");
|
|
16577
17308
|
console.log(" --no-instrument skip the SDK install apply step");
|
|
16578
17309
|
console.log(" --json emit the delta summary as JSON");
|
|
16579
|
-
console.log(" connector Configure
|
|
16580
|
-
console.log(" firebase, cloudflare).
|
|
17310
|
+
console.log(" connector Configure OBSERVED connectors \u2014 pull (supabase, railway,");
|
|
17311
|
+
console.log(" firebase, cloudflare) and push (vercel, via a trace Drain).");
|
|
17312
|
+
console.log(" Subcommands:");
|
|
16581
17313
|
console.log(" add <provider> add a connector; validates the credential");
|
|
16582
17314
|
console.log(" against the provider first (--skip-validate to skip)");
|
|
16583
17315
|
console.log(" flags: --project <name>, --credential/--token <$VAR|value>,");
|
|
@@ -16817,12 +17549,12 @@ async function runInit(opts) {
|
|
|
16817
17549
|
printDiscoveryReport(opts, services);
|
|
16818
17550
|
const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
|
|
16819
17551
|
const patch = renderPatch(sections);
|
|
16820
|
-
const patchPath =
|
|
17552
|
+
const patchPath = import_node_path62.default.join(opts.scanPath, "neat.patch");
|
|
16821
17553
|
if (opts.dryRun) {
|
|
16822
17554
|
await import_node_fs41.promises.writeFile(patchPath, patch, "utf8");
|
|
16823
17555
|
written.push(patchPath);
|
|
16824
17556
|
console.log(`dry-run: patch written to ${patchPath}`);
|
|
16825
|
-
const gitignorePath =
|
|
17557
|
+
const gitignorePath = import_node_path62.default.join(opts.scanPath, ".gitignore");
|
|
16826
17558
|
const gitignoreExists = await import_node_fs41.promises.stat(gitignorePath).then(() => true).catch(() => false);
|
|
16827
17559
|
const verb = gitignoreExists ? "append" : "create";
|
|
16828
17560
|
console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
|
|
@@ -16834,9 +17566,9 @@ async function runInit(opts) {
|
|
|
16834
17566
|
const graph = getGraph(graphKey);
|
|
16835
17567
|
const projectPaths = pathsForProject(
|
|
16836
17568
|
graphKey,
|
|
16837
|
-
|
|
17569
|
+
import_node_path62.default.join(opts.scanPath, "neat-out")
|
|
16838
17570
|
);
|
|
16839
|
-
const errorsPath =
|
|
17571
|
+
const errorsPath = import_node_path62.default.join(import_node_path62.default.dirname(opts.outPath), import_node_path62.default.basename(projectPaths.errorsPath));
|
|
16840
17572
|
const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
|
|
16841
17573
|
await saveGraphToDisk(graph, opts.outPath);
|
|
16842
17574
|
written.push(opts.outPath);
|
|
@@ -16955,9 +17687,9 @@ var CLAUDE_SKILL_CONFIG = {
|
|
|
16955
17687
|
};
|
|
16956
17688
|
function claudeConfigPath() {
|
|
16957
17689
|
const override = process.env.NEAT_CLAUDE_CONFIG;
|
|
16958
|
-
if (override && override.length > 0) return
|
|
17690
|
+
if (override && override.length > 0) return import_node_path62.default.resolve(override);
|
|
16959
17691
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
16960
|
-
return
|
|
17692
|
+
return import_node_path62.default.join(home, ".claude.json");
|
|
16961
17693
|
}
|
|
16962
17694
|
async function runSkill(opts) {
|
|
16963
17695
|
const snippet2 = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
|
|
@@ -16981,7 +17713,7 @@ async function runSkill(opts) {
|
|
|
16981
17713
|
...existing,
|
|
16982
17714
|
mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
|
|
16983
17715
|
};
|
|
16984
|
-
await import_node_fs41.promises.mkdir(
|
|
17716
|
+
await import_node_fs41.promises.mkdir(import_node_path62.default.dirname(target), { recursive: true });
|
|
16985
17717
|
await import_node_fs41.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
16986
17718
|
console.log(`neat skill: wrote mcpServers.neat to ${target}`);
|
|
16987
17719
|
console.log("restart Claude Code to pick up the new MCP server.");
|
|
@@ -17047,12 +17779,12 @@ async function main() {
|
|
|
17047
17779
|
console.error("neat init: --apply and --dry-run are mutually exclusive");
|
|
17048
17780
|
process.exit(2);
|
|
17049
17781
|
}
|
|
17050
|
-
const scanPath =
|
|
17782
|
+
const scanPath = import_node_path62.default.resolve(target);
|
|
17051
17783
|
const projectExplicit = parsed.project !== null;
|
|
17052
|
-
const projectName = projectExplicit ? project :
|
|
17784
|
+
const projectName = projectExplicit ? project : import_node_path62.default.basename(scanPath);
|
|
17053
17785
|
const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
|
|
17054
|
-
const fallback = pathsForProject(projectKey,
|
|
17055
|
-
const outPath =
|
|
17786
|
+
const fallback = pathsForProject(projectKey, import_node_path62.default.join(scanPath, "neat-out")).snapshotPath;
|
|
17787
|
+
const outPath = import_node_path62.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
|
|
17056
17788
|
const result = await runInit({
|
|
17057
17789
|
scanPath,
|
|
17058
17790
|
outPath,
|
|
@@ -17073,21 +17805,21 @@ async function main() {
|
|
|
17073
17805
|
usage2();
|
|
17074
17806
|
process.exit(2);
|
|
17075
17807
|
}
|
|
17076
|
-
const scanPath =
|
|
17808
|
+
const scanPath = import_node_path62.default.resolve(target);
|
|
17077
17809
|
const stat = await import_node_fs41.promises.stat(scanPath).catch(() => null);
|
|
17078
17810
|
if (!stat || !stat.isDirectory()) {
|
|
17079
17811
|
console.error(`neat watch: ${scanPath} is not a directory`);
|
|
17080
17812
|
process.exit(2);
|
|
17081
17813
|
}
|
|
17082
|
-
const projectPaths = pathsForProject(project,
|
|
17083
|
-
const outPath =
|
|
17084
|
-
const errorsPath =
|
|
17085
|
-
process.env.NEAT_ERRORS_PATH ??
|
|
17814
|
+
const projectPaths = pathsForProject(project, import_node_path62.default.join(scanPath, "neat-out"));
|
|
17815
|
+
const outPath = import_node_path62.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
|
|
17816
|
+
const errorsPath = import_node_path62.default.resolve(
|
|
17817
|
+
process.env.NEAT_ERRORS_PATH ?? import_node_path62.default.join(import_node_path62.default.dirname(outPath), import_node_path62.default.basename(projectPaths.errorsPath))
|
|
17086
17818
|
);
|
|
17087
|
-
const staleEventsPath =
|
|
17088
|
-
process.env.NEAT_STALE_EVENTS_PATH ??
|
|
17819
|
+
const staleEventsPath = import_node_path62.default.resolve(
|
|
17820
|
+
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
17821
|
);
|
|
17090
|
-
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ?
|
|
17822
|
+
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path62.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
|
|
17091
17823
|
const handle = await startWatch(getGraph(project), {
|
|
17092
17824
|
scanPath,
|
|
17093
17825
|
outPath,
|
|
@@ -17270,11 +18002,11 @@ async function main() {
|
|
|
17270
18002
|
process.exit(1);
|
|
17271
18003
|
}
|
|
17272
18004
|
async function tryOrchestrator(cmd, parsed) {
|
|
17273
|
-
const scanPath =
|
|
18005
|
+
const scanPath = import_node_path62.default.resolve(cmd);
|
|
17274
18006
|
const stat = await import_node_fs41.promises.stat(scanPath).catch(() => null);
|
|
17275
18007
|
if (!stat || !stat.isDirectory()) return null;
|
|
17276
18008
|
const projectExplicit = parsed.project !== null;
|
|
17277
|
-
const projectName = projectExplicit ? parsed.project :
|
|
18009
|
+
const projectName = projectExplicit ? parsed.project : import_node_path62.default.basename(scanPath);
|
|
17278
18010
|
const result = await runOrchestrator({
|
|
17279
18011
|
scanPath,
|
|
17280
18012
|
project: projectName,
|
|
@@ -17463,10 +18195,10 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
17463
18195
|
const parts = parsed.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
17464
18196
|
const out = [];
|
|
17465
18197
|
for (const p of parts) {
|
|
17466
|
-
const r =
|
|
18198
|
+
const r = import_types50.DivergenceTypeSchema.safeParse(p);
|
|
17467
18199
|
if (!r.success) {
|
|
17468
18200
|
console.error(
|
|
17469
|
-
`neat divergences: unknown --type "${p}". allowed: ${
|
|
18201
|
+
`neat divergences: unknown --type "${p}". allowed: ${import_types50.DivergenceTypeSchema.options.join(", ")}`
|
|
17470
18202
|
);
|
|
17471
18203
|
return 2;
|
|
17472
18204
|
}
|