@forklaunch/core 0.19.5 → 1.0.2

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.
@@ -3,8 +3,8 @@ export { ParsedQs } from 'qs';
3
3
  import { Prettify, SanitizePathSlashes, PrettyCamelCase, TypeSafeFunction, UnionToIntersection, EmptyObject } from '@forklaunch/common';
4
4
  import { AnySchemaValidator } from '@forklaunch/validator';
5
5
  import { ServerOptions, IncomingMessage, ServerResponse } from 'node:http';
6
- import { M as Method, P as PathParamHttpContractDetails, H as HttpContractDetails, E as ExpressLikeRouterOptions, a as SessionObject, b as ParamsObject, R as ResponsesObject, B as Body, Q as QueryObject, c as HeadersObject, V as VersionSchema, d as SchemaAuthMethods, e as ExpressLikeSchemaHandler, f as ResolvedSessionObject, C as ContractDetails, g as PathMatch, L as LiveTypeFunction, h as LiveSdkFunction, A as AuthMethodsBase, O as OpenTelemetryCollector, i as MetricsDefinition, j as ExpressLikeApplicationOptions, k as ParamsDictionary, l as VersionedRequests, m as AuthMethods, D as DecodeResource, n as BasicAuthMethods, F as ForklaunchRequest, o as MiddlewareContractDetails, p as ExpressLikeSchemaAuthMapper, q as ForklaunchNextFunction, r as ForklaunchResponse, s as ForklaunchResHeaders, t as ForklaunchStatusResponse, u as ForklaunchSendableData, T as TelemetryOptions, v as LoggerMeta, w as LogFn } from '../apiDefinition.types-DnUkFmfT.js';
7
- export { x as BodyObject, y as DefaultSubscriptionData, z as DocsConfiguration, G as ErrorContainer, I as ExpressLikeAuthMapper, J as ExpressLikeGlobalAuthOptions, K as ExpressLikeHandler, N as ExpressLikeSchemaGlobalAuthOptions, U as ExtractBody, W as ExtractContentType, X as ExtractResponseBody, Y as ExtractedParamsObject, Z as FileBody, _ as ForklaunchBaseRequest, $ as ForklaunchResErrors, a0 as HmacMethods, a1 as HttpMethod, a2 as JsonBody, a3 as JwtAuthMethods, a4 as LiveTypeFunctionRequestInit, a5 as MapParamsSchema, a6 as MapReqBodySchema, a7 as MapReqHeadersSchema, a8 as MapReqQuerySchema, a9 as MapResBodyMapSchema, aa as MapResHeadersSchema, ab as MapSchema, ac as MapSessionSchema, ad as MapVersionedReqsSchema, ae as MapVersionedRespsSchema, af as MetricType, ag as MultipartForm, ah as NumberOnlyObject, ai as PathParamMethod, aj as RawTypedResponseBody, ak as RequestContext, al as ResolvedForklaunchAuthRequest, am as ResolvedForklaunchRequest, an as ResolvedForklaunchResponse, ao as ResponseBody, ap as ResponseCompiledSchema, aq as ResponseShape, ar as ServerSentEventBody, S as StringOnlyObject, as as TextBody, at as TypedBody, au as TypedRequestBody, av as TypedResponseBody, aw as UnknownBody, ax as UnknownResponseBody, ay as UrlEncodedForm, az as VersionedResponses, aA as httpRequestsTotalCounter, aB as httpServerDurationHistogram } from '../apiDefinition.types-DnUkFmfT.js';
6
+ import { M as Method, P as PathParamHttpContractDetails, H as HttpContractDetails, E as ExpressLikeRouterOptions, a as SessionObject, b as ParamsObject, R as ResponsesObject, B as Body, Q as QueryObject, c as HeadersObject, V as VersionSchema, d as SchemaAuthMethods, e as ExpressLikeSchemaHandler, f as ResolvedSessionObject, C as ContractDetails, g as PathMatch, L as LiveTypeFunction, h as LiveSdkFunction, A as AuthMethodsBase, O as OpenTelemetryCollector, i as MetricsDefinition, j as ExpressLikeApplicationOptions, k as ParamsDictionary, l as VersionedRequests, m as AuthMethods, D as DecodeResource, n as BasicAuthMethods, F as ForklaunchRequest, o as MiddlewareContractDetails, p as ExpressLikeSchemaAuthMapper, q as ForklaunchNextFunction, r as ForklaunchResponse, s as ForklaunchResHeaders, t as ForklaunchStatusResponse, u as ForklaunchSendableData, T as TelemetryOptions, v as LoggerMeta, w as LogFn } from '../apiDefinition.types-Br0fDuBQ.js';
7
+ export { x as AccessLevel, y as BodyObject, z as DefaultSubscriptionData, G as DocsConfiguration, I as ErrorContainer, J as ExpressLikeAuthMapper, K as ExpressLikeGlobalAuthOptions, N as ExpressLikeHandler, U as ExpressLikeSchemaGlobalAuthOptions, W as ExtractBody, X as ExtractContentType, Y as ExtractResponseBody, Z as ExtractedParamsObject, _ as FileBody, $ as ForklaunchBaseRequest, a0 as ForklaunchResErrors, a1 as HmacMethods, a2 as HttpMethod, a3 as JsonBody, a4 as JwtAuthMethods, a5 as LiveTypeFunctionRequestInit, a6 as MapParamsSchema, a7 as MapReqBodySchema, a8 as MapReqHeadersSchema, a9 as MapReqQuerySchema, aa as MapResBodyMapSchema, ab as MapResHeadersSchema, ac as MapSchema, ad as MapSessionSchema, ae as MapVersionedReqsSchema, af as MapVersionedRespsSchema, ag as MetricType, ah as MultipartForm, ai as NumberOnlyObject, aj as PathParamMethod, ak as PermissionSet, al as RawTypedResponseBody, am as RequestContext, an as ResolvedForklaunchAuthRequest, ao as ResolvedForklaunchRequest, ap as ResolvedForklaunchResponse, aq as ResponseBody, ar as ResponseCompiledSchema, as as ResponseShape, at as RoleSet, au as ServerSentEventBody, S as StringOnlyObject, av as TextBody, aw as TypedBody, ax as TypedRequestBody, ay as TypedResponseBody, az as UnknownBody, aA as UnknownResponseBody, aB as UrlEncodedForm, aC as VersionedResponses, aD as httpRequestsTotalCounter, aE as httpServerDurationHistogram } from '../apiDefinition.types-Br0fDuBQ.js';
8
8
  import { JWTPayload, JWK } from 'jose';
9
9
  import { ZodSchemaValidator } from '@forklaunch/validator/zod';
10
10
  import { FastMCP } from 'fastmcp';
@@ -13,6 +13,7 @@ import { OpenAPIObject } from 'openapi3-ts/oas31';
13
13
  import pino, { LevelWithSilentOrString, LevelWithSilent } from 'pino';
14
14
  export { LevelWithSilent, LevelWithSilentOrString, Logger } from 'pino';
15
15
  import { AnyValueMap } from '@opentelemetry/api-logs';
16
+ import { T as TtlCache } from '../ttlCache.interface-DClm-lSa.js';
16
17
  export { ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_HTTP_ROUTE, ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
17
18
  import '@opentelemetry/api';
18
19
  import 'stream';
@@ -1125,6 +1126,25 @@ declare function discriminateResponseBodies<SV extends AnySchemaValidator>(schem
1125
1126
  schema: SV["_ValidSchemaObject"];
1126
1127
  }>;
1127
1128
 
1129
+ type AuditEntry = {
1130
+ timestamp: string;
1131
+ userId: string | null;
1132
+ tenantId: string | null;
1133
+ route: string;
1134
+ method: string;
1135
+ bodyHash: string;
1136
+ status: number;
1137
+ duration: number;
1138
+ redactedFields: string[];
1139
+ eventType: 'http' | 'ws' | 'auth_failure' | 'rate_limit' | 'rbac_deny' | 'super_admin_bypass';
1140
+ };
1141
+ declare class AuditLogger {
1142
+ #private;
1143
+ constructor(otel: OpenTelemetryCollector<MetricsDefinition>);
1144
+ append(entry: AuditEntry): void;
1145
+ static hashBody(body: string | Buffer | undefined | null): string;
1146
+ }
1147
+
1128
1148
  declare const ATTR_API_NAME = "api.name";
1129
1149
  declare const ATTR_CORRELATION_ID = "correlation.id";
1130
1150
  declare const ATTR_APPLICATION_ID = "application_id";
@@ -1158,4 +1178,69 @@ declare function logger(level: LevelWithSilentOrString, meta?: AnyValueMap): Pin
1158
1178
 
1159
1179
  declare function recordMetric<SV extends AnySchemaValidator, P extends ParamsDictionary, ReqBody extends Record<string, unknown>, ReqQuery extends ParsedQs, ResBodyMap extends Record<string, unknown>, ReqHeaders extends Record<string, string>, ResHeaders extends Record<string, unknown>, LocalsObj extends Record<string, unknown>, VersionedReqs extends VersionedRequests, SessionSchema extends Record<string, unknown>>(req: ForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders, Extract<keyof VersionedReqs, string>, SessionSchema>, res: ForklaunchResponse<unknown, ResBodyMap, ResHeaders, LocalsObj, Extract<keyof VersionedReqs, string>>): void;
1160
1180
 
1161
- export { ATTR_API_NAME, ATTR_APPLICATION_ID, ATTR_CORRELATION_ID, AuthMethods, AuthMethodsBase, BasicAuthMethods, Body, type ClusterConfig, type ConstrainedForklaunchRouter, ContractDetails, type ContractDetailsExpressLikeSchemaHandler, type ContractDetailsOrMiddlewareOrTypedHandler, DecodeResource, ExpressLikeApplicationOptions, type ExpressLikeRouter, ExpressLikeRouterOptions, ExpressLikeSchemaAuthMapper, ExpressLikeSchemaHandler, type ExpressLikeTypedHandler, type ExtractLiveTypeFn, type FetchFunction, ForklaunchExpressLikeApplication, ForklaunchExpressLikeRouter, ForklaunchNextFunction, ForklaunchRequest, ForklaunchResHeaders, ForklaunchResponse, type ForklaunchRoute, type ForklaunchRouter, ForklaunchSendableData, ForklaunchStatusResponse, HTTPStatuses, HeadersObject, HttpContractDetails, LiveSdkFunction, LiveTypeFunction, type LiveTypeRouteDefinition, LogFn, LoggerMeta, type MapHandlerToLiveSdk, type MapToFetch, type MapToSdk, Method, MetricsDefinition, MiddlewareContractDetails, type MiddlewareOrMiddlewareWithTypedHandler, type NestableRouterBasedHandler, OPENAPI_DEFAULT_VERSION, OpenTelemetryCollector, ParamsDictionary, ParamsObject, type PathBasedHandler, PathMatch, type PathOrMiddlewareBasedHandler, PathParamHttpContractDetails, PinoLogger, QueryObject, ResolvedSessionObject, ResponsesObject, type RouterMap, type RoutingStrategy, SchemaAuthMethods, type SdkHandler, type SdkHandlerObject, type SdkRouter, SessionObject, type StatusCode, TelemetryOptions, type ToFetchMap, type TypedHandler, type TypedMiddlewareDefinition, type TypedNestableMiddlewareDefinition, VersionSchema, VersionedRequests, createContext, createHmacToken, delete_, discriminateAuthMethod, discriminateBody, discriminateResponseBodies, enrichExpressLikeSend, evaluateTelemetryOptions, extractRouteHandlers, generateHmacAuthHeaders, generateMcpServer, generateOpenApiSpecs, get, getCachedJwks, getCodeForStatus, head, isClientError, isForklaunchRequest, isForklaunchRouter, isInformational, isPortBound, isRedirection, isServerError, isSuccessful, isValidStatusCode, logger, meta, metricsDefinitions, middleware, options, patch, post, put, recordMetric, trace, typedAuthHandler, typedHandler };
1181
+ /**
1182
+ * Configuration for rate limiting with separate read/write limits.
1183
+ */
1184
+ type RateLimitConfig = {
1185
+ /** Max requests per window for GET/HEAD/OPTIONS */
1186
+ read: number;
1187
+ /** Max requests per window for POST/PUT/PATCH/DELETE */
1188
+ write: number;
1189
+ /** Window duration in milliseconds */
1190
+ windowMs: number;
1191
+ };
1192
+ /**
1193
+ * Result of a rate limit check.
1194
+ */
1195
+ type RateLimitResult = {
1196
+ /** Whether the request is allowed */
1197
+ allowed: boolean;
1198
+ /** Number of remaining requests in the current window */
1199
+ remaining: number;
1200
+ /** Unix timestamp (ms) when the current window resets */
1201
+ resetAt: number;
1202
+ };
1203
+ /**
1204
+ * Rate limiter backed by a TtlCache.
1205
+ *
1206
+ * Uses a sliding-window counter pattern: each unique key maps to a
1207
+ * {@link RateLimitCounter} stored in the cache with a TTL equal to the
1208
+ * configured window duration.
1209
+ */
1210
+ declare class RateLimiter {
1211
+ private cache;
1212
+ constructor(cache: TtlCache);
1213
+ /**
1214
+ * Check whether the given key is within its rate limit.
1215
+ *
1216
+ * The counter is read from the cache, incremented, and written back.
1217
+ * If the cache is unreachable the limiter **fails open** (allows the
1218
+ * request) so that a cache outage does not take down the service.
1219
+ *
1220
+ * @param key - The rate limit key (see {@link RateLimiter.buildKey}).
1221
+ * @param limit - Maximum number of requests allowed in the window.
1222
+ * @param windowMs - Window duration in milliseconds.
1223
+ */
1224
+ check(key: string, limit: number, windowMs: number): Promise<RateLimitResult>;
1225
+ /**
1226
+ * Build a rate limit key from request context parts.
1227
+ *
1228
+ * Format: `ratelimit:{tenantId}:{route}:{userId}:{operationType}`
1229
+ *
1230
+ * When `tenantId` or `userId` is `null`, the placeholder `"anon"` is used.
1231
+ */
1232
+ static buildKey(parts: {
1233
+ tenantId: string | null;
1234
+ route: string;
1235
+ userId: string | null;
1236
+ operationType: 'read' | 'write';
1237
+ }): string;
1238
+ /**
1239
+ * Determine whether an HTTP method is a read or write operation.
1240
+ *
1241
+ * GET, HEAD, and OPTIONS are reads; everything else is a write.
1242
+ */
1243
+ static operationType(method: string): 'read' | 'write';
1244
+ }
1245
+
1246
+ export { ATTR_API_NAME, ATTR_APPLICATION_ID, ATTR_CORRELATION_ID, type AuditEntry, AuditLogger, AuthMethods, AuthMethodsBase, BasicAuthMethods, Body, type ClusterConfig, type ConstrainedForklaunchRouter, ContractDetails, type ContractDetailsExpressLikeSchemaHandler, type ContractDetailsOrMiddlewareOrTypedHandler, DecodeResource, ExpressLikeApplicationOptions, type ExpressLikeRouter, ExpressLikeRouterOptions, ExpressLikeSchemaAuthMapper, ExpressLikeSchemaHandler, type ExpressLikeTypedHandler, type ExtractLiveTypeFn, type FetchFunction, ForklaunchExpressLikeApplication, ForklaunchExpressLikeRouter, ForklaunchNextFunction, ForklaunchRequest, ForklaunchResHeaders, ForklaunchResponse, type ForklaunchRoute, type ForklaunchRouter, ForklaunchSendableData, ForklaunchStatusResponse, HTTPStatuses, HeadersObject, HttpContractDetails, LiveSdkFunction, LiveTypeFunction, type LiveTypeRouteDefinition, LogFn, LoggerMeta, type MapHandlerToLiveSdk, type MapToFetch, type MapToSdk, Method, MetricsDefinition, MiddlewareContractDetails, type MiddlewareOrMiddlewareWithTypedHandler, type NestableRouterBasedHandler, OPENAPI_DEFAULT_VERSION, OpenTelemetryCollector, ParamsDictionary, ParamsObject, type PathBasedHandler, PathMatch, type PathOrMiddlewareBasedHandler, PathParamHttpContractDetails, PinoLogger, QueryObject, type RateLimitConfig, type RateLimitResult, RateLimiter, ResolvedSessionObject, ResponsesObject, type RouterMap, type RoutingStrategy, SchemaAuthMethods, type SdkHandler, type SdkHandlerObject, type SdkRouter, SessionObject, type StatusCode, TelemetryOptions, type ToFetchMap, type TypedHandler, type TypedMiddlewareDefinition, type TypedNestableMiddlewareDefinition, VersionSchema, VersionedRequests, createContext, createHmacToken, delete_, discriminateAuthMethod, discriminateBody, discriminateResponseBodies, enrichExpressLikeSend, evaluateTelemetryOptions, extractRouteHandlers, generateHmacAuthHeaders, generateMcpServer, generateOpenApiSpecs, get, getCachedJwks, getCodeForStatus, head, isClientError, isForklaunchRequest, isForklaunchRouter, isInformational, isPortBound, isRedirection, isServerError, isSuccessful, isValidStatusCode, logger, meta, metricsDefinitions, middleware, options, patch, post, put, recordMetric, trace, typedAuthHandler, typedHandler };
package/lib/http/index.js CHANGED
@@ -37,12 +37,14 @@ __export(http_exports, {
37
37
  ATTR_HTTP_RESPONSE_STATUS_CODE: () => import_semantic_conventions.ATTR_HTTP_RESPONSE_STATUS_CODE,
38
38
  ATTR_HTTP_ROUTE: () => import_semantic_conventions.ATTR_HTTP_ROUTE,
39
39
  ATTR_SERVICE_NAME: () => import_semantic_conventions.ATTR_SERVICE_NAME,
40
+ AuditLogger: () => AuditLogger,
40
41
  ForklaunchExpressLikeApplication: () => ForklaunchExpressLikeApplication,
41
42
  ForklaunchExpressLikeRouter: () => ForklaunchExpressLikeRouter,
42
43
  HTTPStatuses: () => HTTPStatuses,
43
44
  OPENAPI_DEFAULT_VERSION: () => OPENAPI_DEFAULT_VERSION,
44
45
  OpenTelemetryCollector: () => OpenTelemetryCollector,
45
46
  PinoLogger: () => PinoLogger,
47
+ RateLimiter: () => RateLimiter,
46
48
  createContext: () => createContext,
47
49
  createHmacToken: () => createHmacToken,
48
50
  delete_: () => delete_,
@@ -318,11 +320,36 @@ function discriminateResponseBodies(schemaValidator, responses) {
318
320
  // src/http/router/routerSharedLogic.ts
319
321
  var import_common10 = require("@forklaunch/common");
320
322
 
323
+ // src/http/guards/hasPermissionChecks.ts
324
+ function hasPermissionChecks(maybePermissionedAuth) {
325
+ return typeof maybePermissionedAuth === "object" && maybePermissionedAuth !== null && ("allowedPermissions" in maybePermissionedAuth || "forbiddenPermissions" in maybePermissionedAuth);
326
+ }
327
+
328
+ // src/http/guards/hasRoleChecks.ts
329
+ function hasRoleChecks(maybeRoledAuth) {
330
+ if (typeof maybeRoledAuth !== "object" || maybeRoledAuth === null) {
331
+ return false;
332
+ }
333
+ const hasAllowedRoles = "allowedRoles" in maybeRoledAuth && maybeRoledAuth.allowedRoles instanceof Set && maybeRoledAuth.allowedRoles.size > 0;
334
+ const hasForbiddenRoles = "forbiddenRoles" in maybeRoledAuth && maybeRoledAuth.forbiddenRoles instanceof Set && maybeRoledAuth.forbiddenRoles.size > 0;
335
+ return hasAllowedRoles || hasForbiddenRoles;
336
+ }
337
+
338
+ // src/http/guards/hasScopeChecks.ts
339
+ function hasScopeChecks(maybePermissionedAuth) {
340
+ return typeof maybePermissionedAuth === "object" && maybePermissionedAuth !== null && "requiredScope" in maybePermissionedAuth && maybePermissionedAuth.requiredScope != null;
341
+ }
342
+
321
343
  // src/http/guards/hasVersionedSchema.ts
322
344
  function hasVersionedSchema(contractDetails) {
323
345
  return typeof contractDetails === "object" && contractDetails !== null && "versions" in contractDetails && contractDetails.versions !== null;
324
346
  }
325
347
 
348
+ // src/http/guards/isHmacMethod.ts
349
+ function isHmacMethod(maybeHmacMethod) {
350
+ return typeof maybeHmacMethod === "object" && maybeHmacMethod !== null && "hmac" in maybeHmacMethod && maybeHmacMethod.hmac != null;
351
+ }
352
+
326
353
  // src/http/guards/isPathParamContractDetails.ts
327
354
  function isPathParamHttpContractDetails(maybePathParamHttpContractDetails) {
328
355
  return maybePathParamHttpContractDetails != null && typeof maybePathParamHttpContractDetails === "object" && "name" in maybePathParamHttpContractDetails && "summary" in maybePathParamHttpContractDetails && maybePathParamHttpContractDetails.name != null && maybePathParamHttpContractDetails.summary != null && ("responses" in maybePathParamHttpContractDetails && maybePathParamHttpContractDetails.responses != null || "versions" in maybePathParamHttpContractDetails && typeof maybePathParamHttpContractDetails.versions === "object" && maybePathParamHttpContractDetails.versions != null && Object.values(maybePathParamHttpContractDetails.versions).every(
@@ -373,11 +400,6 @@ function isBasicAuthMethod(maybeBasicAuthMethod) {
373
400
  return typeof maybeBasicAuthMethod === "object" && maybeBasicAuthMethod !== null && "basic" in maybeBasicAuthMethod && maybeBasicAuthMethod.basic != null;
374
401
  }
375
402
 
376
- // src/http/guards/isHmacMethod.ts
377
- function isHmacMethod(maybeHmacMethod) {
378
- return typeof maybeHmacMethod === "object" && maybeHmacMethod !== null && "hmac" in maybeHmacMethod && maybeHmacMethod.hmac != null;
379
- }
380
-
381
403
  // src/http/guards/isJwtAuthMethod.ts
382
404
  function isJwtAuthMethod(maybeJwtAuthMethod) {
383
405
  return typeof maybeJwtAuthMethod === "object" && maybeJwtAuthMethod !== null && "jwt" in maybeJwtAuthMethod && maybeJwtAuthMethod.jwt != null;
@@ -507,26 +529,6 @@ function hasFeatureChecks(maybeFeatureAuth) {
507
529
  return typeof maybeFeatureAuth === "object" && maybeFeatureAuth !== null && "requiredFeatures" in maybeFeatureAuth && Array.isArray(maybeFeatureAuth.requiredFeatures) && maybeFeatureAuth.requiredFeatures.length > 0;
508
530
  }
509
531
 
510
- // src/http/guards/hasPermissionChecks.ts
511
- function hasPermissionChecks(maybePermissionedAuth) {
512
- return typeof maybePermissionedAuth === "object" && maybePermissionedAuth !== null && ("allowedPermissions" in maybePermissionedAuth || "forbiddenPermissions" in maybePermissionedAuth);
513
- }
514
-
515
- // src/http/guards/hasRoleChecks.ts
516
- function hasRoleChecks(maybeRoledAuth) {
517
- if (typeof maybeRoledAuth !== "object" || maybeRoledAuth === null) {
518
- return false;
519
- }
520
- const hasAllowedRoles = "allowedRoles" in maybeRoledAuth && maybeRoledAuth.allowedRoles instanceof Set && maybeRoledAuth.allowedRoles.size > 0;
521
- const hasForbiddenRoles = "forbiddenRoles" in maybeRoledAuth && maybeRoledAuth.forbiddenRoles instanceof Set && maybeRoledAuth.forbiddenRoles.size > 0;
522
- return hasAllowedRoles || hasForbiddenRoles;
523
- }
524
-
525
- // src/http/guards/hasScopeChecks.ts
526
- function hasScopeChecks(maybePermissionedAuth) {
527
- return typeof maybePermissionedAuth === "object" && maybePermissionedAuth !== null && "requiredScope" in maybePermissionedAuth && maybePermissionedAuth.requiredScope != null;
528
- }
529
-
530
532
  // src/http/guards/hasSubscriptionChecks.ts
531
533
  function hasSubscriptionChecks(maybeSubscriptionAuth) {
532
534
  return typeof maybeSubscriptionAuth === "object" && maybeSubscriptionAuth !== null && "requireActiveSubscription" in maybeSubscriptionAuth && maybeSubscriptionAuth.requireActiveSubscription === true;
@@ -1315,6 +1317,40 @@ function validateContractDetails(contractDetails, schemaValidator) {
1315
1317
  if (!isHttpContractDetails(contractDetails) && !isPathParamHttpContractDetails(contractDetails)) {
1316
1318
  throw new Error("Contract details are malformed for route definition");
1317
1319
  }
1320
+ const access = contractDetails["access"];
1321
+ const auth = contractDetails["auth"];
1322
+ if (access != null) {
1323
+ const validAccess = ["public", "authenticated", "protected", "internal"];
1324
+ if (!validAccess.includes(access)) {
1325
+ throw new Error(
1326
+ `Route '${contractDetails.name}': invalid access level '${access}'. Must be one of: ${validAccess.join(", ")}`
1327
+ );
1328
+ }
1329
+ if (access === "public" && auth != null) {
1330
+ throw new Error(
1331
+ `Route '${contractDetails.name}': access 'public' cannot have auth configured`
1332
+ );
1333
+ }
1334
+ if (access === "protected") {
1335
+ if (!auth) {
1336
+ throw new Error(
1337
+ `Route '${contractDetails.name}': access 'protected' requires auth with roles, permissions, or scope`
1338
+ );
1339
+ }
1340
+ if (!hasPermissionChecks(auth) && !hasRoleChecks(auth) && !hasScopeChecks(auth)) {
1341
+ throw new Error(
1342
+ `Route '${contractDetails.name}': access 'protected' requires at least one of allowedRoles, forbiddenRoles, allowedPermissions, forbiddenPermissions, or requiredScope`
1343
+ );
1344
+ }
1345
+ }
1346
+ if (access === "internal") {
1347
+ if (!auth || !isHmacMethod(auth)) {
1348
+ throw new Error(
1349
+ `Route '${contractDetails.name}': access 'internal' requires HMAC auth`
1350
+ );
1351
+ }
1352
+ }
1353
+ }
1318
1354
  if (contractDetails.versions) {
1319
1355
  const parserTypes = Object.values(contractDetails.versions).map(
1320
1356
  (version) => discriminateBody(schemaValidator, version.body)?.parserType
@@ -4222,6 +4258,44 @@ function generateOpenApiSpecs(schemaValidator, serverUrls, serverDescriptions, a
4222
4258
  );
4223
4259
  }
4224
4260
 
4261
+ // src/http/telemetry/auditLogger.ts
4262
+ var import_crypto3 = require("crypto");
4263
+ var AuditLogger = class {
4264
+ #otel;
4265
+ constructor(otel) {
4266
+ this.#otel = otel;
4267
+ }
4268
+ append(entry) {
4269
+ try {
4270
+ this.#otel.info(
4271
+ {
4272
+ "log.type": "audit",
4273
+ "audit.timestamp": entry.timestamp,
4274
+ "audit.userId": entry.userId ?? "",
4275
+ "audit.tenantId": entry.tenantId ?? "",
4276
+ "audit.route": entry.route,
4277
+ "audit.method": entry.method,
4278
+ "audit.bodyHash": entry.bodyHash,
4279
+ "audit.status": entry.status,
4280
+ "audit.duration": entry.duration,
4281
+ "audit.redactedFields": entry.redactedFields.join(","),
4282
+ "audit.eventType": entry.eventType,
4283
+ _meta: true
4284
+ },
4285
+ `audit:${entry.eventType} ${entry.method} ${entry.route} ${entry.status}`
4286
+ );
4287
+ } catch (error) {
4288
+ console.error("Failed to emit audit log via OTEL:", error);
4289
+ }
4290
+ }
4291
+ static hashBody(body) {
4292
+ if (body === void 0 || body === null || body === "") {
4293
+ return "";
4294
+ }
4295
+ return (0, import_crypto3.createHash)("sha256").update(body).digest("hex");
4296
+ }
4297
+ };
4298
+
4225
4299
  // src/http/telemetry/evaluateTelemetryOptions.ts
4226
4300
  function evaluateTelemetryOptions(telemetryOptions) {
4227
4301
  return {
@@ -4242,6 +4316,73 @@ function evaluateTelemetryOptions(telemetryOptions) {
4242
4316
  function metricsDefinitions(metrics2) {
4243
4317
  return metrics2;
4244
4318
  }
4319
+
4320
+ // src/http/rateLimit/rateLimiter.ts
4321
+ var READ_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
4322
+ var RateLimiter = class {
4323
+ constructor(cache) {
4324
+ this.cache = cache;
4325
+ }
4326
+ /**
4327
+ * Check whether the given key is within its rate limit.
4328
+ *
4329
+ * The counter is read from the cache, incremented, and written back.
4330
+ * If the cache is unreachable the limiter **fails open** (allows the
4331
+ * request) so that a cache outage does not take down the service.
4332
+ *
4333
+ * @param key - The rate limit key (see {@link RateLimiter.buildKey}).
4334
+ * @param limit - Maximum number of requests allowed in the window.
4335
+ * @param windowMs - Window duration in milliseconds.
4336
+ */
4337
+ async check(key, limit, windowMs) {
4338
+ try {
4339
+ const now = Date.now();
4340
+ let counter;
4341
+ try {
4342
+ const record = await this.cache.readRecord(key);
4343
+ counter = record.value;
4344
+ if (now >= counter.resetAt) {
4345
+ counter = { count: 0, resetAt: now + windowMs };
4346
+ }
4347
+ } catch {
4348
+ counter = { count: 0, resetAt: now + windowMs };
4349
+ }
4350
+ counter.count += 1;
4351
+ const ttl = Math.max(counter.resetAt - now, 1);
4352
+ await this.cache.putRecord({
4353
+ key,
4354
+ value: counter,
4355
+ ttlMilliseconds: ttl
4356
+ });
4357
+ const allowed = counter.count <= limit;
4358
+ const remaining = Math.max(limit - counter.count, 0);
4359
+ return { allowed, remaining, resetAt: counter.resetAt };
4360
+ } catch {
4361
+ console.warn(`[RateLimiter] Cache error for key "${key}". Failing open.`);
4362
+ return { allowed: true, remaining: limit, resetAt: 0 };
4363
+ }
4364
+ }
4365
+ /**
4366
+ * Build a rate limit key from request context parts.
4367
+ *
4368
+ * Format: `ratelimit:{tenantId}:{route}:{userId}:{operationType}`
4369
+ *
4370
+ * When `tenantId` or `userId` is `null`, the placeholder `"anon"` is used.
4371
+ */
4372
+ static buildKey(parts) {
4373
+ const tenant = parts.tenantId ?? "anon";
4374
+ const user = parts.userId ?? "anon";
4375
+ return `ratelimit:${tenant}:${parts.route}:${user}:${parts.operationType}`;
4376
+ }
4377
+ /**
4378
+ * Determine whether an HTTP method is a read or write operation.
4379
+ *
4380
+ * GET, HEAD, and OPTIONS are reads; everything else is a write.
4381
+ */
4382
+ static operationType(method) {
4383
+ return READ_METHODS.has(method.toUpperCase()) ? "read" : "write";
4384
+ }
4385
+ };
4245
4386
  // Annotate the CommonJS export names for ESM import in node:
4246
4387
  0 && (module.exports = {
4247
4388
  ATTR_API_NAME,
@@ -4251,12 +4392,14 @@ function metricsDefinitions(metrics2) {
4251
4392
  ATTR_HTTP_RESPONSE_STATUS_CODE,
4252
4393
  ATTR_HTTP_ROUTE,
4253
4394
  ATTR_SERVICE_NAME,
4395
+ AuditLogger,
4254
4396
  ForklaunchExpressLikeApplication,
4255
4397
  ForklaunchExpressLikeRouter,
4256
4398
  HTTPStatuses,
4257
4399
  OPENAPI_DEFAULT_VERSION,
4258
4400
  OpenTelemetryCollector,
4259
4401
  PinoLogger,
4402
+ RateLimiter,
4260
4403
  createContext,
4261
4404
  createHmacToken,
4262
4405
  delete_,