@runa-ai/runa-cli 0.5.51 → 0.5.53
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/commands/build/machine.d.ts.map +1 -1
- package/dist/commands/ci/machine/commands/ci-local.d.ts.map +1 -1
- package/dist/commands/ci/machine/commands/ci-pr.d.ts.map +1 -1
- package/dist/commands/ci/machine/commands/runtime-env.d.ts +8 -0
- package/dist/commands/ci/machine/commands/runtime-env.d.ts.map +1 -0
- package/dist/commands/ci/machine/contract.d.ts +1 -0
- package/dist/commands/ci/machine/contract.d.ts.map +1 -1
- package/dist/commands/ci/machine/helpers.d.ts +13 -0
- package/dist/commands/ci/machine/helpers.d.ts.map +1 -1
- package/dist/commands/ci/machine/machine.d.ts +1 -0
- package/dist/commands/ci/machine/machine.d.ts.map +1 -1
- package/dist/commands/db/apply/helpers/pg-schema-diff-helpers.d.ts +14 -0
- package/dist/commands/db/apply/helpers/pg-schema-diff-helpers.d.ts.map +1 -1
- package/dist/commands/db/apply/helpers/retry-logic.d.ts.map +1 -1
- package/dist/commands/db/constants.d.ts +3 -3
- package/dist/commands/db/constants.d.ts.map +1 -1
- package/dist/commands/db/utils/db-target.d.ts.map +1 -1
- package/dist/commands/db/utils/preflight-check.d.ts.map +1 -1
- package/dist/commands/db/utils/schema-detector.d.ts.map +1 -1
- package/dist/commands/db/utils/sql-table-extractor.d.ts.map +1 -1
- package/dist/commands/env/commands/env-pull.d.ts.map +1 -1
- package/dist/commands/env/constants/local-supabase.d.ts +30 -62
- package/dist/commands/env/constants/local-supabase.d.ts.map +1 -1
- package/dist/commands/inject-test-attrs/action.d.ts.map +1 -1
- package/dist/commands/inject-test-attrs/commands/inject-test-attrs.d.ts.map +1 -1
- package/dist/commands/inject-test-attrs/contract.d.ts +118 -0
- package/dist/commands/inject-test-attrs/contract.d.ts.map +1 -1
- package/dist/commands/inject-test-attrs/detection-diagnostics.d.ts +67 -0
- package/dist/commands/inject-test-attrs/detection-diagnostics.d.ts.map +1 -0
- package/dist/commands/inject-test-attrs/manifest-generator.d.ts +7 -1
- package/dist/commands/inject-test-attrs/manifest-generator.d.ts.map +1 -1
- package/dist/commands/inject-test-attrs/processor-utils.d.ts.map +1 -1
- package/dist/commands/inject-test-attrs/processor.d.ts.map +1 -1
- package/dist/commands/inject-test-attrs/types.d.ts +57 -0
- package/dist/commands/inject-test-attrs/types.d.ts.map +1 -1
- package/dist/commands/manifest/index.d.ts.map +1 -1
- package/dist/commands/test/commands/test-e2e.d.ts.map +1 -1
- package/dist/commands/test/commands/test-integration.d.ts.map +1 -1
- package/dist/commands/test/commands/test-layer.d.ts.map +1 -1
- package/dist/commands/test/commands/test.d.ts.map +1 -1
- package/dist/index.js +1718 -725
- package/dist/internal/machines/index.d.ts +1 -0
- package/dist/internal/machines/index.d.ts.map +1 -1
- package/dist/internal/machines/snapshot-helpers.d.ts +30 -0
- package/dist/internal/machines/snapshot-helpers.d.ts.map +1 -0
- package/dist/validators/risk-detector.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'module';
|
|
3
|
-
import * as
|
|
4
|
-
import
|
|
3
|
+
import * as path11 from 'path';
|
|
4
|
+
import path11__default, { join, dirname, resolve, relative, basename, sep, isAbsolute, normalize } from 'path';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
|
-
import { execSync, spawnSync, execFileSync, exec, spawn } from 'child_process';
|
|
7
6
|
import * as fs5 from 'fs';
|
|
8
7
|
import fs5__default, { existsSync, readFileSync, readdirSync, mkdtempSync, writeFileSync, mkdirSync, copyFileSync, createWriteStream, statSync, rmSync, realpathSync, renameSync, promises, lstatSync, accessSync, constants, chmodSync, unlinkSync } from 'fs';
|
|
8
|
+
import { execSync, spawnSync, execFileSync, exec, spawn } from 'child_process';
|
|
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, getStatusIcon as getStatusIcon$1, findWorkspaceRoot as findWorkspaceRoot$1, checkExtensionConfig, UpgradeTransaction, readRunaVersion, syncTemplates, SyncOutputSchema, DATABASE_PACKAGE_CANDIDATES, ErrorEnvelopeSchema, preCheckSync, findConflictFiles, TestUnitGenOutputSchema, TestE2EGenerateOutputSchema, TestSecurityGenOutputSchema, TestApiGenOutputSchema, TestComponentGenOutputSchema } from '@runa-ai/runa';
|
|
10
10
|
import { z } from 'zod';
|
|
11
11
|
import fs9, { mkdir, writeFile, appendFile, readFile, rm, stat, realpath, cp, readdir, lstat } from 'fs/promises';
|
|
@@ -33,7 +33,7 @@ import { createJiti } from 'jiti';
|
|
|
33
33
|
import ora from 'ora';
|
|
34
34
|
import { stdout, stdin } from 'process';
|
|
35
35
|
import * as readline from 'readline/promises';
|
|
36
|
-
import { clearInjectionRegistry, clearUnifiedRegistry, postProcessRegistries, isPageFile, collectPageInfo, isLayoutFile, collectLayoutInfo, isApiRouteFile, collectApiRouteInfo, isMiddlewareFile, collectAuthBoundaries, hasMachineDefinition, collectMachineDefinition, getInjectionRegistry, buildManifest, getAllMachineDefinitions, generateSelectorTypeScript, getUnifiedRegistry, buildMachineLinks,
|
|
36
|
+
import { clearInjectionRegistry, clearUnifiedRegistry, postProcessRegistries, isPageFile, collectPageInfo, isLayoutFile, collectLayoutInfo, isApiRouteFile, collectApiRouteInfo, isMiddlewareFile, collectAuthBoundaries, hasMachineDefinition, collectMachineDefinition, createReadAndParseFile, createResolveImportPath, transformSync, getInjectionRegistry, buildManifest, getAllMachineDefinitions, generateSelectorTypeScript, getUnifiedRegistry, buildMachineLinks, registerInjection } from '@runa-ai/runa-xstate-test-plugin/standalone';
|
|
37
37
|
import { listSessions, formatDuration as formatDuration$2, cleanupStaleSessions, removeSession, isSessionCheckDisabled, getCurrentSessionId, checkActiveSessions, createSession, addActivity, checkConflicts, formatConflictDetails } from '@runa-ai/runa/session';
|
|
38
38
|
import { render, Box, Text } from 'ink';
|
|
39
39
|
import Spinner from 'ink-spinner';
|
|
@@ -73,10 +73,230 @@ var getFilename, getDirname, __dirname$1;
|
|
|
73
73
|
var init_esm_shims = __esm({
|
|
74
74
|
"../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/esm_shims.js"() {
|
|
75
75
|
getFilename = () => fileURLToPath(import.meta.url);
|
|
76
|
-
getDirname = () =>
|
|
76
|
+
getDirname = () => path11__default.dirname(getFilename());
|
|
77
77
|
__dirname$1 = /* @__PURE__ */ getDirname();
|
|
78
78
|
}
|
|
79
79
|
});
|
|
80
|
+
function isValidPort(value) {
|
|
81
|
+
if (typeof value !== "number") {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
return Number.isInteger(value) && value > 0 && value < 65536;
|
|
85
|
+
}
|
|
86
|
+
function asPort(value, fallback) {
|
|
87
|
+
const parsed = typeof value === "number" ? value : typeof value === "string" ? Number.parseInt(value, 10) : NaN;
|
|
88
|
+
return isValidPort(parsed) ? parsed : fallback;
|
|
89
|
+
}
|
|
90
|
+
function asHost(value) {
|
|
91
|
+
if (!value) return null;
|
|
92
|
+
const trimmed = value.trim();
|
|
93
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
94
|
+
}
|
|
95
|
+
function getStatusValue(status, keys) {
|
|
96
|
+
for (const key of keys) {
|
|
97
|
+
const value = status[key];
|
|
98
|
+
if (typeof value === "string" && value.trim()) {
|
|
99
|
+
return value.trim();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
function extractHost(rawUrl) {
|
|
105
|
+
if (!rawUrl) return null;
|
|
106
|
+
try {
|
|
107
|
+
return new URL(rawUrl).hostname;
|
|
108
|
+
} catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function extractPort(rawUrl) {
|
|
113
|
+
if (!rawUrl) return null;
|
|
114
|
+
try {
|
|
115
|
+
const port = new URL(rawUrl).port;
|
|
116
|
+
if (!port) return null;
|
|
117
|
+
const parsed = Number.parseInt(port, 10);
|
|
118
|
+
return isValidPort(parsed) ? parsed : null;
|
|
119
|
+
} catch {
|
|
120
|
+
const match = rawUrl.match(/:(\d+)\//);
|
|
121
|
+
if (!match) return null;
|
|
122
|
+
const parsed = Number.parseInt(match[1], 10);
|
|
123
|
+
return isValidPort(parsed) ? parsed : null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function parseTomlPort(content, section, key) {
|
|
127
|
+
const sectionRegex = new RegExp(`\\[${section}\\]([\\s\\S]*?)(?=\\n\\[|$)`, "m");
|
|
128
|
+
const sectionMatch = sectionRegex.exec(content);
|
|
129
|
+
if (!sectionMatch) return null;
|
|
130
|
+
const sectionContent = sectionMatch[1];
|
|
131
|
+
const keyRegex = new RegExp(`^\\s*${key}\\s*=\\s*(\\d+)`, "m");
|
|
132
|
+
const keyMatch = keyRegex.exec(sectionContent);
|
|
133
|
+
if (!keyMatch) return null;
|
|
134
|
+
const parsed = Number.parseInt(keyMatch[1], 10);
|
|
135
|
+
return isValidPort(parsed) ? parsed : null;
|
|
136
|
+
}
|
|
137
|
+
function parseTomlPorts(configRoot) {
|
|
138
|
+
const configPath = path11__default.join(configRoot, "supabase", "config.toml");
|
|
139
|
+
if (!existsSync(configPath)) return null;
|
|
140
|
+
try {
|
|
141
|
+
const content = readFileSync(configPath, "utf-8");
|
|
142
|
+
return {
|
|
143
|
+
api: parseTomlPort(content, "api", "port"),
|
|
144
|
+
db: parseTomlPort(content, "db", "port")
|
|
145
|
+
};
|
|
146
|
+
} catch {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function detectStatus(projectRoot) {
|
|
151
|
+
try {
|
|
152
|
+
const result = spawnSync("supabase", ["status", "--output", "json"], {
|
|
153
|
+
cwd: projectRoot,
|
|
154
|
+
encoding: "utf-8",
|
|
155
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
156
|
+
timeout: 5e3
|
|
157
|
+
});
|
|
158
|
+
if (result.status !== 0 || !result.stdout) return null;
|
|
159
|
+
const jsonMatch = result.stdout.match(/\{[\s\S]*\}/);
|
|
160
|
+
if (!jsonMatch) return null;
|
|
161
|
+
return JSON.parse(jsonMatch[0]);
|
|
162
|
+
} catch {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function resolveSupabaseRoot(projectRoot) {
|
|
167
|
+
const start = path11__default.resolve(projectRoot);
|
|
168
|
+
let current = start;
|
|
169
|
+
while (true) {
|
|
170
|
+
if (existsSync(path11__default.join(current, "supabase", "config.toml"))) {
|
|
171
|
+
return current;
|
|
172
|
+
}
|
|
173
|
+
const parent = path11__default.dirname(current);
|
|
174
|
+
if (parent === current) {
|
|
175
|
+
return start;
|
|
176
|
+
}
|
|
177
|
+
current = parent;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function isAllowedLocalhostHost(host) {
|
|
181
|
+
return host === "127.0.0.1" || host === "localhost" || host === "host.docker.internal";
|
|
182
|
+
}
|
|
183
|
+
function readSupabaseOverrides() {
|
|
184
|
+
return {
|
|
185
|
+
host: asHost(process.env.LOCAL_SUPABASE_HOST),
|
|
186
|
+
api: asPort(process.env.LOCAL_SUPABASE_API_PORT, DEFAULT_API_PORT),
|
|
187
|
+
db: asPort(process.env.LOCAL_SUPABASE_DB_PORT, DEFAULT_DB_PORT),
|
|
188
|
+
studio: asPort(process.env.LOCAL_SUPABASE_STUDIO_PORT, DEFAULT_STUDIO_PORT)
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function detectLocalSupabasePorts(projectRoot = process.cwd()) {
|
|
192
|
+
const resolvedRoot = resolveSupabaseRoot(projectRoot);
|
|
193
|
+
const overrides = readSupabaseOverrides();
|
|
194
|
+
const status = detectStatus(resolvedRoot);
|
|
195
|
+
const tomlPorts = parseTomlPorts(resolvedRoot);
|
|
196
|
+
const statusApiUrl = status ? getStatusValue(status, ["API_URL", "API URL", "api_url"]) : null;
|
|
197
|
+
const statusDbUrl = status ? getStatusValue(status, ["DB_URL", "DB URL", "db_url"]) : null;
|
|
198
|
+
const statusStudioUrl = status ? getStatusValue(status, ["STUDIO_URL", "Studio URL", "studio_url"]) : null;
|
|
199
|
+
const detectedHost = asHost(
|
|
200
|
+
overrides.host ?? extractHost(statusApiUrl) ?? extractHost(statusDbUrl) ?? extractHost(statusStudioUrl)
|
|
201
|
+
);
|
|
202
|
+
const detectedApiPort = asPort(
|
|
203
|
+
overrides.api !== DEFAULT_API_PORT ? overrides.api : void 0,
|
|
204
|
+
extractPort(statusApiUrl) ?? tomlPorts?.api ?? DEFAULT_API_PORT
|
|
205
|
+
);
|
|
206
|
+
const detectedDbPort = asPort(
|
|
207
|
+
overrides.db !== DEFAULT_DB_PORT ? overrides.db : void 0,
|
|
208
|
+
extractPort(statusDbUrl) ?? tomlPorts?.db ?? DEFAULT_DB_PORT
|
|
209
|
+
);
|
|
210
|
+
const detectedStudioPort = asPort(
|
|
211
|
+
overrides.studio !== DEFAULT_STUDIO_PORT ? overrides.studio : void 0,
|
|
212
|
+
extractPort(statusStudioUrl) ?? detectedApiPort + 2
|
|
213
|
+
);
|
|
214
|
+
return {
|
|
215
|
+
host: detectedHost ?? DEFAULT_HOST,
|
|
216
|
+
api: detectedApiPort,
|
|
217
|
+
db: detectedDbPort,
|
|
218
|
+
studio: detectedStudioPort
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function buildLocalSupabaseUrl(projectRoot = process.cwd()) {
|
|
222
|
+
const config = detectLocalSupabasePorts(projectRoot);
|
|
223
|
+
return `http://${config.host}:${config.api}`;
|
|
224
|
+
}
|
|
225
|
+
function buildLocalDatabaseUrl(projectRoot = process.cwd()) {
|
|
226
|
+
const config = detectLocalSupabasePorts(projectRoot);
|
|
227
|
+
return `postgresql://postgres:postgres@${config.host}:${config.db}/postgres`;
|
|
228
|
+
}
|
|
229
|
+
function getLocalSupabaseEnvValues(projectRoot = process.cwd()) {
|
|
230
|
+
const config = detectLocalSupabasePorts(projectRoot);
|
|
231
|
+
const supabaseUrl = buildLocalSupabaseUrl(projectRoot);
|
|
232
|
+
const databaseUrl = buildLocalDatabaseUrl(projectRoot);
|
|
233
|
+
return {
|
|
234
|
+
LOCAL_SUPABASE_HOST: config.host,
|
|
235
|
+
LOCAL_SUPABASE_API_PORT: String(config.api),
|
|
236
|
+
LOCAL_SUPABASE_DB_PORT: String(config.db),
|
|
237
|
+
DATABASE_URL: databaseUrl,
|
|
238
|
+
DATABASE_URL_ADMIN: databaseUrl,
|
|
239
|
+
DATABASE_URL_SERVICE: databaseUrl,
|
|
240
|
+
NEXT_PUBLIC_SUPABASE_URL: supabaseUrl,
|
|
241
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY: LOCAL_SUPABASE_ANON_KEY,
|
|
242
|
+
SUPABASE_SERVICE_ROLE_KEY: LOCAL_SUPABASE_SERVICE_ROLE_KEY
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
function getLocalValueDescriptions(projectRoot = process.cwd()) {
|
|
246
|
+
const config = detectLocalSupabasePorts(projectRoot);
|
|
247
|
+
return {
|
|
248
|
+
LOCAL_SUPABASE_HOST: `supabase local host ${config.host}`,
|
|
249
|
+
LOCAL_SUPABASE_API_PORT: `supabase local api port ${config.api}`,
|
|
250
|
+
LOCAL_SUPABASE_DB_PORT: `supabase local db port ${config.db}`,
|
|
251
|
+
DATABASE_URL: `supabase local DB ${config.host}:${config.db}`,
|
|
252
|
+
DATABASE_URL_ADMIN: `supabase local DB ${config.host}:${config.db}`,
|
|
253
|
+
DATABASE_URL_SERVICE: `supabase local DB ${config.host}:${config.db}`,
|
|
254
|
+
NEXT_PUBLIC_SUPABASE_URL: `supabase local API ${config.host}:${config.api}`,
|
|
255
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY: "local anon key (supabase-demo)",
|
|
256
|
+
SUPABASE_SERVICE_ROLE_KEY: "local service key (supabase-demo)"
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
function validateLocalhostValues(envValues) {
|
|
260
|
+
const urlKeys = [
|
|
261
|
+
"DATABASE_URL",
|
|
262
|
+
"DATABASE_URL_ADMIN",
|
|
263
|
+
"DATABASE_URL_SERVICE",
|
|
264
|
+
"NEXT_PUBLIC_SUPABASE_URL"
|
|
265
|
+
];
|
|
266
|
+
const invalidEntries = [];
|
|
267
|
+
for (const key of urlKeys) {
|
|
268
|
+
const value = envValues[key];
|
|
269
|
+
if (!value) continue;
|
|
270
|
+
try {
|
|
271
|
+
const hostname = new URL(value).hostname;
|
|
272
|
+
if (!isAllowedLocalhostHost(hostname)) {
|
|
273
|
+
invalidEntries.push(key);
|
|
274
|
+
}
|
|
275
|
+
} catch {
|
|
276
|
+
invalidEntries.push(key);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (invalidEntries.length > 0) {
|
|
280
|
+
throw new Error(
|
|
281
|
+
`Local Supabase replacement aborted: not a local endpoint. The following keys are not local: ${invalidEntries.join(", ")}`
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
var DEFAULT_HOST, DEFAULT_API_PORT, DEFAULT_DB_PORT, DEFAULT_STUDIO_PORT, LOCAL_SUPABASE_ANON_KEY, LOCAL_SUPABASE_SERVICE_ROLE_KEY, LOCAL_SUPABASE_ENV_VALUES;
|
|
286
|
+
var init_local_supabase = __esm({
|
|
287
|
+
"src/commands/env/constants/local-supabase.ts"() {
|
|
288
|
+
init_esm_shims();
|
|
289
|
+
DEFAULT_HOST = "127.0.0.1";
|
|
290
|
+
DEFAULT_API_PORT = 54321;
|
|
291
|
+
DEFAULT_DB_PORT = 54322;
|
|
292
|
+
DEFAULT_STUDIO_PORT = 54323;
|
|
293
|
+
LOCAL_SUPABASE_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0";
|
|
294
|
+
LOCAL_SUPABASE_SERVICE_ROLE_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU";
|
|
295
|
+
LOCAL_SUPABASE_ENV_VALUES = getLocalSupabaseEnvValues();
|
|
296
|
+
getLocalValueDescriptions();
|
|
297
|
+
validateLocalhostValues(LOCAL_SUPABASE_ENV_VALUES);
|
|
298
|
+
}
|
|
299
|
+
});
|
|
80
300
|
|
|
81
301
|
// src/commands/db/constants.ts
|
|
82
302
|
var constants_exports = {};
|
|
@@ -113,7 +333,7 @@ function detectSupabasePorts(projectRoot) {
|
|
|
113
333
|
const apiUrl = status.API_URL || status["API URL"] || status.api_url;
|
|
114
334
|
const dbUrl = status.DB_URL || status["DB URL"] || status.db_url;
|
|
115
335
|
const studioUrl = status.STUDIO_URL || status["Studio URL"] || status.studio_url;
|
|
116
|
-
const
|
|
336
|
+
const extractPort2 = (url) => {
|
|
117
337
|
try {
|
|
118
338
|
const parsed = new URL(url);
|
|
119
339
|
return parsed.port ? Number.parseInt(parsed.port, 10) : null;
|
|
@@ -122,28 +342,38 @@ function detectSupabasePorts(projectRoot) {
|
|
|
122
342
|
return match ? Number.parseInt(match[1], 10) : null;
|
|
123
343
|
}
|
|
124
344
|
};
|
|
125
|
-
const apiPort = apiUrl ?
|
|
126
|
-
const dbPort = dbUrl ?
|
|
127
|
-
const studioPort = studioUrl ?
|
|
345
|
+
const apiPort = apiUrl ? extractPort2(apiUrl) : null;
|
|
346
|
+
const dbPort = dbUrl ? extractPort2(dbUrl) : null;
|
|
347
|
+
const studioPort = studioUrl ? extractPort2(studioUrl) : null;
|
|
128
348
|
if (!apiPort || !dbPort) {
|
|
129
349
|
throw new Error(
|
|
130
350
|
"Could not detect Supabase ports from status output.\n\nTry restarting Supabase: supabase stop && supabase start"
|
|
131
351
|
);
|
|
132
352
|
}
|
|
133
|
-
return {
|
|
353
|
+
return {
|
|
354
|
+
api: apiPort,
|
|
355
|
+
db: dbPort,
|
|
356
|
+
studio: studioPort ?? apiPort + 2
|
|
357
|
+
};
|
|
134
358
|
}
|
|
135
359
|
function getSupabaseAccessPoints(projectRoot) {
|
|
136
|
-
const ports =
|
|
360
|
+
const ports = detectLocalSupabasePorts(projectRoot);
|
|
361
|
+
const buildSupabaseUrl = (port) => {
|
|
362
|
+
const url = new URL(buildLocalSupabaseUrl(projectRoot));
|
|
363
|
+
url.port = String(port);
|
|
364
|
+
return url.toString().replace(/\/$/, "");
|
|
365
|
+
};
|
|
137
366
|
return {
|
|
138
|
-
studio:
|
|
139
|
-
api:
|
|
140
|
-
db:
|
|
367
|
+
studio: buildSupabaseUrl(ports.studio),
|
|
368
|
+
api: buildLocalSupabaseUrl(projectRoot),
|
|
369
|
+
db: buildLocalDatabaseUrl(projectRoot)
|
|
141
370
|
};
|
|
142
371
|
}
|
|
143
372
|
var DATABASE_DEFAULTS, SEED_DEFAULTS, SCRIPT_LOCATIONS, ERROR_MESSAGES;
|
|
144
373
|
var init_constants = __esm({
|
|
145
374
|
"src/commands/db/constants.ts"() {
|
|
146
375
|
init_esm_shims();
|
|
376
|
+
init_local_supabase();
|
|
147
377
|
DATABASE_DEFAULTS = {
|
|
148
378
|
// Supabase Local defaults (for development)
|
|
149
379
|
// Note: PORT is detected from running Supabase instance
|
|
@@ -152,7 +382,9 @@ var init_constants = __esm({
|
|
|
152
382
|
// This is a documented public value, not a secret.
|
|
153
383
|
// Production uses environment variables via .env.production.
|
|
154
384
|
SUPABASE_LOCAL: {
|
|
155
|
-
HOST
|
|
385
|
+
get HOST() {
|
|
386
|
+
return detectLocalSupabasePorts().host;
|
|
387
|
+
},
|
|
156
388
|
get PORT() {
|
|
157
389
|
return detectSupabasePorts().db;
|
|
158
390
|
},
|
|
@@ -186,15 +418,15 @@ var init_constants = __esm({
|
|
|
186
418
|
if (databaseUrl) {
|
|
187
419
|
try {
|
|
188
420
|
const url2 = new URL(databaseUrl);
|
|
189
|
-
const isLocalhost = url2.hostname === "127.0.0.1" || url2.hostname === "localhost";
|
|
421
|
+
const isLocalhost = url2.hostname === "127.0.0.1" || url2.hostname === "localhost" || url2.hostname === "host.docker.internal";
|
|
190
422
|
if (isLocalhost) {
|
|
191
423
|
return databaseUrl;
|
|
192
424
|
}
|
|
193
425
|
} catch {
|
|
194
426
|
}
|
|
195
427
|
}
|
|
196
|
-
const host = process.env.LOCAL_DB_HOST || process.env.DB_HOST || this.SUPABASE_LOCAL.HOST;
|
|
197
|
-
const port = process.env.LOCAL_DB_PORT || process.env.DB_PORT || String(this.SUPABASE_LOCAL.PORT);
|
|
428
|
+
const host = process.env.LOCAL_DB_HOST || process.env.DB_HOST || process.env.LOCAL_SUPABASE_HOST || this.SUPABASE_LOCAL.HOST;
|
|
429
|
+
const port = process.env.LOCAL_DB_PORT || process.env.DB_PORT || process.env.LOCAL_SUPABASE_DB_PORT || String(this.SUPABASE_LOCAL.PORT);
|
|
198
430
|
const user = process.env.LOCAL_DB_USER || process.env.DB_USER || this.SUPABASE_LOCAL.USER;
|
|
199
431
|
const password = process.env.LOCAL_DB_PASSWORD || process.env.DB_PASSWORD || this.SUPABASE_LOCAL.PASSWORD;
|
|
200
432
|
const database = process.env.LOCAL_DB_NAME || process.env.DB_NAME || this.SUPABASE_LOCAL.DATABASE;
|
|
@@ -748,11 +980,11 @@ function validateRunaConfig(config) {
|
|
|
748
980
|
};
|
|
749
981
|
}
|
|
750
982
|
function hasValidDatabaseIndicators(dir) {
|
|
751
|
-
const hasPrimaryIndicator = existsSync(
|
|
983
|
+
const hasPrimaryIndicator = existsSync(path11__default.join(dir, "drizzle.config.ts")) || existsSync(path11__default.join(dir, "supabase", "schemas")) || existsSync(path11__default.join(dir, "src", "schema")) || existsSync(path11__default.join(dir, "scripts", "db-sync.ts"));
|
|
752
984
|
if (hasPrimaryIndicator) {
|
|
753
985
|
return true;
|
|
754
986
|
}
|
|
755
|
-
const pkgJsonPath =
|
|
987
|
+
const pkgJsonPath = path11__default.join(dir, "package.json");
|
|
756
988
|
if (existsSync(pkgJsonPath)) {
|
|
757
989
|
try {
|
|
758
990
|
const pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
|
|
@@ -768,15 +1000,15 @@ function hasValidDatabaseIndicators(dir) {
|
|
|
768
1000
|
function tryResolveFromConfig(cwd) {
|
|
769
1001
|
try {
|
|
770
1002
|
const loaded = loadRunaConfigOrThrow(cwd);
|
|
771
|
-
const configDir =
|
|
1003
|
+
const configDir = path11__default.dirname(loaded.configPath);
|
|
772
1004
|
const config = loaded.config;
|
|
773
1005
|
if (config.project.monorepo === false && hasValidDatabaseIndicators(configDir)) {
|
|
774
1006
|
return configDir;
|
|
775
1007
|
}
|
|
776
1008
|
const dbConfig = getDatabaseConfig(config);
|
|
777
1009
|
const schemaPath = dbConfig.schemaPath;
|
|
778
|
-
const dbPath = schemaPath.includes("/src/") ? schemaPath.split("/src/")[0] : schemaPath.startsWith("src/") ? "." :
|
|
779
|
-
const fullPath = dbPath === "." ? configDir :
|
|
1010
|
+
const dbPath = schemaPath.includes("/src/") ? schemaPath.split("/src/")[0] : schemaPath.startsWith("src/") ? "." : path11__default.dirname(schemaPath);
|
|
1011
|
+
const fullPath = dbPath === "." ? configDir : path11__default.join(configDir, dbPath);
|
|
780
1012
|
return existsSync(fullPath) ? fullPath : null;
|
|
781
1013
|
} catch {
|
|
782
1014
|
return null;
|
|
@@ -789,9 +1021,9 @@ async function getDatabasePackagePath(cwd = process.cwd()) {
|
|
|
789
1021
|
if (dbPackage) return dbPackage;
|
|
790
1022
|
const baseDir = findWorkspaceRoot(cwd) || cwd;
|
|
791
1023
|
const conventionalPaths = [
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
1024
|
+
path11__default.join(baseDir, "packages", "database"),
|
|
1025
|
+
path11__default.join(baseDir, "packages", "db"),
|
|
1026
|
+
path11__default.join(baseDir, "db"),
|
|
795
1027
|
baseDir
|
|
796
1028
|
];
|
|
797
1029
|
for (const candidate of conventionalPaths) {
|
|
@@ -822,14 +1054,14 @@ Solutions:
|
|
|
822
1054
|
async function getSDKScriptsPath(cwd = process.cwd()) {
|
|
823
1055
|
const sdkPackage = findPackage("sdk", cwd);
|
|
824
1056
|
if (sdkPackage) {
|
|
825
|
-
const scriptsPath =
|
|
1057
|
+
const scriptsPath = path11__default.join(sdkPackage, "scripts");
|
|
826
1058
|
if (existsSync(scriptsPath)) {
|
|
827
1059
|
return scriptsPath;
|
|
828
1060
|
}
|
|
829
1061
|
}
|
|
830
1062
|
const workspaceRoot = findWorkspaceRoot(cwd);
|
|
831
1063
|
const baseDir = workspaceRoot || cwd;
|
|
832
|
-
const installedPath =
|
|
1064
|
+
const installedPath = path11__default.join(baseDir, "node_modules", "@runa-ai", "runa", "scripts");
|
|
833
1065
|
if (existsSync(installedPath)) {
|
|
834
1066
|
return installedPath;
|
|
835
1067
|
}
|
|
@@ -840,14 +1072,14 @@ async function getSDKScriptsPath(cwd = process.cwd()) {
|
|
|
840
1072
|
async function getSDKTemplatesPath(cwd = process.cwd()) {
|
|
841
1073
|
const sdkPackage = findPackage("sdk", cwd);
|
|
842
1074
|
if (sdkPackage) {
|
|
843
|
-
const templatesPath =
|
|
1075
|
+
const templatesPath = path11__default.join(sdkPackage, "templates");
|
|
844
1076
|
if (existsSync(templatesPath)) {
|
|
845
1077
|
return templatesPath;
|
|
846
1078
|
}
|
|
847
1079
|
}
|
|
848
1080
|
const workspaceRoot = findWorkspaceRoot(cwd);
|
|
849
1081
|
const baseDir = workspaceRoot || cwd;
|
|
850
|
-
const installedPath =
|
|
1082
|
+
const installedPath = path11__default.join(baseDir, "node_modules", "@runa-ai", "runa", "templates");
|
|
851
1083
|
if (existsSync(installedPath)) {
|
|
852
1084
|
return installedPath;
|
|
853
1085
|
}
|
|
@@ -929,7 +1161,7 @@ var CLI_VERSION, HAS_ADMIN_COMMAND;
|
|
|
929
1161
|
var init_version = __esm({
|
|
930
1162
|
"src/version.ts"() {
|
|
931
1163
|
init_esm_shims();
|
|
932
|
-
CLI_VERSION = "0.5.
|
|
1164
|
+
CLI_VERSION = "0.5.53";
|
|
933
1165
|
HAS_ADMIN_COMMAND = false;
|
|
934
1166
|
}
|
|
935
1167
|
});
|
|
@@ -1325,7 +1557,7 @@ var init_dependency_analyzer = __esm({
|
|
|
1325
1557
|
name = "DependencyAnalyzer";
|
|
1326
1558
|
categories = ["dependency"];
|
|
1327
1559
|
async analyze(options) {
|
|
1328
|
-
const packageJsonPath =
|
|
1560
|
+
const packageJsonPath = path11__default.join(options.rootDir, "package.json");
|
|
1329
1561
|
try {
|
|
1330
1562
|
await fs9.access(packageJsonPath);
|
|
1331
1563
|
} catch {
|
|
@@ -3079,17 +3311,17 @@ var init_typescript_analyzer = __esm({
|
|
|
3079
3311
|
}
|
|
3080
3312
|
});
|
|
3081
3313
|
function containsPathTraversal4(inputPath) {
|
|
3082
|
-
const normalized =
|
|
3314
|
+
const normalized = path11__default.normalize(inputPath);
|
|
3083
3315
|
if (normalized.includes("..")) return true;
|
|
3084
3316
|
if (inputPath.includes("\0")) return true;
|
|
3085
3317
|
return false;
|
|
3086
3318
|
}
|
|
3087
3319
|
function isPathWithinBoundary(filePath, boundaryDir) {
|
|
3088
3320
|
try {
|
|
3089
|
-
const resolvedFile =
|
|
3090
|
-
const resolvedBoundary =
|
|
3091
|
-
const relative9 =
|
|
3092
|
-
return !relative9.startsWith("..") && !
|
|
3321
|
+
const resolvedFile = path11__default.resolve(filePath);
|
|
3322
|
+
const resolvedBoundary = path11__default.resolve(boundaryDir);
|
|
3323
|
+
const relative9 = path11__default.relative(resolvedBoundary, resolvedFile);
|
|
3324
|
+
return !relative9.startsWith("..") && !path11__default.isAbsolute(relative9);
|
|
3093
3325
|
} catch {
|
|
3094
3326
|
return false;
|
|
3095
3327
|
}
|
|
@@ -3098,14 +3330,14 @@ function resolvePathWithinBoundary(inputPath, boundaryDir) {
|
|
|
3098
3330
|
if (containsPathTraversal4(inputPath)) {
|
|
3099
3331
|
throw new Error(`Path contains traversal patterns: ${inputPath}`);
|
|
3100
3332
|
}
|
|
3101
|
-
const resolvedPath =
|
|
3333
|
+
const resolvedPath = path11__default.isAbsolute(inputPath) ? inputPath : path11__default.resolve(boundaryDir, inputPath);
|
|
3102
3334
|
if (!isPathWithinBoundary(resolvedPath, boundaryDir)) {
|
|
3103
3335
|
throw new Error(`Path is outside allowed boundary: ${inputPath}`);
|
|
3104
3336
|
}
|
|
3105
3337
|
return resolvedPath;
|
|
3106
3338
|
}
|
|
3107
3339
|
function filterPathsWithinBoundary(files, boundaryDir) {
|
|
3108
|
-
const resolvedBoundary =
|
|
3340
|
+
const resolvedBoundary = path11__default.resolve(boundaryDir);
|
|
3109
3341
|
return files.filter((file) => isPathWithinBoundary(file, resolvedBoundary));
|
|
3110
3342
|
}
|
|
3111
3343
|
function validateGlobPatterns(patterns) {
|
|
@@ -3166,7 +3398,7 @@ var init_loader = __esm({
|
|
|
3166
3398
|
}
|
|
3167
3399
|
});
|
|
3168
3400
|
function normalizePath(filePath) {
|
|
3169
|
-
const normalized =
|
|
3401
|
+
const normalized = path11__default.normalize(filePath);
|
|
3170
3402
|
return normalized.replace(/\\/g, "/");
|
|
3171
3403
|
}
|
|
3172
3404
|
function normalizeRuleId(ruleId) {
|
|
@@ -5119,6 +5351,87 @@ var BuildOutputSchema = z.object({
|
|
|
5119
5351
|
// src/commands/build/machine.ts
|
|
5120
5352
|
init_esm_shims();
|
|
5121
5353
|
|
|
5354
|
+
// src/internal/machines/index.ts
|
|
5355
|
+
init_esm_shims();
|
|
5356
|
+
|
|
5357
|
+
// src/internal/machines/machine-runner.ts
|
|
5358
|
+
init_esm_shims();
|
|
5359
|
+
function getOutputOrThrow(snapshot2) {
|
|
5360
|
+
const s = snapshot2;
|
|
5361
|
+
if (s.output === void 0) {
|
|
5362
|
+
throw new CLIError("Machine completed without output", "MACHINE_NO_OUTPUT", [
|
|
5363
|
+
"Ensure the machine defines an `output:` function."
|
|
5364
|
+
]);
|
|
5365
|
+
}
|
|
5366
|
+
return s.output;
|
|
5367
|
+
}
|
|
5368
|
+
async function runMachine(params) {
|
|
5369
|
+
const timeoutMs = params.timeoutMs ?? 10 * 60 * 1e3;
|
|
5370
|
+
return new Promise((resolve12, reject) => {
|
|
5371
|
+
const actor = createActor(params.machine, { input: params.input });
|
|
5372
|
+
const timer = setTimeout(() => {
|
|
5373
|
+
try {
|
|
5374
|
+
actor.stop?.();
|
|
5375
|
+
} catch {
|
|
5376
|
+
}
|
|
5377
|
+
reject(
|
|
5378
|
+
new CLIError("Machine execution timed out", "MACHINE_TIMEOUT", [
|
|
5379
|
+
`Timeout: ${timeoutMs}ms`,
|
|
5380
|
+
"Consider increasing timeoutMs for long-running operations."
|
|
5381
|
+
])
|
|
5382
|
+
);
|
|
5383
|
+
}, timeoutMs);
|
|
5384
|
+
const sub = actor.subscribe((snapshot2) => {
|
|
5385
|
+
try {
|
|
5386
|
+
params.onSnapshot?.(snapshot2);
|
|
5387
|
+
const stateName = params.helpers.getStateName(snapshot2);
|
|
5388
|
+
params.onTransition?.(stateName);
|
|
5389
|
+
if (params.helpers.isComplete(snapshot2)) {
|
|
5390
|
+
clearTimeout(timer);
|
|
5391
|
+
sub.unsubscribe();
|
|
5392
|
+
const out = getOutputOrThrow(snapshot2);
|
|
5393
|
+
resolve12(out);
|
|
5394
|
+
}
|
|
5395
|
+
} catch (err) {
|
|
5396
|
+
clearTimeout(timer);
|
|
5397
|
+
sub.unsubscribe();
|
|
5398
|
+
reject(err);
|
|
5399
|
+
}
|
|
5400
|
+
});
|
|
5401
|
+
try {
|
|
5402
|
+
actor.start();
|
|
5403
|
+
actor.send({ type: "START" });
|
|
5404
|
+
} catch (err) {
|
|
5405
|
+
clearTimeout(timer);
|
|
5406
|
+
sub.unsubscribe();
|
|
5407
|
+
reject(err);
|
|
5408
|
+
}
|
|
5409
|
+
});
|
|
5410
|
+
}
|
|
5411
|
+
|
|
5412
|
+
// src/internal/machines/snapshot-helpers.ts
|
|
5413
|
+
init_esm_shims();
|
|
5414
|
+
function getSnapshotStateName(snapshot2) {
|
|
5415
|
+
if (typeof snapshot2.value === "string") {
|
|
5416
|
+
return snapshot2.value;
|
|
5417
|
+
}
|
|
5418
|
+
if (!snapshot2.value || typeof snapshot2.value !== "object") {
|
|
5419
|
+
return "unknown";
|
|
5420
|
+
}
|
|
5421
|
+
const entries = Object.entries(snapshot2.value);
|
|
5422
|
+
if (entries.length === 0) {
|
|
5423
|
+
return "unknown";
|
|
5424
|
+
}
|
|
5425
|
+
const [parent, child] = entries[0];
|
|
5426
|
+
if (typeof child === "string") {
|
|
5427
|
+
return `${parent}.${child}`;
|
|
5428
|
+
}
|
|
5429
|
+
return parent;
|
|
5430
|
+
}
|
|
5431
|
+
function isSnapshotComplete(snapshot2) {
|
|
5432
|
+
return snapshot2.status === "done";
|
|
5433
|
+
}
|
|
5434
|
+
|
|
5122
5435
|
// src/commands/build/actors/index.ts
|
|
5123
5436
|
init_esm_shims();
|
|
5124
5437
|
|
|
@@ -5329,7 +5642,7 @@ function pipeToSharedLog(params) {
|
|
|
5329
5642
|
}
|
|
5330
5643
|
async function ensureRunaTmpDir(cwd) {
|
|
5331
5644
|
const root = cwd ?? process.cwd();
|
|
5332
|
-
const dir =
|
|
5645
|
+
const dir = path11__default.join(root, ".runa", "tmp");
|
|
5333
5646
|
await mkdir(dir, { recursive: true });
|
|
5334
5647
|
return dir;
|
|
5335
5648
|
}
|
|
@@ -5354,7 +5667,7 @@ var buildActor = fromPromise(
|
|
|
5354
5667
|
const { repoRoot, tmpDir, hasTurbo, e2e, env: env2 = process.env } = input3;
|
|
5355
5668
|
const startTime = Date.now();
|
|
5356
5669
|
try {
|
|
5357
|
-
const fullTmpDir =
|
|
5670
|
+
const fullTmpDir = path11__default.join(repoRoot, tmpDir);
|
|
5358
5671
|
await mkdir(fullTmpDir, { recursive: true });
|
|
5359
5672
|
const buildEnv = { ...env2 };
|
|
5360
5673
|
if (e2e) {
|
|
@@ -5363,7 +5676,7 @@ var buildActor = fromPromise(
|
|
|
5363
5676
|
buildEnv.TURBO_FORCE = "true";
|
|
5364
5677
|
console.log(" E2E mode enabled: NEXT_PUBLIC_E2E_TEST=true, TURBO_FORCE=true");
|
|
5365
5678
|
}
|
|
5366
|
-
const hasApps = existsSync(
|
|
5679
|
+
const hasApps = existsSync(path11__default.join(repoRoot, "apps"));
|
|
5367
5680
|
let args;
|
|
5368
5681
|
if (hasTurbo) {
|
|
5369
5682
|
args = hasApps ? ["turbo", "run", "build", "--filter=./apps/*", "--filter=./packages/*"] : ["turbo", "run", "build"];
|
|
@@ -5376,7 +5689,7 @@ var buildActor = fromPromise(
|
|
|
5376
5689
|
label: "build",
|
|
5377
5690
|
command: "pnpm",
|
|
5378
5691
|
args,
|
|
5379
|
-
logFile:
|
|
5692
|
+
logFile: path11__default.join(fullTmpDir, "build.log")
|
|
5380
5693
|
});
|
|
5381
5694
|
return {
|
|
5382
5695
|
passed: true,
|
|
@@ -5615,7 +5928,7 @@ function deleteIfExists(fullPath, displayPath, verbose) {
|
|
|
5615
5928
|
function deleteRootDirectories(repoRoot, dirs, verbose) {
|
|
5616
5929
|
const deleted = [];
|
|
5617
5930
|
for (const dir of dirs) {
|
|
5618
|
-
const fullPath =
|
|
5931
|
+
const fullPath = path11__default.join(repoRoot, dir);
|
|
5619
5932
|
if (deleteIfExists(fullPath, dir, verbose)) {
|
|
5620
5933
|
deleted.push(dir);
|
|
5621
5934
|
}
|
|
@@ -5626,13 +5939,13 @@ function cleanMonorepoPackages(repoRoot, dirs, verbose) {
|
|
|
5626
5939
|
const deleted = [];
|
|
5627
5940
|
const subdirs = ["apps", "packages"];
|
|
5628
5941
|
for (const subdir of subdirs) {
|
|
5629
|
-
const subdirPath =
|
|
5942
|
+
const subdirPath = path11__default.join(repoRoot, subdir);
|
|
5630
5943
|
if (!existsSync(subdirPath)) continue;
|
|
5631
5944
|
const entries = __require("fs").readdirSync(subdirPath, { withFileTypes: true });
|
|
5632
5945
|
for (const entry of entries) {
|
|
5633
5946
|
if (!entry.isDirectory()) continue;
|
|
5634
5947
|
for (const dir of dirs) {
|
|
5635
|
-
const targetPath =
|
|
5948
|
+
const targetPath = path11__default.join(subdirPath, entry.name, dir);
|
|
5636
5949
|
const displayPath = `${subdir}/${entry.name}/${dir}`;
|
|
5637
5950
|
if (deleteIfExists(targetPath, displayPath, verbose)) {
|
|
5638
5951
|
deleted.push(displayPath);
|
|
@@ -5651,7 +5964,7 @@ var cleanActor = fromPromise(
|
|
|
5651
5964
|
async ({ input: input3 }) => {
|
|
5652
5965
|
const { repoRoot, tmpDir, verbose = false } = input3;
|
|
5653
5966
|
try {
|
|
5654
|
-
await mkdir(
|
|
5967
|
+
await mkdir(path11__default.join(repoRoot, tmpDir), { recursive: true });
|
|
5655
5968
|
if (verbose) {
|
|
5656
5969
|
console.log("Cleaning build caches...");
|
|
5657
5970
|
}
|
|
@@ -5673,7 +5986,7 @@ var freshActor = fromPromise(
|
|
|
5673
5986
|
async ({ input: input3 }) => {
|
|
5674
5987
|
const { repoRoot, tmpDir, verbose = false } = input3;
|
|
5675
5988
|
try {
|
|
5676
|
-
await mkdir(
|
|
5989
|
+
await mkdir(path11__default.join(repoRoot, tmpDir), { recursive: true });
|
|
5677
5990
|
if (verbose) {
|
|
5678
5991
|
console.log("Fresh install: cleaning all caches and node_modules...");
|
|
5679
5992
|
}
|
|
@@ -5745,7 +6058,7 @@ var dbSyncActor = fromPromise(
|
|
|
5745
6058
|
const { repoRoot, tmpDir, env: env2 = process.env } = input3;
|
|
5746
6059
|
const startTime = Date.now();
|
|
5747
6060
|
try {
|
|
5748
|
-
const fullTmpDir =
|
|
6061
|
+
const fullTmpDir = path11__default.join(repoRoot, tmpDir);
|
|
5749
6062
|
await mkdir(fullTmpDir, { recursive: true });
|
|
5750
6063
|
await runLogged({
|
|
5751
6064
|
cwd: repoRoot,
|
|
@@ -5753,7 +6066,7 @@ var dbSyncActor = fromPromise(
|
|
|
5753
6066
|
label: "db-sync",
|
|
5754
6067
|
command: "pnpm",
|
|
5755
6068
|
args: ["exec", "runa", "db", "sync", "--auto-approve"],
|
|
5756
|
-
logFile:
|
|
6069
|
+
logFile: path11__default.join(fullTmpDir, "db-sync.log")
|
|
5757
6070
|
});
|
|
5758
6071
|
return {
|
|
5759
6072
|
passed: true,
|
|
@@ -5789,7 +6102,7 @@ var manifestActor = fromPromise(
|
|
|
5789
6102
|
const { repoRoot, tmpDir, env: env2 = process.env } = input3;
|
|
5790
6103
|
const startTime = Date.now();
|
|
5791
6104
|
try {
|
|
5792
|
-
const fullTmpDir =
|
|
6105
|
+
const fullTmpDir = path11__default.join(repoRoot, tmpDir);
|
|
5793
6106
|
await mkdir(fullTmpDir, { recursive: true });
|
|
5794
6107
|
await runLogged({
|
|
5795
6108
|
cwd: repoRoot,
|
|
@@ -5797,7 +6110,7 @@ var manifestActor = fromPromise(
|
|
|
5797
6110
|
label: "manifest",
|
|
5798
6111
|
command: "pnpm",
|
|
5799
6112
|
args: ["exec", "runa", "manifest"],
|
|
5800
|
-
logFile:
|
|
6113
|
+
logFile: path11__default.join(fullTmpDir, "manifest.log")
|
|
5801
6114
|
});
|
|
5802
6115
|
return {
|
|
5803
6116
|
passed: true,
|
|
@@ -6061,7 +6374,7 @@ function buildOutput(typeCheckPassed, lintPassed, typeCheckDurationMs, lintDurat
|
|
|
6061
6374
|
var staticChecksActor = fromPromise(
|
|
6062
6375
|
async ({ input: input3 }) => {
|
|
6063
6376
|
const { repoRoot, tmpDir, hasTurbo, env: env2 = process.env, skipTypes, skipLint } = input3;
|
|
6064
|
-
const fullTmpDir =
|
|
6377
|
+
const fullTmpDir = path11__default.join(repoRoot, tmpDir);
|
|
6065
6378
|
await mkdir(fullTmpDir, { recursive: true });
|
|
6066
6379
|
const startTime = Date.now();
|
|
6067
6380
|
const checksToRun = [];
|
|
@@ -6075,7 +6388,7 @@ var staticChecksActor = fromPromise(
|
|
|
6075
6388
|
label: "type-check",
|
|
6076
6389
|
command: cmd.command,
|
|
6077
6390
|
args: cmd.args,
|
|
6078
|
-
logFile:
|
|
6391
|
+
logFile: path11__default.join(fullTmpDir, "type-check.log")
|
|
6079
6392
|
})
|
|
6080
6393
|
);
|
|
6081
6394
|
checkOrder.push("type-check");
|
|
@@ -6089,7 +6402,7 @@ var staticChecksActor = fromPromise(
|
|
|
6089
6402
|
label: "lint",
|
|
6090
6403
|
command: cmd.command,
|
|
6091
6404
|
args: cmd.args,
|
|
6092
|
-
logFile:
|
|
6405
|
+
logFile: path11__default.join(fullTmpDir, "lint.log")
|
|
6093
6406
|
})
|
|
6094
6407
|
);
|
|
6095
6408
|
checkOrder.push("lint");
|
|
@@ -6133,14 +6446,14 @@ var validateActor = fromPromise(
|
|
|
6133
6446
|
let manifestsExist = true;
|
|
6134
6447
|
const buildOutputPaths = [
|
|
6135
6448
|
// Single app
|
|
6136
|
-
|
|
6137
|
-
|
|
6449
|
+
path11__default.join(repoRoot, ".next"),
|
|
6450
|
+
path11__default.join(repoRoot, "dist"),
|
|
6138
6451
|
// Monorepo apps
|
|
6139
|
-
|
|
6140
|
-
|
|
6452
|
+
path11__default.join(repoRoot, "apps", "web", ".next"),
|
|
6453
|
+
path11__default.join(repoRoot, "apps", "dashboard", ".next"),
|
|
6141
6454
|
// Packages
|
|
6142
|
-
|
|
6143
|
-
|
|
6455
|
+
path11__default.join(repoRoot, "packages", "cli", "dist"),
|
|
6456
|
+
path11__default.join(repoRoot, "packages", "sdk", "dist")
|
|
6144
6457
|
];
|
|
6145
6458
|
for (const outputPath of buildOutputPaths) {
|
|
6146
6459
|
if (existsSync(outputPath)) {
|
|
@@ -6154,7 +6467,7 @@ var validateActor = fromPromise(
|
|
|
6154
6467
|
);
|
|
6155
6468
|
}
|
|
6156
6469
|
if (!skipManifest) {
|
|
6157
|
-
const manifestPath =
|
|
6470
|
+
const manifestPath = path11__default.join(repoRoot, ".runa", "manifests", "manifest.json");
|
|
6158
6471
|
if (!existsSync(manifestPath)) {
|
|
6159
6472
|
manifestsExist = false;
|
|
6160
6473
|
warnings.push("No manifest.json found. E2E test generation may be affected.");
|
|
@@ -6213,35 +6526,35 @@ function isE2EMode({ context }) {
|
|
|
6213
6526
|
return context.input.e2e;
|
|
6214
6527
|
}
|
|
6215
6528
|
function detectDatabase(repoRoot) {
|
|
6216
|
-
const configPath =
|
|
6529
|
+
const configPath = path11__default.join(repoRoot, "runa.config.ts");
|
|
6217
6530
|
if (!existsSync(configPath)) {
|
|
6218
|
-
const jsConfigPath =
|
|
6531
|
+
const jsConfigPath = path11__default.join(repoRoot, "runa.config.js");
|
|
6219
6532
|
if (!existsSync(jsConfigPath)) {
|
|
6220
6533
|
return false;
|
|
6221
6534
|
}
|
|
6222
6535
|
}
|
|
6223
|
-
const supabaseDir =
|
|
6536
|
+
const supabaseDir = path11__default.join(repoRoot, "supabase");
|
|
6224
6537
|
if (existsSync(supabaseDir)) {
|
|
6225
6538
|
return true;
|
|
6226
6539
|
}
|
|
6227
|
-
const databasePkg =
|
|
6540
|
+
const databasePkg = path11__default.join(repoRoot, "packages", "database");
|
|
6228
6541
|
if (existsSync(databasePkg)) {
|
|
6229
6542
|
return true;
|
|
6230
6543
|
}
|
|
6231
6544
|
return false;
|
|
6232
6545
|
}
|
|
6233
6546
|
function detectManifestTask(repoRoot) {
|
|
6234
|
-
const runaConfigPath =
|
|
6547
|
+
const runaConfigPath = path11__default.join(repoRoot, "runa.config.ts");
|
|
6235
6548
|
if (existsSync(runaConfigPath)) {
|
|
6236
6549
|
return true;
|
|
6237
6550
|
}
|
|
6238
|
-
const rootPkgPath =
|
|
6551
|
+
const rootPkgPath = path11__default.join(repoRoot, "package.json");
|
|
6239
6552
|
if (hasXStateDependency(rootPkgPath)) {
|
|
6240
6553
|
return true;
|
|
6241
6554
|
}
|
|
6242
6555
|
const appDirs = ["apps/web", "apps/app", "apps/dashboard", "app"];
|
|
6243
6556
|
for (const appDir of appDirs) {
|
|
6244
|
-
const appPkgPath =
|
|
6557
|
+
const appPkgPath = path11__default.join(repoRoot, appDir, "package.json");
|
|
6245
6558
|
if (hasXStateDependency(appPkgPath)) {
|
|
6246
6559
|
return true;
|
|
6247
6560
|
}
|
|
@@ -6268,7 +6581,7 @@ function hasXStateDependency(pkgPath) {
|
|
|
6268
6581
|
}
|
|
6269
6582
|
}
|
|
6270
6583
|
function detectTurbo(repoRoot) {
|
|
6271
|
-
const turboJsonPath =
|
|
6584
|
+
const turboJsonPath = path11__default.join(repoRoot, "turbo.json");
|
|
6272
6585
|
return existsSync(turboJsonPath);
|
|
6273
6586
|
}
|
|
6274
6587
|
var guards = {
|
|
@@ -7118,20 +7431,10 @@ var buildMachine = setup({
|
|
|
7118
7431
|
output: ({ context }) => createOutput(context)
|
|
7119
7432
|
});
|
|
7120
7433
|
function getStateName(snapshot2) {
|
|
7121
|
-
|
|
7122
|
-
return snapshot2.value;
|
|
7123
|
-
}
|
|
7124
|
-
const topLevel = Object.keys(snapshot2.value)[0];
|
|
7125
|
-
if (topLevel && typeof snapshot2.value === "object") {
|
|
7126
|
-
const nested = snapshot2.value[topLevel];
|
|
7127
|
-
if (nested && typeof nested === "string") {
|
|
7128
|
-
return `${topLevel}.${nested}`;
|
|
7129
|
-
}
|
|
7130
|
-
}
|
|
7131
|
-
return topLevel ?? "unknown";
|
|
7434
|
+
return getSnapshotStateName(snapshot2);
|
|
7132
7435
|
}
|
|
7133
7436
|
function isComplete(snapshot2) {
|
|
7134
|
-
return snapshot2
|
|
7437
|
+
return isSnapshotComplete(snapshot2);
|
|
7135
7438
|
}
|
|
7136
7439
|
|
|
7137
7440
|
// src/commands/build/commands/build.ts
|
|
@@ -7276,15 +7579,15 @@ function printSummary(logger16, output3) {
|
|
|
7276
7579
|
}
|
|
7277
7580
|
}
|
|
7278
7581
|
function findRepoRoot(startDir) {
|
|
7279
|
-
const { existsSync:
|
|
7582
|
+
const { existsSync: existsSync52, readFileSync: readFileSync29 } = __require("fs");
|
|
7280
7583
|
const { join: join23, dirname: dirname5 } = __require("path");
|
|
7281
7584
|
let current = startDir;
|
|
7282
7585
|
while (current !== dirname5(current)) {
|
|
7283
|
-
if (
|
|
7586
|
+
if (existsSync52(join23(current, "turbo.json"))) {
|
|
7284
7587
|
return current;
|
|
7285
7588
|
}
|
|
7286
7589
|
const pkgPath = join23(current, "package.json");
|
|
7287
|
-
if (
|
|
7590
|
+
if (existsSync52(pkgPath)) {
|
|
7288
7591
|
try {
|
|
7289
7592
|
const pkg = JSON.parse(readFileSync29(pkgPath, "utf-8"));
|
|
7290
7593
|
if (pkg.workspaces) {
|
|
@@ -7527,42 +7830,42 @@ function resolvePolicy(_config, _repoKind) {
|
|
|
7527
7830
|
};
|
|
7528
7831
|
}
|
|
7529
7832
|
function detectRepoKind(repoRoot) {
|
|
7530
|
-
const hasApps = existsSync(
|
|
7531
|
-
const hasPackages = existsSync(
|
|
7532
|
-
const hasTurbo = existsSync(
|
|
7833
|
+
const hasApps = existsSync(path11__default.join(repoRoot, "apps"));
|
|
7834
|
+
const hasPackages = existsSync(path11__default.join(repoRoot, "packages"));
|
|
7835
|
+
const hasTurbo = existsSync(path11__default.join(repoRoot, "turbo.json"));
|
|
7533
7836
|
if (hasApps || hasPackages || hasTurbo) {
|
|
7534
7837
|
return "monorepo";
|
|
7535
7838
|
}
|
|
7536
|
-
const hasSrc = existsSync(
|
|
7537
|
-
const hasApp = existsSync(
|
|
7839
|
+
const hasSrc = existsSync(path11__default.join(repoRoot, "src"));
|
|
7840
|
+
const hasApp = existsSync(path11__default.join(repoRoot, "app"));
|
|
7538
7841
|
if (hasSrc || hasApp) {
|
|
7539
7842
|
return "pj-repo";
|
|
7540
7843
|
}
|
|
7541
7844
|
return "unknown";
|
|
7542
7845
|
}
|
|
7543
7846
|
function hasKnownFrameworkConfig(dir) {
|
|
7544
|
-
if (existsSync(
|
|
7847
|
+
if (existsSync(path11__default.join(dir, "next.config.js")) || existsSync(path11__default.join(dir, "next.config.mjs")) || existsSync(path11__default.join(dir, "next.config.ts"))) {
|
|
7545
7848
|
return true;
|
|
7546
7849
|
}
|
|
7547
|
-
if (existsSync(
|
|
7850
|
+
if (existsSync(path11__default.join(dir, "vite.config.js")) || existsSync(path11__default.join(dir, "vite.config.ts"))) {
|
|
7548
7851
|
return true;
|
|
7549
7852
|
}
|
|
7550
|
-
if (existsSync(
|
|
7853
|
+
if (existsSync(path11__default.join(dir, "remix.config.js"))) {
|
|
7551
7854
|
return true;
|
|
7552
7855
|
}
|
|
7553
|
-
if (existsSync(
|
|
7856
|
+
if (existsSync(path11__default.join(dir, "astro.config.mjs")) || existsSync(path11__default.join(dir, "astro.config.ts"))) {
|
|
7554
7857
|
return true;
|
|
7555
7858
|
}
|
|
7556
|
-
if (existsSync(
|
|
7859
|
+
if (existsSync(path11__default.join(dir, "nuxt.config.js")) || existsSync(path11__default.join(dir, "nuxt.config.ts"))) {
|
|
7557
7860
|
return true;
|
|
7558
7861
|
}
|
|
7559
|
-
if (existsSync(
|
|
7862
|
+
if (existsSync(path11__default.join(dir, "svelte.config.js"))) {
|
|
7560
7863
|
return true;
|
|
7561
7864
|
}
|
|
7562
7865
|
return false;
|
|
7563
7866
|
}
|
|
7564
7867
|
function hasPackageJson(dir) {
|
|
7565
|
-
return existsSync(
|
|
7868
|
+
return existsSync(path11__default.join(dir, "package.json"));
|
|
7566
7869
|
}
|
|
7567
7870
|
var PORT_PATTERNS = [
|
|
7568
7871
|
/-p\s*([0-9]+)/i,
|
|
@@ -7582,7 +7885,7 @@ function extractPortFromScript(script) {
|
|
|
7582
7885
|
return null;
|
|
7583
7886
|
}
|
|
7584
7887
|
function readPortFromScripts(appDir) {
|
|
7585
|
-
const pkgPath =
|
|
7888
|
+
const pkgPath = path11__default.join(appDir, "package.json");
|
|
7586
7889
|
if (!existsSync(pkgPath)) return 3e3;
|
|
7587
7890
|
try {
|
|
7588
7891
|
const { readFileSync: readFileSync29 } = __require("fs");
|
|
@@ -7601,30 +7904,30 @@ function readPortFromScripts(appDir) {
|
|
|
7601
7904
|
return 3e3;
|
|
7602
7905
|
}
|
|
7603
7906
|
function findWebAppUnderApps(repoRoot) {
|
|
7604
|
-
const appsDir =
|
|
7907
|
+
const appsDir = path11__default.join(repoRoot, "apps");
|
|
7605
7908
|
if (!existsSync(appsDir)) return null;
|
|
7606
7909
|
try {
|
|
7607
7910
|
const { readdirSync: readdirSync12 } = __require("fs");
|
|
7608
7911
|
const entries = readdirSync12(appsDir, { withFileTypes: true });
|
|
7609
7912
|
const priority = ["web", "dashboard", "app", "frontend", "client"];
|
|
7610
7913
|
for (const name of priority) {
|
|
7611
|
-
const candidate =
|
|
7914
|
+
const candidate = path11__default.join(appsDir, name);
|
|
7612
7915
|
if (hasKnownFrameworkConfig(candidate)) return candidate;
|
|
7613
7916
|
}
|
|
7614
7917
|
for (const entry of entries) {
|
|
7615
7918
|
if (!entry.isDirectory()) continue;
|
|
7616
7919
|
if (priority.includes(entry.name)) continue;
|
|
7617
|
-
const candidate =
|
|
7920
|
+
const candidate = path11__default.join(appsDir, entry.name);
|
|
7618
7921
|
if (hasKnownFrameworkConfig(candidate)) return candidate;
|
|
7619
7922
|
}
|
|
7620
7923
|
for (const name of priority) {
|
|
7621
|
-
const candidate =
|
|
7924
|
+
const candidate = path11__default.join(appsDir, name);
|
|
7622
7925
|
if (hasPackageJson(candidate)) return candidate;
|
|
7623
7926
|
}
|
|
7624
7927
|
for (const entry of entries) {
|
|
7625
7928
|
if (!entry.isDirectory()) continue;
|
|
7626
7929
|
if (priority.includes(entry.name)) continue;
|
|
7627
|
-
const candidate =
|
|
7930
|
+
const candidate = path11__default.join(appsDir, entry.name);
|
|
7628
7931
|
if (hasPackageJson(candidate)) return candidate;
|
|
7629
7932
|
}
|
|
7630
7933
|
} catch {
|
|
@@ -7633,7 +7936,7 @@ function findWebAppUnderApps(repoRoot) {
|
|
|
7633
7936
|
}
|
|
7634
7937
|
function detectApp(repoRoot, configOverride) {
|
|
7635
7938
|
if (configOverride?.directory && configOverride.directory !== ".") {
|
|
7636
|
-
const configuredDir =
|
|
7939
|
+
const configuredDir = path11__default.join(repoRoot, configOverride.directory);
|
|
7637
7940
|
if (existsSync(configuredDir) && hasPackageJson(configuredDir)) {
|
|
7638
7941
|
return {
|
|
7639
7942
|
appDir: configuredDir,
|
|
@@ -7768,7 +8071,7 @@ function pipeChildOutputToLog(params) {
|
|
|
7768
8071
|
return { cleanup };
|
|
7769
8072
|
}
|
|
7770
8073
|
async function writeEnvLocal(params) {
|
|
7771
|
-
const filePath =
|
|
8074
|
+
const filePath = path11__default.join(params.appDir, ".env.local");
|
|
7772
8075
|
const content = `${Object.entries(params.values).map(([k, v]) => `${k}=${v}`).join("\n")}
|
|
7773
8076
|
`;
|
|
7774
8077
|
await writeFile(filePath, content, "utf-8");
|
|
@@ -7858,10 +8161,10 @@ function determineAppCommand(mode, isMonorepo2, rootScripts, appScripts, repoRoo
|
|
|
7858
8161
|
}
|
|
7859
8162
|
if (appHasCiScript || appHasDefaultScript) {
|
|
7860
8163
|
const scriptName = appHasCiScript ? ciScriptName : "start";
|
|
7861
|
-
const dirArgs2 = isMonorepo2 ? ["-C",
|
|
8164
|
+
const dirArgs2 = isMonorepo2 ? ["-C", path11__default.relative(repoRoot, appDir)] : [];
|
|
7862
8165
|
return { command: ["pnpm", ...dirArgs2, scriptName], useRootScript: false };
|
|
7863
8166
|
}
|
|
7864
|
-
const dirArgs = isMonorepo2 ? ["-C",
|
|
8167
|
+
const dirArgs = isMonorepo2 ? ["-C", path11__default.relative(repoRoot, appDir)] : [];
|
|
7865
8168
|
return {
|
|
7866
8169
|
command: ["pnpm", ...dirArgs, "exec", "next", nextCommand, "-p", String(port)],
|
|
7867
8170
|
useRootScript: false
|
|
@@ -7869,17 +8172,17 @@ function determineAppCommand(mode, isMonorepo2, rootScripts, appScripts, repoRoo
|
|
|
7869
8172
|
}
|
|
7870
8173
|
var NEXT_CRITICAL_FILES = ["routes-manifest.json", "build-manifest.json"];
|
|
7871
8174
|
function cleanStaleNextDevState(appDir) {
|
|
7872
|
-
const nextDir =
|
|
8175
|
+
const nextDir = path11__default.join(appDir, ".next");
|
|
7873
8176
|
if (!existsSync(nextDir)) {
|
|
7874
8177
|
return { cleaned: false };
|
|
7875
8178
|
}
|
|
7876
8179
|
for (const file of NEXT_CRITICAL_FILES) {
|
|
7877
|
-
if (!existsSync(
|
|
8180
|
+
if (!existsSync(path11__default.join(nextDir, file))) {
|
|
7878
8181
|
cleanNextDir(nextDir, `Missing ${file}`);
|
|
7879
8182
|
return { cleaned: true, reason: `Missing ${file}` };
|
|
7880
8183
|
}
|
|
7881
8184
|
}
|
|
7882
|
-
const serverDir =
|
|
8185
|
+
const serverDir = path11__default.join(nextDir, "server");
|
|
7883
8186
|
if (!existsSync(serverDir)) {
|
|
7884
8187
|
try {
|
|
7885
8188
|
const nextStat = statSync(nextDir);
|
|
@@ -7901,7 +8204,7 @@ function cleanNextDir(nextDir, reason) {
|
|
|
7901
8204
|
console.log("[runa] Cleanup complete");
|
|
7902
8205
|
} catch {
|
|
7903
8206
|
const staleDir = `${nextDir}-stale-${Date.now()}`;
|
|
7904
|
-
console.log(`[runa] Could not remove .next, quarantining to ${
|
|
8207
|
+
console.log(`[runa] Could not remove .next, quarantining to ${path11__default.basename(staleDir)}`);
|
|
7905
8208
|
try {
|
|
7906
8209
|
renameSync(nextDir, staleDir);
|
|
7907
8210
|
} catch {
|
|
@@ -7912,8 +8215,8 @@ function cleanNextDir(nextDir, reason) {
|
|
|
7912
8215
|
async function startAppBackground(params) {
|
|
7913
8216
|
const mode = params.mode ?? "start";
|
|
7914
8217
|
const isMonorepo2 = params.appDir !== params.repoRoot;
|
|
7915
|
-
const rootPkgPath =
|
|
7916
|
-
const appPkgPath =
|
|
8218
|
+
const rootPkgPath = path11__default.join(params.repoRoot, "package.json");
|
|
8219
|
+
const appPkgPath = path11__default.join(params.appDir, "package.json");
|
|
7917
8220
|
const rootScripts = await readPackageScripts(rootPkgPath);
|
|
7918
8221
|
const appScripts = isMonorepo2 ? await readPackageScripts(appPkgPath) : rootScripts;
|
|
7919
8222
|
const { command, useRootScript } = determineAppCommand(
|
|
@@ -7925,7 +8228,7 @@ async function startAppBackground(params) {
|
|
|
7925
8228
|
params.appDir,
|
|
7926
8229
|
params.port
|
|
7927
8230
|
);
|
|
7928
|
-
const appLog =
|
|
8231
|
+
const appLog = path11__default.join(params.tmpDir, "app.log");
|
|
7929
8232
|
const out = createWriteStream(appLog, { flags: "a" });
|
|
7930
8233
|
const commandStr = command.join(" ");
|
|
7931
8234
|
const modeLabel = mode === "dev" ? "development (hot reload)" : "production";
|
|
@@ -7934,7 +8237,7 @@ async function startAppBackground(params) {
|
|
|
7934
8237
|
`[runa] Mode: ${modeLabel}`,
|
|
7935
8238
|
`[runa] Command: ${commandStr}`,
|
|
7936
8239
|
`[runa] Working directory: ${params.repoRoot}`,
|
|
7937
|
-
`[runa] App directory: ${
|
|
8240
|
+
`[runa] App directory: ${path11__default.relative(params.repoRoot, params.appDir) || "."}`,
|
|
7938
8241
|
useRootScript ? "[runa] Using root package.json script" : "[runa] Using app package.json script",
|
|
7939
8242
|
"---",
|
|
7940
8243
|
""
|
|
@@ -7955,12 +8258,12 @@ async function startAppBackground(params) {
|
|
|
7955
8258
|
streamToTerminal: params.stream
|
|
7956
8259
|
});
|
|
7957
8260
|
const pid = child.pid ?? -1;
|
|
7958
|
-
await writeFile(
|
|
8261
|
+
await writeFile(path11__default.join(params.tmpDir, "app.pid"), `${pid}
|
|
7959
8262
|
`, "utf-8");
|
|
7960
8263
|
return { pid, cleanupStreams };
|
|
7961
8264
|
}
|
|
7962
8265
|
async function waitForAppReady(params) {
|
|
7963
|
-
const logFilePath = params.tmpDir ?
|
|
8266
|
+
const logFilePath = params.tmpDir ? path11__default.join(params.tmpDir, "app.log") : void 0;
|
|
7964
8267
|
await waitHttpOk({
|
|
7965
8268
|
url: `http://127.0.0.1:${params.port}/`,
|
|
7966
8269
|
timeoutSeconds: params.timeoutSeconds,
|
|
@@ -8100,7 +8403,7 @@ var e2eMeta2 = {
|
|
|
8100
8403
|
var appStartActor = fromPromise(
|
|
8101
8404
|
async ({ input: input3 }) => {
|
|
8102
8405
|
const { repoRoot, appDir, port, tmpDir, stream } = input3;
|
|
8103
|
-
const fullTmpDir =
|
|
8406
|
+
const fullTmpDir = path11__default.join(repoRoot, tmpDir);
|
|
8104
8407
|
await mkdir(fullTmpDir, { recursive: true });
|
|
8105
8408
|
cleanStaleNextDevState(appDir);
|
|
8106
8409
|
const result = await startAppBackground({
|
|
@@ -8368,11 +8671,11 @@ function printSummary2(logger16, output3) {
|
|
|
8368
8671
|
}
|
|
8369
8672
|
function findRepoRoot2(startDir) {
|
|
8370
8673
|
let current = startDir;
|
|
8371
|
-
while (current !==
|
|
8372
|
-
if (existsSync(
|
|
8674
|
+
while (current !== path11__default.dirname(current)) {
|
|
8675
|
+
if (existsSync(path11__default.join(current, "turbo.json"))) {
|
|
8373
8676
|
return current;
|
|
8374
8677
|
}
|
|
8375
|
-
const pkgPath =
|
|
8678
|
+
const pkgPath = path11__default.join(current, "package.json");
|
|
8376
8679
|
if (existsSync(pkgPath)) {
|
|
8377
8680
|
try {
|
|
8378
8681
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
@@ -8382,7 +8685,7 @@ function findRepoRoot2(startDir) {
|
|
|
8382
8685
|
} catch {
|
|
8383
8686
|
}
|
|
8384
8687
|
}
|
|
8385
|
-
current =
|
|
8688
|
+
current = path11__default.dirname(current);
|
|
8386
8689
|
}
|
|
8387
8690
|
return startDir;
|
|
8388
8691
|
}
|
|
@@ -8569,19 +8872,19 @@ function isSupportedNodeEnv(value) {
|
|
|
8569
8872
|
function findProjectRoot(from) {
|
|
8570
8873
|
const workspaceRoot = findWorkspaceRoot(from);
|
|
8571
8874
|
if (workspaceRoot) return workspaceRoot;
|
|
8572
|
-
let current =
|
|
8573
|
-
const root =
|
|
8875
|
+
let current = path11__default.resolve(from);
|
|
8876
|
+
const root = path11__default.parse(current).root;
|
|
8574
8877
|
let depth = 0;
|
|
8575
8878
|
while (current !== root && depth < MAX_DIRECTORY_TRAVERSAL_DEPTH) {
|
|
8576
|
-
const hasGit = existsSync(
|
|
8577
|
-
const hasPackageJson2 = existsSync(
|
|
8879
|
+
const hasGit = existsSync(path11__default.join(current, ".git"));
|
|
8880
|
+
const hasPackageJson2 = existsSync(path11__default.join(current, "package.json"));
|
|
8578
8881
|
if (hasGit || hasPackageJson2) return current;
|
|
8579
|
-
const parent =
|
|
8882
|
+
const parent = path11__default.dirname(current);
|
|
8580
8883
|
if (parent === current) break;
|
|
8581
8884
|
current = parent;
|
|
8582
8885
|
depth++;
|
|
8583
8886
|
}
|
|
8584
|
-
return
|
|
8887
|
+
return path11__default.resolve(from);
|
|
8585
8888
|
}
|
|
8586
8889
|
function mapRunaEnvToVercelEnv(runaEnv) {
|
|
8587
8890
|
if (runaEnv === "preview") return "preview";
|
|
@@ -8622,7 +8925,7 @@ function loadEnvFiles(options = {}) {
|
|
|
8622
8925
|
const nodeEnv = resolveNodeEnv(options.nodeEnv);
|
|
8623
8926
|
const projectRoot = findProjectRoot(cwd);
|
|
8624
8927
|
const vercelEnv = options.vercelEnv ?? inferVercelEnv({ nodeEnv, runaEnv: options.runaEnv });
|
|
8625
|
-
const keysFile =
|
|
8928
|
+
const keysFile = path11__default.join(projectRoot, ".env.keys");
|
|
8626
8929
|
const baseFilePaths = buildBaseEnvFilePaths({ nodeEnv, vercelEnv });
|
|
8627
8930
|
const runaFilePaths = options.runaEnv ? buildRunaEnvFilePaths(options.runaEnv) : [];
|
|
8628
8931
|
const allRelPaths = [...baseFilePaths, ...runaFilePaths];
|
|
@@ -8632,7 +8935,7 @@ function loadEnvFiles(options = {}) {
|
|
|
8632
8935
|
const filePaths = [
|
|
8633
8936
|
keysFile,
|
|
8634
8937
|
// Load keys first
|
|
8635
|
-
...allRelPaths.map((rel) =>
|
|
8938
|
+
...allRelPaths.map((rel) => path11__default.join(projectRoot, rel)).filter(existsSync)
|
|
8636
8939
|
];
|
|
8637
8940
|
config({
|
|
8638
8941
|
path: filePaths,
|
|
@@ -8641,7 +8944,7 @@ function loadEnvFiles(options = {}) {
|
|
|
8641
8944
|
quiet: true
|
|
8642
8945
|
// Suppress dotenvx verbose output
|
|
8643
8946
|
});
|
|
8644
|
-
loadedFiles = allRelPaths.filter((rel) => existsSync(
|
|
8947
|
+
loadedFiles = allRelPaths.filter((rel) => existsSync(path11__default.join(projectRoot, rel)));
|
|
8645
8948
|
} else {
|
|
8646
8949
|
const targetEnv = { ...process.env };
|
|
8647
8950
|
loadedFiles = [
|
|
@@ -8716,8 +9019,8 @@ function buildRunaEnvFilePaths(runaEnv) {
|
|
|
8716
9019
|
function loadEnvFilesIntoTarget(params) {
|
|
8717
9020
|
const loadedFiles = [];
|
|
8718
9021
|
for (const relPath of params.relPaths) {
|
|
8719
|
-
const fullPath =
|
|
8720
|
-
const resolvedPath =
|
|
9022
|
+
const fullPath = path11__default.join(params.projectRoot, relPath);
|
|
9023
|
+
const resolvedPath = path11__default.resolve(fullPath);
|
|
8721
9024
|
if (!isPathContained(params.projectRoot, resolvedPath)) {
|
|
8722
9025
|
continue;
|
|
8723
9026
|
}
|
|
@@ -8985,7 +9288,7 @@ async function runTool(params) {
|
|
|
8985
9288
|
exitCode,
|
|
8986
9289
|
issueCount,
|
|
8987
9290
|
durationMs,
|
|
8988
|
-
outputPath:
|
|
9291
|
+
outputPath: path11__default.relative(process.cwd(), params.outputPath)
|
|
8989
9292
|
});
|
|
8990
9293
|
});
|
|
8991
9294
|
proc.on("error", async () => {
|
|
@@ -9004,7 +9307,7 @@ async function runTool(params) {
|
|
|
9004
9307
|
exitCode: -1,
|
|
9005
9308
|
issueCount: 0,
|
|
9006
9309
|
durationMs,
|
|
9007
|
-
outputPath:
|
|
9310
|
+
outputPath: path11__default.relative(process.cwd(), params.outputPath)
|
|
9008
9311
|
});
|
|
9009
9312
|
});
|
|
9010
9313
|
});
|
|
@@ -9036,7 +9339,7 @@ async function executeWorkflowCheck(params) {
|
|
|
9036
9339
|
label: params.label,
|
|
9037
9340
|
command: params.command,
|
|
9038
9341
|
args: params.args,
|
|
9039
|
-
outputPath:
|
|
9342
|
+
outputPath: path11__default.join(params.logDir, `${params.name}-output.txt`),
|
|
9040
9343
|
parseIssueCount: params.parseIssueCount
|
|
9041
9344
|
});
|
|
9042
9345
|
logCheckResult(result, params.isWarningOnly ?? false);
|
|
@@ -9108,7 +9411,7 @@ async function runCiChecks(options) {
|
|
|
9108
9411
|
const mode = options.mode === "github-actions" ? "github-actions" : "local";
|
|
9109
9412
|
const isCI3 = mode === "github-actions";
|
|
9110
9413
|
const workflowsChanged = options.workflowsChanged;
|
|
9111
|
-
const logDir =
|
|
9414
|
+
const logDir = path11__default.join(process.cwd(), ".runa", "tmp", "workflow");
|
|
9112
9415
|
await mkdir(logDir, { recursive: true });
|
|
9113
9416
|
console.log("");
|
|
9114
9417
|
console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
@@ -9117,7 +9420,7 @@ async function runCiChecks(options) {
|
|
|
9117
9420
|
console.log("");
|
|
9118
9421
|
const results = await executeChecks(workflowsChanged, logDir);
|
|
9119
9422
|
const summary = buildChecksSummary(startedAt, workflowsChanged, results);
|
|
9120
|
-
const summaryPath =
|
|
9423
|
+
const summaryPath = path11__default.join(logDir, "checks-summary.json");
|
|
9121
9424
|
await writeFile(summaryPath, JSON.stringify(summary, null, 2), "utf-8");
|
|
9122
9425
|
if (isCI3) {
|
|
9123
9426
|
await appendGithubStepSummary(buildStepSummaryMarkdown(summary));
|
|
@@ -9184,7 +9487,7 @@ function printFinalOutput(summary, summaryPath) {
|
|
|
9184
9487
|
console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
9185
9488
|
console.log(`${summary.status === "failure" ? "\u274C" : "\u2705"} Workflow Checks: ${summary.status}`);
|
|
9186
9489
|
console.log(` Duration: ${formatDuration$1(summary.durationMs)}`);
|
|
9187
|
-
console.log(` Summary: ${
|
|
9490
|
+
console.log(` Summary: ${path11__default.relative(process.cwd(), summaryPath)}`);
|
|
9188
9491
|
console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
9189
9492
|
}
|
|
9190
9493
|
var ciChecksCommand = new Command("checks").description("Run workflow checks (actionlint, zizmor)").option("--mode <mode>", "Execution mode (github-actions | local)", "local").option("--workflows-changed", "Indicate workflows have changed", false).action(runCiChecks);
|
|
@@ -9197,10 +9500,10 @@ init_esm_shims();
|
|
|
9197
9500
|
var CiConfigSchema = z.record(z.string(), z.unknown());
|
|
9198
9501
|
async function loadCiConfig(params) {
|
|
9199
9502
|
const override = typeof params.overridePath === "string" && params.overridePath.trim().length > 0 ? params.overridePath.trim() : null;
|
|
9200
|
-
const defaultPath =
|
|
9503
|
+
const defaultPath = path11__default.join(params.repoRoot, ".runa", "ci.config.json");
|
|
9201
9504
|
const resolvedPath = override ?? (existsSync(defaultPath) ? defaultPath : null);
|
|
9202
9505
|
if (!resolvedPath) return { configPath: null, config: null };
|
|
9203
|
-
const abs =
|
|
9506
|
+
const abs = path11__default.isAbsolute(resolvedPath) ? resolvedPath : path11__default.join(params.repoRoot, resolvedPath);
|
|
9204
9507
|
if (!existsSync(abs)) return { configPath: abs, config: null };
|
|
9205
9508
|
const raw = await readFile(abs, "utf-8");
|
|
9206
9509
|
const parsed = JSON.parse(raw);
|
|
@@ -9302,9 +9605,9 @@ var CiSummarySchema = z.object({
|
|
|
9302
9605
|
async function writeCiSummary(params) {
|
|
9303
9606
|
const cwd = params.cwd ?? process.cwd();
|
|
9304
9607
|
const parsed = CiSummarySchema.parse(params.summary);
|
|
9305
|
-
const dir =
|
|
9608
|
+
const dir = path11__default.join(cwd, ".runa", "tmp");
|
|
9306
9609
|
await mkdir(dir, { recursive: true });
|
|
9307
|
-
const filePath =
|
|
9610
|
+
const filePath = path11__default.join(dir, "ci-summary.json");
|
|
9308
9611
|
await writeFile(filePath, `${JSON.stringify(parsed, null, 2)}
|
|
9309
9612
|
`, "utf-8");
|
|
9310
9613
|
return filePath;
|
|
@@ -9509,6 +9812,89 @@ var EXCLUDED_SCHEMAS = /* @__PURE__ */ new Set([
|
|
|
9509
9812
|
"pgbouncer",
|
|
9510
9813
|
"cron"
|
|
9511
9814
|
]);
|
|
9815
|
+
function stripSqlCommentsPreserveStrings(content) {
|
|
9816
|
+
let result = "";
|
|
9817
|
+
let i = 0;
|
|
9818
|
+
let inSingleQuote = false;
|
|
9819
|
+
let inDoubleQuote = false;
|
|
9820
|
+
let inDollarQuote = false;
|
|
9821
|
+
let dollarTag = "";
|
|
9822
|
+
while (i < content.length) {
|
|
9823
|
+
const char = content[i] ?? "";
|
|
9824
|
+
const next = content[i + 1] ?? "";
|
|
9825
|
+
if (!inSingleQuote && !inDoubleQuote && !inDollarQuote && char === "-" && next === "-") {
|
|
9826
|
+
while (i < content.length && content[i] !== "\n") {
|
|
9827
|
+
result += " ";
|
|
9828
|
+
i++;
|
|
9829
|
+
}
|
|
9830
|
+
continue;
|
|
9831
|
+
}
|
|
9832
|
+
if (!inSingleQuote && !inDoubleQuote && !inDollarQuote && char === "/" && next === "*") {
|
|
9833
|
+
result += " ";
|
|
9834
|
+
result += " ";
|
|
9835
|
+
i += 2;
|
|
9836
|
+
while (i < content.length) {
|
|
9837
|
+
const blockChar = content[i] ?? "";
|
|
9838
|
+
const blockNext = content[i + 1] ?? "";
|
|
9839
|
+
if (blockChar === "*" && blockNext === "/") {
|
|
9840
|
+
result += " ";
|
|
9841
|
+
result += " ";
|
|
9842
|
+
i += 2;
|
|
9843
|
+
break;
|
|
9844
|
+
}
|
|
9845
|
+
result += blockChar === "\n" ? "\n" : " ";
|
|
9846
|
+
i++;
|
|
9847
|
+
}
|
|
9848
|
+
continue;
|
|
9849
|
+
}
|
|
9850
|
+
if (!inSingleQuote && !inDoubleQuote && char === "$") {
|
|
9851
|
+
if (inDollarQuote) {
|
|
9852
|
+
const closeTag = `$${dollarTag}$`;
|
|
9853
|
+
if (content.slice(i).startsWith(closeTag)) {
|
|
9854
|
+
result += closeTag;
|
|
9855
|
+
i += closeTag.length;
|
|
9856
|
+
inDollarQuote = false;
|
|
9857
|
+
dollarTag = "";
|
|
9858
|
+
continue;
|
|
9859
|
+
}
|
|
9860
|
+
} else {
|
|
9861
|
+
const tagMatch = content.slice(i).match(/^\$([a-zA-Z_][a-zA-Z0-9_]*)?\$/);
|
|
9862
|
+
if (tagMatch) {
|
|
9863
|
+
inDollarQuote = true;
|
|
9864
|
+
dollarTag = tagMatch[1] ?? "";
|
|
9865
|
+
result += tagMatch[0];
|
|
9866
|
+
i += tagMatch[0].length;
|
|
9867
|
+
continue;
|
|
9868
|
+
}
|
|
9869
|
+
}
|
|
9870
|
+
}
|
|
9871
|
+
if (!inDoubleQuote && !inDollarQuote && char === "'") {
|
|
9872
|
+
if (inSingleQuote && next === "'") {
|
|
9873
|
+
result += "''";
|
|
9874
|
+
i += 2;
|
|
9875
|
+
continue;
|
|
9876
|
+
}
|
|
9877
|
+
inSingleQuote = !inSingleQuote;
|
|
9878
|
+
result += char;
|
|
9879
|
+
i++;
|
|
9880
|
+
continue;
|
|
9881
|
+
}
|
|
9882
|
+
if (!inSingleQuote && !inDollarQuote && char === '"') {
|
|
9883
|
+
if (inDoubleQuote && next === '"') {
|
|
9884
|
+
result += '""';
|
|
9885
|
+
i += 2;
|
|
9886
|
+
continue;
|
|
9887
|
+
}
|
|
9888
|
+
inDoubleQuote = !inDoubleQuote;
|
|
9889
|
+
result += char;
|
|
9890
|
+
i++;
|
|
9891
|
+
continue;
|
|
9892
|
+
}
|
|
9893
|
+
result += char;
|
|
9894
|
+
i++;
|
|
9895
|
+
}
|
|
9896
|
+
return result;
|
|
9897
|
+
}
|
|
9512
9898
|
function detectAppSchemas(schemasDir, verbose) {
|
|
9513
9899
|
const schemas = /* @__PURE__ */ new Set(["public"]);
|
|
9514
9900
|
if (!existsSync(schemasDir)) {
|
|
@@ -9517,12 +9903,13 @@ function detectAppSchemas(schemasDir, verbose) {
|
|
|
9517
9903
|
const files = readdirSync(schemasDir).filter((f) => f.endsWith(".sql"));
|
|
9518
9904
|
for (const file of files) {
|
|
9519
9905
|
const content = readFileSync(join(schemasDir, file), "utf-8");
|
|
9520
|
-
const contentWithoutComments = content
|
|
9906
|
+
const contentWithoutComments = stripSqlCommentsPreserveStrings(content);
|
|
9521
9907
|
const matches = contentWithoutComments.matchAll(
|
|
9522
|
-
/^\s*CREATE\s+SCHEMA\s+(?:IF\s+NOT\s+EXISTS\s+)?(
|
|
9908
|
+
/^\s*CREATE\s+SCHEMA\s+(?:IF\s+NOT\s+EXISTS\s+)?(?:"((?:[^"]|"")*)"|([a-zA-Z_][a-zA-Z0-9_]*))/gim
|
|
9523
9909
|
);
|
|
9524
9910
|
for (const match of Array.from(matches)) {
|
|
9525
|
-
const
|
|
9911
|
+
const schemaNameRaw = (match[1] ?? match[2] ?? "").replace(/""/g, '"');
|
|
9912
|
+
const schemaName = schemaNameRaw.toLowerCase();
|
|
9526
9913
|
if (!EXCLUDED_SCHEMAS.has(schemaName)) {
|
|
9527
9914
|
schemas.add(schemaName);
|
|
9528
9915
|
}
|
|
@@ -9553,12 +9940,12 @@ async function detectStack(repoRoot, tmpDir, productionDbUrlAdmin) {
|
|
|
9553
9940
|
label: "detect-stack",
|
|
9554
9941
|
command: "pnpm",
|
|
9555
9942
|
args: ["exec", "runa", "db", "detect-stack", "--quiet"],
|
|
9556
|
-
logFile:
|
|
9943
|
+
logFile: path11__default.join(tmpDir, "detect-stack.log")
|
|
9557
9944
|
});
|
|
9558
9945
|
return String(res.stdout ?? "").trim();
|
|
9559
9946
|
}
|
|
9560
9947
|
function checkIfInitialDeployment(repoRoot, productionDbUrl) {
|
|
9561
|
-
const schemasDir =
|
|
9948
|
+
const schemasDir = path11__default.join(repoRoot, "supabase", "schemas", "declarative");
|
|
9562
9949
|
let schemas;
|
|
9563
9950
|
try {
|
|
9564
9951
|
schemas = detectAppSchemas(schemasDir, false);
|
|
@@ -9624,9 +10011,9 @@ function checkIfInitialDeployment(repoRoot, productionDbUrl) {
|
|
|
9624
10011
|
}
|
|
9625
10012
|
}
|
|
9626
10013
|
async function showSchemaDiff(repoRoot, tmpDir) {
|
|
9627
|
-
const diffLog =
|
|
10014
|
+
const diffLog = path11__default.join(tmpDir, "schema-diff.log");
|
|
9628
10015
|
const safeEnv = getSafeEnv();
|
|
9629
|
-
const declarativeSqlPath =
|
|
10016
|
+
const declarativeSqlPath = path11__default.join(repoRoot, "supabase/schemas/declarative");
|
|
9630
10017
|
if (existsSync(declarativeSqlPath)) {
|
|
9631
10018
|
await runLogged({
|
|
9632
10019
|
cwd: repoRoot,
|
|
@@ -9640,8 +10027,8 @@ async function showSchemaDiff(repoRoot, tmpDir) {
|
|
|
9640
10027
|
}
|
|
9641
10028
|
const detected = detectDatabasePackage(repoRoot);
|
|
9642
10029
|
if (detected) {
|
|
9643
|
-
const schemaPath =
|
|
9644
|
-
if (existsSync(
|
|
10030
|
+
const schemaPath = path11__default.join(detected, "src", "schema");
|
|
10031
|
+
if (existsSync(path11__default.join(repoRoot, schemaPath))) {
|
|
9645
10032
|
await runLogged({
|
|
9646
10033
|
cwd: repoRoot,
|
|
9647
10034
|
env: safeEnv,
|
|
@@ -9654,8 +10041,8 @@ async function showSchemaDiff(repoRoot, tmpDir) {
|
|
|
9654
10041
|
}
|
|
9655
10042
|
}
|
|
9656
10043
|
for (const candidate of DATABASE_PACKAGE_CANDIDATES) {
|
|
9657
|
-
const candidatePath =
|
|
9658
|
-
if (existsSync(
|
|
10044
|
+
const candidatePath = path11__default.join("packages", candidate, "src", "schema");
|
|
10045
|
+
if (existsSync(path11__default.join(repoRoot, candidatePath))) {
|
|
9659
10046
|
await runLogged({
|
|
9660
10047
|
cwd: repoRoot,
|
|
9661
10048
|
env: safeEnv,
|
|
@@ -9667,7 +10054,7 @@ async function showSchemaDiff(repoRoot, tmpDir) {
|
|
|
9667
10054
|
return;
|
|
9668
10055
|
}
|
|
9669
10056
|
}
|
|
9670
|
-
if (existsSync(
|
|
10057
|
+
if (existsSync(path11__default.join(repoRoot, "src", "schema"))) {
|
|
9671
10058
|
await runLogged({
|
|
9672
10059
|
cwd: repoRoot,
|
|
9673
10060
|
env: safeEnv,
|
|
@@ -9679,7 +10066,7 @@ async function showSchemaDiff(repoRoot, tmpDir) {
|
|
|
9679
10066
|
}
|
|
9680
10067
|
}
|
|
9681
10068
|
async function detectRisks(repoRoot, tmpDir) {
|
|
9682
|
-
const logFile =
|
|
10069
|
+
const logFile = path11__default.join(tmpDir, "db-risks.log");
|
|
9683
10070
|
try {
|
|
9684
10071
|
const env2 = getFilteredEnv();
|
|
9685
10072
|
await runLogged({
|
|
@@ -9725,7 +10112,7 @@ async function snapshotCreate(repoRoot, tmpDir, productionDbUrlAdmin, commit) {
|
|
|
9725
10112
|
label: "snapshot create production",
|
|
9726
10113
|
command: "pnpm",
|
|
9727
10114
|
args: ["exec", "runa", "db", "snapshot", "create", "production", "--commit", commit],
|
|
9728
|
-
logFile:
|
|
10115
|
+
logFile: path11__default.join(tmpDir, "snapshot-create.log")
|
|
9729
10116
|
});
|
|
9730
10117
|
}
|
|
9731
10118
|
async function snapshotRestoreLatest(repoRoot, tmpDir, productionDbUrlAdmin) {
|
|
@@ -9738,7 +10125,7 @@ async function snapshotRestoreLatest(repoRoot, tmpDir, productionDbUrlAdmin) {
|
|
|
9738
10125
|
label: "snapshot restore production (latest)",
|
|
9739
10126
|
command: "pnpm",
|
|
9740
10127
|
args: ["exec", "runa", "db", "snapshot", "restore", "production", "--latest", "--auto-approve"],
|
|
9741
|
-
logFile:
|
|
10128
|
+
logFile: path11__default.join(tmpDir, "snapshot-restore.log")
|
|
9742
10129
|
});
|
|
9743
10130
|
}
|
|
9744
10131
|
function parseApplyLog(logContent) {
|
|
@@ -9802,7 +10189,7 @@ async function applyProductionSchema(repoRoot, tmpDir, productionDbUrlAdmin, pro
|
|
|
9802
10189
|
if (options?.maxLockWaitMs !== void 0) {
|
|
9803
10190
|
args.push("--max-lock-wait-ms", String(options.maxLockWaitMs));
|
|
9804
10191
|
}
|
|
9805
|
-
const logPath =
|
|
10192
|
+
const logPath = path11__default.join(tmpDir, "db-apply-production.log");
|
|
9806
10193
|
const startTime = Date.now();
|
|
9807
10194
|
await runLogged({
|
|
9808
10195
|
cwd: repoRoot,
|
|
@@ -9847,11 +10234,11 @@ async function auditRecord(repoRoot, tmpDir, productionDbUrlAdmin, params) {
|
|
|
9847
10234
|
label: "git log (commit message)",
|
|
9848
10235
|
command: "git",
|
|
9849
10236
|
args: ["log", "-1", "--pretty=format:%s"],
|
|
9850
|
-
logFile:
|
|
10237
|
+
logFile: path11__default.join(tmpDir, "git-commit-message.log")
|
|
9851
10238
|
});
|
|
9852
10239
|
const commitMsg = String(msg.stdout ?? "").trim();
|
|
9853
10240
|
const schemaPaths = [];
|
|
9854
|
-
const declarativeSqlPath =
|
|
10241
|
+
const declarativeSqlPath = path11__default.join(repoRoot, "supabase/schemas/declarative");
|
|
9855
10242
|
if (existsSync(declarativeSqlPath)) {
|
|
9856
10243
|
schemaPaths.push("supabase/schemas/declarative/");
|
|
9857
10244
|
} else {
|
|
@@ -9871,7 +10258,7 @@ async function auditRecord(repoRoot, tmpDir, productionDbUrlAdmin, params) {
|
|
|
9871
10258
|
label: "git diff (schema)",
|
|
9872
10259
|
command: "git",
|
|
9873
10260
|
args: ["diff", "origin/main", "HEAD", "--", ...schemaPaths],
|
|
9874
|
-
logFile:
|
|
10261
|
+
logFile: path11__default.join(tmpDir, "git-schema-diff.log")
|
|
9875
10262
|
});
|
|
9876
10263
|
const diffRaw = String(diff.stdout ?? "");
|
|
9877
10264
|
const diffLimited = diffRaw.length > 1e4 ? diffRaw.slice(0, 1e4) : diffRaw;
|
|
@@ -9900,7 +10287,7 @@ async function auditRecord(repoRoot, tmpDir, productionDbUrlAdmin, params) {
|
|
|
9900
10287
|
"--environment",
|
|
9901
10288
|
"production"
|
|
9902
10289
|
],
|
|
9903
|
-
logFile:
|
|
10290
|
+
logFile: path11__default.join(tmpDir, "db-audit-record.log")
|
|
9904
10291
|
});
|
|
9905
10292
|
}
|
|
9906
10293
|
async function notifyDeployment(repoRoot, tmpDir, params) {
|
|
@@ -9928,7 +10315,7 @@ async function notifyDeployment(repoRoot, tmpDir, params) {
|
|
|
9928
10315
|
"--type",
|
|
9929
10316
|
"production-schema"
|
|
9930
10317
|
],
|
|
9931
|
-
logFile:
|
|
10318
|
+
logFile: path11__default.join(tmpDir, "notify-deployment.log")
|
|
9932
10319
|
});
|
|
9933
10320
|
}
|
|
9934
10321
|
|
|
@@ -10290,7 +10677,7 @@ $$;
|
|
|
10290
10677
|
label: `repair timestamps (${params.labelPrefix})`,
|
|
10291
10678
|
command: "psql",
|
|
10292
10679
|
args: [...psql.args, "-v", "ON_ERROR_STOP=1", "-c", sql],
|
|
10293
|
-
logFile:
|
|
10680
|
+
logFile: path11__default.join(params.tmpDir, `timestamp-repair-${params.labelPrefix}.log`)
|
|
10294
10681
|
});
|
|
10295
10682
|
}
|
|
10296
10683
|
|
|
@@ -10722,7 +11109,7 @@ async function runCiProdApply(params) {
|
|
|
10722
11109
|
overridePath: params.options.config
|
|
10723
11110
|
});
|
|
10724
11111
|
if (cfg.configPath)
|
|
10725
|
-
params.summary.detected.configPath =
|
|
11112
|
+
params.summary.detected.configPath = path11__default.relative(params.repoRoot, cfg.configPath);
|
|
10726
11113
|
if (cfg.config) params.summary.detected.config = cfg.config;
|
|
10727
11114
|
requireCiAutoApprove({ mode: params.summary.mode, autoApprove: params.options.autoApprove });
|
|
10728
11115
|
const inputs = resolveProdApplyInputs();
|
|
@@ -10815,7 +11202,7 @@ var ciProdApplyCommand = new Command("prod-apply").description("Apply production
|
|
|
10815
11202
|
await appendGithubStepSummary(
|
|
10816
11203
|
buildCiProdApplyStepSummaryMarkdown({
|
|
10817
11204
|
summary,
|
|
10818
|
-
summaryPath:
|
|
11205
|
+
summaryPath: path11__default.relative(repoRoot, summaryPath)
|
|
10819
11206
|
})
|
|
10820
11207
|
);
|
|
10821
11208
|
if (summary.mode === "github-actions") {
|
|
@@ -10831,7 +11218,7 @@ var ciProdApplyCommand = new Command("prod-apply").description("Apply production
|
|
|
10831
11218
|
const summaryPath = await writeCiSummary({ summary });
|
|
10832
11219
|
logSection("Result");
|
|
10833
11220
|
console.log(`status: ${summary.status}`);
|
|
10834
|
-
console.log(`summary: ${
|
|
11221
|
+
console.log(`summary: ${path11__default.relative(repoRoot, summaryPath)}`);
|
|
10835
11222
|
logNextActions([
|
|
10836
11223
|
"Inspect .runa/tmp logs (db-apply-production.log, snapshot-*.log)",
|
|
10837
11224
|
"If rollback succeeded, database should be restored to pre-deploy snapshot"
|
|
@@ -10839,7 +11226,7 @@ var ciProdApplyCommand = new Command("prod-apply").description("Apply production
|
|
|
10839
11226
|
await appendGithubStepSummary(
|
|
10840
11227
|
buildCiProdApplyStepSummaryMarkdown({
|
|
10841
11228
|
summary,
|
|
10842
|
-
summaryPath:
|
|
11229
|
+
summaryPath: path11__default.relative(repoRoot, summaryPath)
|
|
10843
11230
|
})
|
|
10844
11231
|
);
|
|
10845
11232
|
await maybePostFailureComment({
|
|
@@ -10910,7 +11297,7 @@ function getChecks() {
|
|
|
10910
11297
|
}
|
|
10911
11298
|
async function runCheck(check, logDir) {
|
|
10912
11299
|
const startTime = Date.now();
|
|
10913
|
-
const logPath =
|
|
11300
|
+
const logPath = path11__default.join(logDir, `${check.name}.log`);
|
|
10914
11301
|
return new Promise((resolve12) => {
|
|
10915
11302
|
const proc = spawn(check.command, check.args, {
|
|
10916
11303
|
cwd: process.cwd(),
|
|
@@ -10940,7 +11327,7 @@ async function runCheck(check, logDir) {
|
|
|
10940
11327
|
status: exitCode === 0 ? "passed" : "failed",
|
|
10941
11328
|
exitCode,
|
|
10942
11329
|
durationMs,
|
|
10943
|
-
logPath:
|
|
11330
|
+
logPath: path11__default.relative(process.cwd(), logPath)
|
|
10944
11331
|
});
|
|
10945
11332
|
});
|
|
10946
11333
|
proc.on("error", async () => {
|
|
@@ -10955,7 +11342,7 @@ async function runCheck(check, logDir) {
|
|
|
10955
11342
|
status: "failed",
|
|
10956
11343
|
exitCode: 1,
|
|
10957
11344
|
durationMs,
|
|
10958
|
-
logPath:
|
|
11345
|
+
logPath: path11__default.relative(process.cwd(), logPath)
|
|
10959
11346
|
});
|
|
10960
11347
|
});
|
|
10961
11348
|
});
|
|
@@ -11083,19 +11470,19 @@ function printFinalStaticOutput(summary, summaryPath) {
|
|
|
11083
11470
|
console.log(`${summary.status === "success" ? "\u2705" : "\u274C"} Static Analysis: ${summary.status}`);
|
|
11084
11471
|
console.log(` Duration: ${formatDuration$1(summary.durationMs)}`);
|
|
11085
11472
|
console.log(` Passed: ${summary.passedCount}/${summary.checks.length}`);
|
|
11086
|
-
console.log(` Summary: ${
|
|
11473
|
+
console.log(` Summary: ${path11__default.relative(process.cwd(), summaryPath)}`);
|
|
11087
11474
|
console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
11088
11475
|
}
|
|
11089
11476
|
async function runCiStatic(options) {
|
|
11090
11477
|
const startedAt = /* @__PURE__ */ new Date();
|
|
11091
11478
|
const isCI3 = options.mode === "github-actions";
|
|
11092
|
-
const logDir =
|
|
11479
|
+
const logDir = path11__default.join(process.cwd(), ".runa", "tmp", "workflow");
|
|
11093
11480
|
await mkdir(logDir, { recursive: true });
|
|
11094
11481
|
const checksToRun = getChecksToRun(options);
|
|
11095
11482
|
printStaticHeader(options);
|
|
11096
11483
|
const results = options.parallel ? await runChecksParallel(checksToRun, logDir) : await runChecksSequential(checksToRun, logDir);
|
|
11097
11484
|
const summary = buildStaticSummary(startedAt, results);
|
|
11098
|
-
const summaryPath =
|
|
11485
|
+
const summaryPath = path11__default.join(logDir, "static-summary.json");
|
|
11099
11486
|
await writeFile(summaryPath, JSON.stringify(summary, null, 2), "utf-8");
|
|
11100
11487
|
if (isCI3) {
|
|
11101
11488
|
await appendGithubStepSummary(buildStepSummaryMarkdown2(summary));
|
|
@@ -11125,9 +11512,9 @@ var appBuildActor = fromPromise(
|
|
|
11125
11512
|
async ({ input: input3 }) => {
|
|
11126
11513
|
const { repoRoot, tmpDir, env: env2 = process.env } = input3;
|
|
11127
11514
|
try {
|
|
11128
|
-
const hasTurbo = existsSync(
|
|
11129
|
-
const hasApps = existsSync(
|
|
11130
|
-
const hasAppsWeb = existsSync(
|
|
11515
|
+
const hasTurbo = existsSync(path11__default.join(repoRoot, "turbo.json"));
|
|
11516
|
+
const hasApps = existsSync(path11__default.join(repoRoot, "apps"));
|
|
11517
|
+
const hasAppsWeb = existsSync(path11__default.join(repoRoot, "apps", "web"));
|
|
11131
11518
|
const args = hasTurbo ? hasApps && !hasAppsWeb ? ["turbo", "run", "build", "--filter=./apps/*"] : ["turbo", "run", "build"] : ["build"];
|
|
11132
11519
|
await runLogged({
|
|
11133
11520
|
cwd: repoRoot,
|
|
@@ -11135,7 +11522,7 @@ var appBuildActor = fromPromise(
|
|
|
11135
11522
|
label: "build",
|
|
11136
11523
|
command: "pnpm",
|
|
11137
11524
|
args,
|
|
11138
|
-
logFile:
|
|
11525
|
+
logFile: path11__default.join(tmpDir, "build.log")
|
|
11139
11526
|
});
|
|
11140
11527
|
return { passed: true };
|
|
11141
11528
|
} catch (error) {
|
|
@@ -11151,14 +11538,14 @@ var appBuildActor = fromPromise(
|
|
|
11151
11538
|
init_esm_shims();
|
|
11152
11539
|
var APP_CANDIDATES = ["web", "dashboard", "app", "frontend", "client"];
|
|
11153
11540
|
function findExistingAppDirs(repoRoot) {
|
|
11154
|
-
const appsDir =
|
|
11541
|
+
const appsDir = path11__default.join(repoRoot, "apps");
|
|
11155
11542
|
if (!existsSync(appsDir)) return [];
|
|
11156
11543
|
try {
|
|
11157
11544
|
const entries = readdirSync(appsDir, { withFileTypes: true });
|
|
11158
11545
|
const found = [];
|
|
11159
11546
|
for (const entry of entries) {
|
|
11160
11547
|
if (!entry.isDirectory()) continue;
|
|
11161
|
-
const pkgJson =
|
|
11548
|
+
const pkgJson = path11__default.join(appsDir, entry.name, "package.json");
|
|
11162
11549
|
if (existsSync(pkgJson)) {
|
|
11163
11550
|
found.push(`apps/${entry.name}`);
|
|
11164
11551
|
}
|
|
@@ -11178,9 +11565,9 @@ function findExistingAppDirs(repoRoot) {
|
|
|
11178
11565
|
}
|
|
11179
11566
|
}
|
|
11180
11567
|
function validateAppDir(repoRoot, appDir) {
|
|
11181
|
-
const pkgJsonPath =
|
|
11568
|
+
const pkgJsonPath = path11__default.join(appDir, "package.json");
|
|
11182
11569
|
if (!existsSync(appDir)) {
|
|
11183
|
-
const relPath =
|
|
11570
|
+
const relPath = path11__default.relative(repoRoot, appDir) || ".";
|
|
11184
11571
|
const suggestions = findExistingAppDirs(repoRoot);
|
|
11185
11572
|
const errorParts = [`App directory not found: ${relPath}`];
|
|
11186
11573
|
if (suggestions.length > 0) {
|
|
@@ -11202,7 +11589,7 @@ function validateAppDir(repoRoot, appDir) {
|
|
|
11202
11589
|
return errorParts.join("\n");
|
|
11203
11590
|
}
|
|
11204
11591
|
if (!existsSync(pkgJsonPath)) {
|
|
11205
|
-
const relPath =
|
|
11592
|
+
const relPath = path11__default.relative(repoRoot, appDir) || ".";
|
|
11206
11593
|
return `No package.json found in app directory: ${relPath}`;
|
|
11207
11594
|
}
|
|
11208
11595
|
return null;
|
|
@@ -11259,9 +11646,9 @@ init_esm_shims();
|
|
|
11259
11646
|
async function runAppBuild(params) {
|
|
11260
11647
|
const { repoRoot, tmpDir, env: env2 } = params;
|
|
11261
11648
|
try {
|
|
11262
|
-
const hasTurbo = existsSync(
|
|
11263
|
-
const hasApps = existsSync(
|
|
11264
|
-
const hasAppsWeb = existsSync(
|
|
11649
|
+
const hasTurbo = existsSync(path11__default.join(repoRoot, "turbo.json"));
|
|
11650
|
+
const hasApps = existsSync(path11__default.join(repoRoot, "apps"));
|
|
11651
|
+
const hasAppsWeb = existsSync(path11__default.join(repoRoot, "apps", "web"));
|
|
11265
11652
|
const args = hasTurbo ? hasApps && !hasAppsWeb ? ["turbo", "run", "build", "--filter=./apps/*"] : ["turbo", "run", "build"] : ["build"];
|
|
11266
11653
|
await runLogged({
|
|
11267
11654
|
cwd: repoRoot,
|
|
@@ -11269,7 +11656,7 @@ async function runAppBuild(params) {
|
|
|
11269
11656
|
label: "build",
|
|
11270
11657
|
command: "pnpm",
|
|
11271
11658
|
args,
|
|
11272
|
-
logFile:
|
|
11659
|
+
logFile: path11__default.join(tmpDir, "build.log")
|
|
11273
11660
|
});
|
|
11274
11661
|
return { passed: true };
|
|
11275
11662
|
} catch (error) {
|
|
@@ -11288,7 +11675,7 @@ async function runManifestGenerate(params) {
|
|
|
11288
11675
|
label: "manifest:generate",
|
|
11289
11676
|
command: "pnpm",
|
|
11290
11677
|
args: ["manifest:generate"],
|
|
11291
|
-
logFile:
|
|
11678
|
+
logFile: path11__default.join(tmpDir, "manifest-generate.log")
|
|
11292
11679
|
});
|
|
11293
11680
|
return { generated: true };
|
|
11294
11681
|
} catch (error) {
|
|
@@ -11308,7 +11695,7 @@ async function runPlaywrightInstall(params) {
|
|
|
11308
11695
|
label: "playwright install",
|
|
11309
11696
|
command: "pnpm",
|
|
11310
11697
|
args,
|
|
11311
|
-
logFile:
|
|
11698
|
+
logFile: path11__default.join(tmpDir, "playwright-install.log")
|
|
11312
11699
|
});
|
|
11313
11700
|
return { installed: true };
|
|
11314
11701
|
} catch (error) {
|
|
@@ -11381,7 +11768,7 @@ var playwrightInstallActor = fromPromise(
|
|
|
11381
11768
|
label: "playwright install",
|
|
11382
11769
|
command: "pnpm",
|
|
11383
11770
|
args,
|
|
11384
|
-
logFile:
|
|
11771
|
+
logFile: path11__default.join(tmpDir, "playwright-install.log")
|
|
11385
11772
|
});
|
|
11386
11773
|
return { installed: true };
|
|
11387
11774
|
} catch (error) {
|
|
@@ -11405,7 +11792,7 @@ var staticChecksActor2 = fromPromise(
|
|
|
11405
11792
|
label: "type-check",
|
|
11406
11793
|
command: "pnpm",
|
|
11407
11794
|
args: ["type-check"],
|
|
11408
|
-
logFile:
|
|
11795
|
+
logFile: path11__default.join(tmpDir, "type-check.log")
|
|
11409
11796
|
}),
|
|
11410
11797
|
runLogged({
|
|
11411
11798
|
cwd: repoRoot,
|
|
@@ -11413,7 +11800,7 @@ var staticChecksActor2 = fromPromise(
|
|
|
11413
11800
|
label: "lint",
|
|
11414
11801
|
command: "pnpm",
|
|
11415
11802
|
args: ["lint"],
|
|
11416
|
-
logFile:
|
|
11803
|
+
logFile: path11__default.join(tmpDir, "lint.log")
|
|
11417
11804
|
})
|
|
11418
11805
|
]);
|
|
11419
11806
|
const typeCheckPassed = typeCheckResult.status === "fulfilled";
|
|
@@ -11564,7 +11951,7 @@ var applySeedsActor = fromPromise(
|
|
|
11564
11951
|
label: `db seed (${envArg})`,
|
|
11565
11952
|
command: "pnpm",
|
|
11566
11953
|
args: ["exec", "runa", "db", "seed", envArg, "--auto-approve"],
|
|
11567
|
-
logFile:
|
|
11954
|
+
logFile: path11__default.join(tmpDir, `ci-db-seed-${envArg}.log`)
|
|
11568
11955
|
});
|
|
11569
11956
|
return { applied: true };
|
|
11570
11957
|
} catch (error) {
|
|
@@ -11942,7 +12329,7 @@ SELECT (SELECT available FROM a)::text || ',' || (SELECT installed FROM i)::text
|
|
|
11942
12329
|
label: `probe pgtap (best-effort, ${params.label})`,
|
|
11943
12330
|
command: "psql",
|
|
11944
12331
|
args: [ddlUrl, "-X", "-v", "ON_ERROR_STOP=1", "-q", "-t", "-A", "-c", probeSql],
|
|
11945
|
-
logFile:
|
|
12332
|
+
logFile: path11__default.join(params.tmpDir, `pgtap-probe-${params.label}.log`)
|
|
11946
12333
|
}).catch(() => null);
|
|
11947
12334
|
const raw = String(probe?.stdout ?? "").trim();
|
|
11948
12335
|
const [availableRaw, installedRaw] = raw.split(",");
|
|
@@ -11957,11 +12344,11 @@ SELECT (SELECT available FROM a)::text || ',' || (SELECT installed FROM i)::text
|
|
|
11957
12344
|
label: `ensure pgtap (best-effort, ${params.label})`,
|
|
11958
12345
|
command: "psql",
|
|
11959
12346
|
args: [ddlUrl, "-X", "-v", "ON_ERROR_STOP=1", "-q", "-c", installSql],
|
|
11960
|
-
logFile:
|
|
12347
|
+
logFile: path11__default.join(params.tmpDir, `pgtap-ensure-${params.label}.log`)
|
|
11961
12348
|
}).catch(async (error) => {
|
|
11962
12349
|
try {
|
|
11963
12350
|
await writeFile(
|
|
11964
|
-
|
|
12351
|
+
path11__default.join(params.tmpDir, `pgtap-ensure-${params.label}.failed`),
|
|
11965
12352
|
`${error instanceof Error ? error.message : String(error)}
|
|
11966
12353
|
`,
|
|
11967
12354
|
"utf-8"
|
|
@@ -11975,7 +12362,7 @@ SELECT (SELECT available FROM a)::text || ',' || (SELECT installed FROM i)::text
|
|
|
11975
12362
|
label: `probe pgtap (best-effort, ${params.label}, after)`,
|
|
11976
12363
|
command: "psql",
|
|
11977
12364
|
args: [ddlUrl, "-X", "-v", "ON_ERROR_STOP=1", "-q", "-t", "-A", "-c", probeSql],
|
|
11978
|
-
logFile:
|
|
12365
|
+
logFile: path11__default.join(params.tmpDir, `pgtap-probe-${params.label}-after.log`)
|
|
11979
12366
|
}).catch(() => null);
|
|
11980
12367
|
const afterRaw = String(probeAfter?.stdout ?? "").trim();
|
|
11981
12368
|
const [availableAfterRaw, installedAfterRaw] = afterRaw.split(",");
|
|
@@ -12096,14 +12483,14 @@ function extractSqlFromSchemaChanges(fullOutput) {
|
|
|
12096
12483
|
return null;
|
|
12097
12484
|
}
|
|
12098
12485
|
function getIdempotentRoleNames(repoRoot) {
|
|
12099
|
-
const idempotentDir =
|
|
12486
|
+
const idempotentDir = path11__default.join(repoRoot, "supabase", "schemas", "idempotent");
|
|
12100
12487
|
const roles = [];
|
|
12101
12488
|
try {
|
|
12102
12489
|
const fs14 = __require("fs");
|
|
12103
12490
|
if (!fs14.existsSync(idempotentDir)) return [];
|
|
12104
12491
|
const files = fs14.readdirSync(idempotentDir).filter((f) => f.endsWith(".sql"));
|
|
12105
12492
|
for (const file of files) {
|
|
12106
|
-
const content = fs14.readFileSync(
|
|
12493
|
+
const content = fs14.readFileSync(path11__default.join(idempotentDir, file), "utf-8");
|
|
12107
12494
|
const roleMatches = content.matchAll(/CREATE\s+ROLE\s+(\w+)\s+WITH/gi);
|
|
12108
12495
|
for (const match of roleMatches) {
|
|
12109
12496
|
if (match[1]) roles.push(match[1].toLowerCase());
|
|
@@ -12244,7 +12631,7 @@ var productionPreviewActor = fromPromise(
|
|
|
12244
12631
|
);
|
|
12245
12632
|
return buildSkipResult(!!productionUrl);
|
|
12246
12633
|
}
|
|
12247
|
-
const logFile =
|
|
12634
|
+
const logFile = path11__default.join(tmpDir, "ci-production-preview.log");
|
|
12248
12635
|
console.log("\u25B6 production preview (dry-run): runa db apply production --check");
|
|
12249
12636
|
try {
|
|
12250
12637
|
const result = await execa(
|
|
@@ -12334,7 +12721,7 @@ var resetDbActor = fromPromise(
|
|
|
12334
12721
|
label: "db reset (local)",
|
|
12335
12722
|
command: "npx",
|
|
12336
12723
|
args: ["supabase", "db", "reset", "--local"],
|
|
12337
|
-
logFile:
|
|
12724
|
+
logFile: path11__default.join(tmpDir, "ci-db-reset.log")
|
|
12338
12725
|
});
|
|
12339
12726
|
return { reset: true };
|
|
12340
12727
|
} catch (error) {
|
|
@@ -12350,8 +12737,8 @@ var resetDbActor = fromPromise(
|
|
|
12350
12737
|
init_esm_shims();
|
|
12351
12738
|
async function hasSetupRolesScript(repoRoot) {
|
|
12352
12739
|
const candidates = [
|
|
12353
|
-
|
|
12354
|
-
|
|
12740
|
+
path11__default.join(repoRoot, "packages", "database", "package.json"),
|
|
12741
|
+
path11__default.join(repoRoot, "package.json")
|
|
12355
12742
|
];
|
|
12356
12743
|
for (const pkgPath of candidates) {
|
|
12357
12744
|
if (!existsSync(pkgPath)) continue;
|
|
@@ -12389,7 +12776,7 @@ var setupRolesActor = fromPromise(
|
|
|
12389
12776
|
label: "db roles setup",
|
|
12390
12777
|
command: "pnpm",
|
|
12391
12778
|
args: ["db:setup-roles"],
|
|
12392
|
-
logFile:
|
|
12779
|
+
logFile: path11__default.join(tmpDir, "ci-db-roles-setup.log")
|
|
12393
12780
|
});
|
|
12394
12781
|
const stdout = String(result.stdout ?? "");
|
|
12395
12782
|
const appUrlMatch = stdout.match(/APP_DATABASE_URL=["']?([^"'\n]+)["']?/);
|
|
@@ -12502,14 +12889,14 @@ function getSchemaGitDiff(repoRoot) {
|
|
|
12502
12889
|
}
|
|
12503
12890
|
}
|
|
12504
12891
|
function getIdempotentRoleNames2(repoRoot) {
|
|
12505
|
-
const idempotentDir =
|
|
12892
|
+
const idempotentDir = path11__default.join(repoRoot, "supabase", "schemas", "idempotent");
|
|
12506
12893
|
const roles = [];
|
|
12507
12894
|
try {
|
|
12508
12895
|
const fs14 = __require("fs");
|
|
12509
12896
|
if (!fs14.existsSync(idempotentDir)) return [];
|
|
12510
12897
|
const files = fs14.readdirSync(idempotentDir).filter((f) => f.endsWith(".sql"));
|
|
12511
12898
|
for (const file of files) {
|
|
12512
|
-
const content = fs14.readFileSync(
|
|
12899
|
+
const content = fs14.readFileSync(path11__default.join(idempotentDir, file), "utf-8");
|
|
12513
12900
|
const roleMatches = content.matchAll(/CREATE\s+ROLE\s+(\w+)\s+WITH/gi);
|
|
12514
12901
|
for (const match of roleMatches) {
|
|
12515
12902
|
if (match[1]) roles.push(match[1].toLowerCase());
|
|
@@ -12606,7 +12993,7 @@ var syncSchemaActor = fromPromise(
|
|
|
12606
12993
|
// Always verbose for full traceability
|
|
12607
12994
|
...skipCodegen ? ["--skip-codegen"] : []
|
|
12608
12995
|
];
|
|
12609
|
-
const logFile =
|
|
12996
|
+
const logFile = path11__default.join(tmpDir, `ci-db-${useDbApply ? "apply" : "sync"}-${envArg}.log`);
|
|
12610
12997
|
await runLogged({
|
|
12611
12998
|
cwd: repoRoot,
|
|
12612
12999
|
env: baseEnv,
|
|
@@ -12746,7 +13133,7 @@ async function startSupabaseLocal(params) {
|
|
|
12746
13133
|
label: "supabase start (local)",
|
|
12747
13134
|
command: "supabase",
|
|
12748
13135
|
args: ["start", "--exclude", exclude],
|
|
12749
|
-
logFile:
|
|
13136
|
+
logFile: path11__default.join(params.tmpDir, "supabase-start-local.log")
|
|
12750
13137
|
});
|
|
12751
13138
|
}
|
|
12752
13139
|
function getDefaultLocalSupabaseUrl(repoRoot) {
|
|
@@ -12774,7 +13161,7 @@ async function resolveLocalSupabaseEnv(params) {
|
|
|
12774
13161
|
label: "supabase status (json)",
|
|
12775
13162
|
command: "supabase",
|
|
12776
13163
|
args: ["status", "--output", "json"],
|
|
12777
|
-
logFile:
|
|
13164
|
+
logFile: path11__default.join(params.tmpDir, `supabase-status-${i + 1}.log`)
|
|
12778
13165
|
});
|
|
12779
13166
|
const parsed = JSON.parse(String(res.stdout ?? "{}"));
|
|
12780
13167
|
const out = z.object({
|
|
@@ -12894,7 +13281,7 @@ async function findFilesRecursive(params) {
|
|
|
12894
13281
|
continue;
|
|
12895
13282
|
}
|
|
12896
13283
|
for (const e of entries) {
|
|
12897
|
-
const p =
|
|
13284
|
+
const p = path11__default.join(dir, e.name);
|
|
12898
13285
|
if (e.isDirectory()) stack.push(p);
|
|
12899
13286
|
else if (e.isFile() && params.suffixes.some((s) => p.endsWith(s))) out.push(p);
|
|
12900
13287
|
}
|
|
@@ -12908,7 +13295,7 @@ async function queryScalar(params) {
|
|
|
12908
13295
|
label: `psql scalar (${params.logName})`,
|
|
12909
13296
|
command: "psql",
|
|
12910
13297
|
args: [params.databaseUrl, "-X", "-v", "ON_ERROR_STOP=1", "-q", "-t", "-A", "-c", params.sql],
|
|
12911
|
-
logFile:
|
|
13298
|
+
logFile: path11__default.join(params.tmpDir, `psql-${params.logName}.log`)
|
|
12912
13299
|
});
|
|
12913
13300
|
return String(res.stdout ?? "").trim();
|
|
12914
13301
|
}
|
|
@@ -13036,8 +13423,8 @@ function isPlaywrightIgnored(filePath) {
|
|
|
13036
13423
|
async function detectGeneratedE2eCapabilities(params) {
|
|
13037
13424
|
const caps = /* @__PURE__ */ new Set();
|
|
13038
13425
|
const diagnostics = {};
|
|
13039
|
-
const layer4Dir =
|
|
13040
|
-
const tmpDir =
|
|
13426
|
+
const layer4Dir = path11__default.join(params.repoRoot, ".runa", "tests", "layer4");
|
|
13427
|
+
const tmpDir = path11__default.join(params.repoRoot, ".runa", "tmp");
|
|
13041
13428
|
const hasLayer4Dir = await pathExists(layer4Dir);
|
|
13042
13429
|
const hasTmpDir = await pathExists(tmpDir);
|
|
13043
13430
|
if (!hasLayer4Dir && !hasTmpDir) {
|
|
@@ -13547,7 +13934,7 @@ init_esm_shims();
|
|
|
13547
13934
|
// src/commands/ci/utils/test-parallel.ts
|
|
13548
13935
|
init_esm_shims();
|
|
13549
13936
|
async function readLayerResult(tmpDir, layer) {
|
|
13550
|
-
const resultFile =
|
|
13937
|
+
const resultFile = path11__default.join(tmpDir, `vitest-layer${layer}-result.json`);
|
|
13551
13938
|
try {
|
|
13552
13939
|
const content = await readFile(resultFile, "utf-8");
|
|
13553
13940
|
return JSON.parse(content);
|
|
@@ -13665,8 +14052,8 @@ async function waitUntilAllFinished(params) {
|
|
|
13665
14052
|
async function runLayersInParallel(params) {
|
|
13666
14053
|
const spawned = [];
|
|
13667
14054
|
for (const layer of params.layers) {
|
|
13668
|
-
const logFile =
|
|
13669
|
-
const exitFile =
|
|
14055
|
+
const logFile = path11__default.join(params.tmpDir, `layer${layer}.log`);
|
|
14056
|
+
const exitFile = path11__default.join(params.tmpDir, `layer${layer}.exit`);
|
|
13670
14057
|
const proc = runLogged({
|
|
13671
14058
|
cwd: params.repoRoot,
|
|
13672
14059
|
env: {
|
|
@@ -13709,7 +14096,7 @@ async function runLayersInParallel(params) {
|
|
|
13709
14096
|
for (const p of spawned) {
|
|
13710
14097
|
const codeRaw = p.exitCode ?? await p.procPromise;
|
|
13711
14098
|
const code = Number.isNaN(codeRaw) ? 1 : codeRaw;
|
|
13712
|
-
const resultFile =
|
|
14099
|
+
const resultFile = path11__default.join(params.tmpDir, `vitest-layer${p.layer}-result.json`);
|
|
13713
14100
|
const wasKilled = killedLayerSet.has(p.layer);
|
|
13714
14101
|
const actualResult = waitResult.successMap.get(p.layer) ?? {
|
|
13715
14102
|
success: code === 0,
|
|
@@ -13724,9 +14111,9 @@ async function runLayersInParallel(params) {
|
|
|
13724
14111
|
skippedReason: actualResult.skippedReason,
|
|
13725
14112
|
killed: wasKilled,
|
|
13726
14113
|
killedBy: wasKilled ? failFastSource : void 0,
|
|
13727
|
-
logPath:
|
|
13728
|
-
exitPath:
|
|
13729
|
-
resultPath:
|
|
14114
|
+
logPath: path11__default.relative(params.repoRoot, p.logFile),
|
|
14115
|
+
exitPath: path11__default.relative(params.repoRoot, p.exitFile),
|
|
14116
|
+
resultPath: path11__default.relative(params.repoRoot, resultFile),
|
|
13730
14117
|
// Test count information
|
|
13731
14118
|
totalTests: actualResult.totalTests,
|
|
13732
14119
|
passedTests: actualResult.passedTests,
|
|
@@ -14835,6 +15222,24 @@ function getSupabaseUrlWithFallback(context) {
|
|
|
14835
15222
|
function getSupabaseAnonKeyWithFallback(context) {
|
|
14836
15223
|
return context.supabase?.anonKey || DEFAULT_LOCAL_ANON_KEY;
|
|
14837
15224
|
}
|
|
15225
|
+
function buildRuntimeEnv(context, options = {}) {
|
|
15226
|
+
const env2 = {
|
|
15227
|
+
...context.input.runtimeEnv ?? {},
|
|
15228
|
+
DATABASE_URL: getDatabaseUrlForRuntime(context),
|
|
15229
|
+
NEXT_PUBLIC_SUPABASE_URL: getSupabaseUrlWithFallback(context),
|
|
15230
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY: getSupabaseAnonKeyWithFallback(context)
|
|
15231
|
+
};
|
|
15232
|
+
if (options.enablePublicE2EFlag) {
|
|
15233
|
+
env2.NEXT_PUBLIC_E2E_TEST = "true";
|
|
15234
|
+
}
|
|
15235
|
+
if (options.enableServerE2EFlag) {
|
|
15236
|
+
env2.E2E_TEST = "true";
|
|
15237
|
+
}
|
|
15238
|
+
if (options.baseUrl) {
|
|
15239
|
+
env2.BASE_URL = options.baseUrl;
|
|
15240
|
+
}
|
|
15241
|
+
return env2;
|
|
15242
|
+
}
|
|
14838
15243
|
function computeExitCodeFromLayerResults(layerResults) {
|
|
14839
15244
|
const classification = getClassificationForProfile("runa-strict");
|
|
14840
15245
|
const classificationMap = new Map(classification.map((c) => [c.layer, c.level]));
|
|
@@ -15323,7 +15728,7 @@ ${generateProgressCommentBody(progressInput)}`;
|
|
|
15323
15728
|
tmpDir: assertTmpDir(context),
|
|
15324
15729
|
// Execute if GH_DATABASE_URL_ADMIN is set (dry-run will determine if changes exist)
|
|
15325
15730
|
// PRD: GH_DATABASE_URL_ADMIN = postgres role (DDL capable)
|
|
15326
|
-
shouldExecute:
|
|
15731
|
+
shouldExecute: Boolean(context.input.productionDatabaseUrl?.trim())
|
|
15327
15732
|
}),
|
|
15328
15733
|
onDone: {
|
|
15329
15734
|
target: "collectSchemaStats",
|
|
@@ -15515,14 +15920,10 @@ ${generateProgressCommentBody(progressInput)}`;
|
|
|
15515
15920
|
input: ({ context }) => ({
|
|
15516
15921
|
repoRoot: assertRepoRoot(context),
|
|
15517
15922
|
tmpDir: assertTmpDir(context),
|
|
15518
|
-
env: {
|
|
15519
|
-
|
|
15520
|
-
|
|
15521
|
-
|
|
15522
|
-
NEXT_PUBLIC_SUPABASE_ANON_KEY: getSupabaseAnonKeyWithFallback(context),
|
|
15523
|
-
// CRITICAL: Required for XState Test Plugin to inject data-state attributes
|
|
15524
|
-
NEXT_PUBLIC_E2E_TEST: "true"
|
|
15525
|
-
},
|
|
15923
|
+
env: buildRuntimeEnv(context, {
|
|
15924
|
+
// Required for XState Test Plugin to inject data-state attributes.
|
|
15925
|
+
enablePublicE2EFlag: true
|
|
15926
|
+
}),
|
|
15526
15927
|
isCI: context.input.isCI ?? false,
|
|
15527
15928
|
skipPlaywright: shouldSkipPlaywrightInstall(context)
|
|
15528
15929
|
}),
|
|
@@ -15573,16 +15974,11 @@ ${generateProgressCommentBody(progressInput)}`;
|
|
|
15573
15974
|
tmpDir: assertTmpDir(context),
|
|
15574
15975
|
appDir: context.app?.appDir ?? assertRepoRoot(context),
|
|
15575
15976
|
port: context.app?.port ?? 3e3,
|
|
15576
|
-
env: {
|
|
15577
|
-
|
|
15578
|
-
|
|
15579
|
-
|
|
15580
|
-
|
|
15581
|
-
// CRITICAL: Required for middleware to bypass auth in E2E tests
|
|
15582
|
-
// E2E_TEST is server-only (proxy.ts), NEXT_PUBLIC_E2E_TEST is for build-time (xstate-test-plugin)
|
|
15583
|
-
E2E_TEST: "true",
|
|
15584
|
-
NEXT_PUBLIC_E2E_TEST: "true"
|
|
15585
|
-
}
|
|
15977
|
+
env: buildRuntimeEnv(context, {
|
|
15978
|
+
// E2E_TEST is server-only (proxy.ts), NEXT_PUBLIC_E2E_TEST is for build-time.
|
|
15979
|
+
enableServerE2EFlag: true,
|
|
15980
|
+
enablePublicE2EFlag: true
|
|
15981
|
+
})
|
|
15586
15982
|
}),
|
|
15587
15983
|
onDone: [
|
|
15588
15984
|
{
|
|
@@ -15687,12 +16083,7 @@ ${generateProgressCommentBody(progressInput)}`;
|
|
|
15687
16083
|
// ci-local: all selected layers, ci-pr: only core layers (1,2,3)
|
|
15688
16084
|
layers: getLayersForCorePhase(context.selectedLayers, context.mode),
|
|
15689
16085
|
baseUrl: context.baseUrl ?? `http://localhost:${context.app?.port ?? 3e3}`,
|
|
15690
|
-
env:
|
|
15691
|
-
...process.env,
|
|
15692
|
-
DATABASE_URL: getDatabaseUrlForRuntime(context),
|
|
15693
|
-
NEXT_PUBLIC_SUPABASE_URL: getSupabaseUrlWithFallback(context),
|
|
15694
|
-
NEXT_PUBLIC_SUPABASE_ANON_KEY: getSupabaseAnonKeyWithFallback(context)
|
|
15695
|
-
},
|
|
16086
|
+
env: buildRuntimeEnv(context),
|
|
15696
16087
|
failFast: true
|
|
15697
16088
|
}),
|
|
15698
16089
|
onDone: [
|
|
@@ -15846,15 +16237,10 @@ ${generateCommentBody(commentInput)}`;
|
|
|
15846
16237
|
tmpDir: assertTmpDir(context),
|
|
15847
16238
|
layers: [E2E_LAYER],
|
|
15848
16239
|
baseUrl: e2eBaseUrl,
|
|
15849
|
-
env: {
|
|
15850
|
-
|
|
15851
|
-
|
|
15852
|
-
|
|
15853
|
-
NEXT_PUBLIC_SUPABASE_ANON_KEY: getSupabaseAnonKeyWithFallback(context),
|
|
15854
|
-
// CRITICAL: Set BASE_URL for Playwright config and test fixtures
|
|
15855
|
-
// This ensures cookies are set on the same domain Playwright uses
|
|
15856
|
-
BASE_URL: e2eBaseUrl
|
|
15857
|
-
},
|
|
16240
|
+
env: buildRuntimeEnv(context, {
|
|
16241
|
+
// Ensures cookies are set on the same domain Playwright uses.
|
|
16242
|
+
baseUrl: e2eBaseUrl
|
|
16243
|
+
}),
|
|
15858
16244
|
failFast: false
|
|
15859
16245
|
// Don't fail-fast for E2E (warning-only)
|
|
15860
16246
|
};
|
|
@@ -16048,19 +16434,10 @@ ${generateCommentBody(commentInput)}`;
|
|
|
16048
16434
|
output: ({ context }) => createOutput3(context)
|
|
16049
16435
|
});
|
|
16050
16436
|
function getStateName3(snapshot2) {
|
|
16051
|
-
|
|
16052
|
-
if (typeof value === "string") return value;
|
|
16053
|
-
const keys = Object.keys(value);
|
|
16054
|
-
if (keys.length > 0) {
|
|
16055
|
-
const parent = keys[0];
|
|
16056
|
-
const child = value[parent];
|
|
16057
|
-
if (typeof child === "string") return `${parent}.${child}`;
|
|
16058
|
-
return parent;
|
|
16059
|
-
}
|
|
16060
|
-
return "unknown";
|
|
16437
|
+
return getSnapshotStateName(snapshot2);
|
|
16061
16438
|
}
|
|
16062
16439
|
function isComplete3(snapshot2) {
|
|
16063
|
-
return snapshot2
|
|
16440
|
+
return isSnapshotComplete(snapshot2);
|
|
16064
16441
|
}
|
|
16065
16442
|
|
|
16066
16443
|
// src/commands/ci/machine/commands/machine-runner.ts
|
|
@@ -16309,6 +16686,18 @@ async function flushAndExit(exitCode) {
|
|
|
16309
16686
|
process.exit(exitCode);
|
|
16310
16687
|
}
|
|
16311
16688
|
|
|
16689
|
+
// src/commands/ci/machine/commands/runtime-env.ts
|
|
16690
|
+
init_esm_shims();
|
|
16691
|
+
function captureRuntimeEnv(source = process.env) {
|
|
16692
|
+
const captured = {};
|
|
16693
|
+
for (const [key, value] of Object.entries(source)) {
|
|
16694
|
+
if (value !== void 0) {
|
|
16695
|
+
captured[key] = value;
|
|
16696
|
+
}
|
|
16697
|
+
}
|
|
16698
|
+
return captured;
|
|
16699
|
+
}
|
|
16700
|
+
|
|
16312
16701
|
// src/commands/ci/machine/commands/ci-local.ts
|
|
16313
16702
|
var isGitHubActionsMode = false;
|
|
16314
16703
|
var currentGroup = null;
|
|
@@ -16430,7 +16819,8 @@ function buildMachineInput(options) {
|
|
|
16430
16819
|
dbMode: void 0,
|
|
16431
16820
|
// PRD: GH_DATABASE_URL_ADMIN = postgres role (DDL capable, for pg_dump)
|
|
16432
16821
|
productionDatabaseUrl: process.env.GH_DATABASE_URL_ADMIN || process.env.GH_DATABASE_URL,
|
|
16433
|
-
databaseUrl: process.env.DATABASE_URL
|
|
16822
|
+
databaseUrl: process.env.DATABASE_URL,
|
|
16823
|
+
runtimeEnv: captureRuntimeEnv()
|
|
16434
16824
|
};
|
|
16435
16825
|
}
|
|
16436
16826
|
function handleCiError(error, logger16) {
|
|
@@ -16496,6 +16886,8 @@ z.object({
|
|
|
16496
16886
|
productionDatabaseUrl: z.string().optional(),
|
|
16497
16887
|
/** Local database URL */
|
|
16498
16888
|
databaseUrl: z.string().optional(),
|
|
16889
|
+
/** Sanitized process.env captured at entry point */
|
|
16890
|
+
runtimeEnv: z.record(z.string(), z.string()).optional(),
|
|
16499
16891
|
// === GitHub Actions Environment ===
|
|
16500
16892
|
/** GitHub ref (e.g., refs/pull/123/merge) */
|
|
16501
16893
|
githubRef: z.string().optional(),
|
|
@@ -16947,6 +17339,7 @@ function optionsToMachineInput(options) {
|
|
|
16947
17339
|
databaseUrl: process.env.DATABASE_URL,
|
|
16948
17340
|
// PRD: GH_DATABASE_URL_ADMIN = postgres role (DDL capable, for pg-schema-diff dry-run)
|
|
16949
17341
|
productionDatabaseUrl: process.env.GH_DATABASE_URL_ADMIN || process.env.GH_DATABASE_URL,
|
|
17342
|
+
runtimeEnv: captureRuntimeEnv(),
|
|
16950
17343
|
githubRef: process.env.GITHUB_REF,
|
|
16951
17344
|
// FIX: Read action from GITHUB_EVENT_PATH JSON, not non-existent GITHUB_EVENT_ACTION env var
|
|
16952
17345
|
githubEventAction: getGitHubEventAction(),
|
|
@@ -17022,13 +17415,20 @@ init_esm_shims();
|
|
|
17022
17415
|
// src/commands/db/utils/db-target.ts
|
|
17023
17416
|
init_esm_shims();
|
|
17024
17417
|
init_env();
|
|
17025
|
-
|
|
17418
|
+
init_local_supabase();
|
|
17026
17419
|
function getLocalDatabaseUrl() {
|
|
17027
|
-
|
|
17028
|
-
|
|
17420
|
+
return buildLocalDatabaseUrl(process.cwd());
|
|
17421
|
+
}
|
|
17422
|
+
function isLocalHostname(hostname) {
|
|
17423
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "host.docker.internal";
|
|
17029
17424
|
}
|
|
17030
17425
|
function isLocalDatabaseUrl(url) {
|
|
17031
|
-
|
|
17426
|
+
try {
|
|
17427
|
+
const parsed = new URL(url);
|
|
17428
|
+
return isLocalHostname(parsed.hostname);
|
|
17429
|
+
} catch {
|
|
17430
|
+
return false;
|
|
17431
|
+
}
|
|
17032
17432
|
}
|
|
17033
17433
|
function resolveDatabaseUrl(environment) {
|
|
17034
17434
|
switch (environment) {
|
|
@@ -17186,21 +17586,35 @@ async function getSqlParserUtils() {
|
|
|
17186
17586
|
await isAstParserAvailable();
|
|
17187
17587
|
return sqlParserUtils;
|
|
17188
17588
|
}
|
|
17589
|
+
var SQL_IDENTIFIER = String.raw`(?:"(?:[^"]|"")*"|[a-zA-Z_][a-zA-Z0-9_]*)`;
|
|
17189
17590
|
var SQL_PATTERNS = {
|
|
17190
17591
|
// CREATE TABLE [IF NOT EXISTS] schema.table_name (
|
|
17191
|
-
createTable:
|
|
17592
|
+
createTable: new RegExp(
|
|
17593
|
+
`CREATE\\s+TABLE\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?(${SQL_IDENTIFIER})\\.(${SQL_IDENTIFIER})\\s*\\(`,
|
|
17594
|
+
"gi"
|
|
17595
|
+
),
|
|
17192
17596
|
// PRIMARY KEY (columns)
|
|
17193
17597
|
primaryKey: /PRIMARY\s+KEY\s*\(([^)]+)\)/gi,
|
|
17194
17598
|
// FOREIGN KEY (column) REFERENCES schema.table(column) [ON DELETE|UPDATE ...]
|
|
17195
|
-
foreignKey:
|
|
17599
|
+
foreignKey: new RegExp(
|
|
17600
|
+
`FOREIGN\\s+KEY\\s*\\((${SQL_IDENTIFIER})\\)\\s*REFERENCES\\s+(${SQL_IDENTIFIER})\\.(${SQL_IDENTIFIER})\\s*\\((${SQL_IDENTIFIER})\\)(?:\\s+ON\\s+DELETE\\s+(\\w+(?:\\s+\\w+)?))?(?:\\s+ON\\s+UPDATE\\s+(\\w+(?:\\s+\\w+)?))?`,
|
|
17601
|
+
"gi"
|
|
17602
|
+
),
|
|
17196
17603
|
// REFERENCES schema.table(column) - inline column constraint
|
|
17197
|
-
inlineReference:
|
|
17604
|
+
inlineReference: new RegExp(
|
|
17605
|
+
`(${SQL_IDENTIFIER})\\s+\\w+[\\w\\s()]*REFERENCES\\s+(${SQL_IDENTIFIER})\\.(${SQL_IDENTIFIER})\\s*\\((${SQL_IDENTIFIER})\\)`,
|
|
17606
|
+
"gi"
|
|
17607
|
+
),
|
|
17198
17608
|
// CREATE [UNIQUE] INDEX name ON schema.table (columns)
|
|
17199
|
-
createIndex:
|
|
17609
|
+
createIndex: new RegExp(
|
|
17610
|
+
`CREATE\\s+(UNIQUE\\s+)?INDEX\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?(${SQL_IDENTIFIER})\\s+ON\\s+(${SQL_IDENTIFIER})\\.(${SQL_IDENTIFIER})\\s*\\(([^)]+)\\)`,
|
|
17611
|
+
"gi"
|
|
17612
|
+
),
|
|
17200
17613
|
// ALTER TABLE ... ENABLE ROW LEVEL SECURITY
|
|
17201
|
-
enableRls:
|
|
17202
|
-
|
|
17203
|
-
|
|
17614
|
+
enableRls: new RegExp(
|
|
17615
|
+
`ALTER\\s+TABLE\\s+(${SQL_IDENTIFIER})\\.(${SQL_IDENTIFIER})\\s+ENABLE\\s+ROW\\s+LEVEL\\s+SECURITY`,
|
|
17616
|
+
"gi"
|
|
17617
|
+
)
|
|
17204
17618
|
};
|
|
17205
17619
|
function snakeToCamel2(str) {
|
|
17206
17620
|
return str.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
|
|
@@ -17235,6 +17649,96 @@ function extractTableBody(content, startPos) {
|
|
|
17235
17649
|
function normalizeType(type) {
|
|
17236
17650
|
return type.trim().replace(/\s+/g, " ").toLowerCase().replace("character varying", "varchar").replace("timestamp with time zone", "timestamptz").replace("timestamp without time zone", "timestamp");
|
|
17237
17651
|
}
|
|
17652
|
+
function unquoteIdentifier(identifier) {
|
|
17653
|
+
const trimmed = identifier.trim();
|
|
17654
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
|
|
17655
|
+
return trimmed.slice(1, -1).replace(/""/g, '"');
|
|
17656
|
+
}
|
|
17657
|
+
return trimmed;
|
|
17658
|
+
}
|
|
17659
|
+
function stripSqlCommentsPreserveStrings2(content) {
|
|
17660
|
+
let result = "";
|
|
17661
|
+
let i = 0;
|
|
17662
|
+
let inSingleQuote = false;
|
|
17663
|
+
let inDoubleQuote = false;
|
|
17664
|
+
let inDollarQuote = false;
|
|
17665
|
+
let dollarTag = "";
|
|
17666
|
+
while (i < content.length) {
|
|
17667
|
+
const char = content[i] ?? "";
|
|
17668
|
+
const next = content[i + 1] ?? "";
|
|
17669
|
+
if (!inSingleQuote && !inDoubleQuote && !inDollarQuote && char === "-" && next === "-") {
|
|
17670
|
+
while (i < content.length && content[i] !== "\n") {
|
|
17671
|
+
result += " ";
|
|
17672
|
+
i++;
|
|
17673
|
+
}
|
|
17674
|
+
continue;
|
|
17675
|
+
}
|
|
17676
|
+
if (!inSingleQuote && !inDoubleQuote && !inDollarQuote && char === "/" && next === "*") {
|
|
17677
|
+
result += " ";
|
|
17678
|
+
result += " ";
|
|
17679
|
+
i += 2;
|
|
17680
|
+
while (i < content.length) {
|
|
17681
|
+
const blockChar = content[i] ?? "";
|
|
17682
|
+
const blockNext = content[i + 1] ?? "";
|
|
17683
|
+
if (blockChar === "*" && blockNext === "/") {
|
|
17684
|
+
result += " ";
|
|
17685
|
+
result += " ";
|
|
17686
|
+
i += 2;
|
|
17687
|
+
break;
|
|
17688
|
+
}
|
|
17689
|
+
result += blockChar === "\n" ? "\n" : " ";
|
|
17690
|
+
i++;
|
|
17691
|
+
}
|
|
17692
|
+
continue;
|
|
17693
|
+
}
|
|
17694
|
+
if (!inSingleQuote && !inDoubleQuote && char === "$") {
|
|
17695
|
+
if (inDollarQuote) {
|
|
17696
|
+
const closeTag = `$${dollarTag}$`;
|
|
17697
|
+
if (content.slice(i).startsWith(closeTag)) {
|
|
17698
|
+
result += closeTag;
|
|
17699
|
+
i += closeTag.length;
|
|
17700
|
+
inDollarQuote = false;
|
|
17701
|
+
dollarTag = "";
|
|
17702
|
+
continue;
|
|
17703
|
+
}
|
|
17704
|
+
} else {
|
|
17705
|
+
const tagMatch = content.slice(i).match(/^\$([a-zA-Z_][a-zA-Z0-9_]*)?\$/);
|
|
17706
|
+
if (tagMatch) {
|
|
17707
|
+
inDollarQuote = true;
|
|
17708
|
+
dollarTag = tagMatch[1] ?? "";
|
|
17709
|
+
result += tagMatch[0];
|
|
17710
|
+
i += tagMatch[0].length;
|
|
17711
|
+
continue;
|
|
17712
|
+
}
|
|
17713
|
+
}
|
|
17714
|
+
}
|
|
17715
|
+
if (!inDoubleQuote && !inDollarQuote && char === "'") {
|
|
17716
|
+
if (inSingleQuote && next === "'") {
|
|
17717
|
+
result += "''";
|
|
17718
|
+
i += 2;
|
|
17719
|
+
continue;
|
|
17720
|
+
}
|
|
17721
|
+
inSingleQuote = !inSingleQuote;
|
|
17722
|
+
result += char;
|
|
17723
|
+
i++;
|
|
17724
|
+
continue;
|
|
17725
|
+
}
|
|
17726
|
+
if (!inSingleQuote && !inDollarQuote && char === '"') {
|
|
17727
|
+
if (inDoubleQuote && next === '"') {
|
|
17728
|
+
result += '""';
|
|
17729
|
+
i += 2;
|
|
17730
|
+
continue;
|
|
17731
|
+
}
|
|
17732
|
+
inDoubleQuote = !inDoubleQuote;
|
|
17733
|
+
result += char;
|
|
17734
|
+
i++;
|
|
17735
|
+
continue;
|
|
17736
|
+
}
|
|
17737
|
+
result += char;
|
|
17738
|
+
i++;
|
|
17739
|
+
}
|
|
17740
|
+
return result;
|
|
17741
|
+
}
|
|
17238
17742
|
function parseIndexColumns(rawColumns) {
|
|
17239
17743
|
return rawColumns.split(",").map((col) => {
|
|
17240
17744
|
const trimmed = col.trim();
|
|
@@ -17271,8 +17775,8 @@ function findTablesRegex(ctx) {
|
|
|
17271
17775
|
const tables = [];
|
|
17272
17776
|
const regex = new RegExp(SQL_PATTERNS.createTable.source, "gi");
|
|
17273
17777
|
for (const match of ctx.content.matchAll(regex)) {
|
|
17274
|
-
const schema = match[1] ?? "";
|
|
17275
|
-
const name = match[2] ?? "";
|
|
17778
|
+
const schema = unquoteIdentifier(match[1] ?? "");
|
|
17779
|
+
const name = unquoteIdentifier(match[2] ?? "");
|
|
17276
17780
|
if (!schema || !name) continue;
|
|
17277
17781
|
const lineNumber = getLineNumber(ctx.content, match.index ?? 0);
|
|
17278
17782
|
const tableBody = extractTableBody(ctx.content, match.index ?? 0);
|
|
@@ -17294,10 +17798,10 @@ function parseColumnsRegex(tableBody) {
|
|
|
17294
17798
|
const trimmed = line.trim();
|
|
17295
17799
|
if (shouldSkipColumnLine(trimmed)) continue;
|
|
17296
17800
|
const columnMatch = trimmed.match(
|
|
17297
|
-
/^(
|
|
17801
|
+
/^((?:"(?:[^"]|"")*"|[a-zA-Z_][a-zA-Z0-9_]*))\s+([\w\s()]+?)(?:\s+(NOT\s+NULL|NULL))?(?:\s+DEFAULT\s+[^,]+)?(?:\s+REFERENCES)?(?:,|$)/i
|
|
17298
17802
|
);
|
|
17299
17803
|
if (columnMatch) {
|
|
17300
|
-
const name = columnMatch[1] ?? "";
|
|
17804
|
+
const name = unquoteIdentifier(columnMatch[1] ?? "");
|
|
17301
17805
|
if (!name || seen.has(name) || isReservedKeyword(name)) continue;
|
|
17302
17806
|
seen.add(name);
|
|
17303
17807
|
const rawType = columnMatch[2] ?? "";
|
|
@@ -17319,7 +17823,7 @@ function parsePrimaryKeyRegex(tableBody) {
|
|
|
17319
17823
|
const regex = new RegExp(SQL_PATTERNS.primaryKey.source, "gi");
|
|
17320
17824
|
const match = regex.exec(tableBody);
|
|
17321
17825
|
if (match) {
|
|
17322
|
-
return match[1]?.split(",").map((col) => col.trim());
|
|
17826
|
+
return match[1]?.split(",").map((col) => unquoteIdentifier(col.trim())).filter(Boolean) ?? [];
|
|
17323
17827
|
}
|
|
17324
17828
|
return [];
|
|
17325
17829
|
}
|
|
@@ -17327,12 +17831,12 @@ function parseExplicitForeignKeys(tableBody) {
|
|
|
17327
17831
|
const fks = [];
|
|
17328
17832
|
const fkRegex = new RegExp(SQL_PATTERNS.foreignKey.source, "gi");
|
|
17329
17833
|
for (const match of tableBody.matchAll(fkRegex)) {
|
|
17330
|
-
const column = match[1] ?? "";
|
|
17331
|
-
const refTable = match[4] ?? "";
|
|
17834
|
+
const column = unquoteIdentifier(match[1] ?? "");
|
|
17835
|
+
const refTable = unquoteIdentifier(match[4] ?? "");
|
|
17332
17836
|
if (!column || !refTable) continue;
|
|
17333
17837
|
fks.push({
|
|
17334
17838
|
column,
|
|
17335
|
-
referencesTable: `${match[2] ?? ""}.${match[3] ?? ""}`,
|
|
17839
|
+
referencesTable: `${unquoteIdentifier(match[2] ?? "")}.${unquoteIdentifier(match[3] ?? "")}`,
|
|
17336
17840
|
referencesColumn: refTable,
|
|
17337
17841
|
onDelete: normalizeOnAction(match[5]),
|
|
17338
17842
|
onUpdate: normalizeOnAction(match[6])
|
|
@@ -17344,13 +17848,13 @@ function parseInlineForeignKeys(tableBody, existingColumns) {
|
|
|
17344
17848
|
const fks = [];
|
|
17345
17849
|
const inlineRegex = new RegExp(SQL_PATTERNS.inlineReference.source, "gi");
|
|
17346
17850
|
for (const match of tableBody.matchAll(inlineRegex)) {
|
|
17347
|
-
const inlineCol = match[1] ?? "";
|
|
17851
|
+
const inlineCol = unquoteIdentifier(match[1] ?? "");
|
|
17348
17852
|
if (existingColumns.has(inlineCol)) continue;
|
|
17349
|
-
const inlineRefCol = match[4] ?? "";
|
|
17853
|
+
const inlineRefCol = unquoteIdentifier(match[4] ?? "");
|
|
17350
17854
|
if (!inlineCol || !inlineRefCol) continue;
|
|
17351
17855
|
fks.push({
|
|
17352
17856
|
column: inlineCol,
|
|
17353
|
-
referencesTable: `${match[2] ?? ""}.${match[3] ?? ""}`,
|
|
17857
|
+
referencesTable: `${unquoteIdentifier(match[2] ?? "")}.${unquoteIdentifier(match[3] ?? "")}`,
|
|
17354
17858
|
referencesColumn: inlineRefCol
|
|
17355
17859
|
});
|
|
17356
17860
|
}
|
|
@@ -17366,8 +17870,10 @@ function parseIndexesRegex(content, schema, tableName) {
|
|
|
17366
17870
|
const indexes = [];
|
|
17367
17871
|
const regex = new RegExp(SQL_PATTERNS.createIndex.source, "gi");
|
|
17368
17872
|
for (const match of content.matchAll(regex)) {
|
|
17369
|
-
|
|
17370
|
-
|
|
17873
|
+
const indexSchema = unquoteIdentifier(match[3] ?? "");
|
|
17874
|
+
const indexTable = unquoteIdentifier(match[4] ?? "");
|
|
17875
|
+
if (indexSchema === schema && indexTable === tableName) {
|
|
17876
|
+
const indexName = unquoteIdentifier(match[2] ?? "");
|
|
17371
17877
|
if (!indexName) continue;
|
|
17372
17878
|
const rawColumns = match[5] ?? "";
|
|
17373
17879
|
indexes.push({
|
|
@@ -17382,26 +17888,200 @@ function parseIndexesRegex(content, schema, tableName) {
|
|
|
17382
17888
|
function hasRlsEnabledRegex(content, schema, tableName) {
|
|
17383
17889
|
const regex = new RegExp(SQL_PATTERNS.enableRls.source, "gi");
|
|
17384
17890
|
for (const match of content.matchAll(regex)) {
|
|
17385
|
-
|
|
17891
|
+
const matchSchema = unquoteIdentifier(match[1] ?? "");
|
|
17892
|
+
const matchTable = unquoteIdentifier(match[2] ?? "");
|
|
17893
|
+
if (matchSchema === schema && matchTable === tableName) {
|
|
17386
17894
|
return true;
|
|
17387
17895
|
}
|
|
17388
17896
|
}
|
|
17389
17897
|
return false;
|
|
17390
17898
|
}
|
|
17899
|
+
function extractCreatePolicyStatements(content) {
|
|
17900
|
+
const statements = [];
|
|
17901
|
+
const startRegex = /\bCREATE\s+POLICY\b/gi;
|
|
17902
|
+
let match;
|
|
17903
|
+
while ((match = startRegex.exec(content)) !== null) {
|
|
17904
|
+
const startIndex = match.index ?? 0;
|
|
17905
|
+
const endIndex = findSqlStatementEndForPolicy(content, startIndex);
|
|
17906
|
+
statements.push(content.slice(startIndex, endIndex).trim());
|
|
17907
|
+
}
|
|
17908
|
+
return statements;
|
|
17909
|
+
}
|
|
17910
|
+
function findSqlStatementEndForPolicy(content, startIndex) {
|
|
17911
|
+
let inSingleQuote = false;
|
|
17912
|
+
let inDoubleQuote = false;
|
|
17913
|
+
let inDollarQuote = false;
|
|
17914
|
+
let dollarTag = "";
|
|
17915
|
+
for (let i = startIndex; i < content.length; i++) {
|
|
17916
|
+
const char = content[i] ?? "";
|
|
17917
|
+
const next = content[i + 1] ?? "";
|
|
17918
|
+
if (!inSingleQuote && !inDoubleQuote && !inDollarQuote) {
|
|
17919
|
+
if (char === "-" && next === "-") {
|
|
17920
|
+
const newlineIndex = content.indexOf("\n", i);
|
|
17921
|
+
if (newlineIndex === -1) return content.length;
|
|
17922
|
+
i = newlineIndex;
|
|
17923
|
+
continue;
|
|
17924
|
+
}
|
|
17925
|
+
if (char === "/" && next === "*") {
|
|
17926
|
+
const closeIndex = content.indexOf("*/", i + 2);
|
|
17927
|
+
if (closeIndex === -1) return content.length;
|
|
17928
|
+
i = closeIndex + 1;
|
|
17929
|
+
continue;
|
|
17930
|
+
}
|
|
17931
|
+
}
|
|
17932
|
+
if (!inSingleQuote && !inDoubleQuote && char === "$") {
|
|
17933
|
+
if (inDollarQuote) {
|
|
17934
|
+
const closeTag = `$${dollarTag}$`;
|
|
17935
|
+
if (content.slice(i).startsWith(closeTag)) {
|
|
17936
|
+
i += closeTag.length - 1;
|
|
17937
|
+
inDollarQuote = false;
|
|
17938
|
+
dollarTag = "";
|
|
17939
|
+
continue;
|
|
17940
|
+
}
|
|
17941
|
+
} else {
|
|
17942
|
+
const tagMatch = content.slice(i).match(/^\$([a-zA-Z_][a-zA-Z0-9_]*)?\$/);
|
|
17943
|
+
if (tagMatch) {
|
|
17944
|
+
inDollarQuote = true;
|
|
17945
|
+
dollarTag = tagMatch[1] ?? "";
|
|
17946
|
+
i += tagMatch[0].length - 1;
|
|
17947
|
+
continue;
|
|
17948
|
+
}
|
|
17949
|
+
}
|
|
17950
|
+
}
|
|
17951
|
+
if (!inDoubleQuote && !inDollarQuote && char === "'") {
|
|
17952
|
+
if (inSingleQuote) {
|
|
17953
|
+
if (next === "'") {
|
|
17954
|
+
i++;
|
|
17955
|
+
continue;
|
|
17956
|
+
}
|
|
17957
|
+
inSingleQuote = false;
|
|
17958
|
+
} else {
|
|
17959
|
+
inSingleQuote = true;
|
|
17960
|
+
}
|
|
17961
|
+
continue;
|
|
17962
|
+
}
|
|
17963
|
+
if (!inSingleQuote && !inDollarQuote && char === '"') {
|
|
17964
|
+
if (inDoubleQuote) {
|
|
17965
|
+
if (next === '"') {
|
|
17966
|
+
i++;
|
|
17967
|
+
continue;
|
|
17968
|
+
}
|
|
17969
|
+
inDoubleQuote = false;
|
|
17970
|
+
} else {
|
|
17971
|
+
inDoubleQuote = true;
|
|
17972
|
+
}
|
|
17973
|
+
continue;
|
|
17974
|
+
}
|
|
17975
|
+
if (!inSingleQuote && !inDoubleQuote && !inDollarQuote && char === ";") {
|
|
17976
|
+
return i + 1;
|
|
17977
|
+
}
|
|
17978
|
+
}
|
|
17979
|
+
return content.length;
|
|
17980
|
+
}
|
|
17981
|
+
function extractBalancedClause(statement, startIndex) {
|
|
17982
|
+
const openParenIndex = statement.indexOf("(", startIndex);
|
|
17983
|
+
if (openParenIndex === -1) return void 0;
|
|
17984
|
+
let depth = 0;
|
|
17985
|
+
let clauseStart = -1;
|
|
17986
|
+
let inSingleQuote = false;
|
|
17987
|
+
let inDoubleQuote = false;
|
|
17988
|
+
let inDollarQuote = false;
|
|
17989
|
+
let dollarTag = "";
|
|
17990
|
+
for (let i = openParenIndex; i < statement.length; i++) {
|
|
17991
|
+
const char = statement[i] ?? "";
|
|
17992
|
+
const next = statement[i + 1] ?? "";
|
|
17993
|
+
if (!inSingleQuote && !inDoubleQuote && !inDollarQuote) {
|
|
17994
|
+
if (char === "-" && next === "-") {
|
|
17995
|
+
const newlineIndex = statement.indexOf("\n", i);
|
|
17996
|
+
if (newlineIndex === -1) break;
|
|
17997
|
+
i = newlineIndex;
|
|
17998
|
+
continue;
|
|
17999
|
+
}
|
|
18000
|
+
if (char === "/" && next === "*") {
|
|
18001
|
+
const closeIndex = statement.indexOf("*/", i + 2);
|
|
18002
|
+
if (closeIndex === -1) break;
|
|
18003
|
+
i = closeIndex + 1;
|
|
18004
|
+
continue;
|
|
18005
|
+
}
|
|
18006
|
+
}
|
|
18007
|
+
if (!inSingleQuote && !inDoubleQuote && char === "$") {
|
|
18008
|
+
if (inDollarQuote) {
|
|
18009
|
+
const closeTag = `$${dollarTag}$`;
|
|
18010
|
+
if (statement.slice(i).startsWith(closeTag)) {
|
|
18011
|
+
i += closeTag.length - 1;
|
|
18012
|
+
inDollarQuote = false;
|
|
18013
|
+
dollarTag = "";
|
|
18014
|
+
continue;
|
|
18015
|
+
}
|
|
18016
|
+
} else {
|
|
18017
|
+
const tagMatch = statement.slice(i).match(/^\$([a-zA-Z_][a-zA-Z0-9_]*)?\$/);
|
|
18018
|
+
if (tagMatch) {
|
|
18019
|
+
inDollarQuote = true;
|
|
18020
|
+
dollarTag = tagMatch[1] ?? "";
|
|
18021
|
+
i += tagMatch[0].length - 1;
|
|
18022
|
+
continue;
|
|
18023
|
+
}
|
|
18024
|
+
}
|
|
18025
|
+
}
|
|
18026
|
+
if (!inDoubleQuote && !inDollarQuote && char === "'") {
|
|
18027
|
+
if (inSingleQuote) {
|
|
18028
|
+
if (next === "'") {
|
|
18029
|
+
i++;
|
|
18030
|
+
continue;
|
|
18031
|
+
}
|
|
18032
|
+
inSingleQuote = false;
|
|
18033
|
+
} else {
|
|
18034
|
+
inSingleQuote = true;
|
|
18035
|
+
}
|
|
18036
|
+
continue;
|
|
18037
|
+
}
|
|
18038
|
+
if (!inSingleQuote && !inDollarQuote && char === '"') {
|
|
18039
|
+
if (inDoubleQuote) {
|
|
18040
|
+
if (next === '"') {
|
|
18041
|
+
i++;
|
|
18042
|
+
continue;
|
|
18043
|
+
}
|
|
18044
|
+
inDoubleQuote = false;
|
|
18045
|
+
} else {
|
|
18046
|
+
inDoubleQuote = true;
|
|
18047
|
+
}
|
|
18048
|
+
continue;
|
|
18049
|
+
}
|
|
18050
|
+
if (inSingleQuote || inDoubleQuote || inDollarQuote) continue;
|
|
18051
|
+
if (char === "(") {
|
|
18052
|
+
if (depth === 0) clauseStart = i + 1;
|
|
18053
|
+
depth++;
|
|
18054
|
+
} else if (char === ")") {
|
|
18055
|
+
depth--;
|
|
18056
|
+
if (depth === 0 && clauseStart !== -1) {
|
|
18057
|
+
return statement.slice(clauseStart, i).trim();
|
|
18058
|
+
}
|
|
18059
|
+
}
|
|
18060
|
+
}
|
|
18061
|
+
return void 0;
|
|
18062
|
+
}
|
|
17391
18063
|
function parsePoliciesRegex(content, schema, tableName) {
|
|
17392
18064
|
const policies = [];
|
|
17393
|
-
const
|
|
17394
|
-
|
|
17395
|
-
|
|
17396
|
-
|
|
17397
|
-
|
|
17398
|
-
|
|
17399
|
-
|
|
17400
|
-
|
|
17401
|
-
|
|
17402
|
-
|
|
17403
|
-
|
|
17404
|
-
|
|
18065
|
+
const statements = extractCreatePolicyStatements(content);
|
|
18066
|
+
const headerRegex = new RegExp(
|
|
18067
|
+
`^\\s*CREATE\\s+POLICY\\s+(?:"((?:[^"]|"")*)"|([a-zA-Z_][a-zA-Z0-9_]*))\\s+ON\\s+(${SQL_IDENTIFIER})\\.(${SQL_IDENTIFIER})(?:\\s+AS\\s+\\w+)?(?:\\s+FOR\\s+(\\w+))?`,
|
|
18068
|
+
"i"
|
|
18069
|
+
);
|
|
18070
|
+
for (const statement of statements) {
|
|
18071
|
+
const match = statement.match(headerRegex);
|
|
18072
|
+
if (!match) continue;
|
|
18073
|
+
const policyName = unquoteIdentifier(match[1] ?? match[2] ?? "");
|
|
18074
|
+
const policySchema = unquoteIdentifier(match[3] ?? "");
|
|
18075
|
+
const policyTable = unquoteIdentifier(match[4] ?? "");
|
|
18076
|
+
if (!policyName || policySchema !== schema || policyTable !== tableName) continue;
|
|
18077
|
+
const usingIndex = statement.search(/\bUSING\s*\(/i);
|
|
18078
|
+
const withCheckIndex = statement.search(/\bWITH\s+CHECK\s*\(/i);
|
|
18079
|
+
policies.push({
|
|
18080
|
+
name: policyName,
|
|
18081
|
+
command: (match[5] || "ALL").toUpperCase(),
|
|
18082
|
+
using: usingIndex !== -1 ? extractBalancedClause(statement, usingIndex) : void 0,
|
|
18083
|
+
withCheck: withCheckIndex !== -1 ? extractBalancedClause(statement, withCheckIndex) : void 0
|
|
18084
|
+
});
|
|
17405
18085
|
}
|
|
17406
18086
|
return policies;
|
|
17407
18087
|
}
|
|
@@ -17502,7 +18182,8 @@ function buildTableEntryRegex(table, content, filePath, opts) {
|
|
|
17502
18182
|
};
|
|
17503
18183
|
}
|
|
17504
18184
|
function processTablesFromFileRegex(filePath, opts, seen) {
|
|
17505
|
-
const
|
|
18185
|
+
const rawContent = readFileSync(filePath, "utf-8");
|
|
18186
|
+
const content = stripSqlCommentsPreserveStrings2(rawContent);
|
|
17506
18187
|
const ctx = { content, lines: content.split("\n") };
|
|
17507
18188
|
const tables = findTablesRegex(ctx);
|
|
17508
18189
|
const entries = [];
|
|
@@ -18329,6 +19010,60 @@ function handleHazards(hazards, input3) {
|
|
|
18329
19010
|
);
|
|
18330
19011
|
}
|
|
18331
19012
|
}
|
|
19013
|
+
var TYPE_TO_EXTENSION = {
|
|
19014
|
+
geometry: "postgis",
|
|
19015
|
+
geography: "postgis",
|
|
19016
|
+
box2d: "postgis",
|
|
19017
|
+
box3d: "postgis",
|
|
19018
|
+
raster: "postgis_raster",
|
|
19019
|
+
vector: "vector",
|
|
19020
|
+
halfvec: "vector",
|
|
19021
|
+
sparsevec: "vector",
|
|
19022
|
+
cube: "cube",
|
|
19023
|
+
hstore: "hstore",
|
|
19024
|
+
ltree: "ltree",
|
|
19025
|
+
citext: "citext"
|
|
19026
|
+
};
|
|
19027
|
+
function detectMissingExtensionType(errorOutput) {
|
|
19028
|
+
const typePattern = /type ["']?(\w+)["']? does not exist/gi;
|
|
19029
|
+
const matches = [...errorOutput.matchAll(typePattern)];
|
|
19030
|
+
const missingTypes = [...new Set(matches.map((m) => m[1].toLowerCase()))];
|
|
19031
|
+
if (missingTypes.length === 0) {
|
|
19032
|
+
return { detected: false, missingTypes: [], suggestedExtensions: [] };
|
|
19033
|
+
}
|
|
19034
|
+
const extensions = /* @__PURE__ */ new Set();
|
|
19035
|
+
for (const type of missingTypes) {
|
|
19036
|
+
const ext = TYPE_TO_EXTENSION[type];
|
|
19037
|
+
if (ext) extensions.add(ext);
|
|
19038
|
+
}
|
|
19039
|
+
return {
|
|
19040
|
+
detected: true,
|
|
19041
|
+
missingTypes,
|
|
19042
|
+
suggestedExtensions: [...extensions]
|
|
19043
|
+
};
|
|
19044
|
+
}
|
|
19045
|
+
function formatExtensionErrorHint(detection) {
|
|
19046
|
+
if (!detection.detected) return "";
|
|
19047
|
+
const lines = [];
|
|
19048
|
+
lines.push("");
|
|
19049
|
+
lines.push("Missing extension type(s) detected:");
|
|
19050
|
+
for (const type of detection.missingTypes) {
|
|
19051
|
+
const ext = TYPE_TO_EXTENSION[type];
|
|
19052
|
+
lines.push(` type "${type}" \u2192 extension: ${ext ?? "unknown"}`);
|
|
19053
|
+
}
|
|
19054
|
+
if (detection.suggestedExtensions.length > 0) {
|
|
19055
|
+
lines.push("");
|
|
19056
|
+
lines.push("Fix 1: Add to supabase/schemas/idempotent/00_extensions.sql:");
|
|
19057
|
+
for (const ext of detection.suggestedExtensions) {
|
|
19058
|
+
lines.push(` CREATE EXTENSION IF NOT EXISTS ${ext};`);
|
|
19059
|
+
}
|
|
19060
|
+
const extList = detection.suggestedExtensions.map((e) => `'${e}'`).join(", ");
|
|
19061
|
+
lines.push("");
|
|
19062
|
+
lines.push("Fix 2: Configure shadow DB in runa.config.ts:");
|
|
19063
|
+
lines.push(` database: { pgSchemaDiff: { shadowDbExtensions: [${extList}] } }`);
|
|
19064
|
+
}
|
|
19065
|
+
return lines.join("\n");
|
|
19066
|
+
}
|
|
18332
19067
|
function executePgSchemaDiffPlan(dbUrl, schemasDir, includeSchemas, verbose, options) {
|
|
18333
19068
|
const planArgs = [
|
|
18334
19069
|
"plan",
|
|
@@ -18364,6 +19099,10 @@ function executePgSchemaDiffPlan(dbUrl, schemasDir, includeSchemas, verbose, opt
|
|
|
18364
19099
|
logger4.error(`Exit code: ${planResult.status}`);
|
|
18365
19100
|
logger4.error(`Signal: ${planResult.signal || "none"}`);
|
|
18366
19101
|
logger4.error(`Stderr: ${planStderr || "(empty)"}`);
|
|
19102
|
+
const extDetection = detectMissingExtensionType(planStderr || planOutput);
|
|
19103
|
+
if (extDetection.detected) {
|
|
19104
|
+
logger4.error(formatExtensionErrorHint(extDetection));
|
|
19105
|
+
}
|
|
18367
19106
|
throw new Error(
|
|
18368
19107
|
`pg-schema-diff plan failed (exit ${planResult.status}): ${planStderr || "(no output)"}`
|
|
18369
19108
|
);
|
|
@@ -18421,6 +19160,10 @@ async function executeApplyWithRetry(applyArgs, verbose, config) {
|
|
|
18421
19160
|
}
|
|
18422
19161
|
logger5.error("pg-schema-diff apply failed:");
|
|
18423
19162
|
logger5.error(errorOutput);
|
|
19163
|
+
const extDetection = detectMissingExtensionType(errorOutput);
|
|
19164
|
+
if (extDetection.detected) {
|
|
19165
|
+
logger5.error(formatExtensionErrorHint(extDetection));
|
|
19166
|
+
}
|
|
18424
19167
|
return {
|
|
18425
19168
|
success: false,
|
|
18426
19169
|
error: new Error(`pg-schema-diff apply failed: ${errorOutput}`),
|
|
@@ -19887,16 +20630,16 @@ var ERROR_MESSAGES2 = {
|
|
|
19887
20630
|
SCHEMA_NOT_FOUND: "Schema file not found"
|
|
19888
20631
|
};
|
|
19889
20632
|
function containsPathTraversal2(inputPath) {
|
|
19890
|
-
const normalized =
|
|
20633
|
+
const normalized = path11__default.normalize(inputPath);
|
|
19891
20634
|
return normalized.includes("..") || inputPath.includes("\0");
|
|
19892
20635
|
}
|
|
19893
20636
|
function isPathWithinBase(filePath, baseDir) {
|
|
19894
20637
|
try {
|
|
19895
|
-
const resolvedFile =
|
|
19896
|
-
const resolvedBase =
|
|
19897
|
-
const normalizedFile =
|
|
19898
|
-
const normalizedBase =
|
|
19899
|
-
return normalizedFile === normalizedBase || normalizedFile.startsWith(normalizedBase +
|
|
20638
|
+
const resolvedFile = path11__default.resolve(filePath);
|
|
20639
|
+
const resolvedBase = path11__default.resolve(baseDir);
|
|
20640
|
+
const normalizedFile = path11__default.normalize(resolvedFile);
|
|
20641
|
+
const normalizedBase = path11__default.normalize(resolvedBase);
|
|
20642
|
+
return normalizedFile === normalizedBase || normalizedFile.startsWith(normalizedBase + path11__default.sep);
|
|
19900
20643
|
} catch {
|
|
19901
20644
|
return false;
|
|
19902
20645
|
}
|
|
@@ -19905,13 +20648,13 @@ function validateSchemaPath(dbPackagePath, projectRoot = process.cwd()) {
|
|
|
19905
20648
|
if (containsPathTraversal2(dbPackagePath)) {
|
|
19906
20649
|
throw new Error(ERROR_MESSAGES2.PATH_TRAVERSAL);
|
|
19907
20650
|
}
|
|
19908
|
-
const schemaEntry =
|
|
19909
|
-
const absoluteSchemaPath =
|
|
20651
|
+
const schemaEntry = path11__default.join(dbPackagePath, "src", "schema", "index.ts");
|
|
20652
|
+
const absoluteSchemaPath = path11__default.resolve(projectRoot, schemaEntry);
|
|
19910
20653
|
let resolvedProjectRoot;
|
|
19911
20654
|
try {
|
|
19912
20655
|
resolvedProjectRoot = realpathSync(projectRoot);
|
|
19913
20656
|
} catch {
|
|
19914
|
-
resolvedProjectRoot =
|
|
20657
|
+
resolvedProjectRoot = path11__default.resolve(projectRoot);
|
|
19915
20658
|
}
|
|
19916
20659
|
if (!isPathWithinBase(absoluteSchemaPath, resolvedProjectRoot)) {
|
|
19917
20660
|
throw new Error(ERROR_MESSAGES2.PATH_TRAVERSAL);
|
|
@@ -20043,7 +20786,7 @@ function diffSchema(params) {
|
|
|
20043
20786
|
};
|
|
20044
20787
|
}
|
|
20045
20788
|
function extractTablesFromIdempotentSql(idempotentDir, projectRoot = process.cwd()) {
|
|
20046
|
-
const fullPath =
|
|
20789
|
+
const fullPath = path11__default.resolve(projectRoot, idempotentDir);
|
|
20047
20790
|
if (!existsSync(fullPath)) {
|
|
20048
20791
|
return [];
|
|
20049
20792
|
}
|
|
@@ -20052,7 +20795,7 @@ function extractTablesFromIdempotentSql(idempotentDir, projectRoot = process.cwd
|
|
|
20052
20795
|
try {
|
|
20053
20796
|
const files = readdirSync(fullPath).filter((f) => f.endsWith(".sql"));
|
|
20054
20797
|
for (const file of files) {
|
|
20055
|
-
const filePath =
|
|
20798
|
+
const filePath = path11__default.join(fullPath, file);
|
|
20056
20799
|
const content = readFileSync(filePath, "utf-8");
|
|
20057
20800
|
const contentWithoutComments = content.replace(/--.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
20058
20801
|
for (const match of contentWithoutComments.matchAll(createTablePattern)) {
|
|
@@ -20275,7 +21018,7 @@ async function validateGitHubOutputPath(filePath) {
|
|
|
20275
21018
|
if (!filePath || filePath.trim().length === 0) {
|
|
20276
21019
|
return { valid: false, resolvedPath: null, error: "Empty file path" };
|
|
20277
21020
|
}
|
|
20278
|
-
const normalizedPath =
|
|
21021
|
+
const normalizedPath = path11__default.normalize(filePath);
|
|
20279
21022
|
if (normalizedPath.includes("..")) {
|
|
20280
21023
|
return {
|
|
20281
21024
|
valid: false,
|
|
@@ -20283,7 +21026,7 @@ async function validateGitHubOutputPath(filePath) {
|
|
|
20283
21026
|
error: "Path traversal detected (..) in file path"
|
|
20284
21027
|
};
|
|
20285
21028
|
}
|
|
20286
|
-
const absolutePath =
|
|
21029
|
+
const absolutePath = path11__default.resolve(normalizedPath);
|
|
20287
21030
|
for (const forbidden of FORBIDDEN_PATHS) {
|
|
20288
21031
|
if (absolutePath.startsWith(forbidden) || absolutePath.includes(forbidden)) {
|
|
20289
21032
|
return {
|
|
@@ -20637,64 +21380,6 @@ var diffVisualCommand = new Command("diff-visual").description("Visualize schema
|
|
|
20637
21380
|
// src/commands/db/commands/db-drizzle.ts
|
|
20638
21381
|
init_esm_shims();
|
|
20639
21382
|
|
|
20640
|
-
// src/internal/machines/index.ts
|
|
20641
|
-
init_esm_shims();
|
|
20642
|
-
|
|
20643
|
-
// src/internal/machines/machine-runner.ts
|
|
20644
|
-
init_esm_shims();
|
|
20645
|
-
function getOutputOrThrow(snapshot2) {
|
|
20646
|
-
const s = snapshot2;
|
|
20647
|
-
if (s.output === void 0) {
|
|
20648
|
-
throw new CLIError("Machine completed without output", "MACHINE_NO_OUTPUT", [
|
|
20649
|
-
"Ensure the machine defines an `output:` function."
|
|
20650
|
-
]);
|
|
20651
|
-
}
|
|
20652
|
-
return s.output;
|
|
20653
|
-
}
|
|
20654
|
-
async function runMachine(params) {
|
|
20655
|
-
const timeoutMs = params.timeoutMs ?? 10 * 60 * 1e3;
|
|
20656
|
-
return new Promise((resolve12, reject) => {
|
|
20657
|
-
const actor = createActor(params.machine, { input: params.input });
|
|
20658
|
-
const timer = setTimeout(() => {
|
|
20659
|
-
try {
|
|
20660
|
-
actor.stop?.();
|
|
20661
|
-
} catch {
|
|
20662
|
-
}
|
|
20663
|
-
reject(
|
|
20664
|
-
new CLIError("Machine execution timed out", "MACHINE_TIMEOUT", [
|
|
20665
|
-
`Timeout: ${timeoutMs}ms`,
|
|
20666
|
-
"Consider increasing timeoutMs for long-running operations."
|
|
20667
|
-
])
|
|
20668
|
-
);
|
|
20669
|
-
}, timeoutMs);
|
|
20670
|
-
const sub = actor.subscribe((snapshot2) => {
|
|
20671
|
-
try {
|
|
20672
|
-
params.onSnapshot?.(snapshot2);
|
|
20673
|
-
const stateName = params.helpers.getStateName(snapshot2);
|
|
20674
|
-
params.onTransition?.(stateName);
|
|
20675
|
-
if (params.helpers.isComplete(snapshot2)) {
|
|
20676
|
-
clearTimeout(timer);
|
|
20677
|
-
sub.unsubscribe();
|
|
20678
|
-
const out = getOutputOrThrow(snapshot2);
|
|
20679
|
-
resolve12(out);
|
|
20680
|
-
}
|
|
20681
|
-
} catch (err) {
|
|
20682
|
-
clearTimeout(timer);
|
|
20683
|
-
sub.unsubscribe();
|
|
20684
|
-
reject(err);
|
|
20685
|
-
}
|
|
20686
|
-
});
|
|
20687
|
-
try {
|
|
20688
|
-
actor.start();
|
|
20689
|
-
actor.send({ type: "START" });
|
|
20690
|
-
} catch (err) {
|
|
20691
|
-
clearTimeout(timer);
|
|
20692
|
-
sub.unsubscribe();
|
|
20693
|
-
reject(err);
|
|
20694
|
-
}
|
|
20695
|
-
});
|
|
20696
|
-
}
|
|
20697
|
-
|
|
20698
21383
|
// src/utils/execution-plan.ts
|
|
20699
21384
|
init_esm_shims();
|
|
20700
21385
|
function getChangeIcon(type) {
|
|
@@ -20762,7 +21447,7 @@ async function analyzeSchemaChanges() {
|
|
|
20762
21447
|
try {
|
|
20763
21448
|
const { getDatabasePackagePath: getDatabasePackagePath2 } = await Promise.resolve().then(() => (init_config_loader(), config_loader_exports));
|
|
20764
21449
|
const dbPath = await getDatabasePackagePath2();
|
|
20765
|
-
const schemaPath =
|
|
21450
|
+
const schemaPath = path11__default.join(dbPath, "src/schema/");
|
|
20766
21451
|
const { stdout } = await execa("git", ["diff", "--cached", "--", schemaPath]);
|
|
20767
21452
|
const lines = stdout.split("\n");
|
|
20768
21453
|
return parseDiffLines(lines);
|
|
@@ -20965,6 +21650,10 @@ var RISK_PATTERNS = [
|
|
|
20965
21650
|
function normalizeContent(content) {
|
|
20966
21651
|
return content.replace(/--[^\n]*/g, " ").replace(/\/\*[\s\S]*?\*\//g, " ").replace(/\s+/g, " ").trim();
|
|
20967
21652
|
}
|
|
21653
|
+
function stripSqlCommentsPreserveLines(content) {
|
|
21654
|
+
const lineMasked = content.replace(/--[^\n]*/g, (match) => " ".repeat(match.length));
|
|
21655
|
+
return lineMasked.replace(/\/\*[\s\S]*?\*\//g, (match) => match.replace(/[^\n]/g, " "));
|
|
21656
|
+
}
|
|
20968
21657
|
function maskWrapperFunctions(content) {
|
|
20969
21658
|
const wrapperPattern = /CREATE\s+(?:OR\s+REPLACE\s+)?FUNCTION\s+(?:public\.)?runa_auth_\w+\s*\([^)]*\)[^$]*(\$[a-zA-Z_]*\$)[\s\S]*?\1;?/gi;
|
|
20970
21659
|
return content.replace(wrapperPattern, "/* WRAPPER_FUNCTION_MASKED */");
|
|
@@ -20979,6 +21668,9 @@ function findLineNumber(originalContent, matchText) {
|
|
|
20979
21668
|
const beforeMatch = originalContent.substring(0, match.index);
|
|
20980
21669
|
return beforeMatch.split("\n").length;
|
|
20981
21670
|
}
|
|
21671
|
+
function lineNumberFromIndex(content, index) {
|
|
21672
|
+
return content.substring(0, Math.max(0, index)).split("\n").length;
|
|
21673
|
+
}
|
|
20982
21674
|
function detectRisksFromContent(normalizedContent, originalContent) {
|
|
20983
21675
|
const risks = [];
|
|
20984
21676
|
const seenDescriptions = /* @__PURE__ */ new Set();
|
|
@@ -21001,18 +21693,18 @@ function detectRisksFromContent(normalizedContent, originalContent) {
|
|
|
21001
21693
|
}
|
|
21002
21694
|
return risks;
|
|
21003
21695
|
}
|
|
21004
|
-
function detectForeignKeyRisks(
|
|
21696
|
+
function detectForeignKeyRisks(originalContent) {
|
|
21005
21697
|
const risks = [];
|
|
21006
|
-
const fkPattern = /REFERENCES\s+[\w."]+\s*\(/
|
|
21007
|
-
|
|
21008
|
-
|
|
21009
|
-
|
|
21010
|
-
|
|
21011
|
-
|
|
21012
|
-
|
|
21013
|
-
|
|
21014
|
-
|
|
21015
|
-
}
|
|
21698
|
+
const fkPattern = /REFERENCES\s+[\w."]+\s*\(/gi;
|
|
21699
|
+
const contentWithoutComments = stripSqlCommentsPreserveLines(originalContent);
|
|
21700
|
+
let match;
|
|
21701
|
+
while ((match = fkPattern.exec(contentWithoutComments)) !== null) {
|
|
21702
|
+
risks.push({
|
|
21703
|
+
level: "low",
|
|
21704
|
+
description: "Foreign key columns should have indexes",
|
|
21705
|
+
mitigation: "Add index on foreign key column for better performance",
|
|
21706
|
+
line: lineNumberFromIndex(originalContent, match.index ?? 0)
|
|
21707
|
+
});
|
|
21016
21708
|
}
|
|
21017
21709
|
return risks;
|
|
21018
21710
|
}
|
|
@@ -21024,9 +21716,8 @@ async function detectSchemaRisks(filePath) {
|
|
|
21024
21716
|
const content = await readFile(filePath, "utf-8");
|
|
21025
21717
|
const maskedContent = maskWrapperFunctions(content);
|
|
21026
21718
|
const normalizedContent = normalizeContent(maskedContent);
|
|
21027
|
-
const lines = content.split("\n");
|
|
21028
21719
|
const contentRisks = detectRisksFromContent(normalizedContent, content);
|
|
21029
|
-
const fkRisks = detectForeignKeyRisks(
|
|
21720
|
+
const fkRisks = detectForeignKeyRisks(content);
|
|
21030
21721
|
return [...contentRisks, ...fkRisks];
|
|
21031
21722
|
} catch (_error) {
|
|
21032
21723
|
return [];
|
|
@@ -21041,7 +21732,7 @@ function categorizeRisks(risks) {
|
|
|
21041
21732
|
}
|
|
21042
21733
|
|
|
21043
21734
|
// src/commands/db/utils/preflight-check.ts
|
|
21044
|
-
|
|
21735
|
+
init_local_supabase();
|
|
21045
21736
|
init_config_loader();
|
|
21046
21737
|
function createInitialResult() {
|
|
21047
21738
|
return {
|
|
@@ -21095,7 +21786,7 @@ function runDatabasePackageChecks(result, logger16, step) {
|
|
|
21095
21786
|
logger16.error("Database package path is missing");
|
|
21096
21787
|
return null;
|
|
21097
21788
|
}
|
|
21098
|
-
logger16.success(`Found database package: ${
|
|
21789
|
+
logger16.success(`Found database package: ${path11__default.basename(dbPackagePath)}`);
|
|
21099
21790
|
logger16.step("Checking schema files", step.next());
|
|
21100
21791
|
const schemaCheck = checkSchemaFiles(dbPackagePath);
|
|
21101
21792
|
if (!schemaCheck.exists) {
|
|
@@ -21219,9 +21910,9 @@ ${stderr}`.toLowerCase();
|
|
|
21219
21910
|
}
|
|
21220
21911
|
}
|
|
21221
21912
|
async function testDatabaseConnection(projectRoot) {
|
|
21222
|
-
const
|
|
21223
|
-
const
|
|
21224
|
-
const
|
|
21913
|
+
const resolvedRoot = process.cwd();
|
|
21914
|
+
const connectionUrl = buildLocalDatabaseUrl(resolvedRoot);
|
|
21915
|
+
const dbPort = detectLocalSupabasePorts(resolvedRoot).db;
|
|
21225
21916
|
let sql = null;
|
|
21226
21917
|
try {
|
|
21227
21918
|
sql = postgres(connectionUrl, {
|
|
@@ -21243,12 +21934,12 @@ async function testDatabaseConnection(projectRoot) {
|
|
|
21243
21934
|
function checkDatabasePackage() {
|
|
21244
21935
|
const cwd = process.cwd();
|
|
21245
21936
|
const locations = [
|
|
21246
|
-
|
|
21247
|
-
|
|
21248
|
-
|
|
21937
|
+
path11__default.join(cwd, "packages", "database"),
|
|
21938
|
+
path11__default.join(cwd, "packages", "db"),
|
|
21939
|
+
path11__default.join(cwd, "db")
|
|
21249
21940
|
];
|
|
21250
21941
|
for (const location of locations) {
|
|
21251
|
-
const configPath =
|
|
21942
|
+
const configPath = path11__default.join(location, "drizzle.config.ts");
|
|
21252
21943
|
if (existsSync(configPath)) {
|
|
21253
21944
|
return { exists: true, path: location };
|
|
21254
21945
|
}
|
|
@@ -21293,7 +21984,7 @@ function countTsFilesRecursive(dir) {
|
|
|
21293
21984
|
try {
|
|
21294
21985
|
const entries = readdirSync(dir, { withFileTypes: true });
|
|
21295
21986
|
for (const entry of entries) {
|
|
21296
|
-
const fullPath =
|
|
21987
|
+
const fullPath = path11__default.join(dir, entry.name);
|
|
21297
21988
|
if (entry.isDirectory()) {
|
|
21298
21989
|
count += countTsFilesRecursive(fullPath);
|
|
21299
21990
|
} else if (entry.isFile() && entry.name.endsWith(".ts")) {
|
|
@@ -21305,7 +21996,7 @@ function countTsFilesRecursive(dir) {
|
|
|
21305
21996
|
return count;
|
|
21306
21997
|
}
|
|
21307
21998
|
function checkSchemaFiles(dbPackagePath) {
|
|
21308
|
-
const schemaDir =
|
|
21999
|
+
const schemaDir = path11__default.join(dbPackagePath, "src", "schema");
|
|
21309
22000
|
if (!existsSync(schemaDir)) {
|
|
21310
22001
|
return { exists: false, count: 0 };
|
|
21311
22002
|
}
|
|
@@ -21342,9 +22033,9 @@ async function runOrphanCheck(env2, dbPackagePath, result, logger16, step) {
|
|
|
21342
22033
|
logger16.step("Checking for orphaned database objects", step.next());
|
|
21343
22034
|
try {
|
|
21344
22035
|
const { expectedTables, expectedEnums } = await extractSchemaTablesAndEnums(dbPackagePath);
|
|
21345
|
-
const databaseUrl = tryResolveDatabaseUrl("local") ||
|
|
22036
|
+
const databaseUrl = tryResolveDatabaseUrl("local") || buildLocalDatabaseUrl(process.cwd());
|
|
21346
22037
|
const { dbTables, dbEnums } = await fetchDbTablesAndEnums(databaseUrl, {
|
|
21347
|
-
schemaDir:
|
|
22038
|
+
schemaDir: path11__default.join(dbPackagePath, "src", "schema")
|
|
21348
22039
|
});
|
|
21349
22040
|
let excludeFromOrphanDetection = [];
|
|
21350
22041
|
let idempotentSqlDir = "supabase/schemas/idempotent";
|
|
@@ -21386,31 +22077,39 @@ function runExtensionConfigCheck(_result, logger16, step) {
|
|
|
21386
22077
|
logger16.step("Checking PostgreSQL extension configuration", step.next());
|
|
21387
22078
|
try {
|
|
21388
22079
|
const extensionResult = checkExtensionConfig(process.cwd());
|
|
21389
|
-
if (extensionResult
|
|
22080
|
+
if (!extensionResult || typeof extensionResult !== "object") {
|
|
22081
|
+
logger16.warn("Extension config check returned invalid result");
|
|
22082
|
+
return;
|
|
22083
|
+
}
|
|
22084
|
+
const usedExtensions = extensionResult.usedExtensions ?? [];
|
|
22085
|
+
if (usedExtensions.length === 0) {
|
|
21390
22086
|
logger16.success("No extensions detected in SQL files");
|
|
21391
22087
|
return;
|
|
21392
22088
|
}
|
|
22089
|
+
const sqlManaged = extensionResult.sqlManagedExtensions ?? [];
|
|
22090
|
+
const supabaseManaged = extensionResult.supabaseManagedExtensions ?? [];
|
|
21393
22091
|
const parts = [];
|
|
21394
|
-
if (
|
|
21395
|
-
parts.push(`${
|
|
22092
|
+
if (sqlManaged.length > 0) {
|
|
22093
|
+
parts.push(`${sqlManaged.length} SQL-managed`);
|
|
21396
22094
|
}
|
|
21397
|
-
if (
|
|
21398
|
-
parts.push(`${
|
|
22095
|
+
if (supabaseManaged.length > 0) {
|
|
22096
|
+
parts.push(`${supabaseManaged.length} Supabase-managed`);
|
|
21399
22097
|
}
|
|
21400
22098
|
const summary = parts.join(", ");
|
|
21401
|
-
logger16.success(`${
|
|
21402
|
-
|
|
22099
|
+
logger16.success(`${usedExtensions.length} extension(s) found (${summary})`);
|
|
22100
|
+
const notes = extensionResult.notes ?? [];
|
|
22101
|
+
for (const note of notes) {
|
|
21403
22102
|
logger16.info(` ${note}`);
|
|
21404
22103
|
}
|
|
21405
22104
|
} catch (error) {
|
|
21406
22105
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
21407
|
-
logger16.warn(`Extension config check
|
|
22106
|
+
logger16.warn(`Extension config check failed: ${message}`);
|
|
21408
22107
|
}
|
|
21409
22108
|
}
|
|
21410
22109
|
async function runSqlSchemaRiskCheck(result, logger16, step) {
|
|
21411
22110
|
logger16.step("Checking SQL schema for risky patterns", step.next());
|
|
21412
22111
|
const cwd = process.cwd();
|
|
21413
|
-
const sqlDir =
|
|
22112
|
+
const sqlDir = path11__default.join(cwd, "supabase", "schemas", "declarative");
|
|
21414
22113
|
if (!existsSync(sqlDir)) {
|
|
21415
22114
|
logger16.success("No SQL schema directory found (supabase/schemas/declarative/)");
|
|
21416
22115
|
return;
|
|
@@ -21423,7 +22122,7 @@ async function runSqlSchemaRiskCheck(result, logger16, step) {
|
|
|
21423
22122
|
}
|
|
21424
22123
|
const allRisks = [];
|
|
21425
22124
|
for (const sqlFile of sqlFiles) {
|
|
21426
|
-
const filePath =
|
|
22125
|
+
const filePath = path11__default.join(sqlDir, sqlFile);
|
|
21427
22126
|
const risks = await detectSchemaRisks(filePath);
|
|
21428
22127
|
for (const risk of risks) {
|
|
21429
22128
|
allRisks.push({ ...risk, file: sqlFile });
|
|
@@ -23175,7 +23874,7 @@ async function checkPort(port) {
|
|
|
23175
23874
|
};
|
|
23176
23875
|
}
|
|
23177
23876
|
function detectSupabasePortsFromConfig() {
|
|
23178
|
-
const configPath =
|
|
23877
|
+
const configPath = path11__default.join(process.cwd(), "supabase", "config.toml");
|
|
23179
23878
|
const BASE_PORTS = { api: 54321, db: 54322, studio: 54323, inbucket: 54324 };
|
|
23180
23879
|
if (!existsSync(configPath)) {
|
|
23181
23880
|
return Object.values(BASE_PORTS);
|
|
@@ -23264,7 +23963,7 @@ var BASE_PORT = 54321;
|
|
|
23264
23963
|
var PORTS_PER_SLOT = 10;
|
|
23265
23964
|
var TOTAL_SLOTS = 100;
|
|
23266
23965
|
function calculatePortOffset(projectPath) {
|
|
23267
|
-
const normalizedPath =
|
|
23966
|
+
const normalizedPath = path11__default.resolve(projectPath);
|
|
23268
23967
|
const hash = createHash("md5").update(normalizedPath).digest("hex");
|
|
23269
23968
|
return parseInt(hash.slice(0, 8), 16) % TOTAL_SLOTS;
|
|
23270
23969
|
}
|
|
@@ -23285,7 +23984,7 @@ function getSupabasePorts(projectPath) {
|
|
|
23285
23984
|
}
|
|
23286
23985
|
function updateSupabaseConfigPorts(projectPath) {
|
|
23287
23986
|
const ports = getSupabasePorts(projectPath);
|
|
23288
|
-
const configPath =
|
|
23987
|
+
const configPath = path11__default.join(projectPath, "supabase", "config.toml");
|
|
23289
23988
|
if (!existsSync(configPath)) {
|
|
23290
23989
|
return { updated: false, ports, configPath };
|
|
23291
23990
|
}
|
|
@@ -23320,7 +24019,7 @@ function getPortAllocationSummary(projectPath) {
|
|
|
23320
24019
|
const ports = getSupabasePorts(projectPath);
|
|
23321
24020
|
const offset = calculatePortOffset(projectPath);
|
|
23322
24021
|
return [
|
|
23323
|
-
`Port allocation for: ${
|
|
24022
|
+
`Port allocation for: ${path11__default.basename(projectPath)}`,
|
|
23324
24023
|
` Slot: ${offset} (hash-based)`,
|
|
23325
24024
|
` API: ${ports.api}`,
|
|
23326
24025
|
` DB: ${ports.db}`,
|
|
@@ -23354,7 +24053,7 @@ function parseSeedPaths(configPath) {
|
|
|
23354
24053
|
}
|
|
23355
24054
|
}
|
|
23356
24055
|
async function applySeedFile(seedPath, dbUrl) {
|
|
23357
|
-
const supabaseDir =
|
|
24056
|
+
const supabaseDir = path11__default.join(process.cwd(), "supabase");
|
|
23358
24057
|
const absolutePath = resolveSafePath(supabaseDir, seedPath);
|
|
23359
24058
|
if (!existsSync(absolutePath)) {
|
|
23360
24059
|
return;
|
|
@@ -23365,12 +24064,12 @@ async function applySeedFile(seedPath, dbUrl) {
|
|
|
23365
24064
|
});
|
|
23366
24065
|
}
|
|
23367
24066
|
async function applySeeds2(configPath) {
|
|
23368
|
-
const configFile =
|
|
24067
|
+
const configFile = path11__default.join(process.cwd(), "supabase", "config.toml");
|
|
23369
24068
|
const seedPaths = parseSeedPaths(configFile);
|
|
23370
24069
|
if (seedPaths.length === 0) {
|
|
23371
24070
|
return;
|
|
23372
24071
|
}
|
|
23373
|
-
const supabaseDir =
|
|
24072
|
+
const supabaseDir = path11__default.join(process.cwd(), "supabase");
|
|
23374
24073
|
const safePaths = filterSafePaths(seedPaths, supabaseDir);
|
|
23375
24074
|
if (safePaths.length === 0) {
|
|
23376
24075
|
return;
|
|
@@ -23788,7 +24487,7 @@ var validateCommand = new Command("validate").description("Validate schema files
|
|
|
23788
24487
|
const logger16 = createCLILogger("db:validate");
|
|
23789
24488
|
try {
|
|
23790
24489
|
logger16.section("Schema Validation");
|
|
23791
|
-
const schemasPath =
|
|
24490
|
+
const schemasPath = path11__default.join(process.cwd(), "packages", "database", "src", "schema");
|
|
23792
24491
|
if (!existsSync(schemasPath)) {
|
|
23793
24492
|
throw new CLIError("Schema directory not found", "SCHEMA_DIR_NOT_FOUND", [
|
|
23794
24493
|
`Expected location: ${schemasPath}`,
|
|
@@ -23953,8 +24652,8 @@ var generateCommand = new Command("generate").description("Generate TypeScript t
|
|
|
23953
24652
|
var listCommand = new Command("list").description("List managed schemas from drizzle.config.ts").option("--sql", "Output as SQL-compatible string for IN clauses").option("--json", "Output as JSON array").action(async (options) => {
|
|
23954
24653
|
const logger16 = createCLILogger("db:schema:list");
|
|
23955
24654
|
try {
|
|
23956
|
-
const dbPackagePath =
|
|
23957
|
-
if (!existsSync(
|
|
24655
|
+
const dbPackagePath = path11__default.join(process.cwd(), "packages", "database");
|
|
24656
|
+
if (!existsSync(path11__default.join(dbPackagePath, "drizzle.config.ts"))) {
|
|
23958
24657
|
throw new CLIError("drizzle.config.ts not found", "CONFIG_NOT_FOUND", [
|
|
23959
24658
|
`Expected location: ${dbPackagePath}/drizzle.config.ts`,
|
|
23960
24659
|
"Ensure you are in the project root",
|
|
@@ -25193,7 +25892,7 @@ var testGenCommand = new Command("test:gen").description("Generate pgTAP behavio
|
|
|
25193
25892
|
const databaseUrl = options.db || process.env.DATABASE_URL || getLocalDbUrl();
|
|
25194
25893
|
const schemas = options.schemas ? options.schemas.split(",") : void 0;
|
|
25195
25894
|
const dbPackage = databasePaths.package();
|
|
25196
|
-
const defaultOutputPath =
|
|
25895
|
+
const defaultOutputPath = path11__default.join(dbPackage, "tests/00_behavior.generated.test.sql");
|
|
25197
25896
|
const outputPath = options.output || defaultOutputPath;
|
|
25198
25897
|
spinner.text = "Generating pgTAP behavior tests...";
|
|
25199
25898
|
const result = await dbGeneratePgTapTests({
|
|
@@ -26205,108 +26904,8 @@ async function getVercelRootDirectory() {
|
|
|
26205
26904
|
return info?.rootDirectory ?? null;
|
|
26206
26905
|
}
|
|
26207
26906
|
|
|
26208
|
-
// src/commands/env/constants/local-supabase.ts
|
|
26209
|
-
init_esm_shims();
|
|
26210
|
-
var DEFAULT_API_PORT = 54321;
|
|
26211
|
-
var DEFAULT_DB_PORT = 54322;
|
|
26212
|
-
function parsePortFromToml(content, section, key) {
|
|
26213
|
-
const sectionRegex = new RegExp(`\\[${section}\\]([\\s\\S]*?)(?=\\n\\[|$)`, "g");
|
|
26214
|
-
const sectionMatch = sectionRegex.exec(content);
|
|
26215
|
-
if (!sectionMatch) return null;
|
|
26216
|
-
const sectionContent = sectionMatch[1];
|
|
26217
|
-
const keyRegex = new RegExp(`^\\s*${key}\\s*=\\s*(\\d+)`, "m");
|
|
26218
|
-
const keyMatch = keyRegex.exec(sectionContent);
|
|
26219
|
-
if (!keyMatch) return null;
|
|
26220
|
-
const port = Number.parseInt(keyMatch[1], 10);
|
|
26221
|
-
return Number.isFinite(port) && port > 0 && port < 65536 ? port : null;
|
|
26222
|
-
}
|
|
26223
|
-
function readPortsFromConfigToml(projectRoot) {
|
|
26224
|
-
const configPath = path10__default.join(projectRoot, "supabase", "config.toml");
|
|
26225
|
-
try {
|
|
26226
|
-
const content = readFileSync(configPath, "utf-8");
|
|
26227
|
-
const apiPort = parsePortFromToml(content, "api", "port");
|
|
26228
|
-
const dbPort = parsePortFromToml(content, "db", "port");
|
|
26229
|
-
if (apiPort !== null || dbPort !== null) {
|
|
26230
|
-
return {
|
|
26231
|
-
api: apiPort ?? DEFAULT_API_PORT,
|
|
26232
|
-
db: dbPort ?? DEFAULT_DB_PORT
|
|
26233
|
-
};
|
|
26234
|
-
}
|
|
26235
|
-
return null;
|
|
26236
|
-
} catch {
|
|
26237
|
-
return null;
|
|
26238
|
-
}
|
|
26239
|
-
}
|
|
26240
|
-
function detectLocalSupabasePorts(projectRoot = process.cwd()) {
|
|
26241
|
-
const configPorts = readPortsFromConfigToml(projectRoot);
|
|
26242
|
-
if (configPorts) {
|
|
26243
|
-
return configPorts;
|
|
26244
|
-
}
|
|
26245
|
-
return {
|
|
26246
|
-
api: DEFAULT_API_PORT,
|
|
26247
|
-
db: DEFAULT_DB_PORT
|
|
26248
|
-
};
|
|
26249
|
-
}
|
|
26250
|
-
var LOCAL_SUPABASE_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0";
|
|
26251
|
-
var LOCAL_SUPABASE_SERVICE_ROLE_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU";
|
|
26252
|
-
function buildLocalSupabaseUrl(projectRoot = process.cwd()) {
|
|
26253
|
-
const ports = detectLocalSupabasePorts(projectRoot);
|
|
26254
|
-
return `http://127.0.0.1:${ports.api}`;
|
|
26255
|
-
}
|
|
26256
|
-
function buildLocalDatabaseUrl(projectRoot = process.cwd()) {
|
|
26257
|
-
const ports = detectLocalSupabasePorts(projectRoot);
|
|
26258
|
-
return `postgresql://postgres:postgres@127.0.0.1:${ports.db}/postgres`;
|
|
26259
|
-
}
|
|
26260
|
-
function getLocalSupabaseEnvValues(projectRoot = process.cwd()) {
|
|
26261
|
-
const supabaseUrl = buildLocalSupabaseUrl(projectRoot);
|
|
26262
|
-
const databaseUrl = buildLocalDatabaseUrl(projectRoot);
|
|
26263
|
-
return {
|
|
26264
|
-
// Database URLs
|
|
26265
|
-
DATABASE_URL: databaseUrl,
|
|
26266
|
-
DATABASE_URL_ADMIN: databaseUrl,
|
|
26267
|
-
DATABASE_URL_SERVICE: databaseUrl,
|
|
26268
|
-
// Supabase URLs and keys
|
|
26269
|
-
NEXT_PUBLIC_SUPABASE_URL: supabaseUrl,
|
|
26270
|
-
NEXT_PUBLIC_SUPABASE_ANON_KEY: LOCAL_SUPABASE_ANON_KEY,
|
|
26271
|
-
SUPABASE_SERVICE_ROLE_KEY: LOCAL_SUPABASE_SERVICE_ROLE_KEY
|
|
26272
|
-
};
|
|
26273
|
-
}
|
|
26274
|
-
function getLocalValueDescriptions(projectRoot = process.cwd()) {
|
|
26275
|
-
const ports = detectLocalSupabasePorts(projectRoot);
|
|
26276
|
-
return {
|
|
26277
|
-
DATABASE_URL: `localhost:${ports.db} (PostgreSQL)`,
|
|
26278
|
-
DATABASE_URL_ADMIN: `localhost:${ports.db} (PostgreSQL)`,
|
|
26279
|
-
DATABASE_URL_SERVICE: `localhost:${ports.db} (PostgreSQL)`,
|
|
26280
|
-
NEXT_PUBLIC_SUPABASE_URL: `localhost:${ports.api} (Supabase API)`,
|
|
26281
|
-
NEXT_PUBLIC_SUPABASE_ANON_KEY: "local anon key (supabase-demo)",
|
|
26282
|
-
SUPABASE_SERVICE_ROLE_KEY: "local service key (supabase-demo)"
|
|
26283
|
-
};
|
|
26284
|
-
}
|
|
26285
|
-
var LOCAL_SUPABASE_ENV_VALUES = getLocalSupabaseEnvValues();
|
|
26286
|
-
getLocalValueDescriptions();
|
|
26287
|
-
function validateLocalhostValues(envValues) {
|
|
26288
|
-
const urlKeys = [
|
|
26289
|
-
"DATABASE_URL",
|
|
26290
|
-
"DATABASE_URL_ADMIN",
|
|
26291
|
-
"DATABASE_URL_SERVICE",
|
|
26292
|
-
"NEXT_PUBLIC_SUPABASE_URL"
|
|
26293
|
-
];
|
|
26294
|
-
const invalidEntries = [];
|
|
26295
|
-
for (const key of urlKeys) {
|
|
26296
|
-
const value = envValues[key];
|
|
26297
|
-
if (value && !value.includes("127.0.0.1") && !value.includes("localhost")) {
|
|
26298
|
-
invalidEntries.push(key);
|
|
26299
|
-
}
|
|
26300
|
-
}
|
|
26301
|
-
if (invalidEntries.length > 0) {
|
|
26302
|
-
throw new Error(
|
|
26303
|
-
`LOCAL_SUPABASE_ENV_VALUES safety check failed: The following keys do not point to localhost: ${invalidEntries.join(", ")}. This is a safety feature to prevent accidental production database connections during local development.`
|
|
26304
|
-
);
|
|
26305
|
-
}
|
|
26306
|
-
}
|
|
26307
|
-
validateLocalhostValues(LOCAL_SUPABASE_ENV_VALUES);
|
|
26308
|
-
|
|
26309
26907
|
// src/commands/env/commands/env-pull.ts
|
|
26908
|
+
init_local_supabase();
|
|
26310
26909
|
var ERROR_MESSAGES3 = {
|
|
26311
26910
|
INVALID_PATH: "Invalid working directory path",
|
|
26312
26911
|
PATH_TRAVERSAL: "Working directory path validation failed",
|
|
@@ -26338,16 +26937,16 @@ function sanitizeErrorMessage(message) {
|
|
|
26338
26937
|
return sanitized;
|
|
26339
26938
|
}
|
|
26340
26939
|
function containsPathTraversal3(inputPath) {
|
|
26341
|
-
const normalized =
|
|
26940
|
+
const normalized = path11__default.normalize(inputPath);
|
|
26342
26941
|
return normalized.includes("..") || inputPath.includes("\0");
|
|
26343
26942
|
}
|
|
26344
26943
|
function isPathWithinBase2(filePath, baseDir) {
|
|
26345
26944
|
try {
|
|
26346
|
-
const resolvedFile =
|
|
26347
|
-
const resolvedBase =
|
|
26348
|
-
const normalizedFile =
|
|
26349
|
-
const normalizedBase =
|
|
26350
|
-
return normalizedFile === normalizedBase || normalizedFile.startsWith(normalizedBase +
|
|
26945
|
+
const resolvedFile = path11__default.resolve(filePath);
|
|
26946
|
+
const resolvedBase = path11__default.resolve(baseDir);
|
|
26947
|
+
const normalizedFile = path11__default.normalize(resolvedFile);
|
|
26948
|
+
const normalizedBase = path11__default.normalize(resolvedBase);
|
|
26949
|
+
return normalizedFile === normalizedBase || normalizedFile.startsWith(normalizedBase + path11__default.sep);
|
|
26351
26950
|
} catch {
|
|
26352
26951
|
return false;
|
|
26353
26952
|
}
|
|
@@ -26356,12 +26955,12 @@ function validateCustomWorkingDir(cwdPath, projectRoot) {
|
|
|
26356
26955
|
if (containsPathTraversal3(cwdPath)) {
|
|
26357
26956
|
throw new CLIError(ERROR_MESSAGES3.PATH_TRAVERSAL, "ENV_PULL_PATH_TRAVERSAL");
|
|
26358
26957
|
}
|
|
26359
|
-
const absolutePath =
|
|
26958
|
+
const absolutePath = path11__default.isAbsolute(cwdPath) ? cwdPath : path11__default.resolve(projectRoot, cwdPath);
|
|
26360
26959
|
let resolvedProjectRoot;
|
|
26361
26960
|
try {
|
|
26362
26961
|
resolvedProjectRoot = realpathSync(projectRoot);
|
|
26363
26962
|
} catch {
|
|
26364
|
-
resolvedProjectRoot =
|
|
26963
|
+
resolvedProjectRoot = path11__default.resolve(projectRoot);
|
|
26365
26964
|
}
|
|
26366
26965
|
if (!isPathWithinBase2(absolutePath, resolvedProjectRoot)) {
|
|
26367
26966
|
throw new CLIError(ERROR_MESSAGES3.PATH_TRAVERSAL, "ENV_PULL_PATH_TRAVERSAL");
|
|
@@ -26388,6 +26987,27 @@ function validateAppDirectory2(appName, projectRoot) {
|
|
|
26388
26987
|
}
|
|
26389
26988
|
return appDir;
|
|
26390
26989
|
}
|
|
26990
|
+
var LOCAL_BOOTSTRAP_REQUIRED_KEYS = [
|
|
26991
|
+
"LOCAL_SUPABASE_HOST",
|
|
26992
|
+
"LOCAL_SUPABASE_API_PORT",
|
|
26993
|
+
"LOCAL_SUPABASE_DB_PORT"
|
|
26994
|
+
];
|
|
26995
|
+
function getMissingLocalBootstrapKeys(parsed) {
|
|
26996
|
+
return LOCAL_BOOTSTRAP_REQUIRED_KEYS.filter((key) => !(key in parsed));
|
|
26997
|
+
}
|
|
26998
|
+
function getLocalBootstrapStatusFromParsed(parsed) {
|
|
26999
|
+
const missing = getMissingLocalBootstrapKeys(parsed);
|
|
27000
|
+
return {
|
|
27001
|
+
status: missing.length === 0 ? "ok" : "partial",
|
|
27002
|
+
missing
|
|
27003
|
+
};
|
|
27004
|
+
}
|
|
27005
|
+
function missingLocalBootstrapReport() {
|
|
27006
|
+
return {
|
|
27007
|
+
status: "missing",
|
|
27008
|
+
missing: [...LOCAL_BOOTSTRAP_REQUIRED_KEYS]
|
|
27009
|
+
};
|
|
27010
|
+
}
|
|
26391
27011
|
var EnvPullOutputSchema = z.object({
|
|
26392
27012
|
results: z.array(
|
|
26393
27013
|
z.object({
|
|
@@ -26401,6 +27021,10 @@ var EnvPullOutputSchema = z.object({
|
|
|
26401
27021
|
totalPulled: z.number(),
|
|
26402
27022
|
totalFailed: z.number(),
|
|
26403
27023
|
totalEncrypted: z.number().optional(),
|
|
27024
|
+
localBootstrap: z.object({
|
|
27025
|
+
status: z.enum(["ok", "partial", "missing"]),
|
|
27026
|
+
missing: z.array(z.string())
|
|
27027
|
+
}).optional(),
|
|
26404
27028
|
/** Vercel Root Directory sync result */
|
|
26405
27029
|
vercelSync: z.object({
|
|
26406
27030
|
detected: z.boolean(),
|
|
@@ -26533,60 +27157,74 @@ function removePrivateKeysFromEnvFile(filePath) {
|
|
|
26533
27157
|
writeFileSync(filePath, `${newContent}
|
|
26534
27158
|
`, "utf-8");
|
|
26535
27159
|
}
|
|
26536
|
-
function replaceWithLocalValues(filePath, logger16, dryRun = false) {
|
|
27160
|
+
function replaceWithLocalValues(filePath, workDir, logger16, dryRun = false) {
|
|
26537
27161
|
if (!existsSync(filePath)) {
|
|
26538
27162
|
logger16.warn(" \u26A0\uFE0F .env.development not found (was pull successful?)");
|
|
26539
|
-
return;
|
|
27163
|
+
return missingLocalBootstrapReport();
|
|
26540
27164
|
}
|
|
26541
27165
|
const content = readFileSync(filePath, "utf-8");
|
|
26542
27166
|
if (!content.trim()) {
|
|
26543
27167
|
logger16.warn(" \u26A0\uFE0F .env.development is empty (no env vars in Vercel?)");
|
|
26544
|
-
return;
|
|
27168
|
+
return missingLocalBootstrapReport();
|
|
26545
27169
|
}
|
|
26546
27170
|
const parsed = parse(content);
|
|
26547
27171
|
if (Object.keys(parsed).length === 0) {
|
|
26548
27172
|
logger16.warn(" \u26A0\uFE0F No valid env vars found in .env.development (check syntax)");
|
|
26549
|
-
return;
|
|
27173
|
+
return missingLocalBootstrapReport();
|
|
26550
27174
|
}
|
|
26551
|
-
const
|
|
26552
|
-
const
|
|
26553
|
-
const localDescriptions = getLocalValueDescriptions(projectRoot);
|
|
27175
|
+
const localEnvValues = getLocalSupabaseEnvValues(workDir);
|
|
27176
|
+
const localDescriptions = getLocalValueDescriptions(workDir);
|
|
26554
27177
|
const replacementKeys = Object.keys(localEnvValues);
|
|
26555
27178
|
const replacedKeys = [];
|
|
27179
|
+
const hasAnySupabaseKey = Object.keys(parsed).some((key) => key.includes("SUPABASE") || key.includes("DATABASE_URL")) || replacementKeys.some((key) => key in parsed);
|
|
26556
27180
|
for (const key of replacementKeys) {
|
|
26557
|
-
if (key in parsed
|
|
27181
|
+
if (key in parsed || hasAnySupabaseKey) {
|
|
26558
27182
|
parsed[key] = localEnvValues[key];
|
|
26559
27183
|
replacedKeys.push(key);
|
|
26560
27184
|
}
|
|
26561
27185
|
}
|
|
26562
27186
|
if (replacedKeys.length === 0) {
|
|
26563
|
-
const
|
|
27187
|
+
const hasAnySupabaseKey2 = Object.keys(parsed).some(
|
|
26564
27188
|
(k) => k.includes("SUPABASE") || k.includes("DATABASE_URL")
|
|
26565
27189
|
);
|
|
26566
|
-
if (
|
|
27190
|
+
if (hasAnySupabaseKey2) {
|
|
26567
27191
|
logger16.warn(" \u26A0\uFE0F Found Supabase keys but none matched local replacement patterns");
|
|
26568
27192
|
} else {
|
|
26569
27193
|
logger16.info(" No Supabase variables found (may be expected for new projects)");
|
|
26570
27194
|
}
|
|
26571
|
-
return;
|
|
27195
|
+
return getLocalBootstrapStatusFromParsed(parsed);
|
|
26572
27196
|
}
|
|
27197
|
+
const bootstrapStatus = getLocalBootstrapStatusFromParsed(parsed);
|
|
27198
|
+
if (bootstrapStatus.missing.length > 0) {
|
|
27199
|
+
logger16.warn(
|
|
27200
|
+
` \u26A0\uFE0F Some local bootstrap keys were not updated: ${bootstrapStatus.missing.join(", ")}`
|
|
27201
|
+
);
|
|
27202
|
+
}
|
|
27203
|
+
const ensureRequired = bootstrapStatus.status === "ok";
|
|
26573
27204
|
const newContent = Object.entries(parsed).map(([key, value]) => `${key}=${formatEnvValue(value)}`).join("\n");
|
|
26574
27205
|
if (dryRun) {
|
|
26575
|
-
logger16.info(` \u{1F3E0} Would replace ${replacedKeys.length} variables with
|
|
27206
|
+
logger16.info(` \u{1F3E0} Would replace ${replacedKeys.length} variables with local values:`);
|
|
26576
27207
|
for (const key of replacedKeys) {
|
|
26577
27208
|
const description = localDescriptions[key] || "localhost";
|
|
26578
27209
|
logger16.info(` ${key} \u2192 ${description}`);
|
|
26579
27210
|
}
|
|
27211
|
+
logger16.info(
|
|
27212
|
+
` ${ensureRequired ? "\u2705" : "\u26A0\uFE0F"} Local bootstrap keys ready: ${ensureRequired ? "yes" : "partial"}`
|
|
27213
|
+
);
|
|
26580
27214
|
logger16.info(" (dry-run: no files modified)");
|
|
26581
|
-
return;
|
|
27215
|
+
return bootstrapStatus;
|
|
26582
27216
|
}
|
|
26583
27217
|
writeFileSync(filePath, `${newContent}
|
|
26584
27218
|
`, "utf-8");
|
|
26585
|
-
logger16.success(` \u{1F3E0} Replaced ${replacedKeys.length} variables with
|
|
27219
|
+
logger16.success(` \u{1F3E0} Replaced ${replacedKeys.length} variables with local values:`);
|
|
26586
27220
|
for (const key of replacedKeys) {
|
|
26587
27221
|
const description = localDescriptions[key] || "localhost";
|
|
26588
27222
|
logger16.info(` ${key} \u2192 ${description}`);
|
|
26589
27223
|
}
|
|
27224
|
+
logger16.info(
|
|
27225
|
+
` ${ensureRequired ? "\u2705" : "\u26A0\uFE0F"} Local bootstrap keys ready: ${ensureRequired ? "yes" : "partial"}`
|
|
27226
|
+
);
|
|
27227
|
+
return bootstrapStatus;
|
|
26590
27228
|
}
|
|
26591
27229
|
async function encryptFile2(workDir, filePath, logger16) {
|
|
26592
27230
|
if (!existsSync(filePath)) {
|
|
@@ -26818,13 +27456,20 @@ async function runEnvPullAction(options) {
|
|
|
26818
27456
|
}
|
|
26819
27457
|
logger16.info("");
|
|
26820
27458
|
const results = await pullAllEnvironments(workDir, environments, auth, logger16);
|
|
26821
|
-
|
|
27459
|
+
const shouldEvaluateLocalBootstrap = !options.productionAll && (!options.environment || options.environment === "development");
|
|
27460
|
+
let localBootstrap;
|
|
27461
|
+
if (shouldEvaluateLocalBootstrap) {
|
|
26822
27462
|
const devResult = results.find((r) => r.environment === "development" && r.success);
|
|
26823
27463
|
if (devResult) {
|
|
26824
27464
|
logger16.info("");
|
|
26825
27465
|
logger16.info("Applying local Supabase values to .env.development...");
|
|
26826
|
-
replaceWithLocalValues(devResult.outputPath, logger16, options.check);
|
|
27466
|
+
localBootstrap = replaceWithLocalValues(devResult.outputPath, workDir, logger16, options.check);
|
|
27467
|
+
} else {
|
|
27468
|
+
localBootstrap = missingLocalBootstrapReport();
|
|
27469
|
+
logger16.warn(" LOCAL_BOOTSTRAP: skipped (development env pull failed or missing)");
|
|
26827
27470
|
}
|
|
27471
|
+
} else {
|
|
27472
|
+
localBootstrap = void 0;
|
|
26828
27473
|
}
|
|
26829
27474
|
if (options.encrypt && !options.check) {
|
|
26830
27475
|
await encryptPulledFiles(workDir, results, logger16);
|
|
@@ -26845,6 +27490,7 @@ async function runEnvPullAction(options) {
|
|
|
26845
27490
|
totalPulled,
|
|
26846
27491
|
totalFailed,
|
|
26847
27492
|
totalEncrypted: options.encrypt ? totalEncrypted : void 0,
|
|
27493
|
+
localBootstrap,
|
|
26848
27494
|
vercelSync: vercelSyncResult
|
|
26849
27495
|
});
|
|
26850
27496
|
if (totalFailed > 0) {
|
|
@@ -28584,13 +29230,13 @@ z.object({
|
|
|
28584
29230
|
|
|
28585
29231
|
// src/commands/hotfix/metadata.ts
|
|
28586
29232
|
function getHotfixDir(targetDir) {
|
|
28587
|
-
return
|
|
29233
|
+
return path11__default.join(targetDir, ".runa", "hotfix");
|
|
28588
29234
|
}
|
|
28589
29235
|
function getCurrentFile(targetDir) {
|
|
28590
|
-
return
|
|
29236
|
+
return path11__default.join(getHotfixDir(targetDir), "current.json");
|
|
28591
29237
|
}
|
|
28592
29238
|
function getArchiveDir(targetDir) {
|
|
28593
|
-
return
|
|
29239
|
+
return path11__default.join(getHotfixDir(targetDir), "archive");
|
|
28594
29240
|
}
|
|
28595
29241
|
async function ensureHotfixDir(targetDir) {
|
|
28596
29242
|
await mkdir(getHotfixDir(targetDir), { recursive: true });
|
|
@@ -28600,7 +29246,7 @@ async function ensureArchiveDir(targetDir) {
|
|
|
28600
29246
|
}
|
|
28601
29247
|
async function getHotfixMetadata(input3 = {}) {
|
|
28602
29248
|
const parsed = GetHotfixInputSchema.parse(input3);
|
|
28603
|
-
const targetDir = parsed.targetDir ?
|
|
29249
|
+
const targetDir = parsed.targetDir ? path11__default.resolve(parsed.targetDir) : process.cwd();
|
|
28604
29250
|
const currentFile = getCurrentFile(targetDir);
|
|
28605
29251
|
if (!existsSync(currentFile)) {
|
|
28606
29252
|
return null;
|
|
@@ -28610,7 +29256,7 @@ async function getHotfixMetadata(input3 = {}) {
|
|
|
28610
29256
|
}
|
|
28611
29257
|
async function createHotfixMetadata(input3) {
|
|
28612
29258
|
const parsed = CreateHotfixMetadataInputSchema.parse(input3);
|
|
28613
|
-
const targetDir = parsed.targetDir ?
|
|
29259
|
+
const targetDir = parsed.targetDir ? path11__default.resolve(parsed.targetDir) : process.cwd();
|
|
28614
29260
|
await ensureHotfixDir(targetDir);
|
|
28615
29261
|
const metadata = {
|
|
28616
29262
|
branch: parsed.branch,
|
|
@@ -28627,7 +29273,7 @@ async function createHotfixMetadata(input3) {
|
|
|
28627
29273
|
}
|
|
28628
29274
|
async function updateHotfixStatus(input3) {
|
|
28629
29275
|
const parsed = UpdateHotfixInputSchema.parse(input3);
|
|
28630
|
-
const targetDir = parsed.targetDir ?
|
|
29276
|
+
const targetDir = parsed.targetDir ? path11__default.resolve(parsed.targetDir) : process.cwd();
|
|
28631
29277
|
const current = await getHotfixMetadata({ targetDir });
|
|
28632
29278
|
if (!current) {
|
|
28633
29279
|
throw createError("HOTFIX_NOT_FOUND");
|
|
@@ -28644,13 +29290,13 @@ async function updateHotfixStatus(input3) {
|
|
|
28644
29290
|
return updated;
|
|
28645
29291
|
}
|
|
28646
29292
|
async function archiveHotfix(targetDir) {
|
|
28647
|
-
const dir = targetDir ?
|
|
29293
|
+
const dir = targetDir ? path11__default.resolve(targetDir) : process.cwd();
|
|
28648
29294
|
const current = await getHotfixMetadata({ targetDir: dir });
|
|
28649
29295
|
if (!current) {
|
|
28650
29296
|
throw createError("HOTFIX_NOT_FOUND");
|
|
28651
29297
|
}
|
|
28652
29298
|
await ensureArchiveDir(dir);
|
|
28653
|
-
const archiveFile =
|
|
29299
|
+
const archiveFile = path11__default.join(
|
|
28654
29300
|
getArchiveDir(dir),
|
|
28655
29301
|
`${current.branch.replace(/\//g, "-")}-${Date.now()}.json`
|
|
28656
29302
|
);
|
|
@@ -28666,7 +29312,7 @@ async function archiveHotfix(targetDir) {
|
|
|
28666
29312
|
return archived;
|
|
28667
29313
|
}
|
|
28668
29314
|
async function cancelHotfix(targetDir) {
|
|
28669
|
-
const dir = targetDir ?
|
|
29315
|
+
const dir = targetDir ? path11__default.resolve(targetDir) : process.cwd();
|
|
28670
29316
|
await updateHotfixStatus({
|
|
28671
29317
|
status: "cancelled",
|
|
28672
29318
|
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -28676,7 +29322,7 @@ async function cancelHotfix(targetDir) {
|
|
|
28676
29322
|
}
|
|
28677
29323
|
async function listArchivedHotfixes(input3 = {}) {
|
|
28678
29324
|
const parsed = ListArchivedHotfixesInputSchema.parse(input3);
|
|
28679
|
-
const targetDir = parsed.targetDir ?
|
|
29325
|
+
const targetDir = parsed.targetDir ? path11__default.resolve(parsed.targetDir) : process.cwd();
|
|
28680
29326
|
const archiveDir = getArchiveDir(targetDir);
|
|
28681
29327
|
if (!existsSync(archiveDir)) {
|
|
28682
29328
|
return { hotfixes: [] };
|
|
@@ -28685,7 +29331,7 @@ async function listArchivedHotfixes(input3 = {}) {
|
|
|
28685
29331
|
const hotfixes = [];
|
|
28686
29332
|
for (const file of files.filter((f) => f.endsWith(".json"))) {
|
|
28687
29333
|
try {
|
|
28688
|
-
const content = await readFile(
|
|
29334
|
+
const content = await readFile(path11__default.join(archiveDir, file), "utf-8");
|
|
28689
29335
|
hotfixes.push(HotfixMetadataSchema.parse(JSON.parse(content)));
|
|
28690
29336
|
} catch {
|
|
28691
29337
|
}
|
|
@@ -30185,13 +30831,13 @@ function validateVersion(version) {
|
|
|
30185
30831
|
}
|
|
30186
30832
|
}
|
|
30187
30833
|
function getCacheBase() {
|
|
30188
|
-
return
|
|
30834
|
+
return path11__default.join(os.homedir(), ".cache", "runa", "templates");
|
|
30189
30835
|
}
|
|
30190
30836
|
function getCacheDir(version) {
|
|
30191
30837
|
validateVersion(version);
|
|
30192
30838
|
const cacheBase = getCacheBase();
|
|
30193
|
-
const cacheDir =
|
|
30194
|
-
const resolvedCacheDir =
|
|
30839
|
+
const cacheDir = path11__default.join(cacheBase, version);
|
|
30840
|
+
const resolvedCacheDir = path11__default.resolve(cacheDir);
|
|
30195
30841
|
if (!isPathContained(cacheBase, resolvedCacheDir)) {
|
|
30196
30842
|
throw new CLIError(
|
|
30197
30843
|
"Security: cache directory would escape allowed location.",
|
|
@@ -30203,7 +30849,7 @@ function getCacheDir(version) {
|
|
|
30203
30849
|
}
|
|
30204
30850
|
function isCached(version) {
|
|
30205
30851
|
const cacheDir = getCacheDir(version);
|
|
30206
|
-
const templatesDir =
|
|
30852
|
+
const templatesDir = path11__default.join(cacheDir, "templates");
|
|
30207
30853
|
return fs5__default.existsSync(templatesDir);
|
|
30208
30854
|
}
|
|
30209
30855
|
var scopedAuthToken = null;
|
|
@@ -30254,13 +30900,13 @@ async function createTempPackageJson(tempDir, version) {
|
|
|
30254
30900
|
[TEMPLATES_PACKAGE_NAME]: version
|
|
30255
30901
|
}
|
|
30256
30902
|
};
|
|
30257
|
-
await writeFile(
|
|
30903
|
+
await writeFile(path11__default.join(tempDir, "package.json"), JSON.stringify(packageJson, null, 2));
|
|
30258
30904
|
}
|
|
30259
30905
|
async function createTempNpmrc(tempDir) {
|
|
30260
30906
|
const npmrc = `@r06-dev:registry=${GITHUB_PACKAGES_REGISTRY}
|
|
30261
30907
|
//npm.pkg.github.com/:_authToken=\${NODE_AUTH_TOKEN}
|
|
30262
30908
|
`;
|
|
30263
|
-
await writeFile(
|
|
30909
|
+
await writeFile(path11__default.join(tempDir, ".npmrc"), npmrc);
|
|
30264
30910
|
}
|
|
30265
30911
|
async function installTemplates(tempDir, authToken, verbose) {
|
|
30266
30912
|
try {
|
|
@@ -30318,10 +30964,10 @@ async function installTemplates(tempDir, authToken, verbose) {
|
|
|
30318
30964
|
async function verifyNoSymlinks(dir, baseDir) {
|
|
30319
30965
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
30320
30966
|
for (const entry of entries) {
|
|
30321
|
-
const fullPath =
|
|
30967
|
+
const fullPath = path11__default.join(dir, entry.name);
|
|
30322
30968
|
const stats = await lstat(fullPath);
|
|
30323
30969
|
if (stats.isSymbolicLink()) {
|
|
30324
|
-
const relativePath =
|
|
30970
|
+
const relativePath = path11__default.relative(baseDir, fullPath);
|
|
30325
30971
|
throw new CLIError(
|
|
30326
30972
|
"Security: Symlink detected in template package.",
|
|
30327
30973
|
"SYMLINK_ATTACK_DETECTED",
|
|
@@ -30339,7 +30985,7 @@ async function verifyNoSymlinks(dir, baseDir) {
|
|
|
30339
30985
|
}
|
|
30340
30986
|
}
|
|
30341
30987
|
async function copyToCache(tempDir, cacheDir) {
|
|
30342
|
-
const sourceTemplates =
|
|
30988
|
+
const sourceTemplates = path11__default.join(tempDir, "node_modules", TEMPLATES_PACKAGE_NAME, "templates");
|
|
30343
30989
|
if (!fs5__default.existsSync(sourceTemplates)) {
|
|
30344
30990
|
throw new CLIError("Templates directory not found in package.", "TEMPLATES_DIR_NOT_FOUND", [
|
|
30345
30991
|
`Expected: ${sourceTemplates}`,
|
|
@@ -30349,7 +30995,7 @@ async function copyToCache(tempDir, cacheDir) {
|
|
|
30349
30995
|
}
|
|
30350
30996
|
await verifyNoSymlinks(sourceTemplates, sourceTemplates);
|
|
30351
30997
|
await mkdir(cacheDir, { recursive: true });
|
|
30352
|
-
const targetTemplates =
|
|
30998
|
+
const targetTemplates = path11__default.join(cacheDir, "templates");
|
|
30353
30999
|
await cp(sourceTemplates, targetTemplates, {
|
|
30354
31000
|
recursive: true,
|
|
30355
31001
|
dereference: false
|
|
@@ -30371,7 +31017,7 @@ async function fetchTemplates(options = {}) {
|
|
|
30371
31017
|
};
|
|
30372
31018
|
}
|
|
30373
31019
|
const cacheDir = getCacheDir(version);
|
|
30374
|
-
const templatesDir =
|
|
31020
|
+
const templatesDir = path11__default.join(cacheDir, "templates");
|
|
30375
31021
|
if (!fresh && isCached(version)) {
|
|
30376
31022
|
if (verbose) {
|
|
30377
31023
|
console.log(`Using cached templates v${version}: ${templatesDir}`);
|
|
@@ -30387,7 +31033,7 @@ async function fetchTemplates(options = {}) {
|
|
|
30387
31033
|
if (fresh && fs5__default.existsSync(cacheDir)) {
|
|
30388
31034
|
await rm(cacheDir, { recursive: true, force: true });
|
|
30389
31035
|
}
|
|
30390
|
-
const tempDir =
|
|
31036
|
+
const tempDir = path11__default.join(os.tmpdir(), `runa-templates-${Date.now()}`);
|
|
30391
31037
|
await mkdir(tempDir, { recursive: true });
|
|
30392
31038
|
try {
|
|
30393
31039
|
if (verbose) {
|
|
@@ -30415,11 +31061,11 @@ async function fetchTemplates(options = {}) {
|
|
|
30415
31061
|
}
|
|
30416
31062
|
var MAX_WORKSPACE_TRAVERSAL_DEPTH = 10;
|
|
30417
31063
|
function isLegitimateWorkspaceRoot(workspaceRoot) {
|
|
30418
|
-
const pnpmWorkspaceFile =
|
|
31064
|
+
const pnpmWorkspaceFile = path11__default.join(workspaceRoot, "pnpm-workspace.yaml");
|
|
30419
31065
|
if (!fs5__default.existsSync(pnpmWorkspaceFile)) {
|
|
30420
31066
|
return false;
|
|
30421
31067
|
}
|
|
30422
|
-
const rootPackageFile =
|
|
31068
|
+
const rootPackageFile = path11__default.join(workspaceRoot, "package.json");
|
|
30423
31069
|
if (!fs5__default.existsSync(rootPackageFile)) {
|
|
30424
31070
|
return false;
|
|
30425
31071
|
}
|
|
@@ -30433,24 +31079,24 @@ function isLegitimateWorkspaceRoot(workspaceRoot) {
|
|
|
30433
31079
|
}
|
|
30434
31080
|
}
|
|
30435
31081
|
function resolveWorkspaceTemplates() {
|
|
30436
|
-
let current =
|
|
30437
|
-
const root =
|
|
31082
|
+
let current = path11__default.resolve(process.cwd());
|
|
31083
|
+
const root = path11__default.parse(current).root;
|
|
30438
31084
|
let depth = 0;
|
|
30439
31085
|
while (current !== root && depth < MAX_WORKSPACE_TRAVERSAL_DEPTH) {
|
|
30440
31086
|
if (!isLegitimateWorkspaceRoot(current)) {
|
|
30441
|
-
current =
|
|
31087
|
+
current = path11__default.dirname(current);
|
|
30442
31088
|
depth++;
|
|
30443
31089
|
continue;
|
|
30444
31090
|
}
|
|
30445
|
-
const packagesTemplates =
|
|
30446
|
-
const normalizedTemplatesPath =
|
|
30447
|
-
if (!normalizedTemplatesPath.startsWith(current +
|
|
30448
|
-
current =
|
|
31091
|
+
const packagesTemplates = path11__default.join(current, "packages", "runa-templates", "templates");
|
|
31092
|
+
const normalizedTemplatesPath = path11__default.resolve(packagesTemplates);
|
|
31093
|
+
if (!normalizedTemplatesPath.startsWith(current + path11__default.sep)) {
|
|
31094
|
+
current = path11__default.dirname(current);
|
|
30449
31095
|
depth++;
|
|
30450
31096
|
continue;
|
|
30451
31097
|
}
|
|
30452
31098
|
if (fs5__default.existsSync(normalizedTemplatesPath)) {
|
|
30453
|
-
const markerFile =
|
|
31099
|
+
const markerFile = path11__default.join(current, "packages", "runa-templates", "package.json");
|
|
30454
31100
|
if (fs5__default.existsSync(markerFile)) {
|
|
30455
31101
|
try {
|
|
30456
31102
|
const pkg = JSON.parse(fs5__default.readFileSync(markerFile, "utf-8"));
|
|
@@ -30461,7 +31107,7 @@ function resolveWorkspaceTemplates() {
|
|
|
30461
31107
|
}
|
|
30462
31108
|
}
|
|
30463
31109
|
}
|
|
30464
|
-
current =
|
|
31110
|
+
current = path11__default.dirname(current);
|
|
30465
31111
|
depth++;
|
|
30466
31112
|
}
|
|
30467
31113
|
return void 0;
|
|
@@ -30470,9 +31116,9 @@ function resolveWorkspaceTemplates() {
|
|
|
30470
31116
|
// src/commands/init.ts
|
|
30471
31117
|
function checkZodVersion(_logger) {
|
|
30472
31118
|
const zodPaths = [
|
|
30473
|
-
|
|
31119
|
+
path11__default.join(process.cwd(), "node_modules", "zod", "package.json"),
|
|
30474
31120
|
// Check home directory node_modules (common source of conflicts)
|
|
30475
|
-
|
|
31121
|
+
path11__default.join(process.env.HOME ?? "", "node_modules", "zod", "package.json")
|
|
30476
31122
|
];
|
|
30477
31123
|
for (const zodPath of zodPaths) {
|
|
30478
31124
|
if (!fs5__default.existsSync(zodPath)) continue;
|
|
@@ -30486,7 +31132,7 @@ function checkZodVersion(_logger) {
|
|
|
30486
31132
|
"ZOD_VERSION_INCOMPATIBLE",
|
|
30487
31133
|
[
|
|
30488
31134
|
"runa SDK requires zod v4.x, but found zod v3.x",
|
|
30489
|
-
`Conflicting zod found at: ${
|
|
31135
|
+
`Conflicting zod found at: ${path11__default.dirname(zodPath)}`,
|
|
30490
31136
|
"",
|
|
30491
31137
|
"Solutions:",
|
|
30492
31138
|
"1. Run from a clean project directory without existing node_modules",
|
|
@@ -30738,7 +31384,7 @@ var initCommand = new Command("init").description("Initialize pj-repo with runa
|
|
|
30738
31384
|
try {
|
|
30739
31385
|
if (options.repairConfig) {
|
|
30740
31386
|
logger16.section("Repairing runa.config.ts");
|
|
30741
|
-
const projectName2 =
|
|
31387
|
+
const projectName2 = path11__default.basename(process.cwd());
|
|
30742
31388
|
const repairResult = repairRunaConfig(process.cwd(), projectName2);
|
|
30743
31389
|
if (repairResult.success) {
|
|
30744
31390
|
logger16.success("\u2705 Config file repaired successfully");
|
|
@@ -30789,7 +31435,7 @@ var initCommand = new Command("init").description("Initialize pj-repo with runa
|
|
|
30789
31435
|
existingConfig,
|
|
30790
31436
|
force: options.force === true
|
|
30791
31437
|
});
|
|
30792
|
-
const projectName =
|
|
31438
|
+
const projectName = path11__default.basename(process.cwd());
|
|
30793
31439
|
logger16.info(`Project: ${projectName}`);
|
|
30794
31440
|
logger16.step("Applying templates (SDK)", 1);
|
|
30795
31441
|
const result = await initProject({
|
|
@@ -30886,7 +31532,9 @@ var InjectTestAttrsInputSchema = z.object({
|
|
|
30886
31532
|
/** Manifest output directory */
|
|
30887
31533
|
manifestDir: z.string().default(".runa"),
|
|
30888
31534
|
/** Resolve Zod schemas to JSON Schema (v7+) for API contracts */
|
|
30889
|
-
resolveSchemas: z.boolean().default(false)
|
|
31535
|
+
resolveSchemas: z.boolean().default(false),
|
|
31536
|
+
/** Fail when heuristic fallback detection is used */
|
|
31537
|
+
strictDetect: z.boolean().default(false)
|
|
30890
31538
|
}).strict();
|
|
30891
31539
|
var FileChangeSchema = z.object({
|
|
30892
31540
|
filePath: z.string(),
|
|
@@ -30898,6 +31546,51 @@ var MachineWithoutE2EMetaSchema = z.object({
|
|
|
30898
31546
|
id: z.string(),
|
|
30899
31547
|
sourceFile: z.string()
|
|
30900
31548
|
});
|
|
31549
|
+
var DetectionScanSummarySchema = z.object({
|
|
31550
|
+
totalFiles: z.number().int().nonnegative(),
|
|
31551
|
+
filesWithMachineDefinitions: z.number().int().nonnegative(),
|
|
31552
|
+
filesWithMachineHooks: z.number().int().nonnegative(),
|
|
31553
|
+
filesWithJsxOnly: z.number().int().nonnegative()
|
|
31554
|
+
}).strict();
|
|
31555
|
+
var MachineResolutionDetailSchema = z.object({
|
|
31556
|
+
filePath: z.string(),
|
|
31557
|
+
machineRef: z.string(),
|
|
31558
|
+
resolvedId: z.string(),
|
|
31559
|
+
source: z.enum(["explicit", "import", "inline", "fallback"])
|
|
31560
|
+
}).strict();
|
|
31561
|
+
var DetectionDiagnosticsSchema = z.object({
|
|
31562
|
+
schemaVersion: z.literal(1),
|
|
31563
|
+
scan: DetectionScanSummarySchema,
|
|
31564
|
+
resolution: z.object({
|
|
31565
|
+
totalMachineUsages: z.number().int().nonnegative(),
|
|
31566
|
+
bySource: z.object({
|
|
31567
|
+
explicit: z.number().int().nonnegative(),
|
|
31568
|
+
import: z.number().int().nonnegative(),
|
|
31569
|
+
inline: z.number().int().nonnegative(),
|
|
31570
|
+
fallback: z.number().int().nonnegative()
|
|
31571
|
+
}).strict(),
|
|
31572
|
+
fallbackRate: z.number().min(0).max(1),
|
|
31573
|
+
fallbackExamples: z.array(MachineResolutionDetailSchema)
|
|
31574
|
+
}).strict(),
|
|
31575
|
+
coverage: z.object({
|
|
31576
|
+
totalManifestMachines: z.number().int().nonnegative(),
|
|
31577
|
+
linkedManifestMachines: z.number().int().nonnegative(),
|
|
31578
|
+
unlinkedManifestMachines: z.number().int().nonnegative(),
|
|
31579
|
+
machineLinkCoverage: z.number().min(0).max(1),
|
|
31580
|
+
unlinkedManifestMachineIds: z.array(z.string())
|
|
31581
|
+
}).strict(),
|
|
31582
|
+
warnings: z.array(
|
|
31583
|
+
z.object({
|
|
31584
|
+
severity: z.enum(["warning", "info", "hint"]),
|
|
31585
|
+
message: z.string()
|
|
31586
|
+
}).strict()
|
|
31587
|
+
)
|
|
31588
|
+
}).strict();
|
|
31589
|
+
var StrictDetectStatusSchema = z.object({
|
|
31590
|
+
enabled: z.literal(true),
|
|
31591
|
+
passed: z.boolean(),
|
|
31592
|
+
reasons: z.array(z.string())
|
|
31593
|
+
}).strict();
|
|
30901
31594
|
var InjectTestAttrsOutputSchema = z.object({
|
|
30902
31595
|
totalFiles: z.number(),
|
|
30903
31596
|
changedFiles: z.number(),
|
|
@@ -30909,17 +31602,98 @@ var InjectTestAttrsOutputSchema = z.object({
|
|
|
30909
31602
|
/** Number of machines in manifest */
|
|
30910
31603
|
manifestMachineCount: z.number().optional(),
|
|
30911
31604
|
/** Machines without meta.e2e annotation (opt-in warning) */
|
|
30912
|
-
machinesWithoutE2EMeta: z.array(MachineWithoutE2EMetaSchema).optional()
|
|
31605
|
+
machinesWithoutE2EMeta: z.array(MachineWithoutE2EMetaSchema).optional(),
|
|
31606
|
+
/** Structured diagnostics for detection quality */
|
|
31607
|
+
detectionDiagnostics: DetectionDiagnosticsSchema.optional(),
|
|
31608
|
+
/** Strict detection mode result */
|
|
31609
|
+
strictDetect: StrictDetectStatusSchema.optional()
|
|
30913
31610
|
}).strict();
|
|
30914
31611
|
|
|
30915
31612
|
// src/commands/inject-test-attrs/processor.ts
|
|
30916
31613
|
init_esm_shims();
|
|
30917
31614
|
|
|
31615
|
+
// src/commands/inject-test-attrs/detection-diagnostics.ts
|
|
31616
|
+
init_esm_shims();
|
|
31617
|
+
var MAX_EXAMPLES = 20;
|
|
31618
|
+
function normalizeMachineId(id) {
|
|
31619
|
+
return id.replace(/-([a-z])/g, (_, char) => char.toUpperCase()).replace(/Machine$/i, "").toLowerCase();
|
|
31620
|
+
}
|
|
31621
|
+
function buildDetectionDiagnostics(params) {
|
|
31622
|
+
const bySource = {
|
|
31623
|
+
explicit: 0,
|
|
31624
|
+
import: 0,
|
|
31625
|
+
inline: 0,
|
|
31626
|
+
fallback: 0
|
|
31627
|
+
};
|
|
31628
|
+
const fallbackExamples = [];
|
|
31629
|
+
for (const detail of params.resolutionDetails) {
|
|
31630
|
+
bySource[detail.source] += 1;
|
|
31631
|
+
if (detail.source === "fallback" && fallbackExamples.length < MAX_EXAMPLES) {
|
|
31632
|
+
fallbackExamples.push(detail);
|
|
31633
|
+
}
|
|
31634
|
+
}
|
|
31635
|
+
const linkedMachineIds = new Set(
|
|
31636
|
+
params.machineLinks.filter((link) => link.usedIn.length > 0).map((link) => normalizeMachineId(link.machineName))
|
|
31637
|
+
);
|
|
31638
|
+
const linkedManifestMachineIds = [];
|
|
31639
|
+
const unlinkedManifestMachineIds = [];
|
|
31640
|
+
for (const machineId of params.manifestMachineIds) {
|
|
31641
|
+
if (linkedMachineIds.has(normalizeMachineId(machineId))) {
|
|
31642
|
+
linkedManifestMachineIds.push(machineId);
|
|
31643
|
+
continue;
|
|
31644
|
+
}
|
|
31645
|
+
unlinkedManifestMachineIds.push(machineId);
|
|
31646
|
+
}
|
|
31647
|
+
const totalMachineUsages = params.resolutionDetails.length;
|
|
31648
|
+
const fallbackRate = totalMachineUsages > 0 ? bySource.fallback / totalMachineUsages : 0;
|
|
31649
|
+
const totalManifestMachines = params.manifestMachineIds.length;
|
|
31650
|
+
const linkedManifestMachines = linkedManifestMachineIds.length;
|
|
31651
|
+
const machineLinkCoverage = totalManifestMachines > 0 ? linkedManifestMachines / totalManifestMachines : 1;
|
|
31652
|
+
return {
|
|
31653
|
+
schemaVersion: 1,
|
|
31654
|
+
scan: params.scanSummary,
|
|
31655
|
+
resolution: {
|
|
31656
|
+
totalMachineUsages,
|
|
31657
|
+
bySource,
|
|
31658
|
+
fallbackRate,
|
|
31659
|
+
fallbackExamples
|
|
31660
|
+
},
|
|
31661
|
+
coverage: {
|
|
31662
|
+
totalManifestMachines,
|
|
31663
|
+
linkedManifestMachines,
|
|
31664
|
+
unlinkedManifestMachines: totalManifestMachines - linkedManifestMachines,
|
|
31665
|
+
machineLinkCoverage,
|
|
31666
|
+
unlinkedManifestMachineIds: unlinkedManifestMachineIds.slice(0, MAX_EXAMPLES)
|
|
31667
|
+
},
|
|
31668
|
+
warnings: params.warnings
|
|
31669
|
+
};
|
|
31670
|
+
}
|
|
31671
|
+
function evaluateStrictDetect(diagnostics) {
|
|
31672
|
+
const reasons = [];
|
|
31673
|
+
const fallbackCount = diagnostics.resolution.bySource.fallback;
|
|
31674
|
+
if (fallbackCount > 0) {
|
|
31675
|
+
reasons.push(`fallback machine resolution detected (${fallbackCount})`);
|
|
31676
|
+
}
|
|
31677
|
+
if (diagnostics.scan.filesWithMachineHooks > 0 && diagnostics.resolution.totalMachineUsages === 0) {
|
|
31678
|
+
reasons.push(
|
|
31679
|
+
`machine hooks were detected in ${diagnostics.scan.filesWithMachineHooks} files, but no machine usages were resolved`
|
|
31680
|
+
);
|
|
31681
|
+
}
|
|
31682
|
+
if (diagnostics.resolution.totalMachineUsages > 0 && diagnostics.coverage.totalManifestMachines > 0 && diagnostics.coverage.linkedManifestMachines === 0) {
|
|
31683
|
+
reasons.push("machine usages were detected, but no machines were linked to routes/pages");
|
|
31684
|
+
}
|
|
31685
|
+
return {
|
|
31686
|
+
enabled: true,
|
|
31687
|
+
passed: reasons.length === 0,
|
|
31688
|
+
reasons
|
|
31689
|
+
};
|
|
31690
|
+
}
|
|
31691
|
+
|
|
30918
31692
|
// src/commands/inject-test-attrs/formatter.ts
|
|
30919
31693
|
init_esm_shims();
|
|
30920
31694
|
async function formatFilesWithBiome(files, cwd) {
|
|
30921
31695
|
if (files.length === 0) return;
|
|
30922
|
-
const relativePaths = files.map((f) =>
|
|
31696
|
+
const relativePaths = files.map((f) => path11.relative(cwd, f));
|
|
30923
31697
|
try {
|
|
30924
31698
|
await execa("pnpm", ["exec", "biome", "format", "--write", ...relativePaths], {
|
|
30925
31699
|
cwd,
|
|
@@ -31011,7 +31785,8 @@ function emptyResult(filePath) {
|
|
|
31011
31785
|
changed: false,
|
|
31012
31786
|
actions: [],
|
|
31013
31787
|
fields: [],
|
|
31014
|
-
testIds: []
|
|
31788
|
+
testIds: [],
|
|
31789
|
+
resolutionDetails: []
|
|
31015
31790
|
};
|
|
31016
31791
|
}
|
|
31017
31792
|
function ensureDirectoryExists(dirPath) {
|
|
@@ -31021,7 +31796,7 @@ function ensureDirectoryExists(dirPath) {
|
|
|
31021
31796
|
}
|
|
31022
31797
|
function getGeneratorVersion() {
|
|
31023
31798
|
try {
|
|
31024
|
-
const pkgPath =
|
|
31799
|
+
const pkgPath = path11.resolve(__dirname$1, "../../../package.json");
|
|
31025
31800
|
if (fs5.existsSync(pkgPath)) {
|
|
31026
31801
|
const pkg = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
|
|
31027
31802
|
return pkg.version ?? "unknown";
|
|
@@ -31036,15 +31811,15 @@ function detectPlatform(repoRoot) {
|
|
|
31036
31811
|
const nextConfigPatterns = ["next.config.ts", "next.config.js", "next.config.mjs"];
|
|
31037
31812
|
for (const pattern of nextConfigPatterns) {
|
|
31038
31813
|
const paths = [
|
|
31039
|
-
|
|
31040
|
-
|
|
31041
|
-
|
|
31814
|
+
path11.join(repoRoot, pattern),
|
|
31815
|
+
path11.join(repoRoot, "apps", "web", pattern),
|
|
31816
|
+
path11.join(repoRoot, "apps", "dashboard", pattern)
|
|
31042
31817
|
];
|
|
31043
31818
|
if (paths.some((p) => fs5.existsSync(p))) {
|
|
31044
31819
|
return "nextjs";
|
|
31045
31820
|
}
|
|
31046
31821
|
}
|
|
31047
|
-
const appJsonPath =
|
|
31822
|
+
const appJsonPath = path11.join(repoRoot, "app.json");
|
|
31048
31823
|
if (fs5.existsSync(appJsonPath)) {
|
|
31049
31824
|
try {
|
|
31050
31825
|
const appJson = JSON.parse(fs5.readFileSync(appJsonPath, "utf-8"));
|
|
@@ -31054,7 +31829,7 @@ function detectPlatform(repoRoot) {
|
|
|
31054
31829
|
} catch {
|
|
31055
31830
|
}
|
|
31056
31831
|
}
|
|
31057
|
-
if (fs5.existsSync(
|
|
31832
|
+
if (fs5.existsSync(path11.join(repoRoot, "expo.json"))) {
|
|
31058
31833
|
return "expo";
|
|
31059
31834
|
}
|
|
31060
31835
|
const electronPatterns = [
|
|
@@ -31063,56 +31838,38 @@ function detectPlatform(repoRoot) {
|
|
|
31063
31838
|
"electron.vite.config.ts",
|
|
31064
31839
|
"electron.vite.config.js"
|
|
31065
31840
|
];
|
|
31066
|
-
if (electronPatterns.some((p) => fs5.existsSync(
|
|
31841
|
+
if (electronPatterns.some((p) => fs5.existsSync(path11.join(repoRoot, p)))) {
|
|
31067
31842
|
return "electron";
|
|
31068
31843
|
}
|
|
31069
31844
|
return "unknown";
|
|
31070
31845
|
}
|
|
31071
|
-
function
|
|
31072
|
-
|
|
31073
|
-
|
|
31074
|
-
|
|
31075
|
-
|
|
31076
|
-
|
|
31077
|
-
|
|
31078
|
-
|
|
31079
|
-
const routesDir = path10.join(appsDir, app.name, "routes");
|
|
31080
|
-
if (fs5.existsSync(routesDir)) {
|
|
31081
|
-
const files = fs5.readdirSync(routesDir);
|
|
31082
|
-
const hasRouteFiles = files.some(
|
|
31083
|
-
(f) => f.endsWith(".ts") && !f.endsWith(".test.ts") && !f.endsWith(".spec.ts")
|
|
31084
|
-
);
|
|
31085
|
-
if (hasRouteFiles) {
|
|
31086
|
-
possibleDirs.push(`apps/${app.name}/routes`);
|
|
31087
|
-
}
|
|
31088
|
-
}
|
|
31089
|
-
}
|
|
31090
|
-
}
|
|
31091
|
-
} catch {
|
|
31092
|
-
}
|
|
31846
|
+
function hasHonoRouteFiles(dir) {
|
|
31847
|
+
try {
|
|
31848
|
+
const files = fs5.readdirSync(dir);
|
|
31849
|
+
return files.some(
|
|
31850
|
+
(file) => file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".spec.ts")
|
|
31851
|
+
);
|
|
31852
|
+
} catch {
|
|
31853
|
+
return false;
|
|
31093
31854
|
}
|
|
31094
|
-
|
|
31095
|
-
|
|
31096
|
-
|
|
31097
|
-
|
|
31098
|
-
|
|
31099
|
-
if (pkg.isDirectory()) {
|
|
31100
|
-
const routesDir = path10.join(packagesDir, pkg.name, "routes");
|
|
31101
|
-
if (fs5.existsSync(routesDir)) {
|
|
31102
|
-
const files = fs5.readdirSync(routesDir);
|
|
31103
|
-
const hasRouteFiles = files.some(
|
|
31104
|
-
(f) => f.endsWith(".ts") && !f.endsWith(".test.ts") && !f.endsWith(".spec.ts")
|
|
31105
|
-
);
|
|
31106
|
-
if (hasRouteFiles) {
|
|
31107
|
-
possibleDirs.push(`packages/${pkg.name}/routes`);
|
|
31108
|
-
}
|
|
31109
|
-
}
|
|
31110
|
-
}
|
|
31111
|
-
}
|
|
31112
|
-
} catch {
|
|
31113
|
-
}
|
|
31855
|
+
}
|
|
31856
|
+
function collectRouteDirsInScope(repoRoot, scope) {
|
|
31857
|
+
const scopeDir = path11.join(repoRoot, scope);
|
|
31858
|
+
if (!fs5.existsSync(scopeDir)) {
|
|
31859
|
+
return [];
|
|
31114
31860
|
}
|
|
31115
|
-
|
|
31861
|
+
try {
|
|
31862
|
+
const entries = fs5.readdirSync(scopeDir, { withFileTypes: true });
|
|
31863
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((entryName) => hasHonoRouteFiles(path11.join(scopeDir, entryName, "routes"))).map((entryName) => `${scope}/${entryName}/routes`);
|
|
31864
|
+
} catch {
|
|
31865
|
+
return [];
|
|
31866
|
+
}
|
|
31867
|
+
}
|
|
31868
|
+
function findPossibleHonoDirs(repoRoot) {
|
|
31869
|
+
return [
|
|
31870
|
+
...collectRouteDirsInScope(repoRoot, "apps"),
|
|
31871
|
+
...collectRouteDirsInScope(repoRoot, "packages")
|
|
31872
|
+
];
|
|
31116
31873
|
}
|
|
31117
31874
|
function collectManifestWarnings(repoRoot, platform, apiContracts, machinesWithoutE2EMeta, totalMachines) {
|
|
31118
31875
|
const warnings = [];
|
|
@@ -31163,15 +31920,18 @@ function printManifestWarnings(warnings, verbose) {
|
|
|
31163
31920
|
for (const detail of warning.details) {
|
|
31164
31921
|
console.log(detail);
|
|
31165
31922
|
}
|
|
31166
|
-
if (warning.details.length < warning._totalCount) {
|
|
31167
|
-
console.log(` ... and more`);
|
|
31168
|
-
}
|
|
31169
31923
|
}
|
|
31170
31924
|
if (warning.fix) {
|
|
31171
31925
|
console.log(` \u2192 ${warning.fix}`);
|
|
31172
31926
|
}
|
|
31173
31927
|
}
|
|
31174
31928
|
}
|
|
31929
|
+
function toDetectionWarnings(warnings) {
|
|
31930
|
+
return warnings.map((warning) => ({
|
|
31931
|
+
severity: warning.severity,
|
|
31932
|
+
message: warning.message
|
|
31933
|
+
}));
|
|
31934
|
+
}
|
|
31175
31935
|
function normalizeToCanonicalId(id) {
|
|
31176
31936
|
return id.replace(/-([a-z])/g, (_, c) => c.toUpperCase()).replace(/Machine$/i, "");
|
|
31177
31937
|
}
|
|
@@ -31259,7 +32019,7 @@ async function analyzeHonoRoutes(repoRoot, verbose, resolveSchemas = false) {
|
|
|
31259
32019
|
return { apiContracts: [], schemasResolved: false };
|
|
31260
32020
|
}
|
|
31261
32021
|
if (verbose) {
|
|
31262
|
-
console.log(` Analyzing Hono routes in: ${
|
|
32022
|
+
console.log(` Analyzing Hono routes in: ${path11.relative(repoRoot, routesPath)}`);
|
|
31263
32023
|
if (resolveSchemas) {
|
|
31264
32024
|
console.log(" Schema resolution enabled (--resolve-schemas)");
|
|
31265
32025
|
}
|
|
@@ -31305,7 +32065,7 @@ async function analyzeHonoRoutes(repoRoot, verbose, resolveSchemas = false) {
|
|
|
31305
32065
|
return { apiContracts: [], schemasResolved: false };
|
|
31306
32066
|
}
|
|
31307
32067
|
}
|
|
31308
|
-
async function generateManifestFiles(manifestDir, repoRoot, verbose, resolveSchemas = false) {
|
|
32068
|
+
async function generateManifestFiles(manifestDir, repoRoot, verbose, resolveSchemas = false, diagnosticsInput) {
|
|
31309
32069
|
const registry = getInjectionRegistry();
|
|
31310
32070
|
const generatorVersion = getGeneratorVersion();
|
|
31311
32071
|
const e2eManifest = buildManifest(registry, generatorVersion);
|
|
@@ -31319,18 +32079,18 @@ async function generateManifestFiles(manifestDir, repoRoot, verbose, resolveSche
|
|
|
31319
32079
|
}
|
|
31320
32080
|
const definitionMap = buildDefinitionMap(machineDefinitions);
|
|
31321
32081
|
const enhancedMachines = buildEnhancedMachines(e2eManifest, definitionMap, machineDefinitions);
|
|
31322
|
-
const absoluteManifestDir =
|
|
32082
|
+
const absoluteManifestDir = path11.isAbsolute(manifestDir) ? manifestDir : path11.join(repoRoot, manifestDir);
|
|
31323
32083
|
if (!isPathContained(repoRoot, absoluteManifestDir)) {
|
|
31324
32084
|
throw new Error(
|
|
31325
32085
|
`Security error: Manifest directory '${manifestDir}' would escape the project root. The --manifest-dir must be a relative path within the project directory.`
|
|
31326
32086
|
);
|
|
31327
32087
|
}
|
|
31328
32088
|
ensureDirectoryExists(absoluteManifestDir);
|
|
31329
|
-
const generatedDir =
|
|
32089
|
+
const generatedDir = path11.join(absoluteManifestDir, "generated");
|
|
31330
32090
|
ensureDirectoryExists(generatedDir);
|
|
31331
|
-
const manifestsDir =
|
|
32091
|
+
const manifestsDir = path11.join(absoluteManifestDir, "manifests");
|
|
31332
32092
|
ensureDirectoryExists(manifestsDir);
|
|
31333
|
-
const tsPath =
|
|
32093
|
+
const tsPath = path11.join(generatedDir, "selectors.ts");
|
|
31334
32094
|
await fs5.promises.writeFile(tsPath, generateSelectorTypeScript(e2eManifest), "utf-8");
|
|
31335
32095
|
const unifiedRegistry = getUnifiedRegistry();
|
|
31336
32096
|
const machineLinks = buildMachineLinks();
|
|
@@ -31345,6 +32105,29 @@ async function generateManifestFiles(manifestDir, repoRoot, verbose, resolveSche
|
|
|
31345
32105
|
const jstOffset = 9 * 60 * 60 * 1e3;
|
|
31346
32106
|
const jst = new Date(now.getTime() + jstOffset);
|
|
31347
32107
|
const generatedAtJST = `${jst.getUTCFullYear()}-${String(jst.getUTCMonth() + 1).padStart(2, "0")}-${String(jst.getUTCDate()).padStart(2, "0")}T${String(jst.getUTCHours()).padStart(2, "0")}:${String(jst.getUTCMinutes()).padStart(2, "0")}:${String(jst.getUTCSeconds()).padStart(2, "0")}+09:00`;
|
|
32108
|
+
const machinesWithoutE2EMeta = machineDefinitions.filter(
|
|
32109
|
+
(def) => !def.hasE2EMeta && typeof def.id === "string"
|
|
32110
|
+
).map((def) => ({ id: def.id, sourceFile: def.sourceFile }));
|
|
32111
|
+
const warnings = collectManifestWarnings(
|
|
32112
|
+
repoRoot,
|
|
32113
|
+
platform,
|
|
32114
|
+
apiContracts,
|
|
32115
|
+
machinesWithoutE2EMeta,
|
|
32116
|
+
machineDefinitions.length
|
|
32117
|
+
);
|
|
32118
|
+
printManifestWarnings(warnings, verbose);
|
|
32119
|
+
const detectionDiagnostics = buildDetectionDiagnostics({
|
|
32120
|
+
scanSummary: diagnosticsInput?.scanSummary ?? {
|
|
32121
|
+
totalFiles: 0,
|
|
32122
|
+
filesWithMachineDefinitions: 0,
|
|
32123
|
+
filesWithMachineHooks: 0,
|
|
32124
|
+
filesWithJsxOnly: 0
|
|
32125
|
+
},
|
|
32126
|
+
resolutionDetails: diagnosticsInput?.resolutionDetails ?? [],
|
|
32127
|
+
manifestMachineIds: Object.keys(enhancedMachines),
|
|
32128
|
+
machineLinks,
|
|
32129
|
+
warnings: toDetectionWarnings(warnings)
|
|
32130
|
+
});
|
|
31348
32131
|
const unifiedManifest = {
|
|
31349
32132
|
version: manifestVersion,
|
|
31350
32133
|
generatedAt: generatedAtJST,
|
|
@@ -31357,17 +32140,15 @@ async function generateManifestFiles(manifestDir, repoRoot, verbose, resolveSche
|
|
|
31357
32140
|
authBoundaries: unifiedRegistry.authBoundaries,
|
|
31358
32141
|
machineDefinitions: unifiedRegistry.machineDefinitions,
|
|
31359
32142
|
machineLinks,
|
|
31360
|
-
apiContracts
|
|
32143
|
+
apiContracts,
|
|
32144
|
+
detectionDiagnostics
|
|
31361
32145
|
};
|
|
31362
|
-
const manifestPath =
|
|
32146
|
+
const manifestPath = path11.join(manifestsDir, "manifest.json");
|
|
31363
32147
|
await fs5.promises.writeFile(manifestPath, JSON.stringify(unifiedManifest, null, 2), "utf-8");
|
|
31364
|
-
const machinesWithoutE2EMeta = machineDefinitions.filter(
|
|
31365
|
-
(def) => !def.hasE2EMeta && typeof def.id === "string"
|
|
31366
|
-
).map((def) => ({ id: def.id, sourceFile: def.sourceFile }));
|
|
31367
32148
|
if (verbose) {
|
|
31368
|
-
console.log(`Generated selectors: ${
|
|
32149
|
+
console.log(`Generated selectors: ${path11.relative(repoRoot, tsPath)}`);
|
|
31369
32150
|
console.log(
|
|
31370
|
-
`Generated manifest: ${
|
|
32151
|
+
`Generated manifest: ${path11.relative(repoRoot, manifestPath)} (v${manifestVersion})`
|
|
31371
32152
|
);
|
|
31372
32153
|
console.log(` Platform: ${platform}`);
|
|
31373
32154
|
console.log(` Machine definitions: ${machineDefinitions.length}`);
|
|
@@ -31381,19 +32162,15 @@ async function generateManifestFiles(manifestDir, repoRoot, verbose, resolveSche
|
|
|
31381
32162
|
` API contracts: ${apiContracts.length}${schemasResolved ? " (with schemas)" : ""}`
|
|
31382
32163
|
);
|
|
31383
32164
|
console.log(` Machine links: ${machineLinks.length}`);
|
|
32165
|
+
console.log(
|
|
32166
|
+
` Detection: fallback=${detectionDiagnostics.resolution.bySource.fallback}, linkCoverage=${(detectionDiagnostics.coverage.machineLinkCoverage * 100).toFixed(1)}%`
|
|
32167
|
+
);
|
|
31384
32168
|
}
|
|
31385
|
-
const warnings = collectManifestWarnings(
|
|
31386
|
-
repoRoot,
|
|
31387
|
-
platform,
|
|
31388
|
-
apiContracts,
|
|
31389
|
-
machinesWithoutE2EMeta,
|
|
31390
|
-
machineDefinitions.length
|
|
31391
|
-
);
|
|
31392
|
-
printManifestWarnings(warnings, verbose);
|
|
31393
32169
|
return {
|
|
31394
|
-
manifestPath:
|
|
32170
|
+
manifestPath: path11.relative(repoRoot, tsPath),
|
|
31395
32171
|
machineCount: Object.keys(enhancedMachines).length,
|
|
31396
|
-
machinesWithoutE2EMeta
|
|
32172
|
+
machinesWithoutE2EMeta,
|
|
32173
|
+
detectionDiagnostics
|
|
31397
32174
|
};
|
|
31398
32175
|
}
|
|
31399
32176
|
|
|
@@ -31425,7 +32202,7 @@ function registerExistingInjections(machineIds, attrs, sourceFile) {
|
|
|
31425
32202
|
}
|
|
31426
32203
|
async function preprocessFile(filePath, repoRoot, options) {
|
|
31427
32204
|
let code = await fs5.promises.readFile(filePath, "utf-8");
|
|
31428
|
-
const relativePath =
|
|
32205
|
+
const relativePath = path11.relative(repoRoot, filePath);
|
|
31429
32206
|
collectRouteInfo(relativePath, code, options.verbose);
|
|
31430
32207
|
const hasMarker = code.includes(CLI_INJECTION_MARKER);
|
|
31431
32208
|
if (hasMarker && options.force) {
|
|
@@ -31456,14 +32233,15 @@ async function preprocessFile(filePath, repoRoot, options) {
|
|
|
31456
32233
|
injectedCount: 0,
|
|
31457
32234
|
machineIds,
|
|
31458
32235
|
changed: false,
|
|
31459
|
-
...attrs
|
|
32236
|
+
...attrs,
|
|
32237
|
+
resolutionDetails: []
|
|
31460
32238
|
}
|
|
31461
32239
|
};
|
|
31462
32240
|
}
|
|
31463
32241
|
return { code, hasMarker: false, skipped: false };
|
|
31464
32242
|
}
|
|
31465
32243
|
function transformAndRegister(filePath, code, repoRoot, options) {
|
|
31466
|
-
const relativePath =
|
|
32244
|
+
const relativePath = path11.relative(repoRoot, filePath);
|
|
31467
32245
|
const readAndParseFile = createReadAndParseFile(options.verbose);
|
|
31468
32246
|
const resolveImportPath = createResolveImportPath();
|
|
31469
32247
|
try {
|
|
@@ -31479,6 +32257,14 @@ function transformAndRegister(filePath, code, repoRoot, options) {
|
|
|
31479
32257
|
const transformedCode = markerComment + result.code;
|
|
31480
32258
|
const { actions, fields, testIds } = extractInjectedAttributes(transformedCode);
|
|
31481
32259
|
const machineIds = [...result.resolvedIds.values()];
|
|
32260
|
+
const resultWithResolution = result;
|
|
32261
|
+
const rawResolutionDetails = Array.isArray(resultWithResolution.resolutionDetails) ? resultWithResolution.resolutionDetails : [];
|
|
32262
|
+
const resolutionDetails = rawResolutionDetails.map((detail) => ({
|
|
32263
|
+
filePath: relativePath,
|
|
32264
|
+
machineRef: detail.machineRef,
|
|
32265
|
+
resolvedId: detail.resolvedId,
|
|
32266
|
+
source: detail.source
|
|
32267
|
+
}));
|
|
31482
32268
|
registerExistingInjections(machineIds, { actions, fields, testIds }, relativePath);
|
|
31483
32269
|
return {
|
|
31484
32270
|
filePath,
|
|
@@ -31488,7 +32274,8 @@ function transformAndRegister(filePath, code, repoRoot, options) {
|
|
|
31488
32274
|
transformedCode,
|
|
31489
32275
|
actions,
|
|
31490
32276
|
fields,
|
|
31491
|
-
testIds
|
|
32277
|
+
testIds,
|
|
32278
|
+
resolutionDetails
|
|
31492
32279
|
};
|
|
31493
32280
|
} catch (error) {
|
|
31494
32281
|
if (options.verbose) {
|
|
@@ -31509,7 +32296,7 @@ async function processFileForChildInjection(filePath, repoRoot, options) {
|
|
|
31509
32296
|
if (prep.skipped && prep.skipResult) return prep.skipResult;
|
|
31510
32297
|
const result = transformAndRegister(filePath, prep.code, repoRoot, options);
|
|
31511
32298
|
if (options.verbose && result.changed && result.injectedCount > 0) {
|
|
31512
|
-
const relativePath =
|
|
32299
|
+
const relativePath = path11.relative(repoRoot, filePath);
|
|
31513
32300
|
console.log(
|
|
31514
32301
|
` [child] Injected ${result.injectedCount} attrs in ${relativePath} (actions: ${result.actions.join(", ")})`
|
|
31515
32302
|
);
|
|
@@ -31529,7 +32316,7 @@ async function handleChangedFile(result, check, repoRoot, verbose, state2) {
|
|
|
31529
32316
|
await fs5.promises.writeFile(result.filePath, result.transformedCode, "utf-8");
|
|
31530
32317
|
state2.filesToFormat.push(result.filePath);
|
|
31531
32318
|
if (verbose) {
|
|
31532
|
-
const relativePath =
|
|
32319
|
+
const relativePath = path11.relative(repoRoot, result.filePath);
|
|
31533
32320
|
console.log(` Injected ${result.injectedCount} attrs in ${relativePath}`);
|
|
31534
32321
|
}
|
|
31535
32322
|
}
|
|
@@ -31540,6 +32327,60 @@ async function formatChangedFiles(filesToFormat, repoRoot, verbose) {
|
|
|
31540
32327
|
}
|
|
31541
32328
|
await formatFilesWithBiome(filesToFormat, repoRoot);
|
|
31542
32329
|
}
|
|
32330
|
+
function createScanSummary(params) {
|
|
32331
|
+
return {
|
|
32332
|
+
totalFiles: params.files.length,
|
|
32333
|
+
filesWithMachineDefinitions: params.withMachineDefinitions.length,
|
|
32334
|
+
filesWithMachineHooks: params.withMachineHooks.length,
|
|
32335
|
+
filesWithJsxOnly: params.withJsxOnly.length
|
|
32336
|
+
};
|
|
32337
|
+
}
|
|
32338
|
+
async function resolveManifestArtifacts(params) {
|
|
32339
|
+
let manifestPath;
|
|
32340
|
+
let manifestMachineCount;
|
|
32341
|
+
let machinesWithoutE2EMeta;
|
|
32342
|
+
let detectionDiagnostics;
|
|
32343
|
+
if (params.manifest && !params.check) {
|
|
32344
|
+
const diagnosticsInput = {
|
|
32345
|
+
scanSummary: params.scanSummary,
|
|
32346
|
+
resolutionDetails: params.resolutionDetails
|
|
32347
|
+
};
|
|
32348
|
+
const manifestResult = await generateManifestFiles(
|
|
32349
|
+
params.manifestDir,
|
|
32350
|
+
params.repoRoot,
|
|
32351
|
+
params.verbose,
|
|
32352
|
+
params.resolveSchemas,
|
|
32353
|
+
diagnosticsInput
|
|
32354
|
+
);
|
|
32355
|
+
manifestPath = manifestResult.manifestPath;
|
|
32356
|
+
manifestMachineCount = manifestResult.machineCount;
|
|
32357
|
+
machinesWithoutE2EMeta = manifestResult.machinesWithoutE2EMeta;
|
|
32358
|
+
detectionDiagnostics = manifestResult.detectionDiagnostics;
|
|
32359
|
+
} else if (params.manifest && params.check) {
|
|
32360
|
+
const warnings = [];
|
|
32361
|
+
detectionDiagnostics = buildDetectionDiagnostics({
|
|
32362
|
+
scanSummary: params.scanSummary,
|
|
32363
|
+
resolutionDetails: params.resolutionDetails,
|
|
32364
|
+
manifestMachineIds: [],
|
|
32365
|
+
machineLinks: [],
|
|
32366
|
+
warnings
|
|
32367
|
+
});
|
|
32368
|
+
}
|
|
32369
|
+
const strictDetectStatus = params.strictDetect ? detectionDiagnostics ? evaluateStrictDetect(detectionDiagnostics) : {
|
|
32370
|
+
enabled: true,
|
|
32371
|
+
passed: false,
|
|
32372
|
+
reasons: [
|
|
32373
|
+
"strict detect requires manifest diagnostics, but diagnostics were unavailable"
|
|
32374
|
+
]
|
|
32375
|
+
} : void 0;
|
|
32376
|
+
return {
|
|
32377
|
+
manifestPath,
|
|
32378
|
+
manifestMachineCount,
|
|
32379
|
+
machinesWithoutE2EMeta,
|
|
32380
|
+
detectionDiagnostics,
|
|
32381
|
+
strictDetectStatus
|
|
32382
|
+
};
|
|
32383
|
+
}
|
|
31543
32384
|
function mightContainMachineDefinition(code) {
|
|
31544
32385
|
return code.includes("createMachine");
|
|
31545
32386
|
}
|
|
@@ -31563,13 +32404,34 @@ async function categorizeFiles(files) {
|
|
|
31563
32404
|
return { withMachineDefinitions, withMachineHooks, withJsxOnly };
|
|
31564
32405
|
}
|
|
31565
32406
|
async function processFiles(options) {
|
|
31566
|
-
const {
|
|
32407
|
+
const {
|
|
32408
|
+
repoRoot,
|
|
32409
|
+
check,
|
|
32410
|
+
verbose,
|
|
32411
|
+
include,
|
|
32412
|
+
exclude,
|
|
32413
|
+
manifest,
|
|
32414
|
+
manifestDir,
|
|
32415
|
+
resolveSchemas,
|
|
32416
|
+
strictDetect
|
|
32417
|
+
} = options;
|
|
31567
32418
|
clearInjectionRegistry();
|
|
31568
32419
|
clearUnifiedRegistry();
|
|
31569
32420
|
const files = await discoverFiles(repoRoot, include, exclude);
|
|
31570
32421
|
if (verbose) console.log(`Found ${files.length} files to process`);
|
|
31571
|
-
const state2 = {
|
|
32422
|
+
const state2 = {
|
|
32423
|
+
changes: [],
|
|
32424
|
+
filesToFormat: [],
|
|
32425
|
+
totalInjections: 0,
|
|
32426
|
+
resolutionDetails: []
|
|
32427
|
+
};
|
|
31572
32428
|
const { withMachineDefinitions, withMachineHooks, withJsxOnly } = await categorizeFiles(files);
|
|
32429
|
+
const scanSummary = createScanSummary({
|
|
32430
|
+
files,
|
|
32431
|
+
withMachineDefinitions,
|
|
32432
|
+
withMachineHooks,
|
|
32433
|
+
withJsxOnly
|
|
32434
|
+
});
|
|
31573
32435
|
if (verbose) {
|
|
31574
32436
|
console.log(` Files with machine definitions: ${withMachineDefinitions.length}`);
|
|
31575
32437
|
console.log(` Files with machine hooks: ${withMachineHooks.length}`);
|
|
@@ -31579,46 +32441,50 @@ async function processFiles(options) {
|
|
|
31579
32441
|
for (const filePath of withMachineDefinitions) {
|
|
31580
32442
|
try {
|
|
31581
32443
|
const code = await fs5.promises.readFile(filePath, "utf-8");
|
|
31582
|
-
const relativePath =
|
|
32444
|
+
const relativePath = path11.relative(repoRoot, filePath);
|
|
31583
32445
|
collectRouteInfo(relativePath, code, verbose);
|
|
31584
32446
|
} catch {
|
|
31585
32447
|
}
|
|
31586
32448
|
}
|
|
31587
32449
|
for (const filePath of withMachineHooks) {
|
|
31588
32450
|
const result = await processFile(filePath, repoRoot, processOpts);
|
|
32451
|
+
state2.resolutionDetails.push(...result.resolutionDetails);
|
|
31589
32452
|
await handleChangedFile(result, check, repoRoot, verbose, state2);
|
|
31590
32453
|
}
|
|
31591
32454
|
for (const filePath of withJsxOnly) {
|
|
31592
32455
|
const result = await processFileForChildInjection(filePath, repoRoot, processOpts);
|
|
32456
|
+
state2.resolutionDetails.push(...result.resolutionDetails);
|
|
31593
32457
|
await handleChangedFile(result, check, repoRoot, verbose, state2);
|
|
31594
32458
|
}
|
|
31595
32459
|
if (!check) {
|
|
31596
32460
|
await formatChangedFiles(state2.filesToFormat, repoRoot, verbose);
|
|
31597
32461
|
}
|
|
31598
32462
|
postProcessRegistries();
|
|
31599
|
-
|
|
31600
|
-
|
|
31601
|
-
|
|
31602
|
-
|
|
31603
|
-
|
|
31604
|
-
|
|
31605
|
-
|
|
31606
|
-
|
|
31607
|
-
|
|
31608
|
-
|
|
31609
|
-
|
|
31610
|
-
|
|
31611
|
-
|
|
31612
|
-
|
|
32463
|
+
const manifestArtifacts = await resolveManifestArtifacts({
|
|
32464
|
+
manifest,
|
|
32465
|
+
check,
|
|
32466
|
+
manifestDir,
|
|
32467
|
+
repoRoot,
|
|
32468
|
+
verbose,
|
|
32469
|
+
resolveSchemas: resolveSchemas ?? false,
|
|
32470
|
+
strictDetect: strictDetect ?? false,
|
|
32471
|
+
scanSummary,
|
|
32472
|
+
resolutionDetails: state2.resolutionDetails
|
|
32473
|
+
});
|
|
32474
|
+
const hasCheckFailure = check && state2.changes.length > 0;
|
|
32475
|
+
const hasStrictDetectFailure = manifestArtifacts.strictDetectStatus?.passed === false;
|
|
32476
|
+
const exitCode = hasCheckFailure || hasStrictDetectFailure ? 1 : 0;
|
|
31613
32477
|
return {
|
|
31614
32478
|
totalFiles: files.length,
|
|
31615
32479
|
changedFiles: state2.changes.length,
|
|
31616
32480
|
totalInjections: state2.totalInjections,
|
|
31617
32481
|
changes: state2.changes,
|
|
31618
|
-
exitCode
|
|
31619
|
-
manifestPath,
|
|
31620
|
-
manifestMachineCount,
|
|
31621
|
-
machinesWithoutE2EMeta
|
|
32482
|
+
exitCode,
|
|
32483
|
+
manifestPath: manifestArtifacts.manifestPath,
|
|
32484
|
+
manifestMachineCount: manifestArtifacts.manifestMachineCount,
|
|
32485
|
+
machinesWithoutE2EMeta: manifestArtifacts.machinesWithoutE2EMeta,
|
|
32486
|
+
detectionDiagnostics: manifestArtifacts.detectionDiagnostics,
|
|
32487
|
+
strictDetect: manifestArtifacts.strictDetectStatus
|
|
31622
32488
|
};
|
|
31623
32489
|
}
|
|
31624
32490
|
|
|
@@ -31678,6 +32544,35 @@ function logMachinesWithoutE2EMeta(result, logger16) {
|
|
|
31678
32544
|
logger16.info(" }");
|
|
31679
32545
|
logger16.info(" See: .claude/rules/xstate-design.md#meta.e2e");
|
|
31680
32546
|
}
|
|
32547
|
+
function logDetectionDiagnostics(result, logger16) {
|
|
32548
|
+
if (!result.detectionDiagnostics) return;
|
|
32549
|
+
const diagnostics = result.detectionDiagnostics;
|
|
32550
|
+
const fallbackCount = diagnostics.resolution.bySource.fallback;
|
|
32551
|
+
const fallbackRate = (diagnostics.resolution.fallbackRate * 100).toFixed(1);
|
|
32552
|
+
const coverage = (diagnostics.coverage.machineLinkCoverage * 100).toFixed(1);
|
|
32553
|
+
logger16.info("");
|
|
32554
|
+
logger16.info(
|
|
32555
|
+
`Detection diagnostics: usages=${diagnostics.resolution.totalMachineUsages}, fallback=${fallbackCount} (${fallbackRate}%), linkCoverage=${coverage}%`
|
|
32556
|
+
);
|
|
32557
|
+
if (fallbackCount > 0) {
|
|
32558
|
+
for (const item of diagnostics.resolution.fallbackExamples.slice(0, 5)) {
|
|
32559
|
+
logger16.warn(
|
|
32560
|
+
` fallback: ${item.filePath} :: ${item.machineRef} -> ${item.resolvedId} (${item.source})`
|
|
32561
|
+
);
|
|
32562
|
+
}
|
|
32563
|
+
}
|
|
32564
|
+
}
|
|
32565
|
+
function logStrictDetect(result, logger16) {
|
|
32566
|
+
if (!result.strictDetect) return;
|
|
32567
|
+
if (result.strictDetect.passed) {
|
|
32568
|
+
logger16.success("Strict detect passed (no fallback resolution)");
|
|
32569
|
+
return;
|
|
32570
|
+
}
|
|
32571
|
+
logger16.error("Strict detect failed");
|
|
32572
|
+
for (const reason of result.strictDetect.reasons) {
|
|
32573
|
+
logger16.error(` - ${reason}`);
|
|
32574
|
+
}
|
|
32575
|
+
}
|
|
31681
32576
|
async function injectTestAttrsAction(rawOptions, command) {
|
|
31682
32577
|
const logger16 = createCLILogger("manifest");
|
|
31683
32578
|
const parseResult = InjectTestAttrsInputSchema.safeParse(rawOptions);
|
|
@@ -31688,6 +32583,20 @@ async function injectTestAttrsAction(rawOptions, command) {
|
|
|
31688
32583
|
]);
|
|
31689
32584
|
}
|
|
31690
32585
|
const options = parseResult.data;
|
|
32586
|
+
if (options.strictDetect && options.check) {
|
|
32587
|
+
throw new CLIError(
|
|
32588
|
+
"--strict-detect cannot be used with --check",
|
|
32589
|
+
"STRICT_DETECT_CHECK_UNSUPPORTED",
|
|
32590
|
+
["Run without --check to evaluate strict detection against manifest diagnostics"]
|
|
32591
|
+
);
|
|
32592
|
+
}
|
|
32593
|
+
if (options.strictDetect && !options.manifest) {
|
|
32594
|
+
throw new CLIError(
|
|
32595
|
+
"--strict-detect requires manifest generation",
|
|
32596
|
+
"STRICT_DETECT_MANIFEST_REQUIRED",
|
|
32597
|
+
["Remove --no-manifest, or disable --strict-detect"]
|
|
32598
|
+
);
|
|
32599
|
+
}
|
|
31691
32600
|
const repoRoot = findProjectRoot$1() ?? process.cwd();
|
|
31692
32601
|
if (!options.check) {
|
|
31693
32602
|
logger16.section("Generating manifest (manifest.json)");
|
|
@@ -31706,6 +32615,8 @@ async function injectTestAttrsAction(rawOptions, command) {
|
|
|
31706
32615
|
logResultSummary(result, options, logger16);
|
|
31707
32616
|
logManifestInfo(result, logger16);
|
|
31708
32617
|
logMachinesWithoutE2EMeta(result, logger16);
|
|
32618
|
+
logDetectionDiagnostics(result, logger16);
|
|
32619
|
+
logStrictDetect(result, logger16);
|
|
31709
32620
|
emitJsonSuccess(command, InjectTestAttrsOutputSchema, result);
|
|
31710
32621
|
if (result.exitCode !== 0) {
|
|
31711
32622
|
process.exit(result.exitCode);
|
|
@@ -31725,7 +32636,7 @@ var injectTestAttrsCommand = new Command("inject-test-attrs").description("Injec
|
|
|
31725
32636
|
"**/*.test.*",
|
|
31726
32637
|
"**/*.spec.*",
|
|
31727
32638
|
"**/*.stories.*"
|
|
31728
|
-
]).option("--no-manifest", "Skip E2E selector manifest generation").option("--manifest-dir <dir>", "Output directory for manifest files", ".runa").action(async (rawOptions) => {
|
|
32639
|
+
]).option("--no-manifest", "Skip E2E selector manifest generation").option("--manifest-dir <dir>", "Output directory for manifest files", ".runa").option("--strict-detect", "Fail when fallback machine detection is used (for deterministic CI)").action(async (rawOptions) => {
|
|
31729
32640
|
await injectTestAttrsAction(rawOptions, injectTestAttrsCommand);
|
|
31730
32641
|
});
|
|
31731
32642
|
|
|
@@ -31789,7 +32700,7 @@ var manifestCommand = new Command("manifest").description("Generate E2E manifest
|
|
|
31789
32700
|
"**/*.test.*",
|
|
31790
32701
|
"**/*.spec.*",
|
|
31791
32702
|
"**/*.stories.*"
|
|
31792
|
-
]).option("--no-manifest", "Skip manifest file generation (attributes only)").option("--manifest-dir <dir>", "Output directory for manifest files", ".runa").action(async (options) => {
|
|
32703
|
+
]).option("--no-manifest", "Skip manifest file generation (attributes only)").option("--manifest-dir <dir>", "Output directory for manifest files", ".runa").option("--strict-detect", "Fail when fallback machine detection is used (for deterministic CI)").action(async (options) => {
|
|
31793
32704
|
await injectTestAttrsAction(options, manifestCommand);
|
|
31794
32705
|
});
|
|
31795
32706
|
|
|
@@ -31848,7 +32759,7 @@ function assertNodeAuthToken(options) {
|
|
|
31848
32759
|
}
|
|
31849
32760
|
}
|
|
31850
32761
|
function getPackageVersion(packagePath) {
|
|
31851
|
-
const packageJsonPath =
|
|
32762
|
+
const packageJsonPath = path11__default.join(packagePath, "package.json");
|
|
31852
32763
|
if (!existsSync(packageJsonPath)) {
|
|
31853
32764
|
throw new CLIError(`package.json not found at ${packagePath}`, "PACKAGE_NOT_FOUND");
|
|
31854
32765
|
}
|
|
@@ -31867,7 +32778,7 @@ function resolveWorkspaceDependencies(workspaceRoot, packages, logger16) {
|
|
|
31867
32778
|
const sdkPackage = packages.find((p) => p.name === "SDK");
|
|
31868
32779
|
const xstatePluginPackage = packages.find((p) => p.name === "xstate-test-plugin");
|
|
31869
32780
|
if (!sdkPackage) return;
|
|
31870
|
-
const cliPackagePath =
|
|
32781
|
+
const cliPackagePath = path11__default.join(workspaceRoot, "packages", "cli", "package.json");
|
|
31871
32782
|
if (!existsSync(cliPackagePath)) return;
|
|
31872
32783
|
let content = readFileSync(cliPackagePath, "utf-8");
|
|
31873
32784
|
let updated = false;
|
|
@@ -31896,7 +32807,7 @@ function resolveWorkspaceDependencies(workspaceRoot, packages, logger16) {
|
|
|
31896
32807
|
}
|
|
31897
32808
|
}
|
|
31898
32809
|
function restoreWorkspaceDependencies(workspaceRoot) {
|
|
31899
|
-
const cliPackagePath =
|
|
32810
|
+
const cliPackagePath = path11__default.join(workspaceRoot, "packages", "cli", "package.json");
|
|
31900
32811
|
if (!existsSync(cliPackagePath)) return;
|
|
31901
32812
|
let content = readFileSync(cliPackagePath, "utf-8");
|
|
31902
32813
|
content = content.replace(/"@runa-ai\/runa": "\^[\d.]+"/, '"@runa-ai/runa": "workspace:*"');
|
|
@@ -32008,7 +32919,7 @@ function collectPackageInfo(workspaceRoot, logger16, stepNum) {
|
|
|
32008
32919
|
logger16.step("Collecting package info", stepNum);
|
|
32009
32920
|
const packages = [];
|
|
32010
32921
|
for (const pkgConfig of PUBLISHABLE_PACKAGES) {
|
|
32011
|
-
const pkgPath =
|
|
32922
|
+
const pkgPath = path11__default.join(workspaceRoot, "packages", pkgConfig.dir);
|
|
32012
32923
|
const version = getPackageVersion(pkgPath);
|
|
32013
32924
|
const exists = checkVersionExists(pkgConfig.publishName, version);
|
|
32014
32925
|
packages.push({
|
|
@@ -33397,8 +34308,8 @@ var compareActor = fromPromise(async ({ input: input3 }) => {
|
|
|
33397
34308
|
const result = compareBothFiles(
|
|
33398
34309
|
runaRelPath,
|
|
33399
34310
|
templateRelPath,
|
|
33400
|
-
|
|
33401
|
-
|
|
34311
|
+
path11.join(repoRoot, runaRelPath),
|
|
34312
|
+
path11.join(templateDir, templateRelPath),
|
|
33402
34313
|
runaFile.category,
|
|
33403
34314
|
options.diff ?? false
|
|
33404
34315
|
);
|
|
@@ -33849,7 +34760,7 @@ var discoverActor = fromPromise(async ({ input: input3 }) => {
|
|
|
33849
34760
|
const runaPattern = rule.runa;
|
|
33850
34761
|
const runaFiles = await globFiles(repoRoot, runaPattern);
|
|
33851
34762
|
for (const file of runaFiles) {
|
|
33852
|
-
const relativePath =
|
|
34763
|
+
const relativePath = path11.relative(repoRoot, file.absolutePath);
|
|
33853
34764
|
const key = generateComparisonKey(relativePath, false);
|
|
33854
34765
|
inventory.runaFiles.set(relativePath, {
|
|
33855
34766
|
...file,
|
|
@@ -33863,7 +34774,7 @@ var discoverActor = fromPromise(async ({ input: input3 }) => {
|
|
|
33863
34774
|
const templatePattern = rule.template;
|
|
33864
34775
|
const templateFiles = await globFiles(templateDir, templatePattern);
|
|
33865
34776
|
for (const file of templateFiles) {
|
|
33866
|
-
const relativePath =
|
|
34777
|
+
const relativePath = path11.relative(templateDir, file.absolutePath);
|
|
33867
34778
|
const key = generateComparisonKey(relativePath, true);
|
|
33868
34779
|
inventory.templateFiles.set(relativePath, {
|
|
33869
34780
|
...file,
|
|
@@ -33912,7 +34823,7 @@ async function matchDoubleWildcard(ctx, entries) {
|
|
|
33912
34823
|
await walkAndMatch({ ...ctx, partIndex: ctx.partIndex + 1 });
|
|
33913
34824
|
for (const entry of entries) {
|
|
33914
34825
|
if (entry.isDirectory()) {
|
|
33915
|
-
const subDir =
|
|
34826
|
+
const subDir = path11.join(ctx.currentDir, entry.name);
|
|
33916
34827
|
await walkAndMatch({ ...ctx, currentDir: subDir });
|
|
33917
34828
|
}
|
|
33918
34829
|
}
|
|
@@ -33921,7 +34832,7 @@ async function matchSingleWildcard(ctx, entries, pattern, isLastPart) {
|
|
|
33921
34832
|
const regex = patternToRegex(pattern);
|
|
33922
34833
|
for (const entry of entries) {
|
|
33923
34834
|
if (regex.test(entry.name)) {
|
|
33924
|
-
const entryPath =
|
|
34835
|
+
const entryPath = path11.join(ctx.currentDir, entry.name);
|
|
33925
34836
|
await processEntry(ctx, entryPath, entry.isFile(), entry.isDirectory(), isLastPart);
|
|
33926
34837
|
}
|
|
33927
34838
|
}
|
|
@@ -33931,14 +34842,14 @@ async function matchBraceExpansion(ctx, entries, pattern, isLastPart) {
|
|
|
33931
34842
|
for (const option of options) {
|
|
33932
34843
|
for (const entry of entries) {
|
|
33933
34844
|
if (entry.name === option) {
|
|
33934
|
-
const entryPath =
|
|
34845
|
+
const entryPath = path11.join(ctx.currentDir, entry.name);
|
|
33935
34846
|
await processEntry(ctx, entryPath, entry.isFile(), entry.isDirectory(), isLastPart);
|
|
33936
34847
|
}
|
|
33937
34848
|
}
|
|
33938
34849
|
}
|
|
33939
34850
|
}
|
|
33940
34851
|
async function matchLiteral(ctx, pattern, isLastPart) {
|
|
33941
|
-
const entryPath =
|
|
34852
|
+
const entryPath = path11.join(ctx.currentDir, pattern);
|
|
33942
34853
|
if (!fs5.existsSync(entryPath)) return;
|
|
33943
34854
|
const stats = fs5.statSync(entryPath);
|
|
33944
34855
|
await processEntry(ctx, entryPath, stats.isFile(), stats.isDirectory(), isLastPart);
|
|
@@ -34463,15 +35374,15 @@ function printActionsNeeded(logger16, actions) {
|
|
|
34463
35374
|
);
|
|
34464
35375
|
}
|
|
34465
35376
|
function findRepoRoot3(startDir) {
|
|
34466
|
-
const { existsSync:
|
|
35377
|
+
const { existsSync: existsSync52, readFileSync: readFileSync29 } = __require("fs");
|
|
34467
35378
|
const { join: join23, dirname: dirname5 } = __require("path");
|
|
34468
35379
|
let current = startDir;
|
|
34469
35380
|
while (current !== dirname5(current)) {
|
|
34470
|
-
if (
|
|
35381
|
+
if (existsSync52(join23(current, "turbo.json"))) {
|
|
34471
35382
|
return current;
|
|
34472
35383
|
}
|
|
34473
35384
|
const pkgPath = join23(current, "package.json");
|
|
34474
|
-
if (
|
|
35385
|
+
if (existsSync52(pkgPath)) {
|
|
34475
35386
|
try {
|
|
34476
35387
|
const pkg = JSON.parse(readFileSync29(pkgPath, "utf-8"));
|
|
34477
35388
|
if (pkg.workspaces) {
|
|
@@ -34539,10 +35450,10 @@ function generateReportOutput(output3, isJsonMode) {
|
|
|
34539
35450
|
};
|
|
34540
35451
|
}
|
|
34541
35452
|
function validateRunaRepo(repoRoot) {
|
|
34542
|
-
const { existsSync:
|
|
35453
|
+
const { existsSync: existsSync52 } = __require("fs");
|
|
34543
35454
|
const { join: join23 } = __require("path");
|
|
34544
35455
|
const templateDir = join23(repoRoot, "packages/runa-templates/templates");
|
|
34545
|
-
if (!
|
|
35456
|
+
if (!existsSync52(templateDir)) {
|
|
34546
35457
|
throw new CLIError("template-check is a runa-repo only command", "NOT_RUNA_REPO", [
|
|
34547
35458
|
"This command compares runa-repo with pj-repo templates",
|
|
34548
35459
|
"It should only be run in the runa repository",
|
|
@@ -34644,11 +35555,29 @@ init_esm_shims();
|
|
|
34644
35555
|
|
|
34645
35556
|
// src/commands/test/commands/test.ts
|
|
34646
35557
|
init_esm_shims();
|
|
35558
|
+
function resolveEffectiveRequireManifest(options) {
|
|
35559
|
+
if (options.requireManifest && options.allowManifestFallback) {
|
|
35560
|
+
throw new CLIError(
|
|
35561
|
+
"--require-manifest and --allow-manifest-fallback are mutually exclusive",
|
|
35562
|
+
"TEST_MANIFEST_FLAGS_EXCLUSIVE",
|
|
35563
|
+
["Use strict mode (default) OR explicitly allow fallback, but not both"]
|
|
35564
|
+
);
|
|
35565
|
+
}
|
|
35566
|
+
if (options.allowManifestFallback) return false;
|
|
35567
|
+
return options.requireManifest;
|
|
35568
|
+
}
|
|
34647
35569
|
var testCommand = new Command("test").description("Run tests (compat). Prefer: test:layer0..4 or test:static/service/integration.").option("--layer <number>", "Run specific layer (0-4)").option("--from <number>", "Run from layer N to 4").option("--to <number>", "Run from layer 0 to N").option("--ci", "CI mode (fail fast, no interactive)").option("--verbose", "Verbose output").option("--record", "Record test results to database", true).option(
|
|
34648
35570
|
"--report-json <path>",
|
|
34649
35571
|
"Export test report as JSON. If <path> is a bare filename, it will be written under .runa/tmp/."
|
|
34650
|
-
).option("--generate", "Generate tests from XState before running (integration)").option("--advanced", "Generate advanced tests (integration)").option("--auto", "Zero-config auto-generation (integration)").option("--base-url <url>", "Override base URL (integration)").option("--changed", "Run only impacted layers based on git diff (service)").option("--base <ref>", "Base ref for --changed (service)").option("--skip-missing", "Skip layers with missing dependencies instead of failing").
|
|
35572
|
+
).option("--generate", "Generate tests from XState before running (integration)").option("--advanced", "Generate advanced tests (integration)").option("--auto", "Zero-config auto-generation (integration)").option("--base-url <url>", "Override base URL (integration)").option("--changed", "Run only impacted layers based on git diff (service)").option("--base <ref>", "Base ref for --changed (service)").option("--skip-missing", "Skip layers with missing dependencies instead of failing").option(
|
|
35573
|
+
"--require-manifest",
|
|
35574
|
+
"Require manifest to be present and valid for Layer 3/4 tests (strict behavior)"
|
|
35575
|
+
).option(
|
|
35576
|
+
"--allow-manifest-fallback",
|
|
35577
|
+
"Allow fallback behavior when manifest is missing/invalid (disables Layer 4 strict default)"
|
|
35578
|
+
).action(async (options) => {
|
|
34651
35579
|
try {
|
|
35580
|
+
const effectiveRequireManifest = resolveEffectiveRequireManifest(options);
|
|
34652
35581
|
const output3 = await runTest({
|
|
34653
35582
|
layer: options.layer !== void 0 ? Number.parseInt(options.layer, 10) : void 0,
|
|
34654
35583
|
from: options.from !== void 0 ? Number.parseInt(options.from, 10) : void 0,
|
|
@@ -34663,7 +35592,8 @@ var testCommand = new Command("test").description("Run tests (compat). Prefer: t
|
|
|
34663
35592
|
baseUrl: options.baseUrl,
|
|
34664
35593
|
changed: options.changed,
|
|
34665
35594
|
base: options.base,
|
|
34666
|
-
skipMissing: options.skipMissing
|
|
35595
|
+
skipMissing: options.skipMissing,
|
|
35596
|
+
requireManifest: effectiveRequireManifest
|
|
34667
35597
|
});
|
|
34668
35598
|
emitJsonSuccess(testCommand, TestRunOutputSchema, output3);
|
|
34669
35599
|
} catch (error) {
|
|
@@ -34761,11 +35691,29 @@ var testApiCommand = new Command("test:api").description("Run Layer 3: API tests
|
|
|
34761
35691
|
|
|
34762
35692
|
// src/commands/test/commands/test-e2e.ts
|
|
34763
35693
|
init_esm_shims();
|
|
35694
|
+
function resolveEffectiveRequireManifest2(options) {
|
|
35695
|
+
if (options.requireManifest && options.allowManifestFallback) {
|
|
35696
|
+
throw new CLIError(
|
|
35697
|
+
"--require-manifest and --allow-manifest-fallback are mutually exclusive",
|
|
35698
|
+
"TEST_E2E_MANIFEST_FLAGS_EXCLUSIVE",
|
|
35699
|
+
["Use strict mode (default) OR explicitly allow fallback, but not both"]
|
|
35700
|
+
);
|
|
35701
|
+
}
|
|
35702
|
+
if (options.allowManifestFallback) return false;
|
|
35703
|
+
return options.requireManifest;
|
|
35704
|
+
}
|
|
34764
35705
|
var testE2ECommand = new Command("test:e2e").description("Run Layer 4: E2E tests (Playwright) - Requires browser").option("--generate", "Generate tests from XState before running").option("--advanced", "Generate advanced tests (context-aware, guards, invariants, etc.)").option("--auto", "Zero-config auto-generation (implementation-driven)").option("--ci", "CI mode (fail fast, no interactive)").option("--record", "Record test results to database", true).option("--base-url <url>", "Explicit base URL for E2E (disables auto dev server start)").option(
|
|
35706
|
+
"--require-manifest",
|
|
35707
|
+
"Require manifest to be present and valid (strict behavior; enabled by default for Layer 4)"
|
|
35708
|
+
).option(
|
|
35709
|
+
"--allow-manifest-fallback",
|
|
35710
|
+
"Allow fallback behavior when manifest is missing/invalid (disables Layer 4 strict default)"
|
|
35711
|
+
).option(
|
|
34765
35712
|
"--report-json <path>",
|
|
34766
35713
|
"Export test report as JSON. If <path> is a bare filename, it will be written under .runa/tmp/."
|
|
34767
35714
|
).option("--verbose", "Verbose output (full error details, environment info)").action(async (options) => {
|
|
34768
35715
|
try {
|
|
35716
|
+
const effectiveRequireManifest = resolveEffectiveRequireManifest2(options);
|
|
34769
35717
|
const output3 = await runTestIntegration({
|
|
34770
35718
|
ci: options.ci ?? false,
|
|
34771
35719
|
record: options.record,
|
|
@@ -34775,6 +35723,7 @@ var testE2ECommand = new Command("test:e2e").description("Run Layer 4: E2E tests
|
|
|
34775
35723
|
advanced: options.advanced,
|
|
34776
35724
|
auto: options.auto,
|
|
34777
35725
|
baseUrl: options.baseUrl,
|
|
35726
|
+
requireManifest: effectiveRequireManifest,
|
|
34778
35727
|
invokedAs: "runa test:e2e"
|
|
34779
35728
|
});
|
|
34780
35729
|
emitJsonSuccess(testE2ECommand, TestIntegrationOutputSchema, output3);
|
|
@@ -34795,8 +35744,15 @@ var testE2ECommand = new Command("test:e2e").description("Run Layer 4: E2E tests
|
|
|
34795
35744
|
);
|
|
34796
35745
|
}
|
|
34797
35746
|
});
|
|
34798
|
-
var testBrowserCommand = new Command("test:browser").description("Run Layer 4: Browser tests (Playwright E2E) - Alias for test:e2e").option("--generate", "Generate tests from XState before running").option("--advanced", "Generate advanced tests").option("--auto", "Zero-config auto-generation").option("--ci", "CI mode (fail fast, no interactive)").option("--record", "Record test results to database", true).option("--base-url <url>", "Explicit base URL for E2E").option(
|
|
35747
|
+
var testBrowserCommand = new Command("test:browser").description("Run Layer 4: Browser tests (Playwright E2E) - Alias for test:e2e").option("--generate", "Generate tests from XState before running").option("--advanced", "Generate advanced tests").option("--auto", "Zero-config auto-generation").option("--ci", "CI mode (fail fast, no interactive)").option("--record", "Record test results to database", true).option("--base-url <url>", "Explicit base URL for E2E").option(
|
|
35748
|
+
"--require-manifest",
|
|
35749
|
+
"Require manifest to be present and valid (strict behavior; enabled by default for Layer 4)"
|
|
35750
|
+
).option(
|
|
35751
|
+
"--allow-manifest-fallback",
|
|
35752
|
+
"Allow fallback behavior when manifest is missing/invalid (disables Layer 4 strict default)"
|
|
35753
|
+
).option("--report-json <path>", "Export test report as JSON").option("--verbose", "Verbose output").action(async (options) => {
|
|
34799
35754
|
try {
|
|
35755
|
+
const effectiveRequireManifest = resolveEffectiveRequireManifest2(options);
|
|
34800
35756
|
const output3 = await runTestIntegration({
|
|
34801
35757
|
ci: options.ci ?? false,
|
|
34802
35758
|
record: options.record,
|
|
@@ -34806,6 +35762,7 @@ var testBrowserCommand = new Command("test:browser").description("Run Layer 4: B
|
|
|
34806
35762
|
advanced: options.advanced,
|
|
34807
35763
|
auto: options.auto,
|
|
34808
35764
|
baseUrl: options.baseUrl,
|
|
35765
|
+
requireManifest: effectiveRequireManifest,
|
|
34809
35766
|
invokedAs: "runa test:browser"
|
|
34810
35767
|
});
|
|
34811
35768
|
emitJsonSuccess(testBrowserCommand, TestIntegrationOutputSchema, output3);
|
|
@@ -34878,11 +35835,29 @@ var testLintCommand = new Command("test:lint").description("Run Layer 0: Lint (t
|
|
|
34878
35835
|
|
|
34879
35836
|
// src/commands/test/commands/test-integration.ts
|
|
34880
35837
|
init_esm_shims();
|
|
35838
|
+
function resolveEffectiveRequireManifest3(options) {
|
|
35839
|
+
if (options.requireManifest && options.allowManifestFallback) {
|
|
35840
|
+
throw new CLIError(
|
|
35841
|
+
"--require-manifest and --allow-manifest-fallback are mutually exclusive",
|
|
35842
|
+
"TEST_INTEGRATION_MANIFEST_FLAGS_EXCLUSIVE",
|
|
35843
|
+
["Use strict mode (default) OR explicitly allow fallback, but not both"]
|
|
35844
|
+
);
|
|
35845
|
+
}
|
|
35846
|
+
if (options.allowManifestFallback) return false;
|
|
35847
|
+
return options.requireManifest;
|
|
35848
|
+
}
|
|
34881
35849
|
var testIntegrationCommand = new Command("test:integration").description("Run Layer 4: Integration tests (E2E) with auto-generation").option("--generate", "Generate tests from XState before running").option("--advanced", "Generate advanced tests (context-aware, guards, invariants, etc.)").option("--auto", "Zero-config auto-generation (implementation-driven)").option("--ci", "CI mode (fail fast, no interactive)").option("--record", "Record test results to database", true).option("--base-url <url>", "Explicit base URL for E2E/Security (disables auto dev server start)").option(
|
|
35850
|
+
"--require-manifest",
|
|
35851
|
+
"Require manifest to be present and valid (strict behavior; enabled by default for Layer 4)"
|
|
35852
|
+
).option(
|
|
35853
|
+
"--allow-manifest-fallback",
|
|
35854
|
+
"Allow fallback behavior when manifest is missing/invalid (disables Layer 4 strict default)"
|
|
35855
|
+
).option(
|
|
34882
35856
|
"--report-json <path>",
|
|
34883
35857
|
"Export test report as JSON. If <path> is a bare filename, it will be written under .runa/tmp/."
|
|
34884
35858
|
).option("--verbose", "Verbose output (full error details, environment info)").action(async (options) => {
|
|
34885
35859
|
try {
|
|
35860
|
+
const effectiveRequireManifest = resolveEffectiveRequireManifest3(options);
|
|
34886
35861
|
const output3 = await runTestIntegration({
|
|
34887
35862
|
ci: options.ci ?? false,
|
|
34888
35863
|
record: options.record,
|
|
@@ -34892,6 +35867,7 @@ var testIntegrationCommand = new Command("test:integration").description("Run La
|
|
|
34892
35867
|
advanced: options.advanced,
|
|
34893
35868
|
auto: options.auto,
|
|
34894
35869
|
baseUrl: options.baseUrl,
|
|
35870
|
+
requireManifest: effectiveRequireManifest,
|
|
34895
35871
|
invokedAs: "runa test:integration"
|
|
34896
35872
|
});
|
|
34897
35873
|
emitJsonSuccess(testIntegrationCommand, TestIntegrationOutputSchema, output3);
|
|
@@ -34937,7 +35913,7 @@ function injectTestAttrsIfLayer4(layer, options) {
|
|
|
34937
35913
|
"Run `runa inject-test-attrs` manually to see detailed errors",
|
|
34938
35914
|
"Ensure XState machines exist in your project",
|
|
34939
35915
|
"Check that machines have valid meta.e2e definitions",
|
|
34940
|
-
"Or
|
|
35916
|
+
"Or pass --allow-manifest-fallback to continue with fallback behavior"
|
|
34941
35917
|
],
|
|
34942
35918
|
error instanceof Error ? error : void 0
|
|
34943
35919
|
);
|
|
@@ -34947,10 +35923,17 @@ function injectTestAttrsIfLayer4(layer, options) {
|
|
|
34947
35923
|
}
|
|
34948
35924
|
}
|
|
34949
35925
|
}
|
|
35926
|
+
function resolveEffectiveRequireManifest4(layer, options) {
|
|
35927
|
+
if (options.allowManifestFallback) return false;
|
|
35928
|
+
if (options.requireManifest !== void 0) return options.requireManifest;
|
|
35929
|
+
if (layer === 4 && process.env.RUNA_LAYER4_ALLOW_MANIFEST_FALLBACK !== "true") return true;
|
|
35930
|
+
return void 0;
|
|
35931
|
+
}
|
|
34950
35932
|
async function runSingleLayer(params) {
|
|
35933
|
+
const effectiveRequireManifest = resolveEffectiveRequireManifest4(params.layer, params.options);
|
|
34951
35934
|
injectTestAttrsIfLayer4(params.layer, {
|
|
34952
35935
|
verbose: params.options.verbose,
|
|
34953
|
-
requireManifest:
|
|
35936
|
+
requireManifest: effectiveRequireManifest
|
|
34954
35937
|
});
|
|
34955
35938
|
try {
|
|
34956
35939
|
const output3 = await runTest({
|
|
@@ -34967,7 +35950,7 @@ async function runSingleLayer(params) {
|
|
|
34967
35950
|
reviewSnapshots: params.options.reviewSnapshots,
|
|
34968
35951
|
forceRegenerate: params.options.force,
|
|
34969
35952
|
skipGeneration: params.options.skipGeneration,
|
|
34970
|
-
requireManifest:
|
|
35953
|
+
requireManifest: effectiveRequireManifest,
|
|
34971
35954
|
invokedAs: `runa test:layer${params.layer}`
|
|
34972
35955
|
});
|
|
34973
35956
|
emitJsonSuccess(params.cmd, TestRunOutputSchema, output3);
|
|
@@ -35001,6 +35984,13 @@ function validateGenerationFlags(layer, opts) {
|
|
|
35001
35984
|
["Use --auto (implementation-driven) OR --generate/--advanced (XState-driven)"]
|
|
35002
35985
|
);
|
|
35003
35986
|
}
|
|
35987
|
+
if (layer === 4 && opts.requireManifest && opts.allowManifestFallback) {
|
|
35988
|
+
throw new CLIError(
|
|
35989
|
+
"--require-manifest and --allow-manifest-fallback are mutually exclusive",
|
|
35990
|
+
"TEST_LAYER_MANIFEST_FLAGS_EXCLUSIVE",
|
|
35991
|
+
["Use strict mode (default) OR explicitly allow fallback, but not both"]
|
|
35992
|
+
);
|
|
35993
|
+
}
|
|
35004
35994
|
}
|
|
35005
35995
|
function validateSnapshotFlags(opts) {
|
|
35006
35996
|
if (opts.reviewSnapshots && opts.ci) {
|
|
@@ -35039,6 +36029,9 @@ function createLayerCommand(layer) {
|
|
|
35039
36029
|
cmd.option("--generate", "Generate tests from XState before running (Layer 4)").option("--advanced", "Generate advanced tests (requires --generate)").option("--auto", "Zero-config auto-generation (implementation-driven)").option("--update-snapshots", "Update visual baselines (Layer 4)").option("--filter <pattern>", 'Filter tests by pattern (e.g., "checkout-*")').option("--review-snapshots", "Interactive review of changed visual snapshots (Layer 4)").option("--force", "Force regenerate all tests, bypassing cache").option(
|
|
35040
36030
|
"--require-manifest",
|
|
35041
36031
|
"Require manifest to be present and valid (fail if missing or stale)"
|
|
36032
|
+
).option(
|
|
36033
|
+
"--allow-manifest-fallback",
|
|
36034
|
+
"Allow fallback behavior when manifest is missing/invalid (disables Layer 4 strict default)"
|
|
35042
36035
|
);
|
|
35043
36036
|
}
|
|
35044
36037
|
return cmd;
|
|
@@ -36887,8 +37880,8 @@ var logger13 = createCLILogger("watch");
|
|
|
36887
37880
|
function addFallbackSchemaPatterns(patterns) {
|
|
36888
37881
|
const detected = detectDatabasePackage(process.cwd());
|
|
36889
37882
|
if (detected) {
|
|
36890
|
-
patterns.push(
|
|
36891
|
-
patterns.push(
|
|
37883
|
+
patterns.push(path11__default.join(detected, "src/schema/**/*.ts"));
|
|
37884
|
+
patterns.push(path11__default.join(detected, "sql/**/*.sql"));
|
|
36892
37885
|
return;
|
|
36893
37886
|
}
|
|
36894
37887
|
for (const candidate of DATABASE_PACKAGE_CANDIDATES) {
|
|
@@ -36902,8 +37895,8 @@ async function buildSchemaPatterns() {
|
|
|
36902
37895
|
const patterns = ["supabase/schemas/**/*.sql"];
|
|
36903
37896
|
try {
|
|
36904
37897
|
const dbPackagePath = await getDatabasePackagePath();
|
|
36905
|
-
patterns.push(
|
|
36906
|
-
patterns.push(
|
|
37898
|
+
patterns.push(path11__default.join(dbPackagePath, "src/schema/**/*.ts"));
|
|
37899
|
+
patterns.push(path11__default.join(dbPackagePath, "sql/**/*.sql"));
|
|
36907
37900
|
} catch {
|
|
36908
37901
|
addFallbackSchemaPatterns(patterns);
|
|
36909
37902
|
}
|