@neat.is/core 0.4.30-dev.20260716 → 0.4.30

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/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  routeSpanToProject,
3
3
  startDaemon
4
- } from "./chunk-AOLXZWRU.js";
4
+ } from "./chunk-2AJHEZIB.js";
5
5
  import {
6
6
  ProjectNameCollisionError,
7
7
  addProject,
@@ -37,7 +37,7 @@ import {
37
37
  thresholdForEdgeType,
38
38
  touchLastSeen,
39
39
  writeAtomically
40
- } from "./chunk-6HPRD53A.js";
40
+ } from "./chunk-4M2YMGUM.js";
41
41
  import {
42
42
  startOtelGrpcReceiver
43
43
  } from "./chunk-I72HTUOG.js";
package/dist/neatd.cjs CHANGED
@@ -1528,7 +1528,7 @@ function getTransitiveDependencies(graph, nodeId, depth = TRANSITIVE_DEPENDENCIE
1528
1528
  const enqueued = /* @__PURE__ */ new Set([nodeId]);
1529
1529
  while (queue.length > 0) {
1530
1530
  const frame = queue.shift();
1531
- if (frame.distance > 0 && frame.edge) {
1531
+ if (frame.distance > 0 && frame.edge && frame.edge.type !== import_types.EdgeType.CONTAINS) {
1532
1532
  seen.set(frame.nodeId, {
1533
1533
  nodeId: frame.nodeId,
1534
1534
  distance: frame.distance,
@@ -10181,25 +10181,21 @@ function readRailwayToken(credentials) {
10181
10181
  }
10182
10182
  return token;
10183
10183
  }
10184
- async function railwayGraphQL(apiUrl, token, query, variables, accountKey) {
10184
+ function projectAccessTokenHeader(token) {
10185
+ return { "Project-Access-Token": token };
10186
+ }
10187
+ async function railwayGraphQL(apiUrl, token, query, variables, accountKey, fetchImpl) {
10185
10188
  const res = await junctionFetch(
10186
10189
  apiUrl,
10187
10190
  {
10188
10191
  method: "POST",
10189
10192
  headers: {
10190
10193
  "Content-Type": "application/json",
10191
- // docs.railway.com/integrations/api/graphql-overview confirms
10192
- // `Authorization: Bearer <token>` for an account/workspace token;
10193
- // Railway also documents a dedicated `Project-Access-Token: <token>`
10194
- // header for the project-scoped token ADR-127 targets specifically.
10195
- // This connector sends the Bearer form — needs-endpoint-testing
10196
- // whether a live Project-Access-Token requires the dedicated header
10197
- // instead once this poller runs against a real project.
10198
- ...bearerAuthHeader(token)
10194
+ ...projectAccessTokenHeader(token)
10199
10195
  },
10200
10196
  body: JSON.stringify({ query, variables })
10201
10197
  },
10202
- { provider: "railway", accountKey }
10198
+ { provider: "railway", accountKey, ...fetchImpl ? { fetchImpl } : {} }
10203
10199
  );
10204
10200
  if (!res.ok) {
10205
10201
  throw new Error(`Railway GraphQL request failed: ${res.status} ${res.statusText}`);
@@ -10212,20 +10208,8 @@ async function railwayGraphQL(apiUrl, token, query, variables, accountKey) {
10212
10208
  return body.data;
10213
10209
  }
10214
10210
  var HTTP_LOGS_QUERY = `
10215
- query HttpLogs(
10216
- $environmentId: String!
10217
- $serviceId: String!
10218
- $startDate: String!
10219
- $endDate: String!
10220
- $limit: Int
10221
- ) {
10222
- httpLogs(
10223
- environmentId: $environmentId
10224
- serviceId: $serviceId
10225
- startDate: $startDate
10226
- endDate: $endDate
10227
- limit: $limit
10228
- ) {
10211
+ query HttpLogs($deploymentId: String!, $startDate: String, $endDate: String, $limit: Int) {
10212
+ httpLogs(deploymentId: $deploymentId, startDate: $startDate, endDate: $endDate, limit: $limit) {
10229
10213
  timestamp
10230
10214
  method
10231
10215
  path
@@ -10238,21 +10222,9 @@ var HTTP_LOGS_QUERY = `
10238
10222
  }
10239
10223
  `;
10240
10224
  var NETWORK_FLOW_LOGS_QUERY = `
10241
- query NetworkFlowLogs(
10242
- $environmentId: String!
10243
- $serviceId: String!
10244
- $startDate: String!
10245
- $endDate: String!
10246
- $limit: Int
10247
- ) {
10248
- networkFlowLogs(
10249
- environmentId: $environmentId
10250
- serviceId: $serviceId
10251
- startDate: $startDate
10252
- endDate: $endDate
10253
- limit: $limit
10254
- ) {
10255
- timestamp
10225
+ query NetworkFlowLogs($environmentId: String!, $serviceId: String) {
10226
+ networkFlowLogs(environmentId: $environmentId, serviceId: $serviceId) {
10227
+ timestamp: captureStart
10256
10228
  peerServiceId
10257
10229
  peerKind
10258
10230
  direction
@@ -10262,34 +10234,43 @@ var NETWORK_FLOW_LOGS_QUERY = `
10262
10234
  }
10263
10235
  }
10264
10236
  `;
10265
- async function fetchRailwayHttpLogs(config, token, startDate, endDate) {
10237
+ var DEPLOYMENTS_QUERY = `
10238
+ query LatestDeployment($environmentId: String!, $serviceId: String!) {
10239
+ deployments(input: { environmentId: $environmentId, serviceId: $serviceId }, first: 5) {
10240
+ edges { node { id status createdAt } }
10241
+ }
10242
+ }
10243
+ `;
10244
+ async function resolveLatestRailwayDeploymentId(config, token, fetchImpl) {
10245
+ const data = await railwayGraphQL(
10246
+ config.apiUrl ?? DEFAULT_RAILWAY_API_URL,
10247
+ token,
10248
+ DEPLOYMENTS_QUERY,
10249
+ { environmentId: config.environmentId, serviceId: config.serviceId },
10250
+ config.environmentId,
10251
+ fetchImpl
10252
+ );
10253
+ const nodes = data.deployments.edges.map((e) => e.node);
10254
+ const newestFirst = [...nodes].sort((a, b) => b.createdAt.localeCompare(a.createdAt));
10255
+ const success = newestFirst.find((n) => n.status === "SUCCESS");
10256
+ return (success ?? newestFirst[0])?.id ?? null;
10257
+ }
10258
+ async function fetchRailwayHttpLogs(config, token, deploymentId, startDate, endDate) {
10266
10259
  const data = await railwayGraphQL(
10267
10260
  config.apiUrl ?? DEFAULT_RAILWAY_API_URL,
10268
10261
  token,
10269
10262
  HTTP_LOGS_QUERY,
10270
- {
10271
- environmentId: config.environmentId,
10272
- serviceId: config.serviceId,
10273
- startDate,
10274
- endDate,
10275
- limit: config.limit ?? DEFAULT_LOG_LIMIT2
10276
- },
10263
+ { deploymentId, startDate, endDate, limit: config.limit ?? DEFAULT_LOG_LIMIT2 },
10277
10264
  config.environmentId
10278
10265
  );
10279
10266
  return data.httpLogs;
10280
10267
  }
10281
- async function fetchRailwayNetworkFlowLogs(config, token, startDate, endDate) {
10268
+ async function fetchRailwayNetworkFlowLogs(config, token) {
10282
10269
  const data = await railwayGraphQL(
10283
10270
  config.apiUrl ?? DEFAULT_RAILWAY_API_URL,
10284
10271
  token,
10285
10272
  NETWORK_FLOW_LOGS_QUERY,
10286
- {
10287
- environmentId: config.environmentId,
10288
- serviceId: config.serviceId,
10289
- startDate,
10290
- endDate,
10291
- limit: config.limit ?? DEFAULT_LOG_LIMIT2
10292
- },
10273
+ { environmentId: config.environmentId, serviceId: config.serviceId },
10293
10274
  config.environmentId
10294
10275
  );
10295
10276
  return data.networkFlowLogs;
@@ -10425,10 +10406,19 @@ function createRailwayConnector(graph, config) {
10425
10406
  const maxLookbackMs = config.maxLookbackMs ?? DEFAULT_MAX_LOOKBACK_MS2;
10426
10407
  const startDate = boundedRailwayStartDate(ctx.since, now, maxLookbackMs);
10427
10408
  const endDate = now.toISOString();
10428
- const [httpLogs, flowLogs] = await Promise.all([
10429
- fetchRailwayHttpLogs(config, token, startDate, endDate),
10430
- fetchRailwayNetworkFlowLogs(config, token, startDate, endDate)
10409
+ const deploymentId = await resolveLatestRailwayDeploymentId(config, token);
10410
+ const [httpLogsResult, flowLogsResult] = await Promise.allSettled([
10411
+ deploymentId ? fetchRailwayHttpLogs(config, token, deploymentId, startDate, endDate) : Promise.resolve([]),
10412
+ fetchRailwayNetworkFlowLogs(config, token)
10431
10413
  ]);
10414
+ if (httpLogsResult.status === "rejected") {
10415
+ console.error("[neat connector] railway httpLogs poll failed", httpLogsResult.reason);
10416
+ }
10417
+ if (flowLogsResult.status === "rejected") {
10418
+ console.error("[neat connector] railway networkFlowLogs poll failed", flowLogsResult.reason);
10419
+ }
10420
+ const httpLogs = httpLogsResult.status === "fulfilled" ? httpLogsResult.value : [];
10421
+ const flowLogs = flowLogsResult.status === "fulfilled" ? flowLogsResult.value : [];
10432
10422
  const serviceName = config.serviceNameById[config.serviceId];
10433
10423
  const routeIndex = serviceName ? buildRailwayRouteIndex(graph, serviceName) : [];
10434
10424
  return [
@@ -10684,6 +10674,12 @@ async function queryWorkerInvocations(ctx, config, window, fetchImpl = fetch) {
10684
10674
  timeframe: { from: window.fromMs, to: window.toMs },
10685
10675
  view: "events",
10686
10676
  limit: config.eventLimit ?? DEFAULT_EVENT_LIMIT,
10677
+ // The inline query definition. WITHOUT `parameters`, the API treats
10678
+ // `queryId` as a reference to a previously *saved* query and answers 400
10679
+ // "Query not found" (confirmed against the live endpoint) — omitting it was
10680
+ // the reason the connector never worked against real Cloudflare. Naming the
10681
+ // `cloudflare-workers` dataset runs the invocation-telemetry query ad-hoc.
10682
+ parameters: { datasets: ["cloudflare-workers"] },
10687
10683
  // Execute without persisting — this is a read, not a saved query
10688
10684
  // (connectors.md §2's "never writes on the read path" applies to
10689
10685
  // Cloudflare's own query-history state too).
@@ -10938,24 +10934,35 @@ var PROVIDER_DISPATCH = {
10938
10934
  resolveTarget: createRailwayResolveTarget(config)
10939
10935
  };
10940
10936
  },
10941
- // A minimal GraphQL POST Railway's gateway 401s an unauthenticated call
10942
- // at the HTTP layer, so a 2xx confirms the token was accepted regardless of
10943
- // the query's own shape (the connector's real queries are still
10944
- // needs-endpoint-testing per railway/types.ts auth is what we check).
10945
- validate({ credentials, options, fetchImpl }) {
10937
+ // Runs the same `deployments` lookup the poller itself needs
10938
+ // (railway/client.ts's resolveLatestRailwayDeploymentId) rather than a
10939
+ // trivial `{ __typename }` probe. That trivial form is a false positive
10940
+ // for this provider: `Authorization: Bearer <token>` authenticates at
10941
+ // Railway's HTTP gateway (any well-formed token gets a 2xx on a query
10942
+ // that touches no real data) but is not authorized for the connector's
10943
+ // actual queries, which come back as an HTTP-200 response carrying a
10944
+ // GraphQL-level "Not Authorized" error — invisible to authProbe's
10945
+ // status-code-only check. Probing with the real lookup, through the same
10946
+ // Project-Access-Token header client.ts sends, catches both failure
10947
+ // modes: an HTTP-level rejection (thrown as a fetch/status error) and a
10948
+ // GraphQL-level one (thrown by railwayGraphQL's own body.errors check).
10949
+ async validate({ credentials, options, fetchImpl }) {
10946
10950
  const cfg = options;
10947
- return authProbe({
10948
- provider: "railway",
10949
- accountKey: cfg.environmentId ?? "validate",
10950
- url: cfg.apiUrl ?? DEFAULT_RAILWAY_API_URL,
10951
- token: String(credentials.token ?? ""),
10952
- init: {
10953
- method: "POST",
10954
- headers: { "Content-Type": "application/json" },
10955
- body: JSON.stringify({ query: "{ __typename }" })
10956
- },
10957
- ...fetchImpl ? { fetchImpl } : {}
10958
- });
10951
+ if (!cfg.environmentId || !cfg.serviceId) {
10952
+ return { ok: false, reason: "railway: environmentId and serviceId are required to validate" };
10953
+ }
10954
+ const config = {
10955
+ environmentId: cfg.environmentId,
10956
+ serviceId: cfg.serviceId,
10957
+ serviceNameById: cfg.serviceNameById ?? {},
10958
+ ...cfg.apiUrl ? { apiUrl: cfg.apiUrl } : {}
10959
+ };
10960
+ try {
10961
+ await resolveLatestRailwayDeploymentId(config, String(credentials.token ?? ""), fetchImpl);
10962
+ return { ok: true };
10963
+ } catch (err) {
10964
+ return { ok: false, reason: `railway auth check failed: ${err.message}` };
10965
+ }
10959
10966
  }
10960
10967
  },
10961
10968
  firebase: {