@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 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
+ [![npm version](https://img.shields.io/npm/v/@emmett-community/emmett-expressjs-with-openapi.svg)](https://www.npmjs.com/package/@emmett-community/emmett-expressjs-with-openapi) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Build and test](https://github.com/emmett-community/emmett-expressjs-with-openapi/actions/workflows/build_and_test.yml/badge.svg)](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 # optional peer dependency
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
- Other peer requirements (`express`, `@event-driven-io/emmett`, etc.) follow the versions listed in `package.json`.
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(readFileSync(new URL('./openapi.yml', import.meta.url), 'utf-8'));
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
- ## Configuration highlights
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
- ## Firebase Auth (optional)
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
- Head to [`docs/openapi-validation.md`](docs/openapi-validation.md) for the full matrix of options, extended explanations, and complete examples. Keeping that document allows us to document advanced scenarios without bloating the README.
163
+ ## Observability
94
164
 
95
- ## Examples
165
+ This package supports optional logging and tracing.
96
166
 
97
- Working examples live under `examples/`:
167
+ ### Logging
98
168
 
99
- - `examples/shopping-cart` – feature-complete Emmett sample (business logic, memory store/publisher, security handlers, OpenAPI file, unit/int/e2e tests, `.http` scripts).
100
- - Legacy quick starts:
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.
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
- ## Tests
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
- Run `npm test` to execute the whole suite (unit + integration + e2e) or use:
237
+ ### Running tests
115
238
 
116
- - `npm run test:unit` - Unit tests only
117
- - `npm run test:int` - Integration tests only
118
- - `npm run test:e2e` - E2E tests only
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
- Issues and PRs are welcome! Please open a discussion or ticket if you are unsure about the direction of a change before coding.
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-OJGFQON5.js.map
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-4BVBIZX3.cjs.map
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"]}