@kinkai.cloud/vibecheck 0.2.0 → 0.3.0

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/cli.js CHANGED
@@ -5,20 +5,9 @@ var __export = (target, all2) => {
5
5
  __defProp(target, name, { get: all2[name], enumerable: true });
6
6
  };
7
7
 
8
- // ../../packages/checks/src/types.ts
9
- var SEVERITIES = ["fatal", "critical", "high", "medium", "low"];
10
- var NODE_KINDS = [
11
- "browser",
12
- "app",
13
- "api",
14
- "db",
15
- "storage",
16
- "auth",
17
- "ai",
18
- "payments",
19
- "secrets"
20
- ];
21
- var CATEGORIES = ["secrets", "data", "storage", "auth", "ai", "config"];
8
+ // src/analyze.ts
9
+ import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, existsSync, lstatSync as lstatSync2 } from "fs";
10
+ import { resolve as resolve2, join as join3 } from "path";
22
11
 
23
12
  // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
24
13
  var external_exports = {};
@@ -804,14 +793,14 @@ function promiseAllObject(promisesObj) {
804
793
  }
805
794
  function randomString(length = 10) {
806
795
  const chars = "abcdefghijklmnopqrstuvwxyz";
807
- let str = "";
796
+ let str2 = "";
808
797
  for (let i = 0; i < length; i++) {
809
- str += chars[Math.floor(Math.random() * chars.length)];
798
+ str2 += chars[Math.floor(Math.random() * chars.length)];
810
799
  }
811
- return str;
800
+ return str2;
812
801
  }
813
- function esc(str) {
814
- return JSON.stringify(str);
802
+ function esc(str2) {
803
+ return JSON.stringify(str2);
815
804
  }
816
805
  function slugify(input) {
817
806
  return input.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
@@ -925,8 +914,8 @@ var primitiveTypes = /* @__PURE__ */ new Set([
925
914
  "symbol",
926
915
  "undefined"
927
916
  ]);
928
- function escapeRegex(str) {
929
- return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
917
+ function escapeRegex(str2) {
918
+ return str2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
930
919
  }
931
920
  function clone(inst, def, params) {
932
921
  const cl = new inst._zod.constr(def ?? inst._zod.def);
@@ -7658,8 +7647,8 @@ function ko_default() {
7658
7647
  }
7659
7648
 
7660
7649
  // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/locales/lt.js
7661
- var capitalizeFirstCharacter = (text) => {
7662
- return text.charAt(0).toUpperCase() + text.slice(1);
7650
+ var capitalizeFirstCharacter = (text2) => {
7651
+ return text2.charAt(0).toUpperCase() + text2.slice(1);
7663
7652
  };
7664
7653
  function getUnitTypeFromNumber(number4) {
7665
7654
  const abs = Math.abs(number4);
@@ -14534,200 +14523,3469 @@ function date4(params) {
14534
14523
  // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
14535
14524
  config(en_default());
14536
14525
 
14537
- // ../../packages/graph/src/types.ts
14538
- var EDGE_TYPES = ["imports", "calls", "contains"];
14539
- var MAX_NODES = 400;
14540
- var MAX_EDGES = 800;
14541
- var MAX_COMMUNITIES = 9;
14542
- var MAX_FILES_PER_NODE = 12;
14543
- var MAX_COMMUNITY_FILES = 8;
14544
- var MAX_BYTES = 256e3;
14545
- var MAX_LABEL = 80;
14546
- var MAX_ID = 64;
14547
- var MAX_PATH = 180;
14548
- var MAX_PACKAGES = 32;
14549
- var MAX_HINT_ROUTES = 80;
14550
- var MAX_HINT_TABLES = 40;
14551
- var MAX_HINT_BUCKETS = 24;
14552
- var MAX_HINT_COLLECTIONS = 24;
14553
- var EMPTY_DATA = { tables: [], buckets: [], collections: [] };
14554
- var pathSchema = external_exports.string().min(1).max(MAX_PATH);
14555
- var nodeSchema = external_exports.object({
14556
- id: external_exports.string().min(1).max(MAX_ID),
14557
- kind: external_exports.enum(NODE_KINDS),
14558
- label: external_exports.string().min(1).max(MAX_LABEL),
14559
- files: external_exports.array(pathSchema).max(MAX_FILES_PER_NODE)
14560
- });
14561
- var edgeSchema = external_exports.object({
14562
- from: external_exports.string().min(1).max(MAX_ID),
14563
- to: external_exports.string().min(1).max(MAX_ID),
14564
- type: external_exports.enum(EDGE_TYPES)
14565
- });
14566
- var communitySchema = external_exports.object({
14567
- nodeKind: external_exports.enum(NODE_KINDS),
14568
- label: external_exports.string().min(1).max(MAX_LABEL),
14569
- files: external_exports.array(pathSchema).max(MAX_COMMUNITY_FILES)
14570
- });
14571
- var tableName = external_exports.string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,62}$/);
14572
- var bucketName = external_exports.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,62}$/);
14573
- var dataSchema = external_exports.object({
14574
- tables: external_exports.array(tableName).max(MAX_HINT_TABLES),
14575
- buckets: external_exports.array(bucketName).max(MAX_HINT_BUCKETS),
14576
- collections: external_exports.array(tableName).max(MAX_HINT_COLLECTIONS)
14577
- });
14578
- var scanGraphSchema = external_exports.object({
14579
- version: external_exports.literal(1),
14580
- advanced: external_exports.boolean(),
14581
- nodes: external_exports.array(nodeSchema).max(MAX_NODES),
14582
- edges: external_exports.array(edgeSchema).max(MAX_EDGES),
14583
- communities: external_exports.array(communitySchema).max(MAX_COMMUNITIES),
14584
- packages: external_exports.array(external_exports.string().min(1).max(MAX_LABEL)).max(MAX_PACKAGES),
14585
- data: dataSchema.optional()
14586
- });
14587
- var EMPTY_HINTS = {
14588
- apiRoutes: [],
14589
- tableNames: [],
14590
- buckets: [],
14591
- collections: []
14592
- };
14526
+ // ../../packages/analysis/src/index.ts
14527
+ import { randomUUID } from "crypto";
14528
+ import {
14529
+ mkdtempSync,
14530
+ readFileSync as readFileSync2,
14531
+ writeFileSync,
14532
+ rmSync,
14533
+ readdirSync,
14534
+ lstatSync,
14535
+ mkdirSync,
14536
+ copyFileSync
14537
+ } from "fs";
14538
+ import { tmpdir } from "os";
14539
+ import { join as join2, relative, resolve, sep } from "path";
14593
14540
 
14594
- // ../../packages/graph/src/hints.ts
14595
- function uniqueCapped(values, cap) {
14596
- const seen = /* @__PURE__ */ new Set();
14541
+ // ../../packages/checks/src/code/types.ts
14542
+ var API_ROUTE_FILE = /(^|\/)(app\/.*\/route\.(ts|js|tsx|jsx)|pages\/api\/.+\.(ts|js))$/;
14543
+ function isApiRoute(path) {
14544
+ return API_ROUTE_FILE.test(path);
14545
+ }
14546
+
14547
+ // ../../packages/checks/src/code/checks.ts
14548
+ var MIGRATION_FILE = /(^|\/)supabase\/migrations\/[^/]+\.sql$/;
14549
+ var CREATE_TABLE = /create\s+table\s+(?:if\s+not\s+exists\s+)?(?:"?[a-z_][a-z0-9_]*"?\.)?"?([a-z_][a-z0-9_]*)"?/gi;
14550
+ var ENABLE_RLS = /alter\s+table\s+(?:if\s+exists\s+)?(?:only\s+)?(?:"?[a-z_][a-z0-9_]*"?\.)?"?([a-z_][a-z0-9_]*)"?\s+enable\s+row\s+level\s+security/gi;
14551
+ function all(pattern, text2) {
14597
14552
  const out = [];
14598
- for (const value of values) {
14599
- if (out.length >= cap) break;
14600
- if (seen.has(value)) continue;
14601
- seen.add(value);
14602
- out.push(value);
14553
+ for (const m of text2.matchAll(pattern)) {
14554
+ const nome = m[1];
14555
+ if (nome !== void 0) out.push(nome.toLowerCase());
14603
14556
  }
14604
14557
  return out;
14605
14558
  }
14606
- function routeFromFile(path) {
14607
- const normal = path.replaceAll("\\", "/");
14608
- const app = /(?:^|\/)app\/(.+)\/route\.(t|j)sx?$/i.exec(normal);
14609
- if (app?.[1] !== void 0) {
14610
- const segments = app[1].split("/").filter((s) => s.length > 0 && !s.startsWith("(") && !s.startsWith("@")).map((s) => s.replace(/^\[+\.\.\.(.+)\]+$/, "*").replace(/^\[+(.+)\]+$/, ":$1"));
14611
- if (segments.length === 0) return "/api";
14612
- return `/${segments.join("/")}`;
14613
- }
14614
- const pages = /(?:^|\/)pages\/api\/(.+)\.(t|j)sx?$/i.exec(normal);
14615
- if (pages?.[1] !== void 0) {
14616
- const withoutIndex = pages[1].replace(/\/index$/i, "");
14617
- return `/api/${withoutIndex}`;
14618
- }
14619
- return null;
14620
- }
14621
- function filesFromGraph(graph) {
14622
- return [
14623
- ...graph.nodes.flatMap((n) => n.files),
14624
- ...graph.communities.flatMap((c) => c.files)
14625
- ];
14626
- }
14627
- function hintsFromGraph(graph) {
14628
- if (graph === null || graph === void 0 || !graph.advanced) return EMPTY_HINTS;
14629
- const routes = [];
14630
- for (const file2 of filesFromGraph(graph)) {
14631
- const route = routeFromFile(file2);
14632
- if (route !== null) routes.push(route);
14633
- }
14634
- routes.sort((a, b) => a.localeCompare(b));
14635
- const data = graph.data ?? EMPTY_DATA;
14636
- return {
14637
- apiRoutes: uniqueCapped(routes, MAX_HINT_ROUTES),
14638
- tableNames: uniqueCapped(data.tables, MAX_HINT_TABLES),
14639
- buckets: uniqueCapped(data.buckets, MAX_HINT_BUCKETS),
14640
- collections: uniqueCapped(data.collections, MAX_HINT_COLLECTIONS)
14641
- };
14559
+ function isClientFile(file2) {
14560
+ if (/^\s*['"]use client['"]/.test(file2.content)) return true;
14561
+ if (/\.client\.(tsx|jsx|ts|js)$/.test(file2.path)) return true;
14562
+ return /(^|\/)pages\/(?!api\/).+\.(tsx|jsx|ts|js)$/.test(file2.path);
14642
14563
  }
14643
-
14644
- // ../../packages/graph/src/metrics.ts
14645
- function importCycles(graph) {
14646
- const adjacencia = /* @__PURE__ */ new Map();
14647
- for (const aresta of graph.edges) {
14648
- if (aresta.type !== "imports") continue;
14649
- const vizinhos = adjacencia.get(aresta.from) ?? [];
14650
- vizinhos.push(aresta.to);
14651
- adjacencia.set(aresta.from, vizinhos);
14652
- }
14653
- const indice = /* @__PURE__ */ new Map();
14654
- const lowlink = /* @__PURE__ */ new Map();
14655
- const naPilha = /* @__PURE__ */ new Set();
14656
- const pilha = [];
14657
- let proximo = 0;
14658
- let ciclos = 0;
14659
- for (const raiz of graph.nodes.map((n) => n.id)) {
14660
- if (indice.has(raiz)) continue;
14661
- const quadros = [{ no: raiz, vizinho: 0 }];
14662
- indice.set(raiz, proximo);
14663
- lowlink.set(raiz, proximo);
14664
- proximo += 1;
14665
- pilha.push(raiz);
14666
- naPilha.add(raiz);
14667
- while (quadros.length > 0) {
14668
- const quadro = quadros[quadros.length - 1];
14669
- if (quadro === void 0) break;
14670
- const vizinhos = adjacencia.get(quadro.no) ?? [];
14671
- if (quadro.vizinho < vizinhos.length) {
14672
- const vizinho = vizinhos[quadro.vizinho];
14673
- quadro.vizinho += 1;
14674
- if (!indice.has(vizinho)) {
14675
- indice.set(vizinho, proximo);
14676
- lowlink.set(vizinho, proximo);
14677
- proximo += 1;
14678
- pilha.push(vizinho);
14679
- naPilha.add(vizinho);
14680
- quadros.push({ no: vizinho, vizinho: 0 });
14681
- } else if (naPilha.has(vizinho)) {
14682
- lowlink.set(
14683
- quadro.no,
14684
- Math.min(lowlink.get(quadro.no) ?? 0, indice.get(vizinho) ?? 0)
14685
- );
14686
- }
14687
- continue;
14688
- }
14689
- quadros.pop();
14690
- const pai = quadros[quadros.length - 1];
14691
- if (pai !== void 0) {
14692
- lowlink.set(
14693
- pai.no,
14694
- Math.min(lowlink.get(pai.no) ?? 0, lowlink.get(quadro.no) ?? 0)
14695
- );
14696
- }
14697
- if (lowlink.get(quadro.no) === indice.get(quadro.no)) {
14698
- let tamanho = 0;
14699
- let atual;
14700
- do {
14701
- atual = pilha.pop();
14702
- if (atual === void 0) break;
14703
- naPilha.delete(atual);
14704
- tamanho += 1;
14705
- } while (atual !== quadro.no);
14706
- if (tamanho > 1) ciclos += 1;
14564
+ var TOUCHES_DB = /\b(supabase\.(?:from|rpc|storage)\b|prisma\.\w+\.\w+\(|drizzle\(|\bdb\.(?:select|insert|update|delete|query|execute|from)\(|\.from\(['"]|sql`|pool\.query\(|knex\(|mongoose\.|\.collection\(['"])/;
14565
+ var HAS_AUTH = /\b(getSession|getUser|auth\(\)|currentUser|requireAuth|verifyToken|verifyJwt|jwtVerify|authorization|Bearer|withAuth|clerk|next-auth|getServerSession|session)\b/i;
14566
+ var PUBLIC_ROUTE = /(^|\/)(webhooks?|health|healthz|ping|status|robots|sitemap|og|opengraph|cron)(\/|\.|$)/i;
14567
+ var AI_SDK = /from\s+['"](openai|@anthropic-ai\/sdk|ai|@ai-sdk\/[^'"]+|@google\/generative-ai|groq-sdk|cohere-ai|@mistralai\/mistralai)['"]/;
14568
+ var HAS_LIMITER = /\b(ratelimit|rateLimit|Ratelimit|upstash|limiter|throttle|slidingWindow|tokenBucket)\b/;
14569
+ var STRIPE_IMPORT = /from\s+['"]stripe['"]/;
14570
+ var WEBHOOK_PATH = /webhook/i;
14571
+ var SIGNATURE_CHECK = /constructEvent(?:Async)?\(|stripe-signature/;
14572
+ var SERVICE_ROLE_REF = /\b(SUPABASE_SERVICE_ROLE_KEY|SERVICE_ROLE_KEY|sb_secret_[A-Za-z0-9_]+|NEXT_PUBLIC_[A-Z0-9_]*(?:SECRET|SERVICE_ROLE|PRIVATE)[A-Z0-9_]*)\b/;
14573
+ var ENV_FILES = [
14574
+ ".env",
14575
+ ".env.local",
14576
+ ".env.production",
14577
+ ".env.development",
14578
+ ".env.staging"
14579
+ ];
14580
+ var codeRlsMissing = {
14581
+ id: "code-rls-missing",
14582
+ source: "code",
14583
+ category: "data",
14584
+ severity: "critical",
14585
+ title: "Table created without row level security",
14586
+ impact: "A migration creates a table and never turns on row level security for it. Once it has rows, anyone with the public key can read them \u2014 this is the hole the RLS check lights up from outside.",
14587
+ howToDetect: (ctx) => {
14588
+ const migrations = ctx.files.filter((f) => MIGRATION_FILE.test(f.path));
14589
+ if (migrations.length === 0) return [];
14590
+ const comRls = /* @__PURE__ */ new Set();
14591
+ for (const m of migrations) for (const t of all(ENABLE_RLS, m.content)) comRls.add(t);
14592
+ const out = [];
14593
+ const vistas = /* @__PURE__ */ new Set();
14594
+ for (const m of migrations) {
14595
+ for (const tabela of all(CREATE_TABLE, m.content)) {
14596
+ if (comRls.has(tabela) || vistas.has(tabela)) continue;
14597
+ vistas.add(tabela);
14598
+ out.push({
14599
+ path: m.path,
14600
+ reason: `table \`${tabela}\` is created without enable row level security`
14601
+ });
14707
14602
  }
14708
14603
  }
14709
- }
14710
- return ciclos;
14711
- }
14712
- function graphMetrics(graph) {
14713
- return {
14714
- nodes: graph.nodes.length,
14715
- edges: graph.edges.length,
14716
- routes: hintsFromGraph(graph).apiRoutes.length,
14717
- packages: graph.packages.length,
14718
- cycles: importCycles(graph)
14719
- };
14720
- }
14604
+ return out;
14605
+ },
14606
+ fixPrompt: (a) => `In your code, ${a.reason} (${a.path}).
14721
14607
 
14722
- // ../../packages/graph/src/identifiers.ts
14723
- var FROM_CALL = /([A-Za-z_$][\w$]*)\s*\.\s*from\(\s*["'`]([A-Za-z0-9._-]{1,63})["'`]\s*\)/g;
14724
- var NOT_A_DATABASE = /* @__PURE__ */ new Set([
14725
- "array",
14726
- "buffer",
14727
- "object",
14728
- "string",
14729
- "number",
14730
- "date",
14608
+ We do not know the columns or who should read what. We cannot write the policy from here.
14609
+
14610
+ Good practice: every table starts with row level security on, in the same migration that creates it. Then one policy per real access: a person reads their own rows; a public catalog, if it exists, is a deliberate read policy; writes always go through a session. Do this in a new migration, never by editing one that already ran.
14611
+
14612
+ After, deploy and read the table with the public key and no session \u2014 it must answer empty or denied.`
14613
+ };
14614
+ var codeServiceRoleInClient = {
14615
+ id: "code-service-role-in-client",
14616
+ source: "code",
14617
+ category: "secrets",
14618
+ severity: "fatal",
14619
+ title: "Service key referenced in client code",
14620
+ impact: "A file that runs in the browser mentions the Supabase service key or a NEXT_PUBLIC_ secret. Whatever the bundler inlines there ships to every visitor \u2014 this is the fatal finding from outside, one build away.",
14621
+ howToDetect: (ctx) => ctx.files.filter(isClientFile).filter((f) => SERVICE_ROLE_REF.test(f.content)).map((f) => ({
14622
+ path: f.path,
14623
+ reason: "client-side file references a service or secret key"
14624
+ })),
14625
+ fixPrompt: (a) => `In your code, a client-side file references a service or secret key (${a.path}).
14626
+
14627
+ We do not have the value and we do not want it. The problem is the place, not the string.
14628
+
14629
+ Good practice: the service key lives only in server code \u2014 a route handler, a server action, an edge function \u2014 read from the environment without the NEXT_PUBLIC_ prefix. The browser talks to your API; your API talks to the database. Remove the reference from the client file and, if the key was ever bundled, rotate it in the dashboard.
14630
+
14631
+ After, search the built bundle for the key prefix and confirm it is gone.`
14632
+ };
14633
+ var codeRouteWithoutAuth = {
14634
+ id: "code-route-without-auth",
14635
+ source: "code",
14636
+ category: "auth",
14637
+ severity: "high",
14638
+ title: "API route touches data without a session check",
14639
+ impact: "An API route reads or writes the database and nothing in it checks who is asking. If the middleware does not cover it, anyone can call it \u2014 this is what api-route-noauth lights up from outside.",
14640
+ howToDetect: (ctx) => ctx.files.filter((f) => isApiRoute(f.path) && !PUBLIC_ROUTE.test(f.path)).filter((f) => TOUCHES_DB.test(f.content) && !HAS_AUTH.test(f.content)).map((f) => ({
14641
+ path: f.path,
14642
+ reason: "route reads or writes data with no session check in the file"
14643
+ })),
14644
+ fixPrompt: (a) => `In your code, a route reads or writes data with no session check in the file (${a.path}).
14645
+
14646
+ We cannot see your middleware. If it already protects this path, mark the finding as known.
14647
+
14648
+ Good practice: the route resolves the session first and answers 401 before touching data; what it reads or writes is scoped to that user. A public route is public on purpose, with a comment saying so, and never exposes rows that belong to someone.
14649
+
14650
+ After, call the route from a logged-out client and confirm it answers 401 or 403.`
14651
+ };
14652
+ var codeEnvCommitted = {
14653
+ id: "code-env-committed",
14654
+ source: "code",
14655
+ category: "secrets",
14656
+ severity: "critical",
14657
+ title: ".env file tracked by git",
14658
+ impact: "An environment file is committed to the repository. Every clone, fork, CI log and AI tool that reads the repo has the secrets in it. Even after deleting it, the history keeps the values.",
14659
+ // Só o nome. O conteúdo nunca é lido — é o único check que não recebe
14660
+ // `content`, e é assim de propósito.
14661
+ howToDetect: (ctx) => ENV_FILES.filter((nome) => ctx.tracked(nome)).map((nome) => ({
14662
+ path: nome,
14663
+ reason: `${nome} is tracked by git`
14664
+ })),
14665
+ fixPrompt: (a) => `In your code, ${a.reason}.
14666
+
14667
+ We did not read it. We only saw that git tracks it.
14668
+
14669
+ Good practice: environment files stay out of version control \u2014 add them to .gitignore, remove them from the index, and keep a committed .env.example with the variable names and no values. Every secret that was ever in the file is compromised: rotate each one in its provider.
14670
+
14671
+ After, run git ls-files and confirm no .env file is listed.`
14672
+ };
14673
+ var codeAiRouteWithoutLimit = {
14674
+ id: "code-ai-route-without-limit",
14675
+ source: "code",
14676
+ category: "ai",
14677
+ severity: "high",
14678
+ title: "AI route with no rate limit",
14679
+ impact: "A route calls an LLM provider and nothing in it limits how often. A loop from one client turns your provider account into an open bill \u2014 this is ai-proxy-open, seen from inside.",
14680
+ howToDetect: (ctx) => ctx.files.filter((f) => isApiRoute(f.path)).filter((f) => AI_SDK.test(f.content) && !HAS_LIMITER.test(f.content)).map((f) => ({
14681
+ path: f.path,
14682
+ reason: "route calls an AI provider with no rate limit in the file"
14683
+ })),
14684
+ fixPrompt: (a) => `In your code, a route calls an AI provider with no rate limit in the file (${a.path}).
14685
+
14686
+ We cannot see limits applied elsewhere. If your gateway already throttles this path, mark the finding as known.
14687
+
14688
+ Good practice: the route requires a session, then applies a per-user limit (so many calls per minute, so many tokens per day) before calling the provider, and answers 429 when it is hit. Cost caps at the provider are the second net, not the first.
14689
+
14690
+ After, call the route in a loop from one client and confirm it starts answering 429.`
14691
+ };
14692
+ var codeWebhookUnsigned = {
14693
+ id: "code-webhook-unsigned",
14694
+ source: "code",
14695
+ category: "config",
14696
+ severity: "high",
14697
+ title: "Stripe webhook without signature verification",
14698
+ impact: 'A webhook route imports Stripe and never verifies the event signature. Anyone can POST a fake "payment succeeded" and your app will believe it \u2014 the one check we refuse to run from outside, because proving it would mean doing exactly that.',
14699
+ howToDetect: (ctx) => ctx.files.filter((f) => isApiRoute(f.path) && WEBHOOK_PATH.test(f.path)).filter((f) => STRIPE_IMPORT.test(f.content) && !SIGNATURE_CHECK.test(f.content)).map((f) => ({
14700
+ path: f.path,
14701
+ reason: "Stripe webhook route never verifies the event signature"
14702
+ })),
14703
+ fixPrompt: (a) => `In your code, a Stripe webhook route never verifies the event signature (${a.path}).
14704
+
14705
+ We cannot see the secret and do not want it.
14706
+
14707
+ Good practice: the route reads the raw body and the stripe-signature header, builds the event with the webhook secret from the environment, and rejects anything that fails. Only the verified event drives state. Each event id is processed once.
14708
+
14709
+ After, POST a fabricated event to the route and confirm it answers 400.`
14710
+ };
14711
+ var CODE_CHECKS = [
14712
+ codeRlsMissing,
14713
+ codeServiceRoleInClient,
14714
+ codeRouteWithoutAuth,
14715
+ codeEnvCommitted,
14716
+ codeAiRouteWithoutLimit,
14717
+ codeWebhookUnsigned
14718
+ ];
14719
+ function runCodeChecks(ctx, catalog = CODE_CHECKS, options = {}) {
14720
+ const out = [];
14721
+ for (const check2 of catalog) {
14722
+ let advisories = [];
14723
+ try {
14724
+ advisories = check2.howToDetect(ctx);
14725
+ } catch {
14726
+ if (options.failOnError) throw new Error(`Code check failed: ${check2.id}`);
14727
+ advisories = [];
14728
+ }
14729
+ for (const a of advisories) {
14730
+ out.push({
14731
+ checkId: check2.id,
14732
+ severity: check2.severity,
14733
+ path: a.path,
14734
+ reason: a.reason
14735
+ });
14736
+ }
14737
+ }
14738
+ return out;
14739
+ }
14740
+
14741
+ // ../../packages/schema/src/analysis.ts
14742
+ var text = external_exports.string().max(400);
14743
+ var relativePath = external_exports.string().min(1).max(400).refine(
14744
+ (p) => !p.startsWith("/") && !p.includes("\\") && !p.includes(":") && !p.split("/").includes("..") && ![...p].some((c) => c.charCodeAt(0) < 32),
14745
+ "Expected repository-relative path"
14746
+ );
14747
+ var location = external_exports.object({ path: relativePath, line: external_exports.number().int().positive().optional() }).strict();
14748
+ var analysisFindingSchema = external_exports.object({
14749
+ fingerprint: external_exports.string().regex(/^[a-f0-9]{64}$/),
14750
+ matchKey: external_exports.string().regex(/^[a-f0-9]{64}$/).optional(),
14751
+ observed: external_exports.boolean().optional(),
14752
+ rule: text,
14753
+ engine: text,
14754
+ category: external_exports.enum(["security", "quality", "dependency", "secret"]),
14755
+ severity: external_exports.enum(["critical", "high", "medium", "low", "unknown"]),
14756
+ language: text,
14757
+ title: text,
14758
+ explanation: text,
14759
+ remediation: text,
14760
+ location,
14761
+ flow: external_exports.array(location).max(100).default([]),
14762
+ state: external_exports.enum(["new", "persisting", "resolved", "unverified", "accepted"]).default("new"),
14763
+ package: external_exports.object({
14764
+ name: text,
14765
+ version: text,
14766
+ ecosystem: text,
14767
+ advisory: text,
14768
+ fixed: external_exports.array(text).max(50),
14769
+ relationship: external_exports.enum(["direct", "transitive", "unknown"])
14770
+ }).strict().optional()
14771
+ }).strict();
14772
+ var engineSchema = external_exports.object({
14773
+ id: text,
14774
+ version: text,
14775
+ rules: text,
14776
+ status: external_exports.enum(["ran", "unavailable", "not-applicable", "failed"]),
14777
+ durationMs: external_exports.number().nonnegative(),
14778
+ files: external_exports.array(relativePath).max(1e5),
14779
+ excluded: external_exports.array(relativePath).max(1e5),
14780
+ message: text
14781
+ }).strict();
14782
+ var policySchema = external_exports.object({
14783
+ version: external_exports.number().int().positive(),
14784
+ coverage: external_exports.number().min(0).max(100),
14785
+ requiredEngines: external_exports.array(external_exports.enum(["semgrep", "osv", "gitleaks", "builtin"])).max(4)
14786
+ }).strict();
14787
+ var DEFAULT_ANALYSIS_POLICY = {
14788
+ version: 1,
14789
+ coverage: 80,
14790
+ requiredEngines: ["semgrep", "osv", "gitleaks", "builtin"]
14791
+ };
14792
+ var conditionSchema = external_exports.object({
14793
+ id: external_exports.enum(["baseline", "engines", "security", "coverage", "tests"]),
14794
+ status: external_exports.enum(["passed", "failed", "inconclusive", "not-applicable"]),
14795
+ value: external_exports.number().nullable(),
14796
+ limit: external_exports.number().nullable()
14797
+ }).strict();
14798
+ var analysisReportSchema = external_exports.object({
14799
+ version: external_exports.literal(2),
14800
+ projectId: external_exports.string().uuid(),
14801
+ runId: external_exports.string().uuid(),
14802
+ provenance: external_exports.literal("local-ci"),
14803
+ startedAt: external_exports.string().datetime(),
14804
+ commit: external_exports.string().regex(/^[a-f0-9]{40,64}$/).nullable(),
14805
+ branch: text,
14806
+ reference: text,
14807
+ mergeBase: external_exports.string().regex(/^[a-f0-9]{40,64}$/).nullable(),
14808
+ baselineRunId: external_exports.string().uuid().nullable(),
14809
+ scope: external_exports.string().max(100),
14810
+ engines: external_exports.array(engineSchema).max(30),
14811
+ findings: external_exports.array(analysisFindingSchema).max(5e4),
14812
+ coverage: external_exports.object({
14813
+ status: external_exports.enum(["ran", "missing", "failed"]),
14814
+ files: external_exports.array(
14815
+ external_exports.object({
14816
+ path: relativePath,
14817
+ lines: external_exports.array(
14818
+ external_exports.object({
14819
+ line: external_exports.number().int().positive(),
14820
+ hits: external_exports.number().int().nonnegative()
14821
+ }).strict()
14822
+ ).max(2e5)
14823
+ }).strict()
14824
+ ).max(1e5)
14825
+ }).strict(),
14826
+ tests: external_exports.object({
14827
+ status: external_exports.enum(["ran", "missing", "failed"]),
14828
+ total: external_exports.number().int().nonnegative(),
14829
+ failed: external_exports.number().int().nonnegative(),
14830
+ skipped: external_exports.number().int().nonnegative(),
14831
+ failures: external_exports.array(
14832
+ external_exports.object({
14833
+ id: external_exports.string().regex(/^[a-f0-9]{64}$/),
14834
+ location: location.optional(),
14835
+ kind: external_exports.enum(["failure", "error"])
14836
+ }).strict()
14837
+ ).max(1e4).optional()
14838
+ }).strict(),
14839
+ changes: external_exports.array(
14840
+ external_exports.object({
14841
+ path: relativePath,
14842
+ previousPath: relativePath.optional(),
14843
+ lines: external_exports.array(external_exports.number().int().positive()).max(2e5)
14844
+ }).strict()
14845
+ ).max(1e5),
14846
+ policy: policySchema,
14847
+ conditions: external_exports.array(conditionSchema).max(10),
14848
+ status: external_exports.enum(["passed", "failed", "inconclusive"])
14849
+ }).strict();
14850
+ function evaluateAnalysis(input, baseline, policy = DEFAULT_ANALYSIS_POLICY, exceptions = [], now = /* @__PURE__ */ new Date()) {
14851
+ const comparable = baseline !== null && input.mergeBase !== null && baseline.commit === input.mergeBase && baseline.projectId === input.projectId && baseline.scope === input.scope;
14852
+ const previous = new Map(
14853
+ (comparable ? baseline.findings : []).filter((f) => f.observed !== false && f.state !== "resolved").map((f) => [f.fingerprint, f])
14854
+ );
14855
+ const observations = input.findings.filter((f) => f.observed !== false);
14856
+ const current = new Set(observations.map((f) => f.fingerprint));
14857
+ const renamed = new Map(
14858
+ input.changes.filter((c) => c.previousPath).map((c) => [c.path, c.previousPath])
14859
+ );
14860
+ const matchedPrevious = /* @__PURE__ */ new Set();
14861
+ const active = new Set(
14862
+ exceptions.filter((e) => e.reason.trim() && new Date(e.expiresAt) > now).map((e) => e.fingerprint)
14863
+ );
14864
+ const findings = observations.map((f) => {
14865
+ const engine = input.engines.find((e) => e.id === f.engine);
14866
+ const oldEngine = baseline?.engines.find((e) => e.id === f.engine);
14867
+ const old = previous.get(f.fingerprint) ?? (f.matchKey && renamed.has(f.location.path) ? [...previous.values()].find(
14868
+ (p) => p.matchKey === f.matchKey && p.location.path === renamed.get(f.location.path)
14869
+ ) : void 0);
14870
+ const known = old !== void 0;
14871
+ if (old) matchedPrevious.add(old.fingerprint);
14872
+ const compatibleEngine = engine?.status === "ran" && oldEngine !== void 0 && ["ran", "not-applicable"].includes(oldEngine.status) && engine.version === oldEngine.version && engine.rules === oldEngine.rules;
14873
+ return {
14874
+ ...f,
14875
+ observed: true,
14876
+ state: active.has(f.fingerprint) ? "accepted" : !comparable || !compatibleEngine ? "unverified" : known ? "persisting" : "new"
14877
+ };
14878
+ });
14879
+ if (comparable)
14880
+ for (const old of previous.values()) {
14881
+ if (current.has(old.fingerprint) || matchedPrevious.has(old.fingerprint)) continue;
14882
+ const engine = input.engines.find((e) => e.id === old.engine);
14883
+ const before = baseline.engines.find((e) => e.id === old.engine);
14884
+ const covered2 = engine?.status === "ran" && engine.version === before?.version && engine.rules === before?.rules && engine.files.includes(old.location.path) && !engine.excluded.includes(old.location.path);
14885
+ findings.push({
14886
+ ...old,
14887
+ observed: false,
14888
+ state: covered2 ? "resolved" : "unverified"
14889
+ });
14890
+ }
14891
+ const missingEngine = input.engines.some((e) => ["failed", "unavailable"].includes(e.status)) || input.engines.some(
14892
+ (e) => input.changes.some((c) => c.lines.length > 0 && e.excluded.includes(c.path))
14893
+ ) || comparable && input.engines.some((e) => {
14894
+ const before = baseline.engines.find((b) => b.id === e.id);
14895
+ return !before || before.rules !== e.rules || before.version !== e.version || !["ran", "not-applicable"].includes(before.status);
14896
+ }) || policy.requiredEngines.some(
14897
+ (id) => !input.engines.some(
14898
+ (e) => e.id === id && ["ran", "not-applicable"].includes(e.status)
14899
+ )
14900
+ );
14901
+ const coverageByPath = new Map(
14902
+ input.coverage.files.map((f) => [
14903
+ f.path,
14904
+ new Map(f.lines.map((l) => [l.line, l.hits]))
14905
+ ])
14906
+ );
14907
+ let executable = 0, covered = 0;
14908
+ let unknownFile = false;
14909
+ for (const change of input.changes) {
14910
+ if (!change.lines.length || !/\.(tsx?|jsx?|py|go|java|cs)$/.test(change.path))
14911
+ continue;
14912
+ const lines = coverageByPath.get(change.path);
14913
+ if (!lines) {
14914
+ unknownFile = true;
14915
+ continue;
14916
+ }
14917
+ for (const line of change.lines)
14918
+ if (lines.has(line)) {
14919
+ executable++;
14920
+ if ((lines.get(line) ?? 0) > 0) covered++;
14921
+ }
14922
+ }
14923
+ const percent = executable ? covered / executable * 100 : null;
14924
+ const blockers = findings.filter(
14925
+ (f) => f.state === "new" && (f.category === "secret" || f.category !== "quality" && ["critical", "high"].includes(f.severity))
14926
+ ).length;
14927
+ const uncertain = findings.some(
14928
+ (f) => (f.state === "unverified" || f.state === "new" && f.severity === "unknown") && f.category !== "quality"
14929
+ );
14930
+ const conditions = [
14931
+ {
14932
+ id: "baseline",
14933
+ status: comparable ? "passed" : "inconclusive",
14934
+ value: null,
14935
+ limit: null
14936
+ },
14937
+ {
14938
+ id: "engines",
14939
+ status: missingEngine || uncertain ? "inconclusive" : "passed",
14940
+ value: null,
14941
+ limit: null
14942
+ },
14943
+ {
14944
+ id: "security",
14945
+ status: blockers ? "failed" : comparable ? "passed" : "inconclusive",
14946
+ value: blockers,
14947
+ limit: 0
14948
+ },
14949
+ {
14950
+ id: "coverage",
14951
+ status: !comparable || unknownFile || input.coverage.status === "failed" || executable > 0 && input.coverage.status !== "ran" ? "inconclusive" : percent === null ? "not-applicable" : percent < policy.coverage ? "failed" : "passed",
14952
+ value: percent,
14953
+ limit: policy.coverage
14954
+ },
14955
+ {
14956
+ id: "tests",
14957
+ status: input.tests.status === "failed" ? "inconclusive" : input.tests.failed ? "failed" : input.tests.status === "missing" ? "not-applicable" : "passed",
14958
+ value: input.tests.failed,
14959
+ limit: 0
14960
+ }
14961
+ ];
14962
+ return {
14963
+ ...input,
14964
+ findings,
14965
+ policy,
14966
+ conditions,
14967
+ status: conditions.some((c) => c.status === "failed") ? "failed" : conditions.some((c) => c.status === "inconclusive") ? "inconclusive" : "passed"
14968
+ };
14969
+ }
14970
+
14971
+ // ../../packages/analysis/src/process.ts
14972
+ import { spawn } from "child_process";
14973
+ function execute(file2, args, cwd, timeoutMs = 12e4) {
14974
+ return new Promise((resolve4) => {
14975
+ const child = spawn(file2, args, {
14976
+ cwd,
14977
+ shell: false,
14978
+ windowsHide: true,
14979
+ detached: process.platform !== "win32",
14980
+ env: { ...process.env, SEMGREP_SEND_METRICS: "off" },
14981
+ stdio: ["ignore", "pipe", "pipe"]
14982
+ });
14983
+ let stdout = "", bytes = 0, done = false;
14984
+ const finish = (code, unavailable = false) => {
14985
+ if (done) return;
14986
+ done = true;
14987
+ clearTimeout(timer);
14988
+ resolve4({ code, stdout, unavailable });
14989
+ };
14990
+ const timer = setTimeout(() => {
14991
+ stop();
14992
+ finish(124);
14993
+ }, timeoutMs);
14994
+ function stop() {
14995
+ try {
14996
+ if (process.platform !== "win32" && child.pid) process.kill(-child.pid, "SIGKILL");
14997
+ else child.kill("SIGKILL");
14998
+ } catch {
14999
+ }
15000
+ }
15001
+ child.stdout.on("data", (chunk) => {
15002
+ bytes += chunk.length;
15003
+ if (bytes > 2e7) {
15004
+ stop();
15005
+ finish(125);
15006
+ } else stdout += chunk.toString();
15007
+ });
15008
+ child.stderr.on("data", (chunk) => {
15009
+ bytes += chunk.length;
15010
+ if (bytes > 2e7) {
15011
+ stop();
15012
+ finish(125);
15013
+ }
15014
+ });
15015
+ child.on("error", () => finish(127, true));
15016
+ child.on("close", (code) => finish(code ?? 126));
15017
+ });
15018
+ }
15019
+
15020
+ // ../../packages/analysis/src/importers.ts
15021
+ import { createHash } from "crypto";
15022
+
15023
+ // ../../node_modules/.pnpm/fast-xml-parser@5.3.8/node_modules/fast-xml-parser/src/util.js
15024
+ var nameStartChar = ":A-Za-z_\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
15025
+ var nameChar = nameStartChar + "\\-.\\d\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
15026
+ var nameRegexp = "[" + nameStartChar + "][" + nameChar + "]*";
15027
+ var regexName = new RegExp("^" + nameRegexp + "$");
15028
+ function getAllMatches(string4, regex) {
15029
+ const matches = [];
15030
+ let match = regex.exec(string4);
15031
+ while (match) {
15032
+ const allmatches = [];
15033
+ allmatches.startIndex = regex.lastIndex - match[0].length;
15034
+ const len = match.length;
15035
+ for (let index = 0; index < len; index++) {
15036
+ allmatches.push(match[index]);
15037
+ }
15038
+ matches.push(allmatches);
15039
+ match = regex.exec(string4);
15040
+ }
15041
+ return matches;
15042
+ }
15043
+ var isName = function(string4) {
15044
+ const match = regexName.exec(string4);
15045
+ return !(match === null || typeof match === "undefined");
15046
+ };
15047
+ function isExist(v) {
15048
+ return typeof v !== "undefined";
15049
+ }
15050
+
15051
+ // ../../node_modules/.pnpm/fast-xml-parser@5.3.8/node_modules/fast-xml-parser/src/validator.js
15052
+ var defaultOptions = {
15053
+ allowBooleanAttributes: false,
15054
+ //A tag can have attributes without any value
15055
+ unpairedTags: []
15056
+ };
15057
+ function validate(xmlData, options) {
15058
+ options = Object.assign({}, defaultOptions, options);
15059
+ const tags = [];
15060
+ let tagFound = false;
15061
+ let reachedRoot = false;
15062
+ if (xmlData[0] === "\uFEFF") {
15063
+ xmlData = xmlData.substr(1);
15064
+ }
15065
+ for (let i = 0; i < xmlData.length; i++) {
15066
+ if (xmlData[i] === "<" && xmlData[i + 1] === "?") {
15067
+ i += 2;
15068
+ i = readPI(xmlData, i);
15069
+ if (i.err) return i;
15070
+ } else if (xmlData[i] === "<") {
15071
+ let tagStartPos = i;
15072
+ i++;
15073
+ if (xmlData[i] === "!") {
15074
+ i = readCommentAndCDATA(xmlData, i);
15075
+ continue;
15076
+ } else {
15077
+ let closingTag = false;
15078
+ if (xmlData[i] === "/") {
15079
+ closingTag = true;
15080
+ i++;
15081
+ }
15082
+ let tagName = "";
15083
+ for (; i < xmlData.length && xmlData[i] !== ">" && xmlData[i] !== " " && xmlData[i] !== " " && xmlData[i] !== "\n" && xmlData[i] !== "\r"; i++) {
15084
+ tagName += xmlData[i];
15085
+ }
15086
+ tagName = tagName.trim();
15087
+ if (tagName[tagName.length - 1] === "/") {
15088
+ tagName = tagName.substring(0, tagName.length - 1);
15089
+ i--;
15090
+ }
15091
+ if (!validateTagName(tagName)) {
15092
+ let msg;
15093
+ if (tagName.trim().length === 0) {
15094
+ msg = "Invalid space after '<'.";
15095
+ } else {
15096
+ msg = "Tag '" + tagName + "' is an invalid name.";
15097
+ }
15098
+ return getErrorObject("InvalidTag", msg, getLineNumberForPosition(xmlData, i));
15099
+ }
15100
+ const result = readAttributeStr(xmlData, i);
15101
+ if (result === false) {
15102
+ return getErrorObject("InvalidAttr", "Attributes for '" + tagName + "' have open quote.", getLineNumberForPosition(xmlData, i));
15103
+ }
15104
+ let attrStr = result.value;
15105
+ i = result.index;
15106
+ if (attrStr[attrStr.length - 1] === "/") {
15107
+ const attrStrStart = i - attrStr.length;
15108
+ attrStr = attrStr.substring(0, attrStr.length - 1);
15109
+ const isValid = validateAttributeString(attrStr, options);
15110
+ if (isValid === true) {
15111
+ tagFound = true;
15112
+ } else {
15113
+ return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, attrStrStart + isValid.err.line));
15114
+ }
15115
+ } else if (closingTag) {
15116
+ if (!result.tagClosed) {
15117
+ return getErrorObject("InvalidTag", "Closing tag '" + tagName + "' doesn't have proper closing.", getLineNumberForPosition(xmlData, i));
15118
+ } else if (attrStr.trim().length > 0) {
15119
+ return getErrorObject("InvalidTag", "Closing tag '" + tagName + "' can't have attributes or invalid starting.", getLineNumberForPosition(xmlData, tagStartPos));
15120
+ } else if (tags.length === 0) {
15121
+ return getErrorObject("InvalidTag", "Closing tag '" + tagName + "' has not been opened.", getLineNumberForPosition(xmlData, tagStartPos));
15122
+ } else {
15123
+ const otg = tags.pop();
15124
+ if (tagName !== otg.tagName) {
15125
+ let openPos = getLineNumberForPosition(xmlData, otg.tagStartPos);
15126
+ return getErrorObject(
15127
+ "InvalidTag",
15128
+ "Expected closing tag '" + otg.tagName + "' (opened in line " + openPos.line + ", col " + openPos.col + ") instead of closing tag '" + tagName + "'.",
15129
+ getLineNumberForPosition(xmlData, tagStartPos)
15130
+ );
15131
+ }
15132
+ if (tags.length == 0) {
15133
+ reachedRoot = true;
15134
+ }
15135
+ }
15136
+ } else {
15137
+ const isValid = validateAttributeString(attrStr, options);
15138
+ if (isValid !== true) {
15139
+ return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, i - attrStr.length + isValid.err.line));
15140
+ }
15141
+ if (reachedRoot === true) {
15142
+ return getErrorObject("InvalidXml", "Multiple possible root nodes found.", getLineNumberForPosition(xmlData, i));
15143
+ } else if (options.unpairedTags.indexOf(tagName) !== -1) {
15144
+ } else {
15145
+ tags.push({ tagName, tagStartPos });
15146
+ }
15147
+ tagFound = true;
15148
+ }
15149
+ for (i++; i < xmlData.length; i++) {
15150
+ if (xmlData[i] === "<") {
15151
+ if (xmlData[i + 1] === "!") {
15152
+ i++;
15153
+ i = readCommentAndCDATA(xmlData, i);
15154
+ continue;
15155
+ } else if (xmlData[i + 1] === "?") {
15156
+ i = readPI(xmlData, ++i);
15157
+ if (i.err) return i;
15158
+ } else {
15159
+ break;
15160
+ }
15161
+ } else if (xmlData[i] === "&") {
15162
+ const afterAmp = validateAmpersand(xmlData, i);
15163
+ if (afterAmp == -1)
15164
+ return getErrorObject("InvalidChar", "char '&' is not expected.", getLineNumberForPosition(xmlData, i));
15165
+ i = afterAmp;
15166
+ } else {
15167
+ if (reachedRoot === true && !isWhiteSpace(xmlData[i])) {
15168
+ return getErrorObject("InvalidXml", "Extra text at the end", getLineNumberForPosition(xmlData, i));
15169
+ }
15170
+ }
15171
+ }
15172
+ if (xmlData[i] === "<") {
15173
+ i--;
15174
+ }
15175
+ }
15176
+ } else {
15177
+ if (isWhiteSpace(xmlData[i])) {
15178
+ continue;
15179
+ }
15180
+ return getErrorObject("InvalidChar", "char '" + xmlData[i] + "' is not expected.", getLineNumberForPosition(xmlData, i));
15181
+ }
15182
+ }
15183
+ if (!tagFound) {
15184
+ return getErrorObject("InvalidXml", "Start tag expected.", 1);
15185
+ } else if (tags.length == 1) {
15186
+ return getErrorObject("InvalidTag", "Unclosed tag '" + tags[0].tagName + "'.", getLineNumberForPosition(xmlData, tags[0].tagStartPos));
15187
+ } else if (tags.length > 0) {
15188
+ return getErrorObject("InvalidXml", "Invalid '" + JSON.stringify(tags.map((t) => t.tagName), null, 4).replace(/\r?\n/g, "") + "' found.", { line: 1, col: 1 });
15189
+ }
15190
+ return true;
15191
+ }
15192
+ function isWhiteSpace(char) {
15193
+ return char === " " || char === " " || char === "\n" || char === "\r";
15194
+ }
15195
+ function readPI(xmlData, i) {
15196
+ const start = i;
15197
+ for (; i < xmlData.length; i++) {
15198
+ if (xmlData[i] == "?" || xmlData[i] == " ") {
15199
+ const tagname = xmlData.substr(start, i - start);
15200
+ if (i > 5 && tagname === "xml") {
15201
+ return getErrorObject("InvalidXml", "XML declaration allowed only at the start of the document.", getLineNumberForPosition(xmlData, i));
15202
+ } else if (xmlData[i] == "?" && xmlData[i + 1] == ">") {
15203
+ i++;
15204
+ break;
15205
+ } else {
15206
+ continue;
15207
+ }
15208
+ }
15209
+ }
15210
+ return i;
15211
+ }
15212
+ function readCommentAndCDATA(xmlData, i) {
15213
+ if (xmlData.length > i + 5 && xmlData[i + 1] === "-" && xmlData[i + 2] === "-") {
15214
+ for (i += 3; i < xmlData.length; i++) {
15215
+ if (xmlData[i] === "-" && xmlData[i + 1] === "-" && xmlData[i + 2] === ">") {
15216
+ i += 2;
15217
+ break;
15218
+ }
15219
+ }
15220
+ } else if (xmlData.length > i + 8 && xmlData[i + 1] === "D" && xmlData[i + 2] === "O" && xmlData[i + 3] === "C" && xmlData[i + 4] === "T" && xmlData[i + 5] === "Y" && xmlData[i + 6] === "P" && xmlData[i + 7] === "E") {
15221
+ let angleBracketsCount = 1;
15222
+ for (i += 8; i < xmlData.length; i++) {
15223
+ if (xmlData[i] === "<") {
15224
+ angleBracketsCount++;
15225
+ } else if (xmlData[i] === ">") {
15226
+ angleBracketsCount--;
15227
+ if (angleBracketsCount === 0) {
15228
+ break;
15229
+ }
15230
+ }
15231
+ }
15232
+ } else if (xmlData.length > i + 9 && xmlData[i + 1] === "[" && xmlData[i + 2] === "C" && xmlData[i + 3] === "D" && xmlData[i + 4] === "A" && xmlData[i + 5] === "T" && xmlData[i + 6] === "A" && xmlData[i + 7] === "[") {
15233
+ for (i += 8; i < xmlData.length; i++) {
15234
+ if (xmlData[i] === "]" && xmlData[i + 1] === "]" && xmlData[i + 2] === ">") {
15235
+ i += 2;
15236
+ break;
15237
+ }
15238
+ }
15239
+ }
15240
+ return i;
15241
+ }
15242
+ var doubleQuote = '"';
15243
+ var singleQuote = "'";
15244
+ function readAttributeStr(xmlData, i) {
15245
+ let attrStr = "";
15246
+ let startChar = "";
15247
+ let tagClosed = false;
15248
+ for (; i < xmlData.length; i++) {
15249
+ if (xmlData[i] === doubleQuote || xmlData[i] === singleQuote) {
15250
+ if (startChar === "") {
15251
+ startChar = xmlData[i];
15252
+ } else if (startChar !== xmlData[i]) {
15253
+ } else {
15254
+ startChar = "";
15255
+ }
15256
+ } else if (xmlData[i] === ">") {
15257
+ if (startChar === "") {
15258
+ tagClosed = true;
15259
+ break;
15260
+ }
15261
+ }
15262
+ attrStr += xmlData[i];
15263
+ }
15264
+ if (startChar !== "") {
15265
+ return false;
15266
+ }
15267
+ return {
15268
+ value: attrStr,
15269
+ index: i,
15270
+ tagClosed
15271
+ };
15272
+ }
15273
+ var validAttrStrRegxp = new RegExp(`(\\s*)([^\\s=]+)(\\s*=)?(\\s*(['"])(([\\s\\S])*?)\\5)?`, "g");
15274
+ function validateAttributeString(attrStr, options) {
15275
+ const matches = getAllMatches(attrStr, validAttrStrRegxp);
15276
+ const attrNames = {};
15277
+ for (let i = 0; i < matches.length; i++) {
15278
+ if (matches[i][1].length === 0) {
15279
+ return getErrorObject("InvalidAttr", "Attribute '" + matches[i][2] + "' has no space in starting.", getPositionFromMatch(matches[i]));
15280
+ } else if (matches[i][3] !== void 0 && matches[i][4] === void 0) {
15281
+ return getErrorObject("InvalidAttr", "Attribute '" + matches[i][2] + "' is without value.", getPositionFromMatch(matches[i]));
15282
+ } else if (matches[i][3] === void 0 && !options.allowBooleanAttributes) {
15283
+ return getErrorObject("InvalidAttr", "boolean attribute '" + matches[i][2] + "' is not allowed.", getPositionFromMatch(matches[i]));
15284
+ }
15285
+ const attrName = matches[i][2];
15286
+ if (!validateAttrName(attrName)) {
15287
+ return getErrorObject("InvalidAttr", "Attribute '" + attrName + "' is an invalid name.", getPositionFromMatch(matches[i]));
15288
+ }
15289
+ if (!Object.prototype.hasOwnProperty.call(attrNames, attrName)) {
15290
+ attrNames[attrName] = 1;
15291
+ } else {
15292
+ return getErrorObject("InvalidAttr", "Attribute '" + attrName + "' is repeated.", getPositionFromMatch(matches[i]));
15293
+ }
15294
+ }
15295
+ return true;
15296
+ }
15297
+ function validateNumberAmpersand(xmlData, i) {
15298
+ let re = /\d/;
15299
+ if (xmlData[i] === "x") {
15300
+ i++;
15301
+ re = /[\da-fA-F]/;
15302
+ }
15303
+ for (; i < xmlData.length; i++) {
15304
+ if (xmlData[i] === ";")
15305
+ return i;
15306
+ if (!xmlData[i].match(re))
15307
+ break;
15308
+ }
15309
+ return -1;
15310
+ }
15311
+ function validateAmpersand(xmlData, i) {
15312
+ i++;
15313
+ if (xmlData[i] === ";")
15314
+ return -1;
15315
+ if (xmlData[i] === "#") {
15316
+ i++;
15317
+ return validateNumberAmpersand(xmlData, i);
15318
+ }
15319
+ let count = 0;
15320
+ for (; i < xmlData.length; i++, count++) {
15321
+ if (xmlData[i].match(/\w/) && count < 20)
15322
+ continue;
15323
+ if (xmlData[i] === ";")
15324
+ break;
15325
+ return -1;
15326
+ }
15327
+ return i;
15328
+ }
15329
+ function getErrorObject(code, message, lineNumber) {
15330
+ return {
15331
+ err: {
15332
+ code,
15333
+ msg: message,
15334
+ line: lineNumber.line || lineNumber,
15335
+ col: lineNumber.col
15336
+ }
15337
+ };
15338
+ }
15339
+ function validateAttrName(attrName) {
15340
+ return isName(attrName);
15341
+ }
15342
+ function validateTagName(tagname) {
15343
+ return isName(tagname);
15344
+ }
15345
+ function getLineNumberForPosition(xmlData, index) {
15346
+ const lines = xmlData.substring(0, index).split(/\r?\n/);
15347
+ return {
15348
+ line: lines.length,
15349
+ // column number is last line's length + 1, because column numbering starts at 1:
15350
+ col: lines[lines.length - 1].length + 1
15351
+ };
15352
+ }
15353
+ function getPositionFromMatch(match) {
15354
+ return match.startIndex + match[1].length;
15355
+ }
15356
+
15357
+ // ../../node_modules/.pnpm/fast-xml-parser@5.3.8/node_modules/fast-xml-parser/src/xmlparser/OptionsBuilder.js
15358
+ var defaultOptions2 = {
15359
+ preserveOrder: false,
15360
+ attributeNamePrefix: "@_",
15361
+ attributesGroupName: false,
15362
+ textNodeName: "#text",
15363
+ ignoreAttributes: true,
15364
+ removeNSPrefix: false,
15365
+ // remove NS from tag name or attribute name if true
15366
+ allowBooleanAttributes: false,
15367
+ //a tag can have attributes without any value
15368
+ //ignoreRootElement : false,
15369
+ parseTagValue: true,
15370
+ parseAttributeValue: false,
15371
+ trimValues: true,
15372
+ //Trim string values of tag and attributes
15373
+ cdataPropName: false,
15374
+ numberParseOptions: {
15375
+ hex: true,
15376
+ leadingZeros: true,
15377
+ eNotation: true
15378
+ },
15379
+ tagValueProcessor: function(tagName, val) {
15380
+ return val;
15381
+ },
15382
+ attributeValueProcessor: function(attrName, val) {
15383
+ return val;
15384
+ },
15385
+ stopNodes: [],
15386
+ //nested tags will not be parsed even for errors
15387
+ alwaysCreateTextNode: false,
15388
+ isArray: () => false,
15389
+ commentPropName: false,
15390
+ unpairedTags: [],
15391
+ processEntities: true,
15392
+ htmlEntities: false,
15393
+ ignoreDeclaration: false,
15394
+ ignorePiTags: false,
15395
+ transformTagName: false,
15396
+ transformAttributeName: false,
15397
+ updateTag: function(tagName, jPath, attrs) {
15398
+ return tagName;
15399
+ },
15400
+ // skipEmptyListItem: false
15401
+ captureMetaData: false,
15402
+ maxNestedTags: 100
15403
+ };
15404
+ function normalizeProcessEntities(value) {
15405
+ if (typeof value === "boolean") {
15406
+ return {
15407
+ enabled: value,
15408
+ // true or false
15409
+ maxEntitySize: 1e4,
15410
+ maxExpansionDepth: 10,
15411
+ maxTotalExpansions: 1e3,
15412
+ maxExpandedLength: 1e5,
15413
+ allowedTags: null,
15414
+ tagFilter: null
15415
+ };
15416
+ }
15417
+ if (typeof value === "object" && value !== null) {
15418
+ return {
15419
+ enabled: value.enabled !== false,
15420
+ // default true if not specified
15421
+ maxEntitySize: value.maxEntitySize ?? 1e4,
15422
+ maxExpansionDepth: value.maxExpansionDepth ?? 10,
15423
+ maxTotalExpansions: value.maxTotalExpansions ?? 1e3,
15424
+ maxExpandedLength: value.maxExpandedLength ?? 1e5,
15425
+ allowedTags: value.allowedTags ?? null,
15426
+ tagFilter: value.tagFilter ?? null
15427
+ };
15428
+ }
15429
+ return normalizeProcessEntities(true);
15430
+ }
15431
+ var buildOptions = function(options) {
15432
+ const built = Object.assign({}, defaultOptions2, options);
15433
+ built.processEntities = normalizeProcessEntities(built.processEntities);
15434
+ return built;
15435
+ };
15436
+
15437
+ // ../../node_modules/.pnpm/fast-xml-parser@5.3.8/node_modules/fast-xml-parser/src/xmlparser/xmlNode.js
15438
+ var METADATA_SYMBOL;
15439
+ if (typeof Symbol !== "function") {
15440
+ METADATA_SYMBOL = "@@xmlMetadata";
15441
+ } else {
15442
+ METADATA_SYMBOL = /* @__PURE__ */ Symbol("XML Node Metadata");
15443
+ }
15444
+ var XmlNode = class {
15445
+ constructor(tagname) {
15446
+ this.tagname = tagname;
15447
+ this.child = [];
15448
+ this[":@"] = /* @__PURE__ */ Object.create(null);
15449
+ }
15450
+ add(key, val) {
15451
+ if (key === "__proto__") key = "#__proto__";
15452
+ this.child.push({ [key]: val });
15453
+ }
15454
+ addChild(node, startIndex) {
15455
+ if (node.tagname === "__proto__") node.tagname = "#__proto__";
15456
+ if (node[":@"] && Object.keys(node[":@"]).length > 0) {
15457
+ this.child.push({ [node.tagname]: node.child, [":@"]: node[":@"] });
15458
+ } else {
15459
+ this.child.push({ [node.tagname]: node.child });
15460
+ }
15461
+ if (startIndex !== void 0) {
15462
+ this.child[this.child.length - 1][METADATA_SYMBOL] = { startIndex };
15463
+ }
15464
+ }
15465
+ /** symbol used for metadata */
15466
+ static getMetaDataSymbol() {
15467
+ return METADATA_SYMBOL;
15468
+ }
15469
+ };
15470
+
15471
+ // ../../node_modules/.pnpm/fast-xml-parser@5.3.8/node_modules/fast-xml-parser/src/xmlparser/DocTypeReader.js
15472
+ var DocTypeReader = class {
15473
+ constructor(options) {
15474
+ this.suppressValidationErr = !options;
15475
+ this.options = options;
15476
+ }
15477
+ readDocType(xmlData, i) {
15478
+ const entities = /* @__PURE__ */ Object.create(null);
15479
+ if (xmlData[i + 3] === "O" && xmlData[i + 4] === "C" && xmlData[i + 5] === "T" && xmlData[i + 6] === "Y" && xmlData[i + 7] === "P" && xmlData[i + 8] === "E") {
15480
+ i = i + 9;
15481
+ let angleBracketsCount = 1;
15482
+ let hasBody = false, comment = false;
15483
+ let exp = "";
15484
+ for (; i < xmlData.length; i++) {
15485
+ if (xmlData[i] === "<" && !comment) {
15486
+ if (hasBody && hasSeq(xmlData, "!ENTITY", i)) {
15487
+ i += 7;
15488
+ let entityName, val;
15489
+ [entityName, val, i] = this.readEntityExp(xmlData, i + 1, this.suppressValidationErr);
15490
+ if (val.indexOf("&") === -1) {
15491
+ const escaped = entityName.replace(/[.\-+*:]/g, "\\.");
15492
+ entities[entityName] = {
15493
+ regx: RegExp(`&${escaped};`, "g"),
15494
+ val
15495
+ };
15496
+ }
15497
+ } else if (hasBody && hasSeq(xmlData, "!ELEMENT", i)) {
15498
+ i += 8;
15499
+ const { index } = this.readElementExp(xmlData, i + 1);
15500
+ i = index;
15501
+ } else if (hasBody && hasSeq(xmlData, "!ATTLIST", i)) {
15502
+ i += 8;
15503
+ } else if (hasBody && hasSeq(xmlData, "!NOTATION", i)) {
15504
+ i += 9;
15505
+ const { index } = this.readNotationExp(xmlData, i + 1, this.suppressValidationErr);
15506
+ i = index;
15507
+ } else if (hasSeq(xmlData, "!--", i)) comment = true;
15508
+ else throw new Error(`Invalid DOCTYPE`);
15509
+ angleBracketsCount++;
15510
+ exp = "";
15511
+ } else if (xmlData[i] === ">") {
15512
+ if (comment) {
15513
+ if (xmlData[i - 1] === "-" && xmlData[i - 2] === "-") {
15514
+ comment = false;
15515
+ angleBracketsCount--;
15516
+ }
15517
+ } else {
15518
+ angleBracketsCount--;
15519
+ }
15520
+ if (angleBracketsCount === 0) {
15521
+ break;
15522
+ }
15523
+ } else if (xmlData[i] === "[") {
15524
+ hasBody = true;
15525
+ } else {
15526
+ exp += xmlData[i];
15527
+ }
15528
+ }
15529
+ if (angleBracketsCount !== 0) {
15530
+ throw new Error(`Unclosed DOCTYPE`);
15531
+ }
15532
+ } else {
15533
+ throw new Error(`Invalid Tag instead of DOCTYPE`);
15534
+ }
15535
+ return { entities, i };
15536
+ }
15537
+ readEntityExp(xmlData, i) {
15538
+ i = skipWhitespace(xmlData, i);
15539
+ let entityName = "";
15540
+ while (i < xmlData.length && !/\s/.test(xmlData[i]) && xmlData[i] !== '"' && xmlData[i] !== "'") {
15541
+ entityName += xmlData[i];
15542
+ i++;
15543
+ }
15544
+ validateEntityName(entityName);
15545
+ i = skipWhitespace(xmlData, i);
15546
+ if (!this.suppressValidationErr) {
15547
+ if (xmlData.substring(i, i + 6).toUpperCase() === "SYSTEM") {
15548
+ throw new Error("External entities are not supported");
15549
+ } else if (xmlData[i] === "%") {
15550
+ throw new Error("Parameter entities are not supported");
15551
+ }
15552
+ }
15553
+ let entityValue = "";
15554
+ [i, entityValue] = this.readIdentifierVal(xmlData, i, "entity");
15555
+ if (this.options.enabled !== false && this.options.maxEntitySize && entityValue.length > this.options.maxEntitySize) {
15556
+ throw new Error(
15557
+ `Entity "${entityName}" size (${entityValue.length}) exceeds maximum allowed size (${this.options.maxEntitySize})`
15558
+ );
15559
+ }
15560
+ i--;
15561
+ return [entityName, entityValue, i];
15562
+ }
15563
+ readNotationExp(xmlData, i) {
15564
+ i = skipWhitespace(xmlData, i);
15565
+ let notationName = "";
15566
+ while (i < xmlData.length && !/\s/.test(xmlData[i])) {
15567
+ notationName += xmlData[i];
15568
+ i++;
15569
+ }
15570
+ !this.suppressValidationErr && validateEntityName(notationName);
15571
+ i = skipWhitespace(xmlData, i);
15572
+ const identifierType = xmlData.substring(i, i + 6).toUpperCase();
15573
+ if (!this.suppressValidationErr && identifierType !== "SYSTEM" && identifierType !== "PUBLIC") {
15574
+ throw new Error(`Expected SYSTEM or PUBLIC, found "${identifierType}"`);
15575
+ }
15576
+ i += identifierType.length;
15577
+ i = skipWhitespace(xmlData, i);
15578
+ let publicIdentifier = null;
15579
+ let systemIdentifier = null;
15580
+ if (identifierType === "PUBLIC") {
15581
+ [i, publicIdentifier] = this.readIdentifierVal(xmlData, i, "publicIdentifier");
15582
+ i = skipWhitespace(xmlData, i);
15583
+ if (xmlData[i] === '"' || xmlData[i] === "'") {
15584
+ [i, systemIdentifier] = this.readIdentifierVal(xmlData, i, "systemIdentifier");
15585
+ }
15586
+ } else if (identifierType === "SYSTEM") {
15587
+ [i, systemIdentifier] = this.readIdentifierVal(xmlData, i, "systemIdentifier");
15588
+ if (!this.suppressValidationErr && !systemIdentifier) {
15589
+ throw new Error("Missing mandatory system identifier for SYSTEM notation");
15590
+ }
15591
+ }
15592
+ return { notationName, publicIdentifier, systemIdentifier, index: --i };
15593
+ }
15594
+ readIdentifierVal(xmlData, i, type) {
15595
+ let identifierVal = "";
15596
+ const startChar = xmlData[i];
15597
+ if (startChar !== '"' && startChar !== "'") {
15598
+ throw new Error(`Expected quoted string, found "${startChar}"`);
15599
+ }
15600
+ i++;
15601
+ while (i < xmlData.length && xmlData[i] !== startChar) {
15602
+ identifierVal += xmlData[i];
15603
+ i++;
15604
+ }
15605
+ if (xmlData[i] !== startChar) {
15606
+ throw new Error(`Unterminated ${type} value`);
15607
+ }
15608
+ i++;
15609
+ return [i, identifierVal];
15610
+ }
15611
+ readElementExp(xmlData, i) {
15612
+ i = skipWhitespace(xmlData, i);
15613
+ let elementName = "";
15614
+ while (i < xmlData.length && !/\s/.test(xmlData[i])) {
15615
+ elementName += xmlData[i];
15616
+ i++;
15617
+ }
15618
+ if (!this.suppressValidationErr && !isName(elementName)) {
15619
+ throw new Error(`Invalid element name: "${elementName}"`);
15620
+ }
15621
+ i = skipWhitespace(xmlData, i);
15622
+ let contentModel = "";
15623
+ if (xmlData[i] === "E" && hasSeq(xmlData, "MPTY", i)) i += 4;
15624
+ else if (xmlData[i] === "A" && hasSeq(xmlData, "NY", i)) i += 2;
15625
+ else if (xmlData[i] === "(") {
15626
+ i++;
15627
+ while (i < xmlData.length && xmlData[i] !== ")") {
15628
+ contentModel += xmlData[i];
15629
+ i++;
15630
+ }
15631
+ if (xmlData[i] !== ")") {
15632
+ throw new Error("Unterminated content model");
15633
+ }
15634
+ } else if (!this.suppressValidationErr) {
15635
+ throw new Error(`Invalid Element Expression, found "${xmlData[i]}"`);
15636
+ }
15637
+ return {
15638
+ elementName,
15639
+ contentModel: contentModel.trim(),
15640
+ index: i
15641
+ };
15642
+ }
15643
+ readAttlistExp(xmlData, i) {
15644
+ i = skipWhitespace(xmlData, i);
15645
+ let elementName = "";
15646
+ while (i < xmlData.length && !/\s/.test(xmlData[i])) {
15647
+ elementName += xmlData[i];
15648
+ i++;
15649
+ }
15650
+ validateEntityName(elementName);
15651
+ i = skipWhitespace(xmlData, i);
15652
+ let attributeName = "";
15653
+ while (i < xmlData.length && !/\s/.test(xmlData[i])) {
15654
+ attributeName += xmlData[i];
15655
+ i++;
15656
+ }
15657
+ if (!validateEntityName(attributeName)) {
15658
+ throw new Error(`Invalid attribute name: "${attributeName}"`);
15659
+ }
15660
+ i = skipWhitespace(xmlData, i);
15661
+ let attributeType = "";
15662
+ if (xmlData.substring(i, i + 8).toUpperCase() === "NOTATION") {
15663
+ attributeType = "NOTATION";
15664
+ i += 8;
15665
+ i = skipWhitespace(xmlData, i);
15666
+ if (xmlData[i] !== "(") {
15667
+ throw new Error(`Expected '(', found "${xmlData[i]}"`);
15668
+ }
15669
+ i++;
15670
+ let allowedNotations = [];
15671
+ while (i < xmlData.length && xmlData[i] !== ")") {
15672
+ let notation = "";
15673
+ while (i < xmlData.length && xmlData[i] !== "|" && xmlData[i] !== ")") {
15674
+ notation += xmlData[i];
15675
+ i++;
15676
+ }
15677
+ notation = notation.trim();
15678
+ if (!validateEntityName(notation)) {
15679
+ throw new Error(`Invalid notation name: "${notation}"`);
15680
+ }
15681
+ allowedNotations.push(notation);
15682
+ if (xmlData[i] === "|") {
15683
+ i++;
15684
+ i = skipWhitespace(xmlData, i);
15685
+ }
15686
+ }
15687
+ if (xmlData[i] !== ")") {
15688
+ throw new Error("Unterminated list of notations");
15689
+ }
15690
+ i++;
15691
+ attributeType += " (" + allowedNotations.join("|") + ")";
15692
+ } else {
15693
+ while (i < xmlData.length && !/\s/.test(xmlData[i])) {
15694
+ attributeType += xmlData[i];
15695
+ i++;
15696
+ }
15697
+ const validTypes = ["CDATA", "ID", "IDREF", "IDREFS", "ENTITY", "ENTITIES", "NMTOKEN", "NMTOKENS"];
15698
+ if (!this.suppressValidationErr && !validTypes.includes(attributeType.toUpperCase())) {
15699
+ throw new Error(`Invalid attribute type: "${attributeType}"`);
15700
+ }
15701
+ }
15702
+ i = skipWhitespace(xmlData, i);
15703
+ let defaultValue = "";
15704
+ if (xmlData.substring(i, i + 8).toUpperCase() === "#REQUIRED") {
15705
+ defaultValue = "#REQUIRED";
15706
+ i += 8;
15707
+ } else if (xmlData.substring(i, i + 7).toUpperCase() === "#IMPLIED") {
15708
+ defaultValue = "#IMPLIED";
15709
+ i += 7;
15710
+ } else {
15711
+ [i, defaultValue] = this.readIdentifierVal(xmlData, i, "ATTLIST");
15712
+ }
15713
+ return {
15714
+ elementName,
15715
+ attributeName,
15716
+ attributeType,
15717
+ defaultValue,
15718
+ index: i
15719
+ };
15720
+ }
15721
+ };
15722
+ var skipWhitespace = (data, index) => {
15723
+ while (index < data.length && /\s/.test(data[index])) {
15724
+ index++;
15725
+ }
15726
+ return index;
15727
+ };
15728
+ function hasSeq(data, seq, i) {
15729
+ for (let j = 0; j < seq.length; j++) {
15730
+ if (seq[j] !== data[i + j + 1]) return false;
15731
+ }
15732
+ return true;
15733
+ }
15734
+ function validateEntityName(name) {
15735
+ if (isName(name))
15736
+ return name;
15737
+ else
15738
+ throw new Error(`Invalid entity name ${name}`);
15739
+ }
15740
+
15741
+ // ../../node_modules/.pnpm/anynum@1.0.1/node_modules/anynum/digitTable.js
15742
+ var SCRIPT_ZEROS = [
15743
+ // Basic Latin (ASCII) — included for completeness / pass-through
15744
+ 48,
15745
+ // 0-9
15746
+ // Arabic scripts
15747
+ 1632,
15748
+ // Arabic-Indic ٠١٢٣٤٥٦٧٨٩
15749
+ 1776,
15750
+ // Extended Arabic-Indic (Urdu/Persian/Sindhi) ۰۱۲۳
15751
+ // Indic scripts
15752
+ 2406,
15753
+ // Devanagari ०१२३४५६७८९
15754
+ 2534,
15755
+ // Bengali ০১২৩৪৫৬৭৮৯
15756
+ 2662,
15757
+ // Gurmukhi ੦੧੨੩੪੫੬੭੮੯
15758
+ 2790,
15759
+ // Gujarati ૦૧૨૩૪૫૬૭૮૯
15760
+ 2918,
15761
+ // Odia ୦୧୨୩୪୫୬୭୮୯
15762
+ 3046,
15763
+ // Tamil ௦௧௨௩௪௫௬௭௮௯
15764
+ 3174,
15765
+ // Telugu ౦౧౨౩౪౫౬౭౮౯
15766
+ 3302,
15767
+ // Kannada ೦೧೨೩೪೫೬೭೮೯
15768
+ 3430,
15769
+ // Malayalam ൦൧൨൩൪൫൬൭൮൯
15770
+ 3558,
15771
+ // Sinhala Archaic ෦෧෨෩෪෫෬෭෮෯
15772
+ // Southeast Asian scripts
15773
+ 3664,
15774
+ // Thai ๐๑๒๓๔๕๖๗๘๙
15775
+ 3792,
15776
+ // Lao ໐໑໒໓໔໕໖໗໘໙
15777
+ 3872,
15778
+ // Tibetan ༠༡༢༣༤༥༦༧༨༩
15779
+ 4160,
15780
+ // Myanmar ၀၁၂၃၄၅၆၇၈၉
15781
+ 4240,
15782
+ // Myanmar Shan ႐႑႒႓႔႕႖႗႘႙
15783
+ 6112,
15784
+ // Khmer ០១២៣៤៥៦៧៨៩
15785
+ 6160,
15786
+ // Mongolian ᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙
15787
+ 6470,
15788
+ // Limbu ᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏
15789
+ 6608,
15790
+ // New Tai Lue ᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙
15791
+ 6784,
15792
+ // Tai Tham Hora ᪀᪁᪂᪃᪄᪅᪆᪇᪈᪉
15793
+ 6800,
15794
+ // Tai Tham Tham ᪐᪑᪒᪓᪔᪕᪖᪗᪘᪙
15795
+ 6992,
15796
+ // Balinese ᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙
15797
+ 7088,
15798
+ // Sundanese ᮰᮱᮲᮳᮴᮵᮶᮷᮸᮹
15799
+ 7232,
15800
+ // Lepcha ᱀᱁᱂᱃᱄᱅᱆᱇᱈᱉
15801
+ 7248,
15802
+ // Ol Chiki ᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙
15803
+ // Fullwidth (CJK context)
15804
+ 65296,
15805
+ // Fullwidth 0123456789
15806
+ // Mathematical digit variants (Unicode math block)
15807
+ 120782,
15808
+ // Mathematical Bold
15809
+ 120792,
15810
+ // Mathematical Double-Struck
15811
+ 120802,
15812
+ // Mathematical Sans-Serif
15813
+ 120812,
15814
+ // Mathematical Sans-Serif Bold
15815
+ 120822,
15816
+ // Mathematical Monospace
15817
+ // Other scripts
15818
+ 66720,
15819
+ // Osmanya 𐒠𐒡𐒢𐒣𐒤𐒥𐒦𐒧𐒨𐒩
15820
+ 68912,
15821
+ // Hanifi Rohingya 𐴰𐴱𐴲𐴳𐴴𐴵𐴶𐴷𐴸𐴹
15822
+ 69734,
15823
+ // Brahmi 𑁦𑁧𑁨𑁩𑁪𑁫𑁬𑁭𑁮𑁯
15824
+ 69872,
15825
+ // Sora Sompeng 𑃰𑃱𑃲𑃳𑃴𑃵𑃶𑃷𑃸𑃹
15826
+ 69942,
15827
+ // Chakma 𑄶𑄷𑄸𑄹𑄺𑄻𑄼𑄽𑄾𑄿
15828
+ 70096,
15829
+ // Sharada 𑇐𑇑𑇒𑇓𑇔𑇕𑇖𑇗𑇘𑇙
15830
+ 70384,
15831
+ // Khudawadi 𑋰𑋱𑋲𑋳𑋴𑋵𑋶𑋷𑋸𑋹
15832
+ 70736,
15833
+ // Newa 𑑐𑑑𑑒𑑓𑑔𑑕𑑖𑑗𑑘𑑙
15834
+ 70864,
15835
+ // Tirhuta 𑓐𑓑𑓒𑓓𑓔𑓕𑓖𑓗𑓘𑓙
15836
+ 71248,
15837
+ // Modi 𑙐𑙑𑙒𑙓𑙔𑙕𑙖𑙗𑙘𑙙
15838
+ 71360,
15839
+ // Takri 𑛀𑛁𑛂𑛃𑛄𑛅𑛆𑛇𑛈𑛉
15840
+ 71472,
15841
+ // Ahom 𑜰𑜱𑜲𑜳𑜴𑜵𑜶𑜷𑜸𑜹
15842
+ 71904,
15843
+ // Warang Citi 𑣠𑣡𑣢𑣣𑣤𑣥𑣦𑣧𑣨𑣩
15844
+ 72016,
15845
+ // Dives Akuru 𑥐𑥑𑥒𑥓𑥔𑥕𑥖𑥗𑥘𑥙
15846
+ 72688,
15847
+ // Khitan Small Script 𑯰𑯱𑯲𑯳𑯴𑯵𑯶𑯷𑯸𑯹
15848
+ 72784,
15849
+ // Bhaiksuki 𑱐𑱑𑱒𑱓𑱔𑱕𑱖𑱗𑱘𑱙
15850
+ 73040,
15851
+ // Masaram Gondi 𑵐𑵑𑵒𑵓𑵔𑵕𑵖𑵗𑵘𑵙
15852
+ 73120,
15853
+ // Gunjala Gondi 𑶠𑶡𑶢𑶣𑶤𑶥𑶦𑶧𑶨𑶩
15854
+ 73552,
15855
+ // Kawi 𑽐𑽑𑽒𑽓𑽔𑽕𑽖𑽗𑽘𑽙
15856
+ 92768,
15857
+ // Mro 𖩠𖩡𖩢𖩣𖩤𖩥𖩦𖩧𖩨𖩩
15858
+ 92864,
15859
+ // Tangsa 𖫀𖫁𖫂𖫃𖫄𖫅𖫆𖫇𖫈𖫉
15860
+ 93008,
15861
+ // Pahawh Hmong 𖭐𖭑𖭒𖭓𖭔𖭕𖭖𖭗𖭘𖭙
15862
+ 123200,
15863
+ // Nyiakeng Puachue Hmong 𞅀𞅁𞅂𞅃𞅄𞅅𞅆𞅇𞅈𞅉
15864
+ 123632,
15865
+ // Wancho 𞋰𞋱𞋲𞋳𞋴𞋵𞋶𞋷𞋸𞋹
15866
+ 124144,
15867
+ // Nag Mundari 𞓰𞓱𞓲𞓳𞓴𞓵𞓶𞓷𞓸𞓹
15868
+ 125264,
15869
+ // Adlam 𞥐𞥑𞥒𞥓𞥔𞥕𞥖𞥗𞥘𞥙
15870
+ 130032
15871
+ // Segmented digit symbols 🯰🯱🯲🯳🯴🯵🯶🯷🯸🯹
15872
+ ];
15873
+ var NOT_DIGIT = 255;
15874
+ var HIGH_MAP = /* @__PURE__ */ new Map();
15875
+ var LOW_MAX = 65535;
15876
+ var LOW_MIN = 1632;
15877
+ var TABLE_OFFSET = LOW_MIN;
15878
+ var TABLE_SIZE = LOW_MAX - LOW_MIN + 1;
15879
+ var TABLE = new Uint8Array(TABLE_SIZE).fill(NOT_DIGIT);
15880
+ for (const zero of SCRIPT_ZEROS) {
15881
+ for (let d = 0; d < 10; d++) {
15882
+ const cp = zero + d;
15883
+ if (cp <= LOW_MAX) {
15884
+ TABLE[cp - TABLE_OFFSET] = d;
15885
+ } else {
15886
+ HIGH_MAP.set(cp, d);
15887
+ }
15888
+ }
15889
+ }
15890
+
15891
+ // ../../node_modules/.pnpm/anynum@1.0.1/node_modules/anynum/anynum.js
15892
+ var CHAR_0 = 48;
15893
+ var CHAR_9 = 57;
15894
+ var CHAR_MINUS = 45;
15895
+ var MINUS_SET = /* @__PURE__ */ new Set([8722, 65293, 65123]);
15896
+ function anynum(str2) {
15897
+ if (typeof str2 !== "string") return str2;
15898
+ const len = str2.length;
15899
+ if (len === 0) return str2;
15900
+ let firstHit = -1;
15901
+ for (let i = 0; i < len; i++) {
15902
+ const cc = str2.charCodeAt(i);
15903
+ if (cc >= CHAR_0 && cc <= CHAR_9 || cc === CHAR_MINUS) continue;
15904
+ if (cc < TABLE_OFFSET) {
15905
+ if (MINUS_SET.has(cc)) {
15906
+ firstHit = i;
15907
+ break;
15908
+ }
15909
+ continue;
15910
+ }
15911
+ if (cc >= 55296 && cc <= 56319) {
15912
+ if (i + 1 < len) {
15913
+ const low = str2.charCodeAt(i + 1);
15914
+ if (low >= 56320 && low <= 57343) {
15915
+ const cp = 65536 + (cc - 55296 << 10) + (low - 56320);
15916
+ if (HIGH_MAP.has(cp)) {
15917
+ firstHit = i;
15918
+ break;
15919
+ }
15920
+ }
15921
+ }
15922
+ continue;
15923
+ }
15924
+ if (TABLE[cc - TABLE_OFFSET] !== NOT_DIGIT || MINUS_SET.has(cc)) {
15925
+ firstHit = i;
15926
+ break;
15927
+ }
15928
+ }
15929
+ if (firstHit === -1) return str2;
15930
+ const chars = [];
15931
+ if (firstHit > 0) chars.push(str2.slice(0, firstHit));
15932
+ for (let i = firstHit; i < len; i++) {
15933
+ const cc = str2.charCodeAt(i);
15934
+ if (cc >= CHAR_0 && cc <= CHAR_9 || cc === CHAR_MINUS) {
15935
+ chars.push(str2[i]);
15936
+ continue;
15937
+ }
15938
+ if (cc < TABLE_OFFSET) {
15939
+ chars.push(MINUS_SET.has(cc) ? "-" : str2[i]);
15940
+ continue;
15941
+ }
15942
+ if (cc >= 55296 && cc <= 56319) {
15943
+ if (i + 1 < len) {
15944
+ const low = str2.charCodeAt(i + 1);
15945
+ if (low >= 56320 && low <= 57343) {
15946
+ const cp = 65536 + (cc - 55296 << 10) + (low - 56320);
15947
+ const d2 = HIGH_MAP.get(cp);
15948
+ if (d2 !== void 0) {
15949
+ chars.push(String.fromCharCode(d2 + 48));
15950
+ i++;
15951
+ continue;
15952
+ }
15953
+ }
15954
+ }
15955
+ chars.push(str2[i]);
15956
+ continue;
15957
+ }
15958
+ if (MINUS_SET.has(cc)) {
15959
+ chars.push("-");
15960
+ continue;
15961
+ }
15962
+ const d = TABLE[cc - TABLE_OFFSET];
15963
+ chars.push(d !== NOT_DIGIT ? String.fromCharCode(d + 48) : str2[i]);
15964
+ }
15965
+ return chars.join("");
15966
+ }
15967
+ var anynum_default = anynum;
15968
+
15969
+ // ../../node_modules/.pnpm/strnum@2.4.2/node_modules/strnum/strnum.js
15970
+ var hexRegex = /^[-+]?0x[a-fA-F0-9]+$/;
15971
+ var binRegex = /^0b[01]+$/;
15972
+ var octRegex = /^0o[0-7]+$/;
15973
+ var numRegex = /^([\-\+])?(0*)([0-9]*(\.[0-9]*)?)$/;
15974
+ var consider = {
15975
+ hex: true,
15976
+ binary: false,
15977
+ octal: false,
15978
+ leadingZeros: true,
15979
+ decimalPoint: ".",
15980
+ eNotation: true,
15981
+ //skipLike: /regex/,
15982
+ infinity: "original",
15983
+ // "null", "infinity" (Infinity type), "string" ("Infinity" (the string literal))
15984
+ unicode: false
15985
+ };
15986
+ function toNumber(str2, options = {}) {
15987
+ options = Object.assign({}, consider, options);
15988
+ if (!str2 || typeof str2 !== "string") return str2;
15989
+ let trimmedStr = str2.trim();
15990
+ if (trimmedStr.length === 0) return str2;
15991
+ else if (options.skipLike !== void 0 && options.skipLike.test(trimmedStr)) return str2;
15992
+ else if (trimmedStr === "0") return 0;
15993
+ if (options.unicode) {
15994
+ trimmedStr = anynum_default(trimmedStr);
15995
+ if (trimmedStr === "0") return 0;
15996
+ }
15997
+ if (options.hex && hexRegex.test(trimmedStr)) {
15998
+ return parse_int(trimmedStr, 16);
15999
+ } else if (options.binary && binRegex.test(trimmedStr)) {
16000
+ return parse_int(trimmedStr, 2);
16001
+ } else if (options.octal && octRegex.test(trimmedStr)) {
16002
+ return parse_int(trimmedStr, 8);
16003
+ } else if (!isFinite(trimmedStr)) {
16004
+ return handleInfinity(str2, Number(trimmedStr), options);
16005
+ } else if (trimmedStr.includes("e") || trimmedStr.includes("E")) {
16006
+ return resolveEnotation(str2, trimmedStr, options);
16007
+ } else {
16008
+ const match = numRegex.exec(trimmedStr);
16009
+ if (match) {
16010
+ const sign = match[1] || "";
16011
+ const leadingZeros = match[2];
16012
+ let numTrimmedByZeros = trimZeros(match[3]);
16013
+ const decimalAdjacentToLeadingZeros = sign ? (
16014
+ // 0., -00., 000.
16015
+ str2[leadingZeros.length + 1] === "."
16016
+ ) : str2[leadingZeros.length] === ".";
16017
+ if (!options.leadingZeros && (leadingZeros.length > 1 || leadingZeros.length === 1 && !decimalAdjacentToLeadingZeros)) {
16018
+ return str2;
16019
+ } else {
16020
+ const num2 = Number(trimmedStr);
16021
+ const parsedStr = String(num2);
16022
+ if (num2 === 0) return num2;
16023
+ if (parsedStr.search(/[eE]/) !== -1) {
16024
+ if (options.eNotation) return num2;
16025
+ else return str2;
16026
+ } else if (trimmedStr.indexOf(".") !== -1) {
16027
+ if (parsedStr === "0") return num2;
16028
+ else if (parsedStr === numTrimmedByZeros) return num2;
16029
+ else if (parsedStr === `${sign}${numTrimmedByZeros}`) return num2;
16030
+ else return str2;
16031
+ }
16032
+ let n = leadingZeros ? numTrimmedByZeros : trimmedStr;
16033
+ if (leadingZeros) {
16034
+ return n === parsedStr || sign + n === parsedStr ? num2 : str2;
16035
+ } else {
16036
+ return n === parsedStr || n === sign + parsedStr ? num2 : str2;
16037
+ }
16038
+ }
16039
+ } else {
16040
+ return str2;
16041
+ }
16042
+ }
16043
+ }
16044
+ var eNotationRegx = /^([-+])?(0*)(\d*(\.\d*)?[eE][-\+]?\d+)$/;
16045
+ function resolveEnotation(str2, trimmedStr, options) {
16046
+ if (!options.eNotation) return str2;
16047
+ const notation = trimmedStr.match(eNotationRegx);
16048
+ if (notation) {
16049
+ let sign = notation[1] || "";
16050
+ const eChar = notation[3].indexOf("e") === -1 ? "E" : "e";
16051
+ const leadingZeros = notation[2];
16052
+ const eAdjacentToLeadingZeros = sign ? (
16053
+ // 0E.
16054
+ str2[leadingZeros.length + 1] === eChar
16055
+ ) : str2[leadingZeros.length] === eChar;
16056
+ if (leadingZeros.length > 1 && eAdjacentToLeadingZeros) return str2;
16057
+ else if (leadingZeros.length === 1 && (notation[3].startsWith(`.${eChar}`) || notation[3][0] === eChar)) {
16058
+ return Number(trimmedStr);
16059
+ } else if (leadingZeros.length > 0) {
16060
+ if (options.leadingZeros && !eAdjacentToLeadingZeros) {
16061
+ trimmedStr = (notation[1] || "") + notation[3];
16062
+ return Number(trimmedStr);
16063
+ } else return str2;
16064
+ } else {
16065
+ return Number(trimmedStr);
16066
+ }
16067
+ } else {
16068
+ return str2;
16069
+ }
16070
+ }
16071
+ function trimZeros(numStr) {
16072
+ if (numStr && numStr.indexOf(".") !== -1) {
16073
+ let end = numStr.length;
16074
+ while (end > 0 && numStr.charCodeAt(end - 1) === 48) end--;
16075
+ numStr = numStr.slice(0, end);
16076
+ if (numStr === ".") numStr = "0";
16077
+ else if (numStr[0] === ".") numStr = "0" + numStr;
16078
+ else if (numStr[numStr.length - 1] === ".") numStr = numStr.substring(0, numStr.length - 1);
16079
+ return numStr;
16080
+ }
16081
+ return numStr;
16082
+ }
16083
+ function parse_int(numStr, base) {
16084
+ const str2 = numStr.trim();
16085
+ if (base === 2 || base === 8) numStr = str2.substring(2);
16086
+ if (parseInt) return parseInt(numStr, base);
16087
+ else if (Number.parseInt) return Number.parseInt(numStr, base);
16088
+ else if (window && window.parseInt) return window.parseInt(numStr, base);
16089
+ else throw new Error("parseInt, Number.parseInt, window.parseInt are not supported");
16090
+ }
16091
+ function handleInfinity(str2, num2, options) {
16092
+ const isPositive = num2 === Infinity;
16093
+ switch (options.infinity.toLowerCase()) {
16094
+ case "null":
16095
+ return null;
16096
+ case "infinity":
16097
+ return num2;
16098
+ // Return Infinity or -Infinity
16099
+ case "string":
16100
+ return isPositive ? "Infinity" : "-Infinity";
16101
+ case "original":
16102
+ default:
16103
+ return str2;
16104
+ }
16105
+ }
16106
+
16107
+ // ../../node_modules/.pnpm/fast-xml-parser@5.3.8/node_modules/fast-xml-parser/src/ignoreAttributes.js
16108
+ function getIgnoreAttributesFn(ignoreAttributes) {
16109
+ if (typeof ignoreAttributes === "function") {
16110
+ return ignoreAttributes;
16111
+ }
16112
+ if (Array.isArray(ignoreAttributes)) {
16113
+ return (attrName) => {
16114
+ for (const pattern of ignoreAttributes) {
16115
+ if (typeof pattern === "string" && attrName === pattern) {
16116
+ return true;
16117
+ }
16118
+ if (pattern instanceof RegExp && pattern.test(attrName)) {
16119
+ return true;
16120
+ }
16121
+ }
16122
+ };
16123
+ }
16124
+ return () => false;
16125
+ }
16126
+
16127
+ // ../../node_modules/.pnpm/fast-xml-parser@5.3.8/node_modules/fast-xml-parser/src/xmlparser/OrderedObjParser.js
16128
+ var OrderedObjParser = class {
16129
+ constructor(options) {
16130
+ this.options = options;
16131
+ this.currentNode = null;
16132
+ this.tagsNodeStack = [];
16133
+ this.docTypeEntities = {};
16134
+ this.lastEntities = {
16135
+ "apos": { regex: /&(apos|#39|#x27);/g, val: "'" },
16136
+ "gt": { regex: /&(gt|#62|#x3E);/g, val: ">" },
16137
+ "lt": { regex: /&(lt|#60|#x3C);/g, val: "<" },
16138
+ "quot": { regex: /&(quot|#34|#x22);/g, val: '"' }
16139
+ };
16140
+ this.ampEntity = { regex: /&(amp|#38|#x26);/g, val: "&" };
16141
+ this.htmlEntities = {
16142
+ "space": { regex: /&(nbsp|#160);/g, val: " " },
16143
+ // "lt" : { regex: /&(lt|#60);/g, val: "<" },
16144
+ // "gt" : { regex: /&(gt|#62);/g, val: ">" },
16145
+ // "amp" : { regex: /&(amp|#38);/g, val: "&" },
16146
+ // "quot" : { regex: /&(quot|#34);/g, val: "\"" },
16147
+ // "apos" : { regex: /&(apos|#39);/g, val: "'" },
16148
+ "cent": { regex: /&(cent|#162);/g, val: "\xA2" },
16149
+ "pound": { regex: /&(pound|#163);/g, val: "\xA3" },
16150
+ "yen": { regex: /&(yen|#165);/g, val: "\xA5" },
16151
+ "euro": { regex: /&(euro|#8364);/g, val: "\u20AC" },
16152
+ "copyright": { regex: /&(copy|#169);/g, val: "\xA9" },
16153
+ "reg": { regex: /&(reg|#174);/g, val: "\xAE" },
16154
+ "inr": { regex: /&(inr|#8377);/g, val: "\u20B9" },
16155
+ "num_dec": { regex: /&#([0-9]{1,7});/g, val: (_, str2) => fromCodePoint(str2, 10, "&#") },
16156
+ "num_hex": { regex: /&#x([0-9a-fA-F]{1,6});/g, val: (_, str2) => fromCodePoint(str2, 16, "&#x") }
16157
+ };
16158
+ this.addExternalEntities = addExternalEntities;
16159
+ this.parseXml = parseXml;
16160
+ this.parseTextData = parseTextData;
16161
+ this.resolveNameSpace = resolveNameSpace;
16162
+ this.buildAttributesMap = buildAttributesMap;
16163
+ this.isItStopNode = isItStopNode;
16164
+ this.replaceEntitiesValue = replaceEntitiesValue;
16165
+ this.readStopNodeData = readStopNodeData;
16166
+ this.saveTextToParentTag = saveTextToParentTag;
16167
+ this.addChild = addChild;
16168
+ this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes);
16169
+ this.entityExpansionCount = 0;
16170
+ this.currentExpandedLength = 0;
16171
+ if (this.options.stopNodes && this.options.stopNodes.length > 0) {
16172
+ this.stopNodesExact = /* @__PURE__ */ new Set();
16173
+ this.stopNodesWildcard = /* @__PURE__ */ new Set();
16174
+ for (let i = 0; i < this.options.stopNodes.length; i++) {
16175
+ const stopNodeExp = this.options.stopNodes[i];
16176
+ if (typeof stopNodeExp !== "string") continue;
16177
+ if (stopNodeExp.startsWith("*.")) {
16178
+ this.stopNodesWildcard.add(stopNodeExp.substring(2));
16179
+ } else {
16180
+ this.stopNodesExact.add(stopNodeExp);
16181
+ }
16182
+ }
16183
+ }
16184
+ }
16185
+ };
16186
+ function addExternalEntities(externalEntities) {
16187
+ const entKeys = Object.keys(externalEntities);
16188
+ for (let i = 0; i < entKeys.length; i++) {
16189
+ const ent = entKeys[i];
16190
+ const escaped = ent.replace(/[.\-+*:]/g, "\\.");
16191
+ this.lastEntities[ent] = {
16192
+ regex: new RegExp("&" + escaped + ";", "g"),
16193
+ val: externalEntities[ent]
16194
+ };
16195
+ }
16196
+ }
16197
+ function parseTextData(val, tagName, jPath, dontTrim, hasAttributes, isLeafNode, escapeEntities) {
16198
+ if (val !== void 0) {
16199
+ if (this.options.trimValues && !dontTrim) {
16200
+ val = val.trim();
16201
+ }
16202
+ if (val.length > 0) {
16203
+ if (!escapeEntities) val = this.replaceEntitiesValue(val, tagName, jPath);
16204
+ const newval = this.options.tagValueProcessor(tagName, val, jPath, hasAttributes, isLeafNode);
16205
+ if (newval === null || newval === void 0) {
16206
+ return val;
16207
+ } else if (typeof newval !== typeof val || newval !== val) {
16208
+ return newval;
16209
+ } else if (this.options.trimValues) {
16210
+ return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions);
16211
+ } else {
16212
+ const trimmedVal = val.trim();
16213
+ if (trimmedVal === val) {
16214
+ return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions);
16215
+ } else {
16216
+ return val;
16217
+ }
16218
+ }
16219
+ }
16220
+ }
16221
+ }
16222
+ function resolveNameSpace(tagname) {
16223
+ if (this.options.removeNSPrefix) {
16224
+ const tags = tagname.split(":");
16225
+ const prefix = tagname.charAt(0) === "/" ? "/" : "";
16226
+ if (tags[0] === "xmlns") {
16227
+ return "";
16228
+ }
16229
+ if (tags.length === 2) {
16230
+ tagname = prefix + tags[1];
16231
+ }
16232
+ }
16233
+ return tagname;
16234
+ }
16235
+ var attrsRegx = new RegExp(`([^\\s=]+)\\s*(=\\s*(['"])([\\s\\S]*?)\\3)?`, "gm");
16236
+ function buildAttributesMap(attrStr, jPath, tagName) {
16237
+ if (this.options.ignoreAttributes !== true && typeof attrStr === "string") {
16238
+ const matches = getAllMatches(attrStr, attrsRegx);
16239
+ const len = matches.length;
16240
+ const attrs = {};
16241
+ for (let i = 0; i < len; i++) {
16242
+ const attrName = this.resolveNameSpace(matches[i][1]);
16243
+ if (this.ignoreAttributesFn(attrName, jPath)) {
16244
+ continue;
16245
+ }
16246
+ let oldVal = matches[i][4];
16247
+ let aName = this.options.attributeNamePrefix + attrName;
16248
+ if (attrName.length) {
16249
+ if (this.options.transformAttributeName) {
16250
+ aName = this.options.transformAttributeName(aName);
16251
+ }
16252
+ if (aName === "__proto__") aName = "#__proto__";
16253
+ if (oldVal !== void 0) {
16254
+ if (this.options.trimValues) {
16255
+ oldVal = oldVal.trim();
16256
+ }
16257
+ oldVal = this.replaceEntitiesValue(oldVal, tagName, jPath);
16258
+ const newVal = this.options.attributeValueProcessor(attrName, oldVal, jPath);
16259
+ if (newVal === null || newVal === void 0) {
16260
+ attrs[aName] = oldVal;
16261
+ } else if (typeof newVal !== typeof oldVal || newVal !== oldVal) {
16262
+ attrs[aName] = newVal;
16263
+ } else {
16264
+ attrs[aName] = parseValue(
16265
+ oldVal,
16266
+ this.options.parseAttributeValue,
16267
+ this.options.numberParseOptions
16268
+ );
16269
+ }
16270
+ } else if (this.options.allowBooleanAttributes) {
16271
+ attrs[aName] = true;
16272
+ }
16273
+ }
16274
+ }
16275
+ if (!Object.keys(attrs).length) {
16276
+ return;
16277
+ }
16278
+ if (this.options.attributesGroupName) {
16279
+ const attrCollection = {};
16280
+ attrCollection[this.options.attributesGroupName] = attrs;
16281
+ return attrCollection;
16282
+ }
16283
+ return attrs;
16284
+ }
16285
+ }
16286
+ var parseXml = function(xmlData) {
16287
+ xmlData = xmlData.replace(/\r\n?/g, "\n");
16288
+ const xmlObj = new XmlNode("!xml");
16289
+ let currentNode = xmlObj;
16290
+ let textData = "";
16291
+ let jPath = "";
16292
+ this.entityExpansionCount = 0;
16293
+ this.currentExpandedLength = 0;
16294
+ const docTypeReader = new DocTypeReader(this.options.processEntities);
16295
+ for (let i = 0; i < xmlData.length; i++) {
16296
+ const ch = xmlData[i];
16297
+ if (ch === "<") {
16298
+ if (xmlData[i + 1] === "/") {
16299
+ const closeIndex = findClosingIndex(xmlData, ">", i, "Closing Tag is not closed.");
16300
+ let tagName = xmlData.substring(i + 2, closeIndex).trim();
16301
+ if (this.options.removeNSPrefix) {
16302
+ const colonIndex = tagName.indexOf(":");
16303
+ if (colonIndex !== -1) {
16304
+ tagName = tagName.substr(colonIndex + 1);
16305
+ }
16306
+ }
16307
+ if (this.options.transformTagName) {
16308
+ tagName = this.options.transformTagName(tagName);
16309
+ }
16310
+ if (currentNode) {
16311
+ textData = this.saveTextToParentTag(textData, currentNode, jPath);
16312
+ }
16313
+ const lastTagName = jPath.substring(jPath.lastIndexOf(".") + 1);
16314
+ if (tagName && this.options.unpairedTags.indexOf(tagName) !== -1) {
16315
+ throw new Error(`Unpaired tag can not be used as closing tag: </${tagName}>`);
16316
+ }
16317
+ let propIndex = 0;
16318
+ if (lastTagName && this.options.unpairedTags.indexOf(lastTagName) !== -1) {
16319
+ propIndex = jPath.lastIndexOf(".", jPath.lastIndexOf(".") - 1);
16320
+ this.tagsNodeStack.pop();
16321
+ } else {
16322
+ propIndex = jPath.lastIndexOf(".");
16323
+ }
16324
+ jPath = jPath.substring(0, propIndex);
16325
+ currentNode = this.tagsNodeStack.pop();
16326
+ textData = "";
16327
+ i = closeIndex;
16328
+ } else if (xmlData[i + 1] === "?") {
16329
+ let tagData = readTagExp(xmlData, i, false, "?>");
16330
+ if (!tagData) throw new Error("Pi Tag is not closed.");
16331
+ textData = this.saveTextToParentTag(textData, currentNode, jPath);
16332
+ if (this.options.ignoreDeclaration && tagData.tagName === "?xml" || this.options.ignorePiTags) {
16333
+ } else {
16334
+ const childNode = new XmlNode(tagData.tagName);
16335
+ childNode.add(this.options.textNodeName, "");
16336
+ if (tagData.tagName !== tagData.tagExp && tagData.attrExpPresent) {
16337
+ childNode[":@"] = this.buildAttributesMap(tagData.tagExp, jPath, tagData.tagName);
16338
+ }
16339
+ this.addChild(currentNode, childNode, jPath, i);
16340
+ }
16341
+ i = tagData.closeIndex + 1;
16342
+ } else if (xmlData.substr(i + 1, 3) === "!--") {
16343
+ const endIndex = findClosingIndex(xmlData, "-->", i + 4, "Comment is not closed.");
16344
+ if (this.options.commentPropName) {
16345
+ const comment = xmlData.substring(i + 4, endIndex - 2);
16346
+ textData = this.saveTextToParentTag(textData, currentNode, jPath);
16347
+ currentNode.add(this.options.commentPropName, [{ [this.options.textNodeName]: comment }]);
16348
+ }
16349
+ i = endIndex;
16350
+ } else if (xmlData.substr(i + 1, 2) === "!D") {
16351
+ const result = docTypeReader.readDocType(xmlData, i);
16352
+ this.docTypeEntities = result.entities;
16353
+ i = result.i;
16354
+ } else if (xmlData.substr(i + 1, 2) === "![") {
16355
+ const closeIndex = findClosingIndex(xmlData, "]]>", i, "CDATA is not closed.") - 2;
16356
+ const tagExp = xmlData.substring(i + 9, closeIndex);
16357
+ textData = this.saveTextToParentTag(textData, currentNode, jPath);
16358
+ let val = this.parseTextData(tagExp, currentNode.tagname, jPath, true, false, true, true);
16359
+ if (val == void 0) val = "";
16360
+ if (this.options.cdataPropName) {
16361
+ currentNode.add(this.options.cdataPropName, [{ [this.options.textNodeName]: tagExp }]);
16362
+ } else {
16363
+ currentNode.add(this.options.textNodeName, val);
16364
+ }
16365
+ i = closeIndex + 2;
16366
+ } else {
16367
+ let result = readTagExp(xmlData, i, this.options.removeNSPrefix);
16368
+ let tagName = result.tagName;
16369
+ const rawTagName = result.rawTagName;
16370
+ let tagExp = result.tagExp;
16371
+ let attrExpPresent = result.attrExpPresent;
16372
+ let closeIndex = result.closeIndex;
16373
+ if (this.options.transformTagName) {
16374
+ const newTagName = this.options.transformTagName(tagName);
16375
+ if (tagExp === tagName) {
16376
+ tagExp = newTagName;
16377
+ }
16378
+ tagName = newTagName;
16379
+ }
16380
+ if (currentNode && textData) {
16381
+ if (currentNode.tagname !== "!xml") {
16382
+ textData = this.saveTextToParentTag(textData, currentNode, jPath, false);
16383
+ }
16384
+ }
16385
+ const lastTag = currentNode;
16386
+ if (lastTag && this.options.unpairedTags.indexOf(lastTag.tagname) !== -1) {
16387
+ currentNode = this.tagsNodeStack.pop();
16388
+ jPath = jPath.substring(0, jPath.lastIndexOf("."));
16389
+ }
16390
+ if (tagName !== xmlObj.tagname) {
16391
+ jPath += jPath ? "." + tagName : tagName;
16392
+ }
16393
+ const startIndex = i;
16394
+ if (this.isItStopNode(this.stopNodesExact, this.stopNodesWildcard, jPath, tagName)) {
16395
+ let tagContent = "";
16396
+ if (tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1) {
16397
+ if (tagName[tagName.length - 1] === "/") {
16398
+ tagName = tagName.substr(0, tagName.length - 1);
16399
+ jPath = jPath.substr(0, jPath.length - 1);
16400
+ tagExp = tagName;
16401
+ } else {
16402
+ tagExp = tagExp.substr(0, tagExp.length - 1);
16403
+ }
16404
+ i = result.closeIndex;
16405
+ } else if (this.options.unpairedTags.indexOf(tagName) !== -1) {
16406
+ i = result.closeIndex;
16407
+ } else {
16408
+ const result2 = this.readStopNodeData(xmlData, rawTagName, closeIndex + 1);
16409
+ if (!result2) throw new Error(`Unexpected end of ${rawTagName}`);
16410
+ i = result2.i;
16411
+ tagContent = result2.tagContent;
16412
+ }
16413
+ const childNode = new XmlNode(tagName);
16414
+ if (tagName !== tagExp && attrExpPresent) {
16415
+ childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
16416
+ }
16417
+ if (tagContent) {
16418
+ tagContent = this.parseTextData(tagContent, tagName, jPath, true, attrExpPresent, true, true);
16419
+ }
16420
+ jPath = jPath.substr(0, jPath.lastIndexOf("."));
16421
+ childNode.add(this.options.textNodeName, tagContent);
16422
+ this.addChild(currentNode, childNode, jPath, startIndex);
16423
+ } else {
16424
+ if (tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1) {
16425
+ if (tagName[tagName.length - 1] === "/") {
16426
+ tagName = tagName.substr(0, tagName.length - 1);
16427
+ jPath = jPath.substr(0, jPath.length - 1);
16428
+ tagExp = tagName;
16429
+ } else {
16430
+ tagExp = tagExp.substr(0, tagExp.length - 1);
16431
+ }
16432
+ if (this.options.transformTagName) {
16433
+ const newTagName = this.options.transformTagName(tagName);
16434
+ if (tagExp === tagName) {
16435
+ tagExp = newTagName;
16436
+ }
16437
+ tagName = newTagName;
16438
+ }
16439
+ const childNode = new XmlNode(tagName);
16440
+ if (tagName !== tagExp && attrExpPresent) {
16441
+ childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
16442
+ }
16443
+ this.addChild(currentNode, childNode, jPath, startIndex);
16444
+ jPath = jPath.substr(0, jPath.lastIndexOf("."));
16445
+ } else {
16446
+ const childNode = new XmlNode(tagName);
16447
+ if (this.tagsNodeStack.length > this.options.maxNestedTags) {
16448
+ throw new Error("Maximum nested tags exceeded");
16449
+ }
16450
+ this.tagsNodeStack.push(currentNode);
16451
+ if (tagName !== tagExp && attrExpPresent) {
16452
+ childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
16453
+ }
16454
+ this.addChild(currentNode, childNode, jPath, startIndex);
16455
+ currentNode = childNode;
16456
+ }
16457
+ textData = "";
16458
+ i = closeIndex;
16459
+ }
16460
+ }
16461
+ } else {
16462
+ textData += xmlData[i];
16463
+ }
16464
+ }
16465
+ return xmlObj.child;
16466
+ };
16467
+ function addChild(currentNode, childNode, jPath, startIndex) {
16468
+ if (!this.options.captureMetaData) startIndex = void 0;
16469
+ const result = this.options.updateTag(childNode.tagname, jPath, childNode[":@"]);
16470
+ if (result === false) {
16471
+ } else if (typeof result === "string") {
16472
+ childNode.tagname = result;
16473
+ currentNode.addChild(childNode, startIndex);
16474
+ } else {
16475
+ currentNode.addChild(childNode, startIndex);
16476
+ }
16477
+ }
16478
+ var replaceEntitiesValue = function(val, tagName, jPath) {
16479
+ if (val.indexOf("&") === -1) {
16480
+ return val;
16481
+ }
16482
+ const entityConfig = this.options.processEntities;
16483
+ if (!entityConfig.enabled) {
16484
+ return val;
16485
+ }
16486
+ if (entityConfig.allowedTags) {
16487
+ if (!entityConfig.allowedTags.includes(tagName)) {
16488
+ return val;
16489
+ }
16490
+ }
16491
+ if (entityConfig.tagFilter) {
16492
+ if (!entityConfig.tagFilter(tagName, jPath)) {
16493
+ return val;
16494
+ }
16495
+ }
16496
+ for (let entityName in this.docTypeEntities) {
16497
+ const entity = this.docTypeEntities[entityName];
16498
+ const matches = val.match(entity.regx);
16499
+ if (matches) {
16500
+ this.entityExpansionCount += matches.length;
16501
+ if (entityConfig.maxTotalExpansions && this.entityExpansionCount > entityConfig.maxTotalExpansions) {
16502
+ throw new Error(
16503
+ `Entity expansion limit exceeded: ${this.entityExpansionCount} > ${entityConfig.maxTotalExpansions}`
16504
+ );
16505
+ }
16506
+ const lengthBefore = val.length;
16507
+ val = val.replace(entity.regx, entity.val);
16508
+ if (entityConfig.maxExpandedLength) {
16509
+ this.currentExpandedLength += val.length - lengthBefore;
16510
+ if (this.currentExpandedLength > entityConfig.maxExpandedLength) {
16511
+ throw new Error(
16512
+ `Total expanded content size exceeded: ${this.currentExpandedLength} > ${entityConfig.maxExpandedLength}`
16513
+ );
16514
+ }
16515
+ }
16516
+ }
16517
+ }
16518
+ if (val.indexOf("&") === -1) return val;
16519
+ for (let entityName in this.lastEntities) {
16520
+ const entity = this.lastEntities[entityName];
16521
+ val = val.replace(entity.regex, entity.val);
16522
+ }
16523
+ if (val.indexOf("&") === -1) return val;
16524
+ if (this.options.htmlEntities) {
16525
+ for (let entityName in this.htmlEntities) {
16526
+ const entity = this.htmlEntities[entityName];
16527
+ val = val.replace(entity.regex, entity.val);
16528
+ }
16529
+ }
16530
+ val = val.replace(this.ampEntity.regex, this.ampEntity.val);
16531
+ return val;
16532
+ };
16533
+ function saveTextToParentTag(textData, currentNode, jPath, isLeafNode) {
16534
+ if (textData) {
16535
+ if (isLeafNode === void 0) isLeafNode = currentNode.child.length === 0;
16536
+ textData = this.parseTextData(
16537
+ textData,
16538
+ currentNode.tagname,
16539
+ jPath,
16540
+ false,
16541
+ currentNode[":@"] ? Object.keys(currentNode[":@"]).length !== 0 : false,
16542
+ isLeafNode
16543
+ );
16544
+ if (textData !== void 0 && textData !== "")
16545
+ currentNode.add(this.options.textNodeName, textData);
16546
+ textData = "";
16547
+ }
16548
+ return textData;
16549
+ }
16550
+ function isItStopNode(stopNodesExact, stopNodesWildcard, jPath, currentTagName) {
16551
+ if (stopNodesWildcard && stopNodesWildcard.has(currentTagName)) return true;
16552
+ if (stopNodesExact && stopNodesExact.has(jPath)) return true;
16553
+ return false;
16554
+ }
16555
+ function tagExpWithClosingIndex(xmlData, i, closingChar = ">") {
16556
+ let attrBoundary;
16557
+ let tagExp = "";
16558
+ for (let index = i; index < xmlData.length; index++) {
16559
+ let ch = xmlData[index];
16560
+ if (attrBoundary) {
16561
+ if (ch === attrBoundary) attrBoundary = "";
16562
+ } else if (ch === '"' || ch === "'") {
16563
+ attrBoundary = ch;
16564
+ } else if (ch === closingChar[0]) {
16565
+ if (closingChar[1]) {
16566
+ if (xmlData[index + 1] === closingChar[1]) {
16567
+ return {
16568
+ data: tagExp,
16569
+ index
16570
+ };
16571
+ }
16572
+ } else {
16573
+ return {
16574
+ data: tagExp,
16575
+ index
16576
+ };
16577
+ }
16578
+ } else if (ch === " ") {
16579
+ ch = " ";
16580
+ }
16581
+ tagExp += ch;
16582
+ }
16583
+ }
16584
+ function findClosingIndex(xmlData, str2, i, errMsg) {
16585
+ const closingIndex = xmlData.indexOf(str2, i);
16586
+ if (closingIndex === -1) {
16587
+ throw new Error(errMsg);
16588
+ } else {
16589
+ return closingIndex + str2.length - 1;
16590
+ }
16591
+ }
16592
+ function readTagExp(xmlData, i, removeNSPrefix, closingChar = ">") {
16593
+ const result = tagExpWithClosingIndex(xmlData, i + 1, closingChar);
16594
+ if (!result) return;
16595
+ let tagExp = result.data;
16596
+ const closeIndex = result.index;
16597
+ const separatorIndex = tagExp.search(/\s/);
16598
+ let tagName = tagExp;
16599
+ let attrExpPresent = true;
16600
+ if (separatorIndex !== -1) {
16601
+ tagName = tagExp.substring(0, separatorIndex);
16602
+ tagExp = tagExp.substring(separatorIndex + 1).trimStart();
16603
+ }
16604
+ const rawTagName = tagName;
16605
+ if (removeNSPrefix) {
16606
+ const colonIndex = tagName.indexOf(":");
16607
+ if (colonIndex !== -1) {
16608
+ tagName = tagName.substr(colonIndex + 1);
16609
+ attrExpPresent = tagName !== result.data.substr(colonIndex + 1);
16610
+ }
16611
+ }
16612
+ return {
16613
+ tagName,
16614
+ tagExp,
16615
+ closeIndex,
16616
+ attrExpPresent,
16617
+ rawTagName
16618
+ };
16619
+ }
16620
+ function readStopNodeData(xmlData, tagName, i) {
16621
+ const startIndex = i;
16622
+ let openTagCount = 1;
16623
+ for (; i < xmlData.length; i++) {
16624
+ if (xmlData[i] === "<") {
16625
+ if (xmlData[i + 1] === "/") {
16626
+ const closeIndex = findClosingIndex(xmlData, ">", i, `${tagName} is not closed`);
16627
+ let closeTagName = xmlData.substring(i + 2, closeIndex).trim();
16628
+ if (closeTagName === tagName) {
16629
+ openTagCount--;
16630
+ if (openTagCount === 0) {
16631
+ return {
16632
+ tagContent: xmlData.substring(startIndex, i),
16633
+ i: closeIndex
16634
+ };
16635
+ }
16636
+ }
16637
+ i = closeIndex;
16638
+ } else if (xmlData[i + 1] === "?") {
16639
+ const closeIndex = findClosingIndex(xmlData, "?>", i + 1, "StopNode is not closed.");
16640
+ i = closeIndex;
16641
+ } else if (xmlData.substr(i + 1, 3) === "!--") {
16642
+ const closeIndex = findClosingIndex(xmlData, "-->", i + 3, "StopNode is not closed.");
16643
+ i = closeIndex;
16644
+ } else if (xmlData.substr(i + 1, 2) === "![") {
16645
+ const closeIndex = findClosingIndex(xmlData, "]]>", i, "StopNode is not closed.") - 2;
16646
+ i = closeIndex;
16647
+ } else {
16648
+ const tagData = readTagExp(xmlData, i, ">");
16649
+ if (tagData) {
16650
+ const openTagName = tagData && tagData.tagName;
16651
+ if (openTagName === tagName && tagData.tagExp[tagData.tagExp.length - 1] !== "/") {
16652
+ openTagCount++;
16653
+ }
16654
+ i = tagData.closeIndex;
16655
+ }
16656
+ }
16657
+ }
16658
+ }
16659
+ }
16660
+ function parseValue(val, shouldParse, options) {
16661
+ if (shouldParse && typeof val === "string") {
16662
+ const newval = val.trim();
16663
+ if (newval === "true") return true;
16664
+ else if (newval === "false") return false;
16665
+ else return toNumber(val, options);
16666
+ } else {
16667
+ if (isExist(val)) {
16668
+ return val;
16669
+ } else {
16670
+ return "";
16671
+ }
16672
+ }
16673
+ }
16674
+ function fromCodePoint(str2, base, prefix) {
16675
+ const codePoint = Number.parseInt(str2, base);
16676
+ if (codePoint >= 0 && codePoint <= 1114111) {
16677
+ return String.fromCodePoint(codePoint);
16678
+ } else {
16679
+ return prefix + str2 + ";";
16680
+ }
16681
+ }
16682
+
16683
+ // ../../node_modules/.pnpm/fast-xml-parser@5.3.8/node_modules/fast-xml-parser/src/xmlparser/node2json.js
16684
+ var METADATA_SYMBOL2 = XmlNode.getMetaDataSymbol();
16685
+ function prettify(node, options) {
16686
+ return compress(node, options);
16687
+ }
16688
+ function compress(arr, options, jPath) {
16689
+ let text2;
16690
+ const compressedObj = {};
16691
+ for (let i = 0; i < arr.length; i++) {
16692
+ const tagObj = arr[i];
16693
+ const property = propName(tagObj);
16694
+ let newJpath = "";
16695
+ if (jPath === void 0) newJpath = property;
16696
+ else newJpath = jPath + "." + property;
16697
+ if (property === options.textNodeName) {
16698
+ if (text2 === void 0) text2 = tagObj[property];
16699
+ else text2 += "" + tagObj[property];
16700
+ } else if (property === void 0) {
16701
+ continue;
16702
+ } else if (tagObj[property]) {
16703
+ let val = compress(tagObj[property], options, newJpath);
16704
+ const isLeaf = isLeafTag(val, options);
16705
+ if (tagObj[":@"]) {
16706
+ assignAttributes(val, tagObj[":@"], newJpath, options);
16707
+ } else if (Object.keys(val).length === 1 && val[options.textNodeName] !== void 0 && !options.alwaysCreateTextNode) {
16708
+ val = val[options.textNodeName];
16709
+ } else if (Object.keys(val).length === 0) {
16710
+ if (options.alwaysCreateTextNode) val[options.textNodeName] = "";
16711
+ else val = "";
16712
+ }
16713
+ if (tagObj[METADATA_SYMBOL2] !== void 0 && typeof val === "object" && val !== null) {
16714
+ val[METADATA_SYMBOL2] = tagObj[METADATA_SYMBOL2];
16715
+ }
16716
+ if (compressedObj[property] !== void 0 && Object.prototype.hasOwnProperty.call(compressedObj, property)) {
16717
+ if (!Array.isArray(compressedObj[property])) {
16718
+ compressedObj[property] = [compressedObj[property]];
16719
+ }
16720
+ compressedObj[property].push(val);
16721
+ } else {
16722
+ if (options.isArray(property, newJpath, isLeaf)) {
16723
+ compressedObj[property] = [val];
16724
+ } else {
16725
+ compressedObj[property] = val;
16726
+ }
16727
+ }
16728
+ }
16729
+ }
16730
+ if (typeof text2 === "string") {
16731
+ if (text2.length > 0) compressedObj[options.textNodeName] = text2;
16732
+ } else if (text2 !== void 0) compressedObj[options.textNodeName] = text2;
16733
+ return compressedObj;
16734
+ }
16735
+ function propName(obj) {
16736
+ const keys = Object.keys(obj);
16737
+ for (let i = 0; i < keys.length; i++) {
16738
+ const key = keys[i];
16739
+ if (key !== ":@") return key;
16740
+ }
16741
+ }
16742
+ function assignAttributes(obj, attrMap, jpath, options) {
16743
+ if (attrMap) {
16744
+ const keys = Object.keys(attrMap);
16745
+ const len = keys.length;
16746
+ for (let i = 0; i < len; i++) {
16747
+ const atrrName = keys[i];
16748
+ if (options.isArray(atrrName, jpath + "." + atrrName, true, true)) {
16749
+ obj[atrrName] = [attrMap[atrrName]];
16750
+ } else {
16751
+ obj[atrrName] = attrMap[atrrName];
16752
+ }
16753
+ }
16754
+ }
16755
+ }
16756
+ function isLeafTag(obj, options) {
16757
+ const { textNodeName } = options;
16758
+ const propCount = Object.keys(obj).length;
16759
+ if (propCount === 0) {
16760
+ return true;
16761
+ }
16762
+ if (propCount === 1 && (obj[textNodeName] || typeof obj[textNodeName] === "boolean" || obj[textNodeName] === 0)) {
16763
+ return true;
16764
+ }
16765
+ return false;
16766
+ }
16767
+
16768
+ // ../../node_modules/.pnpm/fast-xml-parser@5.3.8/node_modules/fast-xml-parser/src/xmlparser/XMLParser.js
16769
+ var XMLParser = class {
16770
+ constructor(options) {
16771
+ this.externalEntities = {};
16772
+ this.options = buildOptions(options);
16773
+ }
16774
+ /**
16775
+ * Parse XML dats to JS object
16776
+ * @param {string|Uint8Array} xmlData
16777
+ * @param {boolean|Object} validationOption
16778
+ */
16779
+ parse(xmlData, validationOption) {
16780
+ if (typeof xmlData !== "string" && xmlData.toString) {
16781
+ xmlData = xmlData.toString();
16782
+ } else if (typeof xmlData !== "string") {
16783
+ throw new Error("XML data is accepted in String or Bytes[] form.");
16784
+ }
16785
+ if (validationOption) {
16786
+ if (validationOption === true) validationOption = {};
16787
+ const result = validate(xmlData, validationOption);
16788
+ if (result !== true) {
16789
+ throw Error(`${result.err.msg}:${result.err.line}:${result.err.col}`);
16790
+ }
16791
+ }
16792
+ const orderedObjParser = new OrderedObjParser(this.options);
16793
+ orderedObjParser.addExternalEntities(this.externalEntities);
16794
+ const orderedResult = orderedObjParser.parseXml(xmlData);
16795
+ if (this.options.preserveOrder || orderedResult === void 0) return orderedResult;
16796
+ else return prettify(orderedResult, this.options);
16797
+ }
16798
+ /**
16799
+ * Add Entity which is not by default supported by this library
16800
+ * @param {string} key
16801
+ * @param {string} value
16802
+ */
16803
+ addEntity(key, value) {
16804
+ if (value.indexOf("&") !== -1) {
16805
+ throw new Error("Entity value can't have '&'");
16806
+ } else if (key.indexOf("&") !== -1 || key.indexOf(";") !== -1) {
16807
+ throw new Error("An entity must be set without '&' and ';'. Eg. use '#xD' for '&#xD;'");
16808
+ } else if (value === "&") {
16809
+ throw new Error("An entity with value '&' is not permitted");
16810
+ } else {
16811
+ this.externalEntities[key] = value;
16812
+ }
16813
+ }
16814
+ /**
16815
+ * Returns a Symbol that can be used to access the metadata
16816
+ * property on a node.
16817
+ *
16818
+ * If Symbol is not available in the environment, an ordinary property is used
16819
+ * and the name of the property is here returned.
16820
+ *
16821
+ * The XMLMetaData property is only present when `captureMetaData`
16822
+ * is true in the options.
16823
+ */
16824
+ static getMetaDataSymbol() {
16825
+ return XmlNode.getMetaDataSymbol();
16826
+ }
16827
+ };
16828
+
16829
+ // ../../node_modules/.pnpm/fast-xml-parser@5.3.8/node_modules/fast-xml-parser/src/fxp.js
16830
+ var XMLValidator = {
16831
+ validate
16832
+ };
16833
+
16834
+ // ../../packages/analysis/src/cvss.ts
16835
+ function cvss3(vector) {
16836
+ if (!/^CVSS:3\.[01]\//.test(vector)) return null;
16837
+ const metrics = Object.fromEntries(
16838
+ vector.split("/").slice(1).map((m) => m.split(":"))
16839
+ );
16840
+ const pick2 = (key, values) => values[metrics[key] ?? ""];
16841
+ const changed = metrics.S === "C";
16842
+ if (!["C", "U"].includes(metrics.S ?? "")) return null;
16843
+ const av = pick2("AV", { N: 0.85, A: 0.62, L: 0.55, P: 0.2 });
16844
+ const ac = pick2("AC", { L: 0.77, H: 0.44 });
16845
+ const pr = pick2(
16846
+ "PR",
16847
+ changed ? { N: 0.85, L: 0.68, H: 0.5 } : { N: 0.85, L: 0.62, H: 0.27 }
16848
+ );
16849
+ const ui = pick2("UI", { N: 0.85, R: 0.62 });
16850
+ const c = pick2("C", { H: 0.56, L: 0.22, N: 0 });
16851
+ const i = pick2("I", { H: 0.56, L: 0.22, N: 0 });
16852
+ const a = pick2("A", { H: 0.56, L: 0.22, N: 0 });
16853
+ if ([av, ac, pr, ui, c, i, a].some((v) => v === void 0)) return null;
16854
+ const iss = 1 - (1 - c) * (1 - i) * (1 - a);
16855
+ const impact = changed ? 7.52 * (iss - 0.029) - 3.25 * (iss - 0.02) ** 15 : 6.42 * iss;
16856
+ if (impact <= 0) return 0;
16857
+ const exploitability = 8.22 * av * ac * pr * ui;
16858
+ return Math.ceil(
16859
+ Math.min((changed ? 1.08 : 1) * (impact + exploitability), 10) * 10 - 1e-8
16860
+ ) / 10;
16861
+ }
16862
+
16863
+ // ../../packages/analysis/src/importers.ts
16864
+ var record2 = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : {};
16865
+ var list = (v) => v == null ? [] : Array.isArray(v) ? v : [v];
16866
+ var str = (v) => typeof v === "string" ? v : "";
16867
+ var num = (v) => Number.isFinite(Number(v)) ? Math.max(0, Number(v)) : 0;
16868
+ function pathOf(v) {
16869
+ const result = relativePath.safeParse(str(v).replace(/^\.\//, ""));
16870
+ if (!result.success) throw new Error("Unsafe report path");
16871
+ return result.data;
16872
+ }
16873
+ var fingerprint = (...parts) => createHash("sha256").update(JSON.stringify(parts)).digest("hex");
16874
+ var language = (path) => ({
16875
+ ts: "typescript",
16876
+ tsx: "typescript",
16877
+ js: "javascript",
16878
+ jsx: "javascript",
16879
+ py: "python",
16880
+ go: "go",
16881
+ java: "java",
16882
+ cs: "csharp"
16883
+ })[path.split(".").pop() ?? ""] ?? "other";
16884
+ function finding(engine, rule, path, line, key, category = "security") {
16885
+ return {
16886
+ engine,
16887
+ rule: rule.slice(0, 400),
16888
+ fingerprint: fingerprint(engine, rule, path, key),
16889
+ matchKey: fingerprint(engine, rule, key),
16890
+ observed: true,
16891
+ category,
16892
+ severity: "high",
16893
+ language: language(path),
16894
+ title: rule.slice(0, 400),
16895
+ explanation: "Review the reported operation and its inputs.",
16896
+ remediation: "Validate untrusted input and verify the fix with a regression test.",
16897
+ location: { path: pathOf(path), ...line > 0 ? { line: Math.floor(line) } : {} },
16898
+ flow: [],
16899
+ state: "new"
16900
+ };
16901
+ }
16902
+ function semgrep(json2) {
16903
+ const root = record2(json2);
16904
+ if (!Array.isArray(root.results) || list(root.errors).length)
16905
+ throw new Error("Semgrep returned incomplete results");
16906
+ return root.results.map((raw) => {
16907
+ const r = record2(raw), extra = record2(r.extra), meta3 = record2(extra.metadata);
16908
+ const f = finding(
16909
+ "semgrep",
16910
+ str(r.check_id),
16911
+ pathOf(r.path),
16912
+ num(record2(r.start).line),
16913
+ fingerprint(str(extra.lines).replace(/\s+/g, " ").trim()),
16914
+ meta3.category === "quality" ? "quality" : "security"
16915
+ );
16916
+ f.severity = extra.severity === "WARNING" ? "medium" : extra.severity === "INFO" ? "low" : "high";
16917
+ return f;
16918
+ });
16919
+ }
16920
+ function gitleaks(json2) {
16921
+ if (!Array.isArray(json2)) throw new Error("Invalid Gitleaks report");
16922
+ return json2.map((raw) => {
16923
+ const r = record2(raw);
16924
+ const f = finding(
16925
+ "gitleaks",
16926
+ str(r.RuleID),
16927
+ pathOf(r.File),
16928
+ num(r.StartLine),
16929
+ fingerprint(str(r.Secret)),
16930
+ "secret"
16931
+ );
16932
+ f.severity = "critical";
16933
+ f.explanation = "A credential pattern was found. Its value was removed locally.";
16934
+ f.remediation = "Remove the credential, rotate it at the provider and scan again.";
16935
+ return f;
16936
+ });
16937
+ }
16938
+ function osv(json2) {
16939
+ const root = record2(json2);
16940
+ if (!Array.isArray(root.results)) throw new Error("Invalid OSV report");
16941
+ return root.results.flatMap((raw) => {
16942
+ const r = record2(raw), path = pathOf(record2(r.source).path);
16943
+ return list(r.packages).flatMap((rawPackage) => {
16944
+ const p = record2(rawPackage), pkg = record2(p.package);
16945
+ return list(p.vulnerabilities).map((rawV) => {
16946
+ const v = record2(rawV), id = str(v.id), f = finding(
16947
+ "osv",
16948
+ id,
16949
+ path,
16950
+ 0,
16951
+ `${str(pkg.ecosystem)}:${str(pkg.name)}:${id}`,
16952
+ "dependency"
16953
+ );
16954
+ const affected = list(v.affected).filter((a) => {
16955
+ const target = record2(record2(a).package);
16956
+ return !target.name || target.name === pkg.name && target.ecosystem === pkg.ecosystem;
16957
+ });
16958
+ const fixed = affected.flatMap(
16959
+ (a) => list(record2(a).ranges).flatMap(
16960
+ (r2) => list(record2(r2).events).map((e) => str(record2(e).fixed)).filter(Boolean)
16961
+ )
16962
+ );
16963
+ const severity = str(record2(v.database_specific).severity).toLowerCase();
16964
+ const scores = list(v.severity).map((s) => cvss3(str(record2(s).score))).filter((n) => n !== null);
16965
+ const score = scores.length ? Math.max(...scores) : null;
16966
+ f.severity = ["critical", "high", "medium", "low"].includes(severity) ? severity : score === null ? "unknown" : score >= 9 ? "critical" : score >= 7 ? "high" : score >= 4 ? "medium" : "low";
16967
+ f.package = {
16968
+ name: str(pkg.name),
16969
+ version: str(pkg.version),
16970
+ ecosystem: str(pkg.ecosystem),
16971
+ advisory: id,
16972
+ fixed: [...new Set(fixed)].slice(0, 50),
16973
+ relationship: "unknown"
16974
+ };
16975
+ f.title = id;
16976
+ f.explanation = "The resolved package version is affected by a published advisory.";
16977
+ f.remediation = "Review the advisory and upgrade to a compatible fixed version; run your tests.";
16978
+ return f;
16979
+ });
16980
+ });
16981
+ });
16982
+ }
16983
+ function sarif(json2) {
16984
+ const root = record2(json2);
16985
+ if (root.version !== "2.1.0" || !Array.isArray(root.runs))
16986
+ throw new Error("Expected SARIF 2.1.0");
16987
+ return root.runs.flatMap((rawRun) => {
16988
+ const run = record2(rawRun);
16989
+ if (list(run.invocations).some((i) => record2(i).executionSuccessful === false))
16990
+ throw new Error("SARIF analysis did not complete");
16991
+ const artifactPath = (location2) => {
16992
+ const artifact = record2(location2.artifactLocation);
16993
+ const indexed = record2(list(run.artifacts)[Number(artifact.index)]);
16994
+ return pathOf(artifact.uri ?? record2(indexed.location).uri);
16995
+ };
16996
+ return list(run.results).map((raw) => {
16997
+ const r = record2(raw), loc = record2(record2(list(r.locations)[0]).physicalLocation);
16998
+ const f = finding(
16999
+ "sarif",
17000
+ str(r.ruleId),
17001
+ artifactPath(loc),
17002
+ num(record2(loc.region).startLine),
17003
+ str(record2(r.partialFingerprints)["vibecheck"]) || str(record2(r.partialFingerprints)["primaryLocationLineHash"]) || fingerprint(
17004
+ str(r.ruleId),
17005
+ String(num(record2(loc.region).startLine)),
17006
+ String(num(record2(loc.region).startColumn))
17007
+ )
17008
+ );
17009
+ f.severity = r.level === "warning" ? "medium" : r.level === "note" ? "low" : "high";
17010
+ f.flow = list(r.codeFlows).flatMap(
17011
+ (c) => list(record2(c).threadFlows).flatMap(
17012
+ (t) => list(record2(t).locations).map((rawLoc) => {
17013
+ const l = record2(record2(record2(rawLoc).location).physicalLocation);
17014
+ return {
17015
+ path: artifactPath(l),
17016
+ line: Math.max(1, num(record2(l.region).startLine))
17017
+ };
17018
+ })
17019
+ )
17020
+ ).slice(0, 100);
17021
+ return f;
17022
+ });
17023
+ });
17024
+ }
17025
+ function importSarif(json2, reportPath) {
17026
+ const root = record2(json2);
17027
+ if (root.version !== "2.1.0" || !Array.isArray(root.runs) || !root.runs.length)
17028
+ throw new Error("Expected nonempty SARIF 2.1.0 runs");
17029
+ const engines = [];
17030
+ const findings = [];
17031
+ for (const [index, raw] of root.runs.entries()) {
17032
+ const run = record2(raw), driver = record2(record2(run.tool).driver);
17033
+ const name = str(driver.name);
17034
+ if (!name || !Array.isArray(run.results))
17035
+ throw new Error("SARIF tool or results missing");
17036
+ const id = "sarif:" + fingerprint(reportPath, name, String(index)).slice(0, 24);
17037
+ const rules = fingerprint(
17038
+ JSON.stringify(
17039
+ list(driver.rules).map((r) => record2(r)).sort((a, b) => str(a.id).localeCompare(str(b.id)))
17040
+ )
17041
+ );
17042
+ const imported = sarif({ version: "2.1.0", runs: [run] }).map((f) => ({
17043
+ ...f,
17044
+ engine: id,
17045
+ fingerprint: fingerprint(id, f.fingerprint),
17046
+ matchKey: fingerprint(id, f.matchKey ?? f.fingerprint)
17047
+ }));
17048
+ const files = list(run.artifacts).filter((a) => list(record2(a).roles).includes("analysisTarget")).map((a) => pathOf(record2(record2(a).location).uri));
17049
+ engines.push({
17050
+ id,
17051
+ version: str(driver.semanticVersion) || str(driver.version) || "unknown",
17052
+ rules,
17053
+ status: "ran",
17054
+ durationMs: 0,
17055
+ files: [...new Set(files)],
17056
+ excluded: [],
17057
+ message: ""
17058
+ });
17059
+ findings.push(...imported);
17060
+ }
17061
+ return { engines, findings };
17062
+ }
17063
+ function xml(input) {
17064
+ input = input.replace(
17065
+ /<!DOCTYPE report PUBLIC "-\/\/JACOCO\/\/DTD Report 1\.1\/\/EN" "report\.dtd">/g,
17066
+ ""
17067
+ );
17068
+ if (/<!DOCTYPE|<!ENTITY/i.test(input) || input.length > 2e7 || XMLValidator.validate(input) !== true)
17069
+ throw new Error("Invalid XML report");
17070
+ return record2(
17071
+ new XMLParser({
17072
+ ignoreAttributes: false,
17073
+ attributeNamePrefix: "",
17074
+ processEntities: false
17075
+ }).parse(input)
17076
+ );
17077
+ }
17078
+ function importCoverage(input, format) {
17079
+ const files = /* @__PURE__ */ new Map();
17080
+ function add(path, line, hits) {
17081
+ if (!Number.isInteger(line) || line < 1) throw new Error("Invalid coverage line");
17082
+ const lines = files.get(path) ?? /* @__PURE__ */ new Map();
17083
+ lines.set(line, Math.max(hits, lines.get(line) ?? 0));
17084
+ files.set(path, lines);
17085
+ }
17086
+ if (format === "lcov") {
17087
+ let path = "";
17088
+ for (const row of input.split(/\r?\n/)) {
17089
+ if (row.startsWith("SF:")) path = pathOf(row.slice(3));
17090
+ if (row.startsWith("DA:")) {
17091
+ const [line, hits] = row.slice(3).split(",");
17092
+ if (!path) throw new Error("LCOV record without file");
17093
+ add(path, Number(line), num(hits));
17094
+ }
17095
+ }
17096
+ } else if (format === "cobertura") {
17097
+ const root = record2(xml(input).coverage);
17098
+ for (const p of list(record2(root.packages).package))
17099
+ for (const c of list(record2(record2(p).classes).class)) {
17100
+ const item = record2(c), path = pathOf(item.filename);
17101
+ for (const l of list(record2(item.lines).line))
17102
+ add(path, num(record2(l).number), num(record2(l).hits));
17103
+ }
17104
+ } else {
17105
+ const root = record2(xml(input).report);
17106
+ for (const p of list(root.package))
17107
+ for (const s of list(record2(p).sourcefile)) {
17108
+ const source = record2(s), prefix = str(record2(p).name);
17109
+ const path = pathOf(`${prefix ? prefix + "/" : ""}${str(source.name)}`);
17110
+ for (const l of list(source.line))
17111
+ add(path, num(record2(l).nr), num(record2(l).ci) > 0 ? 1 : 0);
17112
+ }
17113
+ }
17114
+ if (!files.size) throw new Error("Coverage report contains no files");
17115
+ return {
17116
+ status: "ran",
17117
+ files: [...files].map(([path, lines]) => ({
17118
+ path,
17119
+ lines: [...lines].map(([line, hits]) => ({ line, hits }))
17120
+ }))
17121
+ };
17122
+ }
17123
+ function importJunit(input) {
17124
+ const tree = xml(input);
17125
+ if (!("testsuite" in tree) && !("testsuites" in tree))
17126
+ throw new Error("Expected JUnit testsuite or testsuites");
17127
+ let total = 0, failed = 0, skipped = 0;
17128
+ const failures = [];
17129
+ function visit(node) {
17130
+ const r = record2(node);
17131
+ const before = { total, failed, skipped };
17132
+ for (const test of list(r.testcase)) {
17133
+ total++;
17134
+ const t = record2(test);
17135
+ if ("failure" in t || "error" in t) {
17136
+ failed++;
17137
+ const path = str(t.file) || str(r.file);
17138
+ failures.push({
17139
+ id: fingerprint(
17140
+ str(r.name),
17141
+ str(t.classname),
17142
+ str(t.name),
17143
+ path,
17144
+ String(total)
17145
+ ),
17146
+ kind: "error" in t ? "error" : "failure",
17147
+ ...path ? {
17148
+ location: {
17149
+ path: pathOf(path),
17150
+ ...num(t.line) > 0 ? { line: Math.floor(num(t.line)) } : {}
17151
+ }
17152
+ } : {}
17153
+ });
17154
+ }
17155
+ if ("skipped" in t) skipped++;
17156
+ }
17157
+ for (const s of list(r.testsuite)) visit(s);
17158
+ if ("tests" in r && num(r.tests) !== total - before.total || "failures" in r && num(r.failures) + num(r.errors) !== failed - before.failed || "skipped" in r && num(r.skipped) !== skipped - before.skipped)
17159
+ throw new Error("JUnit totals do not match test cases");
17160
+ }
17161
+ visit(tree.testsuites ?? tree);
17162
+ return {
17163
+ status: "ran",
17164
+ total,
17165
+ failed,
17166
+ skipped,
17167
+ ...failures.length ? { failures } : {}
17168
+ };
17169
+ }
17170
+ function toAnalysisSarif(report) {
17171
+ return {
17172
+ $schema: "https://json.schemastore.org/sarif-2.1.0.json",
17173
+ version: "2.1.0",
17174
+ runs: [
17175
+ {
17176
+ tool: { driver: { name: "VibeCheck", version: "2" } },
17177
+ results: report.findings.filter((f) => f.state !== "resolved").map((f) => ({
17178
+ ruleId: f.rule,
17179
+ level: ["critical", "high"].includes(f.severity) ? "error" : "warning",
17180
+ message: { text: f.title },
17181
+ partialFingerprints: { vibecheck: f.fingerprint },
17182
+ locations: [
17183
+ {
17184
+ physicalLocation: {
17185
+ artifactLocation: { uri: f.location.path },
17186
+ ...f.location.line ? { region: { startLine: f.location.line } } : {}
17187
+ }
17188
+ }
17189
+ ],
17190
+ ...f.flow.length ? {
17191
+ codeFlows: [
17192
+ {
17193
+ threadFlows: [
17194
+ {
17195
+ locations: f.flow.map((l) => ({
17196
+ location: {
17197
+ physicalLocation: {
17198
+ artifactLocation: { uri: l.path },
17199
+ region: { startLine: l.line ?? 1 }
17200
+ }
17201
+ }
17202
+ }))
17203
+ }
17204
+ ]
17205
+ }
17206
+ ]
17207
+ } : {}
17208
+ }))
17209
+ }
17210
+ ]
17211
+ };
17212
+ }
17213
+
17214
+ // ../../packages/analysis/src/rules.ts
17215
+ var RULES_VERSION = "vibecheck-2026.1";
17216
+ var RULES = {
17217
+ rules: [
17218
+ {
17219
+ id: "vc.js.dynamic-eval",
17220
+ languages: ["javascript", "typescript"],
17221
+ severity: "ERROR",
17222
+ message: "Avoid evaluating dynamic code",
17223
+ pattern: "eval($X)"
17224
+ },
17225
+ {
17226
+ id: "vc.python.shell",
17227
+ languages: ["python"],
17228
+ severity: "ERROR",
17229
+ message: "Avoid shell execution",
17230
+ pattern: "subprocess.run(..., shell=True, ...)"
17231
+ },
17232
+ {
17233
+ id: "vc.go.insecure-tls",
17234
+ languages: ["go"],
17235
+ severity: "ERROR",
17236
+ message: "Verify TLS certificates",
17237
+ pattern: "tls.Config{..., InsecureSkipVerify: true, ...}"
17238
+ },
17239
+ {
17240
+ id: "vc.java.weak-hash",
17241
+ languages: ["java"],
17242
+ severity: "ERROR",
17243
+ message: "Replace weak digest",
17244
+ pattern: 'MessageDigest.getInstance("MD5")'
17245
+ },
17246
+ {
17247
+ id: "vc.csharp.weak-hash",
17248
+ languages: ["csharp"],
17249
+ severity: "ERROR",
17250
+ message: "Replace weak digest",
17251
+ pattern: "MD5.Create()"
17252
+ },
17253
+ {
17254
+ id: "vc.js.empty-catch",
17255
+ languages: ["javascript", "typescript"],
17256
+ severity: "WARNING",
17257
+ metadata: { category: "quality" },
17258
+ message: "Handle or explain the error",
17259
+ pattern: "try { ... } catch ($E) { }"
17260
+ }
17261
+ ]
17262
+ };
17263
+
17264
+ // ../../packages/analysis/src/dependencies.ts
17265
+ import { dirname, join } from "path";
17266
+ import { readFileSync } from "fs";
17267
+ function classifyDependencies(findings, root) {
17268
+ const cache = /* @__PURE__ */ new Map();
17269
+ for (const finding2 of findings) {
17270
+ const pkg = finding2.package;
17271
+ if (!pkg) continue;
17272
+ const path = finding2.location.path;
17273
+ if (!path.endsWith("package-lock.json") && !path.endsWith("packages.lock.json"))
17274
+ continue;
17275
+ let lock = cache.get(path);
17276
+ if (!lock) {
17277
+ try {
17278
+ lock = record2(JSON.parse(readFileSync(join(root, path), "utf8")));
17279
+ } catch {
17280
+ continue;
17281
+ }
17282
+ cache.set(path, lock);
17283
+ }
17284
+ if (path.endsWith("packages.lock.json")) {
17285
+ const entries = Object.values(record2(lock.dependencies)).map((target) => record2(record2(target)[pkg.name])).filter((entry) => str(entry.resolved) === pkg.version);
17286
+ if (entries.some((entry) => entry.type === "Direct")) pkg.relationship = "direct";
17287
+ else if (entries.length && entries.every((entry) => entry.type === "Transitive"))
17288
+ pkg.relationship = "transitive";
17289
+ continue;
17290
+ }
17291
+ const packages = record2(lock.packages);
17292
+ if (!("" in packages)) continue;
17293
+ const manifests = Object.entries(packages).filter(
17294
+ ([name]) => !name.includes("node_modules")
17295
+ );
17296
+ const direct = manifests.some(([workspace, raw]) => {
17297
+ const manifest = record2(raw);
17298
+ const declared = [
17299
+ "dependencies",
17300
+ "devDependencies",
17301
+ "optionalDependencies",
17302
+ "peerDependencies"
17303
+ ].some((key) => pkg.name in record2(manifest[key]));
17304
+ if (!declared) return false;
17305
+ let directory = workspace;
17306
+ while (true) {
17307
+ const resolved = record2(
17308
+ packages[`${directory ? directory + "/" : ""}node_modules/${pkg.name}`]
17309
+ );
17310
+ if (str(resolved.version) === pkg.version) return true;
17311
+ if (!directory || directory === ".") return false;
17312
+ directory = dirname(directory);
17313
+ if (directory === ".") directory = "";
17314
+ }
17315
+ });
17316
+ pkg.relationship = direct ? "direct" : "transitive";
17317
+ }
17318
+ }
17319
+
17320
+ // ../../packages/analysis/src/index.ts
17321
+ var ENGINE_VERSIONS = {
17322
+ semgrep: "1.176.0",
17323
+ osv: "2.5.1",
17324
+ gitleaks: "8.30.1"
17325
+ };
17326
+ var SKIP = /* @__PURE__ */ new Set([
17327
+ ".git",
17328
+ ".next",
17329
+ ".vibecheck",
17330
+ ".tools",
17331
+ "__pycache__",
17332
+ "node_modules",
17333
+ "dist",
17334
+ "build",
17335
+ "coverage",
17336
+ ".venv",
17337
+ "vendor",
17338
+ "bin",
17339
+ "obj"
17340
+ ]);
17341
+ function inventory(root) {
17342
+ const out = [];
17343
+ function visit(dir) {
17344
+ for (const e of readdirSync(dir, { withFileTypes: true })) {
17345
+ if (SKIP.has(e.name) || e.isSymbolicLink()) continue;
17346
+ const path = join2(dir, e.name);
17347
+ if (e.isDirectory()) visit(path);
17348
+ else if (e.isFile()) {
17349
+ if (out.length >= 1e5) throw new Error("Repository exceeds 100000 files");
17350
+ out.push(relative(root, path).split(sep).join("/"));
17351
+ }
17352
+ }
17353
+ }
17354
+ visit(root);
17355
+ return out.sort();
17356
+ }
17357
+ function localFile(root, path) {
17358
+ const full = resolve(root, path), rel = relative(root, full);
17359
+ if (rel.startsWith("..") || rel.startsWith(sep))
17360
+ throw new Error("Report is outside repository");
17361
+ let cursor = root;
17362
+ for (const part of rel.split(sep)) {
17363
+ cursor = join2(cursor, part);
17364
+ if (lstatSync(cursor).isSymbolicLink())
17365
+ throw new Error("Symlink report is not allowed");
17366
+ }
17367
+ if (lstatSync(full).size > 2e7) throw new Error("Report exceeds 20 MB");
17368
+ return readFileSync2(full, "utf8");
17369
+ }
17370
+ function parseDiff(diff) {
17371
+ const changes = [];
17372
+ let current;
17373
+ let previousPath;
17374
+ for (const line of diff.split("\n")) {
17375
+ if (line.startsWith("diff --git ")) {
17376
+ current = void 0;
17377
+ previousPath = void 0;
17378
+ }
17379
+ if (line.startsWith("rename from ")) previousPath = line.slice(12);
17380
+ if (line.startsWith("rename to ")) {
17381
+ current = { path: line.slice(10), previousPath, lines: [] };
17382
+ changes.push(current);
17383
+ }
17384
+ if (line.startsWith("+++ b/")) {
17385
+ if (current?.path !== line.slice(6)) {
17386
+ current = { path: line.slice(6), lines: [] };
17387
+ changes.push(current);
17388
+ }
17389
+ }
17390
+ const match = /^@@ .* \+(\d+)(?:,(\d+))? @@/.exec(line);
17391
+ if (match && current) {
17392
+ const start = Number(match[1]), count = match[2] === void 0 ? 1 : Number(match[2]);
17393
+ for (let i = 0; i < count; i++) current.lines.push(start + i);
17394
+ }
17395
+ }
17396
+ return changes;
17397
+ }
17398
+ async function analyze(options) {
17399
+ const root = resolve(options.root), files = inventory(root), reference = options.reference ?? "main", work = mkdtempSync(join2(tmpdir(), "vibecheck-analysis-"));
17400
+ const git = async (args) => execute(
17401
+ "git",
17402
+ ["-c", "core.fsmonitor=false", "-c", `safe.directory=${root}`, ...args],
17403
+ root,
17404
+ 1e4
17405
+ );
17406
+ const [head, branch, base] = await Promise.all([
17407
+ git(["rev-parse", "HEAD"]),
17408
+ git(["branch", "--show-current"]),
17409
+ git(["merge-base", "HEAD", reference])
17410
+ ]);
17411
+ const sha = (s) => /^[a-f0-9]{40,64}$/.test(s.trim()) ? s.trim() : null;
17412
+ const mergeBase = sha(base.stdout);
17413
+ const diff = mergeBase ? await git([
17414
+ "-c",
17415
+ "core.quotePath=false",
17416
+ "diff",
17417
+ "--no-ext-diff",
17418
+ "--no-textconv",
17419
+ "--unified=0",
17420
+ "--find-renames",
17421
+ mergeBase,
17422
+ "--"
17423
+ ]) : null;
17424
+ const report = {
17425
+ version: 2,
17426
+ projectId: options.projectId,
17427
+ runId: randomUUID(),
17428
+ provenance: "local-ci",
17429
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
17430
+ commit: sha(head.stdout),
17431
+ branch: branch.stdout.trim() || "detached",
17432
+ reference,
17433
+ mergeBase,
17434
+ baselineRunId: options.baseline?.runId ?? null,
17435
+ scope: fingerprint([...SKIP].join(",")),
17436
+ engines: [],
17437
+ findings: [],
17438
+ coverage: { status: "missing", files: [] },
17439
+ tests: { status: "missing", total: 0, failed: 0, skipped: 0 },
17440
+ changes: diff?.code === 0 ? parseDiff(diff.stdout) : [],
17441
+ policy: DEFAULT_ANALYSIS_POLICY,
17442
+ conditions: [],
17443
+ status: "inconclusive"
17444
+ };
17445
+ if (diff && diff.code !== 0) report.mergeBase = null;
17446
+ const [untracked, dirty] = await Promise.all([
17447
+ git(["ls-files", "--others", "--exclude-standard", "-z"]),
17448
+ git(["status", "--porcelain", "-z"])
17449
+ ]);
17450
+ if (dirty.stdout.length) report.commit = null;
17451
+ for (const path of untracked.stdout.split("\0").filter((p) => files.includes(p) && /\.(tsx?|jsx?|py|go|java|cs)$/.test(p))) {
17452
+ const count = localFile(root, path).split("\n").length;
17453
+ report.changes.push({ path, lines: Array.from({ length: count }, (_, i) => i + 1) });
17454
+ }
17455
+ try {
17456
+ const rules = join2(work, "rules.json");
17457
+ writeFileSync(rules, JSON.stringify(RULES));
17458
+ const snapshot = join2(work, "repository");
17459
+ mkdirSync(snapshot);
17460
+ const omitted = files.filter(
17461
+ (f) => lstatSync(join2(root, f)).size > 1e6 || /(^|\/)(\.gitleaks\.toml|\.gitleaksignore|\.semgrepignore)$/.test(f)
17462
+ );
17463
+ const selectedFiles = files.filter((f) => !omitted.includes(f));
17464
+ for (const path of selectedFiles) {
17465
+ const dest = join2(snapshot, path);
17466
+ mkdirSync(join2(dest, ".."), { recursive: true });
17467
+ copyFileSync(join2(root, path), dest);
17468
+ }
17469
+ const sourceFiles = selectedFiles.filter(
17470
+ (f) => /\.(tsx?|jsx?|py|go|java|cs)$/.test(f)
17471
+ );
17472
+ const locks = selectedFiles.filter(
17473
+ (f) => /(^|\/)(pnpm-lock.yaml|package-lock.json|yarn.lock|poetry.lock|uv.lock|requirements.txt|go.sum|pom.xml|gradle.lockfile|packages.lock.json)$/.test(
17474
+ f
17475
+ )
17476
+ );
17477
+ const outputs = [
17478
+ {
17479
+ id: "semgrep",
17480
+ binary: "semgrep",
17481
+ args: [
17482
+ "scan",
17483
+ "--config",
17484
+ rules,
17485
+ "--json",
17486
+ "--metrics=off",
17487
+ "--disable-version-check",
17488
+ "--disable-nosem",
17489
+ "--max-target-bytes",
17490
+ "1000000",
17491
+ ...[...SKIP].flatMap((s) => ["--exclude", s]),
17492
+ "."
17493
+ ],
17494
+ files: sourceFiles,
17495
+ parser: semgrep
17496
+ },
17497
+ {
17498
+ id: "osv",
17499
+ binary: "osv-scanner",
17500
+ args: ["scan", "source", "--format=json", ...locks.map((f) => "--lockfile=" + f)],
17501
+ files: locks,
17502
+ parser: osv
17503
+ },
17504
+ {
17505
+ id: "gitleaks",
17506
+ binary: "gitleaks",
17507
+ args: [
17508
+ "dir",
17509
+ ".",
17510
+ "--no-banner",
17511
+ "--report-format",
17512
+ "json",
17513
+ "--report-path",
17514
+ join2(work, "gitleaks.json")
17515
+ ],
17516
+ files: selectedFiles,
17517
+ parser: gitleaks
17518
+ }
17519
+ ];
17520
+ for (const spec of outputs) {
17521
+ const engine = {
17522
+ id: spec.id,
17523
+ version: ENGINE_VERSIONS[spec.id],
17524
+ rules: spec.id === "semgrep" ? RULES_VERSION : "bundled",
17525
+ status: "not-applicable",
17526
+ durationMs: 0,
17527
+ files: [],
17528
+ excluded: omitted,
17529
+ message: ""
17530
+ };
17531
+ report.engines.push(engine);
17532
+ if (!spec.files.length) continue;
17533
+ const started = Date.now();
17534
+ const actual = await execute(
17535
+ spec.binary,
17536
+ [spec.id === "gitleaks" ? "version" : "--version"],
17537
+ root,
17538
+ 1e4
17539
+ );
17540
+ if (actual.unavailable) {
17541
+ engine.status = "unavailable";
17542
+ engine.message = "Install the pinned engine version";
17543
+ continue;
17544
+ }
17545
+ if (actual.code !== 0 || !actual.stdout.split(/\s+/).includes(ENGINE_VERSIONS[spec.id])) {
17546
+ engine.status = "unavailable";
17547
+ engine.message = "Engine version differs from the pinned version";
17548
+ continue;
17549
+ }
17550
+ const result = await execute(spec.binary, spec.args, snapshot);
17551
+ engine.durationMs = Date.now() - started;
17552
+ if (![0, 1].includes(result.code)) {
17553
+ engine.status = "failed";
17554
+ engine.message = "Engine did not finish successfully";
17555
+ continue;
17556
+ }
17557
+ try {
17558
+ const raw = JSON.parse(
17559
+ spec.id === "gitleaks" ? readFileSync2(join2(work, "gitleaks.json"), "utf8") : result.stdout
17560
+ );
17561
+ if (spec.id === "osv")
17562
+ for (const item of list(record2(raw).results)) {
17563
+ const source = record2(record2(item).source);
17564
+ if (str(source.path).startsWith(snapshot + sep))
17565
+ source.path = relative(snapshot, str(source.path)).split(sep).join("/");
17566
+ }
17567
+ const imported = spec.parser(raw);
17568
+ if (spec.id === "osv") classifyDependencies(imported, snapshot);
17569
+ report.findings.push(...imported);
17570
+ engine.status = "ran";
17571
+ engine.files = spec.files;
17572
+ if (spec.id === "semgrep") {
17573
+ const paths = record2(record2(raw).paths);
17574
+ if (Array.isArray(paths.scanned))
17575
+ engine.files = paths.scanned.map((p) => str(p).replace(/^\.\//, ""));
17576
+ engine.excluded = [
17577
+ ...omitted,
17578
+ ...spec.files.filter((p) => !engine.files.includes(p))
17579
+ ];
17580
+ }
17581
+ } catch {
17582
+ engine.status = "failed";
17583
+ engine.message = "Engine output was incomplete or invalid";
17584
+ }
17585
+ }
17586
+ const builtin = {
17587
+ id: "builtin",
17588
+ version: "2",
17589
+ rules: RULES_VERSION,
17590
+ status: "ran",
17591
+ durationMs: 0,
17592
+ files: [],
17593
+ excluded: [],
17594
+ message: ""
17595
+ };
17596
+ report.engines.push(builtin);
17597
+ try {
17598
+ const eligible = files.filter((f) => /\.(tsx?|jsx?|sql)$/.test(f));
17599
+ const selected = eligible.filter((f) => lstatSync(join2(root, f)).size <= 512e3);
17600
+ builtin.files = selected;
17601
+ builtin.excluded = eligible.filter((f) => !selected.includes(f));
17602
+ const tracked = await git(["ls-files", "-z"]);
17603
+ const names = new Set(tracked.code === 0 ? tracked.stdout.split("\0") : []);
17604
+ const found = runCodeChecks(
17605
+ {
17606
+ files: selected.map((path) => ({ path, content: localFile(root, path) })),
17607
+ tracked: (p) => names.has(p)
17608
+ },
17609
+ void 0,
17610
+ { failOnError: true }
17611
+ );
17612
+ report.findings.push(
17613
+ ...found.map((f) => {
17614
+ const r = finding("builtin", f.checkId, f.path, 0, f.checkId);
17615
+ r.severity = f.severity === "fatal" ? "critical" : f.severity;
17616
+ return r;
17617
+ })
17618
+ );
17619
+ } catch {
17620
+ builtin.status = "failed";
17621
+ builtin.message = "Built-in analysis failed";
17622
+ }
17623
+ for (const path of options.sarif ?? [])
17624
+ try {
17625
+ const imported = importSarif(JSON.parse(localFile(root, path)), path);
17626
+ report.findings.push(...imported.findings);
17627
+ report.engines.push(...imported.engines);
17628
+ } catch {
17629
+ report.engines.push({
17630
+ id: "sarif:" + fingerprint(path).slice(0, 24),
17631
+ version: "2.1.0",
17632
+ rules: "unknown",
17633
+ status: "failed",
17634
+ durationMs: 0,
17635
+ files: [],
17636
+ excluded: [],
17637
+ message: "Invalid SARIF"
17638
+ });
17639
+ }
17640
+ for (const item of options.coverage ?? [])
17641
+ try {
17642
+ const parsed = importCoverage(localFile(root, item.path), item.format);
17643
+ for (const file2 of parsed.files) {
17644
+ if (item.sourceRoot)
17645
+ file2.path = pathOf(item.sourceRoot.replace(/\/$/, "") + "/" + file2.path);
17646
+ const existing = report.coverage.files.find((f) => f.path === file2.path);
17647
+ if (!existing) report.coverage.files.push(file2);
17648
+ else {
17649
+ const lines = new Map(existing.lines.map((l) => [l.line, l.hits]));
17650
+ for (const line of file2.lines)
17651
+ lines.set(line.line, Math.max(lines.get(line.line) ?? 0, line.hits));
17652
+ existing.lines = [...lines].map(([line, hits]) => ({ line, hits }));
17653
+ }
17654
+ }
17655
+ if (report.coverage.status !== "failed") report.coverage.status = "ran";
17656
+ } catch {
17657
+ report.coverage.status = "failed";
17658
+ }
17659
+ for (const path of options.junit ?? [])
17660
+ try {
17661
+ const parsed = importJunit(localFile(root, path));
17662
+ if (report.tests.status !== "failed") report.tests.status = "ran";
17663
+ report.tests.total += parsed.total;
17664
+ report.tests.failed += parsed.failed;
17665
+ report.tests.skipped += parsed.skipped;
17666
+ if (parsed.failures?.length)
17667
+ report.tests.failures = [...report.tests.failures ?? [], ...parsed.failures];
17668
+ } catch {
17669
+ report.tests.status = "failed";
17670
+ }
17671
+ report.findings = [
17672
+ ...new Map(report.findings.map((f) => [f.fingerprint, f])).values()
17673
+ ];
17674
+ return analysisReportSchema.parse(evaluateAnalysis(report, options.baseline ?? null));
17675
+ } finally {
17676
+ rmSync(work, { recursive: true, force: true });
17677
+ }
17678
+ }
17679
+
17680
+ // src/analyze.ts
17681
+ var configSchema = external_exports.object({
17682
+ version: external_exports.literal(2).default(2),
17683
+ projectId: external_exports.string().uuid(),
17684
+ reference: external_exports.string().regex(/^[\w./-]{1,100}$/).default("main"),
17685
+ baseline: external_exports.string().optional(),
17686
+ coverage: external_exports.array(
17687
+ external_exports.object({
17688
+ path: external_exports.string(),
17689
+ format: external_exports.enum(["lcov", "cobertura", "jacoco"]),
17690
+ sourceRoot: external_exports.string().optional()
17691
+ })
17692
+ ).default([]),
17693
+ junit: external_exports.array(external_exports.string()).default([]),
17694
+ sarif: external_exports.array(external_exports.string()).default([])
17695
+ }).strict();
17696
+ async function analyzeCommand(argv) {
17697
+ if (argv.includes("--help")) {
17698
+ console.warn(
17699
+ "vibecheck analyze [--dir <repo>] [--config vibecheck.config.json] [--publish]\n\n1. Create a project and copy its configuration from the web Settings.\n2. Install the pinned engines and configure your existing coverage/JUnit/SARIF reports.\n3. Run analyze locally; add --publish with VIBECHECK_PROJECT_TOKEN to upload results.\n\nSetup / Como usar: https://vibecheck.kinkai.cloud/projects/help\nWrites .vibecheck/analysis.{json,sarif}. No arbitrary test commands are executed.\nQuality gate exit: 0 pass, 1 regression, 2 incomplete, 3 operational error.\nPublish the reference branch first; missing comparable evidence means incomplete, not pass."
17700
+ );
17701
+ return 0;
17702
+ }
17703
+ const value = (key) => {
17704
+ const i = argv.indexOf(key);
17705
+ return i < 0 ? void 0 : argv[i + 1];
17706
+ };
17707
+ const root = resolve2(value("--dir") ?? process.cwd()), configPath = value("--config") ?? "vibecheck.config.json";
17708
+ if (!existsSync(join3(root, configPath)))
17709
+ throw new Error(
17710
+ "Create vibecheck.config.json with version: 2 and projectId from the web project."
17711
+ );
17712
+ const config2 = configSchema.parse(JSON.parse(localFile(root, configPath)));
17713
+ let baseline = config2.baseline ? analysisReportSchema.parse(JSON.parse(localFile(root, config2.baseline))) : null;
17714
+ let report = await analyze({ root, ...config2, baseline });
17715
+ const token = process.env["VIBECHECK_PROJECT_TOKEN"], base = process.env["VIBECHECK_URL"] ?? "https://vibecheck.kinkai.cloud";
17716
+ const url2 = new URL(base);
17717
+ if (url2.protocol !== "https:" && !["localhost", "127.0.0.1"].includes(url2.hostname))
17718
+ throw new Error("Use HTTPS for project uploads");
17719
+ const api = async (path, method = "GET", body) => {
17720
+ const result = await fetch(new URL("/api/projects/" + config2.projectId + path, url2), {
17721
+ method,
17722
+ redirect: "error",
17723
+ headers: { authorization: "Bearer " + token, "content-type": "application/json" },
17724
+ ...body === void 0 ? {} : { body: JSON.stringify(body) },
17725
+ signal: AbortSignal.timeout(3e4)
17726
+ });
17727
+ if (!result.ok) throw new Error("Project API rejected request: " + result.status);
17728
+ return result.json();
17729
+ };
17730
+ if (argv.includes("--publish")) {
17731
+ if (!token) throw new Error("VIBECHECK_PROJECT_TOKEN is required to publish");
17732
+ const project = external_exports.object({ policy: policySchema, reference: external_exports.string() }).parse(await api(""));
17733
+ if (project.reference !== config2.reference)
17734
+ throw new Error("Config reference must match the project reference branch");
17735
+ if (!baseline && report.mergeBase) {
17736
+ const runs = external_exports.array(external_exports.object({ id: external_exports.string().uuid() })).parse(await api("/runs?commit=" + report.mergeBase));
17737
+ if (runs[0]) baseline = analysisReportSchema.parse(await api("/runs/" + runs[0].id));
17738
+ }
17739
+ report = evaluateAnalysis(
17740
+ { ...report, baselineRunId: baseline?.runId ?? null },
17741
+ baseline,
17742
+ project.policy
17743
+ );
17744
+ }
17745
+ const dir = join3(root, ".vibecheck");
17746
+ if (existsSync(dir) && lstatSync2(dir).isSymbolicLink())
17747
+ throw new Error("Unsafe artifact directory");
17748
+ mkdirSync2(dir, { recursive: true });
17749
+ const save = (r) => {
17750
+ for (const name of ["analysis.json", "analysis.sarif"]) {
17751
+ const target = join3(dir, name);
17752
+ if (existsSync(target) && (lstatSync2(target).isSymbolicLink() || !lstatSync2(target).isFile()))
17753
+ throw new Error("Unsafe artifact file");
17754
+ }
17755
+ writeFileSync2(join3(dir, "analysis.json"), JSON.stringify(r, null, 2) + "\n");
17756
+ writeFileSync2(
17757
+ join3(dir, "analysis.sarif"),
17758
+ JSON.stringify(toAnalysisSarif(r), null, 2) + "\n"
17759
+ );
17760
+ };
17761
+ save(report);
17762
+ if (argv.includes("--publish")) {
17763
+ const serialized = JSON.stringify(report), parts = serialized.match(/[\s\S]{1,100000}/g) ?? [];
17764
+ await api("/runs", "POST", { runId: report.runId, batches: parts.length });
17765
+ for (const [batch, payload] of parts.entries())
17766
+ await api("/runs/" + report.runId + "/batches", "PUT", { batch, payload });
17767
+ await api("/runs/" + report.runId + "/finalize", "POST", {});
17768
+ report = analysisReportSchema.parse(await api("/runs/" + report.runId));
17769
+ save(report);
17770
+ console.warn(new URL("/projects/" + config2.projectId, url2).href);
17771
+ }
17772
+ console.warn(
17773
+ `${report.status}: ${report.findings.filter((f) => f.state === "new").length} new findings`
17774
+ );
17775
+ for (const engine of report.engines)
17776
+ console.warn(`${engine.id} ${engine.version}: ${engine.status}`);
17777
+ return report.status === "passed" ? 0 : report.status === "failed" ? 1 : 2;
17778
+ }
17779
+
17780
+ // ../../packages/checks/src/types.ts
17781
+ var SEVERITIES = ["fatal", "critical", "high", "medium", "low"];
17782
+ var NODE_KINDS = [
17783
+ "browser",
17784
+ "app",
17785
+ "api",
17786
+ "db",
17787
+ "storage",
17788
+ "auth",
17789
+ "ai",
17790
+ "payments",
17791
+ "secrets"
17792
+ ];
17793
+ var CATEGORIES = ["secrets", "data", "storage", "auth", "ai", "config"];
17794
+
17795
+ // ../../packages/graph/src/types.ts
17796
+ var EDGE_TYPES = ["imports", "calls", "contains"];
17797
+ var MAX_NODES = 400;
17798
+ var MAX_EDGES = 800;
17799
+ var MAX_COMMUNITIES = 9;
17800
+ var MAX_FILES_PER_NODE = 12;
17801
+ var MAX_COMMUNITY_FILES = 8;
17802
+ var MAX_BYTES = 256e3;
17803
+ var MAX_LABEL = 80;
17804
+ var MAX_ID = 64;
17805
+ var MAX_PATH = 180;
17806
+ var MAX_PACKAGES = 32;
17807
+ var MAX_HINT_ROUTES = 80;
17808
+ var MAX_HINT_TABLES = 40;
17809
+ var MAX_HINT_BUCKETS = 24;
17810
+ var MAX_HINT_COLLECTIONS = 24;
17811
+ var EMPTY_DATA = { tables: [], buckets: [], collections: [] };
17812
+ var pathSchema = external_exports.string().min(1).max(MAX_PATH);
17813
+ var nodeSchema = external_exports.object({
17814
+ id: external_exports.string().min(1).max(MAX_ID),
17815
+ kind: external_exports.enum(NODE_KINDS),
17816
+ label: external_exports.string().min(1).max(MAX_LABEL),
17817
+ files: external_exports.array(pathSchema).max(MAX_FILES_PER_NODE)
17818
+ });
17819
+ var edgeSchema = external_exports.object({
17820
+ from: external_exports.string().min(1).max(MAX_ID),
17821
+ to: external_exports.string().min(1).max(MAX_ID),
17822
+ type: external_exports.enum(EDGE_TYPES)
17823
+ });
17824
+ var communitySchema = external_exports.object({
17825
+ nodeKind: external_exports.enum(NODE_KINDS),
17826
+ label: external_exports.string().min(1).max(MAX_LABEL),
17827
+ files: external_exports.array(pathSchema).max(MAX_COMMUNITY_FILES)
17828
+ });
17829
+ var tableName = external_exports.string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,62}$/);
17830
+ var bucketName = external_exports.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,62}$/);
17831
+ var dataSchema = external_exports.object({
17832
+ tables: external_exports.array(tableName).max(MAX_HINT_TABLES),
17833
+ buckets: external_exports.array(bucketName).max(MAX_HINT_BUCKETS),
17834
+ collections: external_exports.array(tableName).max(MAX_HINT_COLLECTIONS)
17835
+ });
17836
+ var scanGraphSchema = external_exports.object({
17837
+ version: external_exports.literal(1),
17838
+ advanced: external_exports.boolean(),
17839
+ nodes: external_exports.array(nodeSchema).max(MAX_NODES),
17840
+ edges: external_exports.array(edgeSchema).max(MAX_EDGES),
17841
+ communities: external_exports.array(communitySchema).max(MAX_COMMUNITIES),
17842
+ packages: external_exports.array(external_exports.string().min(1).max(MAX_LABEL)).max(MAX_PACKAGES),
17843
+ data: dataSchema.optional()
17844
+ });
17845
+ var EMPTY_HINTS = {
17846
+ apiRoutes: [],
17847
+ tableNames: [],
17848
+ buckets: [],
17849
+ collections: []
17850
+ };
17851
+
17852
+ // ../../packages/graph/src/hints.ts
17853
+ function uniqueCapped(values, cap) {
17854
+ const seen = /* @__PURE__ */ new Set();
17855
+ const out = [];
17856
+ for (const value of values) {
17857
+ if (out.length >= cap) break;
17858
+ if (seen.has(value)) continue;
17859
+ seen.add(value);
17860
+ out.push(value);
17861
+ }
17862
+ return out;
17863
+ }
17864
+ function routeFromFile(path) {
17865
+ const normal = path.replaceAll("\\", "/");
17866
+ const app = /(?:^|\/)app\/(.+)\/route\.(t|j)sx?$/i.exec(normal);
17867
+ if (app?.[1] !== void 0) {
17868
+ const segments = app[1].split("/").filter((s) => s.length > 0 && !s.startsWith("(") && !s.startsWith("@")).map((s) => s.replace(/^\[+\.\.\.(.+)\]+$/, "*").replace(/^\[+(.+)\]+$/, ":$1"));
17869
+ if (segments.length === 0) return "/api";
17870
+ return `/${segments.join("/")}`;
17871
+ }
17872
+ const pages = /(?:^|\/)pages\/api\/(.+)\.(t|j)sx?$/i.exec(normal);
17873
+ if (pages?.[1] !== void 0) {
17874
+ const withoutIndex = pages[1].replace(/\/index$/i, "");
17875
+ return `/api/${withoutIndex}`;
17876
+ }
17877
+ return null;
17878
+ }
17879
+ function filesFromGraph(graph) {
17880
+ return [
17881
+ ...graph.nodes.flatMap((n) => n.files),
17882
+ ...graph.communities.flatMap((c) => c.files)
17883
+ ];
17884
+ }
17885
+ function hintsFromGraph(graph) {
17886
+ if (graph === null || graph === void 0 || !graph.advanced) return EMPTY_HINTS;
17887
+ const routes = [];
17888
+ for (const file2 of filesFromGraph(graph)) {
17889
+ const route = routeFromFile(file2);
17890
+ if (route !== null) routes.push(route);
17891
+ }
17892
+ routes.sort((a, b) => a.localeCompare(b));
17893
+ const data = graph.data ?? EMPTY_DATA;
17894
+ return {
17895
+ apiRoutes: uniqueCapped(routes, MAX_HINT_ROUTES),
17896
+ tableNames: uniqueCapped(data.tables, MAX_HINT_TABLES),
17897
+ buckets: uniqueCapped(data.buckets, MAX_HINT_BUCKETS),
17898
+ collections: uniqueCapped(data.collections, MAX_HINT_COLLECTIONS)
17899
+ };
17900
+ }
17901
+
17902
+ // ../../packages/graph/src/metrics.ts
17903
+ function importCycles(graph) {
17904
+ const adjacencia = /* @__PURE__ */ new Map();
17905
+ for (const aresta of graph.edges) {
17906
+ if (aresta.type !== "imports") continue;
17907
+ const vizinhos = adjacencia.get(aresta.from) ?? [];
17908
+ vizinhos.push(aresta.to);
17909
+ adjacencia.set(aresta.from, vizinhos);
17910
+ }
17911
+ const indice = /* @__PURE__ */ new Map();
17912
+ const lowlink = /* @__PURE__ */ new Map();
17913
+ const naPilha = /* @__PURE__ */ new Set();
17914
+ const pilha = [];
17915
+ let proximo = 0;
17916
+ let ciclos = 0;
17917
+ for (const raiz of graph.nodes.map((n) => n.id)) {
17918
+ if (indice.has(raiz)) continue;
17919
+ const quadros = [{ no: raiz, vizinho: 0 }];
17920
+ indice.set(raiz, proximo);
17921
+ lowlink.set(raiz, proximo);
17922
+ proximo += 1;
17923
+ pilha.push(raiz);
17924
+ naPilha.add(raiz);
17925
+ while (quadros.length > 0) {
17926
+ const quadro = quadros[quadros.length - 1];
17927
+ if (quadro === void 0) break;
17928
+ const vizinhos = adjacencia.get(quadro.no) ?? [];
17929
+ if (quadro.vizinho < vizinhos.length) {
17930
+ const vizinho = vizinhos[quadro.vizinho];
17931
+ quadro.vizinho += 1;
17932
+ if (!indice.has(vizinho)) {
17933
+ indice.set(vizinho, proximo);
17934
+ lowlink.set(vizinho, proximo);
17935
+ proximo += 1;
17936
+ pilha.push(vizinho);
17937
+ naPilha.add(vizinho);
17938
+ quadros.push({ no: vizinho, vizinho: 0 });
17939
+ } else if (naPilha.has(vizinho)) {
17940
+ lowlink.set(
17941
+ quadro.no,
17942
+ Math.min(lowlink.get(quadro.no) ?? 0, indice.get(vizinho) ?? 0)
17943
+ );
17944
+ }
17945
+ continue;
17946
+ }
17947
+ quadros.pop();
17948
+ const pai = quadros[quadros.length - 1];
17949
+ if (pai !== void 0) {
17950
+ lowlink.set(
17951
+ pai.no,
17952
+ Math.min(lowlink.get(pai.no) ?? 0, lowlink.get(quadro.no) ?? 0)
17953
+ );
17954
+ }
17955
+ if (lowlink.get(quadro.no) === indice.get(quadro.no)) {
17956
+ let tamanho = 0;
17957
+ let atual;
17958
+ do {
17959
+ atual = pilha.pop();
17960
+ if (atual === void 0) break;
17961
+ naPilha.delete(atual);
17962
+ tamanho += 1;
17963
+ } while (atual !== quadro.no);
17964
+ if (tamanho > 1) ciclos += 1;
17965
+ }
17966
+ }
17967
+ }
17968
+ return ciclos;
17969
+ }
17970
+ function graphMetrics(graph) {
17971
+ return {
17972
+ nodes: graph.nodes.length,
17973
+ edges: graph.edges.length,
17974
+ routes: hintsFromGraph(graph).apiRoutes.length,
17975
+ packages: graph.packages.length,
17976
+ cycles: importCycles(graph)
17977
+ };
17978
+ }
17979
+
17980
+ // ../../packages/graph/src/identifiers.ts
17981
+ var FROM_CALL = /([A-Za-z_$][\w$]*)\s*\.\s*from\(\s*["'`]([A-Za-z0-9._-]{1,63})["'`]\s*\)/g;
17982
+ var NOT_A_DATABASE = /* @__PURE__ */ new Set([
17983
+ "array",
17984
+ "buffer",
17985
+ "object",
17986
+ "string",
17987
+ "number",
17988
+ "date",
14731
17989
  "map",
14732
17990
  "set",
14733
17991
  "promise",
@@ -14837,7 +18095,7 @@ var KIND_LABEL = {
14837
18095
  payments: "payments",
14838
18096
  secrets: "security"
14839
18097
  };
14840
- var RULES = [
18098
+ var RULES2 = [
14841
18099
  {
14842
18100
  kind: "api",
14843
18101
  test: /app\/.+\/route\.(t|j)sx?\b|pages\/api\//i
@@ -14854,16 +18112,16 @@ var RULES = [
14854
18112
  { kind: "browser", test: /\b(middleware\.(t|j)s|proxy\.(t|j)s)\b/i }
14855
18113
  ];
14856
18114
  function mapKind(signal) {
14857
- const text = `${signal.label} ${signal.files.join(" ")}`;
14858
- for (const rule of RULES) {
14859
- if (rule.test.test(text)) return rule.kind;
18115
+ const text2 = `${signal.label} ${signal.files.join(" ")}`;
18116
+ for (const rule of RULES2) {
18117
+ if (rule.test.test(text2)) return rule.kind;
14860
18118
  }
14861
18119
  return "app";
14862
18120
  }
14863
18121
 
14864
18122
  // ../../packages/graph/src/packages.ts
14865
18123
  var SECRETISH2 = /(sk_live_|sk_test_|rk_live_|pk_live_|pk_test_|sb_secret_|eyJ[A-Za-z0-9_-]{20,}|gsk_[A-Za-z0-9]|sk-ant-|sk-proj-|sk-or-v1-)/i;
14866
- var RULES2 = [
18124
+ var RULES3 = [
14867
18125
  { id: "next", test: /^next(?:\/|$)/ },
14868
18126
  { id: "react", test: /^react(?:-dom)?(?:\/|$)/ },
14869
18127
  { id: "vue", test: /^vue(?:\/|$)/ },
@@ -14903,7 +18161,7 @@ function packageId(spec) {
14903
18161
  if (cleaned.length === 0 || cleaned.startsWith(".") || cleaned.startsWith("/"))
14904
18162
  return null;
14905
18163
  if (cleaned.startsWith("node:") || cleaned.startsWith("#")) return null;
14906
- for (const rule of RULES2) {
18164
+ for (const rule of RULES3) {
14907
18165
  if (rule.test.test(cleaned)) return rule.id;
14908
18166
  }
14909
18167
  return null;
@@ -15046,9 +18304,9 @@ function firstExport(value) {
15046
18304
  }
15047
18305
  function nodesFromDump(dump) {
15048
18306
  const raw = dump["nodes"] ?? dump["nos"];
15049
- const list = Array.isArray(raw) ? raw : isRecord(raw) ? Object.entries(raw).map(([id, n]) => isRecord(n) ? { id, ...n } : { id }) : [];
18307
+ const list2 = Array.isArray(raw) ? raw : isRecord(raw) ? Object.entries(raw).map(([id, n]) => isRecord(n) ? { id, ...n } : { id }) : [];
15050
18308
  const nodes = [];
15051
- for (const [i, item] of list.entries()) {
18309
+ for (const [i, item] of list2.entries()) {
15052
18310
  if (!isRecord(item)) continue;
15053
18311
  const files = filesFrom(
15054
18312
  item["files"] ?? item["files"] ?? item["source_file"] ?? item["sourceFile"] ?? item["file"] ?? item["path"]
@@ -15070,10 +18328,10 @@ function nodesFromDump(dump) {
15070
18328
  }
15071
18329
  function edgesFromDump(dump, ids) {
15072
18330
  const raw = dump["edges"] ?? dump["edges"];
15073
- const list = Array.isArray(raw) ? raw : [];
18331
+ const list2 = Array.isArray(raw) ? raw : [];
15074
18332
  const out = [];
15075
18333
  const seen = /* @__PURE__ */ new Set();
15076
- for (const item of list) {
18334
+ for (const item of list2) {
15077
18335
  if (!isRecord(item)) continue;
15078
18336
  const from = idFrom(item["from"] ?? item["de"] ?? item["source"], "");
15079
18337
  const to = idFrom(item["to"] ?? item["para"] ?? item["target"], "");
@@ -15386,19 +18644,19 @@ var sequencedScanEventSchema = external_exports.intersection(
15386
18644
  );
15387
18645
 
15388
18646
  // src/consent.ts
15389
- import { chmodSync as chmodSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
18647
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
15390
18648
  import { createInterface } from "readline/promises";
15391
- import { join as join2, resolve } from "path";
18649
+ import { join as join5, resolve as resolve3 } from "path";
15392
18650
 
15393
18651
  // src/token.ts
15394
- import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "fs";
18652
+ import { chmodSync, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
15395
18653
  import { homedir } from "os";
15396
- import { join } from "path";
18654
+ import { join as join4 } from "path";
15397
18655
  function dirConfig() {
15398
- return join(homedir(), ".config", "vibecheck");
18656
+ return join4(homedir(), ".config", "vibecheck");
15399
18657
  }
15400
18658
  function caminhoToken() {
15401
- return join(dirConfig(), "token");
18659
+ return join4(dirConfig(), "token");
15402
18660
  }
15403
18661
  function lerToken() {
15404
18662
  const env = process.env["VIBECHECK_TOKEN"];
@@ -15407,7 +18665,7 @@ function lerToken() {
15407
18665
  return { token: env, host: hostEnv ?? "" };
15408
18666
  }
15409
18667
  try {
15410
- const bruto = readFileSync(caminhoToken(), "utf8").trim();
18668
+ const bruto = readFileSync3(caminhoToken(), "utf8").trim();
15411
18669
  const [token, host] = bruto.split("\n");
15412
18670
  if (token === void 0 || token.length === 0) return null;
15413
18671
  return { token, host: host ?? "" };
@@ -15417,9 +18675,9 @@ function lerToken() {
15417
18675
  }
15418
18676
  function gravarToken(token, host) {
15419
18677
  const dir = dirConfig();
15420
- mkdirSync(dir, { recursive: true, mode: 448 });
18678
+ mkdirSync3(dir, { recursive: true, mode: 448 });
15421
18679
  const destino = caminhoToken();
15422
- writeFileSync(destino, `${token}
18680
+ writeFileSync3(destino, `${token}
15423
18681
  ${host}
15424
18682
  `, { encoding: "utf8", mode: 384 });
15425
18683
  chmodSync(destino, 384);
@@ -15429,14 +18687,14 @@ ${host}
15429
18687
  var ARQUIVO = "code-consent.json";
15430
18688
  var ENV_CI_ALLOW = "VIBECHECK_ALLOW_CODE_SCAN";
15431
18689
  function caminho(deps) {
15432
- return join2((deps.configDir ?? dirConfig)(), ARQUIVO);
18690
+ return join5((deps.configDir ?? dirConfig)(), ARQUIVO);
15433
18691
  }
15434
18692
  function repoKey(dir) {
15435
- return resolve(dir);
18693
+ return resolve3(dir);
15436
18694
  }
15437
18695
  function listarConsentimentos(deps = {}) {
15438
18696
  try {
15439
- const bruto = JSON.parse(readFileSync2(caminho(deps), "utf8"));
18697
+ const bruto = JSON.parse(readFileSync4(caminho(deps), "utf8"));
15440
18698
  if (!Array.isArray(bruto)) return [];
15441
18699
  return bruto.filter(
15442
18700
  (e) => e !== null && typeof e === "object" && typeof e.repo === "string" && typeof e.acceptedAt === "string"
@@ -15447,9 +18705,9 @@ function listarConsentimentos(deps = {}) {
15447
18705
  }
15448
18706
  function gravar(entradas, deps) {
15449
18707
  const dir = (deps.configDir ?? dirConfig)();
15450
- mkdirSync2(dir, { recursive: true, mode: 448 });
15451
- const arquivo = join2(dir, ARQUIVO);
15452
- writeFileSync2(arquivo, `${JSON.stringify(entradas, null, 2)}
18708
+ mkdirSync4(dir, { recursive: true, mode: 448 });
18709
+ const arquivo = join5(dir, ARQUIVO);
18710
+ writeFileSync4(arquivo, `${JSON.stringify(entradas, null, 2)}
15453
18711
  `, { mode: 384 });
15454
18712
  chmodSync2(arquivo, 384);
15455
18713
  }
@@ -15547,227 +18805,26 @@ async function followScan(base, scanId, shareToken, options = {}) {
15547
18805
  }
15548
18806
 
15549
18807
  // src/gate.ts
15550
- import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
15551
- import { join as join3 } from "path";
15552
-
15553
- // ../../packages/checks/src/code/types.ts
15554
- var API_ROUTE_FILE = /(^|\/)(app\/.*\/route\.(ts|js|tsx|jsx)|pages\/api\/.+\.(ts|js))$/;
15555
- function isApiRoute(path) {
15556
- return API_ROUTE_FILE.test(path);
15557
- }
15558
-
15559
- // ../../packages/checks/src/code/checks.ts
15560
- var MIGRATION_FILE = /(^|\/)supabase\/migrations\/[^/]+\.sql$/;
15561
- var CREATE_TABLE = /create\s+table\s+(?:if\s+not\s+exists\s+)?(?:"?[a-z_][a-z0-9_]*"?\.)?"?([a-z_][a-z0-9_]*)"?/gi;
15562
- var ENABLE_RLS = /alter\s+table\s+(?:if\s+exists\s+)?(?:only\s+)?(?:"?[a-z_][a-z0-9_]*"?\.)?"?([a-z_][a-z0-9_]*)"?\s+enable\s+row\s+level\s+security/gi;
15563
- function all(pattern, text) {
15564
- const out = [];
15565
- for (const m of text.matchAll(pattern)) {
15566
- const nome = m[1];
15567
- if (nome !== void 0) out.push(nome.toLowerCase());
15568
- }
15569
- return out;
15570
- }
15571
- function isClientFile(file2) {
15572
- if (/^\s*['"]use client['"]/.test(file2.content)) return true;
15573
- if (/\.client\.(tsx|jsx|ts|js)$/.test(file2.path)) return true;
15574
- return /(^|\/)pages\/(?!api\/).+\.(tsx|jsx|ts|js)$/.test(file2.path);
15575
- }
15576
- var TOUCHES_DB = /\b(supabase\.(?:from|rpc|storage)\b|prisma\.\w+\.\w+\(|drizzle\(|\bdb\.(?:select|insert|update|delete|query|execute|from)\(|\.from\(['"]|sql`|pool\.query\(|knex\(|mongoose\.|\.collection\(['"])/;
15577
- var HAS_AUTH = /\b(getSession|getUser|auth\(\)|currentUser|requireAuth|verifyToken|verifyJwt|jwtVerify|authorization|Bearer|withAuth|clerk|next-auth|getServerSession|session)\b/i;
15578
- var PUBLIC_ROUTE = /(^|\/)(webhooks?|health|healthz|ping|status|robots|sitemap|og|opengraph|cron)(\/|\.|$)/i;
15579
- var AI_SDK = /from\s+['"](openai|@anthropic-ai\/sdk|ai|@ai-sdk\/[^'"]+|@google\/generative-ai|groq-sdk|cohere-ai|@mistralai\/mistralai)['"]/;
15580
- var HAS_LIMITER = /\b(ratelimit|rateLimit|Ratelimit|upstash|limiter|throttle|slidingWindow|tokenBucket)\b/;
15581
- var STRIPE_IMPORT = /from\s+['"]stripe['"]/;
15582
- var WEBHOOK_PATH = /webhook/i;
15583
- var SIGNATURE_CHECK = /constructEvent(?:Async)?\(|stripe-signature/;
15584
- var SERVICE_ROLE_REF = /\b(SUPABASE_SERVICE_ROLE_KEY|SERVICE_ROLE_KEY|sb_secret_[A-Za-z0-9_]+|NEXT_PUBLIC_[A-Z0-9_]*(?:SECRET|SERVICE_ROLE|PRIVATE)[A-Z0-9_]*)\b/;
15585
- var ENV_FILES = [
15586
- ".env",
15587
- ".env.local",
15588
- ".env.production",
15589
- ".env.development",
15590
- ".env.staging"
15591
- ];
15592
- var codeRlsMissing = {
15593
- id: "code-rls-missing",
15594
- source: "code",
15595
- category: "data",
15596
- severity: "critical",
15597
- title: "Table created without row level security",
15598
- impact: "A migration creates a table and never turns on row level security for it. Once it has rows, anyone with the public key can read them \u2014 this is the hole the RLS check lights up from outside.",
15599
- howToDetect: (ctx) => {
15600
- const migrations = ctx.files.filter((f) => MIGRATION_FILE.test(f.path));
15601
- if (migrations.length === 0) return [];
15602
- const comRls = /* @__PURE__ */ new Set();
15603
- for (const m of migrations) for (const t of all(ENABLE_RLS, m.content)) comRls.add(t);
15604
- const out = [];
15605
- const vistas = /* @__PURE__ */ new Set();
15606
- for (const m of migrations) {
15607
- for (const tabela of all(CREATE_TABLE, m.content)) {
15608
- if (comRls.has(tabela) || vistas.has(tabela)) continue;
15609
- vistas.add(tabela);
15610
- out.push({
15611
- path: m.path,
15612
- reason: `table \`${tabela}\` is created without enable row level security`
15613
- });
15614
- }
15615
- }
15616
- return out;
15617
- },
15618
- fixPrompt: (a) => `In your code, ${a.reason} (${a.path}).
15619
-
15620
- We do not know the columns or who should read what. We cannot write the policy from here.
15621
-
15622
- Good practice: every table starts with row level security on, in the same migration that creates it. Then one policy per real access: a person reads their own rows; a public catalog, if it exists, is a deliberate read policy; writes always go through a session. Do this in a new migration, never by editing one that already ran.
15623
-
15624
- After, deploy and read the table with the public key and no session \u2014 it must answer empty or denied.`
15625
- };
15626
- var codeServiceRoleInClient = {
15627
- id: "code-service-role-in-client",
15628
- source: "code",
15629
- category: "secrets",
15630
- severity: "fatal",
15631
- title: "Service key referenced in client code",
15632
- impact: "A file that runs in the browser mentions the Supabase service key or a NEXT_PUBLIC_ secret. Whatever the bundler inlines there ships to every visitor \u2014 this is the fatal finding from outside, one build away.",
15633
- howToDetect: (ctx) => ctx.files.filter(isClientFile).filter((f) => SERVICE_ROLE_REF.test(f.content)).map((f) => ({
15634
- path: f.path,
15635
- reason: "client-side file references a service or secret key"
15636
- })),
15637
- fixPrompt: (a) => `In your code, a client-side file references a service or secret key (${a.path}).
15638
-
15639
- We do not have the value and we do not want it. The problem is the place, not the string.
15640
-
15641
- Good practice: the service key lives only in server code \u2014 a route handler, a server action, an edge function \u2014 read from the environment without the NEXT_PUBLIC_ prefix. The browser talks to your API; your API talks to the database. Remove the reference from the client file and, if the key was ever bundled, rotate it in the dashboard.
15642
-
15643
- After, search the built bundle for the key prefix and confirm it is gone.`
15644
- };
15645
- var codeRouteWithoutAuth = {
15646
- id: "code-route-without-auth",
15647
- source: "code",
15648
- category: "auth",
15649
- severity: "high",
15650
- title: "API route touches data without a session check",
15651
- impact: "An API route reads or writes the database and nothing in it checks who is asking. If the middleware does not cover it, anyone can call it \u2014 this is what api-route-noauth lights up from outside.",
15652
- howToDetect: (ctx) => ctx.files.filter((f) => isApiRoute(f.path) && !PUBLIC_ROUTE.test(f.path)).filter((f) => TOUCHES_DB.test(f.content) && !HAS_AUTH.test(f.content)).map((f) => ({
15653
- path: f.path,
15654
- reason: "route reads or writes data with no session check in the file"
15655
- })),
15656
- fixPrompt: (a) => `In your code, a route reads or writes data with no session check in the file (${a.path}).
15657
-
15658
- We cannot see your middleware. If it already protects this path, mark the finding as known.
15659
-
15660
- Good practice: the route resolves the session first and answers 401 before touching data; what it reads or writes is scoped to that user. A public route is public on purpose, with a comment saying so, and never exposes rows that belong to someone.
15661
-
15662
- After, call the route from a logged-out client and confirm it answers 401 or 403.`
15663
- };
15664
- var codeEnvCommitted = {
15665
- id: "code-env-committed",
15666
- source: "code",
15667
- category: "secrets",
15668
- severity: "critical",
15669
- title: ".env file tracked by git",
15670
- impact: "An environment file is committed to the repository. Every clone, fork, CI log and AI tool that reads the repo has the secrets in it. Even after deleting it, the history keeps the values.",
15671
- // Só o nome. O conteúdo nunca é lido — é o único check que não recebe
15672
- // `content`, e é assim de propósito.
15673
- howToDetect: (ctx) => ENV_FILES.filter((nome) => ctx.tracked(nome)).map((nome) => ({
15674
- path: nome,
15675
- reason: `${nome} is tracked by git`
15676
- })),
15677
- fixPrompt: (a) => `In your code, ${a.reason}.
15678
-
15679
- We did not read it. We only saw that git tracks it.
15680
-
15681
- Good practice: environment files stay out of version control \u2014 add them to .gitignore, remove them from the index, and keep a committed .env.example with the variable names and no values. Every secret that was ever in the file is compromised: rotate each one in its provider.
15682
-
15683
- After, run git ls-files and confirm no .env file is listed.`
15684
- };
15685
- var codeAiRouteWithoutLimit = {
15686
- id: "code-ai-route-without-limit",
15687
- source: "code",
15688
- category: "ai",
15689
- severity: "high",
15690
- title: "AI route with no rate limit",
15691
- impact: "A route calls an LLM provider and nothing in it limits how often. A loop from one client turns your provider account into an open bill \u2014 this is ai-proxy-open, seen from inside.",
15692
- howToDetect: (ctx) => ctx.files.filter((f) => isApiRoute(f.path)).filter((f) => AI_SDK.test(f.content) && !HAS_LIMITER.test(f.content)).map((f) => ({
15693
- path: f.path,
15694
- reason: "route calls an AI provider with no rate limit in the file"
15695
- })),
15696
- fixPrompt: (a) => `In your code, a route calls an AI provider with no rate limit in the file (${a.path}).
15697
-
15698
- We cannot see limits applied elsewhere. If your gateway already throttles this path, mark the finding as known.
15699
-
15700
- Good practice: the route requires a session, then applies a per-user limit (so many calls per minute, so many tokens per day) before calling the provider, and answers 429 when it is hit. Cost caps at the provider are the second net, not the first.
15701
-
15702
- After, call the route in a loop from one client and confirm it starts answering 429.`
15703
- };
15704
- var codeWebhookUnsigned = {
15705
- id: "code-webhook-unsigned",
15706
- source: "code",
15707
- category: "config",
15708
- severity: "high",
15709
- title: "Stripe webhook without signature verification",
15710
- impact: 'A webhook route imports Stripe and never verifies the event signature. Anyone can POST a fake "payment succeeded" and your app will believe it \u2014 the one check we refuse to run from outside, because proving it would mean doing exactly that.',
15711
- howToDetect: (ctx) => ctx.files.filter((f) => isApiRoute(f.path) && WEBHOOK_PATH.test(f.path)).filter((f) => STRIPE_IMPORT.test(f.content) && !SIGNATURE_CHECK.test(f.content)).map((f) => ({
15712
- path: f.path,
15713
- reason: "Stripe webhook route never verifies the event signature"
15714
- })),
15715
- fixPrompt: (a) => `In your code, a Stripe webhook route never verifies the event signature (${a.path}).
15716
-
15717
- We cannot see the secret and do not want it.
15718
-
15719
- Good practice: the route reads the raw body and the stripe-signature header, builds the event with the webhook secret from the environment, and rejects anything that fails. Only the verified event drives state. Each event id is processed once.
15720
-
15721
- After, POST a fabricated event to the route and confirm it answers 400.`
15722
- };
15723
- var CODE_CHECKS = [
15724
- codeRlsMissing,
15725
- codeServiceRoleInClient,
15726
- codeRouteWithoutAuth,
15727
- codeEnvCommitted,
15728
- codeAiRouteWithoutLimit,
15729
- codeWebhookUnsigned
15730
- ];
15731
- function runCodeChecks(ctx, catalog = CODE_CHECKS) {
15732
- const out = [];
15733
- for (const check2 of catalog) {
15734
- let advisories = [];
15735
- try {
15736
- advisories = check2.howToDetect(ctx);
15737
- } catch {
15738
- advisories = [];
15739
- }
15740
- for (const a of advisories) {
15741
- out.push({
15742
- checkId: check2.id,
15743
- severity: check2.severity,
15744
- path: a.path,
15745
- reason: a.reason
15746
- });
15747
- }
15748
- }
15749
- return out;
15750
- }
15751
-
15752
- // src/gate.ts
18808
+ import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
18809
+ import { join as join6 } from "path";
15753
18810
  var GATE_DIR = ".vibecheck";
15754
18811
  var GATE_JSON = "quality-gate.json";
15755
18812
  var GATE_SARIF = "quality-gate.sarif";
15756
- function escreverArtefatos(dir, report, sarif, deps) {
18813
+ function escreverArtefatos(dir, report, sarif2, deps) {
15757
18814
  deps.writeArtifact(
15758
- join3(dir, GATE_DIR, GATE_JSON),
18815
+ join6(dir, GATE_DIR, GATE_JSON),
15759
18816
  `${JSON.stringify(report, null, 2)}
15760
18817
  `
15761
18818
  );
15762
18819
  deps.writeArtifact(
15763
- join3(dir, GATE_DIR, GATE_SARIF),
15764
- `${JSON.stringify(sarif, null, 2)}
18820
+ join6(dir, GATE_DIR, GATE_SARIF),
18821
+ `${JSON.stringify(sarif2, null, 2)}
15765
18822
  `
15766
18823
  );
15767
18824
  }
15768
18825
  function gravarNoDisco(caminho2, conteudo) {
15769
- mkdirSync3(join3(caminho2, ".."), { recursive: true });
15770
- writeFileSync3(caminho2, conteudo);
18826
+ mkdirSync5(join6(caminho2, ".."), { recursive: true });
18827
+ writeFileSync5(caminho2, conteudo);
15771
18828
  }
15772
18829
  async function rodarHttp(input, grafo, deps) {
15773
18830
  const headers = {
@@ -15845,7 +18902,9 @@ async function runGate(input, deps) {
15845
18902
  const grafo = await deps.extractGraph(input.dir);
15846
18903
  const metrics = grafo === null ? null : graphMetrics(grafo);
15847
18904
  const repo = deps.readRepo(input.dir);
15848
- const codeIssues = runCodeChecks(repo).map((i) => ({
18905
+ const codeIssues = runCodeChecks(repo, void 0, {
18906
+ failOnError: true
18907
+ }).map((i) => ({
15849
18908
  checkId: i.checkId,
15850
18909
  severity: i.severity,
15851
18910
  path: i.path,
@@ -15897,31 +18956,31 @@ async function enviarGate(scanId, report, input, deps) {
15897
18956
  }
15898
18957
 
15899
18958
  // src/graphify.ts
15900
- import { spawn } from "child_process";
18959
+ import { spawn as spawn2 } from "child_process";
15901
18960
  import { readFile } from "fs/promises";
15902
- import { join as join4 } from "path";
18961
+ import { join as join7 } from "path";
15903
18962
  async function tentarGraphify(raiz) {
15904
- const json2 = join4(raiz, "graphify-out", "graph.json");
18963
+ const json2 = join7(raiz, "graphify-out", "graph.json");
15905
18964
  try {
15906
18965
  return JSON.parse(await readFile(json2, "utf8"));
15907
18966
  } catch {
15908
18967
  }
15909
- const ok = await new Promise((resolve2) => {
15910
- const child = spawn("graphify", [".", "--no-viz"], {
18968
+ const ok = await new Promise((resolve4) => {
18969
+ const child = spawn2("graphify", [".", "--no-viz"], {
15911
18970
  cwd: raiz,
15912
18971
  stdio: "ignore"
15913
18972
  });
15914
18973
  const timer = setTimeout(() => {
15915
18974
  child.kill();
15916
- resolve2(false);
18975
+ resolve4(false);
15917
18976
  }, 2e4);
15918
18977
  child.on("exit", (code) => {
15919
18978
  clearTimeout(timer);
15920
- resolve2(code === 0);
18979
+ resolve4(code === 0);
15921
18980
  });
15922
18981
  child.on("error", () => {
15923
18982
  clearTimeout(timer);
15924
- resolve2(false);
18983
+ resolve4(false);
15925
18984
  });
15926
18985
  });
15927
18986
  if (!ok) return null;
@@ -15933,7 +18992,7 @@ async function tentarGraphify(raiz) {
15933
18992
  }
15934
18993
 
15935
18994
  // src/login.ts
15936
- import { spawn as spawn2 } from "child_process";
18995
+ import { spawn as spawn3 } from "child_process";
15937
18996
  function urlBase() {
15938
18997
  return (process.env["VIBECHECK_URL"] ?? "https://vibecheck.kinkai.cloud").replace(
15939
18998
  /\/$/,
@@ -15953,12 +19012,12 @@ function comandosAbrirBrowser(url2, platform = process.platform, env = process.e
15953
19012
  return [["xdg-open", [url2]]];
15954
19013
  }
15955
19014
  function tentarSpawn(cmd, args) {
15956
- return new Promise((resolve2) => {
15957
- const child = spawn2(cmd, args, { stdio: "ignore", detached: true });
15958
- child.once("error", () => resolve2(false));
19015
+ return new Promise((resolve4) => {
19016
+ const child = spawn3(cmd, args, { stdio: "ignore", detached: true });
19017
+ child.once("error", () => resolve4(false));
15959
19018
  child.once("spawn", () => {
15960
19019
  child.unref();
15961
- resolve2(true);
19020
+ resolve4(true);
15962
19021
  });
15963
19022
  });
15964
19023
  }
@@ -16018,8 +19077,8 @@ async function loginDevice(ci, options = {}) {
16018
19077
 
16019
19078
  // src/repo.ts
16020
19079
  import { execFileSync } from "child_process";
16021
- import { lstatSync, readdirSync, readFileSync as readFileSync3 } from "fs";
16022
- import { join as join5, relative } from "path";
19080
+ import { lstatSync as lstatSync3, readdirSync as readdirSync2, readFileSync as readFileSync5 } from "fs";
19081
+ import { join as join8, relative as relative2 } from "path";
16023
19082
  var MAX_FILES = 800;
16024
19083
  var MAX_BYTES2 = 64e3;
16025
19084
  var MAX_SQL_BYTES = 512e3;
@@ -16053,23 +19112,23 @@ function walk(dir, raiz, out) {
16053
19112
  if (out.length >= MAX_FILES) return;
16054
19113
  let entradas;
16055
19114
  try {
16056
- entradas = readdirSync(dir);
19115
+ entradas = readdirSync2(dir);
16057
19116
  } catch {
16058
19117
  return;
16059
19118
  }
16060
19119
  for (const nome of entradas) {
16061
19120
  if (out.length >= MAX_FILES) return;
16062
19121
  if (SKIP_DIR.has(nome) || nome.startsWith(".") && nome !== ".env") continue;
16063
- const caminho2 = join5(dir, nome);
19122
+ const caminho2 = join8(dir, nome);
16064
19123
  let st;
16065
19124
  try {
16066
- st = lstatSync(caminho2);
19125
+ st = lstatSync3(caminho2);
16067
19126
  } catch {
16068
19127
  continue;
16069
19128
  }
16070
19129
  if (st.isSymbolicLink()) continue;
16071
19130
  if (st.isDirectory()) walk(caminho2, raiz, out);
16072
- else if (SOURCE.test(nome)) out.push(relative(raiz, caminho2).split("\\").join("/"));
19131
+ else if (SOURCE.test(nome)) out.push(relative2(raiz, caminho2).split("\\").join("/"));
16073
19132
  }
16074
19133
  }
16075
19134
  function readCodeContext(dir) {
@@ -16079,9 +19138,9 @@ function readCodeContext(dir) {
16079
19138
  const files = [];
16080
19139
  for (const path of caminhos) {
16081
19140
  try {
16082
- const st = lstatSync(join5(dir, path));
19141
+ const st = lstatSync3(join8(dir, path));
16083
19142
  if (st.size > (path.endsWith(".sql") ? MAX_SQL_BYTES : MAX_BYTES2)) continue;
16084
- files.push({ path, content: readFileSync3(join5(dir, path), "utf8") });
19143
+ files.push({ path, content: readFileSync5(join8(dir, path), "utf8") });
16085
19144
  } catch {
16086
19145
  }
16087
19146
  }
@@ -16107,7 +19166,7 @@ function regras(issues) {
16107
19166
  for (const issue2 of issues) {
16108
19167
  if (!vistos.has(issue2.checkId)) vistos.set(issue2.checkId, issue2.reason);
16109
19168
  }
16110
- return [...vistos].map(([id, text]) => ({ id, shortDescription: { text } }));
19169
+ return [...vistos].map(([id, text2]) => ({ id, shortDescription: { text: text2 } }));
16111
19170
  }
16112
19171
  function toSarif(report, version2) {
16113
19172
  return {
@@ -16135,8 +19194,8 @@ function toSarif(report, version2) {
16135
19194
  }
16136
19195
 
16137
19196
  // src/walker.ts
16138
- import { lstatSync as lstatSync2, readdirSync as readdirSync2, readFileSync as readFileSync4 } from "fs";
16139
- import { join as join6, relative as relative2 } from "path";
19197
+ import { lstatSync as lstatSync4, readdirSync as readdirSync3, readFileSync as readFileSync6 } from "fs";
19198
+ import { join as join9, relative as relative3 } from "path";
16140
19199
  var IGNORAR = /* @__PURE__ */ new Set([
16141
19200
  "node_modules",
16142
19201
  ".git",
@@ -16171,16 +19230,16 @@ var HANDLERS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE",
16171
19230
  function listar(raiz, dir, acc) {
16172
19231
  let nomes;
16173
19232
  try {
16174
- nomes = readdirSync2(dir);
19233
+ nomes = readdirSync3(dir);
16175
19234
  } catch {
16176
19235
  return;
16177
19236
  }
16178
19237
  for (const nome of nomes) {
16179
19238
  if (IGNORAR.has(nome) || nome.startsWith(".")) continue;
16180
- const cheio = join6(dir, nome);
19239
+ const cheio = join9(dir, nome);
16181
19240
  let stat;
16182
19241
  try {
16183
- stat = lstatSync2(cheio);
19242
+ stat = lstatSync4(cheio);
16184
19243
  } catch {
16185
19244
  continue;
16186
19245
  }
@@ -16194,7 +19253,7 @@ function listar(raiz, dir, acc) {
16194
19253
  }
16195
19254
  }
16196
19255
  function posixRel(raiz, cheio) {
16197
- return relative2(raiz, cheio).replaceAll("\\", "/");
19256
+ return relative3(raiz, cheio).replaceAll("\\", "/");
16198
19257
  }
16199
19258
  function dirnamePosix(caminho2) {
16200
19259
  const i = caminho2.lastIndexOf("/");
@@ -16266,12 +19325,12 @@ function resolverSpec(de, spec, indice) {
16266
19325
  }
16267
19326
  return null;
16268
19327
  }
16269
- function rotuloDe(rel, exports) {
19328
+ function rotuloDe(rel, exports2) {
16270
19329
  const rota = routeFromFile(rel);
16271
- const handlers = exports.filter((e) => HANDLERS.has(e));
19330
+ const handlers = exports2.filter((e) => HANDLERS.has(e));
16272
19331
  if (rota !== null && handlers.length > 0) return `${handlers[0]} ${rota}`;
16273
19332
  if (rota !== null) return rota;
16274
- const simbolo = exports.find((e) => !HANDLERS.has(e));
19333
+ const simbolo = exports2.find((e) => !HANDLERS.has(e));
16275
19334
  if (simbolo !== void 0) return simbolo;
16276
19335
  const base = rel.split("/").at(-1)?.replace(/\.(m?[jt]sx?)$/i, "");
16277
19336
  return base !== void 0 && base.length > 0 && base !== "index" ? base : rel;
@@ -16280,16 +19339,16 @@ function lerArquivo(raiz, cheio) {
16280
19339
  const rel = posixRel(raiz, cheio);
16281
19340
  let conteudo = "";
16282
19341
  try {
16283
- conteudo = readFileSync4(cheio, "utf8").slice(0, MAX_BYTES_ARQUIVO);
19342
+ conteudo = readFileSync6(cheio, "utf8").slice(0, MAX_BYTES_ARQUIVO);
16284
19343
  } catch {
16285
19344
  return null;
16286
19345
  }
16287
- const exports = exportsDe(conteudo);
19346
+ const exports2 = exportsDe(conteudo);
16288
19347
  const specs = specsDe(conteudo);
16289
19348
  return {
16290
19349
  rel,
16291
- label: rotuloDe(rel, exports),
16292
- exports,
19350
+ label: rotuloDe(rel, exports2),
19351
+ exports: exports2,
16293
19352
  importsRel: specs.filter(
16294
19353
  (s) => s.startsWith(".") || s.startsWith("@/") || s.startsWith("~/")
16295
19354
  ),
@@ -16402,11 +19461,14 @@ function isAdvancedScan(grafo) {
16402
19461
  }
16403
19462
 
16404
19463
  // src/cli.ts
16405
- var VERSION = "0.2.0";
19464
+ var VERSION = "0.3.0";
16406
19465
  var AJUDA = `vibecheck \u2014 security check for AI-built apps
16407
19466
 
16408
19467
  npx @kinkai.cloud/vibecheck [options]
16409
19468
 
19469
+ Commands
19470
+ analyze project quality and security analysis (analyze --help)
19471
+
16410
19472
  Options
16411
19473
  --url <url> target; defaults to https://<verified host of the token>
16412
19474
  --dir <path> repository root (default: current directory)
@@ -16421,6 +19483,7 @@ Options
16421
19483
 
16422
19484
  Environment
16423
19485
  VIBECHECK_TOKEN, VIBECHECK_HOST, VIBECHECK_URL
19486
+ VIBECHECK_PROJECT_TOKEN project token for analyze --publish
16424
19487
  VIBECHECK_ALLOW_CODE_SCAN=1 required by --gate in CI
16425
19488
  `;
16426
19489
  function arg(nome, argv) {
@@ -16476,6 +19539,7 @@ async function comandoConsentimento(acao, raiz) {
16476
19539
  return GATE_EXIT.operational;
16477
19540
  }
16478
19541
  async function main(argv) {
19542
+ if (argv[0] === "analyze") return analyzeCommand(argv.slice(1));
16479
19543
  if (temFlag("--help", argv) || temFlag("-h", argv)) {
16480
19544
  console.warn(AJUDA);
16481
19545
  return 0;