@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.
package/dist/index.js CHANGED
@@ -529,6 +529,7 @@ function resolveService(dependencies, token) {
529
529
  var CORE_CONFIG_TOKEN = "core.config";
530
530
  var CORE_CACHE_TOKEN = "core.cache";
531
531
  var CORE_QUEUE_TOKEN = "core.queue";
532
+ var CORE_EVENT_BUS_TOKEN = "core.eventBus";
532
533
  var CORE_POLICY_GATE_TOKEN = "core.policyGate";
533
534
  var CORE_AUTH_TOKEN = "core.auth";
534
535
  var CORE_TOKEN_SERVICE_TOKEN = "core.tokenService";
@@ -563,6 +564,9 @@ function resolveApplicationCache() {
563
564
  function resolveApplicationQueue() {
564
565
  return requireActiveApplicationContext().container.resolve(CORE_QUEUE_TOKEN);
565
566
  }
567
+ function resolveApplicationEventBus() {
568
+ return requireActiveApplicationContext().container.resolve(CORE_EVENT_BUS_TOKEN);
569
+ }
566
570
  function resolveApplicationAuth() {
567
571
  return requireActiveApplicationContext().container.resolve(CORE_AUTH_TOKEN);
568
572
  }
@@ -579,9 +583,63 @@ function resolveApplicationDependencies() {
579
583
  return requireActiveApplicationContext().dependencies;
580
584
  }
581
585
  // ../../src/bootstrap/discoverModules.ts
582
- var appModules = [];
586
+ import { readdirSync } from "fs";
587
+ import { join } from "path";
588
+ import { pathToFileURL } from "url";
589
+ var DISCOVER_MODULES_STATE_KEY = Symbol.for("@getstrata/discoverModulesState");
590
+ function readDiscoverModulesState() {
591
+ const existing = globalThis[DISCOVER_MODULES_STATE_KEY];
592
+ if (existing) {
593
+ return existing;
594
+ }
595
+ const state = { appModules: [] };
596
+ globalThis[DISCOVER_MODULES_STATE_KEY] = state;
597
+ return state;
598
+ }
599
+ function configureModulesDirectory(modulesDir) {
600
+ readDiscoverModulesState().configuredModulesDir = modulesDir;
601
+ }
602
+ function resolveModulesDirectory(options) {
603
+ const state = readDiscoverModulesState();
604
+ if (options?.modulesDir) {
605
+ return options.modulesDir;
606
+ }
607
+ if (state.configuredModulesDir) {
608
+ return state.configuredModulesDir;
609
+ }
610
+ return join(import.meta.dir, "../modules");
611
+ }
612
+ async function loadDiscoveredModules(options) {
613
+ const modulesDirectory = resolveModulesDirectory(options);
614
+ let moduleNames;
615
+ try {
616
+ moduleNames = readdirSync(modulesDirectory, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
617
+ } catch (error) {
618
+ if (error.code === "ENOENT") {
619
+ return [];
620
+ }
621
+ throw error;
622
+ }
623
+ const modules = await Promise.all(moduleNames.map(async (moduleName) => {
624
+ const moduleUrl = pathToFileURL(join(modulesDirectory, moduleName, "index.ts")).href;
625
+ const loaded = await import(moduleUrl);
626
+ return loaded.default;
627
+ }));
628
+ return modules.filter((module) => module?.name !== undefined).sort((left, right) => (left.order ?? 100) - (right.order ?? 100));
629
+ }
630
+ async function ensureModulesLoaded(options) {
631
+ const state = readDiscoverModulesState();
632
+ if (state.appModules.length > 0) {
633
+ return state.appModules;
634
+ }
635
+ state.modulesReady ??= loadDiscoveredModules(options).then((modules) => {
636
+ state.appModules.splice(0, state.appModules.length, ...modules);
637
+ return state.appModules;
638
+ });
639
+ return state.modulesReady;
640
+ }
583
641
  function discoverModules() {
584
- return appModules;
642
+ return readDiscoverModulesState().appModules;
585
643
  }
586
644
 
587
645
  // ../../src/bootstrap/cache/modelCacheTags.ts
@@ -1790,7 +1848,6 @@ function modelEventName(tableName, action) {
1790
1848
  }
1791
1849
 
1792
1850
  // ../../src/bootstrap/providers/events.ts
1793
- var CORE_EVENT_BUS_TOKEN = "core.eventBus";
1794
1851
  var eventsProvider = {
1795
1852
  name: "core.events",
1796
1853
  register({ container }) {
@@ -1800,14 +1857,14 @@ var eventsProvider = {
1800
1857
  var events_default = eventsProvider;
1801
1858
 
1802
1859
  // ../../src/bootstrap/discoverListeners.ts
1803
- import { readdirSync } from "fs";
1804
- import { join } from "path";
1805
- import { pathToFileURL } from "url";
1860
+ import { readdirSync as readdirSync2 } from "fs";
1861
+ import { join as join2 } from "path";
1862
+ import { pathToFileURL as pathToFileURL2 } from "url";
1806
1863
  async function loadDiscoveredListeners() {
1807
- const listenersDirectory = join(import.meta.dir, "../listeners");
1864
+ const listenersDirectory = join2(import.meta.dir, "../listeners");
1808
1865
  let entries;
1809
1866
  try {
1810
- entries = readdirSync(listenersDirectory, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".ts")).map((entry) => entry.name);
1867
+ entries = readdirSync2(listenersDirectory, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".ts")).map((entry) => entry.name);
1811
1868
  } catch (error) {
1812
1869
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
1813
1870
  return [];
@@ -1815,7 +1872,7 @@ async function loadDiscoveredListeners() {
1815
1872
  throw error;
1816
1873
  }
1817
1874
  const listeners = await Promise.all(entries.map(async (fileName) => {
1818
- const moduleUrl = pathToFileURL(join(listenersDirectory, fileName)).href;
1875
+ const moduleUrl = pathToFileURL2(join2(listenersDirectory, fileName)).href;
1819
1876
  const loaded = await import(moduleUrl);
1820
1877
  return loaded.default;
1821
1878
  }));
@@ -2136,10 +2193,10 @@ function buildHavingClause(tableName, having, params) {
2136
2193
  return body.length > 0 ? ` HAVING ${body}` : "";
2137
2194
  }
2138
2195
  function buildJoinClause(joins = []) {
2139
- return joins.map((join2) => {
2140
- const joinType = join2.type === "left" ? "LEFT JOIN" : "INNER JOIN";
2141
- const onClause = join2.on.map(({ left, right }) => `${qualifyColumn(left.table, left.column)} = ${qualifyColumn(right.table, right.column)}`).join(" AND ");
2142
- return ` ${joinType} ${quoteIdentifier(join2.table)} ON ${onClause}`;
2196
+ return joins.map((join3) => {
2197
+ const joinType = join3.type === "left" ? "LEFT JOIN" : "INNER JOIN";
2198
+ const onClause = join3.on.map(({ left, right }) => `${qualifyColumn(left.table, left.column)} = ${qualifyColumn(right.table, right.column)}`).join(" AND ");
2199
+ return ` ${joinType} ${quoteIdentifier(join3.table)} ON ${onClause}`;
2143
2200
  }).join("");
2144
2201
  }
2145
2202
  function buildLimitClause(limit) {
@@ -2552,7 +2609,7 @@ class RepositoryQuery {
2552
2609
  const rightRef = parseQualifiedColumn(right);
2553
2610
  const table = type === "inner" ? rightRef.table : rightRef.table;
2554
2611
  const joins = this.queryOptions.joins ?? [];
2555
- const existing = joins.find((join2) => join2.table === table && join2.type === type);
2612
+ const existing = joins.find((join3) => join3.table === table && join3.type === type);
2556
2613
  if (existing) {
2557
2614
  existing.on.push({ left: leftRef, right: rightRef });
2558
2615
  return this;
@@ -3914,7 +3971,7 @@ var queue_default = queueProvider;
3914
3971
 
3915
3972
  // ../../src/core/storage/storage.ts
3916
3973
  import { mkdir, readFile, unlink, writeFile } from "fs/promises";
3917
- import { dirname, join as join2 } from "path";
3974
+ import { dirname, join as join3 } from "path";
3918
3975
  var {S3Client } = globalThis.Bun;
3919
3976
 
3920
3977
  class LocalStorageDriver {
@@ -3926,7 +3983,7 @@ class LocalStorageDriver {
3926
3983
  return this.rootDirectory ?? process.env.STORAGE_PATH ?? "storage";
3927
3984
  }
3928
3985
  resolvePath(path) {
3929
- return join2(this.resolveRootDirectory(), path.replace(/^\/+/, ""));
3986
+ return join3(this.resolveRootDirectory(), path.replace(/^\/+/, ""));
3930
3987
  }
3931
3988
  async put(path, contents) {
3932
3989
  const absolutePath = this.resolvePath(path);
@@ -4059,9 +4116,9 @@ function currentRequestMeta() {
4059
4116
  }
4060
4117
 
4061
4118
  // ../../src/core/view/etaViewEngine.ts
4062
- import { join as join3 } from "path";
4119
+ import { join as join4 } from "path";
4063
4120
  import { Eta } from "eta";
4064
- var DEFAULT_VIEWS_DIRECTORY = join3(process.cwd(), "resources/views");
4121
+ var DEFAULT_VIEWS_DIRECTORY = join4(process.cwd(), "resources/views");
4065
4122
  var DEFAULT_LAYOUT = "layouts/app.eta";
4066
4123
 
4067
4124
  class EtaViewEngine {
@@ -4354,7 +4411,7 @@ function assertProductionSecrets(env = process.env) {
4354
4411
  }
4355
4412
 
4356
4413
  // ../../src/bootstrap/context.ts
4357
- function collectProviders(modules = appModules) {
4414
+ function collectProviders(modules = discoverModules()) {
4358
4415
  return [...coreProviders, ...modules.flatMap((module) => module.providers ?? [])];
4359
4416
  }
4360
4417
  function runProviderPhase(providers, phase, context) {
@@ -4387,7 +4444,7 @@ function createAppContext() {
4387
4444
  return appContext;
4388
4445
  }
4389
4446
  // ../../src/bootstrap/createWebRoutes.ts
4390
- import { join as join4 } from "path";
4447
+ import { join as join5 } from "path";
4391
4448
 
4392
4449
  // ../../src/core/http/middleware.ts
4393
4450
  function isRouteHandler(value) {
@@ -4677,7 +4734,17 @@ class RouteRegistry {
4677
4734
  return [...this.routes].sort((left, right) => left.path.localeCompare(right.path));
4678
4735
  }
4679
4736
  }
4680
- var routeRegistry = new RouteRegistry;
4737
+ var ROUTE_REGISTRY_KEY = Symbol.for("@getstrata/routeRegistry");
4738
+ function readSharedRouteRegistry() {
4739
+ const globalRegistry = globalThis[ROUTE_REGISTRY_KEY];
4740
+ if (globalRegistry) {
4741
+ return globalRegistry;
4742
+ }
4743
+ const registry = new RouteRegistry;
4744
+ globalThis[ROUTE_REGISTRY_KEY] = registry;
4745
+ return registry;
4746
+ }
4747
+ var routeRegistry = readSharedRouteRegistry();
4681
4748
 
4682
4749
  // ../../src/bootstrap/createWebRoutes.ts
4683
4750
  function registerRoute(method, path, middleware) {
@@ -4706,7 +4773,7 @@ function createWebRoutes(dependencies) {
4706
4773
  "/": () => Response.redirect("/organizations", 302)
4707
4774
  };
4708
4775
  registerRoute("GET", "/", ["global", "web"]);
4709
- for (const module of appModules) {
4776
+ for (const module of discoverModules()) {
4710
4777
  if (!module.webRoutes) {
4711
4778
  continue;
4712
4779
  }
@@ -4721,7 +4788,7 @@ function createWebRoutes(dependencies) {
4721
4788
  registerRoute("GET", "/assets/*", ["global", "web"]);
4722
4789
  const pathname = new URL(request.url).pathname;
4723
4790
  const relativePath = pathname.replace(/^\//, "");
4724
- const file = Bun.file(join4(process.cwd(), "public", relativePath));
4791
+ const file = Bun.file(join5(process.cwd(), "public", relativePath));
4725
4792
  if (!await file.exists()) {
4726
4793
  return htmlResponse("Not Found", { status: 404 });
4727
4794
  }
@@ -4739,6 +4806,10 @@ function mergeWebRoutes(dependencies, routes) {
4739
4806
  ...routes
4740
4807
  };
4741
4808
  }
4809
+ // ../../src/bootstrap/dependencies.ts
4810
+ function createAppDependencies() {
4811
+ return createAppContext().dependencies;
4812
+ }
4742
4813
  // ../../src/core/crypto/nonCryptographicHash.ts
4743
4814
  function nonCryptographicDigest(input) {
4744
4815
  return Bun.hash(input).toString(16);
@@ -5270,12 +5341,14 @@ export {
5270
5341
  scheduleRunCommand,
5271
5342
  runProviderPhase,
5272
5343
  runDueScheduledTasks,
5344
+ routeRegistry,
5273
5345
  routeParams,
5274
5346
  resolveService,
5275
5347
  resolveMembershipService,
5276
5348
  resolveApplicationQueue,
5277
5349
  resolveApplicationPolicyGate,
5278
5350
  resolveApplicationLogger,
5351
+ resolveApplicationEventBus,
5279
5352
  resolveApplicationDependencies,
5280
5353
  resolveApplicationConfig,
5281
5354
  resolveApplicationCache,
@@ -5285,20 +5358,26 @@ export {
5285
5358
  parseFormBody,
5286
5359
  mergeWebRoutes,
5287
5360
  getRequiredDependency,
5361
+ ensureModulesLoaded,
5362
+ discoverModules,
5288
5363
  discoverModelTableNames,
5289
5364
  createWebServer,
5290
5365
  createWebRoutes,
5291
5366
  createRouteKernel,
5292
5367
  createHttpKernel,
5293
5368
  createCsrfProtection,
5369
+ createAppDependencies,
5294
5370
  createAppContext,
5295
5371
  coreProviders,
5372
+ configureModulesDirectory,
5296
5373
  collectProviders,
5297
5374
  cacheTagsForModelWrite,
5375
+ assertProductionSecrets,
5298
5376
  assertAppDependenciesComplete,
5299
5377
  appSchedule,
5300
5378
  ServiceContainer,
5301
5379
  Schedule,
5380
+ RouteRegistry,
5302
5381
  REDIS_URL_CONFIG_KEY,
5303
5382
  DEFAULT_APP_PORT,
5304
5383
  DATABASE_URL_CONFIG_KEY,
@@ -5306,6 +5385,7 @@ export {
5306
5385
  CORE_TOKEN_SERVICE_TOKEN,
5307
5386
  CORE_QUEUE_TOKEN,
5308
5387
  CORE_POLICY_GATE_TOKEN,
5388
+ CORE_EVENT_BUS_TOKEN,
5309
5389
  CORE_CONFIG_TOKEN,
5310
5390
  CORE_CACHE_TOKEN,
5311
5391
  CORE_AUTH_TOKEN,
@@ -1,4 +1,4 @@
1
- import type { AppDependencies } from "@getstrata/bootstrap/contracts";
1
+ import type { AppDependencies } from "@getstrata/core/contracts/di";
2
2
  import type ScimService from "./service";
3
3
  declare class ScimController {
4
4
  private readonly service;
@@ -1,4 +1,4 @@
1
- import type { AppDependencies } from "@getstrata/bootstrap/contracts";
1
+ import type { AppDependencies } from "@getstrata/core/contracts/di";
2
2
  import OrganizationMemberRepository from "../organization/memberRepository";
3
3
  import OrganizationRepository from "../organization/repository";
4
4
  import type UserRepository from "../user/repository";
@@ -1,4 +1,4 @@
1
- import type { ServiceProvider } from "@getstrata/bootstrap/contracts";
1
+ import type { ServiceProvider } from "@getstrata/core/contracts/di";
2
2
  declare const userRepositoryToken = "user.repository";
3
3
  declare const apiTokenRepositoryToken = "user.apiTokenRepository";
4
4
  declare const tokenServiceToken = "core.tokenService";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getstrata/bootstrap",
3
- "version": "0.2.20",
3
+ "version": "0.2.22",
4
4
  "description": "Strata application bootstrap — HttpKernel, DI, web session helpers",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -45,6 +45,16 @@
45
45
  "import": "./dist/entries/createWebRoutes.js",
46
46
  "default": "./dist/entries/createWebRoutes.js"
47
47
  },
48
+ "./dependencies": {
49
+ "types": "./dist/bootstrap/dependencies.d.ts",
50
+ "import": "./dist/entries/dependencies.js",
51
+ "default": "./dist/entries/dependencies.js"
52
+ },
53
+ "./discoverModules": {
54
+ "types": "./dist/bootstrap/discoverModules.d.ts",
55
+ "import": "./dist/entries/discoverModules.js",
56
+ "default": "./dist/entries/discoverModules.js"
57
+ },
48
58
  "./http/securedRouteModelBinding": {
49
59
  "types": "./dist/bootstrap/http/securedRouteModelBinding.d.ts",
50
60
  "import": "./dist/entries/http/securedRouteModelBinding.js",
@@ -75,6 +85,16 @@
75
85
  "import": "./dist/entries/providers/view.js",
76
86
  "default": "./dist/entries/providers/view.js"
77
87
  },
88
+ "./routeRegistry": {
89
+ "types": "./dist/bootstrap/routeRegistry.d.ts",
90
+ "import": "./dist/entries/routeRegistry.js",
91
+ "default": "./dist/entries/routeRegistry.js"
92
+ },
93
+ "./secretsGuard": {
94
+ "types": "./dist/bootstrap/secretsGuard.d.ts",
95
+ "import": "./dist/entries/secretsGuard.js",
96
+ "default": "./dist/entries/secretsGuard.js"
97
+ },
78
98
  "./web/forms": {
79
99
  "types": "./dist/bootstrap/web/forms.d.ts",
80
100
  "import": "./dist/entries/web/forms.js",
@@ -112,14 +132,14 @@
112
132
  "build:bundle": "bun build index.ts --outdir dist --target bun --external bun --external eta --external @getstrata/core",
113
133
  "build:types": "tsc -p tsconfig.types.json",
114
134
  "prepublishOnly": "bun run build",
115
- "build:subpaths": "bun build entries/applicationRegistry.ts entries/cache/modelCacheTags.ts entries/config.ts entries/context.ts entries/contracts.ts entries/createWebRoutes.ts entries/http/securedRouteModelBinding.ts entries/httpKernel.ts entries/membershipService.ts entries/queue/defaultJobs.ts entries/providers.ts entries/providers/view.ts entries/web/forms.ts entries/web/routing.ts entries/web/server.ts entries/web/session.ts entries/web/slug.ts --outdir dist --root . --target bun --external bun --external eta --external @getstrata/core",
135
+ "build:subpaths": "bun build entries/applicationRegistry.ts entries/cache/modelCacheTags.ts entries/config.ts entries/context.ts entries/contracts.ts entries/createWebRoutes.ts entries/dependencies.ts entries/discoverModules.ts entries/http/securedRouteModelBinding.ts entries/httpKernel.ts entries/membershipService.ts entries/queue/defaultJobs.ts entries/providers.ts entries/providers/view.ts entries/routeRegistry.ts entries/secretsGuard.ts entries/web/forms.ts entries/web/routing.ts entries/web/server.ts entries/web/session.ts entries/web/slug.ts --outdir dist --root . --target bun --external bun --external eta --external @getstrata/core",
116
136
  "build:shims": "true"
117
137
  },
118
138
  "publishConfig": {
119
139
  "access": "public"
120
140
  },
121
141
  "peerDependencies": {
122
- "@getstrata/core": "^0.5.28",
142
+ "@getstrata/core": "^0.5.30",
123
143
  "typescript": "^5.9.0"
124
144
  }
125
145
  }