@rehearsal-db/core 0.1.0-beta.13 → 0.1.0-beta.15

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 (169) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/README.md +4 -1
  3. package/dist/package.json +100 -0
  4. package/dist/scripts/install/create_root_config.d.mts +3 -0
  5. package/dist/scripts/install/create_root_config.mjs +13 -0
  6. package/dist/scripts/runtime/manage_database.d.mts +6 -0
  7. package/{scripts → dist/scripts}/runtime/manage_database.mjs +6 -8
  8. package/dist/src/application/session.d.mts +96 -0
  9. package/dist/src/application/session.mjs +380 -0
  10. package/dist/src/baseline/artifact.d.mts +119 -0
  11. package/dist/src/baseline/artifact.mjs +522 -0
  12. package/dist/src/baseline/builder.d.mts +19 -0
  13. package/dist/src/baseline/builder.mjs +142 -0
  14. package/dist/src/baseline/input_discovery.d.mts +13 -0
  15. package/dist/src/baseline/input_discovery.mjs +149 -0
  16. package/dist/src/baseline/policy_review.d.mts +56 -0
  17. package/dist/src/baseline/policy_review.mjs +73 -0
  18. package/dist/src/baseline/preparation.d.mts +63 -0
  19. package/dist/src/baseline/preparation.mjs +295 -0
  20. package/dist/src/baseline/privacy_engine.d.mts +162 -0
  21. package/dist/src/baseline/privacy_engine.mjs +1162 -0
  22. package/dist/src/baseline/privacy_structured_json.d.mts +66 -0
  23. package/dist/src/baseline/privacy_structured_json.mjs +464 -0
  24. package/dist/src/baseline/sanitization_policy.d.mts +69 -0
  25. package/dist/src/baseline/sanitization_policy.mjs +227 -0
  26. package/dist/src/baseline/schema_snapshot.d.mts +19 -0
  27. package/dist/src/baseline/schema_snapshot.mjs +79 -0
  28. package/dist/src/cli/arguments.d.mts +43 -0
  29. package/dist/src/cli/arguments.mjs +135 -0
  30. package/dist/src/cli/guided.d.mts +51 -0
  31. package/dist/src/cli/guided.mjs +713 -0
  32. package/dist/src/cli/init_command.d.mts +21 -0
  33. package/dist/src/cli/init_command.mjs +90 -0
  34. package/dist/src/cli/project_commands.d.mts +157 -0
  35. package/dist/src/cli/project_commands.mjs +183 -0
  36. package/dist/src/cli/rehearsal.d.mts +7 -0
  37. package/dist/src/cli/rehearsal.mjs +706 -0
  38. package/dist/src/cli/renderers.d.mts +299 -0
  39. package/dist/src/cli/renderers.mjs +554 -0
  40. package/dist/src/cli/runtime_commands.d.mts +265 -0
  41. package/dist/src/cli/runtime_commands.mjs +599 -0
  42. package/dist/src/cli/source_commands.d.mts +432 -0
  43. package/dist/src/cli/source_commands.mjs +299 -0
  44. package/dist/src/cli/terminal.d.mts +59 -0
  45. package/dist/src/cli/terminal.mjs +230 -0
  46. package/dist/src/identity/claim.d.mts +144 -0
  47. package/dist/src/identity/claim.mjs +947 -0
  48. package/dist/src/identity/provider_configuration.d.mts +37 -0
  49. package/dist/src/identity/provider_configuration.mjs +132 -0
  50. package/dist/src/identity/storage.d.mts +21 -0
  51. package/dist/src/identity/storage.mjs +122 -0
  52. package/dist/src/index.d.mts +4 -0
  53. package/dist/src/index.mjs +3 -0
  54. package/dist/src/project/configuration.d.mts +181 -0
  55. package/dist/src/project/configuration.mjs +958 -0
  56. package/dist/src/project/install_scaffold.d.mts +42 -0
  57. package/dist/src/project/install_scaffold.mjs +263 -0
  58. package/dist/src/project/setup.d.mts +87 -0
  59. package/dist/src/project/setup.mjs +414 -0
  60. package/dist/src/project/support_report.d.mts +88 -0
  61. package/dist/src/project/support_report.mjs +83 -0
  62. package/dist/src/runtime/cleanup.d.mts +85 -0
  63. package/dist/src/runtime/cleanup.mjs +311 -0
  64. package/dist/src/runtime/migration_history.d.mts +51 -0
  65. package/dist/src/runtime/migration_history.mjs +170 -0
  66. package/dist/src/runtime/plan.d.mts +134 -0
  67. package/dist/src/runtime/plan.mjs +636 -0
  68. package/dist/src/runtime/policy.d.mts +66 -0
  69. package/dist/src/runtime/policy.mjs +369 -0
  70. package/dist/src/runtime/restore.d.mts +22 -0
  71. package/dist/src/runtime/restore.mjs +276 -0
  72. package/dist/src/runtime/service_environment.d.mts +11 -0
  73. package/dist/src/runtime/service_environment.mjs +58 -0
  74. package/dist/src/runtime/topology.d.mts +51 -0
  75. package/dist/src/runtime/topology.mjs +128 -0
  76. package/dist/src/shared/diagnostics.d.mts +97 -0
  77. package/dist/src/shared/diagnostics.mjs +170 -0
  78. package/dist/src/shared/human_output.d.mts +2 -0
  79. package/{src → dist/src}/shared/human_output.mjs +1 -3
  80. package/dist/src/shared/node_runtime.d.mts +14 -0
  81. package/dist/src/shared/node_runtime.mjs +25 -0
  82. package/dist/src/shared/operation_guard.d.mts +22 -0
  83. package/dist/src/shared/operation_guard.mjs +150 -0
  84. package/dist/src/shared/process_environment.d.mts +15 -0
  85. package/dist/src/shared/process_environment.mjs +50 -0
  86. package/dist/src/source/access.d.mts +165 -0
  87. package/dist/src/source/access.mjs +602 -0
  88. package/dist/src/source/asset_transfer.d.mts +27 -0
  89. package/dist/src/source/asset_transfer.mjs +202 -0
  90. package/dist/src/source/baseline.d.mts +66 -0
  91. package/dist/src/source/baseline.mjs +372 -0
  92. package/dist/src/source/external_reader.d.mts +42 -0
  93. package/dist/src/source/external_reader.mjs +278 -0
  94. package/dist/src/source/postgresql_access.d.mts +86 -0
  95. package/dist/src/source/postgresql_access.mjs +454 -0
  96. package/dist/src/source/privacy_paths.d.mts +10 -0
  97. package/dist/src/source/privacy_paths.mjs +27 -0
  98. package/dist/src/targets/driver.d.mts +2 -0
  99. package/dist/src/targets/driver.mjs +13 -0
  100. package/dist/src/targets/postgresql.d.mts +5 -0
  101. package/dist/src/targets/postgresql.mjs +641 -0
  102. package/dist/src/targets/supabase.d.mts +8 -0
  103. package/dist/src/targets/supabase.mjs +659 -0
  104. package/dist/src/targets/supabase_environment.d.mts +49 -0
  105. package/dist/src/targets/supabase_environment.mjs +162 -0
  106. package/dist/src/targets/target.d.mts +33 -0
  107. package/dist/src/targets/target.mjs +39 -0
  108. package/docs/README.md +1 -0
  109. package/docs/architecture.md +17 -9
  110. package/docs/configuration.md +49 -8
  111. package/docs/getting-started.md +28 -0
  112. package/docs/index.md +1 -0
  113. package/docs/production-source.md +43 -7
  114. package/docs/releasing.md +5 -3
  115. package/docs/roadmap.md +3 -2
  116. package/docs/sanitization.md +415 -14
  117. package/docs/standalone-workflow.md +12 -6
  118. package/docs/troubleshooting.md +5 -1
  119. package/docs/tutorial.md +5 -0
  120. package/docs/typescript.md +87 -0
  121. package/package.json +35 -29
  122. package/scripts/install/create_root_config.mjs +0 -36
  123. package/src/README.md +0 -17
  124. package/src/application/session.mjs +0 -435
  125. package/src/baseline/artifact.mjs +0 -684
  126. package/src/baseline/builder.mjs +0 -164
  127. package/src/baseline/input_discovery.mjs +0 -155
  128. package/src/baseline/policy_review.mjs +0 -92
  129. package/src/baseline/preparation.mjs +0 -323
  130. package/src/baseline/privacy_engine.mjs +0 -952
  131. package/src/baseline/sanitization_policy.mjs +0 -299
  132. package/src/baseline/schema_snapshot.mjs +0 -113
  133. package/src/cli/arguments.mjs +0 -126
  134. package/src/cli/guided.mjs +0 -814
  135. package/src/cli/init_command.mjs +0 -94
  136. package/src/cli/rehearsal.mjs +0 -970
  137. package/src/cli/renderers.mjs +0 -626
  138. package/src/cli/runtime_commands.mjs +0 -707
  139. package/src/cli/source_commands.mjs +0 -347
  140. package/src/cli/terminal.mjs +0 -275
  141. package/src/identity/claim.mjs +0 -1163
  142. package/src/identity/provider_configuration.mjs +0 -177
  143. package/src/identity/storage.mjs +0 -165
  144. package/src/project/configuration.mjs +0 -1337
  145. package/src/project/install_scaffold.mjs +0 -291
  146. package/src/project/setup.mjs +0 -466
  147. package/src/project/support_report.mjs +0 -105
  148. package/src/runtime/cleanup.mjs +0 -381
  149. package/src/runtime/migration_history.mjs +0 -220
  150. package/src/runtime/plan.mjs +0 -722
  151. package/src/runtime/policy.mjs +0 -438
  152. package/src/runtime/restore.mjs +0 -337
  153. package/src/runtime/service_environment.mjs +0 -82
  154. package/src/runtime/topology.mjs +0 -177
  155. package/src/shared/diagnostics.mjs +0 -207
  156. package/src/shared/node_runtime.mjs +0 -31
  157. package/src/shared/operation_guard.mjs +0 -162
  158. package/src/shared/process_environment.mjs +0 -68
  159. package/src/source/access.mjs +0 -753
  160. package/src/source/asset_transfer.mjs +0 -207
  161. package/src/source/baseline.mjs +0 -482
  162. package/src/source/external_reader.mjs +0 -255
  163. package/src/source/postgresql_access.mjs +0 -581
  164. package/src/source/privacy_paths.mjs +0 -38
  165. package/src/targets/postgresql.mjs +0 -808
  166. package/src/targets/supabase.mjs +0 -840
  167. package/src/targets/supabase_environment.mjs +0 -229
  168. package/src/targets/target.mjs +0 -66
  169. /package/{src/project → types}/configuration.d.mts +0 -0
@@ -1,323 +0,0 @@
1
- /**
2
- * Purpose: Inspect explicit local synthetic records and migration evidence, then
3
- * create a fail-closed sanitization-policy draft for human review.
4
- */
5
-
6
- import { createReadStream } from "node:fs";
7
- import { access, lstat, mkdir, open, readFile } from "node:fs/promises";
8
- import { createInterface } from "node:readline";
9
- import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
10
- import { loadRehearsalConfig } from "../project/configuration.mjs";
11
- import { buildMigrationLedgerInventory } from "../runtime/migration_history.mjs";
12
-
13
- const identifierPattern = /^[a-z][a-z0-9_]{0,62}$/u;
14
- const storageBucketPattern = /^[a-z0-9][a-z0-9.-]{0,99}$/u;
15
-
16
- const resolveProjectInput = (projectRoot, value, label) => {
17
- if (typeof value !== "string" || value.trim() === "") {
18
- throw new Error(`${label} is required.`);
19
- }
20
- const path = resolve(projectRoot, value);
21
- const owned = relative(projectRoot, path);
22
- if (
23
- owned === "" ||
24
- owned === ".." ||
25
- owned.startsWith(`..${sep}`) ||
26
- isAbsolute(owned)
27
- ) {
28
- throw new Error(`${label} must be a file inside the project root.`);
29
- }
30
- return path;
31
- };
32
-
33
- const pathExists = (path) =>
34
- access(path)
35
- .then(() => true)
36
- .catch((error) => {
37
- if (error?.code === "ENOENT") return false;
38
- throw error;
39
- });
40
-
41
- const assertRegularInput = async (path, label) => {
42
- let stats;
43
- try {
44
- stats = await lstat(path);
45
- } catch (error) {
46
- if (error?.code === "ENOENT") throw new Error(`${label} does not exist.`);
47
- throw error;
48
- }
49
- if (stats.isSymbolicLink() || !stats.isFile()) {
50
- throw new Error(`${label} must be a regular project-local file.`);
51
- }
52
- };
53
-
54
- const inspectSyntheticRecords = async (path) => {
55
- const tables = new Map();
56
- let rowCount = 0;
57
- const lines = createInterface({
58
- input: createReadStream(path, { encoding: "utf8" }),
59
- crlfDelay: Infinity,
60
- });
61
- for await (const line of lines) {
62
- if (!line.trim()) continue;
63
- let record;
64
- try {
65
- record = JSON.parse(line);
66
- } catch (error) {
67
- throw new Error("Synthetic baseline input contains invalid NDJSON.", {
68
- cause: error,
69
- });
70
- }
71
- if (
72
- !record ||
73
- !identifierPattern.test(record.schema ?? "public") ||
74
- !identifierPattern.test(record.table ?? "") ||
75
- !record.row ||
76
- typeof record.row !== "object" ||
77
- Array.isArray(record.row)
78
- ) {
79
- throw new Error("A synthetic baseline record has an invalid shape.");
80
- }
81
- const columns = Object.keys(record.row);
82
- if (
83
- columns.length === 0 ||
84
- columns.some((column) => !identifierPattern.test(column))
85
- ) {
86
- throw new Error(
87
- `Synthetic baseline table ${record.table} has invalid or empty columns.`,
88
- );
89
- }
90
- const schema = record.schema ?? "public";
91
- const key =
92
- schema === "public" ? record.table : `${schema}.${record.table}`;
93
- const table = tables.get(key) ?? {
94
- schema,
95
- name: record.table,
96
- rowCount: 0,
97
- columns: new Set(),
98
- };
99
- table.rowCount += 1;
100
- for (const column of columns) table.columns.add(column);
101
- tables.set(key, table);
102
- rowCount += 1;
103
- }
104
- if (rowCount === 0) {
105
- throw new Error("Synthetic baseline input contains no records.");
106
- }
107
- return {
108
- rowCount,
109
- tables: [...tables.values()]
110
- .sort((left, right) => left.name.localeCompare(right.name))
111
- .map((table) => ({
112
- schema: table.schema,
113
- name: table.name,
114
- rowCount: table.rowCount,
115
- columns: [...table.columns].sort(),
116
- })),
117
- };
118
- };
119
-
120
- const inspectAssetManifest = async (path, projectRoot) => {
121
- let manifest;
122
- try {
123
- manifest = JSON.parse(await readFile(path, "utf8"));
124
- } catch (error) {
125
- throw new Error("Storage asset manifest contains invalid JSON.", {
126
- cause: error,
127
- });
128
- }
129
- if (!Array.isArray(manifest)) {
130
- throw new Error("Storage asset manifest must be a JSON array.");
131
- }
132
- const destinations = new Set();
133
- for (const [index, asset] of manifest.entries()) {
134
- if (
135
- !asset ||
136
- typeof asset !== "object" ||
137
- Array.isArray(asset) ||
138
- !storageBucketPattern.test(asset.bucket ?? "") ||
139
- typeof asset.objectPath !== "string" ||
140
- !asset.objectPath ||
141
- asset.objectPath
142
- .split("/")
143
- .some((segment) => !segment || segment === "." || segment === "..") ||
144
- typeof asset.file !== "string" ||
145
- !asset.file.trim() ||
146
- (asset.contentType !== undefined && typeof asset.contentType !== "string")
147
- ) {
148
- throw new Error(
149
- `Storage asset manifest entry ${index + 1} has an invalid shape.`,
150
- );
151
- }
152
- const destination = `${asset.bucket}/${asset.objectPath}`;
153
- if (destinations.has(destination)) {
154
- throw new Error(`Duplicate Storage asset destination: ${destination}.`);
155
- }
156
- destinations.add(destination);
157
- const assetFile = resolveProjectInput(
158
- projectRoot,
159
- asset.file,
160
- `Storage asset file in entry ${index + 1}`,
161
- );
162
- await assertRegularInput(
163
- assetFile,
164
- `Storage asset file in entry ${index + 1}`,
165
- );
166
- }
167
- return { assetCount: manifest.length };
168
- };
169
-
170
- export const inspectBaselineInputFiles = async ({
171
- projectRoot = process.cwd(),
172
- configPath,
173
- recordsPath,
174
- ledgerPath,
175
- assetsPath,
176
- }) => {
177
- const loaded = await loadRehearsalConfig({ projectRoot, configPath });
178
- const records = resolveProjectInput(
179
- loaded.projectRoot,
180
- recordsPath,
181
- "Synthetic records path",
182
- );
183
- const ledger = resolveProjectInput(
184
- loaded.projectRoot,
185
- ledgerPath,
186
- "Migration ledger path",
187
- );
188
- const assets = assetsPath
189
- ? resolveProjectInput(
190
- loaded.projectRoot,
191
- assetsPath,
192
- "Storage asset manifest path",
193
- )
194
- : undefined;
195
- await Promise.all([
196
- assertRegularInput(records, "Synthetic records path"),
197
- assertRegularInput(ledger, "Migration ledger path"),
198
- ...(assets
199
- ? [assertRegularInput(assets, "Storage asset manifest path")]
200
- : []),
201
- ]);
202
- let ledgerRows;
203
- try {
204
- ledgerRows = JSON.parse(await readFile(ledger, "utf8"));
205
- } catch (error) {
206
- throw new Error("Migration ledger contains invalid JSON.", {
207
- cause: error,
208
- });
209
- }
210
- const [inspection, assetInspection] = await Promise.all([
211
- inspectSyntheticRecords(records),
212
- assets
213
- ? inspectAssetManifest(assets, loaded.projectRoot)
214
- : Promise.resolve({ assetCount: 0 }),
215
- ]);
216
- const migrationHistory = buildMigrationLedgerInventory(
217
- ledgerRows,
218
- "Synthetic migration ledger",
219
- );
220
- return {
221
- projectRoot: loaded.projectRoot,
222
- recordsPath: relative(loaded.projectRoot, records),
223
- ledgerPath: relative(loaded.projectRoot, ledger),
224
- assetsPath: assets ? relative(loaded.projectRoot, assets) : undefined,
225
- migrationHistory,
226
- migrationCutoff: migrationHistory.at(-1).version,
227
- migrationCount: migrationHistory.length,
228
- rowCount: inspection.rowCount,
229
- tables: inspection.tables,
230
- assetCount: assetInspection.assetCount,
231
- };
232
- };
233
-
234
- const renderPolicyDraft = ({ migrationCutoff, tables }) =>
235
- `${JSON.stringify(
236
- {
237
- policyVersion: 1,
238
- draft: true,
239
- migrationCutoff,
240
- tables: tables.map((table) => ({
241
- ...(table.schema === "public" ? {} : { schema: table.schema }),
242
- name: table.name,
243
- group: "synthetic",
244
- sourceRows: "STREAM AND SANITIZE",
245
- columns: table.columns.map((name) => ({
246
- name,
247
- action: "REVIEW REQUIRED",
248
- generated: "REVIEW REQUIRED",
249
- identity: "REVIEW REQUIRED",
250
- foreignKey: "REVIEW REQUIRED",
251
- })),
252
- })),
253
- },
254
- null,
255
- 2,
256
- )}\n`;
257
-
258
- export const planBaselinePreparation = async ({
259
- projectRoot = process.cwd(),
260
- configPath,
261
- recordsPath,
262
- ledgerPath,
263
- }) => {
264
- const loaded = await loadRehearsalConfig({ projectRoot, configPath });
265
- const inspection = await inspectBaselineInputFiles({
266
- projectRoot: loaded.projectRoot,
267
- configPath,
268
- recordsPath,
269
- ledgerPath,
270
- });
271
- const destination = loaded.paths.sanitizationPolicy;
272
- if (await pathExists(destination)) {
273
- throw new Error(
274
- `Rehearsal baseline preparation will not overwrite ${relative(loaded.projectRoot, destination)}.`,
275
- );
276
- }
277
- return {
278
- projectRoot: loaded.projectRoot,
279
- destination,
280
- destinationRelative: relative(loaded.projectRoot, destination),
281
- recordsPath: inspection.recordsPath,
282
- ledgerPath: inspection.ledgerPath,
283
- migrationCutoff: inspection.migrationCutoff,
284
- migrationCount: inspection.migrationCount,
285
- rowCount: inspection.rowCount,
286
- tables: inspection.tables,
287
- content: renderPolicyDraft({
288
- migrationCutoff: inspection.migrationCutoff,
289
- tables: inspection.tables,
290
- }),
291
- };
292
- };
293
-
294
- export const applyBaselinePreparation = async (plan) => {
295
- if (await pathExists(plan.destination)) {
296
- throw new Error(
297
- `Rehearsal baseline preparation will not overwrite ${plan.destinationRelative}.`,
298
- );
299
- }
300
- await mkdir(dirname(plan.destination), { recursive: true, mode: 0o700 });
301
- const handle = await open(plan.destination, "wx", 0o600);
302
- try {
303
- await handle.writeFile(plan.content);
304
- await handle.sync();
305
- } finally {
306
- await handle.close();
307
- }
308
- };
309
-
310
- export const summarizeBaselinePreparation = (plan, { mode }) => ({
311
- mode,
312
- destination: plan.destinationRelative,
313
- recordsPath: plan.recordsPath,
314
- ledgerPath: plan.ledgerPath,
315
- migrationCutoff: plan.migrationCutoff,
316
- migrationCount: plan.migrationCount,
317
- rowCount: plan.rowCount,
318
- tables: plan.tables,
319
- nextAction:
320
- mode === "written"
321
- ? `Review every REVIEW REQUIRED decision in ${plan.destinationRelative}, remove draft only after review, then create the baseline.`
322
- : "Review this schema-only summary, then rerun with --write to create the draft.",
323
- });