@neat.is/core 0.7.8 → 0.7.10
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-LN75Z624.js → chunk-6T7ZHODF.js} +554 -65
- package/dist/chunk-6T7ZHODF.js.map +1 -0
- package/dist/{chunk-BIY46Q6U.js → chunk-BC53SCT7.js} +36 -2
- package/dist/chunk-BC53SCT7.js.map +1 -0
- package/dist/{chunk-OVRVDSUO.js → chunk-D4OX6MUX.js} +58 -4
- package/dist/chunk-D4OX6MUX.js.map +1 -0
- package/dist/{chunk-4GCV46AP.js → chunk-FCO5Z3RW.js} +2 -2
- package/dist/{chunk-Y43UCVZS.js → chunk-UUYCTH2E.js} +24 -8
- package/dist/chunk-UUYCTH2E.js.map +1 -0
- package/dist/cli.cjs +1392 -314
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +704 -176
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +645 -77
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -1
- package/dist/index.d.ts +28 -1
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +645 -77
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-DPSAGRUU.js → otel-grpc-CO47JRIN.js} +3 -3
- package/dist/server.cjs +618 -74
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +4 -4
- package/package.json +2 -2
- package/dist/chunk-BIY46Q6U.js.map +0 -1
- package/dist/chunk-LN75Z624.js.map +0 -1
- package/dist/chunk-OVRVDSUO.js.map +0 -1
- package/dist/chunk-Y43UCVZS.js.map +0 -1
- /package/dist/{chunk-4GCV46AP.js.map → chunk-FCO5Z3RW.js.map} +0 -0
- /package/dist/{otel-grpc-DPSAGRUU.js.map → otel-grpc-CO47JRIN.js.map} +0 -0
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
mountBearerAuth,
|
|
4
4
|
readAuthEnv,
|
|
5
5
|
tableFromSqlStatement
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-UUYCTH2E.js";
|
|
7
7
|
|
|
8
8
|
// src/graph.ts
|
|
9
9
|
import GraphDefault from "graphology";
|
|
@@ -963,6 +963,7 @@ import { parse as parseYaml } from "yaml";
|
|
|
963
963
|
import { extractedEdgeId } from "@neat.is/types";
|
|
964
964
|
var SERVICE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs", ".cjs", ".ts", ".tsx", ".py", ".go", ".rb", ".php"]);
|
|
965
965
|
var CONFIG_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".yaml", ".yml"]);
|
|
966
|
+
var JSON_CONFIG_FILENAMES = /* @__PURE__ */ new Set(["app.json", "app.config.json", "eas.json"]);
|
|
966
967
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
967
968
|
"node_modules",
|
|
968
969
|
".git",
|
|
@@ -1002,6 +1003,7 @@ async function isPythonVenvDir(dir) {
|
|
|
1002
1003
|
function isConfigFile(name) {
|
|
1003
1004
|
const ext = path3.extname(name);
|
|
1004
1005
|
if (CONFIG_FILE_EXTENSIONS.has(ext)) return { match: true, fileType: ext.slice(1) };
|
|
1006
|
+
if (JSON_CONFIG_FILENAMES.has(name)) return { match: true, fileType: "json" };
|
|
1005
1007
|
if (name === ".env" || name.startsWith(".env.")) {
|
|
1006
1008
|
if (isEnvTemplateFile(name)) return { match: false, fileType: "" };
|
|
1007
1009
|
if (isNeatAuthoredEnvFile(name)) return { match: false, fileType: "" };
|
|
@@ -3599,10 +3601,15 @@ function resolveDistToSrc(absFilepath, line) {
|
|
|
3599
3601
|
}
|
|
3600
3602
|
if (!entry) return null;
|
|
3601
3603
|
try {
|
|
3602
|
-
const
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3604
|
+
const queryLine = line !== void 0 && Number.isFinite(line) ? line : 1;
|
|
3605
|
+
let pos = entry.consumer.originalPositionFor({ line: queryLine, column: 0 });
|
|
3606
|
+
if (!pos || !pos.source) {
|
|
3607
|
+
pos = entry.consumer.originalPositionFor({
|
|
3608
|
+
line: queryLine,
|
|
3609
|
+
column: 0,
|
|
3610
|
+
bias: sourceMapJs.SourceMapConsumer.LEAST_UPPER_BOUND
|
|
3611
|
+
});
|
|
3612
|
+
}
|
|
3606
3613
|
if (!pos || !pos.source) return null;
|
|
3607
3614
|
const root = entry.consumer.sourceRoot ?? "";
|
|
3608
3615
|
const resolved = path8.resolve(entry.dir, root, pos.source);
|
|
@@ -3611,6 +3618,9 @@ function resolveDistToSrc(absFilepath, line) {
|
|
|
3611
3618
|
return null;
|
|
3612
3619
|
}
|
|
3613
3620
|
}
|
|
3621
|
+
function hasAdjacentSourceMap(absFilepath) {
|
|
3622
|
+
return sourceMapCache.get(absFilepath) != null;
|
|
3623
|
+
}
|
|
3614
3624
|
function callSiteFromSpan(span, serviceNode, scanPath) {
|
|
3615
3625
|
const filepath = codeFilepathOf(span.attributes);
|
|
3616
3626
|
if (filepath === void 0) return null;
|
|
@@ -3626,7 +3636,7 @@ function callSiteFromSpan(span, serviceNode, scanPath) {
|
|
|
3626
3636
|
}
|
|
3627
3637
|
const relPath = relPathForRuntimeFile(effectivePath, serviceNode, scanPath);
|
|
3628
3638
|
if (!relPath) return null;
|
|
3629
|
-
if (!resolved && abs.endsWith(".js") && relPath.startsWith("dist/") && serviceNode?.name) {
|
|
3639
|
+
if (!resolved && abs.endsWith(".js") && relPath.startsWith("dist/") && !hasAdjacentSourceMap(abs) && serviceNode?.name) {
|
|
3630
3640
|
warnNoSourceMaps(serviceNode.name);
|
|
3631
3641
|
}
|
|
3632
3642
|
const fn = codeFunctionOf(span.attributes);
|
|
@@ -3902,7 +3912,7 @@ function resolveServiceId(graph, host, env) {
|
|
|
3902
3912
|
function frontierIdFor(host) {
|
|
3903
3913
|
return frontierId(host);
|
|
3904
3914
|
}
|
|
3905
|
-
function
|
|
3915
|
+
function resolveFusedServiceId(graph, serviceName, env) {
|
|
3906
3916
|
const id = serviceId(serviceName, env);
|
|
3907
3917
|
if (graph.hasNode(id)) return id;
|
|
3908
3918
|
const wanted = serviceName.toLowerCase();
|
|
@@ -3912,17 +3922,21 @@ function ensureServiceNode(graph, serviceName, env) {
|
|
|
3912
3922
|
if (svc.discoveredVia === "otel") return false;
|
|
3913
3923
|
return typeof svc.name === "string" && svc.name.toLowerCase() === wanted;
|
|
3914
3924
|
});
|
|
3915
|
-
|
|
3925
|
+
return extractedId ?? id;
|
|
3926
|
+
}
|
|
3927
|
+
function ensureServiceNode(graph, serviceName, env) {
|
|
3928
|
+
const resolved = resolveFusedServiceId(graph, serviceName, env);
|
|
3929
|
+
if (graph.hasNode(resolved)) return resolved;
|
|
3916
3930
|
const node = {
|
|
3917
|
-
id,
|
|
3931
|
+
id: resolved,
|
|
3918
3932
|
type: NodeType4.ServiceNode,
|
|
3919
3933
|
name: serviceName,
|
|
3920
3934
|
language: "unknown",
|
|
3921
3935
|
discoveredVia: "otel",
|
|
3922
3936
|
...env !== "unknown" ? { env } : {}
|
|
3923
3937
|
};
|
|
3924
|
-
graph.addNode(
|
|
3925
|
-
return
|
|
3938
|
+
graph.addNode(resolved, node);
|
|
3939
|
+
return resolved;
|
|
3926
3940
|
}
|
|
3927
3941
|
function ensureInfraNode(graph, kind, name, provider) {
|
|
3928
3942
|
const id = infraId(kind, name);
|
|
@@ -4115,8 +4129,23 @@ async function appendErrorEvent(ctx, ev) {
|
|
|
4115
4129
|
await fs7.mkdir(path8.dirname(ctx.errorsPath), { recursive: true });
|
|
4116
4130
|
await fs7.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
4117
4131
|
}
|
|
4132
|
+
async function appendConnectorIncident(errorsPath, input) {
|
|
4133
|
+
const ev = {
|
|
4134
|
+
id: input.id,
|
|
4135
|
+
timestamp: input.timestamp,
|
|
4136
|
+
service: input.service,
|
|
4137
|
+
traceId: input.id,
|
|
4138
|
+
spanId: input.id,
|
|
4139
|
+
errorType: input.errorType,
|
|
4140
|
+
errorMessage: input.errorMessage,
|
|
4141
|
+
...input.attributes && Object.keys(input.attributes).length > 0 ? { attributes: input.attributes } : {},
|
|
4142
|
+
affectedNode: input.affectedNode
|
|
4143
|
+
};
|
|
4144
|
+
await fs7.mkdir(path8.dirname(errorsPath), { recursive: true });
|
|
4145
|
+
await fs7.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
4146
|
+
}
|
|
4118
4147
|
function incidentAffectedNode(span, graph, scanPath) {
|
|
4119
|
-
const sid = serviceId(span.service, span.env);
|
|
4148
|
+
const sid = graph ? resolveFusedServiceId(graph, span.service, span.env) : serviceId(span.service, span.env);
|
|
4120
4149
|
const serviceNode = graph && graph.hasNode(sid) ? graph.getNodeAttributes(sid) : void 0;
|
|
4121
4150
|
const callSite = callSiteFromSpan(span, serviceNode, scanPath);
|
|
4122
4151
|
if (callSite) {
|
|
@@ -6047,7 +6076,7 @@ function disambiguate(defs) {
|
|
|
6047
6076
|
}
|
|
6048
6077
|
async function addSymbols(graph, services) {
|
|
6049
6078
|
const parsers = /* @__PURE__ */ new Map();
|
|
6050
|
-
const
|
|
6079
|
+
const parserForExt5 = (ext) => {
|
|
6051
6080
|
const grammar = GRAMMAR_BY_EXT[ext];
|
|
6052
6081
|
if (!grammar) return null;
|
|
6053
6082
|
let parser = parsers.get(ext);
|
|
@@ -6063,7 +6092,7 @@ async function addSymbols(graph, services) {
|
|
|
6063
6092
|
for (const service of services) {
|
|
6064
6093
|
const files = await loadSourceFiles(service.dir);
|
|
6065
6094
|
for (const file of files) {
|
|
6066
|
-
const parser =
|
|
6095
|
+
const parser = parserForExt5(path17.extname(file.path));
|
|
6067
6096
|
if (!parser) continue;
|
|
6068
6097
|
const relPath = toPosix(path17.relative(service.dir, file.path));
|
|
6069
6098
|
let defs;
|
|
@@ -6221,7 +6250,7 @@ function stringInner(node) {
|
|
|
6221
6250
|
}
|
|
6222
6251
|
async function addSymbolEdges(graph, services) {
|
|
6223
6252
|
const parsers = /* @__PURE__ */ new Map();
|
|
6224
|
-
const
|
|
6253
|
+
const parserForExt5 = (ext) => {
|
|
6225
6254
|
const grammar = GRAMMAR_BY_EXT[ext];
|
|
6226
6255
|
if (!grammar) return null;
|
|
6227
6256
|
let parser = parsers.get(ext);
|
|
@@ -6238,7 +6267,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
6238
6267
|
const tsPaths = await loadTsPathConfig(service.dir);
|
|
6239
6268
|
const files = await loadSourceFiles(service.dir);
|
|
6240
6269
|
for (const file of files) {
|
|
6241
|
-
const parser =
|
|
6270
|
+
const parser = parserForExt5(path18.extname(file.path));
|
|
6242
6271
|
if (!parser) continue;
|
|
6243
6272
|
const relPath = toPosix(path18.relative(service.dir, file.path));
|
|
6244
6273
|
const fileDir = path18.dirname(file.path);
|
|
@@ -6506,7 +6535,7 @@ function firstReferenceLines(root, wanted) {
|
|
|
6506
6535
|
}
|
|
6507
6536
|
async function addServerActions(graph, services) {
|
|
6508
6537
|
const parsers = /* @__PURE__ */ new Map();
|
|
6509
|
-
const
|
|
6538
|
+
const parserForExt5 = (ext) => {
|
|
6510
6539
|
const grammar = GRAMMAR_BY_EXT[ext];
|
|
6511
6540
|
if (!grammar) return null;
|
|
6512
6541
|
let parser = parsers.get(ext);
|
|
@@ -6529,7 +6558,7 @@ async function addServerActions(graph, services) {
|
|
|
6529
6558
|
const files = await loadSourceFiles(service.dir);
|
|
6530
6559
|
for (const file of files) {
|
|
6531
6560
|
if (isTestPath(file.path)) continue;
|
|
6532
|
-
const parser =
|
|
6561
|
+
const parser = parserForExt5(path19.extname(file.path));
|
|
6533
6562
|
if (!parser) continue;
|
|
6534
6563
|
const relPath = toPosix(path19.relative(service.dir, file.path));
|
|
6535
6564
|
let root;
|
|
@@ -6592,7 +6621,7 @@ async function addServerActions(graph, services) {
|
|
|
6592
6621
|
}
|
|
6593
6622
|
for (const file of files) {
|
|
6594
6623
|
if (isTestPath(file.path)) continue;
|
|
6595
|
-
const parser =
|
|
6624
|
+
const parser = parserForExt5(path19.extname(file.path));
|
|
6596
6625
|
if (!parser) continue;
|
|
6597
6626
|
const relPath = toPosix(path19.relative(service.dir, file.path));
|
|
6598
6627
|
const fileDir = path19.dirname(file.path);
|
|
@@ -7567,6 +7596,7 @@ import {
|
|
|
7567
7596
|
import path30 from "path";
|
|
7568
7597
|
import Parser6 from "tree-sitter";
|
|
7569
7598
|
import JavaScript4 from "tree-sitter-javascript";
|
|
7599
|
+
import TypeScript2 from "tree-sitter-typescript";
|
|
7570
7600
|
import Python3 from "tree-sitter-python";
|
|
7571
7601
|
import {
|
|
7572
7602
|
EdgeType as EdgeType13,
|
|
@@ -7622,19 +7652,27 @@ function callsFromSource(source, parser, knownHosts) {
|
|
|
7622
7652
|
}
|
|
7623
7653
|
return out;
|
|
7624
7654
|
}
|
|
7625
|
-
|
|
7626
|
-
|
|
7627
|
-
|
|
7628
|
-
|
|
7629
|
-
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7633
|
-
|
|
7655
|
+
var GRAMMAR_BY_EXT2 = {
|
|
7656
|
+
".ts": TypeScript2.typescript,
|
|
7657
|
+
".tsx": TypeScript2.tsx,
|
|
7658
|
+
".js": JavaScript4,
|
|
7659
|
+
".jsx": JavaScript4,
|
|
7660
|
+
".mjs": JavaScript4,
|
|
7661
|
+
".cjs": JavaScript4,
|
|
7662
|
+
".py": Python3
|
|
7663
|
+
};
|
|
7664
|
+
function parserForExt(ext, cache) {
|
|
7665
|
+
const grammar = GRAMMAR_BY_EXT2[ext] ?? JavaScript4;
|
|
7666
|
+
let parser = cache.get(grammar);
|
|
7667
|
+
if (!parser) {
|
|
7668
|
+
parser = new Parser6();
|
|
7669
|
+
parser.setLanguage(grammar);
|
|
7670
|
+
cache.set(grammar, parser);
|
|
7671
|
+
}
|
|
7672
|
+
return parser;
|
|
7634
7673
|
}
|
|
7635
7674
|
async function addHttpCallEdges(graph, services) {
|
|
7636
|
-
const
|
|
7637
|
-
const pyParser = makePyParser3();
|
|
7675
|
+
const parserCache = /* @__PURE__ */ new Map();
|
|
7638
7676
|
const { knownHosts, hostToNodeId } = buildServiceHostIndex(services);
|
|
7639
7677
|
let nodesAdded = 0;
|
|
7640
7678
|
let edgesAdded = 0;
|
|
@@ -7643,7 +7681,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
7643
7681
|
const seen = /* @__PURE__ */ new Set();
|
|
7644
7682
|
for (const file of files) {
|
|
7645
7683
|
if (isTestPath(file.path)) continue;
|
|
7646
|
-
const parser = path30.extname(file.path)
|
|
7684
|
+
const parser = parserForExt(path30.extname(file.path), parserCache);
|
|
7647
7685
|
let sites;
|
|
7648
7686
|
try {
|
|
7649
7687
|
sites = callsFromSource(file.content, parser, knownHosts);
|
|
@@ -7722,7 +7760,7 @@ function parseSource5(parser, source) {
|
|
|
7722
7760
|
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK5)
|
|
7723
7761
|
);
|
|
7724
7762
|
}
|
|
7725
|
-
function
|
|
7763
|
+
function makeJsParser3() {
|
|
7726
7764
|
const p = new Parser7();
|
|
7727
7765
|
p.setLanguage(JavaScript5);
|
|
7728
7766
|
return p;
|
|
@@ -7900,7 +7938,7 @@ function findRoute(entries, method, normalizedPath) {
|
|
|
7900
7938
|
);
|
|
7901
7939
|
}
|
|
7902
7940
|
async function addRouteCallEdges(graph, services) {
|
|
7903
|
-
const jsParser =
|
|
7941
|
+
const jsParser = makeJsParser3();
|
|
7904
7942
|
const { knownHosts, hostToNodeId } = buildServiceHostIndex(services);
|
|
7905
7943
|
const routeIndex = buildRouteIndex(graph);
|
|
7906
7944
|
if (routeIndex.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
|
|
@@ -8286,7 +8324,7 @@ import JavaScript6 from "tree-sitter-javascript";
|
|
|
8286
8324
|
import { infraId as infraId7 } from "@neat.is/types";
|
|
8287
8325
|
var FIRESTORE_CLIENT_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])firebase\/firestore['"`]/;
|
|
8288
8326
|
var FIRESTORE_ADMIN_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])firebase-admin(?:\/firestore)?['"`]/;
|
|
8289
|
-
function
|
|
8327
|
+
function parserForExt2(ext) {
|
|
8290
8328
|
const p = new Parser8();
|
|
8291
8329
|
p.setLanguage(GRAMMAR_BY_EXT[ext] ?? JavaScript6);
|
|
8292
8330
|
return p;
|
|
@@ -8451,7 +8489,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
8451
8489
|
const hasAdmin = FIRESTORE_ADMIN_IMPORT_RE.test(file.content);
|
|
8452
8490
|
if (!hasClient && !hasAdmin) return [];
|
|
8453
8491
|
const fileSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
|
|
8454
|
-
const tree = parseSource3(
|
|
8492
|
+
const tree = parseSource3(parserForExt2(path37.extname(file.path)), file.content);
|
|
8455
8493
|
const clientVars = firestoreClientVars(tree.rootNode);
|
|
8456
8494
|
const collLine = /* @__PURE__ */ new Map();
|
|
8457
8495
|
const writes = /* @__PURE__ */ new Map();
|
|
@@ -8866,7 +8904,7 @@ import Python4 from "tree-sitter-python";
|
|
|
8866
8904
|
import { infraId as infraId9 } from "@neat.is/types";
|
|
8867
8905
|
var SQLALCHEMY_IMPORT_RE = /(?:from|import)\s+(?:flask_sqlalchemy|sqlalchemy)\b/;
|
|
8868
8906
|
var PARSE_CHUNK6 = 16384;
|
|
8869
|
-
function
|
|
8907
|
+
function makePyParser3() {
|
|
8870
8908
|
const p = new Parser9();
|
|
8871
8909
|
p.setLanguage(Python4);
|
|
8872
8910
|
return p;
|
|
@@ -8973,7 +9011,7 @@ function foreignKeyParentTable(call) {
|
|
|
8973
9011
|
}
|
|
8974
9012
|
function sqlalchemyForeignKeys(file, serviceDir) {
|
|
8975
9013
|
if (!SQLALCHEMY_IMPORT_RE.test(file.content)) return [];
|
|
8976
|
-
const tree = parseSource6(
|
|
9014
|
+
const tree = parseSource6(makePyParser3(), file.content);
|
|
8977
9015
|
const out = [];
|
|
8978
9016
|
const seen = /* @__PURE__ */ new Set();
|
|
8979
9017
|
walk3(tree.rootNode, (node) => {
|
|
@@ -9010,7 +9048,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
|
|
|
9010
9048
|
}
|
|
9011
9049
|
function sqlalchemyEndpointsFromFile(file, serviceDir) {
|
|
9012
9050
|
if (!SQLALCHEMY_IMPORT_RE.test(file.content)) return [];
|
|
9013
|
-
const tree = parseSource6(
|
|
9051
|
+
const tree = parseSource6(makePyParser3(), file.content);
|
|
9014
9052
|
const out = [];
|
|
9015
9053
|
const seen = /* @__PURE__ */ new Set();
|
|
9016
9054
|
const push = (name, line, columns) => {
|
|
@@ -9062,7 +9100,7 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
|
|
|
9062
9100
|
function buildSqlalchemyModelRegistry(files) {
|
|
9063
9101
|
const table = /* @__PURE__ */ new Map();
|
|
9064
9102
|
const ambiguous = /* @__PURE__ */ new Set();
|
|
9065
|
-
const parser =
|
|
9103
|
+
const parser = makePyParser3();
|
|
9066
9104
|
for (const file of files) {
|
|
9067
9105
|
if (!SQLALCHEMY_IMPORT_RE.test(file.content)) continue;
|
|
9068
9106
|
const tree = parseSource6(parser, file.content);
|
|
@@ -9111,7 +9149,7 @@ function importsModelName(content, name) {
|
|
|
9111
9149
|
function pythonOrmCrossFileEndpoints(files, serviceDir) {
|
|
9112
9150
|
const registry = buildSqlalchemyModelRegistry(files);
|
|
9113
9151
|
if (registry.size === 0) return [];
|
|
9114
|
-
const parser =
|
|
9152
|
+
const parser = makePyParser3();
|
|
9115
9153
|
const out = [];
|
|
9116
9154
|
const seen = /* @__PURE__ */ new Set();
|
|
9117
9155
|
for (const file of files) {
|
|
@@ -9148,7 +9186,7 @@ import Python5 from "tree-sitter-python";
|
|
|
9148
9186
|
import { infraId as infraId10 } from "@neat.is/types";
|
|
9149
9187
|
var DJANGO_IMPORT_RE = /(?:from|import)\s+django\b/;
|
|
9150
9188
|
var PARSE_CHUNK7 = 16384;
|
|
9151
|
-
function
|
|
9189
|
+
function makePyParser4() {
|
|
9152
9190
|
const p = new Parser10();
|
|
9153
9191
|
p.setLanguage(Python5);
|
|
9154
9192
|
return p;
|
|
@@ -9209,7 +9247,7 @@ function readMeta(body) {
|
|
|
9209
9247
|
}
|
|
9210
9248
|
function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
9211
9249
|
if (!DJANGO_IMPORT_RE.test(file.content)) return [];
|
|
9212
|
-
const tree = parseSource7(
|
|
9250
|
+
const tree = parseSource7(makePyParser4(), file.content);
|
|
9213
9251
|
const out = [];
|
|
9214
9252
|
const seen = /* @__PURE__ */ new Set();
|
|
9215
9253
|
const defaultAppLabel = path40.basename(path40.dirname(file.path));
|
|
@@ -9243,7 +9281,7 @@ import JavaScript7 from "tree-sitter-javascript";
|
|
|
9243
9281
|
import { infraId as infraId11 } from "@neat.is/types";
|
|
9244
9282
|
var DRIZZLE_IMPORT_RE = /drizzle-orm/;
|
|
9245
9283
|
var TABLE_BUILDERS = /* @__PURE__ */ new Set(["pgTable", "mysqlTable", "sqliteTable"]);
|
|
9246
|
-
function
|
|
9284
|
+
function parserForExt3(ext) {
|
|
9247
9285
|
const p = new Parser11();
|
|
9248
9286
|
p.setLanguage(GRAMMAR_BY_EXT[ext] ?? JavaScript7);
|
|
9249
9287
|
return p;
|
|
@@ -9315,7 +9353,7 @@ function columnsFromObject(obj) {
|
|
|
9315
9353
|
}
|
|
9316
9354
|
function drizzleEndpointsFromFile(file, serviceDir) {
|
|
9317
9355
|
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
9318
|
-
const tree = parseSource3(
|
|
9356
|
+
const tree = parseSource3(parserForExt3(path41.extname(file.path)), file.content);
|
|
9319
9357
|
const out = [];
|
|
9320
9358
|
const seen = /* @__PURE__ */ new Set();
|
|
9321
9359
|
const walk9 = (node) => {
|
|
@@ -9404,7 +9442,7 @@ function referencesTargetVar(call) {
|
|
|
9404
9442
|
}
|
|
9405
9443
|
function drizzleForeignKeys(file, serviceDir) {
|
|
9406
9444
|
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
9407
|
-
const tree = parseSource3(
|
|
9445
|
+
const tree = parseSource3(parserForExt3(path41.extname(file.path)), file.content);
|
|
9408
9446
|
const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
|
|
9409
9447
|
const out = [];
|
|
9410
9448
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -10467,8 +10505,41 @@ import path45 from "path";
|
|
|
10467
10505
|
import Parser14 from "tree-sitter";
|
|
10468
10506
|
import Go3 from "tree-sitter-go";
|
|
10469
10507
|
import { infraId as infraId15 } from "@neat.is/types";
|
|
10470
|
-
var SQL_METHODS = /* @__PURE__ */ new Set(["Exec", "ExecContext", "Query", "QueryContext", "QueryRow", "QueryRowContext"]);
|
|
10471
10508
|
var PARSE_CHUNK10 = 16384;
|
|
10509
|
+
var DATABASE_SQL_METHODS = /* @__PURE__ */ new Set([
|
|
10510
|
+
"Query",
|
|
10511
|
+
"QueryContext",
|
|
10512
|
+
"QueryRow",
|
|
10513
|
+
"QueryRowContext",
|
|
10514
|
+
"Exec",
|
|
10515
|
+
"ExecContext",
|
|
10516
|
+
"Prepare",
|
|
10517
|
+
"PrepareContext"
|
|
10518
|
+
]);
|
|
10519
|
+
var SQLX_METHODS = /* @__PURE__ */ new Set([
|
|
10520
|
+
"Get",
|
|
10521
|
+
"Select",
|
|
10522
|
+
"Queryx",
|
|
10523
|
+
"QueryRowx",
|
|
10524
|
+
"NamedExec",
|
|
10525
|
+
"NamedQuery",
|
|
10526
|
+
"MustExec",
|
|
10527
|
+
"Preparex",
|
|
10528
|
+
"GetContext",
|
|
10529
|
+
"SelectContext"
|
|
10530
|
+
]);
|
|
10531
|
+
var DATABASE_SQL_IMPORT = "database/sql";
|
|
10532
|
+
var SQLX_IMPORT = "github.com/jmoiron/sqlx";
|
|
10533
|
+
function makeGoParser3() {
|
|
10534
|
+
const p = new Parser14();
|
|
10535
|
+
p.setLanguage(Go3);
|
|
10536
|
+
return p;
|
|
10537
|
+
}
|
|
10538
|
+
function parseSource10(parser, source) {
|
|
10539
|
+
return parser.parse(
|
|
10540
|
+
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK10)
|
|
10541
|
+
);
|
|
10542
|
+
}
|
|
10472
10543
|
function walk7(node, visit) {
|
|
10473
10544
|
visit(node);
|
|
10474
10545
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
@@ -10476,25 +10547,54 @@ function walk7(node, visit) {
|
|
|
10476
10547
|
if (child) walk7(child, visit);
|
|
10477
10548
|
}
|
|
10478
10549
|
}
|
|
10550
|
+
function goStringLiteralValue(node) {
|
|
10551
|
+
if (!node) return null;
|
|
10552
|
+
if (node.type === "interpreted_string_literal" || node.type === "raw_string_literal") {
|
|
10553
|
+
const t = node.text;
|
|
10554
|
+
return t.length >= 2 ? t.slice(1, -1) : "";
|
|
10555
|
+
}
|
|
10556
|
+
return null;
|
|
10557
|
+
}
|
|
10558
|
+
function goImportsAny(root, names) {
|
|
10559
|
+
let found = false;
|
|
10560
|
+
walk7(root, (node) => {
|
|
10561
|
+
if (found || node.type !== "import_spec") return;
|
|
10562
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
10563
|
+
const value = goStringLiteralValue(node.namedChild(i));
|
|
10564
|
+
if (value !== null && names.has(value)) found = true;
|
|
10565
|
+
}
|
|
10566
|
+
});
|
|
10567
|
+
return found;
|
|
10568
|
+
}
|
|
10569
|
+
function firstStringLiteralArg(argsNode) {
|
|
10570
|
+
if (!argsNode) return null;
|
|
10571
|
+
for (let i = 0; i < argsNode.namedChildCount; i++) {
|
|
10572
|
+
const value = goStringLiteralValue(argsNode.namedChild(i));
|
|
10573
|
+
if (value !== null) return value;
|
|
10574
|
+
}
|
|
10575
|
+
return null;
|
|
10576
|
+
}
|
|
10479
10577
|
function goSqlEndpointsFromFile(file, serviceDir) {
|
|
10480
10578
|
if (path45.extname(file.path) !== ".go") return [];
|
|
10481
|
-
|
|
10482
|
-
|
|
10483
|
-
const
|
|
10484
|
-
|
|
10485
|
-
);
|
|
10579
|
+
if (!file.content.includes(DATABASE_SQL_IMPORT) && !file.content.includes(SQLX_IMPORT)) return [];
|
|
10580
|
+
const tree = parseSource10(makeGoParser3(), file.content);
|
|
10581
|
+
const importsDatabaseSql = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([DATABASE_SQL_IMPORT]));
|
|
10582
|
+
const importsSqlx = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([SQLX_IMPORT]));
|
|
10583
|
+
if (!importsDatabaseSql && !importsSqlx) return [];
|
|
10486
10584
|
const out = [];
|
|
10487
10585
|
walk7(tree.rootNode, (node) => {
|
|
10488
10586
|
if (node.type !== "call_expression") return;
|
|
10489
10587
|
const fn = node.childForFieldName("function");
|
|
10490
10588
|
if (fn?.type !== "selector_expression") return;
|
|
10491
10589
|
const method = fn.childForFieldName("field")?.text;
|
|
10492
|
-
if (!method
|
|
10493
|
-
const
|
|
10494
|
-
if (!
|
|
10495
|
-
const sql =
|
|
10590
|
+
if (!method) return;
|
|
10591
|
+
const recognized = DATABASE_SQL_METHODS.has(method) || importsSqlx && SQLX_METHODS.has(method);
|
|
10592
|
+
if (!recognized) return;
|
|
10593
|
+
const sql = firstStringLiteralArg(node.childForFieldName("arguments"));
|
|
10594
|
+
if (sql === null) return;
|
|
10496
10595
|
const table = tableFromSqlStatement(sql);
|
|
10497
10596
|
if (!table) return;
|
|
10597
|
+
const columns = columnsFromSqlStatement(sql);
|
|
10498
10598
|
const line = node.startPosition.row + 1;
|
|
10499
10599
|
out.push({
|
|
10500
10600
|
infraId: infraId15("sql-table", table),
|
|
@@ -10502,7 +10602,12 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
10502
10602
|
kind: "sql-table",
|
|
10503
10603
|
edgeType: "CALLS",
|
|
10504
10604
|
confidenceKind: "verified-call-site",
|
|
10505
|
-
|
|
10605
|
+
...columns.length > 0 ? { columns } : {},
|
|
10606
|
+
evidence: {
|
|
10607
|
+
file: toPosix(path45.relative(serviceDir, file.path)),
|
|
10608
|
+
line,
|
|
10609
|
+
snippet: snippet(file.content, line)
|
|
10610
|
+
}
|
|
10506
10611
|
});
|
|
10507
10612
|
});
|
|
10508
10613
|
return out;
|
|
@@ -10515,12 +10620,12 @@ import Go4 from "tree-sitter-go";
|
|
|
10515
10620
|
import { infraId as infraId16 } from "@neat.is/types";
|
|
10516
10621
|
var GORM_IMPORT_RE = /gorm\.io\/gorm/;
|
|
10517
10622
|
var PARSE_CHUNK11 = 16384;
|
|
10518
|
-
function
|
|
10623
|
+
function makeGoParser4() {
|
|
10519
10624
|
const p = new Parser15();
|
|
10520
10625
|
p.setLanguage(Go4);
|
|
10521
10626
|
return p;
|
|
10522
10627
|
}
|
|
10523
|
-
function
|
|
10628
|
+
function parseSource11(parser, source) {
|
|
10524
10629
|
return parser.parse(
|
|
10525
10630
|
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK11)
|
|
10526
10631
|
);
|
|
@@ -10944,7 +11049,7 @@ function collectColumns(struct, structs, seen, prefix, out, emitted) {
|
|
|
10944
11049
|
function gormEndpointsFromFile(file, serviceDir) {
|
|
10945
11050
|
if (path46.extname(file.path) !== ".go") return [];
|
|
10946
11051
|
if (!GORM_IMPORT_RE.test(file.content)) return [];
|
|
10947
|
-
const tree =
|
|
11052
|
+
const tree = parseSource11(makeGoParser4(), file.content);
|
|
10948
11053
|
const { structs, models, tableFor } = analyze(tree);
|
|
10949
11054
|
const out = [];
|
|
10950
11055
|
const seenTables = /* @__PURE__ */ new Set();
|
|
@@ -10975,7 +11080,7 @@ function gormEndpointsFromFile(file, serviceDir) {
|
|
|
10975
11080
|
function gormForeignKeys(file, serviceDir) {
|
|
10976
11081
|
if (path46.extname(file.path) !== ".go") return [];
|
|
10977
11082
|
if (!GORM_IMPORT_RE.test(file.content)) return [];
|
|
10978
|
-
const tree =
|
|
11083
|
+
const tree = parseSource11(makeGoParser4(), file.content);
|
|
10979
11084
|
const { structs, models, tableFor } = analyze(tree);
|
|
10980
11085
|
const out = [];
|
|
10981
11086
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -12105,7 +12210,7 @@ import {
|
|
|
12105
12210
|
} from "@neat.is/types";
|
|
12106
12211
|
var ZOD_IMPORT_RE = /\bzod\b/;
|
|
12107
12212
|
var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
|
|
12108
|
-
function
|
|
12213
|
+
function parserForExt4(ext) {
|
|
12109
12214
|
const p = new Parser16();
|
|
12110
12215
|
p.setLanguage(GRAMMAR_BY_EXT[ext] ?? JavaScript8);
|
|
12111
12216
|
return p;
|
|
@@ -12194,7 +12299,7 @@ function topLevelSchemas(root) {
|
|
|
12194
12299
|
}
|
|
12195
12300
|
function zodShapesFromFile(file, serviceDir) {
|
|
12196
12301
|
if (!ZOD_IMPORT_RE.test(file.content)) return [];
|
|
12197
|
-
const tree = parseSource3(
|
|
12302
|
+
const tree = parseSource3(parserForExt4(path55.extname(file.path)), file.content);
|
|
12198
12303
|
const out = [];
|
|
12199
12304
|
const seen = /* @__PURE__ */ new Set();
|
|
12200
12305
|
for (const { name, call } of topLevelSchemas(tree.rootNode)) {
|
|
@@ -14399,6 +14504,23 @@ async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
|
|
|
14399
14504
|
unresolved++;
|
|
14400
14505
|
continue;
|
|
14401
14506
|
}
|
|
14507
|
+
if (signal.incident) {
|
|
14508
|
+
ensureServiceNode(graph, resolved.serviceName, NO_ENV);
|
|
14509
|
+
if (!ctx.errorsPath || !graph.hasNode(resolved.targetNodeId)) {
|
|
14510
|
+
unresolved++;
|
|
14511
|
+
continue;
|
|
14512
|
+
}
|
|
14513
|
+
await appendConnectorIncident(ctx.errorsPath, {
|
|
14514
|
+
id: signal.incident.id,
|
|
14515
|
+
timestamp: signal.incident.timestamp,
|
|
14516
|
+
service: signal.incident.service,
|
|
14517
|
+
errorType: signal.incident.errorType,
|
|
14518
|
+
errorMessage: signal.incident.errorMessage,
|
|
14519
|
+
...signal.incident.attributes ? { attributes: signal.incident.attributes } : {},
|
|
14520
|
+
affectedNode: resolved.targetNodeId
|
|
14521
|
+
});
|
|
14522
|
+
continue;
|
|
14523
|
+
}
|
|
14402
14524
|
if (resolved.ensureInfraNode) {
|
|
14403
14525
|
const { kind, name, provider } = resolved.ensureInfraNode;
|
|
14404
14526
|
ensureInfraNode(graph, kind, name, provider);
|
|
@@ -16566,6 +16688,329 @@ function createPlanetscaleConnector(graph, config, deps = {}) {
|
|
|
16566
16688
|
};
|
|
16567
16689
|
}
|
|
16568
16690
|
|
|
16691
|
+
// src/connectors/eas/types.ts
|
|
16692
|
+
function readEasCredentials(raw) {
|
|
16693
|
+
const token = raw["token"];
|
|
16694
|
+
if (typeof token !== "string" || token.length === 0) {
|
|
16695
|
+
throw new Error("eas connector: credentials.token (EXPO_TOKEN) must be a non-empty string");
|
|
16696
|
+
}
|
|
16697
|
+
return { token };
|
|
16698
|
+
}
|
|
16699
|
+
var EAS_STATUS_ERRORED = "ERRORED";
|
|
16700
|
+
var TRANSIENT_BUILD_PHASES = /* @__PURE__ */ new Set([
|
|
16701
|
+
"SPIN_UP_BUILDER",
|
|
16702
|
+
"PREPARE_CREDENTIALS",
|
|
16703
|
+
"RESTORE_CACHE",
|
|
16704
|
+
"UPLOAD_APPLICATION_ARCHIVE"
|
|
16705
|
+
]);
|
|
16706
|
+
var INTERNAL_ERROR_CODE = /INTERNAL_SERVER_ERROR|EAS_BUILD_.*INTERNAL|_INTERNAL_ERROR|UNKNOWN_ERROR/i;
|
|
16707
|
+
function isTransientFailure(err) {
|
|
16708
|
+
if (!err) return false;
|
|
16709
|
+
const phase = typeof err.buildPhase === "string" ? err.buildPhase : void 0;
|
|
16710
|
+
if (phase) {
|
|
16711
|
+
if (TRANSIENT_BUILD_PHASES.has(phase)) return true;
|
|
16712
|
+
if (/^SPIN_UP|_CREDENTIALS$|^RESTORE_CACHE/i.test(phase)) return true;
|
|
16713
|
+
}
|
|
16714
|
+
const code = typeof err.errorCode === "string" ? err.errorCode : void 0;
|
|
16715
|
+
if (code && INTERNAL_ERROR_CODE.test(code)) return true;
|
|
16716
|
+
return false;
|
|
16717
|
+
}
|
|
16718
|
+
var FIELD_SEP3 = "\0";
|
|
16719
|
+
var EAS_TARGET_KIND = "eas-build";
|
|
16720
|
+
function packEasTargetName(identity) {
|
|
16721
|
+
return [identity.serviceName, identity.phase].join(FIELD_SEP3);
|
|
16722
|
+
}
|
|
16723
|
+
function parseEasTargetName(targetName) {
|
|
16724
|
+
const sep = targetName.indexOf(FIELD_SEP3);
|
|
16725
|
+
if (sep === -1) return null;
|
|
16726
|
+
const serviceName = targetName.slice(0, sep);
|
|
16727
|
+
const phase = targetName.slice(sep + 1);
|
|
16728
|
+
if (!serviceName) return null;
|
|
16729
|
+
return { serviceName, phase };
|
|
16730
|
+
}
|
|
16731
|
+
|
|
16732
|
+
// src/connectors/eas/client.ts
|
|
16733
|
+
var DEFAULT_EAS_API_URL = "https://api.expo.dev/graphql";
|
|
16734
|
+
var DEFAULT_PAGE_SIZE = 50;
|
|
16735
|
+
var DEFAULT_MAX_PAGES = 10;
|
|
16736
|
+
var DEFAULT_MAX_LOG_BYTES = 16 * 1024;
|
|
16737
|
+
var DEFAULT_MAX_LOOKBACK_MS6 = 7 * 24 * 60 * 60 * 1e3;
|
|
16738
|
+
var BUILDS_QUERY = `
|
|
16739
|
+
query NeatEasErroredBuilds($appId: String!, $offset: Int!, $limit: Int!) {
|
|
16740
|
+
app {
|
|
16741
|
+
byId(appId: $appId) {
|
|
16742
|
+
builds(offset: $offset, limit: $limit, filter: { status: ERRORED }) {
|
|
16743
|
+
id
|
|
16744
|
+
status
|
|
16745
|
+
platform
|
|
16746
|
+
buildProfile
|
|
16747
|
+
gitCommitHash
|
|
16748
|
+
gitCommitMessage
|
|
16749
|
+
gitRef
|
|
16750
|
+
isGitWorkingTreeDirty
|
|
16751
|
+
createdAt
|
|
16752
|
+
completedAt
|
|
16753
|
+
error {
|
|
16754
|
+
buildPhase
|
|
16755
|
+
errorCode
|
|
16756
|
+
message
|
|
16757
|
+
docsUrl
|
|
16758
|
+
}
|
|
16759
|
+
logFileUrls
|
|
16760
|
+
}
|
|
16761
|
+
}
|
|
16762
|
+
}
|
|
16763
|
+
}
|
|
16764
|
+
`;
|
|
16765
|
+
async function easGraphQL(apiUrl, token, query, variables, accountKey, fetchImpl) {
|
|
16766
|
+
const res = await junctionFetch(
|
|
16767
|
+
apiUrl,
|
|
16768
|
+
{
|
|
16769
|
+
method: "POST",
|
|
16770
|
+
headers: {
|
|
16771
|
+
"Content-Type": "application/json",
|
|
16772
|
+
...bearerAuthHeader(token)
|
|
16773
|
+
},
|
|
16774
|
+
body: JSON.stringify({ query, variables })
|
|
16775
|
+
},
|
|
16776
|
+
// accountKey: the Expo app id — the per-(provider, accountKey) rate-limit
|
|
16777
|
+
// bucket (ADR-131), the closest thing this connector carries to "one account".
|
|
16778
|
+
{ provider: "eas", accountKey, ...fetchImpl ? { fetchImpl } : {} }
|
|
16779
|
+
);
|
|
16780
|
+
if (!res.ok) {
|
|
16781
|
+
throw new Error(`Expo GraphQL request failed: ${res.status} ${res.statusText}`);
|
|
16782
|
+
}
|
|
16783
|
+
const body = await res.json();
|
|
16784
|
+
if (body.errors && body.errors.length > 0) {
|
|
16785
|
+
throw new Error(`Expo GraphQL errors: ${body.errors.map((e) => e.message).join("; ")}`);
|
|
16786
|
+
}
|
|
16787
|
+
if (!body.data) throw new Error("Expo GraphQL response carried no data");
|
|
16788
|
+
return body.data;
|
|
16789
|
+
}
|
|
16790
|
+
async function fetchErroredBuilds(token, config, fetchImpl) {
|
|
16791
|
+
const apiUrl = config.apiUrl ?? DEFAULT_EAS_API_URL;
|
|
16792
|
+
const pageSize = config.pageSize ?? DEFAULT_PAGE_SIZE;
|
|
16793
|
+
const maxPages = config.maxPages ?? DEFAULT_MAX_PAGES;
|
|
16794
|
+
const out = [];
|
|
16795
|
+
const seen = /* @__PURE__ */ new Set();
|
|
16796
|
+
for (let page = 0; page < maxPages; page++) {
|
|
16797
|
+
const data = await easGraphQL(
|
|
16798
|
+
apiUrl,
|
|
16799
|
+
token,
|
|
16800
|
+
BUILDS_QUERY,
|
|
16801
|
+
{ appId: config.appId, offset: page * pageSize, limit: pageSize },
|
|
16802
|
+
config.appId,
|
|
16803
|
+
fetchImpl
|
|
16804
|
+
);
|
|
16805
|
+
const builds = data.app?.byId?.builds;
|
|
16806
|
+
if (!Array.isArray(builds)) break;
|
|
16807
|
+
let added = 0;
|
|
16808
|
+
for (const b of builds) {
|
|
16809
|
+
if (!b || typeof b.id !== "string" || b.id.length === 0) continue;
|
|
16810
|
+
if (b.status !== EAS_STATUS_ERRORED) continue;
|
|
16811
|
+
if (seen.has(b.id)) continue;
|
|
16812
|
+
seen.add(b.id);
|
|
16813
|
+
out.push(b);
|
|
16814
|
+
added++;
|
|
16815
|
+
}
|
|
16816
|
+
if (builds.length < pageSize) break;
|
|
16817
|
+
if (added === 0) break;
|
|
16818
|
+
}
|
|
16819
|
+
return out;
|
|
16820
|
+
}
|
|
16821
|
+
async function fetchBuildLogs(logFileUrls, maxBytes = DEFAULT_MAX_LOG_BYTES, fetchImpl) {
|
|
16822
|
+
if (!Array.isArray(logFileUrls) || logFileUrls.length === 0) return "";
|
|
16823
|
+
const doFetch = fetchImpl ?? fetch;
|
|
16824
|
+
const chunks = [];
|
|
16825
|
+
for (const url of logFileUrls) {
|
|
16826
|
+
if (typeof url !== "string" || url.length === 0) continue;
|
|
16827
|
+
try {
|
|
16828
|
+
const res = await doFetch(url);
|
|
16829
|
+
if (!res.ok) continue;
|
|
16830
|
+
chunks.push(await res.text());
|
|
16831
|
+
} catch {
|
|
16832
|
+
}
|
|
16833
|
+
}
|
|
16834
|
+
const joined = chunks.join("\n");
|
|
16835
|
+
return joined.length > maxBytes ? joined.slice(joined.length - maxBytes) : joined;
|
|
16836
|
+
}
|
|
16837
|
+
|
|
16838
|
+
// src/connectors/eas/map.ts
|
|
16839
|
+
function buildEventTime(build) {
|
|
16840
|
+
if (typeof build.completedAt === "string" && build.completedAt.length > 0) return build.completedAt;
|
|
16841
|
+
if (typeof build.createdAt === "string" && build.createdAt.length > 0) return build.createdAt;
|
|
16842
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
16843
|
+
}
|
|
16844
|
+
function incidentMessage2(build) {
|
|
16845
|
+
const err = build.error ?? {};
|
|
16846
|
+
const phase = typeof err.buildPhase === "string" && err.buildPhase.length > 0 ? ` at ${err.buildPhase}` : "";
|
|
16847
|
+
const detail = typeof err.message === "string" && err.message.trim().length > 0 && err.message.trim() || typeof err.errorCode === "string" && err.errorCode.length > 0 && err.errorCode || "no error detail reported";
|
|
16848
|
+
let msg = `EAS build failed${phase}: ${detail}`;
|
|
16849
|
+
if (build.isGitWorkingTreeDirty === true) {
|
|
16850
|
+
msg += " (built from a dirty working tree \u2014 the commit may not represent what built)";
|
|
16851
|
+
}
|
|
16852
|
+
return msg;
|
|
16853
|
+
}
|
|
16854
|
+
function incidentAttributes(build) {
|
|
16855
|
+
const attrs = {};
|
|
16856
|
+
const err = build.error ?? {};
|
|
16857
|
+
const put = (k, v) => {
|
|
16858
|
+
if (typeof v === "string" && v.length === 0) return;
|
|
16859
|
+
if (v !== void 0 && v !== null) attrs[k] = v;
|
|
16860
|
+
};
|
|
16861
|
+
put("eas.buildId", build.id);
|
|
16862
|
+
put("eas.platform", build.platform ?? void 0);
|
|
16863
|
+
put("eas.buildProfile", build.buildProfile ?? void 0);
|
|
16864
|
+
put("eas.buildPhase", err.buildPhase ?? void 0);
|
|
16865
|
+
put("eas.errorCode", err.errorCode ?? void 0);
|
|
16866
|
+
put("eas.docsUrl", err.docsUrl ?? void 0);
|
|
16867
|
+
put("eas.gitCommitHash", build.gitCommitHash ?? void 0);
|
|
16868
|
+
put("eas.gitRef", build.gitRef ?? void 0);
|
|
16869
|
+
put("eas.gitCommitMessage", build.gitCommitMessage ?? void 0);
|
|
16870
|
+
if (typeof build.isGitWorkingTreeDirty === "boolean") {
|
|
16871
|
+
attrs["eas.gitWorkingTreeDirty"] = build.isGitWorkingTreeDirty;
|
|
16872
|
+
if (build.isGitWorkingTreeDirty) attrs["eas.confidence"] = "low";
|
|
16873
|
+
}
|
|
16874
|
+
put("eas.createdAt", build.createdAt ?? void 0);
|
|
16875
|
+
put("eas.completedAt", build.completedAt ?? void 0);
|
|
16876
|
+
if (typeof build.logsText === "string" && build.logsText.length > 0) {
|
|
16877
|
+
attrs["eas.logs"] = build.logsText;
|
|
16878
|
+
}
|
|
16879
|
+
return attrs;
|
|
16880
|
+
}
|
|
16881
|
+
function mapBuildToSignal(build, serviceName) {
|
|
16882
|
+
if (!build || typeof build !== "object") return null;
|
|
16883
|
+
if (build.status !== EAS_STATUS_ERRORED) return null;
|
|
16884
|
+
if (!build.error) return null;
|
|
16885
|
+
if (isTransientFailure(build.error)) return null;
|
|
16886
|
+
const timestamp = buildEventTime(build);
|
|
16887
|
+
const phase = typeof build.error.buildPhase === "string" ? build.error.buildPhase : "";
|
|
16888
|
+
return {
|
|
16889
|
+
targetKind: EAS_TARGET_KIND,
|
|
16890
|
+
targetName: packEasTargetName({ serviceName, phase }),
|
|
16891
|
+
// Incident-only — no edge, so no call/error count to replay.
|
|
16892
|
+
callCount: 0,
|
|
16893
|
+
errorCount: 0,
|
|
16894
|
+
lastObservedIso: timestamp,
|
|
16895
|
+
incident: {
|
|
16896
|
+
id: `eas:build:${build.id}`,
|
|
16897
|
+
timestamp,
|
|
16898
|
+
service: serviceName,
|
|
16899
|
+
errorType: "eas-build-failure",
|
|
16900
|
+
errorMessage: incidentMessage2(build),
|
|
16901
|
+
attributes: incidentAttributes(build)
|
|
16902
|
+
}
|
|
16903
|
+
};
|
|
16904
|
+
}
|
|
16905
|
+
function mapBuildsToSignals(builds, serviceName) {
|
|
16906
|
+
const out = [];
|
|
16907
|
+
for (const build of builds) {
|
|
16908
|
+
const signal = mapBuildToSignal(build, serviceName);
|
|
16909
|
+
if (signal) out.push(signal);
|
|
16910
|
+
}
|
|
16911
|
+
return out;
|
|
16912
|
+
}
|
|
16913
|
+
|
|
16914
|
+
// src/connectors/eas/resolve.ts
|
|
16915
|
+
import { EdgeType as EdgeType34, NodeType as NodeType32, parseFileId as parseFileId3 } from "@neat.is/types";
|
|
16916
|
+
var NO_ENV2 = "unknown";
|
|
16917
|
+
var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
|
|
16918
|
+
var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
|
|
16919
|
+
"READ_APP_CONFIG",
|
|
16920
|
+
"CONFIGURE_EXPO_UPDATES",
|
|
16921
|
+
"CALCULATE_EXPO_UPDATES_RUNTIME_VERSION"
|
|
16922
|
+
]);
|
|
16923
|
+
function configBasenamesForPhase(phase) {
|
|
16924
|
+
if (EAS_JSON_PHASES.has(phase)) return ["eas.json"];
|
|
16925
|
+
if (APP_CONFIG_PHASES.has(phase)) return ["app.json", "app.config.json"];
|
|
16926
|
+
return [];
|
|
16927
|
+
}
|
|
16928
|
+
function configNodeService(graph, configNodeId) {
|
|
16929
|
+
for (const edgeId of graph.inboundEdges(configNodeId)) {
|
|
16930
|
+
const edge = graph.getEdgeAttributes(edgeId);
|
|
16931
|
+
if (edge.type !== EdgeType34.CONFIGURED_BY) continue;
|
|
16932
|
+
const parsed = parseFileId3(edge.source);
|
|
16933
|
+
if (parsed) return parsed.service;
|
|
16934
|
+
}
|
|
16935
|
+
return null;
|
|
16936
|
+
}
|
|
16937
|
+
function findConfigNode(graph, basenames, serviceName) {
|
|
16938
|
+
let scoped = null;
|
|
16939
|
+
let anyMatch = null;
|
|
16940
|
+
graph.forEachNode((id, attrs) => {
|
|
16941
|
+
if (scoped) return;
|
|
16942
|
+
const node = attrs;
|
|
16943
|
+
if (node.type !== NodeType32.ConfigNode) return;
|
|
16944
|
+
if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
|
|
16945
|
+
if (anyMatch === null) anyMatch = id;
|
|
16946
|
+
if (configNodeService(graph, id) === serviceName) scoped = id;
|
|
16947
|
+
});
|
|
16948
|
+
return scoped ?? anyMatch;
|
|
16949
|
+
}
|
|
16950
|
+
function createEasResolveTarget(graph) {
|
|
16951
|
+
return (signal) => {
|
|
16952
|
+
if (signal.targetKind !== EAS_TARGET_KIND) return null;
|
|
16953
|
+
const identity = parseEasTargetName(signal.targetName);
|
|
16954
|
+
if (!identity) return null;
|
|
16955
|
+
const { serviceName, phase } = identity;
|
|
16956
|
+
const basenames = configBasenamesForPhase(phase);
|
|
16957
|
+
if (basenames.length > 0) {
|
|
16958
|
+
const configNodeId = findConfigNode(graph, basenames, serviceName);
|
|
16959
|
+
if (configNodeId) {
|
|
16960
|
+
return { targetNodeId: configNodeId, serviceName, edgeType: EdgeType34.CALLS };
|
|
16961
|
+
}
|
|
16962
|
+
}
|
|
16963
|
+
return {
|
|
16964
|
+
targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
|
|
16965
|
+
serviceName,
|
|
16966
|
+
edgeType: EdgeType34.CALLS
|
|
16967
|
+
};
|
|
16968
|
+
};
|
|
16969
|
+
}
|
|
16970
|
+
|
|
16971
|
+
// src/connectors/eas/index.ts
|
|
16972
|
+
function isBuildSince(build, sinceIso) {
|
|
16973
|
+
const t = Date.parse(buildEventTime(build));
|
|
16974
|
+
const s = Date.parse(sinceIso);
|
|
16975
|
+
if (Number.isNaN(t) || Number.isNaN(s)) return true;
|
|
16976
|
+
return t > s;
|
|
16977
|
+
}
|
|
16978
|
+
function boundedSinceIso2(since, now, maxLookbackMs) {
|
|
16979
|
+
const floor = new Date(now.getTime() - maxLookbackMs);
|
|
16980
|
+
if (!since) return floor.toISOString();
|
|
16981
|
+
const sinceMs = new Date(since).getTime();
|
|
16982
|
+
if (Number.isNaN(sinceMs)) return floor.toISOString();
|
|
16983
|
+
return sinceMs < floor.getTime() ? floor.toISOString() : new Date(sinceMs).toISOString();
|
|
16984
|
+
}
|
|
16985
|
+
var EasConnector = class {
|
|
16986
|
+
constructor(config, fetchImpl) {
|
|
16987
|
+
this.config = config;
|
|
16988
|
+
this.fetchImpl = fetchImpl;
|
|
16989
|
+
}
|
|
16990
|
+
config;
|
|
16991
|
+
fetchImpl;
|
|
16992
|
+
provider = "eas";
|
|
16993
|
+
async poll(ctx) {
|
|
16994
|
+
const creds = readEasCredentials(ctx.credentials);
|
|
16995
|
+
const serviceName = this.config.serviceName ?? this.config.appId;
|
|
16996
|
+
const maxLookbackMs = this.config.maxLookbackMs ?? DEFAULT_MAX_LOOKBACK_MS6;
|
|
16997
|
+
const sinceIso = boundedSinceIso2(ctx.since, /* @__PURE__ */ new Date(), maxLookbackMs);
|
|
16998
|
+
const builds = await fetchErroredBuilds(creds.token, this.config, this.fetchImpl);
|
|
16999
|
+
const fresh = builds.filter((b) => isBuildSince(b, sinceIso));
|
|
17000
|
+
const maxLogBytes = this.config.maxLogBytes ?? DEFAULT_MAX_LOG_BYTES;
|
|
17001
|
+
for (const build of fresh) {
|
|
17002
|
+
build.logsText = await fetchBuildLogs(build.logFileUrls, maxLogBytes, this.fetchImpl);
|
|
17003
|
+
}
|
|
17004
|
+
return mapBuildsToSignals(fresh, serviceName);
|
|
17005
|
+
}
|
|
17006
|
+
};
|
|
17007
|
+
function createEasConnector(graph, config, fetchImpl) {
|
|
17008
|
+
return {
|
|
17009
|
+
connector: new EasConnector(config, fetchImpl),
|
|
17010
|
+
resolveTarget: createEasResolveTarget(graph)
|
|
17011
|
+
};
|
|
17012
|
+
}
|
|
17013
|
+
|
|
16569
17014
|
// src/connectors/registry.ts
|
|
16570
17015
|
var CLOUDFLARE_API_BASE_URL = "https://api.cloudflare.com/client/v4";
|
|
16571
17016
|
async function authProbe(input) {
|
|
@@ -16853,6 +17298,41 @@ var PROVIDER_DISPATCH = {
|
|
|
16853
17298
|
...fetchImpl ? { fetchImpl } : {}
|
|
16854
17299
|
});
|
|
16855
17300
|
}
|
|
17301
|
+
},
|
|
17302
|
+
eas: {
|
|
17303
|
+
provider: "eas",
|
|
17304
|
+
// The secret is a single robot-user EXPO_TOKEN; a single-string credential
|
|
17305
|
+
// maps to `token`. `appId` is non-secret config (connector-config.md §7.1).
|
|
17306
|
+
primaryCredentialKey: "token",
|
|
17307
|
+
requiredCredentialFields: ["token"],
|
|
17308
|
+
requiredOptionFields: ["appId"],
|
|
17309
|
+
build(graph, options) {
|
|
17310
|
+
return createEasConnector(graph, options);
|
|
17311
|
+
},
|
|
17312
|
+
// Runs the connector's real `builds` query at limit 1 — the exact read poll()
|
|
17313
|
+
// performs, minus the pages — so the probe checks both that the EXPO_TOKEN
|
|
17314
|
+
// authenticates and that this app id is reachable, the same probe-the-real-
|
|
17315
|
+
// query discipline Railway and Cloud Run use over a trivial `{ __typename }`.
|
|
17316
|
+
// A bad or wrong-scoped token comes back as an Expo GraphQL error, which
|
|
17317
|
+
// `fetchErroredBuilds` throws on, so it fails honestly here rather than
|
|
17318
|
+
// silently at the first poll.
|
|
17319
|
+
async validate({ credentials, options, fetchImpl }) {
|
|
17320
|
+
const cfg = options;
|
|
17321
|
+
const appId = String(cfg.appId ?? "");
|
|
17322
|
+
if (!appId) return { ok: false, reason: "eas: appId is required to validate" };
|
|
17323
|
+
const probeConfig = {
|
|
17324
|
+
appId,
|
|
17325
|
+
pageSize: 1,
|
|
17326
|
+
maxPages: 1,
|
|
17327
|
+
...cfg.apiUrl ? { apiUrl: cfg.apiUrl } : {}
|
|
17328
|
+
};
|
|
17329
|
+
try {
|
|
17330
|
+
await fetchErroredBuilds(String(credentials.token ?? ""), probeConfig, fetchImpl);
|
|
17331
|
+
return { ok: true };
|
|
17332
|
+
} catch (err) {
|
|
17333
|
+
return { ok: false, reason: `eas auth check failed: ${err.message}` };
|
|
17334
|
+
}
|
|
17335
|
+
}
|
|
16856
17336
|
}
|
|
16857
17337
|
};
|
|
16858
17338
|
function vercelCredsFrom(credentials) {
|
|
@@ -17065,7 +17545,11 @@ async function startConnectorPolling(input) {
|
|
|
17065
17545
|
const stopFns = all.map(
|
|
17066
17546
|
(registration) => startConnectorPollLoop(
|
|
17067
17547
|
registration.connector,
|
|
17068
|
-
{
|
|
17548
|
+
{
|
|
17549
|
+
projectDir: input.projectDir,
|
|
17550
|
+
credentials: registration.credentials,
|
|
17551
|
+
...input.errorsPath ? { errorsPath: input.errorsPath } : {}
|
|
17552
|
+
},
|
|
17069
17553
|
input.graph,
|
|
17070
17554
|
registration.resolveTarget,
|
|
17071
17555
|
{ intervalMs: registration.intervalMs, connectorId: registration.id }
|
|
@@ -17394,10 +17878,15 @@ function registerRoutes(scope, ctx) {
|
|
|
17394
17878
|
}
|
|
17395
17879
|
const reg = built.registration;
|
|
17396
17880
|
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
17881
|
+
const incidentsPath = errorsPathFor(proj);
|
|
17397
17882
|
try {
|
|
17398
17883
|
const result = await ctx.runPoll(
|
|
17399
17884
|
reg.connector,
|
|
17400
|
-
{
|
|
17885
|
+
{
|
|
17886
|
+
projectDir: proj.scanPath ?? "",
|
|
17887
|
+
credentials: reg.credentials,
|
|
17888
|
+
...incidentsPath ? { errorsPath: incidentsPath } : {}
|
|
17889
|
+
},
|
|
17401
17890
|
proj.graph,
|
|
17402
17891
|
reg.resolveTarget
|
|
17403
17892
|
);
|
|
@@ -18035,4 +18524,4 @@ export {
|
|
|
18035
18524
|
deprovisionConnector,
|
|
18036
18525
|
buildApi
|
|
18037
18526
|
};
|
|
18038
|
-
//# sourceMappingURL=chunk-
|
|
18527
|
+
//# sourceMappingURL=chunk-6T7ZHODF.js.map
|