@emmett-community/emmett-expressjs-with-openapi 0.2.0 → 0.4.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 +223 -25
- package/dist/chunk-BY65ZALY.cjs +12 -0
- package/dist/chunk-BY65ZALY.cjs.map +1 -0
- package/dist/chunk-I46UH36B.js +12 -0
- package/dist/chunk-I46UH36B.js.map +1 -0
- package/dist/{handler-importer-OJGFQON5.js → handler-importer-6A237UML.js} +21 -2
- package/dist/handler-importer-6A237UML.js.map +1 -0
- package/dist/{handler-importer-4BVBIZX3.cjs → handler-importer-UFV7TKBN.cjs} +21 -2
- package/dist/handler-importer-UFV7TKBN.cjs.map +1 -0
- package/dist/index.cjs +129 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +119 -1
- package/dist/index.d.ts +119 -1
- package/dist/index.js +116 -21
- package/dist/index.js.map +1 -1
- package/dist/{openapi-parser-NFUD7ZGZ.cjs → openapi-parser-PW5USNZ4.cjs} +27 -8
- package/dist/openapi-parser-PW5USNZ4.cjs.map +1 -0
- package/dist/{openapi-parser-CCYU636U.js → openapi-parser-QYXNHNSZ.js} +27 -7
- package/dist/openapi-parser-QYXNHNSZ.js.map +1 -0
- package/package.json +9 -2
- package/dist/handler-importer-4BVBIZX3.cjs.map +0 -1
- package/dist/handler-importer-OJGFQON5.js.map +0 -1
- package/dist/openapi-parser-CCYU636U.js.map +0 -1
- package/dist/openapi-parser-NFUD7ZGZ.cjs.map +0 -1
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,33 @@ 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
|
+
|
|
35
|
+
- `@event-driven-io/emmett` ^0.39.1
|
|
36
|
+
- `express` ^4.19.2
|
|
37
|
+
- `express-async-errors` ^3.1.1
|
|
38
|
+
- `http-problem-details` ^0.1.5
|
|
39
|
+
|
|
40
|
+
TypeScript types:
|
|
41
|
+
|
|
42
|
+
- `@types/express` ^4.17.21
|
|
43
|
+
- `@types/supertest` ^6.0.2 (if using testing helpers)
|
|
44
|
+
|
|
45
|
+
Optional (feature-gated):
|
|
46
|
+
|
|
47
|
+
- `express-openapi-validator` ^5.3.7 (OpenAPI validation)
|
|
48
|
+
- `pino-http` ^9.0.0 (HTTP logging)
|
|
49
|
+
- `@my-f-startup/firebase-auth-express` 0.1.0 (Firebase Auth handlers)
|
|
50
|
+
- `supertest` ^7.0.0 (required by testing helpers)
|
|
51
|
+
|
|
52
|
+
Versions follow what is listed in `package.json`.
|
|
19
53
|
|
|
20
54
|
## Quick start
|
|
21
55
|
|
|
@@ -23,15 +57,33 @@ Other peer requirements (`express`, `@event-driven-io/emmett`, etc.) follow the
|
|
|
23
57
|
import {
|
|
24
58
|
getApplication,
|
|
25
59
|
createOpenApiValidatorOptions,
|
|
60
|
+
startAPI,
|
|
26
61
|
} from '@emmett-community/emmett-expressjs-with-openapi';
|
|
27
62
|
|
|
28
|
-
const app = getApplication({
|
|
63
|
+
const app = await getApplication({
|
|
29
64
|
apis: [myApi],
|
|
30
65
|
openApiValidator: createOpenApiValidatorOptions('./openapi.yaml', {
|
|
31
66
|
validateRequests: true,
|
|
32
67
|
validateResponses: process.env.NODE_ENV === 'development',
|
|
33
68
|
}),
|
|
34
69
|
});
|
|
70
|
+
|
|
71
|
+
startAPI(app, { port: 3000 });
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Configuration
|
|
75
|
+
|
|
76
|
+
### OpenAPI validation
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
const app = await getApplication({
|
|
80
|
+
apis: [myApi],
|
|
81
|
+
openApiValidator: createOpenApiValidatorOptions('./openapi.yaml', {
|
|
82
|
+
validateRequests: true,
|
|
83
|
+
validateResponses: true,
|
|
84
|
+
operationHandlers: './handlers',
|
|
85
|
+
}),
|
|
86
|
+
});
|
|
35
87
|
```
|
|
36
88
|
|
|
37
89
|
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 +93,11 @@ import path from 'node:path';
|
|
|
41
93
|
import { readFileSync } from 'node:fs';
|
|
42
94
|
import { parse } from 'yaml';
|
|
43
95
|
|
|
44
|
-
const spec = parse(
|
|
96
|
+
const spec = parse(
|
|
97
|
+
readFileSync(new URL('./openapi.yml', import.meta.url), 'utf-8'),
|
|
98
|
+
);
|
|
45
99
|
|
|
46
|
-
const app = getApplication({
|
|
100
|
+
const app = await getApplication({
|
|
47
101
|
apis: [usersApi],
|
|
48
102
|
openApiValidator: createOpenApiValidatorOptions(spec, {
|
|
49
103
|
operationHandlers: path.join(path.dirname(import.meta.url), './handlers'),
|
|
@@ -51,16 +105,32 @@ const app = getApplication({
|
|
|
51
105
|
});
|
|
52
106
|
```
|
|
53
107
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
`createOpenApiValidatorOptions` forwards every option from `express-openapi-validator`, so you can:
|
|
108
|
+
Highlights:
|
|
57
109
|
|
|
58
110
|
- Enable/disable request or response validation, including per-field tweaks (coercion, unknown query params, removing additional fields, etc.).
|
|
59
111
|
- Register custom security handlers with `validateSecurity.handlers`.
|
|
60
112
|
- Serve the parsed spec via `serveSpec`, configure file upload limits, ignore health-check routes, or validate the spec itself.
|
|
61
113
|
- Reuse `$ref` parsing, custom formats, and file upload middleware exactly as in the upstream validator.
|
|
62
114
|
|
|
63
|
-
|
|
115
|
+
### Logging (optional)
|
|
116
|
+
|
|
117
|
+
Install the optional peer to enable HTTP request/response logging:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
npm install pino-http
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
const app = await getApplication({
|
|
125
|
+
pinoHttp: true, // defaults
|
|
126
|
+
// or:
|
|
127
|
+
pinoHttp: { autoLogging: false },
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
See the full options in the [`pino-http` docs](https://github.com/pinojs/pino-http).
|
|
132
|
+
|
|
133
|
+
### Firebase Auth (optional)
|
|
64
134
|
|
|
65
135
|
If you use Firebase Authentication, install the optional peer and plug it into OpenAPI security handlers:
|
|
66
136
|
|
|
@@ -78,7 +148,7 @@ import {
|
|
|
78
148
|
|
|
79
149
|
admin.initializeApp();
|
|
80
150
|
|
|
81
|
-
const app = getApplication({
|
|
151
|
+
const app = await getApplication({
|
|
82
152
|
apis: [myApi],
|
|
83
153
|
openApiValidator: createOpenApiValidatorOptions('./openapi.yaml', {
|
|
84
154
|
validateSecurity: {
|
|
@@ -90,20 +160,73 @@ const app = getApplication({
|
|
|
90
160
|
|
|
91
161
|
`@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
162
|
|
|
93
|
-
|
|
163
|
+
## Observability
|
|
94
164
|
|
|
95
|
-
|
|
165
|
+
This package supports optional logging and tracing.
|
|
96
166
|
|
|
97
|
-
|
|
167
|
+
### Logging
|
|
98
168
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
169
|
+
Inject a Pino-compatible logger:
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
import pino from 'pino';
|
|
173
|
+
|
|
174
|
+
const logger = pino();
|
|
175
|
+
|
|
176
|
+
const app = await getApplication({
|
|
177
|
+
observability: { logger },
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
startAPI(app, { port: 3000, logger });
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
No logs are emitted when logger is not provided.
|
|
184
|
+
|
|
185
|
+
### Tracing
|
|
186
|
+
|
|
187
|
+
If your application initializes OpenTelemetry, this package will emit spans automatically. Tracing is passive - spans are no-ops when OpenTelemetry is not configured.
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
import { tracedOn, OK } from '@emmett-community/emmett-expressjs-with-openapi';
|
|
191
|
+
|
|
192
|
+
router.post('/carts', tracedOn(async (req) => {
|
|
193
|
+
// Your handler logic
|
|
194
|
+
return OK({ body: { success: true } });
|
|
195
|
+
}));
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
This package never initializes OpenTelemetry - configure your tracer provider in your application.
|
|
199
|
+
|
|
200
|
+
## API Reference
|
|
201
|
+
|
|
202
|
+
### Application
|
|
203
|
+
|
|
204
|
+
- `getApplication(options)` - Creates and configures the Express app.
|
|
205
|
+
- `startAPI(app, options)` - Starts the HTTP server.
|
|
206
|
+
|
|
207
|
+
### OpenAPI
|
|
208
|
+
|
|
209
|
+
- `createOpenApiValidatorOptions(apiSpec, options)` - Helper to assemble OpenAPI validator config.
|
|
210
|
+
- `isOpenApiValidatorAvailable()` - Type guard for optional validator dependency.
|
|
211
|
+
- `createFirebaseAuthSecurityHandlers(options)` - Firebase Auth security handlers.
|
|
212
|
+
- `registerHandlerModule(handlersPath, module)` - Manual registration for operation handlers.
|
|
213
|
+
|
|
214
|
+
### HTTP helpers
|
|
215
|
+
|
|
216
|
+
- `send`, `sendCreated`, `sendAccepted`, `sendProblem` - Standard HTTP responses.
|
|
217
|
+
|
|
218
|
+
### ETag helpers
|
|
219
|
+
|
|
220
|
+
- `toWeakETag`, `getETagFromIfMatch`, `getETagFromIfNotMatch`, `getETagValueFromIfMatch`, `setETag`.
|
|
105
221
|
|
|
106
|
-
|
|
222
|
+
### Testing helpers
|
|
223
|
+
|
|
224
|
+
- `ApiSpecification`, `ApiE2ESpecification`
|
|
225
|
+
- `expect`, `expectNewEvents`, `expectResponse`, `expectError`
|
|
226
|
+
|
|
227
|
+
See [`docs/openapi-validation.md`](docs/openapi-validation.md) for the full matrix of options and extended examples.
|
|
228
|
+
|
|
229
|
+
## Testing
|
|
107
230
|
|
|
108
231
|
The package includes comprehensive test coverage:
|
|
109
232
|
|
|
@@ -111,21 +234,96 @@ The package includes comprehensive test coverage:
|
|
|
111
234
|
- **Integration tests** (`test/integration/`) - Basic routing, OpenAPI validation, operation handlers, optimistic concurrency
|
|
112
235
|
- **E2E tests** (`test/e2e/`) - End-to-end workflows for all features
|
|
113
236
|
|
|
114
|
-
|
|
237
|
+
### Running tests
|
|
115
238
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
239
|
+
```bash
|
|
240
|
+
# Unit tests
|
|
241
|
+
npm run test:unit
|
|
242
|
+
|
|
243
|
+
# Integration tests
|
|
244
|
+
npm run test:int
|
|
245
|
+
|
|
246
|
+
# E2E tests
|
|
247
|
+
npm run test:e2e
|
|
248
|
+
|
|
249
|
+
# All tests
|
|
250
|
+
npm test
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Examples
|
|
254
|
+
|
|
255
|
+
Working examples live under `examples/`:
|
|
256
|
+
|
|
257
|
+
- `examples/shopping-cart` – feature-complete Emmett sample (business logic, memory store/publisher, security handlers, OpenAPI file, unit/int/e2e tests, `.http` scripts).
|
|
258
|
+
- Legacy quick starts:
|
|
259
|
+
- `examples/basic` – manual routes + validation (minimal scaffolding).
|
|
260
|
+
- `examples/with-security` – standalone security handler demo.
|
|
261
|
+
- `examples/operation-handlers` – barebones `operationHandlers` showcase.
|
|
262
|
+
- `examples/firebase-auth` – Firebase Auth security handlers with operation handlers.
|
|
119
263
|
|
|
120
264
|
## Documentation
|
|
121
265
|
|
|
122
266
|
- **Guide:** [`docs/openapi-validation.md`](docs/openapi-validation.md) – authoritative reference for configuration, advanced usage, and troubleshooting.
|
|
123
267
|
- **Emmett docs:** <https://event-driven-io.github.io/emmett/>
|
|
124
268
|
|
|
269
|
+
## Compatibility
|
|
270
|
+
|
|
271
|
+
- **Node.js**: tested in CI with 24.x
|
|
272
|
+
- **Emmett**: ^0.39.1
|
|
273
|
+
- **Express**: ^4.19.2
|
|
274
|
+
|
|
125
275
|
## Contributing
|
|
126
276
|
|
|
127
|
-
|
|
277
|
+
Contributions are welcome! Please:
|
|
278
|
+
|
|
279
|
+
1. Fork the repository
|
|
280
|
+
2. Create a feature branch
|
|
281
|
+
3. Add tests for new functionality
|
|
282
|
+
4. Ensure all tests pass
|
|
283
|
+
5. Submit a pull request
|
|
284
|
+
|
|
285
|
+
## Development
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
# Install dependencies
|
|
289
|
+
npm install
|
|
290
|
+
|
|
291
|
+
# Build
|
|
292
|
+
npm run build
|
|
293
|
+
|
|
294
|
+
# Type-check
|
|
295
|
+
npm run build:ts
|
|
296
|
+
|
|
297
|
+
# Run tests
|
|
298
|
+
npm test
|
|
299
|
+
|
|
300
|
+
# Run unit tests only
|
|
301
|
+
npm run test:unit
|
|
302
|
+
|
|
303
|
+
# Run integration tests
|
|
304
|
+
npm run test:int
|
|
305
|
+
|
|
306
|
+
# Run E2E tests
|
|
307
|
+
npm run test:e2e
|
|
308
|
+
```
|
|
128
309
|
|
|
129
310
|
## License
|
|
130
311
|
|
|
131
312
|
MIT
|
|
313
|
+
|
|
314
|
+
## Related Packages
|
|
315
|
+
|
|
316
|
+
- [@event-driven-io/emmett](https://github.com/event-driven-io/emmett) - Core Emmett framework
|
|
317
|
+
- [@emmett-community/emmett-google-firestore](https://github.com/emmett-community/emmett-google-firestore) - Firestore event store
|
|
318
|
+
- [@emmett-community/emmett-google-realtime-db](https://github.com/emmett-community/emmett-google-realtime-db) - Realtime Database inline projections
|
|
319
|
+
- [@emmett-community/emmett-google-pubsub](https://github.com/emmett-community/emmett-google-pubsub) - Pub/Sub message bus
|
|
320
|
+
- [@event-driven-io/emmett-mongodb](https://github.com/event-driven-io/emmett/tree/main/src/packages/emmett-mongodb) - MongoDB event store
|
|
321
|
+
|
|
322
|
+
## Support
|
|
323
|
+
|
|
324
|
+
- [GitHub Issues](https://github.com/emmett-community/emmett-expressjs-with-openapi/issues)
|
|
325
|
+
- [Emmett Documentation](https://event-driven-io.github.io/emmett/)
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
Made with ❤️ by the Emmett Community
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/observability.ts
|
|
2
|
+
var safeLog = {
|
|
3
|
+
debug: (logger, msg, data) => _optionalChain([logger, 'optionalAccess', _ => _.debug, 'optionalCall', _2 => _2(msg, data)]),
|
|
4
|
+
info: (logger, msg, data) => _optionalChain([logger, 'optionalAccess', _3 => _3.info, 'optionalCall', _4 => _4(msg, data)]),
|
|
5
|
+
warn: (logger, msg, data) => _optionalChain([logger, 'optionalAccess', _5 => _5.warn, 'optionalCall', _6 => _6(msg, data)]),
|
|
6
|
+
error: (logger, msg, error) => _optionalChain([logger, 'optionalAccess', _7 => _7.error, 'optionalCall', _8 => _8(msg, error)])
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
exports.safeLog = safeLog;
|
|
12
|
+
//# sourceMappingURL=chunk-BY65ZALY.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/emmett-expressjs-with-openapi/emmett-expressjs-with-openapi/dist/chunk-BY65ZALY.cjs","../src/observability.ts"],"names":[],"mappings":"AAAA;AC4CO,IAAM,QAAA,EAAU;AAAA,EACrB,KAAA,EAAO,CAAC,MAAA,EAA4B,GAAA,EAAa,IAAA,EAAA,mBAC/C,MAAA,2BAAQ,KAAA,0BAAA,CAAQ,GAAA,EAAK,IAAI,GAAA;AAAA,EAC3B,IAAA,EAAM,CAAC,MAAA,EAA4B,GAAA,EAAa,IAAA,EAAA,mBAC9C,MAAA,6BAAQ,IAAA,0BAAA,CAAO,GAAA,EAAK,IAAI,GAAA;AAAA,EAC1B,IAAA,EAAM,CAAC,MAAA,EAA4B,GAAA,EAAa,IAAA,EAAA,mBAC9C,MAAA,6BAAQ,IAAA,0BAAA,CAAO,GAAA,EAAK,IAAI,GAAA;AAAA,EAC1B,KAAA,EAAO,CAAC,MAAA,EAA4B,GAAA,EAAa,KAAA,EAAA,mBAC/C,MAAA,6BAAQ,KAAA,0BAAA,CAAQ,GAAA,EAAK,KAAK;AAC9B,CAAA;AD9CA;AACA;AACE;AACF,0BAAC","file":"/home/runner/work/emmett-expressjs-with-openapi/emmett-expressjs-with-openapi/dist/chunk-BY65ZALY.cjs","sourcesContent":[null,"/**\n * Observability types for emmett-expressjs-with-openapi.\n *\n * This module provides optional logging integration\n * following a \"silent by default\" philosophy.\n */\n\n/**\n * Minimal logger interface compatible with Pino, Winston, console, and similar loggers.\n * All methods are optional to support partial implementations.\n */\nexport interface Logger {\n debug?(msg: string, data?: unknown): void;\n info?(msg: string, data?: unknown): void;\n warn?(msg: string, data?: unknown): void;\n error?(msg: string, err?: unknown): void;\n}\n\n/**\n * Observability configuration options.\n */\nexport type ObservabilityOptions = {\n /**\n * Optional logger instance for diagnostic output.\n * When not provided, the library operates silently.\n *\n * @example\n * ```typescript\n * import pino from 'pino';\n *\n * const logger = pino();\n *\n * const app = await getApplication({\n * observability: { logger },\n * });\n * ```\n */\n logger?: Logger;\n};\n\n/**\n * Internal helper to safely call logger methods, handling partial implementations.\n * Always use: safeLog.error(logger, msg, error) - never { error }\n */\nexport const safeLog = {\n debug: (logger: Logger | undefined, msg: string, data?: unknown) =>\n logger?.debug?.(msg, data),\n info: (logger: Logger | undefined, msg: string, data?: unknown) =>\n logger?.info?.(msg, data),\n warn: (logger: Logger | undefined, msg: string, data?: unknown) =>\n logger?.warn?.(msg, data),\n error: (logger: Logger | undefined, msg: string, error?: unknown) =>\n logger?.error?.(msg, error),\n};\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// src/observability.ts
|
|
2
|
+
var safeLog = {
|
|
3
|
+
debug: (logger, msg, data) => logger?.debug?.(msg, data),
|
|
4
|
+
info: (logger, msg, data) => logger?.info?.(msg, data),
|
|
5
|
+
warn: (logger, msg, data) => logger?.warn?.(msg, data),
|
|
6
|
+
error: (logger, msg, error) => logger?.error?.(msg, error)
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
safeLog
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=chunk-I46UH36B.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/observability.ts"],"sourcesContent":["/**\n * Observability types for emmett-expressjs-with-openapi.\n *\n * This module provides optional logging integration\n * following a \"silent by default\" philosophy.\n */\n\n/**\n * Minimal logger interface compatible with Pino, Winston, console, and similar loggers.\n * All methods are optional to support partial implementations.\n */\nexport interface Logger {\n debug?(msg: string, data?: unknown): void;\n info?(msg: string, data?: unknown): void;\n warn?(msg: string, data?: unknown): void;\n error?(msg: string, err?: unknown): void;\n}\n\n/**\n * Observability configuration options.\n */\nexport type ObservabilityOptions = {\n /**\n * Optional logger instance for diagnostic output.\n * When not provided, the library operates silently.\n *\n * @example\n * ```typescript\n * import pino from 'pino';\n *\n * const logger = pino();\n *\n * const app = await getApplication({\n * observability: { logger },\n * });\n * ```\n */\n logger?: Logger;\n};\n\n/**\n * Internal helper to safely call logger methods, handling partial implementations.\n * Always use: safeLog.error(logger, msg, error) - never { error }\n */\nexport const safeLog = {\n debug: (logger: Logger | undefined, msg: string, data?: unknown) =>\n logger?.debug?.(msg, data),\n info: (logger: Logger | undefined, msg: string, data?: unknown) =>\n logger?.info?.(msg, data),\n warn: (logger: Logger | undefined, msg: string, data?: unknown) =>\n logger?.warn?.(msg, data),\n error: (logger: Logger | undefined, msg: string, error?: unknown) =>\n logger?.error?.(msg, error),\n};\n"],"mappings":";AA4CO,IAAM,UAAU;AAAA,EACrB,OAAO,CAAC,QAA4B,KAAa,SAC/C,QAAQ,QAAQ,KAAK,IAAI;AAAA,EAC3B,MAAM,CAAC,QAA4B,KAAa,SAC9C,QAAQ,OAAO,KAAK,IAAI;AAAA,EAC1B,MAAM,CAAC,QAA4B,KAAa,SAC9C,QAAQ,OAAO,KAAK,IAAI;AAAA,EAC1B,OAAO,CAAC,QAA4B,KAAa,UAC/C,QAAQ,QAAQ,KAAK,KAAK;AAC9B;","names":[]}
|
|
@@ -1,18 +1,37 @@
|
|
|
1
1
|
import {
|
|
2
2
|
registerHandlerModule
|
|
3
3
|
} from "./chunk-5TC7YUZR.js";
|
|
4
|
+
import {
|
|
5
|
+
safeLog
|
|
6
|
+
} from "./chunk-I46UH36B.js";
|
|
4
7
|
|
|
5
8
|
// src/internal/handler-importer.ts
|
|
6
9
|
import { pathToFileURL } from "url";
|
|
7
|
-
async function importAndRegisterHandlers(modules) {
|
|
10
|
+
async function importAndRegisterHandlers(modules, logger) {
|
|
11
|
+
safeLog.debug(logger, "Importing handler modules", {
|
|
12
|
+
count: modules.length,
|
|
13
|
+
modules: modules.map((m) => m.moduleName)
|
|
14
|
+
});
|
|
8
15
|
const importedHandlers = {};
|
|
9
16
|
for (const module of modules) {
|
|
10
17
|
try {
|
|
18
|
+
safeLog.debug(logger, "Importing handler module", {
|
|
19
|
+
moduleName: module.moduleName,
|
|
20
|
+
absolutePath: module.absolutePath
|
|
21
|
+
});
|
|
11
22
|
const fileUrl = pathToFileURL(module.absolutePath).href;
|
|
12
23
|
const importedModule = await import(fileUrl);
|
|
13
24
|
registerHandlerModule(module.absolutePath, importedModule);
|
|
14
25
|
importedHandlers[module.moduleName] = importedModule;
|
|
26
|
+
safeLog.debug(logger, "Handler module imported successfully", {
|
|
27
|
+
moduleName: module.moduleName
|
|
28
|
+
});
|
|
15
29
|
} catch (error) {
|
|
30
|
+
safeLog.error(
|
|
31
|
+
logger,
|
|
32
|
+
`Failed to import handler module "${module.moduleName}"`,
|
|
33
|
+
error
|
|
34
|
+
);
|
|
16
35
|
throw new Error(
|
|
17
36
|
`Failed to import handler module "${module.moduleName}" from ${module.absolutePath}: ${error.message}`
|
|
18
37
|
);
|
|
@@ -23,4 +42,4 @@ async function importAndRegisterHandlers(modules) {
|
|
|
23
42
|
export {
|
|
24
43
|
importAndRegisterHandlers
|
|
25
44
|
};
|
|
26
|
-
//# sourceMappingURL=handler-importer-
|
|
45
|
+
//# sourceMappingURL=handler-importer-6A237UML.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/internal/handler-importer.ts"],"sourcesContent":["/**\n * Handler module importer.\n *\n * INTERNAL MODULE - Not part of public API.\n *\n * Dynamically imports handler modules and registers them in the ESM resolver cache,\n * enabling automatic handler discovery without manual registration.\n */\n\nimport { pathToFileURL } from 'node:url';\nimport { type Logger, safeLog } from '../observability';\nimport { registerHandlerModule } from './esm-resolver.js';\nimport type { HandlerModuleInfo } from './openapi-parser.js';\n\nexport type ImportedHandlerModules = Record<string, any>;\n\n/**\n * Dynamically import and register all handler modules.\n *\n * @param modules - Handler module information from OpenAPI parser\n * @param logger - Optional logger for debug output\n * @returns Object containing all imported modules, keyed by module name\n */\nexport async function importAndRegisterHandlers(\n modules: HandlerModuleInfo[],\n logger?: Logger,\n): Promise<ImportedHandlerModules> {\n safeLog.debug(logger, 'Importing handler modules', {\n count: modules.length,\n modules: modules.map((m) => m.moduleName),\n });\n\n const importedHandlers: ImportedHandlerModules = {};\n\n for (const module of modules) {\n try {\n safeLog.debug(logger, 'Importing handler module', {\n moduleName: module.moduleName,\n absolutePath: module.absolutePath,\n });\n\n // Convert to file:// URL for dynamic import\n const fileUrl = pathToFileURL(module.absolutePath).href;\n\n // Dynamically import the handler module\n const importedModule = await import(fileUrl);\n\n // Register in ESM resolver cache\n registerHandlerModule(module.absolutePath, importedModule);\n\n // Store in result object keyed by module name\n importedHandlers[module.moduleName] = importedModule;\n\n safeLog.debug(logger, 'Handler module imported successfully', {\n moduleName: module.moduleName,\n });\n } catch (error) {\n safeLog.error(\n logger,\n `Failed to import handler module \"${module.moduleName}\"`,\n error,\n );\n throw new Error(\n `Failed to import handler module \"${module.moduleName}\" from ${module.absolutePath}: ${\n (error as Error).message\n }`,\n );\n }\n }\n\n return importedHandlers;\n}\n"],"mappings":";;;;;;;;AASA,SAAS,qBAAqB;AAc9B,eAAsB,0BACpB,SACA,QACiC;AACjC,UAAQ,MAAM,QAAQ,6BAA6B;AAAA,IACjD,OAAO,QAAQ;AAAA,IACf,SAAS,QAAQ,IAAI,CAAC,MAAM,EAAE,UAAU;AAAA,EAC1C,CAAC;AAED,QAAM,mBAA2C,CAAC;AAElD,aAAW,UAAU,SAAS;AAC5B,QAAI;AACF,cAAQ,MAAM,QAAQ,4BAA4B;AAAA,QAChD,YAAY,OAAO;AAAA,QACnB,cAAc,OAAO;AAAA,MACvB,CAAC;AAGD,YAAM,UAAU,cAAc,OAAO,YAAY,EAAE;AAGnD,YAAM,iBAAiB,MAAM,OAAO;AAGpC,4BAAsB,OAAO,cAAc,cAAc;AAGzD,uBAAiB,OAAO,UAAU,IAAI;AAEtC,cAAQ,MAAM,QAAQ,wCAAwC;AAAA,QAC5D,YAAY,OAAO;AAAA,MACrB,CAAC;AAAA,IACH,SAAS,OAAO;AACd,cAAQ;AAAA,QACN;AAAA,QACA,oCAAoC,OAAO,UAAU;AAAA,QACrD;AAAA,MACF;AACA,YAAM,IAAI;AAAA,QACR,oCAAoC,OAAO,UAAU,UAAU,OAAO,YAAY,KAC/E,MAAgB,OACnB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;","names":[]}
|
|
@@ -1,19 +1,38 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } }
|
|
2
2
|
|
|
3
3
|
var _chunkMRSGTROWcjs = require('./chunk-MRSGTROW.cjs');
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
var _chunkBY65ZALYcjs = require('./chunk-BY65ZALY.cjs');
|
|
4
7
|
require('./chunk-GS7T56RP.cjs');
|
|
5
8
|
|
|
6
9
|
// src/internal/handler-importer.ts
|
|
7
10
|
var _url = require('url');
|
|
8
|
-
async function importAndRegisterHandlers(modules) {
|
|
11
|
+
async function importAndRegisterHandlers(modules, logger) {
|
|
12
|
+
_chunkBY65ZALYcjs.safeLog.debug(logger, "Importing handler modules", {
|
|
13
|
+
count: modules.length,
|
|
14
|
+
modules: modules.map((m) => m.moduleName)
|
|
15
|
+
});
|
|
9
16
|
const importedHandlers = {};
|
|
10
17
|
for (const module of modules) {
|
|
11
18
|
try {
|
|
19
|
+
_chunkBY65ZALYcjs.safeLog.debug(logger, "Importing handler module", {
|
|
20
|
+
moduleName: module.moduleName,
|
|
21
|
+
absolutePath: module.absolutePath
|
|
22
|
+
});
|
|
12
23
|
const fileUrl = _url.pathToFileURL.call(void 0, module.absolutePath).href;
|
|
13
24
|
const importedModule = await Promise.resolve().then(() => _interopRequireWildcard(require(fileUrl)));
|
|
14
25
|
_chunkMRSGTROWcjs.registerHandlerModule.call(void 0, module.absolutePath, importedModule);
|
|
15
26
|
importedHandlers[module.moduleName] = importedModule;
|
|
27
|
+
_chunkBY65ZALYcjs.safeLog.debug(logger, "Handler module imported successfully", {
|
|
28
|
+
moduleName: module.moduleName
|
|
29
|
+
});
|
|
16
30
|
} catch (error) {
|
|
31
|
+
_chunkBY65ZALYcjs.safeLog.error(
|
|
32
|
+
logger,
|
|
33
|
+
`Failed to import handler module "${module.moduleName}"`,
|
|
34
|
+
error
|
|
35
|
+
);
|
|
17
36
|
throw new Error(
|
|
18
37
|
`Failed to import handler module "${module.moduleName}" from ${module.absolutePath}: ${error.message}`
|
|
19
38
|
);
|
|
@@ -24,4 +43,4 @@ async function importAndRegisterHandlers(modules) {
|
|
|
24
43
|
|
|
25
44
|
|
|
26
45
|
exports.importAndRegisterHandlers = importAndRegisterHandlers;
|
|
27
|
-
//# sourceMappingURL=handler-importer-
|
|
46
|
+
//# sourceMappingURL=handler-importer-UFV7TKBN.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/emmett-expressjs-with-openapi/emmett-expressjs-with-openapi/dist/handler-importer-UFV7TKBN.cjs","../src/internal/handler-importer.ts"],"names":[],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B;AACE;AACF,wDAA6B;AAC7B,gCAA6B;AAC7B;AACA;ACCA,0BAA8B;AAc9B,MAAA,SAAsB,yBAAA,CACpB,OAAA,EACA,MAAA,EACiC;AACjC,EAAA,yBAAA,CAAQ,KAAA,CAAM,MAAA,EAAQ,2BAAA,EAA6B;AAAA,IACjD,KAAA,EAAO,OAAA,CAAQ,MAAA;AAAA,IACf,OAAA,EAAS,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,EAAA,GAAM,CAAA,CAAE,UAAU;AAAA,EAC1C,CAAC,CAAA;AAED,EAAA,MAAM,iBAAA,EAA2C,CAAC,CAAA;AAElD,EAAA,IAAA,CAAA,MAAW,OAAA,GAAU,OAAA,EAAS;AAC5B,IAAA,IAAI;AACF,MAAA,yBAAA,CAAQ,KAAA,CAAM,MAAA,EAAQ,0BAAA,EAA4B;AAAA,QAChD,UAAA,EAAY,MAAA,CAAO,UAAA;AAAA,QACnB,YAAA,EAAc,MAAA,CAAO;AAAA,MACvB,CAAC,CAAA;AAGD,MAAA,MAAM,QAAA,EAAU,gCAAA,MAAc,CAAO,YAAY,CAAA,CAAE,IAAA;AAGnD,MAAA,MAAM,eAAA,EAAiB,MAAM,4DAAA,CAAO,OAAA,GAAA;AAGpC,MAAA,qDAAA,MAAsB,CAAO,YAAA,EAAc,cAAc,CAAA;AAGzD,MAAA,gBAAA,CAAiB,MAAA,CAAO,UAAU,EAAA,EAAI,cAAA;AAEtC,MAAA,yBAAA,CAAQ,KAAA,CAAM,MAAA,EAAQ,sCAAA,EAAwC;AAAA,QAC5D,UAAA,EAAY,MAAA,CAAO;AAAA,MACrB,CAAC,CAAA;AAAA,IACH,EAAA,MAAA,CAAS,KAAA,EAAO;AACd,MAAA,yBAAA,CAAQ,KAAA;AAAA,QACN,MAAA;AAAA,QACA,CAAA,iCAAA,EAAoC,MAAA,CAAO,UAAU,CAAA,CAAA,CAAA;AAAA,QACrD;AAAA,MACF,CAAA;AACA,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,iCAAA,EAAoC,MAAA,CAAO,UAAU,CAAA,OAAA,EAAU,MAAA,CAAO,YAAY,CAAA,EAAA,EAC/E,KAAA,CAAgB,OACnB,CAAA;AAAA,MAAA;AACF,IAAA;AACF,EAAA;AAGF,EAAA;AACF;AD7BA;AACA;AACA","file":"/home/runner/work/emmett-expressjs-with-openapi/emmett-expressjs-with-openapi/dist/handler-importer-UFV7TKBN.cjs","sourcesContent":[null,"/**\n * Handler module importer.\n *\n * INTERNAL MODULE - Not part of public API.\n *\n * Dynamically imports handler modules and registers them in the ESM resolver cache,\n * enabling automatic handler discovery without manual registration.\n */\n\nimport { pathToFileURL } from 'node:url';\nimport { type Logger, safeLog } from '../observability';\nimport { registerHandlerModule } from './esm-resolver.js';\nimport type { HandlerModuleInfo } from './openapi-parser.js';\n\nexport type ImportedHandlerModules = Record<string, any>;\n\n/**\n * Dynamically import and register all handler modules.\n *\n * @param modules - Handler module information from OpenAPI parser\n * @param logger - Optional logger for debug output\n * @returns Object containing all imported modules, keyed by module name\n */\nexport async function importAndRegisterHandlers(\n modules: HandlerModuleInfo[],\n logger?: Logger,\n): Promise<ImportedHandlerModules> {\n safeLog.debug(logger, 'Importing handler modules', {\n count: modules.length,\n modules: modules.map((m) => m.moduleName),\n });\n\n const importedHandlers: ImportedHandlerModules = {};\n\n for (const module of modules) {\n try {\n safeLog.debug(logger, 'Importing handler module', {\n moduleName: module.moduleName,\n absolutePath: module.absolutePath,\n });\n\n // Convert to file:// URL for dynamic import\n const fileUrl = pathToFileURL(module.absolutePath).href;\n\n // Dynamically import the handler module\n const importedModule = await import(fileUrl);\n\n // Register in ESM resolver cache\n registerHandlerModule(module.absolutePath, importedModule);\n\n // Store in result object keyed by module name\n importedHandlers[module.moduleName] = importedModule;\n\n safeLog.debug(logger, 'Handler module imported successfully', {\n moduleName: module.moduleName,\n });\n } catch (error) {\n safeLog.error(\n logger,\n `Failed to import handler module \"${module.moduleName}\"`,\n error,\n );\n throw new Error(\n `Failed to import handler module \"${module.moduleName}\" from ${module.absolutePath}: ${\n (error as Error).message\n }`,\n );\n }\n }\n\n return importedHandlers;\n}\n"]}
|