@forklaunch/core 1.3.6 → 1.3.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.
@@ -28,6 +28,31 @@ import {
28
28
  toRecord
29
29
  } from "@forklaunch/common";
30
30
 
31
+ // src/http/guards/hasPermissionChecks.ts
32
+ function hasPermissionChecks(maybePermissionedAuth) {
33
+ if (typeof maybePermissionedAuth !== "object" || maybePermissionedAuth === null) {
34
+ return false;
35
+ }
36
+ const hasAllowedPermissions = "allowedPermissions" in maybePermissionedAuth && maybePermissionedAuth.allowedPermissions instanceof Set && maybePermissionedAuth.allowedPermissions.size > 0;
37
+ const hasForbiddenPermissions = "forbiddenPermissions" in maybePermissionedAuth && maybePermissionedAuth.forbiddenPermissions instanceof Set && maybePermissionedAuth.forbiddenPermissions.size > 0;
38
+ return hasAllowedPermissions || hasForbiddenPermissions;
39
+ }
40
+
41
+ // src/http/guards/hasRoleChecks.ts
42
+ function hasRoleChecks(maybeRoledAuth) {
43
+ if (typeof maybeRoledAuth !== "object" || maybeRoledAuth === null) {
44
+ return false;
45
+ }
46
+ const hasAllowedRoles = "allowedRoles" in maybeRoledAuth && maybeRoledAuth.allowedRoles instanceof Set && maybeRoledAuth.allowedRoles.size > 0;
47
+ const hasForbiddenRoles = "forbiddenRoles" in maybeRoledAuth && maybeRoledAuth.forbiddenRoles instanceof Set && maybeRoledAuth.forbiddenRoles.size > 0;
48
+ return hasAllowedRoles || hasForbiddenRoles;
49
+ }
50
+
51
+ // src/http/guards/hasScopeChecks.ts
52
+ function hasScopeChecks(maybePermissionedAuth) {
53
+ return typeof maybePermissionedAuth === "object" && maybePermissionedAuth !== null && "requiredScope" in maybePermissionedAuth && maybePermissionedAuth.requiredScope != null;
54
+ }
55
+
31
56
  // src/http/guards/isForklaunchRouter.ts
32
57
  function isForklaunchRouter(maybeForklaunchRouter) {
33
58
  return maybeForklaunchRouter != null && typeof maybeForklaunchRouter === "object" && "basePath" in maybeForklaunchRouter && "routes" in maybeForklaunchRouter && Array.isArray(maybeForklaunchRouter.routes);
@@ -242,31 +267,6 @@ function discriminateResponseBodies(schemaValidator, responses) {
242
267
  // src/http/router/routerSharedLogic.ts
243
268
  import { isRecord as isRecord2 } from "@forklaunch/common";
244
269
 
245
- // src/http/guards/hasPermissionChecks.ts
246
- function hasPermissionChecks(maybePermissionedAuth) {
247
- if (typeof maybePermissionedAuth !== "object" || maybePermissionedAuth === null) {
248
- return false;
249
- }
250
- const hasAllowedPermissions = "allowedPermissions" in maybePermissionedAuth && maybePermissionedAuth.allowedPermissions instanceof Set && maybePermissionedAuth.allowedPermissions.size > 0;
251
- const hasForbiddenPermissions = "forbiddenPermissions" in maybePermissionedAuth && maybePermissionedAuth.forbiddenPermissions instanceof Set && maybePermissionedAuth.forbiddenPermissions.size > 0;
252
- return hasAllowedPermissions || hasForbiddenPermissions;
253
- }
254
-
255
- // src/http/guards/hasRoleChecks.ts
256
- function hasRoleChecks(maybeRoledAuth) {
257
- if (typeof maybeRoledAuth !== "object" || maybeRoledAuth === null) {
258
- return false;
259
- }
260
- const hasAllowedRoles = "allowedRoles" in maybeRoledAuth && maybeRoledAuth.allowedRoles instanceof Set && maybeRoledAuth.allowedRoles.size > 0;
261
- const hasForbiddenRoles = "forbiddenRoles" in maybeRoledAuth && maybeRoledAuth.forbiddenRoles instanceof Set && maybeRoledAuth.forbiddenRoles.size > 0;
262
- return hasAllowedRoles || hasForbiddenRoles;
263
- }
264
-
265
- // src/http/guards/hasScopeChecks.ts
266
- function hasScopeChecks(maybePermissionedAuth) {
267
- return typeof maybePermissionedAuth === "object" && maybePermissionedAuth !== null && "requiredScope" in maybePermissionedAuth && maybePermissionedAuth.requiredScope != null;
268
- }
269
-
270
270
  // src/http/guards/hasVersionedSchema.ts
271
271
  function hasVersionedSchema(contractDetails) {
272
272
  return typeof contractDetails === "object" && contractDetails !== null && "versions" in contractDetails && contractDetails.versions !== null;
@@ -1890,6 +1890,45 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
1890
1890
  );
1891
1891
  });
1892
1892
  }
1893
+ /**
1894
+ * Validates that all protected routes have the required surfacing functions
1895
+ * available (from the route, router, or application level).
1896
+ * Call this at listen() time when the full option chain is assembled.
1897
+ */
1898
+ validateSurfacingFunctions(router = this) {
1899
+ const globalAuth = router.routerOptions?.auth && typeof router.routerOptions.auth === "object" ? router.routerOptions.auth : void 0;
1900
+ for (const route of router.routes) {
1901
+ const auth = route.contractDetails.auth;
1902
+ const access = route.contractDetails.access;
1903
+ if (!auth || access !== "protected") continue;
1904
+ const routeAuth = auth;
1905
+ const routeName = route.contractDetails.name;
1906
+ if (hasPermissionChecks(auth)) {
1907
+ if (!routeAuth["surfacePermissions"] && !globalAuth?.surfacePermissions) {
1908
+ throw new Error(
1909
+ `Route '${routeName}': declares allowedPermissions or forbiddenPermissions but no surfacePermissions function was provided on the route, router, or application`
1910
+ );
1911
+ }
1912
+ }
1913
+ if (hasRoleChecks(auth)) {
1914
+ if (!routeAuth["surfaceRoles"] && !globalAuth?.surfaceRoles) {
1915
+ throw new Error(
1916
+ `Route '${routeName}': declares allowedRoles or forbiddenRoles but no surfaceRoles function was provided on the route, router, or application`
1917
+ );
1918
+ }
1919
+ }
1920
+ if (hasScopeChecks(auth)) {
1921
+ if (!routeAuth["surfaceScopes"] && !globalAuth?.surfaceScopes) {
1922
+ throw new Error(
1923
+ `Route '${routeName}': declares requiredScope but no surfaceScopes function was provided on the route, router, or application`
1924
+ );
1925
+ }
1926
+ }
1927
+ }
1928
+ for (const subRouter of router.routers) {
1929
+ this.validateSurfacingFunctions(subRouter);
1930
+ }
1931
+ }
1893
1932
  use = (pathOrContractDetailsOrMiddlewareOrTypedHandler, contractDetailsOrMiddlewareOrTypedHandler, ...middlewareOrMiddlewareWithTypedHandler) => {
1894
1933
  [
1895
1934
  pathOrContractDetailsOrMiddlewareOrTypedHandler,
@@ -2197,6 +2236,13 @@ var ForklaunchExpressLikeApplication = class extends ForklaunchExpressLikeRouter
2197
2236
  this.appOptions = appOptions;
2198
2237
  this.internal.use(cors(this.appOptions?.cors ?? {}));
2199
2238
  }
2239
+ /**
2240
+ * Validates all registered routes across the app and all mounted routers.
2241
+ * Call this at the start of listen() to fail fast at startup.
2242
+ */
2243
+ validateAllRoutes() {
2244
+ this.validateSurfacingFunctions();
2245
+ }
2200
2246
  };
2201
2247
 
2202
2248
  // src/http/cluster/isPortBound.ts
@@ -4055,7 +4101,7 @@ function generateOperationObject(schemaValidator, path, method, controllerName,
4055
4101
  operationObject.security = [
4056
4102
  {
4057
4103
  basic: Array.from(
4058
- "allowedPermissions" in auth ? auth.allowedPermissions?.values() || [] : []
4104
+ "allowedPermissions" in auth && auth.allowedPermissions instanceof Set ? auth.allowedPermissions.values() : []
4059
4105
  )
4060
4106
  }
4061
4107
  ];
@@ -4067,7 +4113,7 @@ function generateOperationObject(schemaValidator, path, method, controllerName,
4067
4113
  operationObject.security = [
4068
4114
  {
4069
4115
  [auth.headerName !== "Authorization" ? "bearer" : "apiKey"]: Array.from(
4070
- "allowedPermissions" in auth ? auth.allowedPermissions?.values() || [] : []
4116
+ "allowedPermissions" in auth && auth.allowedPermissions instanceof Set ? auth.allowedPermissions.values() : []
4071
4117
  )
4072
4118
  }
4073
4119
  ];