@neat.is/core 0.9.5-dev.20260824 → 0.9.5

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.
@@ -20,7 +20,7 @@ import {
20
20
  startStalenessLoop,
21
21
  touchLastSeen,
22
22
  writeAtomically
23
- } from "./chunk-IVVF37OU.js";
23
+ } from "./chunk-DGAI4VOE.js";
24
24
  import {
25
25
  assertBindAuthority,
26
26
  buildOtelReceiver,
@@ -891,4 +891,4 @@ export {
891
891
  resolveHost,
892
892
  startDaemon
893
893
  };
894
- //# sourceMappingURL=chunk-TGCWMMF6.js.map
894
+ //# sourceMappingURL=chunk-4SLKQNG7.js.map
@@ -19575,8 +19575,219 @@ function createCloudRunConnector(graph, config = {}) {
19575
19575
  };
19576
19576
  }
19577
19577
 
19578
+ // src/connectors/gcp-lb/client.ts
19579
+ function gcpLbRequestLogName(projectId) {
19580
+ return `projects/${projectId}/logs/requests`;
19581
+ }
19582
+ function buildGcpLbEntriesFilter(projectId, sinceIso) {
19583
+ return [
19584
+ `logName = "${gcpLbRequestLogName(projectId)}"`,
19585
+ `resource.type = "${GCP_LB_RESOURCE_TYPE}"`,
19586
+ 'httpRequest.requestMethod != ""',
19587
+ `timestamp >= "${sinceIso}"`
19588
+ ].join(" AND ");
19589
+ }
19590
+ var GCP_LB_RESOURCE_TYPE = "http_load_balancer";
19591
+ var DEFAULT_LOOKBACK_MS3 = 24 * 60 * 60 * 1e3;
19592
+ var ENTRIES_LIST_URL3 = "https://logging.googleapis.com/v2/entries:list";
19593
+ var PAGE_SIZE3 = 1e3;
19594
+ var MAX_PAGES3 = 20;
19595
+ async function fetchGcpLbRequestLogEntries(creds, sinceIso, apiUrl = ENTRIES_LIST_URL3) {
19596
+ const filter = buildGcpLbEntriesFilter(creds.projectId, sinceIso);
19597
+ const out = [];
19598
+ let pageToken;
19599
+ for (let page = 0; page < MAX_PAGES3; page++) {
19600
+ const body = {
19601
+ resourceNames: [`projects/${creds.projectId}`],
19602
+ filter,
19603
+ orderBy: "timestamp asc",
19604
+ pageSize: PAGE_SIZE3,
19605
+ ...pageToken ? { pageToken } : {}
19606
+ };
19607
+ const res = await junctionFetch(
19608
+ apiUrl,
19609
+ {
19610
+ method: "POST",
19611
+ headers: {
19612
+ ...bearerAuthHeader(creds.accessToken),
19613
+ "Content-Type": "application/json"
19614
+ },
19615
+ body: JSON.stringify(body)
19616
+ },
19617
+ // accountKey: the GCP project id — one customer's Cloud Logging quota is
19618
+ // scoped per GCP project (ADR-131's per-(provider, accountKey) rate-limit
19619
+ // bucket), the same key Cloud Run's and Firebase's connectors use.
19620
+ { provider: "gcp-lb", accountKey: creds.projectId }
19621
+ );
19622
+ if (!res.ok) {
19623
+ throw new Error(`Cloud Logging entries.list failed: ${res.status} ${res.statusText}`);
19624
+ }
19625
+ const json = await res.json();
19626
+ if (Array.isArray(json.entries)) out.push(...json.entries);
19627
+ if (!json.nextPageToken) break;
19628
+ pageToken = json.nextPageToken;
19629
+ }
19630
+ return out;
19631
+ }
19632
+
19633
+ // src/connectors/gcp-lb/types.ts
19634
+ function readGcpLbCredentials(raw) {
19635
+ const projectId = raw["projectId"];
19636
+ const accessToken = raw["accessToken"];
19637
+ if (typeof projectId !== "string" || projectId.length === 0) {
19638
+ throw new Error("gcp-lb connector: credentials.projectId must be a non-empty string");
19639
+ }
19640
+ if (typeof accessToken !== "string" || accessToken.length === 0) {
19641
+ throw new Error("gcp-lb connector: credentials.accessToken must be a non-empty string");
19642
+ }
19643
+ return { projectId, accessToken };
19644
+ }
19645
+ var GCP_LB_TARGET_KIND = "http_load_balancer";
19646
+ var FIELD_SEP3 = "\0";
19647
+ function packGcpLbTargetName(identity) {
19648
+ return [identity.backendServiceName, identity.method, identity.path].join(FIELD_SEP3);
19649
+ }
19650
+ function parseGcpLbTargetName(targetName) {
19651
+ const firstSep = targetName.indexOf(FIELD_SEP3);
19652
+ if (firstSep === -1) return null;
19653
+ const backendServiceName = targetName.slice(0, firstSep);
19654
+ const rest = targetName.slice(firstSep + 1);
19655
+ const secondSep = rest.indexOf(FIELD_SEP3);
19656
+ if (secondSep === -1) return null;
19657
+ const method = rest.slice(0, secondSep);
19658
+ const path72 = rest.slice(secondSep + 1);
19659
+ if (!backendServiceName || !method || !path72) return null;
19660
+ return { backendServiceName, method, path: path72 };
19661
+ }
19662
+
19663
+ // src/connectors/gcp-lb/map.ts
19664
+ var GCP_LB_RESOURCE_TYPE2 = "http_load_balancer";
19665
+ function pathFromRequestUrl3(requestUrl) {
19666
+ if (typeof requestUrl !== "string" || requestUrl.length === 0) return null;
19667
+ if (requestUrl.startsWith("/")) {
19668
+ const withoutQuery = requestUrl.split("?")[0];
19669
+ return withoutQuery && withoutQuery.length > 0 ? withoutQuery : "/";
19670
+ }
19671
+ try {
19672
+ const candidate = requestUrl.startsWith("//") ? `https:${requestUrl}` : requestUrl;
19673
+ const parsed = new URL(candidate);
19674
+ return parsed.pathname || "/";
19675
+ } catch {
19676
+ return null;
19677
+ }
19678
+ }
19679
+ var ERROR_STATUS_THRESHOLD5 = 500;
19680
+ function mapLogEntryToSignal3(entry) {
19681
+ if (!entry || typeof entry !== "object") return null;
19682
+ if (entry.resource?.type !== GCP_LB_RESOURCE_TYPE2) return null;
19683
+ const backendServiceName = entry.resource?.labels?.["backend_service_name"];
19684
+ if (typeof backendServiceName !== "string" || backendServiceName.length === 0) return null;
19685
+ const req = entry.httpRequest;
19686
+ if (!req) return null;
19687
+ if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
19688
+ const method = req.requestMethod.toUpperCase();
19689
+ const path72 = pathFromRequestUrl3(req.requestUrl);
19690
+ if (path72 === null) return null;
19691
+ const timestamp = entry.timestamp;
19692
+ if (typeof timestamp !== "string" || timestamp.length === 0) return null;
19693
+ const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD5;
19694
+ return {
19695
+ targetKind: GCP_LB_TARGET_KIND,
19696
+ targetName: packGcpLbTargetName({ backendServiceName, method, path: path72 }),
19697
+ callCount: 1,
19698
+ errorCount: isError ? 1 : 0,
19699
+ lastObservedIso: timestamp
19700
+ };
19701
+ }
19702
+ function mapLogEntriesToSignals3(entries) {
19703
+ const out = [];
19704
+ for (const entry of entries) {
19705
+ const signal = mapLogEntryToSignal3(entry);
19706
+ if (signal) out.push(signal);
19707
+ }
19708
+ return out;
19709
+ }
19710
+
19711
+ // src/connectors/gcp-lb/resolve.ts
19712
+ import { EdgeType as EdgeType32, NodeType as NodeType38, infraId as infraId25 } from "@neat.is/types";
19713
+ var GCP_LB_BACKEND_INFRA_KIND = "gcp-lb-backend";
19714
+ function findMatchingRouteNode3(graph, serviceName, method, normalizedPath) {
19715
+ let found = null;
19716
+ graph.forEachNode((_id, attrs) => {
19717
+ if (found) return;
19718
+ const node = attrs;
19719
+ if (node.type !== NodeType38.RouteNode) return;
19720
+ const route = attrs;
19721
+ if (route.service !== serviceName || !route.pathTemplate) return;
19722
+ if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
19723
+ const routeMethod = route.method.toUpperCase();
19724
+ if (routeMethod !== "ALL" && routeMethod !== method) return;
19725
+ found = route.id;
19726
+ });
19727
+ return found;
19728
+ }
19729
+ function createGcpLbResolveTarget(graph, config) {
19730
+ return (signal) => {
19731
+ if (signal.targetKind !== GCP_LB_TARGET_KIND) return null;
19732
+ const identity = parseGcpLbTargetName(signal.targetName);
19733
+ if (!identity) return null;
19734
+ const { backendServiceName, method, path: path72 } = identity;
19735
+ const mappedService = config.backendServiceMap?.[backendServiceName];
19736
+ if (mappedService) {
19737
+ const routeNodeId = findMatchingRouteNode3(
19738
+ graph,
19739
+ mappedService,
19740
+ method,
19741
+ normalizePathTemplate(path72)
19742
+ );
19743
+ if (routeNodeId) {
19744
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: EdgeType32.CALLS };
19745
+ }
19746
+ }
19747
+ return {
19748
+ targetNodeId: infraId25(GCP_LB_BACKEND_INFRA_KIND, backendServiceName),
19749
+ serviceName: mappedService ?? backendServiceName,
19750
+ edgeType: EdgeType32.CALLS,
19751
+ ensureInfraNode: {
19752
+ kind: GCP_LB_BACKEND_INFRA_KIND,
19753
+ name: backendServiceName,
19754
+ provider: "gcp-lb"
19755
+ }
19756
+ };
19757
+ };
19758
+ }
19759
+
19760
+ // src/connectors/gcp-lb/index.ts
19761
+ var GcpLbConnector = class {
19762
+ constructor(config = {}) {
19763
+ this.config = config;
19764
+ }
19765
+ config;
19766
+ provider = "gcp-lb";
19767
+ async poll(ctx) {
19768
+ const creds = readGcpLbCredentials(ctx.credentials);
19769
+ const maxLookbackMs = this.config.maxLookbackMs ?? DEFAULT_LOOKBACK_MS3;
19770
+ const sinceIso = boundedSinceIso2(ctx.since, /* @__PURE__ */ new Date(), maxLookbackMs);
19771
+ const entries = await fetchGcpLbRequestLogEntries(creds, sinceIso, this.config.apiUrl);
19772
+ return mapLogEntriesToSignals3(entries);
19773
+ }
19774
+ };
19775
+ function boundedSinceIso2(since, now, maxLookbackMs) {
19776
+ const floor = new Date(now.getTime() - maxLookbackMs);
19777
+ if (!since) return floor.toISOString();
19778
+ const sinceMs = new Date(since).getTime();
19779
+ if (Number.isNaN(sinceMs)) return floor.toISOString();
19780
+ return sinceMs < floor.getTime() ? floor.toISOString() : new Date(sinceMs).toISOString();
19781
+ }
19782
+ function createGcpLbConnector(graph, config = {}) {
19783
+ return {
19784
+ connector: new GcpLbConnector(config),
19785
+ resolveTarget: createGcpLbResolveTarget(graph, config)
19786
+ };
19787
+ }
19788
+
19578
19789
  // src/connectors/render/index.ts
19579
- import { EdgeType as EdgeType32, NodeType as NodeType38 } from "@neat.is/types";
19790
+ import { EdgeType as EdgeType33, NodeType as NodeType39 } from "@neat.is/types";
19580
19791
 
19581
19792
  // src/connectors/render/types.ts
19582
19793
  function readRenderToken(credentials) {
@@ -19652,7 +19863,7 @@ function buildRenderRouteIndex(graph, serviceName) {
19652
19863
  const out = [];
19653
19864
  graph.forEachNode((_id, attrs) => {
19654
19865
  const node = attrs;
19655
- if (node.type !== NodeType38.RouteNode) return;
19866
+ if (node.type !== NodeType39.RouteNode) return;
19656
19867
  const route = attrs;
19657
19868
  if (route.service !== serviceName) return;
19658
19869
  out.push({
@@ -19737,7 +19948,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
19737
19948
  function createRenderResolveTarget(config) {
19738
19949
  return (signal) => {
19739
19950
  if (signal.targetKind === ROUTE_TARGET_KIND2) {
19740
- return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: EdgeType32.CALLS };
19951
+ return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: EdgeType33.CALLS };
19741
19952
  }
19742
19953
  return null;
19743
19954
  };
@@ -19866,21 +20077,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
19866
20077
  }
19867
20078
 
19868
20079
  // src/connectors/planetscale/resolve.ts
19869
- import { EdgeType as EdgeType33, infraId as infraId25 } from "@neat.is/types";
20080
+ import { EdgeType as EdgeType34, infraId as infraId26 } from "@neat.is/types";
19870
20081
  var PLANETSCALE_DATABASE_KIND = "planetscale-database";
19871
20082
  function createPlanetscaleResolveTarget(graph, config) {
19872
20083
  const databaseName = `${config.organization}/${config.database}`;
19873
20084
  return (signal, _ctx) => {
19874
20085
  if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
19875
- const tableId = infraId25("sql-table", signal.targetName);
20086
+ const tableId = infraId26("sql-table", signal.targetName);
19876
20087
  if (graph.hasNode(tableId)) {
19877
- return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: EdgeType33.CALLS };
20088
+ return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: EdgeType34.CALLS };
19878
20089
  }
19879
- const providerId = infraId25(PLANETSCALE_DATABASE_KIND, databaseName);
20090
+ const providerId = infraId26(PLANETSCALE_DATABASE_KIND, databaseName);
19880
20091
  return {
19881
20092
  targetNodeId: providerId,
19882
20093
  serviceName: config.serviceName,
19883
- edgeType: EdgeType33.CALLS,
20094
+ edgeType: EdgeType34.CALLS,
19884
20095
  ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
19885
20096
  };
19886
20097
  };
@@ -19939,13 +20150,13 @@ function isTransientFailure(err) {
19939
20150
  if (code && INTERNAL_ERROR_CODE.test(code)) return true;
19940
20151
  return false;
19941
20152
  }
19942
- var FIELD_SEP3 = "\0";
20153
+ var FIELD_SEP4 = "\0";
19943
20154
  var EAS_TARGET_KIND = "eas-build";
19944
20155
  function packEasTargetName(identity) {
19945
- return [identity.serviceName, identity.phase].join(FIELD_SEP3);
20156
+ return [identity.serviceName, identity.phase].join(FIELD_SEP4);
19946
20157
  }
19947
20158
  function parseEasTargetName(targetName) {
19948
- const sep = targetName.indexOf(FIELD_SEP3);
20159
+ const sep = targetName.indexOf(FIELD_SEP4);
19949
20160
  if (sep === -1) return null;
19950
20161
  const serviceName = targetName.slice(0, sep);
19951
20162
  const phase = targetName.slice(sep + 1);
@@ -20136,7 +20347,7 @@ function mapBuildsToSignals(builds, serviceName) {
20136
20347
  }
20137
20348
 
20138
20349
  // src/connectors/eas/resolve.ts
20139
- import { EdgeType as EdgeType34, NodeType as NodeType39, parseFileId as parseFileId3 } from "@neat.is/types";
20350
+ import { EdgeType as EdgeType35, NodeType as NodeType40, parseFileId as parseFileId3 } from "@neat.is/types";
20140
20351
  var NO_ENV2 = "unknown";
20141
20352
  var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
20142
20353
  var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
@@ -20152,7 +20363,7 @@ function configBasenamesForPhase(phase) {
20152
20363
  function configNodeService(graph, configNodeId) {
20153
20364
  for (const edgeId of graph.inboundEdges(configNodeId)) {
20154
20365
  const edge = graph.getEdgeAttributes(edgeId);
20155
- if (edge.type !== EdgeType34.CONFIGURED_BY) continue;
20366
+ if (edge.type !== EdgeType35.CONFIGURED_BY) continue;
20156
20367
  const parsed = parseFileId3(edge.source);
20157
20368
  if (parsed) return parsed.service;
20158
20369
  }
@@ -20164,7 +20375,7 @@ function findConfigNode(graph, basenames, serviceName) {
20164
20375
  graph.forEachNode((id, attrs) => {
20165
20376
  if (scoped) return;
20166
20377
  const node = attrs;
20167
- if (node.type !== NodeType39.ConfigNode) return;
20378
+ if (node.type !== NodeType40.ConfigNode) return;
20168
20379
  if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
20169
20380
  if (anyMatch === null) anyMatch = id;
20170
20381
  if (configNodeService(graph, id) === serviceName) scoped = id;
@@ -20181,13 +20392,13 @@ function createEasResolveTarget(graph) {
20181
20392
  if (basenames.length > 0) {
20182
20393
  const configNodeId = findConfigNode(graph, basenames, serviceName);
20183
20394
  if (configNodeId) {
20184
- return { targetNodeId: configNodeId, serviceName, edgeType: EdgeType34.CALLS };
20395
+ return { targetNodeId: configNodeId, serviceName, edgeType: EdgeType35.CALLS };
20185
20396
  }
20186
20397
  }
20187
20398
  return {
20188
20399
  targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
20189
20400
  serviceName,
20190
- edgeType: EdgeType34.CALLS
20401
+ edgeType: EdgeType35.CALLS
20191
20402
  };
20192
20403
  };
20193
20404
  }
@@ -20199,7 +20410,7 @@ function isBuildSince(build, sinceIso) {
20199
20410
  if (Number.isNaN(t) || Number.isNaN(s)) return true;
20200
20411
  return t > s;
20201
20412
  }
20202
- function boundedSinceIso2(since, now, maxLookbackMs) {
20413
+ function boundedSinceIso3(since, now, maxLookbackMs) {
20203
20414
  const floor = new Date(now.getTime() - maxLookbackMs);
20204
20415
  if (!since) return floor.toISOString();
20205
20416
  const sinceMs = new Date(since).getTime();
@@ -20218,7 +20429,7 @@ var EasConnector = class {
20218
20429
  const creds = readEasCredentials(ctx.credentials);
20219
20430
  const serviceName = this.config.serviceName ?? this.config.appId;
20220
20431
  const maxLookbackMs = this.config.maxLookbackMs ?? DEFAULT_MAX_LOOKBACK_MS6;
20221
- const sinceIso = boundedSinceIso2(ctx.since, /* @__PURE__ */ new Date(), maxLookbackMs);
20432
+ const sinceIso = boundedSinceIso3(ctx.since, /* @__PURE__ */ new Date(), maxLookbackMs);
20222
20433
  const builds = await fetchErroredBuilds(creds.token, this.config, this.fetchImpl);
20223
20434
  const fresh = builds.filter((b) => isBuildSince(b, sinceIso));
20224
20435
  const maxLogBytes = this.config.maxLogBytes ?? DEFAULT_MAX_LOG_BYTES;
@@ -20449,6 +20660,40 @@ var PROVIDER_DISPATCH = {
20449
20660
  });
20450
20661
  }
20451
20662
  },
20663
+ "gcp-lb": {
20664
+ provider: "gcp-lb",
20665
+ // Like cloud-run, gcp-lb reads both projectId and accessToken from the
20666
+ // credential; the single-string form maps to the secret (the token), and the
20667
+ // required-fields check below catches a projectId that was never supplied.
20668
+ primaryCredentialKey: "accessToken",
20669
+ requiredCredentialFields: ["projectId", "accessToken"],
20670
+ requiredOptionFields: [],
20671
+ build(graph, options) {
20672
+ return createGcpLbConnector(graph, options);
20673
+ },
20674
+ // POST entries:list with pageSize 1 — the exact surface poll() reads, so the
20675
+ // probe checks the actual `logging.logEntries.list` permission the connector
20676
+ // needs. This is the same Cloud Logging read-verdict cloud-run's validate
20677
+ // uses (a lighter GET on logs.list would instead check `logging.logs.list`,
20678
+ // falsely rejecting a correctly-scoped custom role carrying only
20679
+ // `logging.logEntries.list`). A 2xx means the token can list log entries;
20680
+ // 401/403 means the provider rejected it.
20681
+ validate({ credentials, fetchImpl }) {
20682
+ const projectId = String(credentials.projectId ?? "");
20683
+ return authProbe({
20684
+ provider: "gcp-lb",
20685
+ accountKey: projectId || "validate",
20686
+ url: "https://logging.googleapis.com/v2/entries:list",
20687
+ token: String(credentials.accessToken ?? ""),
20688
+ init: {
20689
+ method: "POST",
20690
+ headers: { "Content-Type": "application/json" },
20691
+ body: JSON.stringify({ resourceNames: [`projects/${projectId}`], pageSize: 1 })
20692
+ },
20693
+ ...fetchImpl ? { fetchImpl } : {}
20694
+ });
20695
+ }
20696
+ },
20452
20697
  render: {
20453
20698
  provider: "render",
20454
20699
  primaryCredentialKey: "token",
@@ -21798,4 +22043,4 @@ export {
21798
22043
  deprovisionConnector,
21799
22044
  buildApi
21800
22045
  };
21801
- //# sourceMappingURL=chunk-IVVF37OU.js.map
22046
+ //# sourceMappingURL=chunk-DGAI4VOE.js.map