@runa-ai/runa-cli 0.5.51 → 0.5.52
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/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/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 +1162 -559
- package/package.json +1 -1
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.52";
|
|
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) {
|
|
@@ -5329,7 +5561,7 @@ function pipeToSharedLog(params) {
|
|
|
5329
5561
|
}
|
|
5330
5562
|
async function ensureRunaTmpDir(cwd) {
|
|
5331
5563
|
const root = cwd ?? process.cwd();
|
|
5332
|
-
const dir =
|
|
5564
|
+
const dir = path11__default.join(root, ".runa", "tmp");
|
|
5333
5565
|
await mkdir(dir, { recursive: true });
|
|
5334
5566
|
return dir;
|
|
5335
5567
|
}
|
|
@@ -5354,7 +5586,7 @@ var buildActor = fromPromise(
|
|
|
5354
5586
|
const { repoRoot, tmpDir, hasTurbo, e2e, env: env2 = process.env } = input3;
|
|
5355
5587
|
const startTime = Date.now();
|
|
5356
5588
|
try {
|
|
5357
|
-
const fullTmpDir =
|
|
5589
|
+
const fullTmpDir = path11__default.join(repoRoot, tmpDir);
|
|
5358
5590
|
await mkdir(fullTmpDir, { recursive: true });
|
|
5359
5591
|
const buildEnv = { ...env2 };
|
|
5360
5592
|
if (e2e) {
|
|
@@ -5363,7 +5595,7 @@ var buildActor = fromPromise(
|
|
|
5363
5595
|
buildEnv.TURBO_FORCE = "true";
|
|
5364
5596
|
console.log(" E2E mode enabled: NEXT_PUBLIC_E2E_TEST=true, TURBO_FORCE=true");
|
|
5365
5597
|
}
|
|
5366
|
-
const hasApps = existsSync(
|
|
5598
|
+
const hasApps = existsSync(path11__default.join(repoRoot, "apps"));
|
|
5367
5599
|
let args;
|
|
5368
5600
|
if (hasTurbo) {
|
|
5369
5601
|
args = hasApps ? ["turbo", "run", "build", "--filter=./apps/*", "--filter=./packages/*"] : ["turbo", "run", "build"];
|
|
@@ -5376,7 +5608,7 @@ var buildActor = fromPromise(
|
|
|
5376
5608
|
label: "build",
|
|
5377
5609
|
command: "pnpm",
|
|
5378
5610
|
args,
|
|
5379
|
-
logFile:
|
|
5611
|
+
logFile: path11__default.join(fullTmpDir, "build.log")
|
|
5380
5612
|
});
|
|
5381
5613
|
return {
|
|
5382
5614
|
passed: true,
|
|
@@ -5615,7 +5847,7 @@ function deleteIfExists(fullPath, displayPath, verbose) {
|
|
|
5615
5847
|
function deleteRootDirectories(repoRoot, dirs, verbose) {
|
|
5616
5848
|
const deleted = [];
|
|
5617
5849
|
for (const dir of dirs) {
|
|
5618
|
-
const fullPath =
|
|
5850
|
+
const fullPath = path11__default.join(repoRoot, dir);
|
|
5619
5851
|
if (deleteIfExists(fullPath, dir, verbose)) {
|
|
5620
5852
|
deleted.push(dir);
|
|
5621
5853
|
}
|
|
@@ -5626,13 +5858,13 @@ function cleanMonorepoPackages(repoRoot, dirs, verbose) {
|
|
|
5626
5858
|
const deleted = [];
|
|
5627
5859
|
const subdirs = ["apps", "packages"];
|
|
5628
5860
|
for (const subdir of subdirs) {
|
|
5629
|
-
const subdirPath =
|
|
5861
|
+
const subdirPath = path11__default.join(repoRoot, subdir);
|
|
5630
5862
|
if (!existsSync(subdirPath)) continue;
|
|
5631
5863
|
const entries = __require("fs").readdirSync(subdirPath, { withFileTypes: true });
|
|
5632
5864
|
for (const entry of entries) {
|
|
5633
5865
|
if (!entry.isDirectory()) continue;
|
|
5634
5866
|
for (const dir of dirs) {
|
|
5635
|
-
const targetPath =
|
|
5867
|
+
const targetPath = path11__default.join(subdirPath, entry.name, dir);
|
|
5636
5868
|
const displayPath = `${subdir}/${entry.name}/${dir}`;
|
|
5637
5869
|
if (deleteIfExists(targetPath, displayPath, verbose)) {
|
|
5638
5870
|
deleted.push(displayPath);
|
|
@@ -5651,7 +5883,7 @@ var cleanActor = fromPromise(
|
|
|
5651
5883
|
async ({ input: input3 }) => {
|
|
5652
5884
|
const { repoRoot, tmpDir, verbose = false } = input3;
|
|
5653
5885
|
try {
|
|
5654
|
-
await mkdir(
|
|
5886
|
+
await mkdir(path11__default.join(repoRoot, tmpDir), { recursive: true });
|
|
5655
5887
|
if (verbose) {
|
|
5656
5888
|
console.log("Cleaning build caches...");
|
|
5657
5889
|
}
|
|
@@ -5673,7 +5905,7 @@ var freshActor = fromPromise(
|
|
|
5673
5905
|
async ({ input: input3 }) => {
|
|
5674
5906
|
const { repoRoot, tmpDir, verbose = false } = input3;
|
|
5675
5907
|
try {
|
|
5676
|
-
await mkdir(
|
|
5908
|
+
await mkdir(path11__default.join(repoRoot, tmpDir), { recursive: true });
|
|
5677
5909
|
if (verbose) {
|
|
5678
5910
|
console.log("Fresh install: cleaning all caches and node_modules...");
|
|
5679
5911
|
}
|
|
@@ -5745,7 +5977,7 @@ var dbSyncActor = fromPromise(
|
|
|
5745
5977
|
const { repoRoot, tmpDir, env: env2 = process.env } = input3;
|
|
5746
5978
|
const startTime = Date.now();
|
|
5747
5979
|
try {
|
|
5748
|
-
const fullTmpDir =
|
|
5980
|
+
const fullTmpDir = path11__default.join(repoRoot, tmpDir);
|
|
5749
5981
|
await mkdir(fullTmpDir, { recursive: true });
|
|
5750
5982
|
await runLogged({
|
|
5751
5983
|
cwd: repoRoot,
|
|
@@ -5753,7 +5985,7 @@ var dbSyncActor = fromPromise(
|
|
|
5753
5985
|
label: "db-sync",
|
|
5754
5986
|
command: "pnpm",
|
|
5755
5987
|
args: ["exec", "runa", "db", "sync", "--auto-approve"],
|
|
5756
|
-
logFile:
|
|
5988
|
+
logFile: path11__default.join(fullTmpDir, "db-sync.log")
|
|
5757
5989
|
});
|
|
5758
5990
|
return {
|
|
5759
5991
|
passed: true,
|
|
@@ -5789,7 +6021,7 @@ var manifestActor = fromPromise(
|
|
|
5789
6021
|
const { repoRoot, tmpDir, env: env2 = process.env } = input3;
|
|
5790
6022
|
const startTime = Date.now();
|
|
5791
6023
|
try {
|
|
5792
|
-
const fullTmpDir =
|
|
6024
|
+
const fullTmpDir = path11__default.join(repoRoot, tmpDir);
|
|
5793
6025
|
await mkdir(fullTmpDir, { recursive: true });
|
|
5794
6026
|
await runLogged({
|
|
5795
6027
|
cwd: repoRoot,
|
|
@@ -5797,7 +6029,7 @@ var manifestActor = fromPromise(
|
|
|
5797
6029
|
label: "manifest",
|
|
5798
6030
|
command: "pnpm",
|
|
5799
6031
|
args: ["exec", "runa", "manifest"],
|
|
5800
|
-
logFile:
|
|
6032
|
+
logFile: path11__default.join(fullTmpDir, "manifest.log")
|
|
5801
6033
|
});
|
|
5802
6034
|
return {
|
|
5803
6035
|
passed: true,
|
|
@@ -6061,7 +6293,7 @@ function buildOutput(typeCheckPassed, lintPassed, typeCheckDurationMs, lintDurat
|
|
|
6061
6293
|
var staticChecksActor = fromPromise(
|
|
6062
6294
|
async ({ input: input3 }) => {
|
|
6063
6295
|
const { repoRoot, tmpDir, hasTurbo, env: env2 = process.env, skipTypes, skipLint } = input3;
|
|
6064
|
-
const fullTmpDir =
|
|
6296
|
+
const fullTmpDir = path11__default.join(repoRoot, tmpDir);
|
|
6065
6297
|
await mkdir(fullTmpDir, { recursive: true });
|
|
6066
6298
|
const startTime = Date.now();
|
|
6067
6299
|
const checksToRun = [];
|
|
@@ -6075,7 +6307,7 @@ var staticChecksActor = fromPromise(
|
|
|
6075
6307
|
label: "type-check",
|
|
6076
6308
|
command: cmd.command,
|
|
6077
6309
|
args: cmd.args,
|
|
6078
|
-
logFile:
|
|
6310
|
+
logFile: path11__default.join(fullTmpDir, "type-check.log")
|
|
6079
6311
|
})
|
|
6080
6312
|
);
|
|
6081
6313
|
checkOrder.push("type-check");
|
|
@@ -6089,7 +6321,7 @@ var staticChecksActor = fromPromise(
|
|
|
6089
6321
|
label: "lint",
|
|
6090
6322
|
command: cmd.command,
|
|
6091
6323
|
args: cmd.args,
|
|
6092
|
-
logFile:
|
|
6324
|
+
logFile: path11__default.join(fullTmpDir, "lint.log")
|
|
6093
6325
|
})
|
|
6094
6326
|
);
|
|
6095
6327
|
checkOrder.push("lint");
|
|
@@ -6133,14 +6365,14 @@ var validateActor = fromPromise(
|
|
|
6133
6365
|
let manifestsExist = true;
|
|
6134
6366
|
const buildOutputPaths = [
|
|
6135
6367
|
// Single app
|
|
6136
|
-
|
|
6137
|
-
|
|
6368
|
+
path11__default.join(repoRoot, ".next"),
|
|
6369
|
+
path11__default.join(repoRoot, "dist"),
|
|
6138
6370
|
// Monorepo apps
|
|
6139
|
-
|
|
6140
|
-
|
|
6371
|
+
path11__default.join(repoRoot, "apps", "web", ".next"),
|
|
6372
|
+
path11__default.join(repoRoot, "apps", "dashboard", ".next"),
|
|
6141
6373
|
// Packages
|
|
6142
|
-
|
|
6143
|
-
|
|
6374
|
+
path11__default.join(repoRoot, "packages", "cli", "dist"),
|
|
6375
|
+
path11__default.join(repoRoot, "packages", "sdk", "dist")
|
|
6144
6376
|
];
|
|
6145
6377
|
for (const outputPath of buildOutputPaths) {
|
|
6146
6378
|
if (existsSync(outputPath)) {
|
|
@@ -6154,7 +6386,7 @@ var validateActor = fromPromise(
|
|
|
6154
6386
|
);
|
|
6155
6387
|
}
|
|
6156
6388
|
if (!skipManifest) {
|
|
6157
|
-
const manifestPath =
|
|
6389
|
+
const manifestPath = path11__default.join(repoRoot, ".runa", "manifests", "manifest.json");
|
|
6158
6390
|
if (!existsSync(manifestPath)) {
|
|
6159
6391
|
manifestsExist = false;
|
|
6160
6392
|
warnings.push("No manifest.json found. E2E test generation may be affected.");
|
|
@@ -6213,35 +6445,35 @@ function isE2EMode({ context }) {
|
|
|
6213
6445
|
return context.input.e2e;
|
|
6214
6446
|
}
|
|
6215
6447
|
function detectDatabase(repoRoot) {
|
|
6216
|
-
const configPath =
|
|
6448
|
+
const configPath = path11__default.join(repoRoot, "runa.config.ts");
|
|
6217
6449
|
if (!existsSync(configPath)) {
|
|
6218
|
-
const jsConfigPath =
|
|
6450
|
+
const jsConfigPath = path11__default.join(repoRoot, "runa.config.js");
|
|
6219
6451
|
if (!existsSync(jsConfigPath)) {
|
|
6220
6452
|
return false;
|
|
6221
6453
|
}
|
|
6222
6454
|
}
|
|
6223
|
-
const supabaseDir =
|
|
6455
|
+
const supabaseDir = path11__default.join(repoRoot, "supabase");
|
|
6224
6456
|
if (existsSync(supabaseDir)) {
|
|
6225
6457
|
return true;
|
|
6226
6458
|
}
|
|
6227
|
-
const databasePkg =
|
|
6459
|
+
const databasePkg = path11__default.join(repoRoot, "packages", "database");
|
|
6228
6460
|
if (existsSync(databasePkg)) {
|
|
6229
6461
|
return true;
|
|
6230
6462
|
}
|
|
6231
6463
|
return false;
|
|
6232
6464
|
}
|
|
6233
6465
|
function detectManifestTask(repoRoot) {
|
|
6234
|
-
const runaConfigPath =
|
|
6466
|
+
const runaConfigPath = path11__default.join(repoRoot, "runa.config.ts");
|
|
6235
6467
|
if (existsSync(runaConfigPath)) {
|
|
6236
6468
|
return true;
|
|
6237
6469
|
}
|
|
6238
|
-
const rootPkgPath =
|
|
6470
|
+
const rootPkgPath = path11__default.join(repoRoot, "package.json");
|
|
6239
6471
|
if (hasXStateDependency(rootPkgPath)) {
|
|
6240
6472
|
return true;
|
|
6241
6473
|
}
|
|
6242
6474
|
const appDirs = ["apps/web", "apps/app", "apps/dashboard", "app"];
|
|
6243
6475
|
for (const appDir of appDirs) {
|
|
6244
|
-
const appPkgPath =
|
|
6476
|
+
const appPkgPath = path11__default.join(repoRoot, appDir, "package.json");
|
|
6245
6477
|
if (hasXStateDependency(appPkgPath)) {
|
|
6246
6478
|
return true;
|
|
6247
6479
|
}
|
|
@@ -6268,7 +6500,7 @@ function hasXStateDependency(pkgPath) {
|
|
|
6268
6500
|
}
|
|
6269
6501
|
}
|
|
6270
6502
|
function detectTurbo(repoRoot) {
|
|
6271
|
-
const turboJsonPath =
|
|
6503
|
+
const turboJsonPath = path11__default.join(repoRoot, "turbo.json");
|
|
6272
6504
|
return existsSync(turboJsonPath);
|
|
6273
6505
|
}
|
|
6274
6506
|
var guards = {
|
|
@@ -7276,15 +7508,15 @@ function printSummary(logger16, output3) {
|
|
|
7276
7508
|
}
|
|
7277
7509
|
}
|
|
7278
7510
|
function findRepoRoot(startDir) {
|
|
7279
|
-
const { existsSync:
|
|
7511
|
+
const { existsSync: existsSync52, readFileSync: readFileSync29 } = __require("fs");
|
|
7280
7512
|
const { join: join23, dirname: dirname5 } = __require("path");
|
|
7281
7513
|
let current = startDir;
|
|
7282
7514
|
while (current !== dirname5(current)) {
|
|
7283
|
-
if (
|
|
7515
|
+
if (existsSync52(join23(current, "turbo.json"))) {
|
|
7284
7516
|
return current;
|
|
7285
7517
|
}
|
|
7286
7518
|
const pkgPath = join23(current, "package.json");
|
|
7287
|
-
if (
|
|
7519
|
+
if (existsSync52(pkgPath)) {
|
|
7288
7520
|
try {
|
|
7289
7521
|
const pkg = JSON.parse(readFileSync29(pkgPath, "utf-8"));
|
|
7290
7522
|
if (pkg.workspaces) {
|
|
@@ -7527,42 +7759,42 @@ function resolvePolicy(_config, _repoKind) {
|
|
|
7527
7759
|
};
|
|
7528
7760
|
}
|
|
7529
7761
|
function detectRepoKind(repoRoot) {
|
|
7530
|
-
const hasApps = existsSync(
|
|
7531
|
-
const hasPackages = existsSync(
|
|
7532
|
-
const hasTurbo = existsSync(
|
|
7762
|
+
const hasApps = existsSync(path11__default.join(repoRoot, "apps"));
|
|
7763
|
+
const hasPackages = existsSync(path11__default.join(repoRoot, "packages"));
|
|
7764
|
+
const hasTurbo = existsSync(path11__default.join(repoRoot, "turbo.json"));
|
|
7533
7765
|
if (hasApps || hasPackages || hasTurbo) {
|
|
7534
7766
|
return "monorepo";
|
|
7535
7767
|
}
|
|
7536
|
-
const hasSrc = existsSync(
|
|
7537
|
-
const hasApp = existsSync(
|
|
7768
|
+
const hasSrc = existsSync(path11__default.join(repoRoot, "src"));
|
|
7769
|
+
const hasApp = existsSync(path11__default.join(repoRoot, "app"));
|
|
7538
7770
|
if (hasSrc || hasApp) {
|
|
7539
7771
|
return "pj-repo";
|
|
7540
7772
|
}
|
|
7541
7773
|
return "unknown";
|
|
7542
7774
|
}
|
|
7543
7775
|
function hasKnownFrameworkConfig(dir) {
|
|
7544
|
-
if (existsSync(
|
|
7776
|
+
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
7777
|
return true;
|
|
7546
7778
|
}
|
|
7547
|
-
if (existsSync(
|
|
7779
|
+
if (existsSync(path11__default.join(dir, "vite.config.js")) || existsSync(path11__default.join(dir, "vite.config.ts"))) {
|
|
7548
7780
|
return true;
|
|
7549
7781
|
}
|
|
7550
|
-
if (existsSync(
|
|
7782
|
+
if (existsSync(path11__default.join(dir, "remix.config.js"))) {
|
|
7551
7783
|
return true;
|
|
7552
7784
|
}
|
|
7553
|
-
if (existsSync(
|
|
7785
|
+
if (existsSync(path11__default.join(dir, "astro.config.mjs")) || existsSync(path11__default.join(dir, "astro.config.ts"))) {
|
|
7554
7786
|
return true;
|
|
7555
7787
|
}
|
|
7556
|
-
if (existsSync(
|
|
7788
|
+
if (existsSync(path11__default.join(dir, "nuxt.config.js")) || existsSync(path11__default.join(dir, "nuxt.config.ts"))) {
|
|
7557
7789
|
return true;
|
|
7558
7790
|
}
|
|
7559
|
-
if (existsSync(
|
|
7791
|
+
if (existsSync(path11__default.join(dir, "svelte.config.js"))) {
|
|
7560
7792
|
return true;
|
|
7561
7793
|
}
|
|
7562
7794
|
return false;
|
|
7563
7795
|
}
|
|
7564
7796
|
function hasPackageJson(dir) {
|
|
7565
|
-
return existsSync(
|
|
7797
|
+
return existsSync(path11__default.join(dir, "package.json"));
|
|
7566
7798
|
}
|
|
7567
7799
|
var PORT_PATTERNS = [
|
|
7568
7800
|
/-p\s*([0-9]+)/i,
|
|
@@ -7582,7 +7814,7 @@ function extractPortFromScript(script) {
|
|
|
7582
7814
|
return null;
|
|
7583
7815
|
}
|
|
7584
7816
|
function readPortFromScripts(appDir) {
|
|
7585
|
-
const pkgPath =
|
|
7817
|
+
const pkgPath = path11__default.join(appDir, "package.json");
|
|
7586
7818
|
if (!existsSync(pkgPath)) return 3e3;
|
|
7587
7819
|
try {
|
|
7588
7820
|
const { readFileSync: readFileSync29 } = __require("fs");
|
|
@@ -7601,30 +7833,30 @@ function readPortFromScripts(appDir) {
|
|
|
7601
7833
|
return 3e3;
|
|
7602
7834
|
}
|
|
7603
7835
|
function findWebAppUnderApps(repoRoot) {
|
|
7604
|
-
const appsDir =
|
|
7836
|
+
const appsDir = path11__default.join(repoRoot, "apps");
|
|
7605
7837
|
if (!existsSync(appsDir)) return null;
|
|
7606
7838
|
try {
|
|
7607
7839
|
const { readdirSync: readdirSync12 } = __require("fs");
|
|
7608
7840
|
const entries = readdirSync12(appsDir, { withFileTypes: true });
|
|
7609
7841
|
const priority = ["web", "dashboard", "app", "frontend", "client"];
|
|
7610
7842
|
for (const name of priority) {
|
|
7611
|
-
const candidate =
|
|
7843
|
+
const candidate = path11__default.join(appsDir, name);
|
|
7612
7844
|
if (hasKnownFrameworkConfig(candidate)) return candidate;
|
|
7613
7845
|
}
|
|
7614
7846
|
for (const entry of entries) {
|
|
7615
7847
|
if (!entry.isDirectory()) continue;
|
|
7616
7848
|
if (priority.includes(entry.name)) continue;
|
|
7617
|
-
const candidate =
|
|
7849
|
+
const candidate = path11__default.join(appsDir, entry.name);
|
|
7618
7850
|
if (hasKnownFrameworkConfig(candidate)) return candidate;
|
|
7619
7851
|
}
|
|
7620
7852
|
for (const name of priority) {
|
|
7621
|
-
const candidate =
|
|
7853
|
+
const candidate = path11__default.join(appsDir, name);
|
|
7622
7854
|
if (hasPackageJson(candidate)) return candidate;
|
|
7623
7855
|
}
|
|
7624
7856
|
for (const entry of entries) {
|
|
7625
7857
|
if (!entry.isDirectory()) continue;
|
|
7626
7858
|
if (priority.includes(entry.name)) continue;
|
|
7627
|
-
const candidate =
|
|
7859
|
+
const candidate = path11__default.join(appsDir, entry.name);
|
|
7628
7860
|
if (hasPackageJson(candidate)) return candidate;
|
|
7629
7861
|
}
|
|
7630
7862
|
} catch {
|
|
@@ -7633,7 +7865,7 @@ function findWebAppUnderApps(repoRoot) {
|
|
|
7633
7865
|
}
|
|
7634
7866
|
function detectApp(repoRoot, configOverride) {
|
|
7635
7867
|
if (configOverride?.directory && configOverride.directory !== ".") {
|
|
7636
|
-
const configuredDir =
|
|
7868
|
+
const configuredDir = path11__default.join(repoRoot, configOverride.directory);
|
|
7637
7869
|
if (existsSync(configuredDir) && hasPackageJson(configuredDir)) {
|
|
7638
7870
|
return {
|
|
7639
7871
|
appDir: configuredDir,
|
|
@@ -7768,7 +8000,7 @@ function pipeChildOutputToLog(params) {
|
|
|
7768
8000
|
return { cleanup };
|
|
7769
8001
|
}
|
|
7770
8002
|
async function writeEnvLocal(params) {
|
|
7771
|
-
const filePath =
|
|
8003
|
+
const filePath = path11__default.join(params.appDir, ".env.local");
|
|
7772
8004
|
const content = `${Object.entries(params.values).map(([k, v]) => `${k}=${v}`).join("\n")}
|
|
7773
8005
|
`;
|
|
7774
8006
|
await writeFile(filePath, content, "utf-8");
|
|
@@ -7858,10 +8090,10 @@ function determineAppCommand(mode, isMonorepo2, rootScripts, appScripts, repoRoo
|
|
|
7858
8090
|
}
|
|
7859
8091
|
if (appHasCiScript || appHasDefaultScript) {
|
|
7860
8092
|
const scriptName = appHasCiScript ? ciScriptName : "start";
|
|
7861
|
-
const dirArgs2 = isMonorepo2 ? ["-C",
|
|
8093
|
+
const dirArgs2 = isMonorepo2 ? ["-C", path11__default.relative(repoRoot, appDir)] : [];
|
|
7862
8094
|
return { command: ["pnpm", ...dirArgs2, scriptName], useRootScript: false };
|
|
7863
8095
|
}
|
|
7864
|
-
const dirArgs = isMonorepo2 ? ["-C",
|
|
8096
|
+
const dirArgs = isMonorepo2 ? ["-C", path11__default.relative(repoRoot, appDir)] : [];
|
|
7865
8097
|
return {
|
|
7866
8098
|
command: ["pnpm", ...dirArgs, "exec", "next", nextCommand, "-p", String(port)],
|
|
7867
8099
|
useRootScript: false
|
|
@@ -7869,17 +8101,17 @@ function determineAppCommand(mode, isMonorepo2, rootScripts, appScripts, repoRoo
|
|
|
7869
8101
|
}
|
|
7870
8102
|
var NEXT_CRITICAL_FILES = ["routes-manifest.json", "build-manifest.json"];
|
|
7871
8103
|
function cleanStaleNextDevState(appDir) {
|
|
7872
|
-
const nextDir =
|
|
8104
|
+
const nextDir = path11__default.join(appDir, ".next");
|
|
7873
8105
|
if (!existsSync(nextDir)) {
|
|
7874
8106
|
return { cleaned: false };
|
|
7875
8107
|
}
|
|
7876
8108
|
for (const file of NEXT_CRITICAL_FILES) {
|
|
7877
|
-
if (!existsSync(
|
|
8109
|
+
if (!existsSync(path11__default.join(nextDir, file))) {
|
|
7878
8110
|
cleanNextDir(nextDir, `Missing ${file}`);
|
|
7879
8111
|
return { cleaned: true, reason: `Missing ${file}` };
|
|
7880
8112
|
}
|
|
7881
8113
|
}
|
|
7882
|
-
const serverDir =
|
|
8114
|
+
const serverDir = path11__default.join(nextDir, "server");
|
|
7883
8115
|
if (!existsSync(serverDir)) {
|
|
7884
8116
|
try {
|
|
7885
8117
|
const nextStat = statSync(nextDir);
|
|
@@ -7901,7 +8133,7 @@ function cleanNextDir(nextDir, reason) {
|
|
|
7901
8133
|
console.log("[runa] Cleanup complete");
|
|
7902
8134
|
} catch {
|
|
7903
8135
|
const staleDir = `${nextDir}-stale-${Date.now()}`;
|
|
7904
|
-
console.log(`[runa] Could not remove .next, quarantining to ${
|
|
8136
|
+
console.log(`[runa] Could not remove .next, quarantining to ${path11__default.basename(staleDir)}`);
|
|
7905
8137
|
try {
|
|
7906
8138
|
renameSync(nextDir, staleDir);
|
|
7907
8139
|
} catch {
|
|
@@ -7912,8 +8144,8 @@ function cleanNextDir(nextDir, reason) {
|
|
|
7912
8144
|
async function startAppBackground(params) {
|
|
7913
8145
|
const mode = params.mode ?? "start";
|
|
7914
8146
|
const isMonorepo2 = params.appDir !== params.repoRoot;
|
|
7915
|
-
const rootPkgPath =
|
|
7916
|
-
const appPkgPath =
|
|
8147
|
+
const rootPkgPath = path11__default.join(params.repoRoot, "package.json");
|
|
8148
|
+
const appPkgPath = path11__default.join(params.appDir, "package.json");
|
|
7917
8149
|
const rootScripts = await readPackageScripts(rootPkgPath);
|
|
7918
8150
|
const appScripts = isMonorepo2 ? await readPackageScripts(appPkgPath) : rootScripts;
|
|
7919
8151
|
const { command, useRootScript } = determineAppCommand(
|
|
@@ -7925,7 +8157,7 @@ async function startAppBackground(params) {
|
|
|
7925
8157
|
params.appDir,
|
|
7926
8158
|
params.port
|
|
7927
8159
|
);
|
|
7928
|
-
const appLog =
|
|
8160
|
+
const appLog = path11__default.join(params.tmpDir, "app.log");
|
|
7929
8161
|
const out = createWriteStream(appLog, { flags: "a" });
|
|
7930
8162
|
const commandStr = command.join(" ");
|
|
7931
8163
|
const modeLabel = mode === "dev" ? "development (hot reload)" : "production";
|
|
@@ -7934,7 +8166,7 @@ async function startAppBackground(params) {
|
|
|
7934
8166
|
`[runa] Mode: ${modeLabel}`,
|
|
7935
8167
|
`[runa] Command: ${commandStr}`,
|
|
7936
8168
|
`[runa] Working directory: ${params.repoRoot}`,
|
|
7937
|
-
`[runa] App directory: ${
|
|
8169
|
+
`[runa] App directory: ${path11__default.relative(params.repoRoot, params.appDir) || "."}`,
|
|
7938
8170
|
useRootScript ? "[runa] Using root package.json script" : "[runa] Using app package.json script",
|
|
7939
8171
|
"---",
|
|
7940
8172
|
""
|
|
@@ -7955,12 +8187,12 @@ async function startAppBackground(params) {
|
|
|
7955
8187
|
streamToTerminal: params.stream
|
|
7956
8188
|
});
|
|
7957
8189
|
const pid = child.pid ?? -1;
|
|
7958
|
-
await writeFile(
|
|
8190
|
+
await writeFile(path11__default.join(params.tmpDir, "app.pid"), `${pid}
|
|
7959
8191
|
`, "utf-8");
|
|
7960
8192
|
return { pid, cleanupStreams };
|
|
7961
8193
|
}
|
|
7962
8194
|
async function waitForAppReady(params) {
|
|
7963
|
-
const logFilePath = params.tmpDir ?
|
|
8195
|
+
const logFilePath = params.tmpDir ? path11__default.join(params.tmpDir, "app.log") : void 0;
|
|
7964
8196
|
await waitHttpOk({
|
|
7965
8197
|
url: `http://127.0.0.1:${params.port}/`,
|
|
7966
8198
|
timeoutSeconds: params.timeoutSeconds,
|
|
@@ -8100,7 +8332,7 @@ var e2eMeta2 = {
|
|
|
8100
8332
|
var appStartActor = fromPromise(
|
|
8101
8333
|
async ({ input: input3 }) => {
|
|
8102
8334
|
const { repoRoot, appDir, port, tmpDir, stream } = input3;
|
|
8103
|
-
const fullTmpDir =
|
|
8335
|
+
const fullTmpDir = path11__default.join(repoRoot, tmpDir);
|
|
8104
8336
|
await mkdir(fullTmpDir, { recursive: true });
|
|
8105
8337
|
cleanStaleNextDevState(appDir);
|
|
8106
8338
|
const result = await startAppBackground({
|
|
@@ -8368,11 +8600,11 @@ function printSummary2(logger16, output3) {
|
|
|
8368
8600
|
}
|
|
8369
8601
|
function findRepoRoot2(startDir) {
|
|
8370
8602
|
let current = startDir;
|
|
8371
|
-
while (current !==
|
|
8372
|
-
if (existsSync(
|
|
8603
|
+
while (current !== path11__default.dirname(current)) {
|
|
8604
|
+
if (existsSync(path11__default.join(current, "turbo.json"))) {
|
|
8373
8605
|
return current;
|
|
8374
8606
|
}
|
|
8375
|
-
const pkgPath =
|
|
8607
|
+
const pkgPath = path11__default.join(current, "package.json");
|
|
8376
8608
|
if (existsSync(pkgPath)) {
|
|
8377
8609
|
try {
|
|
8378
8610
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
@@ -8382,7 +8614,7 @@ function findRepoRoot2(startDir) {
|
|
|
8382
8614
|
} catch {
|
|
8383
8615
|
}
|
|
8384
8616
|
}
|
|
8385
|
-
current =
|
|
8617
|
+
current = path11__default.dirname(current);
|
|
8386
8618
|
}
|
|
8387
8619
|
return startDir;
|
|
8388
8620
|
}
|
|
@@ -8569,19 +8801,19 @@ function isSupportedNodeEnv(value) {
|
|
|
8569
8801
|
function findProjectRoot(from) {
|
|
8570
8802
|
const workspaceRoot = findWorkspaceRoot(from);
|
|
8571
8803
|
if (workspaceRoot) return workspaceRoot;
|
|
8572
|
-
let current =
|
|
8573
|
-
const root =
|
|
8804
|
+
let current = path11__default.resolve(from);
|
|
8805
|
+
const root = path11__default.parse(current).root;
|
|
8574
8806
|
let depth = 0;
|
|
8575
8807
|
while (current !== root && depth < MAX_DIRECTORY_TRAVERSAL_DEPTH) {
|
|
8576
|
-
const hasGit = existsSync(
|
|
8577
|
-
const hasPackageJson2 = existsSync(
|
|
8808
|
+
const hasGit = existsSync(path11__default.join(current, ".git"));
|
|
8809
|
+
const hasPackageJson2 = existsSync(path11__default.join(current, "package.json"));
|
|
8578
8810
|
if (hasGit || hasPackageJson2) return current;
|
|
8579
|
-
const parent =
|
|
8811
|
+
const parent = path11__default.dirname(current);
|
|
8580
8812
|
if (parent === current) break;
|
|
8581
8813
|
current = parent;
|
|
8582
8814
|
depth++;
|
|
8583
8815
|
}
|
|
8584
|
-
return
|
|
8816
|
+
return path11__default.resolve(from);
|
|
8585
8817
|
}
|
|
8586
8818
|
function mapRunaEnvToVercelEnv(runaEnv) {
|
|
8587
8819
|
if (runaEnv === "preview") return "preview";
|
|
@@ -8622,7 +8854,7 @@ function loadEnvFiles(options = {}) {
|
|
|
8622
8854
|
const nodeEnv = resolveNodeEnv(options.nodeEnv);
|
|
8623
8855
|
const projectRoot = findProjectRoot(cwd);
|
|
8624
8856
|
const vercelEnv = options.vercelEnv ?? inferVercelEnv({ nodeEnv, runaEnv: options.runaEnv });
|
|
8625
|
-
const keysFile =
|
|
8857
|
+
const keysFile = path11__default.join(projectRoot, ".env.keys");
|
|
8626
8858
|
const baseFilePaths = buildBaseEnvFilePaths({ nodeEnv, vercelEnv });
|
|
8627
8859
|
const runaFilePaths = options.runaEnv ? buildRunaEnvFilePaths(options.runaEnv) : [];
|
|
8628
8860
|
const allRelPaths = [...baseFilePaths, ...runaFilePaths];
|
|
@@ -8632,7 +8864,7 @@ function loadEnvFiles(options = {}) {
|
|
|
8632
8864
|
const filePaths = [
|
|
8633
8865
|
keysFile,
|
|
8634
8866
|
// Load keys first
|
|
8635
|
-
...allRelPaths.map((rel) =>
|
|
8867
|
+
...allRelPaths.map((rel) => path11__default.join(projectRoot, rel)).filter(existsSync)
|
|
8636
8868
|
];
|
|
8637
8869
|
config({
|
|
8638
8870
|
path: filePaths,
|
|
@@ -8641,7 +8873,7 @@ function loadEnvFiles(options = {}) {
|
|
|
8641
8873
|
quiet: true
|
|
8642
8874
|
// Suppress dotenvx verbose output
|
|
8643
8875
|
});
|
|
8644
|
-
loadedFiles = allRelPaths.filter((rel) => existsSync(
|
|
8876
|
+
loadedFiles = allRelPaths.filter((rel) => existsSync(path11__default.join(projectRoot, rel)));
|
|
8645
8877
|
} else {
|
|
8646
8878
|
const targetEnv = { ...process.env };
|
|
8647
8879
|
loadedFiles = [
|
|
@@ -8716,8 +8948,8 @@ function buildRunaEnvFilePaths(runaEnv) {
|
|
|
8716
8948
|
function loadEnvFilesIntoTarget(params) {
|
|
8717
8949
|
const loadedFiles = [];
|
|
8718
8950
|
for (const relPath of params.relPaths) {
|
|
8719
|
-
const fullPath =
|
|
8720
|
-
const resolvedPath =
|
|
8951
|
+
const fullPath = path11__default.join(params.projectRoot, relPath);
|
|
8952
|
+
const resolvedPath = path11__default.resolve(fullPath);
|
|
8721
8953
|
if (!isPathContained(params.projectRoot, resolvedPath)) {
|
|
8722
8954
|
continue;
|
|
8723
8955
|
}
|
|
@@ -8985,7 +9217,7 @@ async function runTool(params) {
|
|
|
8985
9217
|
exitCode,
|
|
8986
9218
|
issueCount,
|
|
8987
9219
|
durationMs,
|
|
8988
|
-
outputPath:
|
|
9220
|
+
outputPath: path11__default.relative(process.cwd(), params.outputPath)
|
|
8989
9221
|
});
|
|
8990
9222
|
});
|
|
8991
9223
|
proc.on("error", async () => {
|
|
@@ -9004,7 +9236,7 @@ async function runTool(params) {
|
|
|
9004
9236
|
exitCode: -1,
|
|
9005
9237
|
issueCount: 0,
|
|
9006
9238
|
durationMs,
|
|
9007
|
-
outputPath:
|
|
9239
|
+
outputPath: path11__default.relative(process.cwd(), params.outputPath)
|
|
9008
9240
|
});
|
|
9009
9241
|
});
|
|
9010
9242
|
});
|
|
@@ -9036,7 +9268,7 @@ async function executeWorkflowCheck(params) {
|
|
|
9036
9268
|
label: params.label,
|
|
9037
9269
|
command: params.command,
|
|
9038
9270
|
args: params.args,
|
|
9039
|
-
outputPath:
|
|
9271
|
+
outputPath: path11__default.join(params.logDir, `${params.name}-output.txt`),
|
|
9040
9272
|
parseIssueCount: params.parseIssueCount
|
|
9041
9273
|
});
|
|
9042
9274
|
logCheckResult(result, params.isWarningOnly ?? false);
|
|
@@ -9108,7 +9340,7 @@ async function runCiChecks(options) {
|
|
|
9108
9340
|
const mode = options.mode === "github-actions" ? "github-actions" : "local";
|
|
9109
9341
|
const isCI3 = mode === "github-actions";
|
|
9110
9342
|
const workflowsChanged = options.workflowsChanged;
|
|
9111
|
-
const logDir =
|
|
9343
|
+
const logDir = path11__default.join(process.cwd(), ".runa", "tmp", "workflow");
|
|
9112
9344
|
await mkdir(logDir, { recursive: true });
|
|
9113
9345
|
console.log("");
|
|
9114
9346
|
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 +9349,7 @@ async function runCiChecks(options) {
|
|
|
9117
9349
|
console.log("");
|
|
9118
9350
|
const results = await executeChecks(workflowsChanged, logDir);
|
|
9119
9351
|
const summary = buildChecksSummary(startedAt, workflowsChanged, results);
|
|
9120
|
-
const summaryPath =
|
|
9352
|
+
const summaryPath = path11__default.join(logDir, "checks-summary.json");
|
|
9121
9353
|
await writeFile(summaryPath, JSON.stringify(summary, null, 2), "utf-8");
|
|
9122
9354
|
if (isCI3) {
|
|
9123
9355
|
await appendGithubStepSummary(buildStepSummaryMarkdown(summary));
|
|
@@ -9184,7 +9416,7 @@ function printFinalOutput(summary, summaryPath) {
|
|
|
9184
9416
|
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
9417
|
console.log(`${summary.status === "failure" ? "\u274C" : "\u2705"} Workflow Checks: ${summary.status}`);
|
|
9186
9418
|
console.log(` Duration: ${formatDuration$1(summary.durationMs)}`);
|
|
9187
|
-
console.log(` Summary: ${
|
|
9419
|
+
console.log(` Summary: ${path11__default.relative(process.cwd(), summaryPath)}`);
|
|
9188
9420
|
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
9421
|
}
|
|
9190
9422
|
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 +9429,10 @@ init_esm_shims();
|
|
|
9197
9429
|
var CiConfigSchema = z.record(z.string(), z.unknown());
|
|
9198
9430
|
async function loadCiConfig(params) {
|
|
9199
9431
|
const override = typeof params.overridePath === "string" && params.overridePath.trim().length > 0 ? params.overridePath.trim() : null;
|
|
9200
|
-
const defaultPath =
|
|
9432
|
+
const defaultPath = path11__default.join(params.repoRoot, ".runa", "ci.config.json");
|
|
9201
9433
|
const resolvedPath = override ?? (existsSync(defaultPath) ? defaultPath : null);
|
|
9202
9434
|
if (!resolvedPath) return { configPath: null, config: null };
|
|
9203
|
-
const abs =
|
|
9435
|
+
const abs = path11__default.isAbsolute(resolvedPath) ? resolvedPath : path11__default.join(params.repoRoot, resolvedPath);
|
|
9204
9436
|
if (!existsSync(abs)) return { configPath: abs, config: null };
|
|
9205
9437
|
const raw = await readFile(abs, "utf-8");
|
|
9206
9438
|
const parsed = JSON.parse(raw);
|
|
@@ -9302,9 +9534,9 @@ var CiSummarySchema = z.object({
|
|
|
9302
9534
|
async function writeCiSummary(params) {
|
|
9303
9535
|
const cwd = params.cwd ?? process.cwd();
|
|
9304
9536
|
const parsed = CiSummarySchema.parse(params.summary);
|
|
9305
|
-
const dir =
|
|
9537
|
+
const dir = path11__default.join(cwd, ".runa", "tmp");
|
|
9306
9538
|
await mkdir(dir, { recursive: true });
|
|
9307
|
-
const filePath =
|
|
9539
|
+
const filePath = path11__default.join(dir, "ci-summary.json");
|
|
9308
9540
|
await writeFile(filePath, `${JSON.stringify(parsed, null, 2)}
|
|
9309
9541
|
`, "utf-8");
|
|
9310
9542
|
return filePath;
|
|
@@ -9553,12 +9785,12 @@ async function detectStack(repoRoot, tmpDir, productionDbUrlAdmin) {
|
|
|
9553
9785
|
label: "detect-stack",
|
|
9554
9786
|
command: "pnpm",
|
|
9555
9787
|
args: ["exec", "runa", "db", "detect-stack", "--quiet"],
|
|
9556
|
-
logFile:
|
|
9788
|
+
logFile: path11__default.join(tmpDir, "detect-stack.log")
|
|
9557
9789
|
});
|
|
9558
9790
|
return String(res.stdout ?? "").trim();
|
|
9559
9791
|
}
|
|
9560
9792
|
function checkIfInitialDeployment(repoRoot, productionDbUrl) {
|
|
9561
|
-
const schemasDir =
|
|
9793
|
+
const schemasDir = path11__default.join(repoRoot, "supabase", "schemas", "declarative");
|
|
9562
9794
|
let schemas;
|
|
9563
9795
|
try {
|
|
9564
9796
|
schemas = detectAppSchemas(schemasDir, false);
|
|
@@ -9624,9 +9856,9 @@ function checkIfInitialDeployment(repoRoot, productionDbUrl) {
|
|
|
9624
9856
|
}
|
|
9625
9857
|
}
|
|
9626
9858
|
async function showSchemaDiff(repoRoot, tmpDir) {
|
|
9627
|
-
const diffLog =
|
|
9859
|
+
const diffLog = path11__default.join(tmpDir, "schema-diff.log");
|
|
9628
9860
|
const safeEnv = getSafeEnv();
|
|
9629
|
-
const declarativeSqlPath =
|
|
9861
|
+
const declarativeSqlPath = path11__default.join(repoRoot, "supabase/schemas/declarative");
|
|
9630
9862
|
if (existsSync(declarativeSqlPath)) {
|
|
9631
9863
|
await runLogged({
|
|
9632
9864
|
cwd: repoRoot,
|
|
@@ -9640,8 +9872,8 @@ async function showSchemaDiff(repoRoot, tmpDir) {
|
|
|
9640
9872
|
}
|
|
9641
9873
|
const detected = detectDatabasePackage(repoRoot);
|
|
9642
9874
|
if (detected) {
|
|
9643
|
-
const schemaPath =
|
|
9644
|
-
if (existsSync(
|
|
9875
|
+
const schemaPath = path11__default.join(detected, "src", "schema");
|
|
9876
|
+
if (existsSync(path11__default.join(repoRoot, schemaPath))) {
|
|
9645
9877
|
await runLogged({
|
|
9646
9878
|
cwd: repoRoot,
|
|
9647
9879
|
env: safeEnv,
|
|
@@ -9654,8 +9886,8 @@ async function showSchemaDiff(repoRoot, tmpDir) {
|
|
|
9654
9886
|
}
|
|
9655
9887
|
}
|
|
9656
9888
|
for (const candidate of DATABASE_PACKAGE_CANDIDATES) {
|
|
9657
|
-
const candidatePath =
|
|
9658
|
-
if (existsSync(
|
|
9889
|
+
const candidatePath = path11__default.join("packages", candidate, "src", "schema");
|
|
9890
|
+
if (existsSync(path11__default.join(repoRoot, candidatePath))) {
|
|
9659
9891
|
await runLogged({
|
|
9660
9892
|
cwd: repoRoot,
|
|
9661
9893
|
env: safeEnv,
|
|
@@ -9667,7 +9899,7 @@ async function showSchemaDiff(repoRoot, tmpDir) {
|
|
|
9667
9899
|
return;
|
|
9668
9900
|
}
|
|
9669
9901
|
}
|
|
9670
|
-
if (existsSync(
|
|
9902
|
+
if (existsSync(path11__default.join(repoRoot, "src", "schema"))) {
|
|
9671
9903
|
await runLogged({
|
|
9672
9904
|
cwd: repoRoot,
|
|
9673
9905
|
env: safeEnv,
|
|
@@ -9679,7 +9911,7 @@ async function showSchemaDiff(repoRoot, tmpDir) {
|
|
|
9679
9911
|
}
|
|
9680
9912
|
}
|
|
9681
9913
|
async function detectRisks(repoRoot, tmpDir) {
|
|
9682
|
-
const logFile =
|
|
9914
|
+
const logFile = path11__default.join(tmpDir, "db-risks.log");
|
|
9683
9915
|
try {
|
|
9684
9916
|
const env2 = getFilteredEnv();
|
|
9685
9917
|
await runLogged({
|
|
@@ -9725,7 +9957,7 @@ async function snapshotCreate(repoRoot, tmpDir, productionDbUrlAdmin, commit) {
|
|
|
9725
9957
|
label: "snapshot create production",
|
|
9726
9958
|
command: "pnpm",
|
|
9727
9959
|
args: ["exec", "runa", "db", "snapshot", "create", "production", "--commit", commit],
|
|
9728
|
-
logFile:
|
|
9960
|
+
logFile: path11__default.join(tmpDir, "snapshot-create.log")
|
|
9729
9961
|
});
|
|
9730
9962
|
}
|
|
9731
9963
|
async function snapshotRestoreLatest(repoRoot, tmpDir, productionDbUrlAdmin) {
|
|
@@ -9738,7 +9970,7 @@ async function snapshotRestoreLatest(repoRoot, tmpDir, productionDbUrlAdmin) {
|
|
|
9738
9970
|
label: "snapshot restore production (latest)",
|
|
9739
9971
|
command: "pnpm",
|
|
9740
9972
|
args: ["exec", "runa", "db", "snapshot", "restore", "production", "--latest", "--auto-approve"],
|
|
9741
|
-
logFile:
|
|
9973
|
+
logFile: path11__default.join(tmpDir, "snapshot-restore.log")
|
|
9742
9974
|
});
|
|
9743
9975
|
}
|
|
9744
9976
|
function parseApplyLog(logContent) {
|
|
@@ -9802,7 +10034,7 @@ async function applyProductionSchema(repoRoot, tmpDir, productionDbUrlAdmin, pro
|
|
|
9802
10034
|
if (options?.maxLockWaitMs !== void 0) {
|
|
9803
10035
|
args.push("--max-lock-wait-ms", String(options.maxLockWaitMs));
|
|
9804
10036
|
}
|
|
9805
|
-
const logPath =
|
|
10037
|
+
const logPath = path11__default.join(tmpDir, "db-apply-production.log");
|
|
9806
10038
|
const startTime = Date.now();
|
|
9807
10039
|
await runLogged({
|
|
9808
10040
|
cwd: repoRoot,
|
|
@@ -9847,11 +10079,11 @@ async function auditRecord(repoRoot, tmpDir, productionDbUrlAdmin, params) {
|
|
|
9847
10079
|
label: "git log (commit message)",
|
|
9848
10080
|
command: "git",
|
|
9849
10081
|
args: ["log", "-1", "--pretty=format:%s"],
|
|
9850
|
-
logFile:
|
|
10082
|
+
logFile: path11__default.join(tmpDir, "git-commit-message.log")
|
|
9851
10083
|
});
|
|
9852
10084
|
const commitMsg = String(msg.stdout ?? "").trim();
|
|
9853
10085
|
const schemaPaths = [];
|
|
9854
|
-
const declarativeSqlPath =
|
|
10086
|
+
const declarativeSqlPath = path11__default.join(repoRoot, "supabase/schemas/declarative");
|
|
9855
10087
|
if (existsSync(declarativeSqlPath)) {
|
|
9856
10088
|
schemaPaths.push("supabase/schemas/declarative/");
|
|
9857
10089
|
} else {
|
|
@@ -9871,7 +10103,7 @@ async function auditRecord(repoRoot, tmpDir, productionDbUrlAdmin, params) {
|
|
|
9871
10103
|
label: "git diff (schema)",
|
|
9872
10104
|
command: "git",
|
|
9873
10105
|
args: ["diff", "origin/main", "HEAD", "--", ...schemaPaths],
|
|
9874
|
-
logFile:
|
|
10106
|
+
logFile: path11__default.join(tmpDir, "git-schema-diff.log")
|
|
9875
10107
|
});
|
|
9876
10108
|
const diffRaw = String(diff.stdout ?? "");
|
|
9877
10109
|
const diffLimited = diffRaw.length > 1e4 ? diffRaw.slice(0, 1e4) : diffRaw;
|
|
@@ -9900,7 +10132,7 @@ async function auditRecord(repoRoot, tmpDir, productionDbUrlAdmin, params) {
|
|
|
9900
10132
|
"--environment",
|
|
9901
10133
|
"production"
|
|
9902
10134
|
],
|
|
9903
|
-
logFile:
|
|
10135
|
+
logFile: path11__default.join(tmpDir, "db-audit-record.log")
|
|
9904
10136
|
});
|
|
9905
10137
|
}
|
|
9906
10138
|
async function notifyDeployment(repoRoot, tmpDir, params) {
|
|
@@ -9928,7 +10160,7 @@ async function notifyDeployment(repoRoot, tmpDir, params) {
|
|
|
9928
10160
|
"--type",
|
|
9929
10161
|
"production-schema"
|
|
9930
10162
|
],
|
|
9931
|
-
logFile:
|
|
10163
|
+
logFile: path11__default.join(tmpDir, "notify-deployment.log")
|
|
9932
10164
|
});
|
|
9933
10165
|
}
|
|
9934
10166
|
|
|
@@ -10290,7 +10522,7 @@ $$;
|
|
|
10290
10522
|
label: `repair timestamps (${params.labelPrefix})`,
|
|
10291
10523
|
command: "psql",
|
|
10292
10524
|
args: [...psql.args, "-v", "ON_ERROR_STOP=1", "-c", sql],
|
|
10293
|
-
logFile:
|
|
10525
|
+
logFile: path11__default.join(params.tmpDir, `timestamp-repair-${params.labelPrefix}.log`)
|
|
10294
10526
|
});
|
|
10295
10527
|
}
|
|
10296
10528
|
|
|
@@ -10722,7 +10954,7 @@ async function runCiProdApply(params) {
|
|
|
10722
10954
|
overridePath: params.options.config
|
|
10723
10955
|
});
|
|
10724
10956
|
if (cfg.configPath)
|
|
10725
|
-
params.summary.detected.configPath =
|
|
10957
|
+
params.summary.detected.configPath = path11__default.relative(params.repoRoot, cfg.configPath);
|
|
10726
10958
|
if (cfg.config) params.summary.detected.config = cfg.config;
|
|
10727
10959
|
requireCiAutoApprove({ mode: params.summary.mode, autoApprove: params.options.autoApprove });
|
|
10728
10960
|
const inputs = resolveProdApplyInputs();
|
|
@@ -10815,7 +11047,7 @@ var ciProdApplyCommand = new Command("prod-apply").description("Apply production
|
|
|
10815
11047
|
await appendGithubStepSummary(
|
|
10816
11048
|
buildCiProdApplyStepSummaryMarkdown({
|
|
10817
11049
|
summary,
|
|
10818
|
-
summaryPath:
|
|
11050
|
+
summaryPath: path11__default.relative(repoRoot, summaryPath)
|
|
10819
11051
|
})
|
|
10820
11052
|
);
|
|
10821
11053
|
if (summary.mode === "github-actions") {
|
|
@@ -10831,7 +11063,7 @@ var ciProdApplyCommand = new Command("prod-apply").description("Apply production
|
|
|
10831
11063
|
const summaryPath = await writeCiSummary({ summary });
|
|
10832
11064
|
logSection("Result");
|
|
10833
11065
|
console.log(`status: ${summary.status}`);
|
|
10834
|
-
console.log(`summary: ${
|
|
11066
|
+
console.log(`summary: ${path11__default.relative(repoRoot, summaryPath)}`);
|
|
10835
11067
|
logNextActions([
|
|
10836
11068
|
"Inspect .runa/tmp logs (db-apply-production.log, snapshot-*.log)",
|
|
10837
11069
|
"If rollback succeeded, database should be restored to pre-deploy snapshot"
|
|
@@ -10839,7 +11071,7 @@ var ciProdApplyCommand = new Command("prod-apply").description("Apply production
|
|
|
10839
11071
|
await appendGithubStepSummary(
|
|
10840
11072
|
buildCiProdApplyStepSummaryMarkdown({
|
|
10841
11073
|
summary,
|
|
10842
|
-
summaryPath:
|
|
11074
|
+
summaryPath: path11__default.relative(repoRoot, summaryPath)
|
|
10843
11075
|
})
|
|
10844
11076
|
);
|
|
10845
11077
|
await maybePostFailureComment({
|
|
@@ -10910,7 +11142,7 @@ function getChecks() {
|
|
|
10910
11142
|
}
|
|
10911
11143
|
async function runCheck(check, logDir) {
|
|
10912
11144
|
const startTime = Date.now();
|
|
10913
|
-
const logPath =
|
|
11145
|
+
const logPath = path11__default.join(logDir, `${check.name}.log`);
|
|
10914
11146
|
return new Promise((resolve12) => {
|
|
10915
11147
|
const proc = spawn(check.command, check.args, {
|
|
10916
11148
|
cwd: process.cwd(),
|
|
@@ -10940,7 +11172,7 @@ async function runCheck(check, logDir) {
|
|
|
10940
11172
|
status: exitCode === 0 ? "passed" : "failed",
|
|
10941
11173
|
exitCode,
|
|
10942
11174
|
durationMs,
|
|
10943
|
-
logPath:
|
|
11175
|
+
logPath: path11__default.relative(process.cwd(), logPath)
|
|
10944
11176
|
});
|
|
10945
11177
|
});
|
|
10946
11178
|
proc.on("error", async () => {
|
|
@@ -10955,7 +11187,7 @@ async function runCheck(check, logDir) {
|
|
|
10955
11187
|
status: "failed",
|
|
10956
11188
|
exitCode: 1,
|
|
10957
11189
|
durationMs,
|
|
10958
|
-
logPath:
|
|
11190
|
+
logPath: path11__default.relative(process.cwd(), logPath)
|
|
10959
11191
|
});
|
|
10960
11192
|
});
|
|
10961
11193
|
});
|
|
@@ -11083,19 +11315,19 @@ function printFinalStaticOutput(summary, summaryPath) {
|
|
|
11083
11315
|
console.log(`${summary.status === "success" ? "\u2705" : "\u274C"} Static Analysis: ${summary.status}`);
|
|
11084
11316
|
console.log(` Duration: ${formatDuration$1(summary.durationMs)}`);
|
|
11085
11317
|
console.log(` Passed: ${summary.passedCount}/${summary.checks.length}`);
|
|
11086
|
-
console.log(` Summary: ${
|
|
11318
|
+
console.log(` Summary: ${path11__default.relative(process.cwd(), summaryPath)}`);
|
|
11087
11319
|
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
11320
|
}
|
|
11089
11321
|
async function runCiStatic(options) {
|
|
11090
11322
|
const startedAt = /* @__PURE__ */ new Date();
|
|
11091
11323
|
const isCI3 = options.mode === "github-actions";
|
|
11092
|
-
const logDir =
|
|
11324
|
+
const logDir = path11__default.join(process.cwd(), ".runa", "tmp", "workflow");
|
|
11093
11325
|
await mkdir(logDir, { recursive: true });
|
|
11094
11326
|
const checksToRun = getChecksToRun(options);
|
|
11095
11327
|
printStaticHeader(options);
|
|
11096
11328
|
const results = options.parallel ? await runChecksParallel(checksToRun, logDir) : await runChecksSequential(checksToRun, logDir);
|
|
11097
11329
|
const summary = buildStaticSummary(startedAt, results);
|
|
11098
|
-
const summaryPath =
|
|
11330
|
+
const summaryPath = path11__default.join(logDir, "static-summary.json");
|
|
11099
11331
|
await writeFile(summaryPath, JSON.stringify(summary, null, 2), "utf-8");
|
|
11100
11332
|
if (isCI3) {
|
|
11101
11333
|
await appendGithubStepSummary(buildStepSummaryMarkdown2(summary));
|
|
@@ -11125,9 +11357,9 @@ var appBuildActor = fromPromise(
|
|
|
11125
11357
|
async ({ input: input3 }) => {
|
|
11126
11358
|
const { repoRoot, tmpDir, env: env2 = process.env } = input3;
|
|
11127
11359
|
try {
|
|
11128
|
-
const hasTurbo = existsSync(
|
|
11129
|
-
const hasApps = existsSync(
|
|
11130
|
-
const hasAppsWeb = existsSync(
|
|
11360
|
+
const hasTurbo = existsSync(path11__default.join(repoRoot, "turbo.json"));
|
|
11361
|
+
const hasApps = existsSync(path11__default.join(repoRoot, "apps"));
|
|
11362
|
+
const hasAppsWeb = existsSync(path11__default.join(repoRoot, "apps", "web"));
|
|
11131
11363
|
const args = hasTurbo ? hasApps && !hasAppsWeb ? ["turbo", "run", "build", "--filter=./apps/*"] : ["turbo", "run", "build"] : ["build"];
|
|
11132
11364
|
await runLogged({
|
|
11133
11365
|
cwd: repoRoot,
|
|
@@ -11135,7 +11367,7 @@ var appBuildActor = fromPromise(
|
|
|
11135
11367
|
label: "build",
|
|
11136
11368
|
command: "pnpm",
|
|
11137
11369
|
args,
|
|
11138
|
-
logFile:
|
|
11370
|
+
logFile: path11__default.join(tmpDir, "build.log")
|
|
11139
11371
|
});
|
|
11140
11372
|
return { passed: true };
|
|
11141
11373
|
} catch (error) {
|
|
@@ -11151,14 +11383,14 @@ var appBuildActor = fromPromise(
|
|
|
11151
11383
|
init_esm_shims();
|
|
11152
11384
|
var APP_CANDIDATES = ["web", "dashboard", "app", "frontend", "client"];
|
|
11153
11385
|
function findExistingAppDirs(repoRoot) {
|
|
11154
|
-
const appsDir =
|
|
11386
|
+
const appsDir = path11__default.join(repoRoot, "apps");
|
|
11155
11387
|
if (!existsSync(appsDir)) return [];
|
|
11156
11388
|
try {
|
|
11157
11389
|
const entries = readdirSync(appsDir, { withFileTypes: true });
|
|
11158
11390
|
const found = [];
|
|
11159
11391
|
for (const entry of entries) {
|
|
11160
11392
|
if (!entry.isDirectory()) continue;
|
|
11161
|
-
const pkgJson =
|
|
11393
|
+
const pkgJson = path11__default.join(appsDir, entry.name, "package.json");
|
|
11162
11394
|
if (existsSync(pkgJson)) {
|
|
11163
11395
|
found.push(`apps/${entry.name}`);
|
|
11164
11396
|
}
|
|
@@ -11178,9 +11410,9 @@ function findExistingAppDirs(repoRoot) {
|
|
|
11178
11410
|
}
|
|
11179
11411
|
}
|
|
11180
11412
|
function validateAppDir(repoRoot, appDir) {
|
|
11181
|
-
const pkgJsonPath =
|
|
11413
|
+
const pkgJsonPath = path11__default.join(appDir, "package.json");
|
|
11182
11414
|
if (!existsSync(appDir)) {
|
|
11183
|
-
const relPath =
|
|
11415
|
+
const relPath = path11__default.relative(repoRoot, appDir) || ".";
|
|
11184
11416
|
const suggestions = findExistingAppDirs(repoRoot);
|
|
11185
11417
|
const errorParts = [`App directory not found: ${relPath}`];
|
|
11186
11418
|
if (suggestions.length > 0) {
|
|
@@ -11202,7 +11434,7 @@ function validateAppDir(repoRoot, appDir) {
|
|
|
11202
11434
|
return errorParts.join("\n");
|
|
11203
11435
|
}
|
|
11204
11436
|
if (!existsSync(pkgJsonPath)) {
|
|
11205
|
-
const relPath =
|
|
11437
|
+
const relPath = path11__default.relative(repoRoot, appDir) || ".";
|
|
11206
11438
|
return `No package.json found in app directory: ${relPath}`;
|
|
11207
11439
|
}
|
|
11208
11440
|
return null;
|
|
@@ -11259,9 +11491,9 @@ init_esm_shims();
|
|
|
11259
11491
|
async function runAppBuild(params) {
|
|
11260
11492
|
const { repoRoot, tmpDir, env: env2 } = params;
|
|
11261
11493
|
try {
|
|
11262
|
-
const hasTurbo = existsSync(
|
|
11263
|
-
const hasApps = existsSync(
|
|
11264
|
-
const hasAppsWeb = existsSync(
|
|
11494
|
+
const hasTurbo = existsSync(path11__default.join(repoRoot, "turbo.json"));
|
|
11495
|
+
const hasApps = existsSync(path11__default.join(repoRoot, "apps"));
|
|
11496
|
+
const hasAppsWeb = existsSync(path11__default.join(repoRoot, "apps", "web"));
|
|
11265
11497
|
const args = hasTurbo ? hasApps && !hasAppsWeb ? ["turbo", "run", "build", "--filter=./apps/*"] : ["turbo", "run", "build"] : ["build"];
|
|
11266
11498
|
await runLogged({
|
|
11267
11499
|
cwd: repoRoot,
|
|
@@ -11269,7 +11501,7 @@ async function runAppBuild(params) {
|
|
|
11269
11501
|
label: "build",
|
|
11270
11502
|
command: "pnpm",
|
|
11271
11503
|
args,
|
|
11272
|
-
logFile:
|
|
11504
|
+
logFile: path11__default.join(tmpDir, "build.log")
|
|
11273
11505
|
});
|
|
11274
11506
|
return { passed: true };
|
|
11275
11507
|
} catch (error) {
|
|
@@ -11288,7 +11520,7 @@ async function runManifestGenerate(params) {
|
|
|
11288
11520
|
label: "manifest:generate",
|
|
11289
11521
|
command: "pnpm",
|
|
11290
11522
|
args: ["manifest:generate"],
|
|
11291
|
-
logFile:
|
|
11523
|
+
logFile: path11__default.join(tmpDir, "manifest-generate.log")
|
|
11292
11524
|
});
|
|
11293
11525
|
return { generated: true };
|
|
11294
11526
|
} catch (error) {
|
|
@@ -11308,7 +11540,7 @@ async function runPlaywrightInstall(params) {
|
|
|
11308
11540
|
label: "playwright install",
|
|
11309
11541
|
command: "pnpm",
|
|
11310
11542
|
args,
|
|
11311
|
-
logFile:
|
|
11543
|
+
logFile: path11__default.join(tmpDir, "playwright-install.log")
|
|
11312
11544
|
});
|
|
11313
11545
|
return { installed: true };
|
|
11314
11546
|
} catch (error) {
|
|
@@ -11381,7 +11613,7 @@ var playwrightInstallActor = fromPromise(
|
|
|
11381
11613
|
label: "playwright install",
|
|
11382
11614
|
command: "pnpm",
|
|
11383
11615
|
args,
|
|
11384
|
-
logFile:
|
|
11616
|
+
logFile: path11__default.join(tmpDir, "playwright-install.log")
|
|
11385
11617
|
});
|
|
11386
11618
|
return { installed: true };
|
|
11387
11619
|
} catch (error) {
|
|
@@ -11405,7 +11637,7 @@ var staticChecksActor2 = fromPromise(
|
|
|
11405
11637
|
label: "type-check",
|
|
11406
11638
|
command: "pnpm",
|
|
11407
11639
|
args: ["type-check"],
|
|
11408
|
-
logFile:
|
|
11640
|
+
logFile: path11__default.join(tmpDir, "type-check.log")
|
|
11409
11641
|
}),
|
|
11410
11642
|
runLogged({
|
|
11411
11643
|
cwd: repoRoot,
|
|
@@ -11413,7 +11645,7 @@ var staticChecksActor2 = fromPromise(
|
|
|
11413
11645
|
label: "lint",
|
|
11414
11646
|
command: "pnpm",
|
|
11415
11647
|
args: ["lint"],
|
|
11416
|
-
logFile:
|
|
11648
|
+
logFile: path11__default.join(tmpDir, "lint.log")
|
|
11417
11649
|
})
|
|
11418
11650
|
]);
|
|
11419
11651
|
const typeCheckPassed = typeCheckResult.status === "fulfilled";
|
|
@@ -11564,7 +11796,7 @@ var applySeedsActor = fromPromise(
|
|
|
11564
11796
|
label: `db seed (${envArg})`,
|
|
11565
11797
|
command: "pnpm",
|
|
11566
11798
|
args: ["exec", "runa", "db", "seed", envArg, "--auto-approve"],
|
|
11567
|
-
logFile:
|
|
11799
|
+
logFile: path11__default.join(tmpDir, `ci-db-seed-${envArg}.log`)
|
|
11568
11800
|
});
|
|
11569
11801
|
return { applied: true };
|
|
11570
11802
|
} catch (error) {
|
|
@@ -11942,7 +12174,7 @@ SELECT (SELECT available FROM a)::text || ',' || (SELECT installed FROM i)::text
|
|
|
11942
12174
|
label: `probe pgtap (best-effort, ${params.label})`,
|
|
11943
12175
|
command: "psql",
|
|
11944
12176
|
args: [ddlUrl, "-X", "-v", "ON_ERROR_STOP=1", "-q", "-t", "-A", "-c", probeSql],
|
|
11945
|
-
logFile:
|
|
12177
|
+
logFile: path11__default.join(params.tmpDir, `pgtap-probe-${params.label}.log`)
|
|
11946
12178
|
}).catch(() => null);
|
|
11947
12179
|
const raw = String(probe?.stdout ?? "").trim();
|
|
11948
12180
|
const [availableRaw, installedRaw] = raw.split(",");
|
|
@@ -11957,11 +12189,11 @@ SELECT (SELECT available FROM a)::text || ',' || (SELECT installed FROM i)::text
|
|
|
11957
12189
|
label: `ensure pgtap (best-effort, ${params.label})`,
|
|
11958
12190
|
command: "psql",
|
|
11959
12191
|
args: [ddlUrl, "-X", "-v", "ON_ERROR_STOP=1", "-q", "-c", installSql],
|
|
11960
|
-
logFile:
|
|
12192
|
+
logFile: path11__default.join(params.tmpDir, `pgtap-ensure-${params.label}.log`)
|
|
11961
12193
|
}).catch(async (error) => {
|
|
11962
12194
|
try {
|
|
11963
12195
|
await writeFile(
|
|
11964
|
-
|
|
12196
|
+
path11__default.join(params.tmpDir, `pgtap-ensure-${params.label}.failed`),
|
|
11965
12197
|
`${error instanceof Error ? error.message : String(error)}
|
|
11966
12198
|
`,
|
|
11967
12199
|
"utf-8"
|
|
@@ -11975,7 +12207,7 @@ SELECT (SELECT available FROM a)::text || ',' || (SELECT installed FROM i)::text
|
|
|
11975
12207
|
label: `probe pgtap (best-effort, ${params.label}, after)`,
|
|
11976
12208
|
command: "psql",
|
|
11977
12209
|
args: [ddlUrl, "-X", "-v", "ON_ERROR_STOP=1", "-q", "-t", "-A", "-c", probeSql],
|
|
11978
|
-
logFile:
|
|
12210
|
+
logFile: path11__default.join(params.tmpDir, `pgtap-probe-${params.label}-after.log`)
|
|
11979
12211
|
}).catch(() => null);
|
|
11980
12212
|
const afterRaw = String(probeAfter?.stdout ?? "").trim();
|
|
11981
12213
|
const [availableAfterRaw, installedAfterRaw] = afterRaw.split(",");
|
|
@@ -12096,14 +12328,14 @@ function extractSqlFromSchemaChanges(fullOutput) {
|
|
|
12096
12328
|
return null;
|
|
12097
12329
|
}
|
|
12098
12330
|
function getIdempotentRoleNames(repoRoot) {
|
|
12099
|
-
const idempotentDir =
|
|
12331
|
+
const idempotentDir = path11__default.join(repoRoot, "supabase", "schemas", "idempotent");
|
|
12100
12332
|
const roles = [];
|
|
12101
12333
|
try {
|
|
12102
12334
|
const fs14 = __require("fs");
|
|
12103
12335
|
if (!fs14.existsSync(idempotentDir)) return [];
|
|
12104
12336
|
const files = fs14.readdirSync(idempotentDir).filter((f) => f.endsWith(".sql"));
|
|
12105
12337
|
for (const file of files) {
|
|
12106
|
-
const content = fs14.readFileSync(
|
|
12338
|
+
const content = fs14.readFileSync(path11__default.join(idempotentDir, file), "utf-8");
|
|
12107
12339
|
const roleMatches = content.matchAll(/CREATE\s+ROLE\s+(\w+)\s+WITH/gi);
|
|
12108
12340
|
for (const match of roleMatches) {
|
|
12109
12341
|
if (match[1]) roles.push(match[1].toLowerCase());
|
|
@@ -12244,7 +12476,7 @@ var productionPreviewActor = fromPromise(
|
|
|
12244
12476
|
);
|
|
12245
12477
|
return buildSkipResult(!!productionUrl);
|
|
12246
12478
|
}
|
|
12247
|
-
const logFile =
|
|
12479
|
+
const logFile = path11__default.join(tmpDir, "ci-production-preview.log");
|
|
12248
12480
|
console.log("\u25B6 production preview (dry-run): runa db apply production --check");
|
|
12249
12481
|
try {
|
|
12250
12482
|
const result = await execa(
|
|
@@ -12334,7 +12566,7 @@ var resetDbActor = fromPromise(
|
|
|
12334
12566
|
label: "db reset (local)",
|
|
12335
12567
|
command: "npx",
|
|
12336
12568
|
args: ["supabase", "db", "reset", "--local"],
|
|
12337
|
-
logFile:
|
|
12569
|
+
logFile: path11__default.join(tmpDir, "ci-db-reset.log")
|
|
12338
12570
|
});
|
|
12339
12571
|
return { reset: true };
|
|
12340
12572
|
} catch (error) {
|
|
@@ -12350,8 +12582,8 @@ var resetDbActor = fromPromise(
|
|
|
12350
12582
|
init_esm_shims();
|
|
12351
12583
|
async function hasSetupRolesScript(repoRoot) {
|
|
12352
12584
|
const candidates = [
|
|
12353
|
-
|
|
12354
|
-
|
|
12585
|
+
path11__default.join(repoRoot, "packages", "database", "package.json"),
|
|
12586
|
+
path11__default.join(repoRoot, "package.json")
|
|
12355
12587
|
];
|
|
12356
12588
|
for (const pkgPath of candidates) {
|
|
12357
12589
|
if (!existsSync(pkgPath)) continue;
|
|
@@ -12389,7 +12621,7 @@ var setupRolesActor = fromPromise(
|
|
|
12389
12621
|
label: "db roles setup",
|
|
12390
12622
|
command: "pnpm",
|
|
12391
12623
|
args: ["db:setup-roles"],
|
|
12392
|
-
logFile:
|
|
12624
|
+
logFile: path11__default.join(tmpDir, "ci-db-roles-setup.log")
|
|
12393
12625
|
});
|
|
12394
12626
|
const stdout = String(result.stdout ?? "");
|
|
12395
12627
|
const appUrlMatch = stdout.match(/APP_DATABASE_URL=["']?([^"'\n]+)["']?/);
|
|
@@ -12502,14 +12734,14 @@ function getSchemaGitDiff(repoRoot) {
|
|
|
12502
12734
|
}
|
|
12503
12735
|
}
|
|
12504
12736
|
function getIdempotentRoleNames2(repoRoot) {
|
|
12505
|
-
const idempotentDir =
|
|
12737
|
+
const idempotentDir = path11__default.join(repoRoot, "supabase", "schemas", "idempotent");
|
|
12506
12738
|
const roles = [];
|
|
12507
12739
|
try {
|
|
12508
12740
|
const fs14 = __require("fs");
|
|
12509
12741
|
if (!fs14.existsSync(idempotentDir)) return [];
|
|
12510
12742
|
const files = fs14.readdirSync(idempotentDir).filter((f) => f.endsWith(".sql"));
|
|
12511
12743
|
for (const file of files) {
|
|
12512
|
-
const content = fs14.readFileSync(
|
|
12744
|
+
const content = fs14.readFileSync(path11__default.join(idempotentDir, file), "utf-8");
|
|
12513
12745
|
const roleMatches = content.matchAll(/CREATE\s+ROLE\s+(\w+)\s+WITH/gi);
|
|
12514
12746
|
for (const match of roleMatches) {
|
|
12515
12747
|
if (match[1]) roles.push(match[1].toLowerCase());
|
|
@@ -12606,7 +12838,7 @@ var syncSchemaActor = fromPromise(
|
|
|
12606
12838
|
// Always verbose for full traceability
|
|
12607
12839
|
...skipCodegen ? ["--skip-codegen"] : []
|
|
12608
12840
|
];
|
|
12609
|
-
const logFile =
|
|
12841
|
+
const logFile = path11__default.join(tmpDir, `ci-db-${useDbApply ? "apply" : "sync"}-${envArg}.log`);
|
|
12610
12842
|
await runLogged({
|
|
12611
12843
|
cwd: repoRoot,
|
|
12612
12844
|
env: baseEnv,
|
|
@@ -12746,7 +12978,7 @@ async function startSupabaseLocal(params) {
|
|
|
12746
12978
|
label: "supabase start (local)",
|
|
12747
12979
|
command: "supabase",
|
|
12748
12980
|
args: ["start", "--exclude", exclude],
|
|
12749
|
-
logFile:
|
|
12981
|
+
logFile: path11__default.join(params.tmpDir, "supabase-start-local.log")
|
|
12750
12982
|
});
|
|
12751
12983
|
}
|
|
12752
12984
|
function getDefaultLocalSupabaseUrl(repoRoot) {
|
|
@@ -12774,7 +13006,7 @@ async function resolveLocalSupabaseEnv(params) {
|
|
|
12774
13006
|
label: "supabase status (json)",
|
|
12775
13007
|
command: "supabase",
|
|
12776
13008
|
args: ["status", "--output", "json"],
|
|
12777
|
-
logFile:
|
|
13009
|
+
logFile: path11__default.join(params.tmpDir, `supabase-status-${i + 1}.log`)
|
|
12778
13010
|
});
|
|
12779
13011
|
const parsed = JSON.parse(String(res.stdout ?? "{}"));
|
|
12780
13012
|
const out = z.object({
|
|
@@ -12894,7 +13126,7 @@ async function findFilesRecursive(params) {
|
|
|
12894
13126
|
continue;
|
|
12895
13127
|
}
|
|
12896
13128
|
for (const e of entries) {
|
|
12897
|
-
const p =
|
|
13129
|
+
const p = path11__default.join(dir, e.name);
|
|
12898
13130
|
if (e.isDirectory()) stack.push(p);
|
|
12899
13131
|
else if (e.isFile() && params.suffixes.some((s) => p.endsWith(s))) out.push(p);
|
|
12900
13132
|
}
|
|
@@ -12908,7 +13140,7 @@ async function queryScalar(params) {
|
|
|
12908
13140
|
label: `psql scalar (${params.logName})`,
|
|
12909
13141
|
command: "psql",
|
|
12910
13142
|
args: [params.databaseUrl, "-X", "-v", "ON_ERROR_STOP=1", "-q", "-t", "-A", "-c", params.sql],
|
|
12911
|
-
logFile:
|
|
13143
|
+
logFile: path11__default.join(params.tmpDir, `psql-${params.logName}.log`)
|
|
12912
13144
|
});
|
|
12913
13145
|
return String(res.stdout ?? "").trim();
|
|
12914
13146
|
}
|
|
@@ -13036,8 +13268,8 @@ function isPlaywrightIgnored(filePath) {
|
|
|
13036
13268
|
async function detectGeneratedE2eCapabilities(params) {
|
|
13037
13269
|
const caps = /* @__PURE__ */ new Set();
|
|
13038
13270
|
const diagnostics = {};
|
|
13039
|
-
const layer4Dir =
|
|
13040
|
-
const tmpDir =
|
|
13271
|
+
const layer4Dir = path11__default.join(params.repoRoot, ".runa", "tests", "layer4");
|
|
13272
|
+
const tmpDir = path11__default.join(params.repoRoot, ".runa", "tmp");
|
|
13041
13273
|
const hasLayer4Dir = await pathExists(layer4Dir);
|
|
13042
13274
|
const hasTmpDir = await pathExists(tmpDir);
|
|
13043
13275
|
if (!hasLayer4Dir && !hasTmpDir) {
|
|
@@ -13547,7 +13779,7 @@ init_esm_shims();
|
|
|
13547
13779
|
// src/commands/ci/utils/test-parallel.ts
|
|
13548
13780
|
init_esm_shims();
|
|
13549
13781
|
async function readLayerResult(tmpDir, layer) {
|
|
13550
|
-
const resultFile =
|
|
13782
|
+
const resultFile = path11__default.join(tmpDir, `vitest-layer${layer}-result.json`);
|
|
13551
13783
|
try {
|
|
13552
13784
|
const content = await readFile(resultFile, "utf-8");
|
|
13553
13785
|
return JSON.parse(content);
|
|
@@ -13665,8 +13897,8 @@ async function waitUntilAllFinished(params) {
|
|
|
13665
13897
|
async function runLayersInParallel(params) {
|
|
13666
13898
|
const spawned = [];
|
|
13667
13899
|
for (const layer of params.layers) {
|
|
13668
|
-
const logFile =
|
|
13669
|
-
const exitFile =
|
|
13900
|
+
const logFile = path11__default.join(params.tmpDir, `layer${layer}.log`);
|
|
13901
|
+
const exitFile = path11__default.join(params.tmpDir, `layer${layer}.exit`);
|
|
13670
13902
|
const proc = runLogged({
|
|
13671
13903
|
cwd: params.repoRoot,
|
|
13672
13904
|
env: {
|
|
@@ -13709,7 +13941,7 @@ async function runLayersInParallel(params) {
|
|
|
13709
13941
|
for (const p of spawned) {
|
|
13710
13942
|
const codeRaw = p.exitCode ?? await p.procPromise;
|
|
13711
13943
|
const code = Number.isNaN(codeRaw) ? 1 : codeRaw;
|
|
13712
|
-
const resultFile =
|
|
13944
|
+
const resultFile = path11__default.join(params.tmpDir, `vitest-layer${p.layer}-result.json`);
|
|
13713
13945
|
const wasKilled = killedLayerSet.has(p.layer);
|
|
13714
13946
|
const actualResult = waitResult.successMap.get(p.layer) ?? {
|
|
13715
13947
|
success: code === 0,
|
|
@@ -13724,9 +13956,9 @@ async function runLayersInParallel(params) {
|
|
|
13724
13956
|
skippedReason: actualResult.skippedReason,
|
|
13725
13957
|
killed: wasKilled,
|
|
13726
13958
|
killedBy: wasKilled ? failFastSource : void 0,
|
|
13727
|
-
logPath:
|
|
13728
|
-
exitPath:
|
|
13729
|
-
resultPath:
|
|
13959
|
+
logPath: path11__default.relative(params.repoRoot, p.logFile),
|
|
13960
|
+
exitPath: path11__default.relative(params.repoRoot, p.exitFile),
|
|
13961
|
+
resultPath: path11__default.relative(params.repoRoot, resultFile),
|
|
13730
13962
|
// Test count information
|
|
13731
13963
|
totalTests: actualResult.totalTests,
|
|
13732
13964
|
passedTests: actualResult.passedTests,
|
|
@@ -17022,13 +17254,20 @@ init_esm_shims();
|
|
|
17022
17254
|
// src/commands/db/utils/db-target.ts
|
|
17023
17255
|
init_esm_shims();
|
|
17024
17256
|
init_env();
|
|
17025
|
-
|
|
17257
|
+
init_local_supabase();
|
|
17026
17258
|
function getLocalDatabaseUrl() {
|
|
17027
|
-
|
|
17028
|
-
|
|
17259
|
+
return buildLocalDatabaseUrl(process.cwd());
|
|
17260
|
+
}
|
|
17261
|
+
function isLocalHostname(hostname) {
|
|
17262
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "host.docker.internal";
|
|
17029
17263
|
}
|
|
17030
17264
|
function isLocalDatabaseUrl(url) {
|
|
17031
|
-
|
|
17265
|
+
try {
|
|
17266
|
+
const parsed = new URL(url);
|
|
17267
|
+
return isLocalHostname(parsed.hostname);
|
|
17268
|
+
} catch {
|
|
17269
|
+
return false;
|
|
17270
|
+
}
|
|
17032
17271
|
}
|
|
17033
17272
|
function resolveDatabaseUrl(environment) {
|
|
17034
17273
|
switch (environment) {
|
|
@@ -18329,6 +18568,60 @@ function handleHazards(hazards, input3) {
|
|
|
18329
18568
|
);
|
|
18330
18569
|
}
|
|
18331
18570
|
}
|
|
18571
|
+
var TYPE_TO_EXTENSION = {
|
|
18572
|
+
geometry: "postgis",
|
|
18573
|
+
geography: "postgis",
|
|
18574
|
+
box2d: "postgis",
|
|
18575
|
+
box3d: "postgis",
|
|
18576
|
+
raster: "postgis_raster",
|
|
18577
|
+
vector: "vector",
|
|
18578
|
+
halfvec: "vector",
|
|
18579
|
+
sparsevec: "vector",
|
|
18580
|
+
cube: "cube",
|
|
18581
|
+
hstore: "hstore",
|
|
18582
|
+
ltree: "ltree",
|
|
18583
|
+
citext: "citext"
|
|
18584
|
+
};
|
|
18585
|
+
function detectMissingExtensionType(errorOutput) {
|
|
18586
|
+
const typePattern = /type ["']?(\w+)["']? does not exist/gi;
|
|
18587
|
+
const matches = [...errorOutput.matchAll(typePattern)];
|
|
18588
|
+
const missingTypes = [...new Set(matches.map((m) => m[1].toLowerCase()))];
|
|
18589
|
+
if (missingTypes.length === 0) {
|
|
18590
|
+
return { detected: false, missingTypes: [], suggestedExtensions: [] };
|
|
18591
|
+
}
|
|
18592
|
+
const extensions = /* @__PURE__ */ new Set();
|
|
18593
|
+
for (const type of missingTypes) {
|
|
18594
|
+
const ext = TYPE_TO_EXTENSION[type];
|
|
18595
|
+
if (ext) extensions.add(ext);
|
|
18596
|
+
}
|
|
18597
|
+
return {
|
|
18598
|
+
detected: true,
|
|
18599
|
+
missingTypes,
|
|
18600
|
+
suggestedExtensions: [...extensions]
|
|
18601
|
+
};
|
|
18602
|
+
}
|
|
18603
|
+
function formatExtensionErrorHint(detection) {
|
|
18604
|
+
if (!detection.detected) return "";
|
|
18605
|
+
const lines = [];
|
|
18606
|
+
lines.push("");
|
|
18607
|
+
lines.push("Missing extension type(s) detected:");
|
|
18608
|
+
for (const type of detection.missingTypes) {
|
|
18609
|
+
const ext = TYPE_TO_EXTENSION[type];
|
|
18610
|
+
lines.push(` type "${type}" \u2192 extension: ${ext ?? "unknown"}`);
|
|
18611
|
+
}
|
|
18612
|
+
if (detection.suggestedExtensions.length > 0) {
|
|
18613
|
+
lines.push("");
|
|
18614
|
+
lines.push("Fix 1: Add to supabase/schemas/idempotent/00_extensions.sql:");
|
|
18615
|
+
for (const ext of detection.suggestedExtensions) {
|
|
18616
|
+
lines.push(` CREATE EXTENSION IF NOT EXISTS ${ext};`);
|
|
18617
|
+
}
|
|
18618
|
+
const extList = detection.suggestedExtensions.map((e) => `'${e}'`).join(", ");
|
|
18619
|
+
lines.push("");
|
|
18620
|
+
lines.push("Fix 2: Configure shadow DB in runa.config.ts:");
|
|
18621
|
+
lines.push(` database: { pgSchemaDiff: { shadowDbExtensions: [${extList}] } }`);
|
|
18622
|
+
}
|
|
18623
|
+
return lines.join("\n");
|
|
18624
|
+
}
|
|
18332
18625
|
function executePgSchemaDiffPlan(dbUrl, schemasDir, includeSchemas, verbose, options) {
|
|
18333
18626
|
const planArgs = [
|
|
18334
18627
|
"plan",
|
|
@@ -18364,6 +18657,10 @@ function executePgSchemaDiffPlan(dbUrl, schemasDir, includeSchemas, verbose, opt
|
|
|
18364
18657
|
logger4.error(`Exit code: ${planResult.status}`);
|
|
18365
18658
|
logger4.error(`Signal: ${planResult.signal || "none"}`);
|
|
18366
18659
|
logger4.error(`Stderr: ${planStderr || "(empty)"}`);
|
|
18660
|
+
const extDetection = detectMissingExtensionType(planStderr || planOutput);
|
|
18661
|
+
if (extDetection.detected) {
|
|
18662
|
+
logger4.error(formatExtensionErrorHint(extDetection));
|
|
18663
|
+
}
|
|
18367
18664
|
throw new Error(
|
|
18368
18665
|
`pg-schema-diff plan failed (exit ${planResult.status}): ${planStderr || "(no output)"}`
|
|
18369
18666
|
);
|
|
@@ -18421,6 +18718,10 @@ async function executeApplyWithRetry(applyArgs, verbose, config) {
|
|
|
18421
18718
|
}
|
|
18422
18719
|
logger5.error("pg-schema-diff apply failed:");
|
|
18423
18720
|
logger5.error(errorOutput);
|
|
18721
|
+
const extDetection = detectMissingExtensionType(errorOutput);
|
|
18722
|
+
if (extDetection.detected) {
|
|
18723
|
+
logger5.error(formatExtensionErrorHint(extDetection));
|
|
18724
|
+
}
|
|
18424
18725
|
return {
|
|
18425
18726
|
success: false,
|
|
18426
18727
|
error: new Error(`pg-schema-diff apply failed: ${errorOutput}`),
|
|
@@ -19887,16 +20188,16 @@ var ERROR_MESSAGES2 = {
|
|
|
19887
20188
|
SCHEMA_NOT_FOUND: "Schema file not found"
|
|
19888
20189
|
};
|
|
19889
20190
|
function containsPathTraversal2(inputPath) {
|
|
19890
|
-
const normalized =
|
|
20191
|
+
const normalized = path11__default.normalize(inputPath);
|
|
19891
20192
|
return normalized.includes("..") || inputPath.includes("\0");
|
|
19892
20193
|
}
|
|
19893
20194
|
function isPathWithinBase(filePath, baseDir) {
|
|
19894
20195
|
try {
|
|
19895
|
-
const resolvedFile =
|
|
19896
|
-
const resolvedBase =
|
|
19897
|
-
const normalizedFile =
|
|
19898
|
-
const normalizedBase =
|
|
19899
|
-
return normalizedFile === normalizedBase || normalizedFile.startsWith(normalizedBase +
|
|
20196
|
+
const resolvedFile = path11__default.resolve(filePath);
|
|
20197
|
+
const resolvedBase = path11__default.resolve(baseDir);
|
|
20198
|
+
const normalizedFile = path11__default.normalize(resolvedFile);
|
|
20199
|
+
const normalizedBase = path11__default.normalize(resolvedBase);
|
|
20200
|
+
return normalizedFile === normalizedBase || normalizedFile.startsWith(normalizedBase + path11__default.sep);
|
|
19900
20201
|
} catch {
|
|
19901
20202
|
return false;
|
|
19902
20203
|
}
|
|
@@ -19905,13 +20206,13 @@ function validateSchemaPath(dbPackagePath, projectRoot = process.cwd()) {
|
|
|
19905
20206
|
if (containsPathTraversal2(dbPackagePath)) {
|
|
19906
20207
|
throw new Error(ERROR_MESSAGES2.PATH_TRAVERSAL);
|
|
19907
20208
|
}
|
|
19908
|
-
const schemaEntry =
|
|
19909
|
-
const absoluteSchemaPath =
|
|
20209
|
+
const schemaEntry = path11__default.join(dbPackagePath, "src", "schema", "index.ts");
|
|
20210
|
+
const absoluteSchemaPath = path11__default.resolve(projectRoot, schemaEntry);
|
|
19910
20211
|
let resolvedProjectRoot;
|
|
19911
20212
|
try {
|
|
19912
20213
|
resolvedProjectRoot = realpathSync(projectRoot);
|
|
19913
20214
|
} catch {
|
|
19914
|
-
resolvedProjectRoot =
|
|
20215
|
+
resolvedProjectRoot = path11__default.resolve(projectRoot);
|
|
19915
20216
|
}
|
|
19916
20217
|
if (!isPathWithinBase(absoluteSchemaPath, resolvedProjectRoot)) {
|
|
19917
20218
|
throw new Error(ERROR_MESSAGES2.PATH_TRAVERSAL);
|
|
@@ -20043,7 +20344,7 @@ function diffSchema(params) {
|
|
|
20043
20344
|
};
|
|
20044
20345
|
}
|
|
20045
20346
|
function extractTablesFromIdempotentSql(idempotentDir, projectRoot = process.cwd()) {
|
|
20046
|
-
const fullPath =
|
|
20347
|
+
const fullPath = path11__default.resolve(projectRoot, idempotentDir);
|
|
20047
20348
|
if (!existsSync(fullPath)) {
|
|
20048
20349
|
return [];
|
|
20049
20350
|
}
|
|
@@ -20052,7 +20353,7 @@ function extractTablesFromIdempotentSql(idempotentDir, projectRoot = process.cwd
|
|
|
20052
20353
|
try {
|
|
20053
20354
|
const files = readdirSync(fullPath).filter((f) => f.endsWith(".sql"));
|
|
20054
20355
|
for (const file of files) {
|
|
20055
|
-
const filePath =
|
|
20356
|
+
const filePath = path11__default.join(fullPath, file);
|
|
20056
20357
|
const content = readFileSync(filePath, "utf-8");
|
|
20057
20358
|
const contentWithoutComments = content.replace(/--.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
20058
20359
|
for (const match of contentWithoutComments.matchAll(createTablePattern)) {
|
|
@@ -20275,7 +20576,7 @@ async function validateGitHubOutputPath(filePath) {
|
|
|
20275
20576
|
if (!filePath || filePath.trim().length === 0) {
|
|
20276
20577
|
return { valid: false, resolvedPath: null, error: "Empty file path" };
|
|
20277
20578
|
}
|
|
20278
|
-
const normalizedPath =
|
|
20579
|
+
const normalizedPath = path11__default.normalize(filePath);
|
|
20279
20580
|
if (normalizedPath.includes("..")) {
|
|
20280
20581
|
return {
|
|
20281
20582
|
valid: false,
|
|
@@ -20283,7 +20584,7 @@ async function validateGitHubOutputPath(filePath) {
|
|
|
20283
20584
|
error: "Path traversal detected (..) in file path"
|
|
20284
20585
|
};
|
|
20285
20586
|
}
|
|
20286
|
-
const absolutePath =
|
|
20587
|
+
const absolutePath = path11__default.resolve(normalizedPath);
|
|
20287
20588
|
for (const forbidden of FORBIDDEN_PATHS) {
|
|
20288
20589
|
if (absolutePath.startsWith(forbidden) || absolutePath.includes(forbidden)) {
|
|
20289
20590
|
return {
|
|
@@ -20762,7 +21063,7 @@ async function analyzeSchemaChanges() {
|
|
|
20762
21063
|
try {
|
|
20763
21064
|
const { getDatabasePackagePath: getDatabasePackagePath2 } = await Promise.resolve().then(() => (init_config_loader(), config_loader_exports));
|
|
20764
21065
|
const dbPath = await getDatabasePackagePath2();
|
|
20765
|
-
const schemaPath =
|
|
21066
|
+
const schemaPath = path11__default.join(dbPath, "src/schema/");
|
|
20766
21067
|
const { stdout } = await execa("git", ["diff", "--cached", "--", schemaPath]);
|
|
20767
21068
|
const lines = stdout.split("\n");
|
|
20768
21069
|
return parseDiffLines(lines);
|
|
@@ -21041,7 +21342,7 @@ function categorizeRisks(risks) {
|
|
|
21041
21342
|
}
|
|
21042
21343
|
|
|
21043
21344
|
// src/commands/db/utils/preflight-check.ts
|
|
21044
|
-
|
|
21345
|
+
init_local_supabase();
|
|
21045
21346
|
init_config_loader();
|
|
21046
21347
|
function createInitialResult() {
|
|
21047
21348
|
return {
|
|
@@ -21095,7 +21396,7 @@ function runDatabasePackageChecks(result, logger16, step) {
|
|
|
21095
21396
|
logger16.error("Database package path is missing");
|
|
21096
21397
|
return null;
|
|
21097
21398
|
}
|
|
21098
|
-
logger16.success(`Found database package: ${
|
|
21399
|
+
logger16.success(`Found database package: ${path11__default.basename(dbPackagePath)}`);
|
|
21099
21400
|
logger16.step("Checking schema files", step.next());
|
|
21100
21401
|
const schemaCheck = checkSchemaFiles(dbPackagePath);
|
|
21101
21402
|
if (!schemaCheck.exists) {
|
|
@@ -21219,9 +21520,9 @@ ${stderr}`.toLowerCase();
|
|
|
21219
21520
|
}
|
|
21220
21521
|
}
|
|
21221
21522
|
async function testDatabaseConnection(projectRoot) {
|
|
21222
|
-
const
|
|
21223
|
-
const
|
|
21224
|
-
const
|
|
21523
|
+
const resolvedRoot = process.cwd();
|
|
21524
|
+
const connectionUrl = buildLocalDatabaseUrl(resolvedRoot);
|
|
21525
|
+
const dbPort = detectLocalSupabasePorts(resolvedRoot).db;
|
|
21225
21526
|
let sql = null;
|
|
21226
21527
|
try {
|
|
21227
21528
|
sql = postgres(connectionUrl, {
|
|
@@ -21243,12 +21544,12 @@ async function testDatabaseConnection(projectRoot) {
|
|
|
21243
21544
|
function checkDatabasePackage() {
|
|
21244
21545
|
const cwd = process.cwd();
|
|
21245
21546
|
const locations = [
|
|
21246
|
-
|
|
21247
|
-
|
|
21248
|
-
|
|
21547
|
+
path11__default.join(cwd, "packages", "database"),
|
|
21548
|
+
path11__default.join(cwd, "packages", "db"),
|
|
21549
|
+
path11__default.join(cwd, "db")
|
|
21249
21550
|
];
|
|
21250
21551
|
for (const location of locations) {
|
|
21251
|
-
const configPath =
|
|
21552
|
+
const configPath = path11__default.join(location, "drizzle.config.ts");
|
|
21252
21553
|
if (existsSync(configPath)) {
|
|
21253
21554
|
return { exists: true, path: location };
|
|
21254
21555
|
}
|
|
@@ -21293,7 +21594,7 @@ function countTsFilesRecursive(dir) {
|
|
|
21293
21594
|
try {
|
|
21294
21595
|
const entries = readdirSync(dir, { withFileTypes: true });
|
|
21295
21596
|
for (const entry of entries) {
|
|
21296
|
-
const fullPath =
|
|
21597
|
+
const fullPath = path11__default.join(dir, entry.name);
|
|
21297
21598
|
if (entry.isDirectory()) {
|
|
21298
21599
|
count += countTsFilesRecursive(fullPath);
|
|
21299
21600
|
} else if (entry.isFile() && entry.name.endsWith(".ts")) {
|
|
@@ -21305,7 +21606,7 @@ function countTsFilesRecursive(dir) {
|
|
|
21305
21606
|
return count;
|
|
21306
21607
|
}
|
|
21307
21608
|
function checkSchemaFiles(dbPackagePath) {
|
|
21308
|
-
const schemaDir =
|
|
21609
|
+
const schemaDir = path11__default.join(dbPackagePath, "src", "schema");
|
|
21309
21610
|
if (!existsSync(schemaDir)) {
|
|
21310
21611
|
return { exists: false, count: 0 };
|
|
21311
21612
|
}
|
|
@@ -21342,9 +21643,9 @@ async function runOrphanCheck(env2, dbPackagePath, result, logger16, step) {
|
|
|
21342
21643
|
logger16.step("Checking for orphaned database objects", step.next());
|
|
21343
21644
|
try {
|
|
21344
21645
|
const { expectedTables, expectedEnums } = await extractSchemaTablesAndEnums(dbPackagePath);
|
|
21345
|
-
const databaseUrl = tryResolveDatabaseUrl("local") ||
|
|
21646
|
+
const databaseUrl = tryResolveDatabaseUrl("local") || buildLocalDatabaseUrl(process.cwd());
|
|
21346
21647
|
const { dbTables, dbEnums } = await fetchDbTablesAndEnums(databaseUrl, {
|
|
21347
|
-
schemaDir:
|
|
21648
|
+
schemaDir: path11__default.join(dbPackagePath, "src", "schema")
|
|
21348
21649
|
});
|
|
21349
21650
|
let excludeFromOrphanDetection = [];
|
|
21350
21651
|
let idempotentSqlDir = "supabase/schemas/idempotent";
|
|
@@ -21386,31 +21687,39 @@ function runExtensionConfigCheck(_result, logger16, step) {
|
|
|
21386
21687
|
logger16.step("Checking PostgreSQL extension configuration", step.next());
|
|
21387
21688
|
try {
|
|
21388
21689
|
const extensionResult = checkExtensionConfig(process.cwd());
|
|
21389
|
-
if (extensionResult
|
|
21690
|
+
if (!extensionResult || typeof extensionResult !== "object") {
|
|
21691
|
+
logger16.warn("Extension config check returned invalid result");
|
|
21692
|
+
return;
|
|
21693
|
+
}
|
|
21694
|
+
const usedExtensions = extensionResult.usedExtensions ?? [];
|
|
21695
|
+
if (usedExtensions.length === 0) {
|
|
21390
21696
|
logger16.success("No extensions detected in SQL files");
|
|
21391
21697
|
return;
|
|
21392
21698
|
}
|
|
21699
|
+
const sqlManaged = extensionResult.sqlManagedExtensions ?? [];
|
|
21700
|
+
const supabaseManaged = extensionResult.supabaseManagedExtensions ?? [];
|
|
21393
21701
|
const parts = [];
|
|
21394
|
-
if (
|
|
21395
|
-
parts.push(`${
|
|
21702
|
+
if (sqlManaged.length > 0) {
|
|
21703
|
+
parts.push(`${sqlManaged.length} SQL-managed`);
|
|
21396
21704
|
}
|
|
21397
|
-
if (
|
|
21398
|
-
parts.push(`${
|
|
21705
|
+
if (supabaseManaged.length > 0) {
|
|
21706
|
+
parts.push(`${supabaseManaged.length} Supabase-managed`);
|
|
21399
21707
|
}
|
|
21400
21708
|
const summary = parts.join(", ");
|
|
21401
|
-
logger16.success(`${
|
|
21402
|
-
|
|
21709
|
+
logger16.success(`${usedExtensions.length} extension(s) found (${summary})`);
|
|
21710
|
+
const notes = extensionResult.notes ?? [];
|
|
21711
|
+
for (const note of notes) {
|
|
21403
21712
|
logger16.info(` ${note}`);
|
|
21404
21713
|
}
|
|
21405
21714
|
} catch (error) {
|
|
21406
21715
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
21407
|
-
logger16.warn(`Extension config check
|
|
21716
|
+
logger16.warn(`Extension config check failed: ${message}`);
|
|
21408
21717
|
}
|
|
21409
21718
|
}
|
|
21410
21719
|
async function runSqlSchemaRiskCheck(result, logger16, step) {
|
|
21411
21720
|
logger16.step("Checking SQL schema for risky patterns", step.next());
|
|
21412
21721
|
const cwd = process.cwd();
|
|
21413
|
-
const sqlDir =
|
|
21722
|
+
const sqlDir = path11__default.join(cwd, "supabase", "schemas", "declarative");
|
|
21414
21723
|
if (!existsSync(sqlDir)) {
|
|
21415
21724
|
logger16.success("No SQL schema directory found (supabase/schemas/declarative/)");
|
|
21416
21725
|
return;
|
|
@@ -21423,7 +21732,7 @@ async function runSqlSchemaRiskCheck(result, logger16, step) {
|
|
|
21423
21732
|
}
|
|
21424
21733
|
const allRisks = [];
|
|
21425
21734
|
for (const sqlFile of sqlFiles) {
|
|
21426
|
-
const filePath =
|
|
21735
|
+
const filePath = path11__default.join(sqlDir, sqlFile);
|
|
21427
21736
|
const risks = await detectSchemaRisks(filePath);
|
|
21428
21737
|
for (const risk of risks) {
|
|
21429
21738
|
allRisks.push({ ...risk, file: sqlFile });
|
|
@@ -23175,7 +23484,7 @@ async function checkPort(port) {
|
|
|
23175
23484
|
};
|
|
23176
23485
|
}
|
|
23177
23486
|
function detectSupabasePortsFromConfig() {
|
|
23178
|
-
const configPath =
|
|
23487
|
+
const configPath = path11__default.join(process.cwd(), "supabase", "config.toml");
|
|
23179
23488
|
const BASE_PORTS = { api: 54321, db: 54322, studio: 54323, inbucket: 54324 };
|
|
23180
23489
|
if (!existsSync(configPath)) {
|
|
23181
23490
|
return Object.values(BASE_PORTS);
|
|
@@ -23264,7 +23573,7 @@ var BASE_PORT = 54321;
|
|
|
23264
23573
|
var PORTS_PER_SLOT = 10;
|
|
23265
23574
|
var TOTAL_SLOTS = 100;
|
|
23266
23575
|
function calculatePortOffset(projectPath) {
|
|
23267
|
-
const normalizedPath =
|
|
23576
|
+
const normalizedPath = path11__default.resolve(projectPath);
|
|
23268
23577
|
const hash = createHash("md5").update(normalizedPath).digest("hex");
|
|
23269
23578
|
return parseInt(hash.slice(0, 8), 16) % TOTAL_SLOTS;
|
|
23270
23579
|
}
|
|
@@ -23285,7 +23594,7 @@ function getSupabasePorts(projectPath) {
|
|
|
23285
23594
|
}
|
|
23286
23595
|
function updateSupabaseConfigPorts(projectPath) {
|
|
23287
23596
|
const ports = getSupabasePorts(projectPath);
|
|
23288
|
-
const configPath =
|
|
23597
|
+
const configPath = path11__default.join(projectPath, "supabase", "config.toml");
|
|
23289
23598
|
if (!existsSync(configPath)) {
|
|
23290
23599
|
return { updated: false, ports, configPath };
|
|
23291
23600
|
}
|
|
@@ -23320,7 +23629,7 @@ function getPortAllocationSummary(projectPath) {
|
|
|
23320
23629
|
const ports = getSupabasePorts(projectPath);
|
|
23321
23630
|
const offset = calculatePortOffset(projectPath);
|
|
23322
23631
|
return [
|
|
23323
|
-
`Port allocation for: ${
|
|
23632
|
+
`Port allocation for: ${path11__default.basename(projectPath)}`,
|
|
23324
23633
|
` Slot: ${offset} (hash-based)`,
|
|
23325
23634
|
` API: ${ports.api}`,
|
|
23326
23635
|
` DB: ${ports.db}`,
|
|
@@ -23354,7 +23663,7 @@ function parseSeedPaths(configPath) {
|
|
|
23354
23663
|
}
|
|
23355
23664
|
}
|
|
23356
23665
|
async function applySeedFile(seedPath, dbUrl) {
|
|
23357
|
-
const supabaseDir =
|
|
23666
|
+
const supabaseDir = path11__default.join(process.cwd(), "supabase");
|
|
23358
23667
|
const absolutePath = resolveSafePath(supabaseDir, seedPath);
|
|
23359
23668
|
if (!existsSync(absolutePath)) {
|
|
23360
23669
|
return;
|
|
@@ -23365,12 +23674,12 @@ async function applySeedFile(seedPath, dbUrl) {
|
|
|
23365
23674
|
});
|
|
23366
23675
|
}
|
|
23367
23676
|
async function applySeeds2(configPath) {
|
|
23368
|
-
const configFile =
|
|
23677
|
+
const configFile = path11__default.join(process.cwd(), "supabase", "config.toml");
|
|
23369
23678
|
const seedPaths = parseSeedPaths(configFile);
|
|
23370
23679
|
if (seedPaths.length === 0) {
|
|
23371
23680
|
return;
|
|
23372
23681
|
}
|
|
23373
|
-
const supabaseDir =
|
|
23682
|
+
const supabaseDir = path11__default.join(process.cwd(), "supabase");
|
|
23374
23683
|
const safePaths = filterSafePaths(seedPaths, supabaseDir);
|
|
23375
23684
|
if (safePaths.length === 0) {
|
|
23376
23685
|
return;
|
|
@@ -23788,7 +24097,7 @@ var validateCommand = new Command("validate").description("Validate schema files
|
|
|
23788
24097
|
const logger16 = createCLILogger("db:validate");
|
|
23789
24098
|
try {
|
|
23790
24099
|
logger16.section("Schema Validation");
|
|
23791
|
-
const schemasPath =
|
|
24100
|
+
const schemasPath = path11__default.join(process.cwd(), "packages", "database", "src", "schema");
|
|
23792
24101
|
if (!existsSync(schemasPath)) {
|
|
23793
24102
|
throw new CLIError("Schema directory not found", "SCHEMA_DIR_NOT_FOUND", [
|
|
23794
24103
|
`Expected location: ${schemasPath}`,
|
|
@@ -23953,8 +24262,8 @@ var generateCommand = new Command("generate").description("Generate TypeScript t
|
|
|
23953
24262
|
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
24263
|
const logger16 = createCLILogger("db:schema:list");
|
|
23955
24264
|
try {
|
|
23956
|
-
const dbPackagePath =
|
|
23957
|
-
if (!existsSync(
|
|
24265
|
+
const dbPackagePath = path11__default.join(process.cwd(), "packages", "database");
|
|
24266
|
+
if (!existsSync(path11__default.join(dbPackagePath, "drizzle.config.ts"))) {
|
|
23958
24267
|
throw new CLIError("drizzle.config.ts not found", "CONFIG_NOT_FOUND", [
|
|
23959
24268
|
`Expected location: ${dbPackagePath}/drizzle.config.ts`,
|
|
23960
24269
|
"Ensure you are in the project root",
|
|
@@ -25193,7 +25502,7 @@ var testGenCommand = new Command("test:gen").description("Generate pgTAP behavio
|
|
|
25193
25502
|
const databaseUrl = options.db || process.env.DATABASE_URL || getLocalDbUrl();
|
|
25194
25503
|
const schemas = options.schemas ? options.schemas.split(",") : void 0;
|
|
25195
25504
|
const dbPackage = databasePaths.package();
|
|
25196
|
-
const defaultOutputPath =
|
|
25505
|
+
const defaultOutputPath = path11__default.join(dbPackage, "tests/00_behavior.generated.test.sql");
|
|
25197
25506
|
const outputPath = options.output || defaultOutputPath;
|
|
25198
25507
|
spinner.text = "Generating pgTAP behavior tests...";
|
|
25199
25508
|
const result = await dbGeneratePgTapTests({
|
|
@@ -26205,108 +26514,8 @@ async function getVercelRootDirectory() {
|
|
|
26205
26514
|
return info?.rootDirectory ?? null;
|
|
26206
26515
|
}
|
|
26207
26516
|
|
|
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
26517
|
// src/commands/env/commands/env-pull.ts
|
|
26518
|
+
init_local_supabase();
|
|
26310
26519
|
var ERROR_MESSAGES3 = {
|
|
26311
26520
|
INVALID_PATH: "Invalid working directory path",
|
|
26312
26521
|
PATH_TRAVERSAL: "Working directory path validation failed",
|
|
@@ -26338,16 +26547,16 @@ function sanitizeErrorMessage(message) {
|
|
|
26338
26547
|
return sanitized;
|
|
26339
26548
|
}
|
|
26340
26549
|
function containsPathTraversal3(inputPath) {
|
|
26341
|
-
const normalized =
|
|
26550
|
+
const normalized = path11__default.normalize(inputPath);
|
|
26342
26551
|
return normalized.includes("..") || inputPath.includes("\0");
|
|
26343
26552
|
}
|
|
26344
26553
|
function isPathWithinBase2(filePath, baseDir) {
|
|
26345
26554
|
try {
|
|
26346
|
-
const resolvedFile =
|
|
26347
|
-
const resolvedBase =
|
|
26348
|
-
const normalizedFile =
|
|
26349
|
-
const normalizedBase =
|
|
26350
|
-
return normalizedFile === normalizedBase || normalizedFile.startsWith(normalizedBase +
|
|
26555
|
+
const resolvedFile = path11__default.resolve(filePath);
|
|
26556
|
+
const resolvedBase = path11__default.resolve(baseDir);
|
|
26557
|
+
const normalizedFile = path11__default.normalize(resolvedFile);
|
|
26558
|
+
const normalizedBase = path11__default.normalize(resolvedBase);
|
|
26559
|
+
return normalizedFile === normalizedBase || normalizedFile.startsWith(normalizedBase + path11__default.sep);
|
|
26351
26560
|
} catch {
|
|
26352
26561
|
return false;
|
|
26353
26562
|
}
|
|
@@ -26356,12 +26565,12 @@ function validateCustomWorkingDir(cwdPath, projectRoot) {
|
|
|
26356
26565
|
if (containsPathTraversal3(cwdPath)) {
|
|
26357
26566
|
throw new CLIError(ERROR_MESSAGES3.PATH_TRAVERSAL, "ENV_PULL_PATH_TRAVERSAL");
|
|
26358
26567
|
}
|
|
26359
|
-
const absolutePath =
|
|
26568
|
+
const absolutePath = path11__default.isAbsolute(cwdPath) ? cwdPath : path11__default.resolve(projectRoot, cwdPath);
|
|
26360
26569
|
let resolvedProjectRoot;
|
|
26361
26570
|
try {
|
|
26362
26571
|
resolvedProjectRoot = realpathSync(projectRoot);
|
|
26363
26572
|
} catch {
|
|
26364
|
-
resolvedProjectRoot =
|
|
26573
|
+
resolvedProjectRoot = path11__default.resolve(projectRoot);
|
|
26365
26574
|
}
|
|
26366
26575
|
if (!isPathWithinBase2(absolutePath, resolvedProjectRoot)) {
|
|
26367
26576
|
throw new CLIError(ERROR_MESSAGES3.PATH_TRAVERSAL, "ENV_PULL_PATH_TRAVERSAL");
|
|
@@ -26388,6 +26597,27 @@ function validateAppDirectory2(appName, projectRoot) {
|
|
|
26388
26597
|
}
|
|
26389
26598
|
return appDir;
|
|
26390
26599
|
}
|
|
26600
|
+
var LOCAL_BOOTSTRAP_REQUIRED_KEYS = [
|
|
26601
|
+
"LOCAL_SUPABASE_HOST",
|
|
26602
|
+
"LOCAL_SUPABASE_API_PORT",
|
|
26603
|
+
"LOCAL_SUPABASE_DB_PORT"
|
|
26604
|
+
];
|
|
26605
|
+
function getMissingLocalBootstrapKeys(parsed) {
|
|
26606
|
+
return LOCAL_BOOTSTRAP_REQUIRED_KEYS.filter((key) => !(key in parsed));
|
|
26607
|
+
}
|
|
26608
|
+
function getLocalBootstrapStatusFromParsed(parsed) {
|
|
26609
|
+
const missing = getMissingLocalBootstrapKeys(parsed);
|
|
26610
|
+
return {
|
|
26611
|
+
status: missing.length === 0 ? "ok" : "partial",
|
|
26612
|
+
missing
|
|
26613
|
+
};
|
|
26614
|
+
}
|
|
26615
|
+
function missingLocalBootstrapReport() {
|
|
26616
|
+
return {
|
|
26617
|
+
status: "missing",
|
|
26618
|
+
missing: [...LOCAL_BOOTSTRAP_REQUIRED_KEYS]
|
|
26619
|
+
};
|
|
26620
|
+
}
|
|
26391
26621
|
var EnvPullOutputSchema = z.object({
|
|
26392
26622
|
results: z.array(
|
|
26393
26623
|
z.object({
|
|
@@ -26401,6 +26631,10 @@ var EnvPullOutputSchema = z.object({
|
|
|
26401
26631
|
totalPulled: z.number(),
|
|
26402
26632
|
totalFailed: z.number(),
|
|
26403
26633
|
totalEncrypted: z.number().optional(),
|
|
26634
|
+
localBootstrap: z.object({
|
|
26635
|
+
status: z.enum(["ok", "partial", "missing"]),
|
|
26636
|
+
missing: z.array(z.string())
|
|
26637
|
+
}).optional(),
|
|
26404
26638
|
/** Vercel Root Directory sync result */
|
|
26405
26639
|
vercelSync: z.object({
|
|
26406
26640
|
detected: z.boolean(),
|
|
@@ -26533,60 +26767,74 @@ function removePrivateKeysFromEnvFile(filePath) {
|
|
|
26533
26767
|
writeFileSync(filePath, `${newContent}
|
|
26534
26768
|
`, "utf-8");
|
|
26535
26769
|
}
|
|
26536
|
-
function replaceWithLocalValues(filePath, logger16, dryRun = false) {
|
|
26770
|
+
function replaceWithLocalValues(filePath, workDir, logger16, dryRun = false) {
|
|
26537
26771
|
if (!existsSync(filePath)) {
|
|
26538
26772
|
logger16.warn(" \u26A0\uFE0F .env.development not found (was pull successful?)");
|
|
26539
|
-
return;
|
|
26773
|
+
return missingLocalBootstrapReport();
|
|
26540
26774
|
}
|
|
26541
26775
|
const content = readFileSync(filePath, "utf-8");
|
|
26542
26776
|
if (!content.trim()) {
|
|
26543
26777
|
logger16.warn(" \u26A0\uFE0F .env.development is empty (no env vars in Vercel?)");
|
|
26544
|
-
return;
|
|
26778
|
+
return missingLocalBootstrapReport();
|
|
26545
26779
|
}
|
|
26546
26780
|
const parsed = parse(content);
|
|
26547
26781
|
if (Object.keys(parsed).length === 0) {
|
|
26548
26782
|
logger16.warn(" \u26A0\uFE0F No valid env vars found in .env.development (check syntax)");
|
|
26549
|
-
return;
|
|
26783
|
+
return missingLocalBootstrapReport();
|
|
26550
26784
|
}
|
|
26551
|
-
const
|
|
26552
|
-
const
|
|
26553
|
-
const localDescriptions = getLocalValueDescriptions(projectRoot);
|
|
26785
|
+
const localEnvValues = getLocalSupabaseEnvValues(workDir);
|
|
26786
|
+
const localDescriptions = getLocalValueDescriptions(workDir);
|
|
26554
26787
|
const replacementKeys = Object.keys(localEnvValues);
|
|
26555
26788
|
const replacedKeys = [];
|
|
26789
|
+
const hasAnySupabaseKey = Object.keys(parsed).some((key) => key.includes("SUPABASE") || key.includes("DATABASE_URL")) || replacementKeys.some((key) => key in parsed);
|
|
26556
26790
|
for (const key of replacementKeys) {
|
|
26557
|
-
if (key in parsed
|
|
26791
|
+
if (key in parsed || hasAnySupabaseKey) {
|
|
26558
26792
|
parsed[key] = localEnvValues[key];
|
|
26559
26793
|
replacedKeys.push(key);
|
|
26560
26794
|
}
|
|
26561
26795
|
}
|
|
26562
26796
|
if (replacedKeys.length === 0) {
|
|
26563
|
-
const
|
|
26797
|
+
const hasAnySupabaseKey2 = Object.keys(parsed).some(
|
|
26564
26798
|
(k) => k.includes("SUPABASE") || k.includes("DATABASE_URL")
|
|
26565
26799
|
);
|
|
26566
|
-
if (
|
|
26800
|
+
if (hasAnySupabaseKey2) {
|
|
26567
26801
|
logger16.warn(" \u26A0\uFE0F Found Supabase keys but none matched local replacement patterns");
|
|
26568
26802
|
} else {
|
|
26569
26803
|
logger16.info(" No Supabase variables found (may be expected for new projects)");
|
|
26570
26804
|
}
|
|
26571
|
-
return;
|
|
26805
|
+
return getLocalBootstrapStatusFromParsed(parsed);
|
|
26806
|
+
}
|
|
26807
|
+
const bootstrapStatus = getLocalBootstrapStatusFromParsed(parsed);
|
|
26808
|
+
if (bootstrapStatus.missing.length > 0) {
|
|
26809
|
+
logger16.warn(
|
|
26810
|
+
` \u26A0\uFE0F Some local bootstrap keys were not updated: ${bootstrapStatus.missing.join(", ")}`
|
|
26811
|
+
);
|
|
26572
26812
|
}
|
|
26813
|
+
const ensureRequired = bootstrapStatus.status === "ok";
|
|
26573
26814
|
const newContent = Object.entries(parsed).map(([key, value]) => `${key}=${formatEnvValue(value)}`).join("\n");
|
|
26574
26815
|
if (dryRun) {
|
|
26575
|
-
logger16.info(` \u{1F3E0} Would replace ${replacedKeys.length} variables with
|
|
26816
|
+
logger16.info(` \u{1F3E0} Would replace ${replacedKeys.length} variables with local values:`);
|
|
26576
26817
|
for (const key of replacedKeys) {
|
|
26577
26818
|
const description = localDescriptions[key] || "localhost";
|
|
26578
26819
|
logger16.info(` ${key} \u2192 ${description}`);
|
|
26579
26820
|
}
|
|
26821
|
+
logger16.info(
|
|
26822
|
+
` ${ensureRequired ? "\u2705" : "\u26A0\uFE0F"} Local bootstrap keys ready: ${ensureRequired ? "yes" : "partial"}`
|
|
26823
|
+
);
|
|
26580
26824
|
logger16.info(" (dry-run: no files modified)");
|
|
26581
|
-
return;
|
|
26825
|
+
return bootstrapStatus;
|
|
26582
26826
|
}
|
|
26583
26827
|
writeFileSync(filePath, `${newContent}
|
|
26584
26828
|
`, "utf-8");
|
|
26585
|
-
logger16.success(` \u{1F3E0} Replaced ${replacedKeys.length} variables with
|
|
26829
|
+
logger16.success(` \u{1F3E0} Replaced ${replacedKeys.length} variables with local values:`);
|
|
26586
26830
|
for (const key of replacedKeys) {
|
|
26587
26831
|
const description = localDescriptions[key] || "localhost";
|
|
26588
26832
|
logger16.info(` ${key} \u2192 ${description}`);
|
|
26589
26833
|
}
|
|
26834
|
+
logger16.info(
|
|
26835
|
+
` ${ensureRequired ? "\u2705" : "\u26A0\uFE0F"} Local bootstrap keys ready: ${ensureRequired ? "yes" : "partial"}`
|
|
26836
|
+
);
|
|
26837
|
+
return bootstrapStatus;
|
|
26590
26838
|
}
|
|
26591
26839
|
async function encryptFile2(workDir, filePath, logger16) {
|
|
26592
26840
|
if (!existsSync(filePath)) {
|
|
@@ -26818,13 +27066,20 @@ async function runEnvPullAction(options) {
|
|
|
26818
27066
|
}
|
|
26819
27067
|
logger16.info("");
|
|
26820
27068
|
const results = await pullAllEnvironments(workDir, environments, auth, logger16);
|
|
26821
|
-
|
|
27069
|
+
const shouldEvaluateLocalBootstrap = !options.productionAll && (!options.environment || options.environment === "development");
|
|
27070
|
+
let localBootstrap;
|
|
27071
|
+
if (shouldEvaluateLocalBootstrap) {
|
|
26822
27072
|
const devResult = results.find((r) => r.environment === "development" && r.success);
|
|
26823
27073
|
if (devResult) {
|
|
26824
27074
|
logger16.info("");
|
|
26825
27075
|
logger16.info("Applying local Supabase values to .env.development...");
|
|
26826
|
-
replaceWithLocalValues(devResult.outputPath, logger16, options.check);
|
|
27076
|
+
localBootstrap = replaceWithLocalValues(devResult.outputPath, workDir, logger16, options.check);
|
|
27077
|
+
} else {
|
|
27078
|
+
localBootstrap = missingLocalBootstrapReport();
|
|
27079
|
+
logger16.warn(" LOCAL_BOOTSTRAP: skipped (development env pull failed or missing)");
|
|
26827
27080
|
}
|
|
27081
|
+
} else {
|
|
27082
|
+
localBootstrap = void 0;
|
|
26828
27083
|
}
|
|
26829
27084
|
if (options.encrypt && !options.check) {
|
|
26830
27085
|
await encryptPulledFiles(workDir, results, logger16);
|
|
@@ -26845,6 +27100,7 @@ async function runEnvPullAction(options) {
|
|
|
26845
27100
|
totalPulled,
|
|
26846
27101
|
totalFailed,
|
|
26847
27102
|
totalEncrypted: options.encrypt ? totalEncrypted : void 0,
|
|
27103
|
+
localBootstrap,
|
|
26848
27104
|
vercelSync: vercelSyncResult
|
|
26849
27105
|
});
|
|
26850
27106
|
if (totalFailed > 0) {
|
|
@@ -28584,13 +28840,13 @@ z.object({
|
|
|
28584
28840
|
|
|
28585
28841
|
// src/commands/hotfix/metadata.ts
|
|
28586
28842
|
function getHotfixDir(targetDir) {
|
|
28587
|
-
return
|
|
28843
|
+
return path11__default.join(targetDir, ".runa", "hotfix");
|
|
28588
28844
|
}
|
|
28589
28845
|
function getCurrentFile(targetDir) {
|
|
28590
|
-
return
|
|
28846
|
+
return path11__default.join(getHotfixDir(targetDir), "current.json");
|
|
28591
28847
|
}
|
|
28592
28848
|
function getArchiveDir(targetDir) {
|
|
28593
|
-
return
|
|
28849
|
+
return path11__default.join(getHotfixDir(targetDir), "archive");
|
|
28594
28850
|
}
|
|
28595
28851
|
async function ensureHotfixDir(targetDir) {
|
|
28596
28852
|
await mkdir(getHotfixDir(targetDir), { recursive: true });
|
|
@@ -28600,7 +28856,7 @@ async function ensureArchiveDir(targetDir) {
|
|
|
28600
28856
|
}
|
|
28601
28857
|
async function getHotfixMetadata(input3 = {}) {
|
|
28602
28858
|
const parsed = GetHotfixInputSchema.parse(input3);
|
|
28603
|
-
const targetDir = parsed.targetDir ?
|
|
28859
|
+
const targetDir = parsed.targetDir ? path11__default.resolve(parsed.targetDir) : process.cwd();
|
|
28604
28860
|
const currentFile = getCurrentFile(targetDir);
|
|
28605
28861
|
if (!existsSync(currentFile)) {
|
|
28606
28862
|
return null;
|
|
@@ -28610,7 +28866,7 @@ async function getHotfixMetadata(input3 = {}) {
|
|
|
28610
28866
|
}
|
|
28611
28867
|
async function createHotfixMetadata(input3) {
|
|
28612
28868
|
const parsed = CreateHotfixMetadataInputSchema.parse(input3);
|
|
28613
|
-
const targetDir = parsed.targetDir ?
|
|
28869
|
+
const targetDir = parsed.targetDir ? path11__default.resolve(parsed.targetDir) : process.cwd();
|
|
28614
28870
|
await ensureHotfixDir(targetDir);
|
|
28615
28871
|
const metadata = {
|
|
28616
28872
|
branch: parsed.branch,
|
|
@@ -28627,7 +28883,7 @@ async function createHotfixMetadata(input3) {
|
|
|
28627
28883
|
}
|
|
28628
28884
|
async function updateHotfixStatus(input3) {
|
|
28629
28885
|
const parsed = UpdateHotfixInputSchema.parse(input3);
|
|
28630
|
-
const targetDir = parsed.targetDir ?
|
|
28886
|
+
const targetDir = parsed.targetDir ? path11__default.resolve(parsed.targetDir) : process.cwd();
|
|
28631
28887
|
const current = await getHotfixMetadata({ targetDir });
|
|
28632
28888
|
if (!current) {
|
|
28633
28889
|
throw createError("HOTFIX_NOT_FOUND");
|
|
@@ -28644,13 +28900,13 @@ async function updateHotfixStatus(input3) {
|
|
|
28644
28900
|
return updated;
|
|
28645
28901
|
}
|
|
28646
28902
|
async function archiveHotfix(targetDir) {
|
|
28647
|
-
const dir = targetDir ?
|
|
28903
|
+
const dir = targetDir ? path11__default.resolve(targetDir) : process.cwd();
|
|
28648
28904
|
const current = await getHotfixMetadata({ targetDir: dir });
|
|
28649
28905
|
if (!current) {
|
|
28650
28906
|
throw createError("HOTFIX_NOT_FOUND");
|
|
28651
28907
|
}
|
|
28652
28908
|
await ensureArchiveDir(dir);
|
|
28653
|
-
const archiveFile =
|
|
28909
|
+
const archiveFile = path11__default.join(
|
|
28654
28910
|
getArchiveDir(dir),
|
|
28655
28911
|
`${current.branch.replace(/\//g, "-")}-${Date.now()}.json`
|
|
28656
28912
|
);
|
|
@@ -28666,7 +28922,7 @@ async function archiveHotfix(targetDir) {
|
|
|
28666
28922
|
return archived;
|
|
28667
28923
|
}
|
|
28668
28924
|
async function cancelHotfix(targetDir) {
|
|
28669
|
-
const dir = targetDir ?
|
|
28925
|
+
const dir = targetDir ? path11__default.resolve(targetDir) : process.cwd();
|
|
28670
28926
|
await updateHotfixStatus({
|
|
28671
28927
|
status: "cancelled",
|
|
28672
28928
|
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -28676,7 +28932,7 @@ async function cancelHotfix(targetDir) {
|
|
|
28676
28932
|
}
|
|
28677
28933
|
async function listArchivedHotfixes(input3 = {}) {
|
|
28678
28934
|
const parsed = ListArchivedHotfixesInputSchema.parse(input3);
|
|
28679
|
-
const targetDir = parsed.targetDir ?
|
|
28935
|
+
const targetDir = parsed.targetDir ? path11__default.resolve(parsed.targetDir) : process.cwd();
|
|
28680
28936
|
const archiveDir = getArchiveDir(targetDir);
|
|
28681
28937
|
if (!existsSync(archiveDir)) {
|
|
28682
28938
|
return { hotfixes: [] };
|
|
@@ -28685,7 +28941,7 @@ async function listArchivedHotfixes(input3 = {}) {
|
|
|
28685
28941
|
const hotfixes = [];
|
|
28686
28942
|
for (const file of files.filter((f) => f.endsWith(".json"))) {
|
|
28687
28943
|
try {
|
|
28688
|
-
const content = await readFile(
|
|
28944
|
+
const content = await readFile(path11__default.join(archiveDir, file), "utf-8");
|
|
28689
28945
|
hotfixes.push(HotfixMetadataSchema.parse(JSON.parse(content)));
|
|
28690
28946
|
} catch {
|
|
28691
28947
|
}
|
|
@@ -30185,13 +30441,13 @@ function validateVersion(version) {
|
|
|
30185
30441
|
}
|
|
30186
30442
|
}
|
|
30187
30443
|
function getCacheBase() {
|
|
30188
|
-
return
|
|
30444
|
+
return path11__default.join(os.homedir(), ".cache", "runa", "templates");
|
|
30189
30445
|
}
|
|
30190
30446
|
function getCacheDir(version) {
|
|
30191
30447
|
validateVersion(version);
|
|
30192
30448
|
const cacheBase = getCacheBase();
|
|
30193
|
-
const cacheDir =
|
|
30194
|
-
const resolvedCacheDir =
|
|
30449
|
+
const cacheDir = path11__default.join(cacheBase, version);
|
|
30450
|
+
const resolvedCacheDir = path11__default.resolve(cacheDir);
|
|
30195
30451
|
if (!isPathContained(cacheBase, resolvedCacheDir)) {
|
|
30196
30452
|
throw new CLIError(
|
|
30197
30453
|
"Security: cache directory would escape allowed location.",
|
|
@@ -30203,7 +30459,7 @@ function getCacheDir(version) {
|
|
|
30203
30459
|
}
|
|
30204
30460
|
function isCached(version) {
|
|
30205
30461
|
const cacheDir = getCacheDir(version);
|
|
30206
|
-
const templatesDir =
|
|
30462
|
+
const templatesDir = path11__default.join(cacheDir, "templates");
|
|
30207
30463
|
return fs5__default.existsSync(templatesDir);
|
|
30208
30464
|
}
|
|
30209
30465
|
var scopedAuthToken = null;
|
|
@@ -30254,13 +30510,13 @@ async function createTempPackageJson(tempDir, version) {
|
|
|
30254
30510
|
[TEMPLATES_PACKAGE_NAME]: version
|
|
30255
30511
|
}
|
|
30256
30512
|
};
|
|
30257
|
-
await writeFile(
|
|
30513
|
+
await writeFile(path11__default.join(tempDir, "package.json"), JSON.stringify(packageJson, null, 2));
|
|
30258
30514
|
}
|
|
30259
30515
|
async function createTempNpmrc(tempDir) {
|
|
30260
30516
|
const npmrc = `@r06-dev:registry=${GITHUB_PACKAGES_REGISTRY}
|
|
30261
30517
|
//npm.pkg.github.com/:_authToken=\${NODE_AUTH_TOKEN}
|
|
30262
30518
|
`;
|
|
30263
|
-
await writeFile(
|
|
30519
|
+
await writeFile(path11__default.join(tempDir, ".npmrc"), npmrc);
|
|
30264
30520
|
}
|
|
30265
30521
|
async function installTemplates(tempDir, authToken, verbose) {
|
|
30266
30522
|
try {
|
|
@@ -30318,10 +30574,10 @@ async function installTemplates(tempDir, authToken, verbose) {
|
|
|
30318
30574
|
async function verifyNoSymlinks(dir, baseDir) {
|
|
30319
30575
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
30320
30576
|
for (const entry of entries) {
|
|
30321
|
-
const fullPath =
|
|
30577
|
+
const fullPath = path11__default.join(dir, entry.name);
|
|
30322
30578
|
const stats = await lstat(fullPath);
|
|
30323
30579
|
if (stats.isSymbolicLink()) {
|
|
30324
|
-
const relativePath =
|
|
30580
|
+
const relativePath = path11__default.relative(baseDir, fullPath);
|
|
30325
30581
|
throw new CLIError(
|
|
30326
30582
|
"Security: Symlink detected in template package.",
|
|
30327
30583
|
"SYMLINK_ATTACK_DETECTED",
|
|
@@ -30339,7 +30595,7 @@ async function verifyNoSymlinks(dir, baseDir) {
|
|
|
30339
30595
|
}
|
|
30340
30596
|
}
|
|
30341
30597
|
async function copyToCache(tempDir, cacheDir) {
|
|
30342
|
-
const sourceTemplates =
|
|
30598
|
+
const sourceTemplates = path11__default.join(tempDir, "node_modules", TEMPLATES_PACKAGE_NAME, "templates");
|
|
30343
30599
|
if (!fs5__default.existsSync(sourceTemplates)) {
|
|
30344
30600
|
throw new CLIError("Templates directory not found in package.", "TEMPLATES_DIR_NOT_FOUND", [
|
|
30345
30601
|
`Expected: ${sourceTemplates}`,
|
|
@@ -30349,7 +30605,7 @@ async function copyToCache(tempDir, cacheDir) {
|
|
|
30349
30605
|
}
|
|
30350
30606
|
await verifyNoSymlinks(sourceTemplates, sourceTemplates);
|
|
30351
30607
|
await mkdir(cacheDir, { recursive: true });
|
|
30352
|
-
const targetTemplates =
|
|
30608
|
+
const targetTemplates = path11__default.join(cacheDir, "templates");
|
|
30353
30609
|
await cp(sourceTemplates, targetTemplates, {
|
|
30354
30610
|
recursive: true,
|
|
30355
30611
|
dereference: false
|
|
@@ -30371,7 +30627,7 @@ async function fetchTemplates(options = {}) {
|
|
|
30371
30627
|
};
|
|
30372
30628
|
}
|
|
30373
30629
|
const cacheDir = getCacheDir(version);
|
|
30374
|
-
const templatesDir =
|
|
30630
|
+
const templatesDir = path11__default.join(cacheDir, "templates");
|
|
30375
30631
|
if (!fresh && isCached(version)) {
|
|
30376
30632
|
if (verbose) {
|
|
30377
30633
|
console.log(`Using cached templates v${version}: ${templatesDir}`);
|
|
@@ -30387,7 +30643,7 @@ async function fetchTemplates(options = {}) {
|
|
|
30387
30643
|
if (fresh && fs5__default.existsSync(cacheDir)) {
|
|
30388
30644
|
await rm(cacheDir, { recursive: true, force: true });
|
|
30389
30645
|
}
|
|
30390
|
-
const tempDir =
|
|
30646
|
+
const tempDir = path11__default.join(os.tmpdir(), `runa-templates-${Date.now()}`);
|
|
30391
30647
|
await mkdir(tempDir, { recursive: true });
|
|
30392
30648
|
try {
|
|
30393
30649
|
if (verbose) {
|
|
@@ -30415,11 +30671,11 @@ async function fetchTemplates(options = {}) {
|
|
|
30415
30671
|
}
|
|
30416
30672
|
var MAX_WORKSPACE_TRAVERSAL_DEPTH = 10;
|
|
30417
30673
|
function isLegitimateWorkspaceRoot(workspaceRoot) {
|
|
30418
|
-
const pnpmWorkspaceFile =
|
|
30674
|
+
const pnpmWorkspaceFile = path11__default.join(workspaceRoot, "pnpm-workspace.yaml");
|
|
30419
30675
|
if (!fs5__default.existsSync(pnpmWorkspaceFile)) {
|
|
30420
30676
|
return false;
|
|
30421
30677
|
}
|
|
30422
|
-
const rootPackageFile =
|
|
30678
|
+
const rootPackageFile = path11__default.join(workspaceRoot, "package.json");
|
|
30423
30679
|
if (!fs5__default.existsSync(rootPackageFile)) {
|
|
30424
30680
|
return false;
|
|
30425
30681
|
}
|
|
@@ -30433,24 +30689,24 @@ function isLegitimateWorkspaceRoot(workspaceRoot) {
|
|
|
30433
30689
|
}
|
|
30434
30690
|
}
|
|
30435
30691
|
function resolveWorkspaceTemplates() {
|
|
30436
|
-
let current =
|
|
30437
|
-
const root =
|
|
30692
|
+
let current = path11__default.resolve(process.cwd());
|
|
30693
|
+
const root = path11__default.parse(current).root;
|
|
30438
30694
|
let depth = 0;
|
|
30439
30695
|
while (current !== root && depth < MAX_WORKSPACE_TRAVERSAL_DEPTH) {
|
|
30440
30696
|
if (!isLegitimateWorkspaceRoot(current)) {
|
|
30441
|
-
current =
|
|
30697
|
+
current = path11__default.dirname(current);
|
|
30442
30698
|
depth++;
|
|
30443
30699
|
continue;
|
|
30444
30700
|
}
|
|
30445
|
-
const packagesTemplates =
|
|
30446
|
-
const normalizedTemplatesPath =
|
|
30447
|
-
if (!normalizedTemplatesPath.startsWith(current +
|
|
30448
|
-
current =
|
|
30701
|
+
const packagesTemplates = path11__default.join(current, "packages", "runa-templates", "templates");
|
|
30702
|
+
const normalizedTemplatesPath = path11__default.resolve(packagesTemplates);
|
|
30703
|
+
if (!normalizedTemplatesPath.startsWith(current + path11__default.sep)) {
|
|
30704
|
+
current = path11__default.dirname(current);
|
|
30449
30705
|
depth++;
|
|
30450
30706
|
continue;
|
|
30451
30707
|
}
|
|
30452
30708
|
if (fs5__default.existsSync(normalizedTemplatesPath)) {
|
|
30453
|
-
const markerFile =
|
|
30709
|
+
const markerFile = path11__default.join(current, "packages", "runa-templates", "package.json");
|
|
30454
30710
|
if (fs5__default.existsSync(markerFile)) {
|
|
30455
30711
|
try {
|
|
30456
30712
|
const pkg = JSON.parse(fs5__default.readFileSync(markerFile, "utf-8"));
|
|
@@ -30461,7 +30717,7 @@ function resolveWorkspaceTemplates() {
|
|
|
30461
30717
|
}
|
|
30462
30718
|
}
|
|
30463
30719
|
}
|
|
30464
|
-
current =
|
|
30720
|
+
current = path11__default.dirname(current);
|
|
30465
30721
|
depth++;
|
|
30466
30722
|
}
|
|
30467
30723
|
return void 0;
|
|
@@ -30470,9 +30726,9 @@ function resolveWorkspaceTemplates() {
|
|
|
30470
30726
|
// src/commands/init.ts
|
|
30471
30727
|
function checkZodVersion(_logger) {
|
|
30472
30728
|
const zodPaths = [
|
|
30473
|
-
|
|
30729
|
+
path11__default.join(process.cwd(), "node_modules", "zod", "package.json"),
|
|
30474
30730
|
// Check home directory node_modules (common source of conflicts)
|
|
30475
|
-
|
|
30731
|
+
path11__default.join(process.env.HOME ?? "", "node_modules", "zod", "package.json")
|
|
30476
30732
|
];
|
|
30477
30733
|
for (const zodPath of zodPaths) {
|
|
30478
30734
|
if (!fs5__default.existsSync(zodPath)) continue;
|
|
@@ -30486,7 +30742,7 @@ function checkZodVersion(_logger) {
|
|
|
30486
30742
|
"ZOD_VERSION_INCOMPATIBLE",
|
|
30487
30743
|
[
|
|
30488
30744
|
"runa SDK requires zod v4.x, but found zod v3.x",
|
|
30489
|
-
`Conflicting zod found at: ${
|
|
30745
|
+
`Conflicting zod found at: ${path11__default.dirname(zodPath)}`,
|
|
30490
30746
|
"",
|
|
30491
30747
|
"Solutions:",
|
|
30492
30748
|
"1. Run from a clean project directory without existing node_modules",
|
|
@@ -30738,7 +30994,7 @@ var initCommand = new Command("init").description("Initialize pj-repo with runa
|
|
|
30738
30994
|
try {
|
|
30739
30995
|
if (options.repairConfig) {
|
|
30740
30996
|
logger16.section("Repairing runa.config.ts");
|
|
30741
|
-
const projectName2 =
|
|
30997
|
+
const projectName2 = path11__default.basename(process.cwd());
|
|
30742
30998
|
const repairResult = repairRunaConfig(process.cwd(), projectName2);
|
|
30743
30999
|
if (repairResult.success) {
|
|
30744
31000
|
logger16.success("\u2705 Config file repaired successfully");
|
|
@@ -30789,7 +31045,7 @@ var initCommand = new Command("init").description("Initialize pj-repo with runa
|
|
|
30789
31045
|
existingConfig,
|
|
30790
31046
|
force: options.force === true
|
|
30791
31047
|
});
|
|
30792
|
-
const projectName =
|
|
31048
|
+
const projectName = path11__default.basename(process.cwd());
|
|
30793
31049
|
logger16.info(`Project: ${projectName}`);
|
|
30794
31050
|
logger16.step("Applying templates (SDK)", 1);
|
|
30795
31051
|
const result = await initProject({
|
|
@@ -30886,7 +31142,9 @@ var InjectTestAttrsInputSchema = z.object({
|
|
|
30886
31142
|
/** Manifest output directory */
|
|
30887
31143
|
manifestDir: z.string().default(".runa"),
|
|
30888
31144
|
/** Resolve Zod schemas to JSON Schema (v7+) for API contracts */
|
|
30889
|
-
resolveSchemas: z.boolean().default(false)
|
|
31145
|
+
resolveSchemas: z.boolean().default(false),
|
|
31146
|
+
/** Fail when heuristic fallback detection is used */
|
|
31147
|
+
strictDetect: z.boolean().default(false)
|
|
30890
31148
|
}).strict();
|
|
30891
31149
|
var FileChangeSchema = z.object({
|
|
30892
31150
|
filePath: z.string(),
|
|
@@ -30898,6 +31156,51 @@ var MachineWithoutE2EMetaSchema = z.object({
|
|
|
30898
31156
|
id: z.string(),
|
|
30899
31157
|
sourceFile: z.string()
|
|
30900
31158
|
});
|
|
31159
|
+
var DetectionScanSummarySchema = z.object({
|
|
31160
|
+
totalFiles: z.number().int().nonnegative(),
|
|
31161
|
+
filesWithMachineDefinitions: z.number().int().nonnegative(),
|
|
31162
|
+
filesWithMachineHooks: z.number().int().nonnegative(),
|
|
31163
|
+
filesWithJsxOnly: z.number().int().nonnegative()
|
|
31164
|
+
}).strict();
|
|
31165
|
+
var MachineResolutionDetailSchema = z.object({
|
|
31166
|
+
filePath: z.string(),
|
|
31167
|
+
machineRef: z.string(),
|
|
31168
|
+
resolvedId: z.string(),
|
|
31169
|
+
source: z.enum(["explicit", "import", "inline", "fallback"])
|
|
31170
|
+
}).strict();
|
|
31171
|
+
var DetectionDiagnosticsSchema = z.object({
|
|
31172
|
+
schemaVersion: z.literal(1),
|
|
31173
|
+
scan: DetectionScanSummarySchema,
|
|
31174
|
+
resolution: z.object({
|
|
31175
|
+
totalMachineUsages: z.number().int().nonnegative(),
|
|
31176
|
+
bySource: z.object({
|
|
31177
|
+
explicit: z.number().int().nonnegative(),
|
|
31178
|
+
import: z.number().int().nonnegative(),
|
|
31179
|
+
inline: z.number().int().nonnegative(),
|
|
31180
|
+
fallback: z.number().int().nonnegative()
|
|
31181
|
+
}).strict(),
|
|
31182
|
+
fallbackRate: z.number().min(0).max(1),
|
|
31183
|
+
fallbackExamples: z.array(MachineResolutionDetailSchema)
|
|
31184
|
+
}).strict(),
|
|
31185
|
+
coverage: z.object({
|
|
31186
|
+
totalManifestMachines: z.number().int().nonnegative(),
|
|
31187
|
+
linkedManifestMachines: z.number().int().nonnegative(),
|
|
31188
|
+
unlinkedManifestMachines: z.number().int().nonnegative(),
|
|
31189
|
+
machineLinkCoverage: z.number().min(0).max(1),
|
|
31190
|
+
unlinkedManifestMachineIds: z.array(z.string())
|
|
31191
|
+
}).strict(),
|
|
31192
|
+
warnings: z.array(
|
|
31193
|
+
z.object({
|
|
31194
|
+
severity: z.enum(["warning", "info", "hint"]),
|
|
31195
|
+
message: z.string()
|
|
31196
|
+
}).strict()
|
|
31197
|
+
)
|
|
31198
|
+
}).strict();
|
|
31199
|
+
var StrictDetectStatusSchema = z.object({
|
|
31200
|
+
enabled: z.literal(true),
|
|
31201
|
+
passed: z.boolean(),
|
|
31202
|
+
reasons: z.array(z.string())
|
|
31203
|
+
}).strict();
|
|
30901
31204
|
var InjectTestAttrsOutputSchema = z.object({
|
|
30902
31205
|
totalFiles: z.number(),
|
|
30903
31206
|
changedFiles: z.number(),
|
|
@@ -30909,17 +31212,98 @@ var InjectTestAttrsOutputSchema = z.object({
|
|
|
30909
31212
|
/** Number of machines in manifest */
|
|
30910
31213
|
manifestMachineCount: z.number().optional(),
|
|
30911
31214
|
/** Machines without meta.e2e annotation (opt-in warning) */
|
|
30912
|
-
machinesWithoutE2EMeta: z.array(MachineWithoutE2EMetaSchema).optional()
|
|
31215
|
+
machinesWithoutE2EMeta: z.array(MachineWithoutE2EMetaSchema).optional(),
|
|
31216
|
+
/** Structured diagnostics for detection quality */
|
|
31217
|
+
detectionDiagnostics: DetectionDiagnosticsSchema.optional(),
|
|
31218
|
+
/** Strict detection mode result */
|
|
31219
|
+
strictDetect: StrictDetectStatusSchema.optional()
|
|
30913
31220
|
}).strict();
|
|
30914
31221
|
|
|
30915
31222
|
// src/commands/inject-test-attrs/processor.ts
|
|
30916
31223
|
init_esm_shims();
|
|
30917
31224
|
|
|
31225
|
+
// src/commands/inject-test-attrs/detection-diagnostics.ts
|
|
31226
|
+
init_esm_shims();
|
|
31227
|
+
var MAX_EXAMPLES = 20;
|
|
31228
|
+
function normalizeMachineId(id) {
|
|
31229
|
+
return id.replace(/-([a-z])/g, (_, char) => char.toUpperCase()).replace(/Machine$/i, "").toLowerCase();
|
|
31230
|
+
}
|
|
31231
|
+
function buildDetectionDiagnostics(params) {
|
|
31232
|
+
const bySource = {
|
|
31233
|
+
explicit: 0,
|
|
31234
|
+
import: 0,
|
|
31235
|
+
inline: 0,
|
|
31236
|
+
fallback: 0
|
|
31237
|
+
};
|
|
31238
|
+
const fallbackExamples = [];
|
|
31239
|
+
for (const detail of params.resolutionDetails) {
|
|
31240
|
+
bySource[detail.source] += 1;
|
|
31241
|
+
if (detail.source === "fallback" && fallbackExamples.length < MAX_EXAMPLES) {
|
|
31242
|
+
fallbackExamples.push(detail);
|
|
31243
|
+
}
|
|
31244
|
+
}
|
|
31245
|
+
const linkedMachineIds = new Set(
|
|
31246
|
+
params.machineLinks.filter((link) => link.usedIn.length > 0).map((link) => normalizeMachineId(link.machineName))
|
|
31247
|
+
);
|
|
31248
|
+
const linkedManifestMachineIds = [];
|
|
31249
|
+
const unlinkedManifestMachineIds = [];
|
|
31250
|
+
for (const machineId of params.manifestMachineIds) {
|
|
31251
|
+
if (linkedMachineIds.has(normalizeMachineId(machineId))) {
|
|
31252
|
+
linkedManifestMachineIds.push(machineId);
|
|
31253
|
+
continue;
|
|
31254
|
+
}
|
|
31255
|
+
unlinkedManifestMachineIds.push(machineId);
|
|
31256
|
+
}
|
|
31257
|
+
const totalMachineUsages = params.resolutionDetails.length;
|
|
31258
|
+
const fallbackRate = totalMachineUsages > 0 ? bySource.fallback / totalMachineUsages : 0;
|
|
31259
|
+
const totalManifestMachines = params.manifestMachineIds.length;
|
|
31260
|
+
const linkedManifestMachines = linkedManifestMachineIds.length;
|
|
31261
|
+
const machineLinkCoverage = totalManifestMachines > 0 ? linkedManifestMachines / totalManifestMachines : 1;
|
|
31262
|
+
return {
|
|
31263
|
+
schemaVersion: 1,
|
|
31264
|
+
scan: params.scanSummary,
|
|
31265
|
+
resolution: {
|
|
31266
|
+
totalMachineUsages,
|
|
31267
|
+
bySource,
|
|
31268
|
+
fallbackRate,
|
|
31269
|
+
fallbackExamples
|
|
31270
|
+
},
|
|
31271
|
+
coverage: {
|
|
31272
|
+
totalManifestMachines,
|
|
31273
|
+
linkedManifestMachines,
|
|
31274
|
+
unlinkedManifestMachines: totalManifestMachines - linkedManifestMachines,
|
|
31275
|
+
machineLinkCoverage,
|
|
31276
|
+
unlinkedManifestMachineIds: unlinkedManifestMachineIds.slice(0, MAX_EXAMPLES)
|
|
31277
|
+
},
|
|
31278
|
+
warnings: params.warnings
|
|
31279
|
+
};
|
|
31280
|
+
}
|
|
31281
|
+
function evaluateStrictDetect(diagnostics) {
|
|
31282
|
+
const reasons = [];
|
|
31283
|
+
const fallbackCount = diagnostics.resolution.bySource.fallback;
|
|
31284
|
+
if (fallbackCount > 0) {
|
|
31285
|
+
reasons.push(`fallback machine resolution detected (${fallbackCount})`);
|
|
31286
|
+
}
|
|
31287
|
+
if (diagnostics.scan.filesWithMachineHooks > 0 && diagnostics.resolution.totalMachineUsages === 0) {
|
|
31288
|
+
reasons.push(
|
|
31289
|
+
`machine hooks were detected in ${diagnostics.scan.filesWithMachineHooks} files, but no machine usages were resolved`
|
|
31290
|
+
);
|
|
31291
|
+
}
|
|
31292
|
+
if (diagnostics.resolution.totalMachineUsages > 0 && diagnostics.coverage.totalManifestMachines > 0 && diagnostics.coverage.linkedManifestMachines === 0) {
|
|
31293
|
+
reasons.push("machine usages were detected, but no machines were linked to routes/pages");
|
|
31294
|
+
}
|
|
31295
|
+
return {
|
|
31296
|
+
enabled: true,
|
|
31297
|
+
passed: reasons.length === 0,
|
|
31298
|
+
reasons
|
|
31299
|
+
};
|
|
31300
|
+
}
|
|
31301
|
+
|
|
30918
31302
|
// src/commands/inject-test-attrs/formatter.ts
|
|
30919
31303
|
init_esm_shims();
|
|
30920
31304
|
async function formatFilesWithBiome(files, cwd) {
|
|
30921
31305
|
if (files.length === 0) return;
|
|
30922
|
-
const relativePaths = files.map((f) =>
|
|
31306
|
+
const relativePaths = files.map((f) => path11.relative(cwd, f));
|
|
30923
31307
|
try {
|
|
30924
31308
|
await execa("pnpm", ["exec", "biome", "format", "--write", ...relativePaths], {
|
|
30925
31309
|
cwd,
|
|
@@ -31011,7 +31395,8 @@ function emptyResult(filePath) {
|
|
|
31011
31395
|
changed: false,
|
|
31012
31396
|
actions: [],
|
|
31013
31397
|
fields: [],
|
|
31014
|
-
testIds: []
|
|
31398
|
+
testIds: [],
|
|
31399
|
+
resolutionDetails: []
|
|
31015
31400
|
};
|
|
31016
31401
|
}
|
|
31017
31402
|
function ensureDirectoryExists(dirPath) {
|
|
@@ -31021,7 +31406,7 @@ function ensureDirectoryExists(dirPath) {
|
|
|
31021
31406
|
}
|
|
31022
31407
|
function getGeneratorVersion() {
|
|
31023
31408
|
try {
|
|
31024
|
-
const pkgPath =
|
|
31409
|
+
const pkgPath = path11.resolve(__dirname$1, "../../../package.json");
|
|
31025
31410
|
if (fs5.existsSync(pkgPath)) {
|
|
31026
31411
|
const pkg = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
|
|
31027
31412
|
return pkg.version ?? "unknown";
|
|
@@ -31036,15 +31421,15 @@ function detectPlatform(repoRoot) {
|
|
|
31036
31421
|
const nextConfigPatterns = ["next.config.ts", "next.config.js", "next.config.mjs"];
|
|
31037
31422
|
for (const pattern of nextConfigPatterns) {
|
|
31038
31423
|
const paths = [
|
|
31039
|
-
|
|
31040
|
-
|
|
31041
|
-
|
|
31424
|
+
path11.join(repoRoot, pattern),
|
|
31425
|
+
path11.join(repoRoot, "apps", "web", pattern),
|
|
31426
|
+
path11.join(repoRoot, "apps", "dashboard", pattern)
|
|
31042
31427
|
];
|
|
31043
31428
|
if (paths.some((p) => fs5.existsSync(p))) {
|
|
31044
31429
|
return "nextjs";
|
|
31045
31430
|
}
|
|
31046
31431
|
}
|
|
31047
|
-
const appJsonPath =
|
|
31432
|
+
const appJsonPath = path11.join(repoRoot, "app.json");
|
|
31048
31433
|
if (fs5.existsSync(appJsonPath)) {
|
|
31049
31434
|
try {
|
|
31050
31435
|
const appJson = JSON.parse(fs5.readFileSync(appJsonPath, "utf-8"));
|
|
@@ -31054,7 +31439,7 @@ function detectPlatform(repoRoot) {
|
|
|
31054
31439
|
} catch {
|
|
31055
31440
|
}
|
|
31056
31441
|
}
|
|
31057
|
-
if (fs5.existsSync(
|
|
31442
|
+
if (fs5.existsSync(path11.join(repoRoot, "expo.json"))) {
|
|
31058
31443
|
return "expo";
|
|
31059
31444
|
}
|
|
31060
31445
|
const electronPatterns = [
|
|
@@ -31063,56 +31448,38 @@ function detectPlatform(repoRoot) {
|
|
|
31063
31448
|
"electron.vite.config.ts",
|
|
31064
31449
|
"electron.vite.config.js"
|
|
31065
31450
|
];
|
|
31066
|
-
if (electronPatterns.some((p) => fs5.existsSync(
|
|
31451
|
+
if (electronPatterns.some((p) => fs5.existsSync(path11.join(repoRoot, p)))) {
|
|
31067
31452
|
return "electron";
|
|
31068
31453
|
}
|
|
31069
31454
|
return "unknown";
|
|
31070
31455
|
}
|
|
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
|
-
}
|
|
31456
|
+
function hasHonoRouteFiles(dir) {
|
|
31457
|
+
try {
|
|
31458
|
+
const files = fs5.readdirSync(dir);
|
|
31459
|
+
return files.some(
|
|
31460
|
+
(file) => file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".spec.ts")
|
|
31461
|
+
);
|
|
31462
|
+
} catch {
|
|
31463
|
+
return false;
|
|
31093
31464
|
}
|
|
31094
|
-
|
|
31095
|
-
|
|
31096
|
-
|
|
31097
|
-
|
|
31098
|
-
|
|
31099
|
-
|
|
31100
|
-
|
|
31101
|
-
|
|
31102
|
-
|
|
31103
|
-
|
|
31104
|
-
|
|
31105
|
-
);
|
|
31106
|
-
if (hasRouteFiles) {
|
|
31107
|
-
possibleDirs.push(`packages/${pkg.name}/routes`);
|
|
31108
|
-
}
|
|
31109
|
-
}
|
|
31110
|
-
}
|
|
31111
|
-
}
|
|
31112
|
-
} catch {
|
|
31113
|
-
}
|
|
31465
|
+
}
|
|
31466
|
+
function collectRouteDirsInScope(repoRoot, scope) {
|
|
31467
|
+
const scopeDir = path11.join(repoRoot, scope);
|
|
31468
|
+
if (!fs5.existsSync(scopeDir)) {
|
|
31469
|
+
return [];
|
|
31470
|
+
}
|
|
31471
|
+
try {
|
|
31472
|
+
const entries = fs5.readdirSync(scopeDir, { withFileTypes: true });
|
|
31473
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((entryName) => hasHonoRouteFiles(path11.join(scopeDir, entryName, "routes"))).map((entryName) => `${scope}/${entryName}/routes`);
|
|
31474
|
+
} catch {
|
|
31475
|
+
return [];
|
|
31114
31476
|
}
|
|
31115
|
-
|
|
31477
|
+
}
|
|
31478
|
+
function findPossibleHonoDirs(repoRoot) {
|
|
31479
|
+
return [
|
|
31480
|
+
...collectRouteDirsInScope(repoRoot, "apps"),
|
|
31481
|
+
...collectRouteDirsInScope(repoRoot, "packages")
|
|
31482
|
+
];
|
|
31116
31483
|
}
|
|
31117
31484
|
function collectManifestWarnings(repoRoot, platform, apiContracts, machinesWithoutE2EMeta, totalMachines) {
|
|
31118
31485
|
const warnings = [];
|
|
@@ -31163,15 +31530,18 @@ function printManifestWarnings(warnings, verbose) {
|
|
|
31163
31530
|
for (const detail of warning.details) {
|
|
31164
31531
|
console.log(detail);
|
|
31165
31532
|
}
|
|
31166
|
-
if (warning.details.length < warning._totalCount) {
|
|
31167
|
-
console.log(` ... and more`);
|
|
31168
|
-
}
|
|
31169
31533
|
}
|
|
31170
31534
|
if (warning.fix) {
|
|
31171
31535
|
console.log(` \u2192 ${warning.fix}`);
|
|
31172
31536
|
}
|
|
31173
31537
|
}
|
|
31174
31538
|
}
|
|
31539
|
+
function toDetectionWarnings(warnings) {
|
|
31540
|
+
return warnings.map((warning) => ({
|
|
31541
|
+
severity: warning.severity,
|
|
31542
|
+
message: warning.message
|
|
31543
|
+
}));
|
|
31544
|
+
}
|
|
31175
31545
|
function normalizeToCanonicalId(id) {
|
|
31176
31546
|
return id.replace(/-([a-z])/g, (_, c) => c.toUpperCase()).replace(/Machine$/i, "");
|
|
31177
31547
|
}
|
|
@@ -31259,7 +31629,7 @@ async function analyzeHonoRoutes(repoRoot, verbose, resolveSchemas = false) {
|
|
|
31259
31629
|
return { apiContracts: [], schemasResolved: false };
|
|
31260
31630
|
}
|
|
31261
31631
|
if (verbose) {
|
|
31262
|
-
console.log(` Analyzing Hono routes in: ${
|
|
31632
|
+
console.log(` Analyzing Hono routes in: ${path11.relative(repoRoot, routesPath)}`);
|
|
31263
31633
|
if (resolveSchemas) {
|
|
31264
31634
|
console.log(" Schema resolution enabled (--resolve-schemas)");
|
|
31265
31635
|
}
|
|
@@ -31305,7 +31675,7 @@ async function analyzeHonoRoutes(repoRoot, verbose, resolveSchemas = false) {
|
|
|
31305
31675
|
return { apiContracts: [], schemasResolved: false };
|
|
31306
31676
|
}
|
|
31307
31677
|
}
|
|
31308
|
-
async function generateManifestFiles(manifestDir, repoRoot, verbose, resolveSchemas = false) {
|
|
31678
|
+
async function generateManifestFiles(manifestDir, repoRoot, verbose, resolveSchemas = false, diagnosticsInput) {
|
|
31309
31679
|
const registry = getInjectionRegistry();
|
|
31310
31680
|
const generatorVersion = getGeneratorVersion();
|
|
31311
31681
|
const e2eManifest = buildManifest(registry, generatorVersion);
|
|
@@ -31319,18 +31689,18 @@ async function generateManifestFiles(manifestDir, repoRoot, verbose, resolveSche
|
|
|
31319
31689
|
}
|
|
31320
31690
|
const definitionMap = buildDefinitionMap(machineDefinitions);
|
|
31321
31691
|
const enhancedMachines = buildEnhancedMachines(e2eManifest, definitionMap, machineDefinitions);
|
|
31322
|
-
const absoluteManifestDir =
|
|
31692
|
+
const absoluteManifestDir = path11.isAbsolute(manifestDir) ? manifestDir : path11.join(repoRoot, manifestDir);
|
|
31323
31693
|
if (!isPathContained(repoRoot, absoluteManifestDir)) {
|
|
31324
31694
|
throw new Error(
|
|
31325
31695
|
`Security error: Manifest directory '${manifestDir}' would escape the project root. The --manifest-dir must be a relative path within the project directory.`
|
|
31326
31696
|
);
|
|
31327
31697
|
}
|
|
31328
31698
|
ensureDirectoryExists(absoluteManifestDir);
|
|
31329
|
-
const generatedDir =
|
|
31699
|
+
const generatedDir = path11.join(absoluteManifestDir, "generated");
|
|
31330
31700
|
ensureDirectoryExists(generatedDir);
|
|
31331
|
-
const manifestsDir =
|
|
31701
|
+
const manifestsDir = path11.join(absoluteManifestDir, "manifests");
|
|
31332
31702
|
ensureDirectoryExists(manifestsDir);
|
|
31333
|
-
const tsPath =
|
|
31703
|
+
const tsPath = path11.join(generatedDir, "selectors.ts");
|
|
31334
31704
|
await fs5.promises.writeFile(tsPath, generateSelectorTypeScript(e2eManifest), "utf-8");
|
|
31335
31705
|
const unifiedRegistry = getUnifiedRegistry();
|
|
31336
31706
|
const machineLinks = buildMachineLinks();
|
|
@@ -31345,6 +31715,29 @@ async function generateManifestFiles(manifestDir, repoRoot, verbose, resolveSche
|
|
|
31345
31715
|
const jstOffset = 9 * 60 * 60 * 1e3;
|
|
31346
31716
|
const jst = new Date(now.getTime() + jstOffset);
|
|
31347
31717
|
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`;
|
|
31718
|
+
const machinesWithoutE2EMeta = machineDefinitions.filter(
|
|
31719
|
+
(def) => !def.hasE2EMeta && typeof def.id === "string"
|
|
31720
|
+
).map((def) => ({ id: def.id, sourceFile: def.sourceFile }));
|
|
31721
|
+
const warnings = collectManifestWarnings(
|
|
31722
|
+
repoRoot,
|
|
31723
|
+
platform,
|
|
31724
|
+
apiContracts,
|
|
31725
|
+
machinesWithoutE2EMeta,
|
|
31726
|
+
machineDefinitions.length
|
|
31727
|
+
);
|
|
31728
|
+
printManifestWarnings(warnings, verbose);
|
|
31729
|
+
const detectionDiagnostics = buildDetectionDiagnostics({
|
|
31730
|
+
scanSummary: diagnosticsInput?.scanSummary ?? {
|
|
31731
|
+
totalFiles: 0,
|
|
31732
|
+
filesWithMachineDefinitions: 0,
|
|
31733
|
+
filesWithMachineHooks: 0,
|
|
31734
|
+
filesWithJsxOnly: 0
|
|
31735
|
+
},
|
|
31736
|
+
resolutionDetails: diagnosticsInput?.resolutionDetails ?? [],
|
|
31737
|
+
manifestMachineIds: Object.keys(enhancedMachines),
|
|
31738
|
+
machineLinks,
|
|
31739
|
+
warnings: toDetectionWarnings(warnings)
|
|
31740
|
+
});
|
|
31348
31741
|
const unifiedManifest = {
|
|
31349
31742
|
version: manifestVersion,
|
|
31350
31743
|
generatedAt: generatedAtJST,
|
|
@@ -31357,17 +31750,15 @@ async function generateManifestFiles(manifestDir, repoRoot, verbose, resolveSche
|
|
|
31357
31750
|
authBoundaries: unifiedRegistry.authBoundaries,
|
|
31358
31751
|
machineDefinitions: unifiedRegistry.machineDefinitions,
|
|
31359
31752
|
machineLinks,
|
|
31360
|
-
apiContracts
|
|
31753
|
+
apiContracts,
|
|
31754
|
+
detectionDiagnostics
|
|
31361
31755
|
};
|
|
31362
|
-
const manifestPath =
|
|
31756
|
+
const manifestPath = path11.join(manifestsDir, "manifest.json");
|
|
31363
31757
|
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
31758
|
if (verbose) {
|
|
31368
|
-
console.log(`Generated selectors: ${
|
|
31759
|
+
console.log(`Generated selectors: ${path11.relative(repoRoot, tsPath)}`);
|
|
31369
31760
|
console.log(
|
|
31370
|
-
`Generated manifest: ${
|
|
31761
|
+
`Generated manifest: ${path11.relative(repoRoot, manifestPath)} (v${manifestVersion})`
|
|
31371
31762
|
);
|
|
31372
31763
|
console.log(` Platform: ${platform}`);
|
|
31373
31764
|
console.log(` Machine definitions: ${machineDefinitions.length}`);
|
|
@@ -31381,19 +31772,15 @@ async function generateManifestFiles(manifestDir, repoRoot, verbose, resolveSche
|
|
|
31381
31772
|
` API contracts: ${apiContracts.length}${schemasResolved ? " (with schemas)" : ""}`
|
|
31382
31773
|
);
|
|
31383
31774
|
console.log(` Machine links: ${machineLinks.length}`);
|
|
31775
|
+
console.log(
|
|
31776
|
+
` Detection: fallback=${detectionDiagnostics.resolution.bySource.fallback}, linkCoverage=${(detectionDiagnostics.coverage.machineLinkCoverage * 100).toFixed(1)}%`
|
|
31777
|
+
);
|
|
31384
31778
|
}
|
|
31385
|
-
const warnings = collectManifestWarnings(
|
|
31386
|
-
repoRoot,
|
|
31387
|
-
platform,
|
|
31388
|
-
apiContracts,
|
|
31389
|
-
machinesWithoutE2EMeta,
|
|
31390
|
-
machineDefinitions.length
|
|
31391
|
-
);
|
|
31392
|
-
printManifestWarnings(warnings, verbose);
|
|
31393
31779
|
return {
|
|
31394
|
-
manifestPath:
|
|
31780
|
+
manifestPath: path11.relative(repoRoot, tsPath),
|
|
31395
31781
|
machineCount: Object.keys(enhancedMachines).length,
|
|
31396
|
-
machinesWithoutE2EMeta
|
|
31782
|
+
machinesWithoutE2EMeta,
|
|
31783
|
+
detectionDiagnostics
|
|
31397
31784
|
};
|
|
31398
31785
|
}
|
|
31399
31786
|
|
|
@@ -31425,7 +31812,7 @@ function registerExistingInjections(machineIds, attrs, sourceFile) {
|
|
|
31425
31812
|
}
|
|
31426
31813
|
async function preprocessFile(filePath, repoRoot, options) {
|
|
31427
31814
|
let code = await fs5.promises.readFile(filePath, "utf-8");
|
|
31428
|
-
const relativePath =
|
|
31815
|
+
const relativePath = path11.relative(repoRoot, filePath);
|
|
31429
31816
|
collectRouteInfo(relativePath, code, options.verbose);
|
|
31430
31817
|
const hasMarker = code.includes(CLI_INJECTION_MARKER);
|
|
31431
31818
|
if (hasMarker && options.force) {
|
|
@@ -31456,14 +31843,15 @@ async function preprocessFile(filePath, repoRoot, options) {
|
|
|
31456
31843
|
injectedCount: 0,
|
|
31457
31844
|
machineIds,
|
|
31458
31845
|
changed: false,
|
|
31459
|
-
...attrs
|
|
31846
|
+
...attrs,
|
|
31847
|
+
resolutionDetails: []
|
|
31460
31848
|
}
|
|
31461
31849
|
};
|
|
31462
31850
|
}
|
|
31463
31851
|
return { code, hasMarker: false, skipped: false };
|
|
31464
31852
|
}
|
|
31465
31853
|
function transformAndRegister(filePath, code, repoRoot, options) {
|
|
31466
|
-
const relativePath =
|
|
31854
|
+
const relativePath = path11.relative(repoRoot, filePath);
|
|
31467
31855
|
const readAndParseFile = createReadAndParseFile(options.verbose);
|
|
31468
31856
|
const resolveImportPath = createResolveImportPath();
|
|
31469
31857
|
try {
|
|
@@ -31479,6 +31867,14 @@ function transformAndRegister(filePath, code, repoRoot, options) {
|
|
|
31479
31867
|
const transformedCode = markerComment + result.code;
|
|
31480
31868
|
const { actions, fields, testIds } = extractInjectedAttributes(transformedCode);
|
|
31481
31869
|
const machineIds = [...result.resolvedIds.values()];
|
|
31870
|
+
const resultWithResolution = result;
|
|
31871
|
+
const rawResolutionDetails = Array.isArray(resultWithResolution.resolutionDetails) ? resultWithResolution.resolutionDetails : [];
|
|
31872
|
+
const resolutionDetails = rawResolutionDetails.map((detail) => ({
|
|
31873
|
+
filePath: relativePath,
|
|
31874
|
+
machineRef: detail.machineRef,
|
|
31875
|
+
resolvedId: detail.resolvedId,
|
|
31876
|
+
source: detail.source
|
|
31877
|
+
}));
|
|
31482
31878
|
registerExistingInjections(machineIds, { actions, fields, testIds }, relativePath);
|
|
31483
31879
|
return {
|
|
31484
31880
|
filePath,
|
|
@@ -31488,7 +31884,8 @@ function transformAndRegister(filePath, code, repoRoot, options) {
|
|
|
31488
31884
|
transformedCode,
|
|
31489
31885
|
actions,
|
|
31490
31886
|
fields,
|
|
31491
|
-
testIds
|
|
31887
|
+
testIds,
|
|
31888
|
+
resolutionDetails
|
|
31492
31889
|
};
|
|
31493
31890
|
} catch (error) {
|
|
31494
31891
|
if (options.verbose) {
|
|
@@ -31509,7 +31906,7 @@ async function processFileForChildInjection(filePath, repoRoot, options) {
|
|
|
31509
31906
|
if (prep.skipped && prep.skipResult) return prep.skipResult;
|
|
31510
31907
|
const result = transformAndRegister(filePath, prep.code, repoRoot, options);
|
|
31511
31908
|
if (options.verbose && result.changed && result.injectedCount > 0) {
|
|
31512
|
-
const relativePath =
|
|
31909
|
+
const relativePath = path11.relative(repoRoot, filePath);
|
|
31513
31910
|
console.log(
|
|
31514
31911
|
` [child] Injected ${result.injectedCount} attrs in ${relativePath} (actions: ${result.actions.join(", ")})`
|
|
31515
31912
|
);
|
|
@@ -31529,7 +31926,7 @@ async function handleChangedFile(result, check, repoRoot, verbose, state2) {
|
|
|
31529
31926
|
await fs5.promises.writeFile(result.filePath, result.transformedCode, "utf-8");
|
|
31530
31927
|
state2.filesToFormat.push(result.filePath);
|
|
31531
31928
|
if (verbose) {
|
|
31532
|
-
const relativePath =
|
|
31929
|
+
const relativePath = path11.relative(repoRoot, result.filePath);
|
|
31533
31930
|
console.log(` Injected ${result.injectedCount} attrs in ${relativePath}`);
|
|
31534
31931
|
}
|
|
31535
31932
|
}
|
|
@@ -31540,6 +31937,60 @@ async function formatChangedFiles(filesToFormat, repoRoot, verbose) {
|
|
|
31540
31937
|
}
|
|
31541
31938
|
await formatFilesWithBiome(filesToFormat, repoRoot);
|
|
31542
31939
|
}
|
|
31940
|
+
function createScanSummary(params) {
|
|
31941
|
+
return {
|
|
31942
|
+
totalFiles: params.files.length,
|
|
31943
|
+
filesWithMachineDefinitions: params.withMachineDefinitions.length,
|
|
31944
|
+
filesWithMachineHooks: params.withMachineHooks.length,
|
|
31945
|
+
filesWithJsxOnly: params.withJsxOnly.length
|
|
31946
|
+
};
|
|
31947
|
+
}
|
|
31948
|
+
async function resolveManifestArtifacts(params) {
|
|
31949
|
+
let manifestPath;
|
|
31950
|
+
let manifestMachineCount;
|
|
31951
|
+
let machinesWithoutE2EMeta;
|
|
31952
|
+
let detectionDiagnostics;
|
|
31953
|
+
if (params.manifest && !params.check) {
|
|
31954
|
+
const diagnosticsInput = {
|
|
31955
|
+
scanSummary: params.scanSummary,
|
|
31956
|
+
resolutionDetails: params.resolutionDetails
|
|
31957
|
+
};
|
|
31958
|
+
const manifestResult = await generateManifestFiles(
|
|
31959
|
+
params.manifestDir,
|
|
31960
|
+
params.repoRoot,
|
|
31961
|
+
params.verbose,
|
|
31962
|
+
params.resolveSchemas,
|
|
31963
|
+
diagnosticsInput
|
|
31964
|
+
);
|
|
31965
|
+
manifestPath = manifestResult.manifestPath;
|
|
31966
|
+
manifestMachineCount = manifestResult.machineCount;
|
|
31967
|
+
machinesWithoutE2EMeta = manifestResult.machinesWithoutE2EMeta;
|
|
31968
|
+
detectionDiagnostics = manifestResult.detectionDiagnostics;
|
|
31969
|
+
} else if (params.manifest && params.check) {
|
|
31970
|
+
const warnings = [];
|
|
31971
|
+
detectionDiagnostics = buildDetectionDiagnostics({
|
|
31972
|
+
scanSummary: params.scanSummary,
|
|
31973
|
+
resolutionDetails: params.resolutionDetails,
|
|
31974
|
+
manifestMachineIds: [],
|
|
31975
|
+
machineLinks: [],
|
|
31976
|
+
warnings
|
|
31977
|
+
});
|
|
31978
|
+
}
|
|
31979
|
+
const strictDetectStatus = params.strictDetect ? detectionDiagnostics ? evaluateStrictDetect(detectionDiagnostics) : {
|
|
31980
|
+
enabled: true,
|
|
31981
|
+
passed: false,
|
|
31982
|
+
reasons: [
|
|
31983
|
+
"strict detect requires manifest diagnostics, but diagnostics were unavailable"
|
|
31984
|
+
]
|
|
31985
|
+
} : void 0;
|
|
31986
|
+
return {
|
|
31987
|
+
manifestPath,
|
|
31988
|
+
manifestMachineCount,
|
|
31989
|
+
machinesWithoutE2EMeta,
|
|
31990
|
+
detectionDiagnostics,
|
|
31991
|
+
strictDetectStatus
|
|
31992
|
+
};
|
|
31993
|
+
}
|
|
31543
31994
|
function mightContainMachineDefinition(code) {
|
|
31544
31995
|
return code.includes("createMachine");
|
|
31545
31996
|
}
|
|
@@ -31563,13 +32014,34 @@ async function categorizeFiles(files) {
|
|
|
31563
32014
|
return { withMachineDefinitions, withMachineHooks, withJsxOnly };
|
|
31564
32015
|
}
|
|
31565
32016
|
async function processFiles(options) {
|
|
31566
|
-
const {
|
|
32017
|
+
const {
|
|
32018
|
+
repoRoot,
|
|
32019
|
+
check,
|
|
32020
|
+
verbose,
|
|
32021
|
+
include,
|
|
32022
|
+
exclude,
|
|
32023
|
+
manifest,
|
|
32024
|
+
manifestDir,
|
|
32025
|
+
resolveSchemas,
|
|
32026
|
+
strictDetect
|
|
32027
|
+
} = options;
|
|
31567
32028
|
clearInjectionRegistry();
|
|
31568
32029
|
clearUnifiedRegistry();
|
|
31569
32030
|
const files = await discoverFiles(repoRoot, include, exclude);
|
|
31570
32031
|
if (verbose) console.log(`Found ${files.length} files to process`);
|
|
31571
|
-
const state2 = {
|
|
32032
|
+
const state2 = {
|
|
32033
|
+
changes: [],
|
|
32034
|
+
filesToFormat: [],
|
|
32035
|
+
totalInjections: 0,
|
|
32036
|
+
resolutionDetails: []
|
|
32037
|
+
};
|
|
31572
32038
|
const { withMachineDefinitions, withMachineHooks, withJsxOnly } = await categorizeFiles(files);
|
|
32039
|
+
const scanSummary = createScanSummary({
|
|
32040
|
+
files,
|
|
32041
|
+
withMachineDefinitions,
|
|
32042
|
+
withMachineHooks,
|
|
32043
|
+
withJsxOnly
|
|
32044
|
+
});
|
|
31573
32045
|
if (verbose) {
|
|
31574
32046
|
console.log(` Files with machine definitions: ${withMachineDefinitions.length}`);
|
|
31575
32047
|
console.log(` Files with machine hooks: ${withMachineHooks.length}`);
|
|
@@ -31579,46 +32051,50 @@ async function processFiles(options) {
|
|
|
31579
32051
|
for (const filePath of withMachineDefinitions) {
|
|
31580
32052
|
try {
|
|
31581
32053
|
const code = await fs5.promises.readFile(filePath, "utf-8");
|
|
31582
|
-
const relativePath =
|
|
32054
|
+
const relativePath = path11.relative(repoRoot, filePath);
|
|
31583
32055
|
collectRouteInfo(relativePath, code, verbose);
|
|
31584
32056
|
} catch {
|
|
31585
32057
|
}
|
|
31586
32058
|
}
|
|
31587
32059
|
for (const filePath of withMachineHooks) {
|
|
31588
32060
|
const result = await processFile(filePath, repoRoot, processOpts);
|
|
32061
|
+
state2.resolutionDetails.push(...result.resolutionDetails);
|
|
31589
32062
|
await handleChangedFile(result, check, repoRoot, verbose, state2);
|
|
31590
32063
|
}
|
|
31591
32064
|
for (const filePath of withJsxOnly) {
|
|
31592
32065
|
const result = await processFileForChildInjection(filePath, repoRoot, processOpts);
|
|
32066
|
+
state2.resolutionDetails.push(...result.resolutionDetails);
|
|
31593
32067
|
await handleChangedFile(result, check, repoRoot, verbose, state2);
|
|
31594
32068
|
}
|
|
31595
32069
|
if (!check) {
|
|
31596
32070
|
await formatChangedFiles(state2.filesToFormat, repoRoot, verbose);
|
|
31597
32071
|
}
|
|
31598
32072
|
postProcessRegistries();
|
|
31599
|
-
|
|
31600
|
-
|
|
31601
|
-
|
|
31602
|
-
|
|
31603
|
-
|
|
31604
|
-
|
|
31605
|
-
|
|
31606
|
-
|
|
31607
|
-
|
|
31608
|
-
|
|
31609
|
-
|
|
31610
|
-
|
|
31611
|
-
|
|
31612
|
-
|
|
32073
|
+
const manifestArtifacts = await resolveManifestArtifacts({
|
|
32074
|
+
manifest,
|
|
32075
|
+
check,
|
|
32076
|
+
manifestDir,
|
|
32077
|
+
repoRoot,
|
|
32078
|
+
verbose,
|
|
32079
|
+
resolveSchemas: resolveSchemas ?? false,
|
|
32080
|
+
strictDetect: strictDetect ?? false,
|
|
32081
|
+
scanSummary,
|
|
32082
|
+
resolutionDetails: state2.resolutionDetails
|
|
32083
|
+
});
|
|
32084
|
+
const hasCheckFailure = check && state2.changes.length > 0;
|
|
32085
|
+
const hasStrictDetectFailure = manifestArtifacts.strictDetectStatus?.passed === false;
|
|
32086
|
+
const exitCode = hasCheckFailure || hasStrictDetectFailure ? 1 : 0;
|
|
31613
32087
|
return {
|
|
31614
32088
|
totalFiles: files.length,
|
|
31615
32089
|
changedFiles: state2.changes.length,
|
|
31616
32090
|
totalInjections: state2.totalInjections,
|
|
31617
32091
|
changes: state2.changes,
|
|
31618
|
-
exitCode
|
|
31619
|
-
manifestPath,
|
|
31620
|
-
manifestMachineCount,
|
|
31621
|
-
machinesWithoutE2EMeta
|
|
32092
|
+
exitCode,
|
|
32093
|
+
manifestPath: manifestArtifacts.manifestPath,
|
|
32094
|
+
manifestMachineCount: manifestArtifacts.manifestMachineCount,
|
|
32095
|
+
machinesWithoutE2EMeta: manifestArtifacts.machinesWithoutE2EMeta,
|
|
32096
|
+
detectionDiagnostics: manifestArtifacts.detectionDiagnostics,
|
|
32097
|
+
strictDetect: manifestArtifacts.strictDetectStatus
|
|
31622
32098
|
};
|
|
31623
32099
|
}
|
|
31624
32100
|
|
|
@@ -31678,6 +32154,35 @@ function logMachinesWithoutE2EMeta(result, logger16) {
|
|
|
31678
32154
|
logger16.info(" }");
|
|
31679
32155
|
logger16.info(" See: .claude/rules/xstate-design.md#meta.e2e");
|
|
31680
32156
|
}
|
|
32157
|
+
function logDetectionDiagnostics(result, logger16) {
|
|
32158
|
+
if (!result.detectionDiagnostics) return;
|
|
32159
|
+
const diagnostics = result.detectionDiagnostics;
|
|
32160
|
+
const fallbackCount = diagnostics.resolution.bySource.fallback;
|
|
32161
|
+
const fallbackRate = (diagnostics.resolution.fallbackRate * 100).toFixed(1);
|
|
32162
|
+
const coverage = (diagnostics.coverage.machineLinkCoverage * 100).toFixed(1);
|
|
32163
|
+
logger16.info("");
|
|
32164
|
+
logger16.info(
|
|
32165
|
+
`Detection diagnostics: usages=${diagnostics.resolution.totalMachineUsages}, fallback=${fallbackCount} (${fallbackRate}%), linkCoverage=${coverage}%`
|
|
32166
|
+
);
|
|
32167
|
+
if (fallbackCount > 0) {
|
|
32168
|
+
for (const item of diagnostics.resolution.fallbackExamples.slice(0, 5)) {
|
|
32169
|
+
logger16.warn(
|
|
32170
|
+
` fallback: ${item.filePath} :: ${item.machineRef} -> ${item.resolvedId} (${item.source})`
|
|
32171
|
+
);
|
|
32172
|
+
}
|
|
32173
|
+
}
|
|
32174
|
+
}
|
|
32175
|
+
function logStrictDetect(result, logger16) {
|
|
32176
|
+
if (!result.strictDetect) return;
|
|
32177
|
+
if (result.strictDetect.passed) {
|
|
32178
|
+
logger16.success("Strict detect passed (no fallback resolution)");
|
|
32179
|
+
return;
|
|
32180
|
+
}
|
|
32181
|
+
logger16.error("Strict detect failed");
|
|
32182
|
+
for (const reason of result.strictDetect.reasons) {
|
|
32183
|
+
logger16.error(` - ${reason}`);
|
|
32184
|
+
}
|
|
32185
|
+
}
|
|
31681
32186
|
async function injectTestAttrsAction(rawOptions, command) {
|
|
31682
32187
|
const logger16 = createCLILogger("manifest");
|
|
31683
32188
|
const parseResult = InjectTestAttrsInputSchema.safeParse(rawOptions);
|
|
@@ -31688,6 +32193,20 @@ async function injectTestAttrsAction(rawOptions, command) {
|
|
|
31688
32193
|
]);
|
|
31689
32194
|
}
|
|
31690
32195
|
const options = parseResult.data;
|
|
32196
|
+
if (options.strictDetect && options.check) {
|
|
32197
|
+
throw new CLIError(
|
|
32198
|
+
"--strict-detect cannot be used with --check",
|
|
32199
|
+
"STRICT_DETECT_CHECK_UNSUPPORTED",
|
|
32200
|
+
["Run without --check to evaluate strict detection against manifest diagnostics"]
|
|
32201
|
+
);
|
|
32202
|
+
}
|
|
32203
|
+
if (options.strictDetect && !options.manifest) {
|
|
32204
|
+
throw new CLIError(
|
|
32205
|
+
"--strict-detect requires manifest generation",
|
|
32206
|
+
"STRICT_DETECT_MANIFEST_REQUIRED",
|
|
32207
|
+
["Remove --no-manifest, or disable --strict-detect"]
|
|
32208
|
+
);
|
|
32209
|
+
}
|
|
31691
32210
|
const repoRoot = findProjectRoot$1() ?? process.cwd();
|
|
31692
32211
|
if (!options.check) {
|
|
31693
32212
|
logger16.section("Generating manifest (manifest.json)");
|
|
@@ -31706,6 +32225,8 @@ async function injectTestAttrsAction(rawOptions, command) {
|
|
|
31706
32225
|
logResultSummary(result, options, logger16);
|
|
31707
32226
|
logManifestInfo(result, logger16);
|
|
31708
32227
|
logMachinesWithoutE2EMeta(result, logger16);
|
|
32228
|
+
logDetectionDiagnostics(result, logger16);
|
|
32229
|
+
logStrictDetect(result, logger16);
|
|
31709
32230
|
emitJsonSuccess(command, InjectTestAttrsOutputSchema, result);
|
|
31710
32231
|
if (result.exitCode !== 0) {
|
|
31711
32232
|
process.exit(result.exitCode);
|
|
@@ -31725,7 +32246,7 @@ var injectTestAttrsCommand = new Command("inject-test-attrs").description("Injec
|
|
|
31725
32246
|
"**/*.test.*",
|
|
31726
32247
|
"**/*.spec.*",
|
|
31727
32248
|
"**/*.stories.*"
|
|
31728
|
-
]).option("--no-manifest", "Skip E2E selector manifest generation").option("--manifest-dir <dir>", "Output directory for manifest files", ".runa").action(async (rawOptions) => {
|
|
32249
|
+
]).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
32250
|
await injectTestAttrsAction(rawOptions, injectTestAttrsCommand);
|
|
31730
32251
|
});
|
|
31731
32252
|
|
|
@@ -31789,7 +32310,7 @@ var manifestCommand = new Command("manifest").description("Generate E2E manifest
|
|
|
31789
32310
|
"**/*.test.*",
|
|
31790
32311
|
"**/*.spec.*",
|
|
31791
32312
|
"**/*.stories.*"
|
|
31792
|
-
]).option("--no-manifest", "Skip manifest file generation (attributes only)").option("--manifest-dir <dir>", "Output directory for manifest files", ".runa").action(async (options) => {
|
|
32313
|
+
]).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
32314
|
await injectTestAttrsAction(options, manifestCommand);
|
|
31794
32315
|
});
|
|
31795
32316
|
|
|
@@ -31848,7 +32369,7 @@ function assertNodeAuthToken(options) {
|
|
|
31848
32369
|
}
|
|
31849
32370
|
}
|
|
31850
32371
|
function getPackageVersion(packagePath) {
|
|
31851
|
-
const packageJsonPath =
|
|
32372
|
+
const packageJsonPath = path11__default.join(packagePath, "package.json");
|
|
31852
32373
|
if (!existsSync(packageJsonPath)) {
|
|
31853
32374
|
throw new CLIError(`package.json not found at ${packagePath}`, "PACKAGE_NOT_FOUND");
|
|
31854
32375
|
}
|
|
@@ -31867,7 +32388,7 @@ function resolveWorkspaceDependencies(workspaceRoot, packages, logger16) {
|
|
|
31867
32388
|
const sdkPackage = packages.find((p) => p.name === "SDK");
|
|
31868
32389
|
const xstatePluginPackage = packages.find((p) => p.name === "xstate-test-plugin");
|
|
31869
32390
|
if (!sdkPackage) return;
|
|
31870
|
-
const cliPackagePath =
|
|
32391
|
+
const cliPackagePath = path11__default.join(workspaceRoot, "packages", "cli", "package.json");
|
|
31871
32392
|
if (!existsSync(cliPackagePath)) return;
|
|
31872
32393
|
let content = readFileSync(cliPackagePath, "utf-8");
|
|
31873
32394
|
let updated = false;
|
|
@@ -31896,7 +32417,7 @@ function resolveWorkspaceDependencies(workspaceRoot, packages, logger16) {
|
|
|
31896
32417
|
}
|
|
31897
32418
|
}
|
|
31898
32419
|
function restoreWorkspaceDependencies(workspaceRoot) {
|
|
31899
|
-
const cliPackagePath =
|
|
32420
|
+
const cliPackagePath = path11__default.join(workspaceRoot, "packages", "cli", "package.json");
|
|
31900
32421
|
if (!existsSync(cliPackagePath)) return;
|
|
31901
32422
|
let content = readFileSync(cliPackagePath, "utf-8");
|
|
31902
32423
|
content = content.replace(/"@runa-ai\/runa": "\^[\d.]+"/, '"@runa-ai/runa": "workspace:*"');
|
|
@@ -32008,7 +32529,7 @@ function collectPackageInfo(workspaceRoot, logger16, stepNum) {
|
|
|
32008
32529
|
logger16.step("Collecting package info", stepNum);
|
|
32009
32530
|
const packages = [];
|
|
32010
32531
|
for (const pkgConfig of PUBLISHABLE_PACKAGES) {
|
|
32011
|
-
const pkgPath =
|
|
32532
|
+
const pkgPath = path11__default.join(workspaceRoot, "packages", pkgConfig.dir);
|
|
32012
32533
|
const version = getPackageVersion(pkgPath);
|
|
32013
32534
|
const exists = checkVersionExists(pkgConfig.publishName, version);
|
|
32014
32535
|
packages.push({
|
|
@@ -33397,8 +33918,8 @@ var compareActor = fromPromise(async ({ input: input3 }) => {
|
|
|
33397
33918
|
const result = compareBothFiles(
|
|
33398
33919
|
runaRelPath,
|
|
33399
33920
|
templateRelPath,
|
|
33400
|
-
|
|
33401
|
-
|
|
33921
|
+
path11.join(repoRoot, runaRelPath),
|
|
33922
|
+
path11.join(templateDir, templateRelPath),
|
|
33402
33923
|
runaFile.category,
|
|
33403
33924
|
options.diff ?? false
|
|
33404
33925
|
);
|
|
@@ -33849,7 +34370,7 @@ var discoverActor = fromPromise(async ({ input: input3 }) => {
|
|
|
33849
34370
|
const runaPattern = rule.runa;
|
|
33850
34371
|
const runaFiles = await globFiles(repoRoot, runaPattern);
|
|
33851
34372
|
for (const file of runaFiles) {
|
|
33852
|
-
const relativePath =
|
|
34373
|
+
const relativePath = path11.relative(repoRoot, file.absolutePath);
|
|
33853
34374
|
const key = generateComparisonKey(relativePath, false);
|
|
33854
34375
|
inventory.runaFiles.set(relativePath, {
|
|
33855
34376
|
...file,
|
|
@@ -33863,7 +34384,7 @@ var discoverActor = fromPromise(async ({ input: input3 }) => {
|
|
|
33863
34384
|
const templatePattern = rule.template;
|
|
33864
34385
|
const templateFiles = await globFiles(templateDir, templatePattern);
|
|
33865
34386
|
for (const file of templateFiles) {
|
|
33866
|
-
const relativePath =
|
|
34387
|
+
const relativePath = path11.relative(templateDir, file.absolutePath);
|
|
33867
34388
|
const key = generateComparisonKey(relativePath, true);
|
|
33868
34389
|
inventory.templateFiles.set(relativePath, {
|
|
33869
34390
|
...file,
|
|
@@ -33912,7 +34433,7 @@ async function matchDoubleWildcard(ctx, entries) {
|
|
|
33912
34433
|
await walkAndMatch({ ...ctx, partIndex: ctx.partIndex + 1 });
|
|
33913
34434
|
for (const entry of entries) {
|
|
33914
34435
|
if (entry.isDirectory()) {
|
|
33915
|
-
const subDir =
|
|
34436
|
+
const subDir = path11.join(ctx.currentDir, entry.name);
|
|
33916
34437
|
await walkAndMatch({ ...ctx, currentDir: subDir });
|
|
33917
34438
|
}
|
|
33918
34439
|
}
|
|
@@ -33921,7 +34442,7 @@ async function matchSingleWildcard(ctx, entries, pattern, isLastPart) {
|
|
|
33921
34442
|
const regex = patternToRegex(pattern);
|
|
33922
34443
|
for (const entry of entries) {
|
|
33923
34444
|
if (regex.test(entry.name)) {
|
|
33924
|
-
const entryPath =
|
|
34445
|
+
const entryPath = path11.join(ctx.currentDir, entry.name);
|
|
33925
34446
|
await processEntry(ctx, entryPath, entry.isFile(), entry.isDirectory(), isLastPart);
|
|
33926
34447
|
}
|
|
33927
34448
|
}
|
|
@@ -33931,14 +34452,14 @@ async function matchBraceExpansion(ctx, entries, pattern, isLastPart) {
|
|
|
33931
34452
|
for (const option of options) {
|
|
33932
34453
|
for (const entry of entries) {
|
|
33933
34454
|
if (entry.name === option) {
|
|
33934
|
-
const entryPath =
|
|
34455
|
+
const entryPath = path11.join(ctx.currentDir, entry.name);
|
|
33935
34456
|
await processEntry(ctx, entryPath, entry.isFile(), entry.isDirectory(), isLastPart);
|
|
33936
34457
|
}
|
|
33937
34458
|
}
|
|
33938
34459
|
}
|
|
33939
34460
|
}
|
|
33940
34461
|
async function matchLiteral(ctx, pattern, isLastPart) {
|
|
33941
|
-
const entryPath =
|
|
34462
|
+
const entryPath = path11.join(ctx.currentDir, pattern);
|
|
33942
34463
|
if (!fs5.existsSync(entryPath)) return;
|
|
33943
34464
|
const stats = fs5.statSync(entryPath);
|
|
33944
34465
|
await processEntry(ctx, entryPath, stats.isFile(), stats.isDirectory(), isLastPart);
|
|
@@ -34463,15 +34984,15 @@ function printActionsNeeded(logger16, actions) {
|
|
|
34463
34984
|
);
|
|
34464
34985
|
}
|
|
34465
34986
|
function findRepoRoot3(startDir) {
|
|
34466
|
-
const { existsSync:
|
|
34987
|
+
const { existsSync: existsSync52, readFileSync: readFileSync29 } = __require("fs");
|
|
34467
34988
|
const { join: join23, dirname: dirname5 } = __require("path");
|
|
34468
34989
|
let current = startDir;
|
|
34469
34990
|
while (current !== dirname5(current)) {
|
|
34470
|
-
if (
|
|
34991
|
+
if (existsSync52(join23(current, "turbo.json"))) {
|
|
34471
34992
|
return current;
|
|
34472
34993
|
}
|
|
34473
34994
|
const pkgPath = join23(current, "package.json");
|
|
34474
|
-
if (
|
|
34995
|
+
if (existsSync52(pkgPath)) {
|
|
34475
34996
|
try {
|
|
34476
34997
|
const pkg = JSON.parse(readFileSync29(pkgPath, "utf-8"));
|
|
34477
34998
|
if (pkg.workspaces) {
|
|
@@ -34539,10 +35060,10 @@ function generateReportOutput(output3, isJsonMode) {
|
|
|
34539
35060
|
};
|
|
34540
35061
|
}
|
|
34541
35062
|
function validateRunaRepo(repoRoot) {
|
|
34542
|
-
const { existsSync:
|
|
35063
|
+
const { existsSync: existsSync52 } = __require("fs");
|
|
34543
35064
|
const { join: join23 } = __require("path");
|
|
34544
35065
|
const templateDir = join23(repoRoot, "packages/runa-templates/templates");
|
|
34545
|
-
if (!
|
|
35066
|
+
if (!existsSync52(templateDir)) {
|
|
34546
35067
|
throw new CLIError("template-check is a runa-repo only command", "NOT_RUNA_REPO", [
|
|
34547
35068
|
"This command compares runa-repo with pj-repo templates",
|
|
34548
35069
|
"It should only be run in the runa repository",
|
|
@@ -34644,11 +35165,29 @@ init_esm_shims();
|
|
|
34644
35165
|
|
|
34645
35166
|
// src/commands/test/commands/test.ts
|
|
34646
35167
|
init_esm_shims();
|
|
35168
|
+
function resolveEffectiveRequireManifest(options) {
|
|
35169
|
+
if (options.requireManifest && options.allowManifestFallback) {
|
|
35170
|
+
throw new CLIError(
|
|
35171
|
+
"--require-manifest and --allow-manifest-fallback are mutually exclusive",
|
|
35172
|
+
"TEST_MANIFEST_FLAGS_EXCLUSIVE",
|
|
35173
|
+
["Use strict mode (default) OR explicitly allow fallback, but not both"]
|
|
35174
|
+
);
|
|
35175
|
+
}
|
|
35176
|
+
if (options.allowManifestFallback) return false;
|
|
35177
|
+
return options.requireManifest;
|
|
35178
|
+
}
|
|
34647
35179
|
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
35180
|
"--report-json <path>",
|
|
34649
35181
|
"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").
|
|
35182
|
+
).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(
|
|
35183
|
+
"--require-manifest",
|
|
35184
|
+
"Require manifest to be present and valid for Layer 3/4 tests (strict behavior)"
|
|
35185
|
+
).option(
|
|
35186
|
+
"--allow-manifest-fallback",
|
|
35187
|
+
"Allow fallback behavior when manifest is missing/invalid (disables Layer 4 strict default)"
|
|
35188
|
+
).action(async (options) => {
|
|
34651
35189
|
try {
|
|
35190
|
+
const effectiveRequireManifest = resolveEffectiveRequireManifest(options);
|
|
34652
35191
|
const output3 = await runTest({
|
|
34653
35192
|
layer: options.layer !== void 0 ? Number.parseInt(options.layer, 10) : void 0,
|
|
34654
35193
|
from: options.from !== void 0 ? Number.parseInt(options.from, 10) : void 0,
|
|
@@ -34663,7 +35202,8 @@ var testCommand = new Command("test").description("Run tests (compat). Prefer: t
|
|
|
34663
35202
|
baseUrl: options.baseUrl,
|
|
34664
35203
|
changed: options.changed,
|
|
34665
35204
|
base: options.base,
|
|
34666
|
-
skipMissing: options.skipMissing
|
|
35205
|
+
skipMissing: options.skipMissing,
|
|
35206
|
+
requireManifest: effectiveRequireManifest
|
|
34667
35207
|
});
|
|
34668
35208
|
emitJsonSuccess(testCommand, TestRunOutputSchema, output3);
|
|
34669
35209
|
} catch (error) {
|
|
@@ -34761,11 +35301,29 @@ var testApiCommand = new Command("test:api").description("Run Layer 3: API tests
|
|
|
34761
35301
|
|
|
34762
35302
|
// src/commands/test/commands/test-e2e.ts
|
|
34763
35303
|
init_esm_shims();
|
|
35304
|
+
function resolveEffectiveRequireManifest2(options) {
|
|
35305
|
+
if (options.requireManifest && options.allowManifestFallback) {
|
|
35306
|
+
throw new CLIError(
|
|
35307
|
+
"--require-manifest and --allow-manifest-fallback are mutually exclusive",
|
|
35308
|
+
"TEST_E2E_MANIFEST_FLAGS_EXCLUSIVE",
|
|
35309
|
+
["Use strict mode (default) OR explicitly allow fallback, but not both"]
|
|
35310
|
+
);
|
|
35311
|
+
}
|
|
35312
|
+
if (options.allowManifestFallback) return false;
|
|
35313
|
+
return options.requireManifest;
|
|
35314
|
+
}
|
|
34764
35315
|
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(
|
|
35316
|
+
"--require-manifest",
|
|
35317
|
+
"Require manifest to be present and valid (strict behavior; enabled by default for Layer 4)"
|
|
35318
|
+
).option(
|
|
35319
|
+
"--allow-manifest-fallback",
|
|
35320
|
+
"Allow fallback behavior when manifest is missing/invalid (disables Layer 4 strict default)"
|
|
35321
|
+
).option(
|
|
34765
35322
|
"--report-json <path>",
|
|
34766
35323
|
"Export test report as JSON. If <path> is a bare filename, it will be written under .runa/tmp/."
|
|
34767
35324
|
).option("--verbose", "Verbose output (full error details, environment info)").action(async (options) => {
|
|
34768
35325
|
try {
|
|
35326
|
+
const effectiveRequireManifest = resolveEffectiveRequireManifest2(options);
|
|
34769
35327
|
const output3 = await runTestIntegration({
|
|
34770
35328
|
ci: options.ci ?? false,
|
|
34771
35329
|
record: options.record,
|
|
@@ -34775,6 +35333,7 @@ var testE2ECommand = new Command("test:e2e").description("Run Layer 4: E2E tests
|
|
|
34775
35333
|
advanced: options.advanced,
|
|
34776
35334
|
auto: options.auto,
|
|
34777
35335
|
baseUrl: options.baseUrl,
|
|
35336
|
+
requireManifest: effectiveRequireManifest,
|
|
34778
35337
|
invokedAs: "runa test:e2e"
|
|
34779
35338
|
});
|
|
34780
35339
|
emitJsonSuccess(testE2ECommand, TestIntegrationOutputSchema, output3);
|
|
@@ -34795,8 +35354,15 @@ var testE2ECommand = new Command("test:e2e").description("Run Layer 4: E2E tests
|
|
|
34795
35354
|
);
|
|
34796
35355
|
}
|
|
34797
35356
|
});
|
|
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(
|
|
35357
|
+
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(
|
|
35358
|
+
"--require-manifest",
|
|
35359
|
+
"Require manifest to be present and valid (strict behavior; enabled by default for Layer 4)"
|
|
35360
|
+
).option(
|
|
35361
|
+
"--allow-manifest-fallback",
|
|
35362
|
+
"Allow fallback behavior when manifest is missing/invalid (disables Layer 4 strict default)"
|
|
35363
|
+
).option("--report-json <path>", "Export test report as JSON").option("--verbose", "Verbose output").action(async (options) => {
|
|
34799
35364
|
try {
|
|
35365
|
+
const effectiveRequireManifest = resolveEffectiveRequireManifest2(options);
|
|
34800
35366
|
const output3 = await runTestIntegration({
|
|
34801
35367
|
ci: options.ci ?? false,
|
|
34802
35368
|
record: options.record,
|
|
@@ -34806,6 +35372,7 @@ var testBrowserCommand = new Command("test:browser").description("Run Layer 4: B
|
|
|
34806
35372
|
advanced: options.advanced,
|
|
34807
35373
|
auto: options.auto,
|
|
34808
35374
|
baseUrl: options.baseUrl,
|
|
35375
|
+
requireManifest: effectiveRequireManifest,
|
|
34809
35376
|
invokedAs: "runa test:browser"
|
|
34810
35377
|
});
|
|
34811
35378
|
emitJsonSuccess(testBrowserCommand, TestIntegrationOutputSchema, output3);
|
|
@@ -34878,11 +35445,29 @@ var testLintCommand = new Command("test:lint").description("Run Layer 0: Lint (t
|
|
|
34878
35445
|
|
|
34879
35446
|
// src/commands/test/commands/test-integration.ts
|
|
34880
35447
|
init_esm_shims();
|
|
35448
|
+
function resolveEffectiveRequireManifest3(options) {
|
|
35449
|
+
if (options.requireManifest && options.allowManifestFallback) {
|
|
35450
|
+
throw new CLIError(
|
|
35451
|
+
"--require-manifest and --allow-manifest-fallback are mutually exclusive",
|
|
35452
|
+
"TEST_INTEGRATION_MANIFEST_FLAGS_EXCLUSIVE",
|
|
35453
|
+
["Use strict mode (default) OR explicitly allow fallback, but not both"]
|
|
35454
|
+
);
|
|
35455
|
+
}
|
|
35456
|
+
if (options.allowManifestFallback) return false;
|
|
35457
|
+
return options.requireManifest;
|
|
35458
|
+
}
|
|
34881
35459
|
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(
|
|
35460
|
+
"--require-manifest",
|
|
35461
|
+
"Require manifest to be present and valid (strict behavior; enabled by default for Layer 4)"
|
|
35462
|
+
).option(
|
|
35463
|
+
"--allow-manifest-fallback",
|
|
35464
|
+
"Allow fallback behavior when manifest is missing/invalid (disables Layer 4 strict default)"
|
|
35465
|
+
).option(
|
|
34882
35466
|
"--report-json <path>",
|
|
34883
35467
|
"Export test report as JSON. If <path> is a bare filename, it will be written under .runa/tmp/."
|
|
34884
35468
|
).option("--verbose", "Verbose output (full error details, environment info)").action(async (options) => {
|
|
34885
35469
|
try {
|
|
35470
|
+
const effectiveRequireManifest = resolveEffectiveRequireManifest3(options);
|
|
34886
35471
|
const output3 = await runTestIntegration({
|
|
34887
35472
|
ci: options.ci ?? false,
|
|
34888
35473
|
record: options.record,
|
|
@@ -34892,6 +35477,7 @@ var testIntegrationCommand = new Command("test:integration").description("Run La
|
|
|
34892
35477
|
advanced: options.advanced,
|
|
34893
35478
|
auto: options.auto,
|
|
34894
35479
|
baseUrl: options.baseUrl,
|
|
35480
|
+
requireManifest: effectiveRequireManifest,
|
|
34895
35481
|
invokedAs: "runa test:integration"
|
|
34896
35482
|
});
|
|
34897
35483
|
emitJsonSuccess(testIntegrationCommand, TestIntegrationOutputSchema, output3);
|
|
@@ -34937,7 +35523,7 @@ function injectTestAttrsIfLayer4(layer, options) {
|
|
|
34937
35523
|
"Run `runa inject-test-attrs` manually to see detailed errors",
|
|
34938
35524
|
"Ensure XState machines exist in your project",
|
|
34939
35525
|
"Check that machines have valid meta.e2e definitions",
|
|
34940
|
-
"Or
|
|
35526
|
+
"Or pass --allow-manifest-fallback to continue with fallback behavior"
|
|
34941
35527
|
],
|
|
34942
35528
|
error instanceof Error ? error : void 0
|
|
34943
35529
|
);
|
|
@@ -34947,10 +35533,17 @@ function injectTestAttrsIfLayer4(layer, options) {
|
|
|
34947
35533
|
}
|
|
34948
35534
|
}
|
|
34949
35535
|
}
|
|
35536
|
+
function resolveEffectiveRequireManifest4(layer, options) {
|
|
35537
|
+
if (options.allowManifestFallback) return false;
|
|
35538
|
+
if (options.requireManifest !== void 0) return options.requireManifest;
|
|
35539
|
+
if (layer === 4 && process.env.RUNA_LAYER4_ALLOW_MANIFEST_FALLBACK !== "true") return true;
|
|
35540
|
+
return void 0;
|
|
35541
|
+
}
|
|
34950
35542
|
async function runSingleLayer(params) {
|
|
35543
|
+
const effectiveRequireManifest = resolveEffectiveRequireManifest4(params.layer, params.options);
|
|
34951
35544
|
injectTestAttrsIfLayer4(params.layer, {
|
|
34952
35545
|
verbose: params.options.verbose,
|
|
34953
|
-
requireManifest:
|
|
35546
|
+
requireManifest: effectiveRequireManifest
|
|
34954
35547
|
});
|
|
34955
35548
|
try {
|
|
34956
35549
|
const output3 = await runTest({
|
|
@@ -34967,7 +35560,7 @@ async function runSingleLayer(params) {
|
|
|
34967
35560
|
reviewSnapshots: params.options.reviewSnapshots,
|
|
34968
35561
|
forceRegenerate: params.options.force,
|
|
34969
35562
|
skipGeneration: params.options.skipGeneration,
|
|
34970
|
-
requireManifest:
|
|
35563
|
+
requireManifest: effectiveRequireManifest,
|
|
34971
35564
|
invokedAs: `runa test:layer${params.layer}`
|
|
34972
35565
|
});
|
|
34973
35566
|
emitJsonSuccess(params.cmd, TestRunOutputSchema, output3);
|
|
@@ -35001,6 +35594,13 @@ function validateGenerationFlags(layer, opts) {
|
|
|
35001
35594
|
["Use --auto (implementation-driven) OR --generate/--advanced (XState-driven)"]
|
|
35002
35595
|
);
|
|
35003
35596
|
}
|
|
35597
|
+
if (layer === 4 && opts.requireManifest && opts.allowManifestFallback) {
|
|
35598
|
+
throw new CLIError(
|
|
35599
|
+
"--require-manifest and --allow-manifest-fallback are mutually exclusive",
|
|
35600
|
+
"TEST_LAYER_MANIFEST_FLAGS_EXCLUSIVE",
|
|
35601
|
+
["Use strict mode (default) OR explicitly allow fallback, but not both"]
|
|
35602
|
+
);
|
|
35603
|
+
}
|
|
35004
35604
|
}
|
|
35005
35605
|
function validateSnapshotFlags(opts) {
|
|
35006
35606
|
if (opts.reviewSnapshots && opts.ci) {
|
|
@@ -35039,6 +35639,9 @@ function createLayerCommand(layer) {
|
|
|
35039
35639
|
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
35640
|
"--require-manifest",
|
|
35041
35641
|
"Require manifest to be present and valid (fail if missing or stale)"
|
|
35642
|
+
).option(
|
|
35643
|
+
"--allow-manifest-fallback",
|
|
35644
|
+
"Allow fallback behavior when manifest is missing/invalid (disables Layer 4 strict default)"
|
|
35042
35645
|
);
|
|
35043
35646
|
}
|
|
35044
35647
|
return cmd;
|
|
@@ -36887,8 +37490,8 @@ var logger13 = createCLILogger("watch");
|
|
|
36887
37490
|
function addFallbackSchemaPatterns(patterns) {
|
|
36888
37491
|
const detected = detectDatabasePackage(process.cwd());
|
|
36889
37492
|
if (detected) {
|
|
36890
|
-
patterns.push(
|
|
36891
|
-
patterns.push(
|
|
37493
|
+
patterns.push(path11__default.join(detected, "src/schema/**/*.ts"));
|
|
37494
|
+
patterns.push(path11__default.join(detected, "sql/**/*.sql"));
|
|
36892
37495
|
return;
|
|
36893
37496
|
}
|
|
36894
37497
|
for (const candidate of DATABASE_PACKAGE_CANDIDATES) {
|
|
@@ -36902,8 +37505,8 @@ async function buildSchemaPatterns() {
|
|
|
36902
37505
|
const patterns = ["supabase/schemas/**/*.sql"];
|
|
36903
37506
|
try {
|
|
36904
37507
|
const dbPackagePath = await getDatabasePackagePath();
|
|
36905
|
-
patterns.push(
|
|
36906
|
-
patterns.push(
|
|
37508
|
+
patterns.push(path11__default.join(dbPackagePath, "src/schema/**/*.ts"));
|
|
37509
|
+
patterns.push(path11__default.join(dbPackagePath, "sql/**/*.sql"));
|
|
36907
37510
|
} catch {
|
|
36908
37511
|
addFallbackSchemaPatterns(patterns);
|
|
36909
37512
|
}
|