expediate 1.0.5 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +138 -0
- package/CONTRIBUTING.md +150 -0
- package/README.md +278 -779
- package/dist/apis.d.ts +372 -12
- package/dist/apis.d.ts.map +1 -1
- package/dist/apis.js +483 -65
- package/dist/apis.js.map +1 -1
- package/dist/cjs/index.js +2290 -807
- package/dist/git.d.ts +1 -1
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +5 -5
- package/dist/git.js.map +1 -1
- package/dist/http-objects.d.ts +26 -0
- package/dist/http-objects.d.ts.map +1 -0
- package/dist/http-objects.js +588 -0
- package/dist/http-objects.js.map +1 -0
- package/dist/index.d.ts +6 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/jwt-auth.d.ts +11 -0
- package/dist/jwt-auth.d.ts.map +1 -1
- package/dist/jwt-auth.js +9 -9
- package/dist/jwt-auth.js.map +1 -1
- package/dist/middleware.js +2 -2
- package/dist/middleware.js.map +1 -1
- package/dist/mimetypes.json +882 -1
- package/dist/misc.d.ts +161 -25
- package/dist/misc.d.ts.map +1 -1
- package/dist/misc.js +228 -80
- package/dist/misc.js.map +1 -1
- package/dist/openapi.d.ts +156 -13
- package/dist/openapi.d.ts.map +1 -1
- package/dist/openapi.js +214 -71
- package/dist/openapi.js.map +1 -1
- package/dist/router-types.d.ts +760 -0
- package/dist/router-types.d.ts.map +1 -0
- package/dist/router-types.js +23 -0
- package/dist/router-types.js.map +1 -0
- package/dist/router.d.ts +7 -530
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +128 -375
- package/dist/router.js.map +1 -1
- package/dist/static.d.ts +2 -2
- package/dist/static.d.ts.map +1 -1
- package/dist/static.js +77 -22
- package/dist/static.js.map +1 -1
- package/docs/THREAT_MODEL.md +52 -0
- package/docs/api-builder-v2-design.md +644 -0
- package/docs/api-builder-v3-design.md +397 -0
- package/docs/api-builder.md +454 -0
- package/docs/benchmark.md +27 -0
- package/docs/body-parsing.md +223 -0
- package/docs/errors.md +359 -0
- package/docs/expediate.png +0 -0
- package/docs/git.md +139 -0
- package/docs/jwt-auth.md +251 -0
- package/docs/logo.svg +12 -0
- package/docs/middleware.md +264 -0
- package/docs/openapi.md +180 -0
- package/docs/router.md +356 -0
- package/docs/static.md +128 -0
- package/docs/wiki.json +123 -0
- package/package.json +30 -8
- package/dist/cjs/apis.js +0 -327
- package/dist/cjs/git.js +0 -293
- package/dist/cjs/jwt-auth.js +0 -532
- package/dist/cjs/middleware.js +0 -511
- package/dist/cjs/mimetypes.json +0 -1
- package/dist/cjs/misc.js +0 -787
- package/dist/cjs/openapi.js +0 -485
- package/dist/cjs/router.js +0 -898
- package/dist/cjs/static.js +0 -669
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to **expediate** are documented here.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## [1.0.6] — 25 June 2026
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **API Builder v2** (see `docs/api-builder-v2-design.md`):
|
|
11
|
+
- **Controllers** — `defineController()` / `ServiceDefinition.controllers` split an API into per-domain files merged into one router and one OpenAPI document, with prefix joining, global specificity sorting, and build-time duplicate-route detection
|
|
12
|
+
- **Guards** — pre-handler hooks in the `ctx` world at API, controller, and route level (`OperationMeta.guards`); returned objects accumulate into the new `ctx.state` bag
|
|
13
|
+
- **Auth binding** — `ServiceDefinition.auth` auto-registers an `authenticate` middleware and enforces declarative `permission` requirements (`OperationMeta.permission` / controller-level) via a default check matching `jwtPlugin.requirePermission` semantics, overridable for resource-scoped models
|
|
14
|
+
- **Request validation** — `ServiceDefinition.validate` executes the JSON Schemas declared in `requestBody` metadata (subset validator, `$ref` resolved against the new `ServiceDefinition.schemas`); failures return `400` with `{ message, fieldErrors }`
|
|
15
|
+
- **Context ergonomics** — `ctx.params` (alias of `ctx.query.route`) and `ApiContext<TUser, TState>` generics
|
|
16
|
+
- **OpenAPI security output** — secured operations emit `security: [{ bearerAuth: [] }]`, an `x-required-permissions` vendor extension, and `components.securitySchemes`
|
|
17
|
+
- **Body parsing** — `raw()` (body as `Buffer`) and `text()` (body as `string`) middleware; Brotli (`br`) request decompression alongside gzip/deflate; new `type` option (string / `string[]` / predicate, with `*` wildcards) to control which requests a parser handles, and a `verify(req, res, buf, encoding)` hook run on the raw body before parsing (throw to reject)
|
|
18
|
+
- **Routing** — `router.route(path)` fluent builder for registering several HTTP methods against one path
|
|
19
|
+
- **Method handling** — `HEAD` requests are now served by the matching `GET` handler (body suppressed); unhandled `OPTIONS` on a registered path replies `204` with an `Allow` header; the `405`/OPTIONS `Allow` headers advertise `HEAD` (when a `GET` exists) and `OPTIONS`. Explicit `use()`/`all()` handlers and `cors()` still take precedence
|
|
20
|
+
- **Routing** — `head()` and `options()` registration helpers (on the router and the `route()` builder) for method-specific `HEAD`/`OPTIONS` handlers
|
|
21
|
+
- **Error handling** — `router.error()` registers an ordered, forwardable error-middleware chain (`(err, req, res, next)`); when exhausted it falls back to the single `onError()` handler and then **bubbles to the parent router**, so a root handler can catch failures from deeply nested sub-routers. `apiBuilder` adds a `ServiceDefinition.onError(err, ctx, req)` hook. `ErrorMiddleware` type exported
|
|
22
|
+
- **Response helpers** — `res.append()`, `res.vary()`, `res.location()`, `res.clearCookie()`, `res.sendStatus()`, `res.attachment()`, and a per-response `res.locals` object
|
|
23
|
+
- **Headers** — `req.header(name)` (case-insensitive request-header lookup, `referer`/`referrer` equivalent) and chainable `res.header(field, value)`
|
|
24
|
+
- **Route safety** — `RegExp` routes that use the `g` (global) or `y` (sticky) flag are now rejected at registration; those flags make `exec()` stateful and cause intermittent, hard-to-debug `404`s. Inline `:name(constraint)` patterns are scanned so regex metacharacters inside them are no longer mistaken for glob wildcards
|
|
25
|
+
- **Cookies** — `res.cookie()` now percent-encodes values (after any `j:`/`s:` wrapping) so semicolons, commas, spaces, quotes, and backslashes transmit safely; cookie parsing de-quotes (RFC 6265 quoted-string) and percent-decodes values before interpreting `j:`/`s:` prefixes. Signed-cookie HMAC remains computed over the unencoded value
|
|
26
|
+
- **Response validation** — `apiBuilder` accepts an optional second argument, `ApiBuilderOptions`, controlling validation. `validateResponses` checks each handler's return against its declared `responses['200']` schema: `true` returns `500 { message, fieldErrors }` when the server would emit an off-spec body, `'warn'` logs the mismatch via `console.warn` and sends the response unchanged. `validateRequests` (default on) toggles the existing request check. `ServiceDefinition.validate` accepts the same `boolean | ApiBuilderOptions` shape
|
|
27
|
+
- **MIME types** — bundled `src/mimetypes.json` table expanded with hundreds of additional extensions
|
|
28
|
+
- `ErrorHandler` and `RouteInfo` types re-exported from the public API
|
|
29
|
+
- `TokenPayload` and `UserRecord` types re-exported from the public API
|
|
30
|
+
- `CorsOptions` type re-exported from the public API
|
|
31
|
+
- `cors()` middleware documented in README
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
- **Breaking:** a duplicate `(verb, path)` route pair now **throws at build time** in `apiBuilder` / `openApiSpec` (was silent shadowing)
|
|
35
|
+
- **Breaking (compile-time):** `ApiContext.user` defaults to `unknown` instead of `any`
|
|
36
|
+
- **Breaking (compile-time):** `RouterRequest.body` is now typed `unknown` instead of `any` — narrow it before use (e.g. `req.body as { id: string }`)
|
|
37
|
+
- `req.user` is now typed (`TokenPayload`) on `RouterRequest` when the JWT plugin is loaded, instead of requiring an `as any` cast
|
|
38
|
+
- `ServiceDefinition.schemas` supersedes `SpecOptions.schemas` on name conflicts (the spec-options form is kept as a fallback)
|
|
39
|
+
|
|
40
|
+
### Removed
|
|
41
|
+
- **Breaking:** `router.setNotFound()` and the not-found handler hook. Register a catch-all layer **last** instead — `app.all('/**', (req, res) => …)` — which matches in registration order and, unlike `setNotFound()`, also fires correctly inside mounted sub-routers. The built-in `Cannot METHOD /path` 404 remains the default fallback
|
|
42
|
+
|
|
43
|
+
### Performance
|
|
44
|
+
- Per-request `req`/`res` helpers are now defined once on shared prototypes and attached via `Object.setPrototypeOf` instead of allocating ~20 closures on every request; the `status()` range check was folded into the single prototype method (removing a second per-request allocation)
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
- `cors()` with an array `origin` now matches the request's `Origin` header against the allow-list and echoes back only the matching entry (previously it only ever matched a single string origin, so an array never granted access)
|
|
48
|
+
- Body parsing no longer returns `415` when a parser's content type doesn't match — it passes through to the next middleware, so parser stacks (`json()` + `formEncoded()` + …) compose correctly
|
|
49
|
+
- Static file serving hardened against control characters in the path and corrected path-handling edge cases
|
|
50
|
+
- Consistent method-routing behaviour (`HEAD`/`OPTIONS`/`405`) across registered and unregistered paths
|
|
51
|
+
- Misc. test-reliability improvements
|
|
52
|
+
|
|
53
|
+
### Internal / Tooling
|
|
54
|
+
- Split the ~2,200-line `src/router.ts` into `router.ts` + `router-types.ts` (type declarations) + `http-objects.ts` (req/res augmentation and cookie helpers); the public API and exports are unchanged
|
|
55
|
+
- Added a type-aware ESLint flat config (`eslint.config.js`) and `tsconfig.eslint.json`; `npm run lint` now passes. Reduced `any` usage across `src/`
|
|
56
|
+
- Bumped `esbuild` (build-time dev dependency) to resolve security advisories; pruned stale repo files (`AUDIT.md`, duplicate `docs/middlewares.md`)
|
|
57
|
+
- Documentation overhaul: corrected the JWT refresh-token description (signed JWT keyed by `jti`, opt-in via `refreshTokenStore`, `501` when absent — was documented as an always-on opaque hex token), the Brotli request-decompression support (was documented as unsupported), `static.md`'s conditional-request coverage (`If-Match`/`If-Unmodified-Since` → `412`), and `git.md`'s `gitCreate` `bare` option; documented `router.route()`, `raw()`, and `text()`. Removed `ROADMAP.md`, `AUDIT.md` (disposable, point-in-time planning/audit artifacts), and the `docs/mdlw/` directory (20 one-function-per-file pages that duplicated the topic docs in `docs/`) — `docs/wiki.json` updated to drop the now-removed entries
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## [1.0.5] — 8 June 2026
|
|
62
|
+
|
|
63
|
+
### Added
|
|
64
|
+
- **ETag / conditional GET** — `res.etag()` response helper and `conditionalGet()` middleware implement RFC 7232 (`If-None-Match`, `If-Modified-Since`) so clients receive 304 Not Modified when their cache is fresh
|
|
65
|
+
- **Path parameter type constraints** — inline regex in route patterns (`:id(\d+)`, `:slug([a-z-]+)`) so parameters are validated at the routing level without extra middleware
|
|
66
|
+
- **Asymmetric JWT keys** — `createJwtPlugin` now supports RS256/384/512 and ES256/384/512 algorithms via `accessTokenPrivateKey` / `accessTokenPublicKey` PEM options alongside the existing HMAC `accessTokenSecret`
|
|
67
|
+
- **Directory listing sort** — `serveStatic` directory index now supports column sort via `?C=N|M|S;O=A|D` query params (directories always listed first)
|
|
68
|
+
- **ESM-first with CJS compatibility** — package is now `"type": "module"` with a bundled CJS shim at `dist/cjs/index.js`; dual `exports` field in `package.json` routes consumers to the right build automatically
|
|
69
|
+
|
|
70
|
+
### Fixed
|
|
71
|
+
- Cookie reading and writing: signed cookies (HMAC-SHA256 via `createRouter({ secret })`) and JSON-prefixed cookies (`j:`) now work correctly on both read and write paths
|
|
72
|
+
- Async error handling: async middleware rejections are now caught and routed to the error handler (fixes unhandled promise rejections)
|
|
73
|
+
- `listen()` now returns the underlying `http.Server` / `https.Server` instance for graceful shutdown and ephemeral port discovery
|
|
74
|
+
- Git handler options (`strict`, `timeout`, `gitPath`) documented and applied correctly
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## [1.0.4] — May 2026
|
|
79
|
+
|
|
80
|
+
### Added
|
|
81
|
+
- **OpenAPI spec generation** — `describe()`, `openApiSpec()`, and `serializeSpec()` (JSON and YAML) for annotating API service routes with schema metadata
|
|
82
|
+
- **API service builder** — `apiBuilder()` for defining scoped (singleton / keyed / ephemeral) HTTP services with automatic JSON error handling and route priority sorting
|
|
83
|
+
- **Middleware suite** — `compress()` (Brotli/gzip/deflate), `requestId()`, `rateLimit()`, `cacheControl()`, `csrf()` (double-submit cookie), `securityHeaders()`, `conditionalGet()`
|
|
84
|
+
- **CORS support** — `cors()` middleware with origin, credentials, preflight guard, and `Vary` header support
|
|
85
|
+
|
|
86
|
+
### Fixed
|
|
87
|
+
- Common error schema standardised to `{ status, message }` across all built-in error responses
|
|
88
|
+
- Git Smart HTTP handler corrected and end-to-end tested
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## [1.0.3] — Mars 2026
|
|
93
|
+
|
|
94
|
+
### Added
|
|
95
|
+
- **JWT authentication plugin** — `createJwtPlugin()` with login, refresh (token rotation), logout, `authenticate`, `authorize`, `requireRole`, and `requirePermission` middleware; HMAC HS256/384/512 support
|
|
96
|
+
- **TypeScript rewrite** — entire codebase converted to strict TypeScript (ESNext target, `isolatedModules`)
|
|
97
|
+
- **Router interface improvements** — `router.routes()`, `router.onError()`, sub-router path stripping, and `req`/`res` helper augmentation (`req.json()`, `req.text()`, `res.status()`, `res.cookie()`, `res.redirect()`, `res.download()`, `res.type()`)
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## [1.0.2] — Mars 2026
|
|
102
|
+
|
|
103
|
+
### Added
|
|
104
|
+
- **Git Smart HTTP gateway** — `gitHandler()` serves `git clone`, `git fetch`, and `git push` over HTTP; `gitCreate()` initialises bare repositories programmatically
|
|
105
|
+
- **Body parsing** — `json()`, `formData()`, `formEncoded()`, `parseBody()`, `streamFormData()` with size limits, gzip/deflate decompression, and multipart boundary parsing
|
|
106
|
+
- **Request logger** — `logger()` middleware with ANSI-coloured output, structured JSON mode, and lost-request tracking
|
|
107
|
+
- **RegExp routes** — route paths may be `RegExp` instances; named capture groups populate `req.params`
|
|
108
|
+
- **Glob patterns** — `*`, `**`, `?` wildcard support in route paths
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## [1.0.1] — Mars 2026
|
|
113
|
+
|
|
114
|
+
### Added
|
|
115
|
+
- Initial release
|
|
116
|
+
- `createRouter()` — zero-dependency HTTP/HTTPS/HTTP2 server with Express-compatible `use()`, `get()`, `post()`, `put()`, `delete()`, `patch()`, `all()` route registration
|
|
117
|
+
- Named route parameters (`:name`, `:id(\d+)`)
|
|
118
|
+
- `serveStatic()`, `serveFile()`, `sendFile()` for static file serving with ETag caching, dotfile protection, and path traversal guards
|
|
119
|
+
- MIME type detection via `src/mimetypes.json`
|
|
120
|
+
- Dual CJS + ESM output via `tsc` + `esbuild`
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## [1.0.0] — Mars 2026
|
|
125
|
+
|
|
126
|
+
_Not documented_
|
|
127
|
+
|
|
128
|
+
## [0.0.3] — 2022
|
|
129
|
+
|
|
130
|
+
_Not documented_
|
|
131
|
+
|
|
132
|
+
## [0.0.2] — 2021
|
|
133
|
+
|
|
134
|
+
_Not documented_
|
|
135
|
+
|
|
136
|
+
## [0.0.1] — 2021
|
|
137
|
+
|
|
138
|
+
_Not documented_
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
First of all, thank you for taking the time to read this. It means you're considering contributing to one of my projects — and that genuinely matters to me.
|
|
4
|
+
|
|
5
|
+
## Who I am and why this exists
|
|
6
|
+
|
|
7
|
+
I'm a backend and systems developer. I build tools — mostly because I need them, and sometimes because I get frustrated that nothing out there does exactly what I want without dragging along a mountain of complexity I didn't ask for.
|
|
8
|
+
|
|
9
|
+
My projects exist because they solved a real problem for me. They're designed to be **lightweight, easy to integrate, and honest about what they do**. I care about optimization, but never at the cost of core functionality — that's exactly the kind of tradeoff that pushes me away from many professional solutions. If a feature makes the tool harder to understand or harder to use, it's probably not worth it.
|
|
10
|
+
|
|
11
|
+
I like to understand the tools I work with, deeply. I expect the same curiosity from people who contribute here. You don't have to know everything — but you should want to.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Code of conduct
|
|
16
|
+
|
|
17
|
+
This community runs on a simple principle: **be decent to each other**.
|
|
18
|
+
|
|
19
|
+
I don't expect everyone to agree. In fact, I expect the opposite — technically skilled people tend to have strong opinions and can be stubborn (myself included). That's fine. Disagreement is productive. But it requires a constant effort of **comprehension, tolerance, and ego management**.
|
|
20
|
+
|
|
21
|
+
What I won't tolerate:
|
|
22
|
+
- Hostility, condescension, or dismissiveness toward other contributors
|
|
23
|
+
- Behavior that makes this space uncomfortable for others or for me
|
|
24
|
+
|
|
25
|
+
I reserve the right to remove any member whose behavior is inappropriate or disruptive — to the community or to me personally. There's no appeals process for that.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## What you can contribute
|
|
30
|
+
|
|
31
|
+
All forms of contribution are welcome:
|
|
32
|
+
|
|
33
|
+
- **Bug reports** — Something's broken? Tell me.
|
|
34
|
+
- **Feature requests** — Have an idea? Let's talk about it.
|
|
35
|
+
- **Documentation** — Often the most underrated contribution.
|
|
36
|
+
- **Tests & benchmarks** — Always appreciated, especially if they reveal something.
|
|
37
|
+
- **Translations** — If you want to make the project accessible to more people.
|
|
38
|
+
- **Code** — Of course.
|
|
39
|
+
|
|
40
|
+
A note on **reviews and corrections**: I prefer that feedback on PRs comes from people who have already contributed actively to the project. I want to avoid people who show up only to critique, or who try to take ownership of someone else's effort. If you're new here, start by contributing before reviewing.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Git workflow
|
|
45
|
+
|
|
46
|
+
I care about history. Real history — not a rewritten, sanitized version of it.
|
|
47
|
+
|
|
48
|
+
### Merge strategy
|
|
49
|
+
|
|
50
|
+
- **Merges over rebases.** I want to see what actually happened, not what someone wished had happened.
|
|
51
|
+
- **Non-fast-forward merges** are preferred. They preserve the branch structure and make it clear on which version a ticket was resolved.
|
|
52
|
+
- **Squash with caution.** It can be useful to clean up meaningless save-state commits with no real progress, but it should never be applied to a significant branch. Squashing a week of work into one commit destroys context.
|
|
53
|
+
|
|
54
|
+
### Branch naming
|
|
55
|
+
|
|
56
|
+
Use the following format:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
{group}/{alias}/{topic}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
- **group** — the type of work: `feat`, `fix`, `chore`, `release`, `docs`, `test`, etc.
|
|
63
|
+
- **alias** — something that identifies you as the author (your GitHub handle, initials, whatever you use consistently)
|
|
64
|
+
- **topic** — a short, free-form description of the work
|
|
65
|
+
|
|
66
|
+
Examples:
|
|
67
|
+
```
|
|
68
|
+
feat/alex/user-auth
|
|
69
|
+
fix/jdoe/connection-pool-leak
|
|
70
|
+
chore/sam/update-ci-deps
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Publish early
|
|
74
|
+
|
|
75
|
+
**Favor small, focused branches.** Don't disappear for three weeks and resurface with a 2,000-line PR.
|
|
76
|
+
|
|
77
|
+
Open a **draft PR as early as possible**, even if the work isn't done. It signals what you're working on, invites early feedback, and dramatically reduces the chance of a rejection. A PR that surprises everyone is a PR at risk.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Opening an issue
|
|
82
|
+
|
|
83
|
+
I don't have formal templates. I don't want the friction of a strict template to discourage someone from reporting a bug.
|
|
84
|
+
|
|
85
|
+
That said, a good issue saves everyone time. When in doubt, include:
|
|
86
|
+
|
|
87
|
+
- What you expected to happen
|
|
88
|
+
- What actually happened
|
|
89
|
+
- How to reproduce it (minimal example if possible)
|
|
90
|
+
- Your environment (OS, version, runtime)
|
|
91
|
+
|
|
92
|
+
For feature requests: explain the problem you're trying to solve, not just the solution you have in mind. Context helps more than a spec.
|
|
93
|
+
|
|
94
|
+
If your issue is vague, I'll ask for more detail. That's not a rejection — it's a conversation.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Submitting a pull request
|
|
99
|
+
|
|
100
|
+
Before submitting, ask yourself:
|
|
101
|
+
|
|
102
|
+
- [ ] Is there an open, accepted issue for this? If not — did you discuss it first?
|
|
103
|
+
- [ ] Do the existing tests still pass?
|
|
104
|
+
- [ ] Did you add tests for the new behavior? (See below.)
|
|
105
|
+
- [ ] Is documentation up to date?
|
|
106
|
+
- [ ] If performance-relevant, did you run benchmarks?
|
|
107
|
+
|
|
108
|
+
**If you skipped any of these**, that's okay — but explain why in the PR description. "I didn't add tests because this only affects the build pipeline" is fine. Silence is not.
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Code style
|
|
114
|
+
|
|
115
|
+
In case no linter are configured, the rule is simple: **match what's already there**.
|
|
116
|
+
|
|
117
|
+
Read the existing code. Understand its style. Integrate into it — don't reformat it to match your preferences.
|
|
118
|
+
|
|
119
|
+
Beyond formatting, I pay close attention to:
|
|
120
|
+
|
|
121
|
+
- **Readability** — Can someone who didn't write this understand it quickly?
|
|
122
|
+
- **Function length and complexity** — If a function is doing too much, it probably needs to be split.
|
|
123
|
+
- **Naming** — Names should reflect intent, not implementation.
|
|
124
|
+
- **Unnecessary complexity** — If there's a simpler way, use it.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Development environment
|
|
129
|
+
|
|
130
|
+
I change environments often, so I've made a point of keeping setups lightweight and self-contained. Everything you need to get started is in the `README`.
|
|
131
|
+
|
|
132
|
+
If you run into something that isn't documented and blocks you — open an issue or reach out to an active contributor. If the setup is broken or confusing in a way that isn't documented, that's a real bug and worth filing.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Recognition
|
|
137
|
+
|
|
138
|
+
Every contributor is credited in the `CHANGELOG` at release time.
|
|
139
|
+
|
|
140
|
+
Recurring contributors might also appear in the `README` as well.
|
|
141
|
+
|
|
142
|
+
For contributors, I'll ask for:
|
|
143
|
+
- A **real first name** (minimum) — aliases are fine for commits, but I want to know who's in this community
|
|
144
|
+
- A **real email address** (personal or professional) — this is how I'll reach you if needed and how attribution is tracked
|
|
145
|
+
|
|
146
|
+
You can use an alias in your git commits. But when it comes to the community and the changelog, I want real people behind the names.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
*This document reflects how I like to work. It will evolve. If something here is unclear or feels off, open an issue — that's a valid contribution too.*
|