@emmett-community/emmett-expressjs-with-openapi 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +206 -23
- package/dist/chunk-GS7T56RP.cjs.map +1 -1
- package/dist/chunk-MRSGTROW.cjs.map +1 -1
- package/dist/esm-resolver-I5MW2PIQ.cjs.map +1 -1
- package/dist/handler-importer-4BVBIZX3.cjs.map +1 -1
- package/dist/index.cjs +95 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -1
- package/dist/index.d.ts +48 -1
- package/dist/index.js +81 -1
- package/dist/index.js.map +1 -1
- package/dist/{openapi-parser-6EGURSGG.js → openapi-parser-CCYU636U.js} +1 -1
- package/dist/openapi-parser-CCYU636U.js.map +1 -0
- package/dist/{openapi-parser-KI7BS3DC.cjs → openapi-parser-NFUD7ZGZ.cjs} +1 -1
- package/dist/openapi-parser-NFUD7ZGZ.cjs.map +1 -0
- package/package.json +20 -5
- package/dist/openapi-parser-6EGURSGG.js.map +0 -1
- package/dist/openapi-parser-KI7BS3DC.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,30 @@ Express.js utilities for Emmett applications that want OpenAPI 3.x validation wi
|
|
|
12
23
|
|
|
13
24
|
```bash
|
|
14
25
|
npm install @emmett-community/emmett-expressjs-with-openapi
|
|
15
|
-
npm install express-openapi-validator
|
|
26
|
+
npm install express-openapi-validator # optional: OpenAPI validation
|
|
27
|
+
npm install pino-http # optional: HTTP logging
|
|
28
|
+
npm install @my-f-startup/firebase-auth-express # optional: Firebase Auth handlers
|
|
16
29
|
```
|
|
17
30
|
|
|
18
|
-
|
|
31
|
+
### Peer Dependencies
|
|
32
|
+
|
|
33
|
+
Required:
|
|
34
|
+
- `@event-driven-io/emmett` ^0.39.1
|
|
35
|
+
- `express` ^4.19.2
|
|
36
|
+
- `express-async-errors` ^3.1.1
|
|
37
|
+
- `http-problem-details` ^0.1.5
|
|
38
|
+
|
|
39
|
+
TypeScript types:
|
|
40
|
+
- `@types/express` ^4.17.21
|
|
41
|
+
- `@types/supertest` ^6.0.2 (if using testing helpers)
|
|
42
|
+
|
|
43
|
+
Optional (feature-gated):
|
|
44
|
+
- `express-openapi-validator` ^5.3.7 (OpenAPI validation)
|
|
45
|
+
- `pino-http` ^9.0.0 (HTTP logging)
|
|
46
|
+
- `@my-f-startup/firebase-auth-express` 0.1.0 (Firebase Auth handlers)
|
|
47
|
+
- `supertest` ^7.0.0 (required by testing helpers)
|
|
48
|
+
|
|
49
|
+
Versions follow what is listed in `package.json`.
|
|
19
50
|
|
|
20
51
|
## Quick start
|
|
21
52
|
|
|
@@ -23,15 +54,33 @@ Other peer requirements (`express`, `@event-driven-io/emmett`, etc.) follow the
|
|
|
23
54
|
import {
|
|
24
55
|
getApplication,
|
|
25
56
|
createOpenApiValidatorOptions,
|
|
57
|
+
startAPI,
|
|
26
58
|
} from '@emmett-community/emmett-expressjs-with-openapi';
|
|
27
59
|
|
|
28
|
-
const app = getApplication({
|
|
60
|
+
const app = await getApplication({
|
|
29
61
|
apis: [myApi],
|
|
30
62
|
openApiValidator: createOpenApiValidatorOptions('./openapi.yaml', {
|
|
31
63
|
validateRequests: true,
|
|
32
64
|
validateResponses: process.env.NODE_ENV === 'development',
|
|
33
65
|
}),
|
|
34
66
|
});
|
|
67
|
+
|
|
68
|
+
startAPI(app, { port: 3000 });
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Configuration
|
|
72
|
+
|
|
73
|
+
### OpenAPI validation
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
const app = await getApplication({
|
|
77
|
+
apis: [myApi],
|
|
78
|
+
openApiValidator: createOpenApiValidatorOptions('./openapi.yaml', {
|
|
79
|
+
validateRequests: true,
|
|
80
|
+
validateResponses: true,
|
|
81
|
+
operationHandlers: './handlers',
|
|
82
|
+
}),
|
|
83
|
+
});
|
|
35
84
|
```
|
|
36
85
|
|
|
37
86
|
Prefer to parse the spec once and share it? Load the `.yml` document manually and reuse it for routing, validation, and automatic `operationHandlers`.
|
|
@@ -41,9 +90,11 @@ import path from 'node:path';
|
|
|
41
90
|
import { readFileSync } from 'node:fs';
|
|
42
91
|
import { parse } from 'yaml';
|
|
43
92
|
|
|
44
|
-
const spec = parse(
|
|
93
|
+
const spec = parse(
|
|
94
|
+
readFileSync(new URL('./openapi.yml', import.meta.url), 'utf-8'),
|
|
95
|
+
);
|
|
45
96
|
|
|
46
|
-
const app = getApplication({
|
|
97
|
+
const app = await getApplication({
|
|
47
98
|
apis: [usersApi],
|
|
48
99
|
openApiValidator: createOpenApiValidatorOptions(spec, {
|
|
49
100
|
operationHandlers: path.join(path.dirname(import.meta.url), './handlers'),
|
|
@@ -51,28 +102,85 @@ const app = getApplication({
|
|
|
51
102
|
});
|
|
52
103
|
```
|
|
53
104
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
`createOpenApiValidatorOptions` forwards every option from `express-openapi-validator`, so you can:
|
|
57
|
-
|
|
105
|
+
Highlights:
|
|
58
106
|
- Enable/disable request or response validation, including per-field tweaks (coercion, unknown query params, removing additional fields, etc.).
|
|
59
107
|
- Register custom security handlers with `validateSecurity.handlers`.
|
|
60
108
|
- Serve the parsed spec via `serveSpec`, configure file upload limits, ignore health-check routes, or validate the spec itself.
|
|
61
109
|
- Reuse `$ref` parsing, custom formats, and file upload middleware exactly as in the upstream validator.
|
|
62
110
|
|
|
63
|
-
|
|
111
|
+
### Logging (optional)
|
|
64
112
|
|
|
65
|
-
|
|
113
|
+
Install the optional peer to enable HTTP request/response logging:
|
|
66
114
|
|
|
67
|
-
|
|
115
|
+
```bash
|
|
116
|
+
npm install pino-http
|
|
117
|
+
```
|
|
68
118
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
119
|
+
```typescript
|
|
120
|
+
const app = await getApplication({
|
|
121
|
+
pinoHttp: true, // defaults
|
|
122
|
+
// or:
|
|
123
|
+
pinoHttp: { autoLogging: false },
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
See the full options in the [`pino-http` docs](https://github.com/pinojs/pino-http).
|
|
74
128
|
|
|
75
|
-
|
|
129
|
+
### Firebase Auth (optional)
|
|
130
|
+
|
|
131
|
+
If you use Firebase Authentication, install the optional peer and plug it into OpenAPI security handlers:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
npm install @my-f-startup/firebase-auth-express
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
import admin from 'firebase-admin';
|
|
139
|
+
import {
|
|
140
|
+
createFirebaseAuthSecurityHandlers,
|
|
141
|
+
createOpenApiValidatorOptions,
|
|
142
|
+
getApplication,
|
|
143
|
+
} from '@emmett-community/emmett-expressjs-with-openapi';
|
|
144
|
+
|
|
145
|
+
admin.initializeApp();
|
|
146
|
+
|
|
147
|
+
const app = await getApplication({
|
|
148
|
+
apis: [myApi],
|
|
149
|
+
openApiValidator: createOpenApiValidatorOptions('./openapi.yaml', {
|
|
150
|
+
validateSecurity: {
|
|
151
|
+
handlers: createFirebaseAuthSecurityHandlers(),
|
|
152
|
+
},
|
|
153
|
+
}),
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`@my-f-startup/firebase-auth-express` relies on `firebase-admin`. Make sure the Admin SDK is installed, initialized, and configured for your environment (credentials or emulator).
|
|
158
|
+
|
|
159
|
+
## API Reference
|
|
160
|
+
|
|
161
|
+
### Application
|
|
162
|
+
- `getApplication(options)` - Creates and configures the Express app.
|
|
163
|
+
- `startAPI(app, options)` - Starts the HTTP server.
|
|
164
|
+
|
|
165
|
+
### OpenAPI
|
|
166
|
+
- `createOpenApiValidatorOptions(apiSpec, options)` - Helper to assemble OpenAPI validator config.
|
|
167
|
+
- `isOpenApiValidatorAvailable()` - Type guard for optional validator dependency.
|
|
168
|
+
- `createFirebaseAuthSecurityHandlers(options)` - Firebase Auth security handlers.
|
|
169
|
+
- `registerHandlerModule(handlersPath, module)` - Manual registration for operation handlers.
|
|
170
|
+
|
|
171
|
+
### HTTP helpers
|
|
172
|
+
- `send`, `sendCreated`, `sendAccepted`, `sendProblem` - Standard HTTP responses.
|
|
173
|
+
|
|
174
|
+
### ETag helpers
|
|
175
|
+
- `toWeakETag`, `getETagFromIfMatch`, `getETagFromIfNotMatch`, `getETagValueFromIfMatch`, `setETag`.
|
|
176
|
+
|
|
177
|
+
### Testing helpers
|
|
178
|
+
- `ApiSpecification`, `ApiE2ESpecification`
|
|
179
|
+
- `expect`, `expectNewEvents`, `expectResponse`, `expectError`
|
|
180
|
+
|
|
181
|
+
See [`docs/openapi-validation.md`](docs/openapi-validation.md) for the full matrix of options and extended examples.
|
|
182
|
+
|
|
183
|
+
## Testing
|
|
76
184
|
|
|
77
185
|
The package includes comprehensive test coverage:
|
|
78
186
|
|
|
@@ -80,21 +188,96 @@ The package includes comprehensive test coverage:
|
|
|
80
188
|
- **Integration tests** (`test/integration/`) - Basic routing, OpenAPI validation, operation handlers, optimistic concurrency
|
|
81
189
|
- **E2E tests** (`test/e2e/`) - End-to-end workflows for all features
|
|
82
190
|
|
|
83
|
-
|
|
191
|
+
### Running tests
|
|
84
192
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
193
|
+
```bash
|
|
194
|
+
# Unit tests
|
|
195
|
+
npm run test:unit
|
|
196
|
+
|
|
197
|
+
# Integration tests
|
|
198
|
+
npm run test:int
|
|
199
|
+
|
|
200
|
+
# E2E tests
|
|
201
|
+
npm run test:e2e
|
|
202
|
+
|
|
203
|
+
# All tests
|
|
204
|
+
npm test
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Examples
|
|
208
|
+
|
|
209
|
+
Working examples live under `examples/`:
|
|
210
|
+
|
|
211
|
+
- `examples/shopping-cart` – feature-complete Emmett sample (business logic, memory store/publisher, security handlers, OpenAPI file, unit/int/e2e tests, `.http` scripts).
|
|
212
|
+
- Legacy quick starts:
|
|
213
|
+
- `examples/basic` – manual routes + validation (minimal scaffolding).
|
|
214
|
+
- `examples/with-security` – standalone security handler demo.
|
|
215
|
+
- `examples/operation-handlers` – barebones `operationHandlers` showcase.
|
|
216
|
+
- `examples/firebase-auth` – Firebase Auth security handlers with operation handlers.
|
|
88
217
|
|
|
89
218
|
## Documentation
|
|
90
219
|
|
|
91
220
|
- **Guide:** [`docs/openapi-validation.md`](docs/openapi-validation.md) – authoritative reference for configuration, advanced usage, and troubleshooting.
|
|
92
221
|
- **Emmett docs:** <https://event-driven-io.github.io/emmett/>
|
|
93
222
|
|
|
223
|
+
## Compatibility
|
|
224
|
+
|
|
225
|
+
- **Node.js**: tested in CI with 24.x
|
|
226
|
+
- **Emmett**: ^0.39.1
|
|
227
|
+
- **Express**: ^4.19.2
|
|
228
|
+
|
|
94
229
|
## Contributing
|
|
95
230
|
|
|
96
|
-
|
|
231
|
+
Contributions are welcome! Please:
|
|
232
|
+
|
|
233
|
+
1. Fork the repository
|
|
234
|
+
2. Create a feature branch
|
|
235
|
+
3. Add tests for new functionality
|
|
236
|
+
4. Ensure all tests pass
|
|
237
|
+
5. Submit a pull request
|
|
238
|
+
|
|
239
|
+
## Development
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
# Install dependencies
|
|
243
|
+
npm install
|
|
244
|
+
|
|
245
|
+
# Build
|
|
246
|
+
npm run build
|
|
247
|
+
|
|
248
|
+
# Type-check
|
|
249
|
+
npm run build:ts
|
|
250
|
+
|
|
251
|
+
# Run tests
|
|
252
|
+
npm test
|
|
253
|
+
|
|
254
|
+
# Run unit tests only
|
|
255
|
+
npm run test:unit
|
|
256
|
+
|
|
257
|
+
# Run integration tests
|
|
258
|
+
npm run test:int
|
|
259
|
+
|
|
260
|
+
# Run E2E tests
|
|
261
|
+
npm run test:e2e
|
|
262
|
+
```
|
|
97
263
|
|
|
98
264
|
## License
|
|
99
265
|
|
|
100
266
|
MIT
|
|
267
|
+
|
|
268
|
+
## Related Packages
|
|
269
|
+
|
|
270
|
+
- [@event-driven-io/emmett](https://github.com/event-driven-io/emmett) - Core Emmett framework
|
|
271
|
+
- [@emmett-community/emmett-google-firestore](https://github.com/emmett-community/emmett-google-firestore) - Firestore event store
|
|
272
|
+
- [@emmett-community/emmett-google-realtime-db](https://github.com/emmett-community/emmett-google-realtime-db) - Realtime Database inline projections
|
|
273
|
+
- [@emmett-community/emmett-google-pubsub](https://github.com/emmett-community/emmett-google-pubsub) - Pub/Sub message bus
|
|
274
|
+
- [@event-driven-io/emmett-mongodb](https://github.com/event-driven-io/emmett/tree/main/src/packages/emmett-mongodb) - MongoDB event store
|
|
275
|
+
|
|
276
|
+
## Support
|
|
277
|
+
|
|
278
|
+
- [GitHub Issues](https://github.com/emmett-community/emmett-expressjs-with-openapi/issues)
|
|
279
|
+
- [Emmett Documentation](https://event-driven-io.github.io/emmett/)
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
Made with ❤️ by the Emmett Community
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/emmett-expressjs-with-openapi/emmett-expressjs-with-openapi/dist/chunk-GS7T56RP.cjs","../node_modules/tsup/assets/cjs_shims.js"],"names":[],"mappings":"AAAA;ACKA,IAAM,iBAAA,EAAmB,CAAA,EAAA,GACvB,OAAO,SAAA,IAAa,YAAA,EAChB,IAAI,GAAA,CAAI,CAAA,KAAA,EAAQ,UAAU,CAAA,CAAA;AAK8B;ADT4B;AACA;AACA;AACA","file":"/home/runner/work/emmett-expressjs-with-openapi/emmett-expressjs-with-openapi/dist/chunk-GS7T56RP.cjs","sourcesContent":[null,"// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () => \n typeof document === \"undefined\" \n ? new URL(`file:${__filename}`).href \n : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') \n ? document.currentScript.src \n : new URL(\"main.js\", document.baseURI).href;\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/emmett-expressjs-with-openapi/emmett-expressjs-with-openapi/dist/chunk-MRSGTROW.cjs","../src/internal/esm-resolver.ts"],"names":["require"],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B;AACA;AC6BA,gCAA8B;AAC9B,wEAAiB;AAEjB,IAAI,UAAA,EAAY,KAAA;AAChB,IAAM,YAAA,kBAAc,IAAI,GAAA,CAAiB,CAAA;AAMlC,IAAM,sBAAA,EAAwB,CAAC,UAAA,EAAoB,aAAA,EAAA,GAA6B;AACrF,EAAA,WAAA,CAAY,GAAA,CAAI,UAAA,EAAY,aAAa,CAAA;AAC3C,CAAA;AAKO,IAAM,oBAAA,EAAsB,CAAA,EAAA,GAAY;AAC7C,EAAA,GAAA,CAAI,SAAA,EAAW;AACb,IAAA,MAAA;AAAA,EACF;AAEA,EAAA,MAAMA,SAAAA,EAAU,mCAAA,+BAA6B,CAAA;AAC7C,EAAA,MAAM,OAAA,EAASA,QAAAA,CAAQ,QAAQ,CAAA;AAC/B,EAAA,MAAM,aAAA,EAAe,MAAA,CAAO,KAAA;AAE5B,EAAA,MAAA,CAAO,MAAA,EAAQ,QAAA,CAAU,OAAA,EAAiB,MAAA,EAAa,MAAA,EAAiB;AACtE,IAAA,MAAM,OAAA,kBAAS,MAAA,2BAAQ,UAAA;AACvB,IAAA,MAAM,mBAAA,kBAAqB,MAAA,6BAAQ,QAAA,mBAAS,2BAA2B,GAAA;AACvE,IAAA,MAAM,gBAAA,EAAkB,OAAA,CAAQ,QAAA,CAAS,UAAU,CAAA;AAEnD,IAAA,GAAA,CAAI,mBAAA,GAAsB,eAAA,EAAiB;AACzC,MAAA,MAAM,aAAA,EAAe,cAAA,CAAK,UAAA,CAAW,OAAO,EAAA,EACxC,QAAA,EACA,cAAA,CAAK,OAAA,CAAQ,cAAA,CAAK,OAAA,CAAQ,MAAM,CAAA,EAAG,OAAO,CAAA;AAG9C,MAAA,GAAA,CAAI,WAAA,CAAY,GAAA,CAAI,YAAY,CAAA,EAAG;AACjC,QAAA,OAAO,WAAA,CAAY,GAAA,CAAI,YAAY,CAAA;AAAA,MACrC;AAEA,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,+BAAA,EAAkC,YAAY,CAAA,gEAAA;AAAA,MAEhD,CAAA;AAAA,IACF;AAEA,IAAA,OAAO,YAAA,CAAa,IAAA,CAAK,IAAA,EAAM,OAAA,EAAS,MAAA,EAAQ,MAAM,CAAA;AAAA,EACxD,CAAA;AAEA,EAAA,UAAA,EAAY,IAAA;AACd,CAAA;ADhDA;AACA;AACE;AACA;AACF,yGAAC","file":"/home/runner/work/emmett-expressjs-with-openapi/emmett-expressjs-with-openapi/dist/chunk-MRSGTROW.cjs","sourcesContent":[null,"/**\n * ESM Resolver for express-openapi-validator operation handlers.\n *\n * INTERNAL MODULE - Not part of public API.\n *\n * PROBLEM:\n * When using TypeScript runtime (tsx) with express-openapi-validator's operationHandlers:\n * - Our code uses `import()` to load handlers (ESM)\n * - express-openapi-validator uses `require()` to load handlers (CJS)\n * - Node.js maintains separate module caches for ESM and CJS\n * - This creates TWO separate instances of the same handler module\n * - Dependencies injected via module-level variables in one instance don't reach the other\n *\n * SOLUTION:\n * This module monkey-patches Module.prototype.require to intercept when\n * express-openapi-validator loads operation handlers and forces it to use\n * dynamic import() instead of require(). This ensures both sides share the\n * same ESM module instance, allowing module-level variables to work correctly.\n *\n * USAGE:\n * This module is automatically activated by getApplication() when operationHandlers\n * are configured. Applications don't need to import or configure anything.\n *\n * LIMITATIONS:\n * - Relies on heuristic detection (caller path includes 'express-openapi-validator')\n * - May break if express-openapi-validator changes its internal loading mechanism\n * - Adds \"magic\" behavior that may not be immediately obvious to developers\n *\n * FUTURE:\n * If express-openapi-validator migrates to native ESM, this resolver becomes\n * unnecessary and will safely become a no-op.\n */\n\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\n\nlet isPatched = false;\nconst moduleCache = new Map<string, any>();\n\n/**\n * Registers a pre-loaded ESM module so it can be returned synchronously\n * when express-openapi-validator tries to require() it.\n */\nexport const registerHandlerModule = (modulePath: string, moduleExports: any): void => {\n moduleCache.set(modulePath, moduleExports);\n};\n\n/**\n * Activates the ESM resolver for express-openapi-validator handler loading.\n */\nexport const activateESMResolver = (): void => {\n if (isPatched) {\n return;\n }\n\n const require = createRequire(import.meta.url);\n const Module = require('module');\n const originalLoad = Module._load;\n\n Module._load = function (request: string, parent: any, isMain: boolean) {\n const caller = parent?.filename;\n const isValidatorLoading = caller?.includes('express-openapi-validator');\n const isHandlerModule = request.includes('handlers');\n\n if (isValidatorLoading && isHandlerModule) {\n const absolutePath = path.isAbsolute(request)\n ? request\n : path.resolve(path.dirname(caller), request);\n\n // Check if we have this module pre-registered\n if (moduleCache.has(absolutePath)) {\n return moduleCache.get(absolutePath);\n }\n\n throw new Error(\n `Handler module not registered: ${absolutePath}. ` +\n `Make sure initializeHandlers imports and registers the module.`\n );\n }\n\n return originalLoad.call(this, request, parent, isMain);\n };\n\n isPatched = true;\n};\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/emmett-expressjs-with-openapi/emmett-expressjs-with-openapi/dist/esm-resolver-I5MW2PIQ.cjs"],"names":[],"mappings":"AAAA;AACE;AACA;AACF,wDAA6B;AAC7B,gCAA6B;AAC7B;AACE;AACA;AACF,6IAAC","file":"/home/runner/work/emmett-expressjs-with-openapi/emmett-expressjs-with-openapi/dist/esm-resolver-I5MW2PIQ.cjs"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/emmett-expressjs-with-openapi/emmett-expressjs-with-openapi/dist/handler-importer-4BVBIZX3.cjs","../src/internal/handler-importer.ts"],"names":[],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B,gCAA6B;AAC7B;AACA;ACIA,0BAA8B;AAY9B,MAAA,SAAsB,yBAAA,CACpB,OAAA,EACiC;AACjC,EAAA,MAAM,iBAAA,EAA2C,CAAC,CAAA;AAElD,EAAA,IAAA,CAAA,MAAW,OAAA,GAAU,OAAA,EAAS;AAC5B,IAAA,IAAI;AAEF,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;AAAA,IACxC,EAAA,MAAA,CAAS,KAAA,EAAO;AACd,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;AD1BA;AACA;AACA","file":"/home/runner/work/emmett-expressjs-with-openapi/emmett-expressjs-with-openapi/dist/handler-importer-4BVBIZX3.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 { 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 * @returns Object containing all imported modules, keyed by module name\n */\nexport async function importAndRegisterHandlers(\n modules: HandlerModuleInfo[],\n): Promise<ImportedHandlerModules> {\n const importedHandlers: ImportedHandlerModules = {};\n\n for (const module of modules) {\n try {\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 } catch (error) {\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
|
@@ -51,10 +51,31 @@ var getApplication = async (options) => {
|
|
|
51
51
|
disableJsonMiddleware,
|
|
52
52
|
disableUrlEncodingMiddleware,
|
|
53
53
|
disableProblemDetailsMiddleware,
|
|
54
|
+
pinoHttp,
|
|
54
55
|
openApiValidator
|
|
55
56
|
} = options;
|
|
56
57
|
const router = _express.Router.call(void 0, );
|
|
57
58
|
app.set("etag", _nullishCoalesce(enableDefaultExpressEtag, () => ( false)));
|
|
59
|
+
if (pinoHttp !== void 0 && pinoHttp !== false) {
|
|
60
|
+
try {
|
|
61
|
+
const require2 = _module.createRequire.call(void 0, _chunkGS7T56RPcjs.importMetaUrl);
|
|
62
|
+
const mod = require2("pino-http");
|
|
63
|
+
const provider = _nullishCoalesce(mod.default, () => ( mod));
|
|
64
|
+
if (typeof provider !== "function") {
|
|
65
|
+
throw new Error("Invalid pino-http module: missing default export");
|
|
66
|
+
}
|
|
67
|
+
const options2 = pinoHttp === true ? void 0 : pinoHttp;
|
|
68
|
+
const middleware = provider(options2);
|
|
69
|
+
app.use(middleware);
|
|
70
|
+
} catch (e) {
|
|
71
|
+
console.warn(
|
|
72
|
+
"Pino HTTP configuration provided but pino-http package is not installed. Install it with: npm install pino-http"
|
|
73
|
+
);
|
|
74
|
+
throw new Error(
|
|
75
|
+
"pino-http package is required when pinoHttp option is used"
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
58
79
|
if (!disableJsonMiddleware) app.use(_express2.default.json());
|
|
59
80
|
if (!disableUrlEncodingMiddleware)
|
|
60
81
|
app.use(
|
|
@@ -68,7 +89,7 @@ var getApplication = async (options) => {
|
|
|
68
89
|
activateESMResolver();
|
|
69
90
|
const handlersBasePath = typeof openApiValidator.operationHandlers === "string" ? openApiValidator.operationHandlers : openApiValidator.operationHandlers.basePath;
|
|
70
91
|
if (handlersBasePath) {
|
|
71
|
-
const { extractHandlerModules } = await Promise.resolve().then(() => _interopRequireWildcard(require("./openapi-parser-
|
|
92
|
+
const { extractHandlerModules } = await Promise.resolve().then(() => _interopRequireWildcard(require("./openapi-parser-NFUD7ZGZ.cjs")));
|
|
72
93
|
const { importAndRegisterHandlers } = await Promise.resolve().then(() => _interopRequireWildcard(require("./handler-importer-4BVBIZX3.cjs")));
|
|
73
94
|
try {
|
|
74
95
|
const modules = await extractHandlerModules(
|
|
@@ -117,7 +138,7 @@ var getApplication = async (options) => {
|
|
|
117
138
|
} else {
|
|
118
139
|
app.use(middleware);
|
|
119
140
|
}
|
|
120
|
-
} catch (
|
|
141
|
+
} catch (e2) {
|
|
121
142
|
console.warn(
|
|
122
143
|
"OpenAPI validator configuration provided but express-openapi-validator package is not installed. Install it with: npm install express-openapi-validator"
|
|
123
144
|
);
|
|
@@ -220,28 +241,86 @@ var HttpProblem = (statusCode, options) => (response) => {
|
|
|
220
241
|
sendProblem(response, statusCode, options);
|
|
221
242
|
};
|
|
222
243
|
|
|
244
|
+
// src/openapi/firebase-auth.ts
|
|
245
|
+
var loadFirebaseAuth = async () => {
|
|
246
|
+
try {
|
|
247
|
+
const mod = await Promise.resolve().then(() => _interopRequireWildcard(require("@my-f-startup/firebase-auth-express")));
|
|
248
|
+
const provider = _nullishCoalesce(mod.default, () => ( mod));
|
|
249
|
+
const firebaseAuthMiddleware = provider.firebaseAuthMiddleware;
|
|
250
|
+
if (typeof firebaseAuthMiddleware !== "function") {
|
|
251
|
+
throw new Error(
|
|
252
|
+
"Invalid @my-f-startup/firebase-auth-express module: missing firebaseAuthMiddleware export"
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
return provider;
|
|
256
|
+
} catch (error) {
|
|
257
|
+
const message = "@my-f-startup/firebase-auth-express is required for createFirebaseAuthSecurityHandlers. Install it with: npm install @my-f-startup/firebase-auth-express";
|
|
258
|
+
throw new Error(message, { cause: error });
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
var createNullResponse = () => {
|
|
262
|
+
const res = {};
|
|
263
|
+
res.status = () => res;
|
|
264
|
+
res.json = () => res;
|
|
265
|
+
res.send = () => res;
|
|
266
|
+
res.end = () => res;
|
|
267
|
+
res.set = () => res;
|
|
268
|
+
return res;
|
|
269
|
+
};
|
|
270
|
+
var runMiddleware = async (middleware, req) => {
|
|
271
|
+
return new Promise((resolve) => {
|
|
272
|
+
let nextCalled = false;
|
|
273
|
+
const res = createNullResponse();
|
|
274
|
+
const next = () => {
|
|
275
|
+
nextCalled = true;
|
|
276
|
+
resolve(true);
|
|
277
|
+
};
|
|
278
|
+
Promise.resolve(middleware(req, res, next)).then(() => {
|
|
279
|
+
if (!nextCalled) resolve(false);
|
|
280
|
+
}).catch(() => resolve(false));
|
|
281
|
+
});
|
|
282
|
+
};
|
|
283
|
+
var createFirebaseAuthSecurityHandlers = (options = {}) => {
|
|
284
|
+
const securitySchemeName = _nullishCoalesce(options.securitySchemeName, () => ( "bearerAuth"));
|
|
285
|
+
const roleClaim = _nullishCoalesce(options.roleClaim, () => ( "roles"));
|
|
286
|
+
return {
|
|
287
|
+
[securitySchemeName]: async (req, scopes, _schema) => {
|
|
288
|
+
const { firebaseAuthMiddleware } = await loadFirebaseAuth();
|
|
289
|
+
const middleware = firebaseAuthMiddleware({
|
|
290
|
+
authClient: options.authClient
|
|
291
|
+
});
|
|
292
|
+
const isAuthenticated = await runMiddleware(middleware, req);
|
|
293
|
+
if (!isAuthenticated) return false;
|
|
294
|
+
if (!scopes.length) return true;
|
|
295
|
+
const roles = _optionalChain([req, 'optionalAccess', _ => _.auth, 'optionalAccess', _2 => _2.token, 'optionalAccess', _3 => _3[roleClaim]]);
|
|
296
|
+
if (!Array.isArray(roles)) return false;
|
|
297
|
+
return scopes.every((scope) => roles.includes(scope));
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
};
|
|
301
|
+
|
|
223
302
|
// src/openapi/index.ts
|
|
224
303
|
var createOpenApiValidatorOptions = (apiSpec, options) => {
|
|
225
304
|
return {
|
|
226
305
|
apiSpec,
|
|
227
|
-
validateRequests: _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
228
|
-
validateResponses: _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
229
|
-
validateSecurity: _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
230
|
-
validateFormats: _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
231
|
-
operationHandlers: _optionalChain([options, 'optionalAccess',
|
|
232
|
-
ignorePaths: _optionalChain([options, 'optionalAccess',
|
|
233
|
-
validateApiSpec: _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
234
|
-
$refParser: _optionalChain([options, 'optionalAccess',
|
|
235
|
-
serveSpec: _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
236
|
-
fileUploader: _optionalChain([options, 'optionalAccess',
|
|
237
|
-
initializeHandlers: _optionalChain([options, 'optionalAccess',
|
|
306
|
+
validateRequests: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _4 => _4.validateRequests]), () => ( true)),
|
|
307
|
+
validateResponses: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _5 => _5.validateResponses]), () => ( false)),
|
|
308
|
+
validateSecurity: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _6 => _6.validateSecurity]), () => ( true)),
|
|
309
|
+
validateFormats: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _7 => _7.validateFormats]), () => ( true)),
|
|
310
|
+
operationHandlers: _optionalChain([options, 'optionalAccess', _8 => _8.operationHandlers]),
|
|
311
|
+
ignorePaths: _optionalChain([options, 'optionalAccess', _9 => _9.ignorePaths]),
|
|
312
|
+
validateApiSpec: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _10 => _10.validateApiSpec]), () => ( true)),
|
|
313
|
+
$refParser: _optionalChain([options, 'optionalAccess', _11 => _11.$refParser]),
|
|
314
|
+
serveSpec: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _12 => _12.serveSpec]), () => ( false)),
|
|
315
|
+
fileUploader: _optionalChain([options, 'optionalAccess', _13 => _13.fileUploader]),
|
|
316
|
+
initializeHandlers: _optionalChain([options, 'optionalAccess', _14 => _14.initializeHandlers])
|
|
238
317
|
};
|
|
239
318
|
};
|
|
240
319
|
var isOpenApiValidatorAvailable = async () => {
|
|
241
320
|
try {
|
|
242
321
|
await Promise.resolve().then(() => _interopRequireWildcard(require("express-openapi-validator")));
|
|
243
322
|
return true;
|
|
244
|
-
} catch (
|
|
323
|
+
} catch (e3) {
|
|
245
324
|
return false;
|
|
246
325
|
}
|
|
247
326
|
};
|
|
@@ -424,5 +503,6 @@ var ApiSpecification = {
|
|
|
424
503
|
|
|
425
504
|
|
|
426
505
|
|
|
427
|
-
|
|
506
|
+
|
|
507
|
+
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.send = send; exports.sendAccepted = sendAccepted; exports.sendCreated = sendCreated; exports.sendProblem = sendProblem; exports.setETag = setETag; exports.startAPI = startAPI; exports.toWeakETag = toWeakETag;
|
|
428
508
|
//# sourceMappingURL=index.cjs.map
|