@runa-ai/runa-cli 0.5.41 → 0.5.43

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.
Files changed (25) hide show
  1. package/dist/commands/ci/machine/actors/db/collect-schema-stats.d.ts +7 -5
  2. package/dist/commands/ci/machine/actors/db/collect-schema-stats.d.ts.map +1 -1
  3. package/dist/commands/ci/machine/actors/db/index.d.ts +1 -1
  4. package/dist/commands/ci/machine/actors/db/index.d.ts.map +1 -1
  5. package/dist/commands/ci/machine/actors/db/schema-stats.d.ts +63 -23
  6. package/dist/commands/ci/machine/actors/db/schema-stats.d.ts.map +1 -1
  7. package/dist/commands/ci/machine/commands/machine-runner.d.ts.map +1 -1
  8. package/dist/commands/ci/machine/contract.d.ts +5 -0
  9. package/dist/commands/ci/machine/contract.d.ts.map +1 -1
  10. package/dist/commands/ci/machine/formatters/sections/final-comment.d.ts.map +1 -1
  11. package/dist/commands/ci/machine/formatters/sections/schema-matrix.d.ts +1 -0
  12. package/dist/commands/ci/machine/formatters/sections/schema-matrix.d.ts.map +1 -1
  13. package/dist/commands/ci/machine/guards.d.ts.map +1 -1
  14. package/dist/commands/ci/machine/helpers.d.ts +2 -1
  15. package/dist/commands/ci/machine/helpers.d.ts.map +1 -1
  16. package/dist/commands/ci/machine/machine.d.ts +1 -1
  17. package/dist/commands/ci/machine/machine.d.ts.map +1 -1
  18. package/dist/commands/ci/utils/test-parallel.d.ts +4 -0
  19. package/dist/commands/ci/utils/test-parallel.d.ts.map +1 -1
  20. package/dist/commands/db/commands/db-sync.d.ts.map +1 -1
  21. package/dist/commands/db/utils/import-impact-analyzer.d.ts +40 -0
  22. package/dist/commands/db/utils/import-impact-analyzer.d.ts.map +1 -0
  23. package/dist/constants/versions.d.ts +1 -1
  24. package/dist/index.js +524 -265
  25. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -4,11 +4,11 @@ import * as path10 from 'path';
4
4
  import path10__default, { join, dirname, resolve, relative, basename, sep, isAbsolute, normalize } from 'path';
5
5
  import { fileURLToPath } from 'url';
6
6
  import { execSync, spawnSync, execFileSync, exec, spawn } from 'child_process';
7
- import * as fs6 from 'fs';
8
- import fs6__default, { existsSync, readFileSync, readdirSync, mkdtempSync, writeFileSync, mkdirSync, copyFileSync, createWriteStream, statSync, rmSync, realpathSync, promises, lstatSync, accessSync, constants, chmodSync, unlinkSync } from 'fs';
7
+ import * as fs5 from 'fs';
8
+ import fs5__default, { existsSync, readFileSync, readdirSync, mkdtempSync, writeFileSync, mkdirSync, copyFileSync, createWriteStream, statSync, rmSync, realpathSync, promises, lstatSync, accessSync, constants, chmodSync, unlinkSync } from 'fs';
9
9
  import { createCLILogger, cacheClear, CacheClearOutputSchema, CLIError, cachePrune, CachePruneOutputSchema, cacheStats, CacheStatsOutputSchema, cacheList, CacheListOutputSchema, cacheInvalidate, CacheInvalidateOutputSchema, syncFromProduction, dbGenerateDiagram, DbDiagramGenerateOutputSchema, createDbSnapshot, syncDatabase, emitDbPushFailureCapsule, emitDbAnnotations, writeDbPushStepSummary, exportDbReportJson, DbSyncOutputSchema, databasePaths, detectRequiredServices, formatDetectionResults, dbStart, DbLifecycleStartOutputSchema, dbStop, DbLifecycleStopOutputSchema, dbReset, DbLifecycleResetOutputSchema, dbValidateSchemas, DbSchemaValidateOutputSchema, DbSchemaRisksOutputSchema, dbDetectSchemaRisks, dbApplySchemas, DbSchemaApplyOutputSchema, dbGenerateTypes, DbSchemaGenerateOutputSchema, extractSchemaFilter, dbSeedInit, DbSeedInitOutputSchema, dbSeedValidate, DbSeedValidateOutputSchema, dbSeedGenerate, DbSeedGenerateOutputSchema, dbVerifySeeds, DbSeedVerifyOutputSchema, DbSnapshotCreateOutputSchema, restoreDbSnapshot, DbSnapshotRestoreOutputSchema, listDbSnapshots, DbSnapshotListOutputSchema, dbGeneratePgTapTests, DbTestGenOutputSchema, dbUpdateGoldenRecord, DbTestUpdateGoldenOutputSchema, repairRunaConfig, detectExistingInitConfig, initProject, validateInitResult, linkCliGlobally, LinkCliOutputSchema, unlinkCliGlobally, UnlinkCliOutputSchema, checkRepoStatus, CheckRepoStatusOutputSchema, enableTelemetry, disableTelemetry, getTelemetryStatus, uploadTelemetry, TelemetryUploadOutputSchema, runTest, TestRunOutputSchema, runTestService, TestServiceOutputSchema, runTestIntegration, TestIntegrationOutputSchema, runTestStatic, TestStaticOutputSchema, generateOwaspTop10Tests, TestOwaspGenerateOutputSchema, updateGoldenRecord, generateE2ETests, generateSecurityTests, generateUnitTests, generateApiTests, generateComponentTests, generateE2EScaffold, validateConfig, ValidateConfigOutputSchema, deploySchemaToProduction, WorkflowNotifyOutputSchema, devopsSync, workflowSync, validateInfrastructure, emitWorkflowValidateFailureCapsule, emitWorkflowAnnotations, writeWorkflowValidateStepSummary, exportWorkflowReportJson, WorkflowValidateInfrastructureOutputSchema, createSuccessEnvelopeSchema, CLI_CONTRACT_VERSION, runChecks, RunCheckOutputSchema, formatDuration as formatDuration$1, GITHUB_API, loadRunaConfig, getClassificationForProfile, loadRunaConfigOrThrow, recordSchemaAudit, RecordSchemaAuditOutputSchema, createBackup, CreateBackupOutputSchema, listBackups, ListBackupsOutputSchema, getBackupMetadata, restoreBackup, RestoreBackupOutputSchema, deleteBackup, DeleteBackupOutputSchema, detectSchemaNames, SUPABASE_SYSTEM_SCHEMAS, dbSeedApply, writeDbSeedStepSummary, DbSeedApplyOutputSchema, emitDbSeedFailureCapsule, syncEnvironment, EnvSyncOutputSchema, detectDatabasePackage, findProjectRoot as findProjectRoot$1, TelemetryEnableOutputSchema, TelemetryDisableOutputSchema, TelemetryStatusOutputSchema, workflowNotify, DevOpsSyncOutputSchema, WorkflowSyncOutputSchema, formatCLIError, DATABASE_PACKAGE_CANDIDATES, getStatusIcon as getStatusIcon$1, findWorkspaceRoot as findWorkspaceRoot$1, checkExtensionConfig, UpgradeTransaction, readRunaVersion, syncTemplates, SyncOutputSchema, ErrorEnvelopeSchema, preCheckSync, findConflictFiles, TestUnitGenOutputSchema, TestE2EGenerateOutputSchema, TestSecurityGenOutputSchema, TestApiGenOutputSchema, TestComponentGenOutputSchema } from '@runa-ai/runa';
10
10
  import { z } from 'zod';
11
- import fs10, { mkdir, writeFile, appendFile, readFile, rm, stat, realpath, cp, readdir, lstat } from 'fs/promises';
11
+ import fs9, { mkdir, writeFile, appendFile, readFile, rm, stat, realpath, cp, readdir, lstat } from 'fs/promises';
12
12
  import { promisify } from 'util';
13
13
  import { glob } from 'glob';
14
14
  import { Project, Node, SyntaxKind } from 'ts-morph';
@@ -925,7 +925,7 @@ var CLI_VERSION, HAS_ADMIN_COMMAND;
925
925
  var init_version = __esm({
926
926
  "src/version.ts"() {
927
927
  init_esm_shims();
928
- CLI_VERSION = "0.5.41";
928
+ CLI_VERSION = "0.5.43";
929
929
  HAS_ADMIN_COMMAND = false;
930
930
  }
931
931
  });
@@ -1323,7 +1323,7 @@ var init_dependency_analyzer = __esm({
1323
1323
  async analyze(options) {
1324
1324
  const packageJsonPath = path10__default.join(options.rootDir, "package.json");
1325
1325
  try {
1326
- await fs10.access(packageJsonPath);
1326
+ await fs9.access(packageJsonPath);
1327
1327
  } catch {
1328
1328
  return [];
1329
1329
  }
@@ -1356,7 +1356,7 @@ var init_dependency_analyzer = __esm({
1356
1356
  }
1357
1357
  async checkTyposquatting(packageJsonPath) {
1358
1358
  try {
1359
- const content = await fs10.readFile(packageJsonPath, "utf-8");
1359
+ const content = await fs9.readFile(packageJsonPath, "utf-8");
1360
1360
  const packageJson = JSON.parse(content);
1361
1361
  const allDeps = {
1362
1362
  ...packageJson.dependencies || {},
@@ -1534,7 +1534,7 @@ function createPolicyFromMatch(match, content, filePath) {
1534
1534
  };
1535
1535
  }
1536
1536
  async function parseSqlFileForPolicies(filePath) {
1537
- const content = await fs10.readFile(filePath, "utf-8");
1537
+ const content = await fs9.readFile(filePath, "utf-8");
1538
1538
  const matches = findPolicyMatches(content);
1539
1539
  return matches.map((match) => createPolicyFromMatch(match, content, filePath));
1540
1540
  }
@@ -2019,11 +2019,11 @@ function scanLineForSecrets(line, lineNumber, filePath) {
2019
2019
  return findings;
2020
2020
  }
2021
2021
  async function scanFileForSecrets(filePath) {
2022
- const stats = await fs10.stat(filePath);
2023
- if (stats.size > MAX_FILE_SIZE) {
2022
+ const stats = await fs9.stat(filePath);
2023
+ if (stats.size > MAX_FILE_SIZE2) {
2024
2024
  return [];
2025
2025
  }
2026
- const content = await fs10.readFile(filePath, "utf-8");
2026
+ const content = await fs9.readFile(filePath, "utf-8");
2027
2027
  const lines = content.split("\n");
2028
2028
  const findings = [];
2029
2029
  for (let i = 0; i < lines.length; i++) {
@@ -2098,13 +2098,13 @@ function isMultiLineFalsePositive(blockContent, lines, startLine) {
2098
2098
  }
2099
2099
  return false;
2100
2100
  }
2101
- var MAX_LINE_LENGTH, MAX_FILE_SIZE, SECRET_PATTERNS, BASE64_SECRET_INDICATORS, EXCLUDED_PATTERNS, MULTILINE_PATTERNS, SecretAnalyzer;
2101
+ var MAX_LINE_LENGTH, MAX_FILE_SIZE2, SECRET_PATTERNS, BASE64_SECRET_INDICATORS, EXCLUDED_PATTERNS, MULTILINE_PATTERNS, SecretAnalyzer;
2102
2102
  var init_secret_analyzer = __esm({
2103
2103
  "src/internal/vuln-checker/analyzers/secret-analyzer.ts"() {
2104
2104
  init_esm_shims();
2105
2105
  init_constants2();
2106
2106
  MAX_LINE_LENGTH = 2e3;
2107
- MAX_FILE_SIZE = 1024 * 1024;
2107
+ MAX_FILE_SIZE2 = 1024 * 1024;
2108
2108
  SECRET_PATTERNS = [
2109
2109
  // AWS
2110
2110
  {
@@ -3053,8 +3053,8 @@ function isPathWithinBoundary(filePath, boundaryDir) {
3053
3053
  try {
3054
3054
  const resolvedFile = path10__default.resolve(filePath);
3055
3055
  const resolvedBoundary = path10__default.resolve(boundaryDir);
3056
- const relative8 = path10__default.relative(resolvedBoundary, resolvedFile);
3057
- return !relative8.startsWith("..") && !path10__default.isAbsolute(relative8);
3056
+ const relative9 = path10__default.relative(resolvedBoundary, resolvedFile);
3057
+ return !relative9.startsWith("..") && !path10__default.isAbsolute(relative9);
3058
3058
  } catch {
3059
3059
  return false;
3060
3060
  }
@@ -3088,7 +3088,7 @@ var init_path_validation = __esm({
3088
3088
  async function loadConfig(configPath, rootDir) {
3089
3089
  const fullPath = resolvePathWithinBoundary(configPath, rootDir);
3090
3090
  try {
3091
- const content = await fs10.readFile(fullPath, "utf-8");
3091
+ const content = await fs9.readFile(fullPath, "utf-8");
3092
3092
  if (fullPath.endsWith(".yml") || fullPath.endsWith(".yaml")) {
3093
3093
  return yaml.load(content, { schema: yaml.JSON_SCHEMA });
3094
3094
  }
@@ -3103,7 +3103,7 @@ async function loadConfig(configPath, rootDir) {
3103
3103
  async function loadIgnores(ignorePath, rootDir) {
3104
3104
  const fullPath = resolvePathWithinBoundary(ignorePath, rootDir);
3105
3105
  try {
3106
- const content = await fs10.readFile(fullPath, "utf-8");
3106
+ const content = await fs9.readFile(fullPath, "utf-8");
3107
3107
  let parsed;
3108
3108
  if (fullPath.endsWith(".yml") || fullPath.endsWith(".yaml")) {
3109
3109
  parsed = yaml.load(content, { schema: yaml.JSON_SCHEMA });
@@ -3178,7 +3178,7 @@ function createAnnotationPatterns() {
3178
3178
  async function parseInlineAnnotations(filePath) {
3179
3179
  const annotations = [];
3180
3180
  try {
3181
- const content = await fs10.readFile(filePath, "utf-8");
3181
+ const content = await fs9.readFile(filePath, "utf-8");
3182
3182
  const lines = content.split("\n");
3183
3183
  for (let i = 0; i < lines.length; i++) {
3184
3184
  const line = lines[i];
@@ -7220,17 +7220,17 @@ function printSummary(logger16, output3) {
7220
7220
  }
7221
7221
  }
7222
7222
  function findRepoRoot(startDir) {
7223
- const { existsSync: existsSync50, readFileSync: readFileSync28 } = __require("fs");
7224
- const { join: join22, dirname: dirname5 } = __require("path");
7223
+ const { existsSync: existsSync51, readFileSync: readFileSync29 } = __require("fs");
7224
+ const { join: join23, dirname: dirname5 } = __require("path");
7225
7225
  let current = startDir;
7226
7226
  while (current !== dirname5(current)) {
7227
- if (existsSync50(join22(current, "turbo.json"))) {
7227
+ if (existsSync51(join23(current, "turbo.json"))) {
7228
7228
  return current;
7229
7229
  }
7230
- const pkgPath = join22(current, "package.json");
7231
- if (existsSync50(pkgPath)) {
7230
+ const pkgPath = join23(current, "package.json");
7231
+ if (existsSync51(pkgPath)) {
7232
7232
  try {
7233
- const pkg = JSON.parse(readFileSync28(pkgPath, "utf-8"));
7233
+ const pkg = JSON.parse(readFileSync29(pkgPath, "utf-8"));
7234
7234
  if (pkg.workspaces) {
7235
7235
  return current;
7236
7236
  }
@@ -7527,8 +7527,8 @@ function readPortFromScripts(appDir) {
7527
7527
  const pkgPath = path10__default.join(appDir, "package.json");
7528
7528
  if (!existsSync(pkgPath)) return 3e3;
7529
7529
  try {
7530
- const { readFileSync: readFileSync28 } = __require("fs");
7531
- const raw = readFileSync28(pkgPath, "utf-8");
7530
+ const { readFileSync: readFileSync29 } = __require("fs");
7531
+ const raw = readFileSync29(pkgPath, "utf-8");
7532
7532
  const parsed = JSON.parse(raw);
7533
7533
  const scripts = parsed.scripts;
7534
7534
  for (const key of ["start:ci", "start", "dev"]) {
@@ -7546,8 +7546,8 @@ function findWebAppUnderApps(repoRoot) {
7546
7546
  const appsDir = path10__default.join(repoRoot, "apps");
7547
7547
  if (!existsSync(appsDir)) return null;
7548
7548
  try {
7549
- const { readdirSync: readdirSync11 } = __require("fs");
7550
- const entries = readdirSync11(appsDir, { withFileTypes: true });
7549
+ const { readdirSync: readdirSync12 } = __require("fs");
7550
+ const entries = readdirSync12(appsDir, { withFileTypes: true });
7551
7551
  const priority = ["web", "dashboard", "app", "frontend", "client"];
7552
7552
  for (const name of priority) {
7553
7553
  const candidate = path10__default.join(appsDir, name);
@@ -9559,8 +9559,8 @@ async function detectRisks(repoRoot, tmpDir) {
9559
9559
  } catch (error) {
9560
9560
  let logContent = "";
9561
9561
  try {
9562
- const { readFileSync: readFileSync28 } = await import('fs');
9563
- logContent = readFileSync28(logFile, "utf-8");
9562
+ const { readFileSync: readFileSync29 } = await import('fs');
9563
+ logContent = readFileSync29(logFile, "utf-8");
9564
9564
  } catch {
9565
9565
  }
9566
9566
  const isInitialDeployment = logContent.includes("No common ancestor") || logContent.includes("INITIAL DEPLOYMENT");
@@ -9688,8 +9688,8 @@ async function applyProductionSchema(repoRoot, tmpDir, productionDbUrlAdmin, pro
9688
9688
  const totalMs = Date.now() - startTime;
9689
9689
  let logContent = "";
9690
9690
  try {
9691
- const { readFileSync: readFileSync28 } = await import('fs');
9692
- logContent = readFileSync28(logPath, "utf-8");
9691
+ const { readFileSync: readFileSync29 } = await import('fs');
9692
+ logContent = readFileSync29(logPath, "utf-8");
9693
9693
  } catch {
9694
9694
  }
9695
9695
  const parsed = parseApplyLog(logContent);
@@ -11398,98 +11398,32 @@ init_esm_shims();
11398
11398
 
11399
11399
  // src/commands/ci/machine/actors/db/schema-stats.ts
11400
11400
  init_esm_shims();
11401
- var EXCLUDED_SCHEMAS_FOR_LOCAL = ["pg_catalog", "information_schema"];
11402
- var IDENT_PATTERN = `(?:"([^"]+)"|(\\w+))`;
11403
- var SCHEMA_TABLE_PATTERN = `${IDENT_PATTERN}\\.${IDENT_PATTERN}`;
11404
- function extractIdentifier(quotedGroup, unquotedGroup) {
11405
- const raw = quotedGroup ?? unquotedGroup ?? "";
11406
- return raw.toLowerCase();
11407
- }
11408
- async function getLocalSchemaStats(repoRoot) {
11409
- const schemasDir = path10__default.join(repoRoot, "supabase", "schemas", "declarative");
11410
- const schemas = {};
11411
- const total = { tables: 0, functions: 0, policies: 0, indexes: 0, triggers: 0 };
11412
- try {
11413
- const files = await fs10.readdir(schemasDir);
11414
- const sqlFiles = files.filter((f) => f.endsWith(".sql")).sort();
11415
- for (const file of sqlFiles) {
11416
- const filePath = path10__default.join(schemasDir, file);
11417
- const content = await fs10.readFile(filePath, "utf-8");
11418
- parseLocalSqlFile(content, schemas, total);
11419
- }
11420
- } catch (error) {
11421
- console.warn(`[schema-stats] Could not read SQL files: ${error}`);
11422
- }
11423
- return { schemas, total };
11424
- }
11425
- function ensureSchema(schemas, schemaName, excluded) {
11426
- if (!schemas[schemaName] && !excluded.includes(schemaName)) {
11427
- schemas[schemaName] = { tables: 0, functions: 0, policies: 0, indexes: 0, triggers: 0 };
11428
- }
11429
- }
11430
- function parseLocalSqlFile(content, schemas, total) {
11431
- const cleanContent = content.replace(/--.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
11432
- const tableRegex = new RegExp(
11433
- `CREATE\\s+TABLE\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?${SCHEMA_TABLE_PATTERN}`,
11434
- "gi"
11435
- );
11436
- for (const match of cleanContent.matchAll(tableRegex)) {
11437
- const schemaName = extractIdentifier(match[1], match[2]);
11438
- ensureSchema(schemas, schemaName, EXCLUDED_SCHEMAS_FOR_LOCAL);
11439
- if (schemas[schemaName]) {
11440
- schemas[schemaName].tables++;
11441
- total.tables++;
11442
- }
11443
- }
11444
- const functionRegex = new RegExp(
11445
- `CREATE\\s+(?:OR\\s+REPLACE\\s+)?FUNCTION\\s+${SCHEMA_TABLE_PATTERN}`,
11446
- "gi"
11447
- );
11448
- for (const match of cleanContent.matchAll(functionRegex)) {
11449
- const schemaName = extractIdentifier(match[1], match[2]);
11450
- ensureSchema(schemas, schemaName, EXCLUDED_SCHEMAS_FOR_LOCAL);
11451
- if (schemas[schemaName]) {
11452
- schemas[schemaName].functions++;
11453
- total.functions++;
11454
- }
11455
- }
11456
- const policyRegex = new RegExp(
11457
- `CREATE\\s+POLICY\\s+(?:"[^"]+"|'[^']+'|\\S+)\\s+ON\\s+${SCHEMA_TABLE_PATTERN}`,
11458
- "gi"
11459
- );
11460
- for (const match of cleanContent.matchAll(policyRegex)) {
11461
- const schemaName = extractIdentifier(match[1], match[2]);
11462
- ensureSchema(schemas, schemaName, EXCLUDED_SCHEMAS_FOR_LOCAL);
11463
- if (schemas[schemaName]) {
11464
- schemas[schemaName].policies++;
11465
- total.policies++;
11466
- }
11467
- }
11468
- const indexRegex = new RegExp(
11469
- `CREATE\\s+(?:UNIQUE\\s+)?INDEX\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?(?:CONCURRENTLY\\s+)?${IDENT_PATTERN}\\s+ON\\s+${SCHEMA_TABLE_PATTERN}`,
11470
- "gi"
11471
- );
11472
- for (const match of cleanContent.matchAll(indexRegex)) {
11473
- const schemaName = extractIdentifier(match[3], match[4]);
11474
- ensureSchema(schemas, schemaName, EXCLUDED_SCHEMAS_FOR_LOCAL);
11475
- if (schemas[schemaName]) {
11476
- schemas[schemaName].indexes++;
11477
- total.indexes++;
11478
- }
11479
- }
11480
- const triggerRegex = new RegExp(
11481
- `CREATE\\s+TRIGGER\\s+${IDENT_PATTERN}[\\s\\S]*?ON\\s+${SCHEMA_TABLE_PATTERN}`,
11482
- "gi"
11483
- );
11484
- for (const match of cleanContent.matchAll(triggerRegex)) {
11485
- const schemaName = extractIdentifier(match[3], match[4]);
11486
- ensureSchema(schemas, schemaName, EXCLUDED_SCHEMAS_FOR_LOCAL);
11487
- if (schemas[schemaName]) {
11488
- schemas[schemaName].triggers++;
11489
- total.triggers++;
11490
- }
11491
- }
11492
- }
11401
+ var EXCLUDED_SCHEMAS2 = [
11402
+ // PostgreSQL system schemas
11403
+ "pg_catalog",
11404
+ "pg_toast",
11405
+ "information_schema",
11406
+ // Supabase managed schemas (vary by Supabase version)
11407
+ "auth",
11408
+ "storage",
11409
+ "realtime",
11410
+ "_realtime",
11411
+ // Supabase realtime internal schema
11412
+ "supabase_functions",
11413
+ "supabase_migrations",
11414
+ "vault",
11415
+ "pgsodium",
11416
+ "pgsodium_masks",
11417
+ "graphql",
11418
+ "graphql_public",
11419
+ "extensions",
11420
+ // PostgREST
11421
+ "net",
11422
+ // Cron
11423
+ "cron",
11424
+ // pgTAP test schema (test-only, should not exist in production)
11425
+ "tests"
11426
+ ];
11493
11427
  function processQueryResult(result, schemas, total, field) {
11494
11428
  for (const row of result) {
11495
11429
  const schemaName = row.schemaname;
@@ -11564,16 +11498,53 @@ async function executeSchemaQueries(sql, schemaList, schemas, total) {
11564
11498
  `;
11565
11499
  processQueryResult(triggersResult, schemas, total, "triggers");
11566
11500
  }
11567
- async function getDbSchemaStats(databaseUrl, targetSchemas) {
11501
+ async function getDetailedIndexList(sql, schemaList) {
11502
+ const result = await sql`
11503
+ SELECT
11504
+ n.nspname as schema,
11505
+ i.relname as name,
11506
+ t.relname as table,
11507
+ idx.indisunique as is_unique,
11508
+ idx.indpred IS NOT NULL as is_partial,
11509
+ CASE
11510
+ WHEN idx.indpred IS NOT NULL
11511
+ THEN pg_get_expr(idx.indpred, idx.indrelid, true)
11512
+ ELSE NULL
11513
+ END as where_clause
11514
+ FROM pg_index idx
11515
+ JOIN pg_class i ON i.oid = idx.indexrelid
11516
+ JOIN pg_class t ON t.oid = idx.indrelid
11517
+ JOIN pg_namespace n ON n.oid = i.relnamespace
11518
+ WHERE n.nspname IN (${sql.unsafe(schemaList)})
11519
+ AND NOT EXISTS (
11520
+ SELECT 1 FROM pg_constraint c
11521
+ WHERE c.conindid = idx.indexrelid
11522
+ )
11523
+ ORDER BY n.nspname, t.relname, i.relname
11524
+ `;
11525
+ return result.map((row) => ({
11526
+ schema: row.schema,
11527
+ name: row.name,
11528
+ table: row.table,
11529
+ isUnique: row.is_unique,
11530
+ isPartial: row.is_partial,
11531
+ whereClause: row.where_clause || void 0
11532
+ }));
11533
+ }
11534
+ async function getUserSchemas(sql) {
11535
+ const excludeList = EXCLUDED_SCHEMAS2.map((s) => `'${s}'`).join(", ");
11536
+ const result = await sql`
11537
+ SELECT nspname
11538
+ FROM pg_namespace
11539
+ WHERE nspname NOT IN (${sql.unsafe(excludeList)})
11540
+ AND nspname NOT LIKE 'pg_%'
11541
+ ORDER BY nspname
11542
+ `;
11543
+ return result.map((row) => row.nspname);
11544
+ }
11545
+ async function getDbSchemaStats(databaseUrl) {
11568
11546
  const schemas = {};
11569
11547
  const total = { tables: 0, functions: 0, policies: 0, indexes: 0, triggers: 0 };
11570
- if (targetSchemas.length === 0) {
11571
- return { schemas, total };
11572
- }
11573
- for (const schemaName of targetSchemas) {
11574
- schemas[schemaName] = { tables: 0, functions: 0, policies: 0, indexes: 0, triggers: 0 };
11575
- }
11576
- const schemaList = targetSchemas.map((s) => `'${s}'`).join(", ");
11577
11548
  let sql = null;
11578
11549
  const isRemoteSupabase = databaseUrl.includes(".supabase.co");
11579
11550
  try {
@@ -11584,7 +11555,17 @@ async function getDbSchemaStats(databaseUrl, targetSchemas) {
11584
11555
  // Supabase production requires SSL; local Docker does not
11585
11556
  ...isRemoteSupabase && { ssl: "require" }
11586
11557
  });
11558
+ const userSchemas = await getUserSchemas(sql);
11559
+ if (userSchemas.length === 0) {
11560
+ return { schemas, total };
11561
+ }
11562
+ for (const schemaName of userSchemas) {
11563
+ schemas[schemaName] = { tables: 0, functions: 0, policies: 0, indexes: 0, triggers: 0 };
11564
+ }
11565
+ const schemaList = userSchemas.map((s) => `'${s}'`).join(", ");
11587
11566
  await executeSchemaQueries(sql, schemaList, schemas, total);
11567
+ const indexList = await getDetailedIndexList(sql, schemaList);
11568
+ return { schemas, total, indexList };
11588
11569
  } catch (error) {
11589
11570
  console.warn(`[schema-stats] Could not query database: ${error}`);
11590
11571
  } finally {
@@ -11637,39 +11618,73 @@ function hasDisplayedStatsDiff(a, b) {
11637
11618
  function getActiveSchemas(stats) {
11638
11619
  return Object.entries(stats.schemas).filter(([_, s]) => s.tables > 0 || s.functions > 0 || s.policies > 0).map(([name]) => name).sort();
11639
11620
  }
11621
+ function compareIndexLists(reference, target) {
11622
+ const refIndexes = reference.indexList || [];
11623
+ const targetIndexes = target.indexList || [];
11624
+ const refKeys = new Set(refIndexes.map((i) => `${i.schema}.${i.name}`));
11625
+ const targetKeys = new Set(targetIndexes.map((i) => `${i.schema}.${i.name}`));
11626
+ const missing = refIndexes.filter((i) => !targetKeys.has(`${i.schema}.${i.name}`));
11627
+ const extra = targetIndexes.filter((i) => !refKeys.has(`${i.schema}.${i.name}`));
11628
+ return { missing, extra };
11629
+ }
11630
+ function formatIndexInfo(index) {
11631
+ const qualifiedName = `${index.schema}.${index.name}`;
11632
+ const tags = [];
11633
+ if (index.isUnique) tags.push("unique");
11634
+ if (index.isPartial) tags.push("partial");
11635
+ const tagStr = tags.length > 0 ? ` (${tags.join(", ")})` : "";
11636
+ return `${qualifiedName}${tagStr}`;
11637
+ }
11638
+ function hasIndexDiff(diff) {
11639
+ return diff.missing.length > 0 || diff.extra.length > 0;
11640
+ }
11640
11641
 
11641
11642
  // src/commands/ci/machine/actors/db/collect-schema-stats.ts
11643
+ function emptyStats() {
11644
+ return {
11645
+ schemas: {},
11646
+ total: { tables: 0, functions: 0, policies: 0, indexes: 0, triggers: 0 }
11647
+ };
11648
+ }
11642
11649
  var collectSchemaStatsActor = fromPromise(
11643
11650
  async ({ input: input3 }) => {
11644
- const { repoRoot, ciDbUrl, queryProduction } = input3;
11645
- console.log("[schema-stats] Collecting schema statistics...");
11646
- console.log("[schema-stats] Parsing local SQL files...");
11647
- const local = await getLocalSchemaStats(repoRoot);
11648
- const targetSchemas = Object.keys(local.schemas);
11649
- console.log(
11650
- `[schema-stats] Local: ${local.total.tables}T ${local.total.functions}F ${local.total.policies}P (schemas: ${targetSchemas.join(", ")})`
11651
- );
11652
- let ci = local;
11651
+ const { localDbUrl, ciDbUrl, queryProduction } = input3;
11652
+ console.log("[schema-stats] Collecting schema statistics (DB query mode)...");
11653
+ let local = emptyStats();
11654
+ if (localDbUrl) {
11655
+ console.log("[schema-stats] Querying local database...");
11656
+ try {
11657
+ local = await getDbSchemaStats(localDbUrl);
11658
+ const schemas = Object.keys(local.schemas);
11659
+ console.log(
11660
+ `[schema-stats] Local: ${local.total.tables}T ${local.total.functions}F ${local.total.policies}P (schemas: ${schemas.join(", ")})`
11661
+ );
11662
+ } catch (error) {
11663
+ console.warn(`[schema-stats] Failed to query local database: ${error}`);
11664
+ }
11665
+ } else {
11666
+ console.log("[schema-stats] No local database URL, skipping local stats");
11667
+ }
11668
+ let ci = emptyStats();
11653
11669
  if (ciDbUrl) {
11654
11670
  console.log("[schema-stats] Querying CI database...");
11655
11671
  try {
11656
- ci = await getDbSchemaStats(ciDbUrl, targetSchemas);
11672
+ ci = await getDbSchemaStats(ciDbUrl);
11657
11673
  console.log(
11658
11674
  `[schema-stats] CI: ${ci.total.tables}T ${ci.total.functions}F ${ci.total.policies}P`
11659
11675
  );
11660
11676
  } catch (error) {
11661
11677
  console.warn(`[schema-stats] Failed to query CI database: ${error}`);
11662
- ci = local;
11663
11678
  }
11664
11679
  } else {
11665
- console.log("[schema-stats] No CI database URL, using local stats for CI");
11680
+ console.log("[schema-stats] No CI database URL, skipping CI stats");
11666
11681
  }
11667
11682
  let production = null;
11668
11683
  const productionUrl = process.env.GH_DATABASE_URL_ADMIN || process.env.GH_DATABASE_URL;
11669
11684
  if (queryProduction && productionUrl) {
11670
11685
  console.log("[schema-stats] Querying production database...");
11671
11686
  try {
11672
- production = await getDbSchemaStats(productionUrl, targetSchemas);
11687
+ production = await getDbSchemaStats(productionUrl);
11673
11688
  console.log(
11674
11689
  `[schema-stats] Production: ${production.total.tables}T ${production.total.functions}F ${production.total.policies}P`
11675
11690
  );
@@ -11902,11 +11917,11 @@ function getIdempotentRoleNames(repoRoot) {
11902
11917
  const idempotentDir = path10__default.join(repoRoot, "supabase", "schemas", "idempotent");
11903
11918
  const roles = [];
11904
11919
  try {
11905
- const fs15 = __require("fs");
11906
- if (!fs15.existsSync(idempotentDir)) return [];
11907
- const files = fs15.readdirSync(idempotentDir).filter((f) => f.endsWith(".sql"));
11920
+ const fs14 = __require("fs");
11921
+ if (!fs14.existsSync(idempotentDir)) return [];
11922
+ const files = fs14.readdirSync(idempotentDir).filter((f) => f.endsWith(".sql"));
11908
11923
  for (const file of files) {
11909
- const content = fs15.readFileSync(path10__default.join(idempotentDir, file), "utf-8");
11924
+ const content = fs14.readFileSync(path10__default.join(idempotentDir, file), "utf-8");
11910
11925
  const roleMatches = content.matchAll(/CREATE\s+ROLE\s+(\w+)\s+WITH/gi);
11911
11926
  for (const match of roleMatches) {
11912
11927
  if (match[1]) roles.push(match[1].toLowerCase());
@@ -12016,8 +12031,8 @@ function isCommandSuccess(exitCode, fullOutput) {
12016
12031
  }
12017
12032
  async function writeLogFile(logFile, content) {
12018
12033
  try {
12019
- const fs15 = await import('fs/promises');
12020
- await fs15.writeFile(logFile, content);
12034
+ const fs14 = await import('fs/promises');
12035
+ await fs14.writeFile(logFile, content);
12021
12036
  console.log(`[DEBUG] productionPreviewActor: wrote log to ${logFile}`);
12022
12037
  } catch {
12023
12038
  }
@@ -12308,11 +12323,11 @@ function getIdempotentRoleNames2(repoRoot) {
12308
12323
  const idempotentDir = path10__default.join(repoRoot, "supabase", "schemas", "idempotent");
12309
12324
  const roles = [];
12310
12325
  try {
12311
- const fs15 = __require("fs");
12312
- if (!fs15.existsSync(idempotentDir)) return [];
12313
- const files = fs15.readdirSync(idempotentDir).filter((f) => f.endsWith(".sql"));
12326
+ const fs14 = __require("fs");
12327
+ if (!fs14.existsSync(idempotentDir)) return [];
12328
+ const files = fs14.readdirSync(idempotentDir).filter((f) => f.endsWith(".sql"));
12314
12329
  for (const file of files) {
12315
- const content = fs15.readFileSync(path10__default.join(idempotentDir, file), "utf-8");
12330
+ const content = fs14.readFileSync(path10__default.join(idempotentDir, file), "utf-8");
12316
12331
  const roleMatches = content.matchAll(/CREATE\s+ROLE\s+(\w+)\s+WITH/gi);
12317
12332
  for (const match of roleMatches) {
12318
12333
  if (match[1]) roles.push(match[1].toLowerCase());
@@ -13448,16 +13463,22 @@ async function waitUntilAllFinished(params) {
13448
13463
  ` ${statusIcon} layer${finished.p.layer} finished (exit=${finished.code}, success=${actualResult.success}${mismatchNote}), remaining=${remaining.size}`
13449
13464
  );
13450
13465
  if (params.failFast && !actualResult.success && !actualResult.skipped) {
13466
+ const killedLayers = Array.from(remaining).map((p) => p.layer);
13451
13467
  killAllSafe(Array.from(remaining));
13452
- throw new CLIError(`Layer ${finished.p.layer} failed (fail-fast)`, "CI_TEST_FAILED", [
13453
- "See logs in .runa/tmp"
13454
- ]);
13468
+ clearTimeout(timeoutId);
13469
+ return {
13470
+ successMap,
13471
+ failFast: {
13472
+ failedLayer: finished.p.layer,
13473
+ killedLayers
13474
+ }
13475
+ };
13455
13476
  }
13456
13477
  }
13457
13478
  } finally {
13458
13479
  clearTimeout(timeoutId);
13459
13480
  }
13460
- return successMap;
13481
+ return { successMap };
13461
13482
  }
13462
13483
  async function runLayersInParallel(params) {
13463
13484
  const spawned = [];
@@ -13493,29 +13514,34 @@ async function runLayersInParallel(params) {
13493
13514
  kill: () => proc.kill("SIGTERM")
13494
13515
  });
13495
13516
  }
13496
- const successMap = await waitUntilAllFinished({
13517
+ const waitResult = await waitUntilAllFinished({
13497
13518
  spawned,
13498
13519
  tmpDir: params.tmpDir,
13499
13520
  failFast: params.failFast,
13500
13521
  maxWaitSeconds: params.maxWaitSeconds,
13501
13522
  progressIntervalSeconds: params.progressIntervalSeconds
13502
13523
  });
13524
+ const killedLayerSet = new Set(waitResult.failFast?.killedLayers ?? []);
13525
+ const failFastSource = waitResult.failFast?.failedLayer;
13503
13526
  const results = [];
13504
13527
  for (const p of spawned) {
13505
13528
  const codeRaw = p.exitCode ?? await p.procPromise;
13506
13529
  const code = Number.isNaN(codeRaw) ? 1 : codeRaw;
13507
13530
  const resultFile = path10__default.join(params.tmpDir, `vitest-layer${p.layer}-result.json`);
13508
- const actualResult = successMap.get(p.layer) ?? {
13531
+ const wasKilled = killedLayerSet.has(p.layer);
13532
+ const actualResult = waitResult.successMap.get(p.layer) ?? {
13509
13533
  success: code === 0,
13510
13534
  exitCodeMismatch: false
13511
13535
  };
13512
13536
  results.push({
13513
13537
  layer: p.layer,
13514
13538
  exitCode: code,
13515
- success: actualResult.success,
13539
+ success: wasKilled ? false : actualResult.success,
13516
13540
  exitCodeMismatch: actualResult.exitCodeMismatch,
13517
13541
  skipped: actualResult.skipped,
13518
13542
  skippedReason: actualResult.skippedReason,
13543
+ killed: wasKilled,
13544
+ killedBy: wasKilled ? failFastSource : void 0,
13519
13545
  logPath: path10__default.relative(params.repoRoot, p.logFile),
13520
13546
  exitPath: path10__default.relative(params.repoRoot, p.exitFile),
13521
13547
  resultPath: path10__default.relative(params.repoRoot, resultFile),
@@ -13755,7 +13781,7 @@ function formatSkippedLayers(layerSkipReasons, originalSelectedLayers) {
13755
13781
 
13756
13782
  // src/commands/ci/machine/formatters/sections/schema-matrix.ts
13757
13783
  init_esm_shims();
13758
- function emptyStats() {
13784
+ function emptyStats2() {
13759
13785
  return { tables: 0, functions: 0, policies: 0, indexes: 0, triggers: 0 };
13760
13786
  }
13761
13787
  function formatLocalCell(stats) {
@@ -13802,8 +13828,8 @@ function getSortedActiveSchemas(schemaStats) {
13802
13828
  function generateSchemaTableRows(schemaStats, sortedSchemas, hasProduction) {
13803
13829
  const lines = [];
13804
13830
  for (const schemaName of sortedSchemas) {
13805
- const localStats = schemaStats.local.schemas[schemaName] ?? emptyStats();
13806
- const ciStats = schemaStats.ci.schemas[schemaName] ?? emptyStats();
13831
+ const localStats = schemaStats.local.schemas[schemaName] ?? emptyStats2();
13832
+ const ciStats = schemaStats.ci.schemas[schemaName] ?? emptyStats2();
13807
13833
  const prodStats = schemaStats.production?.schemas[schemaName] ?? null;
13808
13834
  const localCell = formatLocalCell(localStats);
13809
13835
  const ciCell = formatCiCellWithDiff(ciStats, localStats);
@@ -13827,6 +13853,28 @@ function generateSchemaMatrixSummary(localTotal, ciTotal, prodTotal) {
13827
13853
  }
13828
13854
  return parts.join(" ");
13829
13855
  }
13856
+ function generateIndexDiffSection(schemaStats) {
13857
+ if (!schemaStats.production) return [];
13858
+ const diff = compareIndexLists(schemaStats.local, schemaStats.production);
13859
+ if (!hasIndexDiff(diff)) return [];
13860
+ const lines = [];
13861
+ if (diff.missing.length > 0) {
13862
+ lines.push("");
13863
+ lines.push("> \u26A0\uFE0F **\u672C\u756A\u306B\u4E0D\u8DB3\u3057\u3066\u3044\u308B\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9:**");
13864
+ for (const idx of diff.missing) {
13865
+ const whereInfo = idx.whereClause ? ` WHERE ${idx.whereClause}` : "";
13866
+ lines.push(`> - \`${formatIndexInfo(idx)}\` on \`${idx.table}\`${whereInfo}`);
13867
+ }
13868
+ }
13869
+ if (diff.extra.length > 0) {
13870
+ lines.push("");
13871
+ lines.push("> \u{1F4DD} **\u672C\u756A\u306B\u4F59\u5206\u306A\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9:**");
13872
+ for (const idx of diff.extra) {
13873
+ lines.push(`> - \`${formatIndexInfo(idx)}\` on \`${idx.table}\``);
13874
+ }
13875
+ }
13876
+ return lines;
13877
+ }
13830
13878
  function formatSchemaMatrix(schemaStats, gitDiff) {
13831
13879
  if (!schemaStats) {
13832
13880
  return ["### \u30B9\u30AD\u30FC\u30DE\u72B6\u6CC1", "", "_\u30B9\u30AD\u30FC\u30DE\u7D71\u8A08\u60C5\u5831\u304C\u5229\u7528\u3067\u304D\u307E\u305B\u3093_", ""];
@@ -13846,6 +13894,7 @@ function formatSchemaMatrix(schemaStats, gitDiff) {
13846
13894
  const prodTotalCell = production ? formatProdTotalCellWithDiff(production.total, local.total) : null;
13847
13895
  const totalRow = hasProduction ? `| **Total** | ${localTotalCell} | ${ciTotalCell} | ${prodTotalCell ?? "n/a"} |` : `| **Total** | ${localTotalCell} | ${ciTotalCell} |`;
13848
13896
  const summary = generateSchemaMatrixSummary(local.total, ci.total, production?.total ?? null);
13897
+ const indexDiffSection = generateIndexDiffSection(schemaStats);
13849
13898
  return [
13850
13899
  header,
13851
13900
  "",
@@ -13854,6 +13903,7 @@ function formatSchemaMatrix(schemaStats, gitDiff) {
13854
13903
  totalRow,
13855
13904
  "",
13856
13905
  summary,
13906
+ ...indexDiffSection,
13857
13907
  "",
13858
13908
  "<sub>T=\u30C6\u30FC\u30D6\u30EB F=\u95A2\u6570 P=\u30DD\u30EA\u30B7\u30FC I=\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9 Tr=\u30C8\u30EA\u30AC\u30FC</sub>",
13859
13909
  ""
@@ -13951,7 +14001,7 @@ function generatePreviewDetails(prodPreview) {
13951
14001
  ""
13952
14002
  ];
13953
14003
  }
13954
- function generateProductionPreviewSection(prodPreview, schemaDrift) {
14004
+ function generateProductionPreviewSection(prodPreview, schemaDrift, schemaStats) {
13955
14005
  if (!prodPreview?.executed) {
13956
14006
  if (schemaDrift?.gitDiff?.filesChanged?.length) {
13957
14007
  return [
@@ -13978,18 +14028,37 @@ function generateProductionPreviewSection(prodPreview, schemaDrift) {
13978
14028
  }
13979
14029
  return lines;
13980
14030
  }
14031
+ if (schemaStats?.local && schemaStats?.production) {
14032
+ const indexDiff = compareIndexLists(schemaStats.local, schemaStats.production);
14033
+ if (hasIndexDiff(indexDiff)) {
14034
+ const missingCount = indexDiff.missing.length;
14035
+ const extraCount = indexDiff.extra.length;
14036
+ const diffParts = [];
14037
+ if (missingCount > 0) diffParts.push(`${missingCount}\u4EF6\u4E0D\u8DB3`);
14038
+ if (extraCount > 0) diffParts.push(`${extraCount}\u4EF6\u4F59\u5206`);
14039
+ return [
14040
+ `> \u26A0\uFE0F **\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u5DEE\u5206\u691C\u51FA** (${diffParts.join(", ")})`,
14041
+ ">",
14042
+ "> pg-schema-diff \u3067\u306F\u691C\u51FA\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F\u304C\u3001\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u306E\u5DEE\u5206\u304C\u3042\u308A\u307E\u3059\u3002",
14043
+ "> \u30B9\u30AD\u30FC\u30DE\u30DE\u30C8\u30EA\u30C3\u30AF\u30B9\u306E\u8A73\u7D30\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
14044
+ ""
14045
+ ];
14046
+ }
14047
+ }
13981
14048
  return ["**\u672C\u756A\u30D7\u30EC\u30D3\u30E5\u30FC**: \u2705 \u672C\u756A\u306B\u9069\u7528\u3059\u308B\u30B9\u30AD\u30FC\u30DE\u5909\u66F4\u306F\u3042\u308A\u307E\u305B\u3093", ""];
13982
14049
  }
13983
- function generateDeploySection(exitCode, layerResults, gitBranchName, productionPreview, schemaDrift, env2) {
14050
+ function generateDeploySection(exitCode, layerResults, gitBranchName, productionPreview, schemaDrift, schemaStats, env2) {
13984
14051
  if (!checkIfDeployable(exitCode, layerResults)) return [];
13985
14052
  const deployWorkflowUrl = env2.repository ? `${env2.serverUrl}/${env2.repository}/actions/workflows/deploy-db.yml` : null;
13986
- const hasSchemaChanges = productionPreview?.hasChanges === true;
14053
+ const hasPgSchemaDiffChanges = productionPreview?.hasChanges === true;
14054
+ const hasIndexChanges = schemaStats?.local && schemaStats?.production ? hasIndexDiff(compareIndexLists(schemaStats.local, schemaStats.production)) : false;
14055
+ const hasSchemaChanges = hasPgSchemaDiffChanges || hasIndexChanges;
13987
14056
  const headerEmoji = hasSchemaChanges ? "\u{1F6A8}" : "\u{1F680}";
13988
14057
  const headerText = hasSchemaChanges ? "\u672C\u756A\u30C7\u30D7\u30ED\u30A4 (\u30B9\u30AD\u30FC\u30DE\u5909\u66F4\u3042\u308A!)" : "\u672C\u756A\u30C7\u30D7\u30ED\u30A4";
13989
14058
  const lines = ["---", "", `### ${headerEmoji} ${headerText}`, ""];
13990
14059
  if (exitCode !== 0)
13991
14060
  lines.push("> **\u6CE8**: Layer 4 (E2E) \u306F\u5931\u6557\u3057\u307E\u3057\u305F\u304C\u3001\u30B9\u30AD\u30FC\u30DE\u5909\u66F4\u306F\u30C7\u30D7\u30ED\u30A4\u53EF\u80FD\u3067\u3059\u3002", "");
13992
- lines.push(...generateProductionPreviewSection(productionPreview, schemaDrift));
14061
+ lines.push(...generateProductionPreviewSection(productionPreview, schemaDrift, schemaStats));
13993
14062
  if (deployWorkflowUrl) {
13994
14063
  const buttonText = hasSchemaChanges ? "\u26A1 \u30B9\u30AD\u30FC\u30DE\u5909\u66F4\u3092\u672C\u756A\u306B\u30C7\u30D7\u30ED\u30A4" : "\u25B6\uFE0F \u672C\u756A\u306B\u30C7\u30D7\u30ED\u30A4";
13995
14064
  lines.push(
@@ -14034,6 +14103,7 @@ function generateIntermediateCommentBody(input3) {
14034
14103
  gitBranchName,
14035
14104
  productionPreview,
14036
14105
  schemaDrift,
14106
+ input3.schemaStats ?? null,
14037
14107
  env2
14038
14108
  ),
14039
14109
  "<sub>\u{1F916} RUNA CI \u751F\u6210 (\u4E2D\u9593\u66F4\u65B0)</sub>"
@@ -14064,6 +14134,7 @@ function generateCommentBody(input3) {
14064
14134
  gitBranchName,
14065
14135
  input3.productionPreview,
14066
14136
  schemaDrift,
14137
+ input3.schemaStats ?? null,
14067
14138
  env2
14068
14139
  ),
14069
14140
  "<sub>\u{1F916} RUNA CI \u751F\u6210</sub>"
@@ -14525,7 +14596,7 @@ function hasError(context) {
14525
14596
  function allTestsPassed(context) {
14526
14597
  if (!context.testsRun) return false;
14527
14598
  for (const result of Object.values(context.layerResults)) {
14528
- if (result.status === "failed") return false;
14599
+ if (result.status === "failed" || result.status === "killed") return false;
14529
14600
  }
14530
14601
  return true;
14531
14602
  }
@@ -14553,7 +14624,9 @@ function convertLayerResults(results) {
14553
14624
  const layerResults = {};
14554
14625
  for (const r of results) {
14555
14626
  let status;
14556
- if (r.skipped) {
14627
+ if (r.killed) {
14628
+ status = "killed";
14629
+ } else if (r.skipped) {
14557
14630
  status = "skipped";
14558
14631
  } else {
14559
14632
  status = r.success ? "passed" : "failed";
@@ -15108,7 +15181,8 @@ ${generateProgressCommentBody(progressInput)}`;
15108
15181
  invoke: {
15109
15182
  src: "collectSchemaStats",
15110
15183
  input: ({ context }) => ({
15111
- repoRoot: assertRepoRoot(context),
15184
+ // In CI mode, local and CI both use the Docker Supabase
15185
+ localDbUrl: context.supabase?.databaseUrlRaw ?? null,
15112
15186
  ciDbUrl: context.supabase?.databaseUrlRaw ?? null,
15113
15187
  // Query production only in ci-pr modes
15114
15188
  queryProduction: context.mode !== "ci-local"
@@ -16107,11 +16181,36 @@ async function runCiMachine(input3, logger16, onStateChange) {
16107
16181
  }
16108
16182
  function formatLayerResults2(layers) {
16109
16183
  const passed = Object.values(layers).filter((r) => r.status === "passed").length;
16110
- const failed = Object.values(layers).filter((r) => r.status === "failed").length;
16184
+ const failed = Object.values(layers).filter(
16185
+ (r) => r.status === "failed" || r.status === "killed"
16186
+ ).length;
16111
16187
  const lines = [];
16112
16188
  for (const [layer, result] of Object.entries(layers)) {
16113
- const icon = result.status === "passed" ? "\u2705" : result.status === "failed" ? "\u274C" : "\u23ED\uFE0F";
16114
- lines.push(` Layer ${layer}: ${icon} ${result.status.toUpperCase()}`);
16189
+ let icon;
16190
+ let statusText;
16191
+ switch (result.status) {
16192
+ case "passed":
16193
+ icon = "\u2705";
16194
+ statusText = "PASSED";
16195
+ break;
16196
+ case "failed":
16197
+ icon = "\u274C";
16198
+ statusText = "FAILED";
16199
+ break;
16200
+ case "killed":
16201
+ icon = "\u23F9\uFE0F";
16202
+ statusText = "KILLED (fail-fast)";
16203
+ break;
16204
+ case "skipped":
16205
+ icon = "\u23ED\uFE0F";
16206
+ statusText = "SKIPPED";
16207
+ break;
16208
+ case "timeout":
16209
+ icon = "\u23F1\uFE0F";
16210
+ statusText = "TIMEOUT";
16211
+ break;
16212
+ }
16213
+ lines.push(` Layer ${layer}: ${icon} ${statusText}`);
16115
16214
  }
16116
16215
  return { passed, failed, lines };
16117
16216
  }
@@ -16299,7 +16398,7 @@ var CiPhaseSchema = z.enum(["all", "test"]);
16299
16398
  var CiDbModeSchema = z.enum(["auto", "local"]);
16300
16399
  var RepoKindSchema = z.enum(["monorepo", "pj-repo", "unknown"]);
16301
16400
  var StepStatusSchema = z.enum(["passed", "failed", "skipped", "timeout"]);
16302
- var LayerStatusSchema = z.enum(["passed", "failed", "skipped", "timeout"]);
16401
+ var LayerStatusSchema = z.enum(["passed", "failed", "skipped", "timeout", "killed"]);
16303
16402
  z.object({
16304
16403
  // === Command ===
16305
16404
  /** CLI command: 'local' for ci local, 'pr' for ci pr */
@@ -16735,8 +16834,8 @@ function getGitHubEventAction() {
16735
16834
  const eventPath = process.env.GITHUB_EVENT_PATH;
16736
16835
  if (!eventPath) return void 0;
16737
16836
  try {
16738
- const fs15 = __require("fs");
16739
- const eventPayload = JSON.parse(fs15.readFileSync(eventPath, "utf-8"));
16837
+ const fs14 = __require("fs");
16838
+ const eventPayload = JSON.parse(fs14.readFileSync(eventPath, "utf-8"));
16740
16839
  return eventPayload.action;
16741
16840
  } catch {
16742
16841
  return void 0;
@@ -24722,6 +24821,157 @@ statsCommand.command("reset").description("Reset query statistics").option("--en
24722
24821
  // src/commands/db/commands/db-sync.ts
24723
24822
  init_esm_shims();
24724
24823
  init_config_loader();
24824
+
24825
+ // src/commands/db/utils/import-impact-analyzer.ts
24826
+ init_esm_shims();
24827
+ var SKIP_DIRS = /* @__PURE__ */ new Set([
24828
+ "node_modules",
24829
+ ".git",
24830
+ "dist",
24831
+ "build",
24832
+ ".next",
24833
+ ".runa",
24834
+ "coverage",
24835
+ "__snapshots__"
24836
+ ]);
24837
+ var MAX_FILE_SIZE = 1024 * 1024;
24838
+ var SCHEMA_IMPORT_PATTERNS = [
24839
+ /from\s+['"]@runa-ai\/runa-database\/schema['"]/,
24840
+ /from\s+['"]@runa\/database\/schema['"]/,
24841
+ /from\s+['"]@runa\/database['"]/,
24842
+ /from\s+['"].*\/database\/src\/schema['"]/,
24843
+ /from\s+['"].*\/packages\/database\/src\/schema['"]/
24844
+ ];
24845
+ function findTsFiles(dir, files = []) {
24846
+ if (!existsSync(dir)) return files;
24847
+ const entries = readdirSync(dir, { withFileTypes: true });
24848
+ for (const entry of entries) {
24849
+ const fullPath = join(dir, entry.name);
24850
+ if (entry.isDirectory()) {
24851
+ if (!SKIP_DIRS.has(entry.name)) {
24852
+ findTsFiles(fullPath, files);
24853
+ }
24854
+ } else if (entry.isFile()) {
24855
+ if (/\.(ts|tsx)$/.test(entry.name) && !entry.name.endsWith(".d.ts")) {
24856
+ files.push(fullPath);
24857
+ }
24858
+ }
24859
+ }
24860
+ return files;
24861
+ }
24862
+ function extractSchemaImports(filePath, rootDir) {
24863
+ const imports = [];
24864
+ try {
24865
+ const stats = statSync(filePath);
24866
+ if (stats.size > MAX_FILE_SIZE) {
24867
+ return imports;
24868
+ }
24869
+ const content = readFileSync(filePath, "utf-8");
24870
+ const lines = content.split("\n");
24871
+ for (let i = 0; i < lines.length; i++) {
24872
+ const line = lines[i];
24873
+ const isSchemaImport = SCHEMA_IMPORT_PATTERNS.some((pattern) => pattern.test(line));
24874
+ if (!isSchemaImport) continue;
24875
+ const sourceMatch = line.match(/from\s+['"]([^'"]+)['"]/);
24876
+ if (!sourceMatch) continue;
24877
+ const source = sourceMatch[1];
24878
+ const namedMatch = line.match(/import\s*\{([^}]+)\}/);
24879
+ if (namedMatch) {
24880
+ const symbols = namedMatch[1].split(",").map((s) => {
24881
+ const parts = s.trim().split(/\s+as\s+/);
24882
+ return parts[0].trim();
24883
+ });
24884
+ for (const symbol of symbols) {
24885
+ if (symbol) {
24886
+ imports.push({
24887
+ file: relative(rootDir, filePath),
24888
+ line: i + 1,
24889
+ symbol,
24890
+ source
24891
+ });
24892
+ }
24893
+ }
24894
+ }
24895
+ const namespaceMatch = line.match(/import\s+\*\s+as\s+(\w+)/);
24896
+ if (namespaceMatch) {
24897
+ imports.push({
24898
+ file: relative(rootDir, filePath),
24899
+ line: i + 1,
24900
+ symbol: `* as ${namespaceMatch[1]}`,
24901
+ source
24902
+ });
24903
+ }
24904
+ const typeMatch = line.match(/import\s+type\s*\{([^}]+)\}/);
24905
+ if (typeMatch) {
24906
+ const symbols = typeMatch[1].split(",").map(
24907
+ (s) => s.trim().split(/\s+as\s+/)[0].trim()
24908
+ );
24909
+ for (const symbol of symbols) {
24910
+ if (symbol) {
24911
+ imports.push({
24912
+ file: relative(rootDir, filePath),
24913
+ line: i + 1,
24914
+ symbol: `type ${symbol}`,
24915
+ source
24916
+ });
24917
+ }
24918
+ }
24919
+ }
24920
+ }
24921
+ } catch {
24922
+ }
24923
+ return imports;
24924
+ }
24925
+ function analyzeImportImpact(rootDir) {
24926
+ const symbolUsage = /* @__PURE__ */ new Map();
24927
+ const tsFiles = findTsFiles(rootDir);
24928
+ let filesWithImports = 0;
24929
+ for (const file of tsFiles) {
24930
+ const imports = extractSchemaImports(file, rootDir);
24931
+ if (imports.length > 0) {
24932
+ filesWithImports++;
24933
+ }
24934
+ for (const imp of imports) {
24935
+ const existing = symbolUsage.get(imp.symbol) ?? [];
24936
+ existing.push(imp);
24937
+ symbolUsage.set(imp.symbol, existing);
24938
+ }
24939
+ }
24940
+ return {
24941
+ symbolUsage,
24942
+ totalFilesScanned: tsFiles.length,
24943
+ filesWithImports
24944
+ };
24945
+ }
24946
+ function formatImportImpactReport(report, changedSymbols) {
24947
+ const lines = [];
24948
+ lines.push("\u{1F4CA} Schema Import Impact Analysis");
24949
+ lines.push(` Files scanned: ${report.totalFilesScanned}`);
24950
+ lines.push(` Files with schema imports: ${report.filesWithImports}`);
24951
+ lines.push("");
24952
+ const symbolsToShow = Array.from(report.symbolUsage.keys()).sort();
24953
+ if (symbolsToShow.length === 0) {
24954
+ lines.push(" No schema imports found.");
24955
+ return lines;
24956
+ }
24957
+ for (const symbol of symbolsToShow) {
24958
+ const refs = report.symbolUsage.get(symbol);
24959
+ if (!refs || refs.length === 0) continue;
24960
+ lines.push(` ${symbol}:`);
24961
+ for (const ref of refs) {
24962
+ lines.push(` \u2022 ${ref.file}:${ref.line}`);
24963
+ }
24964
+ lines.push("");
24965
+ }
24966
+ const totalRefs = Array.from(report.symbolUsage.values()).reduce(
24967
+ (sum, refs) => sum + refs.length,
24968
+ 0
24969
+ );
24970
+ lines.push(` Total: ${report.symbolUsage.size} symbols, ${totalRefs} references`);
24971
+ return lines;
24972
+ }
24973
+
24974
+ // src/commands/db/commands/db-sync.ts
24725
24975
  function printList(title, items, logger16) {
24726
24976
  if (items.length === 0) return;
24727
24977
  logger16.warn(`
@@ -24834,6 +25084,14 @@ async function runSyncAction(env2, options) {
24834
25084
  return;
24835
25085
  }
24836
25086
  logHumanSyncReport({ logger: logger16, expectedTables, expectedEnums, dbTables, dbEnums, diff });
25087
+ if (options.showImportImpact) {
25088
+ logger16.info("");
25089
+ const report = analyzeImportImpact(process.cwd());
25090
+ const lines = formatImportImpactReport(report);
25091
+ for (const line of lines) {
25092
+ logger16.info(line);
25093
+ }
25094
+ }
24837
25095
  if (countSyncIssues(diff) === 0) return;
24838
25096
  throw new CLIError("Database schema is out of sync", "DB_SCHEMA_OUT_OF_SYNC", [
24839
25097
  "Review the diff output above",
@@ -24854,7 +25112,7 @@ async function runSyncAction(env2, options) {
24854
25112
  );
24855
25113
  }
24856
25114
  }
24857
- var checkCommand2 = new Command("check").description("Compare TypeScript schema (Drizzle) vs target database (tables/enums)").argument("[env]", "Environment: local, preview, main, or production", "local").option("--json", "Output diff as JSON").action(async (env2, options) => await runSyncAction(env2, options));
25115
+ var checkCommand2 = new Command("check").description("Compare TypeScript schema (Drizzle) vs target database (tables/enums)").argument("[env]", "Environment: local, preview, main, or production", "local").option("--json", "Output diff as JSON").option("--show-import-impact", "Analyze which files import schema tables").action(async (env2, options) => await runSyncAction(env2, options));
24858
25116
 
24859
25117
  // src/commands/db/commands/db-test.ts
24860
25118
  init_esm_shims();
@@ -27303,7 +27561,8 @@ async function setVercelEnvVar(linkedDir, key, value, overwrite, logger16) {
27303
27561
  proc.stderr?.on("data", (data) => {
27304
27562
  stderr += data.toString();
27305
27563
  });
27306
- proc.stdin?.write(value);
27564
+ proc.stdin?.write(`${value}
27565
+ `);
27307
27566
  proc.stdin?.end();
27308
27567
  proc.on("close", (code) => {
27309
27568
  if (code === 0) {
@@ -29766,7 +30025,7 @@ init_esm_shims();
29766
30025
 
29767
30026
  // src/constants/versions.ts
29768
30027
  init_esm_shims();
29769
- var COMPATIBLE_TEMPLATES_VERSION = "0.5.41";
30028
+ var COMPATIBLE_TEMPLATES_VERSION = "0.5.43";
29770
30029
  var TEMPLATES_PACKAGE_NAME = "@r06-dev/runa-templates";
29771
30030
  var GITHUB_PACKAGES_REGISTRY = "https://npm.pkg.github.com";
29772
30031
 
@@ -29819,7 +30078,7 @@ function getCacheDir(version) {
29819
30078
  function isCached(version) {
29820
30079
  const cacheDir = getCacheDir(version);
29821
30080
  const templatesDir = path10__default.join(cacheDir, "templates");
29822
- return fs6__default.existsSync(templatesDir);
30081
+ return fs5__default.existsSync(templatesDir);
29823
30082
  }
29824
30083
  var scopedAuthToken = null;
29825
30084
  var tokenWasAutoDetected = false;
@@ -29955,7 +30214,7 @@ async function verifyNoSymlinks(dir, baseDir) {
29955
30214
  }
29956
30215
  async function copyToCache(tempDir, cacheDir) {
29957
30216
  const sourceTemplates = path10__default.join(tempDir, "node_modules", TEMPLATES_PACKAGE_NAME, "templates");
29958
- if (!fs6__default.existsSync(sourceTemplates)) {
30217
+ if (!fs5__default.existsSync(sourceTemplates)) {
29959
30218
  throw new CLIError("Templates directory not found in package.", "TEMPLATES_DIR_NOT_FOUND", [
29960
30219
  `Expected: ${sourceTemplates}`,
29961
30220
  "The templates package may be corrupted.",
@@ -29999,7 +30258,7 @@ async function fetchTemplates(options = {}) {
29999
30258
  }
30000
30259
  const authToken = await getAuthToken();
30001
30260
  try {
30002
- if (fresh && fs6__default.existsSync(cacheDir)) {
30261
+ if (fresh && fs5__default.existsSync(cacheDir)) {
30003
30262
  await rm(cacheDir, { recursive: true, force: true });
30004
30263
  }
30005
30264
  const tempDir = path10__default.join(os.tmpdir(), `runa-templates-${Date.now()}`);
@@ -30031,15 +30290,15 @@ async function fetchTemplates(options = {}) {
30031
30290
  var MAX_WORKSPACE_TRAVERSAL_DEPTH = 10;
30032
30291
  function isLegitimateWorkspaceRoot(workspaceRoot) {
30033
30292
  const pnpmWorkspaceFile = path10__default.join(workspaceRoot, "pnpm-workspace.yaml");
30034
- if (!fs6__default.existsSync(pnpmWorkspaceFile)) {
30293
+ if (!fs5__default.existsSync(pnpmWorkspaceFile)) {
30035
30294
  return false;
30036
30295
  }
30037
30296
  const rootPackageFile = path10__default.join(workspaceRoot, "package.json");
30038
- if (!fs6__default.existsSync(rootPackageFile)) {
30297
+ if (!fs5__default.existsSync(rootPackageFile)) {
30039
30298
  return false;
30040
30299
  }
30041
30300
  try {
30042
- const rootPkg = JSON.parse(fs6__default.readFileSync(rootPackageFile, "utf-8"));
30301
+ const rootPkg = JSON.parse(fs5__default.readFileSync(rootPackageFile, "utf-8"));
30043
30302
  const hasWorkspaces = Array.isArray(rootPkg.workspaces) && rootPkg.workspaces.length > 0;
30044
30303
  const hasExpectedName = rootPkg.name === "runa" || rootPkg.name === "@r06-dev/runa";
30045
30304
  return hasWorkspaces || hasExpectedName;
@@ -30064,11 +30323,11 @@ function resolveWorkspaceTemplates() {
30064
30323
  depth++;
30065
30324
  continue;
30066
30325
  }
30067
- if (fs6__default.existsSync(normalizedTemplatesPath)) {
30326
+ if (fs5__default.existsSync(normalizedTemplatesPath)) {
30068
30327
  const markerFile = path10__default.join(current, "packages", "runa-templates", "package.json");
30069
- if (fs6__default.existsSync(markerFile)) {
30328
+ if (fs5__default.existsSync(markerFile)) {
30070
30329
  try {
30071
- const pkg = JSON.parse(fs6__default.readFileSync(markerFile, "utf-8"));
30330
+ const pkg = JSON.parse(fs5__default.readFileSync(markerFile, "utf-8"));
30072
30331
  if (pkg.name === "@r06-dev/runa-templates") {
30073
30332
  return normalizedTemplatesPath;
30074
30333
  }
@@ -30090,9 +30349,9 @@ function checkZodVersion(_logger) {
30090
30349
  path10__default.join(process.env.HOME ?? "", "node_modules", "zod", "package.json")
30091
30350
  ];
30092
30351
  for (const zodPath of zodPaths) {
30093
- if (!fs6__default.existsSync(zodPath)) continue;
30352
+ if (!fs5__default.existsSync(zodPath)) continue;
30094
30353
  try {
30095
- const raw = fs6__default.readFileSync(zodPath, "utf-8");
30354
+ const raw = fs5__default.readFileSync(zodPath, "utf-8");
30096
30355
  const pkg = JSON.parse(raw);
30097
30356
  const version = pkg.version ?? "";
30098
30357
  if (version.startsWith("3.")) {
@@ -30630,15 +30889,15 @@ function emptyResult(filePath) {
30630
30889
  };
30631
30890
  }
30632
30891
  function ensureDirectoryExists(dirPath) {
30633
- if (!fs6.existsSync(dirPath)) {
30634
- fs6.mkdirSync(dirPath, { recursive: true });
30892
+ if (!fs5.existsSync(dirPath)) {
30893
+ fs5.mkdirSync(dirPath, { recursive: true });
30635
30894
  }
30636
30895
  }
30637
30896
  function getGeneratorVersion() {
30638
30897
  try {
30639
30898
  const pkgPath = path10.resolve(__dirname$1, "../../../package.json");
30640
- if (fs6.existsSync(pkgPath)) {
30641
- const pkg = JSON.parse(fs6.readFileSync(pkgPath, "utf-8"));
30899
+ if (fs5.existsSync(pkgPath)) {
30900
+ const pkg = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
30642
30901
  return pkg.version ?? "unknown";
30643
30902
  }
30644
30903
  } catch {
@@ -30655,21 +30914,21 @@ function detectPlatform(repoRoot) {
30655
30914
  path10.join(repoRoot, "apps", "web", pattern),
30656
30915
  path10.join(repoRoot, "apps", "dashboard", pattern)
30657
30916
  ];
30658
- if (paths.some((p) => fs6.existsSync(p))) {
30917
+ if (paths.some((p) => fs5.existsSync(p))) {
30659
30918
  return "nextjs";
30660
30919
  }
30661
30920
  }
30662
30921
  const appJsonPath = path10.join(repoRoot, "app.json");
30663
- if (fs6.existsSync(appJsonPath)) {
30922
+ if (fs5.existsSync(appJsonPath)) {
30664
30923
  try {
30665
- const appJson = JSON.parse(fs6.readFileSync(appJsonPath, "utf-8"));
30924
+ const appJson = JSON.parse(fs5.readFileSync(appJsonPath, "utf-8"));
30666
30925
  if (appJson.expo) {
30667
30926
  return "expo";
30668
30927
  }
30669
30928
  } catch {
30670
30929
  }
30671
30930
  }
30672
- if (fs6.existsSync(path10.join(repoRoot, "expo.json"))) {
30931
+ if (fs5.existsSync(path10.join(repoRoot, "expo.json"))) {
30673
30932
  return "expo";
30674
30933
  }
30675
30934
  const electronPatterns = [
@@ -30678,7 +30937,7 @@ function detectPlatform(repoRoot) {
30678
30937
  "electron.vite.config.ts",
30679
30938
  "electron.vite.config.js"
30680
30939
  ];
30681
- if (electronPatterns.some((p) => fs6.existsSync(path10.join(repoRoot, p)))) {
30940
+ if (electronPatterns.some((p) => fs5.existsSync(path10.join(repoRoot, p)))) {
30682
30941
  return "electron";
30683
30942
  }
30684
30943
  return "unknown";
@@ -30686,14 +30945,14 @@ function detectPlatform(repoRoot) {
30686
30945
  function findPossibleHonoDirs(repoRoot) {
30687
30946
  const possibleDirs = [];
30688
30947
  const appsDir = path10.join(repoRoot, "apps");
30689
- if (fs6.existsSync(appsDir)) {
30948
+ if (fs5.existsSync(appsDir)) {
30690
30949
  try {
30691
- const apps = fs6.readdirSync(appsDir, { withFileTypes: true });
30950
+ const apps = fs5.readdirSync(appsDir, { withFileTypes: true });
30692
30951
  for (const app of apps) {
30693
30952
  if (app.isDirectory()) {
30694
30953
  const routesDir = path10.join(appsDir, app.name, "routes");
30695
- if (fs6.existsSync(routesDir)) {
30696
- const files = fs6.readdirSync(routesDir);
30954
+ if (fs5.existsSync(routesDir)) {
30955
+ const files = fs5.readdirSync(routesDir);
30697
30956
  const hasRouteFiles = files.some(
30698
30957
  (f) => f.endsWith(".ts") && !f.endsWith(".test.ts") && !f.endsWith(".spec.ts")
30699
30958
  );
@@ -30707,14 +30966,14 @@ function findPossibleHonoDirs(repoRoot) {
30707
30966
  }
30708
30967
  }
30709
30968
  const packagesDir = path10.join(repoRoot, "packages");
30710
- if (fs6.existsSync(packagesDir)) {
30969
+ if (fs5.existsSync(packagesDir)) {
30711
30970
  try {
30712
- const packages = fs6.readdirSync(packagesDir, { withFileTypes: true });
30971
+ const packages = fs5.readdirSync(packagesDir, { withFileTypes: true });
30713
30972
  for (const pkg of packages) {
30714
30973
  if (pkg.isDirectory()) {
30715
30974
  const routesDir = path10.join(packagesDir, pkg.name, "routes");
30716
- if (fs6.existsSync(routesDir)) {
30717
- const files = fs6.readdirSync(routesDir);
30975
+ if (fs5.existsSync(routesDir)) {
30976
+ const files = fs5.readdirSync(routesDir);
30718
30977
  const hasRouteFiles = files.some(
30719
30978
  (f) => f.endsWith(".ts") && !f.endsWith(".test.ts") && !f.endsWith(".spec.ts")
30720
30979
  );
@@ -30804,7 +31063,7 @@ function buildDefinitionMap(definitions) {
30804
31063
  function buildEnhancedMachines(e2eManifest, definitionMap, machineDefinitions) {
30805
31064
  const enhanced = {};
30806
31065
  const normalizedIdToCanonical = /* @__PURE__ */ new Map();
30807
- const isMachineFile = (path64) => path64.includes("machine.ts") || path64.includes("machines/") || path64.includes(".machine.ts") || path64.includes("/machine/");
31066
+ const isMachineFile = (path63) => path63.includes("machine.ts") || path63.includes("machines/") || path63.includes(".machine.ts") || path63.includes("/machine/");
30808
31067
  const addMachine = (id, sourceFile, entryBuilder) => {
30809
31068
  const normalizedId = normalizeToCanonicalId(id).toLowerCase();
30810
31069
  const existingCanonical = normalizedIdToCanonical.get(normalizedId);
@@ -30946,7 +31205,7 @@ async function generateManifestFiles(manifestDir, repoRoot, verbose, resolveSche
30946
31205
  const manifestsDir = path10.join(absoluteManifestDir, "manifests");
30947
31206
  ensureDirectoryExists(manifestsDir);
30948
31207
  const tsPath = path10.join(generatedDir, "selectors.ts");
30949
- await fs6.promises.writeFile(tsPath, generateSelectorTypeScript(e2eManifest), "utf-8");
31208
+ await fs5.promises.writeFile(tsPath, generateSelectorTypeScript(e2eManifest), "utf-8");
30950
31209
  const unifiedRegistry = getUnifiedRegistry();
30951
31210
  const machineLinks = buildMachineLinks();
30952
31211
  const { apiContracts, schemasResolved } = await analyzeHonoRoutes(
@@ -30975,7 +31234,7 @@ async function generateManifestFiles(manifestDir, repoRoot, verbose, resolveSche
30975
31234
  apiContracts
30976
31235
  };
30977
31236
  const manifestPath = path10.join(manifestsDir, "manifest.json");
30978
- await fs6.promises.writeFile(manifestPath, JSON.stringify(unifiedManifest, null, 2), "utf-8");
31237
+ await fs5.promises.writeFile(manifestPath, JSON.stringify(unifiedManifest, null, 2), "utf-8");
30979
31238
  const machinesWithoutE2EMeta = machineDefinitions.filter(
30980
31239
  (def) => !def.hasE2EMeta && typeof def.id === "string"
30981
31240
  ).map((def) => ({ id: def.id, sourceFile: def.sourceFile }));
@@ -31039,7 +31298,7 @@ function registerExistingInjections(machineIds, attrs, sourceFile) {
31039
31298
  }
31040
31299
  }
31041
31300
  async function preprocessFile(filePath, repoRoot, options) {
31042
- let code = await fs6.promises.readFile(filePath, "utf-8");
31301
+ let code = await fs5.promises.readFile(filePath, "utf-8");
31043
31302
  const relativePath = path10.relative(repoRoot, filePath);
31044
31303
  collectRouteInfo(relativePath, code, options.verbose);
31045
31304
  const hasMarker = code.includes(CLI_INJECTION_MARKER);
@@ -31141,7 +31400,7 @@ async function handleChangedFile(result, check, repoRoot, verbose, state2) {
31141
31400
  });
31142
31401
  state2.totalInjections += result.injectedCount;
31143
31402
  if (check) return;
31144
- await fs6.promises.writeFile(result.filePath, result.transformedCode, "utf-8");
31403
+ await fs5.promises.writeFile(result.filePath, result.transformedCode, "utf-8");
31145
31404
  state2.filesToFormat.push(result.filePath);
31146
31405
  if (verbose) {
31147
31406
  const relativePath = path10.relative(repoRoot, result.filePath);
@@ -31164,7 +31423,7 @@ async function categorizeFiles(files) {
31164
31423
  const withJsxOnly = [];
31165
31424
  for (const filePath of files) {
31166
31425
  try {
31167
- const code = await fs6.promises.readFile(filePath, "utf-8");
31426
+ const code = await fs5.promises.readFile(filePath, "utf-8");
31168
31427
  if (mightContainMachineHooks(code)) {
31169
31428
  withMachineHooks.push(filePath);
31170
31429
  } else if (mightContainJsx(code)) {
@@ -31193,7 +31452,7 @@ async function processFiles(options) {
31193
31452
  const processOpts = { verbose, force: options.force ?? false };
31194
31453
  for (const filePath of withMachineDefinitions) {
31195
31454
  try {
31196
- const code = await fs6.promises.readFile(filePath, "utf-8");
31455
+ const code = await fs5.promises.readFile(filePath, "utf-8");
31197
31456
  const relativePath = path10.relative(repoRoot, filePath);
31198
31457
  collectRouteInfo(relativePath, code, verbose);
31199
31458
  } catch {
@@ -32922,8 +33181,8 @@ function formatLineDiff(diff) {
32922
33181
 
32923
33182
  // src/commands/template-check/actors/compare.ts
32924
33183
  function compareBothFiles(runaRelPath, templateRelPath, runaAbsPath, templateAbsPath, category, includeDiff) {
32925
- const runaContent = fs6.readFileSync(runaAbsPath, "utf-8");
32926
- const templateContent = fs6.readFileSync(templateAbsPath, "utf-8");
33184
+ const runaContent = fs5.readFileSync(runaAbsPath, "utf-8");
33185
+ const templateContent = fs5.readFileSync(templateAbsPath, "utf-8");
32927
33186
  const comparison = compareFiles(runaContent, templateContent);
32928
33187
  let status;
32929
33188
  if (comparison.identical) {
@@ -33400,9 +33659,9 @@ var PATH_MAPPING_RULES = [
33400
33659
  category: "prompts"
33401
33660
  }
33402
33661
  ];
33403
- function getCategoryForPath(path64) {
33662
+ function getCategoryForPath(path63) {
33404
33663
  for (const rule of PATH_MAPPING_RULES) {
33405
- if (matchGlobPattern(path64, rule.runa) || matchGlobPattern(path64, rule.template)) {
33664
+ if (matchGlobPattern(path63, rule.runa) || matchGlobPattern(path63, rule.template)) {
33406
33665
  return rule.category;
33407
33666
  }
33408
33667
  }
@@ -33422,17 +33681,17 @@ function applyReverseRename(normalized, rule, isTemplate) {
33422
33681
  }
33423
33682
  return normalized;
33424
33683
  }
33425
- function generateComparisonKey(path64, isTemplate) {
33426
- let normalized = isTemplate ? normalizeTemplatePath(path64) : path64;
33684
+ function generateComparisonKey(path63, isTemplate) {
33685
+ let normalized = isTemplate ? normalizeTemplatePath(path63) : path63;
33427
33686
  for (const rule of PATH_MAPPING_RULES) {
33428
33687
  normalized = applyReverseRename(normalized, rule, isTemplate);
33429
33688
  }
33430
33689
  return normalized;
33431
33690
  }
33432
- function matchGlobPattern(path64, pattern) {
33691
+ function matchGlobPattern(path63, pattern) {
33433
33692
  const regexPattern = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "<<DOUBLE_STAR>>").replace(/\*/g, "([^/]*)").replace(/<<DOUBLE_STAR>>/g, "(.*)");
33434
33693
  const regex = new RegExp(`^${regexPattern}$`);
33435
- const match = path64.match(regex);
33694
+ const match = path63.match(regex);
33436
33695
  if (match) {
33437
33696
  const subPath = match[1] ?? "";
33438
33697
  return { matched: true, subPath };
@@ -33496,7 +33755,7 @@ async function globFiles(baseDir, pattern) {
33496
33755
  return results;
33497
33756
  }
33498
33757
  function addFileResult(entryPath, fullPattern, results) {
33499
- const stats = fs6.statSync(entryPath);
33758
+ const stats = fs5.statSync(entryPath);
33500
33759
  results.push({
33501
33760
  absolutePath: entryPath,
33502
33761
  size: stats.size,
@@ -33543,16 +33802,16 @@ async function matchBraceExpansion(ctx, entries, pattern, isLastPart) {
33543
33802
  }
33544
33803
  async function matchLiteral(ctx, pattern, isLastPart) {
33545
33804
  const entryPath = path10.join(ctx.currentDir, pattern);
33546
- if (!fs6.existsSync(entryPath)) return;
33547
- const stats = fs6.statSync(entryPath);
33805
+ if (!fs5.existsSync(entryPath)) return;
33806
+ const stats = fs5.statSync(entryPath);
33548
33807
  await processEntry(ctx, entryPath, stats.isFile(), stats.isDirectory(), isLastPart);
33549
33808
  }
33550
33809
  async function walkAndMatch(ctx) {
33551
33810
  if (ctx.partIndex >= ctx.patternParts.length) return;
33552
- if (!fs6.existsSync(ctx.currentDir)) return;
33811
+ if (!fs5.existsSync(ctx.currentDir)) return;
33553
33812
  const currentPattern = ctx.patternParts[ctx.partIndex];
33554
33813
  const isLastPart = ctx.partIndex === ctx.patternParts.length - 1;
33555
- const entries = fs6.readdirSync(ctx.currentDir, { withFileTypes: true });
33814
+ const entries = fs5.readdirSync(ctx.currentDir, { withFileTypes: true });
33556
33815
  if (currentPattern === "**") {
33557
33816
  await matchDoubleWildcard(ctx, entries);
33558
33817
  } else if (currentPattern.includes("*")) {
@@ -34067,17 +34326,17 @@ function printActionsNeeded(logger16, actions) {
34067
34326
  );
34068
34327
  }
34069
34328
  function findRepoRoot3(startDir) {
34070
- const { existsSync: existsSync50, readFileSync: readFileSync28 } = __require("fs");
34071
- const { join: join22, dirname: dirname5 } = __require("path");
34329
+ const { existsSync: existsSync51, readFileSync: readFileSync29 } = __require("fs");
34330
+ const { join: join23, dirname: dirname5 } = __require("path");
34072
34331
  let current = startDir;
34073
34332
  while (current !== dirname5(current)) {
34074
- if (existsSync50(join22(current, "turbo.json"))) {
34333
+ if (existsSync51(join23(current, "turbo.json"))) {
34075
34334
  return current;
34076
34335
  }
34077
- const pkgPath = join22(current, "package.json");
34078
- if (existsSync50(pkgPath)) {
34336
+ const pkgPath = join23(current, "package.json");
34337
+ if (existsSync51(pkgPath)) {
34079
34338
  try {
34080
- const pkg = JSON.parse(readFileSync28(pkgPath, "utf-8"));
34339
+ const pkg = JSON.parse(readFileSync29(pkgPath, "utf-8"));
34081
34340
  if (pkg.workspaces) {
34082
34341
  return current;
34083
34342
  }
@@ -34143,10 +34402,10 @@ function generateReportOutput(output3, isJsonMode) {
34143
34402
  };
34144
34403
  }
34145
34404
  function validateRunaRepo(repoRoot) {
34146
- const { existsSync: existsSync50 } = __require("fs");
34147
- const { join: join22 } = __require("path");
34148
- const templateDir = join22(repoRoot, "packages/runa-templates/templates");
34149
- if (!existsSync50(templateDir)) {
34405
+ const { existsSync: existsSync51 } = __require("fs");
34406
+ const { join: join23 } = __require("path");
34407
+ const templateDir = join23(repoRoot, "packages/runa-templates/templates");
34408
+ if (!existsSync51(templateDir)) {
34150
34409
  throw new CLIError("template-check is a runa-repo only command", "NOT_RUNA_REPO", [
34151
34410
  "This command compares runa-repo with pj-repo templates",
34152
34411
  "It should only be run in the runa repository",
@@ -35379,8 +35638,8 @@ async function runVerification(logger16) {
35379
35638
  return allPassed;
35380
35639
  }
35381
35640
  async function findWorkspacesWithSdkPackages(rootDir, packageNames) {
35382
- const fs15 = await import('fs/promises');
35383
- const path64 = await import('path');
35641
+ const fs14 = await import('fs/promises');
35642
+ const path63 = await import('path');
35384
35643
  const { glob: glob8 } = await import('glob');
35385
35644
  const result = [];
35386
35645
  const packageJsonPaths = await glob8("**/package.json", {
@@ -35389,9 +35648,9 @@ async function findWorkspacesWithSdkPackages(rootDir, packageNames) {
35389
35648
  // Exclude root
35390
35649
  });
35391
35650
  for (const relPath of packageJsonPaths) {
35392
- const fullPath = path64.join(rootDir, relPath);
35651
+ const fullPath = path63.join(rootDir, relPath);
35393
35652
  try {
35394
- const content = await fs15.readFile(fullPath, "utf-8");
35653
+ const content = await fs14.readFile(fullPath, "utf-8");
35395
35654
  const pkg = JSON.parse(content);
35396
35655
  let hasAnySdkPackage = false;
35397
35656
  for (const pkgName of packageNames) {
@@ -35402,7 +35661,7 @@ async function findWorkspacesWithSdkPackages(rootDir, packageNames) {
35402
35661
  }
35403
35662
  if (hasAnySdkPackage) {
35404
35663
  result.push({
35405
- dir: path64.dirname(fullPath),
35664
+ dir: path63.dirname(fullPath),
35406
35665
  packages: packageNames
35407
35666
  // All packages, not just found ones
35408
35667
  });
@@ -36045,7 +36304,7 @@ var SchemaWatcher = class {
36045
36304
  persistent: true,
36046
36305
  ignoreInitial: true
36047
36306
  });
36048
- this.watcher.on("add", (path64) => this.handleFileEvent("add", path64)).on("change", (path64) => this.handleFileEvent("change", path64)).on("unlink", (path64) => this.handleFileEvent("unlink", path64)).on("error", (error) => this.handleError(error));
36307
+ this.watcher.on("add", (path63) => this.handleFileEvent("add", path63)).on("change", (path63) => this.handleFileEvent("change", path63)).on("unlink", (path63) => this.handleFileEvent("unlink", path63)).on("error", (error) => this.handleError(error));
36049
36308
  this.logger.success("\u2705 Schema watcher started");
36050
36309
  this.logger.info(chalk.dim(" Press Ctrl+C to stop\n"));
36051
36310
  }
@@ -36066,23 +36325,23 @@ var SchemaWatcher = class {
36066
36325
  /**
36067
36326
  * Handle file system events with debouncing
36068
36327
  */
36069
- handleFileEvent(type, path64) {
36070
- const existingTimer = this.debounceTimers.get(path64);
36328
+ handleFileEvent(type, path63) {
36329
+ const existingTimer = this.debounceTimers.get(path63);
36071
36330
  if (existingTimer) {
36072
36331
  clearTimeout(existingTimer);
36073
36332
  }
36074
36333
  const timer = setTimeout(() => {
36075
- this.processFileEvent({ type, path: path64, timestamp: /* @__PURE__ */ new Date() });
36076
- this.debounceTimers.delete(path64);
36334
+ this.processFileEvent({ type, path: path63, timestamp: /* @__PURE__ */ new Date() });
36335
+ this.debounceTimers.delete(path63);
36077
36336
  }, this.options.debounceMs);
36078
- this.debounceTimers.set(path64, timer);
36337
+ this.debounceTimers.set(path63, timer);
36079
36338
  }
36080
36339
  /**
36081
36340
  * Process file system event
36082
36341
  */
36083
36342
  async processFileEvent(event) {
36084
- const { type, path: path64 } = event;
36085
- const fileName = path64.split("/").pop() || path64;
36343
+ const { type, path: path63 } = event;
36344
+ const fileName = path63.split("/").pop() || path63;
36086
36345
  switch (type) {
36087
36346
  case "add":
36088
36347
  this.logger.info(chalk.green(`\u2795 Added: ${fileName}`));
@@ -36095,19 +36354,19 @@ var SchemaWatcher = class {
36095
36354
  return;
36096
36355
  }
36097
36356
  if (this.options.autoValidate) {
36098
- await this.validateFile(path64);
36357
+ await this.validateFile(path63);
36099
36358
  }
36100
36359
  }
36101
36360
  /**
36102
36361
  * Validate schema file
36103
36362
  */
36104
- async validateFile(path64) {
36363
+ async validateFile(path63) {
36105
36364
  try {
36106
36365
  this.logger.info(chalk.dim(" Validating..."));
36107
- const validationResult = await validateSchemaFile(path64);
36366
+ const validationResult = await validateSchemaFile(path63);
36108
36367
  if (validationResult.isValid) {
36109
36368
  this.logger.success(chalk.green(" \u2713 Validation passed"));
36110
- const risks = await detectSchemaRisks(path64);
36369
+ const risks = await detectSchemaRisks(path63);
36111
36370
  if (risks.length > 0) {
36112
36371
  this.logger.warn(` \u26A0\uFE0F ${risks.length} risk(s) detected`);
36113
36372
  for (const risk of risks) {
@@ -36122,7 +36381,7 @@ var SchemaWatcher = class {
36122
36381
  if (this.options.notifyOnError) {
36123
36382
  await notifyDesktop({
36124
36383
  title: "Schema Validation Failed",
36125
- message: `${validationResult.errors.length} error(s) in ${path64.split("/").pop()}`,
36384
+ message: `${validationResult.errors.length} error(s) in ${path63.split("/").pop()}`,
36126
36385
  type: "error"
36127
36386
  });
36128
36387
  }
@@ -36341,7 +36600,7 @@ var TestWatcher = class {
36341
36600
  persistent: true,
36342
36601
  ignoreInitial: true
36343
36602
  });
36344
- this.watcher.on("add", (path64) => this.handleFileEvent("add", path64)).on("change", (path64) => this.handleFileEvent("change", path64)).on("unlink", (path64) => this.handleFileEvent("unlink", path64)).on("error", (error) => this.handleError(error));
36603
+ this.watcher.on("add", (path63) => this.handleFileEvent("add", path63)).on("change", (path63) => this.handleFileEvent("change", path63)).on("unlink", (path63) => this.handleFileEvent("unlink", path63)).on("error", (error) => this.handleError(error));
36345
36604
  this.logger.success("\u2705 Test watcher started");
36346
36605
  this.logger.info(chalk.dim(" Press Ctrl+C to stop\n"));
36347
36606
  }
@@ -36362,26 +36621,26 @@ var TestWatcher = class {
36362
36621
  /**
36363
36622
  * Handle file system events with debouncing
36364
36623
  */
36365
- handleFileEvent(type, path64) {
36366
- const existingTimer = this.debounceTimers.get(path64);
36624
+ handleFileEvent(type, path63) {
36625
+ const existingTimer = this.debounceTimers.get(path63);
36367
36626
  if (existingTimer) {
36368
36627
  clearTimeout(existingTimer);
36369
36628
  }
36370
36629
  const timer = setTimeout(() => {
36371
- this.processFileEvent(type, path64);
36372
- this.debounceTimers.delete(path64);
36630
+ this.processFileEvent(type, path63);
36631
+ this.debounceTimers.delete(path63);
36373
36632
  }, this.options.debounceMs);
36374
- this.debounceTimers.set(path64, timer);
36633
+ this.debounceTimers.set(path63, timer);
36375
36634
  }
36376
36635
  /**
36377
36636
  * Process file system event
36378
36637
  */
36379
- async processFileEvent(type, path64) {
36638
+ async processFileEvent(type, path63) {
36380
36639
  if (this.isRunning) {
36381
36640
  this.logger.warn(chalk.yellow("\u23F3 Tests already running, skipping..."));
36382
36641
  return;
36383
36642
  }
36384
- const fileName = path64.split("/").pop() || path64;
36643
+ const fileName = path63.split("/").pop() || path63;
36385
36644
  switch (type) {
36386
36645
  case "add":
36387
36646
  this.logger.info(chalk.green(`\u2795 Test added: ${fileName}`));
@@ -36394,7 +36653,7 @@ var TestWatcher = class {
36394
36653
  return;
36395
36654
  }
36396
36655
  if (this.options.autoRun) {
36397
- await this.runTests(path64);
36656
+ await this.runTests(path63);
36398
36657
  }
36399
36658
  }
36400
36659
  /**