@getstrata/core 0.5.42 → 0.5.44

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 (51) hide show
  1. package/dist/entries/admin/formatValue.js +32 -0
  2. package/dist/entries/admin/registry.js +32 -0
  3. package/dist/entries/audit/siemFormatter.js +37 -0
  4. package/dist/entries/auth/abilityChecker.js +1 -0
  5. package/dist/entries/auth/membershipMiddleware.js +298 -0
  6. package/dist/entries/auth/scimAuthMiddleware.js +251 -0
  7. package/dist/entries/auth/sessionGuard.js +72 -0
  8. package/dist/entries/database/migrations/types.js +1 -0
  9. package/dist/entries/database/migrations.js +127 -0
  10. package/dist/entries/database/schema.js +1054 -0
  11. package/dist/entries/database/seeders/types.js +1 -0
  12. package/dist/entries/http/conditionalResponse.js +192 -0
  13. package/dist/entries/http/corsMiddleware.js +54 -0
  14. package/dist/entries/http/csrfMiddleware.js +236 -0
  15. package/dist/entries/http/csrfToken.js +3 -0
  16. package/dist/entries/http/flashMiddleware.js +143 -0
  17. package/dist/entries/http/formRequest.js +152 -0
  18. package/dist/entries/http/loginThrottleMiddleware.js +46 -0
  19. package/dist/entries/http/memoryThrottleMiddleware.js +30 -0
  20. package/dist/entries/http/requireAbilityMiddleware.js +110 -0
  21. package/dist/entries/http/requireAuthMiddleware.js +80 -0
  22. package/dist/entries/http/requireGlobalAdminMiddleware.js +147 -0
  23. package/dist/entries/http/requireWebAuthMiddleware.js +107 -0
  24. package/dist/entries/http/route.js +8 -0
  25. package/dist/entries/http/routeMiddleware.js +32 -0
  26. package/dist/entries/http/routeModelBinding.js +141 -0
  27. package/dist/entries/http/scimThrottleMiddleware.js +23 -0
  28. package/dist/entries/http/securedRouteModelBinding.js +10 -0
  29. package/dist/entries/http/securityHeadersMiddleware.js +77 -0
  30. package/dist/entries/http/throttleMiddleware.js +87 -0
  31. package/dist/entries/http/webErrorResponse.js +72 -0
  32. package/dist/entries/http/webFormRequest.js +10 -0
  33. package/dist/entries/logging/requestLoggingMiddleware.js +89 -0
  34. package/dist/entries/mail/mailer.js +208 -0
  35. package/dist/entries/mail/markdownMail.js +63 -0
  36. package/dist/entries/mail/markdownMailable.js +78 -0
  37. package/dist/entries/notifications.js +152 -0
  38. package/dist/entries/openapi/generator.js +178 -0
  39. package/dist/entries/openapi/validate.js +28 -0
  40. package/dist/entries/queue/createAppQueue.js +58 -0
  41. package/dist/entries/queue/failedJobRepository.js +58 -0
  42. package/dist/entries/queue/publicQueue.js +58 -0
  43. package/dist/entries/queue/queueMetrics.js +58 -0
  44. package/dist/entries/runtime/asyncContextStore.js +17 -0
  45. package/dist/entries/security/safeFetch.js +211 -0
  46. package/dist/entries/security/timingSafeCompare.js +14 -0
  47. package/dist/entries/tenant/databaseTenantContext.js +116 -0
  48. package/dist/entries/tenant/tenantDatabaseScope.js +10 -0
  49. package/dist/entries/tracing/tracingMiddleware.js +103 -0
  50. package/dist/entries/view.js +72 -0
  51. package/package.json +202 -7
@@ -2131,6 +2131,45 @@ class Blueprint {
2131
2131
  });
2132
2132
  }
2133
2133
  }
2134
+ // ../../src/core/database/schema/driver.ts
2135
+ function normalizeConnectionName(connection) {
2136
+ const normalized = connection.trim().toLowerCase();
2137
+ if (normalized === "pgsql" || normalized === "postgres" || normalized === "postgresql") {
2138
+ return "pgsql";
2139
+ }
2140
+ if (normalized === "mysql" || normalized === "mariadb") {
2141
+ return "mysql";
2142
+ }
2143
+ if (normalized === "sqlite") {
2144
+ return "sqlite";
2145
+ }
2146
+ throw new Error(`Unsupported DB_CONNECTION: ${connection}`);
2147
+ }
2148
+ function resolveDriverFromUrl(url) {
2149
+ const normalized = url.trim().toLowerCase();
2150
+ if (normalized.startsWith("postgres://") || normalized.startsWith("postgresql://") || normalized.startsWith("postgres:")) {
2151
+ return "pgsql";
2152
+ }
2153
+ if (normalized.startsWith("mysql://") || normalized.startsWith("mysql:")) {
2154
+ return "mysql";
2155
+ }
2156
+ if (normalized.startsWith("sqlite:")) {
2157
+ return "sqlite";
2158
+ }
2159
+ return null;
2160
+ }
2161
+ function resolveDatabaseDriver(options = {}) {
2162
+ const connection = options.connection ?? process.env.DB_CONNECTION;
2163
+ if (connection) {
2164
+ return normalizeConnectionName(connection);
2165
+ }
2166
+ const url = options.url ?? process.env.DATABASE_URL ?? "";
2167
+ const fromUrl = resolveDriverFromUrl(url);
2168
+ if (fromUrl) {
2169
+ return fromUrl;
2170
+ }
2171
+ return "pgsql";
2172
+ }
2134
2173
  // ../../src/core/database/schema/errors.ts
2135
2174
  class UnsupportedSchemaFeatureError extends Error {
2136
2175
  constructor(feature, driver) {
@@ -2475,6 +2514,25 @@ class SchemaBuilder {
2475
2514
  }
2476
2515
  }
2477
2516
  }
2517
+
2518
+ class Schema {
2519
+ static builder(driver) {
2520
+ return new SchemaBuilder(driver ?? resolveDatabaseDriver());
2521
+ }
2522
+ static async run(db, driver, callback) {
2523
+ const schema = Schema.builder(driver);
2524
+ await callback(schema);
2525
+ await schema.execute(db);
2526
+ }
2527
+ }
2528
+ function createSchemaBuilder(db, driver) {
2529
+ const builder = Schema.builder(driver);
2530
+ return Object.assign(builder, {
2531
+ async commit() {
2532
+ await builder.execute(db);
2533
+ }
2534
+ });
2535
+ }
2478
2536
  // ../../src/core/database/table.ts
2479
2537
  function defineTable(definition) {
2480
2538
  return definition;
@@ -2545,6 +2603,7 @@ var db = new Proxy(function database() {}, {
2545
2603
  return typeof value === "function" ? value.bind(connection) : value;
2546
2604
  }
2547
2605
  });
2606
+ var connection_default = db;
2548
2607
 
2549
2608
  // ../../src/modules/user/apiTokenTable.ts
2550
2609
  var apiTokenTable = defineTable({
@@ -2686,6 +2745,9 @@ function currentAuthUser() {
2686
2745
 
2687
2746
  // ../../src/core/http/requestMetaContext.ts
2688
2747
  var requestMetaContext = createAsyncContextStore("@getstrata/requestMetaContext");
2748
+ function runWithRequestMeta(meta, callback) {
2749
+ return requestMetaContext.run(meta, callback);
2750
+ }
2689
2751
  function currentRequestMeta() {
2690
2752
  return requestMetaContext.getStore() ?? {
2691
2753
  ipAddress: null,
@@ -2779,6 +2841,16 @@ function currentTenant() {
2779
2841
  function currentTenantId() {
2780
2842
  return currentTenant()?.id ?? 1;
2781
2843
  }
2844
+ function rateLimitMultiplierForPlan(plan) {
2845
+ switch (plan) {
2846
+ case "enterprise":
2847
+ return 4;
2848
+ case "pro":
2849
+ return 2;
2850
+ default:
2851
+ return 1;
2852
+ }
2853
+ }
2782
2854
 
2783
2855
  // ../../src/modules/user/authService.ts
2784
2856
  class AuthService {
@@ -0,0 +1 @@
1
+ // @bun
@@ -0,0 +1,127 @@
1
+ // @bun
2
+ // ../../src/core/database/migrations/runner.ts
3
+ import { readdir } from "fs/promises";
4
+ import { join } from "path";
5
+ import { pathToFileURL } from "url";
6
+
7
+ // ../../src/core/database/migrations/advisoryLock.ts
8
+ var MIGRATION_LOCK_KEY = 42424242;
9
+ async function withMigrationLock(db, callback, lockKey = MIGRATION_LOCK_KEY) {
10
+ await db.unsafe("SELECT pg_advisory_lock($1)", [lockKey]);
11
+ try {
12
+ return await callback();
13
+ } finally {
14
+ await db.unsafe("SELECT pg_advisory_unlock($1)", [lockKey]);
15
+ }
16
+ }
17
+
18
+ // ../../src/core/database/migrations/runner.ts
19
+ var MIGRATIONS_TABLE = "framework_migrations";
20
+ async function ensureMigrationsTable(db) {
21
+ await db.unsafe(`
22
+ CREATE TABLE IF NOT EXISTS ${MIGRATIONS_TABLE} (
23
+ name TEXT PRIMARY KEY,
24
+ batch INTEGER NOT NULL,
25
+ run_on TIMESTAMPTZ NOT NULL DEFAULT NOW()
26
+ )
27
+ `);
28
+ }
29
+ async function getAppliedMigrations(db) {
30
+ await ensureMigrationsTable(db);
31
+ return await db.unsafe(`
32
+ SELECT name, batch
33
+ FROM ${MIGRATIONS_TABLE}
34
+ ORDER BY batch ASC, name ASC
35
+ `);
36
+ }
37
+ async function loadMigrationsFromDirectory(directory) {
38
+ const entries = await readdir(directory);
39
+ const migrationFiles = entries.filter((entry) => (entry.endsWith(".ts") || entry.endsWith(".js")) && entry !== "types.ts" && entry !== "index.ts" && entry !== "runner.ts").sort();
40
+ const loadedMigrations = await Promise.all(migrationFiles.map(async (fileName) => {
41
+ const moduleUrl = pathToFileURL(join(directory, fileName)).href;
42
+ const module = await import(moduleUrl);
43
+ return module.default;
44
+ }));
45
+ return loadedMigrations.filter((migration) => migration?.name !== undefined);
46
+ }
47
+ async function getMigrationStatus(db, migrations) {
48
+ const applied = await getAppliedMigrations(db);
49
+ const appliedByName = new Map(applied.map(({ name, batch }) => [name, Number(batch)]));
50
+ return migrations.map(({ name }) => ({
51
+ name,
52
+ status: appliedByName.has(name) ? "up" : "pending",
53
+ batch: appliedByName.get(name) ?? null
54
+ }));
55
+ }
56
+ async function runPendingMigrations(db, migrations, options = {}) {
57
+ const applied = await getAppliedMigrations(db);
58
+ const appliedNames = new Set(applied.map(({ name }) => name));
59
+ const nextBatch = applied.reduce((currentMax, { batch }) => Math.max(currentMax, Number(batch)), 0) + 1;
60
+ const pendingMigrations = migrations.filter(({ name }) => !appliedNames.has(name));
61
+ for (const migration of pendingMigrations) {
62
+ options.onMigration?.(migration.name);
63
+ await migration.up(db);
64
+ const inserted = await db.unsafe(`INSERT INTO ${MIGRATIONS_TABLE} (name, batch) VALUES ($1, $2) ON CONFLICT (name) DO NOTHING RETURNING name`, [migration.name, nextBatch]);
65
+ if (inserted.length === 0) {
66
+ throw new Error(`Migration ${migration.name} was applied but not recorded.`);
67
+ }
68
+ }
69
+ return pendingMigrations.length;
70
+ }
71
+ async function migrateDatabase(db, migrations, options = {}) {
72
+ const { advisoryLock = false, onMigration } = options;
73
+ if (advisoryLock) {
74
+ return withMigrationLock(db, () => runPendingMigrations(db, migrations, { onMigration }));
75
+ }
76
+ return runPendingMigrations(db, migrations, { onMigration });
77
+ }
78
+ async function rollbackDatabase(db, migrations, options = {}) {
79
+ const applied = await getAppliedMigrations(db);
80
+ if (applied.length === 0) {
81
+ return 0;
82
+ }
83
+ const lastBatch = applied.reduce((currentMax, { batch }) => Math.max(currentMax, Number(batch)), 0);
84
+ const migrationsToRollback = new Set(applied.filter(({ batch }) => Number(batch) === lastBatch).map(({ name }) => name));
85
+ let rolledBack = 0;
86
+ for (const migration of [...migrations].reverse()) {
87
+ if (!migrationsToRollback.has(migration.name)) {
88
+ continue;
89
+ }
90
+ options.onMigration?.(migration.name);
91
+ await migration.down(db);
92
+ await db.unsafe(`DELETE FROM ${MIGRATIONS_TABLE} WHERE name = $1`, [migration.name]);
93
+ rolledBack += 1;
94
+ }
95
+ return rolledBack;
96
+ }
97
+ async function freshDatabase(db, migrations, options = {}) {
98
+ const runFresh = async () => {
99
+ const applied = await getAppliedMigrations(db);
100
+ const appliedNames = new Set(applied.map(({ name }) => name));
101
+ const appliedMigrations = migrations.filter(({ name }) => appliedNames.has(name));
102
+ for (const migration of [...appliedMigrations].reverse()) {
103
+ options.onMigration?.(migration.name);
104
+ await migration.down(db);
105
+ }
106
+ if (appliedMigrations.length > 0) {
107
+ await db.unsafe(`DELETE FROM ${MIGRATIONS_TABLE}`);
108
+ }
109
+ await runPendingMigrations(db, migrations, options);
110
+ };
111
+ if (options.advisoryLock) {
112
+ await withMigrationLock(db, runFresh);
113
+ return;
114
+ }
115
+ await runFresh();
116
+ }
117
+ export {
118
+ withMigrationLock,
119
+ runPendingMigrations,
120
+ rollbackDatabase,
121
+ migrateDatabase,
122
+ loadMigrationsFromDirectory,
123
+ getMigrationStatus,
124
+ getAppliedMigrations,
125
+ freshDatabase,
126
+ ensureMigrationsTable
127
+ };