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

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 (71) hide show
  1. package/dist/generation/generate.js +338 -170
  2. package/dist/runtime/IRuntime.d.ts +2 -1
  3. package/dist/runtime/lib/getSqlAst.js +8 -6
  4. package/dist/runtime/lib/shared.js +51 -11
  5. package/dist/runtime/lib/stringifyWhere.js +7 -0
  6. package/package.json +1 -1
  7. package/dist/src/generation/generate.d.ts +0 -21
  8. package/dist/src/generation/generate.js +0 -2349
  9. package/dist/src/index.d.ts +0 -4
  10. package/dist/src/index.js +0 -11
  11. package/dist/src/lib/CustomError.d.ts +0 -3
  12. package/dist/src/lib/CustomError.js +0 -10
  13. package/dist/src/lib/capitalizeFirstLetter.d.ts +0 -1
  14. package/dist/src/lib/capitalizeFirstLetter.js +0 -6
  15. package/dist/src/lib/getDuplicates.d.ts +0 -1
  16. package/dist/src/lib/getDuplicates.js +0 -9
  17. package/dist/src/lib/isNotNullOrUndefined.d.ts +0 -1
  18. package/dist/src/lib/isNotNullOrUndefined.js +0 -7
  19. package/dist/src/runtime/Cache.d.ts +0 -28
  20. package/dist/src/runtime/Cache.js +0 -142
  21. package/dist/src/runtime/IRuntime.d.ts +0 -209
  22. package/dist/src/runtime/IRuntime.js +0 -12
  23. package/dist/src/runtime/RuntimeMySQL.d.ts +0 -26
  24. package/dist/src/runtime/RuntimeMySQL.js +0 -132
  25. package/dist/src/runtime/RuntimeSQLite.d.ts +0 -42
  26. package/dist/src/runtime/RuntimeSQLite.js +0 -150
  27. package/dist/src/runtime/Stats.d.ts +0 -8
  28. package/dist/src/runtime/Stats.js +0 -31
  29. package/dist/src/runtime/lib/MySQL.d.ts +0 -13
  30. package/dist/src/runtime/lib/MySQL.js +0 -116
  31. package/dist/src/runtime/lib/SDKBadWhereError.d.ts +0 -4
  32. package/dist/src/runtime/lib/SDKBadWhereError.js +0 -10
  33. package/dist/src/runtime/lib/SDKNotFoundError.d.ts +0 -4
  34. package/dist/src/runtime/lib/SDKNotFoundError.js +0 -10
  35. package/dist/src/runtime/lib/addNullFallbacks.d.ts +0 -1
  36. package/dist/src/runtime/lib/addNullFallbacks.js +0 -32
  37. package/dist/src/runtime/lib/addNullFallbacks.test.d.ts +0 -1
  38. package/dist/src/runtime/lib/addNullFallbacks.test.js +0 -206
  39. package/dist/src/runtime/lib/cursor.d.ts +0 -2
  40. package/dist/src/runtime/lib/cursor.js +0 -10
  41. package/dist/src/runtime/lib/getDateTimeStringMySQL.d.ts +0 -1
  42. package/dist/src/runtime/lib/getDateTimeStringMySQL.js +0 -7
  43. package/dist/src/runtime/lib/getOrderBy.d.ts +0 -5
  44. package/dist/src/runtime/lib/getOrderBy.js +0 -52
  45. package/dist/src/runtime/lib/getSqlAst.d.ts +0 -2
  46. package/dist/src/runtime/lib/getSqlAst.js +0 -245
  47. package/dist/src/runtime/lib/getWhere.d.ts +0 -2
  48. package/dist/src/runtime/lib/getWhere.js +0 -20
  49. package/dist/src/runtime/lib/shared.d.ts +0 -13
  50. package/dist/src/runtime/lib/shared.js +0 -1118
  51. package/dist/src/runtime/lib/stringifyWhere.d.ts +0 -18
  52. package/dist/src/runtime/lib/stringifyWhere.js +0 -257
  53. package/dist/src/runtime/lib/stringifyWhere.test.d.ts +0 -1
  54. package/dist/src/runtime/lib/stringifyWhere.test.js +0 -245
  55. package/dist/src/runtime/lib/utility.d.ts +0 -5
  56. package/dist/src/runtime/lib/utility.js +0 -14
  57. package/dist/src/traverseFieldArgs.d.ts +0 -2
  58. package/dist/src/traverseFieldArgs.js +0 -17
  59. package/dist/src/traverseFieldArgs.test.d.ts +0 -1
  60. package/dist/src/traverseFieldArgs.test.js +0 -56
  61. package/dist/test/addWhereValidTrue.d.ts +0 -1
  62. package/dist/test/addWhereValidTrue.js +0 -39
  63. package/dist/test/globalSetup.d.ts +0 -13
  64. package/dist/test/globalSetup.js +0 -436
  65. package/dist/test/postgres/__generated__/sdk-ts/artifacts.d.ts +0 -8425
  66. package/dist/test/postgres/__generated__/sdk-ts/artifacts.js +0 -10469
  67. package/dist/test/postgres/__generated__/sdk-ts/index.js +0 -12162
  68. /package/dist/{src/runtime → runtime}/RuntimePostgreSQL.d.ts +0 -0
  69. /package/dist/{src/runtime → runtime}/RuntimePostgreSQL.js +0 -0
  70. /package/dist/{src/runtime → runtime}/lib/PostgreSQL.d.ts +0 -0
  71. /package/dist/{src/runtime → runtime}/lib/PostgreSQL.js +0 -0
@@ -30,24 +30,27 @@ 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"));
35
36
  const prettier = __importStar(require("prettier"));
36
37
  const changeCase = __importStar(require("change-case"));
37
38
  const fse = __importStar(require("fs-extra"));
38
39
  const _ = __importStar(require("lodash/fp"));
40
+ const memoize_1 = __importDefault(require("lodash/memoize"));
39
41
  const json_schema_to_typescript_1 = require("json-schema-to-typescript");
40
42
  const getDuplicates_1 = require("../lib/getDuplicates");
41
43
  const isNotNullOrUndefined_1 = require("../lib/isNotNullOrUndefined");
44
+ const pg_1 = require("pg");
42
45
  const MySQL_1 = require("../runtime/lib/MySQL");
43
46
  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";
47
+ const ctxStorage = new node_async_hooks_1.AsyncLocalStorage();
48
+ function getCtx() {
49
+ const c = ctxStorage.getStore();
50
+ if (!c)
51
+ throw new Error("generate() context missing");
52
+ return c;
53
+ }
51
54
  const json2TsOpts = {
52
55
  bannerComment: ""
53
56
  };
@@ -62,159 +65,176 @@ async function generate(input) {
62
65
  const specialCaseUuidColumn = input.specialCaseUuidColumn ?? true;
63
66
  const includeMappedFields = input.includeMappedFields ?? true;
64
67
  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]
105
- };
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"
119
- }
68
+ const ctx = {
69
+ runId: node_crypto_1.default.randomUUID(),
70
+ dialect: input.dialect,
71
+ query: undefined
120
72
  };
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);
73
+ return ctxStorage.run(ctx, async () => {
74
+ init(input);
75
+ let tables = await getTableNames();
76
+ if (tables.length === 0) {
77
+ throw new Error("No tables found");
135
78
  }
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);
79
+ if (input.tables != null) {
80
+ tables = tables.filter((x) => input.tables?.includes(x));
139
81
  }
140
- if (x.kind === "postOne") {
141
- fs.writeFileSync(path.join(typesDirPath, x.typeDataName + ".ts"), x.typeData);
82
+ if (input.excludeTables != null) {
83
+ tables = tables.filter((x) => !input.excludeTables?.includes(x));
142
84
  }
143
- if (x.kind === "patchOne") {
144
- fs.writeFileSync(path.join(typesDirPath, x.typeDataName + ".ts"), x.typeData);
85
+ const data = await Promise.all(tables.flatMap((x) => [
86
+ getGetOneData(x, includeMappedFields),
87
+ getGetListData(x),
88
+ getGetListPaginatedData(x),
89
+ getPostOneData(x, specialCaseUuidColumn, includeMappedFields),
90
+ getPatchOneData(x, specialCaseUuidColumn, includeMappedFields),
91
+ getPatchListData(x),
92
+ getDeleteOneData(x),
93
+ getDeleteListData(x)
94
+ ]));
95
+ const artifacts = await getArtifacts(tables, includeMappedFields, specialCaseUuidColumn);
96
+ const artifactsSource = getArtifactsSource(artifacts);
97
+ const sdkSource = await getSDKSource(data, specialCaseUuidColumn, supplementClientOpts, artifacts, input.outputSqliteSchema);
98
+ const sdkFilename = "index.ts";
99
+ const sourceIRuntimeFilePath = fs.existsSync(path.join(__dirname, "../runtime", "IRuntime.ts"))
100
+ ? path.join(__dirname, "../runtime", "IRuntime.ts")
101
+ : path.join(__dirname, "../runtime", "IRuntime.js");
102
+ const IRuntimeFilename = path.basename(sourceIRuntimeFilePath);
103
+ const artifactsFilename = "artifacts.ts";
104
+ const tsConfigJSON = {
105
+ compilerOptions: {
106
+ module: "commonjs",
107
+ moduleResolution: "node",
108
+ target: "es2020",
109
+ declaration: true,
110
+ outDir: "./sdk-ts"
111
+ },
112
+ include: [sdkFilename, artifactsFilename, IRuntimeFilename]
113
+ };
114
+ const packageJSON = {
115
+ name: "temp",
116
+ version: "1.0.0",
117
+ // Deps need to be included so that they're inlined by ncc
118
+ dependencies: require("../../package.json").dependencies,
119
+ devDependencies: {
120
+ "@types/node": require("../../package.json").devDependencies["@types/node"],
121
+ typescript: require("../../package.json").devDependencies.typescript
122
+ },
123
+ // Not `resolutions` because npm used for install
124
+ overrides: {
125
+ // Fix for: `Cannot find type definition file for 'glob'`
126
+ glob: ">9.0.0"
127
+ }
128
+ };
129
+ const tmpDirPath = path.join(os.tmpdir(),
130
+ // _ because - in filename is not supported by mysql2sqlite
131
+ `dsg_${node_crypto_1.default.randomUUID()}`.replace(/-/g, "_"));
132
+ fse.mkdirpSync(tmpDirPath);
133
+ fs.writeFileSync(path.join(tmpDirPath, sdkFilename), sdkSource);
134
+ fs.writeFileSync(path.join(tmpDirPath, artifactsFilename), artifactsSource);
135
+ fse.copyFileSync(sourceIRuntimeFilePath, path.join(tmpDirPath, IRuntimeFilename));
136
+ const typesDirPath = path.join(tmpDirPath, "types");
137
+ fse.mkdirpSync(typesDirPath);
138
+ fs.writeFileSync(path.join(typesDirPath, "_shared.ts"), getTypeShared());
139
+ for (let x of data) {
140
+ if (x.kind === "getOne") {
141
+ fs.writeFileSync(path.join(typesDirPath, x.typeFieldsName + ".ts"), x.typeFields);
142
+ fs.writeFileSync(path.join(typesDirPath, x.typeReturnBaseName + ".ts"), x.typeReturnBase);
143
+ }
144
+ if (x.kind === "getList") {
145
+ fs.writeFileSync(path.join(typesDirPath, x.typeWhereName + ".ts"), x.typeWhere);
146
+ fs.writeFileSync(path.join(typesDirPath, x.typeOrderByName + ".ts"), x.typeOrderBy);
147
+ }
148
+ if (x.kind === "postOne") {
149
+ fs.writeFileSync(path.join(typesDirPath, x.typeDataName + ".ts"), x.typeData);
150
+ }
151
+ if (x.kind === "patchOne") {
152
+ fs.writeFileSync(path.join(typesDirPath, x.typeDataName + ".ts"), x.typeData);
153
+ }
145
154
  }
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) {
155
+ fs.writeFileSync(path.join(typesDirPath, "index.ts"), getTypeTypesIndex(data));
156
+ fs.writeFileSync(path.join(tmpDirPath, "package.json"), JSON.stringify(packageJSON, null, 2));
157
+ fs.writeFileSync(path.join(tmpDirPath, "tsconfig.json"), JSON.stringify(tsConfigJSON, null, 2));
158
+ fse.copySync(__dirname, path.join(tmpDirPath, "src"));
159
+ const tmpBuildOutputPath = path.join(tmpDirPath, "sdk-ts");
160
+ const outdir = path.resolve(input.outdir);
161
+ const sdkOutputPath = path.join(outdir, "sdk-ts");
162
+ const nccVersion = "^0.33.0";
163
+ child_process.execSync("npm i", { cwd: tmpDirPath, stdio: "inherit" });
164
+ child_process.execSync(`npm_config_yes=true npx -p @vercel/ncc@${nccVersion} ncc build ./${sdkFilename} -o ${tmpBuildOutputPath} -e ./artifacts`, { cwd: tmpDirPath, stdio: "inherit" });
165
+ // TODO: workaround for artifacts.js not being output by ncc
166
+ fs.writeFileSync(path.join(tmpBuildOutputPath, "artifacts.js"), artifactsSource
167
+ .replace("export const artifacts: IArtifacts = ", "module.exports.artifacts = ")
168
+ .split("\n")
169
+ // Remove import
170
+ .slice(2)
171
+ .join("\n"));
172
+ // TODO: workaround for IRuntime.d.ts not being included
173
+ // copyFileSync hangs for some reason, so use writeFileSync + readFileSync instead
174
+ fs.writeFileSync(path.join(tmpBuildOutputPath, "IRuntime.d.ts"), fs.existsSync(path.join(__dirname, "../runtime", "IRuntime.d.ts"))
175
+ ? fs.readFileSync(path.join(__dirname, "../runtime", "IRuntime.d.ts"), "utf-8")
176
+ : fs.readFileSync(sourceIRuntimeFilePath, "utf-8"));
177
+ if (getCtx().dialect === "mysql" && input.outputSqliteSchema) {
178
+ // Since mysql2sqlite outputs a malformed string if a column
179
+ // has the name `enum`, temporarily change the name to something else,
180
+ // then change it back.
181
+ const enumMarker = "`" + node_crypto_1.default.randomUUID() + "`";
182
+ const schemaMySql = Object.values(artifacts)
183
+ .reduce((acc, x) => {
184
+ let d = x.dump?.schema;
185
+ if (!d) {
186
+ return acc;
187
+ }
188
+ d = d.replace(/`enum`/g, enumMarker);
189
+ d += ";";
190
+ acc.push(d);
178
191
  return acc;
179
- }
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);
192
+ }, [])
193
+ .join("\n\n");
194
+ const mysql2SqliteSrc = getMysql2sqliteSrc();
195
+ const mysql2SqlitePath = path.join(tmpDirPath, "mysql2sqlite");
196
+ fs.writeFileSync(mysql2SqlitePath, mysql2SqliteSrc);
197
+ fs.chmodSync(mysql2SqlitePath, 0o755);
198
+ const tmpMySqlSchemaFilename = "tmp.sql";
199
+ const tmpMySqlSchemaPath = path.join(tmpDirPath, tmpMySqlSchemaFilename);
200
+ fs.writeFileSync(tmpMySqlSchemaPath, schemaMySql);
201
+ let schemaSqlite = child_process
202
+ .execFileSync(mysql2SqlitePath, [tmpMySqlSchemaFilename], { cwd: tmpDirPath })
203
+ .toString();
204
+ schemaSqlite = schemaSqlite.replace(new RegExp(enumMarker, "g"), "`enum`");
205
+ const src = prettier.format(`module.exports = { schema: \`${schemaSqlite.replace(/`/g, "\\`")}\` }`, { parser: "babel" });
206
+ fs.writeFileSync(path.join(tmpBuildOutputPath, "artifacts.sqlite.js"), src);
207
+ }
208
+ if (!fs.existsSync(outdir)) {
209
+ fse.mkdirpSync(outdir);
210
+ }
211
+ fse.emptyDirSync(sdkOutputPath);
212
+ fse.copySync(tmpBuildOutputPath, sdkOutputPath);
213
+ fse.removeSync(tmpDirPath);
214
+ });
206
215
  }
207
216
  function init(input) {
217
+ const ctx = getCtx();
208
218
  const { database, user, password, host, port, server } = input;
209
- if (dialect === "mysql") {
210
- let mysql = new MySQL_1.MySQL({
219
+ if (ctx.dialect === "mysql") {
220
+ const mysql = new MySQL_1.MySQL({
211
221
  user,
212
222
  password,
213
223
  host,
214
224
  port,
215
225
  database
216
226
  });
217
- query = mysql.query.bind(mysql);
227
+ ctx.query = mysql.query.bind(mysql);
228
+ }
229
+ if (ctx.dialect === "postgresql") {
230
+ const pool = new pg_1.Pool({
231
+ host: host ?? "localhost",
232
+ port: port ?? 5432,
233
+ user,
234
+ password,
235
+ database
236
+ });
237
+ ctx.query = (q, values) => pool.query(q, values ?? []).then((r) => r.rows);
218
238
  }
219
239
  }
220
240
  // It's a bit awkward to put __whereNeedsProcessing, __prepareWhere on the class,
@@ -1068,7 +1088,7 @@ async function getMappedFields(table) {
1068
1088
  name: "uuid",
1069
1089
  // Replace `Id` with `Uuid`
1070
1090
  as: x.foreignKey.slice(0, -2) + "Uuid",
1071
- type: getBaseJSONType(uuidColumn.Type)
1091
+ type: getBaseJSONType(uuidColumn.Type, getCtx().dialect)
1072
1092
  });
1073
1093
  }
1074
1094
  return out;
@@ -1489,7 +1509,7 @@ async function getArtifacts(tables, includeMappedFields, specialCaseUuidColumn)
1489
1509
  }
1490
1510
  return {
1491
1511
  kind: "scalar",
1492
- type: getBaseJSONType(t.Type),
1512
+ type: getBaseJSONType(t.Type, getCtx().dialect),
1493
1513
  name: t.Field,
1494
1514
  nullable,
1495
1515
  hasDefaultValue: !!t.Default
@@ -1534,7 +1554,7 @@ async function getArtifacts(tables, includeMappedFields, specialCaseUuidColumn)
1534
1554
  }, {});
1535
1555
  return artifacts;
1536
1556
  }
1537
- const getRelationInfo = _.memoize(async function getRelationInfo(table) {
1557
+ const getRelationInfo = (0, memoize_1.default)(async function getRelationInfo(table) {
1538
1558
  const relationsManyToOne = await getRelationsManyToOne(table);
1539
1559
  const relationsOneToMany = await getRelationsOneToMany(table);
1540
1560
  let out = [];
@@ -1604,7 +1624,7 @@ const getRelationInfo = _.memoize(async function getRelationInfo(table) {
1604
1624
  out = out.concat(relationsManyToMany);
1605
1625
  out = _.sortBy((x) => x.table, out);
1606
1626
  return out;
1607
- });
1627
+ }, (table) => getCtx().runId + ":" + table);
1608
1628
  function getRelationManyToOneFieldName(x) {
1609
1629
  return changeCase.camelCase(x.foreignKey.replace(new RegExp(x.referencedKey + "$", "i"), ""));
1610
1630
  }
@@ -1627,11 +1647,12 @@ async function getJunctionTables() {
1627
1647
  }
1628
1648
  // `from` relations
1629
1649
  // 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 = `
1650
+ const getRelationsManyToOne = (0, memoize_1.default)(async function getRelationsManyToOne(table) {
1651
+ const { dialect, query } = getCtx();
1652
+ const tableMeta = await getTableMeta(table);
1653
+ let rs;
1654
+ if (dialect === "mysql") {
1655
+ const sql = `
1635
1656
  SELECT
1636
1657
  TABLE_SCHEMA as db,
1637
1658
  TABLE_NAME as t1,
@@ -1646,8 +1667,18 @@ const getRelationsManyToOne = _.memoize(async function getRelationsManyToOne(tab
1646
1667
  AND REFERENCED_TABLE_NAME IS NOT NULL
1647
1668
  AND (TABLE_NAME = ?);
1648
1669
  `;
1649
- const tableMeta = await getTableMeta(table);
1650
- const rs = await query(sql, [table]);
1670
+ rs = await query(sql, [table]);
1671
+ }
1672
+ else if (dialect === "postgresql") {
1673
+ rs = await query(`SELECT kcu.column_name AS "t1Field", ccu.table_name AS t2, ccu.column_name AS "t2Field"
1674
+ FROM information_schema.key_column_usage kcu
1675
+ JOIN information_schema.referential_constraints rc ON kcu.constraint_name = rc.constraint_name AND kcu.table_schema = rc.constraint_schema
1676
+ JOIN information_schema.constraint_column_usage ccu ON rc.unique_constraint_name = ccu.constraint_name AND rc.unique_constraint_schema = ccu.table_schema
1677
+ WHERE kcu.table_schema = 'public' AND kcu.table_name = $1`, [table]);
1678
+ }
1679
+ else {
1680
+ throw new Error("Unsupported dialect: " + dialect);
1681
+ }
1651
1682
  const xs = await Promise.all(_.uniqWith(_.isEqual, rs.map(async (v) => {
1652
1683
  return {
1653
1684
  table: table,
@@ -1658,10 +1689,13 @@ const getRelationsManyToOne = _.memoize(async function getRelationsManyToOne(tab
1658
1689
  };
1659
1690
  })));
1660
1691
  return _.sortBy((x) => x.referencedTable, xs);
1661
- });
1692
+ }, (table) => getCtx().runId + ":" + table);
1662
1693
  // `to` relations
1663
- const getRelationsOneToMany = _.memoize(async function getRelationsOneToMany(table) {
1664
- const sql = `
1694
+ const getRelationsOneToMany = (0, memoize_1.default)(async function getRelationsOneToMany(table) {
1695
+ const { dialect, query } = getCtx();
1696
+ let rs;
1697
+ if (dialect === "mysql") {
1698
+ const sql = `
1665
1699
  SELECT
1666
1700
  TABLE_SCHEMA as db,
1667
1701
  TABLE_NAME as t1,
@@ -1676,19 +1710,29 @@ const getRelationsOneToMany = _.memoize(async function getRelationsOneToMany(tab
1676
1710
  AND REFERENCED_TABLE_NAME IS NOT NULL
1677
1711
  AND (REFERENCED_TABLE_NAME = ?);
1678
1712
  `;
1679
- const rs = await query(sql, [table]);
1713
+ rs = await query(sql, [table]);
1714
+ }
1715
+ else if (dialect === "postgresql") {
1716
+ rs = await query(`SELECT kcu.table_name AS t1, kcu.column_name AS "t1Field", ccu.column_name AS "t2Field"
1717
+ FROM information_schema.key_column_usage kcu
1718
+ JOIN information_schema.referential_constraints rc ON kcu.constraint_name = rc.constraint_name AND kcu.table_schema = rc.constraint_schema
1719
+ JOIN information_schema.constraint_column_usage ccu ON rc.unique_constraint_name = ccu.constraint_name AND rc.unique_constraint_schema = ccu.table_schema
1720
+ WHERE kcu.table_schema = 'public' AND ccu.table_name = $1`, [table]);
1721
+ }
1722
+ else {
1723
+ throw new Error("Unsupported dialect: " + dialect);
1724
+ }
1680
1725
  const xs = await Promise.all(_.uniqWith(_.isEqual, rs.map(async (v) => {
1681
1726
  return {
1682
1727
  table: table,
1683
1728
  foreignKey: v.t2Field,
1684
1729
  referencedTable: v.t1,
1685
1730
  referencedKey: v.t1Field,
1686
- // TODO? I think this is right, since it's one-to-many, so a list
1687
1731
  nullable: false
1688
1732
  };
1689
1733
  })));
1690
1734
  return _.sortBy((x) => x.referencedKey, _.sortBy((x) => x.referencedTable, xs));
1691
- });
1735
+ }, (table) => getCtx().runId + ":" + table);
1692
1736
  async function getPrimaryColumn(table) {
1693
1737
  const tableMeta = await getTableMeta(table);
1694
1738
  const columns = tableMeta.filter((x) => x.Key === "PRI");
@@ -1698,7 +1742,7 @@ async function getPrimaryColumn(table) {
1698
1742
  const column = columns[0];
1699
1743
  return {
1700
1744
  name: column.Field,
1701
- type: getBaseJSONType(column.Type),
1745
+ type: getBaseJSONType(column.Type, getCtx().dialect),
1702
1746
  nullable: column.Null === "YES"
1703
1747
  };
1704
1748
  }
@@ -1710,7 +1754,7 @@ async function getUniqueColumns(table, specialCaseUuidColumn) {
1710
1754
  (specialCaseUuidColumn && x.Field === "uuid"))
1711
1755
  .map((x) => ({
1712
1756
  name: x.Field,
1713
- type: getBaseJSONType(x.Type),
1757
+ type: getBaseJSONType(x.Type, getCtx().dialect),
1714
1758
  nullable: x.Null === "YES"
1715
1759
  }));
1716
1760
  }
@@ -1726,23 +1770,116 @@ async function getUuidColumn(table) {
1726
1770
  nullable: column.Null === "YES"
1727
1771
  };
1728
1772
  }
1729
- const getTableMeta = _.memoize(async function getTableMeta(table) {
1773
+ const getPgEnumDefinition = (0, memoize_1.default)(async function getPgEnumDefinition(udtName) {
1774
+ const { dialect, query } = getCtx();
1775
+ if (dialect !== "postgresql")
1776
+ return null;
1777
+ const rows = await query(`SELECT e.enumlabel FROM pg_enum e
1778
+ JOIN pg_type t ON e.enumtypid = t.oid
1779
+ JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid
1780
+ WHERE t.typname = $1 AND n.nspname = 'public'
1781
+ ORDER BY e.enumsortorder`, [udtName]);
1782
+ if (rows.length === 0)
1783
+ return null;
1784
+ const labels = rows.map((r) => String(r.enumlabel).replace(/'/g, "''"));
1785
+ return "enum('" + labels.join("', '") + "')";
1786
+ }, (udtName) => getCtx().runId + ":" + udtName);
1787
+ const getTableMeta = (0, memoize_1.default)(async function getTableMeta(table) {
1788
+ const { dialect, query } = getCtx();
1730
1789
  if (dialect === "mysql") {
1731
1790
  return query("DESCRIBE ??", [table]).then((xs) => _.sortBy((x) => x.Field, xs));
1732
1791
  }
1792
+ if (dialect === "postgresql") {
1793
+ const columns = await query(`SELECT column_name AS "Field", data_type, udt_name, character_maximum_length AS char_max, is_nullable, column_default AS "Default"
1794
+ FROM information_schema.columns
1795
+ WHERE table_schema = 'public' AND table_name = $1
1796
+ ORDER BY ordinal_position`, [table]);
1797
+ const keyInfo = await query(`SELECT a.attname AS col, 'PRI' AS key_type
1798
+ FROM pg_index i
1799
+ JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey) AND NOT a.attisdropped AND a.attnum > 0
1800
+ JOIN pg_class c ON c.oid = i.indrelid
1801
+ JOIN pg_namespace n ON n.oid = c.relnamespace
1802
+ WHERE n.nspname = 'public' AND c.relname = $1 AND i.indisprimary
1803
+ UNION ALL
1804
+ SELECT kcu.column_name AS col, 'UNI' AS key_type
1805
+ FROM information_schema.table_constraints tc
1806
+ JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema
1807
+ WHERE tc.table_schema = 'public' AND tc.table_name = $1 AND tc.constraint_type = 'UNIQUE'
1808
+ UNION ALL
1809
+ SELECT kcu.column_name AS col, 'MUL' AS key_type
1810
+ FROM information_schema.table_constraints tc
1811
+ JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema
1812
+ WHERE tc.table_schema = 'public' AND tc.table_name = $1 AND tc.constraint_type = 'FOREIGN KEY'`, [table]);
1813
+ const keyMap = new Map();
1814
+ for (const k of keyInfo) {
1815
+ if (!keyMap.has(k.col) || k.key_type === "PRI")
1816
+ keyMap.set(k.col, k.key_type);
1817
+ }
1818
+ const udtNames = [
1819
+ ...new Set(columns
1820
+ .filter((c) => c.data_type === "USER-DEFINED" && c.udt_name != null)
1821
+ .map((c) => c.udt_name))
1822
+ ];
1823
+ const enumDefs = await Promise.all(udtNames.map((udt) => getPgEnumDefinition(udt)));
1824
+ const enumMap = new Map(udtNames.map((udt, i) => [udt, enumDefs[i] ?? "varchar(255)"]));
1825
+ return columns.map((c) => {
1826
+ let type;
1827
+ if (c.data_type === "USER-DEFINED" && c.udt_name != null) {
1828
+ type = enumMap.get(c.udt_name) ?? "character varying(255)";
1829
+ }
1830
+ else {
1831
+ type = c.data_type;
1832
+ if ((c.data_type === "character varying" || c.data_type === "character") &&
1833
+ c.char_max != null) {
1834
+ type += "(" + c.char_max + ")";
1835
+ }
1836
+ }
1837
+ return {
1838
+ Field: c.Field,
1839
+ Type: type,
1840
+ Null: c.is_nullable === "YES" ? "YES" : "NO",
1841
+ Key: keyMap.get(c.Field) ?? "",
1842
+ Default: c.Default ?? ""
1843
+ };
1844
+ });
1845
+ }
1733
1846
  throw new Error("Unsupported dialect: " + dialect);
1734
- });
1735
- function getShowCreateTable(table) {
1847
+ }, (table) => getCtx().runId + ":" + table);
1848
+ async function getShowCreateTable(table) {
1849
+ const { dialect, query } = getCtx();
1736
1850
  if (dialect === "mysql") {
1737
1851
  return query("SHOW CREATE TABLE ??", [table]).then((xs) => xs[0]["Create Table"]
1738
1852
  // https://github.com/bradzacher/mysqldump/blob/66839a57e572a07c046b0ba98753f30a7026cbd8/src/getSchemaDump.ts#L65
1739
1853
  .replace(/AUTO_INCREMENT\s*=\s*\d+ /g, ""));
1740
1854
  }
1855
+ if (dialect === "postgresql") {
1856
+ const [tableMeta, relations] = await Promise.all([
1857
+ getTableMeta(table),
1858
+ getRelationsManyToOne(table)
1859
+ ]);
1860
+ const refByFk = new Map(relations.map((r) => [r.foreignKey, r]));
1861
+ const columnDefs = tableMeta.map((c) => {
1862
+ let def = `"${c.Field.replace(/"/g, '""')}" ${c.Type} ${c.Null === "YES" ? "NULL" : "NOT NULL"}`;
1863
+ if (c.Default != null && c.Default !== "") {
1864
+ def += ` DEFAULT ${c.Default}`;
1865
+ }
1866
+ if (c.Key === "PRI")
1867
+ def += " PRIMARY KEY";
1868
+ if (c.Key === "UNI")
1869
+ def += " UNIQUE";
1870
+ const ref = refByFk.get(c.Field);
1871
+ if (ref != null) {
1872
+ def += ` REFERENCES "${ref.referencedTable.replace(/"/g, '""')}" ("${ref.referencedKey.replace(/"/g, '""')}")`;
1873
+ }
1874
+ return def;
1875
+ });
1876
+ return `CREATE TABLE "${table.replace(/"/g, '""')}" (\n ${columnDefs.join(",\n ")}\n)`;
1877
+ }
1741
1878
  return Promise.resolve(null);
1742
1879
  }
1743
1880
  function getJSONSchemaObjProperties(tableMeta) {
1744
1881
  return tableMeta.reduce((acc, m) => {
1745
- const baseType = getBaseJSONType(m.Type);
1882
+ const baseType = getBaseJSONType(m.Type, getCtx().dialect);
1746
1883
  const format = getPropertyFormat(m.Type);
1747
1884
  const nullable = m.Null === "YES";
1748
1885
  const isEnum = m.Type.startsWith("enum");
@@ -1778,7 +1915,29 @@ function getJSONTypes(baseType, nullable) {
1778
1915
  return baseType;
1779
1916
  }
1780
1917
  // https://github.com/mysqljs/mysql#type-casting
1781
- function getBaseJSONType(sqlType) {
1918
+ function getBaseJSONType(sqlType, dialect) {
1919
+ if (dialect === "postgresql") {
1920
+ if (sqlType === "boolean" || sqlType === "bool")
1921
+ return "boolean";
1922
+ if (["smallint", "int2", "integer", "int4", "bigint", "int8"].includes(sqlType)) {
1923
+ return "integer";
1924
+ }
1925
+ if (["real", "float4", "double precision", "float8"].includes(sqlType) ||
1926
+ sqlType.startsWith("numeric") ||
1927
+ sqlType.startsWith("decimal")) {
1928
+ return "number";
1929
+ }
1930
+ if (["text", "uuid", "json", "jsonb"].includes(sqlType) ||
1931
+ sqlType === "date" ||
1932
+ sqlType === "time" ||
1933
+ sqlType.startsWith("timestamp") ||
1934
+ sqlType.startsWith("character varying") ||
1935
+ sqlType.startsWith("character") ||
1936
+ sqlType.startsWith("enum")) {
1937
+ return "string";
1938
+ }
1939
+ throw new Error("Unable to map to JSON type: " + sqlType);
1940
+ }
1782
1941
  if (
1783
1942
  // TODO?
1784
1943
  sqlType === "tinyint(1)" ||
@@ -1863,7 +2022,10 @@ function getPropertyEnum(sqlType) {
1863
2022
  return c;
1864
2023
  }
1865
2024
  function getPropertyFormat(sqlType) {
1866
- if (sqlType === "datetime" || sqlType === "datetime2" || sqlType === "timestamp") {
2025
+ if (sqlType === "datetime" ||
2026
+ sqlType === "datetime2" ||
2027
+ sqlType === "timestamp" ||
2028
+ sqlType.startsWith("timestamp")) {
1867
2029
  // TODO: not sure this is correct for `timestamp`
1868
2030
  return "date-time";
1869
2031
  }
@@ -1882,9 +2044,15 @@ function getPropertyFormat(sqlType) {
1882
2044
  return undefined;
1883
2045
  }
1884
2046
  async function getTableNames() {
2047
+ const { dialect, query } = getCtx();
1885
2048
  if (dialect === "mysql") {
1886
2049
  return query("SHOW TABLES").then((xs) => xs.flatMap((x) => Object.values(x)).sort());
1887
2050
  }
2051
+ if (dialect === "postgresql") {
2052
+ return query(`SELECT table_name FROM information_schema.tables
2053
+ WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
2054
+ ORDER BY table_name`).then((rows) => rows.map((r) => r.table_name));
2055
+ }
1888
2056
  throw new Error("Unsupported dialect: " + dialect);
1889
2057
  }
1890
2058
  function getMysql2sqliteSrc() {