introspeql 0.0.1 → 0.0.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/README.md +106 -7
- package/dist/append-schema.d.ts +7 -1
- package/dist/append-schema.js +45 -41
- package/dist/generate-types.d.ts +8 -0
- package/dist/{introspect-database.js → generate-types.js} +36 -27
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/introspect-columns.d.ts +10 -11
- package/dist/introspect-columns.js +8 -1
- package/dist/introspect-enum.d.ts +11 -1
- package/dist/introspect-enum.js +8 -1
- package/dist/introspect-procedures.d.ts +13 -5
- package/dist/introspect-procedures.js +39 -5
- package/dist/introspect-tables.d.ts +12 -8
- package/dist/introspect-tables.js +15 -4
- package/dist/introspeql-config.d.ts +15 -18
- package/dist/introspeql-config.js +63 -24
- package/dist/prepare-data-for-writing.d.ts +16 -5
- package/dist/prepare-data-for-writing.js +25 -14
- package/dist/snake-case-to-pascal-case.d.ts +7 -0
- package/dist/snake-case-to-pascal-case.js +12 -5
- package/dist/write-header.d.ts +1 -1
- package/dist/write-header.js +5 -5
- package/package.json +4 -2
- package/dist/introspect-database.d.ts +0 -2
package/README.md
CHANGED
|
@@ -1,14 +1,113 @@
|
|
|
1
1
|
# IntrospeQL
|
|
2
2
|
|
|
3
|
-
IntrospeQL is a
|
|
4
|
-
and generating TypeScript types from them.
|
|
5
|
-
make
|
|
6
|
-
easy, comprehensive, and accurate.
|
|
3
|
+
IntrospeQL is a TypeScript utility for **introspecting PostgreSQL** schemas
|
|
4
|
+
(tables, functions, types) and **generating TypeScript types** from them.
|
|
5
|
+
The aim is to make capturing PostgreSQL types and producing matching TypeScript
|
|
6
|
+
types easy, comprehensive, and accurate.
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
> ⚠️ **Warning: Alpha / Not Production Ready**
|
|
9
|
+
> This library is currently in alpha. Use with caution; it is not yet hardened
|
|
10
|
+
> for production.
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- Connects to a PostgreSQL database and introspects table schemas, functions,
|
|
15
|
+
and enums.
|
|
16
|
+
- Automatically emits corresponding TypeScript types.
|
|
17
|
+
- Converts PostgreSQL types to TypeScript types in alignment with Node-Postgres
|
|
18
|
+
defaults. Can be extended to convert PostgreSQL types to any desired TypeScript
|
|
19
|
+
type.
|
|
20
|
+
|
|
21
|
+
## Example Usage
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
// src/scripts/gen-db-types.ts
|
|
25
|
+
import 'dotenv/config';
|
|
26
|
+
import path from 'node:path';
|
|
27
|
+
import { generateTypes, type IntrospeQLConfig } from 'introspeql';
|
|
28
|
+
|
|
29
|
+
const config: IntrospeQLConfig = {
|
|
30
|
+
dbConnectionString: process.env.DATABASE_URL,
|
|
31
|
+
outFile: path.join(import.meta.dirname, '../db/types.ts'),
|
|
32
|
+
schemas: ['public'],
|
|
33
|
+
ignoreTables: [
|
|
34
|
+
{
|
|
35
|
+
schema: 'public',
|
|
36
|
+
name: 'spatial_ref_sys',
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
header:
|
|
40
|
+
`
|
|
41
|
+
// Custom header:
|
|
42
|
+
import type { Geography } from '../model/geography';
|
|
43
|
+
`
|
|
44
|
+
types: {
|
|
45
|
+
'public.geography': 'Geography'
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
generateTypes(config);
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Example Output
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
// src/db/types.ts
|
|
56
|
+
|
|
57
|
+
/* This file was generated by IntrospeQL. Do not edit manually. */
|
|
58
|
+
// Custom header:
|
|
59
|
+
import type { Geography } from '../model/geography';
|
|
60
|
+
|
|
61
|
+
export namespace Public {
|
|
62
|
+
export const SchemaName = 'public';
|
|
63
|
+
|
|
64
|
+
export namespace Enums {
|
|
65
|
+
export enum DistanceUnits {
|
|
66
|
+
METERS = 'METERS',
|
|
67
|
+
KILOMETERS = 'KILOMETERS',
|
|
68
|
+
MILES = 'MILES',
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export namespace Tables {
|
|
73
|
+
export namespace StoreLocations {
|
|
74
|
+
export const TableName = 'store_locations';
|
|
75
|
+
|
|
76
|
+
export enum ColumnNames {
|
|
77
|
+
Id = 'id',
|
|
78
|
+
Coordinates = 'coordinates',
|
|
79
|
+
StreetAddressLine1 = 'street_address_line_1',
|
|
80
|
+
StreetAddressLine2 = 'street_address_line_2',
|
|
81
|
+
City = 'city',
|
|
82
|
+
State = 'state',
|
|
83
|
+
ZipCode = 'zip_code',
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface RowType {
|
|
87
|
+
[ColumnNames.Id]: number;
|
|
88
|
+
[ColumnNames.Coordinates]: Geography;
|
|
89
|
+
[ColumnNames.StreetAddressLine1]: string;
|
|
90
|
+
[ColumnNames.StreetAddressLine2]: string | null;
|
|
91
|
+
[ColumnNames.City]: string;
|
|
92
|
+
[ColumnNames.State]: string;
|
|
93
|
+
[ColumnNames.ZipCode]: string;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export namespace Procedures {
|
|
99
|
+
export namespace DistanceWithUnits {
|
|
100
|
+
export const ProcedureName = 'distance_with_units';
|
|
101
|
+
|
|
102
|
+
export const ArgNames = ['point_a', 'point_b', 'units'] as const;
|
|
103
|
+
|
|
104
|
+
export type ArgTypes = [Geography, Geography, Enums.DistanceUnits];
|
|
105
|
+
|
|
106
|
+
export type ReturnType = number;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
12
111
|
|
|
13
112
|
## License
|
|
14
113
|
|
package/dist/append-schema.d.ts
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
|
-
import type { Schema } from
|
|
1
|
+
import type { Schema } from './prepare-data-for-writing';
|
|
2
|
+
/**
|
|
3
|
+
* Appends a namespace representing the given schema to the provided output file.
|
|
4
|
+
*
|
|
5
|
+
* @param outputPath
|
|
6
|
+
* @param schema
|
|
7
|
+
*/
|
|
2
8
|
export declare function appendSchema(outputPath: string, schema: Schema): void;
|
package/dist/append-schema.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import fs from
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
/**
|
|
3
|
+
* Appends a namespace representing the given schema to the provided output file.
|
|
4
|
+
*
|
|
5
|
+
* @param outputPath
|
|
6
|
+
* @param schema
|
|
7
|
+
*/
|
|
2
8
|
export function appendSchema(outputPath, schema) {
|
|
3
9
|
let schemaTypeDefinitions = `export namespace ${schema.formattedName} {\n`;
|
|
4
10
|
schemaTypeDefinitions += ` export const SchemaName = '${schema.rawName}';\n`;
|
|
@@ -12,81 +18,79 @@ export function appendSchema(outputPath, schema) {
|
|
|
12
18
|
schemaTypeDefinitions = appendProcedures(schemaTypeDefinitions, schema);
|
|
13
19
|
}
|
|
14
20
|
schemaTypeDefinitions += `};`;
|
|
15
|
-
fs.appendFileSync(outputPath, schemaTypeDefinitions,
|
|
21
|
+
fs.appendFileSync(outputPath, schemaTypeDefinitions, 'utf-8');
|
|
16
22
|
}
|
|
17
23
|
function appendEnums(typeDefs, schema) {
|
|
18
|
-
typeDefs +=
|
|
24
|
+
typeDefs += '\n' + ' '.repeat(2) + 'export namespace Enums {';
|
|
19
25
|
for (const e of schema.enums) {
|
|
20
|
-
typeDefs +=
|
|
21
|
-
typeDefs += e.values
|
|
22
|
-
|
|
23
|
-
.join("");
|
|
24
|
-
typeDefs += " ".repeat(4) + "};\n";
|
|
26
|
+
typeDefs += '\n' + ' '.repeat(4) + `export enum ${e.name} {\n`;
|
|
27
|
+
typeDefs += e.values.map(v => ' '.repeat(6) + `${v} = '${v}',\n`).join('');
|
|
28
|
+
typeDefs += ' '.repeat(4) + '};\n';
|
|
25
29
|
}
|
|
26
|
-
typeDefs +=
|
|
30
|
+
typeDefs += ' '.repeat(2) + `};\n`;
|
|
27
31
|
return typeDefs;
|
|
28
32
|
}
|
|
29
33
|
function appendTables(typeDefs, schema) {
|
|
30
|
-
typeDefs +=
|
|
34
|
+
typeDefs += '\n' + ' '.repeat(2) + 'export namespace Tables {';
|
|
31
35
|
for (const t of schema.tables) {
|
|
32
36
|
typeDefs +=
|
|
33
|
-
|
|
34
|
-
typeDefs +=
|
|
37
|
+
'\n' + ' '.repeat(4) + `export namespace ${t.formattedName} {\n`;
|
|
38
|
+
typeDefs += ' '.repeat(6) + `export const TableName = '${t.rawName}';\n`;
|
|
35
39
|
if (Object.entries(t.columnNames).length > 0) {
|
|
36
|
-
typeDefs +=
|
|
40
|
+
typeDefs += '\n' + ' '.repeat(6) + 'export enum ColumnNames {\n';
|
|
37
41
|
for (const [formattedName, rawName] of Object.entries(t.columnNames)) {
|
|
38
|
-
typeDefs +=
|
|
42
|
+
typeDefs += ' '.repeat(8) + `${formattedName} = '${rawName}',\n`;
|
|
39
43
|
}
|
|
40
|
-
typeDefs +=
|
|
44
|
+
typeDefs += ' '.repeat(6) + '};\n';
|
|
41
45
|
}
|
|
42
46
|
if (Object.entries(t.rowType).length > 0) {
|
|
43
|
-
typeDefs +=
|
|
47
|
+
typeDefs += '\n' + ' '.repeat(6) + 'export interface RowType {\n';
|
|
44
48
|
for (const [rawName, type] of Object.entries(t.rowType)) {
|
|
45
|
-
const columnName = Object.entries(t.columnNames).find(
|
|
49
|
+
const columnName = Object.entries(t.columnNames).find(entry => {
|
|
46
50
|
return entry[1] === rawName;
|
|
47
51
|
})[0];
|
|
48
|
-
typeDefs +=
|
|
52
|
+
typeDefs += ' '.repeat(8) + `[ColumnNames.${columnName}]: ${type};\n`;
|
|
49
53
|
}
|
|
50
|
-
typeDefs +=
|
|
54
|
+
typeDefs += ' '.repeat(6) + '};\n';
|
|
51
55
|
}
|
|
52
|
-
typeDefs +=
|
|
56
|
+
typeDefs += ' '.repeat(4) + '};\n';
|
|
53
57
|
}
|
|
54
|
-
typeDefs +=
|
|
58
|
+
typeDefs += ' '.repeat(2) + '};\n';
|
|
55
59
|
return typeDefs;
|
|
56
60
|
}
|
|
57
61
|
function appendProcedures(typeDefs, schema) {
|
|
58
|
-
typeDefs +=
|
|
62
|
+
typeDefs += '\n' + ' '.repeat(2) + 'export namespace Procedures {';
|
|
59
63
|
for (const proc of schema.procedures) {
|
|
60
64
|
typeDefs +=
|
|
61
|
-
|
|
65
|
+
'\n' + ' '.repeat(4) + `export namespace ${proc.formattedName} {\n`;
|
|
62
66
|
typeDefs +=
|
|
63
|
-
|
|
67
|
+
' '.repeat(6) + `export const ProcedureName = '${proc.rawName}';\n`;
|
|
64
68
|
typeDefs +=
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
'\n' +
|
|
70
|
+
' '.repeat(6) +
|
|
67
71
|
`export const ArgNames = [\n` +
|
|
68
72
|
proc.argNames
|
|
69
|
-
.map(
|
|
70
|
-
return
|
|
73
|
+
.map(n => {
|
|
74
|
+
return ' '.repeat(8) + `'${n}',\n`;
|
|
71
75
|
})
|
|
72
|
-
.join(
|
|
73
|
-
|
|
74
|
-
|
|
76
|
+
.join('') +
|
|
77
|
+
' '.repeat(6) +
|
|
78
|
+
'] as const;\n';
|
|
75
79
|
typeDefs +=
|
|
76
|
-
|
|
77
|
-
|
|
80
|
+
'\n' +
|
|
81
|
+
' '.repeat(6) +
|
|
78
82
|
`export type ArgTypes = [\n` +
|
|
79
83
|
proc.argTypes
|
|
80
|
-
.map(
|
|
81
|
-
return
|
|
84
|
+
.map(t => {
|
|
85
|
+
return ' '.repeat(8) + t + ',\n';
|
|
82
86
|
})
|
|
83
|
-
.join(
|
|
84
|
-
|
|
85
|
-
|
|
87
|
+
.join('') +
|
|
88
|
+
' '.repeat(6) +
|
|
89
|
+
'];\n';
|
|
86
90
|
typeDefs +=
|
|
87
|
-
|
|
88
|
-
typeDefs +=
|
|
91
|
+
'\n' + ' '.repeat(6) + `export type ReturnType = ${proc.returnType};\n`;
|
|
92
|
+
typeDefs += ' '.repeat(4) + '};\n';
|
|
89
93
|
}
|
|
90
|
-
typeDefs +=
|
|
94
|
+
typeDefs += ' '.repeat(2) + '};\n';
|
|
91
95
|
return typeDefs;
|
|
92
96
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type IntrospeQLConfig } from './introspeql-config';
|
|
2
|
+
/**
|
|
3
|
+
* Reads table, function, and enum metadata from a database and writes
|
|
4
|
+
* corresponding TypeScript type definitions to the provided output path.
|
|
5
|
+
*
|
|
6
|
+
* @param config
|
|
7
|
+
*/
|
|
8
|
+
export declare function generateTypes(config: IntrospeQLConfig): Promise<void>;
|
|
@@ -1,17 +1,24 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { Client } from 'pg';
|
|
4
|
+
import { introspectTables } from './introspect-tables';
|
|
5
|
+
import { introspectColumns } from './introspect-columns';
|
|
6
|
+
import { introspectProcedures } from './introspect-procedures';
|
|
7
|
+
import { introspectEnum, } from './introspect-enum';
|
|
8
|
+
import { writeHeader } from './write-header';
|
|
9
|
+
import { prepareDataForWriting } from './prepare-data-for-writing';
|
|
10
|
+
import { appendSchema } from './append-schema';
|
|
11
|
+
import { introspeqlConfig } from './introspeql-config';
|
|
12
|
+
/**
|
|
13
|
+
* Reads table, function, and enum metadata from a database and writes
|
|
14
|
+
* corresponding TypeScript type definitions to the provided output path.
|
|
15
|
+
*
|
|
16
|
+
* @param config
|
|
17
|
+
*/
|
|
18
|
+
export async function generateTypes(config) {
|
|
12
19
|
const parsedConfig = introspeqlConfig.parse(config);
|
|
13
|
-
const client =
|
|
14
|
-
|
|
20
|
+
const client = 'dbConnectionString' in parsedConfig ?
|
|
21
|
+
new Client({
|
|
15
22
|
connectionString: parsedConfig.dbConnectionString,
|
|
16
23
|
})
|
|
17
24
|
: new Client({
|
|
@@ -21,7 +28,7 @@ export async function introspectDatabase(config) {
|
|
|
21
28
|
await client.connect();
|
|
22
29
|
}
|
|
23
30
|
catch (e) {
|
|
24
|
-
throw new Error(
|
|
31
|
+
throw new Error('Failed to connect to the database.', { cause: e });
|
|
25
32
|
}
|
|
26
33
|
// Read table data
|
|
27
34
|
let tableDataObjects;
|
|
@@ -29,7 +36,7 @@ export async function introspectDatabase(config) {
|
|
|
29
36
|
tableDataObjects = await introspectTables(client, parsedConfig);
|
|
30
37
|
}
|
|
31
38
|
catch (e) {
|
|
32
|
-
throw new Error(
|
|
39
|
+
throw new Error('Failed to introspect tables.', { cause: e });
|
|
33
40
|
}
|
|
34
41
|
// Read column data and update enums object when enum types are found
|
|
35
42
|
const columnDataObjectsByTableId = {};
|
|
@@ -40,7 +47,7 @@ export async function introspectDatabase(config) {
|
|
|
40
47
|
columnDataObjectsByTableId[tableDataObj.id] = columnDataObjects;
|
|
41
48
|
for (const columnDataObj of columnDataObjects) {
|
|
42
49
|
if (columnDataObj.is_enum &&
|
|
43
|
-
!partialEnumDataObjects.find(
|
|
50
|
+
!partialEnumDataObjects.find(d => d.id === columnDataObj.column_type_id)) {
|
|
44
51
|
partialEnumDataObjects.push({
|
|
45
52
|
id: columnDataObj.column_type_id,
|
|
46
53
|
schema: columnDataObj.column_type_schema,
|
|
@@ -50,7 +57,7 @@ export async function introspectDatabase(config) {
|
|
|
50
57
|
}
|
|
51
58
|
}
|
|
52
59
|
catch (e) {
|
|
53
|
-
throw new Error(
|
|
60
|
+
throw new Error('Failed to introspect columns.', { cause: e });
|
|
54
61
|
}
|
|
55
62
|
}
|
|
56
63
|
// Read procedure data and update enums object when enum types are found
|
|
@@ -59,12 +66,12 @@ export async function introspectDatabase(config) {
|
|
|
59
66
|
procedureDataObjects = await introspectProcedures(client, parsedConfig);
|
|
60
67
|
}
|
|
61
68
|
catch (e) {
|
|
62
|
-
throw new Error(
|
|
69
|
+
throw new Error('Failed to introspect procedures.', { cause: e });
|
|
63
70
|
}
|
|
64
71
|
for (const procedureDataObject of procedureDataObjects) {
|
|
65
72
|
for (const argType of procedureDataObject.arg_types) {
|
|
66
73
|
if (argType.is_enum &&
|
|
67
|
-
!partialEnumDataObjects.find(
|
|
74
|
+
!partialEnumDataObjects.find(d => d.id === argType.id)) {
|
|
68
75
|
partialEnumDataObjects.push({
|
|
69
76
|
id: argType.id,
|
|
70
77
|
schema: argType.schema,
|
|
@@ -73,7 +80,7 @@ export async function introspectDatabase(config) {
|
|
|
73
80
|
}
|
|
74
81
|
}
|
|
75
82
|
if (procedureDataObject.return_type.is_enum &&
|
|
76
|
-
!partialEnumDataObjects.find(
|
|
83
|
+
!partialEnumDataObjects.find(d => d.id === procedureDataObject.return_type.id)) {
|
|
77
84
|
partialEnumDataObjects.push({
|
|
78
85
|
id: procedureDataObject.return_type.id,
|
|
79
86
|
schema: procedureDataObject.return_type.schema,
|
|
@@ -81,7 +88,7 @@ export async function introspectDatabase(config) {
|
|
|
81
88
|
});
|
|
82
89
|
}
|
|
83
90
|
}
|
|
84
|
-
//
|
|
91
|
+
// Read enum metadata for all enums that were featured in tables or procedures
|
|
85
92
|
const enumDataObjects = [];
|
|
86
93
|
for (const partialEnumDataObj of partialEnumDataObjects) {
|
|
87
94
|
try {
|
|
@@ -89,14 +96,16 @@ export async function introspectDatabase(config) {
|
|
|
89
96
|
enumDataObjects.push(enumData);
|
|
90
97
|
}
|
|
91
98
|
catch (e) {
|
|
92
|
-
throw new Error(
|
|
99
|
+
throw new Error('Failed to introspect enum.', { cause: e });
|
|
93
100
|
}
|
|
94
101
|
}
|
|
95
102
|
await client.end();
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
103
|
+
const outDir = path.dirname(config.outFile);
|
|
104
|
+
if (!fs.existsSync(outDir)) {
|
|
105
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
106
|
+
}
|
|
107
|
+
if (!fs)
|
|
108
|
+
writeHeader(parsedConfig.outFile, parsedConfig);
|
|
100
109
|
const schemas = prepareDataForWriting(enumDataObjects, tableDataObjects, columnDataObjectsByTableId, procedureDataObjects, parsedConfig);
|
|
101
|
-
schemas.forEach(
|
|
110
|
+
schemas.forEach(schema => appendSchema(parsedConfig.outFile, schema));
|
|
102
111
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export type { IntrospeQLConfig } from
|
|
1
|
+
export { generateTypes } from './generate-types';
|
|
2
|
+
export type { IntrospeQLConfig } from './introspeql-config';
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { generateTypes } from './generate-types';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import type { Client } from
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { Client } from 'pg';
|
|
3
3
|
declare const columnDataSchema: z.ZodObject<{
|
|
4
4
|
column_name: z.ZodString;
|
|
5
5
|
column_type_schema: z.ZodString;
|
|
@@ -10,13 +10,12 @@ declare const columnDataSchema: z.ZodObject<{
|
|
|
10
10
|
nullable: z.ZodBoolean;
|
|
11
11
|
}, z.core.$strip>;
|
|
12
12
|
export type ColumnData = z.infer<typeof columnDataSchema>;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}[]>;
|
|
13
|
+
/**
|
|
14
|
+
* Reads metadata for each column of a given table.
|
|
15
|
+
*
|
|
16
|
+
* @param client
|
|
17
|
+
* @param tableId
|
|
18
|
+
* @returns A {@link Promise}<{@link ColumnData}[]>
|
|
19
|
+
*/
|
|
20
|
+
export declare function introspectColumns(client: Client, tableId: number): Promise<ColumnData[]>;
|
|
22
21
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { z } from
|
|
1
|
+
import { z } from 'zod';
|
|
2
2
|
const columnDataSchema = z.object({
|
|
3
3
|
column_name: z.string(),
|
|
4
4
|
column_type_schema: z.string(),
|
|
@@ -8,6 +8,13 @@ const columnDataSchema = z.object({
|
|
|
8
8
|
num_dimensions: z.number(),
|
|
9
9
|
nullable: z.boolean(),
|
|
10
10
|
});
|
|
11
|
+
/**
|
|
12
|
+
* Reads metadata for each column of a given table.
|
|
13
|
+
*
|
|
14
|
+
* @param client
|
|
15
|
+
* @param tableId
|
|
16
|
+
* @returns A {@link Promise}<{@link ColumnData}[]>
|
|
17
|
+
*/
|
|
11
18
|
export async function introspectColumns(client, tableId) {
|
|
12
19
|
const result = await client.query(`
|
|
13
20
|
SELECT
|
|
@@ -1,10 +1,20 @@
|
|
|
1
|
-
import type { Client } from
|
|
1
|
+
import type { Client } from 'pg';
|
|
2
2
|
export type PartialEnumData = {
|
|
3
|
+
/** The OID of the enum. */
|
|
3
4
|
id: number;
|
|
5
|
+
/** The name of the schema in which the enum was defined. */
|
|
4
6
|
schema: string;
|
|
7
|
+
/** The name of the enum. */
|
|
5
8
|
name: string;
|
|
6
9
|
};
|
|
7
10
|
export type EnumData = PartialEnumData & {
|
|
8
11
|
values: string[];
|
|
9
12
|
};
|
|
13
|
+
/**
|
|
14
|
+
* Reads metadata for the provided enum.
|
|
15
|
+
*
|
|
16
|
+
* @param client
|
|
17
|
+
* @param data
|
|
18
|
+
* @returns A {@link Promise}<{@link EnumData}[]>
|
|
19
|
+
*/
|
|
10
20
|
export declare function introspectEnum(client: Client, data: PartialEnumData): Promise<EnumData>;
|
package/dist/introspect-enum.js
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import { z } from
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Reads metadata for the provided enum.
|
|
4
|
+
*
|
|
5
|
+
* @param client
|
|
6
|
+
* @param data
|
|
7
|
+
* @returns A {@link Promise}<{@link EnumData}[]>
|
|
8
|
+
*/
|
|
2
9
|
export async function introspectEnum(client, data) {
|
|
3
10
|
const query = `
|
|
4
11
|
SELECT ARRAY(SELECT enumlabel FROM pg_enum WHERE enumtypid = $1)::text[] AS enum_values;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import type { Client } from
|
|
3
|
-
import type { ParsedIntrospeQLConfig } from
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { Client } from 'pg';
|
|
3
|
+
import type { ParsedIntrospeQLConfig } from './introspeql-config';
|
|
4
4
|
declare const procedureDataSchema: z.ZodObject<{
|
|
5
5
|
id: z.ZodNumber;
|
|
6
6
|
schema: z.ZodString;
|
|
7
7
|
name: z.ZodString;
|
|
8
|
-
arg_names: z.ZodPipe<z.ZodNullable<z.ZodArray<z.ZodString>>, z.ZodTransform<string[], string[]>>;
|
|
8
|
+
arg_names: z.ZodPipe<z.ZodNullable<z.ZodArray<z.ZodString>>, z.ZodTransform<string[], string[] | null>>;
|
|
9
9
|
arg_types: z.ZodArray<z.ZodObject<{
|
|
10
10
|
id: z.ZodCoercedNumber<unknown>;
|
|
11
11
|
schema: z.ZodString;
|
|
@@ -22,10 +22,19 @@ declare const procedureDataSchema: z.ZodObject<{
|
|
|
22
22
|
}, z.core.$strip>;
|
|
23
23
|
}, z.core.$strip>;
|
|
24
24
|
export type ProcedureData = z.infer<typeof procedureDataSchema>;
|
|
25
|
+
/**
|
|
26
|
+
* Reads information about procedures in the provided schema(s), including their
|
|
27
|
+
* ids, names, schema names, argument names, argument types, and return type.
|
|
28
|
+
*
|
|
29
|
+
* @param client
|
|
30
|
+
* @param config
|
|
31
|
+
* @returns A {@link Promise}<{@link ProcedureData}[]>
|
|
32
|
+
*/
|
|
25
33
|
export declare function introspectProcedures(client: Client, config: ParsedIntrospeQLConfig): Promise<{
|
|
26
34
|
id: number;
|
|
27
35
|
schema: string;
|
|
28
36
|
name: string;
|
|
37
|
+
arg_names: string[];
|
|
29
38
|
arg_types: {
|
|
30
39
|
id: number;
|
|
31
40
|
schema: string;
|
|
@@ -40,6 +49,5 @@ export declare function introspectProcedures(client: Client, config: ParsedIntro
|
|
|
40
49
|
is_array: boolean;
|
|
41
50
|
is_enum: boolean;
|
|
42
51
|
};
|
|
43
|
-
arg_names?: string[];
|
|
44
52
|
}[]>;
|
|
45
53
|
export {};
|
|
@@ -1,27 +1,61 @@
|
|
|
1
|
-
import { z } from
|
|
1
|
+
import { z } from 'zod';
|
|
2
2
|
const typeSchema = z.object({
|
|
3
|
+
/** The OID of the type. */
|
|
3
4
|
id: z.coerce.number(),
|
|
5
|
+
/** The name of the schema in which the type was defined. */
|
|
4
6
|
schema: z.string(),
|
|
7
|
+
/**
|
|
8
|
+
* The name of the type, or the name of the type of each element if the type
|
|
9
|
+
* is an array.
|
|
10
|
+
*/
|
|
5
11
|
name: z.string(),
|
|
12
|
+
/** Whether or not the type is an array. */
|
|
6
13
|
is_array: z.boolean(),
|
|
14
|
+
/**
|
|
15
|
+
* Whether or not the type (or the type of each element if the type is an
|
|
16
|
+
* array) is an enum.
|
|
17
|
+
*/
|
|
7
18
|
is_enum: z.boolean(),
|
|
8
19
|
});
|
|
9
20
|
const procedureDataSchema = z.object({
|
|
21
|
+
/** The OID of the procedure. */
|
|
10
22
|
id: z.number(),
|
|
23
|
+
/** The name of the schema in which the procedure was defined. */
|
|
11
24
|
schema: z.string(),
|
|
25
|
+
/** The name of the procedure. */
|
|
12
26
|
name: z.string(),
|
|
27
|
+
/**
|
|
28
|
+
* An array containing the names of the procedure's arguments. Note that this
|
|
29
|
+
* array can be empty even for procedures with arguments if those arguments
|
|
30
|
+
* are unnamed.
|
|
31
|
+
*/
|
|
13
32
|
arg_names: z
|
|
14
33
|
.string()
|
|
15
34
|
.array()
|
|
16
35
|
.nullable()
|
|
17
|
-
.transform(
|
|
36
|
+
.transform(v => (v ? v : [])),
|
|
37
|
+
/**
|
|
38
|
+
* An array containing type information for each of the procedure's
|
|
39
|
+
* arguments.
|
|
40
|
+
*/
|
|
18
41
|
arg_types: typeSchema.array(),
|
|
42
|
+
/**
|
|
43
|
+
* Type information for the procedure's return type.
|
|
44
|
+
*/
|
|
19
45
|
return_type: typeSchema,
|
|
20
46
|
});
|
|
47
|
+
/**
|
|
48
|
+
* Reads information about procedures in the provided schema(s), including their
|
|
49
|
+
* ids, names, schema names, argument names, argument types, and return type.
|
|
50
|
+
*
|
|
51
|
+
* @param client
|
|
52
|
+
* @param config
|
|
53
|
+
* @returns A {@link Promise}<{@link ProcedureData}[]>
|
|
54
|
+
*/
|
|
21
55
|
export async function introspectProcedures(client, config) {
|
|
22
56
|
const schemaPlaceholders = config.schemas
|
|
23
57
|
.map((_, i) => `$${i + 1}`)
|
|
24
|
-
.join(
|
|
58
|
+
.join(', ');
|
|
25
59
|
let query = `
|
|
26
60
|
SELECT
|
|
27
61
|
p.oid AS id,
|
|
@@ -86,9 +120,9 @@ WHERE n.nspname IN (${schemaPlaceholders})
|
|
|
86
120
|
parameters.push(procedureToIgnore.schema);
|
|
87
121
|
parameters.push(procedureToIgnore.name);
|
|
88
122
|
}
|
|
89
|
-
query += `AND NOT (${filters.join(
|
|
123
|
+
query += `AND NOT (${filters.join(' OR\n')})`;
|
|
90
124
|
}
|
|
91
|
-
query +=
|
|
125
|
+
query += ';';
|
|
92
126
|
const result = await client.query(query, parameters);
|
|
93
127
|
const procedureData = procedureDataSchema.array().parse(result.rows);
|
|
94
128
|
return procedureData;
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import type { Client } from
|
|
3
|
-
import type { ParsedIntrospeQLConfig } from
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { Client } from 'pg';
|
|
3
|
+
import type { ParsedIntrospeQLConfig } from './introspeql-config';
|
|
4
4
|
declare const tableDataSchema: z.ZodObject<{
|
|
5
5
|
id: z.ZodNumber;
|
|
6
6
|
schema: z.ZodString;
|
|
7
7
|
name: z.ZodString;
|
|
8
8
|
}, z.core.$strip>;
|
|
9
9
|
export type TableData = z.infer<typeof tableDataSchema>;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Returns information about tables in the provided schema(s), including their
|
|
12
|
+
* ids, names, and schema names.
|
|
13
|
+
*
|
|
14
|
+
* @param client
|
|
15
|
+
* @param config
|
|
16
|
+
* @returns A {@link Promise}<{@link TableData}[]>
|
|
17
|
+
*/
|
|
18
|
+
export declare function introspectTables(client: Client, config: ParsedIntrospeQLConfig): Promise<TableData[]>;
|
|
15
19
|
export {};
|
|
@@ -1,13 +1,24 @@
|
|
|
1
|
-
import { z } from
|
|
1
|
+
import { z } from 'zod';
|
|
2
2
|
const tableDataSchema = z.object({
|
|
3
|
+
/** The OID of the table. */
|
|
3
4
|
id: z.number(),
|
|
5
|
+
/** The name of the schema containing the table. */
|
|
4
6
|
schema: z.string(),
|
|
7
|
+
/** The name of the table. */
|
|
5
8
|
name: z.string(),
|
|
6
9
|
});
|
|
10
|
+
/**
|
|
11
|
+
* Returns information about tables in the provided schema(s), including their
|
|
12
|
+
* ids, names, and schema names.
|
|
13
|
+
*
|
|
14
|
+
* @param client
|
|
15
|
+
* @param config
|
|
16
|
+
* @returns A {@link Promise}<{@link TableData}[]>
|
|
17
|
+
*/
|
|
7
18
|
export async function introspectTables(client, config) {
|
|
8
19
|
const schemaPlaceholders = config.schemas
|
|
9
20
|
.map((_, i) => `$${i + 1}`)
|
|
10
|
-
.join(
|
|
21
|
+
.join(', ');
|
|
11
22
|
let query = `
|
|
12
23
|
SELECT c.oid AS id, n.nspname AS schema, c.relname AS name
|
|
13
24
|
FROM pg_catalog.pg_class AS c
|
|
@@ -23,9 +34,9 @@ WHERE c.relkind = 'r' AND n.nspname IN (${schemaPlaceholders})
|
|
|
23
34
|
parameters.push(tableToIgnore.schema);
|
|
24
35
|
parameters.push(tableToIgnore.name);
|
|
25
36
|
}
|
|
26
|
-
query += `AND NOT (${filters.join(
|
|
37
|
+
query += `AND NOT (${filters.join(' OR\n')})`;
|
|
27
38
|
}
|
|
28
|
-
query +=
|
|
39
|
+
query += ';';
|
|
29
40
|
const result = await client.query(query, parameters);
|
|
30
41
|
const tableData = tableDataSchema.array().parse(result.rows);
|
|
31
42
|
return tableData;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
declare const introspeqlConfig: z.ZodIntersection<z.
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
declare const introspeqlConfig: z.ZodIntersection<z.ZodUnion<readonly [z.ZodObject<{
|
|
3
3
|
dbConnectionString: z.ZodString;
|
|
4
4
|
}, z.core.$strip>, z.ZodObject<{
|
|
5
5
|
dbConnectionParams: z.ZodObject<{
|
|
@@ -9,26 +9,23 @@ declare const introspeqlConfig: z.ZodIntersection<z.ZodIntersection<z.ZodUnion<r
|
|
|
9
9
|
port: z.ZodOptional<z.ZodNumber>;
|
|
10
10
|
database: z.ZodOptional<z.ZodString>;
|
|
11
11
|
}, z.core.$strip>;
|
|
12
|
-
}, z.core.$strip>]>, z.
|
|
13
|
-
outDir: z.ZodString;
|
|
14
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
12
|
+
}, z.core.$strip>]>, z.ZodObject<{
|
|
15
13
|
outFile: z.ZodString;
|
|
16
|
-
}, z.core.$strip>]>>, z.ZodObject<{
|
|
17
14
|
schemas: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
18
15
|
header: z.ZodOptional<z.ZodString>;
|
|
19
16
|
types: z.ZodPipe<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>, z.ZodTransform<{
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}, Record<string, string
|
|
17
|
+
'pg_catalog.bool': string;
|
|
18
|
+
'pg_catalog.int2': string;
|
|
19
|
+
'pg_catalog.int4': string;
|
|
20
|
+
'pg_catalog.float4': string;
|
|
21
|
+
'pg_catalog.float8': string;
|
|
22
|
+
'pg_catalog.json': string;
|
|
23
|
+
'pg_catalog.jsonb': string;
|
|
24
|
+
'pg_catalog.date': string;
|
|
25
|
+
'pg_catalog.timestamp': string;
|
|
26
|
+
'pg_catalog.timestamptz': string;
|
|
27
|
+
'pg_catalog.void': string;
|
|
28
|
+
}, Record<string, string> | undefined>>;
|
|
32
29
|
ignoreTables: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
33
30
|
schema: z.ZodString;
|
|
34
31
|
name: z.ZodString;
|
|
@@ -1,8 +1,17 @@
|
|
|
1
|
-
import { z } from
|
|
1
|
+
import { z } from 'zod';
|
|
2
2
|
const databaseConnectionConfig = z.union([
|
|
3
|
+
/**
|
|
4
|
+
* A PostgreSQL connection string specifying the database to connect to.
|
|
5
|
+
* Useful if you are already storing such a string as an environment variable.
|
|
6
|
+
*/
|
|
3
7
|
z.object({
|
|
4
8
|
dbConnectionString: z.string(),
|
|
5
9
|
}),
|
|
10
|
+
/**
|
|
11
|
+
* An object containing the components of a PostgreSQL connection string that
|
|
12
|
+
* together specify the database to connect to. Useful if you are already
|
|
13
|
+
* storing such values as individual environment variables.
|
|
14
|
+
*/
|
|
6
15
|
z.object({
|
|
7
16
|
dbConnectionParams: z.object({
|
|
8
17
|
user: z.string().optional(),
|
|
@@ -13,53 +22,83 @@ const databaseConnectionConfig = z.union([
|
|
|
13
22
|
}),
|
|
14
23
|
}),
|
|
15
24
|
]);
|
|
16
|
-
const outputConfig = z.union([
|
|
17
|
-
z.object({
|
|
18
|
-
outDir: z.string(),
|
|
19
|
-
}),
|
|
20
|
-
z.object({
|
|
21
|
-
outFile: z.string(),
|
|
22
|
-
}),
|
|
23
|
-
]);
|
|
24
25
|
const entityData = z.object({
|
|
25
26
|
schema: z.string(),
|
|
26
27
|
name: z.string(),
|
|
27
28
|
});
|
|
28
29
|
const otherConfig = z.object({
|
|
30
|
+
/**
|
|
31
|
+
* The file to which TypeScript types will be written.
|
|
32
|
+
*/
|
|
33
|
+
outFile: z.string(),
|
|
34
|
+
/**
|
|
35
|
+
* The database schemas from which types should be generated.
|
|
36
|
+
*/
|
|
29
37
|
schemas: z
|
|
30
38
|
.string()
|
|
31
39
|
.array()
|
|
32
40
|
.nonempty({
|
|
33
|
-
message:
|
|
41
|
+
message: 'Please provide at least one schema.',
|
|
34
42
|
})
|
|
35
43
|
.optional()
|
|
36
|
-
.default(() => [
|
|
44
|
+
.default(() => ['public']),
|
|
45
|
+
/**
|
|
46
|
+
* An optional header that will be written to the generated type definition
|
|
47
|
+
* file before the types. Useful when you want to define or import types
|
|
48
|
+
* directly in the generated file.
|
|
49
|
+
*/
|
|
37
50
|
header: z.string().optional(),
|
|
51
|
+
/**
|
|
52
|
+
* Custom type mappings. Default settings align with transformations applied
|
|
53
|
+
* by node-postgres. To override these settings for a type, provide the
|
|
54
|
+
* name of the schema in which the type is defined and the name of the type
|
|
55
|
+
* (separated by a dot) as the key, and a string representation of the
|
|
56
|
+
* desired type as the value.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```
|
|
60
|
+
* {
|
|
61
|
+
* types: {
|
|
62
|
+
* "pg_catalog.int8" : "bigint"
|
|
63
|
+
* }
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
38
67
|
types: z
|
|
39
68
|
.record(z.string(), z.string())
|
|
40
69
|
.optional()
|
|
41
|
-
.transform(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
70
|
+
.transform(t => ({
|
|
71
|
+
'pg_catalog.bool': 'boolean',
|
|
72
|
+
'pg_catalog.int2': 'number',
|
|
73
|
+
'pg_catalog.int4': 'number',
|
|
74
|
+
'pg_catalog.float4': 'number',
|
|
75
|
+
'pg_catalog.float8': 'number',
|
|
76
|
+
'pg_catalog.json': 'object',
|
|
77
|
+
'pg_catalog.jsonb': 'object',
|
|
78
|
+
'pg_catalog.date': 'Date',
|
|
79
|
+
'pg_catalog.timestamp': 'Date',
|
|
80
|
+
'pg_catalog.timestamptz': 'Date',
|
|
81
|
+
'pg_catalog.void': 'void',
|
|
53
82
|
...t,
|
|
54
83
|
})),
|
|
84
|
+
/**
|
|
85
|
+
* An array of tables that should NOT be included in the generated type
|
|
86
|
+
* definitions. Useful for ignoring tables such as `spatial_ref_sys`
|
|
87
|
+
* when using PostGIS, or tables containing information about
|
|
88
|
+
* migrations.
|
|
89
|
+
*/
|
|
55
90
|
ignoreTables: entityData
|
|
56
91
|
.array()
|
|
57
92
|
.optional()
|
|
58
93
|
.default(() => []),
|
|
94
|
+
/**
|
|
95
|
+
* An array of procedures that should NOT be included in the generated type
|
|
96
|
+
* definitions.
|
|
97
|
+
*/
|
|
59
98
|
ignoreProcedures: entityData
|
|
60
99
|
.array()
|
|
61
100
|
.optional()
|
|
62
101
|
.default(() => []),
|
|
63
102
|
});
|
|
64
|
-
const introspeqlConfig = z.intersection(
|
|
103
|
+
const introspeqlConfig = z.intersection(databaseConnectionConfig, otherConfig);
|
|
65
104
|
export { introspeqlConfig };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { EnumData } from
|
|
2
|
-
import type { TableData } from
|
|
3
|
-
import type { ColumnData } from
|
|
4
|
-
import type { ProcedureData } from
|
|
5
|
-
import type { ParsedIntrospeQLConfig } from
|
|
1
|
+
import type { EnumData } from './introspect-enum';
|
|
2
|
+
import type { TableData } from './introspect-tables';
|
|
3
|
+
import type { ColumnData } from './introspect-columns';
|
|
4
|
+
import type { ProcedureData } from './introspect-procedures';
|
|
5
|
+
import type { ParsedIntrospeQLConfig } from './introspeql-config';
|
|
6
6
|
export interface Schema {
|
|
7
7
|
formattedName: string;
|
|
8
8
|
rawName: string;
|
|
@@ -24,4 +24,15 @@ export interface Schema {
|
|
|
24
24
|
returnType: string;
|
|
25
25
|
}>;
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Organizes data by schema, table, and procedure, and formats it in
|
|
29
|
+
* preparation for type generation.
|
|
30
|
+
*
|
|
31
|
+
* @param enumDataObjects
|
|
32
|
+
* @param tableDataObjects
|
|
33
|
+
* @param columnDataObjectsByTableId
|
|
34
|
+
* @param procedureDataObjects
|
|
35
|
+
* @param config
|
|
36
|
+
* @returns An array of {@link Schema} objects.
|
|
37
|
+
*/
|
|
27
38
|
export declare function prepareDataForWriting(enumDataObjects: EnumData[], tableDataObjects: TableData[], columnDataObjectsByTableId: Record<number, ColumnData[]>, procedureDataObjects: ProcedureData[], config: ParsedIntrospeQLConfig): Schema[];
|
|
@@ -1,4 +1,15 @@
|
|
|
1
|
-
import { snakeCaseToPascalCase } from
|
|
1
|
+
import { snakeCaseToPascalCase } from './snake-case-to-pascal-case';
|
|
2
|
+
/**
|
|
3
|
+
* Organizes data by schema, table, and procedure, and formats it in
|
|
4
|
+
* preparation for type generation.
|
|
5
|
+
*
|
|
6
|
+
* @param enumDataObjects
|
|
7
|
+
* @param tableDataObjects
|
|
8
|
+
* @param columnDataObjectsByTableId
|
|
9
|
+
* @param procedureDataObjects
|
|
10
|
+
* @param config
|
|
11
|
+
* @returns An array of {@link Schema} objects.
|
|
12
|
+
*/
|
|
2
13
|
export function prepareDataForWriting(enumDataObjects, tableDataObjects, columnDataObjectsByTableId, procedureDataObjects, config) {
|
|
3
14
|
const schemas = [];
|
|
4
15
|
prepareEnums(enumDataObjects, schemas, config);
|
|
@@ -46,11 +57,11 @@ function prepareTables(tableDataObjects, columnDataObjectsByTableId, schemas, co
|
|
|
46
57
|
}
|
|
47
58
|
}
|
|
48
59
|
else {
|
|
49
|
-
type =
|
|
60
|
+
type = 'string';
|
|
50
61
|
}
|
|
51
|
-
type +=
|
|
62
|
+
type += '[]'.repeat(current.num_dimensions);
|
|
52
63
|
if (current.nullable) {
|
|
53
|
-
type +=
|
|
64
|
+
type += ' | null';
|
|
54
65
|
}
|
|
55
66
|
acc[current.column_name] = type;
|
|
56
67
|
return acc;
|
|
@@ -62,15 +73,15 @@ function prepareTables(tableDataObjects, columnDataObjectsByTableId, schemas, co
|
|
|
62
73
|
function prepareProcedures(procedureDataObjects, schemas, config) {
|
|
63
74
|
for (const procDataObj of procedureDataObjects) {
|
|
64
75
|
const schema = findOrInsertSchema(schemas, procDataObj.schema);
|
|
65
|
-
const extantProcedureDeclarations = schema.procedures.filter(
|
|
76
|
+
const extantProcedureDeclarations = schema.procedures.filter(p => p.rawName === procDataObj.name).length;
|
|
66
77
|
const procData = {
|
|
67
78
|
formattedName: snakeCaseToPascalCase(procDataObj.name) +
|
|
68
|
-
(extantProcedureDeclarations > 0
|
|
69
|
-
|
|
70
|
-
:
|
|
79
|
+
(extantProcedureDeclarations > 0 ?
|
|
80
|
+
'_' + extantProcedureDeclarations
|
|
81
|
+
: ''),
|
|
71
82
|
rawName: procDataObj.name,
|
|
72
83
|
argNames: procDataObj.arg_names,
|
|
73
|
-
argTypes: procDataObj.arg_types.map(
|
|
84
|
+
argTypes: procDataObj.arg_types.map(a => {
|
|
74
85
|
let type;
|
|
75
86
|
const typeMappingKey = `${a.schema}.${a.name}`;
|
|
76
87
|
if (typeMappingKey in config.types) {
|
|
@@ -85,10 +96,10 @@ function prepareProcedures(procedureDataObjects, schemas, config) {
|
|
|
85
96
|
}
|
|
86
97
|
}
|
|
87
98
|
else {
|
|
88
|
-
type =
|
|
99
|
+
type = 'string';
|
|
89
100
|
}
|
|
90
101
|
if (a.is_array) {
|
|
91
|
-
type +=
|
|
102
|
+
type += '[]';
|
|
92
103
|
}
|
|
93
104
|
return type;
|
|
94
105
|
}),
|
|
@@ -107,10 +118,10 @@ function prepareProcedures(procedureDataObjects, schemas, config) {
|
|
|
107
118
|
}
|
|
108
119
|
}
|
|
109
120
|
else {
|
|
110
|
-
type =
|
|
121
|
+
type = 'string';
|
|
111
122
|
}
|
|
112
123
|
if (procDataObj.return_type.is_array) {
|
|
113
|
-
type +=
|
|
124
|
+
type += '[]';
|
|
114
125
|
}
|
|
115
126
|
return type;
|
|
116
127
|
})(),
|
|
@@ -119,7 +130,7 @@ function prepareProcedures(procedureDataObjects, schemas, config) {
|
|
|
119
130
|
}
|
|
120
131
|
}
|
|
121
132
|
function findOrInsertSchema(schemas, rawName) {
|
|
122
|
-
let schema = schemas.find(
|
|
133
|
+
let schema = schemas.find(s => s.rawName === rawName);
|
|
123
134
|
if (schema)
|
|
124
135
|
return schema;
|
|
125
136
|
schema = {
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts snake_case to PascalCase, preserving one leading underscore, if
|
|
3
|
+
* present.
|
|
4
|
+
*
|
|
5
|
+
* @param str - A snake_case string.
|
|
6
|
+
* @returns A PascalCase string.
|
|
7
|
+
*/
|
|
1
8
|
export function snakeCaseToPascalCase(str) {
|
|
2
9
|
let formatted = str
|
|
3
|
-
.split(
|
|
4
|
-
.map(
|
|
5
|
-
.join(
|
|
6
|
-
if (str.startsWith(
|
|
7
|
-
formatted =
|
|
10
|
+
.split('_')
|
|
11
|
+
.map(s => s.slice(0, 1).toUpperCase() + s.slice(1))
|
|
12
|
+
.join('');
|
|
13
|
+
if (str.startsWith('_')) {
|
|
14
|
+
formatted = '_' + formatted;
|
|
8
15
|
}
|
|
9
16
|
return formatted;
|
|
10
17
|
}
|
package/dist/write-header.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { ParsedIntrospeQLConfig } from
|
|
1
|
+
import type { ParsedIntrospeQLConfig } from './introspeql-config';
|
|
2
2
|
export declare function writeHeader(outputPath: string, config: ParsedIntrospeQLConfig): void;
|
package/dist/write-header.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import fs from
|
|
1
|
+
import fs from 'node:fs';
|
|
2
2
|
export function writeHeader(outputPath, config) {
|
|
3
3
|
const comment = `/* This file was generated by IntrospeQL. Do not edit manually. */\n\n`;
|
|
4
|
-
const header = config.header
|
|
5
|
-
|
|
4
|
+
const header = config.header ?
|
|
5
|
+
comment +
|
|
6
6
|
config.header +
|
|
7
|
-
|
|
7
|
+
'\n'.repeat(config.header.endsWith('\n') ? 1 : 2)
|
|
8
8
|
: comment;
|
|
9
|
-
fs.writeFileSync(outputPath, header,
|
|
9
|
+
fs.writeFileSync(outputPath, header, 'utf-8');
|
|
10
10
|
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "introspeql",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Easy Postgres to TypeScript type generation.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
|
+
"format": "npx prettier . --write",
|
|
8
9
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
9
10
|
"build": "tsc"
|
|
10
11
|
},
|
|
@@ -18,10 +19,11 @@
|
|
|
18
19
|
"TypeScript"
|
|
19
20
|
],
|
|
20
21
|
"author": "Joseph Dvorak",
|
|
21
|
-
"license": "
|
|
22
|
+
"license": "MIT",
|
|
22
23
|
"type": "commonjs",
|
|
23
24
|
"devDependencies": {
|
|
24
25
|
"@types/node": "^24.5.2",
|
|
26
|
+
"prettier": "^3.6.2",
|
|
25
27
|
"tsx": "^4.20.6",
|
|
26
28
|
"typescript": "^5.9.2"
|
|
27
29
|
},
|