@getstrata/bootstrap 0.2.26 → 0.2.29

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 (36) hide show
  1. package/dist/bootstrap/http/securedRouteModelBinding.d.ts +2 -2
  2. package/dist/bootstrap/listeners/invalidateCacheOnModelWrite.d.ts +1 -1
  3. package/dist/bootstrap/schedule.d.ts +2 -1
  4. package/dist/bootstrap/web/forms.d.ts +1 -1
  5. package/dist/entries/applicationRegistry.js +2 -0
  6. package/dist/entries/buildModuleRoutes.js +5 -0
  7. package/dist/entries/buildWebModuleRoutes.js +5 -0
  8. package/dist/entries/cache/modelCacheTags.js +2 -0
  9. package/dist/entries/config.js +2 -0
  10. package/dist/entries/context.js +99 -3156
  11. package/dist/entries/contracts.js +2 -0
  12. package/dist/entries/createRoutes.js +1468 -0
  13. package/dist/entries/createSpaRoutes.js +64 -0
  14. package/dist/entries/createWebRoutes.js +5 -0
  15. package/dist/entries/dependencies.js +99 -3156
  16. package/dist/entries/discoverModules.js +2 -0
  17. package/dist/entries/health.js +215 -0
  18. package/dist/entries/http/securedRouteModelBinding.js +6 -293
  19. package/dist/entries/httpKernel.js +5 -0
  20. package/dist/entries/listeners/invalidateCacheOnModelWrite.js +120 -0
  21. package/dist/entries/membershipService.js +2 -0
  22. package/dist/entries/metricsRoutes.js +18 -0
  23. package/dist/entries/providers/view.js +8 -603
  24. package/dist/entries/providers.js +94 -3158
  25. package/dist/entries/queue/defaultJobs.js +7 -465
  26. package/dist/entries/routeRegistry.js +2 -0
  27. package/dist/entries/schedule.js +49 -0
  28. package/dist/entries/secretsGuard.js +9 -0
  29. package/dist/entries/web/forms.js +4 -18
  30. package/dist/entries/web/routing.js +21 -304
  31. package/dist/entries/web/server.js +2 -0
  32. package/dist/entries/web/session.js +3 -26
  33. package/dist/entries/web/slug.js +2 -0
  34. package/dist/index-sfreg6q3.js +0 -0
  35. package/dist/index.js +176 -3543
  36. package/package.json +32 -2
@@ -1,301 +1,15 @@
1
1
  // @bun
2
+ var __jsonParse = (a) => JSON.parse(a);
3
+
2
4
  // ../../src/bootstrap/web/routing.ts
3
5
  import { withErrorHandling } from "@getstrata/core";
4
6
 
5
- // ../../src/core/runtime/asyncContextStore.ts
6
- import { AsyncLocalStorage } from "async_hooks";
7
- function createAsyncContextStore(key) {
8
- const symbol = Symbol.for(key);
9
- const globalRecord = globalThis;
10
- const existing = globalRecord[symbol];
11
- if (existing) {
12
- return existing;
13
- }
14
- const store = new AsyncLocalStorage;
15
- globalRecord[symbol] = store;
16
- return store;
17
- }
18
-
19
- // ../../src/core/auth/authContext.ts
20
- var authContext = createAsyncContextStore("@getstrata/authContext");
21
- function currentAuthUser() {
22
- return authContext.getStore() ?? null;
23
- }
24
-
25
- // ../../src/core/errors/http.ts
26
- class HttpError extends Error {
27
- status;
28
- details;
29
- constructor(status, message, details) {
30
- super(message);
31
- this.name = new.target.name;
32
- this.status = status;
33
- this.details = details;
34
- }
35
- }
36
-
37
- class BadRequestError extends HttpError {
38
- constructor(message = "Bad Request", details) {
39
- super(400, message, details);
40
- }
41
- }
42
- class ConflictError extends HttpError {
43
- constructor(message = "Conflict", details) {
44
- super(409, message, details);
45
- }
46
- }
47
-
48
- class UnprocessableEntityError extends HttpError {
49
- constructor(message = "Unprocessable Entity", details) {
50
- super(422, message, details);
51
- }
52
- }
53
- class ForbiddenError extends HttpError {
54
- constructor(message = "Forbidden", details) {
55
- super(403, message, details);
56
- }
57
- }
58
-
59
- class UnauthorizedError extends HttpError {
60
- constructor(message = "Unauthorized", details) {
61
- super(401, message, details);
62
- }
63
- }
64
- class PreconditionFailedError extends HttpError {
65
- constructor(message = "Precondition Failed", details) {
66
- super(412, message, details);
67
- }
68
- }
69
-
70
- // ../../src/core/contracts/di.ts
71
- function getRequiredDependency(dependencies, key) {
72
- const dependency = dependencies[key];
73
- if (dependency === undefined) {
74
- throw new Error(`Required dependency "${key}" is not registered.`);
75
- }
76
- return dependency;
77
- }
78
-
79
- // ../../src/core/contracts/serviceTokens.ts
80
- var CORE_POLICY_GATE_TOKEN = "core.policyGate";
81
- var CORE_AUTH_TOKEN = "core.auth";
82
-
83
- // ../../src/core/logging/logger.ts
84
- class Logger {
85
- channel;
86
- constructor(channel = "app") {
87
- this.channel = channel;
88
- }
89
- write(level, message, context = {}) {
90
- const entry = {
91
- level,
92
- channel: this.channel,
93
- message,
94
- timestamp: new Date().toISOString(),
95
- ...context
96
- };
97
- const line = JSON.stringify(entry);
98
- if (level === "error") {
99
- console.error(line);
100
- return;
101
- }
102
- console.log(line);
103
- }
104
- debug(message, context) {
105
- this.write("debug", message, context);
106
- }
107
- info(message, context) {
108
- this.write("info", message, context);
109
- }
110
- warn(message, context) {
111
- this.write("warn", message, context);
112
- }
113
- error(message, context) {
114
- this.write("error", message, context);
115
- }
116
- }
117
- var appLogger = new Logger("app");
118
-
119
- // ../../src/core/runtime/applicationRegistry.ts
120
- var APPLICATION_CONTEXT_KEY = Symbol.for("@getstrata/applicationContext");
121
- var activeContext;
122
- function readStoredApplicationContext() {
123
- if (activeContext) {
124
- return activeContext;
125
- }
126
- const globalContext = globalThis[APPLICATION_CONTEXT_KEY];
127
- if (globalContext) {
128
- activeContext = globalContext;
129
- }
130
- return activeContext;
131
- }
132
- function requireActiveApplicationContext() {
133
- const context = readStoredApplicationContext();
134
- if (!context) {
135
- throw new Error("The application context has not been bootstrapped.");
136
- }
137
- return context;
138
- }
139
- function resolveApplicationCache() {
140
- return getRequiredDependency(requireActiveApplicationContext().dependencies, "cache");
141
- }
142
- function resolveApplicationAuth() {
143
- return requireActiveApplicationContext().container.resolve(CORE_AUTH_TOKEN);
144
- }
145
- function resolveApplicationPolicyGate() {
146
- return requireActiveApplicationContext().container.resolve(CORE_POLICY_GATE_TOKEN);
147
- }
148
-
149
- // ../../src/core/crypto/nonCryptographicHash.ts
150
- function nonCryptographicDigest(input) {
151
- return Bun.hash(input).toString(16);
152
- }
153
-
154
- // ../../src/core/http/etag.ts
155
- function isEtagEnabled() {
156
- return (process.env.FEATURE_ETAG ?? "true") !== "false";
157
- }
158
- function formatWeakEtag(digest) {
159
- return `W/"${digest}"`;
160
- }
161
- function etagFromResource(resource) {
162
- const version = resource.updated_at ?? resource.created_at ?? "";
163
- const versionText = version instanceof Date ? version.toISOString() : version ? String(version) : "0";
164
- const digest = nonCryptographicDigest(`${String(resource.id ?? "0")}:${versionText}`).slice(0, 32);
165
- return formatWeakEtag(digest);
166
- }
167
- function normalizeEtag(value) {
168
- return value.trim();
169
- }
170
- function etagValuesMatch(left, right) {
171
- return normalizeEtag(left) === normalizeEtag(right);
172
- }
173
- function parseEtagList(header) {
174
- if (!header) {
175
- return [];
176
- }
177
- return header.split(",").map((value) => normalizeEtag(value)).filter(Boolean);
178
- }
179
- function ifNoneMatchSatisfied(request, etag) {
180
- const header = request.headers.get("if-none-match");
181
- if (!header) {
182
- return false;
183
- }
184
- if (header.trim() === "*") {
185
- return true;
186
- }
187
- return parseEtagList(header).some((candidate) => etagValuesMatch(candidate, etag));
188
- }
189
- function ifMatchSatisfied(request, etag) {
190
- const header = request.headers.get("if-match");
191
- if (!header) {
192
- return false;
193
- }
194
- if (header.trim() === "*") {
195
- return true;
196
- }
197
- return parseEtagList(header).some((candidate) => etagValuesMatch(candidate, etag));
198
- }
199
- function assertIfMatch(request, etag, options = {}) {
200
- const header = request.headers.get("if-match");
201
- if (!header) {
202
- if (options.required) {
203
- throw new PreconditionFailedError("If-Match header is required.");
204
- }
205
- return;
206
- }
207
- if (!ifMatchSatisfied(request, etag)) {
208
- throw new PreconditionFailedError("Resource ETag does not match If-Match.");
209
- }
210
- }
211
- function applyEtagHeaders(headers, etag) {
212
- const next = new Headers(headers);
213
- next.set("ETag", etag);
214
- next.set("Cache-Control", "private, must-revalidate");
215
- next.append("Vary", "Authorization");
216
- next.append("Vary", "X-Tenant-Id");
217
- return next;
218
- }
219
- function notModifiedResponse(etag) {
220
- return new Response(null, {
221
- status: 304,
222
- headers: applyEtagHeaders(new Headers, etag)
223
- });
224
- }
225
- function applyConditionalGet(request, response, etag) {
226
- if (!isEtagEnabled()) {
227
- return response;
228
- }
229
- if (ifNoneMatchSatisfied(request, etag)) {
230
- return notModifiedResponse(etag);
231
- }
232
- const headers = applyEtagHeaders(new Headers(response.headers), etag);
233
- return new Response(response.body, {
234
- status: response.status,
235
- statusText: response.statusText,
236
- headers
237
- });
238
- }
239
-
240
- // ../../src/core/tenant/tenantContext.ts
241
- var tenantContext = createAsyncContextStore("@getstrata/tenantContext");
242
-
243
- // ../../src/core/http/validation.ts
244
- function parsePositiveIntParam(value, name = "id") {
245
- const parsed = Number.parseInt(value, 10);
246
- if (!Number.isInteger(parsed) || parsed <= 0) {
247
- throw new BadRequestError(`Invalid ${name}. Expected a positive integer.`);
248
- }
249
- return parsed;
250
- }
7
+ // ../../src/bootstrap/http/securedRouteModelBinding.ts
8
+ import {
9
+ securedBindRouteModel,
10
+ securedBindRouteModelByKey
11
+ } from "@getstrata/core/http/securedRouteModelBinding";
251
12
 
252
- // ../../src/core/http/securedRouteModelBinding.ts
253
- function isMutatingPolicyAction(action) {
254
- return action === "update" || action === "delete";
255
- }
256
- function securedBindRouteModel(param, resolver, authorization, handler) {
257
- return async (request) => {
258
- const id = parsePositiveIntParam(String(request.params[param]), String(param));
259
- const model = await resolver(id, request);
260
- const gate = resolveApplicationPolicyGate();
261
- const auth = resolveApplicationAuth();
262
- const user = currentAuthUser() ?? await auth.resolve(request);
263
- gate.authorize(authorization.resource, authorization.action, user, model);
264
- if (isEtagEnabled() && isMutatingPolicyAction(authorization.action)) {
265
- assertIfMatch(request, etagFromResource(model), {
266
- required: authorization.requireIfMatch ?? true
267
- });
268
- }
269
- const response = await handler(request, model);
270
- if (isEtagEnabled() && authorization.action === "view") {
271
- return applyConditionalGet(request, response, etagFromResource(model));
272
- }
273
- return response;
274
- };
275
- }
276
- function securedBindRouteModelByKey(param, resolver, authorization, handler) {
277
- return async (request) => {
278
- const key = String(request.params[param] ?? "").trim();
279
- if (!key) {
280
- throw new BadRequestError(`Missing route parameter "${String(param)}".`);
281
- }
282
- const model = await resolver(key, request);
283
- const gate = resolveApplicationPolicyGate();
284
- const auth = resolveApplicationAuth();
285
- const user = currentAuthUser() ?? await auth.resolve(request);
286
- gate.authorize(authorization.resource, authorization.action, user, model);
287
- if (isEtagEnabled() && isMutatingPolicyAction(authorization.action)) {
288
- assertIfMatch(request, etagFromResource(model), {
289
- required: authorization.requireIfMatch ?? true
290
- });
291
- }
292
- const response = await handler(request, model);
293
- if (isEtagEnabled() && authorization.action === "view") {
294
- return applyConditionalGet(request, response, etagFromResource(model));
295
- }
296
- return response;
297
- };
298
- }
299
13
  // ../../src/bootstrap/httpKernel.ts
300
14
  import {
301
15
  createAuthMiddleware,
@@ -336,6 +50,9 @@ function readFrontendMode() {
336
50
  function isViewsEnabled() {
337
51
  return readFrontendMode() === "server-htmx";
338
52
  }
53
+ function isSpaEnabled() {
54
+ return readFrontendMode() === "spa-react";
55
+ }
339
56
 
340
57
  // ../../src/config/rateLimit.ts
341
58
  var LOCAL_LOGIN_RATE_LIMIT = {
@@ -373,12 +90,12 @@ function resolveRegisterRateLimit() {
373
90
 
374
91
  // ../../src/bootstrap/config.ts
375
92
  import {
376
- CORE_AUTH_TOKEN as CORE_AUTH_TOKEN2,
93
+ CORE_AUTH_TOKEN,
377
94
  CORE_CACHE_TOKEN,
378
95
  CORE_CONFIG_TOKEN,
379
- CORE_EVENT_BUS_TOKEN as CORE_EVENT_BUS_TOKEN2,
380
- CORE_POLICY_GATE_TOKEN as CORE_POLICY_GATE_TOKEN2,
381
- CORE_QUEUE_TOKEN as CORE_QUEUE_TOKEN2,
96
+ CORE_EVENT_BUS_TOKEN,
97
+ CORE_POLICY_GATE_TOKEN,
98
+ CORE_QUEUE_TOKEN,
382
99
  CORE_TOKEN_SERVICE_TOKEN
383
100
  } from "@getstrata/core/contracts/serviceTokens";
384
101
  var APP_PORT_CONFIG_KEY = "app.port";
@@ -402,7 +119,7 @@ class HttpKernel {
402
119
  this.dependencies = dependencies;
403
120
  }
404
121
  globalMiddleware() {
405
- const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN2);
122
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
406
123
  return [
407
124
  createCorsMiddleware(),
408
125
  createSecurityHeadersMiddleware(),
@@ -417,7 +134,7 @@ class HttpKernel {
417
134
  ];
418
135
  }
419
136
  group(name) {
420
- const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN2);
137
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
421
138
  switch (name) {
422
139
  case "authenticated":
423
140
  return [createRequireAuthMiddleware(auth)];
@@ -472,18 +189,18 @@ class HttpKernel {
472
189
  return this.wrapWebAuthenticated(handler);
473
190
  }
474
191
  wrapWebAuthenticated(handler) {
475
- const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN2);
192
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
476
193
  return withMiddleware(createRequireWebAuthMiddleware(auth))(handler);
477
194
  }
478
195
  wrapWebAbility(ability, handler) {
479
- const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN2);
196
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
480
197
  const abilityChecker = this.dependencies.container.resolve(CORE_TOKEN_SERVICE_TOKEN);
481
198
  const requireAbility = createRequireAbilityMiddleware(abilityChecker);
482
199
  const middleware = [createRequireWebAuthMiddleware(auth), requireAbility(ability)];
483
200
  return withMiddleware(...middleware)(handler);
484
201
  }
485
202
  wrapWebGlobalAdmin(handler) {
486
- const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN2);
203
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
487
204
  const middleware = [createRequireWebAuthMiddleware(auth), createRequireGlobalAdminMiddleware()];
488
205
  return withMiddleware(...middleware)(handler);
489
206
  }
@@ -507,8 +224,8 @@ class HttpKernel {
507
224
  return withMiddleware(...middleware)(handler);
508
225
  }
509
226
  wrapPolicy(resource, action, handler) {
510
- const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN2);
511
- const gate = this.dependencies.container.resolve(CORE_POLICY_GATE_TOKEN2);
227
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
228
+ const gate = this.dependencies.container.resolve(CORE_POLICY_GATE_TOKEN);
512
229
  return withMiddleware(createAuthorizeMiddleware(gate, auth, resource, action))(handler);
513
230
  }
514
231
  wrapLogin(handler) {
@@ -1,4 +1,6 @@
1
1
  // @bun
2
+ var __jsonParse = (a) => JSON.parse(a);
3
+
2
4
  // ../../src/bootstrap/web/server.ts
3
5
  function wrapRouteHandler(handler) {
4
6
  return async (request) => {
@@ -1,33 +1,10 @@
1
1
  // @bun
2
+ var __jsonParse = (a) => JSON.parse(a);
3
+
2
4
  // ../../src/bootstrap/web/session.ts
3
5
  import { createHash, randomBytes } from "crypto";
6
+ import { readRequestCookie } from "@getstrata/core/http/cookies";
4
7
 
5
- // ../../src/core/http/cookies.ts
6
- function readRequestCookie(request, name) {
7
- const cookies = request.cookies;
8
- if (cookies && typeof cookies.get === "function") {
9
- const value = cookies.get(name);
10
- if (value) {
11
- return value;
12
- }
13
- }
14
- const header = request.headers.get("cookie");
15
- if (!header) {
16
- return null;
17
- }
18
- for (const part of header.split(";")) {
19
- const idx = part.indexOf("=");
20
- if (idx === -1)
21
- continue;
22
- const cookieName = part.slice(0, idx).trim();
23
- if (cookieName !== name)
24
- continue;
25
- return decodeURIComponent(part.slice(idx + 1).trim());
26
- }
27
- return null;
28
- }
29
-
30
- // ../../src/bootstrap/web/session.ts
31
8
  class CookieSessionStore {
32
9
  sql;
33
10
  secret;
@@ -1,4 +1,6 @@
1
1
  // @bun
2
+ var __jsonParse = (a) => JSON.parse(a);
3
+
2
4
  // ../../src/bootstrap/web/slug.ts
3
5
  function slugify(value) {
4
6
  return value.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
File without changes