From 5e8822e5f2be5f211bf5ae38fd3a80c7850ae671 Mon Sep 17 00:00:00 2001 From: saltyaom Date: Thu, 18 Dec 2025 20:48:13 +0700 Subject: [PATCH 01/12] :broom: chore: thing --- example/a.ts | 21 +++++++++++---------- src/types.ts | 2 ++ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/example/a.ts b/example/a.ts index 670b0bf7..b308ce2d 100644 --- a/example/a.ts +++ b/example/a.ts @@ -1,11 +1,12 @@ -import { Elysia } from '../src' +import { Elysia, t } from '../src' -// This uses aot: true by default in 1.4 (broken on Bun) -const app = new Elysia({ systemRouter: true }) - .get("/", "Hello Elysia") - .get("/json", () => ({ message: "Hello World", timestamp: Date.now() })) - -Bun.serve({ - port: 3000, - fetch: app.fetch -}) +new Elysia() + .model({ + 'character.name': t.String(), + 'character.thing': t.Object({ + name: t.String() + }) + }) + .get('/id/:id/name/:name', ({ params }) => {}, { + params: 'character.thing' + }) diff --git a/src/types.ts b/src/types.ts index b481969b..6073bfb8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -535,6 +535,8 @@ export type UnwrapBodySchema< : unknown : unknown + type A = Prettify> + export interface UnwrapRoute< in out Schema extends InputSchema, in out Definitions extends DefinitionBase['typebox'] = {}, From 038d1b3f3d6eec79d388789e01d964a7f37c9890 Mon Sep 17 00:00:00 2001 From: saltyaom Date: Thu, 18 Dec 2025 20:50:41 +0700 Subject: [PATCH 02/12] :wrench: fix: unwrap params when model is available --- example/a.ts | 4 +--- src/types.ts | 4 +--- test/types/index.ts | 17 +++++++++++++++++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/example/a.ts b/example/a.ts index b308ce2d..bd1c291c 100644 --- a/example/a.ts +++ b/example/a.ts @@ -7,6 +7,4 @@ new Elysia() name: t.String() }) }) - .get('/id/:id/name/:name', ({ params }) => {}, { - params: 'character.thing' - }) + .get('/id/:id/name/:name', ({ params }) => {}) diff --git a/src/types.ts b/src/types.ts index 6073bfb8..de96d88c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -535,8 +535,6 @@ export type UnwrapBodySchema< : unknown : unknown - type A = Prettify> - export interface UnwrapRoute< in out Schema extends InputSchema, in out Definitions extends DefinitionBase['typebox'] = {}, @@ -547,7 +545,7 @@ export interface UnwrapRoute< query: UnwrapSchema params: {} extends Schema['params'] ? ResolvePath - : InputSchema extends Schema + : {} extends Schema ? ResolvePath : UnwrapSchema cookie: UnwrapSchema diff --git a/test/types/index.ts b/test/types/index.ts index 3ea20c9a..2c6e99d6 100644 --- a/test/types/index.ts +++ b/test/types/index.ts @@ -2952,3 +2952,20 @@ type a = keyof {} ) ) } + +// ? params with model +{ + new Elysia() + .model({ + 'character.name': t.String(), + 'character.thing': t.Object({ + name: t.String() + }) + }) + .get('/id/:id/name/:name', ({ params }) => { + expectTypeOf().toEqualTypeOf<{ + id: string + name: string + }>() + }) +} From 875d191074ea8ad3d24647e94335f329d5264402 Mon Sep 17 00:00:00 2001 From: saltyaom Date: Fri, 26 Dec 2025 01:07:13 +0700 Subject: [PATCH 03/12] :tada: feat: ModelValidator.schema --- CHANGELOG.md | 7 ++++++ example/a.ts | 22 ++++++++++++---- src/index.ts | 5 +++- src/schema.ts | 70 ++++++++++++++++++++++++++++++--------------------- src/types.ts | 1 + 5 files changed, 71 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a34570be..16d74a35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 1.4.20 +Improvement: +- add `ModelValidator.schema` for accessing raw schema + +Bug fix: +- `Elysia.models` broke when referencing non typebox model + # 1.4.19 - 13 Dec 2025 Security: - reject invalid cookie signature when using cookie rotation diff --git a/example/a.ts b/example/a.ts index bd1c291c..42351f27 100644 --- a/example/a.ts +++ b/example/a.ts @@ -1,10 +1,22 @@ import { Elysia, t } from '../src' +import { z } from 'zod' -new Elysia() +const app = new Elysia() .model({ - 'character.name': t.String(), - 'character.thing': t.Object({ - name: t.String() + sign: t.Object({ + username: t.String(), + password: t.String() + }), + zodSign: z.object({ + username: z.string(), + password: z.string() }) }) - .get('/id/:id/name/:name', ({ params }) => {}) + +// For TypeBox +// const zodSign = app.models.zodSign.Schema() +// type zodSign = z.infer +// ^? + + +console.log(app.models.sign.schema) diff --git a/src/index.ts b/src/index.ts index 2acceb20..3e823d0b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -467,7 +467,10 @@ export default class Elysia< for (const name of Object.keys(this.definitions.type)) models[name] = getSchemaValidator( - this.definitions.typebox.Import(name as never) + this.definitions.typebox.Import(name as never), + { + models: this.definitions.type + } ) // @ts-expect-error diff --git a/src/schema.ts b/src/schema.ts index cec272ef..8a1ed200 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -18,7 +18,7 @@ import { import { t, type TypeCheck } from './type-system' -import { deepClone, mergeCookie, mergeDeep, randomId } from './utils' +import { mergeCookie, mergeDeep, randomId } from './utils' import { mapValueError } from './error' import type { CookieOptions } from './cookies' @@ -28,11 +28,15 @@ import type { MaybeArray, StandaloneInputSchema, StandardSchemaV1LikeValidate, -UnwrapSchema + UnwrapSchema } from './types' import type { StandardSchemaV1Like } from './types' -import { replaceSchemaTypeFromManyOptions, type ReplaceSchemaTypeOptions, stringToStructureCoercions } from './replace-schema' +import { + replaceSchemaTypeFromManyOptions, + type ReplaceSchemaTypeOptions, + stringToStructureCoercions +} from './replace-schema' type MapValueError = ReturnType @@ -127,12 +131,12 @@ export const hasAdditionalProperties = ( /** * Resolve a schema that might be a model reference (string) to the actual schema */ - export const resolveSchema = ( +export const resolveSchema = ( schema: TAnySchema | string | undefined, models?: Record, modules?: TModule - ): TAnySchema | StandardSchemaV1Like | undefined => { - if (!schema) return undefined +): TAnySchema | StandardSchemaV1Like | undefined => { + if (!schema) return undefined if (typeof schema !== 'string') return schema // Check modules first (higher priority) @@ -143,7 +147,7 @@ export const hasAdditionalProperties = ( // Then check models return models?.[schema] - } +} export const hasType = (type: string, schema: TAnySchema): boolean => { if (!schema) return false @@ -165,7 +169,11 @@ export const hasType = (type: string, schema: TAnySchema): boolean => { if (schema.allOf) return schema.allOf.some((s: TSchema) => hasType(type, s)) if (schema.type === 'array' && schema.items) { - if (type === 'Files' && Kind in schema.items && schema.items[Kind] === 'File') { + if ( + type === 'Files' && + Kind in schema.items && + schema.items[Kind] === 'File' + ) { return true } return hasType(type, schema.items) @@ -458,18 +466,20 @@ export const getSchemaValidator = < if (!schema) return undefined as any } - const hasAdditionalCoerce = Array.isArray(additionalCoerce) ? - additionalCoerce.length > 0 : !!additionalCoerce + const hasAdditionalCoerce = Array.isArray(additionalCoerce) + ? additionalCoerce.length > 0 + : !!additionalCoerce + if (Kind in schema) { if (schema[Kind] === 'Import') { if (!hasRef(schema.$defs[schema.$ref])) { - schema = schema.$defs[schema.$ref] + schema = schema.$defs[schema.$ref] ?? models[schema.$ref] if (coerce || hasAdditionalCoerce) { schema = replaceSchema(schema as TSchema) - if ('$id' in schema && !schema.$defs) { - schema.$id = `${schema.$id}_coerced_${randomId()}`; - } + + if ('$id' in schema && !schema.$defs) + schema.$id = `${schema.$id}_coerced_${randomId()}` } } } else { @@ -492,6 +502,7 @@ export const getSchemaValidator = < } let schema = mapSchema(s) + // console.log([s, schema]) let _validators = validators if ( @@ -695,19 +706,19 @@ export const getSchemaValidator = < ) schema.additionalProperties = additionalProperties else - schema = replaceSchemaTypeFromManyOptions(schema, { - onlyFirst: "object", - from: t.Object({}), - to(schema) { - if (!schema.properties) return schema; - if ("additionalProperties" in schema) return schema; - - return t.Object(schema.properties, { - ...schema, - additionalProperties: false, - }); - } - }); + schema = replaceSchemaTypeFromManyOptions(schema, { + onlyFirst: 'object', + from: t.Object({}), + to(schema) { + if (!schema.properties) return schema + if ('additionalProperties' in schema) return schema + + return t.Object(schema.properties, { + ...schema, + additionalProperties: false + }) + } + }) } if (dynamic) { @@ -1097,7 +1108,10 @@ export const mergeObjectSchemas = ( ...newSchema.properties, ...schema.properties }, - required: [...(newSchema?.required ?? []), ...(schema.required ?? [])] + required: [ + ...(newSchema?.required ?? []), + ...(schema.required ?? []) + ] } as TObject } diff --git a/src/types.ts b/src/types.ts index de96d88c..0d539a45 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2355,6 +2355,7 @@ export interface ModelValidatorError extends ValueError { // @ts-ignore trust me bro export interface ModelValidator extends TypeCheck { + schema: T parse(a: T): T safeParse(a: T): | { success: true; data: T; error: null } From 42d6eecba3fb4e5b9af98225128b232bef5ce844 Mon Sep 17 00:00:00 2001 From: saltyaom Date: Fri, 26 Dec 2025 22:22:00 +0700 Subject: [PATCH 04/12] :wrench: fix: 1.4.20 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 34e84ba4..333fd708 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "elysia", "description": "Ergonomic Framework for Human", - "version": "1.4.19", + "version": "1.4.20", "author": { "name": "saltyAom", "url": "https://github.com/SaltyAom", From ec61f1115c05418174805b793be9f26900ee9bf8 Mon Sep 17 00:00:00 2001 From: saltyaom Date: Sun, 28 Dec 2025 23:13:26 +0700 Subject: [PATCH 05/12] :wrench: fix: _r_r is not defined --- CHANGELOG.md | 3 +++ example/a.ts | 28 ++++++++++++---------------- src/compose.ts | 4 ++-- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16d74a35..da5ae0cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ Improvement: - add `ModelValidator.schema` for accessing raw schema +Bug fix: +- [#1639](https://github.com/elysiajs/elysia/issues/1639) compose: ReferenceError: "_r_r is not defined" when onError returns plain object & mapResponse exists + Bug fix: - `Elysia.models` broke when referencing non typebox model diff --git a/example/a.ts b/example/a.ts index 42351f27..a93ac3aa 100644 --- a/example/a.ts +++ b/example/a.ts @@ -1,22 +1,18 @@ import { Elysia, t } from '../src' import { z } from 'zod' +import { req } from '../test/utils' const app = new Elysia() - .model({ - sign: t.Object({ - username: t.String(), - password: t.String() - }), - zodSign: z.object({ - username: z.string(), - password: z.string() - }) + .onError(() => { + // Condition 1: onError returns a plain object (not a Response instance) + return { hello: 'world' } }) + .mapResponse(({ responseValue }) => { + return new Response('A') + }) + .get('/', () => 'hello') + .listen(3000) -// For TypeBox -// const zodSign = app.models.zodSign.Schema() -// type zodSign = z.infer -// ^? - - -console.log(app.models.sign.schema) +app.handle(req('/')) + .then(x => x.text()) + .then(console.log) diff --git a/src/compose.ts b/src/compose.ts index 2c50053c..f78444dc 100644 --- a/src/compose.ts +++ b/src/compose.ts @@ -2658,7 +2658,7 @@ export const composeErrorHandler = (app: AnyElysia) => { `return mapResponse(_r,set${adapter.mapResponseContext})}` + `if(_r instanceof ElysiaCustomStatusResponse){` + `error.status=error.code\n` + - `error.message = error.response` + + `error.message=error.response` + `}` + `if(set.status===200||!set.status)set.status=error.status\n` @@ -2676,7 +2676,7 @@ export const composeErrorHandler = (app: AnyElysia) => { ) fnLiteral += - `context.response=context.responseValue=_r` + + `context.response=context.responseValue=_r\n` + `_r=${isAsyncName(mapResponse) ? 'await ' : ''}onMapResponse[${i}](context)\n` endUnit() From ee8b2bf27151d902f10eb7852fab1cb7d5c20355 Mon Sep 17 00:00:00 2001 From: saltyaom Date: Sun, 28 Dec 2025 23:14:16 +0700 Subject: [PATCH 06/12] :blue_book: doc: document change --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index da5ae0cf..a5ec63ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # 1.4.20 Improvement: - add `ModelValidator.schema` for accessing raw schema +- [#1638](https://github.com/elysiajs/elysia/pull/1638) unref Sucrose gc Bug fix: - [#1639](https://github.com/elysiajs/elysia/issues/1639) compose: ReferenceError: "_r_r is not defined" when onError returns plain object & mapResponse exists From 0ba69d925194c97bc9e3c0093f0925190c758f6f Mon Sep 17 00:00:00 2001 From: saltyaom Date: Sat, 3 Jan 2026 20:25:58 +0700 Subject: [PATCH 07/12] :wrench: fix: #1636 add subscription to Elysia.ws context --- CHANGELOG.md | 1 + bun.lock | 11 ++++------- example/a.ts | 25 +++++++++++++------------ package.json | 2 +- src/ws/bun.ts | 10 ++++++++++ src/ws/index.ts | 9 +++++++-- 6 files changed, 36 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5ec63ce..449a082d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # 1.4.20 Improvement: - add `ModelValidator.schema` for accessing raw schema +- [#1636](https://github.com/elysiajs/elysia/issues/1636) add `subscriptions` to `Elysia.ws` context - [#1638](https://github.com/elysiajs/elysia/pull/1638) unref Sucrose gc Bug fix: diff --git a/bun.lock b/bun.lock index 10083783..90b13296 100644 --- a/bun.lock +++ b/bun.lock @@ -11,9 +11,8 @@ "memoirist": "^0.4.0", }, "devDependencies": { - "@elysiajs/cors": "^1.4.0", "@elysiajs/openapi": "^1.4.1", - "@types/bun": "^1.2.12", + "@types/bun": "^1.3.5", "@types/cookie": "1.0.0", "@types/fast-decode-uri-component": "^1.0.0", "@typescript-eslint/eslint-plugin": "^8.30.1", @@ -34,7 +33,7 @@ }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", - "@types/bun": ">= 1.2.0", + "@types/bun": "^1.3.3", "exact-mirror": ">= 0.0.9", "file-type": ">= 20.0.0", "openapi-types": ">= 12.0.0", @@ -56,8 +55,6 @@ "@borewit/text-codec": ["@borewit/text-codec@0.1.1", "", {}, "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA=="], - "@elysiajs/cors": ["@elysiajs/cors@1.4.0", "", { "peerDependencies": { "elysia": ">= 1.4.0" } }, "sha512-pb0SCzBfFbFSYA/U40HHO7R+YrcXBJXOWgL20eSViK33ol1e20ru2/KUaZYo5IMUn63yaTJI/bQERuQ+77ND8g=="], - "@elysiajs/openapi": ["@elysiajs/openapi@1.4.11", "", { "peerDependencies": { "elysia": ">= 1.4.0" } }, "sha512-d75bMxYJpN6qSDi/z9L1S7SLk1S/8Px+cTb3W2lrYzU8uQ5E0kXdy1oOMJEfTyVsz3OA19NP9KNxE7ztSbLBLg=="], "@emnapi/core": ["@emnapi/core@1.7.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg=="], @@ -248,7 +245,7 @@ "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], - "@types/bun": ["@types/bun@1.3.3", "", { "dependencies": { "bun-types": "1.3.3" } }, "sha512-ogrKbJ2X5N0kWLLFKeytG0eHDleBYtngtlbu9cyBKFtNL3cnpDZkNdQj8flVf6WTZUX5ulI9AY1oa7ljhSrp+g=="], + "@types/bun": ["@types/bun@1.3.5", "", { "dependencies": { "bun-types": "1.3.5" } }, "sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w=="], "@types/cookie": ["@types/cookie@1.0.0", "", { "dependencies": { "cookie": "*" } }, "sha512-mGFXbkDQJ6kAXByHS7QAggRXgols0mAdP4MuXgloGY1tXokvzaFFM4SMqWvf7AH0oafI7zlFJwoGWzmhDqTZ9w=="], @@ -304,7 +301,7 @@ "builtin-modules": ["builtin-modules@3.3.0", "", {}, "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw=="], - "bun-types": ["bun-types@1.3.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-z3Xwlg7j2l9JY27x5Qn3Wlyos8YAp0kKRlrePAOjgjMGS5IG6E7Jnlx736vH9UVI4wUICwwhC9anYL++XeOgTQ=="], + "bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="], "bundle-require": ["bundle-require@5.1.0", "", { "dependencies": { "load-tsconfig": "^0.2.3" }, "peerDependencies": { "esbuild": ">=0.18" } }, "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA=="], diff --git a/example/a.ts b/example/a.ts index a93ac3aa..3d880fde 100644 --- a/example/a.ts +++ b/example/a.ts @@ -2,17 +2,18 @@ import { Elysia, t } from '../src' import { z } from 'zod' import { req } from '../test/utils' -const app = new Elysia() - .onError(() => { - // Condition 1: onError returns a plain object (not a Response instance) - return { hello: 'world' } +export const app = new Elysia() + .ws('/', { + message(a) { + a.subscriptions + } }) - .mapResponse(({ responseValue }) => { - return new Response('A') - }) - .get('/', () => 'hello') - .listen(3000) -app.handle(req('/')) - .then(x => x.text()) - .then(console.log) +Bun.serve({ + fetch() {}, + websocket: { + message(a) { + a.subscriptions + } + } +}) diff --git a/package.json b/package.json index 333fd708..24a472ee 100644 --- a/package.json +++ b/package.json @@ -197,7 +197,7 @@ }, "devDependencies": { "@elysiajs/openapi": "^1.4.1", - "@types/bun": "^1.2.12", + "@types/bun": "^1.3.5", "@types/cookie": "1.0.0", "@types/fast-decode-uri-component": "^1.0.0", "@typescript-eslint/eslint-plugin": "^8.30.1", diff --git a/src/ws/bun.ts b/src/ws/bun.ts index 690c1767..a72ba9e6 100644 --- a/src/ws/bun.ts +++ b/src/ws/bun.ts @@ -236,6 +236,16 @@ export interface ServerWebSocket { */ isSubscribed(topic: string): boolean + /** + * Returns an array of all topics the client is currently subscribed to. + * + * @example + * ws.subscribe("chat"); + * ws.subscribe("notifications"); + * console.log(ws.subscriptions); // ["chat", "notifications"] + */ + readonly subscriptions: string[] + /** * Batches `send()` and `publish()` operations, which makes it faster to send data. * diff --git a/src/ws/index.ts b/src/ws/index.ts index 54f27dcd..7c7aaaa5 100644 --- a/src/ws/index.ts +++ b/src/ws/index.ts @@ -69,6 +69,7 @@ export class ElysiaWS this.remoteAddress = raw.remoteAddress this.binaryType = raw.binaryType this.data = raw.data as any + this.subscriptions = raw.subscriptions this.send = this.send.bind(this) this.ping = this.ping.bind(this) @@ -190,6 +191,7 @@ export class ElysiaWS cork: ServerWebSocket['cork'] remoteAddress: ServerWebSocket['remoteAddress'] binaryType: ServerWebSocket['binaryType'] + subscriptions: ServerWebSocket['subscriptions'] get readyState() { return this.raw.readyState @@ -286,8 +288,11 @@ export const createHandleWSResponse = ( if (validateResponse && validateResponse(first)) return ws.send( - new ValidationError('message', responseValidator!, first) - .message + new ValidationError( + 'message', + responseValidator!, + first + ).message ) send(first.value as any) From cf79a9d8e6ff00844c27a476cd452b4af5e66f65 Mon Sep 17 00:00:00 2001 From: saltyaom Date: Sat, 3 Jan 2026 20:33:25 +0700 Subject: [PATCH 08/12] :wrench: fix: #1630 enforce fetch to return MaybePromise --- CHANGELOG.md | 1 + example/a.ts | 9 +-------- src/compose.ts | 5 ++++- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 449a082d..5178ae49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Improvement: Bug fix: - [#1639](https://github.com/elysiajs/elysia/issues/1639) compose: ReferenceError: "_r_r is not defined" when onError returns plain object & mapResponse exists +- [#1630](https://github.com/elysiajs/elysia/issues/1630) enforce fetch to return MaybePromise Bug fix: - `Elysia.models` broke when referencing non typebox model diff --git a/example/a.ts b/example/a.ts index 3d880fde..46b7676b 100644 --- a/example/a.ts +++ b/example/a.ts @@ -9,11 +9,4 @@ export const app = new Elysia() } }) -Bun.serve({ - fetch() {}, - websocket: { - message(a) { - a.subscriptions - } - } -}) +const a = app.fetch(req('/')) diff --git a/src/compose.ts b/src/compose.ts index f78444dc..df891a1d 100644 --- a/src/compose.ts +++ b/src/compose.ts @@ -60,6 +60,7 @@ import type { Handler, HookContainer, LifeCycleStore, + MaybePromise, SchemaValidator } from './types' import { tee } from './adapter/utils' @@ -2308,7 +2309,9 @@ export const createHoc = (app: AnyElysia, fnName = 'map') => { return `return function hocMap(${adapter.parameters}){return ${handler}(${adapter.parameters})}` } -export const composeGeneralHandler = (app: AnyElysia) => { +export const composeGeneralHandler = ( + app: AnyElysia +): ((request: Request) => MaybePromise) => { const adapter = app['~adapter'].composeGeneralHandler app.router.http.build() From 443eef236a2b95b18807a702ca41c33a29d1591d Mon Sep 17 00:00:00 2001 From: saltyaom Date: Sat, 3 Jan 2026 20:49:51 +0700 Subject: [PATCH 09/12] :wrench: fix: #1631 update exact mirror tot 0.2.6 --- CHANGELOG.md | 1 + bun.lock | 6 +++--- package.json | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5178ae49..eccb6443 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Improvement: Bug fix: - [#1639](https://github.com/elysiajs/elysia/issues/1639) compose: ReferenceError: "_r_r is not defined" when onError returns plain object & mapResponse exists +- [#1631](https://github.com/elysiajs/elysia/issues/1631) update Exact Mirror to 0.2.6 - [#1630](https://github.com/elysiajs/elysia/issues/1630) enforce fetch to return MaybePromise Bug fix: diff --git a/bun.lock b/bun.lock index 90b13296..96efb547 100644 --- a/bun.lock +++ b/bun.lock @@ -6,7 +6,7 @@ "name": "elysia", "dependencies": { "cookie": "^1.1.1", - "exact-mirror": "0.2.5", + "exact-mirror": "^0.2.6", "fast-decode-uri-component": "^1.0.1", "memoirist": "^0.4.0", }, @@ -33,7 +33,7 @@ }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", - "@types/bun": "^1.3.3", + "@types/bun": ">= 1.2.0", "exact-mirror": ">= 0.0.9", "file-type": ">= 20.0.0", "openapi-types": ">= 12.0.0", @@ -363,7 +363,7 @@ "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], - "exact-mirror": ["exact-mirror@0.2.5", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-u8Wu2lO8nio5lKSJubOydsdNtQmH8ENba5m0nbQYmTvsjksXKYIS1nSShdDlO8Uem+kbo+N6eD5I03cpZ+QsRQ=="], + "exact-mirror": ["exact-mirror@0.2.6", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-7s059UIx9/tnOKSySzUk5cPGkoILhTE4p6ncf6uIPaQ+9aRBQzQjc9+q85l51+oZ+P6aBxh084pD0CzBQPcFUA=="], "expect-type": ["expect-type@1.2.2", "", {}, "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA=="], diff --git a/package.json b/package.json index 24a472ee..cbbaf63f 100644 --- a/package.json +++ b/package.json @@ -191,7 +191,7 @@ }, "dependencies": { "cookie": "^1.1.1", - "exact-mirror": "0.2.5", + "exact-mirror": "^0.2.6", "fast-decode-uri-component": "^1.0.1", "memoirist": "^0.4.0" }, From 127bc9386e4ece0afa80dd574261c97f1cfa81bd Mon Sep 17 00:00:00 2001 From: saltyaom Date: Sat, 3 Jan 2026 21:12:31 +0700 Subject: [PATCH 10/12] :tada: feat: 1.4.20 --- CHANGELOG.md | 2 +- example/a.ts | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eccb6443..736fff41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# 1.4.20 +# 1.4.20 - 3 Jan 2026 Improvement: - add `ModelValidator.schema` for accessing raw schema - [#1636](https://github.com/elysiajs/elysia/issues/1636) add `subscriptions` to `Elysia.ws` context diff --git a/example/a.ts b/example/a.ts index 46b7676b..175c5bac 100644 --- a/example/a.ts +++ b/example/a.ts @@ -4,9 +4,11 @@ import { req } from '../test/utils' export const app = new Elysia() .ws('/', { + open(ws) { + ws.subscribe('a') + }, message(a) { - a.subscriptions + console.log(a.subscriptions) } }) - -const a = app.fetch(req('/')) + .listen(3000) From 7337ba741d681ddcd2fe1a3593bdb154b8b5e6c5 Mon Sep 17 00:00:00 2001 From: saltyaom Date: Sat, 3 Jan 2026 21:19:35 +0700 Subject: [PATCH 11/12] :blue_book: doc: document changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 736fff41..f44c5a60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ Improvement: - [#1638](https://github.com/elysiajs/elysia/pull/1638) unref Sucrose gc Bug fix: +- [#1649](https://github.com/elysiajs/elysia/pull/1649) add null check for object properties (t.Record) +- [#1646](https://github.com/elysiajs/elysia/pull/1646) use constant-time comparison for signed cookie verification - [#1639](https://github.com/elysiajs/elysia/issues/1639) compose: ReferenceError: "_r_r is not defined" when onError returns plain object & mapResponse exists - [#1631](https://github.com/elysiajs/elysia/issues/1631) update Exact Mirror to 0.2.6 - [#1630](https://github.com/elysiajs/elysia/issues/1630) enforce fetch to return MaybePromise From 87c7836e7ff5d6a9bb3e5e3de81c5e0b9439e5d0 Mon Sep 17 00:00:00 2001 From: saltyaom Date: Sat, 3 Jan 2026 21:23:49 +0700 Subject: [PATCH 12/12] :wrench: fix: using crypto namepsace instead of import for cf --- src/utils.ts | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index 81608510..761c167b 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,6 +1,5 @@ import type { Sucrose } from './sucrose' import type { TraceHandler } from './trace' -import { timingSafeEqual } from 'crypto' import type { LifeCycleStore, @@ -686,21 +685,23 @@ export const signCookie = async (val: string, secret: string | null) => { ) } -const constantTimeEqual = (a: string, b: string) => { - // Compare as UTF-8 bytes; timingSafeEqual requires equal length - const ab = Buffer.from(a, 'utf8') - const bb = Buffer.from(b, 'utf8') +const constantTimeEqual = + typeof crypto?.timingSafeEqual === 'function' + ? (a: string, b: string) => { + // Compare as UTF-8 bytes; timingSafeEqual requires equal length + const ab = Buffer.from(a, 'utf8') + const bb = Buffer.from(b, 'utf8') - if (ab.length !== bb.length) return false - return timingSafeEqual(ab, bb) -} + if (ab.length !== bb.length) return false + return crypto.timingSafeEqual(ab, bb) + } + : (a: string, b: string) => a === b export const unsignCookie = async (input: string, secret: string | null) => { if (typeof input !== 'string') throw new TypeError('Signed cookie string must be provided.') - if (secret === null) - throw new TypeError('Secret key must be provided.') + if (secret === null) throw new TypeError('Secret key must be provided.') const dot = input.lastIndexOf('.') if (dot <= 0) return false