@dbml/connector 5.5.1-alpha.0 → 5.6.0-alpha.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/eslint.config.ts CHANGED
@@ -19,7 +19,7 @@ export default defineConfig(
19
19
  'node_modules/*',
20
20
  'dist/*',
21
21
  'eslint.config.ts',
22
- 'jest.config.ts',
22
+ 'vite.config.ts',
23
23
  '**/*.test.ts',
24
24
  '__tests__/**/*',
25
25
  ],
@@ -32,6 +32,7 @@ export default defineConfig(
32
32
  sourceType: 'module',
33
33
  ecmaVersion: 2020,
34
34
  project: './tsconfig.json',
35
+ tsconfigRootDir: import.meta.dirname,
35
36
  },
36
37
  },
37
38
  plugins: {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package",
3
3
  "name": "@dbml/connector",
4
- "version": "5.5.1-alpha.0",
4
+ "version": "5.6.0-alpha.0",
5
5
  "description": "This package was created to fetch the schema JSON from many kind of databases.",
6
6
  "author": "huy.phung.sw@gmail.com",
7
7
  "license": "MIT",
@@ -10,42 +10,25 @@
10
10
  "main": "dist/index.js",
11
11
  "types": "dist/index.d.ts",
12
12
  "devDependencies": {
13
- "@babel/core": "^7.21.4",
14
- "@babel/node": "^7.20.7",
15
- "@babel/plugin-transform-runtime": "^7.21.4",
16
- "@babel/preset-env": "^7.21.4",
17
- "@babel/preset-typescript": "^7.24.7",
18
- "@babel/runtime": "^7.21.0",
19
- "@stylistic/eslint-plugin": "^5.5.0",
20
- "@types/jest": "^29.5.12",
21
13
  "@types/lodash": "^4.17.16",
22
14
  "@types/mssql": "^9.1.5",
23
15
  "@types/node": "^22.2.0",
24
16
  "@types/oracledb": "^6.10.0",
25
17
  "@types/pg": "^8.11.6",
26
- "@typescript-eslint/eslint-plugin": "^8.46.3",
27
- "@typescript-eslint/parser": "^8.46.3",
28
- "babel-jest": "^29.5.0",
29
- "eslint": "^9.39.1",
30
- "jest": "^29.7.0",
31
- "ts-jest": "^29.2.4",
32
- "ts-node": "^10.9.2",
33
- "typescript": "^5.9.3",
34
- "typescript-eslint": "^8.46.3"
18
+ "ts-node": "^10.9.2"
35
19
  },
36
20
  "scripts": {
37
- "build": "tsc",
21
+ "build": "rm -rf ./dist && vite build",
22
+ "dev": "vite build --watch",
38
23
  "start": "node dist/index.js",
39
- "test": "jest --coverage=false",
40
- "coverage": "jest --coverage",
24
+ "test:watch": "vitest tests",
25
+ "test": "vitest",
26
+ "coverage": "vitest --coverage",
41
27
  "lint": "eslint .",
42
28
  "lint:fix": "eslint --fix ."
43
29
  },
44
30
  "dependencies": {
45
31
  "@google-cloud/bigquery": "^7.9.0",
46
- "@typescript-eslint/eslint-plugin": "^8.46.3",
47
- "@typescript-eslint/parser": "^8.46.3",
48
- "eslint": "^9.39.1",
49
32
  "lodash": "^4.17.21",
50
33
  "mssql": "^11.0.1",
51
34
  "mysql2": "^3.11.0",
@@ -56,5 +39,5 @@
56
39
  "engines": {
57
40
  "node": ">=18"
58
41
  },
59
- "gitHead": "355cbecd2d809b9750aea2854fbc85bf469db257"
42
+ "gitHead": "e0cd0b00099344c05af0a83e30c2c9c2ccc325cc"
60
43
  }
package/vite.config.ts ADDED
@@ -0,0 +1,53 @@
1
+ /// <reference types="vitest" />
2
+
3
+ import path from 'path';
4
+ import { defineConfig } from 'vite';
5
+ import dts from 'vite-plugin-dts';
6
+
7
+ export default defineConfig({
8
+ plugins: [
9
+ dts({ insertTypesEntry: true }),
10
+ ],
11
+ resolve: {
12
+ alias: {
13
+ "@": path.resolve(__dirname, "src/"),
14
+ },
15
+ },
16
+ build: {
17
+ target: 'node18',
18
+ lib: {
19
+ entry: path.resolve(__dirname, 'src/index.ts'),
20
+ fileName: 'index',
21
+ formats: ['cjs', 'es'],
22
+ },
23
+ rollupOptions: {
24
+ external: [
25
+ '@google-cloud/bigquery',
26
+ 'lodash',
27
+ 'mssql',
28
+ 'mysql2',
29
+ 'oracledb',
30
+ 'pg',
31
+ 'snowflake-sdk',
32
+ 'fs',
33
+ 'fs/promises',
34
+ 'path',
35
+ 'crypto',
36
+ 'stream',
37
+ 'util',
38
+ 'timers',
39
+ 'zlib',
40
+ 'tls',
41
+ 'url',
42
+ 'net',
43
+ ],
44
+ },
45
+ },
46
+ test: {
47
+ globals: true,
48
+ coverage: {
49
+ provider: 'v8',
50
+ reporter: ['json', 'json-summary', 'html', 'text'],
51
+ },
52
+ },
53
+ });
package/.babelrc DELETED
@@ -1,13 +0,0 @@
1
- {
2
- "presets": [
3
- "@babel/preset-env",
4
- "@babel/preset-typescript"
5
- ],
6
- "env": {
7
- "test": {
8
- "plugins": [
9
- "@babel/plugin-transform-runtime"
10
- ]
11
- }
12
- }
13
- }
@@ -1,291 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fetchSchemaJson = fetchSchemaJson;
4
- const bigquery_1 = require("@google-cloud/bigquery");
5
- const credential_loader_1 = require("../../utils/credential-loader");
6
- const helpers_1 = require("../../utils/helpers");
7
- // BigQuery client authentication strategy:
8
- // 1. Start with environment/default authentication:
9
- // - GOOGLE_APPLICATION_CREDENTIALS env variable (service account key file)
10
- // - Application Default Credentials (gcloud auth application-default login)
11
- //
12
- // 2. Override with explicitly provided properties:
13
- // - If projectId is provided, override the default project
14
- // - If credentials (clientEmail + privateKey) provided, override auth method
15
- // - If datasets specified in config, fetch only those datasets
16
- async function connectBigQuery(credential) {
17
- const config = {};
18
- // Override project if explicitly provided
19
- if (credential?.projectId) {
20
- config.projectId = credential.projectId;
21
- }
22
- // Override credentials if both email and key are provided
23
- if (credential?.credentials?.clientEmail && credential?.credentials?.privateKey) {
24
- config.credentials = {
25
- client_email: credential.credentials.clientEmail,
26
- private_key: credential.credentials.privateKey,
27
- };
28
- }
29
- // Create client with merged config
30
- const client = new bigquery_1.BigQuery(config);
31
- try {
32
- const query = 'SELECT CURRENT_DATE() as today';
33
- await client.query(query);
34
- return client;
35
- }
36
- catch (error) {
37
- if (error instanceof Error) {
38
- throw new Error(`BigQuery connection error: ${error.message}`);
39
- }
40
- throw error;
41
- }
42
- }
43
- function getDbDefault(columnDefault, defaultValueType) {
44
- if (defaultValueType === null) {
45
- return null;
46
- }
47
- if (defaultValueType === 'string') {
48
- // bigquery store the double qoutes and quotes at the end and the beginning of the string: "string" or 'string'
49
- return { value: columnDefault.slice(1, -1).replaceAll('\'', '\''), type: defaultValueType };
50
- }
51
- return { value: columnDefault, type: defaultValueType };
52
- }
53
- function generateField(row) {
54
- const { columnName, isNullable, dataType, columnDefault, columnDefaultType, columnDescription, } = row;
55
- const fieldDefaultValue = getDbDefault(columnDefault, columnDefaultType);
56
- const field = {
57
- name: columnName,
58
- not_null: isNullable === 'NO',
59
- type: {
60
- type_name: dataType,
61
- schemaName: null,
62
- },
63
- note: {
64
- value: columnDescription || '',
65
- },
66
- dbdefault: fieldDefaultValue,
67
- increment: false, // bigquery does not support native auto-increment
68
- };
69
- return field;
70
- }
71
- async function generateTablesAndFields(client, projectId, datasetId) {
72
- const tableNameToFetch = `${projectId}.${datasetId}`;
73
- const query = `
74
- with
75
- table_comments as (
76
- select table_schema, table_name, option_value as description
77
- from ${tableNameToFetch}.INFORMATION_SCHEMA.TABLE_OPTIONS
78
- where option_name = 'description'
79
- ),
80
- column_comments as (
81
- select column_name, table_schema, table_name, description
82
- from ${tableNameToFetch}.INFORMATION_SCHEMA.COLUMN_FIELD_PATHS
83
- where column_name = field_path
84
- )
85
- select
86
- t.table_schema as tableSchema,
87
- t.table_name as tableName,
88
- (select description from table_comments tc where tc.table_name = t.table_name and tc.table_schema = t.table_schema) as tableDescription,
89
- (
90
- select description
91
- from column_comments cc
92
- where cc.table_name = c.table_name and cc.table_schema = c.table_schema and cc.column_name = c.column_name
93
- ) as columnDescription,
94
- c.column_name as columnName,
95
- c.is_nullable as isNullable,
96
- c.data_type as dataType,
97
- c.column_default as columnDefault,
98
- CASE
99
- WHEN SAFE_CAST(c.column_default AS FLOAT64) IS NOT NULL THEN 'number'
100
- WHEN SAFE_CAST(c.column_default AS BOOLEAN) IS NOT NULL THEN 'boolean'
101
- -- string value is enclosed in single quotes, double quotes
102
- WHEN REGEXP_CONTAINS(c.column_default, r"^['\\"].*['\\"]$") THEN 'string'
103
- WHEN c.column_default = "NULL" THEN NULL
104
- ELSE 'expression'
105
- END AS columnDefaultType
106
- from ${tableNameToFetch}.INFORMATION_SCHEMA.TABLES t
107
- join ${tableNameToFetch}.INFORMATION_SCHEMA.COLUMNS c
108
- on
109
- t.table_schema = c.table_schema
110
- and t.table_name = c.table_name
111
- where t.table_type = 'BASE TABLE'
112
- order by t.creation_time, c.ordinal_position;
113
- `;
114
- const [queryResult] = await client.query({ query });
115
- const tableMap = {};
116
- const fieldMap = {};
117
- queryResult.forEach((row) => {
118
- const { tableSchema, tableName, tableComment } = row;
119
- const key = (0, helpers_1.getTableSchemaKey)(tableSchema, tableName);
120
- if (!tableMap[key]) {
121
- tableMap[key] = {
122
- name: tableName,
123
- note: { value: tableComment || '' },
124
- schemaName: tableSchema,
125
- };
126
- }
127
- if (!fieldMap[key]) {
128
- fieldMap[key] = [];
129
- }
130
- const field = generateField(row);
131
- fieldMap[key].push(field);
132
- });
133
- return {
134
- tables: Object.values(tableMap),
135
- fieldMap,
136
- };
137
- }
138
- async function generateIndexes(client, projectId, datasetId) {
139
- const tableNameToFetch = `${projectId}.${datasetId}`;
140
- const query = `
141
- select
142
- s.index_schema as indexSchema,
143
- s.table_name as tableName,
144
- s.index_name as indexName,
145
- string_agg(sc.index_column_name, ', ') as indexColumnNames
146
- from ${tableNameToFetch}.INFORMATION_SCHEMA.SEARCH_INDEXES s
147
- join ${tableNameToFetch}.INFORMATION_SCHEMA.SEARCH_INDEX_COLUMNS sc
148
- on
149
- s.index_schema = sc.index_schema
150
- and s.table_name = sc.table_name
151
- and s.index_name = sc.index_name
152
- where sc.index_column_name = sc.index_field_path
153
- group by s.index_schema, s.table_name, s.index_name
154
- order by s.table_name, s.index_name;
155
- `;
156
- const [queryResult] = await client.query(query);
157
- const indexMap = {};
158
- queryResult.forEach((row) => {
159
- const { indexSchema, tableName, indexName, indexColumnNames, } = row;
160
- const indexColumns = indexColumnNames.split(', ').map((column) => {
161
- return {
162
- type: 'column',
163
- value: column,
164
- };
165
- });
166
- const index = {
167
- name: indexName,
168
- type: '',
169
- columns: [...indexColumns],
170
- };
171
- const key = (0, helpers_1.getTableSchemaKey)(indexSchema, tableName);
172
- if (indexMap[key]) {
173
- indexMap[key].push(index);
174
- }
175
- else {
176
- indexMap[key] = [index];
177
- }
178
- });
179
- return indexMap;
180
- }
181
- async function generateTableConstraints(client, projectId, datasetId) {
182
- const tableNameToFetch = `${projectId}.${datasetId}`;
183
- const query = `
184
- select
185
- tc.table_schema as tableSchema,
186
- tc.table_name as tableName,
187
- tc.constraint_name as constraintName,
188
- string_agg(kcu.column_name, ', ') as columnNames,
189
- count(kcu.column_name) as columnCount,
190
- from ${tableNameToFetch}.INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
191
- join ${tableNameToFetch}.INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu
192
- on
193
- tc.table_schema = kcu.table_schema
194
- and tc.table_name = kcu.table_name
195
- and tc.constraint_name = kcu.constraint_name
196
- where tc.constraint_type = 'PRIMARY KEY'
197
- group by tc.table_schema, tc.table_name, tc.constraint_name
198
- order by tc.table_name, tc.constraint_name;
199
- `;
200
- const [queryResult] = await client.query(query);
201
- const inlinePrimaryKeyList = queryResult.filter((row) => row.columnCount && row.columnCount === 1);
202
- const compositePrimaryKeyList = queryResult.filter((row) => row.columnCount && row.columnCount > 1);
203
- const inlinePrimaryKeyMap = {};
204
- inlinePrimaryKeyList.forEach((inlineKey) => {
205
- const { tableSchema, tableName, columnNames } = inlineKey;
206
- const key = (0, helpers_1.getTableSchemaKey)(tableSchema, tableName);
207
- if (!inlinePrimaryKeyMap[key]) {
208
- inlinePrimaryKeyMap[key] = {};
209
- }
210
- if (!inlinePrimaryKeyMap[key][columnNames]) {
211
- inlinePrimaryKeyMap[key][columnNames] = { checks: [] };
212
- }
213
- inlinePrimaryKeyMap[key][columnNames].pk = true;
214
- });
215
- const compositePrimaryKeyMap = {};
216
- compositePrimaryKeyList.forEach((compositeKey) => {
217
- const { tableSchema, tableName, constraintName, columnNames, } = compositeKey;
218
- const indexColumns = columnNames.split(', ').map((column) => {
219
- return {
220
- type: 'column',
221
- value: column,
222
- };
223
- });
224
- const index = {
225
- name: constraintName,
226
- type: '',
227
- columns: [...indexColumns],
228
- pk: true,
229
- };
230
- const key = (0, helpers_1.getTableSchemaKey)(tableSchema, tableName);
231
- if (compositePrimaryKeyMap[key]) {
232
- compositePrimaryKeyMap[key].push(index);
233
- }
234
- else {
235
- compositePrimaryKeyMap[key] = [index];
236
- }
237
- });
238
- return {
239
- inlinePrimaryKeyMap,
240
- compositePrimaryKeyMap,
241
- };
242
- }
243
- async function validateDatasetIdList(client, datasetIdList) {
244
- const [datasets] = await client.getDatasets();
245
- const rawDatasetIdList = datasets.map((dataset) => dataset.id).filter((id) => id);
246
- // If user does not specify which datasets to be fetched, we will fetch all datasets
247
- if (datasetIdList.length === 0) {
248
- return rawDatasetIdList;
249
- }
250
- return (0, helpers_1.getIntersection)(datasetIdList, rawDatasetIdList);
251
- }
252
- async function fetchSchemaJsonByDataset(client, projectId, datasetId) {
253
- const res = await Promise.all([
254
- generateTablesAndFields(client, projectId, datasetId),
255
- generateIndexes(client, projectId, datasetId),
256
- generateTableConstraints(client, projectId, datasetId),
257
- ]);
258
- const { tables, fieldMap } = res[0];
259
- const indexMap = res[1];
260
- const { inlinePrimaryKeyMap, compositePrimaryKeyMap } = res[2];
261
- return {
262
- tables,
263
- fields: fieldMap,
264
- refs: [],
265
- enums: [],
266
- indexes: (0, helpers_1.mergeIndexDictionary)(indexMap, compositePrimaryKeyMap),
267
- tableConstraints: inlinePrimaryKeyMap,
268
- // Checks are not supported in BigQuery
269
- checks: {},
270
- };
271
- }
272
- async function fetchSchemaJson(keyFilename) {
273
- const rawCredential = await (0, credential_loader_1.loadCredentialFromFile)(keyFilename);
274
- const credentialJson = (0, credential_loader_1.parseBigQueryCredential)(rawCredential);
275
- const client = await connectBigQuery(credentialJson);
276
- const projectId = await client.getProjectId();
277
- const datasetIdList = await validateDatasetIdList(client, credentialJson.datasets);
278
- const querySchemaPromises = datasetIdList.map((dataset) => fetchSchemaJsonByDataset(client, projectId, dataset));
279
- const databaseSchemaRes = await Promise.all(querySchemaPromises);
280
- const res = databaseSchemaRes.reduce((acc, dbSchema) => {
281
- const { tables, fields, indexes, tableConstraints, } = dbSchema;
282
- acc.tables = (0, helpers_1.mergeTables)(acc.tables, tables);
283
- acc.fields = (0, helpers_1.mergeFieldDictionary)(acc.fields, fields);
284
- acc.indexes = (0, helpers_1.mergeIndexDictionary)(acc.indexes, indexes);
285
- acc.tableConstraints = (0, helpers_1.mergeTableConstraintDictionary)(acc.tableConstraints, tableConstraints);
286
- return acc;
287
- }, {
288
- tables: [], fields: {}, tableConstraints: {}, indexes: {}, refs: [], enums: [], checks: {},
289
- });
290
- return res;
291
- }
@@ -1,28 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fetchSchemaJson = void 0;
4
- const postgres_1 = require("./postgres");
5
- const mssql_1 = require("./mssql");
6
- const mysql_1 = require("./mysql");
7
- const snowflake_1 = require("./snowflake");
8
- const bigquery_1 = require("./bigquery");
9
- const oracle_1 = require("./oracle");
10
- const fetchSchemaJson = async (connection, databaseType) => {
11
- switch (databaseType) {
12
- case 'postgres':
13
- return (0, postgres_1.fetchSchemaJson)(connection);
14
- case 'mssql':
15
- return (0, mssql_1.fetchSchemaJson)(connection);
16
- case 'mysql':
17
- return (0, mysql_1.fetchSchemaJson)(connection);
18
- case 'snowflake':
19
- return (0, snowflake_1.fetchSchemaJson)(connection);
20
- case 'bigquery':
21
- return (0, bigquery_1.fetchSchemaJson)(connection);
22
- case 'oracle':
23
- return (0, oracle_1.fetchSchemaJson)(connection);
24
- default:
25
- throw new Error(`Unsupported database type: ${databaseType}`);
26
- }
27
- };
28
- exports.fetchSchemaJson = fetchSchemaJson;