@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.
package/dist/neatd.js CHANGED
@@ -2,11 +2,11 @@
2
2
  import {
3
3
  reconcileDaemonRecordSync,
4
4
  startDaemon
5
- } from "./chunk-TGCWMMF6.js";
5
+ } from "./chunk-4SLKQNG7.js";
6
6
  import {
7
7
  listProjects,
8
8
  registryPath
9
- } from "./chunk-IVVF37OU.js";
9
+ } from "./chunk-DGAI4VOE.js";
10
10
  import {
11
11
  BindAuthorityError,
12
12
  __require
package/dist/server.cjs CHANGED
@@ -787,7 +787,7 @@ function getGraph(project = DEFAULT_PROJECT) {
787
787
  init_cjs_shims();
788
788
  var import_fastify2 = __toESM(require("fastify"), 1);
789
789
  var import_cors = __toESM(require("@fastify/cors"), 1);
790
- var import_types93 = require("@neat.is/types");
790
+ var import_types97 = require("@neat.is/types");
791
791
 
792
792
  // src/extend/index.ts
793
793
  init_cjs_shims();
@@ -19799,9 +19799,229 @@ function createCloudRunConnector(graph, config = {}) {
19799
19799
  };
19800
19800
  }
19801
19801
 
19802
+ // src/connectors/gcp-lb/index.ts
19803
+ init_cjs_shims();
19804
+
19805
+ // src/connectors/gcp-lb/client.ts
19806
+ init_cjs_shims();
19807
+ function gcpLbRequestLogName(projectId) {
19808
+ return `projects/${projectId}/logs/requests`;
19809
+ }
19810
+ function buildGcpLbEntriesFilter(projectId, sinceIso) {
19811
+ return [
19812
+ `logName = "${gcpLbRequestLogName(projectId)}"`,
19813
+ `resource.type = "${GCP_LB_RESOURCE_TYPE}"`,
19814
+ 'httpRequest.requestMethod != ""',
19815
+ `timestamp >= "${sinceIso}"`
19816
+ ].join(" AND ");
19817
+ }
19818
+ var GCP_LB_RESOURCE_TYPE = "http_load_balancer";
19819
+ var DEFAULT_LOOKBACK_MS3 = 24 * 60 * 60 * 1e3;
19820
+ var ENTRIES_LIST_URL3 = "https://logging.googleapis.com/v2/entries:list";
19821
+ var PAGE_SIZE3 = 1e3;
19822
+ var MAX_PAGES3 = 20;
19823
+ async function fetchGcpLbRequestLogEntries(creds, sinceIso, apiUrl = ENTRIES_LIST_URL3) {
19824
+ const filter = buildGcpLbEntriesFilter(creds.projectId, sinceIso);
19825
+ const out = [];
19826
+ let pageToken;
19827
+ for (let page = 0; page < MAX_PAGES3; page++) {
19828
+ const body = {
19829
+ resourceNames: [`projects/${creds.projectId}`],
19830
+ filter,
19831
+ orderBy: "timestamp asc",
19832
+ pageSize: PAGE_SIZE3,
19833
+ ...pageToken ? { pageToken } : {}
19834
+ };
19835
+ const res = await junctionFetch(
19836
+ apiUrl,
19837
+ {
19838
+ method: "POST",
19839
+ headers: {
19840
+ ...bearerAuthHeader(creds.accessToken),
19841
+ "Content-Type": "application/json"
19842
+ },
19843
+ body: JSON.stringify(body)
19844
+ },
19845
+ // accountKey: the GCP project id — one customer's Cloud Logging quota is
19846
+ // scoped per GCP project (ADR-131's per-(provider, accountKey) rate-limit
19847
+ // bucket), the same key Cloud Run's and Firebase's connectors use.
19848
+ { provider: "gcp-lb", accountKey: creds.projectId }
19849
+ );
19850
+ if (!res.ok) {
19851
+ throw new Error(`Cloud Logging entries.list failed: ${res.status} ${res.statusText}`);
19852
+ }
19853
+ const json = await res.json();
19854
+ if (Array.isArray(json.entries)) out.push(...json.entries);
19855
+ if (!json.nextPageToken) break;
19856
+ pageToken = json.nextPageToken;
19857
+ }
19858
+ return out;
19859
+ }
19860
+
19861
+ // src/connectors/gcp-lb/map.ts
19862
+ init_cjs_shims();
19863
+
19864
+ // src/connectors/gcp-lb/types.ts
19865
+ init_cjs_shims();
19866
+ function readGcpLbCredentials(raw) {
19867
+ const projectId = raw["projectId"];
19868
+ const accessToken = raw["accessToken"];
19869
+ if (typeof projectId !== "string" || projectId.length === 0) {
19870
+ throw new Error("gcp-lb connector: credentials.projectId must be a non-empty string");
19871
+ }
19872
+ if (typeof accessToken !== "string" || accessToken.length === 0) {
19873
+ throw new Error("gcp-lb connector: credentials.accessToken must be a non-empty string");
19874
+ }
19875
+ return { projectId, accessToken };
19876
+ }
19877
+ var GCP_LB_TARGET_KIND = "http_load_balancer";
19878
+ var FIELD_SEP3 = "\0";
19879
+ function packGcpLbTargetName(identity) {
19880
+ return [identity.backendServiceName, identity.method, identity.path].join(FIELD_SEP3);
19881
+ }
19882
+ function parseGcpLbTargetName(targetName) {
19883
+ const firstSep = targetName.indexOf(FIELD_SEP3);
19884
+ if (firstSep === -1) return null;
19885
+ const backendServiceName = targetName.slice(0, firstSep);
19886
+ const rest = targetName.slice(firstSep + 1);
19887
+ const secondSep = rest.indexOf(FIELD_SEP3);
19888
+ if (secondSep === -1) return null;
19889
+ const method = rest.slice(0, secondSep);
19890
+ const path76 = rest.slice(secondSep + 1);
19891
+ if (!backendServiceName || !method || !path76) return null;
19892
+ return { backendServiceName, method, path: path76 };
19893
+ }
19894
+
19895
+ // src/connectors/gcp-lb/map.ts
19896
+ var GCP_LB_RESOURCE_TYPE2 = "http_load_balancer";
19897
+ function pathFromRequestUrl3(requestUrl) {
19898
+ if (typeof requestUrl !== "string" || requestUrl.length === 0) return null;
19899
+ if (requestUrl.startsWith("/")) {
19900
+ const withoutQuery = requestUrl.split("?")[0];
19901
+ return withoutQuery && withoutQuery.length > 0 ? withoutQuery : "/";
19902
+ }
19903
+ try {
19904
+ const candidate = requestUrl.startsWith("//") ? `https:${requestUrl}` : requestUrl;
19905
+ const parsed = new URL(candidate);
19906
+ return parsed.pathname || "/";
19907
+ } catch {
19908
+ return null;
19909
+ }
19910
+ }
19911
+ var ERROR_STATUS_THRESHOLD5 = 500;
19912
+ function mapLogEntryToSignal3(entry) {
19913
+ if (!entry || typeof entry !== "object") return null;
19914
+ if (entry.resource?.type !== GCP_LB_RESOURCE_TYPE2) return null;
19915
+ const backendServiceName = entry.resource?.labels?.["backend_service_name"];
19916
+ if (typeof backendServiceName !== "string" || backendServiceName.length === 0) return null;
19917
+ const req = entry.httpRequest;
19918
+ if (!req) return null;
19919
+ if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
19920
+ const method = req.requestMethod.toUpperCase();
19921
+ const path76 = pathFromRequestUrl3(req.requestUrl);
19922
+ if (path76 === null) return null;
19923
+ const timestamp = entry.timestamp;
19924
+ if (typeof timestamp !== "string" || timestamp.length === 0) return null;
19925
+ const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD5;
19926
+ return {
19927
+ targetKind: GCP_LB_TARGET_KIND,
19928
+ targetName: packGcpLbTargetName({ backendServiceName, method, path: path76 }),
19929
+ callCount: 1,
19930
+ errorCount: isError ? 1 : 0,
19931
+ lastObservedIso: timestamp
19932
+ };
19933
+ }
19934
+ function mapLogEntriesToSignals3(entries) {
19935
+ const out = [];
19936
+ for (const entry of entries) {
19937
+ const signal = mapLogEntryToSignal3(entry);
19938
+ if (signal) out.push(signal);
19939
+ }
19940
+ return out;
19941
+ }
19942
+
19943
+ // src/connectors/gcp-lb/resolve.ts
19944
+ init_cjs_shims();
19945
+ var import_types82 = require("@neat.is/types");
19946
+ var GCP_LB_BACKEND_INFRA_KIND = "gcp-lb-backend";
19947
+ function findMatchingRouteNode3(graph, serviceName, method, normalizedPath) {
19948
+ let found = null;
19949
+ graph.forEachNode((_id, attrs) => {
19950
+ if (found) return;
19951
+ const node = attrs;
19952
+ if (node.type !== import_types82.NodeType.RouteNode) return;
19953
+ const route = attrs;
19954
+ if (route.service !== serviceName || !route.pathTemplate) return;
19955
+ if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
19956
+ const routeMethod = route.method.toUpperCase();
19957
+ if (routeMethod !== "ALL" && routeMethod !== method) return;
19958
+ found = route.id;
19959
+ });
19960
+ return found;
19961
+ }
19962
+ function createGcpLbResolveTarget(graph, config) {
19963
+ return (signal) => {
19964
+ if (signal.targetKind !== GCP_LB_TARGET_KIND) return null;
19965
+ const identity = parseGcpLbTargetName(signal.targetName);
19966
+ if (!identity) return null;
19967
+ const { backendServiceName, method, path: path76 } = identity;
19968
+ const mappedService = config.backendServiceMap?.[backendServiceName];
19969
+ if (mappedService) {
19970
+ const routeNodeId = findMatchingRouteNode3(
19971
+ graph,
19972
+ mappedService,
19973
+ method,
19974
+ normalizePathTemplate(path76)
19975
+ );
19976
+ if (routeNodeId) {
19977
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types82.EdgeType.CALLS };
19978
+ }
19979
+ }
19980
+ return {
19981
+ targetNodeId: (0, import_types82.infraId)(GCP_LB_BACKEND_INFRA_KIND, backendServiceName),
19982
+ serviceName: mappedService ?? backendServiceName,
19983
+ edgeType: import_types82.EdgeType.CALLS,
19984
+ ensureInfraNode: {
19985
+ kind: GCP_LB_BACKEND_INFRA_KIND,
19986
+ name: backendServiceName,
19987
+ provider: "gcp-lb"
19988
+ }
19989
+ };
19990
+ };
19991
+ }
19992
+
19993
+ // src/connectors/gcp-lb/index.ts
19994
+ var GcpLbConnector = class {
19995
+ constructor(config = {}) {
19996
+ this.config = config;
19997
+ }
19998
+ config;
19999
+ provider = "gcp-lb";
20000
+ async poll(ctx) {
20001
+ const creds = readGcpLbCredentials(ctx.credentials);
20002
+ const maxLookbackMs = this.config.maxLookbackMs ?? DEFAULT_LOOKBACK_MS3;
20003
+ const sinceIso = boundedSinceIso2(ctx.since, /* @__PURE__ */ new Date(), maxLookbackMs);
20004
+ const entries = await fetchGcpLbRequestLogEntries(creds, sinceIso, this.config.apiUrl);
20005
+ return mapLogEntriesToSignals3(entries);
20006
+ }
20007
+ };
20008
+ function boundedSinceIso2(since, now, maxLookbackMs) {
20009
+ const floor = new Date(now.getTime() - maxLookbackMs);
20010
+ if (!since) return floor.toISOString();
20011
+ const sinceMs = new Date(since).getTime();
20012
+ if (Number.isNaN(sinceMs)) return floor.toISOString();
20013
+ return sinceMs < floor.getTime() ? floor.toISOString() : new Date(sinceMs).toISOString();
20014
+ }
20015
+ function createGcpLbConnector(graph, config = {}) {
20016
+ return {
20017
+ connector: new GcpLbConnector(config),
20018
+ resolveTarget: createGcpLbResolveTarget(graph, config)
20019
+ };
20020
+ }
20021
+
19802
20022
  // src/connectors/render/index.ts
19803
20023
  init_cjs_shims();
19804
- var import_types81 = require("@neat.is/types");
20024
+ var import_types85 = require("@neat.is/types");
19805
20025
 
19806
20026
  // src/connectors/render/types.ts
19807
20027
  init_cjs_shims();
@@ -19879,7 +20099,7 @@ function buildRenderRouteIndex(graph, serviceName) {
19879
20099
  const out = [];
19880
20100
  graph.forEachNode((_id, attrs) => {
19881
20101
  const node = attrs;
19882
- if (node.type !== import_types81.NodeType.RouteNode) return;
20102
+ if (node.type !== import_types85.NodeType.RouteNode) return;
19883
20103
  const route = attrs;
19884
20104
  if (route.service !== serviceName) return;
19885
20105
  out.push({
@@ -19964,7 +20184,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
19964
20184
  function createRenderResolveTarget(config) {
19965
20185
  return (signal) => {
19966
20186
  if (signal.targetKind === ROUTE_TARGET_KIND2) {
19967
- return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types81.EdgeType.CALLS };
20187
+ return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types85.EdgeType.CALLS };
19968
20188
  }
19969
20189
  return null;
19970
20190
  };
@@ -20102,21 +20322,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
20102
20322
 
20103
20323
  // src/connectors/planetscale/resolve.ts
20104
20324
  init_cjs_shims();
20105
- var import_types85 = require("@neat.is/types");
20325
+ var import_types89 = require("@neat.is/types");
20106
20326
  var PLANETSCALE_DATABASE_KIND = "planetscale-database";
20107
20327
  function createPlanetscaleResolveTarget(graph, config) {
20108
20328
  const databaseName = `${config.organization}/${config.database}`;
20109
20329
  return (signal, _ctx) => {
20110
20330
  if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
20111
- const tableId = (0, import_types85.infraId)("sql-table", signal.targetName);
20331
+ const tableId = (0, import_types89.infraId)("sql-table", signal.targetName);
20112
20332
  if (graph.hasNode(tableId)) {
20113
- return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types85.EdgeType.CALLS };
20333
+ return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types89.EdgeType.CALLS };
20114
20334
  }
20115
- const providerId = (0, import_types85.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
20335
+ const providerId = (0, import_types89.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
20116
20336
  return {
20117
20337
  targetNodeId: providerId,
20118
20338
  serviceName: config.serviceName,
20119
- edgeType: import_types85.EdgeType.CALLS,
20339
+ edgeType: import_types89.EdgeType.CALLS,
20120
20340
  ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
20121
20341
  };
20122
20342
  };
@@ -20182,13 +20402,13 @@ function isTransientFailure(err) {
20182
20402
  if (code && INTERNAL_ERROR_CODE.test(code)) return true;
20183
20403
  return false;
20184
20404
  }
20185
- var FIELD_SEP3 = "\0";
20405
+ var FIELD_SEP4 = "\0";
20186
20406
  var EAS_TARGET_KIND = "eas-build";
20187
20407
  function packEasTargetName(identity) {
20188
- return [identity.serviceName, identity.phase].join(FIELD_SEP3);
20408
+ return [identity.serviceName, identity.phase].join(FIELD_SEP4);
20189
20409
  }
20190
20410
  function parseEasTargetName(targetName) {
20191
- const sep = targetName.indexOf(FIELD_SEP3);
20411
+ const sep = targetName.indexOf(FIELD_SEP4);
20192
20412
  if (sep === -1) return null;
20193
20413
  const serviceName = targetName.slice(0, sep);
20194
20414
  const phase = targetName.slice(sep + 1);
@@ -20381,7 +20601,7 @@ function mapBuildsToSignals(builds, serviceName) {
20381
20601
 
20382
20602
  // src/connectors/eas/resolve.ts
20383
20603
  init_cjs_shims();
20384
- var import_types90 = require("@neat.is/types");
20604
+ var import_types94 = require("@neat.is/types");
20385
20605
  var NO_ENV2 = "unknown";
20386
20606
  var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
20387
20607
  var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
@@ -20397,8 +20617,8 @@ function configBasenamesForPhase(phase) {
20397
20617
  function configNodeService(graph, configNodeId) {
20398
20618
  for (const edgeId of graph.inboundEdges(configNodeId)) {
20399
20619
  const edge = graph.getEdgeAttributes(edgeId);
20400
- if (edge.type !== import_types90.EdgeType.CONFIGURED_BY) continue;
20401
- const parsed = (0, import_types90.parseFileId)(edge.source);
20620
+ if (edge.type !== import_types94.EdgeType.CONFIGURED_BY) continue;
20621
+ const parsed = (0, import_types94.parseFileId)(edge.source);
20402
20622
  if (parsed) return parsed.service;
20403
20623
  }
20404
20624
  return null;
@@ -20409,7 +20629,7 @@ function findConfigNode(graph, basenames, serviceName) {
20409
20629
  graph.forEachNode((id, attrs) => {
20410
20630
  if (scoped) return;
20411
20631
  const node = attrs;
20412
- if (node.type !== import_types90.NodeType.ConfigNode) return;
20632
+ if (node.type !== import_types94.NodeType.ConfigNode) return;
20413
20633
  if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
20414
20634
  if (anyMatch === null) anyMatch = id;
20415
20635
  if (configNodeService(graph, id) === serviceName) scoped = id;
@@ -20426,13 +20646,13 @@ function createEasResolveTarget(graph) {
20426
20646
  if (basenames.length > 0) {
20427
20647
  const configNodeId = findConfigNode(graph, basenames, serviceName);
20428
20648
  if (configNodeId) {
20429
- return { targetNodeId: configNodeId, serviceName, edgeType: import_types90.EdgeType.CALLS };
20649
+ return { targetNodeId: configNodeId, serviceName, edgeType: import_types94.EdgeType.CALLS };
20430
20650
  }
20431
20651
  }
20432
20652
  return {
20433
20653
  targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
20434
20654
  serviceName,
20435
- edgeType: import_types90.EdgeType.CALLS
20655
+ edgeType: import_types94.EdgeType.CALLS
20436
20656
  };
20437
20657
  };
20438
20658
  }
@@ -20444,7 +20664,7 @@ function isBuildSince(build, sinceIso) {
20444
20664
  if (Number.isNaN(t) || Number.isNaN(s)) return true;
20445
20665
  return t > s;
20446
20666
  }
20447
- function boundedSinceIso2(since, now, maxLookbackMs) {
20667
+ function boundedSinceIso3(since, now, maxLookbackMs) {
20448
20668
  const floor = new Date(now.getTime() - maxLookbackMs);
20449
20669
  if (!since) return floor.toISOString();
20450
20670
  const sinceMs = new Date(since).getTime();
@@ -20463,7 +20683,7 @@ var EasConnector = class {
20463
20683
  const creds = readEasCredentials(ctx.credentials);
20464
20684
  const serviceName = this.config.serviceName ?? this.config.appId;
20465
20685
  const maxLookbackMs = this.config.maxLookbackMs ?? DEFAULT_MAX_LOOKBACK_MS6;
20466
- const sinceIso = boundedSinceIso2(ctx.since, /* @__PURE__ */ new Date(), maxLookbackMs);
20686
+ const sinceIso = boundedSinceIso3(ctx.since, /* @__PURE__ */ new Date(), maxLookbackMs);
20467
20687
  const builds = await fetchErroredBuilds(creds.token, this.config, this.fetchImpl);
20468
20688
  const fresh = builds.filter((b) => isBuildSince(b, sinceIso));
20469
20689
  const maxLogBytes = this.config.maxLogBytes ?? DEFAULT_MAX_LOG_BYTES;
@@ -20694,6 +20914,40 @@ var PROVIDER_DISPATCH = {
20694
20914
  });
20695
20915
  }
20696
20916
  },
20917
+ "gcp-lb": {
20918
+ provider: "gcp-lb",
20919
+ // Like cloud-run, gcp-lb reads both projectId and accessToken from the
20920
+ // credential; the single-string form maps to the secret (the token), and the
20921
+ // required-fields check below catches a projectId that was never supplied.
20922
+ primaryCredentialKey: "accessToken",
20923
+ requiredCredentialFields: ["projectId", "accessToken"],
20924
+ requiredOptionFields: [],
20925
+ build(graph, options) {
20926
+ return createGcpLbConnector(graph, options);
20927
+ },
20928
+ // POST entries:list with pageSize 1 — the exact surface poll() reads, so the
20929
+ // probe checks the actual `logging.logEntries.list` permission the connector
20930
+ // needs. This is the same Cloud Logging read-verdict cloud-run's validate
20931
+ // uses (a lighter GET on logs.list would instead check `logging.logs.list`,
20932
+ // falsely rejecting a correctly-scoped custom role carrying only
20933
+ // `logging.logEntries.list`). A 2xx means the token can list log entries;
20934
+ // 401/403 means the provider rejected it.
20935
+ validate({ credentials, fetchImpl }) {
20936
+ const projectId = String(credentials.projectId ?? "");
20937
+ return authProbe({
20938
+ provider: "gcp-lb",
20939
+ accountKey: projectId || "validate",
20940
+ url: "https://logging.googleapis.com/v2/entries:list",
20941
+ token: String(credentials.accessToken ?? ""),
20942
+ init: {
20943
+ method: "POST",
20944
+ headers: { "Content-Type": "application/json" },
20945
+ body: JSON.stringify({ resourceNames: [`projects/${projectId}`], pageSize: 1 })
20946
+ },
20947
+ ...fetchImpl ? { fetchImpl } : {}
20948
+ });
20949
+ }
20950
+ },
20697
20951
  render: {
20698
20952
  provider: "render",
20699
20953
  primaryCredentialKey: "token",
@@ -21113,11 +21367,11 @@ function registerRoutes(scope, ctx) {
21113
21367
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
21114
21368
  const parsed = [];
21115
21369
  for (const c of candidates) {
21116
- const r = import_types93.DivergenceTypeSchema.safeParse(c);
21370
+ const r = import_types97.DivergenceTypeSchema.safeParse(c);
21117
21371
  if (!r.success) {
21118
21372
  return reply.code(400).send({
21119
21373
  error: `unknown divergence type "${c}"`,
21120
- allowed: import_types93.DivergenceTypeSchema.options
21374
+ allowed: import_types97.DivergenceTypeSchema.options
21121
21375
  });
21122
21376
  }
21123
21377
  parsed.push(r.data);
@@ -21479,7 +21733,7 @@ function registerRoutes(scope, ctx) {
21479
21733
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
21480
21734
  let violations = await log.readAll();
21481
21735
  if (req.query.severity) {
21482
- const sev = import_types93.PolicySeveritySchema.safeParse(req.query.severity);
21736
+ const sev = import_types97.PolicySeveritySchema.safeParse(req.query.severity);
21483
21737
  if (!sev.success) {
21484
21738
  return reply.code(400).send({
21485
21739
  error: "invalid severity",
@@ -21518,7 +21772,7 @@ function registerRoutes(scope, ctx) {
21518
21772
  scope.post("/policies/check", async (req, reply) => {
21519
21773
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
21520
21774
  if (!proj) return;
21521
- const parsed = import_types93.PoliciesCheckBodySchema.safeParse(req.body ?? {});
21775
+ const parsed = import_types97.PoliciesCheckBodySchema.safeParse(req.body ?? {});
21522
21776
  if (!parsed.success) {
21523
21777
  return reply.code(400).send({
21524
21778
  error: "invalid /policies/check body",