@getstrata/core 2.0.2 → 2.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/core/database/baseRepository.d.ts +2 -0
  3. package/dist/core/database/dialect.d.ts +4 -0
  4. package/dist/core/database/mysqlConnection.d.ts +12 -2
  5. package/dist/core/database/sqliteConnection.d.ts +2 -1
  6. package/dist/core/database/transaction.d.ts +2 -2
  7. package/dist/core/database/transactionControl.d.ts +2 -1
  8. package/dist/core/events/eventBus.d.ts +4 -0
  9. package/dist/core/events/outbox/index.d.ts +55 -0
  10. package/dist/core/events/outbox/migration.d.ts +7 -0
  11. package/dist/core/http/requestMetaContext.d.ts +2 -0
  12. package/dist/core/http/response.d.ts +2 -1
  13. package/dist/core/openapi/registeredRoute.d.ts +40 -1
  14. package/dist/core/security/safeFetch.d.ts +1 -0
  15. package/dist/entries/audit/exportAuditLogs.js +137 -17
  16. package/dist/entries/auth/basicAuthGuard.js +18 -0
  17. package/dist/entries/auth/oauth/oidcProvider.js +100 -17
  18. package/dist/entries/auth/oauth/providers.js +100 -17
  19. package/dist/entries/auth/oauth/samlProvider.js +18 -0
  20. package/dist/entries/auth/oneTimeToken.js +18 -0
  21. package/dist/entries/auth/passwordLogin.js +18 -0
  22. package/dist/entries/auth/saml/samlServiceProvider.js +18 -0
  23. package/dist/entries/auth/scimAuthMiddleware.js +37 -0
  24. package/dist/entries/database/migrations.js +18 -0
  25. package/dist/entries/database/sqliteConnection.js +101 -7
  26. package/dist/entries/events/outbox.js +1 -0
  27. package/dist/entries/http/response.js +39 -5
  28. package/dist/entries/jobs/exportAuditLogsJob.js +137 -17
  29. package/dist/entries/openapi/generator.js +42 -12
  30. package/dist/entries/openapi/validate.js +10 -0
  31. package/dist/entries/security/safeFetch.js +100 -17
  32. package/dist/entries/tenant/databaseTenantContext.js +37 -0
  33. package/dist/entries/tenant/tenantDatabaseScope.js +37 -0
  34. package/dist/entries/tracing/tracingMiddleware.js +100 -17
  35. package/dist/framework/public-api.d.ts +3 -1
  36. package/dist/index.js +649 -60
  37. package/package.json +6 -1
@@ -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 timeout = setTimeout(() => controller.abort(), timeoutMs);
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 (!location || redirectCount >= maxRedirects) {
392
- return response;
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
- } finally {
401
- clearTimeout(timeout);
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";