Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit b4a66ed

Browse files
committedJun 17, 2024
feat: pure-js sql client agnostic base lib
1 parent a473298 commit b4a66ed

32 files changed

+1403
-443
lines changed
 

‎package-lock.json

Lines changed: 1094 additions & 221 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,24 @@
1515
"imports": {
1616
"#package.json": "./package.json"
1717
},
18+
"exports": {
19+
".": {
20+
"types": "./dist/lib/index.d.ts",
21+
"require": "./dist/lib/index.cjs",
22+
"import": "./dist/lib/index.js"
23+
},
24+
"./base": {
25+
"types": "./dist/lib/base.d.ts",
26+
"require": "./dist/lib/base.cjs",
27+
"import": "./dist/lib/base.js"
28+
}
29+
},
1830
"repository": "supabase/postgres-meta",
1931
"scripts": {
2032
"check": "tsc -p tsconfig.json --noEmit",
2133
"clean": "rimraf dist tsconfig.tsbuildinfo",
2234
"format": "prettier --write '{src,test}/**/*.ts' '*.ts'",
23-
"build": "tsc -p tsconfig.json && cpy 'src/lib/sql/*.sql' dist/lib/sql",
35+
"build": "tsup",
2436
"docs:export": "PG_META_EXPORT_DOCS=true node --loader ts-node/esm src/server/server.ts > openapi.json",
2537
"gen:types:typescript": "PG_META_GENERATE_TYPES=typescript node --loader ts-node/esm src/server/server.ts",
2638
"gen:types:go": "PG_META_GENERATE_TYPES=go node --loader ts-node/esm src/server/server.ts",
@@ -66,7 +78,9 @@
6678
"pino-pretty": "^10.2.3",
6779
"rimraf": "^5.0.5",
6880
"ts-node": "^10.9.1",
81+
"tsup": "^8.1.0",
6982
"typescript": "^5.2.2",
83+
"vite": "^5.3.1",
7084
"vitest": "^1.2.2"
7185
}
7286
}

‎src/lib/PostgresMeta.ts

Lines changed: 4 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,12 @@
11
import { PoolConfig } from 'pg'
2-
import * as Parser from './Parser.js'
3-
import PostgresMetaColumnPrivileges from './PostgresMetaColumnPrivileges.js'
4-
import PostgresMetaColumns from './PostgresMetaColumns.js'
5-
import PostgresMetaConfig from './PostgresMetaConfig.js'
6-
import PostgresMetaExtensions from './PostgresMetaExtensions.js'
7-
import PostgresMetaForeignTables from './PostgresMetaForeignTables.js'
8-
import PostgresMetaFunctions from './PostgresMetaFunctions.js'
9-
import PostgresMetaIndexes from './PostgresMetaIndexes.js'
10-
import PostgresMetaMaterializedViews from './PostgresMetaMaterializedViews.js'
11-
import PostgresMetaPolicies from './PostgresMetaPolicies.js'
12-
import PostgresMetaPublications from './PostgresMetaPublications.js'
13-
import PostgresMetaRelationships from './PostgresMetaRelationships.js'
14-
import PostgresMetaRoles from './PostgresMetaRoles.js'
15-
import PostgresMetaSchemas from './PostgresMetaSchemas.js'
16-
import PostgresMetaTablePrivileges from './PostgresMetaTablePrivileges.js'
17-
import PostgresMetaTables from './PostgresMetaTables.js'
18-
import PostgresMetaTriggers from './PostgresMetaTriggers.js'
19-
import PostgresMetaTypes from './PostgresMetaTypes.js'
20-
import PostgresMetaVersion from './PostgresMetaVersion.js'
21-
import PostgresMetaViews from './PostgresMetaViews.js'
2+
import PostgresMetaBase from './PostgresMetaBase.js'
223
import { init } from './db.js'
23-
import { PostgresMetaResult } from './types.js'
24-
25-
export default class PostgresMeta {
26-
query: (sql: string) => Promise<PostgresMetaResult<any>>
27-
end: () => Promise<void>
28-
columnPrivileges: PostgresMetaColumnPrivileges
29-
columns: PostgresMetaColumns
30-
config: PostgresMetaConfig
31-
extensions: PostgresMetaExtensions
32-
foreignTables: PostgresMetaForeignTables
33-
functions: PostgresMetaFunctions
34-
indexes: PostgresMetaIndexes
35-
materializedViews: PostgresMetaMaterializedViews
36-
policies: PostgresMetaPolicies
37-
publications: PostgresMetaPublications
38-
relationships: PostgresMetaRelationships
39-
roles: PostgresMetaRoles
40-
schemas: PostgresMetaSchemas
41-
tablePrivileges: PostgresMetaTablePrivileges
42-
tables: PostgresMetaTables
43-
triggers: PostgresMetaTriggers
44-
types: PostgresMetaTypes
45-
version: PostgresMetaVersion
46-
views: PostgresMetaViews
47-
48-
parse = Parser.Parse
49-
deparse = Parser.Deparse
50-
format = Parser.Format
514

5+
export default class PostgresMeta extends PostgresMetaBase {
526
constructor(config: PoolConfig) {
537
const { query, end } = init(config)
54-
this.query = query
8+
super({ query, end })
9+
5510
this.end = end
56-
this.columnPrivileges = new PostgresMetaColumnPrivileges(this.query)
57-
this.columns = new PostgresMetaColumns(this.query)
58-
this.config = new PostgresMetaConfig(this.query)
59-
this.extensions = new PostgresMetaExtensions(this.query)
60-
this.foreignTables = new PostgresMetaForeignTables(this.query)
61-
this.functions = new PostgresMetaFunctions(this.query)
62-
this.indexes = new PostgresMetaIndexes(this.query)
63-
this.materializedViews = new PostgresMetaMaterializedViews(this.query)
64-
this.policies = new PostgresMetaPolicies(this.query)
65-
this.publications = new PostgresMetaPublications(this.query)
66-
this.relationships = new PostgresMetaRelationships(this.query)
67-
this.roles = new PostgresMetaRoles(this.query)
68-
this.schemas = new PostgresMetaSchemas(this.query)
69-
this.tablePrivileges = new PostgresMetaTablePrivileges(this.query)
70-
this.tables = new PostgresMetaTables(this.query)
71-
this.triggers = new PostgresMetaTriggers(this.query)
72-
this.types = new PostgresMetaTypes(this.query)
73-
this.version = new PostgresMetaVersion(this.query)
74-
this.views = new PostgresMetaViews(this.query)
7511
}
7612
}

‎src/lib/PostgresMetaBase.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import * as Parser from './Parser.js'
2+
import PostgresMetaColumnPrivileges from './PostgresMetaColumnPrivileges.js'
3+
import PostgresMetaColumns from './PostgresMetaColumns.js'
4+
import PostgresMetaConfig from './PostgresMetaConfig.js'
5+
import PostgresMetaExtensions from './PostgresMetaExtensions.js'
6+
import PostgresMetaForeignTables from './PostgresMetaForeignTables.js'
7+
import PostgresMetaFunctions from './PostgresMetaFunctions.js'
8+
import PostgresMetaIndexes from './PostgresMetaIndexes.js'
9+
import PostgresMetaMaterializedViews from './PostgresMetaMaterializedViews.js'
10+
import PostgresMetaPolicies from './PostgresMetaPolicies.js'
11+
import PostgresMetaPublications from './PostgresMetaPublications.js'
12+
import PostgresMetaRelationships from './PostgresMetaRelationships.js'
13+
import PostgresMetaRoles from './PostgresMetaRoles.js'
14+
import PostgresMetaSchemas from './PostgresMetaSchemas.js'
15+
import PostgresMetaTablePrivileges from './PostgresMetaTablePrivileges.js'
16+
import PostgresMetaTables from './PostgresMetaTables.js'
17+
import PostgresMetaTriggers from './PostgresMetaTriggers.js'
18+
import PostgresMetaTypes from './PostgresMetaTypes.js'
19+
import PostgresMetaVersion from './PostgresMetaVersion.js'
20+
import PostgresMetaViews from './PostgresMetaViews.js'
21+
import { EndFn, QueryFn } from './types.js'
22+
23+
export type PostgresMetaBaseOptions = {
24+
query: QueryFn
25+
end: EndFn
26+
}
27+
28+
export default class PostgresMetaBase {
29+
query: QueryFn
30+
end: EndFn
31+
32+
columnPrivileges: PostgresMetaColumnPrivileges
33+
columns: PostgresMetaColumns
34+
config: PostgresMetaConfig
35+
extensions: PostgresMetaExtensions
36+
foreignTables: PostgresMetaForeignTables
37+
functions: PostgresMetaFunctions
38+
indexes: PostgresMetaIndexes
39+
materializedViews: PostgresMetaMaterializedViews
40+
policies: PostgresMetaPolicies
41+
publications: PostgresMetaPublications
42+
relationships: PostgresMetaRelationships
43+
roles: PostgresMetaRoles
44+
schemas: PostgresMetaSchemas
45+
tablePrivileges: PostgresMetaTablePrivileges
46+
tables: PostgresMetaTables
47+
triggers: PostgresMetaTriggers
48+
types: PostgresMetaTypes
49+
version: PostgresMetaVersion
50+
views: PostgresMetaViews
51+
52+
parse = Parser.Parse
53+
deparse = Parser.Deparse
54+
format = Parser.Format
55+
56+
constructor(options: PostgresMetaBaseOptions) {
57+
this.query = options.query
58+
this.end = options.end
59+
60+
this.columnPrivileges = new PostgresMetaColumnPrivileges(this.query)
61+
this.columns = new PostgresMetaColumns(this.query)
62+
this.config = new PostgresMetaConfig(this.query)
63+
this.extensions = new PostgresMetaExtensions(this.query)
64+
this.foreignTables = new PostgresMetaForeignTables(this.query)
65+
this.functions = new PostgresMetaFunctions(this.query)
66+
this.indexes = new PostgresMetaIndexes(this.query)
67+
this.materializedViews = new PostgresMetaMaterializedViews(this.query)
68+
this.policies = new PostgresMetaPolicies(this.query)
69+
this.publications = new PostgresMetaPublications(this.query)
70+
this.relationships = new PostgresMetaRelationships(this.query)
71+
this.roles = new PostgresMetaRoles(this.query)
72+
this.schemas = new PostgresMetaSchemas(this.query)
73+
this.tablePrivileges = new PostgresMetaTablePrivileges(this.query)
74+
this.tables = new PostgresMetaTables(this.query)
75+
this.triggers = new PostgresMetaTriggers(this.query)
76+
this.types = new PostgresMetaTypes(this.query)
77+
this.version = new PostgresMetaVersion(this.query)
78+
this.views = new PostgresMetaViews(this.query)
79+
}
80+
}

‎src/lib/PostgresMetaColumnPrivileges.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ident, literal } from 'pg-format'
22
import { DEFAULT_SYSTEM_SCHEMAS } from './constants.js'
33
import { filterByList } from './helpers.js'
4-
import { columnPrivilegesSql } from './sql/index.js'
4+
import columnPrivilegesSql from './sql/column_privileges.sql'
55
import {
66
PostgresMetaResult,
77
PostgresColumnPrivileges,

‎src/lib/PostgresMetaColumns.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ident, literal } from 'pg-format'
22
import PostgresMetaTables from './PostgresMetaTables.js'
33
import { DEFAULT_SYSTEM_SCHEMAS } from './constants.js'
4-
import { columnsSql } from './sql/index.js'
4+
import columnsSql from './sql/columns.sql'
55
import { PostgresMetaResult, PostgresColumn } from './types.js'
66
import { filterByList } from './helpers.js'
77

‎src/lib/PostgresMetaConfig.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { configSql } from './sql/index.js'
1+
import configSql from './sql/config.sql'
22
import { PostgresMetaResult, PostgresConfig } from './types.js'
33

44
export default class PostgresMetaConfig {

‎src/lib/PostgresMetaExtensions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ident, literal } from 'pg-format'
2-
import { extensionsSql } from './sql/index.js'
2+
import extensionsSql from './sql/extensions.sql'
33
import { PostgresMetaResult, PostgresExtension } from './types.js'
44

55
export default class PostgresMetaExtensions {

‎src/lib/PostgresMetaForeignTables.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { literal } from 'pg-format'
22
import { coalesceRowsToArray, filterByList } from './helpers.js'
3-
import { columnsSql, foreignTablesSql } from './sql/index.js'
3+
import columnsSql from './sql/columns.sql'
4+
import foreignTablesSql from './sql/foreign_tables.sql'
45
import { PostgresMetaResult, PostgresForeignTable } from './types.js'
56

67
export default class PostgresMetaForeignTables {

‎src/lib/PostgresMetaFunctions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ident, literal } from 'pg-format'
22
import { DEFAULT_SYSTEM_SCHEMAS } from './constants.js'
33
import { filterByList } from './helpers.js'
4-
import { functionsSql } from './sql/index.js'
4+
import functionsSql from './sql/functions.sql'
55
import { PostgresMetaResult, PostgresFunction, PostgresFunctionCreate } from './types.js'
66

77
export default class PostgresMetaFunctions {

‎src/lib/PostgresMetaIndexes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ident, literal } from 'pg-format'
22
import { DEFAULT_SYSTEM_SCHEMAS } from './constants.js'
33
import { filterByList } from './helpers.js'
4-
import { indexesSql } from './sql/index.js'
4+
import indexesSql from './sql/indexes.sql'
55
import { PostgresMetaResult, PostgresIndex } from './types.js'
66

77
export default class PostgresMetaFunctions {

‎src/lib/PostgresMetaMaterializedViews.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { literal } from 'pg-format'
22
import { coalesceRowsToArray, filterByList } from './helpers.js'
3-
import { columnsSql, materializedViewsSql } from './sql/index.js'
3+
import columnsSql from './sql/columns.sql'
4+
import materializedViewsSql from './sql/materialized_views.sql'
45
import { PostgresMetaResult, PostgresMaterializedView } from './types.js'
56

67
export default class PostgresMetaMaterializedViews {

‎src/lib/PostgresMetaPolicies.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ident, literal } from 'pg-format'
22
import { DEFAULT_SYSTEM_SCHEMAS } from './constants.js'
33
import { filterByList } from './helpers.js'
4-
import { policiesSql } from './sql/index.js'
4+
import policiesSql from './sql/policies.sql'
55
import { PostgresMetaResult, PostgresPolicy } from './types.js'
66

77
export default class PostgresMetaPolicies {

‎src/lib/PostgresMetaPublications.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ident, literal } from 'pg-format'
2-
import { publicationsSql } from './sql/index.js'
2+
import publicationsSql from './sql/publications.sql'
33
import { PostgresMetaResult, PostgresPublication, PostgresTable } from './types.js'
44

55
export default class PostgresMetaPublications {

‎src/lib/PostgresMetaRelationships.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { literal } from 'pg-format'
22
import { DEFAULT_SYSTEM_SCHEMAS } from './constants.js'
3-
import { tableRelationshipsSql, viewsKeyDependenciesSql } from './sql/index.js'
3+
import tableRelationshipsSql from './sql/table_relationships.sql'
4+
import viewsKeyDependenciesSql from './sql/views_key_dependencies.sql'
45
import { PostgresMetaResult, PostgresRelationship } from './types.js'
56

67
/*

‎src/lib/PostgresMetaRoles.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ident, literal } from 'pg-format'
2-
import { rolesSql } from './sql/index.js'
2+
import rolesSql from './sql/roles.sql'
33
import {
44
PostgresMetaResult,
55
PostgresRole,

‎src/lib/PostgresMetaSchemas.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ident, literal } from 'pg-format'
22
import { DEFAULT_SYSTEM_SCHEMAS } from './constants.js'
3-
import { schemasSql } from './sql/index.js'
3+
import schemasSql from './sql/schemas.sql'
44
import {
55
PostgresMetaResult,
66
PostgresSchema,

‎src/lib/PostgresMetaTablePrivileges.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ident, literal } from 'pg-format'
22
import { DEFAULT_SYSTEM_SCHEMAS } from './constants.js'
33
import { filterByList } from './helpers.js'
4-
import { tablePrivilegesSql } from './sql/index.js'
4+
import tablePrivilegesSql from './sql/table_privileges.sql'
55
import {
66
PostgresMetaResult,
77
PostgresTablePrivileges,

‎src/lib/PostgresMetaTables.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { ident, literal } from 'pg-format'
22
import { DEFAULT_SYSTEM_SCHEMAS } from './constants.js'
33
import { coalesceRowsToArray, filterByList } from './helpers.js'
4-
import { columnsSql, tablesSql } from './sql/index.js'
4+
import columnsSql from './sql/columns.sql'
5+
import tablesSql from './sql/tables.sql'
56
import {
67
PostgresMetaResult,
78
PostgresTable,

‎src/lib/PostgresMetaTriggers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ident, literal } from 'pg-format'
22
import { DEFAULT_SYSTEM_SCHEMAS } from './constants.js'
33
import { filterByList } from './helpers.js'
4-
import { triggersSql } from './sql/index.js'
4+
import triggersSql from './sql/triggers.sql'
55
import { PostgresMetaResult, PostgresTrigger } from './types.js'
66

77
export default class PostgresMetaTriggers {

‎src/lib/PostgresMetaTypes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { DEFAULT_SYSTEM_SCHEMAS } from './constants.js'
22
import { filterByList } from './helpers.js'
3-
import { typesSql } from './sql/index.js'
3+
import typesSql from './sql/types.sql'
44
import { PostgresMetaResult, PostgresType } from './types.js'
55

66
export default class PostgresMetaTypes {

‎src/lib/PostgresMetaVersion.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { versionSql } from './sql/index.js'
1+
import versionSql from './sql/version.sql'
22
import { PostgresMetaResult, PostgresVersion } from './types.js'
33

44
export default class PostgresMetaVersion {

‎src/lib/PostgresMetaViews.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { literal } from 'pg-format'
22
import { DEFAULT_SYSTEM_SCHEMAS } from './constants.js'
33
import { coalesceRowsToArray, filterByList } from './helpers.js'
4-
import { columnsSql, viewsSql } from './sql/index.js'
4+
import columnsSql from './sql/columns.sql'
5+
import viewsSql from './sql/views.sql'
56
import { PostgresMetaResult, PostgresView } from './types.js'
67

78
export default class PostgresMetaViews {

‎src/lib/base.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
export { default as PostgresMetaBase } from './PostgresMetaBase.js'
2+
export { wrapError, wrapResult } from './helpers.js'
3+
export {
4+
EndFn,
5+
PostgresColumn,
6+
PostgresConfig,
7+
PostgresExtension,
8+
PostgresFunction,
9+
PostgresFunctionCreate,
10+
PostgresIndex,
11+
PostgresMaterializedView,
12+
PostgresMetaErr,
13+
PostgresMetaOk,
14+
PostgresMetaResult,
15+
PostgresPolicy,
16+
PostgresPrimaryKey,
17+
PostgresPublication,
18+
PostgresRelationship,
19+
PostgresRole,
20+
PostgresSchema,
21+
PostgresSchemaCreate,
22+
PostgresSchemaUpdate,
23+
PostgresTable,
24+
PostgresTrigger,
25+
PostgresType,
26+
PostgresVersion,
27+
PostgresView,
28+
QueryFn,
29+
} from './types.js'

‎src/lib/db.ts

Lines changed: 7 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import pg, { PoolConfig } from 'pg'
2-
import { DatabaseError } from 'pg-protocol'
32
import { parse as parseArray } from 'postgres-array'
4-
import { PostgresMetaResult } from './types.js'
3+
import { wrapError, wrapResult } from './helpers.js'
4+
import { EndFn, QueryFn } from './types.js'
55

66
pg.types.setTypeParser(pg.types.builtins.INT8, (x) => {
77
const asNumber = Number(x)
@@ -21,8 +21,8 @@ pg.types.setTypeParser(600, (x) => x) // point
2121
pg.types.setTypeParser(1017, (x) => x) // _point
2222

2323
export const init: (config: PoolConfig) => {
24-
query: (sql: string) => Promise<PostgresMetaResult<any>>
25-
end: () => Promise<void>
24+
query: QueryFn
25+
end: EndFn
2626
} = (config) => {
2727
// node-postgres ignores config.ssl if any of sslmode, sslca, sslkey, sslcert,
2828
// sslrootcert are in the connection string. Here we allow setting sslmode in
@@ -76,77 +76,9 @@ export const init: (config: PoolConfig) => {
7676
if (Array.isArray(res)) {
7777
res = res.reverse().find((x) => x.rows.length !== 0) ?? { rows: [] }
7878
}
79-
return { data: res.rows, error: null }
80-
} catch (error: any) {
81-
if (error instanceof DatabaseError) {
82-
// Roughly based on:
83-
// - https://github.com/postgres/postgres/blob/fc4089f3c65a5f1b413a3299ba02b66a8e5e37d0/src/interfaces/libpq/fe-protocol3.c#L1018
84-
// - https://github.com/brianc/node-postgres/blob/b1a8947738ce0af004cb926f79829bb2abc64aa6/packages/pg/lib/native/query.js#L33
85-
let formattedError = ''
86-
{
87-
if (error.severity) {
88-
formattedError += `${error.severity}: `
89-
}
90-
if (error.code) {
91-
formattedError += `${error.code}: `
92-
}
93-
if (error.message) {
94-
formattedError += error.message
95-
}
96-
formattedError += '\n'
97-
if (error.position) {
98-
// error.position is 1-based
99-
const position = Number(error.position) - 1
100-
101-
let line = ''
102-
let lineNumber = 0
103-
let lineOffset = 0
104-
105-
const lines = sql.split('\n')
106-
let currentOffset = 0
107-
for (let i = 0; i < lines.length; i++) {
108-
if (currentOffset + lines[i].length > position) {
109-
line = lines[i]
110-
lineNumber = i + 1 // 1-based
111-
lineOffset = position - currentOffset
112-
break
113-
}
114-
currentOffset += lines[i].length + 1 // 1 extra offset for newline
115-
}
116-
formattedError += `LINE ${lineNumber}: ${line}
117-
${' '.repeat(5 + lineNumber.toString().length + 2 + lineOffset)}^
118-
`
119-
}
120-
if (error.detail) {
121-
formattedError += `DETAIL: ${error.detail}
122-
`
123-
}
124-
if (error.hint) {
125-
formattedError += `HINT: ${error.hint}
126-
`
127-
}
128-
if (error.internalQuery) {
129-
formattedError += `QUERY: ${error.internalQuery}
130-
`
131-
}
132-
if (error.where) {
133-
formattedError += `CONTEXT: ${error.where}
134-
`
135-
}
136-
}
137-
138-
return {
139-
data: null,
140-
error: {
141-
...error,
142-
// error.message is non-enumerable
143-
message: error.message,
144-
formattedError,
145-
},
146-
}
147-
}
148-
149-
return { data: null, error: { message: error.message } }
79+
return wrapResult(res.rows)
80+
} catch (error) {
81+
return wrapError(error, sql)
15082
}
15183
},
15284

‎src/lib/helpers.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,86 @@
11
import { literal } from 'pg-format'
2+
import { DatabaseError } from 'pg-protocol'
3+
import { PostgresMetaErr, PostgresMetaOk } from './types.js'
4+
5+
export const wrapResult = <T>(result: T): PostgresMetaOk<T> => {
6+
return { data: result, error: null }
7+
}
8+
9+
export const wrapError = (error: unknown, sql: string): PostgresMetaErr => {
10+
if (error instanceof DatabaseError) {
11+
// Roughly based on:
12+
// - https://github.com/postgres/postgres/blob/fc4089f3c65a5f1b413a3299ba02b66a8e5e37d0/src/interfaces/libpq/fe-protocol3.c#L1018
13+
// - https://github.com/brianc/node-postgres/blob/b1a8947738ce0af004cb926f79829bb2abc64aa6/packages/pg/lib/native/query.js#L33
14+
let formattedError = ''
15+
{
16+
if (error.severity) {
17+
formattedError += `${error.severity}: `
18+
}
19+
if (error.code) {
20+
formattedError += `${error.code}: `
21+
}
22+
if (error.message) {
23+
formattedError += error.message
24+
}
25+
formattedError += '\n'
26+
if (error.position) {
27+
// error.position is 1-based
28+
const position = Number(error.position) - 1
29+
30+
let line = ''
31+
let lineNumber = 0
32+
let lineOffset = 0
33+
34+
const lines = sql.split('\n')
35+
let currentOffset = 0
36+
for (let i = 0; i < lines.length; i++) {
37+
if (currentOffset + lines[i].length > position) {
38+
line = lines[i]
39+
lineNumber = i + 1 // 1-based
40+
lineOffset = position - currentOffset
41+
break
42+
}
43+
currentOffset += lines[i].length + 1 // 1 extra offset for newline
44+
}
45+
formattedError += `LINE ${lineNumber}: ${line}
46+
${' '.repeat(5 + lineNumber.toString().length + 2 + lineOffset)}^
47+
`
48+
}
49+
if (error.detail) {
50+
formattedError += `DETAIL: ${error.detail}
51+
`
52+
}
53+
if (error.hint) {
54+
formattedError += `HINT: ${error.hint}
55+
`
56+
}
57+
if (error.internalQuery) {
58+
formattedError += `QUERY: ${error.internalQuery}
59+
`
60+
}
61+
if (error.where) {
62+
formattedError += `CONTEXT: ${error.where}
63+
`
64+
}
65+
}
66+
67+
return {
68+
data: null,
69+
error: {
70+
...error,
71+
// error.message is non-enumerable
72+
message: error.message,
73+
formattedError,
74+
},
75+
}
76+
}
77+
78+
if (error instanceof Error) {
79+
return { data: null, error: { message: error.message } }
80+
}
81+
82+
throw error
83+
}
284

385
export const coalesceRowsToArray = (source: string, filter: string) => {
486
return `

‎src/lib/index.ts

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,3 @@
1+
export * from './base.js'
2+
13
export { default as PostgresMeta } from './PostgresMeta.js'
2-
export {
3-
PostgresMetaOk,
4-
PostgresMetaErr,
5-
PostgresMetaResult,
6-
PostgresColumn,
7-
PostgresConfig,
8-
PostgresExtension,
9-
PostgresFunction,
10-
PostgresFunctionCreate,
11-
PostgresIndex,
12-
PostgresMaterializedView,
13-
PostgresPolicy,
14-
PostgresPrimaryKey,
15-
PostgresPublication,
16-
PostgresRelationship,
17-
PostgresRole,
18-
PostgresSchema,
19-
PostgresSchemaCreate,
20-
PostgresSchemaUpdate,
21-
PostgresTable,
22-
PostgresTrigger,
23-
PostgresType,
24-
PostgresVersion,
25-
PostgresView,
26-
} from './types.js'

‎src/lib/sql/index.ts

Lines changed: 0 additions & 34 deletions
This file was deleted.

‎src/lib/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ export interface PostgresMetaErr {
1616

1717
export type PostgresMetaResult<T> = PostgresMetaOk<T> | PostgresMetaErr
1818

19+
export type QueryFn = <T>(sql: string) => Promise<PostgresMetaResult<T[]>>
20+
export type EndFn = () => Promise<void>
21+
1922
export const postgresColumnSchema = Type.Object({
2023
table_id: Type.Integer(),
2124
schema: Type.String(),

‎src/types/sql.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
declare module '*.sql' {
2+
const sql: string;
3+
export default sql;
4+
}

‎tsup.config.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { defineConfig } from 'tsup'
2+
3+
export default defineConfig([
4+
{
5+
name: 'lib',
6+
entry: {
7+
index: './src/lib/index.ts',
8+
base: './src/lib/base.ts',
9+
},
10+
11+
format: ['cjs', 'esm'],
12+
dts: {
13+
compilerOptions: {
14+
incremental: false,
15+
},
16+
},
17+
sourcemap: true,
18+
clean: true,
19+
loader: {
20+
'.sql': 'text',
21+
},
22+
outDir: 'dist/lib',
23+
},
24+
{
25+
name: 'server',
26+
entry: {
27+
server: './src/server/server.ts',
28+
},
29+
30+
format: ['esm'],
31+
dts: {
32+
compilerOptions: {
33+
incremental: false,
34+
},
35+
},
36+
sourcemap: true,
37+
clean: true,
38+
loader: {
39+
'.sql': 'text',
40+
},
41+
outDir: 'dist/server',
42+
},
43+
])

‎vitest.config.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,23 @@
11
/// <reference types="vitest" />
2+
import { readFile } from 'fs/promises'
3+
import { Plugin } from 'vite'
24
import { defineConfig } from 'vitest/config'
35

6+
function sqlLoaderPlugin(): Plugin {
7+
return {
8+
name: 'sql-loader',
9+
async transform(code, id) {
10+
if (id.endsWith('.sql')) {
11+
const textContent = await readFile(id, 'utf8')
12+
return `export default ${JSON.stringify(textContent)};`
13+
}
14+
return code
15+
},
16+
}
17+
}
18+
419
export default defineConfig({
20+
plugins: [sqlLoaderPlugin()],
521
test: {
622
maxConcurrency: 1,
723
// https://github.com/vitest-dev/vitest/issues/317#issuecomment-1542319622

0 commit comments

Comments
 (0)
Please sign in to comment.