@neat.is/core 0.4.29 → 0.4.30-dev.20260717

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.
@@ -31,7 +31,7 @@ import {
31
31
  touchLastSeen,
32
32
  upsertObservedEdge,
33
33
  writeAtomically
34
- } from "./chunk-6HPRD53A.js";
34
+ } from "./chunk-4M2YMGUM.js";
35
35
  import {
36
36
  assertBindAuthority,
37
37
  buildOtelReceiver,
@@ -724,25 +724,21 @@ function readRailwayToken(credentials) {
724
724
  }
725
725
  return token;
726
726
  }
727
- async function railwayGraphQL(apiUrl, token, query, variables, accountKey) {
727
+ function projectAccessTokenHeader(token) {
728
+ return { "Project-Access-Token": token };
729
+ }
730
+ async function railwayGraphQL(apiUrl, token, query, variables, accountKey, fetchImpl) {
728
731
  const res = await junctionFetch(
729
732
  apiUrl,
730
733
  {
731
734
  method: "POST",
732
735
  headers: {
733
736
  "Content-Type": "application/json",
734
- // docs.railway.com/integrations/api/graphql-overview confirms
735
- // `Authorization: Bearer <token>` for an account/workspace token;
736
- // Railway also documents a dedicated `Project-Access-Token: <token>`
737
- // header for the project-scoped token ADR-127 targets specifically.
738
- // This connector sends the Bearer form — needs-endpoint-testing
739
- // whether a live Project-Access-Token requires the dedicated header
740
- // instead once this poller runs against a real project.
741
- ...bearerAuthHeader(token)
737
+ ...projectAccessTokenHeader(token)
742
738
  },
743
739
  body: JSON.stringify({ query, variables })
744
740
  },
745
- { provider: "railway", accountKey }
741
+ { provider: "railway", accountKey, ...fetchImpl ? { fetchImpl } : {} }
746
742
  );
747
743
  if (!res.ok) {
748
744
  throw new Error(`Railway GraphQL request failed: ${res.status} ${res.statusText}`);
@@ -755,20 +751,8 @@ async function railwayGraphQL(apiUrl, token, query, variables, accountKey) {
755
751
  return body.data;
756
752
  }
757
753
  var HTTP_LOGS_QUERY = `
758
- query HttpLogs(
759
- $environmentId: String!
760
- $serviceId: String!
761
- $startDate: String!
762
- $endDate: String!
763
- $limit: Int
764
- ) {
765
- httpLogs(
766
- environmentId: $environmentId
767
- serviceId: $serviceId
768
- startDate: $startDate
769
- endDate: $endDate
770
- limit: $limit
771
- ) {
754
+ query HttpLogs($deploymentId: String!, $startDate: String, $endDate: String, $limit: Int) {
755
+ httpLogs(deploymentId: $deploymentId, startDate: $startDate, endDate: $endDate, limit: $limit) {
772
756
  timestamp
773
757
  method
774
758
  path
@@ -781,21 +765,9 @@ var HTTP_LOGS_QUERY = `
781
765
  }
782
766
  `;
783
767
  var NETWORK_FLOW_LOGS_QUERY = `
784
- query NetworkFlowLogs(
785
- $environmentId: String!
786
- $serviceId: String!
787
- $startDate: String!
788
- $endDate: String!
789
- $limit: Int
790
- ) {
791
- networkFlowLogs(
792
- environmentId: $environmentId
793
- serviceId: $serviceId
794
- startDate: $startDate
795
- endDate: $endDate
796
- limit: $limit
797
- ) {
798
- timestamp
768
+ query NetworkFlowLogs($environmentId: String!, $serviceId: String) {
769
+ networkFlowLogs(environmentId: $environmentId, serviceId: $serviceId) {
770
+ timestamp: captureStart
799
771
  peerServiceId
800
772
  peerKind
801
773
  direction
@@ -805,34 +777,43 @@ var NETWORK_FLOW_LOGS_QUERY = `
805
777
  }
806
778
  }
807
779
  `;
808
- async function fetchRailwayHttpLogs(config, token, startDate, endDate) {
780
+ var DEPLOYMENTS_QUERY = `
781
+ query LatestDeployment($environmentId: String!, $serviceId: String!) {
782
+ deployments(input: { environmentId: $environmentId, serviceId: $serviceId }, first: 5) {
783
+ edges { node { id status createdAt } }
784
+ }
785
+ }
786
+ `;
787
+ async function resolveLatestRailwayDeploymentId(config, token, fetchImpl) {
788
+ const data = await railwayGraphQL(
789
+ config.apiUrl ?? DEFAULT_RAILWAY_API_URL,
790
+ token,
791
+ DEPLOYMENTS_QUERY,
792
+ { environmentId: config.environmentId, serviceId: config.serviceId },
793
+ config.environmentId,
794
+ fetchImpl
795
+ );
796
+ const nodes = data.deployments.edges.map((e) => e.node);
797
+ const newestFirst = [...nodes].sort((a, b) => b.createdAt.localeCompare(a.createdAt));
798
+ const success = newestFirst.find((n) => n.status === "SUCCESS");
799
+ return (success ?? newestFirst[0])?.id ?? null;
800
+ }
801
+ async function fetchRailwayHttpLogs(config, token, deploymentId, startDate, endDate) {
809
802
  const data = await railwayGraphQL(
810
803
  config.apiUrl ?? DEFAULT_RAILWAY_API_URL,
811
804
  token,
812
805
  HTTP_LOGS_QUERY,
813
- {
814
- environmentId: config.environmentId,
815
- serviceId: config.serviceId,
816
- startDate,
817
- endDate,
818
- limit: config.limit ?? DEFAULT_LOG_LIMIT2
819
- },
806
+ { deploymentId, startDate, endDate, limit: config.limit ?? DEFAULT_LOG_LIMIT2 },
820
807
  config.environmentId
821
808
  );
822
809
  return data.httpLogs;
823
810
  }
824
- async function fetchRailwayNetworkFlowLogs(config, token, startDate, endDate) {
811
+ async function fetchRailwayNetworkFlowLogs(config, token) {
825
812
  const data = await railwayGraphQL(
826
813
  config.apiUrl ?? DEFAULT_RAILWAY_API_URL,
827
814
  token,
828
815
  NETWORK_FLOW_LOGS_QUERY,
829
- {
830
- environmentId: config.environmentId,
831
- serviceId: config.serviceId,
832
- startDate,
833
- endDate,
834
- limit: config.limit ?? DEFAULT_LOG_LIMIT2
835
- },
816
+ { environmentId: config.environmentId, serviceId: config.serviceId },
836
817
  config.environmentId
837
818
  );
838
819
  return data.networkFlowLogs;
@@ -968,10 +949,19 @@ function createRailwayConnector(graph, config) {
968
949
  const maxLookbackMs = config.maxLookbackMs ?? DEFAULT_MAX_LOOKBACK_MS2;
969
950
  const startDate = boundedRailwayStartDate(ctx.since, now, maxLookbackMs);
970
951
  const endDate = now.toISOString();
971
- const [httpLogs, flowLogs] = await Promise.all([
972
- fetchRailwayHttpLogs(config, token, startDate, endDate),
973
- fetchRailwayNetworkFlowLogs(config, token, startDate, endDate)
952
+ const deploymentId = await resolveLatestRailwayDeploymentId(config, token);
953
+ const [httpLogsResult, flowLogsResult] = await Promise.allSettled([
954
+ deploymentId ? fetchRailwayHttpLogs(config, token, deploymentId, startDate, endDate) : Promise.resolve([]),
955
+ fetchRailwayNetworkFlowLogs(config, token)
974
956
  ]);
957
+ if (httpLogsResult.status === "rejected") {
958
+ console.error("[neat connector] railway httpLogs poll failed", httpLogsResult.reason);
959
+ }
960
+ if (flowLogsResult.status === "rejected") {
961
+ console.error("[neat connector] railway networkFlowLogs poll failed", flowLogsResult.reason);
962
+ }
963
+ const httpLogs = httpLogsResult.status === "fulfilled" ? httpLogsResult.value : [];
964
+ const flowLogs = flowLogsResult.status === "fulfilled" ? flowLogsResult.value : [];
975
965
  const serviceName = config.serviceNameById[config.serviceId];
976
966
  const routeIndex = serviceName ? buildRailwayRouteIndex(graph, serviceName) : [];
977
967
  return [
@@ -1216,6 +1206,12 @@ async function queryWorkerInvocations(ctx, config, window, fetchImpl = fetch) {
1216
1206
  timeframe: { from: window.fromMs, to: window.toMs },
1217
1207
  view: "events",
1218
1208
  limit: config.eventLimit ?? DEFAULT_EVENT_LIMIT,
1209
+ // The inline query definition. WITHOUT `parameters`, the API treats
1210
+ // `queryId` as a reference to a previously *saved* query and answers 400
1211
+ // "Query not found" (confirmed against the live endpoint) — omitting it was
1212
+ // the reason the connector never worked against real Cloudflare. Naming the
1213
+ // `cloudflare-workers` dataset runs the invocation-telemetry query ad-hoc.
1214
+ parameters: { datasets: ["cloudflare-workers"] },
1219
1215
  // Execute without persisting — this is a read, not a saved query
1220
1216
  // (connectors.md §2's "never writes on the read path" applies to
1221
1217
  // Cloudflare's own query-history state too).
@@ -1466,24 +1462,35 @@ var PROVIDER_DISPATCH = {
1466
1462
  resolveTarget: createRailwayResolveTarget(config)
1467
1463
  };
1468
1464
  },
1469
- // A minimal GraphQL POST Railway's gateway 401s an unauthenticated call
1470
- // at the HTTP layer, so a 2xx confirms the token was accepted regardless of
1471
- // the query's own shape (the connector's real queries are still
1472
- // needs-endpoint-testing per railway/types.ts auth is what we check).
1473
- validate({ credentials, options, fetchImpl }) {
1465
+ // Runs the same `deployments` lookup the poller itself needs
1466
+ // (railway/client.ts's resolveLatestRailwayDeploymentId) rather than a
1467
+ // trivial `{ __typename }` probe. That trivial form is a false positive
1468
+ // for this provider: `Authorization: Bearer <token>` authenticates at
1469
+ // Railway's HTTP gateway (any well-formed token gets a 2xx on a query
1470
+ // that touches no real data) but is not authorized for the connector's
1471
+ // actual queries, which come back as an HTTP-200 response carrying a
1472
+ // GraphQL-level "Not Authorized" error — invisible to authProbe's
1473
+ // status-code-only check. Probing with the real lookup, through the same
1474
+ // Project-Access-Token header client.ts sends, catches both failure
1475
+ // modes: an HTTP-level rejection (thrown as a fetch/status error) and a
1476
+ // GraphQL-level one (thrown by railwayGraphQL's own body.errors check).
1477
+ async validate({ credentials, options, fetchImpl }) {
1474
1478
  const cfg = options;
1475
- return authProbe({
1476
- provider: "railway",
1477
- accountKey: cfg.environmentId ?? "validate",
1478
- url: cfg.apiUrl ?? DEFAULT_RAILWAY_API_URL,
1479
- token: String(credentials.token ?? ""),
1480
- init: {
1481
- method: "POST",
1482
- headers: { "Content-Type": "application/json" },
1483
- body: JSON.stringify({ query: "{ __typename }" })
1484
- },
1485
- ...fetchImpl ? { fetchImpl } : {}
1486
- });
1479
+ if (!cfg.environmentId || !cfg.serviceId) {
1480
+ return { ok: false, reason: "railway: environmentId and serviceId are required to validate" };
1481
+ }
1482
+ const config = {
1483
+ environmentId: cfg.environmentId,
1484
+ serviceId: cfg.serviceId,
1485
+ serviceNameById: cfg.serviceNameById ?? {},
1486
+ ...cfg.apiUrl ? { apiUrl: cfg.apiUrl } : {}
1487
+ };
1488
+ try {
1489
+ await resolveLatestRailwayDeploymentId(config, String(credentials.token ?? ""), fetchImpl);
1490
+ return { ok: true };
1491
+ } catch (err) {
1492
+ return { ok: false, reason: `railway auth check failed: ${err.message}` };
1493
+ }
1487
1494
  }
1488
1495
  },
1489
1496
  firebase: {
@@ -2454,4 +2461,4 @@ export {
2454
2461
  resolveHost,
2455
2462
  startDaemon
2456
2463
  };
2457
- //# sourceMappingURL=chunk-AOLXZWRU.js.map
2464
+ //# sourceMappingURL=chunk-2AJHEZIB.js.map