@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.
@@ -0,0 +1,65 @@
1
+ // @bun
2
+ // ../../src/bootstrap/discoverModules.ts
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
+ }
58
+ function discoverModules() {
59
+ return readDiscoverModulesState().appModules;
60
+ }
61
+ export {
62
+ ensureModulesLoaded,
63
+ discoverModules,
64
+ configureModulesDirectory
65
+ };
@@ -90,6 +90,7 @@ function resolveService(dependencies, token) {
90
90
  var CORE_CONFIG_TOKEN = "core.config";
91
91
  var CORE_CACHE_TOKEN = "core.cache";
92
92
  var CORE_QUEUE_TOKEN = "core.queue";
93
+ var CORE_EVENT_BUS_TOKEN = "core.eventBus";
93
94
  var CORE_POLICY_GATE_TOKEN = "core.policyGate";
94
95
  var CORE_AUTH_TOKEN = "core.auth";
95
96
  var CORE_TOKEN_SERVICE_TOKEN = "core.tokenService";
@@ -160,6 +161,9 @@ function resolveApplicationCache() {
160
161
  function resolveApplicationQueue() {
161
162
  return requireActiveApplicationContext().container.resolve(CORE_QUEUE_TOKEN);
162
163
  }
164
+ function resolveApplicationEventBus() {
165
+ return requireActiveApplicationContext().container.resolve(CORE_EVENT_BUS_TOKEN);
166
+ }
163
167
  function resolveApplicationAuth() {
164
168
  return requireActiveApplicationContext().container.resolve(CORE_AUTH_TOKEN);
165
169
  }
@@ -78,6 +78,7 @@ function resolveRegisterRateLimit() {
78
78
  var CORE_CONFIG_TOKEN = "core.config";
79
79
  var CORE_CACHE_TOKEN = "core.cache";
80
80
  var CORE_QUEUE_TOKEN = "core.queue";
81
+ var CORE_EVENT_BUS_TOKEN = "core.eventBus";
81
82
  var CORE_POLICY_GATE_TOKEN = "core.policyGate";
82
83
  var CORE_AUTH_TOKEN = "core.auth";
83
84
  var CORE_TOKEN_SERVICE_TOKEN = "core.tokenService";
@@ -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
  }
@@ -83,6 +83,7 @@ function htmlResponse(html, init = {}) {
83
83
  var CORE_CONFIG_TOKEN = "core.config";
84
84
  var CORE_CACHE_TOKEN = "core.cache";
85
85
  var CORE_QUEUE_TOKEN = "core.queue";
86
+ var CORE_EVENT_BUS_TOKEN = "core.eventBus";
86
87
  var CORE_POLICY_GATE_TOKEN = "core.policyGate";
87
88
  var CORE_AUTH_TOKEN = "core.auth";
88
89
  var CORE_TOKEN_SERVICE_TOKEN = "core.tokenService";
@@ -47,6 +47,7 @@ function resolveAbilitiesForRole(role) {
47
47
  var CORE_CONFIG_TOKEN = "core.config";
48
48
  var CORE_CACHE_TOKEN = "core.cache";
49
49
  var CORE_QUEUE_TOKEN = "core.queue";
50
+ var CORE_EVENT_BUS_TOKEN = "core.eventBus";
50
51
  var CORE_POLICY_GATE_TOKEN = "core.policyGate";
51
52
  var CORE_AUTH_TOKEN = "core.auth";
52
53
  var CORE_TOKEN_SERVICE_TOKEN = "core.tokenService";
@@ -1277,7 +1278,6 @@ function modelEventName(tableName, action) {
1277
1278
  }
1278
1279
 
1279
1280
  // ../../src/bootstrap/providers/events.ts
1280
- var CORE_EVENT_BUS_TOKEN = "core.eventBus";
1281
1281
  var eventsProvider = {
1282
1282
  name: "core.events",
1283
1283
  register({ container }) {
@@ -3332,6 +3332,9 @@ function resolveApplicationCache() {
3332
3332
  function resolveApplicationQueue() {
3333
3333
  return requireActiveApplicationContext().container.resolve(CORE_QUEUE_TOKEN);
3334
3334
  }
3335
+ function resolveApplicationEventBus() {
3336
+ return requireActiveApplicationContext().container.resolve(CORE_EVENT_BUS_TOKEN);
3337
+ }
3335
3338
  function resolveApplicationAuth() {
3336
3339
  return requireActiveApplicationContext().container.resolve(CORE_AUTH_TOKEN);
3337
3340
  }
@@ -3348,9 +3351,63 @@ function resolveApplicationDependencies() {
3348
3351
  return requireActiveApplicationContext().dependencies;
3349
3352
  }
3350
3353
  // ../../src/bootstrap/discoverModules.ts
3351
- var appModules = [];
3354
+ import { readdirSync as readdirSync2 } from "fs";
3355
+ import { join as join2 } from "path";
3356
+ import { pathToFileURL as pathToFileURL2 } from "url";
3357
+ var DISCOVER_MODULES_STATE_KEY = Symbol.for("@getstrata/discoverModulesState");
3358
+ function readDiscoverModulesState() {
3359
+ const existing = globalThis[DISCOVER_MODULES_STATE_KEY];
3360
+ if (existing) {
3361
+ return existing;
3362
+ }
3363
+ const state = { appModules: [] };
3364
+ globalThis[DISCOVER_MODULES_STATE_KEY] = state;
3365
+ return state;
3366
+ }
3367
+ function configureModulesDirectory(modulesDir) {
3368
+ readDiscoverModulesState().configuredModulesDir = modulesDir;
3369
+ }
3370
+ function resolveModulesDirectory(options) {
3371
+ const state = readDiscoverModulesState();
3372
+ if (options?.modulesDir) {
3373
+ return options.modulesDir;
3374
+ }
3375
+ if (state.configuredModulesDir) {
3376
+ return state.configuredModulesDir;
3377
+ }
3378
+ return join2(import.meta.dir, "../modules");
3379
+ }
3380
+ async function loadDiscoveredModules(options) {
3381
+ const modulesDirectory = resolveModulesDirectory(options);
3382
+ let moduleNames;
3383
+ try {
3384
+ moduleNames = readdirSync2(modulesDirectory, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
3385
+ } catch (error) {
3386
+ if (error.code === "ENOENT") {
3387
+ return [];
3388
+ }
3389
+ throw error;
3390
+ }
3391
+ const modules = await Promise.all(moduleNames.map(async (moduleName) => {
3392
+ const moduleUrl = pathToFileURL2(join2(modulesDirectory, moduleName, "index.ts")).href;
3393
+ const loaded = await import(moduleUrl);
3394
+ return loaded.default;
3395
+ }));
3396
+ return modules.filter((module) => module?.name !== undefined).sort((left, right) => (left.order ?? 100) - (right.order ?? 100));
3397
+ }
3398
+ async function ensureModulesLoaded(options) {
3399
+ const state = readDiscoverModulesState();
3400
+ if (state.appModules.length > 0) {
3401
+ return state.appModules;
3402
+ }
3403
+ state.modulesReady ??= loadDiscoveredModules(options).then((modules) => {
3404
+ state.appModules.splice(0, state.appModules.length, ...modules);
3405
+ return state.appModules;
3406
+ });
3407
+ return state.modulesReady;
3408
+ }
3352
3409
  function discoverModules() {
3353
- return appModules;
3410
+ return readDiscoverModulesState().appModules;
3354
3411
  }
3355
3412
 
3356
3413
  // ../../src/bootstrap/cache/modelCacheTags.ts
@@ -3671,7 +3728,7 @@ var queue_default = queueProvider;
3671
3728
 
3672
3729
  // ../../src/core/storage/storage.ts
3673
3730
  import { mkdir, readFile, unlink, writeFile } from "fs/promises";
3674
- import { dirname, join as join2 } from "path";
3731
+ import { dirname, join as join3 } from "path";
3675
3732
  var {S3Client } = globalThis.Bun;
3676
3733
 
3677
3734
  class LocalStorageDriver {
@@ -3683,7 +3740,7 @@ class LocalStorageDriver {
3683
3740
  return this.rootDirectory ?? process.env.STORAGE_PATH ?? "storage";
3684
3741
  }
3685
3742
  resolvePath(path) {
3686
- return join2(this.resolveRootDirectory(), path.replace(/^\/+/, ""));
3743
+ return join3(this.resolveRootDirectory(), path.replace(/^\/+/, ""));
3687
3744
  }
3688
3745
  async put(path, contents) {
3689
3746
  const absolutePath = this.resolvePath(path);
@@ -3816,9 +3873,9 @@ function currentRequestMeta() {
3816
3873
  }
3817
3874
 
3818
3875
  // ../../src/core/view/etaViewEngine.ts
3819
- import { join as join3 } from "path";
3876
+ import { join as join4 } from "path";
3820
3877
  import { Eta } from "eta";
3821
- var DEFAULT_VIEWS_DIRECTORY = join3(process.cwd(), "resources/views");
3878
+ var DEFAULT_VIEWS_DIRECTORY = join4(process.cwd(), "resources/views");
3822
3879
  var DEFAULT_LAYOUT = "layouts/app.eta";
3823
3880
 
3824
3881
  class EtaViewEngine {
@@ -410,6 +410,7 @@ function resolveService(dependencies, token) {
410
410
  var CORE_CONFIG_TOKEN = "core.config";
411
411
  var CORE_CACHE_TOKEN = "core.cache";
412
412
  var CORE_QUEUE_TOKEN = "core.queue";
413
+ var CORE_EVENT_BUS_TOKEN = "core.eventBus";
413
414
  var CORE_POLICY_GATE_TOKEN = "core.policyGate";
414
415
  var CORE_AUTH_TOKEN = "core.auth";
415
416
  var CORE_TOKEN_SERVICE_TOKEN = "core.tokenService";
@@ -480,6 +481,9 @@ function resolveApplicationCache() {
480
481
  function resolveApplicationQueue() {
481
482
  return requireActiveApplicationContext().container.resolve(CORE_QUEUE_TOKEN);
482
483
  }
484
+ function resolveApplicationEventBus() {
485
+ return requireActiveApplicationContext().container.resolve(CORE_EVENT_BUS_TOKEN);
486
+ }
483
487
  function resolveApplicationAuth() {
484
488
  return requireActiveApplicationContext().container.resolve(CORE_AUTH_TOKEN);
485
489
  }
@@ -0,0 +1,29 @@
1
+ // @bun
2
+ // ../../src/bootstrap/routeRegistry.ts
3
+ class RouteRegistry {
4
+ routes = [];
5
+ register(route) {
6
+ this.routes.push(route);
7
+ }
8
+ clear() {
9
+ this.routes.length = 0;
10
+ }
11
+ list() {
12
+ return [...this.routes].sort((left, right) => left.path.localeCompare(right.path));
13
+ }
14
+ }
15
+ var ROUTE_REGISTRY_KEY = Symbol.for("@getstrata/routeRegistry");
16
+ function readSharedRouteRegistry() {
17
+ const globalRegistry = globalThis[ROUTE_REGISTRY_KEY];
18
+ if (globalRegistry) {
19
+ return globalRegistry;
20
+ }
21
+ const registry = new RouteRegistry;
22
+ globalThis[ROUTE_REGISTRY_KEY] = registry;
23
+ return registry;
24
+ }
25
+ var routeRegistry = readSharedRouteRegistry();
26
+ export {
27
+ routeRegistry,
28
+ RouteRegistry
29
+ };
@@ -0,0 +1,91 @@
1
+ // @bun
2
+ // ../../src/config/app.ts
3
+ var appConfig = {
4
+ name: "WorkHub",
5
+ env: process.env.APP_ENV ?? "local",
6
+ debug: (process.env.APP_DEBUG ?? "true") !== "false",
7
+ url: process.env.APP_URL ?? "http://localhost:3000",
8
+ apiPrefix: process.env.API_PREFIX ?? "/api/v1"
9
+ };
10
+
11
+ // ../../src/config/features.ts
12
+ function readFeatureFlags() {
13
+ return {
14
+ webhooks: (process.env.FEATURE_WEBHOOKS ?? "true") !== "false",
15
+ fullTextSearch: (process.env.FEATURE_SEARCH ?? "true") !== "false",
16
+ auditLog: (process.env.FEATURE_AUDIT_LOG ?? "true") !== "false",
17
+ oauthLogin: (process.env.FEATURE_OAUTH ?? "true") !== "false",
18
+ samlLogin: (process.env.FEATURE_SAML ?? "false") === "true",
19
+ scim: (process.env.FEATURE_SCIM ?? "true") !== "false",
20
+ billing: (process.env.FEATURE_BILLING ?? "true") !== "false",
21
+ siemExport: (process.env.FEATURE_SIEM_EXPORT ?? "true") !== "false",
22
+ publicReads: (process.env.FEATURE_PUBLIC_READS ?? "true") !== "false",
23
+ emailVerification: (process.env.FEATURE_EMAIL_VERIFICATION ?? "false") === "true",
24
+ mfa: (process.env.FEATURE_MFA ?? "false") === "true"
25
+ };
26
+ }
27
+ var featureFlags = readFeatureFlags();
28
+ function isFeatureEnabled(feature) {
29
+ return readFeatureFlags()[feature];
30
+ }
31
+
32
+ // ../../src/domain/auth.ts
33
+ var TEST_ADMIN_API_TOKEN = "workhub-admin-test-token";
34
+ var TEST_MEMBER_API_TOKEN = "workhub-member-test-token";
35
+
36
+ // ../../src/domain/scim.ts
37
+ var TEST_SCIM_BEARER_TOKEN = "workhub-scim-test-token";
38
+ var DEFAULT_SCIM_BEARER_TOKEN = TEST_SCIM_BEARER_TOKEN;
39
+
40
+ // ../../src/bootstrap/secretsGuard.ts
41
+ var DEFAULT_TOKENS = new Set([TEST_ADMIN_API_TOKEN, TEST_MEMBER_API_TOKEN]);
42
+ var DEFAULT_SCIM_TOKENS = new Set([TEST_SCIM_BEARER_TOKEN, DEFAULT_SCIM_BEARER_TOKEN]);
43
+ function assertProductionSecrets(env = process.env) {
44
+ const appEnv = env.APP_ENV ?? appConfig.env;
45
+ if (appEnv !== "production") {
46
+ return;
47
+ }
48
+ const adminToken = env.ADMIN_API_TOKEN ?? TEST_ADMIN_API_TOKEN;
49
+ const memberToken = env.MEMBER_API_TOKEN ?? TEST_MEMBER_API_TOKEN;
50
+ const scimToken = env.SCIM_BEARER_TOKEN ?? DEFAULT_SCIM_BEARER_TOKEN;
51
+ const encryptionEnabled = env.FEATURE_FIELD_ENCRYPTION !== "false";
52
+ const devHeadersEnabled = (env.AUTH_DEV_HEADERS ?? "true") !== "false";
53
+ if (devHeadersEnabled) {
54
+ throw new Error("Production startup blocked: set AUTH_DEV_HEADERS=false to disable development auth headers.");
55
+ }
56
+ if (DEFAULT_TOKENS.has(adminToken) || DEFAULT_TOKENS.has(memberToken)) {
57
+ throw new Error("Production startup blocked: rotate ADMIN_API_TOKEN and MEMBER_API_TOKEN away from default WorkHub test values.");
58
+ }
59
+ if (DEFAULT_SCIM_TOKENS.has(scimToken)) {
60
+ throw new Error("Production startup blocked: rotate SCIM_BEARER_TOKEN away from default WorkHub test values.");
61
+ }
62
+ if (encryptionEnabled && !env.KMS_ENCRYPTION_KEY?.trim()) {
63
+ throw new Error("Production startup blocked: set KMS_ENCRYPTION_KEY when field encryption is enabled.");
64
+ }
65
+ if (!env.SIEM_EXPORT_URL?.trim() && isFeatureEnabled("siemExport")) {
66
+ console.warn("[secrets] SIEM_EXPORT_URL is not configured; audit logs remain database-only.");
67
+ }
68
+ const billingEnabled = (env.FEATURE_BILLING ?? "true") !== "false";
69
+ if (billingEnabled && !env.STRIPE_WEBHOOK_SECRET?.trim()) {
70
+ throw new Error("Production startup blocked: set STRIPE_WEBHOOK_SECRET when billing webhooks are enabled.");
71
+ }
72
+ const corsOrigins = (env.CORS_ALLOWED_ORIGINS ?? "*").split(",").map((origin) => origin.trim());
73
+ if (corsOrigins.includes("*")) {
74
+ throw new Error("Production startup blocked: set explicit CORS_ALLOWED_ORIGINS instead of wildcard.");
75
+ }
76
+ if ((env.FEATURE_PUBLIC_READS ?? "true") !== "false") {
77
+ throw new Error("Production startup blocked: set FEATURE_PUBLIC_READS=false for authenticated-only reads.");
78
+ }
79
+ if (!env.OAUTH_STATE_SECRET?.trim()) {
80
+ throw new Error("Production startup blocked: set OAUTH_STATE_SECRET for OAuth CSRF protection.");
81
+ }
82
+ if (!env.TOKEN_HASH_PEPPER?.trim()) {
83
+ throw new Error("Production startup blocked: set TOKEN_HASH_PEPPER for API token hashing.");
84
+ }
85
+ if (!env.API_TOKEN_DEFAULT_EXPIRY_DAYS?.trim()) {
86
+ throw new Error("Production startup blocked: set API_TOKEN_DEFAULT_EXPIRY_DAYS to enforce token rotation.");
87
+ }
88
+ }
89
+ export {
90
+ assertProductionSecrets
91
+ };
@@ -93,6 +93,7 @@ function resolveService(dependencies, token) {
93
93
  var CORE_CONFIG_TOKEN = "core.config";
94
94
  var CORE_CACHE_TOKEN = "core.cache";
95
95
  var CORE_QUEUE_TOKEN = "core.queue";
96
+ var CORE_EVENT_BUS_TOKEN = "core.eventBus";
96
97
  var CORE_POLICY_GATE_TOKEN = "core.policyGate";
97
98
  var CORE_AUTH_TOKEN = "core.auth";
98
99
  var CORE_TOKEN_SERVICE_TOKEN = "core.tokenService";
@@ -163,6 +164,9 @@ function resolveApplicationCache() {
163
164
  function resolveApplicationQueue() {
164
165
  return requireActiveApplicationContext().container.resolve(CORE_QUEUE_TOKEN);
165
166
  }
167
+ function resolveApplicationEventBus() {
168
+ return requireActiveApplicationContext().container.resolve(CORE_EVENT_BUS_TOKEN);
169
+ }
166
170
  function resolveApplicationAuth() {
167
171
  return requireActiveApplicationContext().container.resolve(CORE_AUTH_TOKEN);
168
172
  }
@@ -95,7 +95,7 @@ export { AsyncQueue, createQueue, Job, SyncQueue } from "../core/queue/index.ts"
95
95
  export { createFailedJobService, createProductionQueue, createQueueWorker, createTrackedJob, FailedJobRepository, FailedJobService, jobRegistry, QueueWorker, RedisQueue, ResilientQueue, runQueueJob, } from "../core/queue/publicQueue.ts";
96
96
  export type { QueueMetricsSnapshot } from "../core/queue/queueMetrics.ts";
97
97
  export { collectQueueMetrics } from "../core/queue/queueMetrics.ts";
98
- export { resolveApplicationAuth, resolveApplicationCache, resolveApplicationConfig, resolveApplicationDependencies, resolveApplicationLogger, resolveApplicationPolicyGate, resolveApplicationQueue, setActiveApplicationContext, } from "../core/runtime/applicationRegistry.ts";
98
+ export { resolveApplicationAuth, resolveApplicationCache, resolveApplicationConfig, resolveApplicationDependencies, resolveApplicationEventBus, resolveApplicationLogger, resolveApplicationPolicyGate, resolveApplicationQueue, setActiveApplicationContext, } from "../core/runtime/applicationRegistry.ts";
99
99
  export type { ScheduledTask } from "../core/scheduler/schedule.ts";
100
100
  export { appSchedule, runDueScheduledTasks, Schedule } from "../core/scheduler/schedule.ts";
101
101
  export { guestCanViewResource, isPublicReadsEnabled } from "../core/security/publicReads.ts";