hazo_env 0.10.0 → 0.10.2
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 +22 -0
- package/dist/cli.js +0 -0
- package/dist/lib/i18n.d.ts +2 -0
- package/dist/lib/i18n.d.ts.map +1 -0
- package/dist/lib/i18n.js +21 -0
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +1 -0
- package/dist/locales/en.json +1 -0
- package/package.json +17 -13
- package/dist/mask/registry.d.ts +0 -8
- package/dist/mask/registry.d.ts.map +0 -1
- package/dist/mask/registry.js +0 -29
- package/dist/mask/ruleset.d.ts +0 -15
- package/dist/mask/ruleset.d.ts.map +0 -1
- package/dist/mask/ruleset.js +0 -77
- package/dist/migrate/audit.d.ts +0 -21
- package/dist/migrate/audit.d.ts.map +0 -1
- package/dist/migrate/audit.js +0 -36
- package/dist/migrate/clear.d.ts +0 -3
- package/dist/migrate/clear.d.ts.map +0 -1
- package/dist/migrate/clear.js +0 -117
- package/dist/migrate/db-dump-restore.d.ts +0 -24
- package/dist/migrate/db-dump-restore.d.ts.map +0 -1
- package/dist/migrate/db-dump-restore.js +0 -72
- package/dist/migrate/db.d.ts +0 -17
- package/dist/migrate/db.d.ts.map +0 -1
- package/dist/migrate/db.js +0 -127
- package/dist/migrate/db.postgrest.d.ts +0 -4
- package/dist/migrate/db.postgrest.d.ts.map +0 -1
- package/dist/migrate/db.postgrest.js +0 -95
- package/dist/migrate/files.d.ts +0 -15
- package/dist/migrate/files.d.ts.map +0 -1
- package/dist/migrate/files.js +0 -131
- package/dist/migrate/progress.d.ts +0 -5
- package/dist/migrate/progress.d.ts.map +0 -1
- package/dist/migrate/progress.js +0 -24
- package/dist/migrate/run.d.ts +0 -16
- package/dist/migrate/run.d.ts.map +0 -1
- package/dist/migrate/run.js +0 -270
- package/dist/migrate/snapshot.d.ts +0 -8
- package/dist/migrate/snapshot.d.ts.map +0 -1
- package/dist/migrate/snapshot.js +0 -51
- package/dist/migrate/transport.d.ts +0 -3
- package/dist/migrate/transport.d.ts.map +0 -1
- package/dist/migrate/transport.js +0 -12
- package/dist/migrate/verify.d.ts +0 -9
- package/dist/migrate/verify.d.ts.map +0 -1
- package/dist/migrate/verify.js +0 -141
- package/dist/resolve/backup.d.ts +0 -23
- package/dist/resolve/backup.d.ts.map +0 -1
- package/dist/resolve/backup.js +0 -52
- package/dist/resolve/migrate.d.ts +0 -3
- package/dist/resolve/migrate.d.ts.map +0 -1
- package/dist/resolve/migrate.js +0 -29
package/CHANGE_LOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# hazo_env — Change Log
|
|
2
2
|
|
|
3
|
+
## 0.10.2 — 2026-09-19
|
|
4
|
+
|
|
5
|
+
Pure plumbing — no behavior change. Future-proofs hazo_env for the workspace's i18n adoption
|
|
6
|
+
effort (`hazo_core/i18n`'s canonical `createPackageT` bridge, see `hazo_i18n`).
|
|
7
|
+
|
|
8
|
+
- Verified hazo_env is genuinely a "zero-string" package: it's a CLI/resolver tool with no
|
|
9
|
+
React UI (no `.tsx` in `src/`); the only string content is developer/operator-facing —
|
|
10
|
+
`console.log` output in `src/cli.ts` and thrown `HazoError` messages in `src/resolve/*` /
|
|
11
|
+
`src/env.server.ts` for misconfiguration diagnostics — not text rendered to an end user of a
|
|
12
|
+
consuming app.
|
|
13
|
+
- Added `src/locales/en.json` (empty flat catalog `{}`) and `src/lib/i18n.ts`, exporting
|
|
14
|
+
`t = createPackageT('hazo_env', en)` via the existing `src/lib/index.ts` barrel (server entry
|
|
15
|
+
only — `t` reads its catalog via `node:module`'s `createRequire` because this package's
|
|
16
|
+
tsconfig uses `module: "Node16"`, which doesn't support `import ... with { type: 'json' }`
|
|
17
|
+
(TS2823)).
|
|
18
|
+
- `build` script now copies `src/locales` to `dist/locales` so the JSON catalog ships with the
|
|
19
|
+
compiled output.
|
|
20
|
+
- Added `hazo_i18n` as an optional peer dependency (`^0.5.0`); `hazo_core` was already a
|
|
21
|
+
required peer.
|
|
22
|
+
- test-app `/autotest`: added a documented zero-case `i18n-stub` scenario group (`t` is
|
|
23
|
+
server-only in this package's layout, so it can't be exercised from the client-side harness).
|
|
24
|
+
|
|
3
25
|
## 0.10.0 — 2026-07-29
|
|
4
26
|
|
|
5
27
|
Security/safety hardening of `envsync`. Six defects fixed, each with a regression test. These are **behavior changes** — new validation refuses inputs that previously ran, one new opt-in flag is required to keep an old behavior, and diff/log output is now redacted.
|
package/dist/cli.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/lib/i18n.ts"],"names":[],"mappings":"AAsBA,eAAO,MAAM,CAAC,gCAAiC,CAAC"}
|
package/dist/lib/i18n.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// hazo_env/src/lib/i18n.ts — canonical i18n bridge wiring
|
|
2
|
+
//
|
|
3
|
+
// Future-proofing plumbing only: hazo_env is a "zero-string" package (no
|
|
4
|
+
// user-facing UI text — it's a CLI/resolver tool; console output in
|
|
5
|
+
// src/cli.ts and thrown HazoError messages in src/resolve/* and
|
|
6
|
+
// src/env.server.ts are developer/operator-facing diagnostics, not
|
|
7
|
+
// application UI). `en` is an empty flat catalog today. If this package
|
|
8
|
+
// ever grows a user-facing string, add it to `src/locales/en.json` and read
|
|
9
|
+
// it via `t('some.key')` instead of a literal.
|
|
10
|
+
//
|
|
11
|
+
// NOTE: this package's tsconfig uses `module: "Node16"`, which does not
|
|
12
|
+
// support `import ... with { type: 'json' }` (TS2823). We use
|
|
13
|
+
// `createRequire` instead, matching the workaround documented for other
|
|
14
|
+
// Node16 packages in this adoption effort. The build script copies
|
|
15
|
+
// `src/locales` to `dist/locales` so the JSON ships alongside the compiled
|
|
16
|
+
// output.
|
|
17
|
+
import { createRequire } from 'node:module';
|
|
18
|
+
import { createPackageT } from 'hazo_core/i18n';
|
|
19
|
+
const require = createRequire(import.meta.url);
|
|
20
|
+
const en = require('../locales/en.json');
|
|
21
|
+
export const t = createPackageT('hazo_env', en);
|
package/dist/lib/index.d.ts
CHANGED
package/dist/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AACA,cAAc,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AACA,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,CAAC,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/lib/index.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hazo_env",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.2",
|
|
4
4
|
"description": "Canonical environment resolver — typed env names, per-env DB/file/secret config, doctor and CLI for hazo apps",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"CHANGE_LOG.md"
|
|
28
28
|
],
|
|
29
29
|
"scripts": {
|
|
30
|
-
"build": "tsc -p tsconfig.build.json",
|
|
30
|
+
"build": "tsc -p tsconfig.build.json && cp -r src/locales dist/locales",
|
|
31
31
|
"lint": "tsc --noEmit",
|
|
32
32
|
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
|
|
33
33
|
"dev:test-app": "npm run build && cd test-app && npm run dev",
|
|
@@ -38,13 +38,14 @@
|
|
|
38
38
|
"picocolors": "^1.1.1"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"hazo_core": "^
|
|
42
|
-
"hazo_config": "^2.
|
|
43
|
-
"hazo_connect": "^3.
|
|
44
|
-
"hazo_files": "^3.
|
|
45
|
-
"hazo_secure": "^1.
|
|
46
|
-
"hazo_audit": "^2.
|
|
47
|
-
"hazo_pdf": "^2.
|
|
41
|
+
"hazo_core": "^2.4.0",
|
|
42
|
+
"hazo_config": "^2.6.0",
|
|
43
|
+
"hazo_connect": "^4.3.0",
|
|
44
|
+
"hazo_files": "^3.4.0",
|
|
45
|
+
"hazo_secure": "^1.8.1",
|
|
46
|
+
"hazo_audit": "^2.3.0",
|
|
47
|
+
"hazo_pdf": "^2.4.0",
|
|
48
|
+
"hazo_i18n": "^0.5.0",
|
|
48
49
|
"react": "^18.0.0 || ^19.0.0",
|
|
49
50
|
"react-dom": "^18.0.0 || ^19.0.0",
|
|
50
51
|
"next": "^14.0.0 || ^16.0.0"
|
|
@@ -53,6 +54,9 @@
|
|
|
53
54
|
"hazo_connect": {
|
|
54
55
|
"optional": true
|
|
55
56
|
},
|
|
57
|
+
"hazo_i18n": {
|
|
58
|
+
"optional": true
|
|
59
|
+
},
|
|
56
60
|
"hazo_files": {
|
|
57
61
|
"optional": true
|
|
58
62
|
},
|
|
@@ -84,10 +88,10 @@
|
|
|
84
88
|
"@types/node": "^22.10.0",
|
|
85
89
|
"@types/react": "^19.0.0",
|
|
86
90
|
"@types/react-dom": "^19.0.0",
|
|
87
|
-
"hazo_core": "^
|
|
88
|
-
"hazo_config": "^2.
|
|
89
|
-
"hazo_connect": "^3.
|
|
90
|
-
"hazo_files": "^3.
|
|
91
|
+
"hazo_core": "^2.4.0",
|
|
92
|
+
"hazo_config": "^2.6.0",
|
|
93
|
+
"hazo_connect": "^4.3.0",
|
|
94
|
+
"hazo_files": "^3.4.0",
|
|
91
95
|
"next": "^16.0.10",
|
|
92
96
|
"react": "^19.0.0",
|
|
93
97
|
"react-dom": "^19.0.0",
|
package/dist/mask/registry.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { MaskTransform } from '../types/index.js';
|
|
2
|
-
/** Register a custom transform (or override a built-in). */
|
|
3
|
-
export declare function registerMask(name: string, fn: MaskTransform): void;
|
|
4
|
-
/** Get the transform function for a given name. Returns undefined if not registered. */
|
|
5
|
-
export declare function getTransform(name: string): Promise<MaskTransform | undefined>;
|
|
6
|
-
/** List all registered transform names. */
|
|
7
|
-
export declare function listTransforms(): Promise<string[]>;
|
|
8
|
-
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/mask/registry.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAiBvD,4DAA4D;AAC5D,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,aAAa,GAAG,IAAI,CAElE;AAED,wFAAwF;AACxF,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAGnF;AAED,2CAA2C;AAC3C,wBAAsB,cAAc,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAGxD"}
|
package/dist/mask/registry.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
// hazo_env/src/mask/registry.ts — mask transform registry
|
|
2
|
-
import { optional_import } from 'hazo_core';
|
|
3
|
-
const _registry = new Map();
|
|
4
|
-
let _seeded = false;
|
|
5
|
-
async function ensureSeeded() {
|
|
6
|
-
if (_seeded)
|
|
7
|
-
return;
|
|
8
|
-
_seeded = true;
|
|
9
|
-
const maskModule = await optional_import('hazo_secure/mask');
|
|
10
|
-
if (maskModule?.BUILTIN_TRANSFORMS) {
|
|
11
|
-
for (const [name, fn] of Object.entries(maskModule.BUILTIN_TRANSFORMS)) {
|
|
12
|
-
_registry.set(name, fn);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
/** Register a custom transform (or override a built-in). */
|
|
17
|
-
export function registerMask(name, fn) {
|
|
18
|
-
_registry.set(name, fn);
|
|
19
|
-
}
|
|
20
|
-
/** Get the transform function for a given name. Returns undefined if not registered. */
|
|
21
|
-
export async function getTransform(name) {
|
|
22
|
-
await ensureSeeded();
|
|
23
|
-
return _registry.get(name);
|
|
24
|
-
}
|
|
25
|
-
/** List all registered transform names. */
|
|
26
|
-
export async function listTransforms() {
|
|
27
|
-
await ensureSeeded();
|
|
28
|
-
return [..._registry.keys()].sort();
|
|
29
|
-
}
|
package/dist/mask/ruleset.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { HazoConnectAdapter } from 'hazo_connect';
|
|
2
|
-
export interface MaskRule {
|
|
3
|
-
table: string;
|
|
4
|
-
column: string;
|
|
5
|
-
transform: string;
|
|
6
|
-
params?: Record<string, unknown>;
|
|
7
|
-
}
|
|
8
|
-
export declare function ensureAppConfigTable(adapter: HazoConnectAdapter): Promise<void>;
|
|
9
|
-
/** Load rules from hazo_env_masking.ini into hazo_app_config. */
|
|
10
|
-
export declare function syncRulesetFromIni(adapter: HazoConnectAdapter, iniPath?: string): Promise<number>;
|
|
11
|
-
/** Read the effective ruleset from hazo_app_config. */
|
|
12
|
-
export declare function loadRuleset(adapter: HazoConnectAdapter): Promise<MaskRule[]>;
|
|
13
|
-
/** Parse INI-format masking rules. Format: [table_name] / column = transform */
|
|
14
|
-
export declare function parseIniRules(content: string): MaskRule[];
|
|
15
|
-
//# sourceMappingURL=ruleset.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ruleset.d.ts","sourceRoot":"","sources":["../../src/mask/ruleset.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AASD,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAYrF;AAED,iEAAiE;AACjE,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,kBAAkB,EAC3B,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,CAwBjB;AAED,uDAAuD;AACvD,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAYlF;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,EAAE,CAkBzD"}
|
package/dist/mask/ruleset.js
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
// hazo_env/src/mask/ruleset.ts — ruleset persistence (hazo_app_config)
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
const CONFIG_SECTION = 'hazo_env_masking';
|
|
5
|
-
// Ensure hazo_app_config table exists in the given SQLite adapter
|
|
6
|
-
function rawQuery(adapter, sql, params = []) {
|
|
7
|
-
return adapter.rawQuery(sql, { params });
|
|
8
|
-
}
|
|
9
|
-
export async function ensureAppConfigTable(adapter) {
|
|
10
|
-
await rawQuery(adapter, `
|
|
11
|
-
CREATE TABLE IF NOT EXISTS hazo_app_config (
|
|
12
|
-
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
|
|
13
|
-
config_section TEXT NOT NULL,
|
|
14
|
-
config_name TEXT NOT NULL,
|
|
15
|
-
config_value_json TEXT,
|
|
16
|
-
created_at TEXT DEFAULT (datetime('now')),
|
|
17
|
-
updated_at TEXT DEFAULT (datetime('now')),
|
|
18
|
-
UNIQUE(config_section, config_name)
|
|
19
|
-
)
|
|
20
|
-
`);
|
|
21
|
-
}
|
|
22
|
-
/** Load rules from hazo_env_masking.ini into hazo_app_config. */
|
|
23
|
-
export async function syncRulesetFromIni(adapter, iniPath) {
|
|
24
|
-
const resolvedPath = iniPath ?? path.resolve(process.cwd(), 'config', 'hazo_env_masking.ini');
|
|
25
|
-
if (!fs.existsSync(resolvedPath)) {
|
|
26
|
-
throw new Error(`hazo_env_masking.ini not found at ${resolvedPath}`);
|
|
27
|
-
}
|
|
28
|
-
const content = fs.readFileSync(resolvedPath, 'utf-8');
|
|
29
|
-
const rules = parseIniRules(content);
|
|
30
|
-
await ensureAppConfigTable(adapter);
|
|
31
|
-
let count = 0;
|
|
32
|
-
for (const rule of rules) {
|
|
33
|
-
const name = `${rule.table}.${rule.column}`;
|
|
34
|
-
const valueJson = JSON.stringify({ transform: rule.transform, params: rule.params ?? {} });
|
|
35
|
-
await rawQuery(adapter, `
|
|
36
|
-
INSERT INTO hazo_app_config (config_section, config_name, config_value_json)
|
|
37
|
-
VALUES (?, ?, ?)
|
|
38
|
-
ON CONFLICT(config_section, config_name) DO UPDATE SET
|
|
39
|
-
config_value_json = excluded.config_value_json,
|
|
40
|
-
updated_at = datetime('now')
|
|
41
|
-
`, [CONFIG_SECTION, name, valueJson]);
|
|
42
|
-
count++;
|
|
43
|
-
}
|
|
44
|
-
return count;
|
|
45
|
-
}
|
|
46
|
-
/** Read the effective ruleset from hazo_app_config. */
|
|
47
|
-
export async function loadRuleset(adapter) {
|
|
48
|
-
await ensureAppConfigTable(adapter);
|
|
49
|
-
const rows = await rawQuery(adapter, `SELECT config_name, config_value_json FROM hazo_app_config WHERE config_section = ? ORDER BY config_name`, [CONFIG_SECTION]);
|
|
50
|
-
return rows.map((row) => {
|
|
51
|
-
const [table, column] = row.config_name.split('.');
|
|
52
|
-
const parsed = JSON.parse(row.config_value_json ?? '{}');
|
|
53
|
-
return { table, column, transform: parsed.transform, params: parsed.params };
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
/** Parse INI-format masking rules. Format: [table_name] / column = transform */
|
|
57
|
-
export function parseIniRules(content) {
|
|
58
|
-
const rules = [];
|
|
59
|
-
let currentTable = '';
|
|
60
|
-
for (const rawLine of content.split('\n')) {
|
|
61
|
-
const line = rawLine.trim();
|
|
62
|
-
if (!line || line.startsWith(';') || line.startsWith('#'))
|
|
63
|
-
continue;
|
|
64
|
-
if (line.startsWith('[') && line.endsWith(']')) {
|
|
65
|
-
currentTable = line.slice(1, -1).trim();
|
|
66
|
-
}
|
|
67
|
-
else if (currentTable && line.includes('=')) {
|
|
68
|
-
const eqIdx = line.indexOf('=');
|
|
69
|
-
const column = line.slice(0, eqIdx).trim();
|
|
70
|
-
const transform = line.slice(eqIdx + 1).trim();
|
|
71
|
-
if (column && transform) {
|
|
72
|
-
rules.push({ table: currentTable, column, transform });
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return rules;
|
|
77
|
-
}
|
package/dist/migrate/audit.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { HazoConnectAdapter } from 'hazo_connect';
|
|
2
|
-
export interface AuditMigrationPayload {
|
|
3
|
-
from: string;
|
|
4
|
-
to: string;
|
|
5
|
-
scrub: string;
|
|
6
|
-
db?: {
|
|
7
|
-
tables: number;
|
|
8
|
-
rows: number;
|
|
9
|
-
scrubbed: number;
|
|
10
|
-
};
|
|
11
|
-
files?: {
|
|
12
|
-
copied: number;
|
|
13
|
-
bytes: number;
|
|
14
|
-
};
|
|
15
|
-
verify?: {
|
|
16
|
-
ok: boolean;
|
|
17
|
-
checked: number;
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
export declare function auditMigration(adapter: HazoConnectAdapter, payload: AuditMigrationPayload): Promise<void>;
|
|
21
|
-
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/migrate/audit.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACxD,KAAK,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,MAAM,CAAC,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3C;AAOD,wBAAsB,cAAc,CAClC,OAAO,EAAE,kBAAkB,EAC3B,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAgCf"}
|
package/dist/migrate/audit.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// hazo_env/src/migrate/audit.ts — emit audit intent event (wrapped, never fails migration)
|
|
2
|
-
import { optional_import } from 'hazo_core';
|
|
3
|
-
// Cast helper for SQLite rawQuery params extension
|
|
4
|
-
function rawQuery(adapter, sql, params = []) {
|
|
5
|
-
return adapter.rawQuery(sql, { params });
|
|
6
|
-
}
|
|
7
|
-
export async function auditMigration(adapter, payload) {
|
|
8
|
-
try {
|
|
9
|
-
// Ensure hazo_audit_intent table exists (CREATE TABLE IF NOT EXISTS)
|
|
10
|
-
await rawQuery(adapter, `
|
|
11
|
-
CREATE TABLE IF NOT EXISTS hazo_audit_intent (
|
|
12
|
-
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
|
|
13
|
-
event_name TEXT NOT NULL,
|
|
14
|
-
payload TEXT,
|
|
15
|
-
subject_kind TEXT,
|
|
16
|
-
subject_id TEXT,
|
|
17
|
-
created_at TEXT DEFAULT (datetime('now'))
|
|
18
|
-
)
|
|
19
|
-
`).catch(() => { });
|
|
20
|
-
const auditModule = await optional_import('hazo_audit/server');
|
|
21
|
-
if (auditModule?.emitIntentEvent) {
|
|
22
|
-
await auditModule.emitIntentEvent(adapter, {
|
|
23
|
-
event_name: 'hazo_env.migration',
|
|
24
|
-
payload: payload,
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
// Fallback: insert directly
|
|
29
|
-
await rawQuery(adapter, `INSERT INTO hazo_audit_intent (event_name, payload, subject_kind) VALUES (?, ?, ?)`, ['hazo_env.migration', JSON.stringify(payload), 'migration']);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
catch (e) {
|
|
33
|
-
// Audit write failure never fails the migration
|
|
34
|
-
console.warn('[hazo_env] audit write failed (non-fatal):', e instanceof Error ? e.message : String(e));
|
|
35
|
-
}
|
|
36
|
-
}
|
package/dist/migrate/clear.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
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"}
|
package/dist/migrate/clear.js
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { BackupConfig } from '../resolve/backup.js';
|
|
2
|
-
export interface DbDumpRestoreResult {
|
|
3
|
-
tables: number;
|
|
4
|
-
rows: number;
|
|
5
|
-
scrubbed: number;
|
|
6
|
-
mode: string;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Restore a Postgres database on a remote server via SSH pg_dump/pg_restore.
|
|
10
|
-
*
|
|
11
|
-
* Steps executed remotely:
|
|
12
|
-
* 1. Obtain a dump file (fresh pg_dump or locate an existing .pgdump)
|
|
13
|
-
* 2. Run pre_restore_cmd (if configured)
|
|
14
|
-
* 3. dropdb --if-exists + createdb -O <owner>
|
|
15
|
-
* 4. pg_restore -d <target_db>
|
|
16
|
-
* 5. Run post_restore_cmd (if configured)
|
|
17
|
-
*
|
|
18
|
-
* Returns a DbDumpRestoreResult with mode: 'dump-restore' and zero
|
|
19
|
-
* row/table counts (binary restore does not report these).
|
|
20
|
-
*/
|
|
21
|
-
export declare function restoreDbViaDump(backup: BackupConfig, opts?: {
|
|
22
|
-
onProgress?: (msg: string) => void;
|
|
23
|
-
}): Promise<DbDumpRestoreResult>;
|
|
24
|
-
//# sourceMappingURL=db-dump-restore.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"db-dump-restore.d.ts","sourceRoot":"","sources":["../../src/migrate/db-dump-restore.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,YAAY,EACpB,IAAI,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,GAC5C,OAAO,CAAC,mBAAmB,CAAC,CA6D9B"}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
// hazo_env/src/migrate/db-dump-restore.ts — DB copy via SSH pg_dump/pg_restore
|
|
2
|
-
//
|
|
3
|
-
// Builds a single remote bash script and runs it via runSsh.
|
|
4
|
-
// ⚠️ No PII scrubbing on this path — pg_restore copies raw binary rows.
|
|
5
|
-
// Use the default api transport when scrubbed data is required in dev.
|
|
6
|
-
import { resolveSshConfig } from '../resolve/ssh.js';
|
|
7
|
-
import { runSsh } from './ssh-exec.js';
|
|
8
|
-
/**
|
|
9
|
-
* Restore a Postgres database on a remote server via SSH pg_dump/pg_restore.
|
|
10
|
-
*
|
|
11
|
-
* Steps executed remotely:
|
|
12
|
-
* 1. Obtain a dump file (fresh pg_dump or locate an existing .pgdump)
|
|
13
|
-
* 2. Run pre_restore_cmd (if configured)
|
|
14
|
-
* 3. dropdb --if-exists + createdb -O <owner>
|
|
15
|
-
* 4. pg_restore -d <target_db>
|
|
16
|
-
* 5. Run post_restore_cmd (if configured)
|
|
17
|
-
*
|
|
18
|
-
* Returns a DbDumpRestoreResult with mode: 'dump-restore' and zero
|
|
19
|
-
* row/table counts (binary restore does not report these).
|
|
20
|
-
*/
|
|
21
|
-
export async function restoreDbViaDump(backup, opts) {
|
|
22
|
-
const ssh = resolveSshConfig(backup.ssh_env);
|
|
23
|
-
if (!ssh) {
|
|
24
|
-
throw new Error(`dump-restore: no [transport.ssh.${backup.ssh_env}] config found — ` +
|
|
25
|
-
`set ssh_env to a configured SSH transport in [backup.*]`);
|
|
26
|
-
}
|
|
27
|
-
// Build remote bash script lines
|
|
28
|
-
const lines = ['set -euo pipefail', ''];
|
|
29
|
-
// Step 1: Get or create dump
|
|
30
|
-
lines.push('# Step 1: Get or create dump');
|
|
31
|
-
if (backup.dump_source === 'fresh') {
|
|
32
|
-
lines.push('DUMP_FILE="/tmp/he-$(date +%s).pgdump"');
|
|
33
|
-
lines.push(`echo "Creating fresh dump of ${backup.fresh_dump_db}..."`);
|
|
34
|
-
lines.push(`pg_dump -Fc ${backup.fresh_dump_db} > "$DUMP_FILE"`);
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
lines.push(`echo "Locating dump file matching ${backup.dump_source}..."`);
|
|
38
|
-
lines.push(`DUMP_FILE=$(ls -t ${backup.dump_source} 2>/dev/null | head -1)`);
|
|
39
|
-
lines.push(`if [ -z "$DUMP_FILE" ]; then echo "No dump file found matching ${backup.dump_source}"; exit 1; fi`);
|
|
40
|
-
lines.push('echo "Using dump: $DUMP_FILE"');
|
|
41
|
-
}
|
|
42
|
-
lines.push('');
|
|
43
|
-
// Step 2: Pre-restore command (optional)
|
|
44
|
-
if (backup.pre_restore_cmd) {
|
|
45
|
-
lines.push('# Step 2: Pre-restore command');
|
|
46
|
-
lines.push('echo "Running pre-restore..."');
|
|
47
|
-
lines.push(backup.pre_restore_cmd);
|
|
48
|
-
lines.push('');
|
|
49
|
-
}
|
|
50
|
-
// Step 3: Drop and recreate target DB
|
|
51
|
-
lines.push('# Step 3: Drop and recreate target DB');
|
|
52
|
-
lines.push(`echo "Dropping ${backup.target_db}..."`);
|
|
53
|
-
lines.push(`dropdb --if-exists ${backup.target_db}`);
|
|
54
|
-
lines.push(`echo "Creating ${backup.target_db}..."`);
|
|
55
|
-
lines.push(`createdb -O ${backup.owner} ${backup.target_db}`);
|
|
56
|
-
lines.push('');
|
|
57
|
-
// Step 4: Restore
|
|
58
|
-
lines.push('# Step 4: Restore');
|
|
59
|
-
lines.push(`echo "Restoring to ${backup.target_db}..."`);
|
|
60
|
-
lines.push(`pg_restore -d ${backup.target_db} "$DUMP_FILE"`);
|
|
61
|
-
lines.push('echo "Restore complete."');
|
|
62
|
-
// Step 5: Post-restore command (optional)
|
|
63
|
-
if (backup.post_restore_cmd) {
|
|
64
|
-
lines.push('');
|
|
65
|
-
lines.push('# Step 5: Post-restore command');
|
|
66
|
-
lines.push('echo "Running post-restore..."');
|
|
67
|
-
lines.push(backup.post_restore_cmd);
|
|
68
|
-
}
|
|
69
|
-
const script = lines.join('\n');
|
|
70
|
-
await runSsh(ssh, script, { onProgress: opts?.onProgress });
|
|
71
|
-
return { tables: 0, rows: 0, scrubbed: 0, mode: 'dump-restore' };
|
|
72
|
-
}
|
package/dist/migrate/db.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { HazoConnectAdapter } from 'hazo_connect';
|
|
2
|
-
export interface DbCopyOptions {
|
|
3
|
-
type?: 'sqlite' | 'postgrest';
|
|
4
|
-
tables?: '*' | string[];
|
|
5
|
-
preserve?: string[];
|
|
6
|
-
pkOverrides?: Record<string, string>;
|
|
7
|
-
scrubHook?: (tableName: string, row: Record<string, unknown>) => Record<string, unknown>;
|
|
8
|
-
onProgress?: (msg: string) => void;
|
|
9
|
-
onTableProgress?: (tableIdx: number, totalTables: number, tableName: string) => void;
|
|
10
|
-
}
|
|
11
|
-
export interface DbCopyResult {
|
|
12
|
-
tables: number;
|
|
13
|
-
rows: number;
|
|
14
|
-
scrubbed: number;
|
|
15
|
-
}
|
|
16
|
-
export declare function copyDb(srcAdapter: HazoConnectAdapter, tgtAdapter: HazoConnectAdapter, opts?: DbCopyOptions): Promise<DbCopyResult>;
|
|
17
|
-
//# sourceMappingURL=db.d.ts.map
|
package/dist/migrate/db.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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"}
|