@emmett-community/emmett-expressjs-with-openapi 0.2.0 → 0.3.0
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/README.md +178 -26
- package/dist/index.cjs +23 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -1
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
Express.js utilities for Emmett applications that want OpenAPI 3.x validation without pulling the whole Emmett core. This package wires [`express-openapi-validator`](https://github.com/cdimascio/express-openapi-validator) into the Emmett application builder so you can validate requests, responses, security handlers, file uploads, and formats from a single OpenAPI document.
|
|
4
4
|
|
|
5
|
+
[](https://www.npmjs.com/package/@emmett-community/emmett-expressjs-with-openapi) [](https://opensource.org/licenses/MIT) [](https://github.com/emmett-community/emmett-expressjs-with-openapi/actions/workflows/build_and_test.yml)
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- ✅ **OpenAPI 3.x validation** - Validate requests, responses, security, and file uploads.
|
|
10
|
+
- ✅ **Operation handlers** - Auto-wire handlers from `operationId` or `x-eov-operation-handler`.
|
|
11
|
+
- ✅ **Problem Details middleware** - RFC 7807 error responses out of the box.
|
|
12
|
+
- ✅ **ETag helpers** - Built-in optimistic concurrency utilities.
|
|
13
|
+
- ✅ **Testing helpers** - `ApiSpecification` and `ApiE2ESpecification` for tests.
|
|
14
|
+
- ✅ **Optional add-ons** - Firebase Auth security handlers and Pino HTTP logging.
|
|
15
|
+
|
|
5
16
|
## Why this package?
|
|
6
17
|
|
|
7
18
|
- Built as a standalone module extracted from the original `@event-driven-io/emmett` repo so it can evolve independently.
|
|
@@ -12,10 +23,30 @@ Express.js utilities for Emmett applications that want OpenAPI 3.x validation wi
|
|
|
12
23
|
|
|
13
24
|
```bash
|
|
14
25
|
npm install @emmett-community/emmett-expressjs-with-openapi
|
|
15
|
-
npm install express-openapi-validator
|
|
26
|
+
npm install express-openapi-validator # optional: OpenAPI validation
|
|
27
|
+
npm install pino-http # optional: HTTP logging
|
|
28
|
+
npm install @my-f-startup/firebase-auth-express # optional: Firebase Auth handlers
|
|
16
29
|
```
|
|
17
30
|
|
|
18
|
-
|
|
31
|
+
### Peer Dependencies
|
|
32
|
+
|
|
33
|
+
Required:
|
|
34
|
+
- `@event-driven-io/emmett` ^0.39.1
|
|
35
|
+
- `express` ^4.19.2
|
|
36
|
+
- `express-async-errors` ^3.1.1
|
|
37
|
+
- `http-problem-details` ^0.1.5
|
|
38
|
+
|
|
39
|
+
TypeScript types:
|
|
40
|
+
- `@types/express` ^4.17.21
|
|
41
|
+
- `@types/supertest` ^6.0.2 (if using testing helpers)
|
|
42
|
+
|
|
43
|
+
Optional (feature-gated):
|
|
44
|
+
- `express-openapi-validator` ^5.3.7 (OpenAPI validation)
|
|
45
|
+
- `pino-http` ^9.0.0 (HTTP logging)
|
|
46
|
+
- `@my-f-startup/firebase-auth-express` 0.1.0 (Firebase Auth handlers)
|
|
47
|
+
- `supertest` ^7.0.0 (required by testing helpers)
|
|
48
|
+
|
|
49
|
+
Versions follow what is listed in `package.json`.
|
|
19
50
|
|
|
20
51
|
## Quick start
|
|
21
52
|
|
|
@@ -23,15 +54,33 @@ Other peer requirements (`express`, `@event-driven-io/emmett`, etc.) follow the
|
|
|
23
54
|
import {
|
|
24
55
|
getApplication,
|
|
25
56
|
createOpenApiValidatorOptions,
|
|
57
|
+
startAPI,
|
|
26
58
|
} from '@emmett-community/emmett-expressjs-with-openapi';
|
|
27
59
|
|
|
28
|
-
const app = getApplication({
|
|
60
|
+
const app = await getApplication({
|
|
29
61
|
apis: [myApi],
|
|
30
62
|
openApiValidator: createOpenApiValidatorOptions('./openapi.yaml', {
|
|
31
63
|
validateRequests: true,
|
|
32
64
|
validateResponses: process.env.NODE_ENV === 'development',
|
|
33
65
|
}),
|
|
34
66
|
});
|
|
67
|
+
|
|
68
|
+
startAPI(app, { port: 3000 });
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Configuration
|
|
72
|
+
|
|
73
|
+
### OpenAPI validation
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
const app = await getApplication({
|
|
77
|
+
apis: [myApi],
|
|
78
|
+
openApiValidator: createOpenApiValidatorOptions('./openapi.yaml', {
|
|
79
|
+
validateRequests: true,
|
|
80
|
+
validateResponses: true,
|
|
81
|
+
operationHandlers: './handlers',
|
|
82
|
+
}),
|
|
83
|
+
});
|
|
35
84
|
```
|
|
36
85
|
|
|
37
86
|
Prefer to parse the spec once and share it? Load the `.yml` document manually and reuse it for routing, validation, and automatic `operationHandlers`.
|
|
@@ -41,9 +90,11 @@ import path from 'node:path';
|
|
|
41
90
|
import { readFileSync } from 'node:fs';
|
|
42
91
|
import { parse } from 'yaml';
|
|
43
92
|
|
|
44
|
-
const spec = parse(
|
|
93
|
+
const spec = parse(
|
|
94
|
+
readFileSync(new URL('./openapi.yml', import.meta.url), 'utf-8'),
|
|
95
|
+
);
|
|
45
96
|
|
|
46
|
-
const app = getApplication({
|
|
97
|
+
const app = await getApplication({
|
|
47
98
|
apis: [usersApi],
|
|
48
99
|
openApiValidator: createOpenApiValidatorOptions(spec, {
|
|
49
100
|
operationHandlers: path.join(path.dirname(import.meta.url), './handlers'),
|
|
@@ -51,16 +102,31 @@ const app = getApplication({
|
|
|
51
102
|
});
|
|
52
103
|
```
|
|
53
104
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
`createOpenApiValidatorOptions` forwards every option from `express-openapi-validator`, so you can:
|
|
57
|
-
|
|
105
|
+
Highlights:
|
|
58
106
|
- Enable/disable request or response validation, including per-field tweaks (coercion, unknown query params, removing additional fields, etc.).
|
|
59
107
|
- Register custom security handlers with `validateSecurity.handlers`.
|
|
60
108
|
- Serve the parsed spec via `serveSpec`, configure file upload limits, ignore health-check routes, or validate the spec itself.
|
|
61
109
|
- Reuse `$ref` parsing, custom formats, and file upload middleware exactly as in the upstream validator.
|
|
62
110
|
|
|
63
|
-
|
|
111
|
+
### Logging (optional)
|
|
112
|
+
|
|
113
|
+
Install the optional peer to enable HTTP request/response logging:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
npm install pino-http
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
const app = await getApplication({
|
|
121
|
+
pinoHttp: true, // defaults
|
|
122
|
+
// or:
|
|
123
|
+
pinoHttp: { autoLogging: false },
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
See the full options in the [`pino-http` docs](https://github.com/pinojs/pino-http).
|
|
128
|
+
|
|
129
|
+
### Firebase Auth (optional)
|
|
64
130
|
|
|
65
131
|
If you use Firebase Authentication, install the optional peer and plug it into OpenAPI security handlers:
|
|
66
132
|
|
|
@@ -78,7 +144,7 @@ import {
|
|
|
78
144
|
|
|
79
145
|
admin.initializeApp();
|
|
80
146
|
|
|
81
|
-
const app = getApplication({
|
|
147
|
+
const app = await getApplication({
|
|
82
148
|
apis: [myApi],
|
|
83
149
|
openApiValidator: createOpenApiValidatorOptions('./openapi.yaml', {
|
|
84
150
|
validateSecurity: {
|
|
@@ -90,20 +156,31 @@ const app = getApplication({
|
|
|
90
156
|
|
|
91
157
|
`@my-f-startup/firebase-auth-express` relies on `firebase-admin`. Make sure the Admin SDK is installed, initialized, and configured for your environment (credentials or emulator).
|
|
92
158
|
|
|
93
|
-
|
|
159
|
+
## API Reference
|
|
94
160
|
|
|
95
|
-
|
|
161
|
+
### Application
|
|
162
|
+
- `getApplication(options)` - Creates and configures the Express app.
|
|
163
|
+
- `startAPI(app, options)` - Starts the HTTP server.
|
|
96
164
|
|
|
97
|
-
|
|
165
|
+
### OpenAPI
|
|
166
|
+
- `createOpenApiValidatorOptions(apiSpec, options)` - Helper to assemble OpenAPI validator config.
|
|
167
|
+
- `isOpenApiValidatorAvailable()` - Type guard for optional validator dependency.
|
|
168
|
+
- `createFirebaseAuthSecurityHandlers(options)` - Firebase Auth security handlers.
|
|
169
|
+
- `registerHandlerModule(handlersPath, module)` - Manual registration for operation handlers.
|
|
98
170
|
|
|
99
|
-
|
|
100
|
-
-
|
|
101
|
-
- `examples/basic` – manual routes + validation (minimal scaffolding).
|
|
102
|
-
- `examples/with-security` – standalone security handler demo.
|
|
103
|
-
- `examples/operation-handlers` – barebones `operationHandlers` showcase.
|
|
104
|
-
- `examples/firebase-auth` – Firebase Auth security handlers with operation handlers.
|
|
171
|
+
### HTTP helpers
|
|
172
|
+
- `send`, `sendCreated`, `sendAccepted`, `sendProblem` - Standard HTTP responses.
|
|
105
173
|
|
|
106
|
-
|
|
174
|
+
### ETag helpers
|
|
175
|
+
- `toWeakETag`, `getETagFromIfMatch`, `getETagFromIfNotMatch`, `getETagValueFromIfMatch`, `setETag`.
|
|
176
|
+
|
|
177
|
+
### Testing helpers
|
|
178
|
+
- `ApiSpecification`, `ApiE2ESpecification`
|
|
179
|
+
- `expect`, `expectNewEvents`, `expectResponse`, `expectError`
|
|
180
|
+
|
|
181
|
+
See [`docs/openapi-validation.md`](docs/openapi-validation.md) for the full matrix of options and extended examples.
|
|
182
|
+
|
|
183
|
+
## Testing
|
|
107
184
|
|
|
108
185
|
The package includes comprehensive test coverage:
|
|
109
186
|
|
|
@@ -111,21 +188,96 @@ The package includes comprehensive test coverage:
|
|
|
111
188
|
- **Integration tests** (`test/integration/`) - Basic routing, OpenAPI validation, operation handlers, optimistic concurrency
|
|
112
189
|
- **E2E tests** (`test/e2e/`) - End-to-end workflows for all features
|
|
113
190
|
|
|
114
|
-
|
|
191
|
+
### Running tests
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# Unit tests
|
|
195
|
+
npm run test:unit
|
|
196
|
+
|
|
197
|
+
# Integration tests
|
|
198
|
+
npm run test:int
|
|
199
|
+
|
|
200
|
+
# E2E tests
|
|
201
|
+
npm run test:e2e
|
|
115
202
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
203
|
+
# All tests
|
|
204
|
+
npm test
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Examples
|
|
208
|
+
|
|
209
|
+
Working examples live under `examples/`:
|
|
210
|
+
|
|
211
|
+
- `examples/shopping-cart` – feature-complete Emmett sample (business logic, memory store/publisher, security handlers, OpenAPI file, unit/int/e2e tests, `.http` scripts).
|
|
212
|
+
- Legacy quick starts:
|
|
213
|
+
- `examples/basic` – manual routes + validation (minimal scaffolding).
|
|
214
|
+
- `examples/with-security` – standalone security handler demo.
|
|
215
|
+
- `examples/operation-handlers` – barebones `operationHandlers` showcase.
|
|
216
|
+
- `examples/firebase-auth` – Firebase Auth security handlers with operation handlers.
|
|
119
217
|
|
|
120
218
|
## Documentation
|
|
121
219
|
|
|
122
220
|
- **Guide:** [`docs/openapi-validation.md`](docs/openapi-validation.md) – authoritative reference for configuration, advanced usage, and troubleshooting.
|
|
123
221
|
- **Emmett docs:** <https://event-driven-io.github.io/emmett/>
|
|
124
222
|
|
|
223
|
+
## Compatibility
|
|
224
|
+
|
|
225
|
+
- **Node.js**: tested in CI with 24.x
|
|
226
|
+
- **Emmett**: ^0.39.1
|
|
227
|
+
- **Express**: ^4.19.2
|
|
228
|
+
|
|
125
229
|
## Contributing
|
|
126
230
|
|
|
127
|
-
|
|
231
|
+
Contributions are welcome! Please:
|
|
232
|
+
|
|
233
|
+
1. Fork the repository
|
|
234
|
+
2. Create a feature branch
|
|
235
|
+
3. Add tests for new functionality
|
|
236
|
+
4. Ensure all tests pass
|
|
237
|
+
5. Submit a pull request
|
|
238
|
+
|
|
239
|
+
## Development
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
# Install dependencies
|
|
243
|
+
npm install
|
|
244
|
+
|
|
245
|
+
# Build
|
|
246
|
+
npm run build
|
|
247
|
+
|
|
248
|
+
# Type-check
|
|
249
|
+
npm run build:ts
|
|
250
|
+
|
|
251
|
+
# Run tests
|
|
252
|
+
npm test
|
|
253
|
+
|
|
254
|
+
# Run unit tests only
|
|
255
|
+
npm run test:unit
|
|
256
|
+
|
|
257
|
+
# Run integration tests
|
|
258
|
+
npm run test:int
|
|
259
|
+
|
|
260
|
+
# Run E2E tests
|
|
261
|
+
npm run test:e2e
|
|
262
|
+
```
|
|
128
263
|
|
|
129
264
|
## License
|
|
130
265
|
|
|
131
266
|
MIT
|
|
267
|
+
|
|
268
|
+
## Related Packages
|
|
269
|
+
|
|
270
|
+
- [@event-driven-io/emmett](https://github.com/event-driven-io/emmett) - Core Emmett framework
|
|
271
|
+
- [@emmett-community/emmett-google-firestore](https://github.com/emmett-community/emmett-google-firestore) - Firestore event store
|
|
272
|
+
- [@emmett-community/emmett-google-realtime-db](https://github.com/emmett-community/emmett-google-realtime-db) - Realtime Database inline projections
|
|
273
|
+
- [@emmett-community/emmett-google-pubsub](https://github.com/emmett-community/emmett-google-pubsub) - Pub/Sub message bus
|
|
274
|
+
- [@event-driven-io/emmett-mongodb](https://github.com/event-driven-io/emmett/tree/main/src/packages/emmett-mongodb) - MongoDB event store
|
|
275
|
+
|
|
276
|
+
## Support
|
|
277
|
+
|
|
278
|
+
- [GitHub Issues](https://github.com/emmett-community/emmett-expressjs-with-openapi/issues)
|
|
279
|
+
- [Emmett Documentation](https://event-driven-io.github.io/emmett/)
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
Made with ❤️ by the Emmett Community
|
package/dist/index.cjs
CHANGED
|
@@ -51,10 +51,31 @@ var getApplication = async (options) => {
|
|
|
51
51
|
disableJsonMiddleware,
|
|
52
52
|
disableUrlEncodingMiddleware,
|
|
53
53
|
disableProblemDetailsMiddleware,
|
|
54
|
+
pinoHttp,
|
|
54
55
|
openApiValidator
|
|
55
56
|
} = options;
|
|
56
57
|
const router = _express.Router.call(void 0, );
|
|
57
58
|
app.set("etag", _nullishCoalesce(enableDefaultExpressEtag, () => ( false)));
|
|
59
|
+
if (pinoHttp !== void 0 && pinoHttp !== false) {
|
|
60
|
+
try {
|
|
61
|
+
const require2 = _module.createRequire.call(void 0, _chunkGS7T56RPcjs.importMetaUrl);
|
|
62
|
+
const mod = require2("pino-http");
|
|
63
|
+
const provider = _nullishCoalesce(mod.default, () => ( mod));
|
|
64
|
+
if (typeof provider !== "function") {
|
|
65
|
+
throw new Error("Invalid pino-http module: missing default export");
|
|
66
|
+
}
|
|
67
|
+
const options2 = pinoHttp === true ? void 0 : pinoHttp;
|
|
68
|
+
const middleware = provider(options2);
|
|
69
|
+
app.use(middleware);
|
|
70
|
+
} catch (e) {
|
|
71
|
+
console.warn(
|
|
72
|
+
"Pino HTTP configuration provided but pino-http package is not installed. Install it with: npm install pino-http"
|
|
73
|
+
);
|
|
74
|
+
throw new Error(
|
|
75
|
+
"pino-http package is required when pinoHttp option is used"
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
58
79
|
if (!disableJsonMiddleware) app.use(_express2.default.json());
|
|
59
80
|
if (!disableUrlEncodingMiddleware)
|
|
60
81
|
app.use(
|
|
@@ -117,7 +138,7 @@ var getApplication = async (options) => {
|
|
|
117
138
|
} else {
|
|
118
139
|
app.use(middleware);
|
|
119
140
|
}
|
|
120
|
-
} catch (
|
|
141
|
+
} catch (e2) {
|
|
121
142
|
console.warn(
|
|
122
143
|
"OpenAPI validator configuration provided but express-openapi-validator package is not installed. Install it with: npm install express-openapi-validator"
|
|
123
144
|
);
|
|
@@ -299,7 +320,7 @@ var isOpenApiValidatorAvailable = async () => {
|
|
|
299
320
|
try {
|
|
300
321
|
await Promise.resolve().then(() => _interopRequireWildcard(require("express-openapi-validator")));
|
|
301
322
|
return true;
|
|
302
|
-
} catch (
|
|
323
|
+
} catch (e3) {
|
|
303
324
|
return false;
|
|
304
325
|
}
|
|
305
326
|
};
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/emmett-expressjs-with-openapi/emmett-expressjs-with-openapi/dist/index.cjs","../src/index.ts","../src/application.ts","../src/middlewares/problemDetailsMiddleware.ts","../src/etag.ts","../src/handler.ts","../src/openapi/firebase-auth.ts","../src/openapi/index.ts","../src/responses.ts","../src/testing/apiE2ESpecification.ts","../src/testing/apiSpecification.ts"],"names":["require","ETagErrors"],"mappings":"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","file":"/home/runner/work/emmett-expressjs-with-openapi/emmett-expressjs-with-openapi/dist/index.cjs","sourcesContent":[null,"import 'express-async-errors';\n\nexport * from './application';\nexport * from './etag';\nexport * from './handler';\nexport * from './openapi';\nexport * from './responses';\nexport * from './testing';\nexport { registerHandlerModule } from './internal/esm-resolver';\n","import express, {\n Router,\n type Application,\n type RequestHandler,\n} from 'express';\nimport 'express-async-errors';\nimport http from 'http';\nimport { createRequire } from 'node:module';\nimport { problemDetailsMiddleware } from './middlewares/problemDetailsMiddleware';\nimport type { OpenApiValidatorOptions } from './openapi';\nimport type { ErrorToProblemDetailsMapping } from './responses';\n\n// #region web-api-setup\nexport type WebApiSetup = (router: Router) => void;\n// #endregion web-api-setup\n\nexport type ApplicationOptions = {\n apis?: WebApiSetup[];\n mapError?: ErrorToProblemDetailsMapping;\n enableDefaultExpressEtag?: boolean;\n disableJsonMiddleware?: boolean;\n disableUrlEncodingMiddleware?: boolean;\n disableProblemDetailsMiddleware?: boolean;\n /**\n * Optional OpenAPI validator configuration.\n * When provided, enables request/response validation against an OpenAPI specification.\n * Requires the 'express-openapi-validator' package to be installed.\n *\n * @see https://github.com/cdimascio/express-openapi-validator\n * @example\n * ```typescript\n * import { getApplication, createOpenApiValidatorOptions } from '@event-driven-io/emmett-expressjs';\n *\n * type AppDeps = {\n * eventStore: EventStore;\n * messageBus: EventsPublisher;\n * };\n *\n * const app = await getApplication({\n * openApiValidator: createOpenApiValidatorOptions<AppDeps>('./openapi.yaml', {\n * validateResponses: true,\n * operationHandlers: './handlers',\n * initializeHandlers: (deps) => {\n * initializeHandlers(deps.eventStore, deps.messageBus);\n * }\n * })\n * });\n * ```\n */\n openApiValidator?: OpenApiValidatorOptions;\n};\n\nexport const getApplication = async (options: ApplicationOptions) => {\n const app: Application = express();\n\n const {\n apis,\n mapError,\n enableDefaultExpressEtag,\n disableJsonMiddleware,\n disableUrlEncodingMiddleware,\n disableProblemDetailsMiddleware,\n openApiValidator,\n } = options;\n\n const router = Router();\n\n // disabling default etag behaviour\n // to use etags in if-match and if-not-match headers\n app.set('etag', enableDefaultExpressEtag ?? false);\n\n // add json middleware\n if (!disableJsonMiddleware) app.use(express.json());\n\n // enable url encoded urls and bodies\n if (!disableUrlEncodingMiddleware)\n app.use(\n express.urlencoded({\n extended: true,\n }),\n );\n\n // add OpenAPI validator middleware if configured\n if (openApiValidator) {\n // Activate ESM resolver if operationHandlers are configured\n // This ensures handler modules are loaded via ESM import() instead of CJS require(),\n // preventing dual module loading issues when using TypeScript runtimes (tsx, ts-node)\n if (openApiValidator.operationHandlers) {\n const { activateESMResolver } = await import(\n './internal/esm-resolver.js'\n );\n activateESMResolver();\n\n // NEW: Auto-discover and import handler modules from OpenAPI spec\n const handlersBasePath =\n typeof openApiValidator.operationHandlers === 'string'\n ? openApiValidator.operationHandlers\n : openApiValidator.operationHandlers.basePath;\n\n if (handlersBasePath) {\n const { extractHandlerModules } = await import(\n './internal/openapi-parser.js'\n );\n const { importAndRegisterHandlers } = await import(\n './internal/handler-importer.js'\n );\n\n try {\n // Parse OpenAPI spec to find handler modules\n const modules = await extractHandlerModules(\n openApiValidator.apiSpec,\n handlersBasePath,\n );\n\n // Dynamically import and register all handler modules\n const importedHandlers = await importAndRegisterHandlers(modules);\n\n // Call user's initializeHandlers callback with imported modules\n if (openApiValidator.initializeHandlers) {\n await openApiValidator.initializeHandlers(importedHandlers);\n }\n } catch (error) {\n console.error('Failed to auto-import handler modules:', error);\n throw error;\n }\n }\n } else {\n // No operationHandlers, just call initializeHandlers if provided\n if (openApiValidator.initializeHandlers) {\n await openApiValidator.initializeHandlers();\n }\n }\n\n try {\n const require = createRequire(import.meta.url);\n // express-openapi-validator exports a default with .middleware (ESM/CJS compatibility)\n const mod = require('express-openapi-validator') as Record<\n string,\n unknown\n >;\n const provider = (mod.default ?? mod) as Record<string, unknown>;\n\n if (typeof provider.middleware !== 'function') {\n throw new Error(\n 'Invalid express-openapi-validator module: missing middleware export',\n );\n }\n\n // Serve OpenAPI spec if configured\n if (openApiValidator.serveSpec) {\n if (typeof openApiValidator.apiSpec === 'string') {\n // If apiSpec is a file path, serve it as a static file\n app.use(\n openApiValidator.serveSpec,\n express.static(openApiValidator.apiSpec),\n );\n } else {\n // If apiSpec is an object, serve it as JSON\n app.get(openApiValidator.serveSpec, (_req, res) => {\n res.json(openApiValidator.apiSpec);\n });\n }\n }\n\n const factory = provider.middleware as (\n opts: OpenApiValidatorOptions,\n ) => RequestHandler | RequestHandler[];\n const middleware = factory(openApiValidator);\n if (Array.isArray(middleware)) {\n for (const m of middleware) app.use(m);\n } else {\n app.use(middleware);\n }\n } catch {\n console.warn(\n 'OpenAPI validator configuration provided but express-openapi-validator package is not installed. ' +\n 'Install it with: npm install express-openapi-validator',\n );\n throw new Error(\n 'express-openapi-validator package is required when openApiValidator option is used',\n );\n }\n }\n\n // Register API routes if provided\n if (apis) {\n for (const api of apis) {\n api(router);\n }\n app.use(router);\n }\n\n // add problem details middleware\n if (!disableProblemDetailsMiddleware)\n app.use(problemDetailsMiddleware(mapError));\n\n return app;\n};\n\nexport type StartApiOptions = {\n port?: number;\n};\n\nexport const startAPI = (\n app: Application,\n options: StartApiOptions = { port: 3000 },\n) => {\n const { port } = options;\n const server = http.createServer(app);\n\n server.on('listening', () => {\n console.info('server up listening');\n });\n\n return server.listen(port);\n};\n","import type { NextFunction, Request, Response } from 'express';\nimport { ProblemDocument } from 'http-problem-details';\nimport { sendProblem, type ErrorToProblemDetailsMapping } from '..';\n\nexport const problemDetailsMiddleware =\n (mapError?: ErrorToProblemDetailsMapping) =>\n (\n error: Error,\n request: Request,\n response: Response,\n _next: NextFunction,\n ): void => {\n let problemDetails: ProblemDocument | undefined;\n\n if (mapError) problemDetails = mapError(error, request);\n\n problemDetails =\n problemDetails ?? defaultErrorToProblemDetailsMapping(error);\n\n sendProblem(response, problemDetails.status, { problem: problemDetails });\n };\n\nexport const defaultErrorToProblemDetailsMapping = (\n error: Error,\n): ProblemDocument => {\n let statusCode = 500;\n\n // Prefer standard `status` code if present (e.g., express-openapi-validator)\n const errObj = error as unknown as Record<string, unknown>;\n const maybeStatus = errObj['status'];\n if (\n typeof maybeStatus === 'number' &&\n maybeStatus >= 100 &&\n maybeStatus < 600\n ) {\n statusCode = maybeStatus;\n }\n\n const maybeErrorCode = errObj['errorCode'];\n if (\n typeof maybeErrorCode === 'number' &&\n maybeErrorCode >= 100 &&\n maybeErrorCode < 600\n ) {\n statusCode = maybeErrorCode;\n }\n\n return new ProblemDocument({\n detail: error.message,\n status: statusCode,\n });\n};\n","import { type Brand } from '@event-driven-io/emmett';\nimport type { Request, Response } from 'express';\n\n//////////////////////////////////////\n/// ETAG\n//////////////////////////////////////\n\nexport const HeaderNames = {\n IF_MATCH: 'if-match',\n IF_NOT_MATCH: 'if-not-match',\n ETag: 'etag',\n};\n\nexport type WeakETag = Brand<`W/${string}`, 'ETag'>;\nexport type ETag = Brand<string, 'ETag'>;\n\nexport const WeakETagRegex = /W\\/\"(-?\\d+.*)\"/;\n\nexport const enum ETagErrors {\n WRONG_WEAK_ETAG_FORMAT = 'WRONG_WEAK_ETAG_FORMAT',\n MISSING_IF_MATCH_HEADER = 'MISSING_IF_MATCH_HEADER',\n MISSING_IF_NOT_MATCH_HEADER = 'MISSING_IF_NOT_MATCH_HEADER',\n}\n\nexport const isWeakETag = (etag: ETag): etag is WeakETag => {\n return WeakETagRegex.test(etag as string);\n};\n\nexport const getWeakETagValue = (etag: ETag): string => {\n const result = WeakETagRegex.exec(etag as string);\n if (result === null || result.length === 0) {\n throw new Error(ETagErrors.WRONG_WEAK_ETAG_FORMAT);\n }\n return result[1]!;\n};\n\nexport const toWeakETag = (value: number | bigint | string): WeakETag => {\n return `W/\"${value}\"` as WeakETag;\n};\n\nexport const getETagFromIfMatch = (request: Request): ETag => {\n const etag = request.headers[HeaderNames.IF_MATCH];\n\n if (etag === undefined) {\n throw new Error(ETagErrors.MISSING_IF_MATCH_HEADER);\n }\n\n return etag as ETag;\n};\n\nexport const getETagFromIfNotMatch = (request: Request): ETag => {\n const etag = request.headers[HeaderNames.IF_NOT_MATCH];\n\n if (etag === undefined) {\n throw new Error(ETagErrors.MISSING_IF_MATCH_HEADER);\n }\n\n return (Array.isArray(etag) ? etag[0] : etag) as ETag;\n};\n\nexport const setETag = (response: Response, etag: ETag): void => {\n response.setHeader(HeaderNames.ETag, etag as string);\n};\n\nexport const getETagValueFromIfMatch = (request: Request): string => {\n const eTagValue: ETag = getETagFromIfMatch(request);\n\n return isWeakETag(eTagValue)\n ? getWeakETagValue(eTagValue)\n : (eTagValue as string);\n};\n","import { type NextFunction, type Request, type Response } from 'express';\nimport {\n send,\n sendAccepted,\n sendCreated,\n sendProblem,\n type AcceptedHttpResponseOptions,\n type CreatedHttpResponseOptions,\n type HttpProblemResponseOptions,\n type HttpResponseOptions,\n type NoContentHttpResponseOptions,\n} from '.';\n\n// #region httpresponse-on\nexport type HttpResponse = (response: Response) => void;\n\nexport type HttpHandler<RequestType extends Request> = (\n request: RequestType,\n) => Promise<HttpResponse> | HttpResponse;\n\nexport const on =\n <RequestType extends Request>(handle: HttpHandler<RequestType>) =>\n async (\n request: RequestType,\n response: Response,\n _next: NextFunction,\n ): Promise<void> => {\n const setResponse = await Promise.resolve(handle(request));\n\n return setResponse(response);\n };\n// #endregion httpresponse-on\n\nexport const OK =\n (options?: HttpResponseOptions): HttpResponse =>\n (response: Response) => {\n send(response, 200, options);\n };\n\nexport const Created =\n (options: CreatedHttpResponseOptions): HttpResponse =>\n (response: Response) => {\n sendCreated(response, options);\n };\n\nexport const Accepted =\n (options: AcceptedHttpResponseOptions): HttpResponse =>\n (response: Response) => {\n sendAccepted(response, options);\n };\n\nexport const NoContent = (\n options?: NoContentHttpResponseOptions,\n): HttpResponse => HttpResponse(204, options);\n\nexport const HttpResponse =\n (statusCode: number, options?: HttpResponseOptions): HttpResponse =>\n (response: Response) => {\n send(response, statusCode, options);\n };\n\n/////////////////////\n// ERRORS\n/////////////////////\n\nexport const BadRequest = (\n options?: HttpProblemResponseOptions,\n): HttpResponse => HttpProblem(400, options);\n\nexport const Forbidden = (options?: HttpProblemResponseOptions): HttpResponse =>\n HttpProblem(403, options);\n\nexport const NotFound = (options?: HttpProblemResponseOptions): HttpResponse =>\n HttpProblem(404, options);\n\nexport const Conflict = (options?: HttpProblemResponseOptions): HttpResponse =>\n HttpProblem(409, options);\n\nexport const PreconditionFailed = (\n options: HttpProblemResponseOptions,\n): HttpResponse => HttpProblem(412, options);\n\nexport const HttpProblem =\n (statusCode: number, options?: HttpProblemResponseOptions): HttpResponse =>\n (response: Response) => {\n sendProblem(response, statusCode, options);\n };\n","import type { SecurityHandlers } from './index';\n\ntype AuthClient = {\n verifyIdToken: (token: string) => Promise<unknown>;\n};\n\nexport type FirebaseAuthSecurityOptions = {\n /**\n * Name of the OpenAPI security scheme to attach the handler to.\n * Defaults to \"bearerAuth\".\n */\n securitySchemeName?: string;\n /**\n * Custom auth client for tests or alternate Firebase auth instances.\n */\n authClient?: AuthClient;\n /**\n * Token claim used for role-based checks when scopes are defined.\n * Defaults to \"roles\".\n */\n roleClaim?: string;\n};\n\ntype FirebaseAuthModule = {\n firebaseAuthMiddleware: (options?: { authClient?: AuthClient }) => (\n req: unknown,\n res: unknown,\n next: () => void,\n ) => Promise<void> | void;\n};\n\nconst loadFirebaseAuth = async (): Promise<FirebaseAuthModule> => {\n try {\n const mod = await import('@my-f-startup/firebase-auth-express');\n const provider = (mod as unknown as Record<string, unknown>).default ?? mod;\n const firebaseAuthMiddleware =\n (provider as Record<string, unknown>).firebaseAuthMiddleware;\n\n if (typeof firebaseAuthMiddleware !== 'function') {\n throw new Error(\n 'Invalid @my-f-startup/firebase-auth-express module: missing firebaseAuthMiddleware export',\n );\n }\n\n return provider as FirebaseAuthModule;\n } catch (error) {\n const message =\n '@my-f-startup/firebase-auth-express is required for createFirebaseAuthSecurityHandlers. ' +\n 'Install it with: npm install @my-f-startup/firebase-auth-express';\n throw new Error(message, { cause: error as Error });\n }\n};\n\nconst createNullResponse = () => {\n const res: Record<string, unknown> = {};\n res.status = () => res;\n res.json = () => res;\n res.send = () => res;\n res.end = () => res;\n res.set = () => res;\n return res;\n};\n\nconst runMiddleware = async (\n middleware: (req: unknown, res: unknown, next: () => void) => Promise<void> | void,\n req: unknown,\n): Promise<boolean> => {\n return new Promise((resolve) => {\n let nextCalled = false;\n const res = createNullResponse();\n const next = () => {\n nextCalled = true;\n resolve(true);\n };\n\n Promise.resolve(middleware(req, res, next))\n .then(() => {\n if (!nextCalled) resolve(false);\n })\n .catch(() => resolve(false));\n });\n};\n\nexport const createFirebaseAuthSecurityHandlers = (\n options: FirebaseAuthSecurityOptions = {},\n): SecurityHandlers => {\n const securitySchemeName = options.securitySchemeName ?? 'bearerAuth';\n const roleClaim = options.roleClaim ?? 'roles';\n\n return {\n [securitySchemeName]: async (req, scopes, _schema) => {\n const { firebaseAuthMiddleware } = await loadFirebaseAuth();\n const middleware = firebaseAuthMiddleware({\n authClient: options.authClient,\n });\n\n const isAuthenticated = await runMiddleware(middleware, req);\n if (!isAuthenticated) return false;\n\n if (!scopes.length) return true;\n\n const roles = (req as Record<string, any>)?.auth?.token?.[roleClaim];\n if (!Array.isArray(roles)) return false;\n\n return scopes.every((scope: string) => roles.includes(scope));\n },\n };\n};\n","/**\n * OpenAPI v3 Document type (to avoid requiring express-openapi-validator types directly)\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type OpenAPIV3Document = any;\n\n/**\n * Imported handler modules, keyed by module name.\n * Automatically populated by the framework when operationHandlers is configured.\n */\nexport type ImportedHandlerModules = Record<string, any>;\n\n/**\n * Security handlers for custom authentication/authorization logic.\n * Maps security scheme names to handler functions.\n *\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/usage-validate-security/\n */\n\nexport type SecurityHandlers = Record<\n string,\n (req: any, scopes: string[], schema: any) => boolean | Promise<boolean>\n>;\n\nexport * from './firebase-auth';\n\n/**\n * Configuration options for express-openapi-validator middleware.\n * This allows optional validation of API requests and responses against an OpenAPI specification.\n *\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/\n */\nexport type OpenApiValidatorOptions = {\n /**\n * Path to the OpenAPI specification file (JSON or YAML)\n * or an OpenAPI specification object.\n */\n apiSpec: string | OpenAPIV3Document;\n\n /**\n * Determines whether the validator should validate requests.\n * Can be a boolean or an object with detailed request validation options.\n * @default true\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/usage-validate-requests/\n */\n validateRequests?:\n | boolean\n | {\n /**\n * Allow unknown query parameters (not defined in the spec).\n * @default false\n */\n allowUnknownQueryParameters?: boolean;\n /**\n * Coerce types in request parameters.\n * @default true\n */\n coerceTypes?: boolean | 'array';\n /**\n * Remove additional properties not defined in the spec.\n * @default false\n */\n removeAdditional?: boolean | 'all' | 'failing';\n };\n\n /**\n * Determines whether the validator should validate responses.\n * Can be a boolean or an object with detailed response validation options.\n * @default false\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/usage-validate-responses/\n */\n validateResponses?:\n | boolean\n | {\n /**\n * Remove additional properties from responses not defined in the spec.\n * @default false\n */\n removeAdditional?: boolean | 'all' | 'failing';\n /**\n * Coerce types in responses.\n * @default true\n */\n coerceTypes?: boolean;\n /**\n * Callback to handle response validation errors.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onError?: (error: any, body: any, req: any) => void;\n };\n\n /**\n * Determines whether the validator should validate security.\n * Can be a boolean or an object with security handlers.\n * @default true\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/usage-validate-security/\n */\n validateSecurity?:\n | boolean\n | {\n /**\n * Custom security handlers for authentication/authorization.\n */\n handlers?: SecurityHandlers;\n };\n\n /**\n * Defines how the validator should validate formats.\n * When true, uses ajv-formats for format validation.\n * When false, format validation is disabled.\n * Can also be 'fast' or 'full' for different validation modes.\n * @default true\n */\n validateFormats?: boolean | 'fast' | 'full';\n\n /**\n * The base path to the operation handlers directory.\n * When set to a path, automatically wires OpenAPI operations to handler functions\n * based on operationId or x-eov-operation-id.\n * When false, operation handlers are disabled (manual routing required).\n * @default false\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/guide-operation-handlers/\n */\n operationHandlers?:\n | string\n | false\n | {\n /**\n * Base path to operation handlers directory.\n */\n basePath?: string;\n /**\n * Resolver function to map operationId to handler module path.\n */\n resolver?: (\n handlersPath: string,\n route: string,\n apiDoc: OpenAPIV3Document,\n ) => string;\n };\n\n /**\n * Paths or pattern to ignore during validation.\n * @default undefined\n */\n ignorePaths?: RegExp | ((path: string) => boolean);\n\n /**\n * Validate the OpenAPI specification itself.\n * @default true\n */\n validateApiSpec?: boolean;\n\n /**\n * $ref parser configuration for handling OpenAPI references.\n * @default undefined\n */\n $refParser?: {\n mode: 'bundle' | 'dereference';\n };\n\n /**\n * Serve the OpenAPI specification at a specific path.\n * When set to a string, the spec will be served at that path.\n * When false, the spec will not be served.\n * @default false\n * @example '/api-docs/openapi.json'\n */\n serveSpec?: string | false;\n\n /**\n * File upload configuration options.\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/usage-file-uploads/\n */\n fileUploader?:\n | boolean\n | {\n /**\n * Destination directory for uploaded files.\n */\n dest?: string;\n /**\n * File size limit in bytes.\n */\n limits?: {\n fileSize?: number;\n files?: number;\n };\n };\n\n /**\n * Optional callback to initialize operation handlers with dependencies.\n * Called before the OpenAPI validator middleware is configured.\n *\n * The framework automatically imports handler modules referenced in your\n * OpenAPI spec (via x-eov-operation-handler) and passes them as the first parameter.\n *\n * @param handlers - Auto-imported handler modules, keyed by module name\n * @returns void or a Promise that resolves when initialization is complete\n *\n * @example\n * ```typescript\n * // With automatic import (recommended)\n * initializeHandlers: async (handlers) => {\n * handlers.shoppingCarts.initializeHandlers(eventStore, messageBus, getUnitPrice, getCurrentTime);\n * }\n *\n * // Manual import (still supported for backward compatibility)\n * import * as handlersModule from './handlers/shoppingCarts';\n * import { registerHandlerModule } from '@emmett-community/emmett-expressjs-with-openapi';\n * initializeHandlers: () => {\n * const handlersPath = path.join(__dirname, './handlers/shoppingCarts');\n * registerHandlerModule(handlersPath, handlersModule);\n * handlersModule.initializeHandlers(eventStore, messageBus, getUnitPrice, getCurrentTime);\n * }\n * ```\n */\n initializeHandlers?: (\n handlers?: ImportedHandlerModules,\n ) => void | Promise<void>;\n};\n\n/**\n * Helper function to create OpenAPI validator configuration with sensible defaults.\n *\n * @param apiSpec - Path to OpenAPI spec file or OpenAPI document object\n * @param options - Additional validator options\n * @returns Complete OpenApiValidatorOptions configuration\n *\n * @example\n * ```typescript\n * // Basic usage with default options\n * const validatorOptions = createOpenApiValidatorOptions('./openapi.yaml');\n *\n * // With response validation enabled\n * const validatorOptions = createOpenApiValidatorOptions('./openapi.yaml', {\n * validateResponses: true\n * });\n *\n * // With custom security handlers\n * const validatorOptions = createOpenApiValidatorOptions('./openapi.yaml', {\n * validateSecurity: {\n * handlers: {\n * bearerAuth: async (req, scopes) => {\n * // Custom authentication logic\n * return true;\n * }\n * }\n * }\n * });\n *\n * // Serving the spec at /api-docs\n * const validatorOptions = createOpenApiValidatorOptions('./openapi.yaml', {\n * serveSpec: '/api-docs/openapi.json'\n * });\n *\n * // With dependency injection for operation handlers\n * type ShoppingCartDeps = {\n * eventStore: EventStore;\n * messageBus: EventsPublisher;\n * getUnitPrice: (productId: string) => Promise<number>;\n * getCurrentTime: () => Date;\n * };\n *\n * const validatorOptions = createOpenApiValidatorOptions<ShoppingCartDeps>(\n * './openapi.yaml',\n * {\n * operationHandlers: './handlers',\n * initializeHandlers: (deps) => {\n * initializeHandlers(\n * deps.eventStore,\n * deps.messageBus,\n * deps.getUnitPrice,\n * deps.getCurrentTime\n * );\n * }\n * }\n * );\n *\n * const app = getApplication({\n * apis: [myApi],\n * openApiValidator: validatorOptions\n * });\n * ```\n */\nexport const createOpenApiValidatorOptions = (\n apiSpec: string | OpenAPIV3Document,\n options?: Partial<Omit<OpenApiValidatorOptions, 'apiSpec'>>,\n): OpenApiValidatorOptions => {\n return {\n apiSpec,\n validateRequests: options?.validateRequests ?? true,\n validateResponses: options?.validateResponses ?? false,\n validateSecurity: options?.validateSecurity ?? true,\n validateFormats: options?.validateFormats ?? true,\n operationHandlers: options?.operationHandlers,\n ignorePaths: options?.ignorePaths,\n validateApiSpec: options?.validateApiSpec ?? true,\n $refParser: options?.$refParser,\n serveSpec: options?.serveSpec ?? false,\n fileUploader: options?.fileUploader,\n initializeHandlers: options?.initializeHandlers,\n };\n};\n\n/**\n * Type guard to check if express-openapi-validator is available\n */\nexport const isOpenApiValidatorAvailable = async (): Promise<boolean> => {\n try {\n await import('express-openapi-validator');\n return true;\n } catch {\n return false;\n }\n};\n","import { type Request, type Response } from 'express';\nimport { ProblemDocument } from 'http-problem-details';\nimport { setETag, type ETag } from './etag';\n\nexport type ErrorToProblemDetailsMapping = (\n error: Error,\n request: Request,\n) => ProblemDocument | undefined;\n\nexport type HttpResponseOptions = {\n body?: unknown;\n location?: string;\n eTag?: ETag;\n};\nexport const DefaultHttpResponseOptions: HttpResponseOptions = {};\n\nexport type HttpProblemResponseOptions = {\n location?: string;\n eTag?: ETag;\n} & Omit<HttpResponseOptions, 'body'> &\n (\n | {\n problem: ProblemDocument;\n }\n | { problemDetails: string }\n );\nexport const DefaultHttpProblemResponseOptions: HttpProblemResponseOptions = {\n problemDetails: 'Error occured!',\n};\n\nexport type CreatedHttpResponseOptions = (\n | {\n createdId: string;\n }\n | {\n createdId?: string;\n url: string;\n }\n) &\n HttpResponseOptions;\n\nexport const sendCreated = (\n response: Response,\n { eTag, ...options }: CreatedHttpResponseOptions,\n): void =>\n send(response, 201, {\n location:\n 'url' in options\n ? options.url\n : `${response.req.url}/${options.createdId}`,\n body: 'createdId' in options ? { id: options.createdId } : undefined,\n eTag,\n });\n\nexport type AcceptedHttpResponseOptions = {\n location: string;\n} & HttpResponseOptions;\n\nexport const sendAccepted = (\n response: Response,\n options: AcceptedHttpResponseOptions,\n): void => send(response, 202, options);\n\nexport type NoContentHttpResponseOptions = Omit<HttpResponseOptions, 'body'>;\n\nexport const send = (\n response: Response,\n statusCode: number,\n options?: HttpResponseOptions,\n): void => {\n const { location, body, eTag } = options ?? DefaultHttpResponseOptions;\n // HEADERS\n if (eTag) setETag(response, eTag);\n if (location) response.setHeader('Location', location);\n\n if (body) {\n response.statusCode = statusCode;\n response.send(body);\n } else {\n response.sendStatus(statusCode);\n }\n};\n\nexport const sendProblem = (\n response: Response,\n statusCode: number,\n options?: HttpProblemResponseOptions,\n): void => {\n options = options ?? DefaultHttpProblemResponseOptions;\n\n const { location, eTag } = options;\n\n const problemDetails =\n 'problem' in options\n ? options.problem\n : new ProblemDocument({\n detail: options.problemDetails,\n status: statusCode,\n });\n\n // HEADERS\n if (eTag) setETag(response, eTag);\n if (location) response.setHeader('Location', location);\n\n response.setHeader('Content-Type', 'application/problem+json');\n\n response.statusCode = statusCode;\n response.json(problemDetails);\n};\n","import supertest, { type Response } from 'supertest';\n\nimport type { EventStore } from '@event-driven-io/emmett';\nimport assert from 'assert';\nimport type { Application } from 'express';\nimport type { TestRequest } from './apiSpecification';\n\nexport type E2EResponseAssert = (response: Response) => boolean | void;\n\nexport type ApiE2ESpecificationAssert = [E2EResponseAssert];\n\nexport type ApiE2ESpecification = (...givenRequests: TestRequest[]) => {\n when: (setupRequest: TestRequest) => {\n then: (verify: ApiE2ESpecificationAssert) => Promise<void>;\n };\n};\n\nexport const ApiE2ESpecification = {\n for: <Store extends EventStore = EventStore>(\n getEventStore: () => Store,\n getApplication: (eventStore: Store) => Application | Promise<Application>,\n ): ApiE2ESpecification => {\n {\n return (...givenRequests: TestRequest[]) => {\n const eventStore = getEventStore();\n\n return {\n when: (setupRequest: TestRequest) => {\n const handle = async () => {\n const application = await Promise.resolve(getApplication(eventStore));\n\n for (const requestFn of givenRequests) {\n await requestFn(supertest(application));\n }\n\n return setupRequest(supertest(application));\n };\n\n return {\n then: async (\n verify: ApiE2ESpecificationAssert,\n ): Promise<void> => {\n const response = await handle();\n\n verify.forEach((assertion) => {\n const succeeded = assertion(response);\n\n if (succeeded === false) assert.fail();\n });\n },\n };\n },\n };\n };\n }\n },\n};\n","import {\n WrapEventStore,\n assertEqual,\n assertFails,\n assertMatches,\n type Event,\n type EventStore,\n type TestEventStream,\n} from '@event-driven-io/emmett';\nimport { type Application } from 'express';\nimport type { ProblemDocument } from 'http-problem-details';\nimport type { Response, Test } from 'supertest';\nimport supertest from 'supertest';\nimport type TestAgent from 'supertest/lib/agent';\n\n////////////////////////////////\n/////////// Setup\n////////////////////////////////\n\nexport type TestRequest = (request: TestAgent<supertest.Test>) => Test;\n\nexport const existingStream = <EventType extends Event = Event>(\n streamId: string,\n events: EventType[],\n): TestEventStream<EventType> => {\n return [streamId, events];\n};\n\n////////////////////////////////\n/////////// Asserts\n////////////////////////////////\n\nexport type ResponseAssert = (response: Response) => boolean | void;\n\nexport type ApiSpecificationAssert<EventType extends Event = Event> =\n | TestEventStream<EventType>[]\n | ResponseAssert\n | [ResponseAssert, ...TestEventStream<EventType>[]];\n\nexport const expect = <EventType extends Event = Event>(\n streamId: string,\n events: EventType[],\n): TestEventStream<EventType> => {\n return [streamId, events];\n};\n\nexport const expectNewEvents = <EventType extends Event = Event>(\n streamId: string,\n events: EventType[],\n): TestEventStream<EventType> => {\n return [streamId, events];\n};\n\nexport const expectResponse =\n <Body = unknown>(\n statusCode: number,\n options?: { body?: Body; headers?: { [index: string]: string } },\n ) =>\n (response: Response): void => {\n const { body, headers } = options ?? {};\n assertEqual(statusCode, response.statusCode, \"Response code doesn't match\");\n if (body) assertMatches(response.body, body);\n if (headers) assertMatches(response.headers, headers);\n };\n\nexport const expectError = (\n errorCode: number,\n problemDetails?: Partial<ProblemDocument>,\n) =>\n expectResponse(\n errorCode,\n problemDetails ? { body: problemDetails } : undefined,\n );\n\n////////////////////////////////\n/////////// Api Specification\n////////////////////////////////\n\nexport type ApiSpecification<EventType extends Event = Event> = (\n ...givenStreams: TestEventStream<EventType>[]\n) => {\n when: (setupRequest: TestRequest) => {\n then: (verify: ApiSpecificationAssert<EventType>) => Promise<void>;\n };\n};\n\nexport const ApiSpecification = {\n for: <\n EventType extends Event = Event,\n Store extends EventStore<import('@event-driven-io/emmett').ReadEventMetadataWithGlobalPosition> = EventStore<import('@event-driven-io/emmett').ReadEventMetadataWithGlobalPosition>\n >(\n getEventStore: () => Store,\n getApplication: (eventStore: Store) => Application | Promise<Application>,\n ): ApiSpecification<EventType> => {\n {\n return (...givenStreams: TestEventStream<EventType>[]) => {\n const eventStore = WrapEventStore(getEventStore());\n\n return {\n when: (setupRequest: TestRequest) => {\n const handle = async () => {\n const application = await Promise.resolve(getApplication(eventStore));\n\n for (const [streamName, events] of givenStreams) {\n await eventStore.setup(streamName, events);\n }\n\n return setupRequest(supertest(application));\n };\n\n return {\n then: async (\n verify: ApiSpecificationAssert<EventType>,\n ): Promise<void> => {\n const response = await handle();\n\n if (typeof verify === 'function') {\n const succeeded = verify(response);\n\n if (succeeded === false) assertFails();\n } else if (Array.isArray(verify)) {\n const [first, ...rest] = verify;\n\n if (typeof first === 'function') {\n const succeeded = first(response);\n\n if (succeeded === false) assertFails();\n }\n\n const events = typeof first === 'function' ? rest : verify;\n\n assertMatches(\n Array.from(eventStore.appendedEvents.values()),\n events,\n );\n }\n },\n };\n },\n };\n };\n }\n },\n};\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/emmett-expressjs-with-openapi/emmett-expressjs-with-openapi/dist/index.cjs","../src/index.ts","../src/application.ts","../src/middlewares/problemDetailsMiddleware.ts","../src/etag.ts","../src/handler.ts","../src/openapi/firebase-auth.ts","../src/openapi/index.ts","../src/responses.ts","../src/testing/apiE2ESpecification.ts","../src/testing/apiSpecification.ts"],"names":["require","options","ETagErrors"],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B;AACE;AACF,wDAA6B;AAC7B;AACA;ACPA,gCAAO;ADSP;AACA;AEVA;AACE;AAAA,oFAGK;AACP;AACA,wEAAiB;AACjB,gCAA8B;AFU9B;AACA;AGjBA,0DAAgC;AAGzB,IAAM,yBAAA,EACX,CAAC,QAAA,EAAA,GACD,CACE,KAAA,EACA,OAAA,EACA,QAAA,EACA,KAAA,EAAA,GACS;AACT,EAAA,IAAI,cAAA;AAEJ,EAAA,GAAA,CAAI,QAAA,EAAU,eAAA,EAAiB,QAAA,CAAS,KAAA,EAAO,OAAO,CAAA;AAEtD,EAAA,eAAA,mBACE,cAAA,UAAkB,mCAAA,CAAoC,KAAK,GAAA;AAE7D,EAAA,WAAA,CAAY,QAAA,EAAU,cAAA,CAAe,MAAA,EAAQ,EAAE,OAAA,EAAS,eAAe,CAAC,CAAA;AAC1E,CAAA;AAEK,IAAM,oCAAA,EAAsC,CACjD,KAAA,EAAA,GACoB;AACpB,EAAA,IAAI,WAAA,EAAa,GAAA;AAGjB,EAAA,MAAM,OAAA,EAAS,KAAA;AACf,EAAA,MAAM,YAAA,EAAc,MAAA,CAAO,QAAQ,CAAA;AACnC,EAAA,GAAA,CACE,OAAO,YAAA,IAAgB,SAAA,GACvB,YAAA,GAAe,IAAA,GACf,YAAA,EAAc,GAAA,EACd;AACA,IAAA,WAAA,EAAa,WAAA;AAAA,EACf;AAEA,EAAA,MAAM,eAAA,EAAiB,MAAA,CAAO,WAAW,CAAA;AACzC,EAAA,GAAA,CACE,OAAO,eAAA,IAAmB,SAAA,GAC1B,eAAA,GAAkB,IAAA,GAClB,eAAA,EAAiB,GAAA,EACjB;AACA,IAAA,WAAA,EAAa,cAAA;AAAA,EACf;AAEA,EAAA,OAAO,IAAI,wCAAA,CAAgB;AAAA,IACzB,MAAA,EAAQ,KAAA,CAAM,OAAA;AAAA,IACd,MAAA,EAAQ;AAAA,EACV,CAAC,CAAA;AACH,CAAA;AHTA;AACA;AEmCO,IAAM,eAAA,EAAiB,MAAA,CAAO,OAAA,EAAA,GAAgC;AACnE,EAAA,MAAM,IAAA,EAAmB,+BAAA,CAAQ;AAEjC,EAAA,MAAM;AAAA,IACJ,IAAA;AAAA,IACA,QAAA;AAAA,IACA,wBAAA;AAAA,IACA,qBAAA;AAAA,IACA,4BAAA;AAAA,IACA,+BAAA;AAAA,IACA,QAAA;AAAA,IACA;AAAA,EACF,EAAA,EAAI,OAAA;AAEJ,EAAA,MAAM,OAAA,EAAS,6BAAA,CAAO;AAItB,EAAA,GAAA,CAAI,GAAA,CAAI,MAAA,mBAAQ,wBAAA,UAA4B,OAAK,CAAA;AAGjD,EAAA,GAAA,CAAI,SAAA,IAAa,KAAA,EAAA,GAAa,SAAA,IAAa,KAAA,EAAO;AAChD,IAAA,IAAI;AACF,MAAA,MAAMA,SAAAA,EAAU,mCAAA,+BAA6B,CAAA;AAC7C,MAAA,MAAM,IAAA,EAAMA,QAAAA,CAAQ,WAAW,CAAA;AAC/B,MAAA,MAAM,SAAA,mBAAY,GAAA,CAAI,OAAA,UAAW,KAAA;AAEjC,MAAA,GAAA,CAAI,OAAO,SAAA,IAAa,UAAA,EAAY;AAClC,QAAA,MAAM,IAAI,KAAA,CAAM,kDAAkD,CAAA;AAAA,MACpE;AAEA,MAAA,MAAMC,SAAAA,EAAU,SAAA,IAAa,KAAA,EAAO,KAAA,EAAA,EAAY,QAAA;AAChD,MAAA,MAAM,WAAA,EACJ,QAAA,CACAA,QAAO,CAAA;AACT,MAAA,GAAA,CAAI,GAAA,CAAI,UAAU,CAAA;AAAA,IACpB,EAAA,UAAQ;AACN,MAAA,OAAA,CAAQ,IAAA;AAAA,QACN;AAAA,MAEF,CAAA;AACA,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,MACF,CAAA;AAAA,IACF;AAAA,EACF;AAGA,EAAA,GAAA,CAAI,CAAC,qBAAA,EAAuB,GAAA,CAAI,GAAA,CAAI,iBAAA,CAAQ,IAAA,CAAK,CAAC,CAAA;AAGlD,EAAA,GAAA,CAAI,CAAC,4BAAA;AACH,IAAA,GAAA,CAAI,GAAA;AAAA,MACF,iBAAA,CAAQ,UAAA,CAAW;AAAA,QACjB,QAAA,EAAU;AAAA,MACZ,CAAC;AAAA,IACH,CAAA;AAGF,EAAA,GAAA,CAAI,gBAAA,EAAkB;AAIpB,IAAA,GAAA,CAAI,gBAAA,CAAiB,iBAAA,EAAmB;AACtC,MAAA,MAAM,EAAE,oBAAoB,EAAA,EAAI,MAAM,4DAAA,CACpC,6BACF,GAAA;AACA,MAAA,mBAAA,CAAoB,CAAA;AAGpB,MAAA,MAAM,iBAAA,EACJ,OAAO,gBAAA,CAAiB,kBAAA,IAAsB,SAAA,EAC1C,gBAAA,CAAiB,kBAAA,EACjB,gBAAA,CAAiB,iBAAA,CAAkB,QAAA;AAEzC,MAAA,GAAA,CAAI,gBAAA,EAAkB;AACpB,QAAA,MAAM,EAAE,sBAAsB,EAAA,EAAI,MAAM,4DAAA,CACtC,+BACF,GAAA;AACA,QAAA,MAAM,EAAE,0BAA0B,EAAA,EAAI,MAAM,4DAAA,CAC1C,iCACF,GAAA;AAEA,QAAA,IAAI;AAEF,UAAA,MAAM,QAAA,EAAU,MAAM,qBAAA;AAAA,YACpB,gBAAA,CAAiB,OAAA;AAAA,YACjB;AAAA,UACF,CAAA;AAGA,UAAA,MAAM,iBAAA,EAAmB,MAAM,yBAAA,CAA0B,OAAO,CAAA;AAGhE,UAAA,GAAA,CAAI,gBAAA,CAAiB,kBAAA,EAAoB;AACvC,YAAA,MAAM,gBAAA,CAAiB,kBAAA,CAAmB,gBAAgB,CAAA;AAAA,UAC5D;AAAA,QACF,EAAA,MAAA,CAAS,KAAA,EAAO;AACd,UAAA,OAAA,CAAQ,KAAA,CAAM,wCAAA,EAA0C,KAAK,CAAA;AAC7D,UAAA,MAAM,KAAA;AAAA,QACR;AAAA,MACF;AAAA,IACF,EAAA,KAAO;AAEL,MAAA,GAAA,CAAI,gBAAA,CAAiB,kBAAA,EAAoB;AACvC,QAAA,MAAM,gBAAA,CAAiB,kBAAA,CAAmB,CAAA;AAAA,MAC5C;AAAA,IACF;AAEA,IAAA,IAAI;AACF,MAAA,MAAMD,SAAAA,EAAU,mCAAA,+BAA6B,CAAA;AAE7C,MAAA,MAAM,IAAA,EAAMA,QAAAA,CAAQ,2BAA2B,CAAA;AAI/C,MAAA,MAAM,SAAA,mBAAY,GAAA,CAAI,OAAA,UAAW,KAAA;AAEjC,MAAA,GAAA,CAAI,OAAO,QAAA,CAAS,WAAA,IAAe,UAAA,EAAY;AAC7C,QAAA,MAAM,IAAI,KAAA;AAAA,UACR;AAAA,QACF,CAAA;AAAA,MACF;AAGA,MAAA,GAAA,CAAI,gBAAA,CAAiB,SAAA,EAAW;AAC9B,QAAA,GAAA,CAAI,OAAO,gBAAA,CAAiB,QAAA,IAAY,QAAA,EAAU;AAEhD,UAAA,GAAA,CAAI,GAAA;AAAA,YACF,gBAAA,CAAiB,SAAA;AAAA,YACjB,iBAAA,CAAQ,MAAA,CAAO,gBAAA,CAAiB,OAAO;AAAA,UACzC,CAAA;AAAA,QACF,EAAA,KAAO;AAEL,UAAA,GAAA,CAAI,GAAA,CAAI,gBAAA,CAAiB,SAAA,EAAW,CAAC,IAAA,EAAM,GAAA,EAAA,GAAQ;AACjD,YAAA,GAAA,CAAI,IAAA,CAAK,gBAAA,CAAiB,OAAO,CAAA;AAAA,UACnC,CAAC,CAAA;AAAA,QACH;AAAA,MACF;AAEA,MAAA,MAAM,QAAA,EAAU,QAAA,CAAS,UAAA;AAGzB,MAAA,MAAM,WAAA,EAAa,OAAA,CAAQ,gBAAgB,CAAA;AAC3C,MAAA,GAAA,CAAI,KAAA,CAAM,OAAA,CAAQ,UAAU,CAAA,EAAG;AAC7B,QAAA,IAAA,CAAA,MAAW,EAAA,GAAK,UAAA,EAAY,GAAA,CAAI,GAAA,CAAI,CAAC,CAAA;AAAA,MACvC,EAAA,KAAO;AACL,QAAA,GAAA,CAAI,GAAA,CAAI,UAAU,CAAA;AAAA,MACpB;AAAA,IACF,EAAA,WAAQ;AACN,MAAA,OAAA,CAAQ,IAAA;AAAA,QACN;AAAA,MAEF,CAAA;AACA,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,MACF,CAAA;AAAA,IACF;AAAA,EACF;AAGA,EAAA,GAAA,CAAI,IAAA,EAAM;AACR,IAAA,IAAA,CAAA,MAAW,IAAA,GAAO,IAAA,EAAM;AACtB,MAAA,GAAA,CAAI,MAAM,CAAA;AAAA,IACZ;AACA,IAAA,GAAA,CAAI,GAAA,CAAI,MAAM,CAAA;AAAA,EAChB;AAGA,EAAA,GAAA,CAAI,CAAC,+BAAA;AACH,IAAA,GAAA,CAAI,GAAA,CAAI,wBAAA,CAAyB,QAAQ,CAAC,CAAA;AAE5C,EAAA,OAAO,GAAA;AACT,CAAA;AAMO,IAAM,SAAA,EAAW,CACtB,GAAA,EACA,QAAA,EAA2B,EAAE,IAAA,EAAM,IAAK,CAAA,EAAA,GACrC;AACH,EAAA,MAAM,EAAE,KAAK,EAAA,EAAI,OAAA;AACjB,EAAA,MAAM,OAAA,EAAS,cAAA,CAAK,YAAA,CAAa,GAAG,CAAA;AAEpC,EAAA,MAAA,CAAO,EAAA,CAAG,WAAA,EAAa,CAAA,EAAA,GAAM;AAC3B,IAAA,OAAA,CAAQ,IAAA,CAAK,qBAAqB,CAAA;AAAA,EACpC,CAAC,CAAA;AAED,EAAA,OAAO,MAAA,CAAO,MAAA,CAAO,IAAI,CAAA;AAC3B,CAAA;AFtGA;AACA;AIjKO,IAAM,YAAA,EAAc;AAAA,EACzB,QAAA,EAAU,UAAA;AAAA,EACV,YAAA,EAAc,cAAA;AAAA,EACd,IAAA,EAAM;AACR,CAAA;AAKO,IAAM,cAAA,EAAgB,gBAAA;AAEtB,IAAW,WAAA,kBAAX,CAAA,CAAWE,WAAAA,EAAAA,GAAX;AACL,EAAAA,WAAAA,CAAA,wBAAA,EAAA,EAAyB,wBAAA;AACzB,EAAAA,WAAAA,CAAA,yBAAA,EAAA,EAA0B,yBAAA;AAC1B,EAAAA,WAAAA,CAAA,6BAAA,EAAA,EAA8B,6BAAA;AAHd,EAAA,OAAAA,WAAAA;AAAA,CAAA,CAAA,CAAA,WAAA,GAAA,CAAA,CAAA,CAAA;AAMX,IAAM,WAAA,EAAa,CAAC,IAAA,EAAA,GAAiC;AAC1D,EAAA,OAAO,aAAA,CAAc,IAAA,CAAK,IAAc,CAAA;AAC1C,CAAA;AAEO,IAAM,iBAAA,EAAmB,CAAC,IAAA,EAAA,GAAuB;AACtD,EAAA,MAAM,OAAA,EAAS,aAAA,CAAc,IAAA,CAAK,IAAc,CAAA;AAChD,EAAA,GAAA,CAAI,OAAA,IAAW,KAAA,GAAQ,MAAA,CAAO,OAAA,IAAW,CAAA,EAAG;AAC1C,IAAA,MAAM,IAAI,KAAA,CAAM,qDAAiC,CAAA;AAAA,EACnD;AACA,EAAA,OAAO,MAAA,CAAO,CAAC,CAAA;AACjB,CAAA;AAEO,IAAM,WAAA,EAAa,CAAC,KAAA,EAAA,GAA8C;AACvE,EAAA,OAAO,CAAA,GAAA,EAAM,KAAK,CAAA,CAAA,CAAA;AACpB,CAAA;AAEO,IAAM,mBAAA,EAAqB,CAAC,OAAA,EAAA,GAA2B;AAC5D,EAAA,MAAM,KAAA,EAAO,OAAA,CAAQ,OAAA,CAAQ,WAAA,CAAY,QAAQ,CAAA;AAEjD,EAAA,GAAA,CAAI,KAAA,IAAS,KAAA,CAAA,EAAW;AACtB,IAAA,MAAM,IAAI,KAAA,CAAM,uDAAkC,CAAA;AAAA,EACpD;AAEA,EAAA,OAAO,IAAA;AACT,CAAA;AAEO,IAAM,sBAAA,EAAwB,CAAC,OAAA,EAAA,GAA2B;AAC/D,EAAA,MAAM,KAAA,EAAO,OAAA,CAAQ,OAAA,CAAQ,WAAA,CAAY,YAAY,CAAA;AAErD,EAAA,GAAA,CAAI,KAAA,IAAS,KAAA,CAAA,EAAW;AACtB,IAAA,MAAM,IAAI,KAAA,CAAM,uDAAkC,CAAA;AAAA,EACpD;AAEA,EAAA,OAAQ,KAAA,CAAM,OAAA,CAAQ,IAAI,EAAA,EAAI,IAAA,CAAK,CAAC,EAAA,EAAI,IAAA;AAC1C,CAAA;AAEO,IAAM,QAAA,EAAU,CAAC,QAAA,EAAoB,IAAA,EAAA,GAAqB;AAC/D,EAAA,QAAA,CAAS,SAAA,CAAU,WAAA,CAAY,IAAA,EAAM,IAAc,CAAA;AACrD,CAAA;AAEO,IAAM,wBAAA,EAA0B,CAAC,OAAA,EAAA,GAA6B;AACnE,EAAA,MAAM,UAAA,EAAkB,kBAAA,CAAmB,OAAO,CAAA;AAElD,EAAA,OAAO,UAAA,CAAW,SAAS,EAAA,EACvB,gBAAA,CAAiB,SAAS,EAAA,EACzB,SAAA;AACP,CAAA;AJiJA;AACA;AKpMO,IAAM,GAAA,EACX,CAA8B,MAAA,EAAA,GAC9B,MAAA,CACE,OAAA,EACA,QAAA,EACA,KAAA,EAAA,GACkB;AAClB,EAAA,MAAM,YAAA,EAAc,MAAM,OAAA,CAAQ,OAAA,CAAQ,MAAA,CAAO,OAAO,CAAC,CAAA;AAEzD,EAAA,OAAO,WAAA,CAAY,QAAQ,CAAA;AAC7B,CAAA;AAGK,IAAM,GAAA,EACX,CAAC,OAAA,EAAA,GACD,CAAC,QAAA,EAAA,GAAuB;AACtB,EAAA,IAAA,CAAK,QAAA,EAAU,GAAA,EAAK,OAAO,CAAA;AAC7B,CAAA;AAEK,IAAM,QAAA,EACX,CAAC,OAAA,EAAA,GACD,CAAC,QAAA,EAAA,GAAuB;AACtB,EAAA,WAAA,CAAY,QAAA,EAAU,OAAO,CAAA;AAC/B,CAAA;AAEK,IAAM,SAAA,EACX,CAAC,OAAA,EAAA,GACD,CAAC,QAAA,EAAA,GAAuB;AACtB,EAAA,YAAA,CAAa,QAAA,EAAU,OAAO,CAAA;AAChC,CAAA;AAEK,IAAM,UAAA,EAAY,CACvB,OAAA,EAAA,GACiB,YAAA,CAAa,GAAA,EAAK,OAAO,CAAA;AAErC,IAAM,aAAA,EACX,CAAC,UAAA,EAAoB,OAAA,EAAA,GACrB,CAAC,QAAA,EAAA,GAAuB;AACtB,EAAA,IAAA,CAAK,QAAA,EAAU,UAAA,EAAY,OAAO,CAAA;AACpC,CAAA;AAMK,IAAM,WAAA,EAAa,CACxB,OAAA,EAAA,GACiB,WAAA,CAAY,GAAA,EAAK,OAAO,CAAA;AAEpC,IAAM,UAAA,EAAY,CAAC,OAAA,EAAA,GACxB,WAAA,CAAY,GAAA,EAAK,OAAO,CAAA;AAEnB,IAAM,SAAA,EAAW,CAAC,OAAA,EAAA,GACvB,WAAA,CAAY,GAAA,EAAK,OAAO,CAAA;AAEnB,IAAM,SAAA,EAAW,CAAC,OAAA,EAAA,GACvB,WAAA,CAAY,GAAA,EAAK,OAAO,CAAA;AAEnB,IAAM,mBAAA,EAAqB,CAChC,OAAA,EAAA,GACiB,WAAA,CAAY,GAAA,EAAK,OAAO,CAAA;AAEpC,IAAM,YAAA,EACX,CAAC,UAAA,EAAoB,OAAA,EAAA,GACrB,CAAC,QAAA,EAAA,GAAuB;AACtB,EAAA,WAAA,CAAY,QAAA,EAAU,UAAA,EAAY,OAAO,CAAA;AAC3C,CAAA;AL4JF;AACA;AMpNA,IAAM,iBAAA,EAAmB,MAAA,CAAA,EAAA,GAAyC;AAChE,EAAA,IAAI;AACF,IAAA,MAAM,IAAA,EAAM,MAAM,4DAAA,CAAO,qCAAqC,GAAA;AAC9D,IAAA,MAAM,SAAA,mBAAY,GAAA,CAA2C,OAAA,UAAW,KAAA;AACxE,IAAA,MAAM,uBAAA,EACH,QAAA,CAAqC,sBAAA;AAExC,IAAA,GAAA,CAAI,OAAO,uBAAA,IAA2B,UAAA,EAAY;AAChD,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,MACF,CAAA;AAAA,IACF;AAEA,IAAA,OAAO,QAAA;AAAA,EACT,EAAA,MAAA,CAAS,KAAA,EAAO;AACd,IAAA,MAAM,QAAA,EACJ,0JAAA;AAEF,IAAA,MAAM,IAAI,KAAA,CAAM,OAAA,EAAS,EAAE,KAAA,EAAO,MAAe,CAAC,CAAA;AAAA,EACpD;AACF,CAAA;AAEA,IAAM,mBAAA,EAAqB,CAAA,EAAA,GAAM;AAC/B,EAAA,MAAM,IAAA,EAA+B,CAAC,CAAA;AACtC,EAAA,GAAA,CAAI,OAAA,EAAS,CAAA,EAAA,GAAM,GAAA;AACnB,EAAA,GAAA,CAAI,KAAA,EAAO,CAAA,EAAA,GAAM,GAAA;AACjB,EAAA,GAAA,CAAI,KAAA,EAAO,CAAA,EAAA,GAAM,GAAA;AACjB,EAAA,GAAA,CAAI,IAAA,EAAM,CAAA,EAAA,GAAM,GAAA;AAChB,EAAA,GAAA,CAAI,IAAA,EAAM,CAAA,EAAA,GAAM,GAAA;AAChB,EAAA,OAAO,GAAA;AACT,CAAA;AAEA,IAAM,cAAA,EAAgB,MAAA,CACpB,UAAA,EACA,GAAA,EAAA,GACqB;AACrB,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAA,GAAY;AAC9B,IAAA,IAAI,WAAA,EAAa,KAAA;AACjB,IAAA,MAAM,IAAA,EAAM,kBAAA,CAAmB,CAAA;AAC/B,IAAA,MAAM,KAAA,EAAO,CAAA,EAAA,GAAM;AACjB,MAAA,WAAA,EAAa,IAAA;AACb,MAAA,OAAA,CAAQ,IAAI,CAAA;AAAA,IACd,CAAA;AAEA,IAAA,OAAA,CAAQ,OAAA,CAAQ,UAAA,CAAW,GAAA,EAAK,GAAA,EAAK,IAAI,CAAC,CAAA,CACvC,IAAA,CAAK,CAAA,EAAA,GAAM;AACV,MAAA,GAAA,CAAI,CAAC,UAAA,EAAY,OAAA,CAAQ,KAAK,CAAA;AAAA,IAChC,CAAC,CAAA,CACA,KAAA,CAAM,CAAA,EAAA,GAAM,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EAC/B,CAAC,CAAA;AACH,CAAA;AAEO,IAAM,mCAAA,EAAqC,CAChD,QAAA,EAAuC,CAAC,CAAA,EAAA,GACnB;AACrB,EAAA,MAAM,mBAAA,mBAAqB,OAAA,CAAQ,kBAAA,UAAsB,cAAA;AACzD,EAAA,MAAM,UAAA,mBAAY,OAAA,CAAQ,SAAA,UAAa,SAAA;AAEvC,EAAA,OAAO;AAAA,IACL,CAAC,kBAAkB,CAAA,EAAG,MAAA,CAAO,GAAA,EAAK,MAAA,EAAQ,OAAA,EAAA,GAAY;AACpD,MAAA,MAAM,EAAE,uBAAuB,EAAA,EAAI,MAAM,gBAAA,CAAiB,CAAA;AAC1D,MAAA,MAAM,WAAA,EAAa,sBAAA,CAAuB;AAAA,QACxC,UAAA,EAAY,OAAA,CAAQ;AAAA,MACtB,CAAC,CAAA;AAED,MAAA,MAAM,gBAAA,EAAkB,MAAM,aAAA,CAAc,UAAA,EAAY,GAAG,CAAA;AAC3D,MAAA,GAAA,CAAI,CAAC,eAAA,EAAiB,OAAO,KAAA;AAE7B,MAAA,GAAA,CAAI,CAAC,MAAA,CAAO,MAAA,EAAQ,OAAO,IAAA;AAE3B,MAAA,MAAM,MAAA,kBAAS,GAAA,2BAA6B,IAAA,6BAAM,KAAA,4BAAA,CAAQ,SAAS,GAAA;AACnE,MAAA,GAAA,CAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG,OAAO,KAAA;AAElC,MAAA,OAAO,MAAA,CAAO,KAAA,CAAM,CAAC,KAAA,EAAA,GAAkB,KAAA,CAAM,QAAA,CAAS,KAAK,CAAC,CAAA;AAAA,IAC9D;AAAA,EACF,CAAA;AACF,CAAA;ANiMA;AACA;AOhBO,IAAM,8BAAA,EAAgC,CAC3C,OAAA,EACA,OAAA,EAAA,GAC4B;AAC5B,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,gBAAA,mCAAkB,OAAA,6BAAS,kBAAA,UAAoB,MAAA;AAAA,IAC/C,iBAAA,mCAAmB,OAAA,6BAAS,mBAAA,UAAqB,OAAA;AAAA,IACjD,gBAAA,mCAAkB,OAAA,6BAAS,kBAAA,UAAoB,MAAA;AAAA,IAC/C,eAAA,mCAAiB,OAAA,6BAAS,iBAAA,UAAmB,MAAA;AAAA,IAC7C,iBAAA,kBAAmB,OAAA,6BAAS,mBAAA;AAAA,IAC5B,WAAA,kBAAa,OAAA,6BAAS,aAAA;AAAA,IACtB,eAAA,mCAAiB,OAAA,+BAAS,iBAAA,UAAmB,MAAA;AAAA,IAC7C,UAAA,kBAAY,OAAA,+BAAS,YAAA;AAAA,IACrB,SAAA,mCAAW,OAAA,+BAAS,WAAA,UAAa,OAAA;AAAA,IACjC,YAAA,kBAAc,OAAA,+BAAS,cAAA;AAAA,IACvB,kBAAA,kBAAoB,OAAA,+BAAS;AAAA,EAC/B,CAAA;AACF,CAAA;AAKO,IAAM,4BAAA,EAA8B,MAAA,CAAA,EAAA,GAA8B;AACvE,EAAA,IAAI;AACF,IAAA,MAAM,4DAAA,CAAO,2BAA2B,GAAA;AACxC,IAAA,OAAO,IAAA;AAAA,EACT,EAAA,WAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF,CAAA;APWA;AACA;AQtUA;AAaO,IAAM,2BAAA,EAAkD,CAAC,CAAA;AAYzD,IAAM,kCAAA,EAAgE;AAAA,EAC3E,cAAA,EAAgB;AAClB,CAAA;AAaO,IAAM,YAAA,EAAc,CACzB,QAAA,EACA,EAAE,IAAA,EAAM,GAAG,QAAQ,CAAA,EAAA,GAEnB,IAAA,CAAK,QAAA,EAAU,GAAA,EAAK;AAAA,EAClB,QAAA,EACE,MAAA,GAAS,QAAA,EACL,OAAA,CAAQ,IAAA,EACR,CAAA,EAAA;AAC+B,EAAA;AACrC,EAAA;AACD;AASQ;AAQA;AACmC,EAAA;AAEZ,EAAA;AACC,EAAA;AAEvB,EAAA;AACc,IAAA;AACJ,IAAA;AACb,EAAA;AACyB,IAAA;AAChC,EAAA;AACF;AAKE;AAEqB,EAAA;AAEM,EAAA;AAGZ,EAAA;AAGS,IAAA;AACR,IAAA;AACT,EAAA;AAGyB,EAAA;AACC,EAAA;AAEE,EAAA;AAEb,EAAA;AACM,EAAA;AAC9B;AR+P8C;AACA;AS5WL;AAGtB;AAcgB;AAIP,EAAA;AACxB,IAAA;AAC8C,MAAA;AACT,QAAA;AAE1B,QAAA;AACgC,UAAA;AACR,YAAA;AACC,cAAA;AAEF,cAAA;AACI,gBAAA;AAC5B,cAAA;AAE8B,cAAA;AAChC,YAAA;AAEO,YAAA;AAGe,cAAA;AACY,gBAAA;AAEA,gBAAA;AACA,kBAAA;AAEH,kBAAA;AAC1B,gBAAA;AACH,cAAA;AACF,YAAA;AACF,UAAA;AACF,QAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AACF;AToV8C;AACA;AU7Y9C;AACE;AACA;AACA;AACA;AAIK;AAIe;AAYW;AACP,EAAA;AAC1B;AAgBiC;AACP,EAAA;AAC1B;AAKiC;AACP,EAAA;AAC1B;AAOE;AACwC,EAAA;AACL,EAAA;AACU,EAAA;AACP,EAAA;AACtC;AAIA;AAGE,EAAA;AAC4C,EAAA;AAC9C;AAc8B;AAOI,EAAA;AAChC,IAAA;AAC4D,MAAA;AACtB,QAAA;AAE3B,QAAA;AACgC,UAAA;AACR,YAAA;AACC,cAAA;AAEI,cAAA;AACL,gBAAA;AACzB,cAAA;AAE8B,cAAA;AAChC,YAAA;AAEO,YAAA;AAGe,cAAA;AACY,gBAAA;AAER,gBAAA;AACK,kBAAA;AAEA,kBAAA;AACF,gBAAA;AACE,kBAAA;AAEJ,kBAAA;AACK,oBAAA;AAEC,oBAAA;AAC3B,kBAAA;AAEsB,kBAAA;AAEtB,kBAAA;AACwB,oBAAA;AACtB,oBAAA;AACF,kBAAA;AACF,gBAAA;AACF,cAAA;AACF,YAAA;AACF,UAAA;AACF,QAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AACF;AViU8C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/emmett-expressjs-with-openapi/emmett-expressjs-with-openapi/dist/index.cjs","sourcesContent":[null,"import 'express-async-errors';\n\nexport * from './application';\nexport * from './etag';\nexport * from './handler';\nexport * from './openapi';\nexport * from './responses';\nexport * from './testing';\nexport { registerHandlerModule } from './internal/esm-resolver';\n","import express, {\n Router,\n type Application,\n type RequestHandler,\n} from 'express';\nimport 'express-async-errors';\nimport http from 'http';\nimport { createRequire } from 'node:module';\nimport { problemDetailsMiddleware } from './middlewares/problemDetailsMiddleware';\nimport type { OpenApiValidatorOptions } from './openapi';\nimport type { ErrorToProblemDetailsMapping } from './responses';\n\n// #region web-api-setup\nexport type WebApiSetup = (router: Router) => void;\n// #endregion web-api-setup\n\n/**\n * Options forwarded to pino-http. Typed loosely to avoid hard dependency.\n *\n * @see https://github.com/pinojs/pino-http\n */\nexport type PinoHttpOptions = Record<string, unknown>;\n\nexport type ApplicationOptions = {\n apis?: WebApiSetup[];\n mapError?: ErrorToProblemDetailsMapping;\n enableDefaultExpressEtag?: boolean;\n disableJsonMiddleware?: boolean;\n disableUrlEncodingMiddleware?: boolean;\n disableProblemDetailsMiddleware?: boolean;\n /**\n * Optional Pino HTTP logger configuration.\n * When true, enables pino-http with defaults.\n * When an object, forwards the options to pino-http.\n * Requires the 'pino-http' package to be installed.\n *\n * @see https://github.com/pinojs/pino-http\n * @example\n * ```typescript\n * const app = await getApplication({\n * pinoHttp: true,\n * });\n *\n * const app = await getApplication({\n * pinoHttp: { autoLogging: false },\n * });\n * ```\n */\n pinoHttp?: boolean | PinoHttpOptions;\n /**\n * Optional OpenAPI validator configuration.\n * When provided, enables request/response validation against an OpenAPI specification.\n * Requires the 'express-openapi-validator' package to be installed.\n *\n * @see https://github.com/cdimascio/express-openapi-validator\n * @example\n * ```typescript\n * import { getApplication, createOpenApiValidatorOptions } from '@event-driven-io/emmett-expressjs';\n *\n * type AppDeps = {\n * eventStore: EventStore;\n * messageBus: EventsPublisher;\n * };\n *\n * const app = await getApplication({\n * openApiValidator: createOpenApiValidatorOptions<AppDeps>('./openapi.yaml', {\n * validateResponses: true,\n * operationHandlers: './handlers',\n * initializeHandlers: (deps) => {\n * initializeHandlers(deps.eventStore, deps.messageBus);\n * }\n * })\n * });\n * ```\n */\n openApiValidator?: OpenApiValidatorOptions;\n};\n\nexport const getApplication = async (options: ApplicationOptions) => {\n const app: Application = express();\n\n const {\n apis,\n mapError,\n enableDefaultExpressEtag,\n disableJsonMiddleware,\n disableUrlEncodingMiddleware,\n disableProblemDetailsMiddleware,\n pinoHttp,\n openApiValidator,\n } = options;\n\n const router = Router();\n\n // disabling default etag behaviour\n // to use etags in if-match and if-not-match headers\n app.set('etag', enableDefaultExpressEtag ?? false);\n\n // add Pino HTTP logger middleware if configured\n if (pinoHttp !== undefined && pinoHttp !== false) {\n try {\n const require = createRequire(import.meta.url);\n const mod = require('pino-http') as Record<string, unknown>;\n const provider = (mod.default ?? mod) as unknown;\n\n if (typeof provider !== 'function') {\n throw new Error('Invalid pino-http module: missing default export');\n }\n\n const options = pinoHttp === true ? undefined : pinoHttp;\n const middleware = (\n provider as (opts?: PinoHttpOptions) => RequestHandler\n )(options);\n app.use(middleware);\n } catch {\n console.warn(\n 'Pino HTTP configuration provided but pino-http package is not installed. ' +\n 'Install it with: npm install pino-http',\n );\n throw new Error(\n 'pino-http package is required when pinoHttp option is used',\n );\n }\n }\n\n // add json middleware\n if (!disableJsonMiddleware) app.use(express.json());\n\n // enable url encoded urls and bodies\n if (!disableUrlEncodingMiddleware)\n app.use(\n express.urlencoded({\n extended: true,\n }),\n );\n\n // add OpenAPI validator middleware if configured\n if (openApiValidator) {\n // Activate ESM resolver if operationHandlers are configured\n // This ensures handler modules are loaded via ESM import() instead of CJS require(),\n // preventing dual module loading issues when using TypeScript runtimes (tsx, ts-node)\n if (openApiValidator.operationHandlers) {\n const { activateESMResolver } = await import(\n './internal/esm-resolver.js'\n );\n activateESMResolver();\n\n // NEW: Auto-discover and import handler modules from OpenAPI spec\n const handlersBasePath =\n typeof openApiValidator.operationHandlers === 'string'\n ? openApiValidator.operationHandlers\n : openApiValidator.operationHandlers.basePath;\n\n if (handlersBasePath) {\n const { extractHandlerModules } = await import(\n './internal/openapi-parser.js'\n );\n const { importAndRegisterHandlers } = await import(\n './internal/handler-importer.js'\n );\n\n try {\n // Parse OpenAPI spec to find handler modules\n const modules = await extractHandlerModules(\n openApiValidator.apiSpec,\n handlersBasePath,\n );\n\n // Dynamically import and register all handler modules\n const importedHandlers = await importAndRegisterHandlers(modules);\n\n // Call user's initializeHandlers callback with imported modules\n if (openApiValidator.initializeHandlers) {\n await openApiValidator.initializeHandlers(importedHandlers);\n }\n } catch (error) {\n console.error('Failed to auto-import handler modules:', error);\n throw error;\n }\n }\n } else {\n // No operationHandlers, just call initializeHandlers if provided\n if (openApiValidator.initializeHandlers) {\n await openApiValidator.initializeHandlers();\n }\n }\n\n try {\n const require = createRequire(import.meta.url);\n // express-openapi-validator exports a default with .middleware (ESM/CJS compatibility)\n const mod = require('express-openapi-validator') as Record<\n string,\n unknown\n >;\n const provider = (mod.default ?? mod) as Record<string, unknown>;\n\n if (typeof provider.middleware !== 'function') {\n throw new Error(\n 'Invalid express-openapi-validator module: missing middleware export',\n );\n }\n\n // Serve OpenAPI spec if configured\n if (openApiValidator.serveSpec) {\n if (typeof openApiValidator.apiSpec === 'string') {\n // If apiSpec is a file path, serve it as a static file\n app.use(\n openApiValidator.serveSpec,\n express.static(openApiValidator.apiSpec),\n );\n } else {\n // If apiSpec is an object, serve it as JSON\n app.get(openApiValidator.serveSpec, (_req, res) => {\n res.json(openApiValidator.apiSpec);\n });\n }\n }\n\n const factory = provider.middleware as (\n opts: OpenApiValidatorOptions,\n ) => RequestHandler | RequestHandler[];\n const middleware = factory(openApiValidator);\n if (Array.isArray(middleware)) {\n for (const m of middleware) app.use(m);\n } else {\n app.use(middleware);\n }\n } catch {\n console.warn(\n 'OpenAPI validator configuration provided but express-openapi-validator package is not installed. ' +\n 'Install it with: npm install express-openapi-validator',\n );\n throw new Error(\n 'express-openapi-validator package is required when openApiValidator option is used',\n );\n }\n }\n\n // Register API routes if provided\n if (apis) {\n for (const api of apis) {\n api(router);\n }\n app.use(router);\n }\n\n // add problem details middleware\n if (!disableProblemDetailsMiddleware)\n app.use(problemDetailsMiddleware(mapError));\n\n return app;\n};\n\nexport type StartApiOptions = {\n port?: number;\n};\n\nexport const startAPI = (\n app: Application,\n options: StartApiOptions = { port: 3000 },\n) => {\n const { port } = options;\n const server = http.createServer(app);\n\n server.on('listening', () => {\n console.info('server up listening');\n });\n\n return server.listen(port);\n};\n","import type { NextFunction, Request, Response } from 'express';\nimport { ProblemDocument } from 'http-problem-details';\nimport { sendProblem, type ErrorToProblemDetailsMapping } from '..';\n\nexport const problemDetailsMiddleware =\n (mapError?: ErrorToProblemDetailsMapping) =>\n (\n error: Error,\n request: Request,\n response: Response,\n _next: NextFunction,\n ): void => {\n let problemDetails: ProblemDocument | undefined;\n\n if (mapError) problemDetails = mapError(error, request);\n\n problemDetails =\n problemDetails ?? defaultErrorToProblemDetailsMapping(error);\n\n sendProblem(response, problemDetails.status, { problem: problemDetails });\n };\n\nexport const defaultErrorToProblemDetailsMapping = (\n error: Error,\n): ProblemDocument => {\n let statusCode = 500;\n\n // Prefer standard `status` code if present (e.g., express-openapi-validator)\n const errObj = error as unknown as Record<string, unknown>;\n const maybeStatus = errObj['status'];\n if (\n typeof maybeStatus === 'number' &&\n maybeStatus >= 100 &&\n maybeStatus < 600\n ) {\n statusCode = maybeStatus;\n }\n\n const maybeErrorCode = errObj['errorCode'];\n if (\n typeof maybeErrorCode === 'number' &&\n maybeErrorCode >= 100 &&\n maybeErrorCode < 600\n ) {\n statusCode = maybeErrorCode;\n }\n\n return new ProblemDocument({\n detail: error.message,\n status: statusCode,\n });\n};\n","import { type Brand } from '@event-driven-io/emmett';\nimport type { Request, Response } from 'express';\n\n//////////////////////////////////////\n/// ETAG\n//////////////////////////////////////\n\nexport const HeaderNames = {\n IF_MATCH: 'if-match',\n IF_NOT_MATCH: 'if-not-match',\n ETag: 'etag',\n};\n\nexport type WeakETag = Brand<`W/${string}`, 'ETag'>;\nexport type ETag = Brand<string, 'ETag'>;\n\nexport const WeakETagRegex = /W\\/\"(-?\\d+.*)\"/;\n\nexport const enum ETagErrors {\n WRONG_WEAK_ETAG_FORMAT = 'WRONG_WEAK_ETAG_FORMAT',\n MISSING_IF_MATCH_HEADER = 'MISSING_IF_MATCH_HEADER',\n MISSING_IF_NOT_MATCH_HEADER = 'MISSING_IF_NOT_MATCH_HEADER',\n}\n\nexport const isWeakETag = (etag: ETag): etag is WeakETag => {\n return WeakETagRegex.test(etag as string);\n};\n\nexport const getWeakETagValue = (etag: ETag): string => {\n const result = WeakETagRegex.exec(etag as string);\n if (result === null || result.length === 0) {\n throw new Error(ETagErrors.WRONG_WEAK_ETAG_FORMAT);\n }\n return result[1]!;\n};\n\nexport const toWeakETag = (value: number | bigint | string): WeakETag => {\n return `W/\"${value}\"` as WeakETag;\n};\n\nexport const getETagFromIfMatch = (request: Request): ETag => {\n const etag = request.headers[HeaderNames.IF_MATCH];\n\n if (etag === undefined) {\n throw new Error(ETagErrors.MISSING_IF_MATCH_HEADER);\n }\n\n return etag as ETag;\n};\n\nexport const getETagFromIfNotMatch = (request: Request): ETag => {\n const etag = request.headers[HeaderNames.IF_NOT_MATCH];\n\n if (etag === undefined) {\n throw new Error(ETagErrors.MISSING_IF_MATCH_HEADER);\n }\n\n return (Array.isArray(etag) ? etag[0] : etag) as ETag;\n};\n\nexport const setETag = (response: Response, etag: ETag): void => {\n response.setHeader(HeaderNames.ETag, etag as string);\n};\n\nexport const getETagValueFromIfMatch = (request: Request): string => {\n const eTagValue: ETag = getETagFromIfMatch(request);\n\n return isWeakETag(eTagValue)\n ? getWeakETagValue(eTagValue)\n : (eTagValue as string);\n};\n","import { type NextFunction, type Request, type Response } from 'express';\nimport {\n send,\n sendAccepted,\n sendCreated,\n sendProblem,\n type AcceptedHttpResponseOptions,\n type CreatedHttpResponseOptions,\n type HttpProblemResponseOptions,\n type HttpResponseOptions,\n type NoContentHttpResponseOptions,\n} from '.';\n\n// #region httpresponse-on\nexport type HttpResponse = (response: Response) => void;\n\nexport type HttpHandler<RequestType extends Request> = (\n request: RequestType,\n) => Promise<HttpResponse> | HttpResponse;\n\nexport const on =\n <RequestType extends Request>(handle: HttpHandler<RequestType>) =>\n async (\n request: RequestType,\n response: Response,\n _next: NextFunction,\n ): Promise<void> => {\n const setResponse = await Promise.resolve(handle(request));\n\n return setResponse(response);\n };\n// #endregion httpresponse-on\n\nexport const OK =\n (options?: HttpResponseOptions): HttpResponse =>\n (response: Response) => {\n send(response, 200, options);\n };\n\nexport const Created =\n (options: CreatedHttpResponseOptions): HttpResponse =>\n (response: Response) => {\n sendCreated(response, options);\n };\n\nexport const Accepted =\n (options: AcceptedHttpResponseOptions): HttpResponse =>\n (response: Response) => {\n sendAccepted(response, options);\n };\n\nexport const NoContent = (\n options?: NoContentHttpResponseOptions,\n): HttpResponse => HttpResponse(204, options);\n\nexport const HttpResponse =\n (statusCode: number, options?: HttpResponseOptions): HttpResponse =>\n (response: Response) => {\n send(response, statusCode, options);\n };\n\n/////////////////////\n// ERRORS\n/////////////////////\n\nexport const BadRequest = (\n options?: HttpProblemResponseOptions,\n): HttpResponse => HttpProblem(400, options);\n\nexport const Forbidden = (options?: HttpProblemResponseOptions): HttpResponse =>\n HttpProblem(403, options);\n\nexport const NotFound = (options?: HttpProblemResponseOptions): HttpResponse =>\n HttpProblem(404, options);\n\nexport const Conflict = (options?: HttpProblemResponseOptions): HttpResponse =>\n HttpProblem(409, options);\n\nexport const PreconditionFailed = (\n options: HttpProblemResponseOptions,\n): HttpResponse => HttpProblem(412, options);\n\nexport const HttpProblem =\n (statusCode: number, options?: HttpProblemResponseOptions): HttpResponse =>\n (response: Response) => {\n sendProblem(response, statusCode, options);\n };\n","import type { SecurityHandlers } from './index';\n\ntype AuthClient = {\n verifyIdToken: (token: string) => Promise<unknown>;\n};\n\nexport type FirebaseAuthSecurityOptions = {\n /**\n * Name of the OpenAPI security scheme to attach the handler to.\n * Defaults to \"bearerAuth\".\n */\n securitySchemeName?: string;\n /**\n * Custom auth client for tests or alternate Firebase auth instances.\n */\n authClient?: AuthClient;\n /**\n * Token claim used for role-based checks when scopes are defined.\n * Defaults to \"roles\".\n */\n roleClaim?: string;\n};\n\ntype FirebaseAuthModule = {\n firebaseAuthMiddleware: (options?: { authClient?: AuthClient }) => (\n req: unknown,\n res: unknown,\n next: () => void,\n ) => Promise<void> | void;\n};\n\nconst loadFirebaseAuth = async (): Promise<FirebaseAuthModule> => {\n try {\n const mod = await import('@my-f-startup/firebase-auth-express');\n const provider = (mod as unknown as Record<string, unknown>).default ?? mod;\n const firebaseAuthMiddleware =\n (provider as Record<string, unknown>).firebaseAuthMiddleware;\n\n if (typeof firebaseAuthMiddleware !== 'function') {\n throw new Error(\n 'Invalid @my-f-startup/firebase-auth-express module: missing firebaseAuthMiddleware export',\n );\n }\n\n return provider as FirebaseAuthModule;\n } catch (error) {\n const message =\n '@my-f-startup/firebase-auth-express is required for createFirebaseAuthSecurityHandlers. ' +\n 'Install it with: npm install @my-f-startup/firebase-auth-express';\n throw new Error(message, { cause: error as Error });\n }\n};\n\nconst createNullResponse = () => {\n const res: Record<string, unknown> = {};\n res.status = () => res;\n res.json = () => res;\n res.send = () => res;\n res.end = () => res;\n res.set = () => res;\n return res;\n};\n\nconst runMiddleware = async (\n middleware: (req: unknown, res: unknown, next: () => void) => Promise<void> | void,\n req: unknown,\n): Promise<boolean> => {\n return new Promise((resolve) => {\n let nextCalled = false;\n const res = createNullResponse();\n const next = () => {\n nextCalled = true;\n resolve(true);\n };\n\n Promise.resolve(middleware(req, res, next))\n .then(() => {\n if (!nextCalled) resolve(false);\n })\n .catch(() => resolve(false));\n });\n};\n\nexport const createFirebaseAuthSecurityHandlers = (\n options: FirebaseAuthSecurityOptions = {},\n): SecurityHandlers => {\n const securitySchemeName = options.securitySchemeName ?? 'bearerAuth';\n const roleClaim = options.roleClaim ?? 'roles';\n\n return {\n [securitySchemeName]: async (req, scopes, _schema) => {\n const { firebaseAuthMiddleware } = await loadFirebaseAuth();\n const middleware = firebaseAuthMiddleware({\n authClient: options.authClient,\n });\n\n const isAuthenticated = await runMiddleware(middleware, req);\n if (!isAuthenticated) return false;\n\n if (!scopes.length) return true;\n\n const roles = (req as Record<string, any>)?.auth?.token?.[roleClaim];\n if (!Array.isArray(roles)) return false;\n\n return scopes.every((scope: string) => roles.includes(scope));\n },\n };\n};\n","/**\n * OpenAPI v3 Document type (to avoid requiring express-openapi-validator types directly)\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type OpenAPIV3Document = any;\n\n/**\n * Imported handler modules, keyed by module name.\n * Automatically populated by the framework when operationHandlers is configured.\n */\nexport type ImportedHandlerModules = Record<string, any>;\n\n/**\n * Security handlers for custom authentication/authorization logic.\n * Maps security scheme names to handler functions.\n *\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/usage-validate-security/\n */\n\nexport type SecurityHandlers = Record<\n string,\n (req: any, scopes: string[], schema: any) => boolean | Promise<boolean>\n>;\n\nexport * from './firebase-auth';\n\n/**\n * Configuration options for express-openapi-validator middleware.\n * This allows optional validation of API requests and responses against an OpenAPI specification.\n *\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/\n */\nexport type OpenApiValidatorOptions = {\n /**\n * Path to the OpenAPI specification file (JSON or YAML)\n * or an OpenAPI specification object.\n */\n apiSpec: string | OpenAPIV3Document;\n\n /**\n * Determines whether the validator should validate requests.\n * Can be a boolean or an object with detailed request validation options.\n * @default true\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/usage-validate-requests/\n */\n validateRequests?:\n | boolean\n | {\n /**\n * Allow unknown query parameters (not defined in the spec).\n * @default false\n */\n allowUnknownQueryParameters?: boolean;\n /**\n * Coerce types in request parameters.\n * @default true\n */\n coerceTypes?: boolean | 'array';\n /**\n * Remove additional properties not defined in the spec.\n * @default false\n */\n removeAdditional?: boolean | 'all' | 'failing';\n };\n\n /**\n * Determines whether the validator should validate responses.\n * Can be a boolean or an object with detailed response validation options.\n * @default false\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/usage-validate-responses/\n */\n validateResponses?:\n | boolean\n | {\n /**\n * Remove additional properties from responses not defined in the spec.\n * @default false\n */\n removeAdditional?: boolean | 'all' | 'failing';\n /**\n * Coerce types in responses.\n * @default true\n */\n coerceTypes?: boolean;\n /**\n * Callback to handle response validation errors.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onError?: (error: any, body: any, req: any) => void;\n };\n\n /**\n * Determines whether the validator should validate security.\n * Can be a boolean or an object with security handlers.\n * @default true\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/usage-validate-security/\n */\n validateSecurity?:\n | boolean\n | {\n /**\n * Custom security handlers for authentication/authorization.\n */\n handlers?: SecurityHandlers;\n };\n\n /**\n * Defines how the validator should validate formats.\n * When true, uses ajv-formats for format validation.\n * When false, format validation is disabled.\n * Can also be 'fast' or 'full' for different validation modes.\n * @default true\n */\n validateFormats?: boolean | 'fast' | 'full';\n\n /**\n * The base path to the operation handlers directory.\n * When set to a path, automatically wires OpenAPI operations to handler functions\n * based on operationId or x-eov-operation-id.\n * When false, operation handlers are disabled (manual routing required).\n * @default false\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/guide-operation-handlers/\n */\n operationHandlers?:\n | string\n | false\n | {\n /**\n * Base path to operation handlers directory.\n */\n basePath?: string;\n /**\n * Resolver function to map operationId to handler module path.\n */\n resolver?: (\n handlersPath: string,\n route: string,\n apiDoc: OpenAPIV3Document,\n ) => string;\n };\n\n /**\n * Paths or pattern to ignore during validation.\n * @default undefined\n */\n ignorePaths?: RegExp | ((path: string) => boolean);\n\n /**\n * Validate the OpenAPI specification itself.\n * @default true\n */\n validateApiSpec?: boolean;\n\n /**\n * $ref parser configuration for handling OpenAPI references.\n * @default undefined\n */\n $refParser?: {\n mode: 'bundle' | 'dereference';\n };\n\n /**\n * Serve the OpenAPI specification at a specific path.\n * When set to a string, the spec will be served at that path.\n * When false, the spec will not be served.\n * @default false\n * @example '/api-docs/openapi.json'\n */\n serveSpec?: string | false;\n\n /**\n * File upload configuration options.\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/usage-file-uploads/\n */\n fileUploader?:\n | boolean\n | {\n /**\n * Destination directory for uploaded files.\n */\n dest?: string;\n /**\n * File size limit in bytes.\n */\n limits?: {\n fileSize?: number;\n files?: number;\n };\n };\n\n /**\n * Optional callback to initialize operation handlers with dependencies.\n * Called before the OpenAPI validator middleware is configured.\n *\n * The framework automatically imports handler modules referenced in your\n * OpenAPI spec (via x-eov-operation-handler) and passes them as the first parameter.\n *\n * @param handlers - Auto-imported handler modules, keyed by module name\n * @returns void or a Promise that resolves when initialization is complete\n *\n * @example\n * ```typescript\n * // With automatic import (recommended)\n * initializeHandlers: async (handlers) => {\n * handlers.shoppingCarts.initializeHandlers(eventStore, messageBus, getUnitPrice, getCurrentTime);\n * }\n *\n * // Manual import (still supported for backward compatibility)\n * import * as handlersModule from './handlers/shoppingCarts';\n * import { registerHandlerModule } from '@emmett-community/emmett-expressjs-with-openapi';\n * initializeHandlers: () => {\n * const handlersPath = path.join(__dirname, './handlers/shoppingCarts');\n * registerHandlerModule(handlersPath, handlersModule);\n * handlersModule.initializeHandlers(eventStore, messageBus, getUnitPrice, getCurrentTime);\n * }\n * ```\n */\n initializeHandlers?: (\n handlers?: ImportedHandlerModules,\n ) => void | Promise<void>;\n};\n\n/**\n * Helper function to create OpenAPI validator configuration with sensible defaults.\n *\n * @param apiSpec - Path to OpenAPI spec file or OpenAPI document object\n * @param options - Additional validator options\n * @returns Complete OpenApiValidatorOptions configuration\n *\n * @example\n * ```typescript\n * // Basic usage with default options\n * const validatorOptions = createOpenApiValidatorOptions('./openapi.yaml');\n *\n * // With response validation enabled\n * const validatorOptions = createOpenApiValidatorOptions('./openapi.yaml', {\n * validateResponses: true\n * });\n *\n * // With custom security handlers\n * const validatorOptions = createOpenApiValidatorOptions('./openapi.yaml', {\n * validateSecurity: {\n * handlers: {\n * bearerAuth: async (req, scopes) => {\n * // Custom authentication logic\n * return true;\n * }\n * }\n * }\n * });\n *\n * // Serving the spec at /api-docs\n * const validatorOptions = createOpenApiValidatorOptions('./openapi.yaml', {\n * serveSpec: '/api-docs/openapi.json'\n * });\n *\n * // With dependency injection for operation handlers\n * type ShoppingCartDeps = {\n * eventStore: EventStore;\n * messageBus: EventsPublisher;\n * getUnitPrice: (productId: string) => Promise<number>;\n * getCurrentTime: () => Date;\n * };\n *\n * const validatorOptions = createOpenApiValidatorOptions<ShoppingCartDeps>(\n * './openapi.yaml',\n * {\n * operationHandlers: './handlers',\n * initializeHandlers: (deps) => {\n * initializeHandlers(\n * deps.eventStore,\n * deps.messageBus,\n * deps.getUnitPrice,\n * deps.getCurrentTime\n * );\n * }\n * }\n * );\n *\n * const app = getApplication({\n * apis: [myApi],\n * openApiValidator: validatorOptions\n * });\n * ```\n */\nexport const createOpenApiValidatorOptions = (\n apiSpec: string | OpenAPIV3Document,\n options?: Partial<Omit<OpenApiValidatorOptions, 'apiSpec'>>,\n): OpenApiValidatorOptions => {\n return {\n apiSpec,\n validateRequests: options?.validateRequests ?? true,\n validateResponses: options?.validateResponses ?? false,\n validateSecurity: options?.validateSecurity ?? true,\n validateFormats: options?.validateFormats ?? true,\n operationHandlers: options?.operationHandlers,\n ignorePaths: options?.ignorePaths,\n validateApiSpec: options?.validateApiSpec ?? true,\n $refParser: options?.$refParser,\n serveSpec: options?.serveSpec ?? false,\n fileUploader: options?.fileUploader,\n initializeHandlers: options?.initializeHandlers,\n };\n};\n\n/**\n * Type guard to check if express-openapi-validator is available\n */\nexport const isOpenApiValidatorAvailable = async (): Promise<boolean> => {\n try {\n await import('express-openapi-validator');\n return true;\n } catch {\n return false;\n }\n};\n","import { type Request, type Response } from 'express';\nimport { ProblemDocument } from 'http-problem-details';\nimport { setETag, type ETag } from './etag';\n\nexport type ErrorToProblemDetailsMapping = (\n error: Error,\n request: Request,\n) => ProblemDocument | undefined;\n\nexport type HttpResponseOptions = {\n body?: unknown;\n location?: string;\n eTag?: ETag;\n};\nexport const DefaultHttpResponseOptions: HttpResponseOptions = {};\n\nexport type HttpProblemResponseOptions = {\n location?: string;\n eTag?: ETag;\n} & Omit<HttpResponseOptions, 'body'> &\n (\n | {\n problem: ProblemDocument;\n }\n | { problemDetails: string }\n );\nexport const DefaultHttpProblemResponseOptions: HttpProblemResponseOptions = {\n problemDetails: 'Error occured!',\n};\n\nexport type CreatedHttpResponseOptions = (\n | {\n createdId: string;\n }\n | {\n createdId?: string;\n url: string;\n }\n) &\n HttpResponseOptions;\n\nexport const sendCreated = (\n response: Response,\n { eTag, ...options }: CreatedHttpResponseOptions,\n): void =>\n send(response, 201, {\n location:\n 'url' in options\n ? options.url\n : `${response.req.url}/${options.createdId}`,\n body: 'createdId' in options ? { id: options.createdId } : undefined,\n eTag,\n });\n\nexport type AcceptedHttpResponseOptions = {\n location: string;\n} & HttpResponseOptions;\n\nexport const sendAccepted = (\n response: Response,\n options: AcceptedHttpResponseOptions,\n): void => send(response, 202, options);\n\nexport type NoContentHttpResponseOptions = Omit<HttpResponseOptions, 'body'>;\n\nexport const send = (\n response: Response,\n statusCode: number,\n options?: HttpResponseOptions,\n): void => {\n const { location, body, eTag } = options ?? DefaultHttpResponseOptions;\n // HEADERS\n if (eTag) setETag(response, eTag);\n if (location) response.setHeader('Location', location);\n\n if (body) {\n response.statusCode = statusCode;\n response.send(body);\n } else {\n response.sendStatus(statusCode);\n }\n};\n\nexport const sendProblem = (\n response: Response,\n statusCode: number,\n options?: HttpProblemResponseOptions,\n): void => {\n options = options ?? DefaultHttpProblemResponseOptions;\n\n const { location, eTag } = options;\n\n const problemDetails =\n 'problem' in options\n ? options.problem\n : new ProblemDocument({\n detail: options.problemDetails,\n status: statusCode,\n });\n\n // HEADERS\n if (eTag) setETag(response, eTag);\n if (location) response.setHeader('Location', location);\n\n response.setHeader('Content-Type', 'application/problem+json');\n\n response.statusCode = statusCode;\n response.json(problemDetails);\n};\n","import supertest, { type Response } from 'supertest';\n\nimport type { EventStore } from '@event-driven-io/emmett';\nimport assert from 'assert';\nimport type { Application } from 'express';\nimport type { TestRequest } from './apiSpecification';\n\nexport type E2EResponseAssert = (response: Response) => boolean | void;\n\nexport type ApiE2ESpecificationAssert = [E2EResponseAssert];\n\nexport type ApiE2ESpecification = (...givenRequests: TestRequest[]) => {\n when: (setupRequest: TestRequest) => {\n then: (verify: ApiE2ESpecificationAssert) => Promise<void>;\n };\n};\n\nexport const ApiE2ESpecification = {\n for: <Store extends EventStore = EventStore>(\n getEventStore: () => Store,\n getApplication: (eventStore: Store) => Application | Promise<Application>,\n ): ApiE2ESpecification => {\n {\n return (...givenRequests: TestRequest[]) => {\n const eventStore = getEventStore();\n\n return {\n when: (setupRequest: TestRequest) => {\n const handle = async () => {\n const application = await Promise.resolve(getApplication(eventStore));\n\n for (const requestFn of givenRequests) {\n await requestFn(supertest(application));\n }\n\n return setupRequest(supertest(application));\n };\n\n return {\n then: async (\n verify: ApiE2ESpecificationAssert,\n ): Promise<void> => {\n const response = await handle();\n\n verify.forEach((assertion) => {\n const succeeded = assertion(response);\n\n if (succeeded === false) assert.fail();\n });\n },\n };\n },\n };\n };\n }\n },\n};\n","import {\n WrapEventStore,\n assertEqual,\n assertFails,\n assertMatches,\n type Event,\n type EventStore,\n type TestEventStream,\n} from '@event-driven-io/emmett';\nimport { type Application } from 'express';\nimport type { ProblemDocument } from 'http-problem-details';\nimport type { Response, Test } from 'supertest';\nimport supertest from 'supertest';\nimport type TestAgent from 'supertest/lib/agent';\n\n////////////////////////////////\n/////////// Setup\n////////////////////////////////\n\nexport type TestRequest = (request: TestAgent<supertest.Test>) => Test;\n\nexport const existingStream = <EventType extends Event = Event>(\n streamId: string,\n events: EventType[],\n): TestEventStream<EventType> => {\n return [streamId, events];\n};\n\n////////////////////////////////\n/////////// Asserts\n////////////////////////////////\n\nexport type ResponseAssert = (response: Response) => boolean | void;\n\nexport type ApiSpecificationAssert<EventType extends Event = Event> =\n | TestEventStream<EventType>[]\n | ResponseAssert\n | [ResponseAssert, ...TestEventStream<EventType>[]];\n\nexport const expect = <EventType extends Event = Event>(\n streamId: string,\n events: EventType[],\n): TestEventStream<EventType> => {\n return [streamId, events];\n};\n\nexport const expectNewEvents = <EventType extends Event = Event>(\n streamId: string,\n events: EventType[],\n): TestEventStream<EventType> => {\n return [streamId, events];\n};\n\nexport const expectResponse =\n <Body = unknown>(\n statusCode: number,\n options?: { body?: Body; headers?: { [index: string]: string } },\n ) =>\n (response: Response): void => {\n const { body, headers } = options ?? {};\n assertEqual(statusCode, response.statusCode, \"Response code doesn't match\");\n if (body) assertMatches(response.body, body);\n if (headers) assertMatches(response.headers, headers);\n };\n\nexport const expectError = (\n errorCode: number,\n problemDetails?: Partial<ProblemDocument>,\n) =>\n expectResponse(\n errorCode,\n problemDetails ? { body: problemDetails } : undefined,\n );\n\n////////////////////////////////\n/////////// Api Specification\n////////////////////////////////\n\nexport type ApiSpecification<EventType extends Event = Event> = (\n ...givenStreams: TestEventStream<EventType>[]\n) => {\n when: (setupRequest: TestRequest) => {\n then: (verify: ApiSpecificationAssert<EventType>) => Promise<void>;\n };\n};\n\nexport const ApiSpecification = {\n for: <\n EventType extends Event = Event,\n Store extends EventStore<import('@event-driven-io/emmett').ReadEventMetadataWithGlobalPosition> = EventStore<import('@event-driven-io/emmett').ReadEventMetadataWithGlobalPosition>\n >(\n getEventStore: () => Store,\n getApplication: (eventStore: Store) => Application | Promise<Application>,\n ): ApiSpecification<EventType> => {\n {\n return (...givenStreams: TestEventStream<EventType>[]) => {\n const eventStore = WrapEventStore(getEventStore());\n\n return {\n when: (setupRequest: TestRequest) => {\n const handle = async () => {\n const application = await Promise.resolve(getApplication(eventStore));\n\n for (const [streamName, events] of givenStreams) {\n await eventStore.setup(streamName, events);\n }\n\n return setupRequest(supertest(application));\n };\n\n return {\n then: async (\n verify: ApiSpecificationAssert<EventType>,\n ): Promise<void> => {\n const response = await handle();\n\n if (typeof verify === 'function') {\n const succeeded = verify(response);\n\n if (succeeded === false) assertFails();\n } else if (Array.isArray(verify)) {\n const [first, ...rest] = verify;\n\n if (typeof first === 'function') {\n const succeeded = first(response);\n\n if (succeeded === false) assertFails();\n }\n\n const events = typeof first === 'function' ? rest : verify;\n\n assertMatches(\n Array.from(eventStore.appendedEvents.values()),\n events,\n );\n }\n },\n };\n },\n };\n };\n }\n },\n};\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -332,6 +332,12 @@ declare const send: (response: Response, statusCode: number, options?: HttpRespo
|
|
|
332
332
|
declare const sendProblem: (response: Response, statusCode: number, options?: HttpProblemResponseOptions) => void;
|
|
333
333
|
|
|
334
334
|
type WebApiSetup = (router: Router) => void;
|
|
335
|
+
/**
|
|
336
|
+
* Options forwarded to pino-http. Typed loosely to avoid hard dependency.
|
|
337
|
+
*
|
|
338
|
+
* @see https://github.com/pinojs/pino-http
|
|
339
|
+
*/
|
|
340
|
+
type PinoHttpOptions = Record<string, unknown>;
|
|
335
341
|
type ApplicationOptions = {
|
|
336
342
|
apis?: WebApiSetup[];
|
|
337
343
|
mapError?: ErrorToProblemDetailsMapping;
|
|
@@ -339,6 +345,25 @@ type ApplicationOptions = {
|
|
|
339
345
|
disableJsonMiddleware?: boolean;
|
|
340
346
|
disableUrlEncodingMiddleware?: boolean;
|
|
341
347
|
disableProblemDetailsMiddleware?: boolean;
|
|
348
|
+
/**
|
|
349
|
+
* Optional Pino HTTP logger configuration.
|
|
350
|
+
* When true, enables pino-http with defaults.
|
|
351
|
+
* When an object, forwards the options to pino-http.
|
|
352
|
+
* Requires the 'pino-http' package to be installed.
|
|
353
|
+
*
|
|
354
|
+
* @see https://github.com/pinojs/pino-http
|
|
355
|
+
* @example
|
|
356
|
+
* ```typescript
|
|
357
|
+
* const app = await getApplication({
|
|
358
|
+
* pinoHttp: true,
|
|
359
|
+
* });
|
|
360
|
+
*
|
|
361
|
+
* const app = await getApplication({
|
|
362
|
+
* pinoHttp: { autoLogging: false },
|
|
363
|
+
* });
|
|
364
|
+
* ```
|
|
365
|
+
*/
|
|
366
|
+
pinoHttp?: boolean | PinoHttpOptions;
|
|
342
367
|
/**
|
|
343
368
|
* Optional OpenAPI validator configuration.
|
|
344
369
|
* When provided, enables request/response validation against an OpenAPI specification.
|
|
@@ -459,4 +484,4 @@ declare const ApiE2ESpecification: {
|
|
|
459
484
|
*/
|
|
460
485
|
declare const registerHandlerModule: (modulePath: string, moduleExports: any) => void;
|
|
461
486
|
|
|
462
|
-
export { Accepted, type AcceptedHttpResponseOptions, ApiE2ESpecification, type ApiE2ESpecificationAssert, ApiSpecification, type ApiSpecificationAssert, type ApplicationOptions, BadRequest, Conflict, Created, type CreatedHttpResponseOptions, DefaultHttpProblemResponseOptions, DefaultHttpResponseOptions, type E2EResponseAssert, type ETag, ETagErrors, type ErrorToProblemDetailsMapping, type FirebaseAuthSecurityOptions, Forbidden, HeaderNames, type HttpHandler, HttpProblem, type HttpProblemResponseOptions, HttpResponse, type HttpResponseOptions, type ImportedHandlerModules, NoContent, type NoContentHttpResponseOptions, NotFound, OK, type OpenAPIV3Document, type OpenApiValidatorOptions, PreconditionFailed, type ResponseAssert, type SecurityHandlers, type StartApiOptions, type TestRequest, type WeakETag, WeakETagRegex, type WebApiSetup, createFirebaseAuthSecurityHandlers, createOpenApiValidatorOptions, existingStream, expect, expectError, expectNewEvents, expectResponse, getApplication, getETagFromIfMatch, getETagFromIfNotMatch, getETagValueFromIfMatch, getWeakETagValue, isOpenApiValidatorAvailable, isWeakETag, on, registerHandlerModule, send, sendAccepted, sendCreated, sendProblem, setETag, startAPI, toWeakETag };
|
|
487
|
+
export { Accepted, type AcceptedHttpResponseOptions, ApiE2ESpecification, type ApiE2ESpecificationAssert, ApiSpecification, type ApiSpecificationAssert, type ApplicationOptions, BadRequest, Conflict, Created, type CreatedHttpResponseOptions, DefaultHttpProblemResponseOptions, DefaultHttpResponseOptions, type E2EResponseAssert, type ETag, ETagErrors, type ErrorToProblemDetailsMapping, type FirebaseAuthSecurityOptions, Forbidden, HeaderNames, type HttpHandler, HttpProblem, type HttpProblemResponseOptions, HttpResponse, type HttpResponseOptions, type ImportedHandlerModules, NoContent, type NoContentHttpResponseOptions, NotFound, OK, type OpenAPIV3Document, type OpenApiValidatorOptions, type PinoHttpOptions, PreconditionFailed, type ResponseAssert, type SecurityHandlers, type StartApiOptions, type TestRequest, type WeakETag, WeakETagRegex, type WebApiSetup, createFirebaseAuthSecurityHandlers, createOpenApiValidatorOptions, existingStream, expect, expectError, expectNewEvents, expectResponse, getApplication, getETagFromIfMatch, getETagFromIfNotMatch, getETagValueFromIfMatch, getWeakETagValue, isOpenApiValidatorAvailable, isWeakETag, on, registerHandlerModule, send, sendAccepted, sendCreated, sendProblem, setETag, startAPI, toWeakETag };
|
package/dist/index.d.ts
CHANGED
|
@@ -332,6 +332,12 @@ declare const send: (response: Response, statusCode: number, options?: HttpRespo
|
|
|
332
332
|
declare const sendProblem: (response: Response, statusCode: number, options?: HttpProblemResponseOptions) => void;
|
|
333
333
|
|
|
334
334
|
type WebApiSetup = (router: Router) => void;
|
|
335
|
+
/**
|
|
336
|
+
* Options forwarded to pino-http. Typed loosely to avoid hard dependency.
|
|
337
|
+
*
|
|
338
|
+
* @see https://github.com/pinojs/pino-http
|
|
339
|
+
*/
|
|
340
|
+
type PinoHttpOptions = Record<string, unknown>;
|
|
335
341
|
type ApplicationOptions = {
|
|
336
342
|
apis?: WebApiSetup[];
|
|
337
343
|
mapError?: ErrorToProblemDetailsMapping;
|
|
@@ -339,6 +345,25 @@ type ApplicationOptions = {
|
|
|
339
345
|
disableJsonMiddleware?: boolean;
|
|
340
346
|
disableUrlEncodingMiddleware?: boolean;
|
|
341
347
|
disableProblemDetailsMiddleware?: boolean;
|
|
348
|
+
/**
|
|
349
|
+
* Optional Pino HTTP logger configuration.
|
|
350
|
+
* When true, enables pino-http with defaults.
|
|
351
|
+
* When an object, forwards the options to pino-http.
|
|
352
|
+
* Requires the 'pino-http' package to be installed.
|
|
353
|
+
*
|
|
354
|
+
* @see https://github.com/pinojs/pino-http
|
|
355
|
+
* @example
|
|
356
|
+
* ```typescript
|
|
357
|
+
* const app = await getApplication({
|
|
358
|
+
* pinoHttp: true,
|
|
359
|
+
* });
|
|
360
|
+
*
|
|
361
|
+
* const app = await getApplication({
|
|
362
|
+
* pinoHttp: { autoLogging: false },
|
|
363
|
+
* });
|
|
364
|
+
* ```
|
|
365
|
+
*/
|
|
366
|
+
pinoHttp?: boolean | PinoHttpOptions;
|
|
342
367
|
/**
|
|
343
368
|
* Optional OpenAPI validator configuration.
|
|
344
369
|
* When provided, enables request/response validation against an OpenAPI specification.
|
|
@@ -459,4 +484,4 @@ declare const ApiE2ESpecification: {
|
|
|
459
484
|
*/
|
|
460
485
|
declare const registerHandlerModule: (modulePath: string, moduleExports: any) => void;
|
|
461
486
|
|
|
462
|
-
export { Accepted, type AcceptedHttpResponseOptions, ApiE2ESpecification, type ApiE2ESpecificationAssert, ApiSpecification, type ApiSpecificationAssert, type ApplicationOptions, BadRequest, Conflict, Created, type CreatedHttpResponseOptions, DefaultHttpProblemResponseOptions, DefaultHttpResponseOptions, type E2EResponseAssert, type ETag, ETagErrors, type ErrorToProblemDetailsMapping, type FirebaseAuthSecurityOptions, Forbidden, HeaderNames, type HttpHandler, HttpProblem, type HttpProblemResponseOptions, HttpResponse, type HttpResponseOptions, type ImportedHandlerModules, NoContent, type NoContentHttpResponseOptions, NotFound, OK, type OpenAPIV3Document, type OpenApiValidatorOptions, PreconditionFailed, type ResponseAssert, type SecurityHandlers, type StartApiOptions, type TestRequest, type WeakETag, WeakETagRegex, type WebApiSetup, createFirebaseAuthSecurityHandlers, createOpenApiValidatorOptions, existingStream, expect, expectError, expectNewEvents, expectResponse, getApplication, getETagFromIfMatch, getETagFromIfNotMatch, getETagValueFromIfMatch, getWeakETagValue, isOpenApiValidatorAvailable, isWeakETag, on, registerHandlerModule, send, sendAccepted, sendCreated, sendProblem, setETag, startAPI, toWeakETag };
|
|
487
|
+
export { Accepted, type AcceptedHttpResponseOptions, ApiE2ESpecification, type ApiE2ESpecificationAssert, ApiSpecification, type ApiSpecificationAssert, type ApplicationOptions, BadRequest, Conflict, Created, type CreatedHttpResponseOptions, DefaultHttpProblemResponseOptions, DefaultHttpResponseOptions, type E2EResponseAssert, type ETag, ETagErrors, type ErrorToProblemDetailsMapping, type FirebaseAuthSecurityOptions, Forbidden, HeaderNames, type HttpHandler, HttpProblem, type HttpProblemResponseOptions, HttpResponse, type HttpResponseOptions, type ImportedHandlerModules, NoContent, type NoContentHttpResponseOptions, NotFound, OK, type OpenAPIV3Document, type OpenApiValidatorOptions, type PinoHttpOptions, PreconditionFailed, type ResponseAssert, type SecurityHandlers, type StartApiOptions, type TestRequest, type WeakETag, WeakETagRegex, type WebApiSetup, createFirebaseAuthSecurityHandlers, createOpenApiValidatorOptions, existingStream, expect, expectError, expectNewEvents, expectResponse, getApplication, getETagFromIfMatch, getETagFromIfNotMatch, getETagValueFromIfMatch, getWeakETagValue, isOpenApiValidatorAvailable, isWeakETag, on, registerHandlerModule, send, sendAccepted, sendCreated, sendProblem, setETag, startAPI, toWeakETag };
|
package/dist/index.js
CHANGED
|
@@ -48,10 +48,31 @@ var getApplication = async (options) => {
|
|
|
48
48
|
disableJsonMiddleware,
|
|
49
49
|
disableUrlEncodingMiddleware,
|
|
50
50
|
disableProblemDetailsMiddleware,
|
|
51
|
+
pinoHttp,
|
|
51
52
|
openApiValidator
|
|
52
53
|
} = options;
|
|
53
54
|
const router = Router();
|
|
54
55
|
app.set("etag", enableDefaultExpressEtag ?? false);
|
|
56
|
+
if (pinoHttp !== void 0 && pinoHttp !== false) {
|
|
57
|
+
try {
|
|
58
|
+
const require2 = createRequire(import.meta.url);
|
|
59
|
+
const mod = require2("pino-http");
|
|
60
|
+
const provider = mod.default ?? mod;
|
|
61
|
+
if (typeof provider !== "function") {
|
|
62
|
+
throw new Error("Invalid pino-http module: missing default export");
|
|
63
|
+
}
|
|
64
|
+
const options2 = pinoHttp === true ? void 0 : pinoHttp;
|
|
65
|
+
const middleware = provider(options2);
|
|
66
|
+
app.use(middleware);
|
|
67
|
+
} catch {
|
|
68
|
+
console.warn(
|
|
69
|
+
"Pino HTTP configuration provided but pino-http package is not installed. Install it with: npm install pino-http"
|
|
70
|
+
);
|
|
71
|
+
throw new Error(
|
|
72
|
+
"pino-http package is required when pinoHttp option is used"
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
55
76
|
if (!disableJsonMiddleware) app.use(express.json());
|
|
56
77
|
if (!disableUrlEncodingMiddleware)
|
|
57
78
|
app.use(
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/application.ts","../src/middlewares/problemDetailsMiddleware.ts","../src/etag.ts","../src/handler.ts","../src/openapi/firebase-auth.ts","../src/openapi/index.ts","../src/responses.ts","../src/testing/apiE2ESpecification.ts","../src/testing/apiSpecification.ts"],"sourcesContent":["import 'express-async-errors';\n\nexport * from './application';\nexport * from './etag';\nexport * from './handler';\nexport * from './openapi';\nexport * from './responses';\nexport * from './testing';\nexport { registerHandlerModule } from './internal/esm-resolver';\n","import express, {\n Router,\n type Application,\n type RequestHandler,\n} from 'express';\nimport 'express-async-errors';\nimport http from 'http';\nimport { createRequire } from 'node:module';\nimport { problemDetailsMiddleware } from './middlewares/problemDetailsMiddleware';\nimport type { OpenApiValidatorOptions } from './openapi';\nimport type { ErrorToProblemDetailsMapping } from './responses';\n\n// #region web-api-setup\nexport type WebApiSetup = (router: Router) => void;\n// #endregion web-api-setup\n\nexport type ApplicationOptions = {\n apis?: WebApiSetup[];\n mapError?: ErrorToProblemDetailsMapping;\n enableDefaultExpressEtag?: boolean;\n disableJsonMiddleware?: boolean;\n disableUrlEncodingMiddleware?: boolean;\n disableProblemDetailsMiddleware?: boolean;\n /**\n * Optional OpenAPI validator configuration.\n * When provided, enables request/response validation against an OpenAPI specification.\n * Requires the 'express-openapi-validator' package to be installed.\n *\n * @see https://github.com/cdimascio/express-openapi-validator\n * @example\n * ```typescript\n * import { getApplication, createOpenApiValidatorOptions } from '@event-driven-io/emmett-expressjs';\n *\n * type AppDeps = {\n * eventStore: EventStore;\n * messageBus: EventsPublisher;\n * };\n *\n * const app = await getApplication({\n * openApiValidator: createOpenApiValidatorOptions<AppDeps>('./openapi.yaml', {\n * validateResponses: true,\n * operationHandlers: './handlers',\n * initializeHandlers: (deps) => {\n * initializeHandlers(deps.eventStore, deps.messageBus);\n * }\n * })\n * });\n * ```\n */\n openApiValidator?: OpenApiValidatorOptions;\n};\n\nexport const getApplication = async (options: ApplicationOptions) => {\n const app: Application = express();\n\n const {\n apis,\n mapError,\n enableDefaultExpressEtag,\n disableJsonMiddleware,\n disableUrlEncodingMiddleware,\n disableProblemDetailsMiddleware,\n openApiValidator,\n } = options;\n\n const router = Router();\n\n // disabling default etag behaviour\n // to use etags in if-match and if-not-match headers\n app.set('etag', enableDefaultExpressEtag ?? false);\n\n // add json middleware\n if (!disableJsonMiddleware) app.use(express.json());\n\n // enable url encoded urls and bodies\n if (!disableUrlEncodingMiddleware)\n app.use(\n express.urlencoded({\n extended: true,\n }),\n );\n\n // add OpenAPI validator middleware if configured\n if (openApiValidator) {\n // Activate ESM resolver if operationHandlers are configured\n // This ensures handler modules are loaded via ESM import() instead of CJS require(),\n // preventing dual module loading issues when using TypeScript runtimes (tsx, ts-node)\n if (openApiValidator.operationHandlers) {\n const { activateESMResolver } = await import(\n './internal/esm-resolver.js'\n );\n activateESMResolver();\n\n // NEW: Auto-discover and import handler modules from OpenAPI spec\n const handlersBasePath =\n typeof openApiValidator.operationHandlers === 'string'\n ? openApiValidator.operationHandlers\n : openApiValidator.operationHandlers.basePath;\n\n if (handlersBasePath) {\n const { extractHandlerModules } = await import(\n './internal/openapi-parser.js'\n );\n const { importAndRegisterHandlers } = await import(\n './internal/handler-importer.js'\n );\n\n try {\n // Parse OpenAPI spec to find handler modules\n const modules = await extractHandlerModules(\n openApiValidator.apiSpec,\n handlersBasePath,\n );\n\n // Dynamically import and register all handler modules\n const importedHandlers = await importAndRegisterHandlers(modules);\n\n // Call user's initializeHandlers callback with imported modules\n if (openApiValidator.initializeHandlers) {\n await openApiValidator.initializeHandlers(importedHandlers);\n }\n } catch (error) {\n console.error('Failed to auto-import handler modules:', error);\n throw error;\n }\n }\n } else {\n // No operationHandlers, just call initializeHandlers if provided\n if (openApiValidator.initializeHandlers) {\n await openApiValidator.initializeHandlers();\n }\n }\n\n try {\n const require = createRequire(import.meta.url);\n // express-openapi-validator exports a default with .middleware (ESM/CJS compatibility)\n const mod = require('express-openapi-validator') as Record<\n string,\n unknown\n >;\n const provider = (mod.default ?? mod) as Record<string, unknown>;\n\n if (typeof provider.middleware !== 'function') {\n throw new Error(\n 'Invalid express-openapi-validator module: missing middleware export',\n );\n }\n\n // Serve OpenAPI spec if configured\n if (openApiValidator.serveSpec) {\n if (typeof openApiValidator.apiSpec === 'string') {\n // If apiSpec is a file path, serve it as a static file\n app.use(\n openApiValidator.serveSpec,\n express.static(openApiValidator.apiSpec),\n );\n } else {\n // If apiSpec is an object, serve it as JSON\n app.get(openApiValidator.serveSpec, (_req, res) => {\n res.json(openApiValidator.apiSpec);\n });\n }\n }\n\n const factory = provider.middleware as (\n opts: OpenApiValidatorOptions,\n ) => RequestHandler | RequestHandler[];\n const middleware = factory(openApiValidator);\n if (Array.isArray(middleware)) {\n for (const m of middleware) app.use(m);\n } else {\n app.use(middleware);\n }\n } catch {\n console.warn(\n 'OpenAPI validator configuration provided but express-openapi-validator package is not installed. ' +\n 'Install it with: npm install express-openapi-validator',\n );\n throw new Error(\n 'express-openapi-validator package is required when openApiValidator option is used',\n );\n }\n }\n\n // Register API routes if provided\n if (apis) {\n for (const api of apis) {\n api(router);\n }\n app.use(router);\n }\n\n // add problem details middleware\n if (!disableProblemDetailsMiddleware)\n app.use(problemDetailsMiddleware(mapError));\n\n return app;\n};\n\nexport type StartApiOptions = {\n port?: number;\n};\n\nexport const startAPI = (\n app: Application,\n options: StartApiOptions = { port: 3000 },\n) => {\n const { port } = options;\n const server = http.createServer(app);\n\n server.on('listening', () => {\n console.info('server up listening');\n });\n\n return server.listen(port);\n};\n","import type { NextFunction, Request, Response } from 'express';\nimport { ProblemDocument } from 'http-problem-details';\nimport { sendProblem, type ErrorToProblemDetailsMapping } from '..';\n\nexport const problemDetailsMiddleware =\n (mapError?: ErrorToProblemDetailsMapping) =>\n (\n error: Error,\n request: Request,\n response: Response,\n _next: NextFunction,\n ): void => {\n let problemDetails: ProblemDocument | undefined;\n\n if (mapError) problemDetails = mapError(error, request);\n\n problemDetails =\n problemDetails ?? defaultErrorToProblemDetailsMapping(error);\n\n sendProblem(response, problemDetails.status, { problem: problemDetails });\n };\n\nexport const defaultErrorToProblemDetailsMapping = (\n error: Error,\n): ProblemDocument => {\n let statusCode = 500;\n\n // Prefer standard `status` code if present (e.g., express-openapi-validator)\n const errObj = error as unknown as Record<string, unknown>;\n const maybeStatus = errObj['status'];\n if (\n typeof maybeStatus === 'number' &&\n maybeStatus >= 100 &&\n maybeStatus < 600\n ) {\n statusCode = maybeStatus;\n }\n\n const maybeErrorCode = errObj['errorCode'];\n if (\n typeof maybeErrorCode === 'number' &&\n maybeErrorCode >= 100 &&\n maybeErrorCode < 600\n ) {\n statusCode = maybeErrorCode;\n }\n\n return new ProblemDocument({\n detail: error.message,\n status: statusCode,\n });\n};\n","import { type Brand } from '@event-driven-io/emmett';\nimport type { Request, Response } from 'express';\n\n//////////////////////////////////////\n/// ETAG\n//////////////////////////////////////\n\nexport const HeaderNames = {\n IF_MATCH: 'if-match',\n IF_NOT_MATCH: 'if-not-match',\n ETag: 'etag',\n};\n\nexport type WeakETag = Brand<`W/${string}`, 'ETag'>;\nexport type ETag = Brand<string, 'ETag'>;\n\nexport const WeakETagRegex = /W\\/\"(-?\\d+.*)\"/;\n\nexport const enum ETagErrors {\n WRONG_WEAK_ETAG_FORMAT = 'WRONG_WEAK_ETAG_FORMAT',\n MISSING_IF_MATCH_HEADER = 'MISSING_IF_MATCH_HEADER',\n MISSING_IF_NOT_MATCH_HEADER = 'MISSING_IF_NOT_MATCH_HEADER',\n}\n\nexport const isWeakETag = (etag: ETag): etag is WeakETag => {\n return WeakETagRegex.test(etag as string);\n};\n\nexport const getWeakETagValue = (etag: ETag): string => {\n const result = WeakETagRegex.exec(etag as string);\n if (result === null || result.length === 0) {\n throw new Error(ETagErrors.WRONG_WEAK_ETAG_FORMAT);\n }\n return result[1]!;\n};\n\nexport const toWeakETag = (value: number | bigint | string): WeakETag => {\n return `W/\"${value}\"` as WeakETag;\n};\n\nexport const getETagFromIfMatch = (request: Request): ETag => {\n const etag = request.headers[HeaderNames.IF_MATCH];\n\n if (etag === undefined) {\n throw new Error(ETagErrors.MISSING_IF_MATCH_HEADER);\n }\n\n return etag as ETag;\n};\n\nexport const getETagFromIfNotMatch = (request: Request): ETag => {\n const etag = request.headers[HeaderNames.IF_NOT_MATCH];\n\n if (etag === undefined) {\n throw new Error(ETagErrors.MISSING_IF_MATCH_HEADER);\n }\n\n return (Array.isArray(etag) ? etag[0] : etag) as ETag;\n};\n\nexport const setETag = (response: Response, etag: ETag): void => {\n response.setHeader(HeaderNames.ETag, etag as string);\n};\n\nexport const getETagValueFromIfMatch = (request: Request): string => {\n const eTagValue: ETag = getETagFromIfMatch(request);\n\n return isWeakETag(eTagValue)\n ? getWeakETagValue(eTagValue)\n : (eTagValue as string);\n};\n","import { type NextFunction, type Request, type Response } from 'express';\nimport {\n send,\n sendAccepted,\n sendCreated,\n sendProblem,\n type AcceptedHttpResponseOptions,\n type CreatedHttpResponseOptions,\n type HttpProblemResponseOptions,\n type HttpResponseOptions,\n type NoContentHttpResponseOptions,\n} from '.';\n\n// #region httpresponse-on\nexport type HttpResponse = (response: Response) => void;\n\nexport type HttpHandler<RequestType extends Request> = (\n request: RequestType,\n) => Promise<HttpResponse> | HttpResponse;\n\nexport const on =\n <RequestType extends Request>(handle: HttpHandler<RequestType>) =>\n async (\n request: RequestType,\n response: Response,\n _next: NextFunction,\n ): Promise<void> => {\n const setResponse = await Promise.resolve(handle(request));\n\n return setResponse(response);\n };\n// #endregion httpresponse-on\n\nexport const OK =\n (options?: HttpResponseOptions): HttpResponse =>\n (response: Response) => {\n send(response, 200, options);\n };\n\nexport const Created =\n (options: CreatedHttpResponseOptions): HttpResponse =>\n (response: Response) => {\n sendCreated(response, options);\n };\n\nexport const Accepted =\n (options: AcceptedHttpResponseOptions): HttpResponse =>\n (response: Response) => {\n sendAccepted(response, options);\n };\n\nexport const NoContent = (\n options?: NoContentHttpResponseOptions,\n): HttpResponse => HttpResponse(204, options);\n\nexport const HttpResponse =\n (statusCode: number, options?: HttpResponseOptions): HttpResponse =>\n (response: Response) => {\n send(response, statusCode, options);\n };\n\n/////////////////////\n// ERRORS\n/////////////////////\n\nexport const BadRequest = (\n options?: HttpProblemResponseOptions,\n): HttpResponse => HttpProblem(400, options);\n\nexport const Forbidden = (options?: HttpProblemResponseOptions): HttpResponse =>\n HttpProblem(403, options);\n\nexport const NotFound = (options?: HttpProblemResponseOptions): HttpResponse =>\n HttpProblem(404, options);\n\nexport const Conflict = (options?: HttpProblemResponseOptions): HttpResponse =>\n HttpProblem(409, options);\n\nexport const PreconditionFailed = (\n options: HttpProblemResponseOptions,\n): HttpResponse => HttpProblem(412, options);\n\nexport const HttpProblem =\n (statusCode: number, options?: HttpProblemResponseOptions): HttpResponse =>\n (response: Response) => {\n sendProblem(response, statusCode, options);\n };\n","import type { SecurityHandlers } from './index';\n\ntype AuthClient = {\n verifyIdToken: (token: string) => Promise<unknown>;\n};\n\nexport type FirebaseAuthSecurityOptions = {\n /**\n * Name of the OpenAPI security scheme to attach the handler to.\n * Defaults to \"bearerAuth\".\n */\n securitySchemeName?: string;\n /**\n * Custom auth client for tests or alternate Firebase auth instances.\n */\n authClient?: AuthClient;\n /**\n * Token claim used for role-based checks when scopes are defined.\n * Defaults to \"roles\".\n */\n roleClaim?: string;\n};\n\ntype FirebaseAuthModule = {\n firebaseAuthMiddleware: (options?: { authClient?: AuthClient }) => (\n req: unknown,\n res: unknown,\n next: () => void,\n ) => Promise<void> | void;\n};\n\nconst loadFirebaseAuth = async (): Promise<FirebaseAuthModule> => {\n try {\n const mod = await import('@my-f-startup/firebase-auth-express');\n const provider = (mod as unknown as Record<string, unknown>).default ?? mod;\n const firebaseAuthMiddleware =\n (provider as Record<string, unknown>).firebaseAuthMiddleware;\n\n if (typeof firebaseAuthMiddleware !== 'function') {\n throw new Error(\n 'Invalid @my-f-startup/firebase-auth-express module: missing firebaseAuthMiddleware export',\n );\n }\n\n return provider as FirebaseAuthModule;\n } catch (error) {\n const message =\n '@my-f-startup/firebase-auth-express is required for createFirebaseAuthSecurityHandlers. ' +\n 'Install it with: npm install @my-f-startup/firebase-auth-express';\n throw new Error(message, { cause: error as Error });\n }\n};\n\nconst createNullResponse = () => {\n const res: Record<string, unknown> = {};\n res.status = () => res;\n res.json = () => res;\n res.send = () => res;\n res.end = () => res;\n res.set = () => res;\n return res;\n};\n\nconst runMiddleware = async (\n middleware: (req: unknown, res: unknown, next: () => void) => Promise<void> | void,\n req: unknown,\n): Promise<boolean> => {\n return new Promise((resolve) => {\n let nextCalled = false;\n const res = createNullResponse();\n const next = () => {\n nextCalled = true;\n resolve(true);\n };\n\n Promise.resolve(middleware(req, res, next))\n .then(() => {\n if (!nextCalled) resolve(false);\n })\n .catch(() => resolve(false));\n });\n};\n\nexport const createFirebaseAuthSecurityHandlers = (\n options: FirebaseAuthSecurityOptions = {},\n): SecurityHandlers => {\n const securitySchemeName = options.securitySchemeName ?? 'bearerAuth';\n const roleClaim = options.roleClaim ?? 'roles';\n\n return {\n [securitySchemeName]: async (req, scopes, _schema) => {\n const { firebaseAuthMiddleware } = await loadFirebaseAuth();\n const middleware = firebaseAuthMiddleware({\n authClient: options.authClient,\n });\n\n const isAuthenticated = await runMiddleware(middleware, req);\n if (!isAuthenticated) return false;\n\n if (!scopes.length) return true;\n\n const roles = (req as Record<string, any>)?.auth?.token?.[roleClaim];\n if (!Array.isArray(roles)) return false;\n\n return scopes.every((scope: string) => roles.includes(scope));\n },\n };\n};\n","/**\n * OpenAPI v3 Document type (to avoid requiring express-openapi-validator types directly)\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type OpenAPIV3Document = any;\n\n/**\n * Imported handler modules, keyed by module name.\n * Automatically populated by the framework when operationHandlers is configured.\n */\nexport type ImportedHandlerModules = Record<string, any>;\n\n/**\n * Security handlers for custom authentication/authorization logic.\n * Maps security scheme names to handler functions.\n *\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/usage-validate-security/\n */\n\nexport type SecurityHandlers = Record<\n string,\n (req: any, scopes: string[], schema: any) => boolean | Promise<boolean>\n>;\n\nexport * from './firebase-auth';\n\n/**\n * Configuration options for express-openapi-validator middleware.\n * This allows optional validation of API requests and responses against an OpenAPI specification.\n *\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/\n */\nexport type OpenApiValidatorOptions = {\n /**\n * Path to the OpenAPI specification file (JSON or YAML)\n * or an OpenAPI specification object.\n */\n apiSpec: string | OpenAPIV3Document;\n\n /**\n * Determines whether the validator should validate requests.\n * Can be a boolean or an object with detailed request validation options.\n * @default true\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/usage-validate-requests/\n */\n validateRequests?:\n | boolean\n | {\n /**\n * Allow unknown query parameters (not defined in the spec).\n * @default false\n */\n allowUnknownQueryParameters?: boolean;\n /**\n * Coerce types in request parameters.\n * @default true\n */\n coerceTypes?: boolean | 'array';\n /**\n * Remove additional properties not defined in the spec.\n * @default false\n */\n removeAdditional?: boolean | 'all' | 'failing';\n };\n\n /**\n * Determines whether the validator should validate responses.\n * Can be a boolean or an object with detailed response validation options.\n * @default false\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/usage-validate-responses/\n */\n validateResponses?:\n | boolean\n | {\n /**\n * Remove additional properties from responses not defined in the spec.\n * @default false\n */\n removeAdditional?: boolean | 'all' | 'failing';\n /**\n * Coerce types in responses.\n * @default true\n */\n coerceTypes?: boolean;\n /**\n * Callback to handle response validation errors.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onError?: (error: any, body: any, req: any) => void;\n };\n\n /**\n * Determines whether the validator should validate security.\n * Can be a boolean or an object with security handlers.\n * @default true\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/usage-validate-security/\n */\n validateSecurity?:\n | boolean\n | {\n /**\n * Custom security handlers for authentication/authorization.\n */\n handlers?: SecurityHandlers;\n };\n\n /**\n * Defines how the validator should validate formats.\n * When true, uses ajv-formats for format validation.\n * When false, format validation is disabled.\n * Can also be 'fast' or 'full' for different validation modes.\n * @default true\n */\n validateFormats?: boolean | 'fast' | 'full';\n\n /**\n * The base path to the operation handlers directory.\n * When set to a path, automatically wires OpenAPI operations to handler functions\n * based on operationId or x-eov-operation-id.\n * When false, operation handlers are disabled (manual routing required).\n * @default false\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/guide-operation-handlers/\n */\n operationHandlers?:\n | string\n | false\n | {\n /**\n * Base path to operation handlers directory.\n */\n basePath?: string;\n /**\n * Resolver function to map operationId to handler module path.\n */\n resolver?: (\n handlersPath: string,\n route: string,\n apiDoc: OpenAPIV3Document,\n ) => string;\n };\n\n /**\n * Paths or pattern to ignore during validation.\n * @default undefined\n */\n ignorePaths?: RegExp | ((path: string) => boolean);\n\n /**\n * Validate the OpenAPI specification itself.\n * @default true\n */\n validateApiSpec?: boolean;\n\n /**\n * $ref parser configuration for handling OpenAPI references.\n * @default undefined\n */\n $refParser?: {\n mode: 'bundle' | 'dereference';\n };\n\n /**\n * Serve the OpenAPI specification at a specific path.\n * When set to a string, the spec will be served at that path.\n * When false, the spec will not be served.\n * @default false\n * @example '/api-docs/openapi.json'\n */\n serveSpec?: string | false;\n\n /**\n * File upload configuration options.\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/usage-file-uploads/\n */\n fileUploader?:\n | boolean\n | {\n /**\n * Destination directory for uploaded files.\n */\n dest?: string;\n /**\n * File size limit in bytes.\n */\n limits?: {\n fileSize?: number;\n files?: number;\n };\n };\n\n /**\n * Optional callback to initialize operation handlers with dependencies.\n * Called before the OpenAPI validator middleware is configured.\n *\n * The framework automatically imports handler modules referenced in your\n * OpenAPI spec (via x-eov-operation-handler) and passes them as the first parameter.\n *\n * @param handlers - Auto-imported handler modules, keyed by module name\n * @returns void or a Promise that resolves when initialization is complete\n *\n * @example\n * ```typescript\n * // With automatic import (recommended)\n * initializeHandlers: async (handlers) => {\n * handlers.shoppingCarts.initializeHandlers(eventStore, messageBus, getUnitPrice, getCurrentTime);\n * }\n *\n * // Manual import (still supported for backward compatibility)\n * import * as handlersModule from './handlers/shoppingCarts';\n * import { registerHandlerModule } from '@emmett-community/emmett-expressjs-with-openapi';\n * initializeHandlers: () => {\n * const handlersPath = path.join(__dirname, './handlers/shoppingCarts');\n * registerHandlerModule(handlersPath, handlersModule);\n * handlersModule.initializeHandlers(eventStore, messageBus, getUnitPrice, getCurrentTime);\n * }\n * ```\n */\n initializeHandlers?: (\n handlers?: ImportedHandlerModules,\n ) => void | Promise<void>;\n};\n\n/**\n * Helper function to create OpenAPI validator configuration with sensible defaults.\n *\n * @param apiSpec - Path to OpenAPI spec file or OpenAPI document object\n * @param options - Additional validator options\n * @returns Complete OpenApiValidatorOptions configuration\n *\n * @example\n * ```typescript\n * // Basic usage with default options\n * const validatorOptions = createOpenApiValidatorOptions('./openapi.yaml');\n *\n * // With response validation enabled\n * const validatorOptions = createOpenApiValidatorOptions('./openapi.yaml', {\n * validateResponses: true\n * });\n *\n * // With custom security handlers\n * const validatorOptions = createOpenApiValidatorOptions('./openapi.yaml', {\n * validateSecurity: {\n * handlers: {\n * bearerAuth: async (req, scopes) => {\n * // Custom authentication logic\n * return true;\n * }\n * }\n * }\n * });\n *\n * // Serving the spec at /api-docs\n * const validatorOptions = createOpenApiValidatorOptions('./openapi.yaml', {\n * serveSpec: '/api-docs/openapi.json'\n * });\n *\n * // With dependency injection for operation handlers\n * type ShoppingCartDeps = {\n * eventStore: EventStore;\n * messageBus: EventsPublisher;\n * getUnitPrice: (productId: string) => Promise<number>;\n * getCurrentTime: () => Date;\n * };\n *\n * const validatorOptions = createOpenApiValidatorOptions<ShoppingCartDeps>(\n * './openapi.yaml',\n * {\n * operationHandlers: './handlers',\n * initializeHandlers: (deps) => {\n * initializeHandlers(\n * deps.eventStore,\n * deps.messageBus,\n * deps.getUnitPrice,\n * deps.getCurrentTime\n * );\n * }\n * }\n * );\n *\n * const app = getApplication({\n * apis: [myApi],\n * openApiValidator: validatorOptions\n * });\n * ```\n */\nexport const createOpenApiValidatorOptions = (\n apiSpec: string | OpenAPIV3Document,\n options?: Partial<Omit<OpenApiValidatorOptions, 'apiSpec'>>,\n): OpenApiValidatorOptions => {\n return {\n apiSpec,\n validateRequests: options?.validateRequests ?? true,\n validateResponses: options?.validateResponses ?? false,\n validateSecurity: options?.validateSecurity ?? true,\n validateFormats: options?.validateFormats ?? true,\n operationHandlers: options?.operationHandlers,\n ignorePaths: options?.ignorePaths,\n validateApiSpec: options?.validateApiSpec ?? true,\n $refParser: options?.$refParser,\n serveSpec: options?.serveSpec ?? false,\n fileUploader: options?.fileUploader,\n initializeHandlers: options?.initializeHandlers,\n };\n};\n\n/**\n * Type guard to check if express-openapi-validator is available\n */\nexport const isOpenApiValidatorAvailable = async (): Promise<boolean> => {\n try {\n await import('express-openapi-validator');\n return true;\n } catch {\n return false;\n }\n};\n","import { type Request, type Response } from 'express';\nimport { ProblemDocument } from 'http-problem-details';\nimport { setETag, type ETag } from './etag';\n\nexport type ErrorToProblemDetailsMapping = (\n error: Error,\n request: Request,\n) => ProblemDocument | undefined;\n\nexport type HttpResponseOptions = {\n body?: unknown;\n location?: string;\n eTag?: ETag;\n};\nexport const DefaultHttpResponseOptions: HttpResponseOptions = {};\n\nexport type HttpProblemResponseOptions = {\n location?: string;\n eTag?: ETag;\n} & Omit<HttpResponseOptions, 'body'> &\n (\n | {\n problem: ProblemDocument;\n }\n | { problemDetails: string }\n );\nexport const DefaultHttpProblemResponseOptions: HttpProblemResponseOptions = {\n problemDetails: 'Error occured!',\n};\n\nexport type CreatedHttpResponseOptions = (\n | {\n createdId: string;\n }\n | {\n createdId?: string;\n url: string;\n }\n) &\n HttpResponseOptions;\n\nexport const sendCreated = (\n response: Response,\n { eTag, ...options }: CreatedHttpResponseOptions,\n): void =>\n send(response, 201, {\n location:\n 'url' in options\n ? options.url\n : `${response.req.url}/${options.createdId}`,\n body: 'createdId' in options ? { id: options.createdId } : undefined,\n eTag,\n });\n\nexport type AcceptedHttpResponseOptions = {\n location: string;\n} & HttpResponseOptions;\n\nexport const sendAccepted = (\n response: Response,\n options: AcceptedHttpResponseOptions,\n): void => send(response, 202, options);\n\nexport type NoContentHttpResponseOptions = Omit<HttpResponseOptions, 'body'>;\n\nexport const send = (\n response: Response,\n statusCode: number,\n options?: HttpResponseOptions,\n): void => {\n const { location, body, eTag } = options ?? DefaultHttpResponseOptions;\n // HEADERS\n if (eTag) setETag(response, eTag);\n if (location) response.setHeader('Location', location);\n\n if (body) {\n response.statusCode = statusCode;\n response.send(body);\n } else {\n response.sendStatus(statusCode);\n }\n};\n\nexport const sendProblem = (\n response: Response,\n statusCode: number,\n options?: HttpProblemResponseOptions,\n): void => {\n options = options ?? DefaultHttpProblemResponseOptions;\n\n const { location, eTag } = options;\n\n const problemDetails =\n 'problem' in options\n ? options.problem\n : new ProblemDocument({\n detail: options.problemDetails,\n status: statusCode,\n });\n\n // HEADERS\n if (eTag) setETag(response, eTag);\n if (location) response.setHeader('Location', location);\n\n response.setHeader('Content-Type', 'application/problem+json');\n\n response.statusCode = statusCode;\n response.json(problemDetails);\n};\n","import supertest, { type Response } from 'supertest';\n\nimport type { EventStore } from '@event-driven-io/emmett';\nimport assert from 'assert';\nimport type { Application } from 'express';\nimport type { TestRequest } from './apiSpecification';\n\nexport type E2EResponseAssert = (response: Response) => boolean | void;\n\nexport type ApiE2ESpecificationAssert = [E2EResponseAssert];\n\nexport type ApiE2ESpecification = (...givenRequests: TestRequest[]) => {\n when: (setupRequest: TestRequest) => {\n then: (verify: ApiE2ESpecificationAssert) => Promise<void>;\n };\n};\n\nexport const ApiE2ESpecification = {\n for: <Store extends EventStore = EventStore>(\n getEventStore: () => Store,\n getApplication: (eventStore: Store) => Application | Promise<Application>,\n ): ApiE2ESpecification => {\n {\n return (...givenRequests: TestRequest[]) => {\n const eventStore = getEventStore();\n\n return {\n when: (setupRequest: TestRequest) => {\n const handle = async () => {\n const application = await Promise.resolve(getApplication(eventStore));\n\n for (const requestFn of givenRequests) {\n await requestFn(supertest(application));\n }\n\n return setupRequest(supertest(application));\n };\n\n return {\n then: async (\n verify: ApiE2ESpecificationAssert,\n ): Promise<void> => {\n const response = await handle();\n\n verify.forEach((assertion) => {\n const succeeded = assertion(response);\n\n if (succeeded === false) assert.fail();\n });\n },\n };\n },\n };\n };\n }\n },\n};\n","import {\n WrapEventStore,\n assertEqual,\n assertFails,\n assertMatches,\n type Event,\n type EventStore,\n type TestEventStream,\n} from '@event-driven-io/emmett';\nimport { type Application } from 'express';\nimport type { ProblemDocument } from 'http-problem-details';\nimport type { Response, Test } from 'supertest';\nimport supertest from 'supertest';\nimport type TestAgent from 'supertest/lib/agent';\n\n////////////////////////////////\n/////////// Setup\n////////////////////////////////\n\nexport type TestRequest = (request: TestAgent<supertest.Test>) => Test;\n\nexport const existingStream = <EventType extends Event = Event>(\n streamId: string,\n events: EventType[],\n): TestEventStream<EventType> => {\n return [streamId, events];\n};\n\n////////////////////////////////\n/////////// Asserts\n////////////////////////////////\n\nexport type ResponseAssert = (response: Response) => boolean | void;\n\nexport type ApiSpecificationAssert<EventType extends Event = Event> =\n | TestEventStream<EventType>[]\n | ResponseAssert\n | [ResponseAssert, ...TestEventStream<EventType>[]];\n\nexport const expect = <EventType extends Event = Event>(\n streamId: string,\n events: EventType[],\n): TestEventStream<EventType> => {\n return [streamId, events];\n};\n\nexport const expectNewEvents = <EventType extends Event = Event>(\n streamId: string,\n events: EventType[],\n): TestEventStream<EventType> => {\n return [streamId, events];\n};\n\nexport const expectResponse =\n <Body = unknown>(\n statusCode: number,\n options?: { body?: Body; headers?: { [index: string]: string } },\n ) =>\n (response: Response): void => {\n const { body, headers } = options ?? {};\n assertEqual(statusCode, response.statusCode, \"Response code doesn't match\");\n if (body) assertMatches(response.body, body);\n if (headers) assertMatches(response.headers, headers);\n };\n\nexport const expectError = (\n errorCode: number,\n problemDetails?: Partial<ProblemDocument>,\n) =>\n expectResponse(\n errorCode,\n problemDetails ? { body: problemDetails } : undefined,\n );\n\n////////////////////////////////\n/////////// Api Specification\n////////////////////////////////\n\nexport type ApiSpecification<EventType extends Event = Event> = (\n ...givenStreams: TestEventStream<EventType>[]\n) => {\n when: (setupRequest: TestRequest) => {\n then: (verify: ApiSpecificationAssert<EventType>) => Promise<void>;\n };\n};\n\nexport const ApiSpecification = {\n for: <\n EventType extends Event = Event,\n Store extends EventStore<import('@event-driven-io/emmett').ReadEventMetadataWithGlobalPosition> = EventStore<import('@event-driven-io/emmett').ReadEventMetadataWithGlobalPosition>\n >(\n getEventStore: () => Store,\n getApplication: (eventStore: Store) => Application | Promise<Application>,\n ): ApiSpecification<EventType> => {\n {\n return (...givenStreams: TestEventStream<EventType>[]) => {\n const eventStore = WrapEventStore(getEventStore());\n\n return {\n when: (setupRequest: TestRequest) => {\n const handle = async () => {\n const application = await Promise.resolve(getApplication(eventStore));\n\n for (const [streamName, events] of givenStreams) {\n await eventStore.setup(streamName, events);\n }\n\n return setupRequest(supertest(application));\n };\n\n return {\n then: async (\n verify: ApiSpecificationAssert<EventType>,\n ): Promise<void> => {\n const response = await handle();\n\n if (typeof verify === 'function') {\n const succeeded = verify(response);\n\n if (succeeded === false) assertFails();\n } else if (Array.isArray(verify)) {\n const [first, ...rest] = verify;\n\n if (typeof first === 'function') {\n const succeeded = first(response);\n\n if (succeeded === false) assertFails();\n }\n\n const events = typeof first === 'function' ? rest : verify;\n\n assertMatches(\n Array.from(eventStore.appendedEvents.values()),\n events,\n );\n }\n },\n };\n },\n };\n };\n }\n },\n};\n"],"mappings":"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","names":["require","ETagErrors","ProblemDocument","ProblemDocument","getApplication","supertest","getApplication"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/application.ts","../src/middlewares/problemDetailsMiddleware.ts","../src/etag.ts","../src/handler.ts","../src/openapi/firebase-auth.ts","../src/openapi/index.ts","../src/responses.ts","../src/testing/apiE2ESpecification.ts","../src/testing/apiSpecification.ts"],"sourcesContent":["import 'express-async-errors';\n\nexport * from './application';\nexport * from './etag';\nexport * from './handler';\nexport * from './openapi';\nexport * from './responses';\nexport * from './testing';\nexport { registerHandlerModule } from './internal/esm-resolver';\n","import express, {\n Router,\n type Application,\n type RequestHandler,\n} from 'express';\nimport 'express-async-errors';\nimport http from 'http';\nimport { createRequire } from 'node:module';\nimport { problemDetailsMiddleware } from './middlewares/problemDetailsMiddleware';\nimport type { OpenApiValidatorOptions } from './openapi';\nimport type { ErrorToProblemDetailsMapping } from './responses';\n\n// #region web-api-setup\nexport type WebApiSetup = (router: Router) => void;\n// #endregion web-api-setup\n\n/**\n * Options forwarded to pino-http. Typed loosely to avoid hard dependency.\n *\n * @see https://github.com/pinojs/pino-http\n */\nexport type PinoHttpOptions = Record<string, unknown>;\n\nexport type ApplicationOptions = {\n apis?: WebApiSetup[];\n mapError?: ErrorToProblemDetailsMapping;\n enableDefaultExpressEtag?: boolean;\n disableJsonMiddleware?: boolean;\n disableUrlEncodingMiddleware?: boolean;\n disableProblemDetailsMiddleware?: boolean;\n /**\n * Optional Pino HTTP logger configuration.\n * When true, enables pino-http with defaults.\n * When an object, forwards the options to pino-http.\n * Requires the 'pino-http' package to be installed.\n *\n * @see https://github.com/pinojs/pino-http\n * @example\n * ```typescript\n * const app = await getApplication({\n * pinoHttp: true,\n * });\n *\n * const app = await getApplication({\n * pinoHttp: { autoLogging: false },\n * });\n * ```\n */\n pinoHttp?: boolean | PinoHttpOptions;\n /**\n * Optional OpenAPI validator configuration.\n * When provided, enables request/response validation against an OpenAPI specification.\n * Requires the 'express-openapi-validator' package to be installed.\n *\n * @see https://github.com/cdimascio/express-openapi-validator\n * @example\n * ```typescript\n * import { getApplication, createOpenApiValidatorOptions } from '@event-driven-io/emmett-expressjs';\n *\n * type AppDeps = {\n * eventStore: EventStore;\n * messageBus: EventsPublisher;\n * };\n *\n * const app = await getApplication({\n * openApiValidator: createOpenApiValidatorOptions<AppDeps>('./openapi.yaml', {\n * validateResponses: true,\n * operationHandlers: './handlers',\n * initializeHandlers: (deps) => {\n * initializeHandlers(deps.eventStore, deps.messageBus);\n * }\n * })\n * });\n * ```\n */\n openApiValidator?: OpenApiValidatorOptions;\n};\n\nexport const getApplication = async (options: ApplicationOptions) => {\n const app: Application = express();\n\n const {\n apis,\n mapError,\n enableDefaultExpressEtag,\n disableJsonMiddleware,\n disableUrlEncodingMiddleware,\n disableProblemDetailsMiddleware,\n pinoHttp,\n openApiValidator,\n } = options;\n\n const router = Router();\n\n // disabling default etag behaviour\n // to use etags in if-match and if-not-match headers\n app.set('etag', enableDefaultExpressEtag ?? false);\n\n // add Pino HTTP logger middleware if configured\n if (pinoHttp !== undefined && pinoHttp !== false) {\n try {\n const require = createRequire(import.meta.url);\n const mod = require('pino-http') as Record<string, unknown>;\n const provider = (mod.default ?? mod) as unknown;\n\n if (typeof provider !== 'function') {\n throw new Error('Invalid pino-http module: missing default export');\n }\n\n const options = pinoHttp === true ? undefined : pinoHttp;\n const middleware = (\n provider as (opts?: PinoHttpOptions) => RequestHandler\n )(options);\n app.use(middleware);\n } catch {\n console.warn(\n 'Pino HTTP configuration provided but pino-http package is not installed. ' +\n 'Install it with: npm install pino-http',\n );\n throw new Error(\n 'pino-http package is required when pinoHttp option is used',\n );\n }\n }\n\n // add json middleware\n if (!disableJsonMiddleware) app.use(express.json());\n\n // enable url encoded urls and bodies\n if (!disableUrlEncodingMiddleware)\n app.use(\n express.urlencoded({\n extended: true,\n }),\n );\n\n // add OpenAPI validator middleware if configured\n if (openApiValidator) {\n // Activate ESM resolver if operationHandlers are configured\n // This ensures handler modules are loaded via ESM import() instead of CJS require(),\n // preventing dual module loading issues when using TypeScript runtimes (tsx, ts-node)\n if (openApiValidator.operationHandlers) {\n const { activateESMResolver } = await import(\n './internal/esm-resolver.js'\n );\n activateESMResolver();\n\n // NEW: Auto-discover and import handler modules from OpenAPI spec\n const handlersBasePath =\n typeof openApiValidator.operationHandlers === 'string'\n ? openApiValidator.operationHandlers\n : openApiValidator.operationHandlers.basePath;\n\n if (handlersBasePath) {\n const { extractHandlerModules } = await import(\n './internal/openapi-parser.js'\n );\n const { importAndRegisterHandlers } = await import(\n './internal/handler-importer.js'\n );\n\n try {\n // Parse OpenAPI spec to find handler modules\n const modules = await extractHandlerModules(\n openApiValidator.apiSpec,\n handlersBasePath,\n );\n\n // Dynamically import and register all handler modules\n const importedHandlers = await importAndRegisterHandlers(modules);\n\n // Call user's initializeHandlers callback with imported modules\n if (openApiValidator.initializeHandlers) {\n await openApiValidator.initializeHandlers(importedHandlers);\n }\n } catch (error) {\n console.error('Failed to auto-import handler modules:', error);\n throw error;\n }\n }\n } else {\n // No operationHandlers, just call initializeHandlers if provided\n if (openApiValidator.initializeHandlers) {\n await openApiValidator.initializeHandlers();\n }\n }\n\n try {\n const require = createRequire(import.meta.url);\n // express-openapi-validator exports a default with .middleware (ESM/CJS compatibility)\n const mod = require('express-openapi-validator') as Record<\n string,\n unknown\n >;\n const provider = (mod.default ?? mod) as Record<string, unknown>;\n\n if (typeof provider.middleware !== 'function') {\n throw new Error(\n 'Invalid express-openapi-validator module: missing middleware export',\n );\n }\n\n // Serve OpenAPI spec if configured\n if (openApiValidator.serveSpec) {\n if (typeof openApiValidator.apiSpec === 'string') {\n // If apiSpec is a file path, serve it as a static file\n app.use(\n openApiValidator.serveSpec,\n express.static(openApiValidator.apiSpec),\n );\n } else {\n // If apiSpec is an object, serve it as JSON\n app.get(openApiValidator.serveSpec, (_req, res) => {\n res.json(openApiValidator.apiSpec);\n });\n }\n }\n\n const factory = provider.middleware as (\n opts: OpenApiValidatorOptions,\n ) => RequestHandler | RequestHandler[];\n const middleware = factory(openApiValidator);\n if (Array.isArray(middleware)) {\n for (const m of middleware) app.use(m);\n } else {\n app.use(middleware);\n }\n } catch {\n console.warn(\n 'OpenAPI validator configuration provided but express-openapi-validator package is not installed. ' +\n 'Install it with: npm install express-openapi-validator',\n );\n throw new Error(\n 'express-openapi-validator package is required when openApiValidator option is used',\n );\n }\n }\n\n // Register API routes if provided\n if (apis) {\n for (const api of apis) {\n api(router);\n }\n app.use(router);\n }\n\n // add problem details middleware\n if (!disableProblemDetailsMiddleware)\n app.use(problemDetailsMiddleware(mapError));\n\n return app;\n};\n\nexport type StartApiOptions = {\n port?: number;\n};\n\nexport const startAPI = (\n app: Application,\n options: StartApiOptions = { port: 3000 },\n) => {\n const { port } = options;\n const server = http.createServer(app);\n\n server.on('listening', () => {\n console.info('server up listening');\n });\n\n return server.listen(port);\n};\n","import type { NextFunction, Request, Response } from 'express';\nimport { ProblemDocument } from 'http-problem-details';\nimport { sendProblem, type ErrorToProblemDetailsMapping } from '..';\n\nexport const problemDetailsMiddleware =\n (mapError?: ErrorToProblemDetailsMapping) =>\n (\n error: Error,\n request: Request,\n response: Response,\n _next: NextFunction,\n ): void => {\n let problemDetails: ProblemDocument | undefined;\n\n if (mapError) problemDetails = mapError(error, request);\n\n problemDetails =\n problemDetails ?? defaultErrorToProblemDetailsMapping(error);\n\n sendProblem(response, problemDetails.status, { problem: problemDetails });\n };\n\nexport const defaultErrorToProblemDetailsMapping = (\n error: Error,\n): ProblemDocument => {\n let statusCode = 500;\n\n // Prefer standard `status` code if present (e.g., express-openapi-validator)\n const errObj = error as unknown as Record<string, unknown>;\n const maybeStatus = errObj['status'];\n if (\n typeof maybeStatus === 'number' &&\n maybeStatus >= 100 &&\n maybeStatus < 600\n ) {\n statusCode = maybeStatus;\n }\n\n const maybeErrorCode = errObj['errorCode'];\n if (\n typeof maybeErrorCode === 'number' &&\n maybeErrorCode >= 100 &&\n maybeErrorCode < 600\n ) {\n statusCode = maybeErrorCode;\n }\n\n return new ProblemDocument({\n detail: error.message,\n status: statusCode,\n });\n};\n","import { type Brand } from '@event-driven-io/emmett';\nimport type { Request, Response } from 'express';\n\n//////////////////////////////////////\n/// ETAG\n//////////////////////////////////////\n\nexport const HeaderNames = {\n IF_MATCH: 'if-match',\n IF_NOT_MATCH: 'if-not-match',\n ETag: 'etag',\n};\n\nexport type WeakETag = Brand<`W/${string}`, 'ETag'>;\nexport type ETag = Brand<string, 'ETag'>;\n\nexport const WeakETagRegex = /W\\/\"(-?\\d+.*)\"/;\n\nexport const enum ETagErrors {\n WRONG_WEAK_ETAG_FORMAT = 'WRONG_WEAK_ETAG_FORMAT',\n MISSING_IF_MATCH_HEADER = 'MISSING_IF_MATCH_HEADER',\n MISSING_IF_NOT_MATCH_HEADER = 'MISSING_IF_NOT_MATCH_HEADER',\n}\n\nexport const isWeakETag = (etag: ETag): etag is WeakETag => {\n return WeakETagRegex.test(etag as string);\n};\n\nexport const getWeakETagValue = (etag: ETag): string => {\n const result = WeakETagRegex.exec(etag as string);\n if (result === null || result.length === 0) {\n throw new Error(ETagErrors.WRONG_WEAK_ETAG_FORMAT);\n }\n return result[1]!;\n};\n\nexport const toWeakETag = (value: number | bigint | string): WeakETag => {\n return `W/\"${value}\"` as WeakETag;\n};\n\nexport const getETagFromIfMatch = (request: Request): ETag => {\n const etag = request.headers[HeaderNames.IF_MATCH];\n\n if (etag === undefined) {\n throw new Error(ETagErrors.MISSING_IF_MATCH_HEADER);\n }\n\n return etag as ETag;\n};\n\nexport const getETagFromIfNotMatch = (request: Request): ETag => {\n const etag = request.headers[HeaderNames.IF_NOT_MATCH];\n\n if (etag === undefined) {\n throw new Error(ETagErrors.MISSING_IF_MATCH_HEADER);\n }\n\n return (Array.isArray(etag) ? etag[0] : etag) as ETag;\n};\n\nexport const setETag = (response: Response, etag: ETag): void => {\n response.setHeader(HeaderNames.ETag, etag as string);\n};\n\nexport const getETagValueFromIfMatch = (request: Request): string => {\n const eTagValue: ETag = getETagFromIfMatch(request);\n\n return isWeakETag(eTagValue)\n ? getWeakETagValue(eTagValue)\n : (eTagValue as string);\n};\n","import { type NextFunction, type Request, type Response } from 'express';\nimport {\n send,\n sendAccepted,\n sendCreated,\n sendProblem,\n type AcceptedHttpResponseOptions,\n type CreatedHttpResponseOptions,\n type HttpProblemResponseOptions,\n type HttpResponseOptions,\n type NoContentHttpResponseOptions,\n} from '.';\n\n// #region httpresponse-on\nexport type HttpResponse = (response: Response) => void;\n\nexport type HttpHandler<RequestType extends Request> = (\n request: RequestType,\n) => Promise<HttpResponse> | HttpResponse;\n\nexport const on =\n <RequestType extends Request>(handle: HttpHandler<RequestType>) =>\n async (\n request: RequestType,\n response: Response,\n _next: NextFunction,\n ): Promise<void> => {\n const setResponse = await Promise.resolve(handle(request));\n\n return setResponse(response);\n };\n// #endregion httpresponse-on\n\nexport const OK =\n (options?: HttpResponseOptions): HttpResponse =>\n (response: Response) => {\n send(response, 200, options);\n };\n\nexport const Created =\n (options: CreatedHttpResponseOptions): HttpResponse =>\n (response: Response) => {\n sendCreated(response, options);\n };\n\nexport const Accepted =\n (options: AcceptedHttpResponseOptions): HttpResponse =>\n (response: Response) => {\n sendAccepted(response, options);\n };\n\nexport const NoContent = (\n options?: NoContentHttpResponseOptions,\n): HttpResponse => HttpResponse(204, options);\n\nexport const HttpResponse =\n (statusCode: number, options?: HttpResponseOptions): HttpResponse =>\n (response: Response) => {\n send(response, statusCode, options);\n };\n\n/////////////////////\n// ERRORS\n/////////////////////\n\nexport const BadRequest = (\n options?: HttpProblemResponseOptions,\n): HttpResponse => HttpProblem(400, options);\n\nexport const Forbidden = (options?: HttpProblemResponseOptions): HttpResponse =>\n HttpProblem(403, options);\n\nexport const NotFound = (options?: HttpProblemResponseOptions): HttpResponse =>\n HttpProblem(404, options);\n\nexport const Conflict = (options?: HttpProblemResponseOptions): HttpResponse =>\n HttpProblem(409, options);\n\nexport const PreconditionFailed = (\n options: HttpProblemResponseOptions,\n): HttpResponse => HttpProblem(412, options);\n\nexport const HttpProblem =\n (statusCode: number, options?: HttpProblemResponseOptions): HttpResponse =>\n (response: Response) => {\n sendProblem(response, statusCode, options);\n };\n","import type { SecurityHandlers } from './index';\n\ntype AuthClient = {\n verifyIdToken: (token: string) => Promise<unknown>;\n};\n\nexport type FirebaseAuthSecurityOptions = {\n /**\n * Name of the OpenAPI security scheme to attach the handler to.\n * Defaults to \"bearerAuth\".\n */\n securitySchemeName?: string;\n /**\n * Custom auth client for tests or alternate Firebase auth instances.\n */\n authClient?: AuthClient;\n /**\n * Token claim used for role-based checks when scopes are defined.\n * Defaults to \"roles\".\n */\n roleClaim?: string;\n};\n\ntype FirebaseAuthModule = {\n firebaseAuthMiddleware: (options?: { authClient?: AuthClient }) => (\n req: unknown,\n res: unknown,\n next: () => void,\n ) => Promise<void> | void;\n};\n\nconst loadFirebaseAuth = async (): Promise<FirebaseAuthModule> => {\n try {\n const mod = await import('@my-f-startup/firebase-auth-express');\n const provider = (mod as unknown as Record<string, unknown>).default ?? mod;\n const firebaseAuthMiddleware =\n (provider as Record<string, unknown>).firebaseAuthMiddleware;\n\n if (typeof firebaseAuthMiddleware !== 'function') {\n throw new Error(\n 'Invalid @my-f-startup/firebase-auth-express module: missing firebaseAuthMiddleware export',\n );\n }\n\n return provider as FirebaseAuthModule;\n } catch (error) {\n const message =\n '@my-f-startup/firebase-auth-express is required for createFirebaseAuthSecurityHandlers. ' +\n 'Install it with: npm install @my-f-startup/firebase-auth-express';\n throw new Error(message, { cause: error as Error });\n }\n};\n\nconst createNullResponse = () => {\n const res: Record<string, unknown> = {};\n res.status = () => res;\n res.json = () => res;\n res.send = () => res;\n res.end = () => res;\n res.set = () => res;\n return res;\n};\n\nconst runMiddleware = async (\n middleware: (req: unknown, res: unknown, next: () => void) => Promise<void> | void,\n req: unknown,\n): Promise<boolean> => {\n return new Promise((resolve) => {\n let nextCalled = false;\n const res = createNullResponse();\n const next = () => {\n nextCalled = true;\n resolve(true);\n };\n\n Promise.resolve(middleware(req, res, next))\n .then(() => {\n if (!nextCalled) resolve(false);\n })\n .catch(() => resolve(false));\n });\n};\n\nexport const createFirebaseAuthSecurityHandlers = (\n options: FirebaseAuthSecurityOptions = {},\n): SecurityHandlers => {\n const securitySchemeName = options.securitySchemeName ?? 'bearerAuth';\n const roleClaim = options.roleClaim ?? 'roles';\n\n return {\n [securitySchemeName]: async (req, scopes, _schema) => {\n const { firebaseAuthMiddleware } = await loadFirebaseAuth();\n const middleware = firebaseAuthMiddleware({\n authClient: options.authClient,\n });\n\n const isAuthenticated = await runMiddleware(middleware, req);\n if (!isAuthenticated) return false;\n\n if (!scopes.length) return true;\n\n const roles = (req as Record<string, any>)?.auth?.token?.[roleClaim];\n if (!Array.isArray(roles)) return false;\n\n return scopes.every((scope: string) => roles.includes(scope));\n },\n };\n};\n","/**\n * OpenAPI v3 Document type (to avoid requiring express-openapi-validator types directly)\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type OpenAPIV3Document = any;\n\n/**\n * Imported handler modules, keyed by module name.\n * Automatically populated by the framework when operationHandlers is configured.\n */\nexport type ImportedHandlerModules = Record<string, any>;\n\n/**\n * Security handlers for custom authentication/authorization logic.\n * Maps security scheme names to handler functions.\n *\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/usage-validate-security/\n */\n\nexport type SecurityHandlers = Record<\n string,\n (req: any, scopes: string[], schema: any) => boolean | Promise<boolean>\n>;\n\nexport * from './firebase-auth';\n\n/**\n * Configuration options for express-openapi-validator middleware.\n * This allows optional validation of API requests and responses against an OpenAPI specification.\n *\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/\n */\nexport type OpenApiValidatorOptions = {\n /**\n * Path to the OpenAPI specification file (JSON or YAML)\n * or an OpenAPI specification object.\n */\n apiSpec: string | OpenAPIV3Document;\n\n /**\n * Determines whether the validator should validate requests.\n * Can be a boolean or an object with detailed request validation options.\n * @default true\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/usage-validate-requests/\n */\n validateRequests?:\n | boolean\n | {\n /**\n * Allow unknown query parameters (not defined in the spec).\n * @default false\n */\n allowUnknownQueryParameters?: boolean;\n /**\n * Coerce types in request parameters.\n * @default true\n */\n coerceTypes?: boolean | 'array';\n /**\n * Remove additional properties not defined in the spec.\n * @default false\n */\n removeAdditional?: boolean | 'all' | 'failing';\n };\n\n /**\n * Determines whether the validator should validate responses.\n * Can be a boolean or an object with detailed response validation options.\n * @default false\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/usage-validate-responses/\n */\n validateResponses?:\n | boolean\n | {\n /**\n * Remove additional properties from responses not defined in the spec.\n * @default false\n */\n removeAdditional?: boolean | 'all' | 'failing';\n /**\n * Coerce types in responses.\n * @default true\n */\n coerceTypes?: boolean;\n /**\n * Callback to handle response validation errors.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onError?: (error: any, body: any, req: any) => void;\n };\n\n /**\n * Determines whether the validator should validate security.\n * Can be a boolean or an object with security handlers.\n * @default true\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/usage-validate-security/\n */\n validateSecurity?:\n | boolean\n | {\n /**\n * Custom security handlers for authentication/authorization.\n */\n handlers?: SecurityHandlers;\n };\n\n /**\n * Defines how the validator should validate formats.\n * When true, uses ajv-formats for format validation.\n * When false, format validation is disabled.\n * Can also be 'fast' or 'full' for different validation modes.\n * @default true\n */\n validateFormats?: boolean | 'fast' | 'full';\n\n /**\n * The base path to the operation handlers directory.\n * When set to a path, automatically wires OpenAPI operations to handler functions\n * based on operationId or x-eov-operation-id.\n * When false, operation handlers are disabled (manual routing required).\n * @default false\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/guide-operation-handlers/\n */\n operationHandlers?:\n | string\n | false\n | {\n /**\n * Base path to operation handlers directory.\n */\n basePath?: string;\n /**\n * Resolver function to map operationId to handler module path.\n */\n resolver?: (\n handlersPath: string,\n route: string,\n apiDoc: OpenAPIV3Document,\n ) => string;\n };\n\n /**\n * Paths or pattern to ignore during validation.\n * @default undefined\n */\n ignorePaths?: RegExp | ((path: string) => boolean);\n\n /**\n * Validate the OpenAPI specification itself.\n * @default true\n */\n validateApiSpec?: boolean;\n\n /**\n * $ref parser configuration for handling OpenAPI references.\n * @default undefined\n */\n $refParser?: {\n mode: 'bundle' | 'dereference';\n };\n\n /**\n * Serve the OpenAPI specification at a specific path.\n * When set to a string, the spec will be served at that path.\n * When false, the spec will not be served.\n * @default false\n * @example '/api-docs/openapi.json'\n */\n serveSpec?: string | false;\n\n /**\n * File upload configuration options.\n * @see https://cdimascio.github.io/express-openapi-validator-documentation/usage-file-uploads/\n */\n fileUploader?:\n | boolean\n | {\n /**\n * Destination directory for uploaded files.\n */\n dest?: string;\n /**\n * File size limit in bytes.\n */\n limits?: {\n fileSize?: number;\n files?: number;\n };\n };\n\n /**\n * Optional callback to initialize operation handlers with dependencies.\n * Called before the OpenAPI validator middleware is configured.\n *\n * The framework automatically imports handler modules referenced in your\n * OpenAPI spec (via x-eov-operation-handler) and passes them as the first parameter.\n *\n * @param handlers - Auto-imported handler modules, keyed by module name\n * @returns void or a Promise that resolves when initialization is complete\n *\n * @example\n * ```typescript\n * // With automatic import (recommended)\n * initializeHandlers: async (handlers) => {\n * handlers.shoppingCarts.initializeHandlers(eventStore, messageBus, getUnitPrice, getCurrentTime);\n * }\n *\n * // Manual import (still supported for backward compatibility)\n * import * as handlersModule from './handlers/shoppingCarts';\n * import { registerHandlerModule } from '@emmett-community/emmett-expressjs-with-openapi';\n * initializeHandlers: () => {\n * const handlersPath = path.join(__dirname, './handlers/shoppingCarts');\n * registerHandlerModule(handlersPath, handlersModule);\n * handlersModule.initializeHandlers(eventStore, messageBus, getUnitPrice, getCurrentTime);\n * }\n * ```\n */\n initializeHandlers?: (\n handlers?: ImportedHandlerModules,\n ) => void | Promise<void>;\n};\n\n/**\n * Helper function to create OpenAPI validator configuration with sensible defaults.\n *\n * @param apiSpec - Path to OpenAPI spec file or OpenAPI document object\n * @param options - Additional validator options\n * @returns Complete OpenApiValidatorOptions configuration\n *\n * @example\n * ```typescript\n * // Basic usage with default options\n * const validatorOptions = createOpenApiValidatorOptions('./openapi.yaml');\n *\n * // With response validation enabled\n * const validatorOptions = createOpenApiValidatorOptions('./openapi.yaml', {\n * validateResponses: true\n * });\n *\n * // With custom security handlers\n * const validatorOptions = createOpenApiValidatorOptions('./openapi.yaml', {\n * validateSecurity: {\n * handlers: {\n * bearerAuth: async (req, scopes) => {\n * // Custom authentication logic\n * return true;\n * }\n * }\n * }\n * });\n *\n * // Serving the spec at /api-docs\n * const validatorOptions = createOpenApiValidatorOptions('./openapi.yaml', {\n * serveSpec: '/api-docs/openapi.json'\n * });\n *\n * // With dependency injection for operation handlers\n * type ShoppingCartDeps = {\n * eventStore: EventStore;\n * messageBus: EventsPublisher;\n * getUnitPrice: (productId: string) => Promise<number>;\n * getCurrentTime: () => Date;\n * };\n *\n * const validatorOptions = createOpenApiValidatorOptions<ShoppingCartDeps>(\n * './openapi.yaml',\n * {\n * operationHandlers: './handlers',\n * initializeHandlers: (deps) => {\n * initializeHandlers(\n * deps.eventStore,\n * deps.messageBus,\n * deps.getUnitPrice,\n * deps.getCurrentTime\n * );\n * }\n * }\n * );\n *\n * const app = getApplication({\n * apis: [myApi],\n * openApiValidator: validatorOptions\n * });\n * ```\n */\nexport const createOpenApiValidatorOptions = (\n apiSpec: string | OpenAPIV3Document,\n options?: Partial<Omit<OpenApiValidatorOptions, 'apiSpec'>>,\n): OpenApiValidatorOptions => {\n return {\n apiSpec,\n validateRequests: options?.validateRequests ?? true,\n validateResponses: options?.validateResponses ?? false,\n validateSecurity: options?.validateSecurity ?? true,\n validateFormats: options?.validateFormats ?? true,\n operationHandlers: options?.operationHandlers,\n ignorePaths: options?.ignorePaths,\n validateApiSpec: options?.validateApiSpec ?? true,\n $refParser: options?.$refParser,\n serveSpec: options?.serveSpec ?? false,\n fileUploader: options?.fileUploader,\n initializeHandlers: options?.initializeHandlers,\n };\n};\n\n/**\n * Type guard to check if express-openapi-validator is available\n */\nexport const isOpenApiValidatorAvailable = async (): Promise<boolean> => {\n try {\n await import('express-openapi-validator');\n return true;\n } catch {\n return false;\n }\n};\n","import { type Request, type Response } from 'express';\nimport { ProblemDocument } from 'http-problem-details';\nimport { setETag, type ETag } from './etag';\n\nexport type ErrorToProblemDetailsMapping = (\n error: Error,\n request: Request,\n) => ProblemDocument | undefined;\n\nexport type HttpResponseOptions = {\n body?: unknown;\n location?: string;\n eTag?: ETag;\n};\nexport const DefaultHttpResponseOptions: HttpResponseOptions = {};\n\nexport type HttpProblemResponseOptions = {\n location?: string;\n eTag?: ETag;\n} & Omit<HttpResponseOptions, 'body'> &\n (\n | {\n problem: ProblemDocument;\n }\n | { problemDetails: string }\n );\nexport const DefaultHttpProblemResponseOptions: HttpProblemResponseOptions = {\n problemDetails: 'Error occured!',\n};\n\nexport type CreatedHttpResponseOptions = (\n | {\n createdId: string;\n }\n | {\n createdId?: string;\n url: string;\n }\n) &\n HttpResponseOptions;\n\nexport const sendCreated = (\n response: Response,\n { eTag, ...options }: CreatedHttpResponseOptions,\n): void =>\n send(response, 201, {\n location:\n 'url' in options\n ? options.url\n : `${response.req.url}/${options.createdId}`,\n body: 'createdId' in options ? { id: options.createdId } : undefined,\n eTag,\n });\n\nexport type AcceptedHttpResponseOptions = {\n location: string;\n} & HttpResponseOptions;\n\nexport const sendAccepted = (\n response: Response,\n options: AcceptedHttpResponseOptions,\n): void => send(response, 202, options);\n\nexport type NoContentHttpResponseOptions = Omit<HttpResponseOptions, 'body'>;\n\nexport const send = (\n response: Response,\n statusCode: number,\n options?: HttpResponseOptions,\n): void => {\n const { location, body, eTag } = options ?? DefaultHttpResponseOptions;\n // HEADERS\n if (eTag) setETag(response, eTag);\n if (location) response.setHeader('Location', location);\n\n if (body) {\n response.statusCode = statusCode;\n response.send(body);\n } else {\n response.sendStatus(statusCode);\n }\n};\n\nexport const sendProblem = (\n response: Response,\n statusCode: number,\n options?: HttpProblemResponseOptions,\n): void => {\n options = options ?? DefaultHttpProblemResponseOptions;\n\n const { location, eTag } = options;\n\n const problemDetails =\n 'problem' in options\n ? options.problem\n : new ProblemDocument({\n detail: options.problemDetails,\n status: statusCode,\n });\n\n // HEADERS\n if (eTag) setETag(response, eTag);\n if (location) response.setHeader('Location', location);\n\n response.setHeader('Content-Type', 'application/problem+json');\n\n response.statusCode = statusCode;\n response.json(problemDetails);\n};\n","import supertest, { type Response } from 'supertest';\n\nimport type { EventStore } from '@event-driven-io/emmett';\nimport assert from 'assert';\nimport type { Application } from 'express';\nimport type { TestRequest } from './apiSpecification';\n\nexport type E2EResponseAssert = (response: Response) => boolean | void;\n\nexport type ApiE2ESpecificationAssert = [E2EResponseAssert];\n\nexport type ApiE2ESpecification = (...givenRequests: TestRequest[]) => {\n when: (setupRequest: TestRequest) => {\n then: (verify: ApiE2ESpecificationAssert) => Promise<void>;\n };\n};\n\nexport const ApiE2ESpecification = {\n for: <Store extends EventStore = EventStore>(\n getEventStore: () => Store,\n getApplication: (eventStore: Store) => Application | Promise<Application>,\n ): ApiE2ESpecification => {\n {\n return (...givenRequests: TestRequest[]) => {\n const eventStore = getEventStore();\n\n return {\n when: (setupRequest: TestRequest) => {\n const handle = async () => {\n const application = await Promise.resolve(getApplication(eventStore));\n\n for (const requestFn of givenRequests) {\n await requestFn(supertest(application));\n }\n\n return setupRequest(supertest(application));\n };\n\n return {\n then: async (\n verify: ApiE2ESpecificationAssert,\n ): Promise<void> => {\n const response = await handle();\n\n verify.forEach((assertion) => {\n const succeeded = assertion(response);\n\n if (succeeded === false) assert.fail();\n });\n },\n };\n },\n };\n };\n }\n },\n};\n","import {\n WrapEventStore,\n assertEqual,\n assertFails,\n assertMatches,\n type Event,\n type EventStore,\n type TestEventStream,\n} from '@event-driven-io/emmett';\nimport { type Application } from 'express';\nimport type { ProblemDocument } from 'http-problem-details';\nimport type { Response, Test } from 'supertest';\nimport supertest from 'supertest';\nimport type TestAgent from 'supertest/lib/agent';\n\n////////////////////////////////\n/////////// Setup\n////////////////////////////////\n\nexport type TestRequest = (request: TestAgent<supertest.Test>) => Test;\n\nexport const existingStream = <EventType extends Event = Event>(\n streamId: string,\n events: EventType[],\n): TestEventStream<EventType> => {\n return [streamId, events];\n};\n\n////////////////////////////////\n/////////// Asserts\n////////////////////////////////\n\nexport type ResponseAssert = (response: Response) => boolean | void;\n\nexport type ApiSpecificationAssert<EventType extends Event = Event> =\n | TestEventStream<EventType>[]\n | ResponseAssert\n | [ResponseAssert, ...TestEventStream<EventType>[]];\n\nexport const expect = <EventType extends Event = Event>(\n streamId: string,\n events: EventType[],\n): TestEventStream<EventType> => {\n return [streamId, events];\n};\n\nexport const expectNewEvents = <EventType extends Event = Event>(\n streamId: string,\n events: EventType[],\n): TestEventStream<EventType> => {\n return [streamId, events];\n};\n\nexport const expectResponse =\n <Body = unknown>(\n statusCode: number,\n options?: { body?: Body; headers?: { [index: string]: string } },\n ) =>\n (response: Response): void => {\n const { body, headers } = options ?? {};\n assertEqual(statusCode, response.statusCode, \"Response code doesn't match\");\n if (body) assertMatches(response.body, body);\n if (headers) assertMatches(response.headers, headers);\n };\n\nexport const expectError = (\n errorCode: number,\n problemDetails?: Partial<ProblemDocument>,\n) =>\n expectResponse(\n errorCode,\n problemDetails ? { body: problemDetails } : undefined,\n );\n\n////////////////////////////////\n/////////// Api Specification\n////////////////////////////////\n\nexport type ApiSpecification<EventType extends Event = Event> = (\n ...givenStreams: TestEventStream<EventType>[]\n) => {\n when: (setupRequest: TestRequest) => {\n then: (verify: ApiSpecificationAssert<EventType>) => Promise<void>;\n };\n};\n\nexport const ApiSpecification = {\n for: <\n EventType extends Event = Event,\n Store extends EventStore<import('@event-driven-io/emmett').ReadEventMetadataWithGlobalPosition> = EventStore<import('@event-driven-io/emmett').ReadEventMetadataWithGlobalPosition>\n >(\n getEventStore: () => Store,\n getApplication: (eventStore: Store) => Application | Promise<Application>,\n ): ApiSpecification<EventType> => {\n {\n return (...givenStreams: TestEventStream<EventType>[]) => {\n const eventStore = WrapEventStore(getEventStore());\n\n return {\n when: (setupRequest: TestRequest) => {\n const handle = async () => {\n const application = await Promise.resolve(getApplication(eventStore));\n\n for (const [streamName, events] of givenStreams) {\n await eventStore.setup(streamName, events);\n }\n\n return setupRequest(supertest(application));\n };\n\n return {\n then: async (\n verify: ApiSpecificationAssert<EventType>,\n ): Promise<void> => {\n const response = await handle();\n\n if (typeof verify === 'function') {\n const succeeded = verify(response);\n\n if (succeeded === false) assertFails();\n } else if (Array.isArray(verify)) {\n const [first, ...rest] = verify;\n\n if (typeof first === 'function') {\n const succeeded = first(response);\n\n if (succeeded === false) assertFails();\n }\n\n const events = typeof first === 'function' ? rest : verify;\n\n assertMatches(\n Array.from(eventStore.appendedEvents.values()),\n events,\n );\n }\n },\n };\n },\n };\n };\n }\n },\n};\n"],"mappings":"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","names":["require","options","ETagErrors","ProblemDocument","ProblemDocument","getApplication","supertest","getApplication"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@emmett-community/emmett-expressjs-with-openapi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Express.js utilities for Emmett applications that want OpenAPI 3.x validation without pulling the whole Emmett core",
|
|
@@ -49,13 +49,14 @@
|
|
|
49
49
|
],
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"@event-driven-io/emmett": "0.39.1",
|
|
52
|
+
"@my-f-startup/firebase-auth-express": "0.1.0",
|
|
52
53
|
"@types/express": "^4.17.21",
|
|
53
54
|
"@types/supertest": "^6.0.2",
|
|
54
55
|
"express": "^4.19.2",
|
|
55
56
|
"express-async-errors": "^3.1.1",
|
|
56
57
|
"express-openapi-validator": "^5.3.7",
|
|
57
|
-
"@my-f-startup/firebase-auth-express": "0.1.0",
|
|
58
58
|
"http-problem-details": "^0.1.5",
|
|
59
|
+
"pino-http": "^9.0.0",
|
|
59
60
|
"supertest": "^7.0.0"
|
|
60
61
|
},
|
|
61
62
|
"peerDependenciesMeta": {
|
|
@@ -64,6 +65,9 @@
|
|
|
64
65
|
},
|
|
65
66
|
"@my-f-startup/firebase-auth-express": {
|
|
66
67
|
"optional": true
|
|
68
|
+
},
|
|
69
|
+
"pino-http": {
|
|
70
|
+
"optional": true
|
|
67
71
|
}
|
|
68
72
|
},
|
|
69
73
|
"dependencies": {
|
|
@@ -79,6 +83,8 @@
|
|
|
79
83
|
"glob": "^11.0.0",
|
|
80
84
|
"http-problem-details": "^0.1.7",
|
|
81
85
|
"npm-run-all2": "^6.2.2",
|
|
86
|
+
"pino-http": "^9.0.0",
|
|
87
|
+
"pino-pretty": "^13.1.3",
|
|
82
88
|
"supertest": "^7.0.0",
|
|
83
89
|
"tsup": "^8.2.4",
|
|
84
90
|
"tsx": "^4.17.0",
|