@emmett-community/emmett-expressjs-with-openapi 0.3.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 +46 -0
- 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 +110 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +94 -1
- package/dist/index.d.ts +94 -1
- package/dist/index.js +97 -23
- 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 +2 -1
- 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
|
@@ -31,16 +31,19 @@ npm install @my-f-startup/firebase-auth-express # optional: Firebase Auth handl
|
|
|
31
31
|
### Peer Dependencies
|
|
32
32
|
|
|
33
33
|
Required:
|
|
34
|
+
|
|
34
35
|
- `@event-driven-io/emmett` ^0.39.1
|
|
35
36
|
- `express` ^4.19.2
|
|
36
37
|
- `express-async-errors` ^3.1.1
|
|
37
38
|
- `http-problem-details` ^0.1.5
|
|
38
39
|
|
|
39
40
|
TypeScript types:
|
|
41
|
+
|
|
40
42
|
- `@types/express` ^4.17.21
|
|
41
43
|
- `@types/supertest` ^6.0.2 (if using testing helpers)
|
|
42
44
|
|
|
43
45
|
Optional (feature-gated):
|
|
46
|
+
|
|
44
47
|
- `express-openapi-validator` ^5.3.7 (OpenAPI validation)
|
|
45
48
|
- `pino-http` ^9.0.0 (HTTP logging)
|
|
46
49
|
- `@my-f-startup/firebase-auth-express` 0.1.0 (Firebase Auth handlers)
|
|
@@ -103,6 +106,7 @@ const app = await getApplication({
|
|
|
103
106
|
```
|
|
104
107
|
|
|
105
108
|
Highlights:
|
|
109
|
+
|
|
106
110
|
- Enable/disable request or response validation, including per-field tweaks (coercion, unknown query params, removing additional fields, etc.).
|
|
107
111
|
- Register custom security handlers with `validateSecurity.handlers`.
|
|
108
112
|
- Serve the parsed spec via `serveSpec`, configure file upload limits, ignore health-check routes, or validate the spec itself.
|
|
@@ -156,25 +160,67 @@ const app = await getApplication({
|
|
|
156
160
|
|
|
157
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).
|
|
158
162
|
|
|
163
|
+
## Observability
|
|
164
|
+
|
|
165
|
+
This package supports optional logging and tracing.
|
|
166
|
+
|
|
167
|
+
### Logging
|
|
168
|
+
|
|
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
|
+
|
|
159
200
|
## API Reference
|
|
160
201
|
|
|
161
202
|
### Application
|
|
203
|
+
|
|
162
204
|
- `getApplication(options)` - Creates and configures the Express app.
|
|
163
205
|
- `startAPI(app, options)` - Starts the HTTP server.
|
|
164
206
|
|
|
165
207
|
### OpenAPI
|
|
208
|
+
|
|
166
209
|
- `createOpenApiValidatorOptions(apiSpec, options)` - Helper to assemble OpenAPI validator config.
|
|
167
210
|
- `isOpenApiValidatorAvailable()` - Type guard for optional validator dependency.
|
|
168
211
|
- `createFirebaseAuthSecurityHandlers(options)` - Firebase Auth security handlers.
|
|
169
212
|
- `registerHandlerModule(handlersPath, module)` - Manual registration for operation handlers.
|
|
170
213
|
|
|
171
214
|
### HTTP helpers
|
|
215
|
+
|
|
172
216
|
- `send`, `sendCreated`, `sendAccepted`, `sendProblem` - Standard HTTP responses.
|
|
173
217
|
|
|
174
218
|
### ETag helpers
|
|
219
|
+
|
|
175
220
|
- `toWeakETag`, `getETagFromIfMatch`, `getETagFromIfNotMatch`, `getETagValueFromIfMatch`, `setETag`.
|
|
176
221
|
|
|
177
222
|
### Testing helpers
|
|
223
|
+
|
|
178
224
|
- `ApiSpecification`, `ApiE2ESpecification`
|
|
179
225
|
- `expect`, `expectNewEvents`, `expectResponse`, `expectError`
|
|
180
226
|
|
|
@@ -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"]}
|
package/dist/index.cjs
CHANGED
|
@@ -3,12 +3,16 @@
|
|
|
3
3
|
var _chunkMRSGTROWcjs = require('./chunk-MRSGTROW.cjs');
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
var _chunkBY65ZALYcjs = require('./chunk-BY65ZALY.cjs');
|
|
7
|
+
|
|
8
|
+
|
|
6
9
|
var _chunkGS7T56RPcjs = require('./chunk-GS7T56RP.cjs');
|
|
7
10
|
|
|
8
11
|
// src/index.ts
|
|
9
12
|
require('express-async-errors');
|
|
10
13
|
|
|
11
14
|
// src/application.ts
|
|
15
|
+
var _api = require('@opentelemetry/api');
|
|
12
16
|
|
|
13
17
|
|
|
14
18
|
var _express = require('express'); var _express2 = _interopRequireDefault(_express);
|
|
@@ -18,7 +22,8 @@ var _module = require('module');
|
|
|
18
22
|
|
|
19
23
|
// src/middlewares/problemDetailsMiddleware.ts
|
|
20
24
|
var _httpproblemdetails = require('http-problem-details');
|
|
21
|
-
var problemDetailsMiddleware = (mapError) => (error, request, response, _next) => {
|
|
25
|
+
var problemDetailsMiddleware = (mapError, logger) => (error, request, response, _next) => {
|
|
26
|
+
_chunkBY65ZALYcjs.safeLog.error(logger, "Request error", error);
|
|
22
27
|
let problemDetails;
|
|
23
28
|
if (mapError) problemDetails = mapError(error, request);
|
|
24
29
|
problemDetails = _nullishCoalesce(problemDetails, () => ( defaultErrorToProblemDetailsMapping(error)));
|
|
@@ -42,6 +47,7 @@ var defaultErrorToProblemDetailsMapping = (error) => {
|
|
|
42
47
|
};
|
|
43
48
|
|
|
44
49
|
// src/application.ts
|
|
50
|
+
var tracer = _api.trace.getTracer("@emmett-community/emmett-expressjs-with-openapi");
|
|
45
51
|
var getApplication = async (options) => {
|
|
46
52
|
const app = _express2.default.call(void 0, );
|
|
47
53
|
const {
|
|
@@ -52,8 +58,15 @@ var getApplication = async (options) => {
|
|
|
52
58
|
disableUrlEncodingMiddleware,
|
|
53
59
|
disableProblemDetailsMiddleware,
|
|
54
60
|
pinoHttp,
|
|
55
|
-
openApiValidator
|
|
61
|
+
openApiValidator,
|
|
62
|
+
observability
|
|
56
63
|
} = options;
|
|
64
|
+
const logger = _optionalChain([observability, 'optionalAccess', _ => _.logger]);
|
|
65
|
+
_chunkBY65ZALYcjs.safeLog.debug(logger, "Initializing Express application", {
|
|
66
|
+
hasApis: !!_optionalChain([apis, 'optionalAccess', _2 => _2.length]),
|
|
67
|
+
hasOpenApiValidator: !!openApiValidator,
|
|
68
|
+
hasPinoHttp: !!pinoHttp
|
|
69
|
+
});
|
|
57
70
|
const router = _express.Router.call(void 0, );
|
|
58
71
|
app.set("etag", _nullishCoalesce(enableDefaultExpressEtag, () => ( false)));
|
|
59
72
|
if (pinoHttp !== void 0 && pinoHttp !== false) {
|
|
@@ -67,8 +80,9 @@ var getApplication = async (options) => {
|
|
|
67
80
|
const options2 = pinoHttp === true ? void 0 : pinoHttp;
|
|
68
81
|
const middleware = provider(options2);
|
|
69
82
|
app.use(middleware);
|
|
70
|
-
} catch (
|
|
71
|
-
|
|
83
|
+
} catch (error) {
|
|
84
|
+
_chunkBY65ZALYcjs.safeLog.warn(
|
|
85
|
+
logger,
|
|
72
86
|
"Pino HTTP configuration provided but pino-http package is not installed. Install it with: npm install pino-http"
|
|
73
87
|
);
|
|
74
88
|
throw new Error(
|
|
@@ -89,20 +103,52 @@ var getApplication = async (options) => {
|
|
|
89
103
|
activateESMResolver();
|
|
90
104
|
const handlersBasePath = typeof openApiValidator.operationHandlers === "string" ? openApiValidator.operationHandlers : openApiValidator.operationHandlers.basePath;
|
|
91
105
|
if (handlersBasePath) {
|
|
92
|
-
const { extractHandlerModules } = await Promise.resolve().then(() => _interopRequireWildcard(require("./openapi-parser-
|
|
93
|
-
const { importAndRegisterHandlers } = await Promise.resolve().then(() => _interopRequireWildcard(require("./handler-importer-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
106
|
+
const { extractHandlerModules } = await Promise.resolve().then(() => _interopRequireWildcard(require("./openapi-parser-PW5USNZ4.cjs")));
|
|
107
|
+
const { importAndRegisterHandlers } = await Promise.resolve().then(() => _interopRequireWildcard(require("./handler-importer-UFV7TKBN.cjs")));
|
|
108
|
+
const modules = await tracer.startActiveSpan(
|
|
109
|
+
"emmett.openapi.parse_spec",
|
|
110
|
+
async (span) => {
|
|
111
|
+
try {
|
|
112
|
+
const result = await extractHandlerModules(
|
|
113
|
+
openApiValidator.apiSpec,
|
|
114
|
+
handlersBasePath,
|
|
115
|
+
logger
|
|
116
|
+
);
|
|
117
|
+
span.setAttribute("emmett.handlers.count", result.length);
|
|
118
|
+
span.setStatus({ code: _api.SpanStatusCode.OK });
|
|
119
|
+
return result;
|
|
120
|
+
} catch (error) {
|
|
121
|
+
span.recordException(error);
|
|
122
|
+
span.setStatus({ code: _api.SpanStatusCode.ERROR });
|
|
123
|
+
throw error;
|
|
124
|
+
} finally {
|
|
125
|
+
span.end();
|
|
126
|
+
}
|
|
102
127
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
128
|
+
);
|
|
129
|
+
const importedHandlers = await tracer.startActiveSpan(
|
|
130
|
+
"emmett.http.import_handlers",
|
|
131
|
+
async (span) => {
|
|
132
|
+
try {
|
|
133
|
+
const result = await importAndRegisterHandlers(modules, logger);
|
|
134
|
+
span.setAttribute(
|
|
135
|
+
"emmett.handlers.count",
|
|
136
|
+
Object.keys(result).length
|
|
137
|
+
);
|
|
138
|
+
span.setStatus({ code: _api.SpanStatusCode.OK });
|
|
139
|
+
return result;
|
|
140
|
+
} catch (error) {
|
|
141
|
+
_chunkBY65ZALYcjs.safeLog.error(logger, "Failed to auto-import handler modules", error);
|
|
142
|
+
span.recordException(error);
|
|
143
|
+
span.setStatus({ code: _api.SpanStatusCode.ERROR });
|
|
144
|
+
throw error;
|
|
145
|
+
} finally {
|
|
146
|
+
span.end();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
);
|
|
150
|
+
if (openApiValidator.initializeHandlers) {
|
|
151
|
+
await openApiValidator.initializeHandlers(importedHandlers);
|
|
106
152
|
}
|
|
107
153
|
}
|
|
108
154
|
} else {
|
|
@@ -138,8 +184,9 @@ var getApplication = async (options) => {
|
|
|
138
184
|
} else {
|
|
139
185
|
app.use(middleware);
|
|
140
186
|
}
|
|
141
|
-
} catch (
|
|
142
|
-
|
|
187
|
+
} catch (error) {
|
|
188
|
+
_chunkBY65ZALYcjs.safeLog.warn(
|
|
189
|
+
logger,
|
|
143
190
|
"OpenAPI validator configuration provided but express-openapi-validator package is not installed. Install it with: npm install express-openapi-validator"
|
|
144
191
|
);
|
|
145
192
|
throw new Error(
|
|
@@ -154,14 +201,15 @@ var getApplication = async (options) => {
|
|
|
154
201
|
app.use(router);
|
|
155
202
|
}
|
|
156
203
|
if (!disableProblemDetailsMiddleware)
|
|
157
|
-
app.use(problemDetailsMiddleware(mapError));
|
|
204
|
+
app.use(problemDetailsMiddleware(mapError, logger));
|
|
205
|
+
_chunkBY65ZALYcjs.safeLog.info(logger, "Express application initialized");
|
|
158
206
|
return app;
|
|
159
207
|
};
|
|
160
208
|
var startAPI = (app, options = { port: 3e3 }) => {
|
|
161
|
-
const { port } = options;
|
|
209
|
+
const { port, logger } = options;
|
|
162
210
|
const server = _http2.default.createServer(app);
|
|
163
211
|
server.on("listening", () => {
|
|
164
|
-
|
|
212
|
+
_chunkBY65ZALYcjs.safeLog.info(logger, "Server up listening", { port });
|
|
165
213
|
});
|
|
166
214
|
return server.listen(port);
|
|
167
215
|
};
|
|
@@ -215,10 +263,34 @@ var getETagValueFromIfMatch = (request) => {
|
|
|
215
263
|
};
|
|
216
264
|
|
|
217
265
|
// src/handler.ts
|
|
266
|
+
|
|
267
|
+
var tracer2 = _api.trace.getTracer("@emmett-community/emmett-expressjs-with-openapi");
|
|
218
268
|
var on = (handle) => async (request, response, _next) => {
|
|
219
269
|
const setResponse = await Promise.resolve(handle(request));
|
|
220
270
|
return setResponse(response);
|
|
221
271
|
};
|
|
272
|
+
var tracedOn = (handle, options) => async (request, response, _next) => {
|
|
273
|
+
const spanName = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _3 => _3.spanName]), () => ( "emmett.http.handle_request"));
|
|
274
|
+
return tracer2.startActiveSpan(spanName, async (span) => {
|
|
275
|
+
try {
|
|
276
|
+
span.setAttribute("http.method", request.method);
|
|
277
|
+
const route = (_nullishCoalesce(request.baseUrl, () => ( ""))) + (_nullishCoalesce(_optionalChain([request, 'access', _4 => _4.route, 'optionalAccess', _5 => _5.path]), () => ( request.path)));
|
|
278
|
+
span.setAttribute("http.route", route);
|
|
279
|
+
const setResponse = await Promise.resolve(handle(request));
|
|
280
|
+
setResponse(response);
|
|
281
|
+
span.setAttribute("http.status_code", response.statusCode);
|
|
282
|
+
span.setStatus({
|
|
283
|
+
code: response.statusCode >= 400 ? _api.SpanStatusCode.ERROR : _api.SpanStatusCode.OK
|
|
284
|
+
});
|
|
285
|
+
} catch (error) {
|
|
286
|
+
span.recordException(error);
|
|
287
|
+
span.setStatus({ code: _api.SpanStatusCode.ERROR });
|
|
288
|
+
throw error;
|
|
289
|
+
} finally {
|
|
290
|
+
span.end();
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
};
|
|
222
294
|
var OK = (options) => (response) => {
|
|
223
295
|
send(response, 200, options);
|
|
224
296
|
};
|
|
@@ -292,7 +364,7 @@ var createFirebaseAuthSecurityHandlers = (options = {}) => {
|
|
|
292
364
|
const isAuthenticated = await runMiddleware(middleware, req);
|
|
293
365
|
if (!isAuthenticated) return false;
|
|
294
366
|
if (!scopes.length) return true;
|
|
295
|
-
const roles = _optionalChain([req, 'optionalAccess',
|
|
367
|
+
const roles = _optionalChain([req, 'optionalAccess', _6 => _6.auth, 'optionalAccess', _7 => _7.token, 'optionalAccess', _8 => _8[roleClaim]]);
|
|
296
368
|
if (!Array.isArray(roles)) return false;
|
|
297
369
|
return scopes.every((scope) => roles.includes(scope));
|
|
298
370
|
}
|
|
@@ -303,24 +375,24 @@ var createFirebaseAuthSecurityHandlers = (options = {}) => {
|
|
|
303
375
|
var createOpenApiValidatorOptions = (apiSpec, options) => {
|
|
304
376
|
return {
|
|
305
377
|
apiSpec,
|
|
306
|
-
validateRequests: _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
307
|
-
validateResponses: _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
308
|
-
validateSecurity: _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
309
|
-
validateFormats: _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
310
|
-
operationHandlers: _optionalChain([options, 'optionalAccess',
|
|
311
|
-
ignorePaths: _optionalChain([options, 'optionalAccess',
|
|
312
|
-
validateApiSpec: _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
313
|
-
$refParser: _optionalChain([options, 'optionalAccess',
|
|
314
|
-
serveSpec: _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
315
|
-
fileUploader: _optionalChain([options, 'optionalAccess',
|
|
316
|
-
initializeHandlers: _optionalChain([options, 'optionalAccess',
|
|
378
|
+
validateRequests: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _9 => _9.validateRequests]), () => ( true)),
|
|
379
|
+
validateResponses: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _10 => _10.validateResponses]), () => ( false)),
|
|
380
|
+
validateSecurity: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _11 => _11.validateSecurity]), () => ( true)),
|
|
381
|
+
validateFormats: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _12 => _12.validateFormats]), () => ( true)),
|
|
382
|
+
operationHandlers: _optionalChain([options, 'optionalAccess', _13 => _13.operationHandlers]),
|
|
383
|
+
ignorePaths: _optionalChain([options, 'optionalAccess', _14 => _14.ignorePaths]),
|
|
384
|
+
validateApiSpec: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _15 => _15.validateApiSpec]), () => ( true)),
|
|
385
|
+
$refParser: _optionalChain([options, 'optionalAccess', _16 => _16.$refParser]),
|
|
386
|
+
serveSpec: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _17 => _17.serveSpec]), () => ( false)),
|
|
387
|
+
fileUploader: _optionalChain([options, 'optionalAccess', _18 => _18.fileUploader]),
|
|
388
|
+
initializeHandlers: _optionalChain([options, 'optionalAccess', _19 => _19.initializeHandlers])
|
|
317
389
|
};
|
|
318
390
|
};
|
|
319
391
|
var isOpenApiValidatorAvailable = async () => {
|
|
320
392
|
try {
|
|
321
393
|
await Promise.resolve().then(() => _interopRequireWildcard(require("express-openapi-validator")));
|
|
322
394
|
return true;
|
|
323
|
-
} catch (
|
|
395
|
+
} catch (e) {
|
|
324
396
|
return false;
|
|
325
397
|
}
|
|
326
398
|
};
|
|
@@ -504,5 +576,7 @@ var ApiSpecification = {
|
|
|
504
576
|
|
|
505
577
|
|
|
506
578
|
|
|
507
|
-
|
|
579
|
+
|
|
580
|
+
|
|
581
|
+
exports.Accepted = Accepted; exports.ApiE2ESpecification = ApiE2ESpecification; exports.ApiSpecification = ApiSpecification; exports.BadRequest = BadRequest; exports.Conflict = Conflict; exports.Created = Created; exports.DefaultHttpProblemResponseOptions = DefaultHttpProblemResponseOptions; exports.DefaultHttpResponseOptions = DefaultHttpResponseOptions; exports.ETagErrors = ETagErrors; exports.Forbidden = Forbidden; exports.HeaderNames = HeaderNames; exports.HttpProblem = HttpProblem; exports.HttpResponse = HttpResponse; exports.NoContent = NoContent; exports.NotFound = NotFound; exports.OK = OK; exports.PreconditionFailed = PreconditionFailed; exports.WeakETagRegex = WeakETagRegex; exports.createFirebaseAuthSecurityHandlers = createFirebaseAuthSecurityHandlers; exports.createOpenApiValidatorOptions = createOpenApiValidatorOptions; exports.existingStream = existingStream; exports.expect = expect; exports.expectError = expectError; exports.expectNewEvents = expectNewEvents; exports.expectResponse = expectResponse; exports.getApplication = getApplication; exports.getETagFromIfMatch = getETagFromIfMatch; exports.getETagFromIfNotMatch = getETagFromIfNotMatch; exports.getETagValueFromIfMatch = getETagValueFromIfMatch; exports.getWeakETagValue = getWeakETagValue; exports.isOpenApiValidatorAvailable = isOpenApiValidatorAvailable; exports.isWeakETag = isWeakETag; exports.on = on; exports.registerHandlerModule = _chunkMRSGTROWcjs.registerHandlerModule; exports.safeLog = _chunkBY65ZALYcjs.safeLog; exports.send = send; exports.sendAccepted = sendAccepted; exports.sendCreated = sendCreated; exports.sendProblem = sendProblem; exports.setETag = setETag; exports.startAPI = startAPI; exports.toWeakETag = toWeakETag; exports.tracedOn = tracedOn;
|
|
508
582
|
//# sourceMappingURL=index.cjs.map
|