@forklaunch/core 0.14.8 → 0.14.11-beta.1

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.
@@ -81,11 +81,15 @@ function isTypedHandler(maybeTypedHandler) {
81
81
 
82
82
  // src/http/middleware/request/auth.middleware.ts
83
83
  import { isNever } from "@forklaunch/common";
84
+ import {
85
+ prettyPrintParseErrors
86
+ } from "@forklaunch/validator";
84
87
 
85
88
  // src/http/discriminateAuthMethod.ts
86
89
  import { jwtVerify } from "jose";
87
90
 
88
91
  // src/http/createHmacToken.ts
92
+ import { safeStringify } from "@forklaunch/common";
89
93
  import { createHmac } from "crypto";
90
94
  function createHmacToken({
91
95
  method,
@@ -96,11 +100,14 @@ function createHmacToken({
96
100
  secretKey
97
101
  }) {
98
102
  const hmac = createHmac("sha256", secretKey);
99
- hmac.update(`${method}
103
+ const bodyString = body ? `${safeStringify(body)}
104
+ ` : void 0;
105
+ hmac.update(
106
+ `${method}
100
107
  ${path}
101
- ${body}
102
- ${timestamp}
103
- ${nonce}`);
108
+ ${bodyString}${timestamp.toISOString()}
109
+ ${nonce}`
110
+ );
104
111
  return hmac.digest("base64");
105
112
  }
106
113
 
@@ -120,6 +127,12 @@ function isJwtAuthMethod(maybeJwtAuthMethod) {
120
127
  }
121
128
 
122
129
  // src/http/discriminateAuthMethod.ts
130
+ var DEFAULT_TTL = 60 * 1e3 * 5;
131
+ var memoizedJwks = {
132
+ value: null,
133
+ lastUpdated: null,
134
+ ttl: DEFAULT_TTL
135
+ };
123
136
  async function discriminateAuthMethod(auth) {
124
137
  let authMethod;
125
138
  if (isBasicAuthMethod(auth)) {
@@ -144,8 +157,17 @@ async function discriminateAuthMethod(auth) {
144
157
  } else {
145
158
  let jwks;
146
159
  if ("jwksPublicKeyUrl" in jwt) {
147
- const jwksResponse = await fetch(jwt.jwksPublicKeyUrl);
148
- jwks = (await jwksResponse.json()).keys;
160
+ if (memoizedJwks.value && memoizedJwks.lastUpdated && Date.now() - memoizedJwks.lastUpdated.getTime() < memoizedJwks.ttl) {
161
+ jwks = memoizedJwks.value;
162
+ } else {
163
+ const jwksResponse = await fetch(jwt.jwksPublicKeyUrl);
164
+ jwks = (await jwksResponse.json()).keys;
165
+ memoizedJwks.value = jwks;
166
+ memoizedJwks.lastUpdated = /* @__PURE__ */ new Date();
167
+ memoizedJwks.ttl = parseInt(
168
+ jwksResponse.headers.get("cache-control")?.split("=")[1] ?? `${DEFAULT_TTL / 1e3}`
169
+ ) * 1e3;
170
+ }
149
171
  } else if ("jwksPublicKey" in jwt) {
150
172
  jwks = [jwt.jwksPublicKey];
151
173
  }
@@ -155,6 +177,9 @@ async function discriminateAuthMethod(auth) {
155
177
  const { payload } = await jwtVerify(token, key);
156
178
  return payload;
157
179
  } catch {
180
+ memoizedJwks.value = null;
181
+ memoizedJwks.lastUpdated = null;
182
+ memoizedJwks.ttl = DEFAULT_TTL;
158
183
  continue;
159
184
  }
160
185
  }
@@ -172,7 +197,15 @@ async function discriminateAuthMethod(auth) {
172
197
  type: "hmac",
173
198
  auth: {
174
199
  secretKeys: auth.hmac.secretKeys,
175
- verificationFunction: async (method, path, body, timestamp, nonce, signature, secretKey) => {
200
+ verificationFunction: async ({
201
+ method,
202
+ path,
203
+ body,
204
+ timestamp,
205
+ nonce,
206
+ signature,
207
+ secretKey
208
+ }) => {
176
209
  return createHmacToken({
177
210
  method,
178
211
  path,
@@ -250,7 +283,7 @@ function parseHmacTokenPart(part, expectedKey) {
250
283
  if (key !== expectedKey || rest.length === 0) return void 0;
251
284
  return rest.join("=");
252
285
  }
253
- async function checkAuthorizationToken(authorizationMethod, globalOptions, authorizationToken, req) {
286
+ async function checkAuthorizationToken(req, authorizationMethod, authorizationToken, globalOptions) {
254
287
  if (authorizationMethod == null) {
255
288
  return void 0;
256
289
  }
@@ -265,7 +298,7 @@ async function checkAuthorizationToken(authorizationMethod, globalOptions, autho
265
298
  if (!tokenParts.length || !tokenPrefix) {
266
299
  return invalidAuthorizationTokenFormat;
267
300
  }
268
- let resourceId;
301
+ let sessionPayload;
269
302
  const { type, auth } = await discriminateAuthMethod(
270
303
  collapsedAuthorizationMethod
271
304
  );
@@ -285,19 +318,19 @@ async function checkAuthorizationToken(authorizationMethod, globalOptions, autho
285
318
  if (!parsedKeyId || !parsedTimestamp || !parsedNonce || !parsedSignature) {
286
319
  return invalidAuthorizationTokenFormat;
287
320
  }
288
- const verificationResult = await auth.verificationFunction(
289
- req?.method ?? "",
290
- req?.path ?? "",
291
- JSON.stringify(req?.body ?? ""),
292
- parsedTimestamp,
293
- parsedNonce,
294
- parsedSignature,
295
- collapsedAuthorizationMethod.hmac.secretKeys[parsedKeyId]
296
- );
321
+ const verificationResult = await auth.verificationFunction({
322
+ method: req?.method ?? "",
323
+ path: req?.path ?? "",
324
+ body: req?.body,
325
+ timestamp: new Date(parsedTimestamp),
326
+ nonce: parsedNonce,
327
+ signature: parsedSignature,
328
+ secretKey: collapsedAuthorizationMethod.hmac.secretKeys[parsedKeyId]
329
+ });
297
330
  if (!verificationResult) {
298
331
  return invalidAuthorizationSignature;
299
332
  }
300
- resourceId = null;
333
+ sessionPayload = null;
301
334
  break;
302
335
  }
303
336
  case "jwt": {
@@ -310,7 +343,7 @@ async function checkAuthorizationToken(authorizationMethod, globalOptions, autho
310
343
  if (!decodedJwt) {
311
344
  return invalidAuthorizationToken;
312
345
  }
313
- resourceId = decodedJwt;
346
+ sessionPayload = decodedJwt;
314
347
  } catch (error) {
315
348
  req?.openTelemetryCollector.error(error);
316
349
  return invalidAuthorizationToken;
@@ -323,7 +356,7 @@ async function checkAuthorizationToken(authorizationMethod, globalOptions, autho
323
356
  return invalidAuthorizationTokenFormat;
324
357
  }
325
358
  if (auth.decodeResource) {
326
- resourceId = await auth.decodeResource(token);
359
+ sessionPayload = await auth.decodeResource(token);
327
360
  } else {
328
361
  const [username, password] = Buffer.from(token, "base64").toString("utf-8").split(":");
329
362
  if (!username || !password) {
@@ -332,7 +365,7 @@ async function checkAuthorizationToken(authorizationMethod, globalOptions, autho
332
365
  if (!auth.login(username, password)) {
333
366
  return invalidAuthorizationLogin;
334
367
  }
335
- resourceId = {
368
+ sessionPayload = {
336
369
  sub: username
337
370
  };
338
371
  }
@@ -342,16 +375,29 @@ async function checkAuthorizationToken(authorizationMethod, globalOptions, autho
342
375
  isNever(type);
343
376
  return [401, "Invalid Authorization method."];
344
377
  }
345
- if (isHmacMethod(collapsedAuthorizationMethod) && resourceId == null) {
378
+ if (isHmacMethod(collapsedAuthorizationMethod) && sessionPayload == null) {
346
379
  return;
347
380
  }
348
- if (resourceId == null) {
381
+ if (sessionPayload == null) {
349
382
  return invalidAuthorizationToken;
350
383
  }
384
+ req.session = sessionPayload;
385
+ if (collapsedAuthorizationMethod.sessionSchema) {
386
+ const parsedSession = req.schemaValidator.parse(
387
+ collapsedAuthorizationMethod.sessionSchema,
388
+ sessionPayload
389
+ );
390
+ if (!parsedSession.ok) {
391
+ return [
392
+ 400,
393
+ `Invalid session: ${prettyPrintParseErrors(parsedSession.errors, "Session")}`
394
+ ];
395
+ }
396
+ }
351
397
  if (hasScopeChecks(collapsedAuthorizationMethod)) {
352
398
  if (collapsedAuthorizationMethod.surfaceScopes) {
353
399
  const resourceScopes = await collapsedAuthorizationMethod.surfaceScopes(
354
- resourceId,
400
+ sessionPayload,
355
401
  req
356
402
  );
357
403
  if (collapsedAuthorizationMethod.scopeHeirarchy) {
@@ -370,7 +416,7 @@ async function checkAuthorizationToken(authorizationMethod, globalOptions, autho
370
416
  return [500, "No permission surfacing function provided."];
371
417
  }
372
418
  const resourcePermissions = await collapsedAuthorizationMethod.surfacePermissions(
373
- resourceId,
419
+ sessionPayload,
374
420
  req
375
421
  );
376
422
  if ("allowedPermissions" in collapsedAuthorizationMethod && collapsedAuthorizationMethod.allowedPermissions) {
@@ -392,7 +438,7 @@ async function checkAuthorizationToken(authorizationMethod, globalOptions, autho
392
438
  return [500, "No role surfacing function provided."];
393
439
  }
394
440
  const resourceRoles = await collapsedAuthorizationMethod.surfaceRoles(
395
- resourceId,
441
+ sessionPayload,
396
442
  req
397
443
  );
398
444
  if ("allowedRoles" in collapsedAuthorizationMethod && collapsedAuthorizationMethod.allowedRoles) {
@@ -412,10 +458,10 @@ async function checkAuthorizationToken(authorizationMethod, globalOptions, autho
412
458
  async function parseRequestAuth(req, res, next) {
413
459
  const auth = req.contractDetails.auth;
414
460
  const [error, message] = await checkAuthorizationToken(
461
+ req,
415
462
  auth,
416
- req._globalOptions?.()?.auth,
417
463
  req.headers[auth?.headerName ?? "Authorization"] || req.headers[auth?.headerName ?? "authorization"],
418
- req
464
+ req._globalOptions?.()?.auth
419
465
  ) ?? [];
420
466
  if (error != null) {
421
467
  res.type("text/plain");
@@ -794,7 +840,7 @@ function enrichDetails(path, contractDetails, requestSchema, responseSchemas, op
794
840
  // src/http/middleware/request/parse.middleware.ts
795
841
  import { isRecord } from "@forklaunch/common";
796
842
  import {
797
- prettyPrintParseErrors
843
+ prettyPrintParseErrors as prettyPrintParseErrors2
798
844
  } from "@forklaunch/validator";
799
845
 
800
846
  // src/http/guards/hasSend.ts
@@ -833,7 +879,7 @@ function parse(req, res, next) {
833
879
  req.version = version;
834
880
  res.version = req.version;
835
881
  } else {
836
- runningParseErrors += prettyPrintParseErrors(
882
+ runningParseErrors += prettyPrintParseErrors2(
837
883
  parsingResult.errors,
838
884
  `Version ${version} request`
839
885
  );
@@ -891,7 +937,7 @@ function parse(req, res, next) {
891
937
  res.status(400);
892
938
  if (hasSend(res)) {
893
939
  res.send(
894
- `${collectedParseErrors ?? prettyPrintParseErrors(parsedRequest.errors, "Request")}
940
+ `${collectedParseErrors ?? prettyPrintParseErrors2(parsedRequest.errors, "Request")}
895
941
 
896
942
  Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
897
943
  );
@@ -901,7 +947,7 @@ Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
901
947
  return;
902
948
  case "warning":
903
949
  req.openTelemetryCollector.warn(
904
- collectedParseErrors ?? prettyPrintParseErrors(parsedRequest.errors, "Request")
950
+ collectedParseErrors ?? prettyPrintParseErrors2(parsedRequest.errors, "Request")
905
951
  );
906
952
  break;
907
953
  case "none":
@@ -2919,7 +2965,7 @@ var getCodeForStatus = (status) => {
2919
2965
  var httpStatusCodes_default = HTTPStatuses;
2920
2966
 
2921
2967
  // src/http/mcpGenerator/mcpGenerator.ts
2922
- import { isNever as isNever3, isRecord as isRecord3, safeStringify } from "@forklaunch/common";
2968
+ import { isNever as isNever3, isRecord as isRecord3, safeStringify as safeStringify2 } from "@forklaunch/common";
2923
2969
  import { FastMCP } from "@forklaunch/fastmcp-fork";
2924
2970
  import { string, ZodSchemaValidator } from "@forklaunch/validator/zod";
2925
2971
 
@@ -3056,7 +3102,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
3056
3102
  if (discriminatedBody) {
3057
3103
  switch (discriminatedBody.parserType) {
3058
3104
  case "json": {
3059
- parsedBody = safeStringify(body);
3105
+ parsedBody = safeStringify2(body);
3060
3106
  break;
3061
3107
  }
3062
3108
  case "text": {
@@ -3088,7 +3134,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
3088
3134
  parsedBody = new URLSearchParams(
3089
3135
  Object.entries(body).map(([key, value]) => [
3090
3136
  key,
3091
- safeStringify(value)
3137
+ safeStringify2(value)
3092
3138
  ])
3093
3139
  );
3094
3140
  } else {
@@ -3098,7 +3144,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
3098
3144
  }
3099
3145
  default: {
3100
3146
  isNever3(discriminatedBody.parserType);
3101
- parsedBody = safeStringify(body);
3147
+ parsedBody = safeStringify2(body);
3102
3148
  break;
3103
3149
  }
3104
3150
  }
@@ -3107,7 +3153,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
3107
3153
  const queryString = new URLSearchParams(
3108
3154
  Object.entries(query).map(([key, value]) => [
3109
3155
  key,
3110
- safeStringify(value)
3156
+ safeStringify2(value)
3111
3157
  ])
3112
3158
  ).toString();
3113
3159
  url += queryString ? `?${queryString}` : "";
@@ -3140,7 +3186,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
3140
3186
  content: [
3141
3187
  {
3142
3188
  type: "text",
3143
- text: safeStringify(await response.json())
3189
+ text: safeStringify2(await response.json())
3144
3190
  }
3145
3191
  ]
3146
3192
  };
@@ -3186,7 +3232,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
3186
3232
 
3187
3233
  // src/http/middleware/response/parse.middleware.ts
3188
3234
  import {
3189
- prettyPrintParseErrors as prettyPrintParseErrors2
3235
+ prettyPrintParseErrors as prettyPrintParseErrors3
3190
3236
  } from "@forklaunch/validator";
3191
3237
 
3192
3238
  // src/http/guards/isResponseCompiledSchema.ts
@@ -3248,13 +3294,13 @@ function parse2(req, res, next) {
3248
3294
  );
3249
3295
  const parseErrors = [];
3250
3296
  if (!parsedHeaders.ok) {
3251
- const headerErrors = prettyPrintParseErrors2(parsedHeaders.errors, "Header");
3297
+ const headerErrors = prettyPrintParseErrors3(parsedHeaders.errors, "Header");
3252
3298
  if (headerErrors) {
3253
3299
  parseErrors.push(headerErrors);
3254
3300
  }
3255
3301
  }
3256
3302
  if (!parsedResponse.ok) {
3257
- const responseErrors = prettyPrintParseErrors2(
3303
+ const responseErrors = prettyPrintParseErrors3(
3258
3304
  parsedResponse.errors,
3259
3305
  "Response"
3260
3306
  );
@@ -3302,7 +3348,7 @@ import {
3302
3348
  isNodeJsWriteableStream,
3303
3349
  isRecord as isRecord4,
3304
3350
  readableStreamToAsyncIterable,
3305
- safeStringify as safeStringify2
3351
+ safeStringify as safeStringify3
3306
3352
  } from "@forklaunch/common";
3307
3353
  import { Readable, Transform } from "stream";
3308
3354
 
@@ -3406,7 +3452,7 @@ ${res.locals.errorMessage}`;
3406
3452
  if (!errorSent) {
3407
3453
  let data2 = "";
3408
3454
  for (const [key, value] of Object.entries(chunk)) {
3409
- data2 += `${key}: ${typeof value === "string" ? value : safeStringify2(value)}
3455
+ data2 += `${key}: ${typeof value === "string" ? value : safeStringify3(value)}
3410
3456
  `;
3411
3457
  }
3412
3458
  data2 += "\n";
@@ -3810,7 +3856,7 @@ function generateOpenApiSpecs(schemaValidator, serverUrls, serverDescriptions, a
3810
3856
  }
3811
3857
 
3812
3858
  // src/http/sdk/sdkClient.ts
3813
- import { hashString, safeStringify as safeStringify3, toRecord as toRecord2 } from "@forklaunch/common";
3859
+ import { hashString, safeStringify as safeStringify4, toRecord as toRecord2 } from "@forklaunch/common";
3814
3860
 
3815
3861
  // src/http/guards/isSdkRouter.ts
3816
3862
  function isSdkRouter(value) {
@@ -3822,12 +3868,12 @@ function mapToSdk(schemaValidator, routerMap, runningPath = void 0) {
3822
3868
  const routerUniquenessCache = /* @__PURE__ */ new Set();
3823
3869
  return Object.fromEntries(
3824
3870
  Object.entries(routerMap).map(([key, value]) => {
3825
- if (routerUniquenessCache.has(hashString(safeStringify3(value)))) {
3871
+ if (routerUniquenessCache.has(hashString(safeStringify4(value)))) {
3826
3872
  throw new Error(
3827
3873
  `SdkClient: Cannot use the same router pointer twice. Please clone the duplicate router with .clone() or only use the router once.`
3828
3874
  );
3829
3875
  }
3830
- routerUniquenessCache.add(hashString(safeStringify3(value)));
3876
+ routerUniquenessCache.add(hashString(safeStringify4(value)));
3831
3877
  const currentPath = runningPath ? [runningPath, key].join(".") : key;
3832
3878
  if (isSdkRouter(value)) {
3833
3879
  Object.entries(value.sdkPaths).forEach(([routePath, sdkKey]) => {
@@ -3879,12 +3925,36 @@ function mapToFetch(schemaValidator, routerMap) {
3879
3925
  return (version ? toRecord2(toRecord2(flattenedFetchMap[path])[method ?? "GET"])[version] : toRecord2(flattenedFetchMap[path])[method ?? "GET"])(path, reqInit[0]);
3880
3926
  });
3881
3927
  }
3882
- function sdkClient(schemaValidator, routerMap) {
3883
- return {
3928
+ function sdkClient(schemaValidator, routerMap, options2) {
3929
+ if (options2?.lazyEvaluation) {
3930
+ let _sdk;
3931
+ let _fetch;
3932
+ const lazyClient = {
3933
+ _finalizedSdk: true,
3934
+ get sdk() {
3935
+ if (!_sdk) {
3936
+ _sdk = mapToSdk(schemaValidator, routerMap);
3937
+ }
3938
+ return _sdk;
3939
+ },
3940
+ get fetch() {
3941
+ if (!_fetch) {
3942
+ _fetch = mapToFetch(schemaValidator, routerMap);
3943
+ }
3944
+ return _fetch;
3945
+ }
3946
+ };
3947
+ return lazyClient;
3948
+ }
3949
+ const client = {
3884
3950
  _finalizedSdk: true,
3885
3951
  sdk: mapToSdk(schemaValidator, routerMap),
3886
3952
  fetch: mapToFetch(schemaValidator, routerMap)
3887
3953
  };
3954
+ if (options2?.optimizePerformance) {
3955
+ return client;
3956
+ }
3957
+ return client;
3888
3958
  }
3889
3959
 
3890
3960
  // src/http/sdk/sdkRouter.ts