Releases: SaltyAom/kingworld
exp.51 (ジャスト・ライト・スロウ)
0.0.0-experimental.51 - 22 Nov 2022
[Just Right Slow] introduce breaking major changes of KingWorld, specific on a plugin system.
Previously, we define plugin by accepting 2 parameters, KingWorld
and Config
like this:
const plugin = (app: KingWorld, config) => app
new KingWorld().use(plugin, {
// Provide some config here
})
However, this has flaw by the design because:
- No support for async plugin
- No generic for type inference
- Not possible to accept 3...n parameters (if need)
- Hard/heavy work to get type inference
To fix all of the problem above, KingWorld now accept only one parameter.
A callback which return KingWorld Instance, but accept anything before that.
const plugin = (config) => (app: KingWorld) => app
new KingWorld().use(plugin({
// provide some config here
}))
This is a workaround just like the way to register async plugin before exp.51, we accept any parameters in a function which return callback of a KingWorld instance.
This open a new possibility, plugin can now be async, generic type is now possible.
More over that, decorate can now accept any parameters as it doesn't really affect any performance or any real restriction.
Which means that something like this is now possible.
const a = <Name extends string = string>(name: Name) => (app: KingWorld) => app.decorate(name, {
hi: () => 'hi'
})
new KingWorld()
.use(a('customName'))
// Retrieve generic from plugin, not possible before exp.51
.get({ customName } => customName.hi())
This lead to even more safe with type safety, as you can now use any generic as you would like.
The first plugin to leverage this feature is jwt which can introduce jwt function with custom namespace which is type safe.
Change:
- new
decorators
property for assigning fastContext
exp.39 (39みゅーじっく!)
[39みゅーじっく!] is the 100th star release for KingWorld 🎉
This release doesn't have a big change for the code but has a big change for the community.
Nested Schema
.39 introduce better support for the nested schema.
Instead of inheriting the global schema first, KingWorld now handles the local schema first.
If a duplicated schema is found, KingWorld now infers type correctly instead of having infinite cycling.
Community Plugin: Controllers
Now we have another community plugin, Controllers is a plugin for defining decorator and controller-based routing.
import { Controller, Get, kwControllers } from 'kingworld-controllers';
// /users prefix
@Controller('/users/')
class UsersController {
@Get()
index() {
return 'Hello World'
}
}
const app = new KingWorld()
app.use(kwControllers, {
controllers: [UsersController],
})
app.listen(3000);
GraphQL support with Yoga
With @kingworldjs/graphql-yoga, you can now use GraphQL Yoga with KingWorld.
Change Log
Breaking Change:
method
is changed toroute
Improvement:
LocalHook
now prefers the nearest type instead of the merge- Merge the nearest schema first
- add
contentType
as a second parameter forBodyParser
Bug fix:
- Correct type for
after handle
- Fix infinite cycling infer type for
Handler
exp.37 (Sage)
[Sage] is one of the major experimental releases and breaking changes of KingWorld.
The major improvement of Sage is that it provides almost (if not) full support for TypeScript and type inference.
Type Inference
KingWorld has a complex type of system. It's built with the DRY principle in mind, to reduce the developer's workload.
That's why KingWorld tries to type everything at its best, inferring type from your code into TypeScript's type.
For example, writing schema with nested guard
is instructed with type and validation.
This ensures that your type will always be valid no matter what, and inferring type to your IDE automatically.
You can even type response
to make your that you didn't leak any important data by forgetting to update the response when you're doing a migration.
Validator
KingWorld's validator now replaced zod
, and ajv
with @sinclair/typebox
.
With the new validator, validation is now faster than the previous version by 188x if you're using zod, and 4.1x if you're using ajv adapter.
With Edge Computing in mind, refactoring to new validate dropped the unused packages and reduced size by 181.2KB.
To give you an idea, KingWorld without a validator is around 10KB (non-gzipped).
Memory usage is also reduced by almost half by changing the validator.
According to M1 Max running example/simple.ts
, running exp.36 uses 24MB of memory while exp.37 use 12MB of memory
This greatly improves the performance of KingWorld in a long run.
Changelog
Breaking Change:
- Replace
zod
,zod-to-json-schema
,ajv
, with@sinclair/typebox
Improvement:
use
now accept any nonKingWorld<{}, any>
use
now combine typed between current instance and pluginuse
now auto infer type if function is inlineLocalHook
can now inferparams
type from path string
Change:
TypedSchema
is now replaced withInstance['schema']
exp.29 (Regulus)
This version introduces rework for internal architecture. Refine, and unify the structure of how KingWorld works internally.
Although many refactoring might require, I can assure you that this is for the greater good, as the API refinement lay down a solid structure for the future of KingWorld.
Thanks to API refinement, this version also introduced a lot of new interesting features, and many APIs simplified.
Notable improvements and new features:
- Define Schema, auto-infer type, and validation
- Simplifying Handler's generic
- Unifying Life Cycle into one property
- Custom Error handler, and body-parser
- Before start/stop and clean up effect
New Feature:
-
add
schema
to the local hook for strict-type validation and type inference.- Example:
import { z } from 'zod' app.post("/id/:id", ({ body }) => body, { schema: { body: z.object({ username: z.string(), password: z.string() }), params: { id: z.string() } } })
-
add
onError
for handling custom error -
add
onStart
,onStop
for handling server state -
add
stop
for stopping the server
Breaking Change:
-
Remove
TypedRoute
generic on httpMethod, replaced withschema
.- To migrate:
// From app.post<{ body: { username: string password: string }, params: { id: string } }>('/id/:id', ({ body }) => body) // To import { z } from 'zod' app.post("/id/:id", ({ body }) => body, { schema: { body: z.object({ username: z.string(), password: z.string() }), params: { id: z.string() } } })
-
Method Hook is now replaced with
LocalHook
-
Rename
preHandler
tobeforeHandle
-
Rename
bodyParser
toonParse
,on('parse')
-
Rename
.when(event)
toon(event)
-
Rename
.on(HttpMethod)
to.method(HttpMethod)
-
Replace
HookEvent
forLifeCycle
-
Move
bodyParser
,errorHandler
toevent: LifeCycleStore
-
Remove ability for local hook to accept
RegisterHook[]
-
Remove
onRequest
on local hook, as it doesn't execute
Bug fix:
- JSON body parsed as string
exp.28 (GHOST FOOD)
0.0.0-experimental.28 - 30 Oct 2022
Happy Halloween.
This version named [GHOST FOOD] is one of the big improvements for KingWorld, I have been working on lately.
It has a lot of feature change for better performance, and introduce lots of deprecation.
Be sure to follow the migration section in Breaking Change
.
GHOST FOOD introduce a lot of breaking change, laying down a better foundation for KingWorld in the future.
However, the API is still marked as unstable and some might change in the future before moving out of the experiment
version.
GHOST FOOD has better performance in every aspect, with an even better type system for KingWorld.
Like auto infer type for state
, inheriting type from plugins, and auto typing path parameters.
And thanking you all for following this experimental project of mine, happy hacking until next time.
New Feature:
- Auto infer type from
plugin
after merging withuse
decorate
to extendsContext
method- add
addParser
, for custom handler for parsing body
Breaking Change:
-
Moved
store
intocontext.store
- To migrate:
// From app.get(({}, store) => store.a) // To app.get(({ store }) => store.a)
-
ref
, andrefFn
is now removed -
Remove
Plugin
type, simplified Plugin type declaration- To migrate:
// From import type { Plugin } from 'kingworld' const a: Plugin = (app) => app // To import type KingWorld from 'kingworld' const a = (app: KingWorld) => app
-
Migrate
Header
toRecord<string, unknown>
- To migrate:
app.get("/", ({ responseHeader }) => { // From responseHeader.append('X-Powered-By', 'KingWorld') // To responseHeader['X-Powered-By', 'KingWorld'] return "KingWorld" })
Change:
- Store is now globally mutable
Improvement:
- Faster header initialization
- Faster hook initialization