@rehearsal-db/core 0.1.0-rc.2 → 0.1.0-rc.4
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/CHANGELOG.md +55 -1
- package/README.md +4 -1
- package/dist/package.json +4 -1
- package/dist/scripts/runtime/manage_database.mjs +17 -8
- package/dist/src/baseline/artifact.d.mts +14 -0
- package/dist/src/baseline/artifact.mjs +50 -0
- package/dist/src/cli/application_command_handlers.mjs +9 -4
- package/dist/src/cli/basic_command_handlers.mjs +2 -0
- package/dist/src/cli/inspection_command_handlers.mjs +45 -0
- package/dist/src/cli/project_commands.d.mts +9 -1
- package/dist/src/cli/project_commands.mjs +2 -2
- package/dist/src/cli/rehearsal.mjs +77 -65
- package/dist/src/cli/runtime_action_handlers.mjs +36 -9
- package/dist/src/cli/runtime_commands.d.mts +71 -7
- package/dist/src/cli/runtime_commands.mjs +99 -36
- package/dist/src/cli/source_commands.d.mts +12 -2
- package/dist/src/cli/source_commands.mjs +24 -15
- package/dist/src/index.d.mts +2 -2
- package/dist/src/index.mjs +1 -1
- package/dist/src/project/configuration.d.mts +2 -0
- package/dist/src/project/configuration.mjs +1 -0
- package/dist/src/project/configuration_contract.d.mts +10 -0
- package/dist/src/project/configuration_contract.mjs +4 -0
- package/dist/src/project/configuration_normalization.d.mts +4 -0
- package/dist/src/project/configuration_normalization.mjs +11 -1
- package/dist/src/project/configuration_rendering.mjs +14 -0
- package/dist/src/project/configuration_resolution.d.mts +4 -0
- package/dist/src/project/configuration_schema.d.mts +1 -0
- package/dist/src/project/configuration_schema.mjs +4 -0
- package/dist/src/project/project_discovery.d.mts +15 -0
- package/dist/src/project/project_discovery.mjs +350 -0
- package/dist/src/project/project_discovery_application.d.mts +16 -0
- package/dist/src/project/project_discovery_application.mjs +190 -0
- package/dist/src/project/project_discovery_contract.d.mts +78 -0
- package/dist/src/project/project_discovery_contract.mjs +43 -0
- package/dist/src/project/project_discovery_metadata.d.mts +14 -0
- package/dist/src/project/project_discovery_metadata.mjs +138 -0
- package/dist/src/runtime/lifecycle_engine.d.mts +6 -0
- package/dist/src/runtime/lifecycle_engine.mjs +24 -2
- package/dist/src/runtime/persistence.d.mts +19 -0
- package/dist/src/runtime/persistence.mjs +40 -0
- package/dist/src/runtime/plan.d.mts +18 -0
- package/dist/src/runtime/plan.mjs +27 -1
- package/dist/src/runtime/status.d.mts +32 -0
- package/dist/src/runtime/status.mjs +146 -0
- package/dist/src/shared/cancellation.d.mts +18 -0
- package/dist/src/shared/cancellation.mjs +80 -0
- package/dist/src/shared/diagnostics.d.mts +1 -0
- package/dist/src/shared/diagnostics.mjs +1 -0
- package/dist/src/shared/operation_guard.d.mts +12 -0
- package/dist/src/shared/operation_guard.mjs +91 -2
- package/dist/src/shared/owned_process.d.mts +30 -0
- package/dist/src/shared/owned_process.mjs +117 -0
- package/dist/src/targets/postgresql.d.mts +0 -4
- package/dist/src/targets/postgresql.mjs +40 -12
- package/dist/src/targets/postgresql_process.d.mts +8 -0
- package/dist/src/targets/postgresql_process.mjs +34 -0
- package/dist/src/targets/postgresql_status.d.mts +24 -0
- package/dist/src/targets/postgresql_status.mjs +64 -0
- package/dist/src/targets/supabase.mjs +34 -16
- package/dist/src/targets/supabase_environment.mjs +2 -0
- package/dist/src/targets/supabase_process.d.mts +7 -0
- package/dist/src/targets/supabase_process.mjs +21 -0
- package/dist/src/targets/supabase_status.d.mts +15 -0
- package/dist/src/targets/supabase_status.mjs +66 -0
- package/dist/src/targets/target.d.mts +3 -1
- package/dist/src/targets/target.mjs +3 -1
- package/docs/README.md +1 -0
- package/docs/architecture.md +8 -0
- package/docs/commands.md +188 -12
- package/docs/configuration.md +12 -0
- package/docs/runtime-lifecycle.md +31 -5
- package/docs/typescript.md +11 -0
- package/docs/vscode.md +53 -0
- package/package.json +4 -1
|
@@ -8,6 +8,7 @@ import { buildRehearsalPlan } from "../runtime/plan.mjs";
|
|
|
8
8
|
import { loadRuntimeTopology } from "../runtime/topology.mjs";
|
|
9
9
|
import type { RuntimeTopologyTarget } from "../runtime/topology.mjs";
|
|
10
10
|
import type { InstalledPackageFingerprint } from "../shared/operation_guard.mjs";
|
|
11
|
+
import type { RuntimeStatusSnapshot } from "../runtime/status.mjs";
|
|
11
12
|
import type { RehearsalCliFlags } from "./arguments.mjs";
|
|
12
13
|
import type { RehearsalErrorCategory } from "../shared/diagnostics.mjs";
|
|
13
14
|
import type { CliSessionState } from "./command_contract.mjs";
|
|
@@ -18,6 +19,7 @@ interface ManagerReceipt {
|
|
|
18
19
|
readonly target: string;
|
|
19
20
|
readonly durationMs: number;
|
|
20
21
|
readonly output: string;
|
|
22
|
+
readonly runtimeStatus?: RuntimeStatusSnapshot;
|
|
21
23
|
}
|
|
22
24
|
interface RuntimeStackResult {
|
|
23
25
|
readonly topology: RuntimeTopology;
|
|
@@ -73,6 +75,23 @@ export declare const buildRuntimeStackPlan: (planOptions: RehearsalConfigPathOpt
|
|
|
73
75
|
sha256: string;
|
|
74
76
|
}[];
|
|
75
77
|
};
|
|
78
|
+
lifecycle: {
|
|
79
|
+
run: "keep-until-stop" | "stop-after-run";
|
|
80
|
+
open: "keep-until-stop" | "stop-on-application-exit";
|
|
81
|
+
dataOnStop: string;
|
|
82
|
+
};
|
|
83
|
+
application: {
|
|
84
|
+
startCommand: string;
|
|
85
|
+
proofCommand: string;
|
|
86
|
+
readiness: {
|
|
87
|
+
url: string;
|
|
88
|
+
expectedStatus: number;
|
|
89
|
+
timeoutSeconds: number;
|
|
90
|
+
} | null;
|
|
91
|
+
};
|
|
92
|
+
cleanup: {
|
|
93
|
+
retainBaselineGenerations: number;
|
|
94
|
+
};
|
|
76
95
|
execution: string[];
|
|
77
96
|
guarantee: string;
|
|
78
97
|
paths: {
|
|
@@ -81,6 +100,39 @@ export declare const buildRuntimeStackPlan: (planOptions: RehearsalConfigPathOpt
|
|
|
81
100
|
sanitizationPolicy: string;
|
|
82
101
|
};
|
|
83
102
|
} | {
|
|
103
|
+
config: {
|
|
104
|
+
schemaVersion: 1;
|
|
105
|
+
path: string;
|
|
106
|
+
project: string;
|
|
107
|
+
};
|
|
108
|
+
environment: {
|
|
109
|
+
target: "postgresql" | "supabase";
|
|
110
|
+
kind: string;
|
|
111
|
+
applicationUrl: string;
|
|
112
|
+
projectId: string;
|
|
113
|
+
ports: Readonly<{
|
|
114
|
+
api?: number;
|
|
115
|
+
database: number;
|
|
116
|
+
studio?: number;
|
|
117
|
+
}>;
|
|
118
|
+
hostedAccess: "disabled";
|
|
119
|
+
outboundNetwork: "deny";
|
|
120
|
+
authenticationProviders: readonly string[];
|
|
121
|
+
authenticationCallback: string | null;
|
|
122
|
+
barriers: string[];
|
|
123
|
+
};
|
|
124
|
+
application: {
|
|
125
|
+
startCommand: string;
|
|
126
|
+
proofCommand: string;
|
|
127
|
+
readiness: {
|
|
128
|
+
url: string;
|
|
129
|
+
expectedStatus: number;
|
|
130
|
+
timeoutSeconds: number;
|
|
131
|
+
} | null;
|
|
132
|
+
};
|
|
133
|
+
cleanup: {
|
|
134
|
+
retainBaselineGenerations: number;
|
|
135
|
+
};
|
|
84
136
|
targets: {
|
|
85
137
|
name: string;
|
|
86
138
|
declaration: RuntimeTopologyTarget["declaration"];
|
|
@@ -90,6 +142,11 @@ export declare const buildRuntimeStackPlan: (planOptions: RehearsalConfigPathOpt
|
|
|
90
142
|
candidateCount: number;
|
|
91
143
|
candidateSha256: string;
|
|
92
144
|
};
|
|
145
|
+
lifecycle: {
|
|
146
|
+
run: "keep-until-stop" | "stop-after-run";
|
|
147
|
+
open: "keep-until-stop" | "stop-on-application-exit";
|
|
148
|
+
dataOnStop: string;
|
|
149
|
+
};
|
|
93
150
|
execution: string[];
|
|
94
151
|
guarantee: string;
|
|
95
152
|
}>;
|
|
@@ -209,9 +266,9 @@ export declare const createRuntimeCommands: ({ packageRoot, projectRoot, manager
|
|
|
209
266
|
targets: readonly ManagerReceipt[];
|
|
210
267
|
}>;
|
|
211
268
|
preparations: {
|
|
269
|
+
target: string;
|
|
212
270
|
command: string;
|
|
213
271
|
output: string;
|
|
214
|
-
target: string;
|
|
215
272
|
}[];
|
|
216
273
|
application: {
|
|
217
274
|
command: string;
|
|
@@ -222,11 +279,18 @@ export declare const createRuntimeCommands: ({ packageRoot, projectRoot, manager
|
|
|
222
279
|
stoppedBy: string;
|
|
223
280
|
diagnostics: import("../application/session.mjs").ApplicationDiagnostics;
|
|
224
281
|
};
|
|
282
|
+
lifecycle: import("../runtime/persistence.mjs").RuntimePersistenceOutcome<ManagerReceipt | Readonly<{
|
|
283
|
+
action: RuntimeAction;
|
|
284
|
+
durationMs: number;
|
|
285
|
+
output: string;
|
|
286
|
+
targets: readonly ManagerReceipt[];
|
|
287
|
+
}>>;
|
|
225
288
|
}>;
|
|
226
|
-
startRuntimeApplication: ({ flags, planOptions, session: commandSession, }: {
|
|
289
|
+
startRuntimeApplication: ({ flags, planOptions, session: commandSession, onRuntimeStarted, }: {
|
|
227
290
|
flags: RehearsalCliFlags;
|
|
228
291
|
planOptions: RehearsalConfigPathOptions;
|
|
229
292
|
session: CliSessionState;
|
|
293
|
+
onRuntimeStarted?: () => void;
|
|
230
294
|
}) => Promise<{
|
|
231
295
|
started: ManagerReceipt | Readonly<{
|
|
232
296
|
action: RuntimeAction;
|
|
@@ -241,9 +305,9 @@ export declare const createRuntimeCommands: ({ packageRoot, projectRoot, manager
|
|
|
241
305
|
targets: readonly ManagerReceipt[];
|
|
242
306
|
}>;
|
|
243
307
|
preparations: {
|
|
308
|
+
target: string;
|
|
244
309
|
command: string;
|
|
245
310
|
output: string;
|
|
246
|
-
target: string;
|
|
247
311
|
}[];
|
|
248
312
|
command: string;
|
|
249
313
|
session: import("../application/session.mjs").ApplicationSession;
|
|
@@ -257,11 +321,11 @@ export declare const createRuntimeCommands: ({ packageRoot, projectRoot, manager
|
|
|
257
321
|
prepareDependentTargets: ({ topology, environment, }: {
|
|
258
322
|
topology: RuntimeTopology;
|
|
259
323
|
environment: NodeJS.ProcessEnv;
|
|
260
|
-
}) => {
|
|
324
|
+
}) => Promise<{
|
|
325
|
+
target: string;
|
|
261
326
|
command: string;
|
|
262
327
|
output: string;
|
|
263
|
-
|
|
264
|
-
}[];
|
|
328
|
+
}[]>;
|
|
265
329
|
proveRuntimeStack: ({ topology, planOptions, environment, }: {
|
|
266
330
|
topology: RuntimeTopology;
|
|
267
331
|
planOptions: RehearsalConfigPathOptions;
|
|
@@ -283,7 +347,7 @@ export declare const createRuntimeCommands: ({ packageRoot, projectRoot, manager
|
|
|
283
347
|
configPath?: string;
|
|
284
348
|
confirmation?: string;
|
|
285
349
|
targetName?: string;
|
|
286
|
-
}) => ManagerReceipt
|
|
350
|
+
}) => Promise<ManagerReceipt>;
|
|
287
351
|
runRuntimeStack: ({ command, flags, planOptions, session, }: {
|
|
288
352
|
command: RuntimeAction;
|
|
289
353
|
flags: RehearsalCliFlags;
|
|
@@ -13,7 +13,11 @@ import { buildRehearsalPlan, runRehearsalDoctor } from "../runtime/plan.mjs";
|
|
|
13
13
|
import { inspectRuntimeTopologyCandidates, loadRuntimeTopology, } from "../runtime/topology.mjs";
|
|
14
14
|
import { RehearsalError, normalizeRehearsalError, redactDiagnosticValue, } from "../shared/diagnostics.mjs";
|
|
15
15
|
import { assertInstalledPackageFingerprint } from "../shared/operation_guard.mjs";
|
|
16
|
+
import { getOperationCancellationSignal, throwIfOperationCancelled, } from "../shared/cancellation.mjs";
|
|
17
|
+
import { runOwnedProcess } from "../shared/owned_process.mjs";
|
|
16
18
|
import { buildRuntimeManagerArguments } from "../targets/target.mjs";
|
|
19
|
+
import { parseRuntimeStatusSnapshot, renderRuntimeStatusSnapshot, } from "../runtime/status.mjs";
|
|
20
|
+
import { applyRuntimePersistence } from "../runtime/persistence.mjs";
|
|
17
21
|
import { formatDuration, isHumanTerminal, promptForConfirmation, terminalStyle, useStyledPrompts, } from "./terminal.mjs";
|
|
18
22
|
const errorMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
19
23
|
export const buildRuntimeStackPlan = async (planOptions) => {
|
|
@@ -32,12 +36,22 @@ export const buildRuntimeStackPlan = async (planOptions) => {
|
|
|
32
36
|
});
|
|
33
37
|
}
|
|
34
38
|
const candidates = await inspectRuntimeTopologyCandidates(planOptions);
|
|
39
|
+
const primaryPlan = targets[0].plan;
|
|
35
40
|
return {
|
|
41
|
+
config: primaryPlan.config,
|
|
42
|
+
environment: primaryPlan.environment,
|
|
43
|
+
application: primaryPlan.application,
|
|
44
|
+
cleanup: primaryPlan.cleanup,
|
|
36
45
|
targets,
|
|
37
46
|
migrations: {
|
|
38
47
|
candidateCount: candidates.candidateCount,
|
|
39
48
|
candidateSha256: candidates.candidateSha256,
|
|
40
49
|
},
|
|
50
|
+
lifecycle: {
|
|
51
|
+
run: topology.primary.config.lifecycle.run,
|
|
52
|
+
open: topology.primary.config.lifecycle.open,
|
|
53
|
+
dataOnStop: "preserved",
|
|
54
|
+
},
|
|
41
55
|
execution: [
|
|
42
56
|
"verify every immutable baseline and migration prefix",
|
|
43
57
|
"restore and migrate the primary runtime",
|
|
@@ -47,6 +61,9 @@ export const buildRuntimeStackPlan = async (planOptions) => {
|
|
|
47
61
|
.map((target) => `run ${target.name} preparation: ${target.declaration.prepareCommand}`),
|
|
48
62
|
...topology.dependents.map((target) => `run ${target.name} proof: ${target.config.application.proofCommand}`),
|
|
49
63
|
`run primary proof: ${topology.primary.config.application.proofCommand}`,
|
|
64
|
+
topology.primary.config.lifecycle.run === "stop-after-run"
|
|
65
|
+
? "stop every database target after proofs while preserving its data"
|
|
66
|
+
: "keep every database target available until an explicit stop",
|
|
50
67
|
],
|
|
51
68
|
guarantee: "Every database target is isolated by its own project ID, ports, artifact directory, and local-only safety policy. No production resources will be contacted.",
|
|
52
69
|
};
|
|
@@ -256,15 +273,18 @@ export const topologyEnvironmentFiles = (topology) => ({
|
|
|
256
273
|
target.paths.applicationEnvironment,
|
|
257
274
|
])),
|
|
258
275
|
});
|
|
259
|
-
const runProjectCommand = ({ source, configuredRoot, label, project, environment = {}, }) => {
|
|
276
|
+
const runProjectCommand = async ({ source, configuredRoot, label, project, environment = {}, }) => {
|
|
260
277
|
const [command, ...args] = parseCommand(source);
|
|
261
|
-
const
|
|
278
|
+
const cancellationSignal = getOperationCancellationSignal();
|
|
279
|
+
const result = await runOwnedProcess({
|
|
280
|
+
command,
|
|
281
|
+
args,
|
|
262
282
|
cwd: configuredRoot,
|
|
263
|
-
encoding: "utf8",
|
|
264
283
|
env: projectCommandEnvironment(environment),
|
|
265
|
-
|
|
266
|
-
|
|
284
|
+
stdin: "ignore",
|
|
285
|
+
...(cancellationSignal ? { signal: cancellationSignal } : {}),
|
|
267
286
|
});
|
|
287
|
+
throwIfOperationCancelled();
|
|
268
288
|
if (result.error || result.status !== 0) {
|
|
269
289
|
throw new RehearsalError({
|
|
270
290
|
category: "application_proof_failure",
|
|
@@ -286,7 +306,7 @@ const runProjectCommand = ({ source, configuredRoot, label, project, environment
|
|
|
286
306
|
};
|
|
287
307
|
};
|
|
288
308
|
export const createRuntimeCommands = ({ packageRoot, projectRoot, managerPath, getActivePackageFingerprint, }) => {
|
|
289
|
-
const runManager = ({ action, flags, configPath = flags.configPath, confirmation = flags.confirmation, targetName, }) => {
|
|
309
|
+
const runManager = async ({ action, flags, configPath = flags.configPath, confirmation = flags.confirmation, targetName, }) => {
|
|
290
310
|
const fingerprint = getActivePackageFingerprint();
|
|
291
311
|
if (fingerprint) {
|
|
292
312
|
assertInstalledPackageFingerprint({
|
|
@@ -299,6 +319,7 @@ export const createRuntimeCommands = ({ packageRoot, projectRoot, managerPath, g
|
|
|
299
319
|
action,
|
|
300
320
|
...(configPath === undefined ? {} : { configPath }),
|
|
301
321
|
...(confirmation === undefined ? {} : { confirmation }),
|
|
322
|
+
structuredResult: action === "status",
|
|
302
323
|
});
|
|
303
324
|
const runtimeStartedMs = performance.now();
|
|
304
325
|
const actionDescription = {
|
|
@@ -321,13 +342,25 @@ export const createRuntimeCommands = ({ packageRoot, projectRoot, managerPath, g
|
|
|
321
342
|
else if (isHumanTerminal(flags)) {
|
|
322
343
|
console.log(`\n${terminalStyle(flags, "1;36", action === "run" ? "REHEARSING" : "REHEARSAL")}\n${terminalStyle(flags, "36", "→")} ${describedAction}. This can take a moment.`);
|
|
323
344
|
}
|
|
324
|
-
const
|
|
345
|
+
const cancellationSignal = getOperationCancellationSignal();
|
|
346
|
+
// VS Code extensions launch the package CLI through Electron's executable in
|
|
347
|
+
// Node mode. Preserve that mode only for Rehearsal's nested Node manager;
|
|
348
|
+
// project-owned application commands continue to receive the reduced
|
|
349
|
+
// project environment above.
|
|
350
|
+
const managerEnvironment = {
|
|
351
|
+
...(cancellationSignal ? { REHEARSAL_CANCELLABLE_OPERATION: "1" } : {}),
|
|
352
|
+
...(process.env.ELECTRON_RUN_AS_NODE === "1"
|
|
353
|
+
? { ELECTRON_RUN_AS_NODE: "1" }
|
|
354
|
+
: {}),
|
|
355
|
+
};
|
|
356
|
+
const result = await runOwnedProcess({
|
|
357
|
+
command: process.execPath,
|
|
358
|
+
args,
|
|
325
359
|
cwd: projectRoot,
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
stdio: ["inherit", "pipe", "pipe"],
|
|
329
|
-
maxBuffer: 16 * 1024 * 1024,
|
|
360
|
+
env: projectCommandEnvironment(managerEnvironment),
|
|
361
|
+
...(cancellationSignal ? { signal: cancellationSignal } : {}),
|
|
330
362
|
});
|
|
363
|
+
throwIfOperationCancelled();
|
|
331
364
|
const currentFingerprint = getActivePackageFingerprint();
|
|
332
365
|
if (currentFingerprint) {
|
|
333
366
|
assertInstalledPackageFingerprint({
|
|
@@ -396,11 +429,17 @@ export const createRuntimeCommands = ({ packageRoot, projectRoot, managerPath, g
|
|
|
396
429
|
else if (isHumanTerminal(flags)) {
|
|
397
430
|
console.log(`${terminalStyle(flags, "32", "✓")} Local runtime step completed in ${formatDuration(durationMs)}.`);
|
|
398
431
|
}
|
|
432
|
+
const runtimeStatus = action === "status"
|
|
433
|
+
? parseRuntimeStatusSnapshot(JSON.parse(String(result.stdout ?? "")))
|
|
434
|
+
: undefined;
|
|
399
435
|
return {
|
|
400
436
|
action,
|
|
401
437
|
target: targetName ?? "primary",
|
|
402
438
|
durationMs: Math.round(durationMs * 100) / 100,
|
|
403
|
-
output:
|
|
439
|
+
output: runtimeStatus
|
|
440
|
+
? renderRuntimeStatusSnapshot(runtimeStatus)
|
|
441
|
+
: String(redactDiagnosticValue(String(result.stdout ?? "").trim())),
|
|
442
|
+
...(runtimeStatus ? { runtimeStatus } : {}),
|
|
404
443
|
};
|
|
405
444
|
};
|
|
406
445
|
const runRuntimeStack = async ({ command, flags, planOptions, session, }) => {
|
|
@@ -410,16 +449,17 @@ export const createRuntimeCommands = ({ packageRoot, projectRoot, managerPath, g
|
|
|
410
449
|
const targets = reverse
|
|
411
450
|
? [...topology.targets].reverse()
|
|
412
451
|
: topology.targets;
|
|
413
|
-
const results =
|
|
452
|
+
const results = [];
|
|
453
|
+
for (const target of targets) {
|
|
414
454
|
const confirmation = session.targetConfirmations?.get(target.configPath);
|
|
415
|
-
|
|
455
|
+
results.push(await runManager({
|
|
416
456
|
action: command,
|
|
417
457
|
flags,
|
|
418
458
|
configPath: target.configPath,
|
|
419
459
|
...(confirmation === undefined ? {} : { confirmation }),
|
|
420
460
|
...(topology.targets.length > 1 ? { targetName: target.name } : {}),
|
|
421
|
-
});
|
|
422
|
-
}
|
|
461
|
+
}));
|
|
462
|
+
}
|
|
423
463
|
if (results.length === 1)
|
|
424
464
|
return { topology, runtime: results[0] };
|
|
425
465
|
return {
|
|
@@ -449,22 +489,24 @@ export const createRuntimeCommands = ({ packageRoot, projectRoot, managerPath, g
|
|
|
449
489
|
: { environment: options.environment }),
|
|
450
490
|
});
|
|
451
491
|
};
|
|
452
|
-
const prepareDependentTargets = ({ topology, environment, }) =>
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
492
|
+
const prepareDependentTargets = async ({ topology, environment, }) => {
|
|
493
|
+
const preparations = [];
|
|
494
|
+
for (const target of topology.dependents) {
|
|
495
|
+
if (!target.declaration?.prepareCommand)
|
|
496
|
+
continue;
|
|
497
|
+
preparations.push({
|
|
457
498
|
target: target.name,
|
|
458
|
-
...runProjectCommand({
|
|
499
|
+
...(await runProjectCommand({
|
|
459
500
|
source: target.declaration.prepareCommand,
|
|
460
501
|
configuredRoot: topology.projectRoot,
|
|
461
502
|
label: `preparation command for ${target.name}`,
|
|
462
503
|
project: topology.primary.config.project.name,
|
|
463
504
|
environment,
|
|
464
|
-
}),
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
|
|
505
|
+
})),
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
return preparations;
|
|
509
|
+
};
|
|
468
510
|
const proveRuntimeStack = async ({ topology, planOptions, environment, }) => {
|
|
469
511
|
const dependentProofs = [];
|
|
470
512
|
for (const target of topology.dependents) {
|
|
@@ -479,7 +521,7 @@ export const createRuntimeCommands = ({ packageRoot, projectRoot, managerPath, g
|
|
|
479
521
|
});
|
|
480
522
|
return { applicationProof, dependentProofs };
|
|
481
523
|
};
|
|
482
|
-
const startRuntimeApplication = async ({ flags, planOptions, session: commandSession, }) => {
|
|
524
|
+
const startRuntimeApplication = async ({ flags, planOptions, session: commandSession, onRuntimeStarted = () => undefined, }) => {
|
|
483
525
|
const topology = await loadRuntimeTopology(planOptions);
|
|
484
526
|
const application = topology.primary.config.application;
|
|
485
527
|
if (!application.readiness) {
|
|
@@ -505,13 +547,14 @@ export const createRuntimeCommands = ({ packageRoot, projectRoot, managerPath, g
|
|
|
505
547
|
planOptions,
|
|
506
548
|
session: commandSession,
|
|
507
549
|
});
|
|
550
|
+
onRuntimeStarted();
|
|
508
551
|
const verified = await runRuntimeStack({
|
|
509
552
|
command: "verify",
|
|
510
553
|
flags,
|
|
511
554
|
planOptions,
|
|
512
555
|
session: commandSession,
|
|
513
556
|
});
|
|
514
|
-
const preparations = prepareDependentTargets({
|
|
557
|
+
const preparations = await prepareDependentTargets({
|
|
515
558
|
topology,
|
|
516
559
|
environment: topologyCommandEnvironment(topology),
|
|
517
560
|
});
|
|
@@ -531,15 +574,34 @@ export const createRuntimeCommands = ({ packageRoot, projectRoot, managerPath, g
|
|
|
531
574
|
};
|
|
532
575
|
};
|
|
533
576
|
const openRuntimeApplication = async ({ flags, planOptions, session: commandSession, onReady = () => undefined, }) => {
|
|
534
|
-
const
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
577
|
+
const topology = await loadRuntimeTopology(planOptions);
|
|
578
|
+
let runtimeStarted = false;
|
|
579
|
+
const { result, lifecycle } = await applyRuntimePersistence({
|
|
580
|
+
mode: topology.primary.config.lifecycle.open,
|
|
581
|
+
canStop: () => runtimeStarted,
|
|
582
|
+
operation: async () => {
|
|
583
|
+
const active = await startRuntimeApplication({
|
|
584
|
+
flags,
|
|
585
|
+
planOptions,
|
|
586
|
+
session: commandSession,
|
|
587
|
+
onRuntimeStarted: () => {
|
|
588
|
+
runtimeStarted = true;
|
|
589
|
+
},
|
|
590
|
+
});
|
|
591
|
+
const closed = await holdApplicationSession({
|
|
592
|
+
session: active.session,
|
|
593
|
+
onReady,
|
|
594
|
+
});
|
|
595
|
+
return { active, closed };
|
|
596
|
+
},
|
|
597
|
+
stop: async () => (await runRuntimeStack({
|
|
598
|
+
command: "stop",
|
|
599
|
+
flags,
|
|
600
|
+
planOptions,
|
|
601
|
+
session: commandSession,
|
|
602
|
+
})).runtime,
|
|
542
603
|
});
|
|
604
|
+
const { active, closed } = result;
|
|
543
605
|
return {
|
|
544
606
|
action: "open",
|
|
545
607
|
started: active.started,
|
|
@@ -551,6 +613,7 @@ export const createRuntimeCommands = ({ packageRoot, projectRoot, managerPath, g
|
|
|
551
613
|
stoppedBy: closed.signal,
|
|
552
614
|
diagnostics: closed.diagnostics,
|
|
553
615
|
},
|
|
616
|
+
lifecycle,
|
|
554
617
|
};
|
|
555
618
|
};
|
|
556
619
|
const prepareCandidateConfirmation = async ({ command, flags, planOptions, session, }) => {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* CLI orchestration for temporary production-source access and safe baseline
|
|
3
3
|
* replacement. Domain validation remains in src/source and src/baseline.
|
|
4
4
|
*/
|
|
5
|
+
import { activateBaselineGeneration } from "../baseline/artifact.mjs";
|
|
5
6
|
import type { RehearsalConfigPathOptions } from "../project/configuration.mjs";
|
|
6
7
|
import type { RehearsalCliFlags } from "./arguments.mjs";
|
|
7
8
|
import type { CliSessionState } from "./command_contract.mjs";
|
|
@@ -53,6 +54,10 @@ export declare const loadPreparationContext: (planOptions: RehearsalConfigPathOp
|
|
|
53
54
|
containerRuntime: Readonly<{
|
|
54
55
|
autoStartColima: boolean;
|
|
55
56
|
}>;
|
|
57
|
+
lifecycle: Readonly<{
|
|
58
|
+
run: "keep-until-stop" | "stop-after-run";
|
|
59
|
+
open: "keep-until-stop" | "stop-on-application-exit";
|
|
60
|
+
}>;
|
|
56
61
|
cleanup: Readonly<{
|
|
57
62
|
retainBaselineGenerations: number;
|
|
58
63
|
}>;
|
|
@@ -201,6 +206,10 @@ export declare const buildRefreshWorkflowPlan: (planOptions: RehearsalConfigPath
|
|
|
201
206
|
containerRuntime: Readonly<{
|
|
202
207
|
autoStartColima: boolean;
|
|
203
208
|
}>;
|
|
209
|
+
lifecycle: Readonly<{
|
|
210
|
+
run: "keep-until-stop" | "stop-after-run";
|
|
211
|
+
open: "keep-until-stop" | "stop-on-application-exit";
|
|
212
|
+
}>;
|
|
204
213
|
cleanup: Readonly<{
|
|
205
214
|
retainBaselineGenerations: number;
|
|
206
215
|
}>;
|
|
@@ -323,7 +332,7 @@ export declare const buildRefreshWorkflowPlan: (planOptions: RehearsalConfigPath
|
|
|
323
332
|
digest: string;
|
|
324
333
|
};
|
|
325
334
|
}>;
|
|
326
|
-
export declare const createRefreshWorkflow: ({ runRuntimeStack, prepareDependentTargets, topologyCommandEnvironment, buildPlan, refreshBaseline, }: {
|
|
335
|
+
export declare const createRefreshWorkflow: ({ runRuntimeStack, prepareDependentTargets, topologyCommandEnvironment, buildPlan, refreshBaseline, activateBaseline, }: {
|
|
327
336
|
runRuntimeStack: (input: {
|
|
328
337
|
command: "reset";
|
|
329
338
|
flags: RehearsalCliFlags;
|
|
@@ -336,10 +345,11 @@ export declare const createRefreshWorkflow: ({ runRuntimeStack, prepareDependent
|
|
|
336
345
|
prepareDependentTargets: (input: {
|
|
337
346
|
topology: RuntimeTopology;
|
|
338
347
|
environment: NodeJS.ProcessEnv;
|
|
339
|
-
}) => unknown[]
|
|
348
|
+
}) => Promise<unknown[]>;
|
|
340
349
|
topologyCommandEnvironment: (topology: RuntimeTopology) => NodeJS.ProcessEnv;
|
|
341
350
|
buildPlan?: (options: RehearsalConfigPathOptions) => Promise<RefreshPlanContext>;
|
|
342
351
|
refreshBaseline?: typeof runSourceBaselineRefresh;
|
|
352
|
+
activateBaseline?: typeof activateBaselineGeneration;
|
|
343
353
|
}) => ({ flags, planOptions, session, }: SourceCommandInput & {
|
|
344
354
|
session: CliSessionState;
|
|
345
355
|
}) => Promise<{
|
|
@@ -14,6 +14,7 @@ import { refreshPostgresqlBaseline } from "../source/baseline.mjs";
|
|
|
14
14
|
import { resolvePrivacyMappedAssets } from "../source/privacy_paths.mjs";
|
|
15
15
|
import { applyPostgresqlSourceAccess, planPostgresqlSourceAccessRetirement, resolvePostgresqlSourceStatePaths, retirePostgresqlSourceAccess, } from "../source/postgresql_access.mjs";
|
|
16
16
|
import { loadRuntimeTopology } from "../runtime/topology.mjs";
|
|
17
|
+
import { isOperationCancelled, throwIfOperationCancelled, withOperationCancellationShield, } from "../shared/cancellation.mjs";
|
|
17
18
|
const parseSourceAccessReceipt = (source) => {
|
|
18
19
|
const parsed = JSON.parse(source);
|
|
19
20
|
if (typeof parsed.targetFingerprint !== "string" ||
|
|
@@ -224,7 +225,7 @@ export const buildRefreshWorkflowPlan = async (planOptions) => {
|
|
|
224
225
|
},
|
|
225
226
|
};
|
|
226
227
|
};
|
|
227
|
-
export const createRefreshWorkflow = ({ runRuntimeStack, prepareDependentTargets, topologyCommandEnvironment, buildPlan = buildRefreshWorkflowPlan, refreshBaseline = runSourceBaselineRefresh, }) => async ({ flags, planOptions, session, }) => {
|
|
228
|
+
export const createRefreshWorkflow = ({ runRuntimeStack, prepareDependentTargets, topologyCommandEnvironment, buildPlan = buildRefreshWorkflowPlan, refreshBaseline = runSourceBaselineRefresh, activateBaseline = activateBaselineGeneration, }) => async ({ flags, planOptions, session, }) => {
|
|
228
229
|
const context = await buildPlan(planOptions);
|
|
229
230
|
if (!flags.refreshConfirmation) {
|
|
230
231
|
return { mode: "preview", plan: context.plan };
|
|
@@ -236,40 +237,48 @@ export const createRefreshWorkflow = ({ runRuntimeStack, prepareDependentTargets
|
|
|
236
237
|
let runtime;
|
|
237
238
|
let preparations = [];
|
|
238
239
|
try {
|
|
240
|
+
throwIfOperationCancelled();
|
|
239
241
|
refreshed = await refreshBaseline({ planOptions });
|
|
242
|
+
throwIfOperationCancelled();
|
|
240
243
|
const stack = await runRuntimeStack({
|
|
241
244
|
command: "reset",
|
|
242
245
|
flags,
|
|
243
246
|
planOptions,
|
|
244
247
|
session,
|
|
245
248
|
});
|
|
249
|
+
throwIfOperationCancelled();
|
|
246
250
|
runtime = stack.runtime;
|
|
247
|
-
preparations = prepareDependentTargets({
|
|
251
|
+
preparations = await prepareDependentTargets({
|
|
248
252
|
topology: stack.topology,
|
|
249
253
|
environment: topologyCommandEnvironment(stack.topology),
|
|
250
254
|
});
|
|
255
|
+
throwIfOperationCancelled();
|
|
251
256
|
}
|
|
252
257
|
catch (error) {
|
|
253
258
|
if (refreshed) {
|
|
254
|
-
await activateBaselineGeneration({
|
|
255
|
-
artifactRoot: context.loaded.paths.artifactDirectory,
|
|
256
|
-
generationId: context.plan.review.currentBaseline,
|
|
257
|
-
});
|
|
258
259
|
try {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
260
|
+
await withOperationCancellationShield(async () => {
|
|
261
|
+
await activateBaseline({
|
|
262
|
+
artifactRoot: context.loaded.paths.artifactDirectory,
|
|
263
|
+
generationId: context.plan.review.currentBaseline,
|
|
264
|
+
});
|
|
265
|
+
const rollback = await runRuntimeStack({
|
|
266
|
+
command: "reset",
|
|
267
|
+
flags,
|
|
268
|
+
planOptions,
|
|
269
|
+
session,
|
|
270
|
+
});
|
|
271
|
+
await prepareDependentTargets({
|
|
272
|
+
topology: rollback.topology,
|
|
273
|
+
environment: topologyCommandEnvironment(rollback.topology),
|
|
274
|
+
});
|
|
268
275
|
});
|
|
269
276
|
}
|
|
270
277
|
catch (rollbackError) {
|
|
271
278
|
throw new AggregateError([error, rollbackError], "The refreshed runtime failed and the previous local runtime could not be restored automatically. The previous immutable baseline is active; run rehearsal reset after correcting the reported problem.");
|
|
272
279
|
}
|
|
280
|
+
if (isOperationCancelled(error))
|
|
281
|
+
throw error;
|
|
273
282
|
throw new Error("The refreshed runtime failed verification. Rehearsal restored the previous baseline and local runtime.", { cause: error });
|
|
274
283
|
}
|
|
275
284
|
throw error;
|
package/dist/src/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/** Public Rehearsal package entry point. */
|
|
2
|
-
export { REHEARSAL_CONFIG_VERSION, defineRehearsalConfig, findRehearsalConfigPath, inspectDetectedProject, loadRehearsalConfig, renderDetectedConfig, renderDetectedPostgresqlConfig, } from "./project/configuration.mjs";
|
|
3
|
-
export type { DetectedProject, NormalizedRehearsalConfig, RehearsalConfig, RehearsalConfigPathOptions, RehearsalConfigVersion, } from "./project/configuration.mjs";
|
|
2
|
+
export { REHEARSAL_CONFIG_VERSION, defineRehearsalConfig, discoverProjectProfile, findRehearsalConfigPath, inspectDetectedProject, PROJECT_DISCOVERY_SCHEMA_VERSION, loadRehearsalConfig, renderDetectedConfig, renderDetectedPostgresqlConfig, } from "./project/configuration.mjs";
|
|
3
|
+
export type { DetectedProject, DecisionDiscoveryItem, DetectedDiscoveryItem, DiscoveredConfigurationField, DiscoveryConfidence, DiscoveryEvidence, DiscoveryValue, NormalizedRehearsalConfig, ProjectDiscoveryResult, RehearsalConfig, RehearsalConfigPathOptions, RehearsalConfigVersion, } from "./project/configuration.mjs";
|
|
4
4
|
export { EXCLUDED_VALUE, SANITIZATION_ACTIONS, applySanitizationAction, normalizeSanitizationAction, readBoundRuntimeSanitizationPolicy, validateSanitizationCoverage, validateRuntimeSanitizationPolicy, } from "./baseline/sanitization_policy.mjs";
|
package/dist/src/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/** Public Rehearsal package entry point. */
|
|
2
|
-
export { REHEARSAL_CONFIG_VERSION, defineRehearsalConfig, findRehearsalConfigPath, inspectDetectedProject, loadRehearsalConfig, renderDetectedConfig, renderDetectedPostgresqlConfig, } from "./project/configuration.mjs";
|
|
2
|
+
export { REHEARSAL_CONFIG_VERSION, defineRehearsalConfig, discoverProjectProfile, findRehearsalConfigPath, inspectDetectedProject, PROJECT_DISCOVERY_SCHEMA_VERSION, loadRehearsalConfig, renderDetectedConfig, renderDetectedPostgresqlConfig, } from "./project/configuration.mjs";
|
|
3
3
|
export { EXCLUDED_VALUE, SANITIZATION_ACTIONS, applySanitizationAction, normalizeSanitizationAction, readBoundRuntimeSanitizationPolicy, validateSanitizationCoverage, validateRuntimeSanitizationPolicy, } from "./baseline/sanitization_policy.mjs";
|
|
@@ -10,4 +10,6 @@ export { loadRehearsalConfig } from "./configuration_resolution.mjs";
|
|
|
10
10
|
export type { NormalizedRehearsalConfig } from "./configuration_resolution.mjs";
|
|
11
11
|
export { inspectDetectedProject } from "./project_detection.mjs";
|
|
12
12
|
export type { DetectedProject } from "./project_detection.mjs";
|
|
13
|
+
export { discoverProjectProfile, probeContainerEngineWithoutStarting, PROJECT_DISCOVERY_SCHEMA_VERSION, } from "./project_discovery.mjs";
|
|
14
|
+
export type { ContainerEngineProbe, DecisionDiscoveryItem, DetectedDiscoveryItem, DiscoveredConfigurationField, DiscoveryConfidence, DiscoveryEvidence, DiscoveryValue, ProjectDiscoveryResult, SuggestedDiscoveryItem, } from "./project_discovery.mjs";
|
|
13
15
|
export { renderDetectedConfig, renderDetectedPostgresqlConfig, } from "./configuration_rendering.mjs";
|
|
@@ -6,4 +6,5 @@ export { defineRehearsalConfig, REHEARSAL_CONFIG_VERSION, REHEARSAL_DEFAULTS, }
|
|
|
6
6
|
export { findRehearsalConfigPath } from "./configuration_discovery.mjs";
|
|
7
7
|
export { loadRehearsalConfig } from "./configuration_resolution.mjs";
|
|
8
8
|
export { inspectDetectedProject } from "./project_detection.mjs";
|
|
9
|
+
export { discoverProjectProfile, probeContainerEngineWithoutStarting, PROJECT_DISCOVERY_SCHEMA_VERSION, } from "./project_discovery.mjs";
|
|
9
10
|
export { renderDetectedConfig, renderDetectedPostgresqlConfig, } from "./configuration_rendering.mjs";
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/** Public configuration types, format version, and stable defaults. */
|
|
2
2
|
export type RehearsalConfigVersion = 1;
|
|
3
|
+
export type RunPersistenceMode = "keep-until-stop" | "stop-after-run";
|
|
4
|
+
export type OpenPersistenceMode = "keep-until-stop" | "stop-on-application-exit";
|
|
3
5
|
export interface RehearsalConfig {
|
|
4
6
|
schemaVersion: RehearsalConfigVersion;
|
|
5
7
|
project: {
|
|
@@ -38,6 +40,10 @@ export interface RehearsalConfig {
|
|
|
38
40
|
containerRuntime?: {
|
|
39
41
|
autoStartColima?: boolean;
|
|
40
42
|
};
|
|
43
|
+
lifecycle?: {
|
|
44
|
+
run?: RunPersistenceMode;
|
|
45
|
+
open?: OpenPersistenceMode;
|
|
46
|
+
};
|
|
41
47
|
cleanup?: {
|
|
42
48
|
retainBaselineGenerations?: number;
|
|
43
49
|
};
|
|
@@ -108,6 +114,10 @@ export declare const REHEARSAL_DEFAULTS: Readonly<{
|
|
|
108
114
|
containerRuntime: Readonly<{
|
|
109
115
|
autoStartColima: false;
|
|
110
116
|
}>;
|
|
117
|
+
lifecycle: Readonly<{
|
|
118
|
+
run: RunPersistenceMode;
|
|
119
|
+
open: OpenPersistenceMode;
|
|
120
|
+
}>;
|
|
111
121
|
cleanup: Readonly<{
|
|
112
122
|
retainBaselineGenerations: 2;
|
|
113
123
|
}>;
|
|
@@ -3,6 +3,10 @@ export const REHEARSAL_CONFIG_VERSION = 1;
|
|
|
3
3
|
export const REHEARSAL_DEFAULTS = Object.freeze({
|
|
4
4
|
baseline: Object.freeze({ artifactDirectory: ".rehearsal" }),
|
|
5
5
|
containerRuntime: Object.freeze({ autoStartColima: false }),
|
|
6
|
+
lifecycle: Object.freeze({
|
|
7
|
+
run: "keep-until-stop",
|
|
8
|
+
open: "keep-until-stop",
|
|
9
|
+
}),
|
|
6
10
|
cleanup: Object.freeze({ retainBaselineGenerations: 2 }),
|
|
7
11
|
runtime: Object.freeze({
|
|
8
12
|
applicationUrl: "http://localhost:5175",
|
|
@@ -41,6 +41,10 @@ export declare const normalizeRehearsalConfig: (input: unknown) => Readonly<{
|
|
|
41
41
|
containerRuntime: Readonly<{
|
|
42
42
|
autoStartColima: boolean;
|
|
43
43
|
}>;
|
|
44
|
+
lifecycle: Readonly<{
|
|
45
|
+
run: "keep-until-stop" | "stop-after-run";
|
|
46
|
+
open: "keep-until-stop" | "stop-on-application-exit";
|
|
47
|
+
}>;
|
|
44
48
|
cleanup: Readonly<{
|
|
45
49
|
retainBaselineGenerations: number;
|
|
46
50
|
}>;
|