@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.
- package/dist/cloudflare.cjs +4 -3
- package/dist/cloudflare.d.cts +3 -3
- package/dist/cloudflare.d.ts +3 -3
- package/dist/cloudflare.js +4 -4
- package/dist/{core-D-smW7cm.cjs → core-BPSzA-lq.cjs} +2091 -2075
- package/dist/core-BPSzA-lq.cjs.map +1 -0
- package/dist/{core-DXCRR5A9.cjs → core-BuSVyamf.cjs} +33 -3
- package/dist/core-BuSVyamf.cjs.map +1 -0
- package/dist/{core-BchvTX3G.js → core-C3xoqqDs.js} +28 -4
- package/dist/core-C3xoqqDs.js.map +1 -0
- package/dist/{core-DlV8ua1P.js → core-CHw8vO17.js} +9 -4
- package/dist/core-CHw8vO17.js.map +1 -0
- package/dist/{core-CWZb0zOY.cjs → core-CUGYxOEQ.cjs} +14 -3
- package/dist/core-CUGYxOEQ.cjs.map +1 -0
- package/dist/{core-BG__WVn6.js → core-IV7or0Mj.js} +2086 -2070
- package/dist/core-IV7or0Mj.js.map +1 -0
- package/dist/{index-N2ly5shv.d.cts → index-BJC_v03L.d.ts} +9 -3
- package/dist/{index-BDSQvDH2.d.cts → index-CfH0u2y_.d.cts} +260 -163
- package/dist/{index-BgFHGsuP.d.ts → index-DP9b7v4e.d.cts} +9 -3
- package/dist/{index-zkszkJqP.d.ts → index-QWEAqtHF.d.ts} +260 -163
- package/dist/{index-BdWFmDhJ.d.cts → index-qxECrBHo.d.ts} +10 -3
- package/dist/{index-cxtbLiju.d.ts → index-tS9lpLPz.d.cts} +10 -3
- package/dist/index.cjs +24 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -3
- package/dist/index.d.ts +2 -3
- package/dist/index.js +24 -13
- package/dist/index.js.map +1 -1
- package/dist/pg.cjs +4 -3
- package/dist/pg.d.cts +3 -3
- package/dist/pg.d.ts +3 -3
- package/dist/pg.js +4 -4
- package/dist/{postgreSQLMetadata-WGXySO7a.cjs → postgreSQLMetadata-CCsCJ-eH.cjs} +2 -2
- package/dist/{postgreSQLMetadata-WGXySO7a.cjs.map → postgreSQLMetadata-CCsCJ-eH.cjs.map} +1 -1
- package/dist/{postgreSQLMetadata-BTz6vdwE.js → postgreSQLMetadata-bCBDGz1f.js} +2 -2
- package/dist/{postgreSQLMetadata-BTz6vdwE.js.map → postgreSQLMetadata-bCBDGz1f.js.map} +1 -1
- package/dist/postgresql.cjs +3 -2
- package/dist/postgresql.d.cts +3 -3
- package/dist/postgresql.d.ts +3 -3
- package/dist/postgresql.js +3 -3
- package/dist/sqlite.cjs +3 -2
- package/dist/sqlite.d.cts +3 -3
- package/dist/sqlite.d.ts +3 -3
- package/dist/sqlite.js +3 -3
- package/dist/sqlite3.cjs +4 -3
- package/dist/sqlite3.d.cts +3 -3
- package/dist/sqlite3.d.ts +3 -3
- package/dist/sqlite3.js +4 -4
- package/dist/{sqliteMetadata-PJTRPIZ1.cjs → sqliteMetadata-Cc7Z03lm.cjs} +2 -2
- package/dist/{sqliteMetadata-PJTRPIZ1.cjs.map → sqliteMetadata-Cc7Z03lm.cjs.map} +1 -1
- package/dist/{sqliteMetadata-7qBjsMBN.js → sqliteMetadata-CvvEc1-v.js} +2 -2
- package/dist/{sqliteMetadata-7qBjsMBN.js.map → sqliteMetadata-CvvEc1-v.js.map} +1 -1
- package/package.json +1 -1
- package/dist/core-BG__WVn6.js.map +0 -1
- package/dist/core-BchvTX3G.js.map +0 -1
- package/dist/core-CWZb0zOY.cjs.map +0 -1
- package/dist/core-D-smW7cm.cjs.map +0 -1
- package/dist/core-DXCRR5A9.cjs.map +0 -1
- package/dist/core-DlV8ua1P.js.map +0 -1
- package/dist/index-DaP2fTEX.d.ts +0 -67
- 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/
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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
|
-
|
|
305
|
-
|
|
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
|
-
|
|
308
|
-
|
|
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
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
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
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
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
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
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/
|
|
390
|
-
const
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
return
|
|
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
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
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
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
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
|
|
411
|
-
const
|
|
412
|
-
|
|
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
|
|
415
|
-
const
|
|
416
|
-
|
|
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/
|
|
421
|
-
const
|
|
422
|
-
|
|
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
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
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
|
|
479
|
-
|
|
480
|
-
const
|
|
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
|
-
|
|
483
|
-
|
|
351
|
+
transaction: getOrInitCurrentTransaction,
|
|
352
|
+
withTransaction: (handle, options) => executeInTransaction(getOrInitCurrentTransaction(options), handle)
|
|
484
353
|
};
|
|
485
354
|
};
|
|
486
|
-
const
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
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
|
-
|
|
503
|
-
|
|
504
|
-
const
|
|
505
|
-
|
|
506
|
-
|
|
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
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
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
|
-
|
|
526
|
-
|
|
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/
|
|
534
|
-
const
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
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
|
-
|
|
555
|
-
const
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
const
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
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
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
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
|
|
598
|
-
const
|
|
599
|
-
const
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
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
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
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/
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
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
|
-
|
|
678
|
-
|
|
679
|
-
|
|
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
|
-
|
|
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
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
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
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
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
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
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
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
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/
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
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
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
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
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
}
|
|
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
|
|
847
|
-
const
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
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
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
});
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
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/
|
|
887
|
-
const
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
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
|
|
1090
|
-
const
|
|
1091
|
-
|
|
1092
|
-
|
|
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
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
883
|
+
register,
|
|
884
|
+
tryResolve,
|
|
885
|
+
tryGet,
|
|
886
|
+
has,
|
|
887
|
+
get databaseDriverTypes() {
|
|
888
|
+
return Array.from(drivers.keys());
|
|
889
|
+
}
|
|
1101
890
|
};
|
|
1102
891
|
};
|
|
1103
|
-
|
|
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/
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
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
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
|
|
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/
|
|
1199
|
-
const
|
|
1200
|
-
const
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
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/
|
|
1240
|
-
const
|
|
1241
|
-
const
|
|
1242
|
-
|
|
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
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
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
|
|
1261
|
-
const
|
|
1262
|
-
if (
|
|
1263
|
-
|
|
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
|
|
1278
|
-
const
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
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
|
|
1285
|
-
const
|
|
1286
|
-
|
|
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
|
|
1295
|
-
|
|
1296
|
-
|
|
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/
|
|
1317
|
-
const
|
|
1318
|
-
const
|
|
1319
|
-
|
|
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
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
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
|
-
|
|
1342
|
-
|
|
1343
|
-
const
|
|
1344
|
-
const
|
|
1345
|
-
const
|
|
1346
|
-
|
|
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
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
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
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
...
|
|
1362
|
-
},
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
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
|
-
|
|
1373
|
-
|
|
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
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
}
|
|
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
|
-
|
|
1397
|
-
|
|
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
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
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
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
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
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
}
|
|
1456
|
-
const
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
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/
|
|
1466
|
-
const
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
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
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
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
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
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
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
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
|
-
|
|
1529
|
-
|
|
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
|
-
|
|
1538
|
-
|
|
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
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
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
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
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
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
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
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
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
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
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
|
-
|
|
1592
|
-
|
|
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
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
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
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
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
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
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
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
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
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
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
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
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
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
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
|
|
1711
|
-
|
|
1712
|
-
|
|
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
|
|
1739
|
-
const
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
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
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
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/
|
|
1755
|
-
const
|
|
1756
|
-
|
|
1757
|
-
|
|
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
|
-
|
|
1782
|
-
|
|
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
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
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
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1365
|
+
sqlTokenType,
|
|
1366
|
+
[sqlTokenType]: true,
|
|
1367
|
+
...props
|
|
1815
1368
|
};
|
|
1816
|
-
}
|
|
1817
|
-
|
|
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
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
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
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
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/
|
|
1897
|
-
const
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
const
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
}
|
|
1911
|
-
|
|
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
|
|
1936
|
-
|
|
1937
|
-
|
|
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
|
|
1948
|
-
const
|
|
1949
|
-
|
|
1950
|
-
|
|
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
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
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/
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
}
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
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
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
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
|
-
|
|
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
|
|
2055
|
-
const
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
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/
|
|
2079
|
-
const
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
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
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
return
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
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/
|
|
2175
|
-
const
|
|
2176
|
-
|
|
2177
|
-
|
|
2149
|
+
//#region src/core/schema/sqlMigration.ts
|
|
2150
|
+
const sqlMigration = (name, sqls) => ({
|
|
2151
|
+
name,
|
|
2152
|
+
sqls
|
|
2178
2153
|
});
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
};
|
|
2193
|
-
const
|
|
2194
|
-
const
|
|
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
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
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
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
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
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
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
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
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
|
|
2252
|
-
const
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
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
|
|
2266
|
-
|
|
2267
|
-
const
|
|
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
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
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
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
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-
|
|
3259
|
+
//# sourceMappingURL=core-BPSzA-lq.cjs.map
|