@getstrata/core 0.5.13 → 0.5.15
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/README.md +16 -6
- package/dist/bootstrap/contracts.d.ts +2 -0
- package/dist/bootstrap/httpKernel.d.ts +1 -2
- package/dist/core/admin/registry.d.ts +1 -0
- package/dist/core/auth/membershipService.d.ts +5 -1
- package/dist/core/database/baseRepository.d.ts +6 -1
- package/dist/core/database/connectionContext.d.ts +2 -1
- package/dist/core/database/defaultConnection.d.ts +6 -0
- package/dist/core/database/queryProxy.d.ts +3 -0
- package/dist/core/database/repositoryConnection.d.ts +3 -3
- package/dist/core/jobs/dispatchWebhookJob.d.ts +0 -1
- package/dist/core/security/safeFetch.d.ts +2 -0
- package/dist/core/security/safeUrl.d.ts +16 -1
- package/dist/core/storage/storage.d.ts +6 -3
- package/dist/core/tenant/tenantDatabaseScope.d.ts +2 -1
- package/dist/db/connection/index.d.ts +1 -1
- package/dist/entries/auth/accessControl.js +1 -113
- package/dist/entries/auth/authContext.js +1 -15
- package/dist/entries/auth/guard.js +1 -3044
- package/dist/entries/auth/membershipContext.js +1 -276
- package/dist/entries/auth/membershipScope.js +1 -390
- package/dist/entries/auth/membershipService.js +1 -480
- package/dist/entries/auth/policy.js +1 -134
- package/dist/entries/database.js +1 -2398
- package/dist/entries/http/csrfToken.js +0 -6
- package/dist/entries/http/middleware.js +1 -68
- package/dist/entries/http/requestMetaContext.js +1 -18
- package/dist/entries/http/webErrorResponse.js +94 -563
- package/dist/entries/http/webFormRequest.js +0 -131
- package/dist/entries/http.js +1 -4091
- package/dist/entries/jobs/dispatchWebhookJob.js +109 -102
- package/dist/entries/queue/createAppQueue.js +111 -631
- package/dist/entries/queue/jobRunner.js +0 -3
- package/dist/entries/queue/publicQueue.js +45 -546
- package/dist/entries/queue/queueMetrics.js +111 -631
- package/dist/entries/security/safeUrl.js +29 -0
- package/dist/entries/security/securityEvents.js +1 -41
- package/dist/entries/storage/storage.js +14 -4
- package/dist/entries/tenant/tenantContext.js +1 -30
- package/dist/entries/tenant/tenantMiddleware.js +1 -312
- package/dist/entries/tracing/traceContext.js +1 -15
- package/dist/entries/view.js +94 -563
- package/dist/framework/public-api.d.ts +36 -4
- package/dist/index.js +2270 -1666
- package/dist/modules/user/repository.d.ts +1 -0
- package/package.json +6 -5
|
@@ -36,9 +36,6 @@ class Logger {
|
|
|
36
36
|
var appLogger = new Logger("app");
|
|
37
37
|
|
|
38
38
|
// ../../src/bootstrap/config.ts
|
|
39
|
-
var CORE_QUEUE_TOKEN = "core.queue";
|
|
40
|
-
var CORE_POLICY_GATE_TOKEN = "core.policyGate";
|
|
41
|
-
var CORE_AUTH_TOKEN = "core.auth";
|
|
42
39
|
var CORE_TOKEN_SERVICE_TOKEN = "core.tokenService";
|
|
43
40
|
var DEFAULT_QUEUE_DRIVER = "sync";
|
|
44
41
|
|
|
@@ -113,15 +110,9 @@ function getRequiredDependency(dependencies, key) {
|
|
|
113
110
|
}
|
|
114
111
|
return dependency;
|
|
115
112
|
}
|
|
116
|
-
function resolveService(dependencies, token) {
|
|
117
|
-
return dependencies.container.resolve(token);
|
|
118
|
-
}
|
|
119
113
|
|
|
120
114
|
// ../../src/bootstrap/applicationRegistry.ts
|
|
121
115
|
var activeContext;
|
|
122
|
-
function setActiveApplicationContext(context) {
|
|
123
|
-
activeContext = context;
|
|
124
|
-
}
|
|
125
116
|
function requireActiveApplicationContext() {
|
|
126
117
|
if (!activeContext) {
|
|
127
118
|
throw new Error("The application context has not been bootstrapped.");
|
|
@@ -131,24 +122,6 @@ function requireActiveApplicationContext() {
|
|
|
131
122
|
function resolveApplicationCache() {
|
|
132
123
|
return getRequiredDependency(requireActiveApplicationContext().dependencies, "cache");
|
|
133
124
|
}
|
|
134
|
-
function resolveApplicationQueue() {
|
|
135
|
-
return requireActiveApplicationContext().container.resolve(CORE_QUEUE_TOKEN);
|
|
136
|
-
}
|
|
137
|
-
function resolveApplicationAuth() {
|
|
138
|
-
return requireActiveApplicationContext().container.resolve(CORE_AUTH_TOKEN);
|
|
139
|
-
}
|
|
140
|
-
function resolveApplicationPolicyGate() {
|
|
141
|
-
return requireActiveApplicationContext().container.resolve(CORE_POLICY_GATE_TOKEN);
|
|
142
|
-
}
|
|
143
|
-
function resolveApplicationConfig() {
|
|
144
|
-
return requireActiveApplicationContext().config;
|
|
145
|
-
}
|
|
146
|
-
function resolveApplicationLogger() {
|
|
147
|
-
return appLogger;
|
|
148
|
-
}
|
|
149
|
-
function resolveApplicationDependencies() {
|
|
150
|
-
return requireActiveApplicationContext().dependencies;
|
|
151
|
-
}
|
|
152
125
|
|
|
153
126
|
// ../../src/core/jobs/dispatchWebhookJob.ts
|
|
154
127
|
import { createHmac } from "crypto";
|
|
@@ -162,75 +135,77 @@ var appConfig = {
|
|
|
162
135
|
apiPrefix: process.env.API_PREFIX ?? "/api/v1"
|
|
163
136
|
};
|
|
164
137
|
|
|
165
|
-
// ../../src/
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
return Number.isInteger(parsed) && parsed >= 0 ? parsed : fallback;
|
|
169
|
-
}
|
|
170
|
-
var databaseConfig = {
|
|
171
|
-
url: process.env.DATABASE_URL ?? "",
|
|
172
|
-
poolMax: readInteger("DB_POOL_MAX", 10),
|
|
173
|
-
idleTimeoutSeconds: readInteger("DB_POOL_IDLE_TIMEOUT", 30),
|
|
174
|
-
maxLifetimeSeconds: readInteger("DB_POOL_MAX_LIFETIME", 3600),
|
|
175
|
-
connectionTimeoutSeconds: readInteger("DB_CONNECTION_TIMEOUT", 10)
|
|
138
|
+
// ../../src/core/database/boundConnection.ts
|
|
139
|
+
var boundConnectionHolder = {
|
|
140
|
+
connection: null
|
|
176
141
|
};
|
|
142
|
+
function getBoundDatabaseConnection() {
|
|
143
|
+
return boundConnectionHolder.connection;
|
|
144
|
+
}
|
|
177
145
|
|
|
178
146
|
// ../../src/core/database/connectionContext.ts
|
|
179
147
|
import { AsyncLocalStorage } from "async_hooks";
|
|
180
148
|
var activeConnection = new AsyncLocalStorage;
|
|
181
|
-
function runWithDatabaseConnection(connection, callback) {
|
|
182
|
-
return activeConnection.run(connection, callback);
|
|
183
|
-
}
|
|
184
149
|
function getActiveDatabaseConnection(fallback) {
|
|
185
150
|
return activeConnection.getStore() ?? fallback;
|
|
186
151
|
}
|
|
187
152
|
|
|
188
|
-
// ../../src/
|
|
189
|
-
var
|
|
190
|
-
function
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
153
|
+
// ../../src/core/database/queryProxy.ts
|
|
154
|
+
var POOL_CONNECTION_METHODS = new Set(["begin", "close", "connect"]);
|
|
155
|
+
function createDatabaseQueryProxy(pool) {
|
|
156
|
+
function resolveDatabase() {
|
|
157
|
+
return getActiveDatabaseConnection(pool);
|
|
158
|
+
}
|
|
159
|
+
function resolveDatabaseForProperty(property) {
|
|
160
|
+
if (typeof property === "string" && POOL_CONNECTION_METHODS.has(property)) {
|
|
161
|
+
return pool;
|
|
162
|
+
}
|
|
163
|
+
return resolveDatabase();
|
|
164
|
+
}
|
|
165
|
+
return new Proxy(function database() {}, {
|
|
166
|
+
apply(_target, _thisArg, args) {
|
|
167
|
+
return resolveDatabase()(...args);
|
|
168
|
+
},
|
|
169
|
+
get(_target, property) {
|
|
170
|
+
const connection = resolveDatabaseForProperty(property);
|
|
171
|
+
const value = connection[property];
|
|
172
|
+
return typeof value === "function" ? value.bind(connection) : value;
|
|
173
|
+
}
|
|
200
174
|
});
|
|
201
175
|
}
|
|
202
176
|
|
|
203
|
-
// ../../src/
|
|
204
|
-
var
|
|
177
|
+
// ../../src/core/database/defaultConnection.ts
|
|
178
|
+
var defaultPool = {
|
|
205
179
|
connection: null
|
|
206
180
|
};
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
var POOL_CONNECTION_METHODS = new Set(["begin", "close", "connect"]);
|
|
214
|
-
function resolveDatabase() {
|
|
215
|
-
return getActiveDatabaseConnection(getDatabase());
|
|
181
|
+
var defaultQuery = {
|
|
182
|
+
connection: null
|
|
183
|
+
};
|
|
184
|
+
function registerDefaultDatabasePool(connection) {
|
|
185
|
+
defaultPool.connection = connection;
|
|
186
|
+
defaultQuery.connection = createDatabaseQueryProxy(connection);
|
|
216
187
|
}
|
|
217
|
-
function
|
|
218
|
-
if (
|
|
219
|
-
|
|
188
|
+
function getDefaultDatabaseQuery() {
|
|
189
|
+
if (!defaultQuery.connection) {
|
|
190
|
+
throw new Error("Default database query handle is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
220
191
|
}
|
|
221
|
-
return
|
|
192
|
+
return defaultQuery.connection;
|
|
222
193
|
}
|
|
223
|
-
|
|
194
|
+
|
|
195
|
+
// ../../src/core/database/repositoryConnection.ts
|
|
196
|
+
function resolveRepositoryConnection() {
|
|
197
|
+
return getBoundDatabaseConnection() ?? getDefaultDatabaseQuery();
|
|
198
|
+
}
|
|
199
|
+
var repositoryConnection = new Proxy(function repositoryConnection2() {}, {
|
|
224
200
|
apply(_target, _thisArg, args) {
|
|
225
|
-
return
|
|
201
|
+
return resolveRepositoryConnection()(...args);
|
|
226
202
|
},
|
|
227
203
|
get(_target, property) {
|
|
228
|
-
const connection =
|
|
204
|
+
const connection = resolveRepositoryConnection();
|
|
229
205
|
const value = connection[property];
|
|
230
206
|
return typeof value === "function" ? value.bind(connection) : value;
|
|
231
207
|
}
|
|
232
208
|
});
|
|
233
|
-
var connection_default = db;
|
|
234
209
|
|
|
235
210
|
// ../../src/core/queue/index.ts
|
|
236
211
|
class Job {
|
|
@@ -239,56 +214,8 @@ class Job {
|
|
|
239
214
|
priority;
|
|
240
215
|
}
|
|
241
216
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
await job.handle(payload);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
class AsyncQueue {
|
|
249
|
-
async dispatch(job, payload) {
|
|
250
|
-
setTimeout(() => {
|
|
251
|
-
job.handle(payload).catch((error) => {
|
|
252
|
-
console.error("[AsyncQueue] Job failed:", error);
|
|
253
|
-
});
|
|
254
|
-
}, 0);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
function createQueue(driver) {
|
|
258
|
-
return driver === "async" ? new AsyncQueue : new SyncQueue;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// ../../src/core/security/safeFetch.ts
|
|
262
|
-
var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
|
|
263
|
-
async function safeFetch(input, init = {}, options = {}) {
|
|
264
|
-
const timeoutMs = options.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
|
|
265
|
-
const maxRedirects = options.maxRedirects ?? 0;
|
|
266
|
-
const controller = new AbortController;
|
|
267
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
268
|
-
try {
|
|
269
|
-
let currentUrl = input;
|
|
270
|
-
let redirectCount = 0;
|
|
271
|
-
while (true) {
|
|
272
|
-
const response = await fetch(currentUrl, {
|
|
273
|
-
...init,
|
|
274
|
-
signal: controller.signal,
|
|
275
|
-
redirect: "manual"
|
|
276
|
-
});
|
|
277
|
-
if (response.status >= 300 && response.status < 400) {
|
|
278
|
-
const location = response.headers.get("location");
|
|
279
|
-
if (!location || redirectCount >= maxRedirects) {
|
|
280
|
-
return response;
|
|
281
|
-
}
|
|
282
|
-
currentUrl = new URL(location, currentUrl).toString();
|
|
283
|
-
redirectCount += 1;
|
|
284
|
-
continue;
|
|
285
|
-
}
|
|
286
|
-
return response;
|
|
287
|
-
}
|
|
288
|
-
} finally {
|
|
289
|
-
clearTimeout(timeout);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
217
|
+
// ../../src/core/security/safeUrl.ts
|
|
218
|
+
import { lookup as dnsLookupImpl } from "dns/promises";
|
|
292
219
|
|
|
293
220
|
// ../../src/core/errors/http.ts
|
|
294
221
|
class HttpError extends Error {
|
|
@@ -357,6 +284,7 @@ class PreconditionFailedError extends HttpError {
|
|
|
357
284
|
}
|
|
358
285
|
|
|
359
286
|
// ../../src/core/security/safeUrl.ts
|
|
287
|
+
var dnsLookup = dnsLookupImpl;
|
|
360
288
|
var BLOCKED_HOSTNAMES = new Set([
|
|
361
289
|
"localhost",
|
|
362
290
|
"127.0.0.1",
|
|
@@ -428,16 +356,68 @@ function assertSafeOutboundUrl(rawUrl, options = {}) {
|
|
|
428
356
|
}
|
|
429
357
|
return parsed;
|
|
430
358
|
}
|
|
359
|
+
function isBlockedIpAddress(address) {
|
|
360
|
+
return isBlockedHostname(address.trim().toLowerCase());
|
|
361
|
+
}
|
|
362
|
+
async function assertSafeOutboundUrlResolved(rawUrl, options = {}) {
|
|
363
|
+
const parsed = assertSafeOutboundUrl(rawUrl, options);
|
|
364
|
+
if (options.resolveDns === false) {
|
|
365
|
+
return parsed;
|
|
366
|
+
}
|
|
367
|
+
const hostname = parsed.hostname.trim().toLowerCase();
|
|
368
|
+
const results = await dnsLookup(hostname, { all: true, verbatim: true });
|
|
369
|
+
if (results.some((result) => isBlockedIpAddress(result.address))) {
|
|
370
|
+
throw new BadRequestError("Webhook URL targets a blocked host.");
|
|
371
|
+
}
|
|
372
|
+
return parsed;
|
|
373
|
+
}
|
|
374
|
+
function setDnsLookupForTests(lookupFn) {
|
|
375
|
+
dnsLookup = lookupFn;
|
|
376
|
+
}
|
|
377
|
+
function resetDnsLookupForTests() {
|
|
378
|
+
dnsLookup = dnsLookupImpl;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// ../../src/core/security/safeFetch.ts
|
|
382
|
+
var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
|
|
383
|
+
async function safeFetch(input, init = {}, options = {}) {
|
|
384
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
|
|
385
|
+
const maxRedirects = options.maxRedirects ?? 0;
|
|
386
|
+
const resolveDns = options.resolveDns ?? appConfig.env === "production";
|
|
387
|
+
const urlOptions = { allowHttp: options.allowHttp, resolveDns };
|
|
388
|
+
const controller = new AbortController;
|
|
389
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
390
|
+
try {
|
|
391
|
+
let currentUrl = (await assertSafeOutboundUrlResolved(input, urlOptions)).toString();
|
|
392
|
+
let redirectCount = 0;
|
|
393
|
+
while (true) {
|
|
394
|
+
const response = await fetch(currentUrl, {
|
|
395
|
+
...init,
|
|
396
|
+
signal: controller.signal,
|
|
397
|
+
redirect: "manual"
|
|
398
|
+
});
|
|
399
|
+
if (response.status >= 300 && response.status < 400) {
|
|
400
|
+
const location = response.headers.get("location");
|
|
401
|
+
if (!location || redirectCount >= maxRedirects) {
|
|
402
|
+
return response;
|
|
403
|
+
}
|
|
404
|
+
currentUrl = (await assertSafeOutboundUrlResolved(new URL(location, currentUrl).toString(), urlOptions)).toString();
|
|
405
|
+
redirectCount += 1;
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
return response;
|
|
409
|
+
}
|
|
410
|
+
} finally {
|
|
411
|
+
clearTimeout(timeout);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
431
414
|
|
|
432
415
|
// ../../src/core/jobs/dispatchWebhookJob.ts
|
|
433
416
|
class DispatchWebhookJob extends Job {
|
|
434
|
-
constructor() {
|
|
435
|
-
super();
|
|
436
|
-
}
|
|
437
417
|
maxAttempts = 3;
|
|
438
418
|
backoffMs = 2000;
|
|
439
419
|
async handle(payload) {
|
|
440
|
-
const rows = await
|
|
420
|
+
const rows = await repositoryConnection`
|
|
441
421
|
SELECT id, url, secret
|
|
442
422
|
FROM webhook
|
|
443
423
|
WHERE id = ${payload.webhookId} AND active = TRUE
|
|
@@ -460,14 +440,14 @@ class DispatchWebhookJob extends Job {
|
|
|
460
440
|
"x-workhub-signature": signature
|
|
461
441
|
},
|
|
462
442
|
body
|
|
463
|
-
});
|
|
443
|
+
}, { allowHttp: appConfig.env !== "production" });
|
|
464
444
|
responseStatus = response.status;
|
|
465
445
|
if (!response.ok) {
|
|
466
446
|
throw new Error(`Webhook delivery failed with status ${response.status}.`);
|
|
467
447
|
}
|
|
468
448
|
} catch (error) {
|
|
469
449
|
errorMessage = error instanceof Error ? error.message : String(error);
|
|
470
|
-
await
|
|
450
|
+
await repositoryConnection`
|
|
471
451
|
INSERT INTO webhook_delivery (webhook_id, event, payload, response_status, error)
|
|
472
452
|
VALUES (
|
|
473
453
|
${webhook.id},
|
|
@@ -479,7 +459,7 @@ class DispatchWebhookJob extends Job {
|
|
|
479
459
|
`;
|
|
480
460
|
throw error instanceof Error ? error : new Error(errorMessage);
|
|
481
461
|
}
|
|
482
|
-
await
|
|
462
|
+
await repositoryConnection`
|
|
483
463
|
INSERT INTO webhook_delivery (webhook_id, event, payload, response_status)
|
|
484
464
|
VALUES (
|
|
485
465
|
${webhook.id},
|
|
@@ -738,14 +718,6 @@ function appendWhereParts(tableName, where, params) {
|
|
|
738
718
|
}
|
|
739
719
|
return clauses.join(" AND ");
|
|
740
720
|
}
|
|
741
|
-
function buildWhereClause(tableName, where = {}) {
|
|
742
|
-
const params = [];
|
|
743
|
-
const body = appendWhereParts(tableName, where, params);
|
|
744
|
-
return {
|
|
745
|
-
clause: body.length > 0 ? ` WHERE ${body}` : "",
|
|
746
|
-
params
|
|
747
|
-
};
|
|
748
|
-
}
|
|
749
721
|
function buildWhereNodeClause(tableName, node, params) {
|
|
750
722
|
if ("where" in node) {
|
|
751
723
|
return appendWhereParts(tableName, node.where, params);
|
|
@@ -1048,30 +1020,6 @@ function buildDeleteByIdQuery(table, id) {
|
|
|
1048
1020
|
}
|
|
1049
1021
|
|
|
1050
1022
|
// ../../src/core/database/relationships.ts
|
|
1051
|
-
function hasMany(definition) {
|
|
1052
|
-
return {
|
|
1053
|
-
type: "hasMany",
|
|
1054
|
-
...definition
|
|
1055
|
-
};
|
|
1056
|
-
}
|
|
1057
|
-
function hasOne(definition) {
|
|
1058
|
-
return {
|
|
1059
|
-
type: "hasOne",
|
|
1060
|
-
...definition
|
|
1061
|
-
};
|
|
1062
|
-
}
|
|
1063
|
-
function belongsTo(definition) {
|
|
1064
|
-
return {
|
|
1065
|
-
type: "belongsTo",
|
|
1066
|
-
...definition
|
|
1067
|
-
};
|
|
1068
|
-
}
|
|
1069
|
-
function belongsToMany(definition) {
|
|
1070
|
-
return {
|
|
1071
|
-
type: "belongsToMany",
|
|
1072
|
-
...definition
|
|
1073
|
-
};
|
|
1074
|
-
}
|
|
1075
1023
|
function indexHasManyRelation(parents, children, relation) {
|
|
1076
1024
|
const groups = new Map;
|
|
1077
1025
|
for (const parent of parents) {
|
|
@@ -1087,15 +1035,6 @@ function indexHasManyRelation(parents, children, relation) {
|
|
|
1087
1035
|
}
|
|
1088
1036
|
return groups;
|
|
1089
1037
|
}
|
|
1090
|
-
function indexHasOneRelation(parents, children, relation) {
|
|
1091
|
-
const grouped = indexHasManyRelation(parents, children, relation);
|
|
1092
|
-
const result = new Map;
|
|
1093
|
-
for (const parent of parents) {
|
|
1094
|
-
const matches = grouped.get(parent[relation.localKey]) ?? [];
|
|
1095
|
-
result.set(parent[relation.localKey], matches[0]);
|
|
1096
|
-
}
|
|
1097
|
-
return result;
|
|
1098
|
-
}
|
|
1099
1038
|
function indexBelongsToRelation(children, parents, relation) {
|
|
1100
1039
|
const parentsById = new Map;
|
|
1101
1040
|
for (const parent of parents) {
|
|
@@ -1111,45 +1050,6 @@ function indexBelongsToRelation(children, parents, relation) {
|
|
|
1111
1050
|
}
|
|
1112
1051
|
return result;
|
|
1113
1052
|
}
|
|
1114
|
-
function indexBelongsToManyRelation(parents, pivotRows, relatedRows, relation) {
|
|
1115
|
-
const relatedById = new Map;
|
|
1116
|
-
for (const related of relatedRows) {
|
|
1117
|
-
relatedById.set(related[relation.relatedKey], related);
|
|
1118
|
-
}
|
|
1119
|
-
const groups = new Map;
|
|
1120
|
-
for (const parent of parents) {
|
|
1121
|
-
groups.set(parent[relation.parentKey], []);
|
|
1122
|
-
}
|
|
1123
|
-
for (const pivot of pivotRows) {
|
|
1124
|
-
const parentId = pivot[relation.foreignPivotKey];
|
|
1125
|
-
const relatedId = pivot[relation.relatedPivotKey];
|
|
1126
|
-
const group = groups.get(parentId);
|
|
1127
|
-
const related = relatedById.get(relatedId);
|
|
1128
|
-
if (!group || !related) {
|
|
1129
|
-
continue;
|
|
1130
|
-
}
|
|
1131
|
-
group.push(related);
|
|
1132
|
-
}
|
|
1133
|
-
return groups;
|
|
1134
|
-
}
|
|
1135
|
-
function morphMany(definition) {
|
|
1136
|
-
return {
|
|
1137
|
-
type: "morphMany",
|
|
1138
|
-
...definition
|
|
1139
|
-
};
|
|
1140
|
-
}
|
|
1141
|
-
function morphOne(definition) {
|
|
1142
|
-
return {
|
|
1143
|
-
type: "morphOne",
|
|
1144
|
-
...definition
|
|
1145
|
-
};
|
|
1146
|
-
}
|
|
1147
|
-
function morphTo(definition) {
|
|
1148
|
-
return {
|
|
1149
|
-
type: "morphTo",
|
|
1150
|
-
...definition
|
|
1151
|
-
};
|
|
1152
|
-
}
|
|
1153
1053
|
function indexMorphManyRelation(parents, children, relation) {
|
|
1154
1054
|
const groups = new Map;
|
|
1155
1055
|
for (const parent of parents) {
|
|
@@ -1168,15 +1068,6 @@ function indexMorphManyRelation(parents, children, relation) {
|
|
|
1168
1068
|
}
|
|
1169
1069
|
return groups;
|
|
1170
1070
|
}
|
|
1171
|
-
function indexMorphOneRelation(parents, children, relation) {
|
|
1172
|
-
const grouped = indexMorphManyRelation(parents, children, relation);
|
|
1173
|
-
const result = new Map;
|
|
1174
|
-
for (const parent of parents) {
|
|
1175
|
-
const matches = grouped.get(parent[relation.localKey]) ?? [];
|
|
1176
|
-
result.set(parent[relation.localKey], matches[0]);
|
|
1177
|
-
}
|
|
1178
|
-
return result;
|
|
1179
|
-
}
|
|
1180
1071
|
function indexMorphToRelation(children, parentsByType, relation) {
|
|
1181
1072
|
const result = new Map;
|
|
1182
1073
|
for (const child of children) {
|
|
@@ -1193,29 +1084,6 @@ function indexMorphToRelation(children, parentsByType, relation) {
|
|
|
1193
1084
|
return result;
|
|
1194
1085
|
}
|
|
1195
1086
|
|
|
1196
|
-
// ../../src/core/database/boundConnection.ts
|
|
1197
|
-
var boundConnectionHolder = {
|
|
1198
|
-
connection: null
|
|
1199
|
-
};
|
|
1200
|
-
function bindDatabaseConnection(connection) {
|
|
1201
|
-
boundConnectionHolder.connection = connection;
|
|
1202
|
-
}
|
|
1203
|
-
function getBoundDatabaseConnection() {
|
|
1204
|
-
return boundConnectionHolder.connection;
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
// ../../src/core/database/repositoryConnection.ts
|
|
1208
|
-
function resolveRepositoryConnection() {
|
|
1209
|
-
return getBoundDatabaseConnection() ?? connection_default;
|
|
1210
|
-
}
|
|
1211
|
-
var repositoryConnection = new Proxy({}, {
|
|
1212
|
-
get(_target, property) {
|
|
1213
|
-
const connection = resolveRepositoryConnection();
|
|
1214
|
-
const value = connection[property];
|
|
1215
|
-
return typeof value === "function" ? value.bind(connection) : value;
|
|
1216
|
-
}
|
|
1217
|
-
});
|
|
1218
|
-
|
|
1219
1087
|
// ../../src/core/database/whereBuilder.ts
|
|
1220
1088
|
class WhereBuilder {
|
|
1221
1089
|
nodes = [];
|
|
@@ -1771,327 +1639,10 @@ class BaseRepository {
|
|
|
1771
1639
|
}
|
|
1772
1640
|
}
|
|
1773
1641
|
var baseRepository_default = BaseRepository;
|
|
1774
|
-
// ../../src/core/database/connection.ts
|
|
1775
|
-
function createDatabaseConnection2(source) {
|
|
1776
|
-
return {
|
|
1777
|
-
async unsafe(query, params = []) {
|
|
1778
|
-
return await source.unsafe(query, params);
|
|
1779
|
-
}
|
|
1780
|
-
};
|
|
1781
|
-
}
|
|
1782
1642
|
// ../../src/core/database/model.ts
|
|
1783
1643
|
var modelRepositories = new WeakMap;
|
|
1784
1644
|
var modelGlobalScopes = new WeakMap;
|
|
1785
1645
|
var modelBooted = new WeakSet;
|
|
1786
|
-
function resolveModelRepository(model) {
|
|
1787
|
-
const repository = modelRepositories.get(model);
|
|
1788
|
-
if (!repository) {
|
|
1789
|
-
throw new Error(`${model.name}.repository() is not implemented.`);
|
|
1790
|
-
}
|
|
1791
|
-
return repository;
|
|
1792
|
-
}
|
|
1793
|
-
function modelStatics(model) {
|
|
1794
|
-
return model;
|
|
1795
|
-
}
|
|
1796
|
-
function ensureBooted(model) {
|
|
1797
|
-
if (modelBooted.has(model)) {
|
|
1798
|
-
return;
|
|
1799
|
-
}
|
|
1800
|
-
modelBooted.add(model);
|
|
1801
|
-
const boot = model.boot;
|
|
1802
|
-
if (typeof boot === "function") {
|
|
1803
|
-
boot.call(model);
|
|
1804
|
-
}
|
|
1805
|
-
}
|
|
1806
|
-
function getGlobalScopes(model) {
|
|
1807
|
-
return modelGlobalScopes.get(model) ?? [];
|
|
1808
|
-
}
|
|
1809
|
-
function hydrateValue(value, cast) {
|
|
1810
|
-
if (value === null || value === undefined) {
|
|
1811
|
-
return value;
|
|
1812
|
-
}
|
|
1813
|
-
switch (cast) {
|
|
1814
|
-
case "date":
|
|
1815
|
-
case "datetime":
|
|
1816
|
-
return value instanceof Date ? value : new Date(String(value));
|
|
1817
|
-
case "json":
|
|
1818
|
-
return typeof value === "string" ? JSON.parse(value) : value;
|
|
1819
|
-
case "bool":
|
|
1820
|
-
case "boolean":
|
|
1821
|
-
return value === true || value === 1 || value === "1" || value === "true";
|
|
1822
|
-
default:
|
|
1823
|
-
return value;
|
|
1824
|
-
}
|
|
1825
|
-
}
|
|
1826
|
-
function dehydrateValue(value, cast) {
|
|
1827
|
-
if (value === null || value === undefined) {
|
|
1828
|
-
return value;
|
|
1829
|
-
}
|
|
1830
|
-
switch (cast) {
|
|
1831
|
-
case "date":
|
|
1832
|
-
case "datetime":
|
|
1833
|
-
return value instanceof Date ? value : new Date(String(value));
|
|
1834
|
-
case "json":
|
|
1835
|
-
return typeof value === "string" ? value : JSON.stringify(value);
|
|
1836
|
-
case "bool":
|
|
1837
|
-
case "boolean":
|
|
1838
|
-
return Boolean(value);
|
|
1839
|
-
default:
|
|
1840
|
-
return value;
|
|
1841
|
-
}
|
|
1842
|
-
}
|
|
1843
|
-
function filterMassAssignable(fillable, guarded, input) {
|
|
1844
|
-
const resolvedGuarded = guarded ?? true;
|
|
1845
|
-
if (fillable && fillable.length > 0) {
|
|
1846
|
-
const allowed = new Set(fillable);
|
|
1847
|
-
return Object.fromEntries(Object.entries(input).filter(([key]) => allowed.has(key)));
|
|
1848
|
-
}
|
|
1849
|
-
if (resolvedGuarded === true || resolvedGuarded.includes("*")) {
|
|
1850
|
-
return {};
|
|
1851
|
-
}
|
|
1852
|
-
const blocked = new Set(resolvedGuarded);
|
|
1853
|
-
return Object.fromEntries(Object.entries(input).filter(([key]) => !blocked.has(key)));
|
|
1854
|
-
}
|
|
1855
|
-
function applyCasts(values, casts, direction) {
|
|
1856
|
-
if (Object.keys(casts).length === 0) {
|
|
1857
|
-
return values;
|
|
1858
|
-
}
|
|
1859
|
-
const result = { ...values };
|
|
1860
|
-
const castFn = direction === "hydrate" ? hydrateValue : dehydrateValue;
|
|
1861
|
-
for (const [key, cast] of Object.entries(casts)) {
|
|
1862
|
-
if (key in result && cast) {
|
|
1863
|
-
result[key] = castFn(result[key], cast);
|
|
1864
|
-
}
|
|
1865
|
-
}
|
|
1866
|
-
return result;
|
|
1867
|
-
}
|
|
1868
|
-
function applyTimestampsOnCreate(columns, values, enabled) {
|
|
1869
|
-
if (!enabled) {
|
|
1870
|
-
return values;
|
|
1871
|
-
}
|
|
1872
|
-
const now = new Date;
|
|
1873
|
-
const result = { ...values };
|
|
1874
|
-
if (columns.includes("created_at")) {
|
|
1875
|
-
result.created_at = now;
|
|
1876
|
-
}
|
|
1877
|
-
if (columns.includes("updated_at")) {
|
|
1878
|
-
result.updated_at = now;
|
|
1879
|
-
}
|
|
1880
|
-
return result;
|
|
1881
|
-
}
|
|
1882
|
-
function applyTimestampsOnUpdate(columns, values, enabled) {
|
|
1883
|
-
if (!enabled) {
|
|
1884
|
-
return values;
|
|
1885
|
-
}
|
|
1886
|
-
const result = { ...values };
|
|
1887
|
-
if (columns.includes("updated_at")) {
|
|
1888
|
-
result.updated_at = new Date;
|
|
1889
|
-
}
|
|
1890
|
-
return result;
|
|
1891
|
-
}
|
|
1892
|
-
|
|
1893
|
-
class Model {
|
|
1894
|
-
attributes;
|
|
1895
|
-
repository;
|
|
1896
|
-
static $fillable;
|
|
1897
|
-
static $guarded;
|
|
1898
|
-
static $casts = {};
|
|
1899
|
-
static $timestamps = true;
|
|
1900
|
-
_exists;
|
|
1901
|
-
constructor(attributes, repository, exists = true) {
|
|
1902
|
-
this.attributes = attributes;
|
|
1903
|
-
this.repository = repository;
|
|
1904
|
-
this._exists = exists;
|
|
1905
|
-
}
|
|
1906
|
-
get $exists() {
|
|
1907
|
-
return this._exists;
|
|
1908
|
-
}
|
|
1909
|
-
get(key) {
|
|
1910
|
-
return this.attributes[key];
|
|
1911
|
-
}
|
|
1912
|
-
get id() {
|
|
1913
|
-
return this.attributes[this.primaryKey()];
|
|
1914
|
-
}
|
|
1915
|
-
toObject() {
|
|
1916
|
-
return { ...this.attributes };
|
|
1917
|
-
}
|
|
1918
|
-
primaryKey() {
|
|
1919
|
-
throw new Error(`${this.constructor.name}.primaryKey() is not implemented.`);
|
|
1920
|
-
}
|
|
1921
|
-
static primaryKeyField() {
|
|
1922
|
-
return resolveModelRepository(this).getTable().primaryKey;
|
|
1923
|
-
}
|
|
1924
|
-
static hydrateAttributes(attributes) {
|
|
1925
|
-
const casts = modelStatics(this).$casts ?? {};
|
|
1926
|
-
return applyCasts(attributes, casts, "hydrate");
|
|
1927
|
-
}
|
|
1928
|
-
static dehydrateAttributes(attributes) {
|
|
1929
|
-
const casts = modelStatics(this).$casts ?? {};
|
|
1930
|
-
return applyCasts(attributes, casts, "dehydrate");
|
|
1931
|
-
}
|
|
1932
|
-
static fromRecord(record, repository, exists = true) {
|
|
1933
|
-
const statics = modelStatics(this);
|
|
1934
|
-
const hydrated = statics.hydrateAttributes(record);
|
|
1935
|
-
return new statics(hydrated, repository, exists);
|
|
1936
|
-
}
|
|
1937
|
-
static boot() {}
|
|
1938
|
-
static addGlobalScope(_name, scope) {
|
|
1939
|
-
ensureBooted(this);
|
|
1940
|
-
const existing = modelGlobalScopes.get(this) ?? [];
|
|
1941
|
-
modelGlobalScopes.set(this, [
|
|
1942
|
-
...existing,
|
|
1943
|
-
scope
|
|
1944
|
-
]);
|
|
1945
|
-
}
|
|
1946
|
-
static repository() {
|
|
1947
|
-
return resolveModelRepository(this);
|
|
1948
|
-
}
|
|
1949
|
-
static query() {
|
|
1950
|
-
ensureBooted(this);
|
|
1951
|
-
const repository = resolveModelRepository(this);
|
|
1952
|
-
let query = repository.query();
|
|
1953
|
-
for (const scope of getGlobalScopes(this)) {
|
|
1954
|
-
query = scope(query);
|
|
1955
|
-
}
|
|
1956
|
-
return query;
|
|
1957
|
-
}
|
|
1958
|
-
static async create(attributes) {
|
|
1959
|
-
const statics = modelStatics(this);
|
|
1960
|
-
ensureBooted(this);
|
|
1961
|
-
const repository = resolveModelRepository(this);
|
|
1962
|
-
const table = repository.getTable();
|
|
1963
|
-
const timestamps = statics.$timestamps ?? true;
|
|
1964
|
-
const assignable = filterMassAssignable(statics.$fillable, statics.$guarded, attributes);
|
|
1965
|
-
const withTimestamps = applyTimestampsOnCreate(table.columns, assignable, timestamps);
|
|
1966
|
-
const payload = statics.dehydrateAttributes(withTimestamps);
|
|
1967
|
-
const record = await repository.create(payload);
|
|
1968
|
-
return statics.fromRecord(record, repository, true);
|
|
1969
|
-
}
|
|
1970
|
-
static async find(id) {
|
|
1971
|
-
const statics = modelStatics(this);
|
|
1972
|
-
const repository = resolveModelRepository(this);
|
|
1973
|
-
const primaryKey = repository.getTable().primaryKey;
|
|
1974
|
-
const record = await Model.query.call(this).where({ [primaryKey]: id }).first();
|
|
1975
|
-
return record ? statics.fromRecord(record, repository, true) : null;
|
|
1976
|
-
}
|
|
1977
|
-
static async findOrFail(id, errorFactory) {
|
|
1978
|
-
const model = await Model.find.call(this, id);
|
|
1979
|
-
if (model) {
|
|
1980
|
-
return model;
|
|
1981
|
-
}
|
|
1982
|
-
throw errorFactory?.(id) ?? new NotFoundError(`${this.name} ${String(id)} not found.`);
|
|
1983
|
-
}
|
|
1984
|
-
static async all(options = {}) {
|
|
1985
|
-
const statics = modelStatics(this);
|
|
1986
|
-
const repository = resolveModelRepository(this);
|
|
1987
|
-
let query = Model.query.call(this);
|
|
1988
|
-
if (options.orderBy) {
|
|
1989
|
-
query = query.orderBy(options.orderBy);
|
|
1990
|
-
}
|
|
1991
|
-
if (options.limit !== undefined) {
|
|
1992
|
-
query = query.limit(options.limit);
|
|
1993
|
-
}
|
|
1994
|
-
const rows = await query.get();
|
|
1995
|
-
return rows.map((row) => statics.fromRecord(row, repository, true));
|
|
1996
|
-
}
|
|
1997
|
-
static async firstWhere(where, options = {}) {
|
|
1998
|
-
const statics = modelStatics(this);
|
|
1999
|
-
const repository = resolveModelRepository(this);
|
|
2000
|
-
let query = Model.query.call(this).where(where);
|
|
2001
|
-
if (options.orderBy) {
|
|
2002
|
-
query = query.orderBy(options.orderBy);
|
|
2003
|
-
}
|
|
2004
|
-
const record = await query.first();
|
|
2005
|
-
return record ? statics.fromRecord(record, repository, true) : null;
|
|
2006
|
-
}
|
|
2007
|
-
async save() {
|
|
2008
|
-
const ModelClass = modelStatics(this.constructor);
|
|
2009
|
-
const timestamps = ModelClass.$timestamps ?? true;
|
|
2010
|
-
const casts = ModelClass.$casts ?? {};
|
|
2011
|
-
const table = this.repository.getTable();
|
|
2012
|
-
if (this.$exists) {
|
|
2013
|
-
const changes = applyTimestampsOnUpdate(table.columns, applyCasts(this.attributes, casts, "dehydrate"), timestamps);
|
|
2014
|
-
const record2 = await this.repository.updateByIdOrThrow(this.id, changes);
|
|
2015
|
-
this.attributes = ModelClass.hydrateAttributes(record2);
|
|
2016
|
-
return this;
|
|
2017
|
-
}
|
|
2018
|
-
const assignable = filterMassAssignable(ModelClass.$fillable, ModelClass.$guarded, this.attributes);
|
|
2019
|
-
const withTimestamps = applyTimestampsOnCreate(table.columns, assignable, timestamps);
|
|
2020
|
-
const payload = ModelClass.dehydrateAttributes(withTimestamps);
|
|
2021
|
-
const record = await this.repository.create(payload);
|
|
2022
|
-
this.attributes = ModelClass.hydrateAttributes(record);
|
|
2023
|
-
this._exists = true;
|
|
2024
|
-
return this;
|
|
2025
|
-
}
|
|
2026
|
-
async update(changes) {
|
|
2027
|
-
const ModelClass = modelStatics(this.constructor);
|
|
2028
|
-
const assignable = filterMassAssignable(ModelClass.$fillable, ModelClass.$guarded, changes);
|
|
2029
|
-
Object.assign(this.attributes, assignable);
|
|
2030
|
-
return await this.save();
|
|
2031
|
-
}
|
|
2032
|
-
async delete() {
|
|
2033
|
-
if (resolveSoftDeleteColumn(this.repository.getTable())) {
|
|
2034
|
-
return await this.repository.deleteById(this.id);
|
|
2035
|
-
}
|
|
2036
|
-
return await this.repository.forceDeleteById(this.id);
|
|
2037
|
-
}
|
|
2038
|
-
async forceDelete() {
|
|
2039
|
-
return await this.repository.forceDeleteById(this.id);
|
|
2040
|
-
}
|
|
2041
|
-
async restore() {
|
|
2042
|
-
const ModelClass = modelStatics(this.constructor);
|
|
2043
|
-
const record = await this.repository.restoreById(this.id);
|
|
2044
|
-
if (!record) {
|
|
2045
|
-
return null;
|
|
2046
|
-
}
|
|
2047
|
-
this.attributes = ModelClass.hydrateAttributes(record);
|
|
2048
|
-
return this;
|
|
2049
|
-
}
|
|
2050
|
-
async loadHasMany(as, relation, childRepository, options = {}) {
|
|
2051
|
-
const grouped = await childRepository.withConnection(this.repository.getConnection()).loadHasManyForParents([this.attributes], relation, options);
|
|
2052
|
-
const loaded = grouped.get(this.attributes[relation.localKey]) ?? [];
|
|
2053
|
-
return Object.assign(this, { [as]: loaded });
|
|
2054
|
-
}
|
|
2055
|
-
async loadHasOne(as, relation, childRepository, options = {}) {
|
|
2056
|
-
const loaded = await this.loadHasMany(as, relation, childRepository, { ...options, limit: 1 });
|
|
2057
|
-
const value = loaded[as]?.[0];
|
|
2058
|
-
return Object.assign(this, { [as]: value });
|
|
2059
|
-
}
|
|
2060
|
-
async loadBelongsTo(as, relation, parentRepository, options = {}) {
|
|
2061
|
-
const grouped = await this.repository.loadBelongsToForParents([this.attributes], relation, parentRepository, options);
|
|
2062
|
-
const loaded = grouped.get(this.attributes[relation.foreignKey]);
|
|
2063
|
-
return Object.assign(this, { [as]: loaded });
|
|
2064
|
-
}
|
|
2065
|
-
async loadBelongsToMany(as, relation, relatedRepository, options = {}) {
|
|
2066
|
-
const connection = this.repository.getConnection();
|
|
2067
|
-
const parentId = this.attributes[relation.parentKey];
|
|
2068
|
-
const pivotRows = await connection.unsafe(`SELECT * FROM ${relation.pivotTable} WHERE ${String(relation.foreignPivotKey)} = $1`, [parentId]);
|
|
2069
|
-
if (pivotRows.length === 0) {
|
|
2070
|
-
return Object.assign(this, { [as]: [] });
|
|
2071
|
-
}
|
|
2072
|
-
const relatedIds = [
|
|
2073
|
-
...new Set(pivotRows.map((row) => row[relation.relatedPivotKey]))
|
|
2074
|
-
];
|
|
2075
|
-
const relatedRows = await relatedRepository.withConnection(connection).findAll({
|
|
2076
|
-
...options,
|
|
2077
|
-
where: {
|
|
2078
|
-
[relation.relatedKey]: relatedIds
|
|
2079
|
-
}
|
|
2080
|
-
});
|
|
2081
|
-
const grouped = indexBelongsToManyRelation([this.attributes], pivotRows, relatedRows, relation);
|
|
2082
|
-
const loaded = grouped.get(parentId) ?? [];
|
|
2083
|
-
return Object.assign(this, { [as]: loaded });
|
|
2084
|
-
}
|
|
2085
|
-
mergeAttributes(patch) {
|
|
2086
|
-
Object.assign(this.attributes, patch);
|
|
2087
|
-
return this;
|
|
2088
|
-
}
|
|
2089
|
-
}
|
|
2090
|
-
function registerModelRepository(model, repository) {
|
|
2091
|
-
modelRepositories.set(model, repository);
|
|
2092
|
-
ensureBooted(model);
|
|
2093
|
-
return model;
|
|
2094
|
-
}
|
|
2095
1646
|
// ../../src/core/database/schema/columnDefinition.ts
|
|
2096
1647
|
class ColumnDefinition {
|
|
2097
1648
|
name;
|
|
@@ -2313,45 +1864,6 @@ class Blueprint {
|
|
|
2313
1864
|
});
|
|
2314
1865
|
}
|
|
2315
1866
|
}
|
|
2316
|
-
// ../../src/core/database/schema/driver.ts
|
|
2317
|
-
function normalizeConnectionName(connection) {
|
|
2318
|
-
const normalized = connection.trim().toLowerCase();
|
|
2319
|
-
if (normalized === "pgsql" || normalized === "postgres" || normalized === "postgresql") {
|
|
2320
|
-
return "pgsql";
|
|
2321
|
-
}
|
|
2322
|
-
if (normalized === "mysql" || normalized === "mariadb") {
|
|
2323
|
-
return "mysql";
|
|
2324
|
-
}
|
|
2325
|
-
if (normalized === "sqlite") {
|
|
2326
|
-
return "sqlite";
|
|
2327
|
-
}
|
|
2328
|
-
throw new Error(`Unsupported DB_CONNECTION: ${connection}`);
|
|
2329
|
-
}
|
|
2330
|
-
function resolveDriverFromUrl(url) {
|
|
2331
|
-
const normalized = url.trim().toLowerCase();
|
|
2332
|
-
if (normalized.startsWith("postgres://") || normalized.startsWith("postgresql://") || normalized.startsWith("postgres:")) {
|
|
2333
|
-
return "pgsql";
|
|
2334
|
-
}
|
|
2335
|
-
if (normalized.startsWith("mysql://") || normalized.startsWith("mysql:")) {
|
|
2336
|
-
return "mysql";
|
|
2337
|
-
}
|
|
2338
|
-
if (normalized.startsWith("sqlite:")) {
|
|
2339
|
-
return "sqlite";
|
|
2340
|
-
}
|
|
2341
|
-
return null;
|
|
2342
|
-
}
|
|
2343
|
-
function resolveDatabaseDriver(options = {}) {
|
|
2344
|
-
const connection = options.connection ?? process.env.DB_CONNECTION;
|
|
2345
|
-
if (connection) {
|
|
2346
|
-
return normalizeConnectionName(connection);
|
|
2347
|
-
}
|
|
2348
|
-
const url = options.url ?? process.env.DATABASE_URL ?? "";
|
|
2349
|
-
const fromUrl = resolveDriverFromUrl(url);
|
|
2350
|
-
if (fromUrl) {
|
|
2351
|
-
return fromUrl;
|
|
2352
|
-
}
|
|
2353
|
-
return "pgsql";
|
|
2354
|
-
}
|
|
2355
1867
|
// ../../src/core/database/schema/errors.ts
|
|
2356
1868
|
class UnsupportedSchemaFeatureError extends Error {
|
|
2357
1869
|
constructor(feature, driver) {
|
|
@@ -2690,48 +2202,16 @@ class SchemaBuilder {
|
|
|
2690
2202
|
toSql() {
|
|
2691
2203
|
return [...this.#statements];
|
|
2692
2204
|
}
|
|
2693
|
-
async execute(
|
|
2205
|
+
async execute(db) {
|
|
2694
2206
|
for (const statement of this.#statements) {
|
|
2695
|
-
await
|
|
2207
|
+
await db.unsafe(statement);
|
|
2696
2208
|
}
|
|
2697
2209
|
}
|
|
2698
2210
|
}
|
|
2699
|
-
|
|
2700
|
-
class Schema {
|
|
2701
|
-
static builder(driver) {
|
|
2702
|
-
return new SchemaBuilder(driver ?? resolveDatabaseDriver());
|
|
2703
|
-
}
|
|
2704
|
-
static async run(db2, driver, callback) {
|
|
2705
|
-
const schema = Schema.builder(driver);
|
|
2706
|
-
await callback(schema);
|
|
2707
|
-
await schema.execute(db2);
|
|
2708
|
-
}
|
|
2709
|
-
}
|
|
2710
|
-
function createSchemaBuilder(db2, driver) {
|
|
2711
|
-
const builder = Schema.builder(driver);
|
|
2712
|
-
return Object.assign(builder, {
|
|
2713
|
-
async commit() {
|
|
2714
|
-
await builder.execute(db2);
|
|
2715
|
-
}
|
|
2716
|
-
});
|
|
2717
|
-
}
|
|
2718
2211
|
// ../../src/core/database/table.ts
|
|
2719
2212
|
function defineTable(definition) {
|
|
2720
2213
|
return definition;
|
|
2721
2214
|
}
|
|
2722
|
-
// ../../src/core/database/transaction.ts
|
|
2723
|
-
function supportsTransactions(connection) {
|
|
2724
|
-
return typeof connection.begin === "function";
|
|
2725
|
-
}
|
|
2726
|
-
async function runInTransaction(operation) {
|
|
2727
|
-
const pool = resolveRepositoryConnection();
|
|
2728
|
-
if (!supportsTransactions(pool)) {
|
|
2729
|
-
throw new Error("Active database connection does not support transactions. Bind a client with begin() via bindDatabaseConnection.");
|
|
2730
|
-
}
|
|
2731
|
-
return await pool.begin(async (transaction) => {
|
|
2732
|
-
return await operation(createDatabaseConnection2(transaction));
|
|
2733
|
-
});
|
|
2734
|
-
}
|
|
2735
2215
|
// ../../src/core/queue/failedJobTable.ts
|
|
2736
2216
|
var failedJobTable = defineTable({
|
|
2737
2217
|
name: "failed_job",
|