@getstrata/bootstrap 0.2.21 → 0.2.23

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 (238) hide show
  1. package/dist/bootstrap/applicationRegistry.d.ts +1 -1
  2. package/dist/bootstrap/buildModuleRoutes.d.ts +10 -0
  3. package/dist/bootstrap/config.d.ts +1 -1
  4. package/dist/bootstrap/contracts.d.ts +4 -4
  5. package/dist/bootstrap/discoverModules.d.ts +7 -3
  6. package/dist/bootstrap/modules.d.ts +1 -1
  7. package/dist/bootstrap/public-api.d.ts +4 -0
  8. package/dist/entries/applicationRegistry.js +12 -115
  9. package/dist/entries/buildModuleRoutes.js +626 -0
  10. package/dist/entries/cache/modelCacheTags.js +56 -2
  11. package/dist/entries/config.js +9 -9
  12. package/dist/entries/context.js +177 -187
  13. package/dist/entries/contracts.js +10 -85
  14. package/dist/entries/createWebRoutes.js +68 -14
  15. package/dist/entries/dependencies.js +177 -187
  16. package/dist/entries/discoverModules.js +65 -0
  17. package/dist/entries/http/securedRouteModelBinding.js +4 -34
  18. package/dist/entries/httpKernel.js +9 -9
  19. package/dist/entries/membershipService.js +0 -34
  20. package/dist/entries/providers/view.js +9 -9
  21. package/dist/entries/providers.js +156 -113
  22. package/dist/entries/queue/defaultJobs.js +0 -34
  23. package/dist/entries/web/routing.js +20 -41
  24. package/dist/index.js +3830 -3762
  25. package/package.json +14 -4
  26. package/dist/cli/commands/scheduleRun.d.ts +0 -3
  27. package/dist/config/app.d.ts +0 -10
  28. package/dist/config/auth.d.ts +0 -7
  29. package/dist/config/contentSecurityPolicy.d.ts +0 -5
  30. package/dist/config/cors.d.ts +0 -9
  31. package/dist/config/database.d.ts +0 -10
  32. package/dist/config/features.d.ts +0 -18
  33. package/dist/config/frontend.d.ts +0 -6
  34. package/dist/config/queue.d.ts +0 -8
  35. package/dist/config/rateLimit.d.ts +0 -10
  36. package/dist/config/uploads.d.ts +0 -5
  37. package/dist/core/admin/formatValue.d.ts +0 -3
  38. package/dist/core/admin/index.d.ts +0 -3
  39. package/dist/core/admin/registry.d.ts +0 -11
  40. package/dist/core/admin/types.d.ts +0 -24
  41. package/dist/core/audit/exportAuditLogs.d.ts +0 -8
  42. package/dist/core/audit/siemFormatter.d.ts +0 -30
  43. package/dist/core/auth/abilityChecker.d.ts +0 -6
  44. package/dist/core/auth/accessControl.d.ts +0 -8
  45. package/dist/core/auth/authContext.d.ts +0 -11
  46. package/dist/core/auth/guard.d.ts +0 -36
  47. package/dist/core/auth/membershipContext.d.ts +0 -15
  48. package/dist/core/auth/membershipContextMiddleware.d.ts +0 -3
  49. package/dist/core/auth/membershipMiddleware.d.ts +0 -2
  50. package/dist/core/auth/membershipScope.d.ts +0 -16
  51. package/dist/core/auth/membershipService.d.ts +0 -23
  52. package/dist/core/auth/oauth/oidcProvider.d.ts +0 -18
  53. package/dist/core/auth/oauth/providers.d.ts +0 -22
  54. package/dist/core/auth/oauth/samlProvider.d.ts +0 -9
  55. package/dist/core/auth/oauth/types.d.ts +0 -11
  56. package/dist/core/auth/password.d.ts +0 -3
  57. package/dist/core/auth/policy.d.ts +0 -15
  58. package/dist/core/auth/resolveMembershipService.d.ts +0 -3
  59. package/dist/core/auth/scimAuthMiddleware.d.ts +0 -3
  60. package/dist/core/auth/sessionCookie.d.ts +0 -6
  61. package/dist/core/auth/sessionGuard.d.ts +0 -9
  62. package/dist/core/auth/tokenHash.d.ts +0 -3
  63. package/dist/core/cache/createCacheStore.d.ts +0 -11
  64. package/dist/core/cache/redisCacheStore.d.ts +0 -23
  65. package/dist/core/cache/repository.d.ts +0 -17
  66. package/dist/core/cache/simpleCache.d.ts +0 -23
  67. package/dist/core/cache/simpleCacheStore.d.ts +0 -16
  68. package/dist/core/cache/store.d.ts +0 -12
  69. package/dist/core/cache/taggedCache.d.ts +0 -9
  70. package/dist/core/cache/tags.d.ts +0 -11
  71. package/dist/core/config/envSchema.d.ts +0 -12
  72. package/dist/core/contracts/container.d.ts +0 -30
  73. package/dist/core/contracts/di.d.ts +0 -31
  74. package/dist/core/contracts/serviceContainer.d.ts +0 -1
  75. package/dist/core/contracts/serviceTokens.d.ts +0 -8
  76. package/dist/core/crypto/fieldEncryption.d.ts +0 -13
  77. package/dist/core/crypto/mfaSecret.d.ts +0 -3
  78. package/dist/core/crypto/nonCryptographicHash.d.ts +0 -2
  79. package/dist/core/database/baseRepository.d.ts +0 -67
  80. package/dist/core/database/bindConnection.d.ts +0 -3
  81. package/dist/core/database/boundConnection.d.ts +0 -5
  82. package/dist/core/database/connection.d.ts +0 -7
  83. package/dist/core/database/connectionContext.d.ts +0 -8
  84. package/dist/core/database/defaultConnection.d.ts +0 -6
  85. package/dist/core/database/errors.d.ts +0 -12
  86. package/dist/core/database/index.d.ts +0 -18
  87. package/dist/core/database/migrations/advisoryLock.d.ts +0 -6
  88. package/dist/core/database/migrations/runner.d.ts +0 -25
  89. package/dist/core/database/migrations/types.d.ts +0 -15
  90. package/dist/core/database/model.d.ts +0 -57
  91. package/dist/core/database/query.d.ts +0 -63
  92. package/dist/core/database/queryProxy.d.ts +0 -3
  93. package/dist/core/database/relationships.d.ts +0 -100
  94. package/dist/core/database/repositoryConnection.d.ts +0 -4
  95. package/dist/core/database/repositoryQuery.d.ts +0 -38
  96. package/dist/core/database/schema/blueprint.d.ts +0 -47
  97. package/dist/core/database/schema/columnDefinition.d.ts +0 -36
  98. package/dist/core/database/schema/driver.d.ts +0 -8
  99. package/dist/core/database/schema/errors.d.ts +0 -4
  100. package/dist/core/database/schema/grammars/compileStatements.d.ts +0 -8
  101. package/dist/core/database/schema/grammars/createGrammar.d.ts +0 -4
  102. package/dist/core/database/schema/grammars/grammar.d.ts +0 -10
  103. package/dist/core/database/schema/grammars/index.d.ts +0 -7
  104. package/dist/core/database/schema/grammars/mysqlGrammar.d.ts +0 -2
  105. package/dist/core/database/schema/grammars/postgresGrammar.d.ts +0 -2
  106. package/dist/core/database/schema/grammars/sqliteGrammar.d.ts +0 -2
  107. package/dist/core/database/schema/index.d.ts +0 -12
  108. package/dist/core/database/schema/schema.d.ts +0 -21
  109. package/dist/core/database/seeders/runner.d.ts +0 -6
  110. package/dist/core/database/seeders/types.d.ts +0 -8
  111. package/dist/core/database/table.d.ts +0 -13
  112. package/dist/core/database/transaction.d.ts +0 -3
  113. package/dist/core/database/types.d.ts +0 -66
  114. package/dist/core/database/whereBuilder.d.ts +0 -17
  115. package/dist/core/errors/http.d.ts +0 -33
  116. package/dist/core/events/eventBus.d.ts +0 -10
  117. package/dist/core/events/index.d.ts +0 -4
  118. package/dist/core/facades/index.d.ts +0 -10
  119. package/dist/core/http/authMiddleware.d.ts +0 -5
  120. package/dist/core/http/authorizeMiddleware.d.ts +0 -5
  121. package/dist/core/http/bodySizeLimitMiddleware.d.ts +0 -4
  122. package/dist/core/http/conditionalResponse.d.ts +0 -2
  123. package/dist/core/http/contentNegotiation.d.ts +0 -2
  124. package/dist/core/http/cookies.d.ts +0 -4
  125. package/dist/core/http/corsMiddleware.d.ts +0 -3
  126. package/dist/core/http/csrfMiddleware.d.ts +0 -3
  127. package/dist/core/http/csrfProtection.d.ts +0 -12
  128. package/dist/core/http/csrfToken.d.ts +0 -14
  129. package/dist/core/http/etag.d.ts +0 -18
  130. package/dist/core/http/flashMiddleware.d.ts +0 -3
  131. package/dist/core/http/flashSession.d.ts +0 -12
  132. package/dist/core/http/formRequest.d.ts +0 -10
  133. package/dist/core/http/index.d.ts +0 -27
  134. package/dist/core/http/loginThrottleMiddleware.d.ts +0 -11
  135. package/dist/core/http/memoryThrottleMiddleware.d.ts +0 -9
  136. package/dist/core/http/metricsMiddleware.d.ts +0 -4
  137. package/dist/core/http/middleware.d.ts +0 -8
  138. package/dist/core/http/pagination.d.ts +0 -11
  139. package/dist/core/http/parseFormBody.d.ts +0 -6
  140. package/dist/core/http/parseMultipartUpload.d.ts +0 -10
  141. package/dist/core/http/requestMetaContext.d.ts +0 -15
  142. package/dist/core/http/requireAbilityMiddleware.d.ts +0 -4
  143. package/dist/core/http/requireAuthMiddleware.d.ts +0 -4
  144. package/dist/core/http/requireGlobalAdminMiddleware.d.ts +0 -3
  145. package/dist/core/http/requireWebAuthMiddleware.d.ts +0 -4
  146. package/dist/core/http/resources.d.ts +0 -7
  147. package/dist/core/http/route.d.ts +0 -6
  148. package/dist/core/http/routeMiddleware.d.ts +0 -3
  149. package/dist/core/http/routeModelBinding.d.ts +0 -3
  150. package/dist/core/http/scimThrottleMiddleware.d.ts +0 -9
  151. package/dist/core/http/securedRouteModelBinding.d.ts +0 -11
  152. package/dist/core/http/securityHeadersMiddleware.d.ts +0 -3
  153. package/dist/core/http/throttleMiddleware.d.ts +0 -11
  154. package/dist/core/http/validation.d.ts +0 -23
  155. package/dist/core/http/webErrorResponse.d.ts +0 -5
  156. package/dist/core/http/webFormRequest.d.ts +0 -7
  157. package/dist/core/jobs/dispatchWebhookJob.d.ts +0 -13
  158. package/dist/core/jobs/invalidateCacheTagsJob.d.ts +0 -12
  159. package/dist/core/lifecycle/gracefulShutdown.d.ts +0 -6
  160. package/dist/core/logging/logger.d.ts +0 -17
  161. package/dist/core/logging/requestLoggingMiddleware.d.ts +0 -3
  162. package/dist/core/mail/mailer.d.ts +0 -38
  163. package/dist/core/mail/markdownMail.d.ts +0 -15
  164. package/dist/core/mail/markdownMailable.d.ts +0 -12
  165. package/dist/core/metrics/prometheus.d.ts +0 -26
  166. package/dist/core/notifications/dispatcher.d.ts +0 -13
  167. package/dist/core/notifications/index.d.ts +0 -3
  168. package/dist/core/notifications/notification.d.ts +0 -7
  169. package/dist/core/notifications/types.d.ts +0 -25
  170. package/dist/core/openapi/registeredRoute.d.ts +0 -6
  171. package/dist/core/pagination/index.d.ts +0 -27
  172. package/dist/core/queue/createAppQueue.d.ts +0 -5
  173. package/dist/core/queue/failedJobRepository.d.ts +0 -6
  174. package/dist/core/queue/failedJobService.d.ts +0 -16
  175. package/dist/core/queue/failedJobTable.d.ts +0 -3
  176. package/dist/core/queue/index.d.ts +0 -19
  177. package/dist/core/queue/jobRegistry.d.ts +0 -13
  178. package/dist/core/queue/jobRunner.d.ts +0 -9
  179. package/dist/core/queue/publicQueue.d.ts +0 -16
  180. package/dist/core/queue/queueMetrics.d.ts +0 -15
  181. package/dist/core/queue/redisQueue.d.ts +0 -29
  182. package/dist/core/queue/resilientQueue.d.ts +0 -9
  183. package/dist/core/queue/types.d.ts +0 -8
  184. package/dist/core/runtime/applicationRegistry.d.ts +0 -17
  185. package/dist/core/runtime/asyncContextStore.d.ts +0 -3
  186. package/dist/core/scheduler/schedule.d.ts +0 -15
  187. package/dist/core/security/publicReads.d.ts +0 -3
  188. package/dist/core/security/safeFetch.d.ts +0 -10
  189. package/dist/core/security/safeUrl.d.ts +0 -20
  190. package/dist/core/security/scimTenantTokens.d.ts +0 -3
  191. package/dist/core/security/securityEvents.d.ts +0 -6
  192. package/dist/core/security/timingSafeCompare.d.ts +0 -2
  193. package/dist/core/security/tokenExpiry.d.ts +0 -2
  194. package/dist/core/security/totp.d.ts +0 -3
  195. package/dist/core/storage/storage.d.ts +0 -44
  196. package/dist/core/tenant/databaseTenantContext.d.ts +0 -2
  197. package/dist/core/tenant/resolveTenant.d.ts +0 -3
  198. package/dist/core/tenant/tenantContext.d.ts +0 -13
  199. package/dist/core/tenant/tenantDatabaseScope.d.ts +0 -8
  200. package/dist/core/tenant/tenantMiddleware.d.ts +0 -6
  201. package/dist/core/tracing/otel.d.ts +0 -26
  202. package/dist/core/tracing/traceContext.d.ts +0 -9
  203. package/dist/core/tracing/tracingMiddleware.d.ts +0 -3
  204. package/dist/core/validation/rules.d.ts +0 -17
  205. package/dist/core/view/etaViewEngine.d.ts +0 -15
  206. package/dist/core/view/htmlResponse.d.ts +0 -6
  207. package/dist/core/view/index.d.ts +0 -5
  208. package/dist/core/view/viewEngine.d.ts +0 -6
  209. package/dist/core/view/webLayoutData.d.ts +0 -17
  210. package/dist/db/connection/createConnection.d.ts +0 -6
  211. package/dist/db/connection/index.d.ts +0 -11
  212. package/dist/domain/abilities.d.ts +0 -5
  213. package/dist/domain/auth.d.ts +0 -4
  214. package/dist/domain/scim.d.ts +0 -9
  215. package/dist/domain/workhub.d.ts +0 -35
  216. package/dist/framework/public-api.d.ts +0 -116
  217. package/dist/modules/organization/memberRepository.d.ts +0 -14
  218. package/dist/modules/organization/memberTypes.d.ts +0 -9
  219. package/dist/modules/organization/repository.d.ts +0 -14
  220. package/dist/modules/organization/table.d.ts +0 -3
  221. package/dist/modules/organization/types.d.ts +0 -10
  222. package/dist/modules/scim/controller.d.ts +0 -16
  223. package/dist/modules/scim/scimResponse.d.ts +0 -10
  224. package/dist/modules/scim/service.d.ts +0 -214
  225. package/dist/modules/user/apiTokenRepository.d.ts +0 -8
  226. package/dist/modules/user/apiTokenTable.d.ts +0 -3
  227. package/dist/modules/user/authService.d.ts +0 -23
  228. package/dist/modules/user/notificationRepository.d.ts +0 -6
  229. package/dist/modules/user/notificationService.d.ts +0 -25
  230. package/dist/modules/user/notificationTable.d.ts +0 -3
  231. package/dist/modules/user/notificationTypes.d.ts +0 -12
  232. package/dist/modules/user/oauthIdentityRepository.d.ts +0 -9
  233. package/dist/modules/user/provider.d.ts +0 -11
  234. package/dist/modules/user/repository.d.ts +0 -13
  235. package/dist/modules/user/table.d.ts +0 -3
  236. package/dist/modules/user/tokenService.d.ts +0 -29
  237. package/dist/modules/user/types.d.ts +0 -46
  238. package/dist/types/services.d.ts +0 -16
@@ -0,0 +1,626 @@
1
+ // @bun
2
+ // ../../src/core/crypto/nonCryptographicHash.ts
3
+ function nonCryptographicDigest(input) {
4
+ return Bun.hash(input).toString(16);
5
+ }
6
+
7
+ // ../../src/core/errors/http.ts
8
+ class HttpError extends Error {
9
+ status;
10
+ details;
11
+ constructor(status, message, details) {
12
+ super(message);
13
+ this.name = new.target.name;
14
+ this.status = status;
15
+ this.details = details;
16
+ }
17
+ }
18
+
19
+ class BadRequestError extends HttpError {
20
+ constructor(message = "Bad Request", details) {
21
+ super(400, message, details);
22
+ }
23
+ }
24
+ class ConflictError extends HttpError {
25
+ constructor(message = "Conflict", details) {
26
+ super(409, message, details);
27
+ }
28
+ }
29
+
30
+ class UnprocessableEntityError extends HttpError {
31
+ constructor(message = "Unprocessable Entity", details) {
32
+ super(422, message, details);
33
+ }
34
+ }
35
+ class ForbiddenError extends HttpError {
36
+ constructor(message = "Forbidden", details) {
37
+ super(403, message, details);
38
+ }
39
+ }
40
+
41
+ class UnauthorizedError extends HttpError {
42
+ constructor(message = "Unauthorized", details) {
43
+ super(401, message, details);
44
+ }
45
+ }
46
+ class PreconditionFailedError extends HttpError {
47
+ constructor(message = "Precondition Failed", details) {
48
+ super(412, message, details);
49
+ }
50
+ }
51
+
52
+ // ../../src/core/http/etag.ts
53
+ function isEtagEnabled() {
54
+ return (process.env.FEATURE_ETAG ?? "true") !== "false";
55
+ }
56
+ function formatWeakEtag(digest) {
57
+ return `W/"${digest}"`;
58
+ }
59
+ function computeEtagFromJson(data) {
60
+ const digest = nonCryptographicDigest(JSON.stringify(data)).slice(0, 32);
61
+ return formatWeakEtag(digest);
62
+ }
63
+ function etagFromResource(resource) {
64
+ const version = resource.updated_at ?? resource.created_at ?? "";
65
+ const versionText = version instanceof Date ? version.toISOString() : version ? String(version) : "0";
66
+ const digest = nonCryptographicDigest(`${String(resource.id ?? "0")}:${versionText}`).slice(0, 32);
67
+ return formatWeakEtag(digest);
68
+ }
69
+ function normalizeEtag(value) {
70
+ return value.trim();
71
+ }
72
+ function etagValuesMatch(left, right) {
73
+ return normalizeEtag(left) === normalizeEtag(right);
74
+ }
75
+ function parseEtagList(header) {
76
+ if (!header) {
77
+ return [];
78
+ }
79
+ return header.split(",").map((value) => normalizeEtag(value)).filter(Boolean);
80
+ }
81
+ function ifNoneMatchSatisfied(request, etag) {
82
+ const header = request.headers.get("if-none-match");
83
+ if (!header) {
84
+ return false;
85
+ }
86
+ if (header.trim() === "*") {
87
+ return true;
88
+ }
89
+ return parseEtagList(header).some((candidate) => etagValuesMatch(candidate, etag));
90
+ }
91
+ function ifMatchSatisfied(request, etag) {
92
+ const header = request.headers.get("if-match");
93
+ if (!header) {
94
+ return false;
95
+ }
96
+ if (header.trim() === "*") {
97
+ return true;
98
+ }
99
+ return parseEtagList(header).some((candidate) => etagValuesMatch(candidate, etag));
100
+ }
101
+ function assertIfMatch(request, etag, options = {}) {
102
+ const header = request.headers.get("if-match");
103
+ if (!header) {
104
+ if (options.required) {
105
+ throw new PreconditionFailedError("If-Match header is required.");
106
+ }
107
+ return;
108
+ }
109
+ if (!ifMatchSatisfied(request, etag)) {
110
+ throw new PreconditionFailedError("Resource ETag does not match If-Match.");
111
+ }
112
+ }
113
+ function applyEtagHeaders(headers, etag) {
114
+ const next = new Headers(headers);
115
+ next.set("ETag", etag);
116
+ next.set("Cache-Control", "private, must-revalidate");
117
+ next.append("Vary", "Authorization");
118
+ next.append("Vary", "X-Tenant-Id");
119
+ return next;
120
+ }
121
+ function notModifiedResponse(etag) {
122
+ return new Response(null, {
123
+ status: 304,
124
+ headers: applyEtagHeaders(new Headers, etag)
125
+ });
126
+ }
127
+ function applyConditionalGet(request, response, etag) {
128
+ if (!isEtagEnabled()) {
129
+ return response;
130
+ }
131
+ if (ifNoneMatchSatisfied(request, etag)) {
132
+ return notModifiedResponse(etag);
133
+ }
134
+ const headers = applyEtagHeaders(new Headers(response.headers), etag);
135
+ return new Response(response.body, {
136
+ status: response.status,
137
+ statusText: response.statusText,
138
+ headers
139
+ });
140
+ }
141
+
142
+ // ../../src/core/http/conditionalResponse.ts
143
+ function jsonResponse(data, init = {}) {
144
+ return Response.json(data, {
145
+ status: init.status ?? 200,
146
+ headers: init.headers
147
+ });
148
+ }
149
+ function conditionalJsonResponse(request, data, init = {}) {
150
+ if (!request || !isEtagEnabled()) {
151
+ return jsonResponse(data, init);
152
+ }
153
+ const etag = computeEtagFromJson(data);
154
+ if (ifNoneMatchSatisfied(request, etag)) {
155
+ return notModifiedResponse(etag);
156
+ }
157
+ const response = jsonResponse(data, init);
158
+ const headers = new Headers(response.headers);
159
+ headers.set("ETag", etag);
160
+ headers.set("Cache-Control", "private, must-revalidate");
161
+ headers.append("Vary", "Authorization");
162
+ headers.append("Vary", "X-Tenant-Id");
163
+ return new Response(response.body, {
164
+ status: response.status,
165
+ statusText: response.statusText,
166
+ headers
167
+ });
168
+ }
169
+
170
+ // ../../src/core/http/middleware.ts
171
+ function isRouteHandler(value) {
172
+ return typeof value === "function";
173
+ }
174
+ function isMethodRouteMap(value) {
175
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
176
+ return false;
177
+ }
178
+ const entries = Object.entries(value);
179
+ return entries.length > 0 && entries.every(([, handler]) => isRouteHandler(handler));
180
+ }
181
+ function composeMiddleware(...middleware) {
182
+ return (handler) => {
183
+ return async (request) => {
184
+ let index = 0;
185
+ const dispatch = async () => {
186
+ if (index >= middleware.length) {
187
+ return await handler(request);
188
+ }
189
+ const current = middleware[index];
190
+ index += 1;
191
+ if (!current) {
192
+ return await handler(request);
193
+ }
194
+ return await current(request, dispatch);
195
+ };
196
+ return await dispatch();
197
+ };
198
+ };
199
+ }
200
+ function wrapRouteHandler(handler, middleware) {
201
+ if (isMethodRouteMap(handler)) {
202
+ const wrapped = {};
203
+ for (const [method, routeHandler] of Object.entries(handler)) {
204
+ wrapped[method] = composeMiddleware(...middleware)(routeHandler);
205
+ }
206
+ return wrapped;
207
+ }
208
+ if (isRouteHandler(handler)) {
209
+ return composeMiddleware(...middleware)(handler);
210
+ }
211
+ return handler;
212
+ }
213
+ function applyMiddlewareToRoutes(routes, middleware) {
214
+ const wrapped = {};
215
+ for (const [path, routeHandler] of Object.entries(routes)) {
216
+ wrapped[path] = wrapRouteHandler(routeHandler, middleware);
217
+ }
218
+ return wrapped;
219
+ }
220
+
221
+ // ../../src/bootstrap/httpKernel.ts
222
+ import {
223
+ createAuthMiddleware,
224
+ createAuthorizeMiddleware,
225
+ createBodySizeLimitMiddleware,
226
+ createCorsMiddleware,
227
+ createCsrfMiddleware,
228
+ createFlashMiddleware,
229
+ createLoginThrottleMiddleware,
230
+ createMembershipMiddleware,
231
+ createMemoryThrottleMiddleware,
232
+ createMetricsMiddleware,
233
+ createRequestLoggingMiddleware,
234
+ createRequireAbilityMiddleware,
235
+ createRequireAuthMiddleware,
236
+ createRequireGlobalAdminMiddleware,
237
+ createRequireWebAuthMiddleware,
238
+ createSecurityHeadersMiddleware,
239
+ createTenantMiddleware,
240
+ createThrottleMiddleware,
241
+ createTracingMiddleware,
242
+ isPublicReadsEnabled,
243
+ requestIdMiddleware,
244
+ withMiddleware
245
+ } from "@getstrata/core";
246
+
247
+ // ../../src/config/frontend.ts
248
+ function readFrontendMode() {
249
+ const mode = (process.env.FRONTEND_MODE ?? "api").trim();
250
+ if (mode === "server-htmx") {
251
+ return "server-htmx";
252
+ }
253
+ if (mode === "spa-react") {
254
+ return "spa-react";
255
+ }
256
+ return "api";
257
+ }
258
+ function isViewsEnabled() {
259
+ return readFrontendMode() === "server-htmx";
260
+ }
261
+
262
+ // ../../src/config/rateLimit.ts
263
+ var LOCAL_LOGIN_RATE_LIMIT = {
264
+ maxAttempts: 100,
265
+ decaySeconds: 60
266
+ };
267
+ var PRODUCTION_LOGIN_RATE_LIMIT = {
268
+ maxAttempts: 5,
269
+ decaySeconds: 900
270
+ };
271
+ function isLocalAppEnv() {
272
+ return (process.env.APP_ENV ?? "local") === "local";
273
+ }
274
+ function parsePositiveInt(value, fallback) {
275
+ const parsed = Number(value);
276
+ if (!Number.isFinite(parsed) || parsed <= 0) {
277
+ return fallback;
278
+ }
279
+ return Math.trunc(parsed);
280
+ }
281
+ function resolveLoginRateLimit() {
282
+ const defaults = isLocalAppEnv() ? LOCAL_LOGIN_RATE_LIMIT : PRODUCTION_LOGIN_RATE_LIMIT;
283
+ return {
284
+ maxAttempts: parsePositiveInt(process.env.LOGIN_RATE_LIMIT_PER_WINDOW, defaults.maxAttempts),
285
+ decaySeconds: parsePositiveInt(process.env.LOGIN_RATE_LIMIT_WINDOW_SECONDS, defaults.decaySeconds)
286
+ };
287
+ }
288
+ function resolveRegisterRateLimit() {
289
+ const defaults = isLocalAppEnv() ? { maxAttempts: 100, decaySeconds: 60 } : { maxAttempts: 10, decaySeconds: 60 };
290
+ return {
291
+ maxAttempts: parsePositiveInt(process.env.REGISTER_RATE_LIMIT_PER_WINDOW, defaults.maxAttempts),
292
+ 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)
293
+ };
294
+ }
295
+
296
+ // ../../src/bootstrap/config.ts
297
+ import {
298
+ CORE_AUTH_TOKEN,
299
+ CORE_CACHE_TOKEN,
300
+ CORE_CONFIG_TOKEN,
301
+ CORE_EVENT_BUS_TOKEN,
302
+ CORE_POLICY_GATE_TOKEN,
303
+ CORE_QUEUE_TOKEN,
304
+ CORE_TOKEN_SERVICE_TOKEN
305
+ } from "@getstrata/core/contracts/serviceTokens.ts";
306
+ var APP_PORT_CONFIG_KEY = "app.port";
307
+ var CACHE_TTL_MS_CONFIG_KEY = "cache.ttlMs";
308
+ var CACHE_MAX_ENTRIES_CONFIG_KEY = "cache.maxEntries";
309
+ var CACHE_DRIVER_CONFIG_KEY = "cache.driver";
310
+ var REDIS_URL_CONFIG_KEY = "cache.redisUrl";
311
+ var DATABASE_URL_CONFIG_KEY = "database.url";
312
+ var AUTH_DEV_HEADERS_CONFIG_KEY = "auth.allowDevHeaders";
313
+ var DEFAULT_APP_PORT = 3000;
314
+ var DEFAULT_CACHE_TTL_MS = 3600000;
315
+ var DEFAULT_CACHE_MAX_ENTRIES = 100;
316
+ var DEFAULT_CACHE_DRIVER = "array";
317
+ var DEFAULT_API_TOKEN = "";
318
+ var DEFAULT_QUEUE_DRIVER = "sync";
319
+
320
+ // ../../src/bootstrap/httpKernel.ts
321
+ class HttpKernel {
322
+ dependencies;
323
+ constructor(dependencies) {
324
+ this.dependencies = dependencies;
325
+ }
326
+ globalMiddleware() {
327
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
328
+ return [
329
+ createCorsMiddleware(),
330
+ createSecurityHeadersMiddleware(),
331
+ createBodySizeLimitMiddleware(),
332
+ createTracingMiddleware(),
333
+ createMetricsMiddleware(),
334
+ createRequestLoggingMiddleware(),
335
+ requestIdMiddleware,
336
+ createAuthMiddleware(auth),
337
+ createMembershipMiddleware(),
338
+ createTenantMiddleware()
339
+ ];
340
+ }
341
+ group(name) {
342
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
343
+ switch (name) {
344
+ case "authenticated":
345
+ return [createRequireAuthMiddleware(auth)];
346
+ case "web":
347
+ return isViewsEnabled() ? [createFlashMiddleware(), createCsrfMiddleware()] : [];
348
+ case "api": {
349
+ if (!this.dependencies.container.has(CORE_CONFIG_TOKEN)) {
350
+ return [];
351
+ }
352
+ const config = this.dependencies.container.resolve(CORE_CONFIG_TOKEN);
353
+ const redisUrl = config.get(REDIS_URL_CONFIG_KEY)?.trim() ?? "";
354
+ if (!redisUrl) {
355
+ const maxAttempts2 = Number(process.env.RATE_LIMIT_PER_MINUTE ?? "120");
356
+ return [
357
+ createMemoryThrottleMiddleware({
358
+ maxAttempts: Number.isFinite(maxAttempts2) ? maxAttempts2 : 120,
359
+ decaySeconds: 60
360
+ })
361
+ ];
362
+ }
363
+ const maxAttempts = Number(process.env.RATE_LIMIT_PER_MINUTE ?? "120");
364
+ return [
365
+ createThrottleMiddleware({
366
+ redisUrl,
367
+ maxAttempts: Number.isFinite(maxAttempts) ? maxAttempts : 120,
368
+ decaySeconds: 60
369
+ })
370
+ ];
371
+ }
372
+ default:
373
+ throw new Error(`Unknown middleware group "${name}".`);
374
+ }
375
+ }
376
+ wrap(groups, handler) {
377
+ const names = Array.isArray(groups) ? groups : [groups];
378
+ const middleware = names.flatMap((name) => this.group(name));
379
+ if (middleware.length === 0) {
380
+ return handler;
381
+ }
382
+ return withMiddleware(...middleware)(handler);
383
+ }
384
+ wrapApi(handler) {
385
+ return this.wrap(["api", "authenticated"], handler);
386
+ }
387
+ wrapWeb(handler) {
388
+ return handler;
389
+ }
390
+ wrapWebPublicRead(handler) {
391
+ if (isPublicReadsEnabled()) {
392
+ return this.wrapWeb(handler);
393
+ }
394
+ return this.wrapWebAuthenticated(handler);
395
+ }
396
+ wrapWebAuthenticated(handler) {
397
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
398
+ return withMiddleware(createRequireWebAuthMiddleware(auth))(handler);
399
+ }
400
+ wrapWebAbility(ability, handler) {
401
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
402
+ const abilityChecker = this.dependencies.container.resolve(CORE_TOKEN_SERVICE_TOKEN);
403
+ const requireAbility = createRequireAbilityMiddleware(abilityChecker);
404
+ const middleware = [createRequireWebAuthMiddleware(auth), requireAbility(ability)];
405
+ return withMiddleware(...middleware)(handler);
406
+ }
407
+ wrapWebGlobalAdmin(handler) {
408
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
409
+ const middleware = [createRequireWebAuthMiddleware(auth), createRequireGlobalAdminMiddleware()];
410
+ return withMiddleware(...middleware)(handler);
411
+ }
412
+ wrapAuthenticated(handler) {
413
+ return this.wrap("authenticated", handler);
414
+ }
415
+ wrapPublicRead(handler) {
416
+ if (isPublicReadsEnabled()) {
417
+ return handler;
418
+ }
419
+ return this.wrapAuthenticated(handler);
420
+ }
421
+ wrapGlobalAdmin(handler) {
422
+ const middleware = [...this.group("authenticated"), createRequireGlobalAdminMiddleware()];
423
+ return withMiddleware(...middleware)(handler);
424
+ }
425
+ wrapAbility(ability, handler) {
426
+ const abilityChecker = this.dependencies.container.resolve(CORE_TOKEN_SERVICE_TOKEN);
427
+ const requireAbility = createRequireAbilityMiddleware(abilityChecker);
428
+ const middleware = [...this.group("authenticated"), requireAbility(ability)];
429
+ return withMiddleware(...middleware)(handler);
430
+ }
431
+ wrapPolicy(resource, action, handler) {
432
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
433
+ const gate = this.dependencies.container.resolve(CORE_POLICY_GATE_TOKEN);
434
+ return withMiddleware(createAuthorizeMiddleware(gate, auth, resource, action))(handler);
435
+ }
436
+ wrapLogin(handler) {
437
+ return this.wrapThrottle("login", resolveLoginRateLimit(), handler);
438
+ }
439
+ wrapRegister(handler) {
440
+ return this.wrapThrottle("register", resolveRegisterRateLimit(), handler);
441
+ }
442
+ wrapThrottle(scope, rateLimit, handler) {
443
+ const middleware = [];
444
+ const memoryKeyPrefix = scope === "login" ? "login-throttle:" : "register-throttle:";
445
+ if (this.dependencies.container.has(CORE_CONFIG_TOKEN)) {
446
+ const config = this.dependencies.container.resolve(CORE_CONFIG_TOKEN);
447
+ const redisUrl = config.get(REDIS_URL_CONFIG_KEY)?.trim() ?? "";
448
+ if (redisUrl) {
449
+ const throttle = scope === "login" ? createLoginThrottleMiddleware({
450
+ redisUrl,
451
+ maxAttempts: rateLimit.maxAttempts,
452
+ decaySeconds: rateLimit.decaySeconds
453
+ }) : createThrottleMiddleware({
454
+ redisUrl,
455
+ maxAttempts: rateLimit.maxAttempts,
456
+ decaySeconds: rateLimit.decaySeconds,
457
+ keyPrefix: memoryKeyPrefix
458
+ });
459
+ middleware.push(throttle);
460
+ } else {
461
+ middleware.push(createMemoryThrottleMiddleware({
462
+ maxAttempts: rateLimit.maxAttempts,
463
+ decaySeconds: rateLimit.decaySeconds,
464
+ keyPrefix: memoryKeyPrefix
465
+ }));
466
+ }
467
+ } else {
468
+ middleware.push(createMemoryThrottleMiddleware({
469
+ maxAttempts: rateLimit.maxAttempts,
470
+ decaySeconds: rateLimit.decaySeconds,
471
+ keyPrefix: memoryKeyPrefix
472
+ }));
473
+ }
474
+ if (middleware.length === 0) {
475
+ return handler;
476
+ }
477
+ return withMiddleware(...middleware)(handler);
478
+ }
479
+ }
480
+ function createHttpKernel(dependencies) {
481
+ return new HttpKernel(dependencies);
482
+ }
483
+
484
+ // ../../src/bootstrap/discoverModules.ts
485
+ import { readdirSync } from "fs";
486
+ import { join } from "path";
487
+ import { pathToFileURL } from "url";
488
+ var DISCOVER_MODULES_STATE_KEY = Symbol.for("@getstrata/discoverModulesState");
489
+ function readDiscoverModulesState() {
490
+ const existing = globalThis[DISCOVER_MODULES_STATE_KEY];
491
+ if (existing) {
492
+ return existing;
493
+ }
494
+ const state = { appModules: [] };
495
+ globalThis[DISCOVER_MODULES_STATE_KEY] = state;
496
+ return state;
497
+ }
498
+ function configureModulesDirectory(modulesDir) {
499
+ readDiscoverModulesState().configuredModulesDir = modulesDir;
500
+ }
501
+ function resolveModulesDirectory(options) {
502
+ const state = readDiscoverModulesState();
503
+ if (options?.modulesDir) {
504
+ return options.modulesDir;
505
+ }
506
+ if (state.configuredModulesDir) {
507
+ return state.configuredModulesDir;
508
+ }
509
+ return join(import.meta.dir, "../modules");
510
+ }
511
+ async function loadDiscoveredModules(options) {
512
+ const modulesDirectory = resolveModulesDirectory(options);
513
+ let moduleNames;
514
+ try {
515
+ moduleNames = readdirSync(modulesDirectory, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
516
+ } catch (error) {
517
+ if (error.code === "ENOENT") {
518
+ return [];
519
+ }
520
+ throw error;
521
+ }
522
+ const modules = await Promise.all(moduleNames.map(async (moduleName) => {
523
+ const moduleUrl = pathToFileURL(join(modulesDirectory, moduleName, "index.ts")).href;
524
+ const loaded = await import(moduleUrl);
525
+ return loaded.default;
526
+ }));
527
+ return modules.filter((module) => module?.name !== undefined).sort((left, right) => (left.order ?? 100) - (right.order ?? 100));
528
+ }
529
+ async function ensureModulesLoaded(options) {
530
+ const state = readDiscoverModulesState();
531
+ if (state.appModules.length > 0) {
532
+ return state.appModules;
533
+ }
534
+ state.modulesReady ??= loadDiscoveredModules(options).then((modules) => {
535
+ state.appModules.splice(0, state.appModules.length, ...modules);
536
+ return state.appModules;
537
+ });
538
+ return state.modulesReady;
539
+ }
540
+ function discoverModules() {
541
+ return readDiscoverModulesState().appModules;
542
+ }
543
+ // ../../src/bootstrap/prefixRouteMap.ts
544
+ function prefixRouteMap(prefix, routes) {
545
+ const normalizedPrefix = prefix.replace(/\/$/, "");
546
+ const prefixed = {};
547
+ for (const [path, handler] of Object.entries(routes)) {
548
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
549
+ prefixed[`${normalizedPrefix}${normalizedPath}`] = handler;
550
+ }
551
+ return prefixed;
552
+ }
553
+
554
+ // ../../src/bootstrap/routeRegistry.ts
555
+ class RouteRegistry {
556
+ routes = [];
557
+ register(route) {
558
+ this.routes.push(route);
559
+ }
560
+ clear() {
561
+ this.routes.length = 0;
562
+ }
563
+ list() {
564
+ return [...this.routes].sort((left, right) => left.path.localeCompare(right.path));
565
+ }
566
+ }
567
+ var ROUTE_REGISTRY_KEY = Symbol.for("@getstrata/routeRegistry");
568
+ function readSharedRouteRegistry() {
569
+ const globalRegistry = globalThis[ROUTE_REGISTRY_KEY];
570
+ if (globalRegistry) {
571
+ return globalRegistry;
572
+ }
573
+ const registry = new RouteRegistry;
574
+ globalThis[ROUTE_REGISTRY_KEY] = registry;
575
+ return registry;
576
+ }
577
+ var routeRegistry = readSharedRouteRegistry();
578
+
579
+ // ../../src/bootstrap/buildModuleRoutes.ts
580
+ function registerOpenApiRoute(method, path, middleware) {
581
+ routeRegistry.register({ method, path, middleware });
582
+ }
583
+ function registerOpenApiRouteMap(routes, middleware) {
584
+ const registered = {};
585
+ for (const [path, handler] of Object.entries(routes)) {
586
+ if (handler && typeof handler === "object" && !Array.isArray(handler)) {
587
+ const methodMap = handler;
588
+ registered[path] = methodMap;
589
+ for (const method of Object.keys(methodMap)) {
590
+ registerOpenApiRoute(method.toUpperCase(), path, middleware);
591
+ }
592
+ continue;
593
+ }
594
+ registered[path] = handler;
595
+ registerOpenApiRoute("GET", path, middleware);
596
+ }
597
+ return registered;
598
+ }
599
+ function createCachedJson(dependencies) {
600
+ return async (cacheKey, loader, tags = [], request) => {
601
+ const data = tags.length > 0 ? await dependencies.cache.tags(...tags).remember(cacheKey, loader) : await dependencies.cache.remember(cacheKey, loader);
602
+ return conditionalJsonResponse(request, data);
603
+ };
604
+ }
605
+ function buildModuleRoutes(dependencies, options = {}) {
606
+ const { apiPrefix = "", modules = discoverModules(), clearRegistry = true } = options;
607
+ if (clearRegistry) {
608
+ routeRegistry.clear();
609
+ }
610
+ const kernel = createHttpKernel(dependencies);
611
+ const middleware = [...kernel.globalMiddleware(), ...kernel.group("api")];
612
+ const cachedJson = createCachedJson(dependencies);
613
+ const moduleRoutes = {};
614
+ for (const module of modules) {
615
+ if (!module.routes) {
616
+ continue;
617
+ }
618
+ Object.assign(moduleRoutes, module.routes({ dependencies, cachedJson, kernel }));
619
+ }
620
+ const prefixedModuleRoutes = prefixRouteMap(apiPrefix, moduleRoutes);
621
+ return applyMiddlewareToRoutes(registerOpenApiRouteMap(prefixedModuleRoutes, ["global", "api"]), middleware);
622
+ }
623
+ export {
624
+ registerOpenApiRouteMap,
625
+ buildModuleRoutes
626
+ };
@@ -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
@@ -1,14 +1,14 @@
1
1
  // @bun
2
- // ../../src/core/contracts/serviceTokens.ts
3
- var CORE_CONFIG_TOKEN = "core.config";
4
- var CORE_CACHE_TOKEN = "core.cache";
5
- var CORE_QUEUE_TOKEN = "core.queue";
6
- var CORE_EVENT_BUS_TOKEN = "core.eventBus";
7
- var CORE_POLICY_GATE_TOKEN = "core.policyGate";
8
- var CORE_AUTH_TOKEN = "core.auth";
9
- var CORE_TOKEN_SERVICE_TOKEN = "core.tokenService";
10
-
11
2
  // ../../src/bootstrap/config.ts
3
+ import {
4
+ CORE_AUTH_TOKEN,
5
+ CORE_CACHE_TOKEN,
6
+ CORE_CONFIG_TOKEN,
7
+ CORE_EVENT_BUS_TOKEN,
8
+ CORE_POLICY_GATE_TOKEN,
9
+ CORE_QUEUE_TOKEN,
10
+ CORE_TOKEN_SERVICE_TOKEN
11
+ } from "@getstrata/core/contracts/serviceTokens.ts";
12
12
  var APP_PORT_CONFIG_KEY = "app.port";
13
13
  var CACHE_TTL_MS_CONFIG_KEY = "cache.ttlMs";
14
14
  var CACHE_MAX_ENTRIES_CONFIG_KEY = "cache.maxEntries";