@getstrata/core 2.0.1 → 2.0.3
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/CHANGELOG.md +10 -0
- package/dist/core/database/dialect.d.ts +4 -0
- package/dist/core/database/mysqlConnection.d.ts +12 -2
- package/dist/core/database/sqliteConnection.d.ts +2 -1
- package/dist/core/database/transaction.d.ts +2 -2
- package/dist/core/database/transactionControl.d.ts +2 -1
- package/dist/core/events/outbox/index.d.ts +54 -0
- package/dist/core/events/outbox/migration.d.ts +7 -0
- package/dist/core/http/requestMetaContext.d.ts +2 -0
- package/dist/core/http/response.d.ts +2 -1
- package/dist/core/security/safeFetch.d.ts +1 -0
- package/dist/entries/audit/exportAuditLogs.js +119 -17
- package/dist/entries/auth/basicAuthGuard.js +18 -0
- package/dist/entries/auth/oauth/oidcProvider.js +100 -17
- package/dist/entries/auth/oauth/providers.js +100 -17
- package/dist/entries/auth/oauth/samlProvider.js +18 -0
- package/dist/entries/auth/oneTimeToken.js +18 -0
- package/dist/entries/auth/passwordLogin.js +18 -0
- package/dist/entries/auth/saml/samlServiceProvider.js +18 -0
- package/dist/entries/auth/scimAuthMiddleware.js +19 -0
- package/dist/entries/database/migrations.js +18 -0
- package/dist/entries/database/sqliteConnection.js +101 -7
- package/dist/entries/events/outbox.js +1 -0
- package/dist/entries/http/response.js +39 -5
- package/dist/entries/jobs/exportAuditLogsJob.js +119 -17
- package/dist/entries/security/safeFetch.js +100 -17
- package/dist/entries/tenant/databaseTenantContext.js +19 -0
- package/dist/entries/tenant/tenantDatabaseScope.js +19 -0
- package/dist/entries/tracing/tracingMiddleware.js +100 -17
- package/dist/framework/public-api.d.ts +3 -1
- package/dist/index.js +591 -50
- package/package.json +7 -2
|
@@ -164,6 +164,7 @@ async function runWithTransactionScope(callback) {
|
|
|
164
164
|
var TransactionRollback, transactionScopes;
|
|
165
165
|
var init_transactionControl = __esm(() => {
|
|
166
166
|
init_asyncContextStore();
|
|
167
|
+
init_connectionContext();
|
|
167
168
|
TransactionRollback = class TransactionRollback extends Error {
|
|
168
169
|
result;
|
|
169
170
|
constructor(result) {
|
|
@@ -452,6 +453,12 @@ var init_dialect = __esm(() => {
|
|
|
452
453
|
nowExpression() {
|
|
453
454
|
return "NOW()";
|
|
454
455
|
},
|
|
456
|
+
epochMillisecondsExpression() {
|
|
457
|
+
return "CAST(EXTRACT(EPOCH FROM clock_timestamp()) * 1000 AS BIGINT)";
|
|
458
|
+
},
|
|
459
|
+
rowLockClause(skipLocked = false) {
|
|
460
|
+
return ` FOR UPDATE${skipLocked ? " SKIP LOCKED" : ""}`;
|
|
461
|
+
},
|
|
455
462
|
timestampValue(value) {
|
|
456
463
|
return value.toISOString();
|
|
457
464
|
},
|
|
@@ -490,6 +497,12 @@ var init_dialect = __esm(() => {
|
|
|
490
497
|
nowExpression() {
|
|
491
498
|
return "CURRENT_TIMESTAMP";
|
|
492
499
|
},
|
|
500
|
+
epochMillisecondsExpression() {
|
|
501
|
+
return "CAST(UNIX_TIMESTAMP(CURRENT_TIMESTAMP(3)) * 1000 AS UNSIGNED)";
|
|
502
|
+
},
|
|
503
|
+
rowLockClause(skipLocked = false) {
|
|
504
|
+
return ` FOR UPDATE${skipLocked ? " SKIP LOCKED" : ""}`;
|
|
505
|
+
},
|
|
493
506
|
timestampValue(value) {
|
|
494
507
|
return value.toISOString().slice(0, 19).replace("T", " ");
|
|
495
508
|
},
|
|
@@ -528,6 +541,12 @@ var init_dialect = __esm(() => {
|
|
|
528
541
|
nowExpression() {
|
|
529
542
|
return "strftime('%Y-%m-%dT%H:%M:%fZ', 'now')";
|
|
530
543
|
},
|
|
544
|
+
epochMillisecondsExpression() {
|
|
545
|
+
return "CAST((julianday('now') - 2440587.5) * 86400000 AS INTEGER)";
|
|
546
|
+
},
|
|
547
|
+
rowLockClause() {
|
|
548
|
+
return "";
|
|
549
|
+
},
|
|
531
550
|
timestampValue(value) {
|
|
532
551
|
return value.toISOString();
|
|
533
552
|
},
|
|
@@ -1077,50 +1096,133 @@ async function resolveSafeOutboundTarget(rawUrl, options = {}) {
|
|
|
1077
1096
|
|
|
1078
1097
|
// ../../src/core/security/safeFetch.ts
|
|
1079
1098
|
var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
|
|
1099
|
+
function untilAborted(work, signal) {
|
|
1100
|
+
signal.throwIfAborted();
|
|
1101
|
+
return new Promise((resolve, reject) => {
|
|
1102
|
+
const onAbort = () => reject(signal.reason);
|
|
1103
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1104
|
+
work.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
function preserveResponseMetadata(response, original) {
|
|
1108
|
+
Object.defineProperties(response, {
|
|
1109
|
+
url: { value: original.url },
|
|
1110
|
+
redirected: { value: original.redirected },
|
|
1111
|
+
type: { value: original.type }
|
|
1112
|
+
});
|
|
1113
|
+
const clone = response.clone.bind(response);
|
|
1114
|
+
response.clone = () => preserveResponseMetadata(clone(), original);
|
|
1115
|
+
return response;
|
|
1116
|
+
}
|
|
1117
|
+
function withBodyDeadline(response, signal, release) {
|
|
1118
|
+
if (!response.body) {
|
|
1119
|
+
release();
|
|
1120
|
+
return response;
|
|
1121
|
+
}
|
|
1122
|
+
const reader = response.body.getReader();
|
|
1123
|
+
let stopped = false;
|
|
1124
|
+
let bodyController;
|
|
1125
|
+
const stop = (reason, errorBody) => {
|
|
1126
|
+
if (stopped)
|
|
1127
|
+
return;
|
|
1128
|
+
stopped = true;
|
|
1129
|
+
release();
|
|
1130
|
+
signal.removeEventListener("abort", onAbort);
|
|
1131
|
+
if (errorBody)
|
|
1132
|
+
bodyController.error(reason);
|
|
1133
|
+
reader.cancel(reason).catch(() => {}).finally(() => reader.releaseLock());
|
|
1134
|
+
};
|
|
1135
|
+
const onAbort = () => stop(signal.reason, true);
|
|
1136
|
+
const body = new ReadableStream({
|
|
1137
|
+
start(controller) {
|
|
1138
|
+
bodyController = controller;
|
|
1139
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1140
|
+
if (signal.aborted)
|
|
1141
|
+
onAbort();
|
|
1142
|
+
},
|
|
1143
|
+
async pull(controller) {
|
|
1144
|
+
try {
|
|
1145
|
+
const result = await untilAborted(reader.read(), signal);
|
|
1146
|
+
if (stopped)
|
|
1147
|
+
return;
|
|
1148
|
+
if (result.done) {
|
|
1149
|
+
stopped = true;
|
|
1150
|
+
release();
|
|
1151
|
+
signal.removeEventListener("abort", onAbort);
|
|
1152
|
+
reader.releaseLock();
|
|
1153
|
+
controller.close();
|
|
1154
|
+
} else {
|
|
1155
|
+
controller.enqueue(result.value);
|
|
1156
|
+
}
|
|
1157
|
+
} catch (error) {
|
|
1158
|
+
stop(error, true);
|
|
1159
|
+
}
|
|
1160
|
+
},
|
|
1161
|
+
cancel(reason) {
|
|
1162
|
+
stop(reason, false);
|
|
1163
|
+
}
|
|
1164
|
+
}, { highWaterMark: 0 });
|
|
1165
|
+
return preserveResponseMetadata(new Response(body, {
|
|
1166
|
+
status: response.status,
|
|
1167
|
+
statusText: response.statusText,
|
|
1168
|
+
headers: response.headers
|
|
1169
|
+
}), response);
|
|
1170
|
+
}
|
|
1080
1171
|
async function safeFetch(input, init = {}, options = {}) {
|
|
1172
|
+
init.signal?.throwIfAborted();
|
|
1081
1173
|
const timeoutMs = options.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
|
|
1174
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
1175
|
+
throw new RangeError("safeFetch timeoutMs must be a positive finite number.");
|
|
1176
|
+
}
|
|
1082
1177
|
const maxRedirects = options.maxRedirects ?? 0;
|
|
1083
|
-
const resolveDns = options.resolveDns ?? true;
|
|
1084
1178
|
const urlOptions = {
|
|
1085
1179
|
allowHttp: options.allowHttp,
|
|
1086
|
-
resolveDns,
|
|
1180
|
+
resolveDns: options.resolveDns ?? true,
|
|
1087
1181
|
allowPrivate: options.allowPrivate
|
|
1088
1182
|
};
|
|
1089
1183
|
const controller = new AbortController;
|
|
1090
|
-
const
|
|
1184
|
+
const forwardAbort = () => controller.abort(init.signal?.reason);
|
|
1185
|
+
init.signal?.addEventListener("abort", forwardAbort, { once: true });
|
|
1186
|
+
const timeout = setTimeout(() => controller.abort(new DOMException("The operation timed out.", "TimeoutError")), timeoutMs);
|
|
1187
|
+
const release = () => {
|
|
1188
|
+
clearTimeout(timeout);
|
|
1189
|
+
init.signal?.removeEventListener("abort", forwardAbort);
|
|
1190
|
+
};
|
|
1091
1191
|
try {
|
|
1092
|
-
let current = await resolveSafeOutboundTarget(input, urlOptions);
|
|
1192
|
+
let current = await untilAborted(resolveSafeOutboundTarget(input, urlOptions), controller.signal);
|
|
1093
1193
|
let redirectCount = 0;
|
|
1094
1194
|
while (true) {
|
|
1195
|
+
controller.signal.throwIfAborted();
|
|
1095
1196
|
const address = current.addresses[0];
|
|
1096
1197
|
const fetchUrl = address ? pinUrlToAddress(current.url, address).toString() : current.url.toString();
|
|
1097
1198
|
const headers = new Headers(init.headers);
|
|
1098
|
-
if (address && !headers.has("host"))
|
|
1199
|
+
if (address && !headers.has("host"))
|
|
1099
1200
|
headers.set("Host", current.url.host);
|
|
1100
|
-
}
|
|
1101
1201
|
const fetchInit = {
|
|
1102
1202
|
...init,
|
|
1103
1203
|
headers,
|
|
1104
1204
|
signal: controller.signal,
|
|
1105
1205
|
redirect: "manual"
|
|
1106
1206
|
};
|
|
1107
|
-
if (address)
|
|
1207
|
+
if (address)
|
|
1108
1208
|
fetchInit.tls = { serverName: current.url.hostname };
|
|
1109
|
-
|
|
1110
|
-
const response = await fetch(fetchUrl, fetchInit);
|
|
1209
|
+
const response = await untilAborted(fetch(fetchUrl, fetchInit), controller.signal);
|
|
1111
1210
|
if (response.status >= 300 && response.status < 400) {
|
|
1112
1211
|
const location = response.headers.get("location");
|
|
1113
|
-
if (
|
|
1114
|
-
|
|
1212
|
+
if (location && redirectCount < maxRedirects) {
|
|
1213
|
+
if (response.body)
|
|
1214
|
+
await untilAborted(response.body.cancel(), controller.signal);
|
|
1215
|
+
current = await untilAborted(resolveSafeOutboundTarget(new URL(location, current.url).toString(), urlOptions), controller.signal);
|
|
1216
|
+
redirectCount++;
|
|
1217
|
+
continue;
|
|
1115
1218
|
}
|
|
1116
|
-
current = await resolveSafeOutboundTarget(new URL(location, current.url).toString(), urlOptions);
|
|
1117
|
-
redirectCount += 1;
|
|
1118
|
-
continue;
|
|
1119
1219
|
}
|
|
1120
|
-
return response;
|
|
1220
|
+
return withBodyDeadline(response, controller.signal, release);
|
|
1121
1221
|
}
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1222
|
+
} catch (error) {
|
|
1223
|
+
controller.abort(error);
|
|
1224
|
+
release();
|
|
1225
|
+
throw error;
|
|
1124
1226
|
}
|
|
1125
1227
|
}
|
|
1126
1228
|
|
|
@@ -261,50 +261,133 @@ async function resolveSafeOutboundTarget(rawUrl, options = {}) {
|
|
|
261
261
|
|
|
262
262
|
// ../../src/core/security/safeFetch.ts
|
|
263
263
|
var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
|
|
264
|
+
function untilAborted(work, signal) {
|
|
265
|
+
signal.throwIfAborted();
|
|
266
|
+
return new Promise((resolve, reject) => {
|
|
267
|
+
const onAbort = () => reject(signal.reason);
|
|
268
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
269
|
+
work.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
function preserveResponseMetadata(response, original) {
|
|
273
|
+
Object.defineProperties(response, {
|
|
274
|
+
url: { value: original.url },
|
|
275
|
+
redirected: { value: original.redirected },
|
|
276
|
+
type: { value: original.type }
|
|
277
|
+
});
|
|
278
|
+
const clone = response.clone.bind(response);
|
|
279
|
+
response.clone = () => preserveResponseMetadata(clone(), original);
|
|
280
|
+
return response;
|
|
281
|
+
}
|
|
282
|
+
function withBodyDeadline(response, signal, release) {
|
|
283
|
+
if (!response.body) {
|
|
284
|
+
release();
|
|
285
|
+
return response;
|
|
286
|
+
}
|
|
287
|
+
const reader = response.body.getReader();
|
|
288
|
+
let stopped = false;
|
|
289
|
+
let bodyController;
|
|
290
|
+
const stop = (reason, errorBody) => {
|
|
291
|
+
if (stopped)
|
|
292
|
+
return;
|
|
293
|
+
stopped = true;
|
|
294
|
+
release();
|
|
295
|
+
signal.removeEventListener("abort", onAbort);
|
|
296
|
+
if (errorBody)
|
|
297
|
+
bodyController.error(reason);
|
|
298
|
+
reader.cancel(reason).catch(() => {}).finally(() => reader.releaseLock());
|
|
299
|
+
};
|
|
300
|
+
const onAbort = () => stop(signal.reason, true);
|
|
301
|
+
const body = new ReadableStream({
|
|
302
|
+
start(controller) {
|
|
303
|
+
bodyController = controller;
|
|
304
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
305
|
+
if (signal.aborted)
|
|
306
|
+
onAbort();
|
|
307
|
+
},
|
|
308
|
+
async pull(controller) {
|
|
309
|
+
try {
|
|
310
|
+
const result = await untilAborted(reader.read(), signal);
|
|
311
|
+
if (stopped)
|
|
312
|
+
return;
|
|
313
|
+
if (result.done) {
|
|
314
|
+
stopped = true;
|
|
315
|
+
release();
|
|
316
|
+
signal.removeEventListener("abort", onAbort);
|
|
317
|
+
reader.releaseLock();
|
|
318
|
+
controller.close();
|
|
319
|
+
} else {
|
|
320
|
+
controller.enqueue(result.value);
|
|
321
|
+
}
|
|
322
|
+
} catch (error) {
|
|
323
|
+
stop(error, true);
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
cancel(reason) {
|
|
327
|
+
stop(reason, false);
|
|
328
|
+
}
|
|
329
|
+
}, { highWaterMark: 0 });
|
|
330
|
+
return preserveResponseMetadata(new Response(body, {
|
|
331
|
+
status: response.status,
|
|
332
|
+
statusText: response.statusText,
|
|
333
|
+
headers: response.headers
|
|
334
|
+
}), response);
|
|
335
|
+
}
|
|
264
336
|
async function safeFetch(input, init = {}, options = {}) {
|
|
337
|
+
init.signal?.throwIfAborted();
|
|
265
338
|
const timeoutMs = options.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
|
|
339
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
340
|
+
throw new RangeError("safeFetch timeoutMs must be a positive finite number.");
|
|
341
|
+
}
|
|
266
342
|
const maxRedirects = options.maxRedirects ?? 0;
|
|
267
|
-
const resolveDns = options.resolveDns ?? true;
|
|
268
343
|
const urlOptions = {
|
|
269
344
|
allowHttp: options.allowHttp,
|
|
270
|
-
resolveDns,
|
|
345
|
+
resolveDns: options.resolveDns ?? true,
|
|
271
346
|
allowPrivate: options.allowPrivate
|
|
272
347
|
};
|
|
273
348
|
const controller = new AbortController;
|
|
274
|
-
const
|
|
349
|
+
const forwardAbort = () => controller.abort(init.signal?.reason);
|
|
350
|
+
init.signal?.addEventListener("abort", forwardAbort, { once: true });
|
|
351
|
+
const timeout = setTimeout(() => controller.abort(new DOMException("The operation timed out.", "TimeoutError")), timeoutMs);
|
|
352
|
+
const release = () => {
|
|
353
|
+
clearTimeout(timeout);
|
|
354
|
+
init.signal?.removeEventListener("abort", forwardAbort);
|
|
355
|
+
};
|
|
275
356
|
try {
|
|
276
|
-
let current = await resolveSafeOutboundTarget(input, urlOptions);
|
|
357
|
+
let current = await untilAborted(resolveSafeOutboundTarget(input, urlOptions), controller.signal);
|
|
277
358
|
let redirectCount = 0;
|
|
278
359
|
while (true) {
|
|
360
|
+
controller.signal.throwIfAborted();
|
|
279
361
|
const address = current.addresses[0];
|
|
280
362
|
const fetchUrl = address ? pinUrlToAddress(current.url, address).toString() : current.url.toString();
|
|
281
363
|
const headers = new Headers(init.headers);
|
|
282
|
-
if (address && !headers.has("host"))
|
|
364
|
+
if (address && !headers.has("host"))
|
|
283
365
|
headers.set("Host", current.url.host);
|
|
284
|
-
}
|
|
285
366
|
const fetchInit = {
|
|
286
367
|
...init,
|
|
287
368
|
headers,
|
|
288
369
|
signal: controller.signal,
|
|
289
370
|
redirect: "manual"
|
|
290
371
|
};
|
|
291
|
-
if (address)
|
|
372
|
+
if (address)
|
|
292
373
|
fetchInit.tls = { serverName: current.url.hostname };
|
|
293
|
-
|
|
294
|
-
const response = await fetch(fetchUrl, fetchInit);
|
|
374
|
+
const response = await untilAborted(fetch(fetchUrl, fetchInit), controller.signal);
|
|
295
375
|
if (response.status >= 300 && response.status < 400) {
|
|
296
376
|
const location = response.headers.get("location");
|
|
297
|
-
if (
|
|
298
|
-
|
|
377
|
+
if (location && redirectCount < maxRedirects) {
|
|
378
|
+
if (response.body)
|
|
379
|
+
await untilAborted(response.body.cancel(), controller.signal);
|
|
380
|
+
current = await untilAborted(resolveSafeOutboundTarget(new URL(location, current.url).toString(), urlOptions), controller.signal);
|
|
381
|
+
redirectCount++;
|
|
382
|
+
continue;
|
|
299
383
|
}
|
|
300
|
-
current = await resolveSafeOutboundTarget(new URL(location, current.url).toString(), urlOptions);
|
|
301
|
-
redirectCount += 1;
|
|
302
|
-
continue;
|
|
303
384
|
}
|
|
304
|
-
return response;
|
|
385
|
+
return withBodyDeadline(response, controller.signal, release);
|
|
305
386
|
}
|
|
306
|
-
}
|
|
307
|
-
|
|
387
|
+
} catch (error) {
|
|
388
|
+
controller.abort(error);
|
|
389
|
+
release();
|
|
390
|
+
throw error;
|
|
308
391
|
}
|
|
309
392
|
}
|
|
310
393
|
export {
|
|
@@ -164,6 +164,7 @@ async function runWithTransactionScope(callback) {
|
|
|
164
164
|
var TransactionRollback, transactionScopes;
|
|
165
165
|
var init_transactionControl = __esm(() => {
|
|
166
166
|
init_asyncContextStore();
|
|
167
|
+
init_connectionContext();
|
|
167
168
|
TransactionRollback = class TransactionRollback extends Error {
|
|
168
169
|
result;
|
|
169
170
|
constructor(result) {
|
|
@@ -452,6 +453,12 @@ var init_dialect = __esm(() => {
|
|
|
452
453
|
nowExpression() {
|
|
453
454
|
return "NOW()";
|
|
454
455
|
},
|
|
456
|
+
epochMillisecondsExpression() {
|
|
457
|
+
return "CAST(EXTRACT(EPOCH FROM clock_timestamp()) * 1000 AS BIGINT)";
|
|
458
|
+
},
|
|
459
|
+
rowLockClause(skipLocked = false) {
|
|
460
|
+
return ` FOR UPDATE${skipLocked ? " SKIP LOCKED" : ""}`;
|
|
461
|
+
},
|
|
455
462
|
timestampValue(value) {
|
|
456
463
|
return value.toISOString();
|
|
457
464
|
},
|
|
@@ -490,6 +497,12 @@ var init_dialect = __esm(() => {
|
|
|
490
497
|
nowExpression() {
|
|
491
498
|
return "CURRENT_TIMESTAMP";
|
|
492
499
|
},
|
|
500
|
+
epochMillisecondsExpression() {
|
|
501
|
+
return "CAST(UNIX_TIMESTAMP(CURRENT_TIMESTAMP(3)) * 1000 AS UNSIGNED)";
|
|
502
|
+
},
|
|
503
|
+
rowLockClause(skipLocked = false) {
|
|
504
|
+
return ` FOR UPDATE${skipLocked ? " SKIP LOCKED" : ""}`;
|
|
505
|
+
},
|
|
493
506
|
timestampValue(value) {
|
|
494
507
|
return value.toISOString().slice(0, 19).replace("T", " ");
|
|
495
508
|
},
|
|
@@ -528,6 +541,12 @@ var init_dialect = __esm(() => {
|
|
|
528
541
|
nowExpression() {
|
|
529
542
|
return "strftime('%Y-%m-%dT%H:%M:%fZ', 'now')";
|
|
530
543
|
},
|
|
544
|
+
epochMillisecondsExpression() {
|
|
545
|
+
return "CAST((julianday('now') - 2440587.5) * 86400000 AS INTEGER)";
|
|
546
|
+
},
|
|
547
|
+
rowLockClause() {
|
|
548
|
+
return "";
|
|
549
|
+
},
|
|
531
550
|
timestampValue(value) {
|
|
532
551
|
return value.toISOString();
|
|
533
552
|
},
|
|
@@ -105,6 +105,7 @@ async function runWithTransactionScope(callback) {
|
|
|
105
105
|
var TransactionRollback, transactionScopes;
|
|
106
106
|
var init_transactionControl = __esm(() => {
|
|
107
107
|
init_asyncContextStore();
|
|
108
|
+
init_connectionContext();
|
|
108
109
|
TransactionRollback = class TransactionRollback extends Error {
|
|
109
110
|
result;
|
|
110
111
|
constructor(result) {
|
|
@@ -386,6 +387,12 @@ var init_dialect = __esm(() => {
|
|
|
386
387
|
nowExpression() {
|
|
387
388
|
return "NOW()";
|
|
388
389
|
},
|
|
390
|
+
epochMillisecondsExpression() {
|
|
391
|
+
return "CAST(EXTRACT(EPOCH FROM clock_timestamp()) * 1000 AS BIGINT)";
|
|
392
|
+
},
|
|
393
|
+
rowLockClause(skipLocked = false) {
|
|
394
|
+
return ` FOR UPDATE${skipLocked ? " SKIP LOCKED" : ""}`;
|
|
395
|
+
},
|
|
389
396
|
timestampValue(value) {
|
|
390
397
|
return value.toISOString();
|
|
391
398
|
},
|
|
@@ -424,6 +431,12 @@ var init_dialect = __esm(() => {
|
|
|
424
431
|
nowExpression() {
|
|
425
432
|
return "CURRENT_TIMESTAMP";
|
|
426
433
|
},
|
|
434
|
+
epochMillisecondsExpression() {
|
|
435
|
+
return "CAST(UNIX_TIMESTAMP(CURRENT_TIMESTAMP(3)) * 1000 AS UNSIGNED)";
|
|
436
|
+
},
|
|
437
|
+
rowLockClause(skipLocked = false) {
|
|
438
|
+
return ` FOR UPDATE${skipLocked ? " SKIP LOCKED" : ""}`;
|
|
439
|
+
},
|
|
427
440
|
timestampValue(value) {
|
|
428
441
|
return value.toISOString().slice(0, 19).replace("T", " ");
|
|
429
442
|
},
|
|
@@ -462,6 +475,12 @@ var init_dialect = __esm(() => {
|
|
|
462
475
|
nowExpression() {
|
|
463
476
|
return "strftime('%Y-%m-%dT%H:%M:%fZ', 'now')";
|
|
464
477
|
},
|
|
478
|
+
epochMillisecondsExpression() {
|
|
479
|
+
return "CAST((julianday('now') - 2440587.5) * 86400000 AS INTEGER)";
|
|
480
|
+
},
|
|
481
|
+
rowLockClause() {
|
|
482
|
+
return "";
|
|
483
|
+
},
|
|
465
484
|
timestampValue(value) {
|
|
466
485
|
return value.toISOString();
|
|
467
486
|
},
|
|
@@ -355,50 +355,133 @@ async function resolveSafeOutboundTarget(rawUrl, options = {}) {
|
|
|
355
355
|
|
|
356
356
|
// ../../src/core/security/safeFetch.ts
|
|
357
357
|
var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
|
|
358
|
+
function untilAborted(work, signal) {
|
|
359
|
+
signal.throwIfAborted();
|
|
360
|
+
return new Promise((resolve, reject) => {
|
|
361
|
+
const onAbort = () => reject(signal.reason);
|
|
362
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
363
|
+
work.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
function preserveResponseMetadata(response, original) {
|
|
367
|
+
Object.defineProperties(response, {
|
|
368
|
+
url: { value: original.url },
|
|
369
|
+
redirected: { value: original.redirected },
|
|
370
|
+
type: { value: original.type }
|
|
371
|
+
});
|
|
372
|
+
const clone = response.clone.bind(response);
|
|
373
|
+
response.clone = () => preserveResponseMetadata(clone(), original);
|
|
374
|
+
return response;
|
|
375
|
+
}
|
|
376
|
+
function withBodyDeadline(response, signal, release) {
|
|
377
|
+
if (!response.body) {
|
|
378
|
+
release();
|
|
379
|
+
return response;
|
|
380
|
+
}
|
|
381
|
+
const reader = response.body.getReader();
|
|
382
|
+
let stopped = false;
|
|
383
|
+
let bodyController;
|
|
384
|
+
const stop = (reason, errorBody) => {
|
|
385
|
+
if (stopped)
|
|
386
|
+
return;
|
|
387
|
+
stopped = true;
|
|
388
|
+
release();
|
|
389
|
+
signal.removeEventListener("abort", onAbort);
|
|
390
|
+
if (errorBody)
|
|
391
|
+
bodyController.error(reason);
|
|
392
|
+
reader.cancel(reason).catch(() => {}).finally(() => reader.releaseLock());
|
|
393
|
+
};
|
|
394
|
+
const onAbort = () => stop(signal.reason, true);
|
|
395
|
+
const body = new ReadableStream({
|
|
396
|
+
start(controller) {
|
|
397
|
+
bodyController = controller;
|
|
398
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
399
|
+
if (signal.aborted)
|
|
400
|
+
onAbort();
|
|
401
|
+
},
|
|
402
|
+
async pull(controller) {
|
|
403
|
+
try {
|
|
404
|
+
const result = await untilAborted(reader.read(), signal);
|
|
405
|
+
if (stopped)
|
|
406
|
+
return;
|
|
407
|
+
if (result.done) {
|
|
408
|
+
stopped = true;
|
|
409
|
+
release();
|
|
410
|
+
signal.removeEventListener("abort", onAbort);
|
|
411
|
+
reader.releaseLock();
|
|
412
|
+
controller.close();
|
|
413
|
+
} else {
|
|
414
|
+
controller.enqueue(result.value);
|
|
415
|
+
}
|
|
416
|
+
} catch (error) {
|
|
417
|
+
stop(error, true);
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
cancel(reason) {
|
|
421
|
+
stop(reason, false);
|
|
422
|
+
}
|
|
423
|
+
}, { highWaterMark: 0 });
|
|
424
|
+
return preserveResponseMetadata(new Response(body, {
|
|
425
|
+
status: response.status,
|
|
426
|
+
statusText: response.statusText,
|
|
427
|
+
headers: response.headers
|
|
428
|
+
}), response);
|
|
429
|
+
}
|
|
358
430
|
async function safeFetch(input, init = {}, options = {}) {
|
|
431
|
+
init.signal?.throwIfAborted();
|
|
359
432
|
const timeoutMs = options.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
|
|
433
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
434
|
+
throw new RangeError("safeFetch timeoutMs must be a positive finite number.");
|
|
435
|
+
}
|
|
360
436
|
const maxRedirects = options.maxRedirects ?? 0;
|
|
361
|
-
const resolveDns = options.resolveDns ?? true;
|
|
362
437
|
const urlOptions = {
|
|
363
438
|
allowHttp: options.allowHttp,
|
|
364
|
-
resolveDns,
|
|
439
|
+
resolveDns: options.resolveDns ?? true,
|
|
365
440
|
allowPrivate: options.allowPrivate
|
|
366
441
|
};
|
|
367
442
|
const controller = new AbortController;
|
|
368
|
-
const
|
|
443
|
+
const forwardAbort = () => controller.abort(init.signal?.reason);
|
|
444
|
+
init.signal?.addEventListener("abort", forwardAbort, { once: true });
|
|
445
|
+
const timeout = setTimeout(() => controller.abort(new DOMException("The operation timed out.", "TimeoutError")), timeoutMs);
|
|
446
|
+
const release = () => {
|
|
447
|
+
clearTimeout(timeout);
|
|
448
|
+
init.signal?.removeEventListener("abort", forwardAbort);
|
|
449
|
+
};
|
|
369
450
|
try {
|
|
370
|
-
let current = await resolveSafeOutboundTarget(input, urlOptions);
|
|
451
|
+
let current = await untilAborted(resolveSafeOutboundTarget(input, urlOptions), controller.signal);
|
|
371
452
|
let redirectCount = 0;
|
|
372
453
|
while (true) {
|
|
454
|
+
controller.signal.throwIfAborted();
|
|
373
455
|
const address = current.addresses[0];
|
|
374
456
|
const fetchUrl = address ? pinUrlToAddress(current.url, address).toString() : current.url.toString();
|
|
375
457
|
const headers = new Headers(init.headers);
|
|
376
|
-
if (address && !headers.has("host"))
|
|
458
|
+
if (address && !headers.has("host"))
|
|
377
459
|
headers.set("Host", current.url.host);
|
|
378
|
-
}
|
|
379
460
|
const fetchInit = {
|
|
380
461
|
...init,
|
|
381
462
|
headers,
|
|
382
463
|
signal: controller.signal,
|
|
383
464
|
redirect: "manual"
|
|
384
465
|
};
|
|
385
|
-
if (address)
|
|
466
|
+
if (address)
|
|
386
467
|
fetchInit.tls = { serverName: current.url.hostname };
|
|
387
|
-
|
|
388
|
-
const response = await fetch(fetchUrl, fetchInit);
|
|
468
|
+
const response = await untilAborted(fetch(fetchUrl, fetchInit), controller.signal);
|
|
389
469
|
if (response.status >= 300 && response.status < 400) {
|
|
390
470
|
const location = response.headers.get("location");
|
|
391
|
-
if (
|
|
392
|
-
|
|
471
|
+
if (location && redirectCount < maxRedirects) {
|
|
472
|
+
if (response.body)
|
|
473
|
+
await untilAborted(response.body.cancel(), controller.signal);
|
|
474
|
+
current = await untilAborted(resolveSafeOutboundTarget(new URL(location, current.url).toString(), urlOptions), controller.signal);
|
|
475
|
+
redirectCount++;
|
|
476
|
+
continue;
|
|
393
477
|
}
|
|
394
|
-
current = await resolveSafeOutboundTarget(new URL(location, current.url).toString(), urlOptions);
|
|
395
|
-
redirectCount += 1;
|
|
396
|
-
continue;
|
|
397
478
|
}
|
|
398
|
-
return response;
|
|
479
|
+
return withBodyDeadline(response, controller.signal, release);
|
|
399
480
|
}
|
|
400
|
-
}
|
|
401
|
-
|
|
481
|
+
} catch (error) {
|
|
482
|
+
controller.abort(error);
|
|
483
|
+
release();
|
|
484
|
+
throw error;
|
|
402
485
|
}
|
|
403
486
|
}
|
|
404
487
|
|
|
@@ -64,7 +64,7 @@ export { loadSeedersFromDirectory, runSeedersFromDirectory, } from "../core/data
|
|
|
64
64
|
export type { Seeder, SeederDatabase } from "../core/database/seeders/types.ts";
|
|
65
65
|
export { createSqliteConnection } from "../core/database/sqliteConnection.ts";
|
|
66
66
|
export { defineTable } from "../core/database/table.ts";
|
|
67
|
-
export { requestTransactionRollback, runInTransaction } from "../core/database/transaction.ts";
|
|
67
|
+
export { hasActiveTransaction, requestTransactionRollback, runInTransaction, } from "../core/database/transaction.ts";
|
|
68
68
|
export type { QueryJoin, QueryJoinOn, QueryOptions, QueryOrder, QuerySelectItem, QueryWhere, } from "../core/database/types.ts";
|
|
69
69
|
export type { WhereNode } from "../core/database/whereBuilder.ts";
|
|
70
70
|
export { WhereBuilder } from "../core/database/whereBuilder.ts";
|
|
@@ -72,6 +72,8 @@ export { BadRequestError, ConflictError, ForbiddenError, HttpError, InternalServ
|
|
|
72
72
|
export type { EventListener } from "../core/events/eventBus.ts";
|
|
73
73
|
export { EventBus, eventBus, readSharedEventBus } from "../core/events/eventBus.ts";
|
|
74
74
|
export { dispatchModelEvent, modelEventName, runWithDeferredModelEvents, } from "../core/events/index.ts";
|
|
75
|
+
export type { DurableEvent, DurableListener, SqlOutboxOptions, } from "../core/events/outbox/index.ts";
|
|
76
|
+
export { createOutboxMigration, SqlOutbox } from "../core/events/outbox/index.ts";
|
|
75
77
|
export { auth, cache, config, events, log, mail, policyGate, queue, storage, } from "../core/facades/index.ts";
|
|
76
78
|
export { createBodySizeLimitMiddleware } from "../core/http/bodySizeLimitMiddleware.ts";
|
|
77
79
|
export { readClientIp, trustForwardedFor } from "../core/http/clientIp.ts";
|