Skip to content

v1.1.0

Latest

Choose a tag to compare

@marcj marcj released this 31 Oct 21:44
· 172 commits to master since this release

Built for coding agents

deepkit.io now serves the documentation in the form coding agents read best: llms.txt as an index, llms-full.txt with all guides in one file, and every documentation page as plain markdown by adding .md to its URL, for example deepkit.io/en/documentation/orm.md. Package pages include their complete API reference with signatures and source links. Point your agent at it and it has everything it needs to write Deepkit code.

This is the start: we will keep improving how Deepkit works with agents. Runtime types put Deepkit in a unique position here. Every boundary of an application, HTTP, RPC, database, configuration and CLI, validates and serializes against the TypeScript types at runtime. When generated code gets a type subtly wrong, it fails with an exact error at the boundary instead of passing bad data through, so code written by agents is checked in the places where mistakes are expensive.

Fewer dependencies, safer installs

Every dependency is code that runs with our users' permissions, and supply chain attacks on npm packages are common. So this release removes 40 dependencies and replaces the small parts we actually used with our own, tested code. Installing all Deepkit packages now takes 130 MB instead of 219 MB (−41%) and 249 packages instead of 427 (−42%). Our goal is zero external dependencies.

Package Removed Replaced with
@deepkit/type uuid (CVE-2026-41907, #705), buffer (#699) crypto.getRandomValues; the global Buffer where available, otherwise atob/btoa and TextEncoder/TextDecoder
@deepkit/core, @deepkit/rpc dot-prop, to-fast-properties own path functions, still blocking __proto__/prototype/constructor against prototype pollution
@deepkit/logger ansi-styles, format-util own color table with the same tag names; util.format on the server
@deepkit/broker @lukeed/ms, js-xxhash own duration parser and xxHash32
@deepkit/http qs, send own query string parser (qs 6.14 defaults: depth, parameter and array limits, prototype pollution protection) and own static file serving (path traversal and dotfile protection, ETag, conditional and range requests)
@deepkit/framework compression, mime-types, jimp, probe-image-size, nice-table, selfsigned (with node-forge), faker, sift, fs-extra, buffer, md5, pirates zlib response compression, a MIME type table in @deepkit/http, original images in the debugger, a small table renderer, self-signed certificates with node:crypto; the ORM browser's seed feature is removed (see below)
@deepkit/orm sift own query matching ($eq, $in, $regex, $elemMatch, … as used by FilterQuery)
@deepkit/sql, @deepkit/postgres, @deepkit/sqlite sqlstring, sqlstring-sqlite, @types/sqlstring, date-fns, fast-glob SQL escaping in @deepkit/sql, a date formatter, fs.readdir({ recursive })
@deepkit/mongo saslprep-patch (which pulled in a placeholder package named fs) own SASLprep (RFC 4013)
@deepkit/type-compiler, @deepkit/vite micromatch, @rollup/pluginutils, @typescript/vfs, get-tsconfig, lz-string, strip-json-comments own glob matching and createFilter, a local TypeScript lib file list; three were unused
@deepkit/template abstract-syntax-tree (9 packages) its parser and generator meriyah and astring directly (no dependencies of their own) and a small require hook
@deepkit/run tsconfig-paths own paths mapping (also fixes patterns with text after *)
@deepkit/create-app, @deepkit/desktop-ui fs-extra, on-change fs.cpSync, a small change observer
@deepkit/filesystem-google bignumber.js, google-auth-library unused

formidable stays in @deepkit/http for now: it parses all request bodies, and HttpConfig.parser exposes its options.

Also:

  • @deepkit/sql, @deepkit/postgres and @deepkit/sqlite no longer run eval() on column defaults read from the database; they parse them.
  • The ORM's query matching (memory adapter) no longer supports $where, whose string form ran code through new Function.
  • Static file serving no longer reveals whether files outside of the static root exist (a request like /public/../../etc/passwd answered 403 for existing and 404 for missing files; the content was never served).
  • Packages no longer ship .tsbuildinfo files: 4.4 MB less per release.
  • Releases are published only from a clean build that passed the complete test suite (see below).

Smaller installs and bundles

Compared with 1.0.19: a fresh npm install, and a browser bundle (esbuild, minified, gzip).

Use case Install size Installed packages Browser bundle (gzip)
All 48 packages 219.1 → 129.6 MB (−41%) 427 → 249 (−42%), third-party 377 → 200 (−47%) –
@deepkit/framework + @deepkit/sqlite 93.3 → 44.3 MB (−53%) 265 → 84 (−68%) –
RPC client (@deepkit/rpc) 11.2 → 10.4 MB (−7%) 24 → 11 154.7 → 146.7 KB (−5%)
@deepkit/bson 4.4 → 4.0 MB (−9%) 12 → 4 95.5 → 91.4 KB (−4%)
@deepkit/type 3.6 → 3.2 MB (−11%) 11 → 3 78.8 → 73.9 KB (−6%)

buffer, uuid, base64-js, ieee754, dot-prop and to-fast-properties are no longer part of the browser bundle of @deepkit/type.

TypeScript 6, Angular 22, ESM

  • Built with TypeScript 6. The type compiler supports TypeScript 6 and versions before 5.5, and resolves types from .mts/.cts files.
  • The Angular packages and GUIs use Angular 22.
  • The ESM builds of @deepkit/template, @deepkit/mongo, @deepkit/http and @deepkit/framework load again. A new consumer test installs the packed packages into fresh CommonJS and ESM projects before each release.
  • All imported @deepkit/* packages are declared, and @deepkit/* peer dependencies require the current version (#574).

Validation errors that tell you what's wrong

When a value matches no member of a union, the error now points at the actual problem instead of No valid union member found with every member's definition expanded:

shape.kind(type): Expected 'circle' | 'square', got string   // unknown discriminator
shape.size(type): Not a number                               // member selected by the discriminator
tags.1(type): Not a string                                   // closest member: string[]
(type): Expected Circle | Square, got array                  // no member is close

The same errors come from BSON decoding (RPC, MongoDB) and validate()/assert()/cast(). They are built only when validation fails, so valid values are as fast as before.

Faster

Most of the time of is(), validate(), serialize() and deserialize() was spent before the actual work: resolving the type argument on every call, and allocations. Inline types like is<string[]>() even ran the type VM on every call. Type arguments are now passed directly to ReceiveType parameters and created once per call site, and the generated guards do less per call. Measured with the benchmarks that come with this release, against 1.0.19, Node 26, i9-8950HK:

1.0.19 now
is<User>() 412 ns 49 ns
validate<User>(), invalid 617 ns 184 ns
serialize<User>() 641 ns 177 ns
deserialize<User>() 690 ns 191 ns
cast<User>() 1.2 µs 391 ns
deserialize<T[]>(), 100 items 143 µs 9.0 µs
HTTP body with 10 users, deserialize 111 µs 4.4 µs
RPC createRpcMessage() with body 226 ns 10 ns

Of 341 benchmarks, 83 are faster and 4 slower: RPC messages without body, only inside the benchmark suite (equal when measured alone). All results, the comparisons and their history are on deepkit.io/benchmarks. Run them yourself with npm run bench, also against an older version with npm run bench -- --target 1.0.19.

All changes

  • ⚠️ behavior change, please check when updating
  • 🆕 new feature or API
  • 🧪 regression test only: it already worked, now a test keeps it that way

Type compiler

  1. ⚠️ declare statements in your own files (declare type/interface/const, declare global) produce no runtime types any more. A type in the same file that references a declare type gets any for that part. Declaration files of libraries (.d.ts in node_modules) are not affected. (#601, 77b865f)
  2. Default and aliased imports of library types resolve to the exported declaration, e.g. import AI from 'openai', import { Client as C }, export = Client. Classes imported like this were any before. (97074f8)
  3. tsconfig extends can be an array, and the last entry wins, like in tsc. (#600, bdc6533)
  4. Named re-exports (export { X } from './x', also through an index.ts) carry their runtime types. (#634, #318, e8543b1)
  5. Optional chaining with type arguments (a?.b<T>()) no longer produces a SyntaxError. (#612, 9a1f315)
  6. The __type of module-level functions is hoisted, so a function can be reflected before its definition in the file. (#664, 4492545)
  7. Inferring a type parameter replaces the whole type reference, which fixes a crash with TypeScript debug assertions. (#509, f4495fe)
  8. Global lib types of ES2024+ targets (e.g. PromiseWithResolvers) get runtime types, because lib files are now resolved like TypeScript does instead of from a fixed list. (b234b52)
  9. Types from .mts/.cts files resolve, and the module format of .mts/.cts files and nested packages is detected correctly. (#639, 493b2bb, 8cb50c6)
  10. TypeScript versions before 5.5 work again. (7e6cbca)
  11. forwardRef(() => Type) is left undecorated, so Angular's linker accepts libraries built with runtime types, like @deepkit/desktop-ui. (c097e96)
  12. The plugin export paths in package.json are correct, and a test checks them against the sources. (#702, 1cc3068, df4f64d)
  13. The module type for nodenext is detected. (35ff3b9)
  14. 🧪 The injected TypeScript patch is valid JavaScript, also with Windows paths. (#356, 1a6ec39)
  15. 🧪 Types from libraries without runtime types. (#352, #555, f9a443b)
  16. Type arguments are passed as arguments to known ReceiveType parameters instead of fn.Ω, and type arguments without local references are module-level variables, so their type is computed once: is<string[]>() ran the type VM on every call (12.7 µs, now 52 ns). typeOf() still returns a new type for inline types. (c39c8fd)

Type

  1. ⚠️ The UUID type checks RFC 9562 (hex digits, version 1–8, variant, Nil/Max). Before, any 36-character string with dashes in the right places passed. (#706, 79a865e)
  2. ⚠️ Union validation errors report the field that's wrong: the discriminator selects the member (shape.size(type): Not a number), an unknown discriminator says shape.kind(type): Expected 'circle' | 'square', got string, otherwise the closest member's errors or Expected Circle | Square, got array instead of No valid union member found. Valid: …. Literals say Expected 'circle', got number instead of Invalid literal. This only runs when validation fails. (#577, #676, 840b9eb, 97074f8, 35ce1f6, 55bc216)
  3. ⚠️ A tuple only extends an array if all its elements do; before, [boolean] extends string[] was true. (11a70ee)
  4. ⚠️ = new Date without parentheses counts as a computed default, so the SQL schema no longer bakes in a build-time timestamp, and migrations stop showing a change every time. (#545, 82a2455)
  5. ⚠️ typedArrayToBuffer()/nodeBufferToTypedArray() are typed as Uint8Array; at runtime they still return a Buffer where one exists. (9036b3b)
  6. Large literal unions (e.g. 86,400 members) are checked with one Map lookup instead of failing with "Too many context variables", and registered template hooks still apply. (#478, 543e189, ae2790e)
  7. [...infer P, infer L] infers the elements after a rest element from the end. (#524, 7483886)
  8. ValidationErrorItem.toString no longer crashes on circular values. (#505, 70ae003)
  9. Deserialized classes that have a parent class are reflectable again. (#241, b86d3d2)
  10. Errors about missing runtime types explain how to fix the setup. (#562, #508, 8722519)
  11. The uuid and buffer dependencies were removed; replacements: crypto.getRandomValues, plus the global Buffer with an atob/btoa fallback. (#705, #699, 9036b3b)
  12. 🆕 A scored union resolver picks the union member with the most matching properties, also in BSON. (a1f8ee3)
  13. 🆕 Intrinsic types Capitalize, Uppercase, Lowercase, Uncapitalize. (#681, ffd1bc7)
  14. A type no longer collapses when B resolves to unknown. (#684, 1130ddd)
  15. The packages are built with isolatedModules. (#683, c730cfc)
  16. An optional Embedded property can be undefined; validation failed with Not an object before, also when persisting. (#254, 4a8a7a4)
  17. 🧪 Omit of methods whose parameters use keyof. (#430, 4a8a7a4)
  18. ⚠️ Array type guards check every item: is<string[]>(['a', 1, 'b']) was true, because only the last item decided (since 1.0.1). (c39c8fd)
  19. Faster type guards and is()/validate()/serialize()/deserialize()/cast(): discriminated unions select their member by the discriminator, objects outside unions are not scored, no allocations per call, plain objects are recognized without instanceof checks, isFunction/isClass don't call toString() on every call, and anonymous object literals and arrays of primitives are checked inline instead of with a function call. (d5ce462, c39c8fd, b7012e2, 7ce005a)

BSON

  1. 🧪 Typed BSON serialization rejects NaN with a validation error. (#573, ee8c287)
  2. A wrapped BSON value is written as BSON null when it is null or undefined. (#678, 9af338e)
  3. Union mismatches in BSON report the same errors as validate() (discriminator, closest member, Expected …), instead of Cannot convert bson type OBJECT to …. (#676, bc146ee, 97074f8, 35ce1f6)
  4. 🧪 Arrays of object literals with MongoId. (#444, 4a8a7a4)

ORM / SQL / Mongo

  1. ⚠️ filter({ field: undefined }) means "field is not set" in SQL, Mongo (sent as null) and the memory adapter. Before, the field was dropped, so the filter matched all rows, also in deleteMany/patchMany. (#692, 1b945c7, 24c6630)
  2. ⚠️ count() ignores limit/skip/sort and returns the total; it failed on all databases before. (#668, 1ba444a)
  3. ⚠️ Query matching in the memory adapter (the replacement for sift) no longer supports $where and $type; neither is part of FilterQuery. (0070543)
  4. When a reference is loaded as the full entity, the existing instance is upgraded instead of triggering an extra UPDATE. (#636, 32f05c8)
  5. A property that overrides a parent's property with Reference/BackReference is a reference, so joinWith() loads it. (#704, 4a8a7a4)
  6. 🧪 Nested joins over a back reference. (#375, 4a8a7a4)
  7. database.migrate() creates all tables first and adds foreign keys afterwards, which fixes Postgres errors when entities are registered in the wrong order. (1ae363e)
  8. SQL column defaults read from the database are parsed instead of run through eval(). (f0661ec)
  9. SQL escaping moved from sqlstring/sqlstring-sqlite into @deepkit/sql, with the same output per platform. (c09bd2d)
  10. SQL migration commands load via dynamic import, so they work in ESM projects. (#685, 2c5766a)
  11. deepkit-sql --path is back, and the standalone migration commands find the databases again. (#613, 849ef19)
  12. ⚠️ SQLite maxConnections throws for :memory: databases above 1 and for values below 1; before, data was silently split across connections, or the pool hung. (#393, dab7244)
  13. better-sqlite3 was updated to 12.11 for Node.js 26. (e6ad1fb)
  14. Mongo cursor ids are read as bigint in find and aggregate, so large aggregations no longer fail on getMore, and batchSize is applied. (#708, 0dd646a, 4c67a33)
  15. Mongo filters with a path into an embedded object, e.g. { 'tailMessage._id': id }, convert the value with its type, so a MongoId is sent as ObjectId. (#697, 4a8a7a4)
  16. 🆕 Mongo estimateCount option, a fix for the slow empty count, and the verbose logs are gone. (4b142c8, 1be535f)
  17. Mongo SASLprep is our own RFC 4013 implementation (saslprep-patch pulled in a placeholder fs package). (94c9b2e)
  18. ORM event listeners are merged instead of lost when the dispatcher instance changes, and the dispatcher is rebuilt after a context merge. (#671, f3c9683, 168a030)
  19. A unit-of-work listener with preventDefault no longer skips all the other listeners. (#670, 2796bb1)
  20. ⚠️ Soft-delete query results have the same shape as normal query results. (#672, ef7bb9f)
  21. The MongoDB client detects a recovered primary and failovers again: its heartbeat could stop for good after the first round when the timer fired a millisecond early. (701b5d0)

HTTP

  1. ⚠️ An HttpError thrown in a middleware is answered with its status code. (#589, 253fb8f)
  2. ⚠️ Other middleware errors return 500 "Internal error" instead of 404. (#439, a953246)
  3. ⚠️ .timeout() on a middleware fails the request with 504 HttpGatewayTimeoutError instead of continuing to the controller, and a late next() is ignored. (#439, a953246)
  4. onResponse and the HTTP logger also run when a middleware ends the response. (#590, 7aa3d08)
  5. The middleware documentation describes next(), errors and timeouts as they work now. (#591, 4a8a7a4, 56714ba)
  6. ⚠️ HttpHeader parameters match case-insensitively, and the test request builder lowercases header names. (#653, beebd60)
  7. ⚠️ Query strings use our own parser with the qs 6.14 defaults: 1000 parameters, depth 5, array index limit 20, prototype keys dropped. (b3a50ae)
  8. ⚠️ Static file serving is our own, replacing send. It covers ETag, range and conditional requests, and dotfile and path-traversal protection, and no longer reveals whether files outside the root exist. (414a199, 52141c6)
  9. ⚠️ The MIME table is our own: .js → text/javascript, .mp4 → video/mp4, and rare extensions get no Content-Type. (414a199, 68acfea)
  10. Errors outside actions, e.g. in kernel.authenticate(), also go through transformError. (17f309e)
  11. ⚠️ The correct InjectorContext scope is set in HTTP and RPC; RpcInjectorContext was removed. (#673, a56b7dd)
  12. 🧪 Validators inside HttpQuery don't leak into other parameters, and body parameters from imported files work. (#614, #458, 47afde0, 7cfaf60)
  13. Query strings without brackets are parsed by a fast path, checked against qs in tests. (d5ce462)

RPC / Broker

  1. 🆕 preSerialize(value) serializes a value once for many connections; message types that can't splice it throw an RpcError. (#711, 43ecb1e, 1799053)
  2. A socket that was replaced no longer strands calls on the new connection (reconnect race). (#707, 2de8673)
  3. Subscribed observables and subjects stay alive until they complete, so they are not garbage-collected mid-stream. (6243c3e)
  4. Messages sent while the server is unreachable are rejected instead of hanging. (1bb0bf9)
  5. Stopwatch frame names are normalized. (26a06cd)
  6. The broker uses its own duration parser and xxHash32 instead of @lukeed/ms/js-xxhash. (f5a8471)

App / Injector / Framework

  1. Unscoped providers are used in scopes that have none of their own, e.g. CLI; this fixes the migration:* commands with FrameworkModule. (dca8e69)
  2. Injector performance: a token's type and a config type's place are computed once. (#709, 8b37311)
  3. Flag properties keep their default when the flag isn't given. (4764a5c)
  4. Names that are already uppercase stay the same. (afbf6e2)
  5. 🧪 Failing CLI commands show their error when debug is enabled. (#687, 262983f)
  6. GUI packages (debugger, API console, ORM browser) are found via module resolution, so they work with Yarn PnP. (#608, e767a1e)
  7. Without workers, debug mode uses an in-process broker, and an unreachable broker no longer hangs shutdown. (eb3cad7, 43f0144)
  8. ⚠️ The debugger sends image previews unchanged instead of downscaling them (jimp was dropped). (2556945)
  9. ⚠️ selfSigned certificates use node:crypto with RSA 2048/SHA-256 and a PKCS#8 key; already stored certificates are reused. (bf03088)
  10. Response compression is our own zlib implementation, the CLI table renderer is our own, and the MIME types come from @deepkit/http. (da8e800, d6a893e, 68acfea)
  11. ⚠️ The ORM browser's seed feature was removed together with faker: the Seed tab, the seed/getFakerTypes actions and the seed types in @deepkit/orm-browser-api. Seed databases with a script instead. (604c89d, a9571fb)
  12. The ORM browser shows the app's databases without forDatabases(), renders array content and no longer allows editing it. (f848e4f, 06d058d)

Core / Logger / Other packages

  1. Path get/set/delete is our own code, still blocking __proto__/prototype/constructor, replacing dot-prop/to-fast-properties. (6e9ca42)
  2. Windows paths work in getCurrentDirName (npm init @deepkit/app) and in the local filesystem adapter. (#688, #694, 75816ef)
  3. The logger uses its own color table with the same tag names, plus util.format on the server, replacing ansi-styles/format-util. (c2687b9) Single-string messages skip util.format, so logging is 14% faster than in 1.0.19. (f397091)
  4. Template: meriyah and astring replace abstract-syntax-tree and pirates, files with static {} class blocks no longer crash, and the JSX types ship in jsx-runtime. (614737b, d8b829b)
  5. @deepkit/run does its own paths mapping, which also fixes patterns with text after *. (dc519a6)
  6. Vite and the type compiler use their own glob matching, and accept FilterPattern for include/exclude. (#696, 4d5a636, c1bcfda)
  7. The Bun plugin applies compilerOptions, include and exclude. (7fd19ac)
  8. The Bun plugin tests build and run a fixture with Bun.build and the runtime preload, and CI installs Bun. (#691, 2ed1f57)
  9. Filesystem: S3 keys no longer get a leading slash with the path option, and FTP/SFTP report directory size 0. (9225f71, b0948b3)
  10. desktop-ui (1f8cff6, 758e8ba, 4b92999, 5288594, 01d52f7):
    - dates are formatted with LOCALE_ID and a 24-hour clock (#710)
    - the dropdown only re-observes its position when the target changes
    - on-change was replaced with a small observer
    - fewer runtime types are emitted
    - ⚠️ docs.json is no longer exported
  11. The deepkit-sql and dui-create-font bins got their shebang. (#598, beb019e)
  12. The ESM builds of template, mongo, http and framework load again. (814ca04)

Packaging / release process

  1. ⚠️ All imported @deepkit/* packages are declared, and @deepkit/* peer dependencies require the current version. (#574, ba76daa, ee7546f)
  2. ⚠️ The packages need Node.js 20 or newer, and they are built with TypeScript 6 and Angular 22; development needs Node.js 22. (8dbef51, e6f2ad0)
  3. .tsbuildinfo is no longer published (4.4 MB less per release), and dist/esm/package.json is committed for all ESM packages. (48881dd, 9dbdfcc)
  4. @deepkit/framework no longer installs @types/ws. (#346, 4a8a7a4)
  5. npm run build starts clean, npm run test writes a certificate after a complete green run, and commits and npm run publish require that certificate. (8a0efaf, 958502c, 4195ea8)
  6. npm run test:consumer installs the packed packages into fresh CommonJS and ESM projects. (b3ae2cf)
  7. 40 dependencies were removed in total, some of them unused, e.g. bignumber.js, google-auth-library, lz-string, get-tsconfig, strip-json-comments. (3827d6f and the commits above)
  8. 🆕 Every package has benchmarks in benchmarks/*.bench.ts, plus comparisons with typia, zod, valibot, arktype, TypeBox, class-validator/class-transformer and bson. npm run bench writes the results per machine to benchmarks/results, --target 1.0.19 runs the same benchmarks against a published version. The ORM comparisons measure Deepkit against Prisma, Drizzle, TypeORM, MikroORM, Sequelize and Mongoose on PostgreSQL, SQLite and MongoDB, with the raw drivers as lower bound. (b5abf6f, 710602b, 975abdc)
  9. Development uses Yarn 4.18.1, which installs no package versions younger than 7 days (npmMinimalAgeGate). (afb6e4e)

Website

  1. The desktop-ui API tables are generated by our own api-docs.ts instead of typedoc. (a95d1a7)
  2. The translate command is back with OpenAI gpt-6.1-sol, as a separate CLI entry that Angular never bundles; the other AI features and the Discord bot stay removed. (9df1f68, ee39a35)
  3. 🆕 deepkit.io/benchmarks shows the comparisons with other libraries and databases, the last two releases against master, and the history of whole features (validation, serialization, database, DI, HTTP requests). (57c2f89, add08b7, f646d8c)
  4. 🆕 The start page explains Deepkit again: one type for validation, HTTP and database in one example, benchmark numbers, sections with code for each part, and the library catalog with what each category is for. (b7abb7c)
  5. 🆕 Documentation for coding agents: /llms.txt, /llms-full.txt and every documentation page as markdown under its URL plus .md, with the API reference on package pages. (f59fb57)
  6. 🆕 Every page has a social preview image generated from its content. (e385fb9)
  7. Search engines: moved URLs redirect permanently (301) instead of temporarily, pages declare their language versions (hreflang) and canonical URL, /sitemap.xml and /robots.txt exist, every page has its own description, and broken links in the documentation are fixed. The community questions and examples are removed, their URLs redirect to the documentation. (d0becd8, 5d7e1aa, ae434d8)

How we release now

  • npm run build always starts clean.
  • npm run test writes a test certificate only after a complete, green run.
  • Commits require that certificate for their changes.
  • npm run publish only publishes the exact build that passed the tests.

This makes sure what you install is what we tested.

Thanks

Thanks to @marcus-sa, @lionelhorn, @fergusean, @otnc, @pxDot, @mojobeeping, @Rush, @LEW21 and @jho406 for their contributions to this release.