@jaypie/express 1.2.1 → 1.2.3
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/dist/cjs/createServer.d.ts +60 -0
- package/dist/cjs/expressHandler.d.ts +1 -0
- package/dist/cjs/expressStreamHandler.d.ts +1 -0
- package/dist/cjs/getCurrentInvokeUuid.adapter.d.ts +10 -1
- package/dist/cjs/getCurrentInvokeUuid.webadapter.d.ts +12 -0
- package/dist/cjs/index.cjs +194 -9
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.ts +3 -0
- package/dist/esm/createServer.d.ts +60 -0
- package/dist/esm/expressHandler.d.ts +1 -0
- package/dist/esm/expressStreamHandler.d.ts +1 -0
- package/dist/esm/getCurrentInvokeUuid.adapter.d.ts +10 -1
- package/dist/esm/getCurrentInvokeUuid.webadapter.d.ts +12 -0
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.js +192 -9
- package/dist/esm/index.js.map +1 -1
- package/package.json +5 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { Application, RequestHandler } from "express";
|
|
2
|
+
import type { Server } from "http";
|
|
3
|
+
import type { CorsConfig } from "./cors.helper.js";
|
|
4
|
+
export interface CreateServerOptions {
|
|
5
|
+
/**
|
|
6
|
+
* CORS configuration. Pass false to disable CORS middleware.
|
|
7
|
+
*/
|
|
8
|
+
cors?: CorsConfig | false;
|
|
9
|
+
/**
|
|
10
|
+
* JSON body parser limit. Defaults to "1mb".
|
|
11
|
+
*/
|
|
12
|
+
jsonLimit?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Additional middleware to apply before routes.
|
|
15
|
+
*/
|
|
16
|
+
middleware?: RequestHandler[];
|
|
17
|
+
/**
|
|
18
|
+
* Port to listen on. Defaults to PORT env var or 8080.
|
|
19
|
+
*/
|
|
20
|
+
port?: number | string;
|
|
21
|
+
}
|
|
22
|
+
export interface ServerResult {
|
|
23
|
+
/**
|
|
24
|
+
* The HTTP server instance.
|
|
25
|
+
*/
|
|
26
|
+
server: Server;
|
|
27
|
+
/**
|
|
28
|
+
* The port the server is listening on.
|
|
29
|
+
*/
|
|
30
|
+
port: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Creates and starts an Express server with standard Jaypie middleware.
|
|
34
|
+
*
|
|
35
|
+
* Features:
|
|
36
|
+
* - CORS handling (configurable)
|
|
37
|
+
* - JSON body parsing
|
|
38
|
+
* - Listens on PORT env var (default 8080)
|
|
39
|
+
*
|
|
40
|
+
* Usage:
|
|
41
|
+
* ```ts
|
|
42
|
+
* import express from "express";
|
|
43
|
+
* import { createServer, expressHandler } from "@jaypie/express";
|
|
44
|
+
*
|
|
45
|
+
* const app = express();
|
|
46
|
+
*
|
|
47
|
+
* app.get("/", expressHandler(async (req, res) => {
|
|
48
|
+
* return { message: "Hello World" };
|
|
49
|
+
* }));
|
|
50
|
+
*
|
|
51
|
+
* const { server, port } = await createServer(app);
|
|
52
|
+
* console.log(`Server running on port ${port}`);
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* @param app - Express application instance
|
|
56
|
+
* @param options - Server configuration options
|
|
57
|
+
* @returns Promise resolving to server instance and port
|
|
58
|
+
*/
|
|
59
|
+
declare function createServer(app: Application, options?: CreateServerOptions): Promise<ServerResult>;
|
|
60
|
+
export default createServer;
|
|
@@ -7,6 +7,7 @@ export interface ExpressHandlerOptions {
|
|
|
7
7
|
chaos?: string;
|
|
8
8
|
locals?: Record<string, unknown | ExpressHandlerLocals>;
|
|
9
9
|
name?: string;
|
|
10
|
+
secrets?: string[];
|
|
10
11
|
setup?: JaypieHandlerSetup[] | JaypieHandlerSetup;
|
|
11
12
|
teardown?: JaypieHandlerTeardown[] | JaypieHandlerTeardown;
|
|
12
13
|
unavailable?: boolean;
|
|
@@ -8,6 +8,7 @@ export interface ExpressStreamHandlerOptions {
|
|
|
8
8
|
contentType?: string;
|
|
9
9
|
locals?: Record<string, unknown | ExpressStreamHandlerLocals>;
|
|
10
10
|
name?: string;
|
|
11
|
+
secrets?: string[];
|
|
11
12
|
setup?: JaypieStreamHandlerSetup[] | JaypieStreamHandlerSetup;
|
|
12
13
|
teardown?: JaypieStreamHandlerTeardown[] | JaypieStreamHandlerTeardown;
|
|
13
14
|
unavailable?: boolean;
|
|
@@ -1,2 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
import type { Request } from "express";
|
|
2
|
+
/**
|
|
3
|
+
* Get the current invoke UUID from Lambda context.
|
|
4
|
+
* Works in both serverless-express mode and Lambda Web Adapter mode.
|
|
5
|
+
*
|
|
6
|
+
* @param req - Optional Express request object. Required for Web Adapter mode
|
|
7
|
+
* to extract the x-amzn-request-id header.
|
|
8
|
+
* @returns The AWS request ID or undefined if not in Lambda context
|
|
9
|
+
*/
|
|
10
|
+
declare function getCurrentInvokeUuid(req?: Request): string | undefined;
|
|
2
11
|
export default getCurrentInvokeUuid;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Request } from "express";
|
|
2
|
+
/**
|
|
3
|
+
* Get the current invoke UUID from Lambda Web Adapter context.
|
|
4
|
+
* This function extracts the request ID from either:
|
|
5
|
+
* 1. The x-amzn-request-id header (set by Lambda Web Adapter)
|
|
6
|
+
* 2. The _X_AMZN_TRACE_ID environment variable (set by Lambda runtime)
|
|
7
|
+
*
|
|
8
|
+
* @param req - Optional Express request object to extract headers from
|
|
9
|
+
* @returns The AWS request ID or undefined if not in Lambda context
|
|
10
|
+
*/
|
|
11
|
+
declare function getWebAdapterUuid(req?: Request): string | undefined;
|
|
12
|
+
export default getWebAdapterUuid;
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -4,6 +4,7 @@ var errors = require('@jaypie/errors');
|
|
|
4
4
|
var kit = require('@jaypie/kit');
|
|
5
5
|
var expressCors = require('cors');
|
|
6
6
|
var logger$2 = require('@jaypie/logger');
|
|
7
|
+
var aws = require('@jaypie/aws');
|
|
7
8
|
var datadog = require('@jaypie/datadog');
|
|
8
9
|
var serverlessExpress = require('@codegenie/serverless-express');
|
|
9
10
|
|
|
@@ -93,7 +94,7 @@ const corsHelper = (config = {}) => {
|
|
|
93
94
|
};
|
|
94
95
|
return expressCors(options);
|
|
95
96
|
};
|
|
96
|
-
var
|
|
97
|
+
var cors = (config) => {
|
|
97
98
|
const cors = corsHelper(config);
|
|
98
99
|
return (req, res, next) => {
|
|
99
100
|
cors(req, res, (error) => {
|
|
@@ -108,11 +109,157 @@ var cors_helper = (config) => {
|
|
|
108
109
|
};
|
|
109
110
|
};
|
|
110
111
|
|
|
112
|
+
//
|
|
113
|
+
//
|
|
114
|
+
// Constants
|
|
115
|
+
//
|
|
116
|
+
const DEFAULT_PORT = 8080;
|
|
117
|
+
//
|
|
118
|
+
//
|
|
119
|
+
// Main
|
|
120
|
+
//
|
|
121
|
+
/**
|
|
122
|
+
* Creates and starts an Express server with standard Jaypie middleware.
|
|
123
|
+
*
|
|
124
|
+
* Features:
|
|
125
|
+
* - CORS handling (configurable)
|
|
126
|
+
* - JSON body parsing
|
|
127
|
+
* - Listens on PORT env var (default 8080)
|
|
128
|
+
*
|
|
129
|
+
* Usage:
|
|
130
|
+
* ```ts
|
|
131
|
+
* import express from "express";
|
|
132
|
+
* import { createServer, expressHandler } from "@jaypie/express";
|
|
133
|
+
*
|
|
134
|
+
* const app = express();
|
|
135
|
+
*
|
|
136
|
+
* app.get("/", expressHandler(async (req, res) => {
|
|
137
|
+
* return { message: "Hello World" };
|
|
138
|
+
* }));
|
|
139
|
+
*
|
|
140
|
+
* const { server, port } = await createServer(app);
|
|
141
|
+
* console.log(`Server running on port ${port}`);
|
|
142
|
+
* ```
|
|
143
|
+
*
|
|
144
|
+
* @param app - Express application instance
|
|
145
|
+
* @param options - Server configuration options
|
|
146
|
+
* @returns Promise resolving to server instance and port
|
|
147
|
+
*/
|
|
148
|
+
async function createServer(app, options = {}) {
|
|
149
|
+
const { cors: corsConfig, jsonLimit = "1mb", middleware = [], port: portOption, } = options;
|
|
150
|
+
// Determine port
|
|
151
|
+
const port = typeof portOption === "string"
|
|
152
|
+
? parseInt(portOption, 10)
|
|
153
|
+
: (portOption ?? parseInt(process.env.PORT || String(DEFAULT_PORT), 10));
|
|
154
|
+
// Apply CORS middleware (unless explicitly disabled)
|
|
155
|
+
if (corsConfig !== false) {
|
|
156
|
+
app.use(cors(corsConfig));
|
|
157
|
+
}
|
|
158
|
+
// Apply JSON body parser
|
|
159
|
+
// Note: We use dynamic import to avoid requiring express as a direct dependency
|
|
160
|
+
const express = await import('express');
|
|
161
|
+
app.use(express.json({ limit: jsonLimit }));
|
|
162
|
+
// Apply additional middleware
|
|
163
|
+
for (const mw of middleware) {
|
|
164
|
+
app.use(mw);
|
|
165
|
+
}
|
|
166
|
+
// Start server
|
|
167
|
+
return new Promise((resolve, reject) => {
|
|
168
|
+
try {
|
|
169
|
+
const server = app.listen(port, () => {
|
|
170
|
+
// Get the actual port (important when port 0 is passed to get an ephemeral port)
|
|
171
|
+
const address = server.address();
|
|
172
|
+
const actualPort = address?.port ?? port;
|
|
173
|
+
logger$2.log.info(`Server listening on port ${actualPort}`);
|
|
174
|
+
resolve({ port: actualPort, server });
|
|
175
|
+
});
|
|
176
|
+
server.on("error", (error) => {
|
|
177
|
+
logger$2.log.error("Server error", { error });
|
|
178
|
+
reject(error);
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
reject(error);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
//
|
|
188
|
+
//
|
|
189
|
+
// Constants
|
|
190
|
+
//
|
|
191
|
+
const HEADER_AMZN_REQUEST_ID$1 = "x-amzn-request-id";
|
|
192
|
+
const ENV_AMZN_TRACE_ID = "_X_AMZN_TRACE_ID";
|
|
111
193
|
//
|
|
112
194
|
//
|
|
113
195
|
// Helper Functions
|
|
114
196
|
//
|
|
115
|
-
|
|
197
|
+
/**
|
|
198
|
+
* Extract request ID from X-Ray trace ID environment variable
|
|
199
|
+
* Format: Root=1-5e6b4a90-example;Parent=example;Sampled=1
|
|
200
|
+
* We extract the trace ID from the Root segment
|
|
201
|
+
*/
|
|
202
|
+
function parseTraceId(traceId) {
|
|
203
|
+
if (!traceId)
|
|
204
|
+
return undefined;
|
|
205
|
+
// Extract the Root segment (format: Root=1-{timestamp}-{uuid})
|
|
206
|
+
const rootMatch = traceId.match(/Root=([^;]+)/);
|
|
207
|
+
if (rootMatch && rootMatch[1]) {
|
|
208
|
+
return rootMatch[1];
|
|
209
|
+
}
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
//
|
|
213
|
+
//
|
|
214
|
+
// Main
|
|
215
|
+
//
|
|
216
|
+
/**
|
|
217
|
+
* Get the current invoke UUID from Lambda Web Adapter context.
|
|
218
|
+
* This function extracts the request ID from either:
|
|
219
|
+
* 1. The x-amzn-request-id header (set by Lambda Web Adapter)
|
|
220
|
+
* 2. The _X_AMZN_TRACE_ID environment variable (set by Lambda runtime)
|
|
221
|
+
*
|
|
222
|
+
* @param req - Optional Express request object to extract headers from
|
|
223
|
+
* @returns The AWS request ID or undefined if not in Lambda context
|
|
224
|
+
*/
|
|
225
|
+
function getWebAdapterUuid(req) {
|
|
226
|
+
// First, try to get from request headers
|
|
227
|
+
if (req && req.headers) {
|
|
228
|
+
const headerValue = req.headers[HEADER_AMZN_REQUEST_ID$1];
|
|
229
|
+
if (headerValue) {
|
|
230
|
+
return Array.isArray(headerValue) ? headerValue[0] : headerValue;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// Fall back to environment variable (X-Ray trace ID)
|
|
234
|
+
const traceId = process.env[ENV_AMZN_TRACE_ID];
|
|
235
|
+
if (traceId) {
|
|
236
|
+
return parseTraceId(traceId);
|
|
237
|
+
}
|
|
238
|
+
return undefined;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
//
|
|
242
|
+
//
|
|
243
|
+
// Constants
|
|
244
|
+
//
|
|
245
|
+
const HEADER_AMZN_REQUEST_ID = "x-amzn-request-id";
|
|
246
|
+
//
|
|
247
|
+
//
|
|
248
|
+
// Helper Functions
|
|
249
|
+
//
|
|
250
|
+
/**
|
|
251
|
+
* Detect if we're running in Lambda Web Adapter mode.
|
|
252
|
+
* Web Adapter sets the x-amzn-request-id header on requests.
|
|
253
|
+
*/
|
|
254
|
+
function isWebAdapterMode(req) {
|
|
255
|
+
if (req && req.headers && req.headers[HEADER_AMZN_REQUEST_ID]) {
|
|
256
|
+
return true;
|
|
257
|
+
}
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Adapter for the "@codegenie/serverless-express" uuid
|
|
262
|
+
*/
|
|
116
263
|
function getServerlessExpressUuid() {
|
|
117
264
|
const currentInvoke = serverlessExpress.getCurrentInvoke();
|
|
118
265
|
if (currentInvoke &&
|
|
@@ -126,7 +273,27 @@ function getServerlessExpressUuid() {
|
|
|
126
273
|
//
|
|
127
274
|
// Main
|
|
128
275
|
//
|
|
129
|
-
|
|
276
|
+
/**
|
|
277
|
+
* Get the current invoke UUID from Lambda context.
|
|
278
|
+
* Works in both serverless-express mode and Lambda Web Adapter mode.
|
|
279
|
+
*
|
|
280
|
+
* @param req - Optional Express request object. Required for Web Adapter mode
|
|
281
|
+
* to extract the x-amzn-request-id header.
|
|
282
|
+
* @returns The AWS request ID or undefined if not in Lambda context
|
|
283
|
+
*/
|
|
284
|
+
function getCurrentInvokeUuid(req) {
|
|
285
|
+
// If request is provided and has Web Adapter header, use Web Adapter mode
|
|
286
|
+
if (isWebAdapterMode(req)) {
|
|
287
|
+
return getWebAdapterUuid(req);
|
|
288
|
+
}
|
|
289
|
+
// Try serverless-express mode first
|
|
290
|
+
const serverlessExpressUuid = getServerlessExpressUuid();
|
|
291
|
+
if (serverlessExpressUuid) {
|
|
292
|
+
return serverlessExpressUuid;
|
|
293
|
+
}
|
|
294
|
+
// If no request but we might be in Web Adapter mode, try env var fallback
|
|
295
|
+
return getWebAdapterUuid();
|
|
296
|
+
}
|
|
130
297
|
|
|
131
298
|
//
|
|
132
299
|
//
|
|
@@ -251,7 +418,7 @@ function expressHandler(handlerOrOptions, optionsOrHandler) {
|
|
|
251
418
|
//
|
|
252
419
|
// Validate
|
|
253
420
|
//
|
|
254
|
-
let { chaos, locals, name, setup = [], teardown = [], unavailable, validate, } = options;
|
|
421
|
+
let { chaos, locals, name, secrets, setup = [], teardown = [], unavailable, validate, } = options;
|
|
255
422
|
if (typeof handler !== "function") {
|
|
256
423
|
throw new errors.BadRequestError(`Argument "${handler}" doesn't match type "function"`);
|
|
257
424
|
}
|
|
@@ -288,7 +455,7 @@ function expressHandler(handlerOrOptions, optionsOrHandler) {
|
|
|
288
455
|
lib: kit.JAYPIE.LIB.EXPRESS,
|
|
289
456
|
});
|
|
290
457
|
// Update the public logger with the request ID
|
|
291
|
-
const invokeUuid = getCurrentInvokeUuid();
|
|
458
|
+
const invokeUuid = getCurrentInvokeUuid(req);
|
|
292
459
|
if (invokeUuid) {
|
|
293
460
|
logger$1.tag({ invoke: invokeUuid });
|
|
294
461
|
logger$1.tag({ shortInvoke: invokeUuid.slice(0, 8) });
|
|
@@ -357,6 +524,14 @@ function expressHandler(handlerOrOptions, optionsOrHandler) {
|
|
|
357
524
|
//
|
|
358
525
|
// Preprocess
|
|
359
526
|
//
|
|
527
|
+
// Load secrets into process.env if configured
|
|
528
|
+
if (secrets && secrets.length > 0) {
|
|
529
|
+
const secretsToLoad = secrets;
|
|
530
|
+
const secretsSetup = async () => {
|
|
531
|
+
await aws.loadEnvSecrets(...secretsToLoad);
|
|
532
|
+
};
|
|
533
|
+
setup.unshift(secretsSetup);
|
|
534
|
+
}
|
|
360
535
|
if (locals) {
|
|
361
536
|
// Locals
|
|
362
537
|
const keys = Object.keys(locals);
|
|
@@ -546,7 +721,7 @@ const logger = logger$2.log;
|
|
|
546
721
|
function formatErrorSSE(error) {
|
|
547
722
|
const isJaypieError = error.isProjectError;
|
|
548
723
|
const body = isJaypieError
|
|
549
|
-
? error.body?.() ?? { error: error.message }
|
|
724
|
+
? (error.body?.() ?? { error: error.message })
|
|
550
725
|
: new errors.UnhandledError().body();
|
|
551
726
|
return `event: error\ndata: ${JSON.stringify(body)}\n\n`;
|
|
552
727
|
}
|
|
@@ -568,7 +743,7 @@ function expressStreamHandler(handlerOrOptions, optionsOrHandler) {
|
|
|
568
743
|
//
|
|
569
744
|
// Validate
|
|
570
745
|
//
|
|
571
|
-
let { chaos, contentType = "text/event-stream", locals, name, setup = [], teardown = [], unavailable, validate, } = options;
|
|
746
|
+
let { chaos, contentType = "text/event-stream", locals, name, secrets, setup = [], teardown = [], unavailable, validate, } = options;
|
|
572
747
|
if (typeof handler !== "function") {
|
|
573
748
|
throw new errors.BadRequestError(`Argument "${handler}" doesn't match type "function"`);
|
|
574
749
|
}
|
|
@@ -602,7 +777,7 @@ function expressStreamHandler(handlerOrOptions, optionsOrHandler) {
|
|
|
602
777
|
lib: kit.JAYPIE.LIB.EXPRESS,
|
|
603
778
|
});
|
|
604
779
|
// Update the public logger with the request ID
|
|
605
|
-
const invokeUuid = getCurrentInvokeUuid();
|
|
780
|
+
const invokeUuid = getCurrentInvokeUuid(req);
|
|
606
781
|
if (invokeUuid) {
|
|
607
782
|
logger.tag({ invoke: invokeUuid });
|
|
608
783
|
logger.tag({ shortInvoke: invokeUuid.slice(0, 8) });
|
|
@@ -646,6 +821,14 @@ function expressStreamHandler(handlerOrOptions, optionsOrHandler) {
|
|
|
646
821
|
//
|
|
647
822
|
// Preprocess
|
|
648
823
|
//
|
|
824
|
+
// Load secrets into process.env if configured
|
|
825
|
+
if (secrets && secrets.length > 0) {
|
|
826
|
+
const secretsToLoad = secrets;
|
|
827
|
+
const secretsSetup = async () => {
|
|
828
|
+
await aws.loadEnvSecrets(...secretsToLoad);
|
|
829
|
+
};
|
|
830
|
+
setup.unshift(secretsSetup);
|
|
831
|
+
}
|
|
649
832
|
if (locals) {
|
|
650
833
|
const keys = Object.keys(locals);
|
|
651
834
|
if (keys.length > 0) {
|
|
@@ -851,12 +1034,14 @@ const notImplementedRoute = routes.notImplementedRoute;
|
|
|
851
1034
|
|
|
852
1035
|
exports.EXPRESS = EXPRESS;
|
|
853
1036
|
exports.badRequestRoute = badRequestRoute;
|
|
854
|
-
exports.cors =
|
|
1037
|
+
exports.cors = cors;
|
|
1038
|
+
exports.createServer = createServer;
|
|
855
1039
|
exports.echoRoute = echoRoute;
|
|
856
1040
|
exports.expressHandler = expressHandler;
|
|
857
1041
|
exports.expressHttpCodeHandler = httpHandler;
|
|
858
1042
|
exports.expressStreamHandler = expressStreamHandler;
|
|
859
1043
|
exports.forbiddenRoute = forbiddenRoute;
|
|
1044
|
+
exports.getCurrentInvokeUuid = getCurrentInvokeUuid;
|
|
860
1045
|
exports.goneRoute = goneRoute;
|
|
861
1046
|
exports.methodNotAllowedRoute = methodNotAllowedRoute;
|
|
862
1047
|
exports.noContentRoute = noContentRoute;
|