@technicity/data-service-generator 0.23.0-next.0 → 0.23.0-next.10

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.
Files changed (74) hide show
  1. package/dist/generation/generate.d.ts +4 -0
  2. package/dist/generation/generate.js +450 -183
  3. package/dist/generation/pg2sqliteSchema.d.ts +6 -0
  4. package/dist/generation/pg2sqliteSchema.js +121 -0
  5. package/dist/runtime/IRuntime.d.ts +2 -1
  6. package/dist/runtime/lib/getSqlAst.js +8 -6
  7. package/dist/runtime/lib/shared.js +57 -13
  8. package/dist/runtime/lib/stringifyWhere.js +7 -0
  9. package/package.json +3 -1
  10. package/dist/src/generation/generate.d.ts +0 -21
  11. package/dist/src/generation/generate.js +0 -2349
  12. package/dist/src/index.d.ts +0 -4
  13. package/dist/src/index.js +0 -11
  14. package/dist/src/lib/CustomError.d.ts +0 -3
  15. package/dist/src/lib/CustomError.js +0 -10
  16. package/dist/src/lib/capitalizeFirstLetter.d.ts +0 -1
  17. package/dist/src/lib/capitalizeFirstLetter.js +0 -6
  18. package/dist/src/lib/getDuplicates.d.ts +0 -1
  19. package/dist/src/lib/getDuplicates.js +0 -9
  20. package/dist/src/lib/isNotNullOrUndefined.d.ts +0 -1
  21. package/dist/src/lib/isNotNullOrUndefined.js +0 -7
  22. package/dist/src/runtime/Cache.d.ts +0 -28
  23. package/dist/src/runtime/Cache.js +0 -142
  24. package/dist/src/runtime/IRuntime.d.ts +0 -209
  25. package/dist/src/runtime/IRuntime.js +0 -12
  26. package/dist/src/runtime/RuntimeMySQL.d.ts +0 -26
  27. package/dist/src/runtime/RuntimeMySQL.js +0 -132
  28. package/dist/src/runtime/RuntimeSQLite.d.ts +0 -42
  29. package/dist/src/runtime/RuntimeSQLite.js +0 -150
  30. package/dist/src/runtime/Stats.d.ts +0 -8
  31. package/dist/src/runtime/Stats.js +0 -31
  32. package/dist/src/runtime/lib/MySQL.d.ts +0 -13
  33. package/dist/src/runtime/lib/MySQL.js +0 -116
  34. package/dist/src/runtime/lib/SDKBadWhereError.d.ts +0 -4
  35. package/dist/src/runtime/lib/SDKBadWhereError.js +0 -10
  36. package/dist/src/runtime/lib/SDKNotFoundError.d.ts +0 -4
  37. package/dist/src/runtime/lib/SDKNotFoundError.js +0 -10
  38. package/dist/src/runtime/lib/addNullFallbacks.d.ts +0 -1
  39. package/dist/src/runtime/lib/addNullFallbacks.js +0 -32
  40. package/dist/src/runtime/lib/addNullFallbacks.test.d.ts +0 -1
  41. package/dist/src/runtime/lib/addNullFallbacks.test.js +0 -206
  42. package/dist/src/runtime/lib/cursor.d.ts +0 -2
  43. package/dist/src/runtime/lib/cursor.js +0 -10
  44. package/dist/src/runtime/lib/getDateTimeStringMySQL.d.ts +0 -1
  45. package/dist/src/runtime/lib/getDateTimeStringMySQL.js +0 -7
  46. package/dist/src/runtime/lib/getOrderBy.d.ts +0 -5
  47. package/dist/src/runtime/lib/getOrderBy.js +0 -52
  48. package/dist/src/runtime/lib/getSqlAst.d.ts +0 -2
  49. package/dist/src/runtime/lib/getSqlAst.js +0 -245
  50. package/dist/src/runtime/lib/getWhere.d.ts +0 -2
  51. package/dist/src/runtime/lib/getWhere.js +0 -20
  52. package/dist/src/runtime/lib/shared.d.ts +0 -13
  53. package/dist/src/runtime/lib/shared.js +0 -1118
  54. package/dist/src/runtime/lib/stringifyWhere.d.ts +0 -18
  55. package/dist/src/runtime/lib/stringifyWhere.js +0 -257
  56. package/dist/src/runtime/lib/stringifyWhere.test.d.ts +0 -1
  57. package/dist/src/runtime/lib/stringifyWhere.test.js +0 -245
  58. package/dist/src/runtime/lib/utility.d.ts +0 -5
  59. package/dist/src/runtime/lib/utility.js +0 -14
  60. package/dist/src/traverseFieldArgs.d.ts +0 -2
  61. package/dist/src/traverseFieldArgs.js +0 -17
  62. package/dist/src/traverseFieldArgs.test.d.ts +0 -1
  63. package/dist/src/traverseFieldArgs.test.js +0 -56
  64. package/dist/test/addWhereValidTrue.d.ts +0 -1
  65. package/dist/test/addWhereValidTrue.js +0 -39
  66. package/dist/test/globalSetup.d.ts +0 -13
  67. package/dist/test/globalSetup.js +0 -436
  68. package/dist/test/postgres/__generated__/sdk-ts/artifacts.d.ts +0 -8425
  69. package/dist/test/postgres/__generated__/sdk-ts/artifacts.js +0 -10469
  70. package/dist/test/postgres/__generated__/sdk-ts/index.js +0 -12162
  71. /package/dist/{src/runtime → runtime}/RuntimePostgreSQL.d.ts +0 -0
  72. /package/dist/{src/runtime → runtime}/RuntimePostgreSQL.js +0 -0
  73. /package/dist/{src/runtime → runtime}/lib/PostgreSQL.d.ts +0 -0
  74. /package/dist/{src/runtime → runtime}/lib/PostgreSQL.js +0 -0
@@ -30,24 +30,29 @@ exports.generate = generate;
30
30
  const path = __importStar(require("node:path"));
31
31
  const fs = __importStar(require("node:fs"));
32
32
  const os = __importStar(require("node:os"));
33
+ const node_async_hooks_1 = require("node:async_hooks");
33
34
  const child_process = __importStar(require("node:child_process"));
34
35
  const node_crypto_1 = __importDefault(require("node:crypto"));
36
+ const pino_1 = require("pino");
35
37
  const prettier = __importStar(require("prettier"));
36
38
  const changeCase = __importStar(require("change-case"));
37
39
  const fse = __importStar(require("fs-extra"));
38
40
  const _ = __importStar(require("lodash/fp"));
41
+ const memoize_1 = __importDefault(require("lodash/memoize"));
39
42
  const json_schema_to_typescript_1 = require("json-schema-to-typescript");
40
43
  const getDuplicates_1 = require("../lib/getDuplicates");
41
44
  const isNotNullOrUndefined_1 = require("../lib/isNotNullOrUndefined");
45
+ const pg_1 = require("pg");
42
46
  const MySQL_1 = require("../runtime/lib/MySQL");
43
47
  const capitalizeFirstLetter_1 = require("../lib/capitalizeFirstLetter");
44
- // json-schema-to-typescript inlines everything. We don't want that,
45
- // so use `tsType`, and add imports manually.
46
- // https://github.com/bcherny/json-schema-to-typescript#custom-schema-properties
47
- // https://github.com/bcherny/json-schema-to-typescript/issues/258
48
- // Meh
49
- let query = undefined;
50
- let dialect = "mysql";
48
+ const pg2sqliteSchema_1 = require("./pg2sqliteSchema");
49
+ const ctxStorage = new node_async_hooks_1.AsyncLocalStorage();
50
+ function getCtx() {
51
+ const c = ctxStorage.getStore();
52
+ if (!c)
53
+ throw new Error("generate() context missing");
54
+ return c;
55
+ }
51
56
  const json2TsOpts = {
52
57
  bannerComment: ""
53
58
  };
@@ -59,162 +64,237 @@ async function generate(input) {
59
64
  if (input.dialect == null) {
60
65
  throw new Error("Must specify `dialect`");
61
66
  }
67
+ const log = (0, pino_1.pino)({
68
+ name: "generate",
69
+ level: input.logLevel ?? process.env.LOG_LEVEL ?? "info",
70
+ transport: {
71
+ target: "pino-pretty",
72
+ options: {
73
+ colorize: true
74
+ }
75
+ }
76
+ });
62
77
  const specialCaseUuidColumn = input.specialCaseUuidColumn ?? true;
63
78
  const includeMappedFields = input.includeMappedFields ?? true;
64
79
  const supplementClientOpts = input.supplementClientOpts ?? true;
65
- dialect = input.dialect;
66
- init(input);
67
- let tables = await getTableNames();
68
- if (tables.length === 0) {
69
- throw new Error("No tables found");
70
- }
71
- if (input.tables != null) {
72
- tables = tables.filter((x) => input.tables?.includes(x));
73
- }
74
- if (input.excludeTables != null) {
75
- tables = tables.filter((x) => !input.excludeTables?.includes(x));
76
- }
77
- const data = await Promise.all(tables.flatMap((x) => [
78
- getGetOneData(x, includeMappedFields),
79
- getGetListData(x),
80
- getGetListPaginatedData(x),
81
- getPostOneData(x, specialCaseUuidColumn, includeMappedFields),
82
- getPatchOneData(x, specialCaseUuidColumn, includeMappedFields),
83
- getPatchListData(x),
84
- getDeleteOneData(x),
85
- getDeleteListData(x)
86
- ]));
87
- const artifacts = await getArtifacts(tables, includeMappedFields, specialCaseUuidColumn);
88
- const artifactsSource = getArtifactsSource(artifacts);
89
- const sdkSource = await getSDKSource(data, specialCaseUuidColumn, supplementClientOpts, artifacts, input.outputSqliteSchema);
90
- const sdkFilename = "index.ts";
91
- const sourceIRuntimeFilePath = fs.existsSync(path.join(__dirname, "../runtime", "IRuntime.ts"))
92
- ? path.join(__dirname, "../runtime", "IRuntime.ts")
93
- : path.join(__dirname, "../runtime", "IRuntime.js");
94
- const IRuntimeFilename = path.basename(sourceIRuntimeFilePath);
95
- const artifactsFilename = "artifacts.ts";
96
- const tsConfigJSON = {
97
- compilerOptions: {
98
- module: "commonjs",
99
- moduleResolution: "node",
100
- target: "es2020",
101
- declaration: true,
102
- outDir: "./sdk-ts"
103
- },
104
- include: [sdkFilename, artifactsFilename, IRuntimeFilename]
80
+ const runId = node_crypto_1.default.randomUUID();
81
+ const ctx = {
82
+ runId,
83
+ log: log.child({}),
84
+ dialect: input.dialect,
85
+ query: undefined,
86
+ pool: undefined
105
87
  };
106
- const packageJSON = {
107
- name: "temp",
108
- version: "1.0.0",
109
- // Deps need to be included so that they're inlined by ncc
110
- dependencies: require("../../package.json").dependencies,
111
- devDependencies: {
112
- "@types/node": require("../../package.json").devDependencies["@types/node"],
113
- typescript: require("../../package.json").devDependencies.typescript
114
- },
115
- // Not `resolutions` because npm used for install
116
- overrides: {
117
- // Fix for: `Cannot find type definition file for 'glob'`
118
- glob: ">9.0.0"
88
+ log.debug({
89
+ runId: ctx.runId,
90
+ dialect: input.dialect,
91
+ database: input.database,
92
+ outdir: input.outdir
93
+ }, "generate() started");
94
+ return ctxStorage.run(ctx, async () => {
95
+ init(input);
96
+ ctx.log.debug("init() completed");
97
+ let tables = await getTableNames();
98
+ ctx.log.debug({ tableCount: tables.length, tables }, "getTableNames() completed");
99
+ if (tables.length === 0) {
100
+ throw new Error("No tables found");
119
101
  }
120
- };
121
- const tmpDirPath = path.join(os.tmpdir(),
122
- // _ because - in filename is not supported by mysql2sqlite
123
- `dsg_${node_crypto_1.default.randomUUID()}`.replace(/-/g, "_"));
124
- fse.mkdirpSync(tmpDirPath);
125
- fs.writeFileSync(path.join(tmpDirPath, sdkFilename), sdkSource);
126
- fs.writeFileSync(path.join(tmpDirPath, artifactsFilename), artifactsSource);
127
- fse.copyFileSync(sourceIRuntimeFilePath, path.join(tmpDirPath, IRuntimeFilename));
128
- const typesDirPath = path.join(tmpDirPath, "types");
129
- fse.mkdirpSync(typesDirPath);
130
- fs.writeFileSync(path.join(typesDirPath, "_shared.ts"), getTypeShared());
131
- for (let x of data) {
132
- if (x.kind === "getOne") {
133
- fs.writeFileSync(path.join(typesDirPath, x.typeFieldsName + ".ts"), x.typeFields);
134
- fs.writeFileSync(path.join(typesDirPath, x.typeReturnBaseName + ".ts"), x.typeReturnBase);
102
+ if (input.tables != null) {
103
+ tables = tables.filter((x) => input.tables?.includes(x));
135
104
  }
136
- if (x.kind === "getList") {
137
- fs.writeFileSync(path.join(typesDirPath, x.typeWhereName + ".ts"), x.typeWhere);
138
- fs.writeFileSync(path.join(typesDirPath, x.typeOrderByName + ".ts"), x.typeOrderBy);
105
+ if (input.excludeTables != null) {
106
+ tables = tables.filter((x) => !input.excludeTables?.includes(x));
139
107
  }
140
- if (x.kind === "postOne") {
141
- fs.writeFileSync(path.join(typesDirPath, x.typeDataName + ".ts"), x.typeData);
142
- }
143
- if (x.kind === "patchOne") {
144
- fs.writeFileSync(path.join(typesDirPath, x.typeDataName + ".ts"), x.typeData);
108
+ ctx.log.debug({ tableCount: tables.length, tables }, "tables after filter");
109
+ const data = await Promise.all(tables.flatMap((x) => [
110
+ getGetOneData(x, includeMappedFields),
111
+ getGetListData(x),
112
+ getGetListPaginatedData(x),
113
+ getPostOneData(x, specialCaseUuidColumn, includeMappedFields),
114
+ getPatchOneData(x, specialCaseUuidColumn, includeMappedFields),
115
+ getPatchListData(x),
116
+ getDeleteOneData(x),
117
+ getDeleteListData(x)
118
+ ]));
119
+ ctx.log.debug({ inputLength: data.length }, "SDK input data collected");
120
+ const artifacts = await getArtifacts(tables, includeMappedFields, specialCaseUuidColumn);
121
+ ctx.log.debug("getArtifacts() completed");
122
+ const artifactsSource = getArtifactsSource(artifacts);
123
+ const sdkSource = await getSDKSource(data, specialCaseUuidColumn, supplementClientOpts, artifacts, input.outputSqliteSchema);
124
+ ctx.log.debug("getSDKSource() completed");
125
+ const sdkFilename = "index.ts";
126
+ const sourceIRuntimeFilePath = fs.existsSync(path.join(__dirname, "../runtime", "IRuntime.ts"))
127
+ ? path.join(__dirname, "../runtime", "IRuntime.ts")
128
+ : path.join(__dirname, "../runtime", "IRuntime.js");
129
+ const IRuntimeFilename = path.basename(sourceIRuntimeFilePath);
130
+ const artifactsFilename = "artifacts.ts";
131
+ const tsConfigJSON = {
132
+ compilerOptions: {
133
+ module: "commonjs",
134
+ moduleResolution: "node",
135
+ target: "es2020",
136
+ declaration: true,
137
+ outDir: "./sdk-ts"
138
+ },
139
+ include: [sdkFilename, artifactsFilename, IRuntimeFilename]
140
+ };
141
+ const packageJSON = {
142
+ name: "temp",
143
+ version: "1.0.0",
144
+ // Deps need to be included so that they're inlined by ncc
145
+ dependencies: require("../../package.json").dependencies,
146
+ devDependencies: {
147
+ "@types/node": require("../../package.json").devDependencies["@types/node"],
148
+ typescript: require("../../package.json").devDependencies.typescript
149
+ },
150
+ // Not `resolutions` because npm used for install
151
+ overrides: {
152
+ // Fix for: `Cannot find type definition file for 'glob'`
153
+ glob: ">9.0.0"
154
+ }
155
+ };
156
+ const tmpDirPath = path.join(os.tmpdir(),
157
+ // _ because - in filename is not supported by mysql2sqlite
158
+ `dsg_${node_crypto_1.default.randomUUID()}`.replace(/-/g, "_"));
159
+ ctx.log.debug({ tmpDirPath }, "writing SDK and artifacts to tmp dir");
160
+ fse.mkdirpSync(tmpDirPath);
161
+ fs.writeFileSync(path.join(tmpDirPath, sdkFilename), sdkSource);
162
+ fs.writeFileSync(path.join(tmpDirPath, artifactsFilename), artifactsSource);
163
+ fse.copyFileSync(sourceIRuntimeFilePath, path.join(tmpDirPath, IRuntimeFilename));
164
+ const typesDirPath = path.join(tmpDirPath, "types");
165
+ fse.mkdirpSync(typesDirPath);
166
+ fs.writeFileSync(path.join(typesDirPath, "_shared.ts"), getTypeShared());
167
+ for (let x of data) {
168
+ if (x.kind === "getOne") {
169
+ fs.writeFileSync(path.join(typesDirPath, x.typeFieldsName + ".ts"), x.typeFields);
170
+ fs.writeFileSync(path.join(typesDirPath, x.typeReturnBaseName + ".ts"), x.typeReturnBase);
171
+ }
172
+ if (x.kind === "getList") {
173
+ fs.writeFileSync(path.join(typesDirPath, x.typeWhereName + ".ts"), x.typeWhere);
174
+ fs.writeFileSync(path.join(typesDirPath, x.typeOrderByName + ".ts"), x.typeOrderBy);
175
+ }
176
+ if (x.kind === "postOne") {
177
+ fs.writeFileSync(path.join(typesDirPath, x.typeDataName + ".ts"), x.typeData);
178
+ }
179
+ if (x.kind === "patchOne") {
180
+ fs.writeFileSync(path.join(typesDirPath, x.typeDataName + ".ts"), x.typeData);
181
+ }
145
182
  }
146
- }
147
- fs.writeFileSync(path.join(typesDirPath, "index.ts"), getTypeTypesIndex(data));
148
- fs.writeFileSync(path.join(tmpDirPath, "package.json"), JSON.stringify(packageJSON, null, 2));
149
- fs.writeFileSync(path.join(tmpDirPath, "tsconfig.json"), JSON.stringify(tsConfigJSON, null, 2));
150
- fse.copySync(__dirname, path.join(tmpDirPath, "src"));
151
- const tmpBuildOutputPath = path.join(tmpDirPath, "sdk-ts");
152
- const outdir = path.resolve(input.outdir);
153
- const sdkOutputPath = path.join(outdir, "sdk-ts");
154
- const nccVersion = "^0.33.0";
155
- child_process.execSync("npm i", { cwd: tmpDirPath, stdio: "inherit" });
156
- child_process.execSync(`npm_config_yes=true npx -p @vercel/ncc@${nccVersion} ncc build ./${sdkFilename} -o ${tmpBuildOutputPath} -e ./artifacts`, { cwd: tmpDirPath, stdio: "inherit" });
157
- // TODO: workaround for artifacts.js not being output by ncc
158
- fs.writeFileSync(path.join(tmpBuildOutputPath, "artifacts.js"), artifactsSource
159
- .replace("export const artifacts: IArtifacts = ", "module.exports.artifacts = ")
160
- .split("\n")
161
- // Remove import
162
- .slice(2)
163
- .join("\n"));
164
- // TODO: workaround for IRuntime.d.ts not being included
165
- // copyFileSync hangs for some reason, so use writeFileSync + readFileSync instead
166
- fs.writeFileSync(path.join(tmpBuildOutputPath, "IRuntime.d.ts"), fs.existsSync(path.join(__dirname, "../runtime", "IRuntime.d.ts"))
167
- ? fs.readFileSync(path.join(__dirname, "../runtime", "IRuntime.d.ts"), "utf-8")
168
- : fs.readFileSync(sourceIRuntimeFilePath, "utf-8"));
169
- if (dialect === "mysql" && input.outputSqliteSchema) {
170
- // Since mysql2sqlite outputs a malformed string if a column
171
- // has the name `enum`, temporarily change the name to something else,
172
- // then change it back.
173
- const enumMarker = "`" + node_crypto_1.default.randomUUID() + "`";
174
- const schemaMySql = Object.values(artifacts)
175
- .reduce((acc, x) => {
176
- let d = x.dump?.schema;
177
- if (!d) {
178
- return acc;
183
+ fs.writeFileSync(path.join(typesDirPath, "index.ts"), getTypeTypesIndex(data));
184
+ fs.writeFileSync(path.join(tmpDirPath, "package.json"), JSON.stringify(packageJSON, null, 2));
185
+ fs.writeFileSync(path.join(tmpDirPath, "tsconfig.json"), JSON.stringify(tsConfigJSON, null, 2));
186
+ fse.copySync(__dirname, path.join(tmpDirPath, "src"));
187
+ const tmpBuildOutputPath = path.join(tmpDirPath, "sdk-ts");
188
+ const outdir = path.resolve(input.outdir);
189
+ const sdkOutputPath = path.join(outdir, "sdk-ts");
190
+ const nccVersion = "^0.33.0";
191
+ child_process.execSync("npm i", { cwd: tmpDirPath, stdio: "inherit" });
192
+ child_process.execSync(`npm_config_yes=true npx -p @vercel/ncc@${nccVersion} ncc build ./${sdkFilename} -o ${tmpBuildOutputPath} -e ./artifacts`, { cwd: tmpDirPath, stdio: "inherit" });
193
+ // TODO: workaround for artifacts.js not being output by ncc
194
+ fs.writeFileSync(path.join(tmpBuildOutputPath, "artifacts.js"), artifactsSource
195
+ .replace("export const artifacts: IArtifacts = ", "module.exports.artifacts = ")
196
+ .split("\n")
197
+ // Remove import
198
+ .slice(2)
199
+ .join("\n"));
200
+ // TODO: workaround for IRuntime.d.ts not being included
201
+ // copyFileSync hangs for some reason, so use writeFileSync + readFileSync instead
202
+ fs.writeFileSync(path.join(tmpBuildOutputPath, "IRuntime.d.ts"), fs.existsSync(path.join(__dirname, "../runtime", "IRuntime.d.ts"))
203
+ ? fs.readFileSync(path.join(__dirname, "../runtime", "IRuntime.d.ts"), "utf-8")
204
+ : fs.readFileSync(sourceIRuntimeFilePath, "utf-8"));
205
+ if (input.outputSqliteSchema) {
206
+ let schemaSqlite = null;
207
+ if (ctx.dialect === "mysql") {
208
+ // Since mysql2sqlite outputs a malformed string if a column
209
+ // has the name `enum`, temporarily change the name to something else,
210
+ // then change it back.
211
+ const enumMarker = "`" + node_crypto_1.default.randomUUID() + "`";
212
+ const schemaMySql = Object.values(artifacts)
213
+ .reduce((acc, x) => {
214
+ let d = x.dump?.schema;
215
+ if (!d) {
216
+ return acc;
217
+ }
218
+ d = d.replace(/`enum`/g, enumMarker);
219
+ d += ";";
220
+ acc.push(d);
221
+ return acc;
222
+ }, [])
223
+ .join("\n\n");
224
+ const mysql2SqliteSrc = getMysql2sqliteSrc();
225
+ const mysql2SqlitePath = path.join(tmpDirPath, "mysql2sqlite");
226
+ fs.writeFileSync(mysql2SqlitePath, mysql2SqliteSrc);
227
+ fs.chmodSync(mysql2SqlitePath, 0o755);
228
+ const tmpMySqlSchemaFilename = "tmp.sql";
229
+ const tmpMySqlSchemaPath = path.join(tmpDirPath, tmpMySqlSchemaFilename);
230
+ fs.writeFileSync(tmpMySqlSchemaPath, schemaMySql);
231
+ schemaSqlite = child_process
232
+ .execFileSync(mysql2SqlitePath, [tmpMySqlSchemaFilename], {
233
+ cwd: tmpDirPath
234
+ })
235
+ .toString();
236
+ schemaSqlite = schemaSqlite.replace(new RegExp(enumMarker, "g"), "`enum`");
179
237
  }
180
- d = d.replace(/`enum`/g, enumMarker);
181
- d += ";";
182
- acc.push(d);
183
- return acc;
184
- }, [])
185
- .join("\n\n");
186
- const mysql2SqliteSrc = getMysql2sqliteSrc();
187
- const mysql2SqlitePath = path.join(tmpDirPath, "mysql2sqlite");
188
- fs.writeFileSync(mysql2SqlitePath, mysql2SqliteSrc);
189
- fs.chmodSync(mysql2SqlitePath, 0o755);
190
- const tmpMySqlSchemaFilename = "tmp.sql";
191
- const tmpMySqlSchemaPath = path.join(tmpDirPath, tmpMySqlSchemaFilename);
192
- fs.writeFileSync(tmpMySqlSchemaPath, schemaMySql);
193
- let schemaSqlite = child_process
194
- .execFileSync(mysql2SqlitePath, [tmpMySqlSchemaFilename], { cwd: tmpDirPath })
195
- .toString();
196
- schemaSqlite = schemaSqlite.replace(new RegExp(enumMarker, "g"), "`enum`");
197
- const src = prettier.format(`module.exports = { schema: \`${schemaSqlite.replace(/`/g, "\\`")}\` }`, { parser: "babel" });
198
- fs.writeFileSync(path.join(tmpBuildOutputPath, "artifacts.sqlite.js"), src);
199
- }
200
- if (!fs.existsSync(outdir)) {
201
- fse.mkdirpSync(outdir);
202
- }
203
- fse.emptyDirSync(sdkOutputPath);
204
- fse.copySync(tmpBuildOutputPath, sdkOutputPath);
205
- fse.removeSync(tmpDirPath);
238
+ else if (ctx.dialect === "postgresql") {
239
+ schemaSqlite = await (0, pg2sqliteSchema_1.convertPgSchemaToSqlite)(ctx.pool);
240
+ }
241
+ if (schemaSqlite) {
242
+ const src = prettier.format(`module.exports = { schema: \`${schemaSqlite.replace(/`/g, "\\`")}\` }`, { parser: "babel" });
243
+ fs.writeFileSync(path.join(tmpBuildOutputPath, "artifacts.sqlite.js"), src);
244
+ }
245
+ }
246
+ if (!fs.existsSync(outdir)) {
247
+ fse.mkdirpSync(outdir);
248
+ }
249
+ fse.emptyDirSync(sdkOutputPath);
250
+ fse.copySync(tmpBuildOutputPath, sdkOutputPath);
251
+ ctx.log.debug({ outdir, sdkOutputPath }, "copy to output dir completed");
252
+ fse.removeSync(tmpDirPath);
253
+ ctx.log.debug("generate() completed");
254
+ });
206
255
  }
207
256
  function init(input) {
257
+ const ctx = getCtx();
208
258
  const { database, user, password, host, port, server } = input;
209
- if (dialect === "mysql") {
210
- let mysql = new MySQL_1.MySQL({
259
+ if (ctx.dialect === "mysql") {
260
+ const connectionOpts = {
211
261
  user,
212
262
  password,
213
- host,
214
- port,
215
- database
216
- });
217
- query = mysql.query.bind(mysql);
263
+ host: host ?? "localhost",
264
+ port: port ?? 3306,
265
+ database,
266
+ ...input.clientOpts
267
+ };
268
+ ctx.log.debug({
269
+ dialect: "mysql",
270
+ host: connectionOpts.host,
271
+ port: connectionOpts.port,
272
+ database,
273
+ user
274
+ }, "connecting to MySQL");
275
+ const mysql = new MySQL_1.MySQL(connectionOpts);
276
+ ctx.query = mysql.query.bind(mysql);
277
+ ctx.pool = mysql;
278
+ }
279
+ if (ctx.dialect === "postgresql") {
280
+ const connectionOpts = {
281
+ host: host ?? "localhost",
282
+ port: port ?? 5432,
283
+ user,
284
+ password,
285
+ database,
286
+ ...input.clientOpts
287
+ };
288
+ ctx.log.debug({
289
+ dialect: "postgresql",
290
+ host: connectionOpts.host,
291
+ port: connectionOpts.port,
292
+ database,
293
+ user
294
+ }, "connecting to PostgreSQL");
295
+ const pool = new pg_1.Pool(connectionOpts);
296
+ ctx.query = (q, values) => pool.query(q, values ?? []).then((r) => r.rows);
297
+ ctx.pool = pool;
218
298
  }
219
299
  }
220
300
  // It's a bit awkward to put __whereNeedsProcessing, __prepareWhere on the class,
@@ -258,7 +338,7 @@ async function getSDKSource(input, specialCaseUuidColumn, supplementClientOpts,
258
338
  runtime: any;
259
339
  clientOpts: { [k: string]: any; },
260
340
  otherOpts?: { [k: string]: any; },
261
- passBeforeValueToAfterCallback: boolean,
341
+ passBeforeValueToAfterCallback?: boolean,
262
342
  }) {
263
343
  let otherOpts = opts.otherOpts ?? {};
264
344
  if (opts.clientOpts.filename === ":memory:") {
@@ -271,7 +351,7 @@ async function getSDKSource(input, specialCaseUuidColumn, supplementClientOpts,
271
351
  : "otherOpts"}, artifacts);
272
352
  this.onHandlerMap = new Map();
273
353
  this.eventTarget = new EventTarget();
274
- this.passBeforeValueToAfterCallback = opts.passBeforeValueToAfterCallback;
354
+ this.passBeforeValueToAfterCallback = opts.passBeforeValueToAfterCallback ?? false;
275
355
  }
276
356
 
277
357
  $use(middleware: TMiddleware) {
@@ -601,6 +681,7 @@ function getMethodSourceOnHandlerPostOne(x) {
601
681
  return `on${(0, capitalizeFirstLetter_1.capitalizeFirstLetter)(x.methodName)}(handler:
602
682
  (sdk: InstanceType<typeof SDK>, input: { data: ${x.typeDataName} },
603
683
  output: Partial<${getTypeReturnName(x.table)}>,
684
+ before: null,
604
685
  context: TContext,
605
686
  ) => Promise<void>
606
687
  ): void {
@@ -715,6 +796,7 @@ function getMethodSourceOnHandlerDeleteOne(x, findOnes) {
715
796
  .map((findOne) => `{ ${findOne.name}: ${findOne.type}${findOne.nullable ? " | null" : ""} }`)
716
797
  .join(" | ")}, },
717
798
  output: void,
799
+ before: ${getTypeReturnName(x.table)} | null,
718
800
  context: TContext,
719
801
  ) => Promise<void>
720
802
  ): void {
@@ -1068,7 +1150,7 @@ async function getMappedFields(table) {
1068
1150
  name: "uuid",
1069
1151
  // Replace `Id` with `Uuid`
1070
1152
  as: x.foreignKey.slice(0, -2) + "Uuid",
1071
- type: getBaseJSONType(uuidColumn.Type)
1153
+ type: getBaseJSONType(uuidColumn.Type, getCtx().dialect)
1072
1154
  });
1073
1155
  }
1074
1156
  return out;
@@ -1424,6 +1506,7 @@ function getArtifactsSource(artifacts) {
1424
1506
  return prettier.format(src, { parser: "typescript" });
1425
1507
  }
1426
1508
  async function getArtifacts(tables, includeMappedFields, specialCaseUuidColumn) {
1509
+ const ctx = getCtx();
1427
1510
  const tableMetaList = await Promise.all(tables.map(async (table) => {
1428
1511
  const [tableMeta, primaryKey, dumpSchema] = await Promise.all([
1429
1512
  getTableMeta(table),
@@ -1489,7 +1572,7 @@ async function getArtifacts(tables, includeMappedFields, specialCaseUuidColumn)
1489
1572
  }
1490
1573
  return {
1491
1574
  kind: "scalar",
1492
- type: getBaseJSONType(t.Type),
1575
+ type: getBaseJSONType(t.Type, ctx.dialect),
1493
1576
  name: t.Field,
1494
1577
  nullable,
1495
1578
  hasDefaultValue: !!t.Default
@@ -1534,7 +1617,7 @@ async function getArtifacts(tables, includeMappedFields, specialCaseUuidColumn)
1534
1617
  }, {});
1535
1618
  return artifacts;
1536
1619
  }
1537
- const getRelationInfo = _.memoize(async function getRelationInfo(table) {
1620
+ const getRelationInfo = (0, memoize_1.default)(async function getRelationInfo(table) {
1538
1621
  const relationsManyToOne = await getRelationsManyToOne(table);
1539
1622
  const relationsOneToMany = await getRelationsOneToMany(table);
1540
1623
  let out = [];
@@ -1602,14 +1685,14 @@ const getRelationInfo = _.memoize(async function getRelationInfo(table) {
1602
1685
  return acc;
1603
1686
  }, []);
1604
1687
  out = out.concat(relationsManyToMany);
1605
- out = _.sortBy((x) => x.table, out);
1688
+ out = _.sortBy([(x) => x.table, (x) => x.name], out);
1606
1689
  return out;
1607
- });
1690
+ }, (table) => getCtx().runId + ":" + table);
1608
1691
  function getRelationManyToOneFieldName(x) {
1609
1692
  return changeCase.camelCase(x.foreignKey.replace(new RegExp(x.referencedKey + "$", "i"), ""));
1610
1693
  }
1611
1694
  // TODO: not sure if this logic is correct
1612
- async function getJunctionTables() {
1695
+ const getJunctionTables = (0, memoize_1.default)(async function getJunctionTables() {
1613
1696
  const tables = await getTableNames();
1614
1697
  return (await Promise.all(tables.map(async (table) => {
1615
1698
  const relations = await getRelationsManyToOne(table);
@@ -1624,14 +1707,15 @@ async function getJunctionTables() {
1624
1707
  }
1625
1708
  return null;
1626
1709
  }))).filter(isNotNullOrUndefined_1.isNotNullOrUndefined);
1627
- }
1710
+ }, () => getCtx().runId);
1628
1711
  // `from` relations
1629
1712
  // https://stackoverflow.com/a/54732547
1630
- // Note: this function
1631
- // isn't pure (because of `dialect`), but that's fine because its value
1632
- // isn't expected to change.
1633
- const getRelationsManyToOne = _.memoize(async function getRelationsManyToOne(table) {
1634
- const sql = `
1713
+ const getRelationsManyToOne = (0, memoize_1.default)(async function getRelationsManyToOne(table) {
1714
+ const { dialect, query } = getCtx();
1715
+ const tableMeta = await getTableMeta(table);
1716
+ let rs;
1717
+ if (dialect === "mysql") {
1718
+ const sql = `
1635
1719
  SELECT
1636
1720
  TABLE_SCHEMA as db,
1637
1721
  TABLE_NAME as t1,
@@ -1646,8 +1730,19 @@ const getRelationsManyToOne = _.memoize(async function getRelationsManyToOne(tab
1646
1730
  AND REFERENCED_TABLE_NAME IS NOT NULL
1647
1731
  AND (TABLE_NAME = ?);
1648
1732
  `;
1649
- const tableMeta = await getTableMeta(table);
1650
- const rs = await query(sql, [table]);
1733
+ rs = await query(sql, [table]);
1734
+ }
1735
+ else if (dialect === "postgresql") {
1736
+ rs = await query(`SELECT kcu.column_name AS "t1Field", ccu.table_name AS t2, ccu.column_name AS "t2Field"
1737
+ FROM information_schema.key_column_usage kcu
1738
+ JOIN information_schema.referential_constraints rc ON kcu.constraint_name = rc.constraint_name AND kcu.table_schema = rc.constraint_schema
1739
+ JOIN information_schema.constraint_column_usage ccu ON rc.unique_constraint_name = ccu.constraint_name AND rc.unique_constraint_schema = ccu.table_schema
1740
+ WHERE kcu.table_schema = 'public' AND kcu.table_name = $1
1741
+ ORDER BY ccu.table_name, ccu.column_name`, [table]);
1742
+ }
1743
+ else {
1744
+ throw new Error("Unsupported dialect: " + dialect);
1745
+ }
1651
1746
  const xs = await Promise.all(_.uniqWith(_.isEqual, rs.map(async (v) => {
1652
1747
  return {
1653
1748
  table: table,
@@ -1657,11 +1752,14 @@ const getRelationsManyToOne = _.memoize(async function getRelationsManyToOne(tab
1657
1752
  nullable: tableMeta.find((m) => m.Field === v.t1Field)?.Null === "YES"
1658
1753
  };
1659
1754
  })));
1660
- return _.sortBy((x) => x.referencedTable, xs);
1661
- });
1755
+ return _.sortBy([(x) => x.referencedTable, (x) => x.referencedKey, (x) => x.foreignKey], xs);
1756
+ }, (table) => getCtx().runId + ":" + table);
1662
1757
  // `to` relations
1663
- const getRelationsOneToMany = _.memoize(async function getRelationsOneToMany(table) {
1664
- const sql = `
1758
+ const getRelationsOneToMany = (0, memoize_1.default)(async function getRelationsOneToMany(table) {
1759
+ const { dialect, query } = getCtx();
1760
+ let rs;
1761
+ if (dialect === "mysql") {
1762
+ const sql = `
1665
1763
  SELECT
1666
1764
  TABLE_SCHEMA as db,
1667
1765
  TABLE_NAME as t1,
@@ -1676,19 +1774,30 @@ const getRelationsOneToMany = _.memoize(async function getRelationsOneToMany(tab
1676
1774
  AND REFERENCED_TABLE_NAME IS NOT NULL
1677
1775
  AND (REFERENCED_TABLE_NAME = ?);
1678
1776
  `;
1679
- const rs = await query(sql, [table]);
1777
+ rs = await query(sql, [table]);
1778
+ }
1779
+ else if (dialect === "postgresql") {
1780
+ rs = await query(`SELECT kcu.table_name AS t1, kcu.column_name AS "t1Field", ccu.column_name AS "t2Field"
1781
+ FROM information_schema.key_column_usage kcu
1782
+ JOIN information_schema.referential_constraints rc ON kcu.constraint_name = rc.constraint_name AND kcu.table_schema = rc.constraint_schema
1783
+ JOIN information_schema.constraint_column_usage ccu ON rc.unique_constraint_name = ccu.constraint_name AND rc.unique_constraint_schema = ccu.table_schema
1784
+ WHERE kcu.table_schema = 'public' AND ccu.table_name = $1
1785
+ ORDER BY kcu.table_name, kcu.column_name`, [table]);
1786
+ }
1787
+ else {
1788
+ throw new Error("Unsupported dialect: " + dialect);
1789
+ }
1680
1790
  const xs = await Promise.all(_.uniqWith(_.isEqual, rs.map(async (v) => {
1681
1791
  return {
1682
1792
  table: table,
1683
1793
  foreignKey: v.t2Field,
1684
1794
  referencedTable: v.t1,
1685
1795
  referencedKey: v.t1Field,
1686
- // TODO? I think this is right, since it's one-to-many, so a list
1687
1796
  nullable: false
1688
1797
  };
1689
1798
  })));
1690
- return _.sortBy((x) => x.referencedKey, _.sortBy((x) => x.referencedTable, xs));
1691
- });
1799
+ return _.sortBy([(x) => x.referencedTable, (x) => x.referencedKey, (x) => x.foreignKey], xs);
1800
+ }, (table) => getCtx().runId + ":" + table);
1692
1801
  async function getPrimaryColumn(table) {
1693
1802
  const tableMeta = await getTableMeta(table);
1694
1803
  const columns = tableMeta.filter((x) => x.Key === "PRI");
@@ -1698,7 +1807,7 @@ async function getPrimaryColumn(table) {
1698
1807
  const column = columns[0];
1699
1808
  return {
1700
1809
  name: column.Field,
1701
- type: getBaseJSONType(column.Type),
1810
+ type: getBaseJSONType(column.Type, getCtx().dialect),
1702
1811
  nullable: column.Null === "YES"
1703
1812
  };
1704
1813
  }
@@ -1710,7 +1819,7 @@ async function getUniqueColumns(table, specialCaseUuidColumn) {
1710
1819
  (specialCaseUuidColumn && x.Field === "uuid"))
1711
1820
  .map((x) => ({
1712
1821
  name: x.Field,
1713
- type: getBaseJSONType(x.Type),
1822
+ type: getBaseJSONType(x.Type, getCtx().dialect),
1714
1823
  nullable: x.Null === "YES"
1715
1824
  }));
1716
1825
  }
@@ -1726,23 +1835,150 @@ async function getUuidColumn(table) {
1726
1835
  nullable: column.Null === "YES"
1727
1836
  };
1728
1837
  }
1729
- const getTableMeta = _.memoize(async function getTableMeta(table) {
1838
+ const getPgEnumDefinition = (0, memoize_1.default)(async function getPgEnumDefinition(udtSchema, udtName) {
1839
+ const { dialect, query } = getCtx();
1840
+ if (dialect !== "postgresql")
1841
+ return null;
1842
+ const rows = await query(`SELECT e.enumlabel FROM pg_enum e
1843
+ JOIN pg_type t ON e.enumtypid = t.oid
1844
+ JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid
1845
+ WHERE t.typname = $2 AND n.nspname = $1
1846
+ ORDER BY e.enumsortorder`, [udtSchema, udtName]);
1847
+ if (rows.length === 0)
1848
+ return null;
1849
+ const labels = rows.map((r) => String(r.enumlabel).replace(/'/g, "''"));
1850
+ return "enum('" + labels.join("', '") + "')";
1851
+ }, (udtSchema, udtName) => getCtx().runId + ":" + udtSchema + ":" + udtName);
1852
+ const getTableMeta = (0, memoize_1.default)(async function getTableMeta(table) {
1853
+ const ctx = getCtx();
1854
+ const { dialect, query } = ctx;
1855
+ ctx.log.debug({ table }, "getTableMeta() fetching");
1730
1856
  if (dialect === "mysql") {
1731
1857
  return query("DESCRIBE ??", [table]).then((xs) => _.sortBy((x) => x.Field, xs));
1732
1858
  }
1859
+ if (dialect === "postgresql") {
1860
+ const columns = await query(`SELECT column_name AS "Field", data_type, udt_schema, udt_name, character_maximum_length AS char_max, is_nullable, column_default AS "Default"
1861
+ FROM information_schema.columns
1862
+ WHERE table_schema = 'public' AND table_name = $1
1863
+ ORDER BY ordinal_position`, [table]);
1864
+ const keyInfo = await query(`SELECT a.attname AS col, 'PRI' AS key_type
1865
+ FROM pg_index i
1866
+ JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey) AND NOT a.attisdropped AND a.attnum > 0
1867
+ JOIN pg_class c ON c.oid = i.indrelid
1868
+ JOIN pg_namespace n ON n.oid = c.relnamespace
1869
+ WHERE n.nspname = 'public' AND c.relname = $1 AND i.indisprimary
1870
+ UNION ALL
1871
+ SELECT kcu.column_name AS col, 'UNI' AS key_type
1872
+ FROM information_schema.table_constraints tc
1873
+ JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema
1874
+ WHERE tc.table_schema = 'public' AND tc.table_name = $1 AND tc.constraint_type = 'UNIQUE'
1875
+ UNION ALL
1876
+ SELECT kcu.column_name AS col, 'MUL' AS key_type
1877
+ FROM information_schema.table_constraints tc
1878
+ JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema
1879
+ WHERE tc.table_schema = 'public' AND tc.table_name = $1 AND tc.constraint_type = 'FOREIGN KEY'`, [table]);
1880
+ const keyMap = new Map();
1881
+ for (const k of keyInfo) {
1882
+ if (!keyMap.has(k.col) || k.key_type === "PRI")
1883
+ keyMap.set(k.col, k.key_type);
1884
+ }
1885
+ const udtKeys = [
1886
+ ...new Map(columns
1887
+ .filter((c) => c.data_type === "USER-DEFINED" &&
1888
+ c.udt_schema != null &&
1889
+ c.udt_name != null)
1890
+ .map((c) => [`${c.udt_schema}.${c.udt_name}`, c])).keys()
1891
+ ];
1892
+ const udtPairs = udtKeys.map((k) => {
1893
+ const [s, n] = k.split(".", 2);
1894
+ return [s, n];
1895
+ });
1896
+ const enumDefs = await Promise.all(udtPairs.map(([schema, name]) => getPgEnumDefinition(schema, name)));
1897
+ const enumMap = new Map(udtKeys.map((k, i) => [k, enumDefs[i] ?? "varchar(255)"]));
1898
+ const cols = columns.map((c) => {
1899
+ let type;
1900
+ if (c.data_type === "USER-DEFINED" &&
1901
+ c.udt_schema != null &&
1902
+ c.udt_name != null) {
1903
+ const enumKey = `${c.udt_schema}.${c.udt_name}`;
1904
+ type = enumMap.get(enumKey) ?? "character varying(255)";
1905
+ }
1906
+ else {
1907
+ type = c.data_type;
1908
+ if ((c.data_type === "character varying" || c.data_type === "character") &&
1909
+ c.char_max != null) {
1910
+ type += "(" + c.char_max + ")";
1911
+ }
1912
+ }
1913
+ return {
1914
+ Field: c.Field,
1915
+ Type: type,
1916
+ Null: c.is_nullable === "YES" ? "YES" : "NO",
1917
+ Key: keyMap.get(c.Field) ?? "",
1918
+ // Preserve `null` when there is no default so that
1919
+ // required-field detection (via `hasDefault`) works
1920
+ // consistently with the MySQL `DESCRIBE` output.
1921
+ Default: c.Default,
1922
+ ...(c.data_type === "USER-DEFINED" &&
1923
+ c.udt_schema != null &&
1924
+ c.udt_name != null
1925
+ ? { PgType: c.udt_name }
1926
+ : {})
1927
+ };
1928
+ });
1929
+ return _.sortBy((c) => c.Field, cols);
1930
+ }
1733
1931
  throw new Error("Unsupported dialect: " + dialect);
1734
- });
1735
- function getShowCreateTable(table) {
1932
+ }, (table) => getCtx().runId + ":" + table);
1933
+ async function getShowCreateTable(table) {
1934
+ const { dialect, query } = getCtx();
1736
1935
  if (dialect === "mysql") {
1737
1936
  return query("SHOW CREATE TABLE ??", [table]).then((xs) => xs[0]["Create Table"]
1738
1937
  // https://github.com/bradzacher/mysqldump/blob/66839a57e572a07c046b0ba98753f30a7026cbd8/src/getSchemaDump.ts#L65
1739
1938
  .replace(/AUTO_INCREMENT\s*=\s*\d+ /g, ""));
1740
1939
  }
1940
+ if (dialect === "postgresql") {
1941
+ const [tableMeta, relations] = await Promise.all([
1942
+ getTableMeta(table),
1943
+ getRelationsManyToOne(table)
1944
+ ]);
1945
+ const refByFk = new Map(relations.map((r) => [r.foreignKey, r]));
1946
+ const columnDefs = tableMeta.map((c) => {
1947
+ const isSerialPk = c.Key === "PRI" &&
1948
+ c.Default != null &&
1949
+ c.Default !== "" &&
1950
+ /nextval\s*\(/i.test(c.Default);
1951
+ if (isSerialPk) {
1952
+ const baseType = (c.PgType ?? c.Type).toLowerCase();
1953
+ const serialType = baseType === "bigint" || baseType === "int8"
1954
+ ? "BIGSERIAL"
1955
+ : baseType === "smallint" || baseType === "int2"
1956
+ ? "SMALLSERIAL"
1957
+ : "SERIAL";
1958
+ return `"${c.Field.replace(/"/g, '""')}" ${serialType} PRIMARY KEY`;
1959
+ }
1960
+ const pgType = c.PgType ?? c.Type;
1961
+ let def = `"${c.Field.replace(/"/g, '""')}" ${pgType} ${c.Null === "YES" ? "NULL" : "NOT NULL"}`;
1962
+ if (c.Default != null && c.Default !== "") {
1963
+ def += ` DEFAULT ${c.Default}`;
1964
+ }
1965
+ if (c.Key === "PRI")
1966
+ def += " PRIMARY KEY";
1967
+ if (c.Key === "UNI")
1968
+ def += " UNIQUE";
1969
+ const ref = refByFk.get(c.Field);
1970
+ if (ref != null) {
1971
+ def += ` REFERENCES "${ref.referencedTable.replace(/"/g, '""')}" ("${ref.referencedKey.replace(/"/g, '""')}")`;
1972
+ }
1973
+ return def;
1974
+ });
1975
+ return `CREATE TABLE "${table.replace(/"/g, '""')}" (\n ${columnDefs.join(",\n ")}\n);`;
1976
+ }
1741
1977
  return Promise.resolve(null);
1742
1978
  }
1743
1979
  function getJSONSchemaObjProperties(tableMeta) {
1744
1980
  return tableMeta.reduce((acc, m) => {
1745
- const baseType = getBaseJSONType(m.Type);
1981
+ const baseType = getBaseJSONType(m.Type, getCtx().dialect);
1746
1982
  const format = getPropertyFormat(m.Type);
1747
1983
  const nullable = m.Null === "YES";
1748
1984
  const isEnum = m.Type.startsWith("enum");
@@ -1778,7 +2014,29 @@ function getJSONTypes(baseType, nullable) {
1778
2014
  return baseType;
1779
2015
  }
1780
2016
  // https://github.com/mysqljs/mysql#type-casting
1781
- function getBaseJSONType(sqlType) {
2017
+ function getBaseJSONType(sqlType, dialect) {
2018
+ if (dialect === "postgresql") {
2019
+ if (sqlType === "boolean" || sqlType === "bool")
2020
+ return "boolean";
2021
+ if (["smallint", "int2", "integer", "int4", "bigint", "int8"].includes(sqlType)) {
2022
+ return "integer";
2023
+ }
2024
+ if (["real", "float4", "double precision", "float8"].includes(sqlType) ||
2025
+ sqlType.startsWith("numeric") ||
2026
+ sqlType.startsWith("decimal")) {
2027
+ return "number";
2028
+ }
2029
+ if (["text", "uuid", "json", "jsonb"].includes(sqlType) ||
2030
+ sqlType === "date" ||
2031
+ sqlType === "time" ||
2032
+ sqlType.startsWith("timestamp") ||
2033
+ sqlType.startsWith("character varying") ||
2034
+ sqlType.startsWith("character") ||
2035
+ sqlType.startsWith("enum")) {
2036
+ return "string";
2037
+ }
2038
+ throw new Error("Unable to map to JSON type: " + sqlType);
2039
+ }
1782
2040
  if (
1783
2041
  // TODO?
1784
2042
  sqlType === "tinyint(1)" ||
@@ -1863,7 +2121,10 @@ function getPropertyEnum(sqlType) {
1863
2121
  return c;
1864
2122
  }
1865
2123
  function getPropertyFormat(sqlType) {
1866
- if (sqlType === "datetime" || sqlType === "datetime2" || sqlType === "timestamp") {
2124
+ if (sqlType === "datetime" ||
2125
+ sqlType === "datetime2" ||
2126
+ sqlType === "timestamp" ||
2127
+ sqlType.startsWith("timestamp")) {
1867
2128
  // TODO: not sure this is correct for `timestamp`
1868
2129
  return "date-time";
1869
2130
  }
@@ -1881,12 +2142,18 @@ function getPropertyFormat(sqlType) {
1881
2142
  }
1882
2143
  return undefined;
1883
2144
  }
1884
- async function getTableNames() {
2145
+ const getTableNames = (0, memoize_1.default)(async function getTableNames() {
2146
+ const { dialect, query } = getCtx();
1885
2147
  if (dialect === "mysql") {
1886
2148
  return query("SHOW TABLES").then((xs) => xs.flatMap((x) => Object.values(x)).sort());
1887
2149
  }
2150
+ if (dialect === "postgresql") {
2151
+ return query(`SELECT table_name FROM information_schema.tables
2152
+ WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
2153
+ ORDER BY table_name`).then((rows) => rows.map((r) => r.table_name));
2154
+ }
1888
2155
  throw new Error("Unsupported dialect: " + dialect);
1889
- }
2156
+ }, () => getCtx().runId);
1890
2157
  function getMysql2sqliteSrc() {
1891
2158
  return `#!/usr/bin/awk -f
1892
2159