Skip to content
Blog

Elysia 1.0 - Lament of the Fallen

Dreamy Euphony landscape of floating bubble

Elysia 1.0 是经过 1.8 年开发后的第一个稳定版本。

¥Elysia 1.0 is the first stable release after development for 1.8 years.

自创立以来,我们一直在等待一个专注于开发者体验、速度以及如何让代码为人而非机器编写的框架。

¥Since we started, we have always been waiting for a framework that focuses on developer experience, velocity, and how to make writing code for humans, not a machine.

我们在各种情况下对 Elysia 进行了实战测试,模拟了中型和大型项目,并将代码交付给客户,这是我们有足够信心发布的第一个版本。

¥We battle-test Elysia in various situations, simulate medium and large-scale projects, shipping code to clients and this is the first version that we felt confident enough to ship.

Elysia 1.0 引入了重大改进,并包含 1 项必要的重大变更。

¥Elysia 1.0 introduces significant improvements and contains 1 necessary breaking change.


Elysia 的发行说明中通常会有一个以歌曲或媒体命名的版本,这已成为传统。

¥It's a tradition that Elysia's release note have a version named after a song or media.

这个重要版本以 "逝者的哀歌" 命名。

¥This important version is named after "Lament of the Fallen".

来自我最喜欢的系列 "崩坏 3rd" 的动画短片,以及我最喜欢的角色 "雷电之谜",以及她的主题曲 "崩坏世界女神"

¥Animated short from "Honkai Impact 3rd" from my favorite arc, and my favorite character, "Raiden Mei" featuring her theme song, "Honkai World Diva".

这是一款非常棒的游戏,你应该去体验一下。

¥It's a very good game, and you should check it out.

ー SaltyAom

又名《枪少女 Z》、《崩坏 3》、《崩坏星轨》中的雷电魅。她的 "variation",来自《原神》的雷电将军,以及可能来自《崩坏星轨》的阿克伦(因为她很可能是星轨 2.1 中提到的坏结局的律者形态)。

¥Also known as Raiden Mei from Gun Girl Z, Honkai Impact 3rd, Honkai Star Rail. And her "variation", Raiden Shogun from Genshin Impact, and possibly Acheron from Honkai Star Rail (since she's likely a bad-end herrscher form mentioned in Star Rail 2.1).

提示

请记住,ElysiaJS 是一个由志愿者维护的开源库,与米哈游和 Hoyoverse 无关。但是我们是《崩坏》系列的忠实粉丝,对吧?

¥Remember, ElysiaJS is an open source library maintain by volunteers, and isn't associate with Mihoyo nor Hoyoverse. But we are a huge fan of Honkai series, alright?

Sucrose

Elysia 经过优化,在各种基准测试中均表现出色,这主要归功于 Bun 和我们自定义的 JIT 静态代码分析。

¥Elysia is optimized to have an excellent performance proven in various benchmarks, one of the main factors is thanks to Bun, and our custom JIT static code analysis.

如果你不知道,Elysia 嵌入了某种 "compiler",可以读取你的代码并生成优化的函数处理方式。

¥If you are not aware, Elysia has some sort of "compiler" embedded that reads your code and produces an optimized way to handle functions.

该过程快速且即时发生,无需构建步骤。然而,由于它主要用许多复杂的正则表达式编写,维护起来很有挑战性,而且如果发生递归,速度有时会很慢。

¥The process is fast and happens on the fly without a need for a build step. However, it's challenging to maintain as it's written mostly in many complex RegEx, and can be slow at times if recursion happens.

这就是为什么我们重写了静态分析部分,使用基于部分 AST 和模式匹配名称 "Sucrose" 的混合方法将代码注入阶段分离。

¥That's why we rewrote our static analysis part to separate the code injection phase using a hybrid approach between partial AST-based and pattern-matching name "Sucrose".

与其使用更准确的基于 AST 的完整模型,不如选择仅实现一部分需要的规则来提高性能,因为它需要在运行时快速运行。

¥Instead of using full AST-based which is more accurate, we choose to implement only a subset of rules that is needed to improve performance as it needs to be fast on runtime.

Sucrose 擅长准确推断处理函数的递归属性,同时内存占用率低,推断时间最高可提高 37%,内存占用率也显著降低。

¥Sucrose is good at inferring the recursive property of the handler function accurately with low memory usage, resulting in up to 37% faster inference time and significantly reduced memory usage.

从 Elysia 1.0 开始,Sucrose 旨在取代基于正则表达式的部分抽象语法树 (AST) 和模式匹配。

¥Sucrose is shipped to replace RegEx-based to partial AST, and pattern matching starting from Elysia 1.0.

启动时间缩短

¥Improved Startup time

得益于 Sucrose 以及与动态注入阶段的分离,我们可以将分析时间推迟到 JIT 而不是 AOT。

¥Thanks to Sucrose, and separation from the dynamic injection phase, we can defer the analysis time JIT instead of AOT.

换句话说,"compile" 阶段可以延迟求值。

¥In other words, the "compile" phase can be lazily evaluated.

在路由首次匹配时,将评估阶段从 AOT 转移到 JIT,并将结果缓存起来以便按需编译,而不是在服务器启动前对所有路由进行编译。

¥Offloading the evaluation phase from AOT to JIT when a route is matched for the first time and caching the result to compile on demand instead of all routes before server start.

在运行时性能方面,单次编译通常很快,耗时不超过 0.01-0.03 毫秒(毫秒,而不是秒)。

¥In a runtime performance, a single compilation is usually fast and takes no longer than 0.01-0.03 ms (millisecond not second).

在中型应用和压力测试中,我们测得启动时间加快了约 6.5-14 倍。

¥In a medium-sized application and stress test, we measure up to between ~6.5-14x faster start-up time.

移除每个实例约 40 条路由的限制

¥Remove ~40 routes/instance limit

从 Elysia 0.1 开始,每个 Elysia 实例最多只能堆叠约 40 条路由。

¥Previously you could only stack up to ~40 routes / 1 Elysia instance since Elysia 0.1.

这是 TypeScript 的一个限制,即每个队列的内存都是有限的,如果超出内存限制,TypeScript 会认为是 "类型实例化过深,可能无限"。

¥This is the limitation of TypeScript that each queue that has a limited memory and if exceeded, TypeScript will think that "Type instantiation is excessively deep and possibly infinite".

typescript
const main = new Elysia()
    .get('/1', () => '1')
    .get('/2', () => '2')
    .get('/3', () => '3')
    // repeat for 40 times
    .get('/42', () => '42')
    // Type instantiation is excessively deep and possibly infinite

作为一种解决方法,我们需要将实例分离到控制器中以克服限制,并重新合并类型以像这样卸载队列。

¥As a workaround, we need to separate an instance into a controller to overcome the limit and remerge the type to offload the queue like this.

typescript
const controller1 = new Elysia()
    .get('/42', () => '42')
    .get('/43', () => '43')

const main = new Elysia()
    .get('/1', () => '1')
    .get('/2', () => '2')
    // repeat for 40 times
    .use(controller1)

然而,从 Elysia 1.0 开始,经过一年的优化,我们克服了类型性能限制,特别是尾调用优化和方差。

¥However, starting from Elysia 1.0, we have overcome the limit after a year after optimizing for type-performance, specifically Tail Call Optimization, and variances.

这意味着理论上,我们可以堆叠无限数量的路由和方法,直到 TypeScript 崩溃。

¥This means theoretically, we can stack an unlimited amount of routes and methods until TypeScript breaks.

(剧透:我们已经这样做了,在使用 TypeScript CLI 和语言服务器之前,由于每个堆栈/队列的 JavaScript 内存限制,每个实例大约有 558 个路由)

¥(spoiler: we have done that and it's around 558 routes/instance before TypeScript CLI and language server because of JavaScript memory limit per stack/queue)

typescript
const main = new Elysia()
    .get('/1', () => '1')
    .get('/2', () => '2')
    .get('/3', () => '42')
    // repeat for n times
    .get('/550', () => '550')

因此,我们将约 40 条路由的限制改为 JavaScript 内存限制,因此请尽量不要堆叠超过约 558 条路由/实例,并在必要时将其拆分成插件。

¥So we increase the limit of ~40 routes to JavaScript memory limit instead, so try not to stack more than ~558 routes/instance, and separate into a plugin if necessary.

TypeScript breaks on 558 routes

让我们觉得 Elysia 尚未准备好投入生产的障碍终于得到了解决。

¥The blocker that made us feel like Elysia is not ready for production has been finally resolved.

类型推断改进

¥Type Inference improvement

得益于我们为优化所付出的努力,在大多数 Elysia 服务器中,我们的测试结果高达约 82%。

¥Thanks to the effort we put into optimization, we measure up to ~82% in most Elysia servers.

由于移除了堆栈限制,并提升了类型性能,即使在 500 条路由堆栈之后,我们仍然可以实现几乎即时的类型检查和自动补全。

¥Thanks to the removed limitation of stack, and improved type performance, we can expect almost instant type check and auto-completion even after 500 routes stacks.

Eden Treaty 的类型推断性能最高可提升 13 倍,通过预先计算类型而不是将类型重映射到 Eden 来实现。

¥Up to 13x faster for Eden Treaty, type inference performance by precomputing the type instead offload type remap to Eden.

总体而言,Elysia 和 Eden Treaty 结合使用时,速度最高可提高 3.9 倍。

¥Overall, Elysia, and Eden Treaty performing together would be up to ~3.9x faster.

以下是 0.8 和 1.0 版本中 Elysia + Eden Treaty 在 450 条路线上的比较。

¥Here's a comparison between the Elysia + Eden Treaty on 0.8 and 1.0 for 450 routes.

Type performance comparison between Elysia Eden 0.8 and 1.0, the graph shows that Elysia 0.8 took ~1500ms while Elysia 1.0 took ~400ms

使用 Eden Treaty 对 Elysia 进行 450 条路由的压力测试,结果如下:

¥Stress test with 450 routes for Elysia with Eden Treaty, result as follows:

  • Elysia 0.8 耗时约 1500 毫秒

  • Elysia 1.0 耗时约 400 毫秒

由于移除了堆栈限制和重新映射过程,现在可以为单个 Eden Treaty 实例堆叠多达 1,000 条路由。

¥And thanks to the removal of stack limitation, and remapping process, it's now possible to stack up to over 1,000 routes for a single Eden Treaty instance.

条约 2

¥Treaty 2

我们诚邀你就《Eden 条约》提供反馈,了解你喜欢哪些方面以及哪些方面可以改进。你已经向我们指出了 Treaty 设计中的一些缺陷以及一些改进建议。

¥We ask you for feedback on Eden Treaty what you like and what could have been improved. and you have given us some flaws in Treaty design and several proposals to improvement.

因此,今天,我们推出了《Eden 条约 2》,它经过全面改进,设计更加符合人机工程学。

¥That's why today, we introduce Eden Treaty 2, an overhaul to a more ergonomic design.

尽管我们不喜欢重大变更,但《条约 2》是《条约 1》的继承者。

¥As much as we dislike breaking change, Treaty 2 is a successor to Treaty 1.

Treaty 2 中的新功能:

¥What's new in Treaty 2:

  • 更符合人机工程学的语法

  • 单元测试的端到端类型安全

  • 拦截器

  • 无需 "$" 前缀和属性

我们最喜欢的是单元测试的端到端类型安全。

¥Our favorite one is end-to-end type safety for Unit tests.

因此,我们可以使用 Eden Treaty 2 编写具有自动补齐和类型安全的单元测试,而不是启动模拟服务器并发送获取请求。

¥So instead of starting a mock server and sending a fetch request, we can use Eden Treaty 2 to write unit tests with auto-completion and type safety instead.

typescript
// test/index.test.ts
import { describe, expect, it } from 'bun:test'
import { Elysia } from 'elysia'
import { treaty } from '@elysiajs/eden'

const app = new Elysia().get('/hello', () => 'hi')
const api = treaty(app)

describe('Elysia', () => {
    it('return a response', async () => {
        const { data } = await api.hello.get()

        expect(data).toBe('hi')
    })
})

两者的区别在于,Treaty 2 继承了 Treaty 1。

¥The difference between the two is that Treaty 2 is a successor to Treaty 1.

我们不打算对 Treaty 1 进行任何重大更改,也不会强迫你更新到 Treaty 2。

¥We don't intend to introduce any breaking change to Treaty 1 nor force you to update to Treaty 2.

你可以选择继续在当前项目中使用 Treaty 1,而无需更新到 Treaty 2,我们会将其保持在维护模式。

¥You can choose to continue using Treaty 1 for your current project without updating to Treaty 2, and we maintain it in a maintenance mode.

  • 你可以导入 treaty 来使用 Treaty 2。

  • 然后导入 Treaty 1 的 edenTreaty 文件。

新条约的文档可在 Treaty 概述 中找到,条约 1 的文档可在 Treaty 遗留问题 中找到。

¥The documentation for the new Treaty can be found in Treaty overview, and for Treaty 1 in Treaty legacy

钩子类型(重大更改)

¥Hook type (breaking change)

我们讨厌破坏性变更,这是我们第一次大规模地进行此类变更。

¥We hate breaking changes, and this is the first time we do it in large-scale.

我们在 API 设计上投入了大量精力,以减少对 Elysia 的更改,但这对于修复有缺陷的设计是必要的。

¥We put a lot of effort into API design to reduce changes made to Elysia, but this is necessary to fix a flawed design.

之前当我们添加带有 "on" 的钩子(例如 onTransformonBeforeHandle)时,它会变成全局钩子。

¥Previously when we added a hook with "on" like onTransform, or onBeforeHandle, it would become a global hook.

这对于创建类似插件的东西非常有用,但对于像控制器这样的本地实例来说并不理想。

¥This is great for creating something like a plugin but is not ideal for a local instance like a controller.

typescript
const plugin = new Elysia()
    .onBeforeHandle(() => {
        console.log('Hi')
    })
    // log Hi
    .get('/hi', () => 'in plugin')

const app = new Elysia()
    .use(plugin)
    // will also log hi
    .get('/no-hi-please', () => 'oh no')

但是,我们发现这种行为引发了一些问题。

¥However, we found several problems arise from this behavior.

  • 我们发现许多开发者即使在新实例上也有很多嵌套的保护。Guard 几乎可以用作启动新实例以避免副作用的一种方式。

  • 如果不小心,默认使用全局可能会导致不可预测的(副作用)行为,尤其是在开发者经验不足的团队中。

  • 我们咨询了许多熟悉和不熟悉 Elysia 的开发者,发现大多数人最初都希望 hook 是本地的。

  • 根据上一点,我们发现,如果不仔细检查,默认将钩子设置为全局钩子很容易导致意外错误(副作用),并且难以调试和观察。


为了解决这个问题,我们引入了一个钩子类型,通过引入 "hook-type" 来指定钩子应该如何继承。

¥To fix this, we introduce a hook type to specify how the hook should be inherited by introducing a "hook-type".

钩子类型可分为以下几类:

¥Hook types can be classified as follows:

  • 本地(默认) - 仅应用于当前实例和后代

  • scoped - 仅应用于 1 个上级、当前实例和后代

  • 全局(旧行为) - 应用于所有应用该插件的实例(所有上级、当前实例和后代)

要指定钩子的类型,只需在钩子中添加 { as: hookType } 即可。

¥To specify the hook's type, simply add a { as: hookType } to the hook.

typescript
const plugin = new Elysia()
    .onBeforeHandle(() => { 
    .onBeforeHandle({ as: 'global' }, () => { 
        console.log('hi')
    })
    .get('/child', () => 'log hi')

const main = new Elysia()
    .use(plugin)
    .get('/parent', () => 'log hi')

此 API 旨在解决 Elysia 的守卫嵌套问题,此前开发者由于担心副作用而不敢在根实例上引入钩子。

¥This API is designed to fix the guard nesting problem for Elysia, where developers are afraid to introduce a hook on root instances because of fear of side effects.

例如,要为整个实例创建身份验证检查,我们需要将路由封装在守卫中。

¥For example, to create an authentication check for an entire instance, we need to wrap a route in a guard.

typescript
const plugin = new Elysia()
    .guard((app) =>
        app
            .onBeforeHandle(checkAuthSomehow)
            .get('/profile', () => 'log hi')
    )

但是,随着钩子类型的引入,我们可以删除嵌套的守卫样板。

¥However, with the introduction of hook type, we can remove the nesting guard boilerplate.

typescript
const plugin = new Elysia()
    .guard((app) =>
        app 
            .onBeforeHandle(checkAuthSomehow)
            .get('/profile', () => 'log hi')
    ) 

Hook 类型将指定 Hook 的继承方式,让我们创建一个插件来说明 Hook 类型的工作原理。

¥Hook type will specify how the hook should be inherited, let's create a plugin to illustrate how hook type works.

typescript
// ? Value based on table value provided below
const type = 'local'

const child = new Elysia()
    .get('/child', () => 'hello')

const current = new Elysia()
    .onBeforeHandle({ as: type }, () => {
        console.log('hi')
    })
    .use(child)
    .get('/current', () => 'hello')

const parent = new Elysia()
    .use(current)
    .get('/parent', () => 'hello')

const main = new Elysia()
    .use(parent)
    .get('/main', () => 'hello')

通过更改 type 值,结果应如下所示:

¥By changing the type value, the result should be as follows:

typechildcurrentparentmain
'local'
'scope'
'global'

从 Elysia 0.8 迁移过来后,如果要将钩子设为全局钩子,则必须指定该钩子是全局的。

¥Migrating from Elysia 0.8, if you want make a hook global, you have to specify that hook is global.

typescript
// From Elysia 0.8
new Elysia()
    .onBeforeHandle(() => "A")
    .derive(() => {})

// Into Elysia 1.0
new Elysia()
    .onBeforeHandle({ as: 'global' }, () => "A")
    .derive({ as: 'global' }, () => {})

尽管我们讨厌重大变更和迁移,但我们认为这是一个重要的修复,迟早会修复问题。

¥As much as we hate breaking change and migration, we think this is an important fix that will happen sooner or later to fix problems.

大多数服务器可能不需要你自行迁移,但这很大程度上取决于插件作者,或者如果需要迁移,通常只需 5-15 分钟。

¥Most of the server might not need to apply migration yourself but heavily depends on plugin authors, or should migration required, it usually take no longer than 5-15 minutes.

有关完整的迁移说明,请参阅 Elysia#513

¥For a complete migration note, see Elysia#513.

有关钩子类型的文档,请参阅 Lifecycle#hook-type

¥For the documentation of hook type, see Lifecycle#hook-type

内联错误

¥Inline error

从 Elysia 0.8 版本开始,我们可以使用 error 函数返回带有状态码的响应,以便进行 Eden 推断。

¥Starting from Elysia 0.8, we can use the error function to return a response with a status code for Eden inference.

然而,这存在一些缺陷。

¥However, this has some flaws.

如果为路由指定了响应模式,Elysia 将无法提供准确的状态码自动补全。

¥If you specify a response schema for a route, Elysia will be unable to provide an accurate auto-completion for the status code.

例如,缩小可用状态码的范围。

¥For example, narrowing down an available status code.

Using import error in Elysia

内联错误可以从处理程序中解构,如下所示:

¥Inline error can be destructured from handler as follows:

typescript
import { Elysia } from 'elysia'

new Elysia()
    .get('/hello', ({ error }) => {
        if(Math.random() > 0.5) return error(418, 'Nagisa')

        return 'Azusa'
    }, {
        response: t.Object({
            200: t.Literal('Azusa'),
            418: t.Literal('Nagisa')
        })
    })

内联错误可以从模式生成细粒度的类型,提供类型收缩、自动补齐和类型检查以确保值的准确性,并在值(而不是整个函数)处用红色波浪线标出。

¥Inline error can produce a fine-grained type from a schema, providing type narrowing, auto-completion, and type checking to the accuracy of value, underlining red squiggly at a value instead of an entire function.

Using inline error function from Elysia with an auto-completion that shows narrowed down status code

我们建议使用内联错误而不是导入错误,以获得更准确的类型安全性。

¥We recommended using inline error instead of import error for more accurate type safety.

这对 v1 意味着什么,下一步是什么?

¥What does it mean for v1, and what's next

达到稳定版本意味着我们相信 Elysia 已经足够稳定,可以投入生产使用。

¥Reaching stable release means we believe that Elysia is stable enough and ready to be used in production.

保持向后兼容性现在是我们的目标之一,我们努力避免对 Elysia 引入除安全性之外的重大更改。

¥Maintaining backward compatibility is now one of our goals, putting effort into not introducing breaking changes to Elysia except for security.

我们的目标是让后端开发变得轻松、有趣且直观,同时确保使用 Elysia 构建的产品拥有坚实的基础。

¥Our goal is to make backend development feel easy, fun, and intuitive while making sure that the product built with Elysia will have a solid foundation.

此后,我们将专注于完善我们的生态系统和插件。引入一种符合人机工程学的方式来处理冗余和单调的任务,启动一些内部插件重写、身份验证、在 JIT 和非 JIT 模式之间同步行为,并提供通用运行时支持。

¥After this, we will be focusing on refining our ecosystem and plugins. Introducing an ergonomic way to handle redundant and mundane tasks, starting some internal plugin rewrite, authentication, synchronize behavior between JIT and non-JIT mode, and universal runtime support.

Bun 在运行时、包管理器以及其提供的所有工具方面都表现出色,我们相信 Bun 将成为 JavaScript 的未来。

¥Bun works excellently in both runtime, package manager and all the toolings they offers, and we believe that Bun is going to be a future of JavaScript.

我们相信,通过开放 Elysia 的更多运行时环境并提供有趣的 Bun 特定功能(或者至少易于配置,例如 Bun 加载器 API),最终会吸引更多人尝试 Bun,而不是 Elysia 只支持 Bun。

¥We believe that by opening Elysia to more runtime and offers interesting Bun specific feature (or at-least easy to config, eg. Bun Loaders API) will eventually gets people to try Bun more than Elysia choosing to support only Bun.

Elysia 核心本身部分兼容 WinterCG,但并非所有官方插件都兼容 WinterCG,有些插件支持 Bun 特有的功能,我们希望解决这个问题。

¥Elysia core itself partially WinterCG compatible, but not all the official plugin works with WinterCG, there are some with Bun specific features, and we want to fix that.

我们还没有通用运行时支持的具体日期或版本,因为我们将逐步采用和测试,直到确保它能够正常工作而不会出现意外行为。

¥We don't have a specific date or version for universal runtime supports yet as we will gradually adopting and test until we make sure that it would works without unexpected behavior.

你可以期待以下运行时支持:

¥You can looks forward for the following runtime to support:

  • 节点

  • Deno

  • Cloudflare Worker

我们还希望支持以下内容:

¥We also want to support the following:

  • Vercel Edge 函数

  • Netlify 函数

  • AWS Lambda / LLRT

此外,我们还支持并在以下支持服务器端渲染或 Edge Function 的框架上测试 Elysia:

¥More over, we also support, and test Elysia on the following frameworks that support Server Side Rendering or Edge Function:

  • Nextjs

  • Expo

  • Astro

  • SvelteKit

同时,Elysia 的活跃贡献者之一 Bogeychan 维护着一个 Elysia Polyfills

¥In the meantime, there's an Elysia Polyfills maintained by Bogeychan, one of an active contributor to Elysia.

此外,我们重写了 Eden 文档,以便更深入地解释 Eden 的细节,我们认为你应该了解一下。

¥Additionally, we have rewrote Eden documentation to explain more in depth details about Eden and we think you should check it out.

我们还改进了多个页面,并删除了文档中多余的部分。你可以在 Elysia 1.0 文档 PR 上查看受影响的页面。

¥We also improve several pages, and remove redundant part of the documentation, You can check the affected pages on Elysia 1.0 documentation PR.

最后,如果你在迁移过程中遇到问题,或者遇到与 Elysia 相关的其他问题,欢迎随时在 Elysia 的 Discord 服务器中提问。

¥And finally, if you have problems with migration and additional questions related to Elysia, feels free to ask one in Elysia's Discord server.

显著改进

¥Notable Improvement

改进:

¥Improvement:

  • 细粒度响应式 Cookie

  • 使用单一可信来源进行 cookie 处理

  • websocket 宏支持

  • 添加 mapResolve

  • { as: 'global' | 'scoped' | 'local' } 添加到生命周期事件

  • 添加临时类型

  • error 内联到处理程序

  • 内联 error 具有基于状态码的自动补齐和类型检查功能

  • 处理程序现在根据状态码检查 error 的返回类型

  • 使用实用程序 Elysia._types 进行类型推断

  • #495 提供用户友好的解析失败错误提示

  • 处理程序现在推断 Treaty 错误状态的返回类型

  • t.Date 现在允许字符串化日期

  • 改进类型测试用例

  • 为所有生命周期添加测试用例

  • resolve、mapResolve、derive、mapDerive 使用临时类型来精确缩小范围

  • 推断查询动态变量

重大变更:

¥Breaking Change:

  • #513 生命周期现在优先于本地

变更:

¥Change:

  • 组私有 API 属性

  • Elysia.routes 移动到 Elysia.router.history

  • 返回前检测可能的 JSON

  • 未知响应现在按原样返回,而不是 JSON.stringify()

  • 将 Elysia 验证错误从字符串更改为 JSON

错误修复:

¥Bug fix:

  • #466 如果出现 aot: true 错误,Async Derive 会将请求上下文泄露给其他请求。

  • #505 查询模式中缺少空 ObjectString 验证

  • #503 测试版:使用 decorate 和 derive 时未定义类

  • 调用 .stop 时 onStop 回调函数被调用两次

  • mapDerive 现在解析为 Singleton['derive'] 而不是 Singleton['store']

  • ValidationError 不会将 content-type 返回为 application/json

  • validate error(status, value) 根据状态进行验证

  • derive/resolve 始终作用于全局

  • 如果未处理,则重复调用 onError

  • #516 服务器计时中断 beforeHandle 保护

  • cookie.remove() 未设置正确的 cookie 路径

后记

¥Afterword

提示

以下内容包含个人感受、可能发泄、咆哮、可能畏缩和不专业的内容,这些内容不应写在软件发行说明中。你可以选择不继续阅读,因为我们已经列出了发布所需的所有必要内容。

¥The following contains personal feeling, possibly venting, ranting, possibly cringe and unprofessionalism that shouldn't be written in software release note. You may choose to not continue reading as we have stated all the necessary content for the release.

两年前,我有一段悲惨的回忆。

¥2 years ago, I have a tragic memory.

这无疑是我最痛苦的回忆之一,我日夜不停地工作,以完成那些不公平的任务,而这些任务却利用了我们与某些软件公司签订的松散合同。

¥It's easily one of the most painful memory I have, working days and nights to keeps up with unfair tasks that take advantage from loose contract we had with some software house.

这花了六个多月的时间,我必须从起床工作到睡觉(15 个小时),重复工作,一天中没有其他事情可做,甚至连 5 分钟的剩余时间都没有,没有时间放松,将近两个月除了编码什么都没做,甚至没有一天剩余,甚至没有工作日,我几乎不得不在医院的病床上工作。

¥It took more than 6 months, and I have to work since I woke up until I sleep (15 hours) on repeat, without doing anything else not even 5 minutes break for a day, no time for relax, nothing beside coding for almost 2 months, not even a single break day, not even weekdays that I knocked out and almost have to work in hospital bed.

我曾经没有灵魂,人生毫无目标,我唯一的愿望就是把梦想变成现实。

¥I was souless, no purpose in life at all, my only wish is to make it a dream.

当时,Elysia 中存在许多重大变更,无数新功能都是通过宽松的需求和契约漏洞引入的。

¥At the time, there are so many breaking changes, uncountable new features introduced from loop hole of loose requirement and contract.

跟踪它几乎是不可能的,我们甚至因为 "不满足" 被骗了,甚至没有得到应得的报酬,而且我们对此束手无策。

¥Keeping track of it is almost impossible, and we even got scammed not even getting the pay we deserved because of "not satisfied", and we couldn't do anything with it.

我花了一个月的时间才从对编写代码的恐惧中恢复过来,因为我不够专业,甚至无法在精神崩溃的情况下做好自己的工作,我向经理咨询说我精疲力竭了。

¥It took me a month to recover from a fear of writing code, being unprofessional I couldn't even do my job properly in trauma and consults my manager that I suffered burn out.

这就是为什么我们如此讨厌破坏性变更,并希望 Elysia 能够轻松处理变更,即使 TypeScript 的可靠性并不好,但这是我们目前仅有的。

¥That's why we hate breaking change so much, and want to design Elysia to handle changes easily with TypeScript soundness even if it's not good but it's all we have.

我不希望任何人经历这样的事情。

¥I don't want anyone to ever experienced something like that.

我们设计了一个框架来应对该合约中的所有缺陷。

¥We designed a framework to encounter all the flaws that we had from that contract.

我发现其中存在的技术缺陷目前没有任何基于 JavaScript 的解决方案能够让我满意,所以我尝试了一个。

¥The technical flaws I saw in there doesn't have any JavaScript based solution that could satisfies me, yet so I experiment with one.

我本可以继续前进,因为这样以后就可以避免类似的松散契约,并且可以赚钱,而不必花费大部分空闲时间创建框架,但我没有这样做。

¥I could just move on as I could avoid loose contract like this in the future, and make money and not spending most of my free time creating a framework but I didn't.

我最喜欢的部分是 动画短片中的一句台词,其中 Mei 反对 Kiana 为世界牺牲自己的想法,而 Mei 回答道:

¥There's a my favorite part, a quote in the animated short where Mei is against Kiana of the idea that she would sacrifice herself for the world, and Mei replies:

然而你却独自承担了一切,付出了生命的代价。

¥> Yet you shoulder everything alone, at the cost of your life.

也许这是为了更大的利益……

¥> Maybe this is for the greater good...

但我该如何假装这是正确的呢?

¥> But how can I pretend this is the right thing?

我只知道,在内心深处……

¥> I only know that deep down...

世界对我来说毫无意义……

¥> the world means nothing to me...

没有你

¥> without you

它描绘了两种人之间的二元性:一种人愿意为世界牺牲自己,另一种人愿意为拯救所爱之人而牺牲自己。

¥It's depiction of a duality between the person who would sacrifice themself for the world, and the person who would sacrifice themself to save who they love.

如果我们发现问题并继续处理,我们怎么知道后面的人不会遇到同样的问题呢?总得有人来处理吧。

¥If we saw a problem and move on, how can we know that the person who came after us will not stumble upon the same problem we had, someone need to do something.

有人会牺牲自己去拯救他人,但谁来拯救牺牲的人呢?

¥That someone would sacrifice themself to save the others but then who would save the sacrified one?

"逝者的哀歌" 这个名字说明了这一点,也是我们创建 Elysia 的原因。

¥The name "Lament of the Fallen" describe that, and why we create Elysia.

*尽管它是我最喜欢的,我个人可能认为它是有点太多了。

¥*Despite everything about it being my favorite, and I might relate myself personally a bit too much.


尽管构建于糟糕的记忆和悲剧事件之中,但依然如此。很荣幸看到 Elysia 如此受人喜爱地成长。并且看到你构建的内容受到其他人的喜爱和好评。

¥Despite being build from the bad memory, and tragic event. It's a privilege to see that Elysia grew into something with so much love. And to see what you built are loved, and well received by others.

Elysia 是由开源开发者开发的,没有任何公司支持。

¥Elysia is a work of Open Source developer, and not backed by any company.

我们必须为生活而努力,于是利用空闲时间构建了 Elysia。

¥We have to do something for living, and build Elysia in free time.

我曾经一度选择不直接找工作,而是在 Elysia 上工作了几个月。

¥At one point I chose not to not looking for a job straight away just to work on Elysia for several months.

我们很乐意投入时间不断改进 Elysia,你也可以帮助我们改进 GitHub 赞助商,以减少我们维护自身所需的工作量,并拥有更多空闲时间来开发 Elysia。

¥We would love to spent our time to improve Elysia continously, and you could help us with GitHub sponsors to reduce the work we need to support ourself, and have more free time to work on Elysia.

我们只是希望创造一些东西来解决我们面临的问题的创造者。

¥We are just makers that wants to create something to solve problems we have.


我们一直在使用 Elysia 进行大量的开发和实验,将真实的代码发送给客户,并在实际项目中使用 Elysia 来支持我们本地社区 CreatorsGarten(本地技术社区,而非组织)背后的工具。

¥We have been creating and experimented a lot with Elysia, shipping real code to clients, and use Elysia in real projects to power tools behind our local community, CreatorsGarten (local tech community, not organization).

为了确保 Elysia 能够投入生产,我投入了大量的时间、准备和勇气。当然,可能会有 bug,但我们愿意倾听并修复。

¥It took a lot of time, preparation, and courage to make sure that Elysia is ready for production. Of course, there will be bugs, but we are willing to listen, and fix it.

这是一个新事物的开始。

¥It's a start of a something new.

这一切都归功于你。

¥And it's possible because of you.

ー SaltyAom

天上所有炽热的星星都会在世界末日消逝,

你温柔的灵魂被诅咒了。

"深红的月亮照耀着血迹斑斑的城镇"

这位女神悲叹不已。

所有那些甜蜜的小梦,都深埋在记忆中,直到生命的尽头。


如果拯救你是一种罪过,我很乐意成为一个罪人。

Elysia: Ergonomic Framework for Humans