@neat.is/core 0.10.1-dev.20260926 → 0.10.1-dev.20260928

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-EJPNEEJA.js";
4
+ } from "./chunk-HVBD4ULO.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-J3VVEB45.js";
40
+ } from "./chunk-CNOJ4T6I.js";
41
41
  import {
42
42
  startOtelGrpcReceiver
43
43
  } from "./chunk-ERE47MCR.js";
package/dist/neatd.cjs CHANGED
@@ -11834,13 +11834,16 @@ function objectKeys(obj) {
11834
11834
  var MODULAR_WRITES = /* @__PURE__ */ new Set(["addDoc", "setDoc", "updateDoc"]);
11835
11835
  var NAMESPACED_WRITES = /* @__PURE__ */ new Set(["add", "set", "update"]);
11836
11836
  var READ_CLAUSES = /* @__PURE__ */ new Set(["where", "orderBy"]);
11837
- function firestoreEndpointsFromFile(file, serviceDir) {
11837
+ function firestoreEndpointsFromFile(file, serviceDir, imported) {
11838
11838
  const hasClient = FIRESTORE_CLIENT_IMPORT_RE.test(file.content);
11839
11839
  const hasAdmin = FIRESTORE_ADMIN_IMPORT_RE.test(file.content);
11840
- if (!hasClient && !hasAdmin) return [];
11841
- const fileSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
11840
+ const importedNames = imported?.names ?? /* @__PURE__ */ new Set();
11841
+ if (!hasClient && !hasAdmin && importedNames.size === 0) return [];
11842
+ const ownSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
11843
+ const fileSdk = !hasClient && !hasAdmin ? imported?.sdk ?? null : ownSdk;
11842
11844
  const tree = parseSource3(parserForExt2(import_node_path44.default.extname(file.path)), file.content);
11843
11845
  const clientVars = firestoreClientVars(tree.rootNode);
11846
+ for (const n of importedNames) clientVars.add(n);
11844
11847
  const collLine = /* @__PURE__ */ new Map();
11845
11848
  const writes = /* @__PURE__ */ new Map();
11846
11849
  const reads = /* @__PURE__ */ new Map();
@@ -11951,6 +11954,99 @@ function firestoreEndpointsFromFile(file, serviceDir) {
11951
11954
  }
11952
11955
  return out;
11953
11956
  }
11957
+ function exportedFirestoreClients(root) {
11958
+ const out = /* @__PURE__ */ new Set();
11959
+ const collect = (node) => {
11960
+ if (node.type === "variable_declarator") {
11961
+ const name = node.childForFieldName("name");
11962
+ let value = node.childForFieldName("value");
11963
+ if (value?.type === "await_expression") value = namedChildren(value)[0] ?? null;
11964
+ if (name?.type === "identifier" && isFirestoreClientFactory(value)) out.add(name.text);
11965
+ }
11966
+ for (const c of namedChildren(node)) collect(c);
11967
+ };
11968
+ const walk11 = (node) => {
11969
+ if (node.type === "export_statement") collect(node);
11970
+ else for (const c of namedChildren(node)) walk11(c);
11971
+ };
11972
+ walk11(root);
11973
+ return out;
11974
+ }
11975
+ function namedImportBindings(root) {
11976
+ const out = [];
11977
+ const walk11 = (node) => {
11978
+ if (node.type === "import_statement") {
11979
+ const src = node.childForFieldName("source");
11980
+ const specifier = src ? src.text.slice(1, -1) : "";
11981
+ if (specifier.length > 0) {
11982
+ const stack = [node];
11983
+ while (stack.length > 0) {
11984
+ const n = stack.pop();
11985
+ if (n.type === "import_specifier") {
11986
+ const nameNode = n.childForFieldName("name");
11987
+ const aliasNode = n.childForFieldName("alias");
11988
+ const importedName = nameNode?.text;
11989
+ if (importedName) {
11990
+ out.push({ local: aliasNode?.text ?? importedName, imported: importedName, specifier });
11991
+ }
11992
+ continue;
11993
+ }
11994
+ for (const c of namedChildren(n)) stack.push(c);
11995
+ }
11996
+ }
11997
+ }
11998
+ for (const c of namedChildren(node)) walk11(c);
11999
+ };
12000
+ walk11(root);
12001
+ return out;
12002
+ }
12003
+ function sdkOf(content) {
12004
+ const hasClient = FIRESTORE_CLIENT_IMPORT_RE.test(content);
12005
+ const hasAdmin = FIRESTORE_ADMIN_IMPORT_RE.test(content);
12006
+ return hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
12007
+ }
12008
+ function parseOrNull(file) {
12009
+ const ext = import_node_path44.default.extname(file.path);
12010
+ if (!(ext in GRAMMAR_BY_EXT)) return null;
12011
+ try {
12012
+ return parseSource3(parserForExt2(ext), file.content).rootNode;
12013
+ } catch {
12014
+ return null;
12015
+ }
12016
+ }
12017
+ async function firestoreCrossFileEndpoints(files, serviceDir) {
12018
+ const registry = /* @__PURE__ */ new Map();
12019
+ for (const f of files) {
12020
+ if (sdkOf(f.content) === null && !FIRESTORE_CLIENT_IMPORT_RE.test(f.content)) continue;
12021
+ const root = parseOrNull(f);
12022
+ if (!root) continue;
12023
+ const names = exportedFirestoreClients(root);
12024
+ if (names.size === 0) continue;
12025
+ registry.set(toPosix(import_node_path44.default.relative(serviceDir, f.path)), { names, sdk: sdkOf(f.content) });
12026
+ }
12027
+ if (registry.size === 0) return [];
12028
+ const out = [];
12029
+ for (const f of files) {
12030
+ const root = parseOrNull(f);
12031
+ if (!root) continue;
12032
+ if (firestoreClientVars(root).size > 0) continue;
12033
+ const bindings = namedImportBindings(root);
12034
+ if (bindings.length === 0) continue;
12035
+ const names = /* @__PURE__ */ new Set();
12036
+ let sdk = null;
12037
+ for (const b of bindings) {
12038
+ const rel = await resolveJsImport(b.specifier, import_node_path44.default.dirname(f.path), serviceDir, null);
12039
+ if (!rel) continue;
12040
+ const entry2 = registry.get(rel);
12041
+ if (!entry2 || !entry2.names.has(b.imported)) continue;
12042
+ names.add(b.local);
12043
+ sdk ??= entry2.sdk;
12044
+ }
12045
+ if (names.size === 0) continue;
12046
+ out.push(...firestoreEndpointsFromFile(f, serviceDir, { names, sdk }));
12047
+ }
12048
+ return out;
12049
+ }
11954
12050
 
11955
12051
  // src/extract/calls/mongoose.ts
11956
12052
  init_cjs_shims();
@@ -14760,6 +14856,7 @@ async function addExternalEndpointEdges(graph, services) {
14760
14856
  recordExtractionError("python socket call extraction", file.path, err);
14761
14857
  }
14762
14858
  }
14859
+ endpoints.push(...await firestoreCrossFileEndpoints(maskedFiles, service.dir));
14763
14860
  endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
14764
14861
  endpoints.push(...pythonOrmCrossFileEndpoints(maskedFiles, service.dir));
14765
14862
  endpoints.push(...await prismaColumnEndpoints(service.dir));
@@ -16055,6 +16152,9 @@ function stripComments(src) {
16055
16152
  function segmentsFromMatchPath(pathToken) {
16056
16153
  return pathToken.split("/").filter((s) => s.length > 0);
16057
16154
  }
16155
+ function isRecursiveWildcard(seg) {
16156
+ return /^\{[^}]*=\s*\*\*\}$/.test(seg);
16157
+ }
16058
16158
  function collectionKeyFromSegments(segments) {
16059
16159
  let segs = segments;
16060
16160
  if (segs.length >= 3 && segs[0] === "databases" && /^\{.*\}$/.test(segs[1]) && segs[2] === "documents") {
@@ -16067,10 +16167,11 @@ function collectionKeyFromName(name) {
16067
16167
  }
16068
16168
  function fieldsFromCondition(cond) {
16069
16169
  const out = [];
16070
- const callRe = /\b(?:hasAll|hasOnly|hasAny)\s*\(\s*\[([^\]]*)\]\s*\)/g;
16170
+ const callRe = /(!\s*)?\b(?:hasAll|hasOnly|hasAny)\s*\(\s*\[([^\]]*)\]\s*\)/g;
16071
16171
  let m;
16072
16172
  while ((m = callRe.exec(cond)) !== null) {
16073
- const inner = m[1];
16173
+ if (m[1] !== void 0 || isNegatedAt(cond, m.index)) continue;
16174
+ const inner = m[2];
16074
16175
  const litRe = /'([^']*)'|"([^"]*)"/g;
16075
16176
  let lm;
16076
16177
  while ((lm = litRe.exec(inner)) !== null) {
@@ -16080,6 +16181,28 @@ function fieldsFromCondition(cond) {
16080
16181
  }
16081
16182
  return out;
16082
16183
  }
16184
+ function isNegatedAt(cond, idx) {
16185
+ let i = idx - 1;
16186
+ while (i >= 0) {
16187
+ const ch = cond[i];
16188
+ if (ch === ")") {
16189
+ let depth = 1;
16190
+ i--;
16191
+ while (i >= 0 && depth > 0) {
16192
+ if (cond[i] === ")") depth++;
16193
+ else if (cond[i] === "(") depth--;
16194
+ i--;
16195
+ }
16196
+ continue;
16197
+ }
16198
+ if (/[A-Za-z0-9_$.\]['"]/.test(ch) || ch === " ") {
16199
+ i--;
16200
+ continue;
16201
+ }
16202
+ return ch === "!";
16203
+ }
16204
+ return false;
16205
+ }
16083
16206
  function parseFirestoreRules(content) {
16084
16207
  const src = stripComments(content);
16085
16208
  const tokenRe = /match\s+((?:\/(?:\{[^}]*\}|[^\s/{}]+))+)\s*\{|allow\b([^;{}]*);|(\{)|(\})/g;
@@ -16092,7 +16215,8 @@ function parseFirestoreRules(content) {
16092
16215
  while ((m = tokenRe.exec(src)) !== null) {
16093
16216
  if (m[1] !== void 0) {
16094
16217
  depth++;
16095
- matchStack.push({ depth, segs: segmentsFromMatchPath(m[1]) });
16218
+ const segs = segmentsFromMatchPath(m[1]);
16219
+ matchStack.push({ depth, segs, recursive: segs.some(isRecursiveWildcard) });
16096
16220
  continue;
16097
16221
  }
16098
16222
  if (m[2] !== void 0) {
@@ -16103,6 +16227,10 @@ function parseFirestoreRules(content) {
16103
16227
  if (!methods.some((mm) => WRITE_METHODS.has(mm))) continue;
16104
16228
  const key = currentKey();
16105
16229
  if (key.length === 0) continue;
16230
+ if (matchStack.some((f) => f.recursive)) {
16231
+ indeterminate.add(key);
16232
+ continue;
16233
+ }
16106
16234
  const cond = colon === -1 ? "" : body.slice(colon + 1);
16107
16235
  const fields = fieldsFromCondition(cond);
16108
16236
  if (fields.length > 0) {
@@ -23638,6 +23766,7 @@ function decodeRailwayTargetRef(ref) {
23638
23766
  // src/connectors/hosted.ts
23639
23767
  var CREDENTIAL_REFRESH_SKEW_MS = 6e4;
23640
23768
  var CP_REQUEST_TIMEOUT_MS = 1e4;
23769
+ var DEFAULT_RELIST_INTERVAL_MS = 6e4;
23641
23770
  async function cpGet(path81, deps) {
23642
23771
  const f = deps.fetchImpl ?? fetch;
23643
23772
  const res = await f(`${deps.cpUrl.replace(/\/+$/, "")}${path81}`, {
@@ -23808,67 +23937,111 @@ function createHostedCredentialSource(provider, deps) {
23808
23937
  return record;
23809
23938
  };
23810
23939
  }
23811
- async function startHostedConnectors(input) {
23940
+ function connectionSignature(c) {
23941
+ return `${c.projectRef ?? ""}|${c.needsProjectSelection ? 1 : 0}`;
23942
+ }
23943
+ function startConnectionLoops(c, input, startLoop) {
23812
23944
  const { deps, graph, projectDir, project, errorsPath, onSkip } = input;
23813
- const startLoop = input.startLoop ?? startConnectorPollLoop;
23945
+ const stops = [];
23946
+ const credentialSource = createHostedCredentialSource(c.provider, deps);
23947
+ for (const name of HOSTED_CONNECTORS_FOR[c.provider] ?? [c.provider]) {
23948
+ const dispatch = PROVIDER_DISPATCH[name];
23949
+ if (!dispatch) {
23950
+ onSkip?.(name, "no pull connector for this provider");
23951
+ continue;
23952
+ }
23953
+ if (c.needsProjectSelection || !c.projectRef) {
23954
+ onSkip?.(name, "no project selected yet \u2014 not pullable");
23955
+ continue;
23956
+ }
23957
+ const options = hostedOptions(name, c, project, input.firebaseServiceMap);
23958
+ if (!options) {
23959
+ onSkip?.(name, "no hosted option mapping for this provider");
23960
+ continue;
23961
+ }
23962
+ let built;
23963
+ try {
23964
+ built = dispatch.build(graph, options);
23965
+ } catch (err) {
23966
+ onSkip?.(name, err.message);
23967
+ continue;
23968
+ }
23969
+ stops.push(
23970
+ startLoop(
23971
+ built.connector,
23972
+ { projectDir, project, credentials: {}, ...errorsPath ? { errorsPath } : {} },
23973
+ graph,
23974
+ built.resolveTarget,
23975
+ {
23976
+ connectorId: `hosted:${name}`,
23977
+ // The credential is fetched by the CONNECTION's provider (`gcp`), not the connector's name.
23978
+ refreshCredentials: credentialSource
23979
+ }
23980
+ )
23981
+ );
23982
+ }
23983
+ return () => {
23984
+ for (const stop of stops) stop();
23985
+ };
23986
+ }
23987
+ async function runHostedConnectorsPass(input, startLoop, running) {
23988
+ const { deps, onSkip } = input;
23814
23989
  let connections;
23815
23990
  try {
23816
23991
  connections = await cpGet(`/internal/projects/${deps.projectId}/connections`, deps);
23817
23992
  } catch (err) {
23818
23993
  onSkip?.("(all)", `control plane connection list unreadable \u2014 ${err.message}`);
23819
- return () => {
23820
- };
23994
+ return;
23821
23995
  }
23822
- if (!Array.isArray(connections)) return () => {
23823
- };
23824
- const stops = [];
23996
+ if (!Array.isArray(connections)) return;
23825
23997
  const lookupPush = input.pushDispatch ?? getPushProviderDispatch;
23998
+ const seen = /* @__PURE__ */ new Set();
23826
23999
  for (const c of connections) {
24000
+ seen.add(c.provider);
24001
+ const signature = connectionSignature(c);
24002
+ const existing = running.get(c.provider);
24003
+ if (existing && existing.signature === signature) continue;
24004
+ if (existing) existing.stop();
23827
24005
  const push = lookupPush(c.provider);
23828
24006
  if (push) {
23829
24007
  await provisionHostedPushProvider(c, push, input);
24008
+ running.set(c.provider, { signature, stop: () => {
24009
+ } });
23830
24010
  continue;
23831
24011
  }
23832
- const credentialSource = createHostedCredentialSource(c.provider, deps);
23833
- for (const name of HOSTED_CONNECTORS_FOR[c.provider] ?? [c.provider]) {
23834
- const dispatch = PROVIDER_DISPATCH[name];
23835
- if (!dispatch) {
23836
- onSkip?.(name, "no pull connector for this provider");
23837
- continue;
23838
- }
23839
- if (c.needsProjectSelection || !c.projectRef) {
23840
- onSkip?.(name, "no project selected yet \u2014 not pullable");
23841
- continue;
23842
- }
23843
- const options = hostedOptions(name, c, project, input.firebaseServiceMap);
23844
- if (!options) {
23845
- onSkip?.(name, "no hosted option mapping for this provider");
23846
- continue;
23847
- }
23848
- let built;
23849
- try {
23850
- built = dispatch.build(graph, options);
23851
- } catch (err) {
23852
- onSkip?.(name, err.message);
23853
- continue;
23854
- }
23855
- stops.push(
23856
- startLoop(
23857
- built.connector,
23858
- { projectDir, project, credentials: {}, ...errorsPath ? { errorsPath } : {} },
23859
- graph,
23860
- built.resolveTarget,
23861
- {
23862
- connectorId: `hosted:${name}`,
23863
- // The credential is fetched by the CONNECTION's provider (`gcp`), not the connector's name.
23864
- refreshCredentials: credentialSource
23865
- }
23866
- )
23867
- );
23868
- }
24012
+ running.set(c.provider, { signature, stop: startConnectionLoops(c, input, startLoop) });
23869
24013
  }
24014
+ for (const [provider, rc] of running) {
24015
+ if (seen.has(provider)) continue;
24016
+ rc.stop();
24017
+ running.delete(provider);
24018
+ }
24019
+ }
24020
+ async function startHostedConnectors(input) {
24021
+ const startLoop = input.startLoop ?? startConnectorPollLoop;
24022
+ const intervalMs = input.intervalMs ?? DEFAULT_RELIST_INTERVAL_MS;
24023
+ const running = /* @__PURE__ */ new Map();
24024
+ let stopped = false;
24025
+ let inPass = false;
24026
+ const tick = async () => {
24027
+ if (stopped || inPass) return;
24028
+ inPass = true;
24029
+ try {
24030
+ await runHostedConnectorsPass(input, startLoop, running);
24031
+ } finally {
24032
+ inPass = false;
24033
+ }
24034
+ };
24035
+ await tick();
24036
+ const timer = setInterval(() => {
24037
+ void tick();
24038
+ }, intervalMs);
24039
+ if (typeof timer.unref === "function") timer.unref();
23870
24040
  return () => {
23871
- for (const stop of stops) stop();
24041
+ stopped = true;
24042
+ clearInterval(timer);
24043
+ for (const rc of running.values()) rc.stop();
24044
+ running.clear();
23872
24045
  };
23873
24046
  }
23874
24047
  async function maybeStartHostedConnectors(input) {
@@ -23892,7 +24065,8 @@ async function maybeStartHostedConnectors(input) {
23892
24065
  project: input.project,
23893
24066
  ...parseFirebaseServiceMap(env.NEAT_FIREBASE_SERVICE_MAP) ? { firebaseServiceMap: parseFirebaseServiceMap(env.NEAT_FIREBASE_SERVICE_MAP) } : {},
23894
24067
  ...input.errorsPath ? { errorsPath: input.errorsPath } : {},
23895
- ...input.onSkip ? { onSkip: input.onSkip } : {}
24068
+ ...input.onSkip ? { onSkip: input.onSkip } : {},
24069
+ ...input.intervalMs !== void 0 ? { intervalMs: input.intervalMs } : {}
23896
24070
  });
23897
24071
  }
23898
24072