@getstrata/bootstrap 0.2.57 → 0.2.59

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @getstrata/bootstrap changelog
2
2
 
3
+ ## 0.2.59
4
+
5
+ - `HttpKernel.wrapWebGuest()` is Laravel `guest` / `RedirectIfAuthenticated`. Signed-in HTML users redirect to `/organizations` (override the home path). Peer `@getstrata/core` `^0.5.72`.
6
+
7
+ ## 0.2.58
8
+
9
+ - Peer `@getstrata/core` `^0.5.71` for public OpenAPI `/auth/register`.
10
+
3
11
  ## 0.2.57
4
12
 
5
13
  - Peer `@getstrata/core` `^0.5.70` for optional OAuth `redirectUri`.
package/README.md CHANGED
@@ -32,7 +32,7 @@ import { createHttpKernel, createAppContext, coreProviders } from "@getstrata/bo
32
32
 
33
33
  Sibling HTMX apps should bind `createCookieSessionAuthManager` from `@getstrata/bootstrap/web/session` instead of HMAC `SessionGuard`. Use `signIn` / `signOut` (or the redirect helpers) instead of calling `CookieSessionStore` from controllers. Pass `mapUser` to map roles in the app. Pass `loadSessionUser` when the default `learn_subscriber` / `is_admin` SELECT does not match your schema. WorkHub’s table is `sessions` (`0031_create_sessions`); its loader is `loadWorkhubSessionUser` (maps `users.role`, decrypts email). WorkHub web login itself stays on HMAC `SessionGuard`.
34
34
 
35
- `wrapWeb` applies the web group (CSRF + flash) and `withErrorHandling`, so CSRF `ForbiddenError` becomes an HTML 403 in `FRONTEND_MODE=server-htmx`. `wrapWebLogin` / `wrapWebRegister` include that web group plus throttle — do not wrap them with `wrapWeb` again.
35
+ `wrapWeb` applies the web group (CSRF + flash) and `withErrorHandling`, so CSRF `ForbiddenError` becomes an HTML 403 in `FRONTEND_MODE=server-htmx`. `wrapWebGuest` is Laravel `guest` / `RedirectIfAuthenticated` (signed-in users go to `/organizations` by default). `wrapWebLogin` / `wrapWebRegister` include that web group plus throttle — do not wrap them with `wrapWeb` again. The throttle callback should return an HTML form at 429 (WorkHub’s `/login` and `/register` do).
36
36
 
37
37
  These subpaths remain WorkHub-oriented and are not a generic starter API: `@getstrata/bootstrap/createRoutes` (includes SCIM), `@getstrata/bootstrap/schedule`, and `@getstrata/bootstrap/createWebRoutes` (redirects `/` to `/organizations`).
38
38
 
@@ -10,6 +10,8 @@ declare class HttpKernel {
10
10
  wrap(groups: MiddlewareGroupName | MiddlewareGroupName[], handler: RouteHandler): RouteHandler;
11
11
  wrapApi(handler: RouteHandler): RouteHandler;
12
12
  wrapWeb(handler: RouteHandler): RouteHandler;
13
+ /** Laravel `guest` / `RedirectIfAuthenticated` — signed-in users go to `home`. */
14
+ wrapWebGuest(handler: RouteHandler, home?: string): RouteHandler;
13
15
  wrapWebPublicRead(handler: RouteHandler): RouteHandler;
14
16
  wrapWebAuthenticated(handler: RouteHandler): RouteHandler;
15
17
  wrapWebAbility(ability: string, handler: RouteHandler): RouteHandler;
@@ -175,6 +175,16 @@ class HttpKernel {
175
175
  wrapWeb(handler) {
176
176
  return withErrorHandling(this.wrap("web", handler));
177
177
  }
178
+ wrapWebGuest(handler, home = "/organizations") {
179
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
180
+ return this.wrapWeb(async (request) => {
181
+ const user = await auth.resolve(request);
182
+ if (user) {
183
+ return Response.redirect(home, 302);
184
+ }
185
+ return handler(request);
186
+ });
187
+ }
178
188
  wrapWebPublicRead(handler) {
179
189
  if (isPublicReadsEnabled()) {
180
190
  return this.wrapWeb(handler);
@@ -178,6 +178,16 @@ class HttpKernel {
178
178
  wrapWeb(handler) {
179
179
  return withErrorHandling(this.wrap("web", handler));
180
180
  }
181
+ wrapWebGuest(handler, home = "/organizations") {
182
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
183
+ return this.wrapWeb(async (request) => {
184
+ const user = await auth.resolve(request);
185
+ if (user) {
186
+ return Response.redirect(home, 302);
187
+ }
188
+ return handler(request);
189
+ });
190
+ }
181
191
  wrapWebPublicRead(handler) {
182
192
  if (isPublicReadsEnabled()) {
183
193
  return this.wrapWeb(handler);
@@ -32,7 +32,8 @@ function readFeatureFlags() {
32
32
  siemExport: (process.env.FEATURE_SIEM_EXPORT ?? "true") !== "false",
33
33
  publicReads: (process.env.FEATURE_PUBLIC_READS ?? "true") !== "false",
34
34
  emailVerification: (process.env.FEATURE_EMAIL_VERIFICATION ?? "false") === "true",
35
- mfa: (process.env.FEATURE_MFA ?? "false") === "true"
35
+ mfa: (process.env.FEATURE_MFA ?? "false") === "true",
36
+ registration: (process.env.FEATURE_REGISTRATION ?? "true") !== "false"
36
37
  };
37
38
  }
38
39
  var featureFlags = readFeatureFlags();
@@ -214,6 +215,16 @@ class HttpKernel {
214
215
  wrapWeb(handler) {
215
216
  return withErrorHandling(this.wrap("web", handler));
216
217
  }
218
+ wrapWebGuest(handler, home = "/organizations") {
219
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
220
+ return this.wrapWeb(async (request) => {
221
+ const user = await auth.resolve(request);
222
+ if (user) {
223
+ return Response.redirect(home, 302);
224
+ }
225
+ return handler(request);
226
+ });
227
+ }
217
228
  wrapWebPublicRead(handler) {
218
229
  if (isPublicReadsEnabled()) {
219
230
  return this.wrapWeb(handler);
@@ -183,6 +183,16 @@ class HttpKernel {
183
183
  wrapWeb(handler) {
184
184
  return withErrorHandling(this.wrap("web", handler));
185
185
  }
186
+ wrapWebGuest(handler, home = "/organizations") {
187
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
188
+ return this.wrapWeb(async (request) => {
189
+ const user = await auth.resolve(request);
190
+ if (user) {
191
+ return Response.redirect(home, 302);
192
+ }
193
+ return handler(request);
194
+ });
195
+ }
186
196
  wrapWebPublicRead(handler) {
187
197
  if (isPublicReadsEnabled()) {
188
198
  return this.wrapWeb(handler);
@@ -171,6 +171,16 @@ class HttpKernel {
171
171
  wrapWeb(handler) {
172
172
  return withErrorHandling(this.wrap("web", handler));
173
173
  }
174
+ wrapWebGuest(handler, home = "/organizations") {
175
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
176
+ return this.wrapWeb(async (request) => {
177
+ const user = await auth.resolve(request);
178
+ if (user) {
179
+ return Response.redirect(home, 302);
180
+ }
181
+ return handler(request);
182
+ });
183
+ }
174
184
  wrapWebPublicRead(handler) {
175
185
  if (isPublicReadsEnabled()) {
176
186
  return this.wrapWeb(handler);
@@ -19,7 +19,8 @@ function readFeatureFlags() {
19
19
  siemExport: (process.env.FEATURE_SIEM_EXPORT ?? "true") !== "false",
20
20
  publicReads: (process.env.FEATURE_PUBLIC_READS ?? "true") !== "false",
21
21
  emailVerification: (process.env.FEATURE_EMAIL_VERIFICATION ?? "false") === "true",
22
- mfa: (process.env.FEATURE_MFA ?? "false") === "true"
22
+ mfa: (process.env.FEATURE_MFA ?? "false") === "true",
23
+ registration: (process.env.FEATURE_REGISTRATION ?? "true") !== "false"
23
24
  };
24
25
  }
25
26
  var featureFlags = readFeatureFlags();
@@ -180,6 +180,16 @@ class HttpKernel {
180
180
  wrapWeb(handler) {
181
181
  return withErrorHandling(this.wrap("web", handler));
182
182
  }
183
+ wrapWebGuest(handler, home = "/organizations") {
184
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
185
+ return this.wrapWeb(async (request) => {
186
+ const user = await auth.resolve(request);
187
+ if (user) {
188
+ return Response.redirect(home, 302);
189
+ }
190
+ return handler(request);
191
+ });
192
+ }
183
193
  wrapWebPublicRead(handler) {
184
194
  if (isPublicReadsEnabled()) {
185
195
  return this.wrapWeb(handler);
package/dist/index.js CHANGED
@@ -187,6 +187,16 @@ class HttpKernel {
187
187
  wrapWeb(handler) {
188
188
  return withErrorHandling(this.wrap("web", handler));
189
189
  }
190
+ wrapWebGuest(handler, home = "/organizations") {
191
+ const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
192
+ return this.wrapWeb(async (request) => {
193
+ const user = await auth.resolve(request);
194
+ if (user) {
195
+ return Response.redirect(home, 302);
196
+ }
197
+ return handler(request);
198
+ });
199
+ }
190
200
  wrapWebPublicRead(handler) {
191
201
  if (isPublicReadsEnabled()) {
192
202
  return this.wrapWeb(handler);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getstrata/bootstrap",
3
- "version": "0.2.57",
3
+ "version": "0.2.59",
4
4
  "description": "Strata application bootstrap — HttpKernel, DI, web session helpers",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -180,7 +180,7 @@
180
180
  "access": "public"
181
181
  },
182
182
  "peerDependencies": {
183
- "@getstrata/core": "^0.5.70",
183
+ "@getstrata/core": "^0.5.72",
184
184
  "typescript": "^5.9.0"
185
185
  }
186
186
  }