@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.
- package/lib/{apiDefinition.types-DnUkFmfT.d.mts → apiDefinition.types-Br0fDuBQ.d.mts} +42 -8
- package/lib/{apiDefinition.types-DnUkFmfT.d.ts → apiDefinition.types-Br0fDuBQ.d.ts} +42 -8
- package/lib/cache/index.d.mts +3 -133
- package/lib/cache/index.d.ts +3 -133
- package/lib/http/index.d.mts +88 -3
- package/lib/http/index.d.ts +88 -3
- package/lib/http/index.js +168 -25
- package/lib/http/index.js.map +1 -1
- package/lib/http/index.mjs +166 -25
- package/lib/http/index.mjs.map +1 -1
- package/lib/mappers/index.js.map +1 -1
- package/lib/mappers/index.mjs.map +1 -1
- package/lib/persistence/index.d.mts +271 -1
- package/lib/persistence/index.d.ts +271 -1
- package/lib/persistence/index.js +463 -0
- package/lib/persistence/index.js.map +1 -1
- package/lib/persistence/index.mjs +427 -0
- package/lib/persistence/index.mjs.map +1 -1
- package/lib/ttlCache.interface-DClm-lSa.d.mts +133 -0
- package/lib/ttlCache.interface-DClm-lSa.d.ts +133 -0
- package/lib/ws/index.d.mts +1 -1
- package/lib/ws/index.d.ts +1 -1
- package/package.json +9 -9
package/lib/http/index.d.ts
CHANGED
|
@@ -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-
|
|
7
|
-
export { x as
|
|
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
|
-
|
|
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_,
|