@rehearsal-db/core 0.1.0-beta.15 → 0.1.0-beta.16
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 +34 -1
- package/dist/package.json +10 -9
- package/dist/src/baseline/privacy_contract.d.mts +147 -0
- package/dist/src/baseline/privacy_contract.mjs +2 -0
- package/dist/src/baseline/privacy_dependency_graph.d.mts +7 -0
- package/dist/src/baseline/privacy_dependency_graph.mjs +110 -0
- package/dist/src/baseline/privacy_engine.d.mts +5 -162
- package/dist/src/baseline/privacy_engine.mjs +4 -1162
- package/dist/src/baseline/privacy_key.d.mts +7 -0
- package/dist/src/baseline/privacy_key.mjs +42 -0
- package/dist/src/baseline/privacy_pipeline.d.mts +7 -0
- package/dist/src/baseline/privacy_pipeline.mjs +127 -0
- package/dist/src/baseline/privacy_policy.d.mts +6 -0
- package/dist/src/baseline/privacy_policy.mjs +237 -0
- package/dist/src/baseline/privacy_recipe_validation.d.mts +5 -0
- package/dist/src/baseline/privacy_recipe_validation.mjs +289 -0
- package/dist/src/baseline/privacy_rule_registry.d.mts +18 -0
- package/dist/src/baseline/privacy_rule_registry.mjs +143 -0
- package/dist/src/baseline/privacy_transforms.d.mts +18 -0
- package/dist/src/baseline/privacy_transforms.mjs +142 -0
- package/dist/src/baseline/privacy_validation_shared.d.mts +31 -0
- package/dist/src/baseline/privacy_validation_shared.mjs +127 -0
- package/dist/src/cli/application_command_handlers.d.mts +8 -0
- package/dist/src/cli/application_command_handlers.mjs +52 -0
- package/dist/src/cli/arguments.d.mts +0 -5
- package/dist/src/cli/arguments.mjs +0 -7
- package/dist/src/cli/basic_command_handlers.d.mts +4 -0
- package/dist/src/cli/basic_command_handlers.mjs +56 -0
- package/dist/src/cli/cli_commands.d.mts +14 -0
- package/dist/src/cli/cli_commands.mjs +32 -0
- package/dist/src/cli/command_contract.d.mts +57 -0
- package/dist/src/cli/command_contract.mjs +39 -0
- package/dist/src/cli/command_output.d.mts +12 -0
- package/dist/src/cli/command_output.mjs +20 -0
- package/dist/src/cli/command_registry.d.mts +7 -0
- package/dist/src/cli/command_registry.mjs +14 -0
- package/dist/src/cli/guided.d.mts +3 -1
- package/dist/src/cli/guided.mjs +13 -7
- package/dist/src/cli/identity_command_handlers.d.mts +8 -0
- package/dist/src/cli/identity_command_handlers.mjs +43 -0
- package/dist/src/cli/init_command.d.mts +1 -1
- package/dist/src/cli/inspection_command_handlers.d.mts +3 -0
- package/dist/src/cli/inspection_command_handlers.mjs +79 -0
- package/dist/src/cli/project_command_handlers.d.mts +11 -0
- package/dist/src/cli/project_command_handlers.mjs +92 -0
- package/dist/src/cli/project_commands.d.mts +6 -4
- package/dist/src/cli/project_commands.mjs +6 -6
- package/dist/src/cli/rehearsal.d.mts +2 -2
- package/dist/src/cli/rehearsal.mjs +51 -567
- package/dist/src/cli/runtime_action_handlers.d.mts +12 -0
- package/dist/src/cli/runtime_action_handlers.mjs +201 -0
- package/dist/src/cli/runtime_commands.d.mts +7 -3
- package/dist/src/cli/runtime_commands.mjs +10 -8
- package/dist/src/cli/source_command_handlers.d.mts +8 -0
- package/dist/src/cli/source_command_handlers.mjs +62 -0
- package/dist/src/cli/source_commands.d.mts +5 -1
- package/dist/src/cli/source_commands.mjs +3 -1
- package/dist/src/identity/claim.d.mts +5 -144
- package/dist/src/identity/claim.mjs +4 -947
- package/dist/src/identity/claim_contract.d.mts +135 -0
- package/dist/src/identity/claim_contract.mjs +6 -0
- package/dist/src/identity/claim_executor.d.mts +10 -0
- package/dist/src/identity/claim_executor.mjs +358 -0
- package/dist/src/identity/claim_identity_lookup.d.mts +17 -0
- package/dist/src/identity/claim_identity_lookup.mjs +125 -0
- package/dist/src/identity/claim_plan.d.mts +8 -0
- package/dist/src/identity/claim_plan.mjs +61 -0
- package/dist/src/identity/claim_policy.d.mts +3 -0
- package/dist/src/identity/claim_policy.mjs +377 -0
- package/dist/src/identity/claim_sql.d.mts +10 -0
- package/dist/src/identity/claim_sql.mjs +46 -0
- package/dist/src/identity/claim_verification.d.mts +13 -0
- package/dist/src/identity/claim_verification.mjs +35 -0
- package/dist/src/index.d.mts +1 -1
- package/dist/src/project/configuration.d.mts +11 -179
- package/dist/src/project/configuration.mjs +7 -956
- package/dist/src/project/configuration_contract.d.mts +125 -0
- package/dist/src/project/configuration_contract.mjs +27 -0
- package/dist/src/project/configuration_discovery.d.mts +7 -0
- package/dist/src/project/configuration_discovery.mjs +37 -0
- package/dist/src/project/configuration_normalization.d.mts +107 -0
- package/dist/src/project/configuration_normalization.mjs +302 -0
- package/dist/src/project/configuration_rendering.d.mts +14 -0
- package/dist/src/project/configuration_rendering.mjs +224 -0
- package/dist/src/project/configuration_resolution.d.mts +125 -0
- package/dist/src/project/configuration_resolution.mjs +81 -0
- package/dist/src/project/configuration_schema.d.mts +22 -0
- package/dist/src/project/configuration_schema.mjs +160 -0
- package/dist/src/project/configuration_validation.d.mts +22 -0
- package/dist/src/project/configuration_validation.mjs +136 -0
- package/dist/src/project/project_detection.d.mts +14 -0
- package/dist/src/project/project_detection.mjs +47 -0
- package/dist/src/runtime/lifecycle_engine.d.mts +63 -0
- package/dist/src/runtime/lifecycle_engine.mjs +114 -0
- package/dist/src/runtime/plan.mjs +2 -1
- package/dist/src/targets/postgresql.mjs +142 -163
- package/dist/src/targets/supabase.mjs +142 -174
- package/docs/architecture.md +118 -3
- package/docs/releasing.md +8 -3
- package/docs/typescript.md +57 -10
- package/package.json +10 -9
- package/types/configuration.d.mts +5 -164
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/** Database runtime action handlers shared by guided and scripted CLI flows. */
|
|
2
|
+
import * as prompts from "@clack/prompts";
|
|
3
|
+
import { buildApplicationEnvironment, runHttpProofs, startApplicationSession, } from "../application/session.mjs";
|
|
4
|
+
import { exactCommands, renderedCommand, silentCommand, } from "./command_contract.mjs";
|
|
5
|
+
import { topologyCommandEnvironment, topologyEnvironmentFiles, } from "./runtime_commands.mjs";
|
|
6
|
+
import { formatDuration, isHumanTerminal, terminalStyle, useStyledPrompts, } from "./terminal.mjs";
|
|
7
|
+
const RUNTIME_ACTIONS = [
|
|
8
|
+
"run",
|
|
9
|
+
"start",
|
|
10
|
+
"migrate",
|
|
11
|
+
"reset",
|
|
12
|
+
"status",
|
|
13
|
+
"stop",
|
|
14
|
+
"discard",
|
|
15
|
+
"verify",
|
|
16
|
+
];
|
|
17
|
+
const isRuntimeAction = (command) => RUNTIME_ACTIONS.includes(command);
|
|
18
|
+
export const createRuntimeActionHandlers = ({ prepareCandidateConfirmation, prepareDependentTargets, proveRuntimeStack, runRuntimeStack, }) => [
|
|
19
|
+
{
|
|
20
|
+
...exactCommands("runtime-action", RUNTIME_ACTIONS, async () => silentCommand()),
|
|
21
|
+
matches: ({ command, flags }) => isRuntimeAction(command) && !(command === "run" && flags.dryRun),
|
|
22
|
+
handle: async ({ command, flags, planOptions, guided, state }) => {
|
|
23
|
+
if (!isRuntimeAction(command)) {
|
|
24
|
+
throw new Error(`Unsupported runtime action: ${command}.`);
|
|
25
|
+
}
|
|
26
|
+
if (flags.dryRun && command !== "run") {
|
|
27
|
+
throw new Error("--dry-run is supported only by rehearsal run.");
|
|
28
|
+
}
|
|
29
|
+
if (!(await prepareCandidateConfirmation({
|
|
30
|
+
command,
|
|
31
|
+
flags,
|
|
32
|
+
planOptions,
|
|
33
|
+
session: state,
|
|
34
|
+
}))) {
|
|
35
|
+
return silentCommand();
|
|
36
|
+
}
|
|
37
|
+
const stack = await runRuntimeStack({
|
|
38
|
+
command,
|
|
39
|
+
flags,
|
|
40
|
+
planOptions,
|
|
41
|
+
session: state,
|
|
42
|
+
});
|
|
43
|
+
const { runtime, topology } = stack;
|
|
44
|
+
const stackEnvironment = topologyCommandEnvironment(topology);
|
|
45
|
+
const preparations = ["run", "reset", "migrate"].includes(command)
|
|
46
|
+
? prepareDependentTargets({
|
|
47
|
+
topology,
|
|
48
|
+
environment: stackEnvironment,
|
|
49
|
+
})
|
|
50
|
+
: [];
|
|
51
|
+
let applicationProof;
|
|
52
|
+
let dependentProofs = [];
|
|
53
|
+
let httpProofResults = [];
|
|
54
|
+
let applicationReadiness;
|
|
55
|
+
if (command === "run") {
|
|
56
|
+
const applicationConfig = topology.primary.config.application;
|
|
57
|
+
const mappedApplicationEnvironment = Object.keys(applicationConfig.environmentVariables).length
|
|
58
|
+
? await buildApplicationEnvironment({
|
|
59
|
+
files: topologyEnvironmentFiles(topology),
|
|
60
|
+
mappings: applicationConfig.environmentVariables,
|
|
61
|
+
})
|
|
62
|
+
: {};
|
|
63
|
+
const proofEnvironment = {
|
|
64
|
+
...stackEnvironment,
|
|
65
|
+
...mappedApplicationEnvironment,
|
|
66
|
+
};
|
|
67
|
+
const proofLabel = topology.targets.length > 1
|
|
68
|
+
? "project-owned dependent and application proofs"
|
|
69
|
+
: "project-owned application proof";
|
|
70
|
+
const proofSpinner = useStyledPrompts(flags) ? prompts.spinner() : null;
|
|
71
|
+
if (proofSpinner) {
|
|
72
|
+
proofSpinner.start(`Running the ${proofLabel}.`);
|
|
73
|
+
}
|
|
74
|
+
else if (isHumanTerminal(flags)) {
|
|
75
|
+
console.log(`${terminalStyle(flags, "36", "→")} Running the ${proofLabel}.`);
|
|
76
|
+
}
|
|
77
|
+
let applicationSession;
|
|
78
|
+
try {
|
|
79
|
+
if (applicationConfig.readiness) {
|
|
80
|
+
applicationSession = await startApplicationSession({
|
|
81
|
+
command: applicationConfig.startCommand,
|
|
82
|
+
cwd: topology.projectRoot,
|
|
83
|
+
files: topologyEnvironmentFiles(topology),
|
|
84
|
+
mappings: applicationConfig.environmentVariables,
|
|
85
|
+
readiness: applicationConfig.readiness,
|
|
86
|
+
});
|
|
87
|
+
applicationReadiness = applicationSession.ready;
|
|
88
|
+
}
|
|
89
|
+
({ applicationProof, dependentProofs } = await proveRuntimeStack({
|
|
90
|
+
topology,
|
|
91
|
+
planOptions,
|
|
92
|
+
environment: proofEnvironment,
|
|
93
|
+
}));
|
|
94
|
+
if (applicationConfig.httpProofs.length) {
|
|
95
|
+
httpProofResults = await runHttpProofs({
|
|
96
|
+
checks: applicationConfig.httpProofs,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
if (proofSpinner)
|
|
102
|
+
proofSpinner.stop("A project proof failed.");
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
await applicationSession?.stop();
|
|
107
|
+
}
|
|
108
|
+
if (proofSpinner) {
|
|
109
|
+
proofSpinner.stop("Project proofs passed.");
|
|
110
|
+
}
|
|
111
|
+
else if (isHumanTerminal(flags)) {
|
|
112
|
+
console.log(`${terminalStyle(flags, "32", "✓")} Project proofs passed.`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const data = command === "run"
|
|
116
|
+
? {
|
|
117
|
+
runtime,
|
|
118
|
+
preparations,
|
|
119
|
+
dependentProofs,
|
|
120
|
+
applicationProof,
|
|
121
|
+
applicationReadiness,
|
|
122
|
+
httpProofResults,
|
|
123
|
+
}
|
|
124
|
+
: preparations.length
|
|
125
|
+
? { ...runtime, preparations }
|
|
126
|
+
: runtime;
|
|
127
|
+
if (guided) {
|
|
128
|
+
state.lastGuidedDetails = [
|
|
129
|
+
runtime.output,
|
|
130
|
+
...preparations.map((preparation) => preparation.output),
|
|
131
|
+
...dependentProofs.map((proof) => proof.output),
|
|
132
|
+
applicationProof?.output,
|
|
133
|
+
]
|
|
134
|
+
.filter(Boolean)
|
|
135
|
+
.join("\n");
|
|
136
|
+
}
|
|
137
|
+
return renderedCommand({
|
|
138
|
+
data,
|
|
139
|
+
render: (value) => {
|
|
140
|
+
const runtimeResult = "runtime" in value ? value.runtime : value;
|
|
141
|
+
const renderedPreparations = "preparations" in value ? value.preparations : [];
|
|
142
|
+
const renderedDependentProofs = "dependentProofs" in value ? value.dependentProofs : [];
|
|
143
|
+
const renderedApplicationProof = "applicationProof" in value ? value.applicationProof : undefined;
|
|
144
|
+
const renderedApplicationReadiness = "applicationReadiness" in value
|
|
145
|
+
? value.applicationReadiness
|
|
146
|
+
: undefined;
|
|
147
|
+
const renderedHttpProofResults = "httpProofResults" in value ? value.httpProofResults : [];
|
|
148
|
+
const nextAction = {
|
|
149
|
+
run: "Next: run rehearsal open for hands-on testing, then rehearsal verify.",
|
|
150
|
+
start: "Next: run rehearsal open for hands-on testing or rehearsal status.",
|
|
151
|
+
migrate: "Next: run rehearsal verify to prove the current runtime.",
|
|
152
|
+
reset: "Next: run rehearsal open or rehearsal verify.",
|
|
153
|
+
status: "Next: run rehearsal verify, reset, stop, or discard.",
|
|
154
|
+
stop: "Next: run rehearsal start when you want to resume.",
|
|
155
|
+
discard: "The disposable runtime was removed; the immutable baseline remains.",
|
|
156
|
+
verify: "Next: continue testing, reset to the baseline, or stop the runtime.",
|
|
157
|
+
}[runtimeResult.action];
|
|
158
|
+
if (guided) {
|
|
159
|
+
const title = runtimeResult.action === "run"
|
|
160
|
+
? "REHEARSAL PASSED"
|
|
161
|
+
: "ACTION COMPLETE";
|
|
162
|
+
return [
|
|
163
|
+
title,
|
|
164
|
+
`✓ ${runtimeResult.action} completed in ${formatDuration(runtimeResult.durationMs)}`,
|
|
165
|
+
...renderedPreparations.map((preparation) => `✓ ${preparation.target} preparation passed: ${preparation.command}`),
|
|
166
|
+
...renderedDependentProofs.map((proof) => `✓ ${proof.target} proof passed: ${proof.command}`),
|
|
167
|
+
renderedApplicationProof
|
|
168
|
+
? `✓ Application proof passed: ${renderedApplicationProof.command}`
|
|
169
|
+
: null,
|
|
170
|
+
renderedApplicationReadiness
|
|
171
|
+
? `✓ Application became ready at ${renderedApplicationReadiness.url}`
|
|
172
|
+
: null,
|
|
173
|
+
...renderedHttpProofResults.map((proof) => `✓ ${proof.kind} HTTP proof passed: ${proof.name}`),
|
|
174
|
+
"",
|
|
175
|
+
"Technical output is available from Show details in the guide.",
|
|
176
|
+
]
|
|
177
|
+
.filter(Boolean)
|
|
178
|
+
.join("\n");
|
|
179
|
+
}
|
|
180
|
+
return [
|
|
181
|
+
runtimeResult.output ||
|
|
182
|
+
`Rehearsal ${runtimeResult.action} completed.`,
|
|
183
|
+
...renderedPreparations.map((preparation) => `${preparation.target} preparation passed: ${preparation.command}`),
|
|
184
|
+
...renderedDependentProofs.map((proof) => `${proof.target} proof passed: ${proof.command}`),
|
|
185
|
+
renderedApplicationProof
|
|
186
|
+
? `Application proof passed: ${renderedApplicationProof.command}`
|
|
187
|
+
: null,
|
|
188
|
+
renderedApplicationReadiness
|
|
189
|
+
? `Application readiness passed: ${renderedApplicationReadiness.url}`
|
|
190
|
+
: null,
|
|
191
|
+
...renderedHttpProofResults.map((proof) => `${proof.kind} HTTP proof passed: ${proof.name}`),
|
|
192
|
+
nextAction ? "" : null,
|
|
193
|
+
nextAction,
|
|
194
|
+
]
|
|
195
|
+
.filter((entry) => entry !== null)
|
|
196
|
+
.join("\n");
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
];
|
|
@@ -10,6 +10,7 @@ import type { RuntimeTopologyTarget } from "../runtime/topology.mjs";
|
|
|
10
10
|
import type { InstalledPackageFingerprint } from "../shared/operation_guard.mjs";
|
|
11
11
|
import type { RehearsalCliFlags } from "./arguments.mjs";
|
|
12
12
|
import type { RehearsalErrorCategory } from "../shared/diagnostics.mjs";
|
|
13
|
+
import type { CliSessionState } from "./command_contract.mjs";
|
|
13
14
|
type RuntimeTopology = Awaited<ReturnType<typeof loadRuntimeTopology>>;
|
|
14
15
|
type RuntimeAction = "run" | "start" | "migrate" | "reset" | "status" | "stop" | "discard" | "verify";
|
|
15
16
|
interface ManagerReceipt {
|
|
@@ -188,9 +189,10 @@ export declare const createRuntimeCommands: ({ packageRoot, projectRoot, manager
|
|
|
188
189
|
managerPath: string;
|
|
189
190
|
getActivePackageFingerprint: () => InstalledPackageFingerprint | undefined;
|
|
190
191
|
}) => {
|
|
191
|
-
openRuntimeApplication: ({ flags, planOptions, onReady, }: {
|
|
192
|
+
openRuntimeApplication: ({ flags, planOptions, session: commandSession, onReady, }: {
|
|
192
193
|
flags: RehearsalCliFlags;
|
|
193
194
|
planOptions: RehearsalConfigPathOptions;
|
|
195
|
+
session: CliSessionState;
|
|
194
196
|
onReady?: Parameters<typeof holdApplicationSession>[0]["onReady"];
|
|
195
197
|
}) => Promise<{
|
|
196
198
|
action: string;
|
|
@@ -221,10 +223,11 @@ export declare const createRuntimeCommands: ({ packageRoot, projectRoot, manager
|
|
|
221
223
|
diagnostics: import("../application/session.mjs").ApplicationDiagnostics;
|
|
222
224
|
};
|
|
223
225
|
}>;
|
|
224
|
-
prepareCandidateConfirmation: ({ command, flags, planOptions, }: {
|
|
226
|
+
prepareCandidateConfirmation: ({ command, flags, planOptions, session, }: {
|
|
225
227
|
command: RuntimeAction;
|
|
226
228
|
flags: RehearsalCliFlags;
|
|
227
229
|
planOptions: RehearsalConfigPathOptions;
|
|
230
|
+
session: CliSessionState;
|
|
228
231
|
}) => Promise<boolean>;
|
|
229
232
|
prepareDependentTargets: ({ topology, environment, }: {
|
|
230
233
|
topology: RuntimeTopology;
|
|
@@ -256,10 +259,11 @@ export declare const createRuntimeCommands: ({ packageRoot, projectRoot, manager
|
|
|
256
259
|
confirmation?: string;
|
|
257
260
|
targetName?: string;
|
|
258
261
|
}) => ManagerReceipt;
|
|
259
|
-
runRuntimeStack: ({ command, flags, planOptions, }: {
|
|
262
|
+
runRuntimeStack: ({ command, flags, planOptions, session, }: {
|
|
260
263
|
command: RuntimeAction;
|
|
261
264
|
flags: RehearsalCliFlags;
|
|
262
265
|
planOptions: RehearsalConfigPathOptions;
|
|
266
|
+
session: CliSessionState;
|
|
263
267
|
}) => Promise<RuntimeStackResult>;
|
|
264
268
|
};
|
|
265
269
|
export {};
|
|
@@ -403,15 +403,15 @@ export const createRuntimeCommands = ({ packageRoot, projectRoot, managerPath, g
|
|
|
403
403
|
output: String(redactDiagnosticValue(String(result.stdout ?? "").trim())),
|
|
404
404
|
};
|
|
405
405
|
};
|
|
406
|
-
const runRuntimeStack = async ({ command, flags, planOptions, }) => {
|
|
407
|
-
const topology =
|
|
406
|
+
const runRuntimeStack = async ({ command, flags, planOptions, session, }) => {
|
|
407
|
+
const topology = session.runtimeTopology ??
|
|
408
408
|
(await loadRuntimeTopology(planOptions));
|
|
409
409
|
const reverse = ["stop", "discard"].includes(command);
|
|
410
410
|
const targets = reverse
|
|
411
411
|
? [...topology.targets].reverse()
|
|
412
412
|
: topology.targets;
|
|
413
413
|
const results = targets.map((target) => {
|
|
414
|
-
const confirmation =
|
|
414
|
+
const confirmation = session.targetConfirmations?.get(target.configPath);
|
|
415
415
|
return runManager({
|
|
416
416
|
action: command,
|
|
417
417
|
flags,
|
|
@@ -479,7 +479,7 @@ export const createRuntimeCommands = ({ packageRoot, projectRoot, managerPath, g
|
|
|
479
479
|
});
|
|
480
480
|
return { applicationProof, dependentProofs };
|
|
481
481
|
};
|
|
482
|
-
const openRuntimeApplication = async ({ flags, planOptions, onReady = () => undefined, }) => {
|
|
482
|
+
const openRuntimeApplication = async ({ flags, planOptions, session: commandSession, onReady = () => undefined, }) => {
|
|
483
483
|
const topology = await loadRuntimeTopology(planOptions);
|
|
484
484
|
const application = topology.primary.config.application;
|
|
485
485
|
if (!application.readiness) {
|
|
@@ -498,16 +498,18 @@ export const createRuntimeCommands = ({ packageRoot, projectRoot, managerPath, g
|
|
|
498
498
|
if (runtimeReceipts.some((present) => !present)) {
|
|
499
499
|
throw new Error("rehearsal open requires an existing verified runtime. Run rehearsal run first.");
|
|
500
500
|
}
|
|
501
|
-
|
|
501
|
+
commandSession.runtimeTopology = topology;
|
|
502
502
|
const started = await runRuntimeStack({
|
|
503
503
|
command: "start",
|
|
504
504
|
flags,
|
|
505
505
|
planOptions,
|
|
506
|
+
session: commandSession,
|
|
506
507
|
});
|
|
507
508
|
const verified = await runRuntimeStack({
|
|
508
509
|
command: "verify",
|
|
509
510
|
flags,
|
|
510
511
|
planOptions,
|
|
512
|
+
session: commandSession,
|
|
511
513
|
});
|
|
512
514
|
const preparations = prepareDependentTargets({
|
|
513
515
|
topology,
|
|
@@ -534,12 +536,12 @@ export const createRuntimeCommands = ({ packageRoot, projectRoot, managerPath, g
|
|
|
534
536
|
},
|
|
535
537
|
};
|
|
536
538
|
};
|
|
537
|
-
const prepareCandidateConfirmation = async ({ command, flags, planOptions, }) => {
|
|
539
|
+
const prepareCandidateConfirmation = async ({ command, flags, planOptions, session, }) => {
|
|
538
540
|
if (!["run", "migrate"].includes(command) || flags.dryRun)
|
|
539
541
|
return true;
|
|
540
542
|
const summary = await topologyCandidateSummary(planOptions);
|
|
541
|
-
|
|
542
|
-
|
|
543
|
+
session.runtimeTopology = summary.topology;
|
|
544
|
+
session.targetConfirmations = new Map(summary.targets.map((target) => [
|
|
543
545
|
target.configPath,
|
|
544
546
|
target.candidateSha256,
|
|
545
547
|
]));
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Temporary source-access and privacy-safe baseline refresh command handlers. */
|
|
2
|
+
import { createRefreshWorkflow } from "./source_commands.mjs";
|
|
3
|
+
import type { CommandDefinition } from "./command_contract.mjs";
|
|
4
|
+
type RefreshWorkflow = ReturnType<typeof createRefreshWorkflow>;
|
|
5
|
+
export declare const createSourceCommandHandlers: ({ runRefreshWorkflow, }: {
|
|
6
|
+
runRefreshWorkflow: RefreshWorkflow;
|
|
7
|
+
}) => readonly CommandDefinition[];
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/** Temporary source-access and privacy-safe baseline refresh command handlers. */
|
|
2
|
+
import { createSourceAccessPlan } from "../source/access.mjs";
|
|
3
|
+
import { formatCount } from "../shared/human_output.mjs";
|
|
4
|
+
import { createRefreshWorkflow, loadPreparationContext, runSourceAccess, runSourceBaselineRefresh, runSourceRetirement, } from "./source_commands.mjs";
|
|
5
|
+
import { formatBytes, renderRefreshWorkflow, renderSourceAccessPlan, renderSourceRetirement, } from "./renderers.mjs";
|
|
6
|
+
import { exactCommands, renderedCommand } from "./command_contract.mjs";
|
|
7
|
+
export const createSourceCommandHandlers = ({ runRefreshWorkflow, }) => [
|
|
8
|
+
exactCommands("source-plan", ["source plan"], async ({ flags, planOptions }) => {
|
|
9
|
+
const { sourcePolicy } = await loadPreparationContext(planOptions);
|
|
10
|
+
const data = createSourceAccessPlan({ policy: sourcePolicy });
|
|
11
|
+
return renderedCommand({
|
|
12
|
+
data,
|
|
13
|
+
render: renderSourceAccessPlan,
|
|
14
|
+
});
|
|
15
|
+
}),
|
|
16
|
+
exactCommands("source-apply", ["source apply"], async ({ flags, planOptions }) => {
|
|
17
|
+
const data = await runSourceAccess({ flags, planOptions });
|
|
18
|
+
return renderedCommand({
|
|
19
|
+
data,
|
|
20
|
+
render: (result) => result.mode === "preview"
|
|
21
|
+
? renderSourceAccessPlan(result.plan)
|
|
22
|
+
: [
|
|
23
|
+
"SOURCE ACCESS — READY",
|
|
24
|
+
`${result.receipt.accessMode === "external" ? "External" : "Temporary"} reader expires: ${result.receipt.expiresAt}`,
|
|
25
|
+
result.receipt.accessMode === "external"
|
|
26
|
+
? "Rehearsal made no source-side changes."
|
|
27
|
+
: "Rehearsal created only the reviewed temporary source access.",
|
|
28
|
+
`Local credential: ${result.credentialFile}`,
|
|
29
|
+
"",
|
|
30
|
+
"Next: run rehearsal baseline refresh, then retire source access.",
|
|
31
|
+
].join("\n"),
|
|
32
|
+
});
|
|
33
|
+
}),
|
|
34
|
+
exactCommands("source-retire", ["source retire"], async ({ flags, planOptions }) => {
|
|
35
|
+
const data = await runSourceRetirement({ flags, planOptions });
|
|
36
|
+
return renderedCommand({ data, render: renderSourceRetirement });
|
|
37
|
+
}),
|
|
38
|
+
exactCommands("baseline-refresh", ["baseline refresh"], async ({ flags, planOptions }) => {
|
|
39
|
+
const data = await runSourceBaselineRefresh({ planOptions });
|
|
40
|
+
return renderedCommand({
|
|
41
|
+
data,
|
|
42
|
+
render: (result) => [
|
|
43
|
+
`Activated refreshed baseline ${result.generationId}.`,
|
|
44
|
+
`${formatCount(result.manifest.rowCount, "sanitized row")} across ${formatCount(Object.keys(result.manifest.tableCounts).length, "table")}.`,
|
|
45
|
+
`Source estimate: ${formatCount(result.estimate.rows, "row")}, ${formatBytes(result.estimate.bytes)}.`,
|
|
46
|
+
"",
|
|
47
|
+
"Next: run rehearsal source retire, then rehearsal doctor.",
|
|
48
|
+
].join("\n"),
|
|
49
|
+
});
|
|
50
|
+
}),
|
|
51
|
+
exactCommands("refresh", ["refresh"], async ({ flags, planOptions, state }) => {
|
|
52
|
+
const data = await runRefreshWorkflow({
|
|
53
|
+
flags,
|
|
54
|
+
planOptions,
|
|
55
|
+
session: state,
|
|
56
|
+
});
|
|
57
|
+
return renderedCommand({
|
|
58
|
+
data,
|
|
59
|
+
render: (result) => renderRefreshWorkflow(result),
|
|
60
|
+
});
|
|
61
|
+
}),
|
|
62
|
+
];
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { RehearsalConfigPathOptions } from "../project/configuration.mjs";
|
|
6
6
|
import type { RehearsalCliFlags } from "./arguments.mjs";
|
|
7
|
+
import type { CliSessionState } from "./command_contract.mjs";
|
|
7
8
|
import { loadRuntimeTopology } from "../runtime/topology.mjs";
|
|
8
9
|
import type { RuntimeTopologyTarget } from "../runtime/topology.mjs";
|
|
9
10
|
type RuntimeTopology = Awaited<ReturnType<typeof loadRuntimeTopology>>;
|
|
@@ -327,6 +328,7 @@ export declare const createRefreshWorkflow: ({ runRuntimeStack, prepareDependent
|
|
|
327
328
|
command: "reset";
|
|
328
329
|
flags: RehearsalCliFlags;
|
|
329
330
|
planOptions: RehearsalConfigPathOptions;
|
|
331
|
+
session: CliSessionState;
|
|
330
332
|
}) => Promise<{
|
|
331
333
|
runtime: unknown;
|
|
332
334
|
topology: RuntimeTopology;
|
|
@@ -338,7 +340,9 @@ export declare const createRefreshWorkflow: ({ runRuntimeStack, prepareDependent
|
|
|
338
340
|
topologyCommandEnvironment: (topology: RuntimeTopology) => NodeJS.ProcessEnv;
|
|
339
341
|
buildPlan?: (options: RehearsalConfigPathOptions) => Promise<RefreshPlanContext>;
|
|
340
342
|
refreshBaseline?: typeof runSourceBaselineRefresh;
|
|
341
|
-
}) => ({ flags, planOptions }: SourceCommandInput
|
|
343
|
+
}) => ({ flags, planOptions, session, }: SourceCommandInput & {
|
|
344
|
+
session: CliSessionState;
|
|
345
|
+
}) => Promise<{
|
|
342
346
|
mode: "preview";
|
|
343
347
|
plan: {
|
|
344
348
|
review: {
|
|
@@ -224,7 +224,7 @@ export const buildRefreshWorkflowPlan = async (planOptions) => {
|
|
|
224
224
|
},
|
|
225
225
|
};
|
|
226
226
|
};
|
|
227
|
-
export const createRefreshWorkflow = ({ runRuntimeStack, prepareDependentTargets, topologyCommandEnvironment, buildPlan = buildRefreshWorkflowPlan, refreshBaseline = runSourceBaselineRefresh, }) => async ({ flags, planOptions }) => {
|
|
227
|
+
export const createRefreshWorkflow = ({ runRuntimeStack, prepareDependentTargets, topologyCommandEnvironment, buildPlan = buildRefreshWorkflowPlan, refreshBaseline = runSourceBaselineRefresh, }) => async ({ flags, planOptions, session, }) => {
|
|
228
228
|
const context = await buildPlan(planOptions);
|
|
229
229
|
if (!flags.refreshConfirmation) {
|
|
230
230
|
return { mode: "preview", plan: context.plan };
|
|
@@ -241,6 +241,7 @@ export const createRefreshWorkflow = ({ runRuntimeStack, prepareDependentTargets
|
|
|
241
241
|
command: "reset",
|
|
242
242
|
flags,
|
|
243
243
|
planOptions,
|
|
244
|
+
session,
|
|
244
245
|
});
|
|
245
246
|
runtime = stack.runtime;
|
|
246
247
|
preparations = prepareDependentTargets({
|
|
@@ -259,6 +260,7 @@ export const createRefreshWorkflow = ({ runRuntimeStack, prepareDependentTargets
|
|
|
259
260
|
command: "reset",
|
|
260
261
|
flags,
|
|
261
262
|
planOptions,
|
|
263
|
+
session,
|
|
262
264
|
});
|
|
263
265
|
prepareDependentTargets({
|
|
264
266
|
topology: rollback.topology,
|
|
@@ -1,144 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
type SubjectIdentityProvider = Exclude<IdentityProvider, "email">;
|
|
7
|
-
type IdentityStrategy = "transfer" | "preserve-audit";
|
|
8
|
-
type PathValueType = "text" | "jsonb";
|
|
9
|
-
interface Relation {
|
|
10
|
-
readonly schema: string;
|
|
11
|
-
readonly table: string;
|
|
12
|
-
}
|
|
13
|
-
interface PatternDefaultMatcher {
|
|
14
|
-
readonly kind: "pattern";
|
|
15
|
-
readonly pattern: string;
|
|
16
|
-
}
|
|
17
|
-
interface ExactDefaultMatcher {
|
|
18
|
-
readonly kind: "exact";
|
|
19
|
-
readonly value: unknown;
|
|
20
|
-
}
|
|
21
|
-
type SignupDefaultMatcher = PatternDefaultMatcher | ExactDefaultMatcher;
|
|
22
|
-
interface VerifiedEmailMatcher {
|
|
23
|
-
readonly type: "verified-email";
|
|
24
|
-
readonly providers: readonly IdentityProvider[];
|
|
25
|
-
readonly emailEnvironmentVariable: string;
|
|
26
|
-
readonly approvedEmailSha256: string;
|
|
27
|
-
}
|
|
28
|
-
interface ProviderSubjectMatcher {
|
|
29
|
-
readonly type: "provider-subject";
|
|
30
|
-
readonly provider: SubjectIdentityProvider;
|
|
31
|
-
readonly subjectEnvironmentVariable: string;
|
|
32
|
-
readonly approvedSubjectSha256: string;
|
|
33
|
-
}
|
|
34
|
-
type IdentityMatcher = VerifiedEmailMatcher | ProviderSubjectMatcher;
|
|
35
|
-
interface IdentityReference extends Relation {
|
|
36
|
-
readonly column: string;
|
|
37
|
-
readonly required: boolean;
|
|
38
|
-
readonly strategy: IdentityStrategy;
|
|
39
|
-
}
|
|
40
|
-
interface JsonIdentityReference extends Relation {
|
|
41
|
-
readonly column: string;
|
|
42
|
-
readonly path: readonly string[];
|
|
43
|
-
}
|
|
44
|
-
interface SignupDefaultDeclaration {
|
|
45
|
-
readonly table: Relation;
|
|
46
|
-
readonly identityColumn: string;
|
|
47
|
-
readonly ignoredColumns: readonly string[];
|
|
48
|
-
readonly values: Readonly<Record<string, SignupDefaultMatcher>>;
|
|
49
|
-
}
|
|
50
|
-
interface PathIdentityReference extends Relation {
|
|
51
|
-
readonly column: string;
|
|
52
|
-
readonly valueType: PathValueType;
|
|
53
|
-
}
|
|
54
|
-
interface IdentityAsset {
|
|
55
|
-
readonly bucket: string;
|
|
56
|
-
readonly prefix: string;
|
|
57
|
-
readonly rewritePath: boolean;
|
|
58
|
-
}
|
|
59
|
-
interface IdentityTokenHook {
|
|
60
|
-
readonly function: Readonly<{
|
|
61
|
-
schema: string;
|
|
62
|
-
name: string;
|
|
63
|
-
}>;
|
|
64
|
-
readonly expectedClaims: JsonObject;
|
|
65
|
-
}
|
|
66
|
-
export interface IdentityDeclaration {
|
|
67
|
-
readonly name: string;
|
|
68
|
-
readonly matcher: IdentityMatcher;
|
|
69
|
-
readonly placeholderUserId: string;
|
|
70
|
-
readonly references: readonly IdentityReference[];
|
|
71
|
-
readonly jsonReferences: readonly JsonIdentityReference[];
|
|
72
|
-
readonly signupDefaults: readonly SignupDefaultDeclaration[];
|
|
73
|
-
readonly pathReferences: readonly PathIdentityReference[];
|
|
74
|
-
readonly assets: readonly IdentityAsset[];
|
|
75
|
-
readonly claims: JsonObject;
|
|
76
|
-
readonly tokenHook: IdentityTokenHook | null;
|
|
77
|
-
}
|
|
78
|
-
export interface IdentityPolicy {
|
|
79
|
-
readonly identityVersion: 1;
|
|
80
|
-
readonly identities: readonly IdentityDeclaration[];
|
|
81
|
-
}
|
|
82
|
-
interface IdentityClaimReview {
|
|
83
|
-
readonly planVersion: 3;
|
|
84
|
-
readonly operation: "claim-local-identity";
|
|
85
|
-
readonly name: string;
|
|
86
|
-
readonly matcher: IdentityMatcher;
|
|
87
|
-
readonly placeholderUserId: string;
|
|
88
|
-
readonly references: readonly IdentityReference[];
|
|
89
|
-
readonly jsonReferences: readonly JsonIdentityReference[];
|
|
90
|
-
readonly signupDefaults: readonly Readonly<{
|
|
91
|
-
table: Relation;
|
|
92
|
-
identityColumn: string;
|
|
93
|
-
ignoredColumns: readonly string[];
|
|
94
|
-
valuesSha256: string;
|
|
95
|
-
}>[];
|
|
96
|
-
readonly pathReferences: readonly PathIdentityReference[];
|
|
97
|
-
readonly assets: readonly IdentityAsset[];
|
|
98
|
-
readonly claimKeys: readonly string[];
|
|
99
|
-
readonly claimsSha256: string;
|
|
100
|
-
readonly tokenHook: Readonly<{
|
|
101
|
-
function: Readonly<{
|
|
102
|
-
schema: string;
|
|
103
|
-
name: string;
|
|
104
|
-
}>;
|
|
105
|
-
expectedClaimsSha256: string;
|
|
106
|
-
}> | null;
|
|
107
|
-
}
|
|
108
|
-
export interface IdentityClaimPlan {
|
|
109
|
-
readonly identity: IdentityDeclaration;
|
|
110
|
-
readonly review: Readonly<IdentityClaimReview>;
|
|
111
|
-
readonly digest: string;
|
|
112
|
-
}
|
|
113
|
-
interface QueryResult<Row extends JsonObject = JsonObject> {
|
|
114
|
-
readonly rows: Row[];
|
|
115
|
-
}
|
|
116
|
-
interface IdentityDatabaseClient {
|
|
117
|
-
connect?(): Promise<void>;
|
|
118
|
-
end?(): Promise<void>;
|
|
119
|
-
query<Row extends JsonObject = JsonObject>(sql: string, parameters?: readonly unknown[]): Promise<QueryResult<Row>>;
|
|
120
|
-
}
|
|
121
|
-
type IdentityClientFactory = (connectionString: string) => IdentityDatabaseClient | Promise<IdentityDatabaseClient>;
|
|
122
|
-
export declare const validateIdentityPolicy: (policy: unknown) => IdentityPolicy;
|
|
123
|
-
export declare const createIdentityClaimPlan: ({ policy, name, environment, }: {
|
|
124
|
-
policy: unknown;
|
|
125
|
-
name: string;
|
|
126
|
-
environment?: Readonly<Record<string, string | undefined>>;
|
|
127
|
-
}) => IdentityClaimPlan;
|
|
128
|
-
export declare const applyIdentityClaim: ({ plan, confirmation, connectionString, clientFactory, storageTransfer, }: {
|
|
129
|
-
plan: IdentityClaimPlan;
|
|
130
|
-
confirmation: string;
|
|
131
|
-
connectionString: string;
|
|
132
|
-
clientFactory?: IdentityClientFactory;
|
|
133
|
-
storageTransfer?: SupabaseStorageTransfer;
|
|
134
|
-
}) => Promise<{
|
|
135
|
-
name: string;
|
|
136
|
-
provider: IdentityProvider;
|
|
137
|
-
claimed: true;
|
|
138
|
-
removedSignupDefaults: number;
|
|
139
|
-
tokenHookVerified: boolean;
|
|
140
|
-
placeholderRetainedForAudit: boolean;
|
|
141
|
-
storageObjectsTransferred: number;
|
|
142
|
-
idempotent: boolean;
|
|
143
|
-
}>;
|
|
144
|
-
export {};
|
|
1
|
+
/** Stable public facade for local identity claim planning and execution. */
|
|
2
|
+
export type { IdentityClaimPlan, IdentityDeclaration, IdentityPolicy, } from "./claim_contract.mjs";
|
|
3
|
+
export { applyIdentityClaim } from "./claim_executor.mjs";
|
|
4
|
+
export { createIdentityClaimPlan } from "./claim_plan.mjs";
|
|
5
|
+
export { validateIdentityPolicy } from "./claim_policy.mjs";
|