hazo_env 0.3.0 → 0.5.0
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/CHANGE_LOG.md +36 -0
- package/README.md +10 -6
- package/SETUP_CHECKLIST.md +6 -0
- package/config/hazo_env_config.ini.sample +18 -0
- package/dist/cli.js +56 -4
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +152 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/migrate/clear.d.ts +3 -0
- package/dist/migrate/clear.d.ts.map +1 -0
- package/dist/migrate/clear.js +117 -0
- package/dist/migrate/db.d.ts +4 -0
- package/dist/migrate/db.d.ts.map +1 -1
- package/dist/migrate/db.js +6 -1
- package/dist/migrate/db.postgrest.d.ts +4 -0
- package/dist/migrate/db.postgrest.d.ts.map +1 -0
- package/dist/migrate/db.postgrest.js +95 -0
- package/dist/migrate/files.d.ts +2 -1
- package/dist/migrate/files.d.ts.map +1 -1
- package/dist/migrate/files.js +85 -4
- package/dist/migrate/run.d.ts.map +1 -1
- package/dist/migrate/run.js +55 -15
- package/dist/migrate/snapshot.d.ts.map +1 -1
- package/dist/migrate/snapshot.js +3 -0
- package/dist/migrate/transport.d.ts +1 -1
- package/dist/migrate/transport.d.ts.map +1 -1
- package/dist/migrate/transport.js +2 -9
- package/dist/migrate/verify.d.ts +1 -0
- package/dist/migrate/verify.d.ts.map +1 -1
- package/dist/migrate/verify.js +120 -30
- package/dist/resolve/files.d.ts +6 -0
- package/dist/resolve/files.d.ts.map +1 -1
- package/dist/resolve/files.js +23 -0
- package/dist/resolve/migrate.d.ts +3 -0
- package/dist/resolve/migrate.d.ts.map +1 -0
- package/dist/resolve/migrate.js +29 -0
- package/dist/resolve/ssh.d.ts +15 -0
- package/dist/resolve/ssh.d.ts.map +1 -0
- package/dist/resolve/ssh.js +36 -0
- package/dist/types/index.d.ts +28 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +9 -9
package/CHANGE_LOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# hazo_env — Change Log
|
|
2
2
|
|
|
3
|
+
## 0.5.0 — 2026-06-25
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `clearEnv()` — delete all non-preserved tables via PostgREST (reverse FK order) + optional file-dir wipe. Exported from `src/index.ts`. CLI: `hazo-env clear <env>` (prod-target guard: `--allow-prod-target --confirm=<token>`).
|
|
7
|
+
- `ClearRequest` / `ClearResult` types in `src/types/index.ts`; `'clear'` added to `MigrationProgress.phase` union.
|
|
8
|
+
- rsync-over-SSH file transport — `copyFiles()` auto-dispatches to rsync when `[transport.ssh.<fromEnv>]` is configured. `copyFilesRsync()` in `src/migrate/files.ts` spawns `rsync -az --partial --info=progress2`, parses `to-chk=N/TOTAL` for live percent. `onProgress` signature extended with optional `percent` arg.
|
|
9
|
+
- `resolveSshConfig(env)` in `src/resolve/ssh.ts` — reads `[transport.ssh.<env>]` section (host/user/key/path) with `${VAR}` expansion.
|
|
10
|
+
- `resolveTransport()` return type widened to `'local' | 'api' | 'rsync'`; `'ssh'` input now returns `'rsync'` instead of throwing.
|
|
11
|
+
- Per-table DB progress — `DbCopyOptions.onTableProgress` callback fires once per table during PostgREST copy pass; `run.ts` maps it to the 20→70% band with a `"table N/M: <name>"` message.
|
|
12
|
+
- `[transport.ssh.<env>]` section added to `config/hazo_env_config.ini.sample`.
|
|
13
|
+
|
|
14
|
+
### Test-app
|
|
15
|
+
- `POST /api/clear` route added (mirrors `/api/migrate`).
|
|
16
|
+
- `'clear-env'` autotest scenario added (dry-run guard: verifies prod-target refuses without confirm).
|
|
17
|
+
|
|
18
|
+
## 0.4.0 — 2026-06-25
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
- `verifyFiles()` L2–L5 — DB-backed file existence (L2), size check (L3), hash check L4 (`full|sample|none`; sample = 1% + top-10 by size + top-10 by changed_at), orphan detection L5. Previously all layers were stubs.
|
|
22
|
+
- `VerifyOptions.adapter` — pass a `HazoConnectAdapter` to enable DB-backed checks; without it verify falls back to sentinel-only (disk) and sets `report.skippedReason`.
|
|
23
|
+
- `VerifyReport` — new fields: `hashed?: number`, `skippedReason?: string`.
|
|
24
|
+
- `runMigration` — step 7 verify now passes `tgtAdapter`; emits detailed warning on `!ok` (counts + snapshotId) but continues (G2: warns-not-aborts).
|
|
25
|
+
- CLI `hazo-env verify` — new `--hash full|sample|none` flag (default `sample`); exits 1 on failure (CI-safe).
|
|
26
|
+
- `doctor()` — section 7a: `_migrations` parity check across all SQLite envs (`--probe --all`); section 7b: masking ruleset column validation against live schema.
|
|
27
|
+
- `DoctorOptions.probe` + `DoctorOptions.all` — enable live DB reachability probes + multi-env checks.
|
|
28
|
+
- `hazo_files` added as optional peer dependency (`^3.5.0`).
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
- `DoctorReport.passed` replaces `DoctorReport.ok` (previously typo in docs).
|
|
32
|
+
- `DoctorCheck.status` is `'ok'|'warn'|'error'` (not `boolean`).
|
|
33
|
+
|
|
34
|
+
### Test-app
|
|
35
|
+
- Seed script creates 7 `hazo_files` fixture rows (3 clean, 1 size-mismatch, 1 hash-mismatch, 1 missing, 1 orphan) + physical files under `data/test/files/`.
|
|
36
|
+
- `/api/verify` route + `/verify` report page added.
|
|
37
|
+
- `'verify-fixtures'` autotest scenario added (5 cases).
|
|
38
|
+
|
|
3
39
|
## 0.3.0 — 2026-06-10
|
|
4
40
|
|
|
5
41
|
### Added
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ Canonical environment resolver for hazo apps. Typed env names, per-env DB/file/s
|
|
|
10
10
|
- **Secrets** — `getSecret()` resolves from `.env.local` only; placeholders in `hazo_env_config.ini` are substituted at runtime without storing secrets.
|
|
11
11
|
- **Migration engine** — `runMigration({ from, to })` copies a DB + files between envs. Validates → snapshots target → copies tables (paged, schema-checked) → copies files → verifies → writes hazo_audit entry. Prod target refused without an explicit confirm token.
|
|
12
12
|
- **PII masking** — masking rules declared per table/column in `hazo_env_masking.ini` (seed) and synced to `hazo_app_config` at runtime. Built-in transforms: `mask_email`, `fake_name`, `mask_phone`, `jitter_date`, `hash`, `tokenize`, `drop`, `nullify`. Custom transforms via `registerMask()`. Applied automatically when the migration target role is `test` or `staging`.
|
|
13
|
-
- **Doctor** — `doctor()` / `hazo-env doctor` validates pattern, DB reachability, required secrets (no values printed), data_root writability, and
|
|
13
|
+
- **Doctor** — `doctor()` / `hazo-env doctor` validates pattern, DB reachability, required secrets (no values printed), data_root writability, schema level, and (with `--probe --all`) migration parity across envs + masking ruleset column validation.
|
|
14
14
|
- **CLI** — `hazo-env current | doctor | snapshot | migrate | verify | restore | mask`.
|
|
15
15
|
|
|
16
16
|
## Installation
|
|
@@ -147,9 +147,11 @@ getSecret('POSTGREST_API_KEY', { required: true })
|
|
|
147
147
|
```ts
|
|
148
148
|
import { doctor } from 'hazo_env';
|
|
149
149
|
|
|
150
|
-
const report = await doctor();
|
|
151
|
-
// report.
|
|
152
|
-
// report.checks DoctorCheck[] — { label, ok,
|
|
150
|
+
const report = await doctor({ probe: true, all: true });
|
|
151
|
+
// report.passed boolean
|
|
152
|
+
// report.checks DoctorCheck[] — { label, status: 'ok'|'warn'|'error', detail? }
|
|
153
|
+
// Migration-readiness checks (--probe --all): _migrations parity across envs,
|
|
154
|
+
// masking ruleset column validation against live schema.
|
|
153
155
|
```
|
|
154
156
|
|
|
155
157
|
### Migration engine (server-only)
|
|
@@ -171,8 +173,10 @@ const result = await runMigration({
|
|
|
171
173
|
});
|
|
172
174
|
// result.ok, result.db, result.files, result.snapshotId, result.warnings, result.durationMs
|
|
173
175
|
|
|
174
|
-
// Stand-alone verification
|
|
175
|
-
await verifyFiles('staging', dataRoot, { hash: 'sample' });
|
|
176
|
+
// Stand-alone verification (with optional hazo_connect adapter for L2–L5 checks)
|
|
177
|
+
await verifyFiles('staging', dataRoot, { hash: 'sample', checkOrphans: true, adapter });
|
|
178
|
+
// report.ok, report.checked, report.hashed, report.missing[], report.sizeMismatch[],
|
|
179
|
+
// report.hashMismatch[], report.orphans[], report.skippedReason?
|
|
176
180
|
|
|
177
181
|
// Manual snapshot / restore
|
|
178
182
|
const snap = takeSnapshot(connectConfig); // returns { snapshotId }
|
package/SETUP_CHECKLIST.md
CHANGED
|
@@ -94,6 +94,12 @@ All checks should be green before going to production. The doctor validates:
|
|
|
94
94
|
- All required secrets are present in `.env.local` (values are never printed)
|
|
95
95
|
- `data_root` exists and is writable
|
|
96
96
|
|
|
97
|
+
Add `--probe --all` for migration-readiness checks:
|
|
98
|
+
```bash
|
|
99
|
+
npx hazo-env doctor --probe --all
|
|
100
|
+
```
|
|
101
|
+
This additionally checks: live DB reachability for all envs, `_migrations` table parity across envs, masking ruleset column validation against live schema.
|
|
102
|
+
|
|
97
103
|
## 8. (Next.js) Ensure `hazo_env` is in `transpilePackages`
|
|
98
104
|
|
|
99
105
|
```js
|
|
@@ -62,3 +62,21 @@ location = remote
|
|
|
62
62
|
|
|
63
63
|
[host.prod]
|
|
64
64
|
location = remote
|
|
65
|
+
|
|
66
|
+
; ─── SSH transport for cross-host file rsync ─────────────────────────────────
|
|
67
|
+
; When [transport.ssh.<env>] is present, copyFiles will use rsync-over-SSH
|
|
68
|
+
; to pull files from that environment's file server instead of local copy.
|
|
69
|
+
; This enables prod→dev migration when both envs run on separate hosts.
|
|
70
|
+
|
|
71
|
+
[transport.ssh.prod]
|
|
72
|
+
; SSH connection to the prod file server (used when pulling prod→dev).
|
|
73
|
+
; The worker runs rsync FROM prod TO dev — prod stays read-only.
|
|
74
|
+
host = ${SSH_HOST_PROD}
|
|
75
|
+
user = ${SSH_USER_PROD}
|
|
76
|
+
key = ${SSH_KEY_PATH_PROD}
|
|
77
|
+
path = /home/pubs/kinstripe/var/uploads
|
|
78
|
+
; Optional: pin the server's host key (output of `ssh-keyscan <host>` minus the
|
|
79
|
+
; leading hostname, e.g. "ssh-ed25519 AAAA..."). When set, host-key checking is
|
|
80
|
+
; strict and pinned — no trust-on-first-use. When omitted, the first connection
|
|
81
|
+
; is accepted (accept-new) and persisted to config/hazo_env_known_hosts.
|
|
82
|
+
; host_key = ${SSH_HOST_KEY_PROD}
|
package/dist/cli.js
CHANGED
|
@@ -122,12 +122,22 @@ async function runMigrate() {
|
|
|
122
122
|
}
|
|
123
123
|
async function runVerify() {
|
|
124
124
|
const targetEnv = args[0] ?? getEnv();
|
|
125
|
-
|
|
125
|
+
// Parse --hash full|sample|none (default: sample) and --no-orphans
|
|
126
|
+
const hashIdx = args.indexOf('--hash');
|
|
127
|
+
const hashArg = hashIdx >= 0 ? args[hashIdx + 1] : 'sample';
|
|
128
|
+
const hash = (hashArg === 'full' || hashArg === 'none' || hashArg === 'sample') ? hashArg : 'sample';
|
|
129
|
+
const checkOrphans = !args.includes('--no-orphans');
|
|
130
|
+
console.log(`\n${pc.bold('hazo-env verify')} ${pc.dim(targetEnv)} ${pc.dim(`--hash ${hash}`)}\n`);
|
|
126
131
|
const { verifyFiles } = await import('./migrate/verify.js');
|
|
127
132
|
const { resolveFilesConfig } = await import('./resolve/files.js');
|
|
128
133
|
const dataRoot = resolveFilesConfig().local.basePath;
|
|
129
|
-
const report = await verifyFiles(targetEnv, dataRoot, { hash
|
|
130
|
-
|
|
134
|
+
const report = await verifyFiles(targetEnv, dataRoot, { hash, checkOrphans });
|
|
135
|
+
if (report.skippedReason) {
|
|
136
|
+
console.log(` ${pc.yellow('⚠')} ${report.skippedReason}`);
|
|
137
|
+
}
|
|
138
|
+
console.log(` Checked: ${report.checked} files${report.hashed != null ? `, hashed: ${report.hashed}` : ''}`);
|
|
139
|
+
if (report.orphans.length)
|
|
140
|
+
console.log(` ${pc.dim('ℹ')} Orphans (no DB row): ${report.orphans.join(', ')}`);
|
|
131
141
|
if (report.ok) {
|
|
132
142
|
console.log(` ${pc.green('✓')} All files verified\n`);
|
|
133
143
|
}
|
|
@@ -136,8 +146,10 @@ async function runVerify() {
|
|
|
136
146
|
console.log(` ${pc.red('✗')} Missing: ${report.missing.join(', ')}`);
|
|
137
147
|
if (report.sizeMismatch.length)
|
|
138
148
|
console.log(` ${pc.yellow('⚠')} Size mismatch: ${report.sizeMismatch.join(', ')}`);
|
|
149
|
+
if (report.hashMismatch.length)
|
|
150
|
+
console.log(` ${pc.yellow('⚠')} Hash mismatch: ${report.hashMismatch.join(', ')}`);
|
|
139
151
|
console.log('');
|
|
140
|
-
process.exit(1);
|
|
152
|
+
process.exit(1); // S4: standalone verify exits 1 on failure (CI-friendly)
|
|
141
153
|
}
|
|
142
154
|
}
|
|
143
155
|
async function runRestore() {
|
|
@@ -155,6 +167,39 @@ async function runRestore() {
|
|
|
155
167
|
restoreSnapshot(toConfig, snapshotId);
|
|
156
168
|
console.log(` ${pc.green('✓')} Restored\n`);
|
|
157
169
|
}
|
|
170
|
+
async function runClear() {
|
|
171
|
+
const targetEnv = args[0] ?? getEnv();
|
|
172
|
+
const allowProd = args.includes('--allow-prod-target');
|
|
173
|
+
const confirm = args.find((a) => a.startsWith('--confirm='))?.split('=')[1];
|
|
174
|
+
console.log(`\n${pc.bold('hazo-env clear')} ${pc.dim(targetEnv)}\n`);
|
|
175
|
+
const { clearEnv } = await import('./migrate/clear.js');
|
|
176
|
+
try {
|
|
177
|
+
let lastMsg = '';
|
|
178
|
+
const result = await clearEnv({
|
|
179
|
+
env: targetEnv,
|
|
180
|
+
allowProdTarget: allowProd,
|
|
181
|
+
confirmToken: confirm,
|
|
182
|
+
onProgress: (p) => {
|
|
183
|
+
if (p.message !== lastMsg) {
|
|
184
|
+
const pctStr = p.percent != null ? ` [${p.percent}%]` : '';
|
|
185
|
+
console.log(` ${pc.dim('·')} ${p.message}${pctStr}`);
|
|
186
|
+
lastMsg = p.message;
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
if (result.ok) {
|
|
191
|
+
console.log(` ${pc.green('✓')} Cleared ${result.tablesCleared} tables${result.filesCleared ? ' + files' : ''} in ${result.durationMs}ms\n`);
|
|
192
|
+
if (result.warnings.length)
|
|
193
|
+
result.warnings.forEach((w) => console.log(` ${pc.yellow('⚠')} ${w}`));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
catch (e) {
|
|
197
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
198
|
+
console.error(`\n ${pc.red('✗')} ${msg}\n`);
|
|
199
|
+
process.exit(1);
|
|
200
|
+
}
|
|
201
|
+
console.log('');
|
|
202
|
+
}
|
|
158
203
|
async function runMask() {
|
|
159
204
|
const subCmd = args[0];
|
|
160
205
|
if (subCmd === 'sync') {
|
|
@@ -221,10 +266,14 @@ Usage:
|
|
|
221
266
|
--no-db / --no-files Skip DB or file copy
|
|
222
267
|
--allow-prod-target --confirm <token> Override prod-target safety
|
|
223
268
|
hazo-env verify <env> Verify files for an environment
|
|
269
|
+
--hash full|sample|none Hash-check all / sampled / none (default: sample)
|
|
270
|
+
--no-orphans Skip orphan detection
|
|
224
271
|
hazo-env restore <env> --snapshot <id> Restore a snapshot
|
|
225
272
|
hazo-env snapshot <env> Take a snapshot of an environment
|
|
226
273
|
hazo-env mask sync Load masking ruleset from INI into DB
|
|
227
274
|
hazo-env mask list List registered mask transforms
|
|
275
|
+
hazo-env clear <env> Wipe DB tables + files for an environment
|
|
276
|
+
--allow-prod-target --confirm=<token> Override prod-target safety
|
|
228
277
|
`);
|
|
229
278
|
}
|
|
230
279
|
(async () => {
|
|
@@ -250,6 +299,9 @@ Usage:
|
|
|
250
299
|
else if (command === 'mask') {
|
|
251
300
|
await runMask();
|
|
252
301
|
}
|
|
302
|
+
else if (command === 'clear') {
|
|
303
|
+
await runClear();
|
|
304
|
+
}
|
|
253
305
|
else {
|
|
254
306
|
printHelp();
|
|
255
307
|
}
|
package/dist/doctor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,IAAI,GAAG,MAAM,GAAG,OAAO,CAAC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,kEAAkE;IAClE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAMD;;;GAGG;AACH,wBAAsB,MAAM,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,IAAI,GAAG,MAAM,GAAG,OAAO,CAAC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,kEAAkE;IAClE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAMD;;;GAGG;AACH,wBAAsB,MAAM,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CA8R5E"}
|
package/dist/doctor.js
CHANGED
|
@@ -166,6 +166,158 @@ export async function doctor(opts = {}) {
|
|
|
166
166
|
});
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
|
+
// 7. Migration readiness checks
|
|
170
|
+
// 7a. _migrations parity (probe mode, --all): open each SQLite env and compare name-sets
|
|
171
|
+
if (opts.probe && opts.all) {
|
|
172
|
+
const migSets = new Map();
|
|
173
|
+
for (const env of envsToCheck) {
|
|
174
|
+
let cfg;
|
|
175
|
+
try {
|
|
176
|
+
cfg = resolveConnectConfig({ env, allowOtherEnv: true });
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (cfg.type !== 'sqlite' || !cfg.sqlite)
|
|
182
|
+
continue;
|
|
183
|
+
try {
|
|
184
|
+
const Database = require('better-sqlite3');
|
|
185
|
+
const db = Database(cfg.sqlite.database_path);
|
|
186
|
+
let names;
|
|
187
|
+
try {
|
|
188
|
+
names = db.prepare('SELECT name FROM _migrations').all().map((r) => r.name);
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
names = [];
|
|
192
|
+
}
|
|
193
|
+
db.close();
|
|
194
|
+
migSets.set(env, new Set(names));
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
// DB not openable — skip (probe check already reported error)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (migSets.size >= 2) {
|
|
201
|
+
const envList = [...migSets.keys()];
|
|
202
|
+
const referenceSet = migSets.get(envList[0]);
|
|
203
|
+
const referenceEnv = envList[0];
|
|
204
|
+
for (const env of envList.slice(1)) {
|
|
205
|
+
const s = migSets.get(env);
|
|
206
|
+
const onlyInRef = [...referenceSet].filter((n) => !s.has(n));
|
|
207
|
+
const onlyInEnv = [...s].filter((n) => !referenceSet.has(n));
|
|
208
|
+
if (onlyInRef.length === 0 && onlyInEnv.length === 0) {
|
|
209
|
+
checks.push({ label: `Migration parity (${referenceEnv} ↔ ${env})`, status: 'ok', detail: `${s.size} applied` });
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
const detail = [
|
|
213
|
+
onlyInRef.length ? `only in ${referenceEnv}: ${onlyInRef.join(', ')}` : '',
|
|
214
|
+
onlyInEnv.length ? `only in ${env}: ${onlyInEnv.join(', ')}` : '',
|
|
215
|
+
].filter(Boolean).join('; ');
|
|
216
|
+
checks.push({ label: `Migration parity (${referenceEnv} ↔ ${env})`, status: 'warn', detail });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// 7b. Masking ruleset column validation: every (table, column) in the masking INI must
|
|
222
|
+
// exist in at least one SQLite env's schema. Skip silently for PostgREST envs.
|
|
223
|
+
const maskingConfigPath = path.resolve(process.cwd(), 'config', 'hazo_env_masking.ini');
|
|
224
|
+
if (fs.existsSync(maskingConfigPath)) {
|
|
225
|
+
try {
|
|
226
|
+
// Parse the masking INI directly — HazoConfig doesn't expose section enumeration.
|
|
227
|
+
// Format: each [section] is a table name; keys are column names.
|
|
228
|
+
const iniContent = fs.readFileSync(maskingConfigPath, 'utf-8');
|
|
229
|
+
const rules = [];
|
|
230
|
+
let currentTable = '';
|
|
231
|
+
for (const rawLine of iniContent.split('\n')) {
|
|
232
|
+
const line = rawLine.trim();
|
|
233
|
+
if (!line || line.startsWith(';') || line.startsWith('#'))
|
|
234
|
+
continue;
|
|
235
|
+
const sectionMatch = line.match(/^\[([^\]]+)\]$/);
|
|
236
|
+
if (sectionMatch) {
|
|
237
|
+
currentTable = sectionMatch[1].trim();
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
if (!currentTable)
|
|
241
|
+
continue;
|
|
242
|
+
const eqIdx = line.indexOf('=');
|
|
243
|
+
if (eqIdx < 0)
|
|
244
|
+
continue;
|
|
245
|
+
const column = line.slice(0, eqIdx).trim();
|
|
246
|
+
if (column)
|
|
247
|
+
rules.push({ table: currentTable, column });
|
|
248
|
+
}
|
|
249
|
+
if (rules.length > 0) {
|
|
250
|
+
// Collect SQLite schemas for checked envs
|
|
251
|
+
const schemas = new Map(); // "table.column" → present
|
|
252
|
+
for (const env of envsToCheck) {
|
|
253
|
+
let cfg;
|
|
254
|
+
try {
|
|
255
|
+
cfg = resolveConnectConfig({ env, allowOtherEnv: true });
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
if (cfg.type !== 'sqlite' || !cfg.sqlite)
|
|
261
|
+
continue;
|
|
262
|
+
try {
|
|
263
|
+
const Database = require('better-sqlite3');
|
|
264
|
+
const db = Database(cfg.sqlite.database_path);
|
|
265
|
+
try {
|
|
266
|
+
const tables = db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all().map((r) => r.name);
|
|
267
|
+
for (const tbl of tables) {
|
|
268
|
+
const cols = db.prepare(`PRAGMA table_info(${tbl})`).all();
|
|
269
|
+
for (const col of cols) {
|
|
270
|
+
schemas.set(`${tbl}.${col.name}`, new Set());
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
catch { /* DB empty or unreadable */ }
|
|
275
|
+
db.close();
|
|
276
|
+
}
|
|
277
|
+
catch { /* DB not openable */ }
|
|
278
|
+
}
|
|
279
|
+
if (schemas.size > 0) {
|
|
280
|
+
const badRules = [];
|
|
281
|
+
for (const { table, column } of rules) {
|
|
282
|
+
if (!schemas.has(`${table}.${column}`)) {
|
|
283
|
+
badRules.push(`${table}.${column}`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
if (badRules.length === 0) {
|
|
287
|
+
checks.push({ label: 'Masking ruleset columns', status: 'ok', detail: `${rules.length} rule(s) validated` });
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
checks.push({
|
|
291
|
+
label: 'Masking ruleset columns',
|
|
292
|
+
status: 'warn',
|
|
293
|
+
detail: `Column(s) not found in schema: ${badRules.join(', ')} (PostgREST envs skipped)`,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
// No SQLite envs probed — can't validate columns; skip silently
|
|
299
|
+
const hasPostgrest = envsToCheck.some((env) => {
|
|
300
|
+
try {
|
|
301
|
+
return resolveConnectConfig({ env, allowOtherEnv: true }).type === 'postgrest';
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
if (hasPostgrest) {
|
|
308
|
+
checks.push({
|
|
309
|
+
label: 'Masking ruleset columns',
|
|
310
|
+
status: 'ok',
|
|
311
|
+
detail: 'PostgREST env — column validation skipped',
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
catch {
|
|
318
|
+
// Masking config parse failure — non-fatal, skip
|
|
319
|
+
}
|
|
320
|
+
}
|
|
169
321
|
const passed = checks.every((c) => c.status !== 'error');
|
|
170
322
|
return { env: targetEnv, checks, passed };
|
|
171
323
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export * from './resolve/connect.js';
|
|
|
6
6
|
export * from './resolve/files.js';
|
|
7
7
|
export * from './doctor.js';
|
|
8
8
|
export { runMigration } from './migrate/run.js';
|
|
9
|
+
export { clearEnv } from './migrate/clear.js';
|
|
9
10
|
export { verifyFiles } from './migrate/verify.js';
|
|
10
11
|
export { takeSnapshot, restoreSnapshot } from './migrate/snapshot.js';
|
|
11
12
|
export { writeMigrationProgress, readMigrationProgress, clearMigrationProgress } from './migrate/progress.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE7E,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEvG,cAAc,kBAAkB,CAAC;AAEjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AAEnC,cAAc,aAAa,CAAC;AAE5B,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAE9G,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACnF,YAAY,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,cAAc,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE7E,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEvG,cAAc,kBAAkB,CAAC;AAEjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AAEnC,cAAc,aAAa,CAAC;AAE5B,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAE9G,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACnF,YAAY,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,cAAc,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,7 @@ export * from './resolve/files.js';
|
|
|
13
13
|
export * from './doctor.js';
|
|
14
14
|
// Migration engine (Phase 2)
|
|
15
15
|
export { runMigration } from './migrate/run.js';
|
|
16
|
+
export { clearEnv } from './migrate/clear.js';
|
|
16
17
|
export { verifyFiles } from './migrate/verify.js';
|
|
17
18
|
export { takeSnapshot, restoreSnapshot } from './migrate/snapshot.js';
|
|
18
19
|
export { writeMigrationProgress, readMigrationProgress, clearMigrationProgress } from './migrate/progress.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clear.d.ts","sourceRoot":"","sources":["../../src/migrate/clear.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAqB,MAAM,mBAAmB,CAAC;AAYtF,wBAAsB,QAAQ,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAoHtE"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// hazo_env/src/migrate/clear.ts — clearEnv: wipe DB tables + files for a target environment
|
|
2
|
+
import { HazoError, optional_import } from 'hazo_core';
|
|
3
|
+
import { getEnvRole } from '../env.server.js';
|
|
4
|
+
import { resolveMigrateConfig } from '../resolve/migrate.js';
|
|
5
|
+
import { resolveConnectConfig } from '../resolve/connect.js';
|
|
6
|
+
import { resolveFilesRoot } from '../resolve/files.js';
|
|
7
|
+
import { writeMigrationProgress } from './progress.js';
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
function emitProgress(req, p) {
|
|
10
|
+
req.onProgress?.(p);
|
|
11
|
+
if (req.jobId && req.progressDir) {
|
|
12
|
+
writeMigrationProgress(req.progressDir, req.jobId, p);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export async function clearEnv(req) {
|
|
16
|
+
const startMs = Date.now();
|
|
17
|
+
const warnings = [];
|
|
18
|
+
// Guard: refuse production target unless explicitly unlocked with a confirm token
|
|
19
|
+
const role = getEnvRole(req.env);
|
|
20
|
+
if (role === 'production' && !req.allowProdTarget) {
|
|
21
|
+
throw new HazoError({
|
|
22
|
+
code: 'HAZO_ENV_PROD_TARGET_REFUSED',
|
|
23
|
+
pkg: 'hazo_env',
|
|
24
|
+
message: `Refusing to clear production environment "${req.env}". Pass allowProdTarget:true and a confirmToken to override.`,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
if (role === 'production' && req.allowProdTarget && !req.confirmToken) {
|
|
28
|
+
throw new HazoError({
|
|
29
|
+
code: 'HAZO_ENV_PROD_TARGET_REFUSED',
|
|
30
|
+
pkg: 'hazo_env',
|
|
31
|
+
message: 'Production target requires a confirmToken for safety.',
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
emitProgress(req, { phase: 'validate', message: 'Validating clear request', percent: 0 });
|
|
35
|
+
const migrateConfig = resolveMigrateConfig();
|
|
36
|
+
const { tables, preserve = [], pkOverrides = {} } = migrateConfig;
|
|
37
|
+
if (!tables || tables.length === 0) {
|
|
38
|
+
throw new HazoError({
|
|
39
|
+
code: 'HAZO_ENV_MISSING_TABLE_LIST',
|
|
40
|
+
pkg: 'hazo_env',
|
|
41
|
+
message: 'clearEnv requires an explicit table list. ' +
|
|
42
|
+
'Set [migrate] tables = ... in hazo_env_config.ini.',
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
const tablesToClear = [...tables.filter((t) => !preserve.includes(t))].reverse();
|
|
46
|
+
const dbConfig = resolveConnectConfig({ env: req.env, allowOtherEnv: true });
|
|
47
|
+
if (dbConfig.type !== 'postgrest') {
|
|
48
|
+
throw new HazoError({
|
|
49
|
+
code: 'HAZO_ENV_NOT_IMPLEMENTED',
|
|
50
|
+
pkg: 'hazo_env',
|
|
51
|
+
message: `clearEnv only supports PostgREST connections (got "${dbConfig.type}").`,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
if (!dbConfig.postgrest) {
|
|
55
|
+
throw new HazoError({
|
|
56
|
+
code: 'HAZO_ENV_NOT_IMPLEMENTED',
|
|
57
|
+
pkg: 'hazo_env',
|
|
58
|
+
message: 'PostgREST config missing base_url/api_key.',
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
const connect = await optional_import('hazo_connect/server');
|
|
62
|
+
if (!connect) {
|
|
63
|
+
throw new HazoError({
|
|
64
|
+
code: 'HAZO_ENV_MISSING_DEPENDENCY',
|
|
65
|
+
pkg: 'hazo_env',
|
|
66
|
+
message: 'hazo_connect is required for clearEnv. Install hazo_connect to continue.',
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
const tgtAdapter = connect.createHazoConnect({
|
|
70
|
+
type: 'postgrest',
|
|
71
|
+
postgrest: {
|
|
72
|
+
base_url: dbConfig.postgrest.base_url,
|
|
73
|
+
api_key: dbConfig.postgrest.api_key,
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
// DB clear (only if not explicitly disabled)
|
|
77
|
+
let tablesCleared = 0;
|
|
78
|
+
if (req.include?.db !== false) {
|
|
79
|
+
for (let idx = 0; idx < tablesToClear.length; idx++) {
|
|
80
|
+
const table = tablesToClear[idx];
|
|
81
|
+
const percent = Math.round(((idx + 1) / tablesToClear.length) * 90);
|
|
82
|
+
emitProgress(req, {
|
|
83
|
+
phase: 'clear',
|
|
84
|
+
message: `Clearing table ${idx + 1}/${tablesToClear.length}: ${table}`,
|
|
85
|
+
percent,
|
|
86
|
+
});
|
|
87
|
+
const pk = pkOverrides[table] ?? 'id';
|
|
88
|
+
try {
|
|
89
|
+
await tgtAdapter.rawQuery(`/${table}?${pk}=not.is.null`, { method: 'DELETE' });
|
|
90
|
+
tablesCleared++;
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
94
|
+
warnings.push(`Warning: could not clear ${table}: ${msg}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// File clear
|
|
99
|
+
let filesCleared = false;
|
|
100
|
+
if (req.include?.files !== false) {
|
|
101
|
+
emitProgress(req, { phase: 'clear', message: 'Clearing files...', percent: 92 });
|
|
102
|
+
const filesRoot = resolveFilesRoot(req.env);
|
|
103
|
+
if (fs.existsSync(filesRoot)) {
|
|
104
|
+
fs.rmSync(filesRoot, { recursive: true, force: true });
|
|
105
|
+
}
|
|
106
|
+
fs.mkdirSync(filesRoot, { recursive: true });
|
|
107
|
+
filesCleared = true;
|
|
108
|
+
}
|
|
109
|
+
emitProgress(req, { phase: 'done', message: 'Clear complete', percent: 100 });
|
|
110
|
+
return {
|
|
111
|
+
ok: true,
|
|
112
|
+
tablesCleared,
|
|
113
|
+
filesCleared,
|
|
114
|
+
durationMs: Date.now() - startMs,
|
|
115
|
+
warnings,
|
|
116
|
+
};
|
|
117
|
+
}
|
package/dist/migrate/db.d.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import type { HazoConnectAdapter } from 'hazo_connect';
|
|
2
2
|
export interface DbCopyOptions {
|
|
3
|
+
type?: 'sqlite' | 'postgrest';
|
|
3
4
|
tables?: '*' | string[];
|
|
5
|
+
preserve?: string[];
|
|
6
|
+
pkOverrides?: Record<string, string>;
|
|
4
7
|
scrubHook?: (tableName: string, row: Record<string, unknown>) => Record<string, unknown>;
|
|
5
8
|
onProgress?: (msg: string) => void;
|
|
9
|
+
onTableProgress?: (tableIdx: number, totalTables: number, tableName: string) => void;
|
|
6
10
|
}
|
|
7
11
|
export interface DbCopyResult {
|
|
8
12
|
tables: number;
|
package/dist/migrate/db.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/migrate/db.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,GAAG,GAAG,MAAM,EAAE,CAAC;IACxB,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzF,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/migrate/db.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;IAC9B,MAAM,CAAC,EAAE,GAAG,GAAG,MAAM,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzF,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CACtF;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AA6BD,wBAAsB,MAAM,CAC1B,UAAU,EAAE,kBAAkB,EAC9B,UAAU,EAAE,kBAAkB,EAC9B,IAAI,GAAE,aAAkB,GACvB,OAAO,CAAC,YAAY,CAAC,CAqHvB"}
|
package/dist/migrate/db.js
CHANGED
|
@@ -21,6 +21,11 @@ async function getMigrationNames(adapter) {
|
|
|
21
21
|
return rows.map((r) => r.name);
|
|
22
22
|
}
|
|
23
23
|
export async function copyDb(srcAdapter, tgtAdapter, opts = {}) {
|
|
24
|
+
// PostgREST path: dispatch to dedicated implementation
|
|
25
|
+
if (opts.type === 'postgrest') {
|
|
26
|
+
const { copyDbPostgrest } = await import('./db.postgrest.js');
|
|
27
|
+
return copyDbPostgrest(srcAdapter, tgtAdapter, opts);
|
|
28
|
+
}
|
|
24
29
|
const connect = await optional_import('hazo_connect/server');
|
|
25
30
|
if (!connect) {
|
|
26
31
|
throw new HazoError({
|
|
@@ -29,7 +34,7 @@ export async function copyDb(srcAdapter, tgtAdapter, opts = {}) {
|
|
|
29
34
|
message: 'hazo_connect is required for DB migration. Install hazo_connect to continue.',
|
|
30
35
|
});
|
|
31
36
|
}
|
|
32
|
-
// Schema parity check via _migrations
|
|
37
|
+
// Schema parity check via _migrations (SQLite only — PostgREST apps may not use _migrations)
|
|
33
38
|
const srcMigrations = await getMigrationNames(srcAdapter);
|
|
34
39
|
const tgtMigrations = await getMigrationNames(tgtAdapter);
|
|
35
40
|
if (srcMigrations === null || tgtMigrations === null) {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { HazoConnectAdapter } from 'hazo_connect';
|
|
2
|
+
import type { DbCopyOptions, DbCopyResult } from './db.js';
|
|
3
|
+
export declare function copyDbPostgrest(srcAdapter: HazoConnectAdapter, tgtAdapter: HazoConnectAdapter, opts?: DbCopyOptions): Promise<DbCopyResult>;
|
|
4
|
+
//# sourceMappingURL=db.postgrest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.postgrest.d.ts","sourceRoot":"","sources":["../../src/migrate/db.postgrest.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAM3D,wBAAsB,eAAe,CACnC,UAAU,EAAE,kBAAkB,EAC9B,UAAU,EAAE,kBAAkB,EAC9B,IAAI,GAAE,aAAkB,GACvB,OAAO,CAAC,YAAY,CAAC,CA+GvB"}
|