elysia 2.0.0-beta.20 → 2.0.0-beta.21

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.
Files changed (50) hide show
  1. package/dist/compile/aot.d.ts +8 -8
  2. package/dist/compile/aot.js +16 -14
  3. package/dist/compile/aot.mjs +14 -14
  4. package/dist/compile/handler/descriptor.js +2 -3
  5. package/dist/compile/handler/descriptor.mjs +2 -3
  6. package/dist/compile/handler/index.d.ts +2 -1
  7. package/dist/compile/handler/index.js +144 -24
  8. package/dist/compile/handler/index.mjs +146 -27
  9. package/dist/compile/handler/jit.d.ts +2 -0
  10. package/dist/compile/handler/jit.js +3 -2
  11. package/dist/compile/handler/jit.mjs +3 -2
  12. package/dist/compile/handler/reconstruct.js +3 -2
  13. package/dist/compile/handler/reconstruct.mjs +3 -2
  14. package/dist/cookie/config.d.ts +6 -2
  15. package/dist/cookie/config.js +68 -8
  16. package/dist/cookie/config.mjs +69 -10
  17. package/dist/cookie/utils.js +3 -3
  18. package/dist/cookie/utils.mjs +4 -4
  19. package/dist/package.js +1 -1
  20. package/dist/package.mjs +1 -1
  21. package/dist/plugin/aot/core.d.ts +5 -0
  22. package/dist/plugin/aot/core.js +37 -1
  23. package/dist/plugin/aot/core.mjs +37 -1
  24. package/dist/plugin/aot/source.js +24 -9
  25. package/dist/plugin/aot/source.mjs +24 -9
  26. package/dist/schema-snapshot.js +4 -3
  27. package/dist/schema-snapshot.mjs +4 -3
  28. package/dist/type/coerce.d.ts +1 -1
  29. package/dist/type/constants.d.ts +1 -1
  30. package/dist/type/elysia/keep-config.d.ts +14 -0
  31. package/dist/type/elysia/keep-config.js +62 -0
  32. package/dist/type/elysia/keep-config.mjs +53 -0
  33. package/dist/type/exports.d.ts +3 -2
  34. package/dist/type/exports.js +10 -0
  35. package/dist/type/exports.mjs +2 -1
  36. package/dist/type/index.js +10 -0
  37. package/dist/type/index.mjs +10 -0
  38. package/dist/type/typebox-type-live.d.ts +2 -2
  39. package/dist/type/typebox-type-live.js +8 -0
  40. package/dist/type/typebox-type-live.mjs +2 -2
  41. package/dist/type/typebox-type.d.ts +10 -2
  42. package/dist/type/typebox-type.js +64 -0
  43. package/dist/type/typebox-type.mjs +17 -1
  44. package/dist/types.d.ts +3 -2
  45. package/dist/utils.d.ts +3 -1
  46. package/dist/utils.js +23 -0
  47. package/dist/utils.mjs +22 -1
  48. package/dist/ws/route.js +33 -5
  49. package/dist/ws/route.mjs +35 -7
  50. package/package.json +1 -1
@@ -1,15 +1,18 @@
1
1
  import { isBun } from "../../universal/constants.mjs";
2
2
  import { ElysiaFile } from "../../universal/file.mjs";
3
- import { cloneHook, compactBeforeHandleConflicts, compactBeforeHandlePrefix, eventProperties, flattenChain, flattenChainMemo, flattenChainMemoReadonly, fnOrigin, isCompactBeforeHandleOnly, isHTMLBundle, isLocalScope, isNotEmpty, macroEpoch, mergeHook, nullObject, replaceUrlPath, rootSnapshot } from "../../utils.mjs";
3
+ import { cloneHook, compactBeforeHandleConflicts, compactBeforeHandlePrefix, compositionKeys, eventProperties, flattenChain, flattenChainMemo, flattenChainMemoReadonly, fnOrigin, fnv1a, isCompactBeforeHandleOnly, isHTMLBundle, isLocalScope, isNotEmpty, macroEpoch, mergeHook, nullObject, refTargets, replaceUrlPath, rootSnapshot } from "../../utils.mjs";
4
4
  import { ElysiaStatus } from "../../error.mjs";
5
5
  import { mapResponse } from "../../adapter/web-standard/handler.mjs";
6
- import { Capture, Compiled } from "../aot.mjs";
6
+ import { Capture, Compiled, aotDrift, warnAotDrift } from "../aot.mjs";
7
7
  import { frozenRootOf } from "../../generation.mjs";
8
+ import { isAsyncFunction } from "../utils.mjs";
8
9
  import { returnedErrorClasses } from "../../handler/utils.mjs";
10
+ import { deriveModes } from "./utils.mjs";
9
11
  import { resolveHandlerParams } from "./params.mjs";
10
12
  import { compileHandlerJit, createInlineHandler, setCaptureHeaderShorthand } from "./jit.mjs";
11
- import { describeRoute, routeDescriptors } from "./descriptor.mjs";
13
+ import { isResponseMap } from "./frozen-validator.mjs";
12
14
  import { Reconstrct } from "./reconstruct.mjs";
15
+ import { describeRoute, routeDescriptors } from "./descriptor.mjs";
13
16
  import { defaultAdapter } from "../../adapter/constants.mjs";
14
17
 
15
18
  //#region src/compile/handler/index.ts
@@ -273,18 +276,129 @@ function composeRouteHook(instance, localHook, appHook, inheritedChain, root, ma
273
276
  }
274
277
  return hook;
275
278
  }
276
- const hookStateAliases = [
277
- "ho",
278
- "tf",
279
- "bf",
280
- "af",
281
- "mr",
282
- "er",
283
- "ar",
284
- "va",
285
- "cc",
286
- "tr"
279
+ const shapeEvents = [
280
+ "parse",
281
+ "transform",
282
+ "beforeHandle",
283
+ "afterHandle",
284
+ "mapResponse",
285
+ "afterResponse",
286
+ "error",
287
+ "trace"
288
+ ];
289
+ const shapeSlots = [
290
+ "body",
291
+ "headers",
292
+ "params",
293
+ "query",
294
+ "cookie",
295
+ "response"
287
296
  ];
297
+ const isCookieAt = (at) => at === "cookie" || at === "field";
298
+ function childAt(at, k, value, deferred) {
299
+ if (at.endsWith("*")) return at.slice(0, -1);
300
+ if (at.endsWith("^")) return k === "0" ? at.slice(0, -1) + (Array.isArray(value) ? "*" : "") : "";
301
+ if (!isCookieAt(at)) return "";
302
+ switch (k) {
303
+ case "$ref": return at;
304
+ case "properties": return at === "cookie" ? "field*" : "";
305
+ case "parameters": return deferred ? at + "^" : "";
306
+ }
307
+ return k === "$defs" || compositionKeys.includes(k) ? at + "*" : "";
308
+ }
309
+ function routeShape(hook, handler, root) {
310
+ const frozenRoot = frozenRootOf(root);
311
+ const config = frozenRoot["~config"];
312
+ const models = frozenRoot["~ext"]?.models;
313
+ const refs = /* @__PURE__ */ new Map();
314
+ const addRef = (name, at = "") => {
315
+ if (!isCookieAt(at)) at = "";
316
+ if (at === "cookie" || !refs.get(name)) refs.set(name, at);
317
+ };
318
+ const ids = /* @__PURE__ */ new Map();
319
+ const visiting = /* @__PURE__ */ new Set();
320
+ let unsupported = false;
321
+ for (const _ in []) return;
322
+ const shape = (v, at = "") => {
323
+ switch (typeof v) {
324
+ case "string": return JSON.stringify(v);
325
+ case "number": return "n" + (Object.is(v, -0) ? "-0" : v);
326
+ case "bigint": return "b" + v;
327
+ case "boolean": return v ? "T" : "F";
328
+ case "undefined": return "U";
329
+ case "function":
330
+ if (ids.has(v)) return "#" + ids.get(v);
331
+ ids.set(v, ids.size);
332
+ return isAsyncFunction(v) ? "fa" : "fs";
333
+ case "symbol":
334
+ unsupported = true;
335
+ return "";
336
+ }
337
+ if (v === null) return "N";
338
+ if ("~standard" in v) return "S";
339
+ if (visiting.has(v)) {
340
+ unsupported = true;
341
+ return "";
342
+ }
343
+ let tag = "";
344
+ const list = Array.isArray(v);
345
+ const proto = Object.getPrototypeOf(v);
346
+ if (list ? proto !== Array.prototype : proto !== null && proto !== Object.prototype) {
347
+ const kind = proto && Object.getOwnPropertyDescriptor(proto, "~kind");
348
+ if (!kind || !("value" in kind) || Reflect.ownKeys(proto).length !== 1 || Object.getPrototypeOf(proto) !== Object.prototype) {
349
+ unsupported = true;
350
+ return "";
351
+ }
352
+ tag = shape(kind.value);
353
+ }
354
+ visiting.add(v);
355
+ const map = at === "response" && isResponseMap(v);
356
+ const deferred = v["~kind"] === "Deferred";
357
+ let s = (list ? "[" : "{") + tag;
358
+ for (const k of Reflect.ownKeys(v)) {
359
+ const d = Object.getOwnPropertyDescriptor(v, k);
360
+ if (typeof k === "symbol" || !("value" in d)) {
361
+ unsupported = true;
362
+ break;
363
+ }
364
+ const value = d.value;
365
+ if (k === "$ref" && typeof value === "string") addRef(value, at);
366
+ s += "," + (d.enumerable ? "" : "!") + JSON.stringify(k) + ":";
367
+ if (map) s += named(value);
368
+ else if (k === "config" && isCookieAt(at)) s += shape([value?.sign, !!value?.secrets]);
369
+ else if (k === "secrets" && (typeof value === "string" || Array.isArray(value))) s += shape(!!value);
370
+ else s += shape(value, childAt(at, k, value, deferred));
371
+ }
372
+ visiting.delete(v);
373
+ return s + (list ? "]" : "}");
374
+ };
375
+ const named = (v, at) => {
376
+ if (typeof v === "string") addRef(v, at);
377
+ return shape(v, at);
378
+ };
379
+ let s = typeof handler === "function" || handler == null ? shape(handler) : handler instanceof Response ? "R" : handler instanceof Promise ? "P" : typeof handler === "object" ? "O" : "V";
380
+ for (const event of shapeEvents) {
381
+ s += "|";
382
+ const fns = hook?.[event];
383
+ if (fns) for (const fn of Array.isArray(fns) ? fns : [fns]) s += shape(fn);
384
+ }
385
+ const bf = hook?.beforeHandle;
386
+ s += "|" + JSON.stringify(bf && deriveModes(Array.isArray(bf) ? bf : [bf], hook["~deriveEntries"]));
387
+ for (const from of [hook, ...hook?.schemas || []]) for (const slot of shapeSlots) s += "|" + named(from?.[slot], slot);
388
+ let targets;
389
+ for (const [ref, at] of refs) for (const target of at ? (targets ??= refTargets([], models))(ref) : [models?.[ref]]) s += "|" + JSON.stringify(ref) + shape(target, at);
390
+ s += "|" + JSON.stringify([
391
+ config?.cookie?.sign,
392
+ config?.cookie?.verify,
393
+ config?.normalize,
394
+ !!config?.allowUnsafeValidationDetails,
395
+ config?.abortSignal !== false,
396
+ isNotEmpty(frozenRoot["~ext"]?.headers),
397
+ !!(config?.adapter ?? defaultAdapter).response.supportsDefaultHeaderSink,
398
+ !!returnedErrorClasses(hook, root)
399
+ ]) + shape(config?.sanitize);
400
+ return unsupported ? void 0 : fnv1a(s);
401
+ }
288
402
  const isBareArrow = /^(?:async\s*)?\(\s*\)\s*=>/;
289
403
  const isSucroseOpaque = /arguments|eval|\[native code\]/;
290
404
  function compileHandler(route, root, liveOnly = false) {
@@ -302,17 +416,7 @@ function compileHandler(route, root, liveOnly = false) {
302
416
  handler = (c) => handle(new Request(replaceUrlPath(c.request.url, c.path.slice(c.path.startsWith(encRoot) ? encLen : rawLen) || "/"), c.request));
303
417
  }
304
418
  const reconstructed = liveOnly ? void 0 : Compiled.getHandler(frozenRoot["~programId"], method, path);
305
- if (reconstructed && typeof handler === "function" && !frozenRoot["~ext"]?.macro && !frozenRootOf(localMacroRoot(macroScope ?? instance, root))["~ext"]?.macro) {
306
- if (!reconstructed.a.some((name) => hookStateAliases.includes(name))) return reconstructed.f(handler, ...resolveHandlerParams(reconstructed.a, {
307
- root,
308
- parse: adapter.parse,
309
- res: adapter.response,
310
- hook: nullObject(),
311
- vali: void 0,
312
- cookieConfig: void 0,
313
- tracers: void 0
314
- }));
315
- }
419
+ if (!reconstructed) liveOnly ||= Compiled.hasProgram(frozenRoot["~programId"]);
316
420
  const hook = composeRouteHook(instance, localHook, appHook, inheritedChain, root, macroScope, !reconstructed);
317
421
  if (hook) {
318
422
  promoteDerive(hook);
@@ -350,6 +454,20 @@ function compileHandler(route, root, liveOnly = false) {
350
454
  const resolve = (p) => typeof p === "string" && p in namedParsers ? namedParsers[p] : p;
351
455
  hook.parse = Array.isArray(hook.parse) ? hook.parse.map(resolve) : resolve(hook.parse);
352
456
  }
457
+ let shape;
458
+ if (reconstructed || Capture.isCapturing()) {
459
+ shape = routeShape(hook, handler, root);
460
+ if (reconstructed && (!shape || reconstructed.k !== shape)) {
461
+ let live;
462
+ try {
463
+ live = compileHandler(route, root, true);
464
+ } catch (cause) {
465
+ throw new Error(aotDrift(method, path), { cause });
466
+ }
467
+ warnAotDrift(method, path);
468
+ return live;
469
+ }
470
+ }
353
471
  if (reconstructed) return reconstructed.f(handler, ...resolveHandlerParams(reconstructed.a, {
354
472
  root,
355
473
  parse: adapter.parse,
@@ -401,9 +519,10 @@ function compileHandler(route, root, liveOnly = false) {
401
519
  isStaticResponse,
402
520
  isPromiseHandler,
403
521
  errorClasses,
404
- state
522
+ state,
523
+ shape
405
524
  });
406
525
  }
407
526
 
408
527
  //#endregion
409
- export { buildNativeStaticResponse, chainResolver, clearHandlerAnalysisCaches, compileHandler, composeRouteHook, localMacroRoot, resolveLocalHook, resolveWSLocalHook, setCaptureHeaderShorthand };
528
+ export { buildNativeStaticResponse, chainResolver, clearHandlerAnalysisCaches, compileHandler, composeRouteHook, localMacroRoot, resolveLocalHook, resolveWSLocalHook, routeShape, setCaptureHeaderShorthand };
@@ -37,6 +37,8 @@ interface CompileHandlerJitOptions {
37
37
  * The JIT no longer re-derives these facts; it names its emissions off them.
38
38
  */
39
39
  state: RouteCompileState;
40
+ /** `routeShape`, recorded with the captured code */
41
+ shape?: number;
40
42
  }
41
43
  declare function compileHandlerJit(options: CompileHandlerJitOptions, tailPass?: TailMode): CompiledHandler;
42
44
  //#endregion
@@ -162,7 +162,7 @@ const createInlineHandler = (map, h) => ((c) => {
162
162
  return map(r, c.request, true);
163
163
  });
164
164
  function compileHandlerJit(options, tailPass) {
165
- const { method, path, handler, root, errorRoot, hook, adapter, isHandleFunction, isStaticResponse, isPromiseHandler, errorClasses, state } = options;
165
+ const { method, path, handler, root, errorRoot, hook, adapter, isHandleFunction, isStaticResponse, isPromiseHandler, errorClasses, state, shape } = options;
166
166
  const { vali, inference, cookieConfig, beforeHandlePrefix, traceHandlers, tracePhases, hasAnyPhase, traceHandleOn, descriptor: { async: isAsync, tail: tailRoute, responseMode, hasBody, bodyValiIsAsync, headersValiIsAsync, paramsValiIsAsync, queryValiIsAsync, cookieValiIsAsync: cookieValidIsAsync, responseValiAsync, hasCookieSign, syncCookieSign, asyncCookieSign, lazyCookieVerify, hasErrorHook, hasAfterResponse, hasBeforeHandle, hasAfterHandle, hasMapResponse, hasResponseValidator, hasTrace, traceCount, hasLifecycleHook, syncErrorHook, syncAfterResponse } } = state;
167
167
  const hasStaticAfterResponse = !!hook?.afterResponse?.length;
168
168
  const hasDeriveDispose = !!hook?.["~deriveEntries"]?.length;
@@ -567,7 +567,8 @@ if(_vr)_r.response=${responseValiAsync ? awaitValue(encodeStatus, arm) : encodeS
567
567
  method,
568
568
  path,
569
569
  alias: fullAlias,
570
- code
570
+ code,
571
+ k: shape
571
572
  });
572
573
  onEmit?.(code);
573
574
  const isGeneratorHandler = isHandleFunction && handler.constructor.name.endsWith("GeneratorFunction");
@@ -161,7 +161,7 @@ const createInlineHandler = (map, h) => ((c) => {
161
161
  return map(r, c.request, true);
162
162
  });
163
163
  function compileHandlerJit(options, tailPass) {
164
- const { method, path, handler, root, errorRoot, hook, adapter, isHandleFunction, isStaticResponse, isPromiseHandler, errorClasses, state } = options;
164
+ const { method, path, handler, root, errorRoot, hook, adapter, isHandleFunction, isStaticResponse, isPromiseHandler, errorClasses, state, shape } = options;
165
165
  const { vali, inference, cookieConfig, beforeHandlePrefix, traceHandlers, tracePhases, hasAnyPhase, traceHandleOn, descriptor: { async: isAsync, tail: tailRoute, responseMode, hasBody, bodyValiIsAsync, headersValiIsAsync, paramsValiIsAsync, queryValiIsAsync, cookieValiIsAsync: cookieValidIsAsync, responseValiAsync, hasCookieSign, syncCookieSign, asyncCookieSign, lazyCookieVerify, hasErrorHook, hasAfterResponse, hasBeforeHandle, hasAfterHandle, hasMapResponse, hasResponseValidator, hasTrace, traceCount, hasLifecycleHook, syncErrorHook, syncAfterResponse } } = state;
166
166
  const hasStaticAfterResponse = !!hook?.afterResponse?.length;
167
167
  const hasDeriveDispose = !!hook?.["~deriveEntries"]?.length;
@@ -566,7 +566,8 @@ if(_vr)_r.response=${responseValiAsync ? awaitValue(encodeStatus, arm) : encodeS
566
566
  method,
567
567
  path,
568
568
  alias: fullAlias,
569
- code
569
+ code,
570
+ k: shape
570
571
  });
571
572
  onEmit?.(code);
572
573
  const isGeneratorHandler = isHandleFunction && handler.constructor.name.endsWith("GeneratorFunction");
@@ -28,14 +28,15 @@ var Reconstrct = class {
28
28
  });
29
29
  } catch (error) {
30
30
  if (!require_compile_handler_frozen_validator.isBridgeNotInitialized(error)) throw error;
31
- if (liveOnly) throw new Error("Duplicate route must compile JIT but the TypeBox bridge is not initialized", { cause: error });
31
+ if (liveOnly) throw new Error("Route requires live compilation but the TypeBox bridge is not initialized", { cause: error });
32
32
  const frozen = require_compile_handler_frozen_validator.buildFrozenRouteValidator(hook, root, method, path);
33
33
  if (frozen) return frozen;
34
34
  throw error;
35
35
  }
36
36
  }
37
37
  static cookie(hook, root) {
38
- return require_cookie_config.compileCookieConfig(hook?.cookie, require_generation.frozenRootOf(root)["~config"]?.cookie);
38
+ const frozenRoot = require_generation.frozenRootOf(root);
39
+ return require_cookie_config.compileCookieConfig(hook?.cookie, frozenRoot["~config"]?.cookie, frozenRoot["~ext"]?.models, hook?.schemas);
39
40
  }
40
41
  static trace(hook, root) {
41
42
  const traceHandlers = hook?.trace;
@@ -27,14 +27,15 @@ var Reconstrct = class {
27
27
  });
28
28
  } catch (error) {
29
29
  if (!isBridgeNotInitialized(error)) throw error;
30
- if (liveOnly) throw new Error("Duplicate route must compile JIT but the TypeBox bridge is not initialized", { cause: error });
30
+ if (liveOnly) throw new Error("Route requires live compilation but the TypeBox bridge is not initialized", { cause: error });
31
31
  const frozen = buildFrozenRouteValidator(hook, root, method, path);
32
32
  if (frozen) return frozen;
33
33
  throw error;
34
34
  }
35
35
  }
36
36
  static cookie(hook, root) {
37
- return compileCookieConfig(hook?.cookie, frozenRootOf(root)["~config"]?.cookie);
37
+ const frozenRoot = frozenRootOf(root);
38
+ return compileCookieConfig(hook?.cookie, frozenRoot["~config"]?.cookie, frozenRoot["~ext"]?.models, hook?.schemas);
38
39
  }
39
40
  static trace(hook, root) {
40
41
  const traceHandlers = hook?.trace;
@@ -9,6 +9,7 @@ interface FieldCookieConfig {
9
9
  secrets?: string | null | (string | null)[];
10
10
  sign: boolean;
11
11
  defaults?: Partial<BaseCookie>;
12
+ legacySignature?: boolean;
12
13
  }
13
14
  interface CompiledCookieConfig {
14
15
  defaults: Partial<BaseCookie>;
@@ -20,7 +21,10 @@ interface CompiledCookieConfig {
20
21
  verify: 'lazy' | 'eager';
21
22
  legacySignature: boolean;
22
23
  }
23
- declare function compileCookieConfig(routeSchema: AnySchema | undefined, appConfig: AppCookieConfig | undefined): CompiledCookieConfig;
24
+ declare function compileCookieConfig(routeSchema: AnySchema | string | undefined, appConfig: AppCookieConfig | undefined, models?: Record<string, unknown>, schemas?: {
25
+ cookie?: unknown;
26
+ }[]): CompiledCookieConfig;
27
+ declare const legacySignatureOf: (name: string, config: CompiledCookieConfig) => boolean;
24
28
  declare function resolveSignSecrets(name: string, config: CompiledCookieConfig): CompiledCookieConfig['globalSecrets'] | undefined;
25
29
  declare function isCookieSigned(name: string, config: CompiledCookieConfig): {
26
30
  signed: true;
@@ -29,4 +33,4 @@ declare function isCookieSigned(name: string, config: CompiledCookieConfig): {
29
33
  signed: false;
30
34
  };
31
35
  //#endregion
32
- export { AppCookieConfig, CompiledCookieConfig, FieldCookieConfig, compileCookieConfig, isCookieSigned, resolveSignSecrets };
36
+ export { AppCookieConfig, CompiledCookieConfig, FieldCookieConfig, compileCookieConfig, isCookieSigned, legacySignatureOf, resolveSignSecrets };
@@ -26,8 +26,67 @@ function getAttributes(source) {
26
26
  return out;
27
27
  }
28
28
  const hasUsableSecret = (secrets) => Array.isArray(secrets) ? secrets.some((s) => !!s?.trim()) : !!secrets?.trim();
29
- function compileCookieConfig(routeSchema, appConfig) {
30
- const routeConfig = routeSchema?.config ?? void 0;
29
+ const gathered = () => ({
30
+ config: require_utils.nullObject(),
31
+ fields: require_utils.nullObject()
32
+ });
33
+ function canonical(config) {
34
+ const out = require_utils.nullObject();
35
+ for (const key of Object.keys(config).sort()) if (key !== "sign") out[key] = key === "secrets" ? asList(config[key]) : config[key];
36
+ return out;
37
+ }
38
+ const unsigned = (from) => {
39
+ const fields = require_utils.nullObject();
40
+ for (const name of Object.keys(from.fields).sort()) {
41
+ const field = canonical(from.fields[name]);
42
+ if (Object.keys(field).length) fields[name] = field;
43
+ }
44
+ return JSON.stringify([canonical(from.config), fields]);
45
+ };
46
+ const unclear = () => /* @__PURE__ */ new Error("[Elysia] Cookie schema `$ref` is unresolvable or ambiguous; use a model name or `$id` instead");
47
+ const asList = (value) => JSON.stringify(typeof value === "string" ? [value] : value);
48
+ function foldConfig(into, config) {
49
+ for (const key in config) {
50
+ const value = config[key];
51
+ const prev = into[key];
52
+ if (value === void 0) continue;
53
+ if (prev === void 0) into[key] = value;
54
+ else if (key === "sign") into.sign = prev === true || value === true || [].concat(prev, value);
55
+ else if ((key === "secrets" || key === "legacySignature") && asList(prev) !== asList(value)) throw new Error(`[Elysia] Merged cookie schemas disagree on \`${key}\``);
56
+ }
57
+ }
58
+ function compileCookieConfig(routeSchema, appConfig, models, schemas) {
59
+ const roots = [routeSchema, ...(schemas ?? []).map((schema) => schema?.cookie).reverse()].map((schema) => typeof schema === "string" ? models?.[schema] : schema);
60
+ let targets;
61
+ const resolve = (name) => (targets ??= require_utils.refTargets(roots, models))(name);
62
+ const gather = (schema, into, field, path) => {
63
+ if (!schema || path.has(schema)) return;
64
+ path.add(schema);
65
+ const ref = schema.$ref;
66
+ const targets = typeof ref === "string" ? [...resolve(ref)] : [ref];
67
+ if (typeof ref === "string" && !targets.length || schema.$dynamicRef !== void 0 || schema.$recursiveRef !== void 0) throw unclear();
68
+ if (field === void 0) {
69
+ foldConfig(into.config, schema.config);
70
+ for (const name in schema.properties) gather(schema.properties[name], into, name, /* @__PURE__ */ new Set());
71
+ } else if (schema.config) foldConfig(into.fields[field] ??= require_utils.nullObject(), schema.config);
72
+ if (targets.length > 1) {
73
+ let first;
74
+ for (const target of targets) {
75
+ const from = gathered();
76
+ gather(target, from, field, path);
77
+ if ((first ??= unsigned(from)) !== unsigned(from)) throw unclear();
78
+ foldConfig(into.config, from.config);
79
+ for (const name in from.fields) foldConfig(into.fields[name] ??= require_utils.nullObject(), from.fields[name]);
80
+ }
81
+ } else gather(targets[0], into, field, path);
82
+ for (const key of require_utils.compositionKeys) if (Array.isArray(schema[key])) for (const member of schema[key]) gather(member, into, field, path);
83
+ if (schema["~kind"] === "Deferred") for (const operand of [].concat(schema.parameters?.[0])) gather(operand, into, field, path);
84
+ path.delete(schema);
85
+ };
86
+ const route = gathered();
87
+ for (const root of roots) gather(root, route, void 0, /* @__PURE__ */ new Set());
88
+ const routeConfig = route.config;
89
+ const fieldConfigs = route.fields;
31
90
  const appAttributes = getAttributes(appConfig);
32
91
  const routeAttributes = getAttributes(routeConfig);
33
92
  const defaults = appAttributes && routeAttributes ? {
@@ -43,17 +102,16 @@ function compileCookieConfig(routeSchema, appConfig) {
43
102
  else globalSign = [rawSign];
44
103
  const globalSecrets = routeConfig?.secrets !== void 0 ? routeConfig.secrets : appConfig?.secrets;
45
104
  const fields = require_utils.nullObject();
46
- const properties = routeSchema?.properties;
47
105
  let hasSign = false;
48
- if (properties) for (const name in properties) {
49
- const config = properties[name]?.config;
50
- if (!config) continue;
51
- const sign = !!config.secrets || config.sign === true;
106
+ for (const name in fieldConfigs) {
107
+ const config = fieldConfigs[name];
108
+ const sign = config.secrets != null || config.sign === true;
52
109
  if (sign) hasSign = true;
53
110
  fields[name] = {
54
111
  secrets: config.secrets,
55
112
  sign,
56
- defaults: getAttributes(config)
113
+ defaults: getAttributes(config),
114
+ legacySignature: config.legacySignature
57
115
  };
58
116
  }
59
117
  if (globalSign !== void 0) hasSign = true;
@@ -77,6 +135,7 @@ function compileCookieConfig(routeSchema, appConfig) {
77
135
  legacySignature: (routeConfig?.legacySignature ?? appConfig?.legacySignature) !== false
78
136
  };
79
137
  }
138
+ const legacySignatureOf = (name, config) => config.fields[name]?.legacySignature ?? config.legacySignature;
80
139
  function resolveSignSecrets(name, config) {
81
140
  const field = config.fields[name];
82
141
  if (field?.sign) return field.secrets ?? config.globalSecrets;
@@ -95,4 +154,5 @@ function isCookieSigned(name, config) {
95
154
  //#endregion
96
155
  exports.compileCookieConfig = compileCookieConfig;
97
156
  exports.isCookieSigned = isCookieSigned;
157
+ exports.legacySignatureOf = legacySignatureOf;
98
158
  exports.resolveSignSecrets = resolveSignSecrets;
@@ -1,4 +1,4 @@
1
- import { nullObject } from "../utils.mjs";
1
+ import { compositionKeys, nullObject, refTargets } from "../utils.mjs";
2
2
  import { InvalidCookie } from "./error.mjs";
3
3
 
4
4
  //#region src/cookie/config.ts
@@ -25,8 +25,67 @@ function getAttributes(source) {
25
25
  return out;
26
26
  }
27
27
  const hasUsableSecret = (secrets) => Array.isArray(secrets) ? secrets.some((s) => !!s?.trim()) : !!secrets?.trim();
28
- function compileCookieConfig(routeSchema, appConfig) {
29
- const routeConfig = routeSchema?.config ?? void 0;
28
+ const gathered = () => ({
29
+ config: nullObject(),
30
+ fields: nullObject()
31
+ });
32
+ function canonical(config) {
33
+ const out = nullObject();
34
+ for (const key of Object.keys(config).sort()) if (key !== "sign") out[key] = key === "secrets" ? asList(config[key]) : config[key];
35
+ return out;
36
+ }
37
+ const unsigned = (from) => {
38
+ const fields = nullObject();
39
+ for (const name of Object.keys(from.fields).sort()) {
40
+ const field = canonical(from.fields[name]);
41
+ if (Object.keys(field).length) fields[name] = field;
42
+ }
43
+ return JSON.stringify([canonical(from.config), fields]);
44
+ };
45
+ const unclear = () => /* @__PURE__ */ new Error("[Elysia] Cookie schema `$ref` is unresolvable or ambiguous; use a model name or `$id` instead");
46
+ const asList = (value) => JSON.stringify(typeof value === "string" ? [value] : value);
47
+ function foldConfig(into, config) {
48
+ for (const key in config) {
49
+ const value = config[key];
50
+ const prev = into[key];
51
+ if (value === void 0) continue;
52
+ if (prev === void 0) into[key] = value;
53
+ else if (key === "sign") into.sign = prev === true || value === true || [].concat(prev, value);
54
+ else if ((key === "secrets" || key === "legacySignature") && asList(prev) !== asList(value)) throw new Error(`[Elysia] Merged cookie schemas disagree on \`${key}\``);
55
+ }
56
+ }
57
+ function compileCookieConfig(routeSchema, appConfig, models, schemas) {
58
+ const roots = [routeSchema, ...(schemas ?? []).map((schema) => schema?.cookie).reverse()].map((schema) => typeof schema === "string" ? models?.[schema] : schema);
59
+ let targets;
60
+ const resolve = (name) => (targets ??= refTargets(roots, models))(name);
61
+ const gather = (schema, into, field, path) => {
62
+ if (!schema || path.has(schema)) return;
63
+ path.add(schema);
64
+ const ref = schema.$ref;
65
+ const targets = typeof ref === "string" ? [...resolve(ref)] : [ref];
66
+ if (typeof ref === "string" && !targets.length || schema.$dynamicRef !== void 0 || schema.$recursiveRef !== void 0) throw unclear();
67
+ if (field === void 0) {
68
+ foldConfig(into.config, schema.config);
69
+ for (const name in schema.properties) gather(schema.properties[name], into, name, /* @__PURE__ */ new Set());
70
+ } else if (schema.config) foldConfig(into.fields[field] ??= nullObject(), schema.config);
71
+ if (targets.length > 1) {
72
+ let first;
73
+ for (const target of targets) {
74
+ const from = gathered();
75
+ gather(target, from, field, path);
76
+ if ((first ??= unsigned(from)) !== unsigned(from)) throw unclear();
77
+ foldConfig(into.config, from.config);
78
+ for (const name in from.fields) foldConfig(into.fields[name] ??= nullObject(), from.fields[name]);
79
+ }
80
+ } else gather(targets[0], into, field, path);
81
+ for (const key of compositionKeys) if (Array.isArray(schema[key])) for (const member of schema[key]) gather(member, into, field, path);
82
+ if (schema["~kind"] === "Deferred") for (const operand of [].concat(schema.parameters?.[0])) gather(operand, into, field, path);
83
+ path.delete(schema);
84
+ };
85
+ const route = gathered();
86
+ for (const root of roots) gather(root, route, void 0, /* @__PURE__ */ new Set());
87
+ const routeConfig = route.config;
88
+ const fieldConfigs = route.fields;
30
89
  const appAttributes = getAttributes(appConfig);
31
90
  const routeAttributes = getAttributes(routeConfig);
32
91
  const defaults = appAttributes && routeAttributes ? {
@@ -42,17 +101,16 @@ function compileCookieConfig(routeSchema, appConfig) {
42
101
  else globalSign = [rawSign];
43
102
  const globalSecrets = routeConfig?.secrets !== void 0 ? routeConfig.secrets : appConfig?.secrets;
44
103
  const fields = nullObject();
45
- const properties = routeSchema?.properties;
46
104
  let hasSign = false;
47
- if (properties) for (const name in properties) {
48
- const config = properties[name]?.config;
49
- if (!config) continue;
50
- const sign = !!config.secrets || config.sign === true;
105
+ for (const name in fieldConfigs) {
106
+ const config = fieldConfigs[name];
107
+ const sign = config.secrets != null || config.sign === true;
51
108
  if (sign) hasSign = true;
52
109
  fields[name] = {
53
110
  secrets: config.secrets,
54
111
  sign,
55
- defaults: getAttributes(config)
112
+ defaults: getAttributes(config),
113
+ legacySignature: config.legacySignature
56
114
  };
57
115
  }
58
116
  if (globalSign !== void 0) hasSign = true;
@@ -76,6 +134,7 @@ function compileCookieConfig(routeSchema, appConfig) {
76
134
  legacySignature: (routeConfig?.legacySignature ?? appConfig?.legacySignature) !== false
77
135
  };
78
136
  }
137
+ const legacySignatureOf = (name, config) => config.fields[name]?.legacySignature ?? config.legacySignature;
79
138
  function resolveSignSecrets(name, config) {
80
139
  const field = config.fields[name];
81
140
  if (field?.sign) return field.secrets ?? config.globalSecrets;
@@ -92,4 +151,4 @@ function isCookieSigned(name, config) {
92
151
  }
93
152
 
94
153
  //#endregion
95
- export { compileCookieConfig, isCookieSigned, resolveSignSecrets };
154
+ export { compileCookieConfig, isCookieSigned, legacySignatureOf, resolveSignSecrets };
@@ -66,7 +66,7 @@ async function parseCookieRaw(cookieString, config, lazy) {
66
66
  let value = (0, deuri.default)(v) ?? v;
67
67
  const signCheck = require_cookie_config.resolveSignSecrets(name, config);
68
68
  if (signCheck !== void 0) try {
69
- value = await require_cookie_crypto.unsignWithSecrets(name, value, signCheck, config.legacySignature);
69
+ value = await require_cookie_crypto.unsignWithSecrets(name, value, signCheck, require_cookie_config.legacySignatureOf(name, config));
70
70
  } catch (error) {
71
71
  if (!lazy || !(error instanceof require_cookie_error.InvalidCookie)) throw error;
72
72
  out[name] = invalidSignature();
@@ -87,7 +87,7 @@ function parseCookieRawSigned(cookieString, config, lazy) {
87
87
  let value = (0, deuri.default)(v) ?? v;
88
88
  const signCheck = require_cookie_config.resolveSignSecrets(name, config);
89
89
  if (signCheck !== void 0) try {
90
- value = require_cookie_crypto.unsignWithSecretsSync(name, value, signCheck, config.legacySignature);
90
+ value = require_cookie_crypto.unsignWithSecretsSync(name, value, signCheck, require_cookie_config.legacySignatureOf(name, config));
91
91
  } catch (error) {
92
92
  if (!lazy || !(error instanceof require_cookie_error.InvalidCookie)) throw error;
93
93
  out[name] = invalidSignature();
@@ -111,7 +111,7 @@ function buildCookieJar(set, raw, config, lazySign, deferDecode) {
111
111
  const secrets = require_cookie_config.resolveSignSecrets(name, config);
112
112
  if (secrets !== void 0) {
113
113
  entry["~unsign"] = secrets;
114
- if (!config.legacySignature) entry["~strict"] = 1;
114
+ if (!require_cookie_config.legacySignatureOf(name, config)) entry["~strict"] = 1;
115
115
  }
116
116
  } else {
117
117
  const value = entry.value;
@@ -3,7 +3,7 @@ import { InvalidCookie } from "./error.mjs";
3
3
  import { parse } from "./lib.mjs";
4
4
  import { deriveSignKey, hasSyncHmac, maybeJsonDecode, rawJsonValue, resolvePendingCookie, signCookie, signCookieSyncImpl, unsignWithSecrets, unsignWithSecretsSync } from "./crypto.mjs";
5
5
  import { Cookie } from "./cookie.mjs";
6
- import { isCookieSigned, resolveSignSecrets } from "./config.mjs";
6
+ import { isCookieSigned, legacySignatureOf, resolveSignSecrets } from "./config.mjs";
7
7
  import decodeComponent from "deuri";
8
8
 
9
9
  //#region src/cookie/utils.ts
@@ -63,7 +63,7 @@ async function parseCookieRaw(cookieString, config, lazy) {
63
63
  let value = decodeComponent(v) ?? v;
64
64
  const signCheck = resolveSignSecrets(name, config);
65
65
  if (signCheck !== void 0) try {
66
- value = await unsignWithSecrets(name, value, signCheck, config.legacySignature);
66
+ value = await unsignWithSecrets(name, value, signCheck, legacySignatureOf(name, config));
67
67
  } catch (error) {
68
68
  if (!lazy || !(error instanceof InvalidCookie)) throw error;
69
69
  out[name] = invalidSignature();
@@ -84,7 +84,7 @@ function parseCookieRawSigned(cookieString, config, lazy) {
84
84
  let value = decodeComponent(v) ?? v;
85
85
  const signCheck = resolveSignSecrets(name, config);
86
86
  if (signCheck !== void 0) try {
87
- value = unsignWithSecretsSync(name, value, signCheck, config.legacySignature);
87
+ value = unsignWithSecretsSync(name, value, signCheck, legacySignatureOf(name, config));
88
88
  } catch (error) {
89
89
  if (!lazy || !(error instanceof InvalidCookie)) throw error;
90
90
  out[name] = invalidSignature();
@@ -108,7 +108,7 @@ function buildCookieJar(set, raw, config, lazySign, deferDecode) {
108
108
  const secrets = resolveSignSecrets(name, config);
109
109
  if (secrets !== void 0) {
110
110
  entry["~unsign"] = secrets;
111
- if (!config.legacySignature) entry["~strict"] = 1;
111
+ if (!legacySignatureOf(name, config)) entry["~strict"] = 1;
112
112
  }
113
113
  } else {
114
114
  const value = entry.value;
package/dist/package.js CHANGED
@@ -1,6 +1,6 @@
1
1
 
2
2
  //#region package.json
3
- var version = "2.0.0-beta.20";
3
+ var version = "2.0.0-beta.21";
4
4
 
5
5
  //#endregion
6
6
  Object.defineProperty(exports, 'version', {
package/dist/package.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "2.0.0-beta.20";
2
+ var version = "2.0.0-beta.21";
3
3
 
4
4
  //#endregion
5
5
  export { version };
@@ -3,6 +3,11 @@ import { JITProbeResult } from "../../compile/jit-probe.js";
3
3
  import { AotModuleCondition, AotTarget } from "./source.js";
4
4
 
5
5
  //#region src/plugin/aot/core.d.ts
6
+ /**
7
+ * A route that differs from the build compiles live with a warning, or fails
8
+ * when the JIT is stripped. A conditional hook must change the hook list
9
+ * (`isProd ? [auth] : []`), not swap functions. Keep `async` native
10
+ */
6
11
  interface ElysiaAotOptions {
7
12
  /**
8
13
  * Specifier the generated module imports `Compiled` from