@getstrata/bootstrap 0.2.6 → 0.2.7

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 (35) hide show
  1. package/dist/bootstrap/contracts.d.ts +2 -0
  2. package/dist/bootstrap/providers/storage.d.ts +3 -0
  3. package/dist/bootstrap/scimRoutes.d.ts +1 -1
  4. package/dist/core/auth/membershipScope.d.ts +16 -0
  5. package/dist/core/auth/membershipService.d.ts +24 -0
  6. package/dist/core/database/baseRepository.d.ts +6 -1
  7. package/dist/core/database/connectionContext.d.ts +2 -1
  8. package/dist/core/database/defaultConnection.d.ts +6 -0
  9. package/dist/core/database/queryProxy.d.ts +3 -0
  10. package/dist/core/database/repositoryConnection.d.ts +3 -3
  11. package/dist/core/jobs/dispatchWebhookJob.d.ts +0 -1
  12. package/dist/core/queue/queueMetrics.d.ts +15 -0
  13. package/dist/core/security/safeFetch.d.ts +2 -0
  14. package/dist/core/security/safeUrl.d.ts +16 -1
  15. package/dist/core/tenant/tenantDatabaseScope.d.ts +2 -1
  16. package/dist/db/connection/index.d.ts +1 -1
  17. package/dist/domain/workhub.d.ts +35 -0
  18. package/dist/entries/applicationRegistry.js +185 -0
  19. package/dist/entries/config.js +42 -0
  20. package/dist/entries/context.js +4208 -0
  21. package/dist/entries/contracts.js +92 -0
  22. package/dist/entries/createWebRoutes.js +996 -0
  23. package/dist/entries/httpKernel.js +264 -0
  24. package/dist/entries/providers/view.js +635 -0
  25. package/dist/entries/providers.js +4094 -0
  26. package/dist/framework/public-api.d.ts +29 -6
  27. package/dist/index.js +290 -109
  28. package/dist/modules/organization/repository.d.ts +14 -0
  29. package/dist/modules/organization/table.d.ts +3 -0
  30. package/dist/modules/organization/types.d.ts +10 -0
  31. package/dist/modules/scim/controller.d.ts +2 -1
  32. package/dist/modules/scim/scimResponse.d.ts +1 -1
  33. package/dist/modules/scim/service.d.ts +4 -7
  34. package/dist/modules/user/repository.d.ts +1 -0
  35. package/package.json +5 -4
@@ -0,0 +1,264 @@
1
+ // @bun
2
+ // ../../src/bootstrap/httpKernel.ts
3
+ import {
4
+ createAuthMiddleware,
5
+ createAuthorizeMiddleware,
6
+ createBodySizeLimitMiddleware,
7
+ createCorsMiddleware,
8
+ createCsrfMiddleware,
9
+ createFlashMiddleware,
10
+ createLoginThrottleMiddleware,
11
+ createMembershipMiddleware,
12
+ createMemoryThrottleMiddleware,
13
+ createMetricsMiddleware,
14
+ createRequestLoggingMiddleware,
15
+ createRequireAbilityMiddleware,
16
+ createRequireAuthMiddleware,
17
+ createRequireGlobalAdminMiddleware,
18
+ createRequireWebAuthMiddleware,
19
+ createSecurityHeadersMiddleware,
20
+ createTenantMiddleware,
21
+ createThrottleMiddleware,
22
+ createTracingMiddleware,
23
+ isPublicReadsEnabled,
24
+ requestIdMiddleware,
25
+ withMiddleware
26
+ } from "@getstrata/core";
27
+
28
+ // ../../src/config/frontend.ts
29
+ function readFrontendMode() {
30
+ const mode = (process.env.FRONTEND_MODE ?? "api").trim();
31
+ if (mode === "server-htmx") {
32
+ return "server-htmx";
33
+ }
34
+ if (mode === "spa-react") {
35
+ return "spa-react";
36
+ }
37
+ return "api";
38
+ }
39
+ function isViewsEnabled() {
40
+ return readFrontendMode() === "server-htmx";
41
+ }
42
+
43
+ // ../../src/config/rateLimit.ts
44
+ var LOCAL_LOGIN_RATE_LIMIT = {
45
+ maxAttempts: 100,
46
+ decaySeconds: 60
47
+ };
48
+ var PRODUCTION_LOGIN_RATE_LIMIT = {
49
+ maxAttempts: 5,
50
+ decaySeconds: 900
51
+ };
52
+ function isLocalAppEnv() {
53
+ return (process.env.APP_ENV ?? "local") === "local";
54
+ }
55
+ function parsePositiveInt(value, fallback) {
56
+ const parsed = Number(value);
57
+ if (!Number.isFinite(parsed) || parsed <= 0) {
58
+ return fallback;
59
+ }
60
+ return Math.trunc(parsed);
61
+ }
62
+ function resolveLoginRateLimit() {
63
+ const defaults = isLocalAppEnv() ? LOCAL_LOGIN_RATE_LIMIT : PRODUCTION_LOGIN_RATE_LIMIT;
64
+ return {
65
+ maxAttempts: parsePositiveInt(process.env.LOGIN_RATE_LIMIT_PER_WINDOW, defaults.maxAttempts),
66
+ decaySeconds: parsePositiveInt(process.env.LOGIN_RATE_LIMIT_WINDOW_SECONDS, defaults.decaySeconds)
67
+ };
68
+ }
69
+ function resolveRegisterRateLimit() {
70
+ const defaults = isLocalAppEnv() ? { maxAttempts: 100, decaySeconds: 60 } : { maxAttempts: 10, decaySeconds: 60 };
71
+ return {
72
+ maxAttempts: parsePositiveInt(process.env.REGISTER_RATE_LIMIT_PER_WINDOW, defaults.maxAttempts),
73
+ decaySeconds: parsePositiveInt(process.env.REGISTER_RATE_LIMIT_WINDOW_SECONDS ?? process.env.REGISTER_RATE_LIMIT_WINDOW_MS ? String(Number(process.env.REGISTER_RATE_LIMIT_WINDOW_MS) / 1000) : undefined, defaults.decaySeconds)
74
+ };
75
+ }
76
+
77
+ // ../../src/bootstrap/config.ts
78
+ var APP_PORT_CONFIG_KEY = "app.port";
79
+ var CACHE_TTL_MS_CONFIG_KEY = "cache.ttlMs";
80
+ var CACHE_MAX_ENTRIES_CONFIG_KEY = "cache.maxEntries";
81
+ var CACHE_DRIVER_CONFIG_KEY = "cache.driver";
82
+ var REDIS_URL_CONFIG_KEY = "cache.redisUrl";
83
+ var DATABASE_URL_CONFIG_KEY = "database.url";
84
+ var CORE_CONFIG_TOKEN = "core.config";
85
+ var CORE_CACHE_TOKEN = "core.cache";
86
+ var CORE_QUEUE_TOKEN = "core.queue";
87
+ var CORE_POLICY_GATE_TOKEN = "core.policyGate";
88
+ var CORE_AUTH_TOKEN = "core.auth";
89
+ var CORE_TOKEN_SERVICE_TOKEN = "core.tokenService";
90
+ var AUTH_DEV_HEADERS_CONFIG_KEY = "auth.allowDevHeaders";
91
+ var DEFAULT_APP_PORT = 3000;
92
+ var DEFAULT_CACHE_TTL_MS = 3600000;
93
+ var DEFAULT_CACHE_MAX_ENTRIES = 100;
94
+ var DEFAULT_CACHE_DRIVER = "array";
95
+ var DEFAULT_API_TOKEN = "";
96
+ var DEFAULT_QUEUE_DRIVER = "sync";
97
+
98
+ // ../../src/bootstrap/httpKernel.ts
99
+ class HttpKernel {
100
+ dependencies;
101
+ constructor(dependencies) {
102
+ this.dependencies = dependencies;
103
+ }
104
+ globalMiddleware() {
105
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
106
+ return [
107
+ createCorsMiddleware(),
108
+ createSecurityHeadersMiddleware(),
109
+ createBodySizeLimitMiddleware(),
110
+ createTracingMiddleware(),
111
+ createMetricsMiddleware(),
112
+ createRequestLoggingMiddleware(),
113
+ requestIdMiddleware,
114
+ createAuthMiddleware(auth),
115
+ createMembershipMiddleware(),
116
+ createTenantMiddleware()
117
+ ];
118
+ }
119
+ group(name) {
120
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
121
+ switch (name) {
122
+ case "authenticated":
123
+ return [createRequireAuthMiddleware(auth)];
124
+ case "web":
125
+ return isViewsEnabled() ? [createFlashMiddleware(), createCsrfMiddleware()] : [];
126
+ case "api": {
127
+ if (!this.dependencies.container.has(CORE_CONFIG_TOKEN)) {
128
+ return [];
129
+ }
130
+ const config = this.dependencies.container.resolve(CORE_CONFIG_TOKEN);
131
+ const redisUrl = config.get(REDIS_URL_CONFIG_KEY)?.trim() ?? "";
132
+ if (!redisUrl) {
133
+ const maxAttempts2 = Number(process.env.RATE_LIMIT_PER_MINUTE ?? "120");
134
+ return [
135
+ createMemoryThrottleMiddleware({
136
+ maxAttempts: Number.isFinite(maxAttempts2) ? maxAttempts2 : 120,
137
+ decaySeconds: 60
138
+ })
139
+ ];
140
+ }
141
+ const maxAttempts = Number(process.env.RATE_LIMIT_PER_MINUTE ?? "120");
142
+ return [
143
+ createThrottleMiddleware({
144
+ redisUrl,
145
+ maxAttempts: Number.isFinite(maxAttempts) ? maxAttempts : 120,
146
+ decaySeconds: 60
147
+ })
148
+ ];
149
+ }
150
+ default:
151
+ throw new Error(`Unknown middleware group "${name}".`);
152
+ }
153
+ }
154
+ wrap(groups, handler) {
155
+ const names = Array.isArray(groups) ? groups : [groups];
156
+ const middleware = names.flatMap((name) => this.group(name));
157
+ if (middleware.length === 0) {
158
+ return handler;
159
+ }
160
+ return withMiddleware(...middleware)(handler);
161
+ }
162
+ wrapApi(handler) {
163
+ return this.wrap(["api", "authenticated"], handler);
164
+ }
165
+ wrapWeb(handler) {
166
+ return handler;
167
+ }
168
+ wrapWebPublicRead(handler) {
169
+ if (isPublicReadsEnabled()) {
170
+ return this.wrapWeb(handler);
171
+ }
172
+ return this.wrapWebAuthenticated(handler);
173
+ }
174
+ wrapWebAuthenticated(handler) {
175
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
176
+ return withMiddleware(createRequireWebAuthMiddleware(auth))(handler);
177
+ }
178
+ wrapWebAbility(ability, handler) {
179
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
180
+ const abilityChecker = this.dependencies.container.resolve(CORE_TOKEN_SERVICE_TOKEN);
181
+ const requireAbility = createRequireAbilityMiddleware(abilityChecker);
182
+ const middleware = [createRequireWebAuthMiddleware(auth), requireAbility(ability)];
183
+ return withMiddleware(...middleware)(handler);
184
+ }
185
+ wrapWebGlobalAdmin(handler) {
186
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
187
+ const middleware = [createRequireWebAuthMiddleware(auth), createRequireGlobalAdminMiddleware()];
188
+ return withMiddleware(...middleware)(handler);
189
+ }
190
+ wrapAuthenticated(handler) {
191
+ return this.wrap("authenticated", handler);
192
+ }
193
+ wrapPublicRead(handler) {
194
+ if (isPublicReadsEnabled()) {
195
+ return handler;
196
+ }
197
+ return this.wrapAuthenticated(handler);
198
+ }
199
+ wrapGlobalAdmin(handler) {
200
+ const middleware = [...this.group("authenticated"), createRequireGlobalAdminMiddleware()];
201
+ return withMiddleware(...middleware)(handler);
202
+ }
203
+ wrapAbility(ability, handler) {
204
+ const abilityChecker = this.dependencies.container.resolve(CORE_TOKEN_SERVICE_TOKEN);
205
+ const requireAbility = createRequireAbilityMiddleware(abilityChecker);
206
+ const middleware = [...this.group("authenticated"), requireAbility(ability)];
207
+ return withMiddleware(...middleware)(handler);
208
+ }
209
+ wrapPolicy(resource, action, handler) {
210
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
211
+ const gate = this.dependencies.container.resolve(CORE_POLICY_GATE_TOKEN);
212
+ return withMiddleware(createAuthorizeMiddleware(gate, auth, resource, action))(handler);
213
+ }
214
+ wrapLogin(handler) {
215
+ return this.wrapThrottle("login", resolveLoginRateLimit(), handler);
216
+ }
217
+ wrapRegister(handler) {
218
+ return this.wrapThrottle("register", resolveRegisterRateLimit(), handler);
219
+ }
220
+ wrapThrottle(scope, rateLimit, handler) {
221
+ const middleware = [];
222
+ const memoryKeyPrefix = scope === "login" ? "login-throttle:" : "register-throttle:";
223
+ if (this.dependencies.container.has(CORE_CONFIG_TOKEN)) {
224
+ const config = this.dependencies.container.resolve(CORE_CONFIG_TOKEN);
225
+ const redisUrl = config.get(REDIS_URL_CONFIG_KEY)?.trim() ?? "";
226
+ if (redisUrl) {
227
+ const throttle = scope === "login" ? createLoginThrottleMiddleware({
228
+ redisUrl,
229
+ maxAttempts: rateLimit.maxAttempts,
230
+ decaySeconds: rateLimit.decaySeconds
231
+ }) : createThrottleMiddleware({
232
+ redisUrl,
233
+ maxAttempts: rateLimit.maxAttempts,
234
+ decaySeconds: rateLimit.decaySeconds,
235
+ keyPrefix: memoryKeyPrefix
236
+ });
237
+ middleware.push(throttle);
238
+ } else {
239
+ middleware.push(createMemoryThrottleMiddleware({
240
+ maxAttempts: rateLimit.maxAttempts,
241
+ decaySeconds: rateLimit.decaySeconds,
242
+ keyPrefix: memoryKeyPrefix
243
+ }));
244
+ }
245
+ } else {
246
+ middleware.push(createMemoryThrottleMiddleware({
247
+ maxAttempts: rateLimit.maxAttempts,
248
+ decaySeconds: rateLimit.decaySeconds,
249
+ keyPrefix: memoryKeyPrefix
250
+ }));
251
+ }
252
+ if (middleware.length === 0) {
253
+ return handler;
254
+ }
255
+ return withMiddleware(...middleware)(handler);
256
+ }
257
+ }
258
+ function createHttpKernel(dependencies) {
259
+ return new HttpKernel(dependencies);
260
+ }
261
+ export {
262
+ createHttpKernel,
263
+ HttpKernel
264
+ };