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