Skip to content

Commit 4ef8f5e

Browse files
RobinTailrenovate[bot]github-actions[bot]pullfrog[bot]coderabbitai[bot]
authored
v28 is for Koko Da Doll (#3208)
![Koko Da Doll](https://metro.co.uk/wp-content/uploads/2023/04/SEI_152850843-60b7-e1682088625316.jpg?quality=90&strip=all&w=646) [**Koko Da Doll**](https://en.wikipedia.org/wiki/Koko_Da_Doll) (aka _Rasheeda Williams_) was a 35-year-young Black trans woman, performance artist, singer, and star of the acclaimed documentary **Kokomo City**. Born in College Park, Georgia, she was based in Atlanta and became a prominent voice for transgender visibility and the rights of Black trans women. She was a talented rap artist who released two singles: "Trick" (2020) and "Bulletproof" (2022). A song she created was featured on the TV show _The Chi_ in 2023, showcasing her artistic talents beyond her advocacy work. Koko was one of four Black trans women featured in the groundbreaking documentary [**Kokomo City**](https://www.magpictures.com/kokomocity/), directed by D. Smith. The film premiered at the 2023 Sundance Film Festival and won both the NEXT Innovator Award and NEXT Audience Award. It also received the Berlinale Panorama Audience Award. In the film, Koko spoke openly about her experiences as a sex worker and the challenges faced by Black trans women. She discussed doing sex work to avoid homelessness for her mother, sister, and herself. After the Sundance première, Koko wrote on Instagram: > _"I will be the reason there's more opportunities and doors opening for transgender girls."_ She hoped that her participation in the film would help save lives and create more opportunities for young trans women. On April 18, 2023, at around 11pm, Koko Da Doll was found dead with a gunshot wound on a sidewalk near Holmes Shopping Plaza in Southwest Atlanta. She was 35 years old. A 17-year-old suspect was arrested on April 27, 2023, on suspicion of murder, aggravated assault, and possession of a firearm in the commission of a felony. Atlanta police indicated that a hate crime investigation was ongoing. Koko was the 13th trans person killed in the United States in 2023. Following her death, _Kokomo City_ was dedicated to her memory. She was honored during the In Memoriam segment at the 2023 BET Awards. _The New York Times_ described her as someone who _"brims with vitality, ambition, and insight"_ — a woman who fought tooth and nail for her life and self-worth. Her tragic death serves as a stark reminder of the ongoing violence and discrimination faced by trans women in America. [AP News](https://apnews.com/article/koko-da-doll-kokomo-city-killed-transgender-0c02d2c623deffebceaa74912e308609) · [AL.com](https://www.al.com/reckon/2023/07/murdered-kokomo-city-star-koko-da-doll-shares-her-hopes-for-young-trans-girls.html) · [TDoR](https://tdor.translivesmatter.info/reports/2023/04/18/rasheeda-williams_atlanta-georgia-usa_0c3bc39f) ------------------------------- Version 28 drops support for Node.js versions below 22.19.0, adds full Zod 4.4+ compatibility, and makes the Zod plugin an optional peer dependency — you now need to install `@express-zod-api/zod-plugin` manually to keep using `.example()`, `.label()`, and similar methods, while `brand()` is replaced by `.xBrand()` to avoid conflicts with Zod 4.4. Several config properties were renamed for clarity: `wrongMethodBehavior` → `hintAllowedMethods`, `methodLikeRouteBehavior` → `recognizeMethodDependentRoutes`, `shortDescription` → `summary`, and `noContent` → `noBodySchema`. The `hasSummaryFromDescription` boolean was replaced with a more flexible `summarizer` function that lets you customize how endpoint summaries are derived. The automated migration tool has been updated accordingly, now requiring `eslint@^10.0.0` and `typescript-eslint@^8.58.0`. - #3207 - #3242 - #3267 - #3268 - #3287 - #3294 - #3299 - #3300 - #3301 - #3302 - #3303 - #3310 - #3313 - #3334 - #3340 - #3364 (incl. undo of #3357) - #3365 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * v28: public option/metadata renames (shortDescription → summary, wrongMethodBehavior → hintAllowedMethods, methodLikeRouteBehavior → recognizeMethodDependentRoutes, noContent → noBodySchema) and a configurable documentation summarizer. * **Documentation** * CHANGELOG/README/SECURITY updated with v28 notes, summarizer docs, migration examples and migration tool link; README shows Zod plugin as optional and xBrand usage. * **Chores** * Zod plugin moved to optional/peer, branding API renamed (.brand → .xBrand); tooling and Node/module resolution updates. * **Tests** * Test suite updated to align with renames, summarizer, metadata/examples and branding changes. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
1 parent 4882198 commit 4ef8f5e

132 files changed

Lines changed: 1660 additions & 1368 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/headers.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ on:
66
- cron: "0 0 * * 0" # Runs every Sunday at midnight UTC
77

88
permissions:
9-
contents: write # Grants write access to push changes
9+
contents: write # Grants write access to push changes
1010
pull-requests: write # and create PRs
1111

1212
jobs:
@@ -31,7 +31,7 @@ jobs:
3131
run: pnpm install
3232

3333
- name: Check for new headers on IANA.ORG
34-
run: pnpm unrun tools/headers.ts
34+
run: pnpm node tools/headers.ts
3535

3636
- name: Check for changes
3737
id: git-state

.github/workflows/node.js.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
strategy:
1616
fail-fast: false
1717
matrix:
18-
node-version: [20.19.0, 20.x, 22.12.0, 22.x, 24.0.0, 24.x, 26.0.0, 26.x]
18+
node-version: [22.19.0, 22.x, 24.0.0, 24.x, 26.0.0, 26.x]
1919
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
2020
steps:
2121
- name: Checkout

AGENTS.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,17 +103,16 @@ interface SampleInterface {
103103
- **Zod**: Use named import `import { z } from "zod"`
104104
- **Ramda**: Use namespace import `import * as R from "ramda"`
105105
- **Node.js built-ins**: Use `node:` prefix
106-
- **Type-only imports**: Use `import type` for types and interfaces
107-
- **Relative imports**: Must be extensionless
108-
- Combine import from the same module into a single line
106+
- **Type-only imports**: Use `import type` for types and interfaces (verbatimModuleSyntax)
107+
- **Relative imports**: Use `.ts` extension when file is meant to be run by `node` (`example` and testing workspaces)
108+
- Combine imports from the same module into a single statement
109109

110110
```typescript
111111
import { z } from "zod";
112112
import * as R from "ramda";
113113
import { dirname } from "node:path";
114-
import type { SomeType } from "./module-a";
115-
import { someValue } from "./module-b";
116-
import { anotherValue, type AnotherType } from "./module-c";
114+
import type { SomeType } from "./module-a"; // for compiled code
115+
import { someValue } from "./module-b.ts"; // for node execution
117116
```
118117

119118
### 5. Type Declaration Convention

CHANGELOG.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,58 @@
11
# Changelog
22

3+
## Version 28
4+
5+
### v28.0.0
6+
7+
- Supported Node.js versions: `^22.19.0 || ^24.0.0 || ^26.0.0`;
8+
- Zod compatibility: `^4.3.4` (supports Zod 4.4+ without upper limit);
9+
- The Zod plugin is no longer installed automatically — it's an optional peer dependency now:
10+
- To keep using `.example()`, `.label()`, `.remap()`, `.deprecated()` and methods on schemas, as well as runtime
11+
distinguishable brands, install the `@express-zod-api/zod-plugin` manually and import it (ideally at the top of a
12+
file declaring your `Routing`);
13+
- Breaking change: `ZodType::brand()` method is no longer patched by the plugin:
14+
- Use `.xBrand()` method instead — alias for `.meta({ "x-brand": ... })` and does not conflict with Zod 4.4;
15+
- Breaking changes to the `createConfig()` argument (object):
16+
- property `wrongMethodBehavior` (number) changed to `hintAllowedMethods` (boolean);
17+
- property `methodLikeRouteBehavior` (string literal) changed to `recognizeMethodDependentRoutes` (boolean);
18+
- Breaking change to the `EndpointsFactory::build()` argument (object):
19+
- property `shortDescription` renamed to `summary`;
20+
- Breaking change to the `Documentation` constructor argument (object):
21+
- property `hasSummaryFromDescription` (boolean) replaced with `summarizer` (function);
22+
- If used with `false` value, replace it with `summarizer: ({ summary, trim }) => trim(summary)` for same behavior;
23+
- Featuring `summarizer` option to customize the summary of the Endpoint in the generated Documentation:
24+
- The function receives `summary`, `description` and the default `trim()` function as arguments;
25+
- The default summarizer uses `description` as a fallback for missing `summary`;
26+
- The `trim()` function accepts a string and the limit (default: 50, best practice) that you can now customize;
27+
- Breaking change to the `Integration` constructor argument (object):
28+
- property `noContent` renamed to `noBodySchema`;
29+
- Consider using [the automated migration](https://www.npmjs.com/package/@express-zod-api/migration):
30+
- Now requires `eslint@^10.0.0` and `typescript-eslint@^8.58.0`.
31+
32+
```diff
33+
createConfig({
34+
- wrongMethodBehavior: 404,
35+
+ hintAllowedMethods: false,
36+
- methodLikeRouteBehavior: "path",
37+
+ recognizeMethodDependentRoutes: false,
38+
});
39+
40+
factory.build({
41+
- shortDescription: "Retrieves the user.",
42+
+ summary: "Retrieves the user.",
43+
});
44+
45+
new Documentation({
46+
- hasSummaryFromDescription: false,
47+
+ summarizer: ({ summary, trim }) => trim(summary),
48+
});
49+
50+
new Integration({
51+
- noContent: z.undefined(),
52+
+ noBodySchema: z.undefined(),
53+
});
54+
```
55+
356
## Version 27
457

558
### v27.3.0

README.md

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,7 @@ Much can be customized to fit your needs.
164164

165165
- [Typescript](https://www.typescriptlang.org/) first.
166166
- Web server — [Express.js](https://expressjs.com/) v5.
167-
- Schema validation — [Zod 4.x](https://github.com/colinhacks/zod) including [Zod Plugin](#zod-plugin):
168-
- For using with Zod 3.x, install the framework versions below 24.0.0.
167+
- Schema validation — [Zod 4.x](https://github.com/colinhacks/zod);
169168
- Supports any logger having `info()`, `debug()`, `error()` and `warn()` methods;
170169
- Built-in console logger with colorful and pretty inspections by default.
171170
- Generators:
@@ -243,7 +242,7 @@ const helloWorldEndpoint = defaultEndpointsFactory.build({
243242
Connect your endpoint to the `/v1/hello` route:
244243

245244
```ts
246-
import { Routing } from "express-zod-api";
245+
import type { Routing } from "express-zod-api";
247246

248247
const routing: Routing = {
249248
v1: {
@@ -920,7 +919,7 @@ it normalizes errors into consistent HTTP responses with sensible status codes.
920919
- Routing, parsing and upload issues:
921920
- Handled by `ResultHandler` configured as `errorHandler` (the defaults is `defaultResultHandler`);
922921
- Parsing errors: passed through as-is (typically `HttpError` with `4XX` code used for response by default);
923-
- Routing errors: `404` or `405`, based on `wrongMethodBehavior` configuration;
922+
- Routing errors: `404` or `405`, based on `hintAllowedMethods` configuration;
924923
- Upload issues: thrown only if `upload.limitError` is configured (`HttpError::statusCode` can be used for response);
925924
- For other errors the default status code is `500`;
926925
- `ResultHandler` failures:
@@ -1100,8 +1099,21 @@ expect(output).toEqual({ collectedContext: ["prev"], testLength: 9 });
11001099

11011100
## Zod Plugin
11021101

1103-
Express Zod API augments Zod using [Zod Plugin](https://www.npmjs.com/package/@express-zod-api/zod-plugin),
1104-
adding the runtime helpers the framework relies on.
1102+
The [@express-zod-api/zod-plugin](https://www.npmjs.com/package/@express-zod-api/zod-plugin) is an optional package
1103+
that extends Zod with convenience methods:
1104+
1105+
- `.xBrand(name)` — shorthand for `.meta({ "x-brand": name })`;
1106+
- `.example(value)` — shorthand for `.meta({ examples: [value] })`;
1107+
- `.deprecated()` — shorthand for `.meta({ deprecated: true })`;
1108+
- `.label(text)` — shorthand for `.meta({ default: text })` on `ZodDefault`;
1109+
- `.remap(mapping)` — for renaming `ZodObject` shape properties;
1110+
1111+
To benefit from these methods, install `@express-zod-api/zod-plugin` and import it once, preferably at the top of a
1112+
file declaring your `Routing`.
1113+
1114+
```ts
1115+
import "@express-zod-api/zod-plugin"; // in your routing.ts file
1116+
```
11051117

11061118
## End-to-End Type Safety
11071119

@@ -1162,20 +1174,21 @@ in the generated documentation of your API. Consider the following example:
11621174
import { defaultEndpointsFactory } from "express-zod-api";
11631175

11641176
const exampleEndpoint = defaultEndpointsFactory.build({
1165-
shortDescription: "Retrieves the user.", // <—— this becomes the summary line
1177+
summary: "Retrieves the user.",
11661178
description: "The detailed explanaition on what this endpoint does.",
11671179
input: z.object({
11681180
id: z
1169-
.string()
1170-
.example("123") // input examples should be set before transformations
1181+
.string() // input examples should be set before transformations
1182+
.example("123") // requires Zod Plugin, or .meta({ examples: ["123"] })
11711183
.transform(Number)
11721184
.describe("the ID of the user"),
11731185
}),
11741186
// ..., similarly for output and middlewares
11751187
});
11761188
```
11771189

1178-
You can also use `schema.meta({ id: "UniqueName" })` for custom schema naming.
1190+
Setting examples via `.example()` requires [Zod Plugin](#zod-plugin). You can also use `.meta({ examples: [] })` and
1191+
`.meta({ id: "UniqueName" })` for custom schema naming.
11791192
_See the complete example of the generated documentation
11801193
[here](https://github.com/RobinTail/express-zod-api/blob/master/example/example.documentation.yaml)_
11811194

@@ -1213,18 +1226,18 @@ new Documentation({
12131226

12141227
## Deprecated schemas and routes
12151228

1216-
As your API evolves, you may need to mark some parameters or routes as deprecated before deleting them. For this
1217-
purpose, the `.deprecated()` method is available on each schema and `Endpoint`, it's immutable.
1218-
You can also deprecate all routes the `Endpoint` assigned to by setting `EndpointsFactory::build({ deprecated: true })`.
1229+
As your API evolves, you may need to mark some parameters or routes as deprecated before deleting them. This can be
1230+
achieved using the corresponding method or metadata. The `.deprecated()` method on Zod schema requires to install the
1231+
[Zod Plugin](#zod-plugin). Consider the following example:
12191232

12201233
```ts
1221-
import { Routing } from "express-zod-api";
1234+
import type { Routing } from "express-zod-api";
12221235
import { z } from "zod";
12231236

12241237
const someEndpoint = factory.build({
12251238
deprecated: true, // deprecates all routes the endpoint assigned to
12261239
input: z.object({
1227-
prop: z.string().deprecated(), // deprecates the property or a path parameter
1240+
prop: z.string().deprecated(), // requires Zod Plugin, or .meta({ deprecated: true })
12281241
}),
12291242
});
12301243

@@ -1236,10 +1249,11 @@ const routing: Routing = {
12361249

12371250
## Customizable brands handling
12381251

1239-
You can customize handling rules for your schemas in Documentation and Integration. Use the `.brand()` method on your
1240-
schema to make it special and distinguishable for the framework in runtime. Using symbols is recommended for branding.
1241-
After that use the `brandHandling` feature of both constructors to declare your custom implementation. In case you need
1242-
to reuse a handling rule for multiple brands, use the exposed types `Depicter` and `Producer`.
1252+
You can customize handling rules for your schemas in Documentation and Integration. The framework treats your schema
1253+
specially based on its `x-brand` metadata. When the [Zod Plugin](#zod-plugin) is installed you can conveniently use
1254+
the `.xBrand()` method on Zod schema, preferably with a symbol argument for its branding. After that use the
1255+
`brandHandling` feature of both constructors to declare your custom implementation. In case you need to reuse a
1256+
handling rule for multiple brands, use the exposed types `Depicter` and `Producer`.
12431257

12441258
```ts
12451259
import ts from "typescript";
@@ -1252,7 +1266,7 @@ import {
12521266
} from "express-zod-api";
12531267

12541268
const myBrand = Symbol("MamaToldMeImSpecial"); // I recommend to use symbols for this purpose
1255-
const myBrandedSchema = z.string().brand(myBrand);
1269+
const myBrandedSchema = z.string().xBrand(myBrand); // requires Zod Plugin, or .meta({ "x-brand": myBrand })
12561270

12571271
const ruleForDocs: Depicter = (
12581272
{ zodSchema, jsonSchema }, // jsonSchema is the default depiction

SECURITY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
| Version | Code name | Release | Supported |
66
| ------: | :------------ | :------ | :----------------: |
7+
| 28.x.x | Koko | 05.2026 | :white_check_mark: |
78
| 27.x.x | Nikki | 02.2026 | :white_check_mark: |
89
| 26.x.x | Lia | 12.2025 | :white_check_mark: |
910
| 25.x.x | Sara | 08.2025 | :white_check_mark: |

cjs-test/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
2-
"extends": "@tsconfig/node20/tsconfig.json",
2+
"extends": "@tsconfig/node22/tsconfig.json",
33
"include": ["quick-start.ts"]
44
}

cjs-test/zod-plugin.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { createRequire } from "node:module";
22
const require = createRequire(import.meta.url);
33

4-
require("express-zod-api"); // side effect here via Zod Plugin
4+
require("@express-zod-api/zod-plugin"); // side effect here
55
const z = require("zod"); // ensure CJS version of Zod is used
66

77
describe("Zod plugin in CJS environment", () => {

compat-test/dts.spec.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,6 @@ import { describe, test, expect } from "vitest";
22
import { readFile } from "node:fs/promises";
33

44
describe("DTS", () => {
5-
test("Framework must import Zod plugin", async () => {
6-
const fwDts = await readFile(
7-
"./node_modules/express-zod-api/dist/index.d.ts",
8-
"utf-8",
9-
);
10-
expect(fwDts).toMatch(`import "@express-zod-api/zod-plugin";`);
11-
});
12-
135
test("Zod plugin must import augmentation", async () => {
146
const pluginDts = await readFile(
157
"./node_modules/express-zod-api/node_modules/@express-zod-api/zod-plugin/dist/index.d.ts",

compat-test/eslint.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ import migration from "@express-zod-api/migration";
33

44
export default [
55
{ languageOptions: { parser }, plugins: { migration } },
6-
{ files: ["**/*.ts"], rules: { "migration/v27": "error" } },
6+
{ files: ["**/*.ts"], rules: { "migration/v28": "error" } },
77
];

0 commit comments

Comments
 (0)