@getstrata/bootstrap 0.2.20 → 0.2.22

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.
@@ -1 +1 @@
1
- export { resolveApplicationAuth, resolveApplicationCache, resolveApplicationConfig, resolveApplicationDependencies, resolveApplicationLogger, resolveApplicationPolicyGate, resolveApplicationQueue, setActiveApplicationContext, } from "../core/runtime/applicationRegistry.ts";
1
+ export { resolveApplicationAuth, resolveApplicationCache, resolveApplicationConfig, resolveApplicationDependencies, resolveApplicationEventBus, resolveApplicationLogger, resolveApplicationPolicyGate, resolveApplicationQueue, setActiveApplicationContext, } from "../core/runtime/applicationRegistry.ts";
@@ -4,7 +4,7 @@ declare const CACHE_MAX_ENTRIES_CONFIG_KEY = "cache.maxEntries";
4
4
  declare const CACHE_DRIVER_CONFIG_KEY = "cache.driver";
5
5
  declare const REDIS_URL_CONFIG_KEY = "cache.redisUrl";
6
6
  declare const DATABASE_URL_CONFIG_KEY = "database.url";
7
- export { CORE_AUTH_TOKEN, CORE_CACHE_TOKEN, CORE_CONFIG_TOKEN, CORE_POLICY_GATE_TOKEN, CORE_QUEUE_TOKEN, CORE_TOKEN_SERVICE_TOKEN, } from "../core/contracts/serviceTokens.ts";
7
+ export { CORE_AUTH_TOKEN, CORE_CACHE_TOKEN, CORE_CONFIG_TOKEN, CORE_EVENT_BUS_TOKEN, CORE_POLICY_GATE_TOKEN, CORE_QUEUE_TOKEN, CORE_TOKEN_SERVICE_TOKEN, } from "../core/contracts/serviceTokens.ts";
8
8
  declare const AUTH_DEV_HEADERS_CONFIG_KEY = "auth.allowDevHeaders";
9
9
  declare const DEFAULT_APP_PORT = 3000;
10
10
  declare const DEFAULT_CACHE_TTL_MS = 3600000;
@@ -1,5 +1,9 @@
1
1
  import type { AppModule } from "./contracts";
2
- declare let appModules: AppModule[];
3
- declare function ensureModulesLoaded(): Promise<AppModule[]>;
2
+ interface DiscoverModulesOptions {
3
+ modulesDir?: string;
4
+ }
5
+ declare function configureModulesDirectory(modulesDir: string): void;
6
+ declare function ensureModulesLoaded(options?: DiscoverModulesOptions): Promise<AppModule[]>;
4
7
  declare function discoverModules(): AppModule[];
5
- export { appModules, discoverModules, ensureModulesLoaded };
8
+ export type { DiscoverModulesOptions };
9
+ export { configureModulesDirectory, discoverModules, ensureModulesLoaded };
@@ -1 +1 @@
1
- export { appModules, discoverModules, ensureModulesLoaded } from "./discoverModules";
1
+ export { configureModulesDirectory, discoverModules, ensureModulesLoaded, } from "./discoverModules";
@@ -1,5 +1,5 @@
1
+ import { CORE_EVENT_BUS_TOKEN } from "../config";
1
2
  import type { ServiceProvider } from "../contracts";
2
- declare const CORE_EVENT_BUS_TOKEN = "core.eventBus";
3
3
  declare const eventsProvider: ServiceProvider;
4
4
  export default eventsProvider;
5
5
  export { CORE_EVENT_BUS_TOKEN };
@@ -4,17 +4,22 @@
4
4
  export { scheduleRunCommand } from "../cli/commands/scheduleRun.ts";
5
5
  export type { ScheduledTask } from "../core/scheduler/schedule.ts";
6
6
  export { appSchedule, runDueScheduledTasks, Schedule } from "../core/scheduler/schedule.ts";
7
- export { resolveApplicationAuth, resolveApplicationCache, resolveApplicationConfig, resolveApplicationDependencies, resolveApplicationLogger, resolveApplicationPolicyGate, resolveApplicationQueue, setActiveApplicationContext, } from "./applicationRegistry.ts";
7
+ export { resolveApplicationAuth, resolveApplicationCache, resolveApplicationConfig, resolveApplicationDependencies, resolveApplicationEventBus, resolveApplicationLogger, resolveApplicationPolicyGate, resolveApplicationQueue, setActiveApplicationContext, } from "./applicationRegistry.ts";
8
8
  export { cacheTagsForModelWrite, discoverModelTableNames } from "./cache/modelCacheTags.ts";
9
- export { APP_PORT_CONFIG_KEY, CORE_AUTH_TOKEN, CORE_CACHE_TOKEN, CORE_CONFIG_TOKEN, CORE_POLICY_GATE_TOKEN, CORE_QUEUE_TOKEN, CORE_TOKEN_SERVICE_TOKEN, DATABASE_URL_CONFIG_KEY, DEFAULT_APP_PORT, REDIS_URL_CONFIG_KEY, } from "./config.ts";
9
+ export { APP_PORT_CONFIG_KEY, CORE_AUTH_TOKEN, CORE_CACHE_TOKEN, CORE_CONFIG_TOKEN, CORE_EVENT_BUS_TOKEN, CORE_POLICY_GATE_TOKEN, CORE_QUEUE_TOKEN, CORE_TOKEN_SERVICE_TOKEN, DATABASE_URL_CONFIG_KEY, DEFAULT_APP_PORT, REDIS_URL_CONFIG_KEY, } from "./config.ts";
10
10
  export { collectProviders, createAppContext, runProviderPhase } from "./context.ts";
11
11
  export type { AppContext, AppDependencies, AppModule, AppRouteMap, CachedJson, ConfigStore, ModuleRouteContext, MutableAppDependencies, ProviderContext, ServiceFactory, ServiceProvider, } from "./contracts.ts";
12
12
  export { assertAppDependenciesComplete, getRequiredDependency, resolveService, ServiceContainer, } from "./contracts.ts";
13
13
  export { createWebRoutes, mergeWebRoutes } from "./createWebRoutes.ts";
14
+ export { createAppDependencies } from "./dependencies.ts";
15
+ export type { DiscoverModulesOptions } from "./discoverModules.ts";
16
+ export { configureModulesDirectory, discoverModules, ensureModulesLoaded, } from "./discoverModules.ts";
14
17
  export { type RouteModelAuthorization, securedBindRouteModel, securedBindRouteModelByKey, } from "./http/securedRouteModelBinding.ts";
15
18
  export { createHttpKernel, type HttpKernel, type MiddlewareGroupName } from "./httpKernel.ts";
16
19
  export { resolveMembershipService } from "./membershipService.ts";
17
20
  export { prefixRouteMap } from "./prefixRouteMap.ts";
18
21
  export { coreProviders } from "./providers/index.ts";
19
22
  export { registerDefaultJobs } from "./queue/defaultJobs.ts";
23
+ export { RouteRegistry, routeRegistry } from "./routeRegistry.ts";
24
+ export { assertProductionSecrets } from "./secretsGuard.ts";
20
25
  export { CookieSessionStore, createCsrfProtection, createRouteKernel, createWebServer, type ParsedForm, parseFormBody, routeParams, type SessionUser, slugify, toRouteRequest, type WebServerOptions, wrapSecuredRouteModelByKey, wrapWebLogin, wrapWebRegister, } from "./web/index.ts";
@@ -1,7 +1,8 @@
1
1
  declare const CORE_CONFIG_TOKEN = "core.config";
2
2
  declare const CORE_CACHE_TOKEN = "core.cache";
3
3
  declare const CORE_QUEUE_TOKEN = "core.queue";
4
+ declare const CORE_EVENT_BUS_TOKEN = "core.eventBus";
4
5
  declare const CORE_POLICY_GATE_TOKEN = "core.policyGate";
5
6
  declare const CORE_AUTH_TOKEN = "core.auth";
6
7
  declare const CORE_TOKEN_SERVICE_TOKEN = "core.tokenService";
7
- export { CORE_AUTH_TOKEN, CORE_CACHE_TOKEN, CORE_CONFIG_TOKEN, CORE_POLICY_GATE_TOKEN, CORE_QUEUE_TOKEN, CORE_TOKEN_SERVICE_TOKEN, };
8
+ export { CORE_AUTH_TOKEN, CORE_CACHE_TOKEN, CORE_CONFIG_TOKEN, CORE_EVENT_BUS_TOKEN, CORE_POLICY_GATE_TOKEN, CORE_QUEUE_TOKEN, CORE_TOKEN_SERVICE_TOKEN, };
@@ -2,14 +2,16 @@ import type { CacheLike } from "../../types/services";
2
2
  import type { AuthManager } from "../auth/guard";
3
3
  import type { PolicyGate } from "../auth/policy";
4
4
  import type { AppContext } from "../contracts/di";
5
+ import type { EventBus } from "../events";
5
6
  import { type Logger } from "../logging/logger";
6
7
  import type { Queue } from "../queue";
7
8
  declare function setActiveApplicationContext(context: AppContext): void;
8
9
  declare function resolveApplicationCache(): CacheLike;
9
10
  declare function resolveApplicationQueue(): Queue;
11
+ declare function resolveApplicationEventBus(): EventBus;
10
12
  declare function resolveApplicationAuth(): AuthManager;
11
13
  declare function resolveApplicationPolicyGate(): PolicyGate;
12
14
  declare function resolveApplicationConfig(): import("../contracts/container").ConfigStoreLike;
13
15
  declare function resolveApplicationLogger(): Logger;
14
16
  declare function resolveApplicationDependencies(): import("../contracts/di").AppDependencies;
15
- export { resolveApplicationAuth, resolveApplicationCache, resolveApplicationConfig, resolveApplicationDependencies, resolveApplicationLogger, resolveApplicationPolicyGate, resolveApplicationQueue, setActiveApplicationContext, };
17
+ export { resolveApplicationAuth, resolveApplicationCache, resolveApplicationConfig, resolveApplicationDependencies, resolveApplicationEventBus, resolveApplicationLogger, resolveApplicationPolicyGate, resolveApplicationQueue, setActiveApplicationContext, };
@@ -25,6 +25,7 @@ function resolveService(dependencies, token) {
25
25
  var CORE_CONFIG_TOKEN = "core.config";
26
26
  var CORE_CACHE_TOKEN = "core.cache";
27
27
  var CORE_QUEUE_TOKEN = "core.queue";
28
+ var CORE_EVENT_BUS_TOKEN = "core.eventBus";
28
29
  var CORE_POLICY_GATE_TOKEN = "core.policyGate";
29
30
  var CORE_AUTH_TOKEN = "core.auth";
30
31
  var CORE_TOKEN_SERVICE_TOKEN = "core.tokenService";
@@ -95,6 +96,9 @@ function resolveApplicationCache() {
95
96
  function resolveApplicationQueue() {
96
97
  return requireActiveApplicationContext().container.resolve(CORE_QUEUE_TOKEN);
97
98
  }
99
+ function resolveApplicationEventBus() {
100
+ return requireActiveApplicationContext().container.resolve(CORE_EVENT_BUS_TOKEN);
101
+ }
98
102
  function resolveApplicationAuth() {
99
103
  return requireActiveApplicationContext().container.resolve(CORE_AUTH_TOKEN);
100
104
  }
@@ -115,6 +119,7 @@ export {
115
119
  resolveApplicationQueue,
116
120
  resolveApplicationPolicyGate,
117
121
  resolveApplicationLogger,
122
+ resolveApplicationEventBus,
118
123
  resolveApplicationDependencies,
119
124
  resolveApplicationConfig,
120
125
  resolveApplicationCache,
@@ -1,8 +1,62 @@
1
1
  // @bun
2
2
  // ../../src/bootstrap/discoverModules.ts
3
- var appModules = [];
3
+ import { readdirSync } from "fs";
4
+ import { join } from "path";
5
+ import { pathToFileURL } from "url";
6
+ var DISCOVER_MODULES_STATE_KEY = Symbol.for("@getstrata/discoverModulesState");
7
+ function readDiscoverModulesState() {
8
+ const existing = globalThis[DISCOVER_MODULES_STATE_KEY];
9
+ if (existing) {
10
+ return existing;
11
+ }
12
+ const state = { appModules: [] };
13
+ globalThis[DISCOVER_MODULES_STATE_KEY] = state;
14
+ return state;
15
+ }
16
+ function configureModulesDirectory(modulesDir) {
17
+ readDiscoverModulesState().configuredModulesDir = modulesDir;
18
+ }
19
+ function resolveModulesDirectory(options) {
20
+ const state = readDiscoverModulesState();
21
+ if (options?.modulesDir) {
22
+ return options.modulesDir;
23
+ }
24
+ if (state.configuredModulesDir) {
25
+ return state.configuredModulesDir;
26
+ }
27
+ return join(import.meta.dir, "../modules");
28
+ }
29
+ async function loadDiscoveredModules(options) {
30
+ const modulesDirectory = resolveModulesDirectory(options);
31
+ let moduleNames;
32
+ try {
33
+ moduleNames = readdirSync(modulesDirectory, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
34
+ } catch (error) {
35
+ if (error.code === "ENOENT") {
36
+ return [];
37
+ }
38
+ throw error;
39
+ }
40
+ const modules = await Promise.all(moduleNames.map(async (moduleName) => {
41
+ const moduleUrl = pathToFileURL(join(modulesDirectory, moduleName, "index.ts")).href;
42
+ const loaded = await import(moduleUrl);
43
+ return loaded.default;
44
+ }));
45
+ return modules.filter((module) => module?.name !== undefined).sort((left, right) => (left.order ?? 100) - (right.order ?? 100));
46
+ }
47
+ async function ensureModulesLoaded(options) {
48
+ const state = readDiscoverModulesState();
49
+ if (state.appModules.length > 0) {
50
+ return state.appModules;
51
+ }
52
+ state.modulesReady ??= loadDiscoveredModules(options).then((modules) => {
53
+ state.appModules.splice(0, state.appModules.length, ...modules);
54
+ return state.appModules;
55
+ });
56
+ return state.modulesReady;
57
+ }
4
58
  function discoverModules() {
5
- return appModules;
59
+ return readDiscoverModulesState().appModules;
6
60
  }
7
61
 
8
62
  // ../../src/bootstrap/cache/modelCacheTags.ts
@@ -3,6 +3,7 @@
3
3
  var CORE_CONFIG_TOKEN = "core.config";
4
4
  var CORE_CACHE_TOKEN = "core.cache";
5
5
  var CORE_QUEUE_TOKEN = "core.queue";
6
+ var CORE_EVENT_BUS_TOKEN = "core.eventBus";
6
7
  var CORE_POLICY_GATE_TOKEN = "core.policyGate";
7
8
  var CORE_AUTH_TOKEN = "core.auth";
8
9
  var CORE_TOKEN_SERVICE_TOKEN = "core.tokenService";
@@ -33,6 +34,7 @@ export {
33
34
  CORE_TOKEN_SERVICE_TOKEN,
34
35
  CORE_QUEUE_TOKEN,
35
36
  CORE_POLICY_GATE_TOKEN,
37
+ CORE_EVENT_BUS_TOKEN,
36
38
  CORE_CONFIG_TOKEN,
37
39
  CORE_CACHE_TOKEN,
38
40
  CORE_AUTH_TOKEN,
@@ -25,6 +25,7 @@ function resolveService(dependencies, token) {
25
25
  var CORE_CONFIG_TOKEN = "core.config";
26
26
  var CORE_CACHE_TOKEN = "core.cache";
27
27
  var CORE_QUEUE_TOKEN = "core.queue";
28
+ var CORE_EVENT_BUS_TOKEN = "core.eventBus";
28
29
  var CORE_POLICY_GATE_TOKEN = "core.policyGate";
29
30
  var CORE_AUTH_TOKEN = "core.auth";
30
31
  var CORE_TOKEN_SERVICE_TOKEN = "core.tokenService";
@@ -95,6 +96,9 @@ function resolveApplicationCache() {
95
96
  function resolveApplicationQueue() {
96
97
  return requireActiveApplicationContext().container.resolve(CORE_QUEUE_TOKEN);
97
98
  }
99
+ function resolveApplicationEventBus() {
100
+ return requireActiveApplicationContext().container.resolve(CORE_EVENT_BUS_TOKEN);
101
+ }
98
102
  function resolveApplicationAuth() {
99
103
  return requireActiveApplicationContext().container.resolve(CORE_AUTH_TOKEN);
100
104
  }
@@ -175,9 +179,63 @@ class ConfigStore {
175
179
  }
176
180
  }
177
181
  // ../../src/bootstrap/discoverModules.ts
178
- var appModules = [];
182
+ import { readdirSync } from "fs";
183
+ import { join } from "path";
184
+ import { pathToFileURL } from "url";
185
+ var DISCOVER_MODULES_STATE_KEY = Symbol.for("@getstrata/discoverModulesState");
186
+ function readDiscoverModulesState() {
187
+ const existing = globalThis[DISCOVER_MODULES_STATE_KEY];
188
+ if (existing) {
189
+ return existing;
190
+ }
191
+ const state = { appModules: [] };
192
+ globalThis[DISCOVER_MODULES_STATE_KEY] = state;
193
+ return state;
194
+ }
195
+ function configureModulesDirectory(modulesDir) {
196
+ readDiscoverModulesState().configuredModulesDir = modulesDir;
197
+ }
198
+ function resolveModulesDirectory(options) {
199
+ const state = readDiscoverModulesState();
200
+ if (options?.modulesDir) {
201
+ return options.modulesDir;
202
+ }
203
+ if (state.configuredModulesDir) {
204
+ return state.configuredModulesDir;
205
+ }
206
+ return join(import.meta.dir, "../modules");
207
+ }
208
+ async function loadDiscoveredModules(options) {
209
+ const modulesDirectory = resolveModulesDirectory(options);
210
+ let moduleNames;
211
+ try {
212
+ moduleNames = readdirSync(modulesDirectory, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
213
+ } catch (error) {
214
+ if (error.code === "ENOENT") {
215
+ return [];
216
+ }
217
+ throw error;
218
+ }
219
+ const modules = await Promise.all(moduleNames.map(async (moduleName) => {
220
+ const moduleUrl = pathToFileURL(join(modulesDirectory, moduleName, "index.ts")).href;
221
+ const loaded = await import(moduleUrl);
222
+ return loaded.default;
223
+ }));
224
+ return modules.filter((module) => module?.name !== undefined).sort((left, right) => (left.order ?? 100) - (right.order ?? 100));
225
+ }
226
+ async function ensureModulesLoaded(options) {
227
+ const state = readDiscoverModulesState();
228
+ if (state.appModules.length > 0) {
229
+ return state.appModules;
230
+ }
231
+ state.modulesReady ??= loadDiscoveredModules(options).then((modules) => {
232
+ state.appModules.splice(0, state.appModules.length, ...modules);
233
+ return state.appModules;
234
+ });
235
+ return state.modulesReady;
236
+ }
179
237
  function discoverModules() {
180
- return appModules;
238
+ return readDiscoverModulesState().appModules;
181
239
  }
182
240
  // ../../src/config/auth.ts
183
241
  var authConfig = {
@@ -1449,7 +1507,6 @@ function modelEventName(tableName, action) {
1449
1507
  }
1450
1508
 
1451
1509
  // ../../src/bootstrap/providers/events.ts
1452
- var CORE_EVENT_BUS_TOKEN = "core.eventBus";
1453
1510
  var eventsProvider = {
1454
1511
  name: "core.events",
1455
1512
  register({ container }) {
@@ -1459,14 +1516,14 @@ var eventsProvider = {
1459
1516
  var events_default = eventsProvider;
1460
1517
 
1461
1518
  // ../../src/bootstrap/discoverListeners.ts
1462
- import { readdirSync } from "fs";
1463
- import { join } from "path";
1464
- import { pathToFileURL } from "url";
1519
+ import { readdirSync as readdirSync2 } from "fs";
1520
+ import { join as join2 } from "path";
1521
+ import { pathToFileURL as pathToFileURL2 } from "url";
1465
1522
  async function loadDiscoveredListeners() {
1466
- const listenersDirectory = join(import.meta.dir, "../listeners");
1523
+ const listenersDirectory = join2(import.meta.dir, "../listeners");
1467
1524
  let entries;
1468
1525
  try {
1469
- entries = readdirSync(listenersDirectory, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".ts")).map((entry) => entry.name);
1526
+ entries = readdirSync2(listenersDirectory, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".ts")).map((entry) => entry.name);
1470
1527
  } catch (error) {
1471
1528
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
1472
1529
  return [];
@@ -1474,7 +1531,7 @@ async function loadDiscoveredListeners() {
1474
1531
  throw error;
1475
1532
  }
1476
1533
  const listeners = await Promise.all(entries.map(async (fileName) => {
1477
- const moduleUrl = pathToFileURL(join(listenersDirectory, fileName)).href;
1534
+ const moduleUrl = pathToFileURL2(join2(listenersDirectory, fileName)).href;
1478
1535
  const loaded = await import(moduleUrl);
1479
1536
  return loaded.default;
1480
1537
  }));
@@ -1795,10 +1852,10 @@ function buildHavingClause(tableName, having, params) {
1795
1852
  return body.length > 0 ? ` HAVING ${body}` : "";
1796
1853
  }
1797
1854
  function buildJoinClause(joins = []) {
1798
- return joins.map((join2) => {
1799
- const joinType = join2.type === "left" ? "LEFT JOIN" : "INNER JOIN";
1800
- const onClause = join2.on.map(({ left, right }) => `${qualifyColumn(left.table, left.column)} = ${qualifyColumn(right.table, right.column)}`).join(" AND ");
1801
- return ` ${joinType} ${quoteIdentifier(join2.table)} ON ${onClause}`;
1855
+ return joins.map((join3) => {
1856
+ const joinType = join3.type === "left" ? "LEFT JOIN" : "INNER JOIN";
1857
+ const onClause = join3.on.map(({ left, right }) => `${qualifyColumn(left.table, left.column)} = ${qualifyColumn(right.table, right.column)}`).join(" AND ");
1858
+ return ` ${joinType} ${quoteIdentifier(join3.table)} ON ${onClause}`;
1802
1859
  }).join("");
1803
1860
  }
1804
1861
  function buildLimitClause(limit) {
@@ -2234,7 +2291,7 @@ class RepositoryQuery {
2234
2291
  const rightRef = parseQualifiedColumn(right);
2235
2292
  const table = type === "inner" ? rightRef.table : rightRef.table;
2236
2293
  const joins = this.queryOptions.joins ?? [];
2237
- const existing = joins.find((join2) => join2.table === table && join2.type === type);
2294
+ const existing = joins.find((join3) => join3.table === table && join3.type === type);
2238
2295
  if (existing) {
2239
2296
  existing.on.push({ left: leftRef, right: rightRef });
2240
2297
  return this;
@@ -3734,7 +3791,7 @@ var queue_default = queueProvider;
3734
3791
 
3735
3792
  // ../../src/core/storage/storage.ts
3736
3793
  import { mkdir, readFile, unlink, writeFile } from "fs/promises";
3737
- import { dirname, join as join2 } from "path";
3794
+ import { dirname, join as join3 } from "path";
3738
3795
  var {S3Client } = globalThis.Bun;
3739
3796
 
3740
3797
  class LocalStorageDriver {
@@ -3746,7 +3803,7 @@ class LocalStorageDriver {
3746
3803
  return this.rootDirectory ?? process.env.STORAGE_PATH ?? "storage";
3747
3804
  }
3748
3805
  resolvePath(path) {
3749
- return join2(this.resolveRootDirectory(), path.replace(/^\/+/, ""));
3806
+ return join3(this.resolveRootDirectory(), path.replace(/^\/+/, ""));
3750
3807
  }
3751
3808
  async put(path, contents) {
3752
3809
  const absolutePath = this.resolvePath(path);
@@ -3879,9 +3936,9 @@ function currentRequestMeta() {
3879
3936
  }
3880
3937
 
3881
3938
  // ../../src/core/view/etaViewEngine.ts
3882
- import { join as join3 } from "path";
3939
+ import { join as join4 } from "path";
3883
3940
  import { Eta } from "eta";
3884
- var DEFAULT_VIEWS_DIRECTORY = join3(process.cwd(), "resources/views");
3941
+ var DEFAULT_VIEWS_DIRECTORY = join4(process.cwd(), "resources/views");
3885
3942
  var DEFAULT_LAYOUT = "layouts/app.eta";
3886
3943
 
3887
3944
  class EtaViewEngine {
@@ -4174,7 +4231,7 @@ function assertProductionSecrets(env = process.env) {
4174
4231
  }
4175
4232
 
4176
4233
  // ../../src/bootstrap/context.ts
4177
- function collectProviders(modules = appModules) {
4234
+ function collectProviders(modules = discoverModules()) {
4178
4235
  return [...coreProviders, ...modules.flatMap((module) => module.providers ?? [])];
4179
4236
  }
4180
4237
  function runProviderPhase(providers, phase, context) {
@@ -1,6 +1,6 @@
1
1
  // @bun
2
2
  // ../../src/bootstrap/createWebRoutes.ts
3
- import { join as join2 } from "path";
3
+ import { join as join3 } from "path";
4
4
 
5
5
  // ../../src/config/frontend.ts
6
6
  function readFrontendMode() {
@@ -114,6 +114,7 @@ function htmlResponse(html, init = {}) {
114
114
  var CORE_CONFIG_TOKEN = "core.config";
115
115
  var CORE_CACHE_TOKEN = "core.cache";
116
116
  var CORE_QUEUE_TOKEN = "core.queue";
117
+ var CORE_EVENT_BUS_TOKEN = "core.eventBus";
117
118
  var CORE_POLICY_GATE_TOKEN = "core.policyGate";
118
119
  var CORE_AUTH_TOKEN = "core.auth";
119
120
  var CORE_TOKEN_SERVICE_TOKEN = "core.tokenService";
@@ -907,9 +908,63 @@ function createHttpKernel(dependencies) {
907
908
  }
908
909
 
909
910
  // ../../src/bootstrap/discoverModules.ts
910
- var appModules = [];
911
+ import { readdirSync } from "fs";
912
+ import { join as join2 } from "path";
913
+ import { pathToFileURL } from "url";
914
+ var DISCOVER_MODULES_STATE_KEY = Symbol.for("@getstrata/discoverModulesState");
915
+ function readDiscoverModulesState() {
916
+ const existing = globalThis[DISCOVER_MODULES_STATE_KEY];
917
+ if (existing) {
918
+ return existing;
919
+ }
920
+ const state = { appModules: [] };
921
+ globalThis[DISCOVER_MODULES_STATE_KEY] = state;
922
+ return state;
923
+ }
924
+ function configureModulesDirectory(modulesDir) {
925
+ readDiscoverModulesState().configuredModulesDir = modulesDir;
926
+ }
927
+ function resolveModulesDirectory(options) {
928
+ const state = readDiscoverModulesState();
929
+ if (options?.modulesDir) {
930
+ return options.modulesDir;
931
+ }
932
+ if (state.configuredModulesDir) {
933
+ return state.configuredModulesDir;
934
+ }
935
+ return join2(import.meta.dir, "../modules");
936
+ }
937
+ async function loadDiscoveredModules(options) {
938
+ const modulesDirectory = resolveModulesDirectory(options);
939
+ let moduleNames;
940
+ try {
941
+ moduleNames = readdirSync(modulesDirectory, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
942
+ } catch (error) {
943
+ if (error.code === "ENOENT") {
944
+ return [];
945
+ }
946
+ throw error;
947
+ }
948
+ const modules = await Promise.all(moduleNames.map(async (moduleName) => {
949
+ const moduleUrl = pathToFileURL(join2(modulesDirectory, moduleName, "index.ts")).href;
950
+ const loaded = await import(moduleUrl);
951
+ return loaded.default;
952
+ }));
953
+ return modules.filter((module) => module?.name !== undefined).sort((left, right) => (left.order ?? 100) - (right.order ?? 100));
954
+ }
955
+ async function ensureModulesLoaded(options) {
956
+ const state = readDiscoverModulesState();
957
+ if (state.appModules.length > 0) {
958
+ return state.appModules;
959
+ }
960
+ state.modulesReady ??= loadDiscoveredModules(options).then((modules) => {
961
+ state.appModules.splice(0, state.appModules.length, ...modules);
962
+ return state.appModules;
963
+ });
964
+ return state.modulesReady;
965
+ }
911
966
  function discoverModules() {
912
- return appModules;
967
+ return readDiscoverModulesState().appModules;
913
968
  }
914
969
  // ../../src/bootstrap/routeRegistry.ts
915
970
  class RouteRegistry {
@@ -924,7 +979,17 @@ class RouteRegistry {
924
979
  return [...this.routes].sort((left, right) => left.path.localeCompare(right.path));
925
980
  }
926
981
  }
927
- var routeRegistry = new RouteRegistry;
982
+ var ROUTE_REGISTRY_KEY = Symbol.for("@getstrata/routeRegistry");
983
+ function readSharedRouteRegistry() {
984
+ const globalRegistry = globalThis[ROUTE_REGISTRY_KEY];
985
+ if (globalRegistry) {
986
+ return globalRegistry;
987
+ }
988
+ const registry = new RouteRegistry;
989
+ globalThis[ROUTE_REGISTRY_KEY] = registry;
990
+ return registry;
991
+ }
992
+ var routeRegistry = readSharedRouteRegistry();
928
993
 
929
994
  // ../../src/bootstrap/createWebRoutes.ts
930
995
  function registerRoute(method, path, middleware) {
@@ -953,7 +1018,7 @@ function createWebRoutes(dependencies) {
953
1018
  "/": () => Response.redirect("/organizations", 302)
954
1019
  };
955
1020
  registerRoute("GET", "/", ["global", "web"]);
956
- for (const module of appModules) {
1021
+ for (const module of discoverModules()) {
957
1022
  if (!module.webRoutes) {
958
1023
  continue;
959
1024
  }
@@ -968,7 +1033,7 @@ function createWebRoutes(dependencies) {
968
1033
  registerRoute("GET", "/assets/*", ["global", "web"]);
969
1034
  const pathname = new URL(request.url).pathname;
970
1035
  const relativePath = pathname.replace(/^\//, "");
971
- const file = Bun.file(join2(process.cwd(), "public", relativePath));
1036
+ const file = Bun.file(join3(process.cwd(), "public", relativePath));
972
1037
  if (!await file.exists()) {
973
1038
  return htmlResponse("Not Found", { status: 404 });
974
1039
  }