@rehearsal-db/core 0.1.0-rc.3 → 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.
Files changed (39) hide show
  1. package/CHANGELOG.md +24 -1
  2. package/README.md +1 -0
  3. package/dist/package.json +4 -1
  4. package/dist/src/baseline/artifact.d.mts +14 -0
  5. package/dist/src/baseline/artifact.mjs +50 -0
  6. package/dist/src/cli/basic_command_handlers.mjs +1 -0
  7. package/dist/src/cli/inspection_command_handlers.mjs +18 -0
  8. package/dist/src/cli/runtime_commands.d.mts +45 -0
  9. package/dist/src/cli/runtime_commands.mjs +5 -0
  10. package/dist/src/index.d.mts +2 -2
  11. package/dist/src/index.mjs +1 -1
  12. package/dist/src/project/configuration.d.mts +2 -0
  13. package/dist/src/project/configuration.mjs +1 -0
  14. package/dist/src/project/project_discovery.d.mts +15 -0
  15. package/dist/src/project/project_discovery.mjs +350 -0
  16. package/dist/src/project/project_discovery_application.d.mts +16 -0
  17. package/dist/src/project/project_discovery_application.mjs +190 -0
  18. package/dist/src/project/project_discovery_contract.d.mts +78 -0
  19. package/dist/src/project/project_discovery_contract.mjs +43 -0
  20. package/dist/src/project/project_discovery_metadata.d.mts +14 -0
  21. package/dist/src/project/project_discovery_metadata.mjs +138 -0
  22. package/dist/src/runtime/lifecycle_engine.d.mts +6 -0
  23. package/dist/src/runtime/lifecycle_engine.mjs +14 -0
  24. package/dist/src/runtime/plan.d.mts +13 -0
  25. package/dist/src/runtime/plan.mjs +19 -1
  26. package/dist/src/runtime/status.d.mts +2 -0
  27. package/dist/src/runtime/status.mjs +5 -0
  28. package/dist/src/targets/postgresql.mjs +4 -1
  29. package/dist/src/targets/postgresql_status.d.mts +5 -2
  30. package/dist/src/targets/postgresql_status.mjs +14 -1
  31. package/dist/src/targets/supabase.mjs +3 -0
  32. package/dist/src/targets/supabase_status.d.mts +4 -1
  33. package/dist/src/targets/supabase_status.mjs +11 -1
  34. package/docs/README.md +1 -0
  35. package/docs/architecture.md +8 -0
  36. package/docs/commands.md +62 -11
  37. package/docs/typescript.md +11 -0
  38. package/docs/vscode.md +53 -0
  39. package/package.json +4 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,28 @@ a Changelog, and versions will follow Semantic Versioning after the package exis
5
5
 
6
6
  ## Unreleased
7
7
 
8
+ ## [0.1.0-rc.4] - 2026-10-11
9
+
10
+ ### Added
11
+
12
+ - `discover --json` exposes a versioned, read-only Project Profile for editor clients,
13
+ separating proven facts, evidence-backed suggestions, and decisions that remain with
14
+ the user.
15
+ - Project discovery reports bounded application, package-manager, migration, target,
16
+ runtime, baseline-retention, configuration, and local-address metadata without reading
17
+ environment files, contacting databases, starting containers, or executing project code.
18
+ - Public TypeScript contracts now cover discovery results, retained baseline generations,
19
+ application readiness, cleanup policy, and structured PostgreSQL and Supabase runtime
20
+ state consumed by the Rehearsal VS Code extension.
21
+
22
+ ### Changed
23
+
24
+ - Plans and status results expose the additional bounded metadata required for a
25
+ project-aware editor experience while retaining core ownership of all safety checks and
26
+ mutating operations.
27
+ - Documentation now includes the supported VS Code integration boundary, privacy model,
28
+ compatibility floor, and safe automatic-refresh behavior.
29
+
8
30
  ## [0.1.0-rc.3] - 2026-10-10
9
31
 
10
32
  ### Added
@@ -605,7 +627,8 @@ a Changelog, and versions will follow Semantic Versioning after the package exis
605
627
  publication uses short-lived trusted OIDC, and every release tag must already exist on
606
628
  protected `main`.
607
629
 
608
- [Unreleased]: https://github.com/Ddupasquier/rehearsal-db/compare/v0.1.0-rc.3...HEAD
630
+ [Unreleased]: https://github.com/Ddupasquier/rehearsal-db/compare/v0.1.0-rc.4...HEAD
631
+ [0.1.0-rc.4]: https://github.com/Ddupasquier/rehearsal-db/compare/v0.1.0-rc.3...v0.1.0-rc.4
609
632
  [0.1.0-rc.3]: https://github.com/Ddupasquier/rehearsal-db/compare/v0.1.0-rc.2...v0.1.0-rc.3
610
633
  [0.1.0-rc.2]: https://github.com/Ddupasquier/rehearsal-db/compare/v0.1.0-rc.1...v0.1.0-rc.2
611
634
  [0.1.0-rc.1]: https://github.com/Ddupasquier/rehearsal-db/compare/v0.1.0-beta.18...v0.1.0-rc.1
package/README.md CHANGED
@@ -166,6 +166,7 @@ Start here:
166
166
 
167
167
  - [Documentation home](docs/index.md) — choose the shortest path for your goal
168
168
  - [Getting started](docs/getting-started.md) — set up your own project
169
+ - [VS Code extension](docs/vscode.md) — use the project-aware visual workbench
169
170
  - [Safe hands-on tutorial](docs/tutorial.md) — try the full flow in a disposable project
170
171
  - [Troubleshooting](docs/troubleshooting.md) — fix common setup problems
171
172
  - [Next steps](docs/roadmap.md) — see the current product sequence
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rehearsal-db/core",
3
- "version": "0.1.0-rc.3",
3
+ "version": "0.1.0-rc.4",
4
4
  "private": false,
5
5
  "description": "Catch PostgreSQL and Supabase migrations that pass on empty databases but fail on real data—before deploy.",
6
6
  "repository": {
@@ -22,6 +22,9 @@
22
22
  "bin": {
23
23
  "rehearsal": "dist/src/cli/rehearsal.mjs"
24
24
  },
25
+ "rehearsalContracts": {
26
+ "projectDiscovery": 1
27
+ },
25
28
  "exports": {
26
29
  ".": {
27
30
  "types": "./types/configuration.d.mts",
@@ -66,6 +66,17 @@ export interface BaselinePrunePlan {
66
66
  readonly retained: string[];
67
67
  readonly removed: string[];
68
68
  }
69
+ export interface BaselineGenerationSummary {
70
+ readonly generationId: string;
71
+ readonly createdAt: string | null;
72
+ readonly active: boolean;
73
+ readonly formatVersion: 1 | 2;
74
+ readonly rowCount: number;
75
+ readonly tableCount: number;
76
+ readonly migrationCount: number;
77
+ readonly migrationCutoff: string;
78
+ readonly storageAssetCount: number;
79
+ }
69
80
  export declare const createBaselineGenerationId: ({ now, }?: {
70
81
  now?: Date;
71
82
  }) => string;
@@ -91,6 +102,9 @@ export declare const removeStagedBaselineGeneration: ({ artifactRoot, generation
91
102
  export declare const resolveActiveBaselinePaths: ({ artifactRoot, }: {
92
103
  artifactRoot: string;
93
104
  }) => Promise<ActiveBaselinePaths>;
105
+ export declare const inspectBaselineGenerations: ({ artifactRoot, }: {
106
+ artifactRoot: string;
107
+ }) => Promise<readonly BaselineGenerationSummary[]>;
94
108
  export declare const verifyActiveBaseline: ({ artifactRoot, }: {
95
109
  artifactRoot: string;
96
110
  }) => Promise<BaselineManifest>;
@@ -46,6 +46,12 @@ const assertGenerationId = (value) => {
46
46
  }
47
47
  return value;
48
48
  };
49
+ const generationCreatedAt = (generationId) => {
50
+ const match = /^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z-/u.exec(generationId);
51
+ return match
52
+ ? `${match[1]}-${match[2]}-${match[3]}T${match[4]}:${match[5]}:${match[6]}Z`
53
+ : null;
54
+ };
49
55
  const assertArtifactRoot = (artifactRoot) => {
50
56
  const resolved = resolve(artifactRoot);
51
57
  if (basename(resolved) !== ".rehearsal" || resolved === sep) {
@@ -364,6 +370,50 @@ export const resolveActiveBaselinePaths = async ({ artifactRoot, }) => {
364
370
  schemaPath: join(generationDirectory, "production-schema.sql"),
365
371
  };
366
372
  };
373
+ export const inspectBaselineGenerations = async ({ artifactRoot, }) => {
374
+ const root = assertArtifactRoot(artifactRoot);
375
+ const active = basename((await resolveActiveBaselinePaths({ artifactRoot: root }))
376
+ .generationDirectory);
377
+ const generationsRoot = join(root, generationsDirectoryName);
378
+ await verifyNoSymlink(generationsRoot, "The Rehearsal generations directory");
379
+ const entries = await readdir(generationsRoot, { withFileTypes: true });
380
+ if (entries.length > 1_000) {
381
+ throw new Error("The Rehearsal baseline generation limit was exceeded.");
382
+ }
383
+ const summaries = [];
384
+ for (const entry of entries.sort((left, right) => right.name.localeCompare(left.name))) {
385
+ if (!entry.isDirectory() || !generationPattern.test(entry.name))
386
+ continue;
387
+ const generationDirectory = assertInsideRoot(root, join(generationsRoot, entry.name));
388
+ await verifyNoSymlink(generationDirectory, "A Rehearsal baseline generation");
389
+ const manifest = parseManifest(await readFile(join(generationDirectory, "baseline-manifest.json"), "utf8"));
390
+ if (manifest.generationId !== entry.name ||
391
+ ![1, 2].includes(manifest.formatVersion) ||
392
+ !Number.isSafeInteger(manifest.rowCount) ||
393
+ manifest.rowCount < 0 ||
394
+ !isObject(manifest.tableCounts) ||
395
+ !isObject(manifest.migrations) ||
396
+ !isObject(manifest.storageAssets) ||
397
+ typeof manifest.migrationCutoff !== "string") {
398
+ throw new Error(`Rehearsal baseline generation ${entry.name} has an invalid manifest.`);
399
+ }
400
+ summaries.push({
401
+ generationId: entry.name,
402
+ createdAt: generationCreatedAt(entry.name),
403
+ active: entry.name === active,
404
+ formatVersion: manifest.formatVersion,
405
+ rowCount: manifest.rowCount,
406
+ tableCount: Object.keys(manifest.tableCounts).length,
407
+ migrationCount: Object.keys(manifest.migrations).length,
408
+ migrationCutoff: manifest.migrationCutoff,
409
+ storageAssetCount: Object.keys(manifest.storageAssets).length,
410
+ });
411
+ }
412
+ if (!summaries.some(({ active: isActive }) => isActive)) {
413
+ throw new Error("The active Rehearsal baseline generation is unavailable.");
414
+ }
415
+ return Object.freeze(summaries.map((summary) => Object.freeze(summary)));
416
+ };
367
417
  export const verifyActiveBaseline = async ({ artifactRoot, }) => {
368
418
  const paths = await resolveActiveBaselinePaths({ artifactRoot });
369
419
  const { generationDirectory } = paths;
@@ -20,6 +20,7 @@ Commands:
20
20
  identity claim --identity= --confirm-identity= Apply the exact local association
21
21
  identity connect [--identity=] Keep the app open, detect sign-in, and connect once
22
22
  doctor Check whether Rehearsal is safe and ready
23
+ discover Inspect project metadata and explain setup suggestions
23
24
  support Print a safe, copy-ready support report
24
25
  activity Report idle, busy, stale, or invalid operation state
25
26
  explain Show the immutable execution plan
@@ -1,11 +1,29 @@
1
1
  /** Read-only readiness, support, planning, and inspection command handlers. */
2
2
  import { collectRehearsalSupportReport } from "../project/support_report.mjs";
3
+ import { discoverProjectProfile } from "../project/project_discovery.mjs";
3
4
  import { inspectRehearsalBaseline, inspectRehearsalMigrations, } from "../runtime/plan.mjs";
4
5
  import { buildRuntimeStackPlan, runRuntimeStackDoctor, topologyCandidateSummary, } from "./runtime_commands.mjs";
5
6
  import { renderBaseline, renderCandidates, renderDoctor, renderMigrations, renderPlan, renderSupportReport, } from "./renderers.mjs";
6
7
  import { exactCommands, renderedCommand } from "./command_contract.mjs";
7
8
  import { inspectProjectOperationState } from "../shared/operation_guard.mjs";
8
9
  export const createInspectionCommandHandlers = () => [
10
+ exactCommands("discover", ["discover"], async ({ planOptions }) => {
11
+ const data = await discoverProjectProfile({
12
+ projectRoot: planOptions.projectRoot ?? process.cwd(),
13
+ });
14
+ return renderedCommand({
15
+ data,
16
+ render: (result) => [
17
+ `PROJECT PROFILE — READ ONLY`,
18
+ `Detected: ${result.detected.length}`,
19
+ `Suggested: ${result.suggested.length}`,
20
+ `Your decision: ${result.decisions.length}`,
21
+ `Configuration: ${result.configuration.state.replaceAll("_", " ")}`,
22
+ "",
23
+ "Run rehearsal discover --json for evidence and proposed configuration fields.",
24
+ ].join("\n"),
25
+ });
26
+ }),
9
27
  exactCommands("activity", ["activity"], async ({ planOptions }) => {
10
28
  const data = await inspectProjectOperationState({
11
29
  projectRoot: planOptions.projectRoot ?? process.cwd(),
@@ -80,6 +80,18 @@ export declare const buildRuntimeStackPlan: (planOptions: RehearsalConfigPathOpt
80
80
  open: "keep-until-stop" | "stop-on-application-exit";
81
81
  dataOnStop: string;
82
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
+ };
83
95
  execution: string[];
84
96
  guarantee: string;
85
97
  paths: {
@@ -88,6 +100,39 @@ export declare const buildRuntimeStackPlan: (planOptions: RehearsalConfigPathOpt
88
100
  sanitizationPolicy: string;
89
101
  };
90
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
+ };
91
136
  targets: {
92
137
  name: string;
93
138
  declaration: RuntimeTopologyTarget["declaration"];
@@ -36,7 +36,12 @@ export const buildRuntimeStackPlan = async (planOptions) => {
36
36
  });
37
37
  }
38
38
  const candidates = await inspectRuntimeTopologyCandidates(planOptions);
39
+ const primaryPlan = targets[0].plan;
39
40
  return {
41
+ config: primaryPlan.config,
42
+ environment: primaryPlan.environment,
43
+ application: primaryPlan.application,
44
+ cleanup: primaryPlan.cleanup,
40
45
  targets,
41
46
  migrations: {
42
47
  candidateCount: candidates.candidateCount,
@@ -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";
@@ -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";
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Build a read-only, explainable profile from bounded project metadata.
3
+ *
4
+ * Discovery never imports project modules, reads environment files, contacts a
5
+ * database, or starts a container engine. Suggestions are intentionally kept
6
+ * separate from facts and from choices that require a person.
7
+ */
8
+ import type { ContainerEngineProbe, ProjectDiscoveryResult } from "./project_discovery_contract.mjs";
9
+ export { PROJECT_DISCOVERY_SCHEMA_VERSION } from "./project_discovery_contract.mjs";
10
+ export type { ContainerEngineProbe, DecisionDiscoveryItem, DetectedDiscoveryItem, DiscoveredConfigurationField, DiscoveryConfidence, DiscoveryEvidence, DiscoveryValue, ProjectDiscoveryResult, SuggestedDiscoveryItem, } from "./project_discovery_contract.mjs";
11
+ export { probeContainerEngineWithoutStarting } from "./project_discovery_metadata.mjs";
12
+ export declare const discoverProjectProfile: ({ projectRoot, containerEngineProbe, }?: {
13
+ projectRoot?: string;
14
+ containerEngineProbe?: ContainerEngineProbe;
15
+ }) => Promise<ProjectDiscoveryResult>;