relq 1.0.97 → 1.0.99
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/dist/cjs/cli/adapters/cockroachdb/introspect.cjs +20 -7
- package/dist/cjs/cli/adapters/dsql/introspect.cjs +20 -7
- package/dist/cjs/cli/adapters/nile/introspect.cjs +20 -7
- package/dist/cjs/cli/adapters/postgres/introspect.cjs +20 -7
- package/dist/cjs/cli/utils/ast-codegen.cjs +1 -1
- package/dist/cjs/cli/utils/ast-transformer.cjs +10 -0
- package/dist/cjs/cli/utils/certificates.cjs +100 -0
- package/dist/cjs/cli/utils/cockroachdb/introspect.cjs +20 -7
- package/dist/cjs/cli/utils/dsql/introspect.cjs +20 -7
- package/dist/cjs/cli/utils/nile/introspect.cjs +20 -7
- package/dist/cjs/cli/utils/postgres/introspect.cjs +20 -7
- package/dist/esm/cli/adapters/cockroachdb/introspect.js +20 -7
- package/dist/esm/cli/adapters/dsql/introspect.js +20 -7
- package/dist/esm/cli/adapters/nile/introspect.js +20 -7
- package/dist/esm/cli/adapters/postgres/introspect.js +20 -7
- package/dist/esm/cli/utils/ast-codegen.js +1 -1
- package/dist/esm/cli/utils/ast-transformer.js +10 -0
- package/dist/esm/cli/utils/certificates.js +93 -0
- package/dist/esm/cli/utils/cockroachdb/introspect.js +20 -7
- package/dist/esm/cli/utils/dsql/introspect.js +20 -7
- package/dist/esm/cli/utils/nile/introspect.js +20 -7
- package/dist/esm/cli/utils/postgres/introspect.js +20 -7
- package/package.json +1 -1
|
@@ -37,6 +37,15 @@ exports.introspectCockroachDB = introspectCockroachDB;
|
|
|
37
37
|
exports.introspectTable = introspectTable;
|
|
38
38
|
exports.listTables = listTables;
|
|
39
39
|
exports.listSchemas = listSchemas;
|
|
40
|
+
function parsePgArray(val) {
|
|
41
|
+
if (Array.isArray(val))
|
|
42
|
+
return val;
|
|
43
|
+
if (typeof val === 'string') {
|
|
44
|
+
const trimmed = val.replace(/^\{|\}$/g, '');
|
|
45
|
+
return trimmed ? trimmed.split(',') : [];
|
|
46
|
+
}
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
40
49
|
async function introspectCockroachDB(connection, options) {
|
|
41
50
|
const { Pool } = await Promise.resolve().then(() => __importStar(require('pg')));
|
|
42
51
|
const { buildPoolConfig } = await Promise.resolve().then(() => __importStar(require("../../../config/config.cjs")));
|
|
@@ -693,11 +702,15 @@ async function introspectCompositeTypes(pool) {
|
|
|
693
702
|
GROUP BY t.oid, t.typname, n.nspname
|
|
694
703
|
ORDER BY n.nspname, t.typname
|
|
695
704
|
`);
|
|
696
|
-
return result.rows.map((row) =>
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
705
|
+
return result.rows.map((row) => {
|
|
706
|
+
const names = parsePgArray(row.attribute_names);
|
|
707
|
+
const types = parsePgArray(row.attribute_types);
|
|
708
|
+
return {
|
|
709
|
+
name: row.name,
|
|
710
|
+
attributes: names.map((attrName, i) => ({
|
|
711
|
+
name: attrName,
|
|
712
|
+
type: types[i] || 'unknown',
|
|
713
|
+
})),
|
|
714
|
+
};
|
|
715
|
+
});
|
|
703
716
|
}
|
|
@@ -37,6 +37,15 @@ exports.introspectDsql = introspectDsql;
|
|
|
37
37
|
exports.introspectTable = introspectTable;
|
|
38
38
|
exports.listTables = listTables;
|
|
39
39
|
exports.listSchemas = listSchemas;
|
|
40
|
+
function parsePgArray(val) {
|
|
41
|
+
if (Array.isArray(val))
|
|
42
|
+
return val;
|
|
43
|
+
if (typeof val === 'string') {
|
|
44
|
+
const trimmed = val.replace(/^\{|\}$/g, '');
|
|
45
|
+
return trimmed ? trimmed.split(',') : [];
|
|
46
|
+
}
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
40
49
|
async function introspectDsql(connection, options) {
|
|
41
50
|
const { Pool } = await Promise.resolve().then(() => __importStar(require('pg')));
|
|
42
51
|
const { buildPoolConfig } = await Promise.resolve().then(() => __importStar(require("../../../config/config.cjs")));
|
|
@@ -681,13 +690,17 @@ async function introspectCompositeTypes(pool) {
|
|
|
681
690
|
GROUP BY t.oid, t.typname, n.nspname
|
|
682
691
|
ORDER BY n.nspname, t.typname
|
|
683
692
|
`);
|
|
684
|
-
return result.rows.map((row) =>
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
693
|
+
return result.rows.map((row) => {
|
|
694
|
+
const names = parsePgArray(row.attribute_names);
|
|
695
|
+
const types = parsePgArray(row.attribute_types);
|
|
696
|
+
return {
|
|
697
|
+
name: row.name,
|
|
698
|
+
attributes: names.map((attrName, i) => ({
|
|
699
|
+
name: attrName,
|
|
700
|
+
type: types[i] || 'unknown',
|
|
701
|
+
})),
|
|
702
|
+
};
|
|
703
|
+
});
|
|
691
704
|
}
|
|
692
705
|
catch {
|
|
693
706
|
return [];
|
|
@@ -37,6 +37,15 @@ exports.introspectNile = introspectNile;
|
|
|
37
37
|
exports.introspectTable = introspectTable;
|
|
38
38
|
exports.listTables = listTables;
|
|
39
39
|
exports.listSchemas = listSchemas;
|
|
40
|
+
function parsePgArray(val) {
|
|
41
|
+
if (Array.isArray(val))
|
|
42
|
+
return val;
|
|
43
|
+
if (typeof val === 'string') {
|
|
44
|
+
const trimmed = val.replace(/^\{|\}$/g, '');
|
|
45
|
+
return trimmed ? trimmed.split(',') : [];
|
|
46
|
+
}
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
40
49
|
async function introspectNile(connection, options) {
|
|
41
50
|
const { Pool } = await Promise.resolve().then(() => __importStar(require('pg')));
|
|
42
51
|
const { buildPoolConfig } = await Promise.resolve().then(() => __importStar(require("../../../config/config.cjs")));
|
|
@@ -671,11 +680,15 @@ async function introspectCompositeTypes(pool) {
|
|
|
671
680
|
GROUP BY t.oid, t.typname, n.nspname
|
|
672
681
|
ORDER BY n.nspname, t.typname
|
|
673
682
|
`);
|
|
674
|
-
return result.rows.map((row) =>
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
683
|
+
return result.rows.map((row) => {
|
|
684
|
+
const names = parsePgArray(row.attribute_names);
|
|
685
|
+
const types = parsePgArray(row.attribute_types);
|
|
686
|
+
return {
|
|
687
|
+
name: row.name,
|
|
688
|
+
attributes: names.map((attrName, i) => ({
|
|
689
|
+
name: attrName,
|
|
690
|
+
type: types[i] || 'unknown',
|
|
691
|
+
})),
|
|
692
|
+
};
|
|
693
|
+
});
|
|
681
694
|
}
|
|
@@ -37,6 +37,15 @@ exports.introspectPostgres = introspectPostgres;
|
|
|
37
37
|
exports.introspectTable = introspectTable;
|
|
38
38
|
exports.listTables = listTables;
|
|
39
39
|
exports.listSchemas = listSchemas;
|
|
40
|
+
function parsePgArray(val) {
|
|
41
|
+
if (Array.isArray(val))
|
|
42
|
+
return val;
|
|
43
|
+
if (typeof val === 'string') {
|
|
44
|
+
const trimmed = val.replace(/^\{|\}$/g, '');
|
|
45
|
+
return trimmed ? trimmed.split(',') : [];
|
|
46
|
+
}
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
40
49
|
async function introspectPostgres(connection, options) {
|
|
41
50
|
const { Pool } = await Promise.resolve().then(() => __importStar(require('pg')));
|
|
42
51
|
const { buildPoolConfig } = await Promise.resolve().then(() => __importStar(require("../../../config/config.cjs")));
|
|
@@ -675,11 +684,15 @@ async function introspectCompositeTypes(pool) {
|
|
|
675
684
|
GROUP BY t.oid, t.typname, n.nspname
|
|
676
685
|
ORDER BY n.nspname, t.typname
|
|
677
686
|
`);
|
|
678
|
-
return result.rows.map((row) =>
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
687
|
+
return result.rows.map((row) => {
|
|
688
|
+
const names = parsePgArray(row.attribute_names);
|
|
689
|
+
const types = parsePgArray(row.attribute_types);
|
|
690
|
+
return {
|
|
691
|
+
name: row.name,
|
|
692
|
+
attributes: names.map((attrName, i) => ({
|
|
693
|
+
name: attrName,
|
|
694
|
+
type: types[i] || 'unknown',
|
|
695
|
+
})),
|
|
696
|
+
};
|
|
697
|
+
});
|
|
685
698
|
}
|
|
@@ -731,7 +731,7 @@ function generateSequenceCode(seq, useCamelCase) {
|
|
|
731
731
|
if (seq.cycle)
|
|
732
732
|
opts.push(`cycle: true`);
|
|
733
733
|
if (seq.ownedBy)
|
|
734
|
-
opts.push(`ownedBy:
|
|
734
|
+
opts.push(`ownedBy: '${seq.ownedBy.table}.${seq.ownedBy.column}'`);
|
|
735
735
|
const trackingId = seq.trackingId || generateTrackingId('s');
|
|
736
736
|
opts.push(`trackingId: '${trackingId}'`);
|
|
737
737
|
const optsStr = opts.length > 0 ? `, { ${opts.join(', ')} }` : '';
|
|
@@ -749,6 +749,16 @@ async function introspectedToParsedSchema(schema) {
|
|
|
749
749
|
comment: t.comment,
|
|
750
750
|
});
|
|
751
751
|
}
|
|
752
|
+
const isInternal = (name) => name.startsWith('_relq_') || name.startsWith('__relq_');
|
|
753
|
+
parsed.tables = parsed.tables.filter(t => !isInternal(t.name));
|
|
754
|
+
parsed.sequences = parsed.sequences.filter(s => {
|
|
755
|
+
if (isInternal(s.name))
|
|
756
|
+
return false;
|
|
757
|
+
if (s.ownedBy && isInternal(s.ownedBy.table))
|
|
758
|
+
return false;
|
|
759
|
+
return true;
|
|
760
|
+
});
|
|
761
|
+
parsed.triggers = parsed.triggers.filter(t => !isInternal(t.table));
|
|
752
762
|
return parsed;
|
|
753
763
|
}
|
|
754
764
|
function normalizeTypeName(dataType, udtName) {
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ensureCertificates = ensureCertificates;
|
|
4
|
+
exports.checkCertificates = checkCertificates;
|
|
5
|
+
exports.getMkcertCaRoot = getMkcertCaRoot;
|
|
6
|
+
exports.isMkcertInstalled = isMkcertInstalled;
|
|
7
|
+
exports.getMkcertInstallInstructions = getMkcertInstallInstructions;
|
|
8
|
+
const node_child_process_1 = require("node:child_process");
|
|
9
|
+
const promises_1 = require("node:fs/promises");
|
|
10
|
+
const node_path_1 = require("node:path");
|
|
11
|
+
const node_os_1 = require("node:os");
|
|
12
|
+
const DOMAIN = "local.relq.studio";
|
|
13
|
+
const CERTS_DIR = (0, node_path_1.join)((0, node_os_1.homedir)(), ".relq", "certs");
|
|
14
|
+
const KEY_FILE = `${DOMAIN}-key.pem`;
|
|
15
|
+
const CERT_FILE = `${DOMAIN}.pem`;
|
|
16
|
+
async function ensureCertificates() {
|
|
17
|
+
const keyPath = (0, node_path_1.join)(CERTS_DIR, KEY_FILE);
|
|
18
|
+
const certPath = (0, node_path_1.join)(CERTS_DIR, CERT_FILE);
|
|
19
|
+
const status = await checkCertificates();
|
|
20
|
+
if (status.exists && !status.expired) {
|
|
21
|
+
return { key: keyPath, cert: certPath };
|
|
22
|
+
}
|
|
23
|
+
assertMkcertInstalled();
|
|
24
|
+
await (0, promises_1.mkdir)(CERTS_DIR, { recursive: true });
|
|
25
|
+
(0, node_child_process_1.execSync)(`mkcert -key-file "${keyPath}" -cert-file "${certPath}" ${DOMAIN}`, { cwd: CERTS_DIR, stdio: "pipe" });
|
|
26
|
+
return { key: keyPath, cert: certPath };
|
|
27
|
+
}
|
|
28
|
+
async function checkCertificates() {
|
|
29
|
+
const keyPath = (0, node_path_1.join)(CERTS_DIR, KEY_FILE);
|
|
30
|
+
const certPath = (0, node_path_1.join)(CERTS_DIR, CERT_FILE);
|
|
31
|
+
try {
|
|
32
|
+
await (0, promises_1.access)(keyPath);
|
|
33
|
+
await (0, promises_1.access)(certPath);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return { exists: false, expired: false };
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const certContent = await (0, promises_1.readFile)(certPath, "utf-8");
|
|
40
|
+
const expiresAt = parseCertificateExpiry(certContent);
|
|
41
|
+
if (expiresAt && expiresAt.getTime() < Date.now()) {
|
|
42
|
+
return { exists: true, expired: true, expiresAt };
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
exists: true,
|
|
46
|
+
expired: false,
|
|
47
|
+
paths: { key: keyPath, cert: certPath },
|
|
48
|
+
expiresAt: expiresAt ?? undefined,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return { exists: true, expired: true };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function getMkcertCaRoot() {
|
|
56
|
+
try {
|
|
57
|
+
const output = (0, node_child_process_1.execSync)("mkcert -CAROOT", { encoding: "utf-8" });
|
|
58
|
+
return output.trim();
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function isMkcertInstalled() {
|
|
65
|
+
try {
|
|
66
|
+
(0, node_child_process_1.execSync)("mkcert -version", { stdio: "ignore" });
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function getMkcertInstallInstructions() {
|
|
74
|
+
return [
|
|
75
|
+
"mkcert is required for local HTTPS.",
|
|
76
|
+
"",
|
|
77
|
+
"Install mkcert:",
|
|
78
|
+
" macOS: brew install mkcert nss",
|
|
79
|
+
" Linux: sudo apt install mkcert",
|
|
80
|
+
" Windows: choco install mkcert",
|
|
81
|
+
"",
|
|
82
|
+
"Then install the local CA:",
|
|
83
|
+
" mkcert -install",
|
|
84
|
+
].join("\n");
|
|
85
|
+
}
|
|
86
|
+
function assertMkcertInstalled() {
|
|
87
|
+
if (!isMkcertInstalled()) {
|
|
88
|
+
throw new Error(getMkcertInstallInstructions());
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function parseCertificateExpiry(pemContent) {
|
|
92
|
+
try {
|
|
93
|
+
const { X509Certificate } = require("node:crypto");
|
|
94
|
+
const cert = new X509Certificate(pemContent);
|
|
95
|
+
return new Date(cert.validTo);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -35,6 +35,15 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.introspectCockroachDB = introspectCockroachDB;
|
|
37
37
|
const config_1 = require("../../../config/config.cjs");
|
|
38
|
+
function parsePgArray(val) {
|
|
39
|
+
if (Array.isArray(val))
|
|
40
|
+
return val;
|
|
41
|
+
if (typeof val === 'string') {
|
|
42
|
+
const trimmed = val.replace(/^\{|\}$/g, '');
|
|
43
|
+
return trimmed ? trimmed.split(',') : [];
|
|
44
|
+
}
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
38
47
|
function mapInternalType(internalType) {
|
|
39
48
|
const typeMap = {
|
|
40
49
|
'int2': 'smallint',
|
|
@@ -319,13 +328,17 @@ async function introspectCockroachDB(connection, options) {
|
|
|
319
328
|
ORDER BY n.nspname, t.typname
|
|
320
329
|
`);
|
|
321
330
|
onDetailedProgress?.({ step: 'composite_types', count: compositeTypesResult.rows.length, status: 'done' });
|
|
322
|
-
const compositeTypes = compositeTypesResult.rows.map(row =>
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
331
|
+
const compositeTypes = compositeTypesResult.rows.map(row => {
|
|
332
|
+
const names = parsePgArray(row.attribute_names);
|
|
333
|
+
const types = parsePgArray(row.attribute_types);
|
|
334
|
+
return {
|
|
335
|
+
name: row.name,
|
|
336
|
+
attributes: names.map((attrName, i) => ({
|
|
337
|
+
name: attrName,
|
|
338
|
+
type: types[i] || 'unknown',
|
|
339
|
+
})),
|
|
340
|
+
};
|
|
341
|
+
});
|
|
329
342
|
onProgress?.('processing');
|
|
330
343
|
const columnsByTable = new Map();
|
|
331
344
|
const constraintsByTable = new Map();
|
|
@@ -35,6 +35,15 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.introspectDsql = introspectDsql;
|
|
37
37
|
const config_1 = require("../../../config/config.cjs");
|
|
38
|
+
function parsePgArray(val) {
|
|
39
|
+
if (Array.isArray(val))
|
|
40
|
+
return val;
|
|
41
|
+
if (typeof val === 'string') {
|
|
42
|
+
const trimmed = val.replace(/^\{|\}$/g, '');
|
|
43
|
+
return trimmed ? trimmed.split(',') : [];
|
|
44
|
+
}
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
38
47
|
function mapInternalType(internalType) {
|
|
39
48
|
const typeMap = {
|
|
40
49
|
'int2': 'smallint',
|
|
@@ -306,13 +315,17 @@ async function introspectDsql(connection, options) {
|
|
|
306
315
|
GROUP BY t.oid, t.typname, n.nspname
|
|
307
316
|
ORDER BY n.nspname, t.typname
|
|
308
317
|
`);
|
|
309
|
-
compositeTypes = compositeTypesResult.rows.map(row =>
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
318
|
+
compositeTypes = compositeTypesResult.rows.map(row => {
|
|
319
|
+
const names = parsePgArray(row.attribute_names);
|
|
320
|
+
const types = parsePgArray(row.attribute_types);
|
|
321
|
+
return {
|
|
322
|
+
name: row.name,
|
|
323
|
+
attributes: names.map((attrName, i) => ({
|
|
324
|
+
name: attrName,
|
|
325
|
+
type: types[i] || 'unknown',
|
|
326
|
+
})),
|
|
327
|
+
};
|
|
328
|
+
});
|
|
316
329
|
}
|
|
317
330
|
catch {
|
|
318
331
|
}
|
|
@@ -35,6 +35,15 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.introspectNile = introspectNile;
|
|
37
37
|
const config_1 = require("../../../config/config.cjs");
|
|
38
|
+
function parsePgArray(val) {
|
|
39
|
+
if (Array.isArray(val))
|
|
40
|
+
return val;
|
|
41
|
+
if (typeof val === 'string') {
|
|
42
|
+
const trimmed = val.replace(/^\{|\}$/g, '');
|
|
43
|
+
return trimmed ? trimmed.split(',') : [];
|
|
44
|
+
}
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
38
47
|
const NILE_SYSTEM_TABLES = ['tenants', 'tenant_users'];
|
|
39
48
|
const NILE_SYSTEM_SCHEMAS = ['nile', 'pg_catalog', 'information_schema', 'pg_toast'];
|
|
40
49
|
function parseOptionsArray(options) {
|
|
@@ -335,13 +344,17 @@ async function introspectNile(connection, options) {
|
|
|
335
344
|
ORDER BY n.nspname, t.typname
|
|
336
345
|
`);
|
|
337
346
|
onDetailedProgress?.({ step: 'composite_types', count: compositeTypesResult.rows.length, status: 'done' });
|
|
338
|
-
const compositeTypes = compositeTypesResult.rows.map(row =>
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
347
|
+
const compositeTypes = compositeTypesResult.rows.map(row => {
|
|
348
|
+
const names = parsePgArray(row.attribute_names);
|
|
349
|
+
const types = parsePgArray(row.attribute_types);
|
|
350
|
+
return {
|
|
351
|
+
name: row.name,
|
|
352
|
+
attributes: names.map((attrName, i) => ({
|
|
353
|
+
name: attrName,
|
|
354
|
+
type: types[i] || 'unknown',
|
|
355
|
+
})),
|
|
356
|
+
};
|
|
357
|
+
});
|
|
345
358
|
onProgress?.('processing');
|
|
346
359
|
const columnsByTable = new Map();
|
|
347
360
|
const constraintsByTable = new Map();
|
|
@@ -35,6 +35,15 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.introspectPostgres = introspectPostgres;
|
|
37
37
|
const config_1 = require("../../../config/config.cjs");
|
|
38
|
+
function parsePgArray(val) {
|
|
39
|
+
if (Array.isArray(val))
|
|
40
|
+
return val;
|
|
41
|
+
if (typeof val === 'string') {
|
|
42
|
+
const trimmed = val.replace(/^\{|\}$/g, '');
|
|
43
|
+
return trimmed ? trimmed.split(',') : [];
|
|
44
|
+
}
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
38
47
|
function parseOptionsArray(options) {
|
|
39
48
|
if (!options)
|
|
40
49
|
return {};
|
|
@@ -328,13 +337,17 @@ async function introspectPostgres(connection, options) {
|
|
|
328
337
|
ORDER BY n.nspname, t.typname
|
|
329
338
|
`);
|
|
330
339
|
onDetailedProgress?.({ step: 'composite_types', count: compositeTypesResult.rows.length, status: 'done' });
|
|
331
|
-
const compositeTypes = compositeTypesResult.rows.map(row =>
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
340
|
+
const compositeTypes = compositeTypesResult.rows.map(row => {
|
|
341
|
+
const names = parsePgArray(row.attribute_names);
|
|
342
|
+
const types = parsePgArray(row.attribute_types);
|
|
343
|
+
return {
|
|
344
|
+
name: row.name,
|
|
345
|
+
attributes: names.map((attrName, i) => ({
|
|
346
|
+
name: attrName,
|
|
347
|
+
type: types[i] || 'unknown',
|
|
348
|
+
})),
|
|
349
|
+
};
|
|
350
|
+
});
|
|
338
351
|
onProgress?.('processing');
|
|
339
352
|
const columnsByTable = new Map();
|
|
340
353
|
const constraintsByTable = new Map();
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
function parsePgArray(val) {
|
|
2
|
+
if (Array.isArray(val))
|
|
3
|
+
return val;
|
|
4
|
+
if (typeof val === 'string') {
|
|
5
|
+
const trimmed = val.replace(/^\{|\}$/g, '');
|
|
6
|
+
return trimmed ? trimmed.split(',') : [];
|
|
7
|
+
}
|
|
8
|
+
return [];
|
|
9
|
+
}
|
|
1
10
|
export async function introspectCockroachDB(connection, options) {
|
|
2
11
|
const { Pool } = await import('pg');
|
|
3
12
|
const { buildPoolConfig } = await import("../../../config/config.js");
|
|
@@ -654,11 +663,15 @@ async function introspectCompositeTypes(pool) {
|
|
|
654
663
|
GROUP BY t.oid, t.typname, n.nspname
|
|
655
664
|
ORDER BY n.nspname, t.typname
|
|
656
665
|
`);
|
|
657
|
-
return result.rows.map((row) =>
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
666
|
+
return result.rows.map((row) => {
|
|
667
|
+
const names = parsePgArray(row.attribute_names);
|
|
668
|
+
const types = parsePgArray(row.attribute_types);
|
|
669
|
+
return {
|
|
670
|
+
name: row.name,
|
|
671
|
+
attributes: names.map((attrName, i) => ({
|
|
672
|
+
name: attrName,
|
|
673
|
+
type: types[i] || 'unknown',
|
|
674
|
+
})),
|
|
675
|
+
};
|
|
676
|
+
});
|
|
664
677
|
}
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
function parsePgArray(val) {
|
|
2
|
+
if (Array.isArray(val))
|
|
3
|
+
return val;
|
|
4
|
+
if (typeof val === 'string') {
|
|
5
|
+
const trimmed = val.replace(/^\{|\}$/g, '');
|
|
6
|
+
return trimmed ? trimmed.split(',') : [];
|
|
7
|
+
}
|
|
8
|
+
return [];
|
|
9
|
+
}
|
|
1
10
|
export async function introspectDsql(connection, options) {
|
|
2
11
|
const { Pool } = await import('pg');
|
|
3
12
|
const { buildPoolConfig } = await import("../../../config/config.js");
|
|
@@ -642,13 +651,17 @@ async function introspectCompositeTypes(pool) {
|
|
|
642
651
|
GROUP BY t.oid, t.typname, n.nspname
|
|
643
652
|
ORDER BY n.nspname, t.typname
|
|
644
653
|
`);
|
|
645
|
-
return result.rows.map((row) =>
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
654
|
+
return result.rows.map((row) => {
|
|
655
|
+
const names = parsePgArray(row.attribute_names);
|
|
656
|
+
const types = parsePgArray(row.attribute_types);
|
|
657
|
+
return {
|
|
658
|
+
name: row.name,
|
|
659
|
+
attributes: names.map((attrName, i) => ({
|
|
660
|
+
name: attrName,
|
|
661
|
+
type: types[i] || 'unknown',
|
|
662
|
+
})),
|
|
663
|
+
};
|
|
664
|
+
});
|
|
652
665
|
}
|
|
653
666
|
catch {
|
|
654
667
|
return [];
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
function parsePgArray(val) {
|
|
2
|
+
if (Array.isArray(val))
|
|
3
|
+
return val;
|
|
4
|
+
if (typeof val === 'string') {
|
|
5
|
+
const trimmed = val.replace(/^\{|\}$/g, '');
|
|
6
|
+
return trimmed ? trimmed.split(',') : [];
|
|
7
|
+
}
|
|
8
|
+
return [];
|
|
9
|
+
}
|
|
1
10
|
export async function introspectNile(connection, options) {
|
|
2
11
|
const { Pool } = await import('pg');
|
|
3
12
|
const { buildPoolConfig } = await import("../../../config/config.js");
|
|
@@ -632,11 +641,15 @@ async function introspectCompositeTypes(pool) {
|
|
|
632
641
|
GROUP BY t.oid, t.typname, n.nspname
|
|
633
642
|
ORDER BY n.nspname, t.typname
|
|
634
643
|
`);
|
|
635
|
-
return result.rows.map((row) =>
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
644
|
+
return result.rows.map((row) => {
|
|
645
|
+
const names = parsePgArray(row.attribute_names);
|
|
646
|
+
const types = parsePgArray(row.attribute_types);
|
|
647
|
+
return {
|
|
648
|
+
name: row.name,
|
|
649
|
+
attributes: names.map((attrName, i) => ({
|
|
650
|
+
name: attrName,
|
|
651
|
+
type: types[i] || 'unknown',
|
|
652
|
+
})),
|
|
653
|
+
};
|
|
654
|
+
});
|
|
642
655
|
}
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
function parsePgArray(val) {
|
|
2
|
+
if (Array.isArray(val))
|
|
3
|
+
return val;
|
|
4
|
+
if (typeof val === 'string') {
|
|
5
|
+
const trimmed = val.replace(/^\{|\}$/g, '');
|
|
6
|
+
return trimmed ? trimmed.split(',') : [];
|
|
7
|
+
}
|
|
8
|
+
return [];
|
|
9
|
+
}
|
|
1
10
|
export async function introspectPostgres(connection, options) {
|
|
2
11
|
const { Pool } = await import('pg');
|
|
3
12
|
const { buildPoolConfig } = await import("../../../config/config.js");
|
|
@@ -636,11 +645,15 @@ async function introspectCompositeTypes(pool) {
|
|
|
636
645
|
GROUP BY t.oid, t.typname, n.nspname
|
|
637
646
|
ORDER BY n.nspname, t.typname
|
|
638
647
|
`);
|
|
639
|
-
return result.rows.map((row) =>
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
648
|
+
return result.rows.map((row) => {
|
|
649
|
+
const names = parsePgArray(row.attribute_names);
|
|
650
|
+
const types = parsePgArray(row.attribute_types);
|
|
651
|
+
return {
|
|
652
|
+
name: row.name,
|
|
653
|
+
attributes: names.map((attrName, i) => ({
|
|
654
|
+
name: attrName,
|
|
655
|
+
type: types[i] || 'unknown',
|
|
656
|
+
})),
|
|
657
|
+
};
|
|
658
|
+
});
|
|
646
659
|
}
|
|
@@ -722,7 +722,7 @@ function generateSequenceCode(seq, useCamelCase) {
|
|
|
722
722
|
if (seq.cycle)
|
|
723
723
|
opts.push(`cycle: true`);
|
|
724
724
|
if (seq.ownedBy)
|
|
725
|
-
opts.push(`ownedBy:
|
|
725
|
+
opts.push(`ownedBy: '${seq.ownedBy.table}.${seq.ownedBy.column}'`);
|
|
726
726
|
const trackingId = seq.trackingId || generateTrackingId('s');
|
|
727
727
|
opts.push(`trackingId: '${trackingId}'`);
|
|
728
728
|
const optsStr = opts.length > 0 ? `, { ${opts.join(', ')} }` : '';
|
|
@@ -739,6 +739,16 @@ export async function introspectedToParsedSchema(schema) {
|
|
|
739
739
|
comment: t.comment,
|
|
740
740
|
});
|
|
741
741
|
}
|
|
742
|
+
const isInternal = (name) => name.startsWith('_relq_') || name.startsWith('__relq_');
|
|
743
|
+
parsed.tables = parsed.tables.filter(t => !isInternal(t.name));
|
|
744
|
+
parsed.sequences = parsed.sequences.filter(s => {
|
|
745
|
+
if (isInternal(s.name))
|
|
746
|
+
return false;
|
|
747
|
+
if (s.ownedBy && isInternal(s.ownedBy.table))
|
|
748
|
+
return false;
|
|
749
|
+
return true;
|
|
750
|
+
});
|
|
751
|
+
parsed.triggers = parsed.triggers.filter(t => !isInternal(t.table));
|
|
742
752
|
return parsed;
|
|
743
753
|
}
|
|
744
754
|
function normalizeTypeName(dataType, udtName) {
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { access, mkdir, readFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
const DOMAIN = "local.relq.studio";
|
|
6
|
+
const CERTS_DIR = join(homedir(), ".relq", "certs");
|
|
7
|
+
const KEY_FILE = `${DOMAIN}-key.pem`;
|
|
8
|
+
const CERT_FILE = `${DOMAIN}.pem`;
|
|
9
|
+
export async function ensureCertificates() {
|
|
10
|
+
const keyPath = join(CERTS_DIR, KEY_FILE);
|
|
11
|
+
const certPath = join(CERTS_DIR, CERT_FILE);
|
|
12
|
+
const status = await checkCertificates();
|
|
13
|
+
if (status.exists && !status.expired) {
|
|
14
|
+
return { key: keyPath, cert: certPath };
|
|
15
|
+
}
|
|
16
|
+
assertMkcertInstalled();
|
|
17
|
+
await mkdir(CERTS_DIR, { recursive: true });
|
|
18
|
+
execSync(`mkcert -key-file "${keyPath}" -cert-file "${certPath}" ${DOMAIN}`, { cwd: CERTS_DIR, stdio: "pipe" });
|
|
19
|
+
return { key: keyPath, cert: certPath };
|
|
20
|
+
}
|
|
21
|
+
export async function checkCertificates() {
|
|
22
|
+
const keyPath = join(CERTS_DIR, KEY_FILE);
|
|
23
|
+
const certPath = join(CERTS_DIR, CERT_FILE);
|
|
24
|
+
try {
|
|
25
|
+
await access(keyPath);
|
|
26
|
+
await access(certPath);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return { exists: false, expired: false };
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const certContent = await readFile(certPath, "utf-8");
|
|
33
|
+
const expiresAt = parseCertificateExpiry(certContent);
|
|
34
|
+
if (expiresAt && expiresAt.getTime() < Date.now()) {
|
|
35
|
+
return { exists: true, expired: true, expiresAt };
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
exists: true,
|
|
39
|
+
expired: false,
|
|
40
|
+
paths: { key: keyPath, cert: certPath },
|
|
41
|
+
expiresAt: expiresAt ?? undefined,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return { exists: true, expired: true };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export function getMkcertCaRoot() {
|
|
49
|
+
try {
|
|
50
|
+
const output = execSync("mkcert -CAROOT", { encoding: "utf-8" });
|
|
51
|
+
return output.trim();
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export function isMkcertInstalled() {
|
|
58
|
+
try {
|
|
59
|
+
execSync("mkcert -version", { stdio: "ignore" });
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export function getMkcertInstallInstructions() {
|
|
67
|
+
return [
|
|
68
|
+
"mkcert is required for local HTTPS.",
|
|
69
|
+
"",
|
|
70
|
+
"Install mkcert:",
|
|
71
|
+
" macOS: brew install mkcert nss",
|
|
72
|
+
" Linux: sudo apt install mkcert",
|
|
73
|
+
" Windows: choco install mkcert",
|
|
74
|
+
"",
|
|
75
|
+
"Then install the local CA:",
|
|
76
|
+
" mkcert -install",
|
|
77
|
+
].join("\n");
|
|
78
|
+
}
|
|
79
|
+
function assertMkcertInstalled() {
|
|
80
|
+
if (!isMkcertInstalled()) {
|
|
81
|
+
throw new Error(getMkcertInstallInstructions());
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function parseCertificateExpiry(pemContent) {
|
|
85
|
+
try {
|
|
86
|
+
const { X509Certificate } = require("node:crypto");
|
|
87
|
+
const cert = new X509Certificate(pemContent);
|
|
88
|
+
return new Date(cert.validTo);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import { buildPoolConfig } from "../../../config/config.js";
|
|
2
|
+
function parsePgArray(val) {
|
|
3
|
+
if (Array.isArray(val))
|
|
4
|
+
return val;
|
|
5
|
+
if (typeof val === 'string') {
|
|
6
|
+
const trimmed = val.replace(/^\{|\}$/g, '');
|
|
7
|
+
return trimmed ? trimmed.split(',') : [];
|
|
8
|
+
}
|
|
9
|
+
return [];
|
|
10
|
+
}
|
|
2
11
|
function mapInternalType(internalType) {
|
|
3
12
|
const typeMap = {
|
|
4
13
|
'int2': 'smallint',
|
|
@@ -283,13 +292,17 @@ export async function introspectCockroachDB(connection, options) {
|
|
|
283
292
|
ORDER BY n.nspname, t.typname
|
|
284
293
|
`);
|
|
285
294
|
onDetailedProgress?.({ step: 'composite_types', count: compositeTypesResult.rows.length, status: 'done' });
|
|
286
|
-
const compositeTypes = compositeTypesResult.rows.map(row =>
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
295
|
+
const compositeTypes = compositeTypesResult.rows.map(row => {
|
|
296
|
+
const names = parsePgArray(row.attribute_names);
|
|
297
|
+
const types = parsePgArray(row.attribute_types);
|
|
298
|
+
return {
|
|
299
|
+
name: row.name,
|
|
300
|
+
attributes: names.map((attrName, i) => ({
|
|
301
|
+
name: attrName,
|
|
302
|
+
type: types[i] || 'unknown',
|
|
303
|
+
})),
|
|
304
|
+
};
|
|
305
|
+
});
|
|
293
306
|
onProgress?.('processing');
|
|
294
307
|
const columnsByTable = new Map();
|
|
295
308
|
const constraintsByTable = new Map();
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import { buildPoolConfig } from "../../../config/config.js";
|
|
2
|
+
function parsePgArray(val) {
|
|
3
|
+
if (Array.isArray(val))
|
|
4
|
+
return val;
|
|
5
|
+
if (typeof val === 'string') {
|
|
6
|
+
const trimmed = val.replace(/^\{|\}$/g, '');
|
|
7
|
+
return trimmed ? trimmed.split(',') : [];
|
|
8
|
+
}
|
|
9
|
+
return [];
|
|
10
|
+
}
|
|
2
11
|
function mapInternalType(internalType) {
|
|
3
12
|
const typeMap = {
|
|
4
13
|
'int2': 'smallint',
|
|
@@ -270,13 +279,17 @@ export async function introspectDsql(connection, options) {
|
|
|
270
279
|
GROUP BY t.oid, t.typname, n.nspname
|
|
271
280
|
ORDER BY n.nspname, t.typname
|
|
272
281
|
`);
|
|
273
|
-
compositeTypes = compositeTypesResult.rows.map(row =>
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
282
|
+
compositeTypes = compositeTypesResult.rows.map(row => {
|
|
283
|
+
const names = parsePgArray(row.attribute_names);
|
|
284
|
+
const types = parsePgArray(row.attribute_types);
|
|
285
|
+
return {
|
|
286
|
+
name: row.name,
|
|
287
|
+
attributes: names.map((attrName, i) => ({
|
|
288
|
+
name: attrName,
|
|
289
|
+
type: types[i] || 'unknown',
|
|
290
|
+
})),
|
|
291
|
+
};
|
|
292
|
+
});
|
|
280
293
|
}
|
|
281
294
|
catch {
|
|
282
295
|
}
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import { buildPoolConfig } from "../../../config/config.js";
|
|
2
|
+
function parsePgArray(val) {
|
|
3
|
+
if (Array.isArray(val))
|
|
4
|
+
return val;
|
|
5
|
+
if (typeof val === 'string') {
|
|
6
|
+
const trimmed = val.replace(/^\{|\}$/g, '');
|
|
7
|
+
return trimmed ? trimmed.split(',') : [];
|
|
8
|
+
}
|
|
9
|
+
return [];
|
|
10
|
+
}
|
|
2
11
|
const NILE_SYSTEM_TABLES = ['tenants', 'tenant_users'];
|
|
3
12
|
const NILE_SYSTEM_SCHEMAS = ['nile', 'pg_catalog', 'information_schema', 'pg_toast'];
|
|
4
13
|
function parseOptionsArray(options) {
|
|
@@ -299,13 +308,17 @@ export async function introspectNile(connection, options) {
|
|
|
299
308
|
ORDER BY n.nspname, t.typname
|
|
300
309
|
`);
|
|
301
310
|
onDetailedProgress?.({ step: 'composite_types', count: compositeTypesResult.rows.length, status: 'done' });
|
|
302
|
-
const compositeTypes = compositeTypesResult.rows.map(row =>
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
311
|
+
const compositeTypes = compositeTypesResult.rows.map(row => {
|
|
312
|
+
const names = parsePgArray(row.attribute_names);
|
|
313
|
+
const types = parsePgArray(row.attribute_types);
|
|
314
|
+
return {
|
|
315
|
+
name: row.name,
|
|
316
|
+
attributes: names.map((attrName, i) => ({
|
|
317
|
+
name: attrName,
|
|
318
|
+
type: types[i] || 'unknown',
|
|
319
|
+
})),
|
|
320
|
+
};
|
|
321
|
+
});
|
|
309
322
|
onProgress?.('processing');
|
|
310
323
|
const columnsByTable = new Map();
|
|
311
324
|
const constraintsByTable = new Map();
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import { buildPoolConfig } from "../../../config/config.js";
|
|
2
|
+
function parsePgArray(val) {
|
|
3
|
+
if (Array.isArray(val))
|
|
4
|
+
return val;
|
|
5
|
+
if (typeof val === 'string') {
|
|
6
|
+
const trimmed = val.replace(/^\{|\}$/g, '');
|
|
7
|
+
return trimmed ? trimmed.split(',') : [];
|
|
8
|
+
}
|
|
9
|
+
return [];
|
|
10
|
+
}
|
|
2
11
|
function parseOptionsArray(options) {
|
|
3
12
|
if (!options)
|
|
4
13
|
return {};
|
|
@@ -292,13 +301,17 @@ export async function introspectPostgres(connection, options) {
|
|
|
292
301
|
ORDER BY n.nspname, t.typname
|
|
293
302
|
`);
|
|
294
303
|
onDetailedProgress?.({ step: 'composite_types', count: compositeTypesResult.rows.length, status: 'done' });
|
|
295
|
-
const compositeTypes = compositeTypesResult.rows.map(row =>
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
304
|
+
const compositeTypes = compositeTypesResult.rows.map(row => {
|
|
305
|
+
const names = parsePgArray(row.attribute_names);
|
|
306
|
+
const types = parsePgArray(row.attribute_types);
|
|
307
|
+
return {
|
|
308
|
+
name: row.name,
|
|
309
|
+
attributes: names.map((attrName, i) => ({
|
|
310
|
+
name: attrName,
|
|
311
|
+
type: types[i] || 'unknown',
|
|
312
|
+
})),
|
|
313
|
+
};
|
|
314
|
+
});
|
|
302
315
|
onProgress?.('processing');
|
|
303
316
|
const columnsByTable = new Map();
|
|
304
317
|
const constraintsByTable = new Map();
|