@event-driven-io/dumbo 0.13.0-beta.40 → 0.13.0-beta.42

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 (61) hide show
  1. package/dist/cloudflare.cjs +4 -3
  2. package/dist/cloudflare.d.cts +3 -3
  3. package/dist/cloudflare.d.ts +3 -3
  4. package/dist/cloudflare.js +4 -4
  5. package/dist/{core-D-smW7cm.cjs → core-BPSzA-lq.cjs} +2091 -2075
  6. package/dist/core-BPSzA-lq.cjs.map +1 -0
  7. package/dist/{core-DXCRR5A9.cjs → core-BuSVyamf.cjs} +33 -3
  8. package/dist/core-BuSVyamf.cjs.map +1 -0
  9. package/dist/{core-BchvTX3G.js → core-C3xoqqDs.js} +28 -4
  10. package/dist/core-C3xoqqDs.js.map +1 -0
  11. package/dist/{core-DlV8ua1P.js → core-CHw8vO17.js} +9 -4
  12. package/dist/core-CHw8vO17.js.map +1 -0
  13. package/dist/{core-CWZb0zOY.cjs → core-CUGYxOEQ.cjs} +14 -3
  14. package/dist/core-CUGYxOEQ.cjs.map +1 -0
  15. package/dist/{core-BG__WVn6.js → core-IV7or0Mj.js} +2086 -2070
  16. package/dist/core-IV7or0Mj.js.map +1 -0
  17. package/dist/{index-N2ly5shv.d.cts → index-BJC_v03L.d.ts} +9 -3
  18. package/dist/{index-BDSQvDH2.d.cts → index-CfH0u2y_.d.cts} +260 -163
  19. package/dist/{index-BgFHGsuP.d.ts → index-DP9b7v4e.d.cts} +9 -3
  20. package/dist/{index-zkszkJqP.d.ts → index-QWEAqtHF.d.ts} +260 -163
  21. package/dist/{index-BdWFmDhJ.d.cts → index-qxECrBHo.d.ts} +10 -3
  22. package/dist/{index-cxtbLiju.d.ts → index-tS9lpLPz.d.cts} +10 -3
  23. package/dist/index.cjs +24 -12
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +2 -3
  26. package/dist/index.d.ts +2 -3
  27. package/dist/index.js +24 -13
  28. package/dist/index.js.map +1 -1
  29. package/dist/pg.cjs +4 -3
  30. package/dist/pg.d.cts +3 -3
  31. package/dist/pg.d.ts +3 -3
  32. package/dist/pg.js +4 -4
  33. package/dist/{postgreSQLMetadata-WGXySO7a.cjs → postgreSQLMetadata-CCsCJ-eH.cjs} +2 -2
  34. package/dist/{postgreSQLMetadata-WGXySO7a.cjs.map → postgreSQLMetadata-CCsCJ-eH.cjs.map} +1 -1
  35. package/dist/{postgreSQLMetadata-BTz6vdwE.js → postgreSQLMetadata-bCBDGz1f.js} +2 -2
  36. package/dist/{postgreSQLMetadata-BTz6vdwE.js.map → postgreSQLMetadata-bCBDGz1f.js.map} +1 -1
  37. package/dist/postgresql.cjs +3 -2
  38. package/dist/postgresql.d.cts +3 -3
  39. package/dist/postgresql.d.ts +3 -3
  40. package/dist/postgresql.js +3 -3
  41. package/dist/sqlite.cjs +3 -2
  42. package/dist/sqlite.d.cts +3 -3
  43. package/dist/sqlite.d.ts +3 -3
  44. package/dist/sqlite.js +3 -3
  45. package/dist/sqlite3.cjs +4 -3
  46. package/dist/sqlite3.d.cts +3 -3
  47. package/dist/sqlite3.d.ts +3 -3
  48. package/dist/sqlite3.js +4 -4
  49. package/dist/{sqliteMetadata-PJTRPIZ1.cjs → sqliteMetadata-Cc7Z03lm.cjs} +2 -2
  50. package/dist/{sqliteMetadata-PJTRPIZ1.cjs.map → sqliteMetadata-Cc7Z03lm.cjs.map} +1 -1
  51. package/dist/{sqliteMetadata-7qBjsMBN.js → sqliteMetadata-CvvEc1-v.js} +2 -2
  52. package/dist/{sqliteMetadata-7qBjsMBN.js.map → sqliteMetadata-CvvEc1-v.js.map} +1 -1
  53. package/package.json +1 -1
  54. package/dist/core-BG__WVn6.js.map +0 -1
  55. package/dist/core-BchvTX3G.js.map +0 -1
  56. package/dist/core-CWZb0zOY.cjs.map +0 -1
  57. package/dist/core-D-smW7cm.cjs.map +0 -1
  58. package/dist/core-DXCRR5A9.cjs.map +0 -1
  59. package/dist/core-DlV8ua1P.js.map +0 -1
  60. package/dist/index-DaP2fTEX.d.ts +0 -67
  61. package/dist/index-S_wp0Eaf.d.cts +0 -67
@@ -1,2262 +1,2278 @@
1
- import ansis from "ansis";
2
1
  import { v7 } from "uuid";
2
+ import ansis from "ansis";
3
3
 
4
- //#region src/core/schema/schemaComponent.ts
5
- const schemaComponent = (key, options) => {
6
- const componentsMap = new Map(options.components?.map((comp) => [comp.schemaComponentKey, comp]));
7
- const migrations = [...options.migrations ?? []];
8
- return {
9
- schemaComponentKey: key,
10
- components: componentsMap,
11
- get migrations() {
12
- return [...migrations, ...Array.from(componentsMap.values()).flatMap((c) => c.migrations)];
13
- },
14
- addComponent: (component) => {
15
- componentsMap.set(component.schemaComponentKey, component);
16
- migrations.push(...component.migrations);
17
- return component;
18
- },
19
- addMigration: (migration) => {
20
- migrations.push(migration);
21
- }
22
- };
4
+ //#region src/core/errors/index.ts
5
+ const isNumber = (val) => typeof val === "number" && val === val;
6
+ const isString = (val) => typeof val === "string";
7
+ var DumboError = class DumboError extends Error {
8
+ static ErrorCode = 500;
9
+ static ErrorType = "DumboError";
10
+ errorCode;
11
+ errorType;
12
+ innerError;
13
+ constructor(options) {
14
+ const errorCode = options && typeof options === "object" && "errorCode" in options ? options.errorCode : isNumber(options) ? options : DumboError.ErrorCode;
15
+ const errorType = options && typeof options === "object" && "errorType" in options ? options.errorType ?? DumboError.ErrorType : DumboError.ErrorType;
16
+ const message = options && typeof options === "object" && "message" in options ? options.message : isString(options) ? options : `Error with status code '${errorCode}' ocurred during DumboError processing`;
17
+ const innerError = options && typeof options === "object" && "innerError" in options ? options.innerError : void 0;
18
+ super(message, { cause: innerError });
19
+ this.errorCode = errorCode;
20
+ this.errorType = errorType;
21
+ this.innerError = innerError;
22
+ Object.setPrototypeOf(this, DumboError.prototype);
23
+ }
24
+ static isInstanceOf(error, options) {
25
+ if (typeof error !== "object" || error === null || !("errorCode" in error) || !isNumber(error.errorCode) || !("errorType" in error) || !isString(error.errorType)) return false;
26
+ if (!options) return true;
27
+ if (options.errorCode !== void 0 && error.errorCode !== options.errorCode) return false;
28
+ if (options.errorType !== void 0 && error.errorType !== options.errorType) return false;
29
+ return true;
30
+ }
23
31
  };
24
- const isSchemaComponentOfType = (component, prefix) => component.schemaComponentKey.startsWith(prefix);
25
- const filterSchemaComponentsOfType = (components, prefix) => mapSchemaComponentsOfType(components, prefix);
26
- const mapSchemaComponentsOfType = (components, prefix, keyMapper) => new Map(Array.from(components.entries()).filter(([urn]) => urn.startsWith(prefix)).map(([urn, component]) => [keyMapper ? keyMapper(component) : urn, component]));
27
- const findSchemaComponentsOfType = (root, prefix) => {
28
- const results = [];
29
- const traverse = (component) => {
30
- if (component.schemaComponentKey.startsWith(prefix)) results.push(component);
31
- for (const child of component.components.values()) traverse(child);
32
- };
33
- traverse(root);
34
- return results;
32
+ var ConcurrencyError = class ConcurrencyError extends DumboError {
33
+ static ErrorCode = 412;
34
+ static ErrorType = "ConcurrencyError";
35
+ constructor(message, innerError) {
36
+ super({
37
+ errorCode: ConcurrencyError.ErrorCode,
38
+ errorType: ConcurrencyError.ErrorType,
39
+ message: message ?? `Expected document state does not match current one!`,
40
+ innerError
41
+ });
42
+ Object.setPrototypeOf(this, ConcurrencyError.prototype);
43
+ }
35
44
  };
36
-
37
- //#endregion
38
- //#region src/core/schema/components/columnSchemaComponent.ts
39
- const ColumnURNType = "sc:dumbo:column";
40
- const ColumnURN = ({ name }) => `${ColumnURNType}:${name}`;
41
- const columnSchemaComponent = (params) => {
42
- const { columnName, type, notNull, unique, primaryKey, default: defaultValue, ...schemaOptions } = params;
43
- return {
44
- ...schemaComponent(ColumnURN({ name: columnName }), schemaOptions),
45
- columnName,
46
- notNull,
47
- unique,
48
- primaryKey,
49
- defaultValue,
50
- sqlTokenType: "SQL_COLUMN",
51
- name: columnName,
52
- type
53
- };
45
+ var TransientDatabaseError = class TransientDatabaseError extends DumboError {
46
+ static ErrorCode = 503;
47
+ static ErrorType = "TransientDatabaseError";
48
+ constructor(message, innerError) {
49
+ super({
50
+ errorCode: TransientDatabaseError.ErrorCode,
51
+ errorType: TransientDatabaseError.ErrorType,
52
+ message: message ?? `A transient error occurred during database operation. Retrying the operation might succeed.`,
53
+ innerError
54
+ });
55
+ Object.setPrototypeOf(this, TransientDatabaseError.prototype);
56
+ }
54
57
  };
55
-
56
- //#endregion
57
- //#region src/core/schema/components/indexSchemaComponent.ts
58
- const IndexURNType = "sc:dumbo:index";
59
- const IndexURN = ({ name }) => `${IndexURNType}:${name}`;
60
- const indexSchemaComponent = ({ indexName, columnNames, isUnique, ...migrationsOrComponents }) => {
61
- return {
62
- ...schemaComponent(IndexURN({ name: indexName }), {
63
- migrations: migrationsOrComponents.migrations ?? [],
64
- components: [...migrationsOrComponents.components ?? []]
65
- }),
66
- indexName,
67
- get columnNames() {
68
- return columnNames;
69
- },
70
- addColumn: (column) => columnNames.push(typeof column === "string" ? column : column.columnName),
71
- isUnique
72
- };
58
+ var ConnectionError = class ConnectionError extends TransientDatabaseError {
59
+ static ErrorCode = 503;
60
+ static ErrorType = "ConnectionError";
61
+ constructor(message, innerError) {
62
+ super(message ?? `A connection error occurred during database operation.`, innerError);
63
+ this.errorType = ConnectionError.ErrorType;
64
+ Object.setPrototypeOf(this, ConnectionError.prototype);
65
+ }
73
66
  };
74
-
75
- //#endregion
76
- //#region src/core/schema/components/tableSchemaComponent.ts
77
- const TableURNType = "sc:dumbo:table";
78
- const TableURN = ({ name }) => `${TableURNType}:${name}`;
79
- const tableSchemaComponent = ({ tableName, columns, primaryKey, relationships, ...migrationsOrComponents }) => {
80
- columns ??= {};
81
- relationships ??= {};
82
- const base = schemaComponent(TableURN({ name: tableName }), {
83
- migrations: migrationsOrComponents.migrations ?? [],
84
- components: [...migrationsOrComponents.components ?? [], ...Object.values(columns)]
85
- });
86
- return {
87
- ...base,
88
- tableName,
89
- primaryKey: primaryKey ?? [],
90
- relationships,
91
- get columns() {
92
- const columnsMap = mapSchemaComponentsOfType(base.components, ColumnURNType, (c) => c.columnName);
93
- return Object.assign(columnsMap, columns);
94
- },
95
- get indexes() {
96
- return mapSchemaComponentsOfType(base.components, IndexURNType, (c) => c.indexName);
97
- },
98
- addColumn: (column) => base.addComponent(column),
99
- addIndex: (index) => base.addComponent(index)
100
- };
67
+ var SerializationError = class SerializationError extends TransientDatabaseError {
68
+ static ErrorCode = 503;
69
+ static ErrorType = "SerializationError";
70
+ constructor(message, innerError) {
71
+ super(message ?? `A serialization failure occurred. The transaction can be retried.`, innerError);
72
+ this.errorType = SerializationError.ErrorType;
73
+ Object.setPrototypeOf(this, SerializationError.prototype);
74
+ }
101
75
  };
102
-
103
- //#endregion
104
- //#region src/core/schema/components/databaseSchemaSchemaComponent.ts
105
- const DatabaseSchemaURNType = "sc:dumbo:database_schema";
106
- const DatabaseSchemaURN = ({ name }) => `${DatabaseSchemaURNType}:${name}`;
107
- const databaseSchemaSchemaComponent = ({ schemaName, tables, ...migrationsOrComponents }) => {
108
- const base = schemaComponent(DatabaseSchemaURN({ name: schemaName }), {
109
- migrations: migrationsOrComponents.migrations ?? [],
110
- components: [...migrationsOrComponents.components ?? [], ...Object.values(tables ?? {})]
111
- });
112
- return {
113
- ...base,
114
- schemaName,
115
- get tables() {
116
- const tablesMap = mapSchemaComponentsOfType(base.components, TableURNType, (c) => c.tableName);
117
- return Object.assign(tablesMap, tables);
118
- },
119
- addTable: (table) => base.addComponent(typeof table === "string" ? tableSchemaComponent({ tableName: table }) : table)
120
- };
76
+ var DeadlockError = class DeadlockError extends TransientDatabaseError {
77
+ static ErrorCode = 503;
78
+ static ErrorType = "DeadlockError";
79
+ constructor(message, innerError) {
80
+ super(message ?? `A deadlock was detected. The transaction can be retried.`, innerError);
81
+ this.errorType = DeadlockError.ErrorType;
82
+ Object.setPrototypeOf(this, DeadlockError.prototype);
83
+ }
121
84
  };
122
-
123
- //#endregion
124
- //#region src/core/schema/components/databaseSchemaComponent.ts
125
- const DatabaseURNType = "sc:dumbo:database";
126
- const DatabaseURN = ({ name }) => `${DatabaseURNType}:${name}`;
127
- const databaseSchemaComponent = ({ databaseName, schemas, ...migrationsOrComponents }) => {
128
- schemas ??= {};
129
- const base = schemaComponent(DatabaseURN({ name: databaseName }), {
130
- migrations: migrationsOrComponents.migrations ?? [],
131
- components: [...migrationsOrComponents.components ?? [], ...Object.values(schemas)]
132
- });
133
- return {
134
- ...base,
135
- databaseName,
136
- get schemas() {
137
- const schemasMap = mapSchemaComponentsOfType(base.components, DatabaseSchemaURNType, (c) => c.schemaName);
138
- return Object.assign(schemasMap, schemas);
139
- },
140
- addSchema: (schema) => base.addComponent(typeof schema === "string" ? databaseSchemaSchemaComponent({ schemaName: schema }) : schema)
141
- };
85
+ var LockNotAvailableError = class LockNotAvailableError extends TransientDatabaseError {
86
+ static ErrorCode = 503;
87
+ static ErrorType = "LockNotAvailableError";
88
+ constructor(message, innerError) {
89
+ super(message ?? `The requested lock is not available.`, innerError);
90
+ this.errorType = LockNotAvailableError.ErrorType;
91
+ Object.setPrototypeOf(this, LockNotAvailableError.prototype);
92
+ }
142
93
  };
143
-
144
- //#endregion
145
- //#region src/core/schema/components/index.ts
146
- const schemaComponentURN = {
147
- database: DatabaseURN,
148
- schema: DatabaseSchemaURN,
149
- table: TableURN,
150
- column: ColumnURN,
151
- index: IndexURN,
152
- extractName: (urn) => {
153
- const parts = urn.split(":");
154
- return parts[parts.length - 1] || "";
94
+ var InsufficientResourcesError = class InsufficientResourcesError extends TransientDatabaseError {
95
+ static ErrorCode = 503;
96
+ static ErrorType = "InsufficientResourcesError";
97
+ constructor(message, innerError) {
98
+ super(message ?? `Insufficient resources to complete the database operation (e.g. disk full, out of memory, too many connections).`, innerError);
99
+ this.errorType = InsufficientResourcesError.ErrorType;
100
+ Object.setPrototypeOf(this, InsufficientResourcesError.prototype);
155
101
  }
156
102
  };
157
-
158
- //#endregion
159
- //#region src/core/drivers/databaseDriver.ts
160
- const canHandleDriverWithConnectionString = (driver, tryParseConnectionString) => (options) => {
161
- if ("driverType" in options) return options.driverType === driver;
162
- if ("connectionString" in options && typeof options.connectionString === "string") return tryParseConnectionString(options.connectionString) !== null;
163
- return false;
103
+ var SystemError = class SystemError extends TransientDatabaseError {
104
+ static ErrorCode = 503;
105
+ static ErrorType = "SystemError";
106
+ constructor(message, innerError) {
107
+ super(message ?? `A system-level error occurred (e.g. I/O error).`, innerError);
108
+ this.errorType = SystemError.ErrorType;
109
+ Object.setPrototypeOf(this, SystemError.prototype);
110
+ }
164
111
  };
165
- const DumboDatabaseDriverRegistry = () => {
166
- const drivers = /* @__PURE__ */ new Map();
167
- const register = (driverType, plugin) => {
168
- const entry = drivers.get(driverType);
169
- if (entry && (typeof entry !== "function" || typeof plugin === "function")) return;
170
- drivers.set(driverType, plugin);
171
- };
172
- const getDriver = (options) => options.driverType ? drivers.get(options.driverType) : [...drivers.values()].find((d) => typeof d !== "function" && d.canHandle(options));
173
- const tryResolve = async (options) => {
174
- const driver = getDriver(options);
175
- if (!driver) return null;
176
- if (typeof driver !== "function") return driver;
177
- const plugin = await driver();
178
- register(plugin.driverType, plugin);
179
- return plugin;
180
- };
181
- const tryGet = (options) => {
182
- const driver = getDriver(options);
183
- return driver && typeof driver !== "function" ? driver : null;
184
- };
185
- const has = (driverType) => drivers.has(driverType);
186
- return {
187
- register,
188
- tryResolve,
189
- tryGet,
190
- has,
191
- get databaseDriverTypes() {
192
- return Array.from(drivers.keys());
193
- }
194
- };
195
- };
196
- const dumboDatabaseDriverRegistry = globalThis.dumboDatabaseDriverRegistry = globalThis.dumboDatabaseDriverRegistry ?? DumboDatabaseDriverRegistry();
197
-
198
- //#endregion
199
- //#region src/core/drivers/index.ts
200
- /**
201
- * Accepts a `databaseType` (e.g. PostgreSQL, SQLite) and a `driverName`
202
- * (the library name, e.g. pg, sqlite3) and combines them to a singular
203
- * `databaseDriverType` which can be used in database handling.
204
- */
205
- function toDatabaseDriverType(databaseType, driverName) {
206
- return `${databaseType}:${driverName}`;
207
- }
208
- /**
209
- * Accepts a fully formatted `driverType` and returns the broken down
210
- * `databaseType` and `driverName`.
211
- */
212
- function fromDatabaseDriverType(databaseDriverType) {
213
- const parts = databaseDriverType.split(":");
214
- return {
215
- databaseType: parts[0],
216
- driverName: parts[1]
217
- };
218
- }
219
- /**
220
- * Accepts a fully formatted `databaseDriverType` and returns the `driverName`.
221
- */
222
- function getDatabaseDriverName(databaseDriverType) {
223
- const { driverName } = fromDatabaseDriverType(databaseDriverType);
224
- return driverName;
225
- }
226
- /**
227
- * Accepts a fully formatted `databaseDriverType` and returns the `databaseType`.
228
- */
229
- function getDatabaseType(databaseDriverType) {
230
- const { databaseType } = fromDatabaseDriverType(databaseDriverType);
231
- return databaseType;
232
- }
233
-
234
- //#endregion
235
- //#region src/core/schema/databaseMetadata/databaseMetadata.ts
236
- const DumboDatabaseMetadataRegistry = () => {
237
- const infos = /* @__PURE__ */ new Map();
238
- const register = (databaseType, info) => {
239
- const entry = infos.get(databaseType);
240
- if (entry && (typeof entry !== "function" || typeof info === "function")) return;
241
- infos.set(databaseType, info);
242
- };
243
- const tryResolve = async (databaseType) => {
244
- const entry = infos.get(databaseType);
245
- if (!entry) return null;
246
- if (typeof entry !== "function") return entry;
247
- const resolved = await entry();
248
- register(databaseType, resolved);
249
- return resolved;
250
- };
251
- const tryGet = (databaseType) => {
252
- const entry = infos.get(databaseType);
253
- return entry && typeof entry !== "function" ? entry : null;
254
- };
255
- const has = (databaseType) => infos.has(databaseType);
256
- return {
257
- register,
258
- tryResolve,
259
- tryGet,
260
- has,
261
- get databaseTypes() {
262
- return Array.from(infos.keys());
263
- }
264
- };
112
+ var AdminShutdownError = class AdminShutdownError extends TransientDatabaseError {
113
+ static ErrorCode = 503;
114
+ static ErrorType = "AdminShutdownError";
115
+ constructor(message, innerError) {
116
+ super(message ?? `The database server is shutting down or restarting.`, innerError);
117
+ this.errorType = AdminShutdownError.ErrorType;
118
+ Object.setPrototypeOf(this, AdminShutdownError.prototype);
119
+ }
265
120
  };
266
- const dumboDatabaseMetadataRegistry = globalThis.dumboDatabaseMetadataRegistry = globalThis.dumboDatabaseMetadataRegistry ?? DumboDatabaseMetadataRegistry();
267
- const getDatabaseMetadata = (driverType) => {
268
- const { databaseType } = fromDatabaseDriverType(driverType);
269
- return dumboDatabaseMetadataRegistry.tryGet(databaseType);
121
+ var QueryCanceledError = class QueryCanceledError extends TransientDatabaseError {
122
+ static ErrorCode = 503;
123
+ static ErrorType = "QueryCanceledError";
124
+ constructor(message, innerError) {
125
+ super(message ?? `The query was canceled, e.g. due to statement timeout or user request.`, innerError);
126
+ this.errorType = QueryCanceledError.ErrorType;
127
+ Object.setPrototypeOf(this, QueryCanceledError.prototype);
128
+ }
270
129
  };
271
- const resolveDatabaseMetadata = async (driverType, driverOverride) => {
272
- if (driverOverride) return driverOverride;
273
- const { databaseType } = fromDatabaseDriverType(driverType);
274
- return dumboDatabaseMetadataRegistry.tryResolve(databaseType);
130
+ var IntegrityConstraintViolationError = class IntegrityConstraintViolationError extends DumboError {
131
+ static ErrorCode = 409;
132
+ static ErrorType = "IntegrityConstraintViolationError";
133
+ constructor(message, innerError) {
134
+ super({
135
+ errorCode: IntegrityConstraintViolationError.ErrorCode,
136
+ errorType: IntegrityConstraintViolationError.ErrorType,
137
+ message: message ?? `An integrity constraint violation occurred!`,
138
+ innerError
139
+ });
140
+ Object.setPrototypeOf(this, IntegrityConstraintViolationError.prototype);
141
+ }
275
142
  };
276
- const getDefaultDatabase = (driverType) => {
277
- return getDatabaseMetadata(driverType)?.defaultDatabaseName;
143
+ var UniqueConstraintError = class UniqueConstraintError extends IntegrityConstraintViolationError {
144
+ static ErrorCode = 409;
145
+ static ErrorType = "UniqueConstraintError";
146
+ constructor(message, innerError) {
147
+ super(message ?? `Unique constraint violation occurred!`, innerError);
148
+ this.errorType = UniqueConstraintError.ErrorType;
149
+ Object.setPrototypeOf(this, UniqueConstraintError.prototype);
150
+ }
278
151
  };
279
- const getDefaultDatabaseAsync = async (driverType) => {
280
- return (await resolveDatabaseMetadata(driverType))?.defaultDatabaseName;
152
+ var ForeignKeyViolationError = class ForeignKeyViolationError extends IntegrityConstraintViolationError {
153
+ static ErrorCode = 409;
154
+ static ErrorType = "ForeignKeyViolationError";
155
+ constructor(message, innerError) {
156
+ super(message ?? `Foreign key constraint violation occurred!`, innerError);
157
+ this.errorType = ForeignKeyViolationError.ErrorType;
158
+ Object.setPrototypeOf(this, ForeignKeyViolationError.prototype);
159
+ }
281
160
  };
282
-
283
- //#endregion
284
- //#region src/core/schema/dumboSchema/dumboSchema.ts
285
- const DEFAULT_DATABASE_NAME = "__default_database__";
286
- const DEFAULT_DATABASE_SCHEMA_NAME = "__default_database_schema__";
287
- const dumboColumn = (name, type, options) => columnSchemaComponent({
288
- columnName: name,
289
- type,
290
- ...options
291
- });
292
- const dumboIndex = (name, columnNames, options) => indexSchemaComponent({
293
- indexName: name,
294
- columnNames,
295
- isUnique: options?.unique ?? false,
296
- ...options
297
- });
298
- const dumboTable = (name, definition) => {
299
- const { columns, indexes, primaryKey, relationships, ...options } = definition;
300
- const components = [...indexes ? Object.values(indexes) : []];
301
- return tableSchemaComponent({
302
- tableName: name,
303
- columns: columns ?? {},
304
- primaryKey: primaryKey ?? [],
305
- ...relationships !== void 0 ? { relationships } : {},
306
- components,
307
- ...options
308
- });
161
+ var NotNullViolationError = class NotNullViolationError extends IntegrityConstraintViolationError {
162
+ static ErrorCode = 409;
163
+ static ErrorType = "NotNullViolationError";
164
+ constructor(message, innerError) {
165
+ super(message ?? `NOT NULL constraint violation occurred!`, innerError);
166
+ this.errorType = NotNullViolationError.ErrorType;
167
+ Object.setPrototypeOf(this, NotNullViolationError.prototype);
168
+ }
309
169
  };
310
- function dumboDatabaseSchema(nameOrTables, tables, options) {
311
- return databaseSchemaSchemaComponent({
312
- schemaName: typeof nameOrTables === "string" ? nameOrTables : DEFAULT_DATABASE_SCHEMA_NAME,
313
- tables: (typeof nameOrTables === "string" ? tables : nameOrTables) ?? {},
314
- ...options
315
- });
316
- }
317
- dumboDatabaseSchema.from = (schemaName, tableNames) => {
318
- const tables = tableNames.reduce((acc, tableName) => {
319
- acc[tableName] = dumboTable(tableName, {});
320
- return acc;
321
- }, {});
322
- return schemaName ? dumboDatabaseSchema(schemaName, tables) : dumboDatabaseSchema(tables);
170
+ var CheckViolationError = class CheckViolationError extends IntegrityConstraintViolationError {
171
+ static ErrorCode = 409;
172
+ static ErrorType = "CheckViolationError";
173
+ constructor(message, innerError) {
174
+ super(message ?? `CHECK constraint violation occurred!`, innerError);
175
+ this.errorType = CheckViolationError.ErrorType;
176
+ Object.setPrototypeOf(this, CheckViolationError.prototype);
177
+ }
323
178
  };
324
- function dumboDatabase(nameOrSchemas, schemasOrOptions, options) {
325
- const databaseName = typeof nameOrSchemas === "string" ? nameOrSchemas : DEFAULT_DATABASE_NAME;
326
- const schemasOrSchema = typeof nameOrSchemas === "string" ? schemasOrOptions ?? {} : nameOrSchemas;
327
- return databaseSchemaComponent({
328
- databaseName,
329
- schemas: "schemaComponentKey" in schemasOrSchema && isSchemaComponentOfType(schemasOrSchema, "sc:dumbo:database_schema") ? { [DEFAULT_DATABASE_SCHEMA_NAME]: schemasOrSchema } : schemasOrSchema,
330
- ...typeof nameOrSchemas === "string" ? options : schemasOrOptions
331
- });
332
- }
333
- dumboDatabase.from = (databaseName, schemaNames) => {
334
- const schemas = schemaNames.reduce((acc, schemaName) => {
335
- acc[schemaName] = dumboDatabaseSchema(schemaName, {});
336
- return acc;
337
- }, {});
338
- return databaseName ? dumboDatabase(databaseName, schemas) : dumboDatabase(schemas);
179
+ var ExclusionViolationError = class ExclusionViolationError extends IntegrityConstraintViolationError {
180
+ static ErrorCode = 409;
181
+ static ErrorType = "ExclusionViolationError";
182
+ constructor(message, innerError) {
183
+ super(message ?? `Exclusion constraint violation occurred!`, innerError);
184
+ this.errorType = ExclusionViolationError.ErrorType;
185
+ Object.setPrototypeOf(this, ExclusionViolationError.prototype);
186
+ }
339
187
  };
340
- dumboDatabase.defaultName = DEFAULT_DATABASE_NAME;
341
- dumboDatabaseSchema.defaultName = DEFAULT_DATABASE_SCHEMA_NAME;
342
- const dumboSchema = {
343
- database: dumboDatabase,
344
- schema: dumboDatabaseSchema,
345
- table: dumboTable,
346
- column: dumboColumn,
347
- index: dumboIndex
188
+ var DataError = class DataError extends DumboError {
189
+ static ErrorCode = 400;
190
+ static ErrorType = "DataError";
191
+ constructor(message, innerError) {
192
+ super({
193
+ errorCode: DataError.ErrorCode,
194
+ errorType: DataError.ErrorType,
195
+ message: message ?? `A data error occurred (e.g. invalid value, type mismatch).`,
196
+ innerError
197
+ });
198
+ Object.setPrototypeOf(this, DataError.prototype);
199
+ }
348
200
  };
349
-
350
- //#endregion
351
- //#region src/core/locks/databaseLock.ts
352
- const defaultDatabaseLockOptions = { timeoutMs: 1e4 };
353
- const NoDatabaseLock = {
354
- acquire: () => Promise.resolve(),
355
- tryAcquire: () => Promise.resolve(true),
356
- release: () => Promise.resolve(true),
357
- withAcquire: (_execute, handle, _options) => handle()
201
+ var InvalidOperationError = class InvalidOperationError extends DumboError {
202
+ static ErrorCode = 400;
203
+ static ErrorType = "InvalidOperationError";
204
+ constructor(message, innerError) {
205
+ super({
206
+ errorCode: InvalidOperationError.ErrorCode,
207
+ errorType: InvalidOperationError.ErrorType,
208
+ message: message ?? `Invalid operation (e.g. syntax error, insufficient privileges, undefined table).`,
209
+ innerError
210
+ });
211
+ Object.setPrototypeOf(this, InvalidOperationError.prototype);
212
+ }
358
213
  };
359
214
 
360
215
  //#endregion
361
- //#region src/core/query/selectors.ts
362
- const firstOrNull = async (getResult) => {
363
- const result = await getResult;
364
- return result.rows.length > 0 ? result.rows[0] ?? null : null;
365
- };
366
- const first = async (getResult) => {
367
- const result = await getResult;
368
- if (result.rows.length === 0) throw new Error("Query didn't return any result");
369
- return result.rows[0];
216
+ //#region src/core/execute/execute.ts
217
+ const mapColumnToJSON = (column, serializer, options) => ({ [column]: (value) => {
218
+ if (typeof value === "string") try {
219
+ return serializer.deserialize(value, options);
220
+ } catch {}
221
+ return value;
222
+ } });
223
+ const mapColumnToBigint = (column) => ({ [column]: (value) => {
224
+ if (typeof value === "number" || typeof value === "string") return BigInt(value);
225
+ return value;
226
+ } });
227
+ const mapColumnToDate = (column) => ({ [column]: (value) => {
228
+ if (typeof value === "number" || typeof value === "string") return new Date(value);
229
+ return value;
230
+ } });
231
+ const mapSQLQueryResult = (result, mapping) => {
232
+ if (typeof result !== "object" || result === null) return result;
233
+ const mappedResult = { ...result };
234
+ for (const column of Object.keys(mapping)) if (column in mappedResult) mappedResult[column] = mapping[column](mappedResult[column]);
235
+ return mappedResult;
370
236
  };
371
- const singleOrNull = async (getResult) => {
372
- const result = await getResult;
373
- if (result.rows.length > 1) throw new Error("Query had more than one result");
374
- return result.rows.length > 0 ? result.rows[0] ?? null : null;
237
+ var BatchCommandNoChangesError = class BatchCommandNoChangesError extends DumboError {
238
+ static ErrorCode = 409;
239
+ static ErrorType = "BatchCommandNoChangesError";
240
+ statementIndex;
241
+ constructor(statementIndex) {
242
+ super({
243
+ errorCode: BatchCommandNoChangesError.ErrorCode,
244
+ errorType: BatchCommandNoChangesError.ErrorType,
245
+ message: `Batch command at index ${statementIndex} affected no rows`
246
+ });
247
+ this.name = "BatchCommandNoChangesError";
248
+ this.statementIndex = statementIndex;
249
+ Object.setPrototypeOf(this, BatchCommandNoChangesError.prototype);
250
+ }
375
251
  };
376
- const single = async (getResult) => {
377
- const result = await getResult;
378
- if (result.rows.length === 0) throw new Error("Query didn't return any result");
379
- if (result.rows.length > 1) throw new Error("Query had more than one result");
380
- return result.rows[0];
252
+ const sqlExecutor = (sqlExecutor, options) => ({
253
+ query: (sql, queryOptions) => executeInNewDbClient((client) => sqlExecutor.query(client, sql, queryOptions), options),
254
+ batchQuery: (sqls, queryOptions) => executeInNewDbClient((client) => sqlExecutor.batchQuery(client, sqls, queryOptions), options),
255
+ command: (sql, commandOptions) => executeInNewDbClient((client) => sqlExecutor.command(client, sql, commandOptions), options),
256
+ batchCommand: (sqls, commandOptions) => executeInNewDbClient((client) => sqlExecutor.batchCommand(client, sqls, commandOptions), options)
257
+ });
258
+ const sqlExecutorInNewConnection = (options) => ({
259
+ query: (sql, queryOptions) => executeInNewConnection((connection) => connection.execute.query(sql, queryOptions), options),
260
+ batchQuery: (sqls, queryOptions) => executeInNewConnection((connection) => connection.execute.batchQuery(sqls, queryOptions), options),
261
+ command: (sql, commandOptions) => executeInNewConnection((connection) => connection.execute.command(sql, commandOptions), options),
262
+ batchCommand: (sqls, commandOptions) => executeInNewConnection((connection) => connection.execute.batchCommand(sqls, commandOptions), options)
263
+ });
264
+ const sqlExecutorInAmbientConnection = (options) => ({
265
+ query: (sql, queryOptions) => executeInAmbientConnection((connection) => connection.execute.query(sql, queryOptions), options),
266
+ batchQuery: (sqls, queryOptions) => executeInAmbientConnection((connection) => connection.execute.batchQuery(sqls, queryOptions), options),
267
+ command: (sql, commandOptions) => executeInAmbientConnection((connection) => connection.execute.command(sql, commandOptions), options),
268
+ batchCommand: (sqls, commandOptions) => executeInAmbientConnection((connection) => connection.execute.batchCommand(sqls, commandOptions), options)
269
+ });
270
+ const executeInNewDbClient = async (handle, options) => {
271
+ const { connect, close } = options;
272
+ const client = await connect();
273
+ try {
274
+ return await handle(client);
275
+ } catch (error) {
276
+ if (close) await close(client, error);
277
+ throw error;
278
+ }
381
279
  };
382
- const count = async (getResult) => {
383
- const result = await single(getResult);
384
- return Number(result.count);
280
+ const executeInNewConnection = async (handle, options) => {
281
+ const connection = await options.connection();
282
+ try {
283
+ return await handle(connection);
284
+ } finally {
285
+ await connection.close();
286
+ }
385
287
  };
386
- const exists = async (getResult) => {
387
- const result = await single(getResult);
388
- return result.exists === true || result.exists === 1;
288
+ const executeInAmbientConnection = async (handle, options) => {
289
+ const connection = await options.connection();
290
+ try {
291
+ return await handle(connection);
292
+ } finally {}
389
293
  };
390
294
 
391
295
  //#endregion
392
- //#region src/core/serializer/json/index.ts
393
- const bigIntReplacer = (_key, value) => {
394
- return typeof value === "bigint" ? value.toString() : value;
395
- };
396
- const dateReplacer = (_key, value) => {
397
- return value instanceof Date ? value.toISOString() : value;
398
- };
399
- const isFirstLetterNumeric = (str) => {
400
- const c = str.charCodeAt(0);
401
- return c >= 48 && c <= 57;
402
- };
403
- const isFirstLetterNumericOrMinus = (str) => {
404
- const c = str.charCodeAt(0);
405
- return c >= 48 && c <= 57 || c === 45;
406
- };
407
- const bigIntReviver = (_key, value, context) => {
408
- if (typeof value === "number" && Number.isInteger(value) && !Number.isSafeInteger(value)) try {
409
- return BigInt(context?.source ?? value.toString());
410
- } catch {
411
- return value;
412
- }
413
- if (typeof value === "string" && value.length > 15) {
414
- if (isFirstLetterNumericOrMinus(value)) {
415
- const num = Number(value);
416
- if (Number.isFinite(num) && !Number.isSafeInteger(num)) try {
417
- return BigInt(value);
418
- } catch {}
419
- }
420
- }
421
- return value;
296
+ //#region src/core/connections/transaction.ts
297
+ const toTransactionResult = (transactionResult) => transactionResult !== void 0 && transactionResult !== null && typeof transactionResult === "object" && "success" in transactionResult ? transactionResult : {
298
+ success: true,
299
+ result: transactionResult
422
300
  };
423
- const dateReviver = (_key, value) => {
424
- if (typeof value === "string" && value.length === 24 && isFirstLetterNumeric(value) && value[10] === "T" && value[23] === "Z") {
425
- const date = new Date(value);
426
- if (!isNaN(date.getTime())) return date;
301
+ const executeInTransaction = async (transaction, handle) => {
302
+ await transaction.begin();
303
+ try {
304
+ const { success, result } = toTransactionResult(await handle(transaction));
305
+ if (success) await transaction.commit();
306
+ else await transaction.rollback();
307
+ return result;
308
+ } catch (e) {
309
+ await transaction.rollback();
310
+ throw e;
427
311
  }
428
- return value;
429
- };
430
- const composeJSONReplacers = (...replacers) => {
431
- const filteredReplacers = replacers.filter((r) => r !== void 0);
432
- if (filteredReplacers.length === 0) return void 0;
433
- return (key, value) => filteredReplacers.reduce((accValue, replacer) => replacer(key, accValue), value);
434
- };
435
- const composeJSONRevivers = (...revivers) => {
436
- const filteredRevivers = revivers.filter((r) => r !== void 0);
437
- if (filteredRevivers.length === 0) return void 0;
438
- return (key, value, context) => filteredRevivers.reduce((accValue, reviver) => reviver(key, accValue, context), value);
439
- };
440
- const JSONReplacer = (opts) => composeJSONReplacers(opts?.replacer, opts?.failOnBigIntSerialization !== true ? JSONReplacers.bigInt : void 0, opts?.useDefaultDateSerialization !== true ? JSONReplacers.date : void 0);
441
- const JSONReviver = (opts) => composeJSONRevivers(opts?.reviver, opts?.parseBigInts === true ? JSONRevivers.bigInt : void 0, opts?.parseDates === true ? JSONRevivers.date : void 0);
442
- const JSONReplacers = {
443
- bigInt: bigIntReplacer,
444
- date: dateReplacer
445
- };
446
- const JSONRevivers = {
447
- bigInt: bigIntReviver,
448
- date: dateReviver
449
312
  };
450
- const jsonSerializer = (options) => {
451
- const defaultReplacer = JSONReplacer(options);
452
- const defaultReviver = JSONReviver(options);
313
+ const transactionFactoryWithDbClient = (connect, initTransaction) => {
314
+ let currentTransaction = void 0;
315
+ const getOrInitCurrentTransaction = (options) => currentTransaction ?? (currentTransaction = initTransaction(connect(), {
316
+ close: () => {
317
+ currentTransaction = void 0;
318
+ return Promise.resolve();
319
+ },
320
+ ...options ?? {}
321
+ }));
453
322
  return {
454
- serialize: (object, serializerOptions) => JSON.stringify(object, serializerOptions ? JSONReplacer(serializerOptions) : defaultReplacer),
455
- deserialize: (payload, deserializerOptions) => JSON.parse(payload, deserializerOptions ? JSONReviver(deserializerOptions) : defaultReviver)
323
+ transaction: getOrInitCurrentTransaction,
324
+ withTransaction: (handle, options) => executeInTransaction(getOrInitCurrentTransaction(options), handle)
456
325
  };
457
326
  };
458
- const JSONSerializer = Object.assign(jsonSerializer(), { from: (options) => options?.serialization?.serializer ?? (options?.serialization?.options ? jsonSerializer(options?.serialization?.options) : JSONSerializer) });
459
- const JSONCodec = (options) => {
460
- const serializer = "serializer" in options ? options.serializer : jsonSerializer("serializerOptions" in options ? options.serializerOptions : void 0);
461
- const upcast = options.upcast ?? ((doc) => doc);
462
- const downcast = options.downcast ?? ((doc) => doc);
463
- return {
464
- decode: (payload, decodeOptions) => {
465
- return upcast(decodeOptions ? serializer.deserialize(payload, decodeOptions) : serializer.deserialize(payload));
466
- },
467
- encode: (object, encodeOptions) => {
468
- const downcasted = downcast(object);
469
- return encodeOptions ? serializer.serialize(downcasted, encodeOptions) : serializer.serialize(downcasted);
470
- }
471
- };
327
+ const wrapInConnectionClosure = async (connection, handle) => {
328
+ try {
329
+ return await handle();
330
+ } finally {
331
+ await connection.close();
332
+ }
472
333
  };
473
-
474
- //#endregion
475
- //#region src/core/sql/parametrizedSQL/parametrizedSQL.ts
476
- const ParametrizedSQLBuilder = ({ mapParamPlaceholder }) => {
477
- const sql = [];
478
- const params = [];
334
+ const transactionFactoryWithNewConnection = (connect) => ({
335
+ transaction: (options) => {
336
+ const connection = connect();
337
+ const transaction = connection.transaction(options);
338
+ return {
339
+ ...transaction,
340
+ commit: () => wrapInConnectionClosure(connection, () => transaction.commit()),
341
+ rollback: () => wrapInConnectionClosure(connection, () => transaction.rollback())
342
+ };
343
+ },
344
+ withTransaction: (handle, options) => {
345
+ const connection = connect();
346
+ const withTx = connection.withTransaction;
347
+ return wrapInConnectionClosure(connection, () => withTx(handle, options));
348
+ }
349
+ });
350
+ const transactionFactoryWithAmbientConnection = (connect) => ({
351
+ transaction: (options) => {
352
+ const transaction = connect().transaction(options);
353
+ return {
354
+ ...transaction,
355
+ commit: () => transaction.commit(),
356
+ rollback: () => transaction.rollback()
357
+ };
358
+ },
359
+ withTransaction: (handle, options) => {
360
+ const withTx = connect().withTransaction;
361
+ return withTx(handle, options);
362
+ }
363
+ });
364
+ const transactionFactoryWithAsyncAmbientConnection = (driverType, connect, close) => {
365
+ close ??= () => Promise.resolve();
479
366
  return {
480
- addSQL(str) {
481
- sql.push(str);
482
- return this;
483
- },
484
- addParam(value) {
485
- sql.push(mapParamPlaceholder(params.length, value));
486
- params.push(value);
487
- return this;
488
- },
489
- addParams(values) {
490
- const placeholders = values.map((value, i) => mapParamPlaceholder(params.length + i, value));
491
- this.addSQL(`${placeholders.join(", ")}`);
492
- params.push(...values);
493
- return this;
494
- },
495
- build() {
367
+ transaction: (options) => {
368
+ let conn = null;
369
+ let innerTx = null;
370
+ let connectingPromise = null;
371
+ const ensureConnection = async () => {
372
+ if (conn) return innerTx;
373
+ if (!connectingPromise) connectingPromise = (async () => {
374
+ conn = await connect();
375
+ innerTx = conn.transaction(options);
376
+ })();
377
+ await connectingPromise;
378
+ return innerTx;
379
+ };
496
380
  return {
497
- query: sql.join(""),
498
- params
381
+ driverType,
382
+ get connection() {
383
+ if (!conn) throw new Error("Transaction not started - call begin() first");
384
+ return conn;
385
+ },
386
+ execute: {
387
+ query: async (sql, queryOptions) => {
388
+ return (await ensureConnection()).execute.query(sql, queryOptions);
389
+ },
390
+ batchQuery: async (sqls, queryOptions) => {
391
+ return (await ensureConnection()).execute.batchQuery(sqls, queryOptions);
392
+ },
393
+ command: async (sql, commandOptions) => {
394
+ return (await ensureConnection()).execute.command(sql, commandOptions);
395
+ },
396
+ batchCommand: async (sqls, commandOptions) => {
397
+ return (await ensureConnection()).execute.batchCommand(sqls, commandOptions);
398
+ }
399
+ },
400
+ begin: async () => {
401
+ return (await ensureConnection()).begin();
402
+ },
403
+ commit: async () => {
404
+ if (!innerTx) throw new Error("Transaction not started");
405
+ try {
406
+ return await innerTx.commit();
407
+ } finally {
408
+ if (conn) await close(conn);
409
+ }
410
+ },
411
+ rollback: async (error) => {
412
+ if (!innerTx) {
413
+ if (conn) await close(conn);
414
+ return;
415
+ }
416
+ try {
417
+ return await innerTx.rollback(error);
418
+ } finally {
419
+ if (conn) await close(conn);
420
+ }
421
+ },
422
+ _transactionOptions: void 0
499
423
  };
424
+ },
425
+ withTransaction: async (handle, options) => {
426
+ const conn = await connect();
427
+ try {
428
+ const withTx = conn.withTransaction;
429
+ return await withTx(handle, options);
430
+ } finally {
431
+ await close(conn);
432
+ }
500
433
  }
501
434
  };
502
435
  };
503
436
 
504
437
  //#endregion
505
- //#region src/core/sql/tokens/sqlToken.ts
506
- const SQLToken = (sqlTokenType, map) => {
507
- const factory = (input) => {
508
- let props;
509
- if (map !== void 0) props = map(input);
510
- else if (input === void 0 || input === null) props = {};
511
- else if (typeof input === "object" && !Array.isArray(input)) props = input;
512
- else throw new Error(`Cannot create SQLToken of type ${sqlTokenType} with input: ${input}`);
513
- return {
514
- sqlTokenType,
515
- [sqlTokenType]: true,
516
- ...props
517
- };
518
- };
519
- const check = (token) => SQLToken.check(token) && token.sqlTokenType === sqlTokenType;
520
- return {
521
- from: factory,
522
- check,
523
- type: sqlTokenType
438
+ //#region src/core/connections/connection.ts
439
+ const createAmbientConnection = (options) => {
440
+ const { driverType, client, executor, initTransaction, serializer } = options;
441
+ const clientPromise = Promise.resolve(client);
442
+ const closePromise = Promise.resolve();
443
+ const open = () => clientPromise;
444
+ const close = () => closePromise;
445
+ const typedConnection = {
446
+ driverType,
447
+ open,
448
+ close,
449
+ ...transactionFactoryWithDbClient(open, initTransaction(() => typedConnection)),
450
+ execute: sqlExecutor(executor({ serializer }), { connect: open }),
451
+ _transactionType: void 0
524
452
  };
453
+ return typedConnection;
525
454
  };
526
- SQLToken.check = (token) => token !== null && typeof token === "object" && "sqlTokenType" in token;
527
- const SQLIdentifier = SQLToken("SQL_IDENTIFIER", (value) => ({ value }));
528
- const SQLPlain = SQLToken("SQL_RAW", (value) => ({ value }));
529
- const SQLLiteral = SQLToken("SQL_LITERAL", (value) => ({ value }));
530
- const SQLArray = SQLToken("SQL_ARRAY", (input) => {
531
- if (Array.isArray(input)) return { value: input };
532
- return input.mode !== void 0 ? {
533
- value: input.value,
534
- mode: input.mode
535
- } : { value: input.value };
536
- });
537
- const SQLIn = SQLToken("SQL_IN", ({ column, values, mode }) => mode !== void 0 ? {
538
- column: SQLIdentifier.from(column),
539
- values: SQLArray.from(values),
540
- mode
541
- } : {
542
- column: SQLIdentifier.from(column),
543
- values: SQLArray.from(values)
544
- });
545
-
546
- //#endregion
547
- //#region src/core/sql/tokens/columnTokens.ts
548
- const ColumnTypeToken = (sqlTokenType, jsTypeName, map) => {
549
- const factory = (input) => {
550
- let props;
551
- if (map !== void 0) props = map(input);
552
- else if (input === void 0 || input === null) props = {};
553
- else if (typeof input === "object" && !Array.isArray(input)) props = input;
554
- else throw new Error(`Cannot create SQLToken of type ${sqlTokenType} with input: ${input}`);
555
- return {
556
- sqlTokenType,
557
- [sqlTokenType]: true,
558
- jsTypeName,
559
- ...props
560
- };
455
+ const createSingletonConnection = (options) => {
456
+ const { driverType, connect, close, initTransaction, executor, serializer } = options;
457
+ let client = null;
458
+ let connectPromise = null;
459
+ const getClient = async () => {
460
+ if (client) return client;
461
+ if (!connectPromise) connectPromise = connect().then((c) => {
462
+ client = c;
463
+ return c;
464
+ });
465
+ return connectPromise;
561
466
  };
562
- const check = (token) => SQLToken.check(token) && token.sqlTokenType === sqlTokenType;
563
- return {
564
- from: factory,
565
- check,
566
- type: sqlTokenType
467
+ const typedConnection = {
468
+ driverType,
469
+ open: getClient,
470
+ close: () => client ? close(client) : Promise.resolve(),
471
+ ...transactionFactoryWithDbClient(getClient, initTransaction(() => typedConnection)),
472
+ execute: sqlExecutor(executor({ serializer }), { connect: getClient }),
473
+ _transactionType: void 0
567
474
  };
475
+ return typedConnection;
568
476
  };
569
- const SerialToken = ColumnTypeToken("SQL_COLUMN_SERIAL", "value_type:js:number");
570
- const BigSerialToken = ColumnTypeToken("SQL_COLUMN_BIGSERIAL", "value_type:js:bigint");
571
- const IntegerToken = ColumnTypeToken("SQL_COLUMN_INTEGER", "value_type:js:number");
572
- const BigIntegerToken = ColumnTypeToken("SQL_COLUMN_BIGINT", "value_type:js:bigint");
573
- const JSONBToken = {
574
- type: "SQL_COLUMN_JSONB",
575
- from: () => {
576
- return {
577
- sqlTokenType: "SQL_COLUMN_JSONB",
578
- ["SQL_COLUMN_JSONB"]: true
579
- };
580
- },
581
- check: (token) => SQLToken.check(token) && token.sqlTokenType === "SQL_COLUMN_JSONB"
582
- };
583
- const TimestampToken = ColumnTypeToken("SQL_COLUMN_TIMESTAMP", "value_type:js:date");
584
- const TimestamptzToken = ColumnTypeToken("SQL_COLUMN_TIMESTAMPTZ", "value_type:js:date");
585
- const VarcharToken = ColumnTypeToken("SQL_COLUMN_VARCHAR", "value_type:js:string", (length) => ({
586
- length: length ?? "max",
587
- jsTypeName: "value_type:js:string"
588
- }));
589
- const AutoIncrementSQLColumnToken = ColumnTypeToken("SQL_COLUMN_AUTO_INCREMENT", "value_type:js:bigint");
590
- const SQLColumnTypeTokens = {
591
- AutoIncrement: AutoIncrementSQLColumnToken,
592
- BigInteger: BigIntegerToken,
593
- BigSerial: BigSerialToken,
594
- Integer: IntegerToken,
595
- JSONB: JSONBToken,
596
- Serial: SerialToken,
597
- Timestamp: TimestampToken,
598
- Timestamptz: TimestamptzToken,
599
- Varchar: VarcharToken
477
+ const createTransientConnection = (options) => {
478
+ const { driverType, open, close, initTransaction, executor, serializer } = options;
479
+ const typedConnection = {
480
+ driverType,
481
+ open,
482
+ close,
483
+ ...transactionFactoryWithDbClient(open, initTransaction(() => typedConnection)),
484
+ execute: sqlExecutor(executor({ serializer }), { connect: open }),
485
+ _transactionType: void 0
486
+ };
487
+ return typedConnection;
600
488
  };
601
- const SQLColumnTypeTokensFactory = {
602
- AutoIncrement: AutoIncrementSQLColumnToken.from,
603
- BigInteger: BigIntegerToken.from(),
604
- BigSerial: BigSerialToken.from(),
605
- Integer: IntegerToken.from(),
606
- JSONB: JSONBToken.from,
607
- Serial: SerialToken.from(),
608
- Timestamp: TimestampToken.from(),
609
- Timestamptz: TimestamptzToken.from(),
610
- Varchar: VarcharToken.from
489
+ const createConnection = (options) => {
490
+ const { driverType, connect, close, initTransaction, executor, serializer } = options;
491
+ let client = null;
492
+ let connectPromise = null;
493
+ const getClient = async () => {
494
+ if (client) return client;
495
+ if (!connectPromise) connectPromise = connect().then((c) => {
496
+ client = c;
497
+ return c;
498
+ });
499
+ return connectPromise;
500
+ };
501
+ const typedConnection = {
502
+ driverType,
503
+ open: getClient,
504
+ close: () => client ? close(client) : Promise.resolve(),
505
+ ...transactionFactoryWithDbClient(getClient, initTransaction(() => typedConnection)),
506
+ execute: sqlExecutor(executor({ serializer }), { connect: getClient }),
507
+ _transactionType: void 0
508
+ };
509
+ return typedConnection;
611
510
  };
612
- const SQLColumnToken = SQLToken("SQL_COLUMN");
613
-
614
- //#endregion
615
- //#region src/core/sql/processors/sqlProcessor.ts
616
- const SQLProcessor = (options) => options;
617
511
 
618
512
  //#endregion
619
- //#region src/core/sql/processors/defaultProcessors.ts
620
- const ExpandArrayProcessor = SQLProcessor({
621
- canHandle: "SQL_ARRAY",
622
- handle: (token, { builder, serializer, mapper }) => {
623
- if (token.value.length === 0) throw new Error("Empty arrays are not supported. If you're using it with SELECT IN statement Use SQL.in(column, array) helper instead.");
624
- builder.addParams(mapper.mapValue(token.value, serializer));
625
- }
626
- });
627
- const ExpandSQLInProcessor = SQLProcessor({
628
- canHandle: "SQL_IN",
629
- handle: (token, context) => {
630
- const { builder, mapper, processorsRegistry, serializer } = context;
631
- const { values: inValues, column } = token;
632
- if (inValues.value.length === 0) {
633
- builder.addParam(mapper.mapValue(false, serializer));
634
- return;
635
- }
636
- builder.addSQL(mapper.mapValue(column.value, serializer));
637
- builder.addSQL(` IN (`);
638
- const arrayProcessor = processorsRegistry.get(SQLArray.type);
639
- if (!arrayProcessor) throw new Error("No sql processor registered for an array. Cannot expand IN statement");
640
- arrayProcessor.handle(inValues, {
641
- builder,
642
- mapper,
643
- processorsRegistry,
644
- serializer
645
- });
646
- builder.addSQL(`)`);
513
+ //#region src/core/taskProcessing/taskProcessor.ts
514
+ var TaskProcessor = class {
515
+ queue = [];
516
+ isProcessing = false;
517
+ activeTasks = 0;
518
+ activeGroups = /* @__PURE__ */ new Set();
519
+ options;
520
+ stopped = false;
521
+ constructor(options) {
522
+ this.options = options;
647
523
  }
648
- });
649
- const FormatIdentifierProcessor = SQLProcessor({
650
- canHandle: "SQL_IDENTIFIER",
651
- handle: (token, { builder, mapper, serializer }) => {
652
- builder.addSQL(mapper.mapValue(token, serializer));
524
+ enqueue(task, options) {
525
+ if (this.stopped) return Promise.reject(new DumboError("TaskProcessor has been stopped"));
526
+ if (this.queue.length >= this.options.maxQueueSize) return Promise.reject(new TransientDatabaseError("Too many pending connections. Please try again later."));
527
+ return this.schedule(task, options);
653
528
  }
654
- });
655
- const MapLiteralProcessor = SQLProcessor({
656
- canHandle: "SQL_LITERAL",
657
- handle: (token, { builder, mapper, serializer }) => builder.addParam(mapper.mapValue(token.value, serializer))
658
- });
659
-
660
- //#endregion
661
- //#region src/core/sql/processors/sqlProcessorRegistry.ts
662
- const SQLProcessorsRegistry = (options) => {
663
- const processors = options ? new Map(options.from.all()) : /* @__PURE__ */ new Map();
664
- function register(...args) {
665
- if (args.length === 1 && typeof args[0] === "object" && !Array.isArray(args[0])) Object.entries(args[0]).forEach(([_, processor]) => {
666
- processors.set(processor.canHandle, processor);
667
- });
668
- else args.forEach((p) => processors.set(p.canHandle, p));
669
- return registry;
529
+ waitForEndOfProcessing() {
530
+ return this.schedule(({ ack }) => Promise.resolve(ack()));
670
531
  }
671
- const registry = {
672
- register,
673
- get: (tokenType) => {
674
- return processors.get(tokenType) ?? null;
675
- },
676
- all: () => processors
677
- };
678
- return registry;
679
- };
680
-
681
- //#endregion
682
- //#region src/core/sql/processors/columnProcessors.ts
683
- const mapDefaultSQLColumnProcessors = (mapColumnType) => ({
684
- AutoIncrement: SQLProcessor({
685
- canHandle: "SQL_COLUMN_AUTO_INCREMENT",
686
- handle: (token, context) => {
687
- mapColumnType(token, context);
688
- }
689
- }),
690
- BigInteger: SQLProcessor({
691
- canHandle: "SQL_COLUMN_BIGINT",
692
- handle: (token, context) => mapColumnType(token, context)
693
- }),
694
- BigSerial: SQLProcessor({
695
- canHandle: "SQL_COLUMN_BIGSERIAL",
696
- handle: (token, context) => mapColumnType(token, context)
697
- }),
698
- Serial: SQLProcessor({
699
- canHandle: "SQL_COLUMN_SERIAL",
700
- handle: (token, context) => mapColumnType(token, context)
701
- }),
702
- Integer: SQLProcessor({
703
- canHandle: "SQL_COLUMN_INTEGER",
704
- handle: (token, context) => mapColumnType(token, context)
705
- }),
706
- JSONB: SQLProcessor({
707
- canHandle: "SQL_COLUMN_JSONB",
708
- handle: (token, context) => mapColumnType(token, context)
709
- }),
710
- Timestamp: SQLProcessor({
711
- canHandle: "SQL_COLUMN_TIMESTAMP",
712
- handle: (token, context) => mapColumnType(token, context)
713
- }),
714
- Timestamptz: SQLProcessor({
715
- canHandle: "SQL_COLUMN_TIMESTAMPTZ",
716
- handle: (token, context) => mapColumnType(token, context)
717
- }),
718
- Varchar: SQLProcessor({
719
- canHandle: "SQL_COLUMN_VARCHAR",
720
- handle: (token, context) => mapColumnType(token, context)
721
- })
722
- });
723
-
724
- //#endregion
725
- //#region src/core/sql/processors/index.ts
726
- const defaultProcessorsRegistry = globalThis.defaultProcessorsRegistry = globalThis.defaultProcessorsRegistry ?? SQLProcessorsRegistry().register(FormatIdentifierProcessor, MapLiteralProcessor, ExpandArrayProcessor, ExpandSQLInProcessor);
727
-
728
- //#endregion
729
- //#region src/core/sql/tokenizedSQL/tokenizedSQL.ts
730
- const TokenizedSQLBuilder = () => {
731
- const sqlChunks = [];
732
- const sqlTokens = [];
733
- return {
734
- addSQL(str) {
735
- sqlChunks.push(str);
736
- },
737
- addSQLs(str) {
738
- sqlChunks.push(...str);
739
- },
740
- addToken(value) {
741
- sqlTokens.push(value);
742
- },
743
- addTokens(vals) {
744
- sqlTokens.push(...vals);
745
- },
746
- build() {
747
- return sqlChunks.length > 0 ? {
748
- __brand: "tokenized-sql",
749
- sqlChunks,
750
- sqlTokens
751
- } : TokenizedSQL.empty;
532
+ async stop(options) {
533
+ if (this.stopped) return;
534
+ this.stopped = true;
535
+ this.queue.length = 0;
536
+ this.activeGroups.clear();
537
+ if (!options?.force) await this.waitForEndOfProcessing();
538
+ }
539
+ schedule(task, options) {
540
+ return promiseWithDeadline((resolve, reject) => {
541
+ const taskWithContext = () => {
542
+ return new Promise((resolveTask, failTask) => {
543
+ task({ ack: resolveTask }).then(resolve).catch((err) => {
544
+ failTask(err);
545
+ reject(err);
546
+ });
547
+ });
548
+ };
549
+ this.queue.push({
550
+ task: taskWithContext,
551
+ options
552
+ });
553
+ if (!this.isProcessing) this.ensureProcessing();
554
+ }, { deadline: this.options.maxTaskIdleTime });
555
+ }
556
+ ensureProcessing() {
557
+ if (this.isProcessing) return;
558
+ this.isProcessing = true;
559
+ this.processQueue();
560
+ }
561
+ processQueue() {
562
+ try {
563
+ while (this.activeTasks < this.options.maxActiveTasks && this.queue.length > 0) {
564
+ const item = this.takeFirstAvailableItem();
565
+ if (item === null) return;
566
+ const groupId = item.options?.taskGroupId;
567
+ if (groupId) this.activeGroups.add(groupId);
568
+ this.activeTasks++;
569
+ this.executeItem(item);
570
+ }
571
+ } catch (error) {
572
+ console.error(error);
573
+ throw error;
574
+ } finally {
575
+ this.isProcessing = false;
576
+ if (this.hasItemsToProcess() && this.activeTasks < this.options.maxActiveTasks) this.ensureProcessing();
752
577
  }
753
- };
754
- };
755
- const TokenizedSQL = (strings, values) => {
756
- const builder = TokenizedSQLBuilder();
757
- for (let i = 0; i < strings.length; i++) {
758
- if (strings[i] !== "") builder.addSQL(strings[i]);
759
- if (i >= values.length) break;
760
- const value = values[i];
761
- if (isTokenizedSQL(value)) {
762
- builder.addSQLs(value.sqlChunks);
763
- builder.addTokens(value.sqlTokens);
764
- } else if (SQLPlain.check(value)) builder.addSQL(value.value);
765
- else {
766
- builder.addSQL(TokenizedSQL.paramPlaceholder);
767
- builder.addToken(SQLToken.check(value) ? value : Array.isArray(value) ? SQLArray.from(value) : SQLLiteral.from(value));
578
+ }
579
+ async executeItem({ task, options }) {
580
+ try {
581
+ await task();
582
+ } finally {
583
+ this.activeTasks--;
584
+ if (options && options.taskGroupId) this.activeGroups.delete(options.taskGroupId);
585
+ this.ensureProcessing();
768
586
  }
769
587
  }
770
- return builder.build();
771
- };
772
- const isTokenizedSQL = (value) => {
773
- return value !== null && typeof value === "object" && "__brand" in value && value.__brand === "tokenized-sql";
588
+ takeFirstAvailableItem = () => {
589
+ const taskIndex = this.queue.findIndex((item) => !item.options?.taskGroupId || !this.activeGroups.has(item.options.taskGroupId));
590
+ if (taskIndex === -1) return null;
591
+ const [item] = this.queue.splice(taskIndex, 1);
592
+ return item ?? null;
593
+ };
594
+ hasItemsToProcess = () => this.queue.findIndex((item) => !item.options?.taskGroupId || !this.activeGroups.has(item.options.taskGroupId)) !== -1;
774
595
  };
775
- TokenizedSQL.paramPlaceholder = `__P__`;
776
- TokenizedSQL.empty = {
777
- __brand: "tokenized-sql",
778
- sqlChunks: [""],
779
- sqlTokens: []
596
+ const DEFAULT_PROMISE_DEADLINE = 2147483647;
597
+ const promiseWithDeadline = (executor, options) => {
598
+ return new Promise((resolve, reject) => {
599
+ let taskStarted = false;
600
+ let timeoutId = null;
601
+ const deadline = options.deadline ?? DEFAULT_PROMISE_DEADLINE;
602
+ timeoutId = setTimeout(() => {
603
+ if (!taskStarted) reject(/* @__PURE__ */ new Error("Task was not started within the maximum waiting time"));
604
+ }, deadline);
605
+ timeoutId.unref();
606
+ executor((value) => {
607
+ taskStarted = true;
608
+ if (timeoutId) clearTimeout(timeoutId);
609
+ timeoutId = null;
610
+ resolve(value);
611
+ }, (reason) => {
612
+ if (timeoutId) clearTimeout(timeoutId);
613
+ timeoutId = null;
614
+ reject(reason);
615
+ });
616
+ });
780
617
  };
781
618
 
782
619
  //#endregion
783
- //#region src/core/sql/sql.ts
784
- function SQL(strings, ...values) {
785
- return TokenizedSQL(strings, values);
786
- }
787
- function RawSQL(strings, ...values) {
788
- let result = "";
789
- for (let i = 0; i < strings.length; i++) {
790
- result += strings[i];
791
- if (i < values.length) result += String(values[i]);
792
- }
793
- return {
794
- __brand: "tokenized-sql",
795
- sqlChunks: [result],
796
- sqlTokens: []
620
+ //#region src/core/taskProcessing/executionGuards.ts
621
+ const guardBoundedAccess = (getResource, options) => {
622
+ let isStopped = false;
623
+ const taskProcessor = new TaskProcessor({
624
+ maxActiveTasks: options.maxResources,
625
+ maxQueueSize: options.maxQueueSize ?? 1e3
626
+ });
627
+ const resourcePool = [];
628
+ const allResources = /* @__PURE__ */ new Set();
629
+ const ackCallbacks = /* @__PURE__ */ new Map();
630
+ const acquire = async () => taskProcessor.enqueue(async ({ ack }) => {
631
+ try {
632
+ let resource;
633
+ if (options.reuseResources) resource = resourcePool.pop();
634
+ if (!resource) {
635
+ resource = await getResource();
636
+ allResources.add(resource);
637
+ }
638
+ ackCallbacks.set(resource, ack);
639
+ return resource;
640
+ } catch (e) {
641
+ ack();
642
+ throw e;
643
+ }
644
+ });
645
+ const release = (resource) => {
646
+ const ack = ackCallbacks.get(resource);
647
+ if (ack) {
648
+ ackCallbacks.delete(resource);
649
+ if (options.reuseResources) resourcePool.push(resource);
650
+ ack();
651
+ }
652
+ };
653
+ const execute = async (operation) => {
654
+ const resource = await acquire();
655
+ try {
656
+ return await operation(resource);
657
+ } finally {
658
+ release(resource);
659
+ }
660
+ };
661
+ return {
662
+ acquire,
663
+ release,
664
+ execute,
665
+ waitForIdle: () => taskProcessor.waitForEndOfProcessing(),
666
+ stop: async (stopOptions) => {
667
+ if (isStopped) return;
668
+ isStopped = true;
669
+ if (options?.closeResource) {
670
+ const resources = [...allResources];
671
+ allResources.clear();
672
+ resourcePool.length = 0;
673
+ await Promise.all(resources.map(async (resource) => await options.closeResource(resource)));
674
+ }
675
+ await taskProcessor.stop(stopOptions);
676
+ }
797
677
  };
798
- }
799
- const isSQL = (value) => {
800
- if (value === void 0 || value === null) return false;
801
- return isTokenizedSQL(value);
802
678
  };
803
- const emptySQL = {
804
- __brand: "tokenized-sql",
805
- sqlChunks: [""],
806
- sqlTokens: []
679
+ const guardInitializedOnce = (initialize, options) => {
680
+ let initPromise = null;
681
+ const taskProcessor = new TaskProcessor({
682
+ maxActiveTasks: 1,
683
+ maxQueueSize: options?.maxQueueSize ?? 1e3
684
+ });
685
+ const ensureInitialized = async (retryCount = 0) => {
686
+ if (initPromise !== null) return initPromise;
687
+ return taskProcessor.enqueue(async ({ ack }) => {
688
+ if (initPromise !== null) {
689
+ ack();
690
+ return initPromise;
691
+ }
692
+ try {
693
+ const promise = initialize();
694
+ initPromise = promise;
695
+ const result = await promise;
696
+ ack();
697
+ return result;
698
+ } catch (error) {
699
+ initPromise = null;
700
+ ack();
701
+ if (retryCount < (options?.maxRetries ?? 3)) return ensureInitialized(retryCount + 1);
702
+ throw error;
703
+ }
704
+ }, { taskGroupId: v7() });
705
+ };
706
+ return {
707
+ ensureInitialized,
708
+ reset: () => {
709
+ initPromise = null;
710
+ },
711
+ stop: (options) => taskProcessor.stop(options)
712
+ };
807
713
  };
808
- const mergeSQL = (sqls, separator = " ") => {
809
- const parametrized = sqls.filter((sql) => !isEmpty(sql)).map((sql) => sql);
810
- const params = parametrized.flatMap((p) => p.sqlTokens);
811
- const sqlChunks = parametrized.flatMap((p, i) => i == parametrized.length - 1 || separator === "" ? p.sqlChunks : [...p.sqlChunks, separator]);
812
- return sqlChunks.length > 0 ? {
813
- __brand: "tokenized-sql",
814
- sqlChunks,
815
- sqlTokens: params
816
- } : TokenizedSQL.empty;
714
+
715
+ //#endregion
716
+ //#region src/core/connections/pool.ts
717
+ const wrapPooledConnection = (conn, onClose) => ({
718
+ ...conn,
719
+ close: onClose
720
+ });
721
+ const createAmbientConnectionPool = (options) => {
722
+ const { driverType, connection } = options;
723
+ return createConnectionPool({
724
+ driverType,
725
+ getConnection: () => connection,
726
+ execute: connection.execute,
727
+ transaction: (options) => connection.transaction(options),
728
+ withConnection: (handle, _options) => handle(connection),
729
+ withTransaction: (handle, options) => {
730
+ const withTx = connection.withTransaction;
731
+ return withTx(handle, options);
732
+ }
733
+ });
817
734
  };
818
- const concatSQL = (...sqls) => mergeSQL(sqls, "");
819
- const isEmpty = (sql) => {
820
- if (isTokenizedSQL(sql)) {
821
- const parametrized = sql;
822
- return parametrized.sqlChunks.every((chunk) => chunk.trim() === "") && parametrized.sqlTokens.length === 0;
823
- }
824
- return false;
735
+ const createSingletonConnectionPool = (options) => {
736
+ const { driverType, getConnection } = options;
737
+ let connectionPromise = null;
738
+ const getExistingOrNewConnection = () => {
739
+ if (!connectionPromise) connectionPromise ??= Promise.resolve(getConnection());
740
+ return connectionPromise;
741
+ };
742
+ return {
743
+ driverType,
744
+ connection: () => getExistingOrNewConnection().then((conn) => wrapPooledConnection(conn, () => Promise.resolve())),
745
+ execute: sqlExecutorInAmbientConnection({
746
+ driverType,
747
+ connection: getExistingOrNewConnection
748
+ }),
749
+ withConnection: (handle, _options) => executeInAmbientConnection(handle, { connection: getExistingOrNewConnection }),
750
+ ...transactionFactoryWithAsyncAmbientConnection(options.driverType, getExistingOrNewConnection, options.closeConnection),
751
+ close: async () => {
752
+ if (!connectionPromise) return;
753
+ await (await connectionPromise).close();
754
+ }
755
+ };
825
756
  };
826
- SQL.EMPTY = emptySQL;
827
- SQL.concat = concatSQL;
828
- SQL.merge = mergeSQL;
829
- SQL.format = (sql, formatter, options) => formatSQL(sql, formatter, options?.serializer ?? JSONSerializer, options);
830
- SQL.describe = (sql, formatter, options) => describeSQL(sql, formatter, options?.serializer ?? JSONSerializer, options);
831
- SQL.in = (column, values, options) => options?.mode ? SQLIn.from({
832
- column,
833
- values,
834
- mode: options.mode
835
- }) : SQLIn.from({
836
- column,
837
- values
838
- });
839
- SQL.array = (values, options) => SQLArray.from(options?.mode ? {
840
- value: values,
841
- mode: options.mode
842
- } : values);
843
- SQL.identifier = SQLIdentifier.from;
844
- SQL.plain = SQLPlain.from;
845
- SQL.check = {
846
- isSQL,
847
- isTokenizedSQL: (value) => isTokenizedSQL(value),
848
- isEmpty,
849
- isIdentifier: SQLIdentifier.check,
850
- isPlain: SQLPlain.check,
851
- isSQLIn: SQLIn.check
757
+ const createBoundedConnectionPool = (options) => {
758
+ const { driverType, maxConnections } = options;
759
+ const guardMaxConnections = guardBoundedAccess(options.getConnection, {
760
+ maxResources: maxConnections,
761
+ reuseResources: true
762
+ });
763
+ let closed = false;
764
+ const executeWithPooling = async (operation) => {
765
+ const conn = await guardMaxConnections.acquire();
766
+ try {
767
+ return await operation(conn);
768
+ } finally {
769
+ guardMaxConnections.release(conn);
770
+ }
771
+ };
772
+ return {
773
+ driverType,
774
+ connection: async () => {
775
+ const conn = await guardMaxConnections.acquire();
776
+ return wrapPooledConnection(conn, () => Promise.resolve(guardMaxConnections.release(conn)));
777
+ },
778
+ execute: {
779
+ query: (sql, opts) => executeWithPooling((c) => c.execute.query(sql, opts)),
780
+ batchQuery: (sqls, opts) => executeWithPooling((c) => c.execute.batchQuery(sqls, opts)),
781
+ command: (sql, opts) => executeWithPooling((c) => c.execute.command(sql, opts)),
782
+ batchCommand: (sqls, opts) => executeWithPooling((c) => c.execute.batchCommand(sqls, opts))
783
+ },
784
+ withConnection: executeWithPooling,
785
+ ...transactionFactoryWithAsyncAmbientConnection(driverType, guardMaxConnections.acquire, guardMaxConnections.release),
786
+ close: async () => {
787
+ if (closed) return;
788
+ closed = true;
789
+ await guardMaxConnections.stop({ force: true });
790
+ }
791
+ };
792
+ };
793
+ const createSingletonClientConnectionPool = (options) => {
794
+ const { driverType, dbClient } = options;
795
+ return createSingletonConnectionPool({
796
+ getConnection: () => options.connectionFactory({ dbClient }),
797
+ driverType
798
+ });
799
+ };
800
+ const createAlwaysNewConnectionPool = (options) => {
801
+ const { driverType, getConnection, connectionOptions } = options;
802
+ return createConnectionPool({
803
+ driverType,
804
+ getConnection: () => connectionOptions ? getConnection(connectionOptions) : getConnection()
805
+ });
806
+ };
807
+ const createConnectionPool = (pool) => {
808
+ const { driverType, getConnection } = pool;
809
+ const connection = "connection" in pool ? pool.connection : () => Promise.resolve(getConnection());
810
+ return {
811
+ driverType,
812
+ connection,
813
+ withConnection: "withConnection" in pool ? pool.withConnection : (handle, _options) => executeInNewConnection(handle, { connection }),
814
+ close: "close" in pool ? pool.close : () => Promise.resolve(),
815
+ execute: "execute" in pool ? pool.execute : sqlExecutorInNewConnection({
816
+ driverType,
817
+ connection
818
+ }),
819
+ ..."transaction" in pool && "withTransaction" in pool ? {
820
+ transaction: pool.transaction,
821
+ withTransaction: pool.withTransaction
822
+ } : transactionFactoryWithNewConnection(getConnection)
823
+ };
852
824
  };
853
- const columnFactory = SQLColumnToken.from;
854
- columnFactory.type = SQLColumnTypeTokensFactory;
855
- SQL.column = columnFactory;
856
825
 
857
826
  //#endregion
858
- //#region src/core/sql/valueMappers/reservedSqlWords.ts
859
- const ansiSqlReservedMap = {
860
- ALL: true,
861
- AND: true,
862
- ANY: true,
863
- ARRAY: true,
864
- AS: true,
865
- ASC: true,
866
- AUTHORIZATION: true,
867
- BETWEEN: true,
868
- BINARY: true,
869
- BOTH: true,
870
- CASE: true,
871
- CAST: true,
872
- CHECK: true,
873
- COLLATE: true,
874
- COLUMN: true,
875
- CONSTRAINT: true,
876
- CREATE: true,
877
- CROSS: true,
878
- CURRENT_DATE: true,
879
- CURRENT_TIME: true,
880
- CURRENT_TIMESTAMP: true,
881
- CURRENT_USER: true,
882
- DEFAULT: true,
883
- DEFERRABLE: true,
884
- DESC: true,
885
- DISTINCT: true,
886
- DO: true,
887
- ELSE: true,
888
- END: true,
889
- EXCEPT: true,
890
- FALSE: true,
891
- FOR: true,
892
- FOREIGN: true,
893
- FROM: true,
894
- FULL: true,
895
- GRANT: true,
896
- GROUP: true,
897
- HAVING: true,
898
- IN: true,
899
- INITIALLY: true,
900
- INNER: true,
901
- INTERSECT: true,
902
- INTO: true,
903
- IS: true,
904
- JOIN: true,
905
- LEADING: true,
906
- LEFT: true,
907
- LIKE: true,
908
- LOCALTIME: true,
909
- LOCALTIMESTAMP: true,
910
- NATURAL: true,
911
- NEW: true,
912
- NOT: true,
913
- NULL: true,
914
- NULLS: true,
915
- OLD: true,
916
- ON: true,
917
- ONLY: true,
918
- OPEN: true,
919
- OR: true,
920
- ORDER: true,
921
- OUTER: true,
922
- OVERLAPS: true,
923
- PARTITION: true,
924
- PLACING: true,
925
- PRIMARY: true,
926
- REFERENCES: true,
927
- RIGHT: true,
928
- SELECT: true,
929
- SESSION_USER: true,
930
- SIMILAR: true,
931
- SOME: true,
932
- TABLE: true,
933
- THEN: true,
934
- TO: true,
935
- TRAILING: true,
936
- TRUE: true,
937
- UNION: true,
938
- UNIQUE: true,
939
- USER: true,
940
- USING: true,
941
- WHEN: true,
942
- WHERE: true,
943
- WITH: true,
944
- WITHOUT: true,
945
- ADD: true,
946
- ALTER: true,
947
- ARE: true,
948
- AT: true,
949
- BEGIN: true,
950
- BY: true,
951
- CASCADE: true,
952
- CLOSE: true,
953
- COMMIT: true,
954
- CONNECT: true,
955
- CONTINUE: true,
956
- CORRESPONDING: true,
957
- CURSOR: true,
958
- DEALLOCATE: true,
959
- DECLARE: true,
960
- DELETE: true,
961
- DESCRIBE: true,
962
- DISCONNECT: true,
963
- DROP: true,
964
- ESCAPE: true,
965
- EXECUTE: true,
966
- EXISTS: true,
967
- FETCH: true,
968
- FIRST: true,
969
- FLOAT: true,
970
- GET: true,
971
- GLOBAL: true,
972
- GO: true,
973
- GOTO: true,
974
- HOUR: true,
975
- IMMEDIATE: true,
976
- INDICATOR: true,
977
- INPUT: true,
978
- INSERT: true,
979
- INT: true,
980
- INTEGER: true,
981
- INTERVAL: true,
982
- LANGUAGE: true,
983
- LAST: true,
984
- LOCAL: true,
985
- MATCH: true,
986
- MINUTE: true,
987
- MODULE: true,
988
- MONTH: true,
989
- NATIONAL: true,
990
- NEXT: true,
991
- NO: true,
992
- OF: true,
993
- OUTPUT: true,
994
- PARTIAL: true,
995
- PREPARE: true,
996
- PRESERVE: true,
997
- PRIOR: true,
998
- PRIVILEGES: true,
999
- PROCEDURE: true,
1000
- PUBLIC: true,
1001
- READ: true,
1002
- REAL: true,
1003
- RESTRICT: true,
1004
- REVOKE: true,
1005
- ROLLBACK: true,
1006
- ROWS: true,
1007
- SCHEMA: true,
1008
- SCROLL: true,
1009
- SECOND: true,
1010
- SECTION: true,
1011
- SET: true,
1012
- SIZE: true,
1013
- SMALLINT: true,
1014
- SQL: true,
1015
- SQLCODE: true,
1016
- SQLERROR: true,
1017
- SQLSTATE: true,
1018
- TEMPORARY: true,
1019
- TIMEZONE_HOUR: true,
1020
- TIMEZONE_MINUTE: true,
1021
- TRANSACTION: true,
1022
- TRANSLATE: true,
1023
- TRANSLATION: true,
1024
- UNKNOWN: true,
1025
- UPDATE: true,
1026
- VALUE: true,
1027
- VALUES: true,
1028
- VARCHAR: true,
1029
- VARYING: true,
1030
- VIEW: true,
1031
- WHENEVER: true,
1032
- WORK: true,
1033
- WRITE: true,
1034
- YEAR: true,
1035
- ZONE: true
1036
- };
1037
-
1038
- //#endregion
1039
- //#region src/core/sql/valueMappers/sqlValueMapper.ts
1040
- const ANSISQLParamPlaceholder = "?";
1041
- const ANSISQLIdentifierQuote = "\"";
1042
- const mapANSISQLParamPlaceholder = () => "?";
1043
- const isReserved = (value, reserved) => !!reserved[value.toUpperCase()];
1044
- const mapSQLIdentifier = (value, options) => {
1045
- if (value === void 0 || value === null) throw new Error("SQL identifier cannot be null or undefined");
1046
- const ident = value.toString().slice(0);
1047
- const quoteSign = options?.quote ?? "\"";
1048
- if (/^[a-z_][a-z0-9_$]*$/.test(ident) && !isReserved(ident, options?.reservedWords ?? ansiSqlReservedMap)) return ident;
1049
- let quoted = quoteSign;
1050
- for (let i = 0; i < ident.length; i++) {
1051
- const c = ident[i];
1052
- quoted += c === quoteSign ? c + c : c;
1053
- }
1054
- quoted += quoteSign;
1055
- return quoted;
1056
- };
1057
- const DefaultMapSQLParamValueOptions = {
1058
- mapPlaceholder: mapANSISQLParamPlaceholder,
1059
- mapIdentifier: mapSQLIdentifier
827
+ //#region src/core/drivers/databaseDriver.ts
828
+ const canHandleDriverWithConnectionString = (driver, tryParseConnectionString) => (options) => {
829
+ if ("driverType" in options) return options.driverType === driver;
830
+ if ("connectionString" in options && typeof options.connectionString === "string") return tryParseConnectionString(options.connectionString) !== null;
831
+ return false;
1060
832
  };
1061
- const SQLValueMapper = (options) => {
1062
- const mapSQLParamValueOptions = {
1063
- ...DefaultMapSQLParamValueOptions,
1064
- ...options ?? {}
833
+ const DumboDatabaseDriverRegistry = () => {
834
+ const drivers = /* @__PURE__ */ new Map();
835
+ const register = (driverType, plugin) => {
836
+ const entry = drivers.get(driverType);
837
+ if (entry && (typeof entry !== "function" || typeof plugin === "function")) return;
838
+ drivers.set(driverType, plugin);
839
+ };
840
+ const getDriver = (options) => options.driverType ? drivers.get(options.driverType) : [...drivers.values()].find((d) => typeof d !== "function" && d.canHandle(options));
841
+ const tryResolve = async (options) => {
842
+ const driver = getDriver(options);
843
+ if (!driver) return null;
844
+ if (typeof driver !== "function") return driver;
845
+ const plugin = await driver();
846
+ register(plugin.driverType, plugin);
847
+ return plugin;
848
+ };
849
+ const tryGet = (options) => {
850
+ const driver = getDriver(options);
851
+ return driver && typeof driver !== "function" ? driver : null;
1065
852
  };
853
+ const has = (driverType) => drivers.has(driverType);
1066
854
  return {
1067
- mapValue: (value, serializer, mapOptions) => mapSQLParamValue(value, serializer, {
1068
- ...mapSQLParamValueOptions,
1069
- ...mapOptions
1070
- }),
1071
- mapPlaceholder: mapSQLParamValueOptions.mapPlaceholder,
1072
- mapIdentifier: mapSQLParamValueOptions.mapIdentifier
855
+ register,
856
+ tryResolve,
857
+ tryGet,
858
+ has,
859
+ get databaseDriverTypes() {
860
+ return Array.from(drivers.keys());
861
+ }
1073
862
  };
1074
863
  };
1075
- function mapSQLParamValue(value, serializer, options) {
1076
- if (value === null || value === void 0) return null;
1077
- else if (typeof value === "number") return value;
1078
- else if (typeof value === "string") return value;
1079
- else if (Array.isArray(value)) {
1080
- const mapValue = options?.mapValue ?? mapSQLParamValue;
1081
- return options?.mapArray ? options.mapArray(value, mapValue) : value.map((item) => mapValue(item, serializer, options));
1082
- } else if (typeof value === "boolean") return options?.mapBoolean ? options.mapBoolean(value) : value;
1083
- else if (typeof value === "bigint") return options?.mapBigInt ? options.mapBigInt(value) : value.toString();
1084
- else if (value instanceof Date) return options?.mapDate ? options.mapDate(value) : value.toISOString();
1085
- else if (SQL.check.isIdentifier(value)) return (options?.mapIdentifier ?? mapSQLIdentifier)(value.value);
1086
- else if (typeof value === "object") return options?.mapObject ? options.mapObject(value) : `${serializer.serialize(value).replace(/'/g, "''")}`;
1087
- else return serializer.serialize(value);
1088
- }
864
+ const dumboDatabaseDriverRegistry = globalThis.dumboDatabaseDriverRegistry = globalThis.dumboDatabaseDriverRegistry ?? DumboDatabaseDriverRegistry();
1089
865
 
1090
866
  //#endregion
1091
- //#region src/core/sql/formatters/sqlFormatter.ts
1092
- const SQLFormatter = ({ format, describe, valueMapper: valueMapperOptions, processorsRegistry }) => {
1093
- const valueMapper = SQLValueMapper(valueMapperOptions);
1094
- const options = {
1095
- builder: ParametrizedSQLBuilder({ mapParamPlaceholder: valueMapper.mapPlaceholder }),
1096
- mapper: valueMapper,
1097
- processorsRegistry: processorsRegistry ?? defaultProcessorsRegistry
867
+ //#region src/core/drivers/index.ts
868
+ /**
869
+ * Accepts a `databaseType` (e.g. PostgreSQL, SQLite) and a `driverName`
870
+ * (the library name, e.g. pg, sqlite3) and combines them to a singular
871
+ * `databaseDriverType` which can be used in database handling.
872
+ */
873
+ function toDatabaseDriverType(databaseType, driverName) {
874
+ return `${databaseType}:${driverName}`;
875
+ }
876
+ /**
877
+ * Accepts a fully formatted `driverType` and returns the broken down
878
+ * `databaseType` and `driverName`.
879
+ */
880
+ function fromDatabaseDriverType(databaseDriverType) {
881
+ const parts = databaseDriverType.split(":");
882
+ return {
883
+ databaseType: parts[0],
884
+ driverName: parts[1]
1098
885
  };
1099
- const resultFormatter = {
1100
- format: format ?? ((sql, methodOptions) => formatSQL(sql, resultFormatter, methodOptions?.serializer ?? JSONSerializer, {
1101
- ...options,
1102
- ...methodOptions ?? {}
1103
- })),
1104
- describe: describe ?? ((sql, methodOptions) => describeSQL(sql, resultFormatter, methodOptions?.serializer ?? JSONSerializer, {
1105
- ...options,
1106
- ...methodOptions ?? {}
1107
- })),
1108
- valueMapper
1109
- };
1110
- return resultFormatter;
1111
- };
1112
- const dumboSQLFormatters = globalThis.dumboSQLFormatters = globalThis.dumboSQLFormatters ?? {};
1113
- const registerFormatter = (dialect, formatter) => {
1114
- dumboSQLFormatters[dialect] = formatter;
1115
- };
1116
- const getFormatter = (dialect) => {
1117
- const formatterKey = dialect;
1118
- if (!dumboSQLFormatters[formatterKey]) throw new Error(`No SQL formatter registered for dialect: ${dialect}`);
1119
- return dumboSQLFormatters[formatterKey];
1120
- };
1121
- function formatSQL(sql, formatter, serializer, context) {
1122
- const mapper = context?.mapper == void 0 ? formatter.valueMapper : {
1123
- ...formatter.valueMapper,
1124
- ...context.mapper
1125
- };
1126
- const processorsRegistry = context?.processorsRegistry ?? defaultProcessorsRegistry;
1127
- const merged = Array.isArray(sql) ? SQL.merge(sql, "\n") : sql;
1128
- if (!isTokenizedSQL(merged)) throw new Error("Expected TokenizedSQL, got string-based SQL");
1129
- const builder = ParametrizedSQLBuilder({ mapParamPlaceholder: mapper.mapPlaceholder });
1130
- let paramIndex = 0;
1131
- for (let i = 0; i < merged.sqlChunks.length; i++) {
1132
- const sqlChunk = merged.sqlChunks[i];
1133
- if (sqlChunk !== TokenizedSQL.paramPlaceholder) {
1134
- builder.addSQL(sqlChunk);
1135
- continue;
1136
- }
1137
- const token = merged.sqlTokens[paramIndex++];
1138
- const processor = processorsRegistry.get(token.sqlTokenType);
1139
- if (!processor) throw new Error(`No SQL processor registered for token type: ${token.sqlTokenType}`);
1140
- processor.handle(token, {
1141
- builder,
1142
- processorsRegistry,
1143
- serializer,
1144
- mapper
1145
- });
1146
- }
1147
- return builder.build();
1148
886
  }
1149
- const describeSQL = (sql, formatter, serializer, options) => formatSQL(sql, formatter, serializer, {
1150
- ...options ?? {},
1151
- mapper: { mapPlaceholder: (_, value) => serializer.serialize(value) }
1152
- }).query;
1153
-
1154
- //#endregion
1155
- //#region src/core/tracing/printing/color.ts
1156
- let enableColors = true;
1157
- const color = {
1158
- set level(value) {
1159
- enableColors = value === 1;
1160
- },
1161
- hex: (value) => (text) => enableColors ? ansis.hex(value)(text) : text,
1162
- red: (value) => enableColors ? ansis.red(value) : value,
1163
- green: (value) => enableColors ? ansis.green(value) : value,
1164
- blue: (value) => enableColors ? ansis.blue(value) : value,
1165
- cyan: (value) => enableColors ? ansis.cyan(value) : value,
1166
- yellow: (value) => enableColors ? ansis.yellow(value) : value
1167
- };
887
+ /**
888
+ * Accepts a fully formatted `databaseDriverType` and returns the `driverName`.
889
+ */
890
+ function getDatabaseDriverName(databaseDriverType) {
891
+ const { driverName } = fromDatabaseDriverType(databaseDriverType);
892
+ return driverName;
893
+ }
894
+ /**
895
+ * Accepts a fully formatted `databaseDriverType` and returns the `databaseType`.
896
+ */
897
+ function getDatabaseType(databaseDriverType) {
898
+ const { databaseType } = fromDatabaseDriverType(databaseDriverType);
899
+ return databaseType;
900
+ }
1168
901
 
1169
902
  //#endregion
1170
- //#region src/core/tracing/printing/pretty.ts
1171
- const TWO_SPACES = " ";
1172
- const COLOR_STRING = color.hex("#98c379");
1173
- const COLOR_KEY = color.hex("#61afef");
1174
- const COLOR_NUMBER_OR_DATE = color.hex("#d19a66");
1175
- const COLOR_BOOLEAN = color.hex("#c678dd");
1176
- const COLOR_NULL_OR_UNDEFINED = color.hex("#c678dd");
1177
- const COLOR_BRACKETS = color.hex("#abb2bf");
1178
- const processString = (str, indent, handleMultiline) => {
1179
- if (handleMultiline && str.includes("\n")) {
1180
- const indentedLines = str.split("\n").map((line) => indent + TWO_SPACES + COLOR_STRING(line));
1181
- return COLOR_STRING("\"") + "\n" + indentedLines.join("\n") + "\n" + indent + COLOR_STRING("\"");
1182
- }
1183
- return COLOR_STRING(`"${str}"`);
1184
- };
1185
- const shouldPrint = (obj) => typeof obj !== "function" && typeof obj !== "symbol";
1186
- const formatJson = (obj, indentLevel = 0, handleMultiline = false) => {
1187
- const indent = TWO_SPACES.repeat(indentLevel);
1188
- if (obj === null) return COLOR_NULL_OR_UNDEFINED("null");
1189
- if (obj === void 0) return COLOR_NULL_OR_UNDEFINED("undefined");
1190
- if (typeof obj === "string") return processString(obj, indent, handleMultiline);
1191
- if (typeof obj === "number" || typeof obj === "bigint" || obj instanceof Date) return COLOR_NUMBER_OR_DATE(String(obj));
1192
- if (typeof obj === "boolean") return COLOR_BOOLEAN(String(obj));
1193
- if (obj instanceof Error) {
1194
- const errorObj = {};
1195
- Object.getOwnPropertyNames(obj).forEach((key) => {
1196
- errorObj[key] = obj[key];
1197
- });
1198
- return formatJson(errorObj, indentLevel, handleMultiline);
1199
- }
1200
- if (obj instanceof Promise) return COLOR_STRING("Promise {pending}");
1201
- if (Array.isArray(obj)) {
1202
- const arrayItems = obj.map((item) => formatJson(item, indentLevel + 1, handleMultiline));
1203
- return `${COLOR_BRACKETS("[")}\n${indent} ${arrayItems.join(`,\n${indent} `)}\n${indent}${COLOR_BRACKETS("]")}`;
1204
- }
1205
- const entries = Object.entries(obj).filter(([_, value]) => shouldPrint(value)).map(([key, value]) => `${COLOR_KEY(`"${key}"`)}: ${formatJson(value, indentLevel + 1, handleMultiline)}`);
1206
- return `${COLOR_BRACKETS("{")}\n${indent} ${entries.join(`,\n${indent} `)}\n${indent}${COLOR_BRACKETS("}")}`;
903
+ //#region src/core/locks/databaseLock.ts
904
+ const defaultDatabaseLockOptions = { timeoutMs: 1e4 };
905
+ const NoDatabaseLock = {
906
+ acquire: () => Promise.resolve(),
907
+ tryAcquire: () => Promise.resolve(true),
908
+ release: () => Promise.resolve(true),
909
+ withAcquire: (_execute, handle, _options) => handle()
1207
910
  };
1208
- const prettyJson = (obj, options) => formatJson(obj, 0, options?.handleMultiline);
1209
911
 
1210
912
  //#endregion
1211
- //#region src/core/tracing/index.ts
1212
- const tracer = () => {};
1213
- const LogLevel = {
1214
- DISABLED: "DISABLED",
1215
- INFO: "INFO",
1216
- LOG: "LOG",
1217
- WARN: "WARN",
1218
- ERROR: "ERROR"
1219
- };
1220
- const LogStyle = {
1221
- RAW: "RAW",
1222
- PRETTY: "PRETTY"
913
+ //#region src/core/query/selectors.ts
914
+ const firstOrNull = async (getResult) => {
915
+ const result = await getResult;
916
+ return result.rows.length > 0 ? result.rows[0] ?? null : null;
1223
917
  };
1224
- const getEnvVariable = (name) => {
1225
- try {
1226
- if (typeof process !== "undefined" && process.env) return process.env[name];
1227
- return;
1228
- } catch {
1229
- return;
1230
- }
918
+ const first = async (getResult) => {
919
+ const result = await getResult;
920
+ if (result.rows.length === 0) throw new Error("Query didn't return any result");
921
+ return result.rows[0];
1231
922
  };
1232
- const shouldLog = (logLevel) => {
1233
- const definedLogLevel = getEnvVariable("DUMBO_LOG_LEVEL") ?? LogLevel.ERROR;
1234
- if (definedLogLevel === LogLevel.ERROR && logLevel === LogLevel.ERROR) return true;
1235
- if (definedLogLevel === LogLevel.WARN && [LogLevel.ERROR, LogLevel.WARN].includes(logLevel)) return true;
1236
- if (definedLogLevel === LogLevel.LOG && [
1237
- LogLevel.ERROR,
1238
- LogLevel.WARN,
1239
- LogLevel.LOG
1240
- ].includes(logLevel)) return true;
1241
- if (definedLogLevel === LogLevel.INFO && [
1242
- LogLevel.ERROR,
1243
- LogLevel.WARN,
1244
- LogLevel.LOG,
1245
- LogLevel.INFO
1246
- ].includes(logLevel)) return true;
1247
- return false;
923
+ const singleOrNull = async (getResult) => {
924
+ const result = await getResult;
925
+ if (result.rows.length > 1) throw new Error("Query had more than one result");
926
+ return result.rows.length > 0 ? result.rows[0] ?? null : null;
1248
927
  };
1249
- const nulloTraceEventRecorder = () => {};
1250
- const getTraceEventFormatter = (logStyle, serializer = JSONSerializer) => (event) => {
1251
- switch (logStyle) {
1252
- case "RAW": return serializer.serialize(event);
1253
- case "PRETTY": return prettyJson(event, { handleMultiline: true });
1254
- }
928
+ const single = async (getResult) => {
929
+ const result = await getResult;
930
+ if (result.rows.length === 0) throw new Error("Query didn't return any result");
931
+ if (result.rows.length > 1) throw new Error("Query had more than one result");
932
+ return result.rows[0];
1255
933
  };
1256
- const getTraceEventRecorder = (logLevel, logStyle) => {
1257
- const format = getTraceEventFormatter(logStyle);
1258
- switch (logLevel) {
1259
- case "DISABLED": return nulloTraceEventRecorder;
1260
- case "INFO": return (event) => console.info(format(event));
1261
- case "LOG": return (event) => console.log(format(event));
1262
- case "WARN": return (event) => console.warn(format(event));
1263
- case "ERROR": return (event) => console.error(format(event));
1264
- }
934
+ const count = async (getResult) => {
935
+ const result = await single(getResult);
936
+ return Number(result.count);
1265
937
  };
1266
- const recordTraceEvent = (logLevel, eventName, attributes) => {
1267
- if (!shouldLog(LogLevel.LOG)) return;
1268
- const event = {
1269
- name: eventName,
1270
- timestamp: (/* @__PURE__ */ new Date()).getTime(),
1271
- ...attributes
1272
- };
1273
- getTraceEventRecorder(logLevel, getEnvVariable("DUMBO_LOG_STYLE") ?? "RAW")(event);
938
+ const exists = async (getResult) => {
939
+ const result = await single(getResult);
940
+ return result.exists === true || result.exists === 1;
1274
941
  };
1275
- tracer.info = (eventName, attributes) => recordTraceEvent(LogLevel.INFO, eventName, attributes);
1276
- tracer.warn = (eventName, attributes) => recordTraceEvent(LogLevel.WARN, eventName, attributes);
1277
- tracer.log = (eventName, attributes) => recordTraceEvent(LogLevel.LOG, eventName, attributes);
1278
- tracer.error = (eventName, attributes) => recordTraceEvent(LogLevel.ERROR, eventName, attributes);
1279
-
1280
- //#endregion
1281
- //#region src/core/schema/sqlMigration.ts
1282
- const sqlMigration = (name, sqls) => ({
1283
- name,
1284
- sqls
1285
- });
1286
942
 
1287
943
  //#endregion
1288
- //#region src/core/schema/migrators/schemaComponentMigrator.ts
1289
- const { AutoIncrement, Varchar, Timestamp } = SQL.column.type;
1290
- const migrationTableSQL = SQL`
1291
- CREATE TABLE IF NOT EXISTS dmb_migrations (
1292
- id ${AutoIncrement({ primaryKey: true })},
1293
- name ${Varchar(255)} NOT NULL UNIQUE,
1294
- application ${Varchar(255)} NOT NULL DEFAULT 'default',
1295
- sql_hash ${Varchar(64)} NOT NULL,
1296
- timestamp ${Timestamp} NOT NULL DEFAULT CURRENT_TIMESTAMP
1297
- );
1298
- `;
1299
- const migrationTableSchemaComponent = schemaComponent("dumbo:schema-component:migrations-table", { migrations: [sqlMigration("dumbo:migrationTable:001", [migrationTableSQL])] });
1300
- const SchemaComponentMigrator = (component, dumbo) => {
1301
- const completedMigrations = [];
944
+ //#region src/core/schema/schemaComponent.ts
945
+ const schemaComponent = (key, options) => {
946
+ const componentsMap = new Map(options.components?.map((comp) => [comp.schemaComponentKey, comp]));
947
+ const migrations = [...options.migrations ?? []];
1302
948
  return {
1303
- component,
1304
- run: async (options) => {
1305
- const pendingMigrations = component.migrations.filter((m) => !completedMigrations.includes(`${component.schemaComponentKey}:${m.name}`));
1306
- if (pendingMigrations.length === 0) return;
1307
- await runSQLMigrations(dumbo, pendingMigrations, options);
1308
- completedMigrations.push(...pendingMigrations.map((m) => `${component.schemaComponentKey}:${m.name}`));
949
+ schemaComponentKey: key,
950
+ components: componentsMap,
951
+ get migrations() {
952
+ return [...migrations, ...Array.from(componentsMap.values()).flatMap((c) => c.migrations)];
953
+ },
954
+ addComponent: (component) => {
955
+ componentsMap.set(component.schemaComponentKey, component);
956
+ migrations.push(...component.migrations);
957
+ return component;
958
+ },
959
+ addMigration: (migration) => {
960
+ migrations.push(migration);
1309
961
  }
1310
962
  };
1311
963
  };
1312
-
1313
- //#endregion
1314
- //#region src/core/schema/migrators/migrator.ts
1315
- const MIGRATIONS_LOCK_ID = 999956789;
1316
- const defaultMigratorOptions = globalThis.defaultMigratorOptions = globalThis.defaultMigratorOptions ?? {};
1317
- const registerDefaultMigratorOptions = (databaseType, options) => {
1318
- defaultMigratorOptions[databaseType] = options;
964
+ const isSchemaComponentOfType = (component, prefix) => component.schemaComponentKey.startsWith(prefix);
965
+ const filterSchemaComponentsOfType = (components, prefix) => mapSchemaComponentsOfType(components, prefix);
966
+ const mapSchemaComponentsOfType = (components, prefix, keyMapper) => new Map(Array.from(components.entries()).filter(([urn]) => urn.startsWith(prefix)).map(([urn, component]) => [keyMapper ? keyMapper(component) : urn, component]));
967
+ const findSchemaComponentsOfType = (root, prefix) => {
968
+ const results = [];
969
+ const traverse = (component) => {
970
+ if (component.schemaComponentKey.startsWith(prefix)) results.push(component);
971
+ for (const child of component.components.values()) traverse(child);
972
+ };
973
+ traverse(root);
974
+ return results;
1319
975
  };
1320
- const getDefaultMigratorOptionsFromRegistry = (databaseType) => {
1321
- if (!defaultMigratorOptions[databaseType]) throw new Error(`No default migrator options registered for database type: ${databaseType}`);
1322
- return defaultMigratorOptions[databaseType];
976
+
977
+ //#endregion
978
+ //#region src/core/schema/components/columnSchemaComponent.ts
979
+ const ColumnURNType = "sc:dumbo:column";
980
+ const ColumnURN = ({ name }) => `${ColumnURNType}:${name}`;
981
+ const columnSchemaComponent = (params) => {
982
+ const { columnName, type, notNull, unique, primaryKey, default: defaultValue, ...schemaOptions } = params;
983
+ return {
984
+ ...schemaComponent(ColumnURN({ name: columnName }), schemaOptions),
985
+ columnName,
986
+ notNull,
987
+ unique,
988
+ primaryKey,
989
+ defaultValue,
990
+ sqlTokenType: "SQL_COLUMN",
991
+ name: columnName,
992
+ type
993
+ };
1323
994
  };
1324
- const runSQLMigrations = (pool, migrations, partialOptions) => pool.withTransaction(async ({ execute }) => {
1325
- const databaseType = fromDatabaseDriverType(pool.driverType).databaseType;
1326
- const defaultOptions = getDefaultMigratorOptionsFromRegistry(databaseType);
1327
- partialOptions ??= {};
1328
- const options = {
1329
- ...defaultOptions,
1330
- ...partialOptions,
1331
- schema: {
1332
- ...defaultOptions.schema,
1333
- ...partialOptions?.schema ?? {}
1334
- },
1335
- lock: {
1336
- ...defaultOptions.lock,
1337
- ...partialOptions?.lock,
1338
- options: {
1339
- lockId: MIGRATIONS_LOCK_ID,
1340
- ...defaultOptions.lock?.options,
1341
- ...partialOptions?.lock?.options
1342
- }
995
+
996
+ //#endregion
997
+ //#region src/core/schema/components/indexSchemaComponent.ts
998
+ const IndexURNType = "sc:dumbo:index";
999
+ const IndexURN = ({ name }) => `${IndexURNType}:${name}`;
1000
+ const indexSchemaComponent = ({ indexName, columnNames, isUnique, ...migrationsOrComponents }) => {
1001
+ return {
1002
+ ...schemaComponent(IndexURN({ name: indexName }), {
1003
+ migrations: migrationsOrComponents.migrations ?? [],
1004
+ components: [...migrationsOrComponents.components ?? []]
1005
+ }),
1006
+ indexName,
1007
+ get columnNames() {
1008
+ return columnNames;
1343
1009
  },
1344
- dryRun: defaultOptions.dryRun ?? partialOptions?.dryRun,
1345
- ignoreMigrationHashMismatch: defaultOptions.ignoreMigrationHashMismatch ?? partialOptions?.ignoreMigrationHashMismatch,
1346
- migrationTimeoutMs: defaultOptions.migrationTimeoutMs ?? partialOptions?.migrationTimeoutMs
1347
- };
1348
- const { databaseLock: _, ...rest } = options.lock ?? {};
1349
- const databaseLock = options.lock?.databaseLock ?? NoDatabaseLock;
1350
- const lockOptions = {
1351
- lockId: MIGRATIONS_LOCK_ID,
1352
- ...rest
1353
- };
1354
- const coreMigrations = (options.schema?.migrationTable ?? migrationTableSchemaComponent).migrations;
1355
- const result = {
1356
- applied: [],
1357
- skipped: []
1010
+ addColumn: (column) => columnNames.push(typeof column === "string" ? column : column.columnName),
1011
+ isUnique
1358
1012
  };
1359
- await databaseLock.withAcquire(execute, async () => {
1360
- for (const migration of coreMigrations) await execute.batchCommand(migration.sqls, { timeoutMs: options.migrationTimeoutMs });
1361
- for (const migration of migrations) if (await runSQLMigration(databaseType, execute, migration, {
1362
- ignoreMigrationHashMismatch: options.ignoreMigrationHashMismatch ?? false,
1363
- migrationTimeoutMs: options.migrationTimeoutMs
1364
- })) result.applied.push(migration);
1365
- else result.skipped.push(migration);
1366
- }, lockOptions);
1013
+ };
1014
+
1015
+ //#endregion
1016
+ //#region src/core/schema/components/tableSchemaComponent.ts
1017
+ const TableURNType = "sc:dumbo:table";
1018
+ const TableURN = ({ name }) => `${TableURNType}:${name}`;
1019
+ const tableSchemaComponent = ({ tableName, columns, primaryKey, relationships, ...migrationsOrComponents }) => {
1020
+ columns ??= {};
1021
+ relationships ??= {};
1022
+ const base = schemaComponent(TableURN({ name: tableName }), {
1023
+ migrations: migrationsOrComponents.migrations ?? [],
1024
+ components: [...migrationsOrComponents.components ?? [], ...Object.values(columns)]
1025
+ });
1367
1026
  return {
1368
- success: options.dryRun ? false : true,
1369
- result
1027
+ ...base,
1028
+ tableName,
1029
+ primaryKey: primaryKey ?? [],
1030
+ relationships,
1031
+ get columns() {
1032
+ const columnsMap = mapSchemaComponentsOfType(base.components, ColumnURNType, (c) => c.columnName);
1033
+ return Object.assign(columnsMap, columns);
1034
+ },
1035
+ get indexes() {
1036
+ return mapSchemaComponentsOfType(base.components, IndexURNType, (c) => c.indexName);
1037
+ },
1038
+ addColumn: (column) => base.addComponent(column),
1039
+ addIndex: (index) => base.addComponent(index)
1370
1040
  };
1371
- });
1372
- const runSQLMigration = async (databaseType, execute, migration, options) => {
1373
- const sqls = combineMigrations(migration);
1374
- const sqlHash = await getMigrationHash(migration, getFormatter(databaseType));
1375
- try {
1376
- const newMigration = {
1377
- name: migration.name,
1378
- sqlHash
1379
- };
1380
- const checkResult = await ensureMigrationWasNotAppliedYet(execute, newMigration);
1381
- if (checkResult.exists === true) {
1382
- if (checkResult.hashesMatch === true) {
1383
- tracer.info("migration-already-applied", { migrationName: migration.name });
1384
- return false;
1385
- }
1386
- if (options?.ignoreMigrationHashMismatch !== true) throw new Error(`Migration hash mismatch for "${migration.name}". Aborting migration.`);
1387
- tracer.warn("migration-hash-mismatch", {
1388
- migrationName: migration.name,
1389
- expectedHash: sqlHash,
1390
- actualHash: checkResult.hashFromDB
1391
- });
1392
- await updateMigrationHash(execute, newMigration);
1393
- return false;
1394
- }
1395
- await execute.batchCommand(sqls, { timeoutMs: options?.migrationTimeoutMs });
1396
- await recordMigration(execute, newMigration);
1397
- return true;
1398
- } catch (error) {
1399
- tracer.error("migration-error", {
1400
- migationName: migration.name,
1401
- error
1402
- });
1403
- throw error;
1404
- }
1405
- };
1406
- const getMigrationHash = async (sqlMigration, sqlFormatter) => {
1407
- const content = sqlFormatter.describe(sqlMigration.sqls, { serializer: JSONSerializer });
1408
- const data = new TextEncoder().encode(content);
1409
- const hashBuffer = await crypto.subtle.digest("SHA-256", data);
1410
- return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
1411
1041
  };
1412
- const combineMigrations = (...migration) => migration.flatMap((m) => m.sqls);
1413
- const ensureMigrationWasNotAppliedYet = async (execute, migration) => {
1414
- const result = await singleOrNull(execute.query(SQL`SELECT sql_hash as "sqlHash" FROM dmb_migrations WHERE name = ${migration.name}`));
1415
- if (result === null) return { exists: false };
1416
- const { sqlHash } = result;
1042
+
1043
+ //#endregion
1044
+ //#region src/core/schema/components/databaseSchemaSchemaComponent.ts
1045
+ const DatabaseSchemaURNType = "sc:dumbo:database_schema";
1046
+ const DatabaseSchemaURN = ({ name }) => `${DatabaseSchemaURNType}:${name}`;
1047
+ const databaseSchemaSchemaComponent = ({ schemaName, tables, ...migrationsOrComponents }) => {
1048
+ const base = schemaComponent(DatabaseSchemaURN({ name: schemaName }), {
1049
+ migrations: migrationsOrComponents.migrations ?? [],
1050
+ components: [...migrationsOrComponents.components ?? [], ...Object.values(tables ?? {})]
1051
+ });
1417
1052
  return {
1418
- exists: true,
1419
- hashesMatch: sqlHash === migration.sqlHash,
1420
- hashFromDB: sqlHash
1053
+ ...base,
1054
+ schemaName,
1055
+ get tables() {
1056
+ const tablesMap = mapSchemaComponentsOfType(base.components, TableURNType, (c) => c.tableName);
1057
+ return Object.assign(tablesMap, tables);
1058
+ },
1059
+ addTable: (table) => base.addComponent(typeof table === "string" ? tableSchemaComponent({ tableName: table }) : table)
1421
1060
  };
1422
1061
  };
1423
- const recordMigration = async (execute, migration) => {
1424
- await execute.command(SQL`
1425
- INSERT INTO dmb_migrations (name, sql_hash)
1426
- VALUES (${migration.name}, ${migration.sqlHash})`);
1427
- };
1428
- const updateMigrationHash = async (execute, migration) => {
1429
- await execute.command(SQL`
1430
- UPDATE dmb_migrations
1431
- SET sql_hash = ${migration.sqlHash}, timestamp = ${/* @__PURE__ */ new Date()}
1432
- WHERE name = ${migration.name}
1433
- `);
1062
+
1063
+ //#endregion
1064
+ //#region src/core/schema/components/databaseSchemaComponent.ts
1065
+ const DatabaseURNType = "sc:dumbo:database";
1066
+ const DatabaseURN = ({ name }) => `${DatabaseURNType}:${name}`;
1067
+ const databaseSchemaComponent = ({ databaseName, schemas, ...migrationsOrComponents }) => {
1068
+ schemas ??= {};
1069
+ const base = schemaComponent(DatabaseURN({ name: databaseName }), {
1070
+ migrations: migrationsOrComponents.migrations ?? [],
1071
+ components: [...migrationsOrComponents.components ?? [], ...Object.values(schemas)]
1072
+ });
1073
+ return {
1074
+ ...base,
1075
+ databaseName,
1076
+ get schemas() {
1077
+ const schemasMap = mapSchemaComponentsOfType(base.components, DatabaseSchemaURNType, (c) => c.schemaName);
1078
+ return Object.assign(schemasMap, schemas);
1079
+ },
1080
+ addSchema: (schema) => base.addComponent(typeof schema === "string" ? databaseSchemaSchemaComponent({ schemaName: schema }) : schema)
1081
+ };
1434
1082
  };
1435
1083
 
1436
1084
  //#endregion
1437
- //#region src/core/errors/index.ts
1438
- const isNumber = (val) => typeof val === "number" && val === val;
1439
- const isString = (val) => typeof val === "string";
1440
- var DumboError = class DumboError extends Error {
1441
- static ErrorCode = 500;
1442
- static ErrorType = "DumboError";
1443
- errorCode;
1444
- errorType;
1445
- innerError;
1446
- constructor(options) {
1447
- const errorCode = options && typeof options === "object" && "errorCode" in options ? options.errorCode : isNumber(options) ? options : DumboError.ErrorCode;
1448
- const errorType = options && typeof options === "object" && "errorType" in options ? options.errorType ?? DumboError.ErrorType : DumboError.ErrorType;
1449
- const message = options && typeof options === "object" && "message" in options ? options.message : isString(options) ? options : `Error with status code '${errorCode}' ocurred during DumboError processing`;
1450
- const innerError = options && typeof options === "object" && "innerError" in options ? options.innerError : void 0;
1451
- super(message, { cause: innerError });
1452
- this.errorCode = errorCode;
1453
- this.errorType = errorType;
1454
- this.innerError = innerError;
1455
- Object.setPrototypeOf(this, DumboError.prototype);
1456
- }
1457
- static isInstanceOf(error, options) {
1458
- if (typeof error !== "object" || error === null || !("errorCode" in error) || !isNumber(error.errorCode) || !("errorType" in error) || !isString(error.errorType)) return false;
1459
- if (!options) return true;
1460
- if (options.errorCode !== void 0 && error.errorCode !== options.errorCode) return false;
1461
- if (options.errorType !== void 0 && error.errorType !== options.errorType) return false;
1462
- return true;
1085
+ //#region src/core/schema/components/index.ts
1086
+ const schemaComponentURN = {
1087
+ database: DatabaseURN,
1088
+ schema: DatabaseSchemaURN,
1089
+ table: TableURN,
1090
+ column: ColumnURN,
1091
+ index: IndexURN,
1092
+ extractName: (urn) => {
1093
+ const parts = urn.split(":");
1094
+ return parts[parts.length - 1] || "";
1463
1095
  }
1464
1096
  };
1465
- var ConcurrencyError = class ConcurrencyError extends DumboError {
1466
- static ErrorCode = 412;
1467
- static ErrorType = "ConcurrencyError";
1468
- constructor(message, innerError) {
1469
- super({
1470
- errorCode: ConcurrencyError.ErrorCode,
1471
- errorType: ConcurrencyError.ErrorType,
1472
- message: message ?? `Expected document state does not match current one!`,
1473
- innerError
1474
- });
1475
- Object.setPrototypeOf(this, ConcurrencyError.prototype);
1476
- }
1097
+
1098
+ //#endregion
1099
+ //#region src/core/schema/databaseMetadata/databaseMetadata.ts
1100
+ const DumboDatabaseMetadataRegistry = () => {
1101
+ const infos = /* @__PURE__ */ new Map();
1102
+ const register = (databaseType, info) => {
1103
+ const entry = infos.get(databaseType);
1104
+ if (entry && (typeof entry !== "function" || typeof info === "function")) return;
1105
+ infos.set(databaseType, info);
1106
+ };
1107
+ const tryResolve = async (databaseType) => {
1108
+ const entry = infos.get(databaseType);
1109
+ if (!entry) return null;
1110
+ if (typeof entry !== "function") return entry;
1111
+ const resolved = await entry();
1112
+ register(databaseType, resolved);
1113
+ return resolved;
1114
+ };
1115
+ const tryGet = (databaseType) => {
1116
+ const entry = infos.get(databaseType);
1117
+ return entry && typeof entry !== "function" ? entry : null;
1118
+ };
1119
+ const has = (databaseType) => infos.has(databaseType);
1120
+ return {
1121
+ register,
1122
+ tryResolve,
1123
+ tryGet,
1124
+ has,
1125
+ get databaseTypes() {
1126
+ return Array.from(infos.keys());
1127
+ }
1128
+ };
1477
1129
  };
1478
- var TransientDatabaseError = class TransientDatabaseError extends DumboError {
1479
- static ErrorCode = 503;
1480
- static ErrorType = "TransientDatabaseError";
1481
- constructor(message, innerError) {
1482
- super({
1483
- errorCode: TransientDatabaseError.ErrorCode,
1484
- errorType: TransientDatabaseError.ErrorType,
1485
- message: message ?? `A transient error occurred during database operation. Retrying the operation might succeed.`,
1486
- innerError
1487
- });
1488
- Object.setPrototypeOf(this, TransientDatabaseError.prototype);
1489
- }
1130
+ const dumboDatabaseMetadataRegistry = globalThis.dumboDatabaseMetadataRegistry = globalThis.dumboDatabaseMetadataRegistry ?? DumboDatabaseMetadataRegistry();
1131
+ const getDatabaseMetadata = (driverType) => {
1132
+ const { databaseType } = fromDatabaseDriverType(driverType);
1133
+ return dumboDatabaseMetadataRegistry.tryGet(databaseType);
1490
1134
  };
1491
- var ConnectionError = class ConnectionError extends TransientDatabaseError {
1492
- static ErrorCode = 503;
1493
- static ErrorType = "ConnectionError";
1494
- constructor(message, innerError) {
1495
- super(message ?? `A connection error occurred during database operation.`, innerError);
1496
- this.errorType = ConnectionError.ErrorType;
1497
- Object.setPrototypeOf(this, ConnectionError.prototype);
1498
- }
1135
+ const resolveDatabaseMetadata = async (driverType, driverOverride) => {
1136
+ if (driverOverride) return driverOverride;
1137
+ const { databaseType } = fromDatabaseDriverType(driverType);
1138
+ return dumboDatabaseMetadataRegistry.tryResolve(databaseType);
1499
1139
  };
1500
- var SerializationError = class SerializationError extends TransientDatabaseError {
1501
- static ErrorCode = 503;
1502
- static ErrorType = "SerializationError";
1503
- constructor(message, innerError) {
1504
- super(message ?? `A serialization failure occurred. The transaction can be retried.`, innerError);
1505
- this.errorType = SerializationError.ErrorType;
1506
- Object.setPrototypeOf(this, SerializationError.prototype);
1507
- }
1140
+ const getDefaultDatabase = (driverType) => {
1141
+ return getDatabaseMetadata(driverType)?.defaultDatabaseName;
1508
1142
  };
1509
- var DeadlockError = class DeadlockError extends TransientDatabaseError {
1510
- static ErrorCode = 503;
1511
- static ErrorType = "DeadlockError";
1512
- constructor(message, innerError) {
1513
- super(message ?? `A deadlock was detected. The transaction can be retried.`, innerError);
1514
- this.errorType = DeadlockError.ErrorType;
1515
- Object.setPrototypeOf(this, DeadlockError.prototype);
1516
- }
1143
+ const getDefaultDatabaseAsync = async (driverType) => {
1144
+ return (await resolveDatabaseMetadata(driverType))?.defaultDatabaseName;
1517
1145
  };
1518
- var LockNotAvailableError = class LockNotAvailableError extends TransientDatabaseError {
1519
- static ErrorCode = 503;
1520
- static ErrorType = "LockNotAvailableError";
1521
- constructor(message, innerError) {
1522
- super(message ?? `The requested lock is not available.`, innerError);
1523
- this.errorType = LockNotAvailableError.ErrorType;
1524
- Object.setPrototypeOf(this, LockNotAvailableError.prototype);
1525
- }
1146
+
1147
+ //#endregion
1148
+ //#region src/core/schema/dumboSchema/dumboSchema.ts
1149
+ const DEFAULT_DATABASE_NAME = "__default_database__";
1150
+ const DEFAULT_DATABASE_SCHEMA_NAME = "__default_database_schema__";
1151
+ const dumboColumn = (name, type, options) => columnSchemaComponent({
1152
+ columnName: name,
1153
+ type,
1154
+ ...options
1155
+ });
1156
+ const dumboIndex = (name, columnNames, options) => indexSchemaComponent({
1157
+ indexName: name,
1158
+ columnNames,
1159
+ isUnique: options?.unique ?? false,
1160
+ ...options
1161
+ });
1162
+ const dumboTable = (name, definition) => {
1163
+ const { columns, indexes, primaryKey, relationships, ...options } = definition;
1164
+ const components = [...indexes ? Object.values(indexes) : []];
1165
+ return tableSchemaComponent({
1166
+ tableName: name,
1167
+ columns: columns ?? {},
1168
+ primaryKey: primaryKey ?? [],
1169
+ ...relationships !== void 0 ? { relationships } : {},
1170
+ components,
1171
+ ...options
1172
+ });
1526
1173
  };
1527
- var InsufficientResourcesError = class InsufficientResourcesError extends TransientDatabaseError {
1528
- static ErrorCode = 503;
1529
- static ErrorType = "InsufficientResourcesError";
1530
- constructor(message, innerError) {
1531
- super(message ?? `Insufficient resources to complete the database operation (e.g. disk full, out of memory, too many connections).`, innerError);
1532
- this.errorType = InsufficientResourcesError.ErrorType;
1533
- Object.setPrototypeOf(this, InsufficientResourcesError.prototype);
1534
- }
1174
+ function dumboDatabaseSchema(nameOrTables, tables, options) {
1175
+ return databaseSchemaSchemaComponent({
1176
+ schemaName: typeof nameOrTables === "string" ? nameOrTables : DEFAULT_DATABASE_SCHEMA_NAME,
1177
+ tables: (typeof nameOrTables === "string" ? tables : nameOrTables) ?? {},
1178
+ ...options
1179
+ });
1180
+ }
1181
+ dumboDatabaseSchema.from = (schemaName, tableNames) => {
1182
+ const tables = tableNames.reduce((acc, tableName) => {
1183
+ acc[tableName] = dumboTable(tableName, {});
1184
+ return acc;
1185
+ }, {});
1186
+ return schemaName ? dumboDatabaseSchema(schemaName, tables) : dumboDatabaseSchema(tables);
1535
1187
  };
1536
- var SystemError = class SystemError extends TransientDatabaseError {
1537
- static ErrorCode = 503;
1538
- static ErrorType = "SystemError";
1539
- constructor(message, innerError) {
1540
- super(message ?? `A system-level error occurred (e.g. I/O error).`, innerError);
1541
- this.errorType = SystemError.ErrorType;
1542
- Object.setPrototypeOf(this, SystemError.prototype);
1543
- }
1188
+ function dumboDatabase(nameOrSchemas, schemasOrOptions, options) {
1189
+ const databaseName = typeof nameOrSchemas === "string" ? nameOrSchemas : DEFAULT_DATABASE_NAME;
1190
+ const schemasOrSchema = typeof nameOrSchemas === "string" ? schemasOrOptions ?? {} : nameOrSchemas;
1191
+ return databaseSchemaComponent({
1192
+ databaseName,
1193
+ schemas: "schemaComponentKey" in schemasOrSchema && isSchemaComponentOfType(schemasOrSchema, "sc:dumbo:database_schema") ? { [DEFAULT_DATABASE_SCHEMA_NAME]: schemasOrSchema } : schemasOrSchema,
1194
+ ...typeof nameOrSchemas === "string" ? options : schemasOrOptions
1195
+ });
1196
+ }
1197
+ dumboDatabase.from = (databaseName, schemaNames) => {
1198
+ const schemas = schemaNames.reduce((acc, schemaName) => {
1199
+ acc[schemaName] = dumboDatabaseSchema(schemaName, {});
1200
+ return acc;
1201
+ }, {});
1202
+ return databaseName ? dumboDatabase(databaseName, schemas) : dumboDatabase(schemas);
1544
1203
  };
1545
- var AdminShutdownError = class AdminShutdownError extends TransientDatabaseError {
1546
- static ErrorCode = 503;
1547
- static ErrorType = "AdminShutdownError";
1548
- constructor(message, innerError) {
1549
- super(message ?? `The database server is shutting down or restarting.`, innerError);
1550
- this.errorType = AdminShutdownError.ErrorType;
1551
- Object.setPrototypeOf(this, AdminShutdownError.prototype);
1552
- }
1204
+ dumboDatabase.defaultName = DEFAULT_DATABASE_NAME;
1205
+ dumboDatabaseSchema.defaultName = DEFAULT_DATABASE_SCHEMA_NAME;
1206
+ const dumboSchema = {
1207
+ database: dumboDatabase,
1208
+ schema: dumboDatabaseSchema,
1209
+ table: dumboTable,
1210
+ column: dumboColumn,
1211
+ index: dumboIndex
1553
1212
  };
1554
- var QueryCanceledError = class QueryCanceledError extends TransientDatabaseError {
1555
- static ErrorCode = 503;
1556
- static ErrorType = "QueryCanceledError";
1557
- constructor(message, innerError) {
1558
- super(message ?? `The query was canceled, e.g. due to statement timeout or user request.`, innerError);
1559
- this.errorType = QueryCanceledError.ErrorType;
1560
- Object.setPrototypeOf(this, QueryCanceledError.prototype);
1561
- }
1213
+
1214
+ //#endregion
1215
+ //#region src/core/serializer/json/index.ts
1216
+ const bigIntReplacer = (_key, value) => {
1217
+ return typeof value === "bigint" ? value.toString() : value;
1562
1218
  };
1563
- var IntegrityConstraintViolationError = class IntegrityConstraintViolationError extends DumboError {
1564
- static ErrorCode = 409;
1565
- static ErrorType = "IntegrityConstraintViolationError";
1566
- constructor(message, innerError) {
1567
- super({
1568
- errorCode: IntegrityConstraintViolationError.ErrorCode,
1569
- errorType: IntegrityConstraintViolationError.ErrorType,
1570
- message: message ?? `An integrity constraint violation occurred!`,
1571
- innerError
1572
- });
1573
- Object.setPrototypeOf(this, IntegrityConstraintViolationError.prototype);
1574
- }
1219
+ const dateReplacer = (_key, value) => {
1220
+ return value instanceof Date ? value.toISOString() : value;
1575
1221
  };
1576
- var UniqueConstraintError = class UniqueConstraintError extends IntegrityConstraintViolationError {
1577
- static ErrorCode = 409;
1578
- static ErrorType = "UniqueConstraintError";
1579
- constructor(message, innerError) {
1580
- super(message ?? `Unique constraint violation occurred!`, innerError);
1581
- this.errorType = UniqueConstraintError.ErrorType;
1582
- Object.setPrototypeOf(this, UniqueConstraintError.prototype);
1583
- }
1222
+ const isFirstLetterNumeric = (str) => {
1223
+ const c = str.charCodeAt(0);
1224
+ return c >= 48 && c <= 57;
1584
1225
  };
1585
- var ForeignKeyViolationError = class ForeignKeyViolationError extends IntegrityConstraintViolationError {
1586
- static ErrorCode = 409;
1587
- static ErrorType = "ForeignKeyViolationError";
1588
- constructor(message, innerError) {
1589
- super(message ?? `Foreign key constraint violation occurred!`, innerError);
1590
- this.errorType = ForeignKeyViolationError.ErrorType;
1591
- Object.setPrototypeOf(this, ForeignKeyViolationError.prototype);
1592
- }
1226
+ const isFirstLetterNumericOrMinus = (str) => {
1227
+ const c = str.charCodeAt(0);
1228
+ return c >= 48 && c <= 57 || c === 45;
1593
1229
  };
1594
- var NotNullViolationError = class NotNullViolationError extends IntegrityConstraintViolationError {
1595
- static ErrorCode = 409;
1596
- static ErrorType = "NotNullViolationError";
1597
- constructor(message, innerError) {
1598
- super(message ?? `NOT NULL constraint violation occurred!`, innerError);
1599
- this.errorType = NotNullViolationError.ErrorType;
1600
- Object.setPrototypeOf(this, NotNullViolationError.prototype);
1230
+ const bigIntReviver = (_key, value, context) => {
1231
+ if (typeof value === "number" && Number.isInteger(value) && !Number.isSafeInteger(value)) try {
1232
+ return BigInt(context?.source ?? value.toString());
1233
+ } catch {
1234
+ return value;
1235
+ }
1236
+ if (typeof value === "string" && value.length > 15) {
1237
+ if (isFirstLetterNumericOrMinus(value)) {
1238
+ const num = Number(value);
1239
+ if (Number.isFinite(num) && !Number.isSafeInteger(num)) try {
1240
+ return BigInt(value);
1241
+ } catch {}
1242
+ }
1601
1243
  }
1244
+ return value;
1602
1245
  };
1603
- var CheckViolationError = class CheckViolationError extends IntegrityConstraintViolationError {
1604
- static ErrorCode = 409;
1605
- static ErrorType = "CheckViolationError";
1606
- constructor(message, innerError) {
1607
- super(message ?? `CHECK constraint violation occurred!`, innerError);
1608
- this.errorType = CheckViolationError.ErrorType;
1609
- Object.setPrototypeOf(this, CheckViolationError.prototype);
1610
- }
1611
- };
1612
- var ExclusionViolationError = class ExclusionViolationError extends IntegrityConstraintViolationError {
1613
- static ErrorCode = 409;
1614
- static ErrorType = "ExclusionViolationError";
1615
- constructor(message, innerError) {
1616
- super(message ?? `Exclusion constraint violation occurred!`, innerError);
1617
- this.errorType = ExclusionViolationError.ErrorType;
1618
- Object.setPrototypeOf(this, ExclusionViolationError.prototype);
1619
- }
1620
- };
1621
- var DataError = class DataError extends DumboError {
1622
- static ErrorCode = 400;
1623
- static ErrorType = "DataError";
1624
- constructor(message, innerError) {
1625
- super({
1626
- errorCode: DataError.ErrorCode,
1627
- errorType: DataError.ErrorType,
1628
- message: message ?? `A data error occurred (e.g. invalid value, type mismatch).`,
1629
- innerError
1630
- });
1631
- Object.setPrototypeOf(this, DataError.prototype);
1246
+ const dateReviver = (_key, value) => {
1247
+ if (typeof value === "string" && value.length === 24 && isFirstLetterNumeric(value) && value[10] === "T" && value[23] === "Z") {
1248
+ const date = new Date(value);
1249
+ if (!isNaN(date.getTime())) return date;
1632
1250
  }
1251
+ return value;
1633
1252
  };
1634
- var InvalidOperationError = class InvalidOperationError extends DumboError {
1635
- static ErrorCode = 400;
1636
- static ErrorType = "InvalidOperationError";
1637
- constructor(message, innerError) {
1638
- super({
1639
- errorCode: InvalidOperationError.ErrorCode,
1640
- errorType: InvalidOperationError.ErrorType,
1641
- message: message ?? `Invalid operation (e.g. syntax error, insufficient privileges, undefined table).`,
1642
- innerError
1643
- });
1644
- Object.setPrototypeOf(this, InvalidOperationError.prototype);
1645
- }
1253
+ const composeJSONReplacers = (...replacers) => {
1254
+ const filteredReplacers = replacers.filter((r) => r !== void 0);
1255
+ if (filteredReplacers.length === 0) return void 0;
1256
+ return (key, value) => filteredReplacers.reduce((accValue, replacer) => replacer(key, accValue), value);
1646
1257
  };
1647
-
1648
- //#endregion
1649
- //#region src/core/execute/execute.ts
1650
- const mapColumnToJSON = (column, serializer, options) => ({ [column]: (value) => {
1651
- if (typeof value === "string") try {
1652
- return serializer.deserialize(value, options);
1653
- } catch {}
1654
- return value;
1655
- } });
1656
- const mapColumnToBigint = (column) => ({ [column]: (value) => {
1657
- if (typeof value === "number" || typeof value === "string") return BigInt(value);
1658
- return value;
1659
- } });
1660
- const mapColumnToDate = (column) => ({ [column]: (value) => {
1661
- if (typeof value === "number" || typeof value === "string") return new Date(value);
1662
- return value;
1663
- } });
1664
- const mapSQLQueryResult = (result, mapping) => {
1665
- if (typeof result !== "object" || result === null) return result;
1666
- const mappedResult = { ...result };
1667
- for (const column of Object.keys(mapping)) if (column in mappedResult) mappedResult[column] = mapping[column](mappedResult[column]);
1668
- return mappedResult;
1258
+ const composeJSONRevivers = (...revivers) => {
1259
+ const filteredRevivers = revivers.filter((r) => r !== void 0);
1260
+ if (filteredRevivers.length === 0) return void 0;
1261
+ return (key, value, context) => filteredRevivers.reduce((accValue, reviver) => reviver(key, accValue, context), value);
1669
1262
  };
1670
- var BatchCommandNoChangesError = class BatchCommandNoChangesError extends DumboError {
1671
- statementIndex;
1672
- constructor(statementIndex) {
1673
- super({
1674
- errorCode: 409,
1675
- message: `Batch command at index ${statementIndex} affected no rows`
1676
- });
1677
- this.name = "BatchCommandNoChangesError";
1678
- this.statementIndex = statementIndex;
1679
- Object.setPrototypeOf(this, BatchCommandNoChangesError.prototype);
1680
- }
1263
+ const JSONReplacer = (opts) => composeJSONReplacers(opts?.replacer, opts?.failOnBigIntSerialization !== true ? JSONReplacers.bigInt : void 0, opts?.useDefaultDateSerialization !== true ? JSONReplacers.date : void 0);
1264
+ const JSONReviver = (opts) => composeJSONRevivers(opts?.reviver, opts?.parseBigInts === true ? JSONRevivers.bigInt : void 0, opts?.parseDates === true ? JSONRevivers.date : void 0);
1265
+ const JSONReplacers = {
1266
+ bigInt: bigIntReplacer,
1267
+ date: dateReplacer
1681
1268
  };
1682
- const sqlExecutor = (sqlExecutor, options) => ({
1683
- query: (sql, queryOptions) => executeInNewDbClient((client) => sqlExecutor.query(client, sql, queryOptions), options),
1684
- batchQuery: (sqls, queryOptions) => executeInNewDbClient((client) => sqlExecutor.batchQuery(client, sqls, queryOptions), options),
1685
- command: (sql, commandOptions) => executeInNewDbClient((client) => sqlExecutor.command(client, sql, commandOptions), options),
1686
- batchCommand: (sqls, commandOptions) => executeInNewDbClient((client) => sqlExecutor.batchCommand(client, sqls, commandOptions), options)
1687
- });
1688
- const sqlExecutorInNewConnection = (options) => ({
1689
- query: (sql, queryOptions) => executeInNewConnection((connection) => connection.execute.query(sql, queryOptions), options),
1690
- batchQuery: (sqls, queryOptions) => executeInNewConnection((connection) => connection.execute.batchQuery(sqls, queryOptions), options),
1691
- command: (sql, commandOptions) => executeInNewConnection((connection) => connection.execute.command(sql, commandOptions), options),
1692
- batchCommand: (sqls, commandOptions) => executeInNewConnection((connection) => connection.execute.batchCommand(sqls, commandOptions), options)
1693
- });
1694
- const sqlExecutorInAmbientConnection = (options) => ({
1695
- query: (sql, queryOptions) => executeInAmbientConnection((connection) => connection.execute.query(sql, queryOptions), options),
1696
- batchQuery: (sqls, queryOptions) => executeInAmbientConnection((connection) => connection.execute.batchQuery(sqls, queryOptions), options),
1697
- command: (sql, commandOptions) => executeInAmbientConnection((connection) => connection.execute.command(sql, commandOptions), options),
1698
- batchCommand: (sqls, commandOptions) => executeInAmbientConnection((connection) => connection.execute.batchCommand(sqls, commandOptions), options)
1699
- });
1700
- const executeInNewDbClient = async (handle, options) => {
1701
- const { connect, close } = options;
1702
- const client = await connect();
1703
- try {
1704
- return await handle(client);
1705
- } catch (error) {
1706
- if (close) await close(client, error);
1707
- throw error;
1708
- }
1269
+ const JSONRevivers = {
1270
+ bigInt: bigIntReviver,
1271
+ date: dateReviver
1709
1272
  };
1710
- const executeInNewConnection = async (handle, options) => {
1711
- const connection = await options.connection();
1712
- try {
1713
- return await handle(connection);
1714
- } finally {
1715
- await connection.close();
1716
- }
1273
+ const jsonSerializer = (options) => {
1274
+ const defaultReplacer = JSONReplacer(options);
1275
+ const defaultReviver = JSONReviver(options);
1276
+ return {
1277
+ serialize: (object, serializerOptions) => JSON.stringify(object, serializerOptions ? JSONReplacer(serializerOptions) : defaultReplacer),
1278
+ deserialize: (payload, deserializerOptions) => JSON.parse(payload, deserializerOptions ? JSONReviver(deserializerOptions) : defaultReviver)
1279
+ };
1717
1280
  };
1718
- const executeInAmbientConnection = async (handle, options) => {
1719
- const connection = await options.connection();
1720
- try {
1721
- return await handle(connection);
1722
- } finally {}
1281
+ const JSONSerializer = Object.assign(jsonSerializer(), { from: (options) => options?.serialization?.serializer ?? (options?.serialization?.options ? jsonSerializer(options?.serialization?.options) : JSONSerializer) });
1282
+ const JSONCodec = (options) => {
1283
+ const serializer = "serializer" in options ? options.serializer : jsonSerializer("serializerOptions" in options ? options.serializerOptions : void 0);
1284
+ const upcast = options.upcast ?? ((doc) => doc);
1285
+ const downcast = options.downcast ?? ((doc) => doc);
1286
+ return {
1287
+ decode: (payload, decodeOptions) => {
1288
+ return upcast(decodeOptions ? serializer.deserialize(payload, decodeOptions) : serializer.deserialize(payload));
1289
+ },
1290
+ encode: (object, encodeOptions) => {
1291
+ const downcasted = downcast(object);
1292
+ return encodeOptions ? serializer.serialize(downcasted, encodeOptions) : serializer.serialize(downcasted);
1293
+ }
1294
+ };
1723
1295
  };
1724
1296
 
1725
1297
  //#endregion
1726
- //#region src/core/connections/transaction.ts
1727
- const toTransactionResult = (transactionResult) => transactionResult !== void 0 && transactionResult !== null && typeof transactionResult === "object" && "success" in transactionResult ? transactionResult : {
1728
- success: true,
1729
- result: transactionResult
1730
- };
1731
- const executeInTransaction = async (transaction, handle) => {
1732
- await transaction.begin();
1733
- try {
1734
- const { success, result } = toTransactionResult(await handle(transaction));
1735
- if (success) await transaction.commit();
1736
- else await transaction.rollback();
1737
- return result;
1738
- } catch (e) {
1739
- await transaction.rollback();
1740
- throw e;
1741
- }
1742
- };
1743
- const transactionFactoryWithDbClient = (connect, initTransaction) => {
1744
- let currentTransaction = void 0;
1745
- const getOrInitCurrentTransaction = (options) => currentTransaction ?? (currentTransaction = initTransaction(connect(), {
1746
- close: () => {
1747
- currentTransaction = void 0;
1748
- return Promise.resolve();
1749
- },
1750
- ...options ?? {}
1751
- }));
1298
+ //#region src/core/sql/parametrizedSQL/parametrizedSQL.ts
1299
+ const ParametrizedSQLBuilder = ({ mapParamPlaceholder }) => {
1300
+ const sql = [];
1301
+ const params = [];
1752
1302
  return {
1753
- transaction: getOrInitCurrentTransaction,
1754
- withTransaction: (handle, options) => executeInTransaction(getOrInitCurrentTransaction(options), handle)
1303
+ addSQL(str) {
1304
+ sql.push(str);
1305
+ return this;
1306
+ },
1307
+ addParam(value) {
1308
+ sql.push(mapParamPlaceholder(params.length, value));
1309
+ params.push(value);
1310
+ return this;
1311
+ },
1312
+ addParams(values) {
1313
+ const placeholders = values.map((value, i) => mapParamPlaceholder(params.length + i, value));
1314
+ this.addSQL(`${placeholders.join(", ")}`);
1315
+ params.push(...values);
1316
+ return this;
1317
+ },
1318
+ build() {
1319
+ return {
1320
+ query: sql.join(""),
1321
+ params
1322
+ };
1323
+ }
1755
1324
  };
1756
1325
  };
1757
- const wrapInConnectionClosure = async (connection, handle) => {
1758
- try {
1759
- return await handle();
1760
- } finally {
1761
- await connection.close();
1762
- }
1763
- };
1764
- const transactionFactoryWithNewConnection = (connect) => ({
1765
- transaction: (options) => {
1766
- const connection = connect();
1767
- const transaction = connection.transaction(options);
1326
+
1327
+ //#endregion
1328
+ //#region src/core/sql/tokens/sqlToken.ts
1329
+ const SQLToken = (sqlTokenType, map) => {
1330
+ const factory = (input) => {
1331
+ let props;
1332
+ if (map !== void 0) props = map(input);
1333
+ else if (input === void 0 || input === null) props = {};
1334
+ else if (typeof input === "object" && !Array.isArray(input)) props = input;
1335
+ else throw new Error(`Cannot create SQLToken of type ${sqlTokenType} with input: ${input}`);
1768
1336
  return {
1769
- ...transaction,
1770
- commit: () => wrapInConnectionClosure(connection, () => transaction.commit()),
1771
- rollback: () => wrapInConnectionClosure(connection, () => transaction.rollback())
1337
+ sqlTokenType,
1338
+ [sqlTokenType]: true,
1339
+ ...props
1772
1340
  };
1773
- },
1774
- withTransaction: (handle, options) => {
1775
- const connection = connect();
1776
- const withTx = connection.withTransaction;
1777
- return wrapInConnectionClosure(connection, () => withTx(handle, options));
1778
- }
1341
+ };
1342
+ const check = (token) => SQLToken.check(token) && token.sqlTokenType === sqlTokenType;
1343
+ return {
1344
+ from: factory,
1345
+ check,
1346
+ type: sqlTokenType
1347
+ };
1348
+ };
1349
+ SQLToken.check = (token) => token !== null && typeof token === "object" && "sqlTokenType" in token;
1350
+ const SQLIdentifier = SQLToken("SQL_IDENTIFIER", (value) => ({ value }));
1351
+ const SQLPlain = SQLToken("SQL_RAW", (value) => ({ value }));
1352
+ const SQLLiteral = SQLToken("SQL_LITERAL", (value) => ({ value }));
1353
+ const SQLArray = SQLToken("SQL_ARRAY", (input) => {
1354
+ if (Array.isArray(input)) return { value: input };
1355
+ return input.mode !== void 0 ? {
1356
+ value: input.value,
1357
+ mode: input.mode
1358
+ } : { value: input.value };
1779
1359
  });
1780
- const transactionFactoryWithAmbientConnection = (connect) => ({
1781
- transaction: (options) => {
1782
- const transaction = connect().transaction(options);
1360
+ const SQLIn = SQLToken("SQL_IN", ({ column, values, mode }) => mode !== void 0 ? {
1361
+ column: SQLIdentifier.from(column),
1362
+ values: SQLArray.from(values),
1363
+ mode
1364
+ } : {
1365
+ column: SQLIdentifier.from(column),
1366
+ values: SQLArray.from(values)
1367
+ });
1368
+
1369
+ //#endregion
1370
+ //#region src/core/sql/tokens/columnTokens.ts
1371
+ const ColumnTypeToken = (sqlTokenType, jsTypeName, map) => {
1372
+ const factory = (input) => {
1373
+ let props;
1374
+ if (map !== void 0) props = map(input);
1375
+ else if (input === void 0 || input === null) props = {};
1376
+ else if (typeof input === "object" && !Array.isArray(input)) props = input;
1377
+ else throw new Error(`Cannot create SQLToken of type ${sqlTokenType} with input: ${input}`);
1783
1378
  return {
1784
- ...transaction,
1785
- commit: () => transaction.commit(),
1786
- rollback: () => transaction.rollback()
1379
+ sqlTokenType,
1380
+ [sqlTokenType]: true,
1381
+ jsTypeName,
1382
+ ...props
1383
+ };
1384
+ };
1385
+ const check = (token) => SQLToken.check(token) && token.sqlTokenType === sqlTokenType;
1386
+ return {
1387
+ from: factory,
1388
+ check,
1389
+ type: sqlTokenType
1390
+ };
1391
+ };
1392
+ const SerialToken = ColumnTypeToken("SQL_COLUMN_SERIAL", "value_type:js:number");
1393
+ const BigSerialToken = ColumnTypeToken("SQL_COLUMN_BIGSERIAL", "value_type:js:bigint");
1394
+ const IntegerToken = ColumnTypeToken("SQL_COLUMN_INTEGER", "value_type:js:number");
1395
+ const BigIntegerToken = ColumnTypeToken("SQL_COLUMN_BIGINT", "value_type:js:bigint");
1396
+ const JSONBToken = {
1397
+ type: "SQL_COLUMN_JSONB",
1398
+ from: () => {
1399
+ return {
1400
+ sqlTokenType: "SQL_COLUMN_JSONB",
1401
+ ["SQL_COLUMN_JSONB"]: true
1787
1402
  };
1788
1403
  },
1789
- withTransaction: (handle, options) => {
1790
- const withTx = connect().withTransaction;
1791
- return withTx(handle, options);
1404
+ check: (token) => SQLToken.check(token) && token.sqlTokenType === "SQL_COLUMN_JSONB"
1405
+ };
1406
+ const TimestampToken = ColumnTypeToken("SQL_COLUMN_TIMESTAMP", "value_type:js:date");
1407
+ const TimestamptzToken = ColumnTypeToken("SQL_COLUMN_TIMESTAMPTZ", "value_type:js:date");
1408
+ const VarcharToken = ColumnTypeToken("SQL_COLUMN_VARCHAR", "value_type:js:string", (length) => ({
1409
+ length: length ?? "max",
1410
+ jsTypeName: "value_type:js:string"
1411
+ }));
1412
+ const AutoIncrementSQLColumnToken = ColumnTypeToken("SQL_COLUMN_AUTO_INCREMENT", "value_type:js:bigint");
1413
+ const SQLColumnTypeTokens = {
1414
+ AutoIncrement: AutoIncrementSQLColumnToken,
1415
+ BigInteger: BigIntegerToken,
1416
+ BigSerial: BigSerialToken,
1417
+ Integer: IntegerToken,
1418
+ JSONB: JSONBToken,
1419
+ Serial: SerialToken,
1420
+ Timestamp: TimestampToken,
1421
+ Timestamptz: TimestamptzToken,
1422
+ Varchar: VarcharToken
1423
+ };
1424
+ const SQLColumnTypeTokensFactory = {
1425
+ AutoIncrement: AutoIncrementSQLColumnToken.from,
1426
+ BigInteger: BigIntegerToken.from(),
1427
+ BigSerial: BigSerialToken.from(),
1428
+ Integer: IntegerToken.from(),
1429
+ JSONB: JSONBToken.from,
1430
+ Serial: SerialToken.from(),
1431
+ Timestamp: TimestampToken.from(),
1432
+ Timestamptz: TimestamptzToken.from(),
1433
+ Varchar: VarcharToken.from
1434
+ };
1435
+ const SQLColumnToken = SQLToken("SQL_COLUMN");
1436
+
1437
+ //#endregion
1438
+ //#region src/core/sql/processors/sqlProcessor.ts
1439
+ const SQLProcessor = (options) => options;
1440
+
1441
+ //#endregion
1442
+ //#region src/core/sql/processors/defaultProcessors.ts
1443
+ const ExpandArrayProcessor = SQLProcessor({
1444
+ canHandle: "SQL_ARRAY",
1445
+ handle: (token, { builder, serializer, mapper }) => {
1446
+ if (token.value.length === 0) throw new Error("Empty arrays are not supported. If you're using it with SELECT IN statement Use SQL.in(column, array) helper instead.");
1447
+ builder.addParams(mapper.mapValue(token.value, serializer));
1792
1448
  }
1793
1449
  });
1794
- const transactionFactoryWithAsyncAmbientConnection = (driverType, connect, close) => {
1795
- close ??= () => Promise.resolve();
1450
+ const ExpandSQLInProcessor = SQLProcessor({
1451
+ canHandle: "SQL_IN",
1452
+ handle: (token, context) => {
1453
+ const { builder, mapper, processorsRegistry, serializer } = context;
1454
+ const { values: inValues, column } = token;
1455
+ if (inValues.value.length === 0) {
1456
+ builder.addParam(mapper.mapValue(false, serializer));
1457
+ return;
1458
+ }
1459
+ builder.addSQL(mapper.mapValue(column.value, serializer));
1460
+ builder.addSQL(` IN (`);
1461
+ const arrayProcessor = processorsRegistry.get(SQLArray.type);
1462
+ if (!arrayProcessor) throw new Error("No sql processor registered for an array. Cannot expand IN statement");
1463
+ arrayProcessor.handle(inValues, {
1464
+ builder,
1465
+ mapper,
1466
+ processorsRegistry,
1467
+ serializer
1468
+ });
1469
+ builder.addSQL(`)`);
1470
+ }
1471
+ });
1472
+ const FormatIdentifierProcessor = SQLProcessor({
1473
+ canHandle: "SQL_IDENTIFIER",
1474
+ handle: (token, { builder, mapper, serializer }) => {
1475
+ builder.addSQL(mapper.mapValue(token, serializer));
1476
+ }
1477
+ });
1478
+ const MapLiteralProcessor = SQLProcessor({
1479
+ canHandle: "SQL_LITERAL",
1480
+ handle: (token, { builder, mapper, serializer }) => builder.addParam(mapper.mapValue(token.value, serializer))
1481
+ });
1482
+
1483
+ //#endregion
1484
+ //#region src/core/sql/processors/sqlProcessorRegistry.ts
1485
+ const SQLProcessorsRegistry = (options) => {
1486
+ const processors = options ? new Map(options.from.all()) : /* @__PURE__ */ new Map();
1487
+ function register(...args) {
1488
+ if (args.length === 1 && typeof args[0] === "object" && !Array.isArray(args[0])) Object.entries(args[0]).forEach(([_, processor]) => {
1489
+ processors.set(processor.canHandle, processor);
1490
+ });
1491
+ else args.forEach((p) => processors.set(p.canHandle, p));
1492
+ return registry;
1493
+ }
1494
+ const registry = {
1495
+ register,
1496
+ get: (tokenType) => {
1497
+ return processors.get(tokenType) ?? null;
1498
+ },
1499
+ all: () => processors
1500
+ };
1501
+ return registry;
1502
+ };
1503
+
1504
+ //#endregion
1505
+ //#region src/core/sql/processors/columnProcessors.ts
1506
+ const mapDefaultSQLColumnProcessors = (mapColumnType) => ({
1507
+ AutoIncrement: SQLProcessor({
1508
+ canHandle: "SQL_COLUMN_AUTO_INCREMENT",
1509
+ handle: (token, context) => {
1510
+ mapColumnType(token, context);
1511
+ }
1512
+ }),
1513
+ BigInteger: SQLProcessor({
1514
+ canHandle: "SQL_COLUMN_BIGINT",
1515
+ handle: (token, context) => mapColumnType(token, context)
1516
+ }),
1517
+ BigSerial: SQLProcessor({
1518
+ canHandle: "SQL_COLUMN_BIGSERIAL",
1519
+ handle: (token, context) => mapColumnType(token, context)
1520
+ }),
1521
+ Serial: SQLProcessor({
1522
+ canHandle: "SQL_COLUMN_SERIAL",
1523
+ handle: (token, context) => mapColumnType(token, context)
1524
+ }),
1525
+ Integer: SQLProcessor({
1526
+ canHandle: "SQL_COLUMN_INTEGER",
1527
+ handle: (token, context) => mapColumnType(token, context)
1528
+ }),
1529
+ JSONB: SQLProcessor({
1530
+ canHandle: "SQL_COLUMN_JSONB",
1531
+ handle: (token, context) => mapColumnType(token, context)
1532
+ }),
1533
+ Timestamp: SQLProcessor({
1534
+ canHandle: "SQL_COLUMN_TIMESTAMP",
1535
+ handle: (token, context) => mapColumnType(token, context)
1536
+ }),
1537
+ Timestamptz: SQLProcessor({
1538
+ canHandle: "SQL_COLUMN_TIMESTAMPTZ",
1539
+ handle: (token, context) => mapColumnType(token, context)
1540
+ }),
1541
+ Varchar: SQLProcessor({
1542
+ canHandle: "SQL_COLUMN_VARCHAR",
1543
+ handle: (token, context) => mapColumnType(token, context)
1544
+ })
1545
+ });
1546
+
1547
+ //#endregion
1548
+ //#region src/core/sql/processors/index.ts
1549
+ const defaultProcessorsRegistry = globalThis.defaultProcessorsRegistry = globalThis.defaultProcessorsRegistry ?? SQLProcessorsRegistry().register(FormatIdentifierProcessor, MapLiteralProcessor, ExpandArrayProcessor, ExpandSQLInProcessor);
1550
+
1551
+ //#endregion
1552
+ //#region src/core/sql/tokenizedSQL/tokenizedSQL.ts
1553
+ const TokenizedSQLBuilder = () => {
1554
+ const sqlChunks = [];
1555
+ const sqlTokens = [];
1796
1556
  return {
1797
- transaction: (options) => {
1798
- let conn = null;
1799
- let innerTx = null;
1800
- let connectingPromise = null;
1801
- const ensureConnection = async () => {
1802
- if (conn) return innerTx;
1803
- if (!connectingPromise) connectingPromise = (async () => {
1804
- conn = await connect();
1805
- innerTx = conn.transaction(options);
1806
- })();
1807
- await connectingPromise;
1808
- return innerTx;
1809
- };
1810
- return {
1811
- driverType,
1812
- get connection() {
1813
- if (!conn) throw new Error("Transaction not started - call begin() first");
1814
- return conn;
1815
- },
1816
- execute: {
1817
- query: async (sql, queryOptions) => {
1818
- return (await ensureConnection()).execute.query(sql, queryOptions);
1819
- },
1820
- batchQuery: async (sqls, queryOptions) => {
1821
- return (await ensureConnection()).execute.batchQuery(sqls, queryOptions);
1822
- },
1823
- command: async (sql, commandOptions) => {
1824
- return (await ensureConnection()).execute.command(sql, commandOptions);
1825
- },
1826
- batchCommand: async (sqls, commandOptions) => {
1827
- return (await ensureConnection()).execute.batchCommand(sqls, commandOptions);
1828
- }
1829
- },
1830
- begin: async () => {
1831
- return (await ensureConnection()).begin();
1832
- },
1833
- commit: async () => {
1834
- if (!innerTx) throw new Error("Transaction not started");
1835
- try {
1836
- return await innerTx.commit();
1837
- } finally {
1838
- if (conn) await close(conn);
1839
- }
1840
- },
1841
- rollback: async (error) => {
1842
- if (!innerTx) {
1843
- if (conn) await close(conn);
1844
- return;
1845
- }
1846
- try {
1847
- return await innerTx.rollback(error);
1848
- } finally {
1849
- if (conn) await close(conn);
1850
- }
1851
- },
1852
- _transactionOptions: void 0
1853
- };
1557
+ addSQL(str) {
1558
+ sqlChunks.push(str);
1854
1559
  },
1855
- withTransaction: async (handle, options) => {
1856
- const conn = await connect();
1857
- try {
1858
- const withTx = conn.withTransaction;
1859
- return await withTx(handle, options);
1860
- } finally {
1861
- await close(conn);
1862
- }
1560
+ addSQLs(str) {
1561
+ sqlChunks.push(...str);
1562
+ },
1563
+ addToken(value) {
1564
+ sqlTokens.push(value);
1565
+ },
1566
+ addTokens(vals) {
1567
+ sqlTokens.push(...vals);
1568
+ },
1569
+ build() {
1570
+ return sqlChunks.length > 0 ? {
1571
+ __brand: "tokenized-sql",
1572
+ sqlChunks,
1573
+ sqlTokens
1574
+ } : TokenizedSQL.empty;
1863
1575
  }
1864
1576
  };
1865
1577
  };
1578
+ const TokenizedSQL = (strings, values) => {
1579
+ const builder = TokenizedSQLBuilder();
1580
+ for (let i = 0; i < strings.length; i++) {
1581
+ if (strings[i] !== "") builder.addSQL(strings[i]);
1582
+ if (i >= values.length) break;
1583
+ const value = values[i];
1584
+ if (isTokenizedSQL(value)) {
1585
+ builder.addSQLs(value.sqlChunks);
1586
+ builder.addTokens(value.sqlTokens);
1587
+ } else if (SQLPlain.check(value)) builder.addSQL(value.value);
1588
+ else {
1589
+ builder.addSQL(TokenizedSQL.paramPlaceholder);
1590
+ builder.addToken(SQLToken.check(value) ? value : Array.isArray(value) ? SQLArray.from(value) : SQLLiteral.from(value));
1591
+ }
1592
+ }
1593
+ return builder.build();
1594
+ };
1595
+ const isTokenizedSQL = (value) => {
1596
+ return value !== null && typeof value === "object" && "__brand" in value && value.__brand === "tokenized-sql";
1597
+ };
1598
+ TokenizedSQL.paramPlaceholder = `__P__`;
1599
+ TokenizedSQL.empty = {
1600
+ __brand: "tokenized-sql",
1601
+ sqlChunks: [""],
1602
+ sqlTokens: []
1603
+ };
1604
+
1605
+ //#endregion
1606
+ //#region src/core/sql/sql.ts
1607
+ const createSQL = (strings, ...values) => {
1608
+ return TokenizedSQL(strings, values);
1609
+ };
1610
+ /**
1611
+ * Creates SQL by interpolating values directly into the statement.
1612
+ *
1613
+ * Prefer {@link SQL} for query execution. `RawSQL` is for trusted SQL text that
1614
+ * is already safe to inline.
1615
+ */
1616
+ function RawSQL(strings, ...values) {
1617
+ let result = "";
1618
+ for (let i = 0; i < strings.length; i++) {
1619
+ result += strings[i];
1620
+ if (i < values.length) result += String(values[i]);
1621
+ }
1622
+ return {
1623
+ __brand: "tokenized-sql",
1624
+ sqlChunks: [result],
1625
+ sqlTokens: []
1626
+ };
1627
+ }
1628
+ /** Returns true when the value is a tokenized SQL statement. */
1629
+ const isSQL = (value) => {
1630
+ if (value === void 0 || value === null) return false;
1631
+ return isTokenizedSQL(value);
1632
+ };
1633
+ const emptySQL = {
1634
+ __brand: "tokenized-sql",
1635
+ sqlChunks: [""],
1636
+ sqlTokens: []
1637
+ };
1638
+ const mergeSQL = (sqls, separator = " ") => {
1639
+ const parametrized = sqls.filter((sql) => !isEmpty(sql)).map((sql) => sql);
1640
+ const params = parametrized.flatMap((p) => p.sqlTokens);
1641
+ const sqlChunks = parametrized.flatMap((p, i) => i == parametrized.length - 1 || separator === "" ? p.sqlChunks : [...p.sqlChunks, separator]);
1642
+ return sqlChunks.length > 0 ? {
1643
+ __brand: "tokenized-sql",
1644
+ sqlChunks,
1645
+ sqlTokens: params
1646
+ } : TokenizedSQL.empty;
1647
+ };
1648
+ const concatSQL = (...sqls) => mergeSQL(sqls, "");
1649
+ const literal = (value) => SQLPlain.from(`'${value.replace(/'/g, "''")}'`);
1650
+ const isEmpty = (sql) => {
1651
+ if (isTokenizedSQL(sql)) {
1652
+ const parametrized = sql;
1653
+ return parametrized.sqlChunks.every((chunk) => chunk.trim() === "") && parametrized.sqlTokens.length === 0;
1654
+ }
1655
+ return false;
1656
+ };
1657
+ const columnFactory = SQLColumnToken.from;
1658
+ columnFactory.type = SQLColumnTypeTokensFactory;
1659
+ const schemaColumnFactory = ((name, type, options) => columnSchemaComponent({
1660
+ columnName: name,
1661
+ type,
1662
+ ...options
1663
+ }));
1664
+ const SQL = Object.assign(createSQL, {
1665
+ EMPTY: emptySQL,
1666
+ concat: concatSQL,
1667
+ merge: mergeSQL,
1668
+ format: (sql, formatter, options) => formatSQL(sql, formatter, options?.serializer ?? JSONSerializer, options),
1669
+ describe: (sql, formatter, options) => describeSQL(sql, formatter, options?.serializer ?? JSONSerializer, options),
1670
+ in: (column, values, options) => options?.mode ? SQLIn.from({
1671
+ column,
1672
+ values,
1673
+ mode: options.mode
1674
+ }) : SQLIn.from({
1675
+ column,
1676
+ values
1677
+ }),
1678
+ array: (values, options) => SQLArray.from(options?.mode ? {
1679
+ value: values,
1680
+ mode: options.mode
1681
+ } : values),
1682
+ identifier: SQLIdentifier.from,
1683
+ plain: SQLPlain.from,
1684
+ literal,
1685
+ check: {
1686
+ isSQL,
1687
+ isTokenizedSQL: (value) => isTokenizedSQL(value),
1688
+ isEmpty,
1689
+ isIdentifier: SQLIdentifier.check,
1690
+ isPlain: SQLPlain.check,
1691
+ isSQLIn: SQLIn.check
1692
+ },
1693
+ column: columnFactory,
1694
+ columnN: Object.assign(schemaColumnFactory, { type: SQLColumnTypeTokensFactory })
1695
+ });
1696
+
1697
+ //#endregion
1698
+ //#region src/core/sql/valueMappers/reservedSqlWords.ts
1699
+ const ansiSqlReservedMap = {
1700
+ ALL: true,
1701
+ AND: true,
1702
+ ANY: true,
1703
+ ARRAY: true,
1704
+ AS: true,
1705
+ ASC: true,
1706
+ AUTHORIZATION: true,
1707
+ BETWEEN: true,
1708
+ BINARY: true,
1709
+ BOTH: true,
1710
+ CASE: true,
1711
+ CAST: true,
1712
+ CHECK: true,
1713
+ COLLATE: true,
1714
+ COLUMN: true,
1715
+ CONSTRAINT: true,
1716
+ CREATE: true,
1717
+ CROSS: true,
1718
+ CURRENT_DATE: true,
1719
+ CURRENT_TIME: true,
1720
+ CURRENT_TIMESTAMP: true,
1721
+ CURRENT_USER: true,
1722
+ DEFAULT: true,
1723
+ DEFERRABLE: true,
1724
+ DESC: true,
1725
+ DISTINCT: true,
1726
+ DO: true,
1727
+ ELSE: true,
1728
+ END: true,
1729
+ EXCEPT: true,
1730
+ FALSE: true,
1731
+ FOR: true,
1732
+ FOREIGN: true,
1733
+ FROM: true,
1734
+ FULL: true,
1735
+ GRANT: true,
1736
+ GROUP: true,
1737
+ HAVING: true,
1738
+ IN: true,
1739
+ INITIALLY: true,
1740
+ INNER: true,
1741
+ INTERSECT: true,
1742
+ INTO: true,
1743
+ IS: true,
1744
+ JOIN: true,
1745
+ LEADING: true,
1746
+ LEFT: true,
1747
+ LIKE: true,
1748
+ LOCALTIME: true,
1749
+ LOCALTIMESTAMP: true,
1750
+ NATURAL: true,
1751
+ NEW: true,
1752
+ NOT: true,
1753
+ NULL: true,
1754
+ NULLS: true,
1755
+ OLD: true,
1756
+ ON: true,
1757
+ ONLY: true,
1758
+ OPEN: true,
1759
+ OR: true,
1760
+ ORDER: true,
1761
+ OUTER: true,
1762
+ OVERLAPS: true,
1763
+ PARTITION: true,
1764
+ PLACING: true,
1765
+ PRIMARY: true,
1766
+ REFERENCES: true,
1767
+ RIGHT: true,
1768
+ SELECT: true,
1769
+ SESSION_USER: true,
1770
+ SIMILAR: true,
1771
+ SOME: true,
1772
+ TABLE: true,
1773
+ THEN: true,
1774
+ TO: true,
1775
+ TRAILING: true,
1776
+ TRUE: true,
1777
+ UNION: true,
1778
+ UNIQUE: true,
1779
+ USER: true,
1780
+ USING: true,
1781
+ WHEN: true,
1782
+ WHERE: true,
1783
+ WITH: true,
1784
+ WITHOUT: true,
1785
+ ADD: true,
1786
+ ALTER: true,
1787
+ ARE: true,
1788
+ AT: true,
1789
+ BEGIN: true,
1790
+ BY: true,
1791
+ CASCADE: true,
1792
+ CLOSE: true,
1793
+ COMMIT: true,
1794
+ CONNECT: true,
1795
+ CONTINUE: true,
1796
+ CORRESPONDING: true,
1797
+ CURSOR: true,
1798
+ DEALLOCATE: true,
1799
+ DECLARE: true,
1800
+ DELETE: true,
1801
+ DESCRIBE: true,
1802
+ DISCONNECT: true,
1803
+ DROP: true,
1804
+ ESCAPE: true,
1805
+ EXECUTE: true,
1806
+ EXISTS: true,
1807
+ FETCH: true,
1808
+ FIRST: true,
1809
+ FLOAT: true,
1810
+ GET: true,
1811
+ GLOBAL: true,
1812
+ GO: true,
1813
+ GOTO: true,
1814
+ HOUR: true,
1815
+ IMMEDIATE: true,
1816
+ INDICATOR: true,
1817
+ INPUT: true,
1818
+ INSERT: true,
1819
+ INT: true,
1820
+ INTEGER: true,
1821
+ INTERVAL: true,
1822
+ LANGUAGE: true,
1823
+ LAST: true,
1824
+ LOCAL: true,
1825
+ MATCH: true,
1826
+ MINUTE: true,
1827
+ MODULE: true,
1828
+ MONTH: true,
1829
+ NATIONAL: true,
1830
+ NEXT: true,
1831
+ NO: true,
1832
+ OF: true,
1833
+ OUTPUT: true,
1834
+ PARTIAL: true,
1835
+ PREPARE: true,
1836
+ PRESERVE: true,
1837
+ PRIOR: true,
1838
+ PRIVILEGES: true,
1839
+ PROCEDURE: true,
1840
+ PUBLIC: true,
1841
+ READ: true,
1842
+ REAL: true,
1843
+ RESTRICT: true,
1844
+ REVOKE: true,
1845
+ ROLLBACK: true,
1846
+ ROWS: true,
1847
+ SCHEMA: true,
1848
+ SCROLL: true,
1849
+ SECOND: true,
1850
+ SECTION: true,
1851
+ SET: true,
1852
+ SIZE: true,
1853
+ SMALLINT: true,
1854
+ SQL: true,
1855
+ SQLCODE: true,
1856
+ SQLERROR: true,
1857
+ SQLSTATE: true,
1858
+ TEMPORARY: true,
1859
+ TIMEZONE_HOUR: true,
1860
+ TIMEZONE_MINUTE: true,
1861
+ TRANSACTION: true,
1862
+ TRANSLATE: true,
1863
+ TRANSLATION: true,
1864
+ UNKNOWN: true,
1865
+ UPDATE: true,
1866
+ VALUE: true,
1867
+ VALUES: true,
1868
+ VARCHAR: true,
1869
+ VARYING: true,
1870
+ VIEW: true,
1871
+ WHENEVER: true,
1872
+ WORK: true,
1873
+ WRITE: true,
1874
+ YEAR: true,
1875
+ ZONE: true
1876
+ };
1866
1877
 
1867
1878
  //#endregion
1868
- //#region src/core/connections/connection.ts
1869
- const createAmbientConnection = (options) => {
1870
- const { driverType, client, executor, initTransaction, serializer } = options;
1871
- const clientPromise = Promise.resolve(client);
1872
- const closePromise = Promise.resolve();
1873
- const open = () => clientPromise;
1874
- const close = () => closePromise;
1875
- const typedConnection = {
1876
- driverType,
1877
- open,
1878
- close,
1879
- ...transactionFactoryWithDbClient(open, initTransaction(() => typedConnection)),
1880
- execute: sqlExecutor(executor({ serializer }), { connect: open }),
1881
- _transactionType: void 0
1882
- };
1883
- return typedConnection;
1884
- };
1885
- const createSingletonConnection = (options) => {
1886
- const { driverType, connect, close, initTransaction, executor, serializer } = options;
1887
- let client = null;
1888
- let connectPromise = null;
1889
- const getClient = async () => {
1890
- if (client) return client;
1891
- if (!connectPromise) connectPromise = connect().then((c) => {
1892
- client = c;
1893
- return c;
1894
- });
1895
- return connectPromise;
1896
- };
1897
- const typedConnection = {
1898
- driverType,
1899
- open: getClient,
1900
- close: () => client ? close(client) : Promise.resolve(),
1901
- ...transactionFactoryWithDbClient(getClient, initTransaction(() => typedConnection)),
1902
- execute: sqlExecutor(executor({ serializer }), { connect: getClient }),
1903
- _transactionType: void 0
1904
- };
1905
- return typedConnection;
1879
+ //#region src/core/sql/valueMappers/sqlValueMapper.ts
1880
+ const ANSISQLParamPlaceholder = "?";
1881
+ const ANSISQLIdentifierQuote = "\"";
1882
+ const mapANSISQLParamPlaceholder = () => "?";
1883
+ const isReserved = (value, reserved) => !!reserved[value.toUpperCase()];
1884
+ const mapSQLIdentifier = (value, options) => {
1885
+ if (value === void 0 || value === null) throw new Error("SQL identifier cannot be null or undefined");
1886
+ const ident = value.toString().slice(0);
1887
+ const quoteSign = options?.quote ?? "\"";
1888
+ if (/^[a-z_][a-z0-9_$]*$/.test(ident) && !isReserved(ident, options?.reservedWords ?? ansiSqlReservedMap)) return ident;
1889
+ let quoted = quoteSign;
1890
+ for (let i = 0; i < ident.length; i++) {
1891
+ const c = ident[i];
1892
+ quoted += c === quoteSign ? c + c : c;
1893
+ }
1894
+ quoted += quoteSign;
1895
+ return quoted;
1906
1896
  };
1907
- const createTransientConnection = (options) => {
1908
- const { driverType, open, close, initTransaction, executor, serializer } = options;
1909
- const typedConnection = {
1910
- driverType,
1911
- open,
1912
- close,
1913
- ...transactionFactoryWithDbClient(open, initTransaction(() => typedConnection)),
1914
- execute: sqlExecutor(executor({ serializer }), { connect: open }),
1915
- _transactionType: void 0
1916
- };
1917
- return typedConnection;
1897
+ const DefaultMapSQLParamValueOptions = {
1898
+ mapPlaceholder: mapANSISQLParamPlaceholder,
1899
+ mapIdentifier: mapSQLIdentifier
1918
1900
  };
1919
- const createConnection = (options) => {
1920
- const { driverType, connect, close, initTransaction, executor, serializer } = options;
1921
- let client = null;
1922
- let connectPromise = null;
1923
- const getClient = async () => {
1924
- if (client) return client;
1925
- if (!connectPromise) connectPromise = connect().then((c) => {
1926
- client = c;
1927
- return c;
1928
- });
1929
- return connectPromise;
1901
+ const SQLValueMapper = (options) => {
1902
+ const mapSQLParamValueOptions = {
1903
+ ...DefaultMapSQLParamValueOptions,
1904
+ ...options ?? {}
1930
1905
  };
1931
- const typedConnection = {
1932
- driverType,
1933
- open: getClient,
1934
- close: () => client ? close(client) : Promise.resolve(),
1935
- ...transactionFactoryWithDbClient(getClient, initTransaction(() => typedConnection)),
1936
- execute: sqlExecutor(executor({ serializer }), { connect: getClient }),
1937
- _transactionType: void 0
1906
+ return {
1907
+ mapValue: (value, serializer, mapOptions) => mapSQLParamValue(value, serializer, {
1908
+ ...mapSQLParamValueOptions,
1909
+ ...mapOptions
1910
+ }),
1911
+ mapPlaceholder: mapSQLParamValueOptions.mapPlaceholder,
1912
+ mapIdentifier: mapSQLParamValueOptions.mapIdentifier
1938
1913
  };
1939
- return typedConnection;
1940
1914
  };
1915
+ function mapSQLParamValue(value, serializer, options) {
1916
+ if (value === null || value === void 0) return null;
1917
+ else if (typeof value === "number") return value;
1918
+ else if (typeof value === "string") return value;
1919
+ else if (Array.isArray(value)) {
1920
+ const mapValue = options?.mapValue ?? mapSQLParamValue;
1921
+ return options?.mapArray ? options.mapArray(value, mapValue) : value.map((item) => mapValue(item, serializer, options));
1922
+ } else if (typeof value === "boolean") return options?.mapBoolean ? options.mapBoolean(value) : value;
1923
+ else if (typeof value === "bigint") return options?.mapBigInt ? options.mapBigInt(value) : value.toString();
1924
+ else if (value instanceof Date) return options?.mapDate ? options.mapDate(value) : value.toISOString();
1925
+ else if (SQL.check.isIdentifier(value)) return (options?.mapIdentifier ?? mapSQLIdentifier)(value.value);
1926
+ else if (typeof value === "object") return options?.mapObject ? options.mapObject(value) : serializer.serialize(value);
1927
+ else return serializer.serialize(value);
1928
+ }
1941
1929
 
1942
1930
  //#endregion
1943
- //#region src/core/taskProcessing/taskProcessor.ts
1944
- var TaskProcessor = class {
1945
- queue = [];
1946
- isProcessing = false;
1947
- activeTasks = 0;
1948
- activeGroups = /* @__PURE__ */ new Set();
1949
- options;
1950
- stopped = false;
1951
- constructor(options) {
1952
- this.options = options;
1953
- }
1954
- enqueue(task, options) {
1955
- if (this.stopped) return Promise.reject(new DumboError("TaskProcessor has been stopped"));
1956
- if (this.queue.length >= this.options.maxQueueSize) return Promise.reject(new TransientDatabaseError("Too many pending connections. Please try again later."));
1957
- return this.schedule(task, options);
1958
- }
1959
- waitForEndOfProcessing() {
1960
- return this.schedule(({ ack }) => Promise.resolve(ack()));
1961
- }
1962
- async stop(options) {
1963
- if (this.stopped) return;
1964
- this.stopped = true;
1965
- this.queue.length = 0;
1966
- this.activeGroups.clear();
1967
- if (!options?.force) await this.waitForEndOfProcessing();
1968
- }
1969
- schedule(task, options) {
1970
- return promiseWithDeadline((resolve, reject) => {
1971
- const taskWithContext = () => {
1972
- return new Promise((resolveTask, failTask) => {
1973
- task({ ack: resolveTask }).then(resolve).catch((err) => {
1974
- failTask(err);
1975
- reject(err);
1976
- });
1977
- });
1978
- };
1979
- this.queue.push({
1980
- task: taskWithContext,
1981
- options
1982
- });
1983
- if (!this.isProcessing) this.ensureProcessing();
1984
- }, { deadline: this.options.maxTaskIdleTime });
1985
- }
1986
- ensureProcessing() {
1987
- if (this.isProcessing) return;
1988
- this.isProcessing = true;
1989
- this.processQueue();
1990
- }
1991
- processQueue() {
1992
- try {
1993
- while (this.activeTasks < this.options.maxActiveTasks && this.queue.length > 0) {
1994
- const item = this.takeFirstAvailableItem();
1995
- if (item === null) return;
1996
- const groupId = item.options?.taskGroupId;
1997
- if (groupId) this.activeGroups.add(groupId);
1998
- this.activeTasks++;
1999
- this.executeItem(item);
2000
- }
2001
- } catch (error) {
2002
- console.error(error);
2003
- throw error;
2004
- } finally {
2005
- this.isProcessing = false;
2006
- if (this.hasItemsToProcess() && this.activeTasks < this.options.maxActiveTasks) this.ensureProcessing();
1931
+ //#region src/core/sql/formatters/sqlFormatter.ts
1932
+ const SQLFormatter = ({ format, describe, valueMapper: valueMapperOptions, processorsRegistry }) => {
1933
+ const valueMapper = SQLValueMapper(valueMapperOptions);
1934
+ const options = {
1935
+ builder: ParametrizedSQLBuilder({ mapParamPlaceholder: valueMapper.mapPlaceholder }),
1936
+ mapper: valueMapper,
1937
+ processorsRegistry: processorsRegistry ?? defaultProcessorsRegistry
1938
+ };
1939
+ const resultFormatter = {
1940
+ format: format ?? ((sql, methodOptions) => formatSQL(sql, resultFormatter, methodOptions?.serializer ?? JSONSerializer, {
1941
+ ...options,
1942
+ ...methodOptions ?? {}
1943
+ })),
1944
+ describe: describe ?? ((sql, methodOptions) => describeSQL(sql, resultFormatter, methodOptions?.serializer ?? JSONSerializer, {
1945
+ ...options,
1946
+ ...methodOptions ?? {}
1947
+ })),
1948
+ valueMapper
1949
+ };
1950
+ return resultFormatter;
1951
+ };
1952
+ const dumboSQLFormatters = globalThis.dumboSQLFormatters = globalThis.dumboSQLFormatters ?? {};
1953
+ const registerFormatter = (dialect, formatter) => {
1954
+ dumboSQLFormatters[dialect] = formatter;
1955
+ };
1956
+ const getFormatter = (dialect) => {
1957
+ const formatterKey = dialect;
1958
+ if (!dumboSQLFormatters[formatterKey]) throw new Error(`No SQL formatter registered for dialect: ${dialect}`);
1959
+ return dumboSQLFormatters[formatterKey];
1960
+ };
1961
+ function formatSQL(sql, formatter, serializer, context) {
1962
+ const mapper = context?.mapper == void 0 ? formatter.valueMapper : {
1963
+ ...formatter.valueMapper,
1964
+ ...context.mapper
1965
+ };
1966
+ const processorsRegistry = context?.processorsRegistry ?? defaultProcessorsRegistry;
1967
+ const merged = Array.isArray(sql) ? SQL.merge(sql, "\n") : sql;
1968
+ if (!isTokenizedSQL(merged)) throw new Error("Expected TokenizedSQL, got string-based SQL");
1969
+ const builder = ParametrizedSQLBuilder({ mapParamPlaceholder: mapper.mapPlaceholder });
1970
+ let paramIndex = 0;
1971
+ for (let i = 0; i < merged.sqlChunks.length; i++) {
1972
+ const sqlChunk = merged.sqlChunks[i];
1973
+ if (sqlChunk !== TokenizedSQL.paramPlaceholder) {
1974
+ builder.addSQL(sqlChunk);
1975
+ continue;
2007
1976
  }
1977
+ const token = merged.sqlTokens[paramIndex++];
1978
+ const processor = processorsRegistry.get(token.sqlTokenType);
1979
+ if (!processor) throw new Error(`No SQL processor registered for token type: ${token.sqlTokenType}`);
1980
+ processor.handle(token, {
1981
+ builder,
1982
+ processorsRegistry,
1983
+ serializer,
1984
+ mapper
1985
+ });
2008
1986
  }
2009
- async executeItem({ task, options }) {
2010
- try {
2011
- await task();
2012
- } finally {
2013
- this.activeTasks--;
2014
- if (options && options.taskGroupId) this.activeGroups.delete(options.taskGroupId);
2015
- this.ensureProcessing();
2016
- }
1987
+ return builder.build();
1988
+ }
1989
+ const describeSQL = (sql, formatter, serializer, options) => formatSQL(sql, formatter, serializer, {
1990
+ ...options ?? {},
1991
+ mapper: { mapPlaceholder: (_, value) => serializer.serialize(value) }
1992
+ }).query;
1993
+
1994
+ //#endregion
1995
+ //#region src/core/tracing/printing/color.ts
1996
+ let enableColors = true;
1997
+ const color = {
1998
+ set level(value) {
1999
+ enableColors = value === 1;
2000
+ },
2001
+ hex: (value) => (text) => enableColors ? ansis.hex(value)(text) : text,
2002
+ red: (value) => enableColors ? ansis.red(value) : value,
2003
+ green: (value) => enableColors ? ansis.green(value) : value,
2004
+ blue: (value) => enableColors ? ansis.blue(value) : value,
2005
+ cyan: (value) => enableColors ? ansis.cyan(value) : value,
2006
+ yellow: (value) => enableColors ? ansis.yellow(value) : value
2007
+ };
2008
+
2009
+ //#endregion
2010
+ //#region src/core/tracing/printing/pretty.ts
2011
+ const TWO_SPACES = " ";
2012
+ const COLOR_STRING = color.hex("#98c379");
2013
+ const COLOR_KEY = color.hex("#61afef");
2014
+ const COLOR_NUMBER_OR_DATE = color.hex("#d19a66");
2015
+ const COLOR_BOOLEAN = color.hex("#c678dd");
2016
+ const COLOR_NULL_OR_UNDEFINED = color.hex("#c678dd");
2017
+ const COLOR_BRACKETS = color.hex("#abb2bf");
2018
+ const processString = (str, indent, handleMultiline) => {
2019
+ if (handleMultiline && str.includes("\n")) {
2020
+ const indentedLines = str.split("\n").map((line) => indent + TWO_SPACES + COLOR_STRING(line));
2021
+ return COLOR_STRING("\"") + "\n" + indentedLines.join("\n") + "\n" + indent + COLOR_STRING("\"");
2017
2022
  }
2018
- takeFirstAvailableItem = () => {
2019
- const taskIndex = this.queue.findIndex((item) => !item.options?.taskGroupId || !this.activeGroups.has(item.options.taskGroupId));
2020
- if (taskIndex === -1) return null;
2021
- const [item] = this.queue.splice(taskIndex, 1);
2022
- return item ?? null;
2023
- };
2024
- hasItemsToProcess = () => this.queue.findIndex((item) => !item.options?.taskGroupId || !this.activeGroups.has(item.options.taskGroupId)) !== -1;
2023
+ return COLOR_STRING(`"${str}"`);
2025
2024
  };
2026
- const DEFAULT_PROMISE_DEADLINE = 2147483647;
2027
- const promiseWithDeadline = (executor, options) => {
2028
- return new Promise((resolve, reject) => {
2029
- let taskStarted = false;
2030
- let timeoutId = null;
2031
- const deadline = options.deadline ?? DEFAULT_PROMISE_DEADLINE;
2032
- timeoutId = setTimeout(() => {
2033
- if (!taskStarted) reject(/* @__PURE__ */ new Error("Task was not started within the maximum waiting time"));
2034
- }, deadline);
2035
- timeoutId.unref();
2036
- executor((value) => {
2037
- taskStarted = true;
2038
- if (timeoutId) clearTimeout(timeoutId);
2039
- timeoutId = null;
2040
- resolve(value);
2041
- }, (reason) => {
2042
- if (timeoutId) clearTimeout(timeoutId);
2043
- timeoutId = null;
2044
- reject(reason);
2025
+ const shouldPrint = (obj) => typeof obj !== "function" && typeof obj !== "symbol";
2026
+ const formatJson = (obj, indentLevel = 0, handleMultiline = false) => {
2027
+ const indent = TWO_SPACES.repeat(indentLevel);
2028
+ if (obj === null) return COLOR_NULL_OR_UNDEFINED("null");
2029
+ if (obj === void 0) return COLOR_NULL_OR_UNDEFINED("undefined");
2030
+ if (typeof obj === "string") return processString(obj, indent, handleMultiline);
2031
+ if (typeof obj === "number" || typeof obj === "bigint" || obj instanceof Date) return COLOR_NUMBER_OR_DATE(String(obj));
2032
+ if (typeof obj === "boolean") return COLOR_BOOLEAN(String(obj));
2033
+ if (obj instanceof Error) {
2034
+ const errorObj = {};
2035
+ Object.getOwnPropertyNames(obj).forEach((key) => {
2036
+ errorObj[key] = obj[key];
2045
2037
  });
2046
- });
2038
+ return formatJson(errorObj, indentLevel, handleMultiline);
2039
+ }
2040
+ if (obj instanceof Promise) return COLOR_STRING("Promise {pending}");
2041
+ if (Array.isArray(obj)) {
2042
+ const arrayItems = obj.map((item) => formatJson(item, indentLevel + 1, handleMultiline));
2043
+ return `${COLOR_BRACKETS("[")}\n${indent} ${arrayItems.join(`,\n${indent} `)}\n${indent}${COLOR_BRACKETS("]")}`;
2044
+ }
2045
+ const entries = Object.entries(obj).filter(([_, value]) => shouldPrint(value)).map(([key, value]) => `${COLOR_KEY(`"${key}"`)}: ${formatJson(value, indentLevel + 1, handleMultiline)}`);
2046
+ return `${COLOR_BRACKETS("{")}\n${indent} ${entries.join(`,\n${indent} `)}\n${indent}${COLOR_BRACKETS("}")}`;
2047
2047
  };
2048
+ const prettyJson = (obj, options) => formatJson(obj, 0, options?.handleMultiline);
2048
2049
 
2049
2050
  //#endregion
2050
- //#region src/core/taskProcessing/executionGuards.ts
2051
- const guardBoundedAccess = (getResource, options) => {
2052
- let isStopped = false;
2053
- const taskProcessor = new TaskProcessor({
2054
- maxActiveTasks: options.maxResources,
2055
- maxQueueSize: options.maxQueueSize ?? 1e3
2056
- });
2057
- const resourcePool = [];
2058
- const allResources = /* @__PURE__ */ new Set();
2059
- const ackCallbacks = /* @__PURE__ */ new Map();
2060
- const acquire = async () => taskProcessor.enqueue(async ({ ack }) => {
2061
- try {
2062
- let resource;
2063
- if (options.reuseResources) resource = resourcePool.pop();
2064
- if (!resource) {
2065
- resource = await getResource();
2066
- allResources.add(resource);
2067
- }
2068
- ackCallbacks.set(resource, ack);
2069
- return resource;
2070
- } catch (e) {
2071
- ack();
2072
- throw e;
2073
- }
2074
- });
2075
- const release = (resource) => {
2076
- const ack = ackCallbacks.get(resource);
2077
- if (ack) {
2078
- ackCallbacks.delete(resource);
2079
- if (options.reuseResources) resourcePool.push(resource);
2080
- ack();
2081
- }
2082
- };
2083
- const execute = async (operation) => {
2084
- const resource = await acquire();
2085
- try {
2086
- return await operation(resource);
2087
- } finally {
2088
- release(resource);
2089
- }
2090
- };
2091
- return {
2092
- acquire,
2093
- release,
2094
- execute,
2095
- waitForIdle: () => taskProcessor.waitForEndOfProcessing(),
2096
- stop: async (stopOptions) => {
2097
- if (isStopped) return;
2098
- isStopped = true;
2099
- if (options?.closeResource) {
2100
- const resources = [...allResources];
2101
- allResources.clear();
2102
- resourcePool.length = 0;
2103
- await Promise.all(resources.map(async (resource) => await options.closeResource(resource)));
2104
- }
2105
- await taskProcessor.stop(stopOptions);
2106
- }
2107
- };
2051
+ //#region src/core/tracing/index.ts
2052
+ const tracer = () => {};
2053
+ const LogLevel = {
2054
+ DISABLED: "DISABLED",
2055
+ INFO: "INFO",
2056
+ LOG: "LOG",
2057
+ WARN: "WARN",
2058
+ ERROR: "ERROR"
2108
2059
  };
2109
- const guardInitializedOnce = (initialize, options) => {
2110
- let initPromise = null;
2111
- const taskProcessor = new TaskProcessor({
2112
- maxActiveTasks: 1,
2113
- maxQueueSize: options?.maxQueueSize ?? 1e3
2114
- });
2115
- const ensureInitialized = async (retryCount = 0) => {
2116
- if (initPromise !== null) return initPromise;
2117
- return taskProcessor.enqueue(async ({ ack }) => {
2118
- if (initPromise !== null) {
2119
- ack();
2120
- return initPromise;
2121
- }
2122
- try {
2123
- const promise = initialize();
2124
- initPromise = promise;
2125
- const result = await promise;
2126
- ack();
2127
- return result;
2128
- } catch (error) {
2129
- initPromise = null;
2130
- ack();
2131
- if (retryCount < (options?.maxRetries ?? 3)) return ensureInitialized(retryCount + 1);
2132
- throw error;
2133
- }
2134
- }, { taskGroupId: v7() });
2135
- };
2136
- return {
2137
- ensureInitialized,
2138
- reset: () => {
2139
- initPromise = null;
2140
- },
2141
- stop: (options) => taskProcessor.stop(options)
2060
+ const LogStyle = {
2061
+ RAW: "RAW",
2062
+ PRETTY: "PRETTY"
2063
+ };
2064
+ const getEnvVariable = (name) => {
2065
+ try {
2066
+ if (typeof process !== "undefined" && process.env) return process.env[name];
2067
+ return;
2068
+ } catch {
2069
+ return;
2070
+ }
2071
+ };
2072
+ const shouldLog = (logLevel) => {
2073
+ const definedLogLevel = getEnvVariable("DUMBO_LOG_LEVEL") ?? LogLevel.ERROR;
2074
+ if (definedLogLevel === LogLevel.ERROR && logLevel === LogLevel.ERROR) return true;
2075
+ if (definedLogLevel === LogLevel.WARN && [LogLevel.ERROR, LogLevel.WARN].includes(logLevel)) return true;
2076
+ if (definedLogLevel === LogLevel.LOG && [
2077
+ LogLevel.ERROR,
2078
+ LogLevel.WARN,
2079
+ LogLevel.LOG
2080
+ ].includes(logLevel)) return true;
2081
+ if (definedLogLevel === LogLevel.INFO && [
2082
+ LogLevel.ERROR,
2083
+ LogLevel.WARN,
2084
+ LogLevel.LOG,
2085
+ LogLevel.INFO
2086
+ ].includes(logLevel)) return true;
2087
+ return false;
2088
+ };
2089
+ const nulloTraceEventRecorder = () => {};
2090
+ const getTraceEventFormatter = (logStyle, serializer = JSONSerializer) => (event) => {
2091
+ switch (logStyle) {
2092
+ case "RAW": return serializer.serialize(event);
2093
+ case "PRETTY": return prettyJson(event, { handleMultiline: true });
2094
+ }
2095
+ };
2096
+ const getTraceEventRecorder = (logLevel, logStyle) => {
2097
+ const format = getTraceEventFormatter(logStyle);
2098
+ switch (logLevel) {
2099
+ case "DISABLED": return nulloTraceEventRecorder;
2100
+ case "INFO": return (event) => console.info(format(event));
2101
+ case "LOG": return (event) => console.log(format(event));
2102
+ case "WARN": return (event) => console.warn(format(event));
2103
+ case "ERROR": return (event) => console.error(format(event));
2104
+ }
2105
+ };
2106
+ const recordTraceEvent = (logLevel, eventName, attributes) => {
2107
+ if (!shouldLog(LogLevel.LOG)) return;
2108
+ const event = {
2109
+ name: eventName,
2110
+ timestamp: (/* @__PURE__ */ new Date()).getTime(),
2111
+ ...attributes
2142
2112
  };
2113
+ getTraceEventRecorder(logLevel, getEnvVariable("DUMBO_LOG_STYLE") ?? "RAW")(event);
2143
2114
  };
2115
+ tracer.info = (eventName, attributes) => recordTraceEvent(LogLevel.INFO, eventName, attributes);
2116
+ tracer.warn = (eventName, attributes) => recordTraceEvent(LogLevel.WARN, eventName, attributes);
2117
+ tracer.log = (eventName, attributes) => recordTraceEvent(LogLevel.LOG, eventName, attributes);
2118
+ tracer.error = (eventName, attributes) => recordTraceEvent(LogLevel.ERROR, eventName, attributes);
2144
2119
 
2145
2120
  //#endregion
2146
- //#region src/core/connections/pool.ts
2147
- const wrapPooledConnection = (conn, onClose) => ({
2148
- ...conn,
2149
- close: onClose
2121
+ //#region src/core/schema/sqlMigration.ts
2122
+ const sqlMigration = (name, sqls) => ({
2123
+ name,
2124
+ sqls
2150
2125
  });
2151
- const createAmbientConnectionPool = (options) => {
2152
- const { driverType, connection } = options;
2153
- return createConnectionPool({
2154
- driverType,
2155
- getConnection: () => connection,
2156
- execute: connection.execute,
2157
- transaction: (options) => connection.transaction(options),
2158
- withConnection: (handle, _options) => handle(connection),
2159
- withTransaction: (handle, options) => {
2160
- const withTx = connection.withTransaction;
2161
- return withTx(handle, options);
2162
- }
2163
- });
2164
- };
2165
- const createSingletonConnectionPool = (options) => {
2166
- const { driverType, getConnection } = options;
2167
- let connectionPromise = null;
2168
- const getExistingOrNewConnection = () => {
2169
- if (!connectionPromise) connectionPromise ??= Promise.resolve(getConnection());
2170
- return connectionPromise;
2171
- };
2126
+
2127
+ //#endregion
2128
+ //#region src/core/schema/migrators/schemaComponentMigrator.ts
2129
+ const { AutoIncrement, Varchar, Timestamp } = SQL.column.type;
2130
+ const migrationTableSQL = SQL`
2131
+ CREATE TABLE IF NOT EXISTS dmb_migrations (
2132
+ id ${AutoIncrement({ primaryKey: true })},
2133
+ name ${Varchar(255)} NOT NULL UNIQUE,
2134
+ application ${Varchar(255)} NOT NULL DEFAULT 'default',
2135
+ sql_hash ${Varchar(64)} NOT NULL,
2136
+ timestamp ${Timestamp} NOT NULL DEFAULT CURRENT_TIMESTAMP
2137
+ );
2138
+ `;
2139
+ const migrationTableSchemaComponent = schemaComponent("dumbo:schema-component:migrations-table", { migrations: [sqlMigration("dumbo:migrationTable:001", [migrationTableSQL])] });
2140
+ const SchemaComponentMigrator = (component, dumbo) => {
2141
+ const completedMigrations = [];
2172
2142
  return {
2173
- driverType,
2174
- connection: () => getExistingOrNewConnection().then((conn) => wrapPooledConnection(conn, () => Promise.resolve())),
2175
- execute: sqlExecutorInAmbientConnection({
2176
- driverType,
2177
- connection: getExistingOrNewConnection
2178
- }),
2179
- withConnection: (handle, _options) => executeInAmbientConnection(handle, { connection: getExistingOrNewConnection }),
2180
- ...transactionFactoryWithAsyncAmbientConnection(options.driverType, getExistingOrNewConnection, options.closeConnection),
2181
- close: async () => {
2182
- if (!connectionPromise) return;
2183
- await (await connectionPromise).close();
2143
+ component,
2144
+ run: async (options) => {
2145
+ const pendingMigrations = component.migrations.filter((m) => !completedMigrations.includes(`${component.schemaComponentKey}:${m.name}`));
2146
+ if (pendingMigrations.length === 0) return;
2147
+ await runSQLMigrations(dumbo, pendingMigrations, options);
2148
+ completedMigrations.push(...pendingMigrations.map((m) => `${component.schemaComponentKey}:${m.name}`));
2184
2149
  }
2185
2150
  };
2186
2151
  };
2187
- const createBoundedConnectionPool = (options) => {
2188
- const { driverType, maxConnections } = options;
2189
- const guardMaxConnections = guardBoundedAccess(options.getConnection, {
2190
- maxResources: maxConnections,
2191
- reuseResources: true
2192
- });
2193
- let closed = false;
2194
- const executeWithPooling = async (operation) => {
2195
- const conn = await guardMaxConnections.acquire();
2196
- try {
2197
- return await operation(conn);
2198
- } finally {
2199
- guardMaxConnections.release(conn);
2200
- }
2201
- };
2202
- return {
2203
- driverType,
2204
- connection: async () => {
2205
- const conn = await guardMaxConnections.acquire();
2206
- return wrapPooledConnection(conn, () => Promise.resolve(guardMaxConnections.release(conn)));
2152
+
2153
+ //#endregion
2154
+ //#region src/core/schema/migrators/migrator.ts
2155
+ const MIGRATIONS_LOCK_ID = 999956789;
2156
+ const defaultMigratorOptions = globalThis.defaultMigratorOptions = globalThis.defaultMigratorOptions ?? {};
2157
+ const registerDefaultMigratorOptions = (databaseType, options) => {
2158
+ defaultMigratorOptions[databaseType] = options;
2159
+ };
2160
+ const getDefaultMigratorOptionsFromRegistry = (databaseType) => {
2161
+ if (!defaultMigratorOptions[databaseType]) throw new Error(`No default migrator options registered for database type: ${databaseType}`);
2162
+ return defaultMigratorOptions[databaseType];
2163
+ };
2164
+ const runSQLMigrations = (pool, migrations, partialOptions) => pool.withTransaction(async ({ execute }) => {
2165
+ const databaseType = fromDatabaseDriverType(pool.driverType).databaseType;
2166
+ const defaultOptions = getDefaultMigratorOptionsFromRegistry(databaseType);
2167
+ partialOptions ??= {};
2168
+ const options = {
2169
+ ...defaultOptions,
2170
+ ...partialOptions,
2171
+ schema: {
2172
+ ...defaultOptions.schema,
2173
+ ...partialOptions?.schema ?? {}
2207
2174
  },
2208
- execute: {
2209
- query: (sql, opts) => executeWithPooling((c) => c.execute.query(sql, opts)),
2210
- batchQuery: (sqls, opts) => executeWithPooling((c) => c.execute.batchQuery(sqls, opts)),
2211
- command: (sql, opts) => executeWithPooling((c) => c.execute.command(sql, opts)),
2212
- batchCommand: (sqls, opts) => executeWithPooling((c) => c.execute.batchCommand(sqls, opts))
2175
+ lock: {
2176
+ ...defaultOptions.lock,
2177
+ ...partialOptions?.lock,
2178
+ options: {
2179
+ lockId: MIGRATIONS_LOCK_ID,
2180
+ ...defaultOptions.lock?.options,
2181
+ ...partialOptions?.lock?.options
2182
+ }
2213
2183
  },
2214
- withConnection: executeWithPooling,
2215
- ...transactionFactoryWithAsyncAmbientConnection(driverType, guardMaxConnections.acquire, guardMaxConnections.release),
2216
- close: async () => {
2217
- if (closed) return;
2218
- closed = true;
2219
- await guardMaxConnections.stop({ force: true });
2220
- }
2184
+ dryRun: defaultOptions.dryRun ?? partialOptions?.dryRun,
2185
+ ignoreMigrationHashMismatch: defaultOptions.ignoreMigrationHashMismatch ?? partialOptions?.ignoreMigrationHashMismatch,
2186
+ migrationTimeoutMs: defaultOptions.migrationTimeoutMs ?? partialOptions?.migrationTimeoutMs
2221
2187
  };
2188
+ const { databaseLock: _, ...rest } = options.lock ?? {};
2189
+ const databaseLock = options.lock?.databaseLock ?? NoDatabaseLock;
2190
+ const lockOptions = {
2191
+ lockId: MIGRATIONS_LOCK_ID,
2192
+ ...rest
2193
+ };
2194
+ const coreMigrations = (options.schema?.migrationTable ?? migrationTableSchemaComponent).migrations;
2195
+ const result = {
2196
+ applied: [],
2197
+ skipped: []
2198
+ };
2199
+ await databaseLock.withAcquire(execute, async () => {
2200
+ for (const migration of coreMigrations) await execute.batchCommand(migration.sqls, { timeoutMs: options.migrationTimeoutMs });
2201
+ for (const migration of migrations) if (await runSQLMigration(databaseType, execute, migration, {
2202
+ ignoreMigrationHashMismatch: options.ignoreMigrationHashMismatch ?? false,
2203
+ migrationTimeoutMs: options.migrationTimeoutMs
2204
+ })) result.applied.push(migration);
2205
+ else result.skipped.push(migration);
2206
+ }, lockOptions);
2207
+ return {
2208
+ success: options.dryRun ? false : true,
2209
+ result
2210
+ };
2211
+ });
2212
+ const runSQLMigration = async (databaseType, execute, migration, options) => {
2213
+ const sqls = combineMigrations(migration);
2214
+ const sqlHash = await getMigrationHash(migration, getFormatter(databaseType));
2215
+ try {
2216
+ const newMigration = {
2217
+ name: migration.name,
2218
+ sqlHash
2219
+ };
2220
+ const checkResult = await ensureMigrationWasNotAppliedYet(execute, newMigration);
2221
+ if (checkResult.exists === true) {
2222
+ if (checkResult.hashesMatch === true) {
2223
+ tracer.info("migration-already-applied", { migrationName: migration.name });
2224
+ return false;
2225
+ }
2226
+ if (options?.ignoreMigrationHashMismatch !== true) throw new Error(`Migration hash mismatch for "${migration.name}". Aborting migration.`);
2227
+ tracer.warn("migration-hash-mismatch", {
2228
+ migrationName: migration.name,
2229
+ expectedHash: sqlHash,
2230
+ actualHash: checkResult.hashFromDB
2231
+ });
2232
+ await updateMigrationHash(execute, newMigration);
2233
+ return false;
2234
+ }
2235
+ await execute.batchCommand(sqls, { timeoutMs: options?.migrationTimeoutMs });
2236
+ await recordMigration(execute, newMigration);
2237
+ return true;
2238
+ } catch (error) {
2239
+ tracer.error("migration-error", {
2240
+ migationName: migration.name,
2241
+ error
2242
+ });
2243
+ throw error;
2244
+ }
2222
2245
  };
2223
- const createSingletonClientConnectionPool = (options) => {
2224
- const { driverType, dbClient } = options;
2225
- return createSingletonConnectionPool({
2226
- getConnection: () => options.connectionFactory({ dbClient }),
2227
- driverType
2228
- });
2229
- };
2230
- const createAlwaysNewConnectionPool = (options) => {
2231
- const { driverType, getConnection, connectionOptions } = options;
2232
- return createConnectionPool({
2233
- driverType,
2234
- getConnection: () => connectionOptions ? getConnection(connectionOptions) : getConnection()
2235
- });
2246
+ const getMigrationHash = async (sqlMigration, sqlFormatter) => {
2247
+ const content = sqlFormatter.describe(sqlMigration.sqls, { serializer: JSONSerializer });
2248
+ const data = new TextEncoder().encode(content);
2249
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
2250
+ return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
2236
2251
  };
2237
- const createConnectionPool = (pool) => {
2238
- const { driverType, getConnection } = pool;
2239
- const connection = "connection" in pool ? pool.connection : () => Promise.resolve(getConnection());
2252
+ const combineMigrations = (...migration) => migration.flatMap((m) => m.sqls);
2253
+ const ensureMigrationWasNotAppliedYet = async (execute, migration) => {
2254
+ const result = await singleOrNull(execute.query(SQL`SELECT sql_hash as "sqlHash" FROM dmb_migrations WHERE name = ${migration.name}`));
2255
+ if (result === null) return { exists: false };
2256
+ const { sqlHash } = result;
2240
2257
  return {
2241
- driverType,
2242
- connection,
2243
- withConnection: "withConnection" in pool ? pool.withConnection : (handle, _options) => executeInNewConnection(handle, { connection }),
2244
- close: "close" in pool ? pool.close : () => Promise.resolve(),
2245
- execute: "execute" in pool ? pool.execute : sqlExecutorInNewConnection({
2246
- driverType,
2247
- connection
2248
- }),
2249
- ..."transaction" in pool && "withTransaction" in pool ? {
2250
- transaction: pool.transaction,
2251
- withTransaction: pool.withTransaction
2252
- } : transactionFactoryWithNewConnection(getConnection)
2258
+ exists: true,
2259
+ hashesMatch: sqlHash === migration.sqlHash,
2260
+ hashFromDB: sqlHash
2253
2261
  };
2254
2262
  };
2263
+ const recordMigration = async (execute, migration) => {
2264
+ await execute.command(SQL`
2265
+ INSERT INTO dmb_migrations (name, sql_hash)
2266
+ VALUES (${migration.name}, ${migration.sqlHash})`);
2267
+ };
2268
+ const updateMigrationHash = async (execute, migration) => {
2269
+ await execute.command(SQL`
2270
+ UPDATE dmb_migrations
2271
+ SET sql_hash = ${migration.sqlHash}, timestamp = ${/* @__PURE__ */ new Date()}
2272
+ WHERE name = ${migration.name}
2273
+ `);
2274
+ };
2255
2275
 
2256
2276
  //#endregion
2257
- //#region src/core/index.ts
2258
- SQL.columnN = Object.assign(dumboSchema.column, { type: SQLColumnTypeTokensFactory });
2259
-
2260
- //#endregion
2261
- export { SchemaComponentMigrator as $, JSONCodec as $t, CheckViolationError as A, schemaComponentURN as An, FormatIdentifierProcessor as At, InvalidOperationError as B, IndexURN as Bn, SQLColumnTypeTokens as Bt, mapColumnToDate as C, fromDatabaseDriverType as Cn, TokenizedSQL as Ct, sqlExecutorInAmbientConnection as D, DumboDatabaseDriverRegistry as Dn, SQLProcessorsRegistry as Dt, sqlExecutor as E, toDatabaseDriverType as En, mapDefaultSQLColumnProcessors as Et, DumboError as F, DatabaseSchemaURNType as Fn, BigSerialToken as Ft, SystemError as G, columnSchemaComponent as Gn, VarcharToken as Gt, NotNullViolationError as H, indexSchemaComponent as Hn, SerialToken as Ht, ExclusionViolationError as I, databaseSchemaSchemaComponent as In, ColumnTypeToken as It, MIGRATIONS_LOCK_ID as J, isSchemaComponentOfType as Jn, SQLIn as Jt, TransientDatabaseError as K, filterSchemaComponentsOfType as Kn, SQLArray as Kt, ForeignKeyViolationError as L, TableURN as Ln, IntegerToken as Lt, ConnectionError as M, DatabaseURNType as Mn, SQLProcessor as Mt, DataError as N, databaseSchemaComponent as Nn, AutoIncrementSQLColumnToken as Nt, sqlExecutorInNewConnection as O, canHandleDriverWithConnectionString as On, ExpandArrayProcessor as Ot, DeadlockError as P, DatabaseSchemaURN as Pn, BigIntegerToken as Pt, runSQLMigrations as Q, ParametrizedSQLBuilder as Qt, InsufficientResourcesError as R, TableURNType as Rn, JSONBToken as Rt, mapColumnToBigint as S, resolveDatabaseMetadata as Sn, isSQL as St, mapSQLQueryResult as T, getDatabaseType as Tn, defaultProcessorsRegistry as Tt, QueryCanceledError as U, ColumnURN as Un, TimestampToken as Ut, LockNotAvailableError as V, IndexURNType as Vn, SQLColumnTypeTokensFactory as Vt, SerializationError as W, ColumnURNType as Wn, TimestamptzToken as Wt, getDefaultMigratorOptionsFromRegistry as X, schemaComponent as Xn, SQLPlain as Xt, combineMigrations as Y, mapSchemaComponentsOfType as Yn, SQLLiteral as Yt, registerDefaultMigratorOptions as Z, SQLToken as Zt, transactionFactoryWithNewConnection as _, DumboDatabaseMetadataRegistry as _n, mapSQLIdentifier as _t, createSingletonClientConnectionPool as a, composeJSONReplacers as an, prettyJson as at, executeInNewConnection as b, getDefaultDatabase as bn, RawSQL as bt, TaskProcessor as c, count as cn, describeSQL as ct, createSingletonConnection as d, firstOrNull as dn, registerFormatter as dt, JSONReplacer as en, migrationTableSchemaComponent as et, createTransientConnection as f, single as fn, ANSISQLIdentifierQuote as ft, transactionFactoryWithDbClient as g, dumboSchema as gn, mapANSISQLParamPlaceholder as gt, transactionFactoryWithAsyncAmbientConnection as h, defaultDatabaseLockOptions as hn, SQLValueMapper as ht, createConnectionPool as i, JSONSerializer as in, tracer as it, ConcurrencyError as j, DatabaseURN as jn, MapLiteralProcessor as jt, AdminShutdownError as k, dumboDatabaseDriverRegistry as kn, ExpandSQLInProcessor as kt, createAmbientConnection as l, exists as ln, formatSQL as lt, transactionFactoryWithAmbientConnection as m, NoDatabaseLock as mn, DefaultMapSQLParamValueOptions as mt, createAmbientConnectionPool as n, JSONReviver as nn, LogLevel as nt, createSingletonConnectionPool as o, composeJSONRevivers as on, color as ot, executeInTransaction as p, singleOrNull as pn, ANSISQLParamPlaceholder as pt, UniqueConstraintError as q, findSchemaComponentsOfType as qn, SQLIdentifier as qt, createBoundedConnectionPool as r, JSONRevivers as rn, LogStyle as rt, guardInitializedOnce as s, jsonSerializer as sn, SQLFormatter as st, createAlwaysNewConnectionPool as t, JSONReplacers as tn, sqlMigration as tt, createConnection as u, first as un, getFormatter as ut, BatchCommandNoChangesError as v, dumboDatabaseMetadataRegistry as vn, mapSQLParamValue as vt, mapColumnToJSON as w, getDatabaseDriverName as wn, isTokenizedSQL as wt, executeInNewDbClient as x, getDefaultDatabaseAsync as xn, SQL as xt, executeInAmbientConnection as y, getDatabaseMetadata as yn, ansiSqlReservedMap as yt, IntegrityConstraintViolationError as z, tableSchemaComponent as zn, SQLColumnToken as zt };
2262
- //# sourceMappingURL=core-BG__WVn6.js.map
2277
+ export { SQLArray as $, getDatabaseType as $t, TokenizedSQL as A, sqlExecutorInAmbientConnection as An, TableURNType as At, AutoIncrementSQLColumnToken as B, ForeignKeyViolationError as Bn, isSchemaComponentOfType as Bt, mapANSISQLParamPlaceholder as C, executeInNewConnection as Cn, DatabaseURN as Ct, RawSQL as D, mapColumnToJSON as Dn, DatabaseSchemaURNType as Dt, ansiSqlReservedMap as E, mapColumnToDate as En, DatabaseSchemaURN as Et, ExpandArrayProcessor as F, ConnectionError as Fn, ColumnURN as Ft, JSONBToken as G, NotNullViolationError as Gn, first as Gt, BigSerialToken as H, IntegrityConstraintViolationError as Hn, schemaComponent as Ht, ExpandSQLInProcessor as I, DataError as In, ColumnURNType as It, SQLColumnTypeTokensFactory as J, SystemError as Jn, singleOrNull as Jt, SQLColumnToken as K, QueryCanceledError as Kn, firstOrNull as Kt, FormatIdentifierProcessor as L, DeadlockError as Ln, columnSchemaComponent as Lt, defaultProcessorsRegistry as M, AdminShutdownError as Mn, IndexURN as Mt, mapDefaultSQLColumnProcessors as N, CheckViolationError as Nn, IndexURNType as Nt, SQL as O, mapSQLQueryResult as On, databaseSchemaSchemaComponent as Ot, SQLProcessorsRegistry as P, ConcurrencyError as Pn, indexSchemaComponent as Pt, VarcharToken as Q, getDatabaseDriverName as Qt, MapLiteralProcessor as R, DumboError as Rn, filterSchemaComponentsOfType as Rt, SQLValueMapper as S, executeInAmbientConnection as Sn, schemaComponentURN as St, mapSQLParamValue as T, mapColumnToBigint as Tn, databaseSchemaComponent as Tt, ColumnTypeToken as U, InvalidOperationError as Un, count as Ut, BigIntegerToken as V, InsufficientResourcesError as Vn, mapSchemaComponentsOfType as Vt, IntegerToken as W, LockNotAvailableError as Wn, exists as Wt, TimestampToken as X, UniqueConstraintError as Xn, defaultDatabaseLockOptions as Xt, SerialToken as Y, TransientDatabaseError as Yn, NoDatabaseLock as Yt, TimestamptzToken as Z, fromDatabaseDriverType as Zt, getFormatter as _, transactionFactoryWithAmbientConnection as _n, dumboDatabaseMetadataRegistry as _t, runSQLMigrations as a, createAmbientConnectionPool as an, ParametrizedSQLBuilder as at, ANSISQLParamPlaceholder as b, transactionFactoryWithNewConnection as bn, getDefaultDatabaseAsync as bt, sqlMigration as c, createSingletonClientConnectionPool as cn, JSONReplacers as ct, tracer as d, TaskProcessor as dn, JSONSerializer as dt, toDatabaseDriverType as en, SQLIdentifier as et, prettyJson as f, createAmbientConnection as fn, composeJSONReplacers as ft, formatSQL as g, executeInTransaction as gn, DumboDatabaseMetadataRegistry as gt, describeSQL as h, createTransientConnection as hn, dumboSchema as ht, registerDefaultMigratorOptions as i, createAlwaysNewConnectionPool as in, SQLToken as it, isTokenizedSQL as j, sqlExecutorInNewConnection as jn, tableSchemaComponent as jt, isSQL as k, sqlExecutor as kn, TableURN as kt, LogLevel as l, createSingletonConnectionPool as ln, JSONReviver as lt, SQLFormatter as m, createSingletonConnection as mn, jsonSerializer as mt, combineMigrations as n, canHandleDriverWithConnectionString as nn, SQLLiteral as nt, SchemaComponentMigrator as o, createBoundedConnectionPool as on, JSONCodec as ot, color as p, createConnection as pn, composeJSONRevivers as pt, SQLColumnTypeTokens as q, SerializationError as qn, single as qt, getDefaultMigratorOptionsFromRegistry as r, dumboDatabaseDriverRegistry as rn, SQLPlain as rt, migrationTableSchemaComponent as s, createConnectionPool as sn, JSONReplacer as st, MIGRATIONS_LOCK_ID as t, DumboDatabaseDriverRegistry as tn, SQLIn as tt, LogStyle as u, guardInitializedOnce as un, JSONRevivers as ut, registerFormatter as v, transactionFactoryWithAsyncAmbientConnection as vn, getDatabaseMetadata as vt, mapSQLIdentifier as w, executeInNewDbClient as wn, DatabaseURNType as wt, DefaultMapSQLParamValueOptions as x, BatchCommandNoChangesError as xn, resolveDatabaseMetadata as xt, ANSISQLIdentifierQuote as y, transactionFactoryWithDbClient as yn, getDefaultDatabase as yt, SQLProcessor as z, ExclusionViolationError as zn, findSchemaComponentsOfType as zt };
2278
+ //# sourceMappingURL=core-IV7or0Mj.js.map