@jaypie/express 1.2.21 → 1.2.23
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/expressHandler.d.ts +7 -0
- package/dist/cjs/fabricApiResponse.d.ts +14 -0
- package/dist/cjs/index.cjs +68 -4
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.ts +1 -0
- package/dist/esm/expressHandler.d.ts +7 -0
- package/dist/esm/fabricApiResponse.d.ts +14 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +68 -5
- package/dist/esm/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cjs/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export type { ApiGatewayV1Event, CreateLambdaHandlerOptions, FunctionUrlEvent, L
|
|
|
3
3
|
export { EXPRESS } from "./constants.js";
|
|
4
4
|
export { default as cors } from "./cors.helper.js";
|
|
5
5
|
export type { CorsConfig } from "./cors.helper.js";
|
|
6
|
+
export { default as fabricApiResponse } from "./fabricApiResponse.js";
|
|
6
7
|
export { default as expressHandler } from "./expressHandler.js";
|
|
7
8
|
export type { ExpressHandlerLocals, ExpressHandlerOptions, JaypieHandlerSetup, JaypieHandlerTeardown, JaypieHandlerValidate, } from "./expressHandler.js";
|
|
8
9
|
export { default as expressHttpCodeHandler } from "./http.handler.js";
|
|
@@ -5,6 +5,13 @@ export type JaypieHandlerValidate = (req: Request, res: Response) => Promise<boo
|
|
|
5
5
|
export type ExpressHandlerLocals = (req: Request, res: Response) => Promise<unknown> | unknown;
|
|
6
6
|
export interface ExpressHandlerOptions {
|
|
7
7
|
chaos?: string;
|
|
8
|
+
/**
|
|
9
|
+
* When true, the handler's return value is wrapped with
|
|
10
|
+
* `fabricApiResponse` before `res.json()`. Plain objects become
|
|
11
|
+
* `{ data: value }`; pre-wrapped `{ data }` or `{ errors }` payloads
|
|
12
|
+
* pass through unchanged. `null` / `undefined` become `{ data: null }`.
|
|
13
|
+
*/
|
|
14
|
+
fabric?: boolean;
|
|
8
15
|
locals?: Record<string, unknown | ExpressHandlerLocals>;
|
|
9
16
|
name?: string;
|
|
10
17
|
secrets?: string[];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wrap a service return value in the canonical Jaypie API envelope.
|
|
3
|
+
*
|
|
4
|
+
* Rules:
|
|
5
|
+
* - `null` / `undefined` → `{ data: null }`
|
|
6
|
+
* - `{ data }` with exactly one key → passthrough
|
|
7
|
+
* - `{ errors }` with exactly one key → passthrough
|
|
8
|
+
* - everything else (including `{ data, other }`) → `{ data: value }`
|
|
9
|
+
*
|
|
10
|
+
* The "only one key, and that key is `data` or `errors`" check prevents
|
|
11
|
+
* false positives where a domain object happens to contain a `data` field.
|
|
12
|
+
*/
|
|
13
|
+
export declare function fabricApiResponse(result: unknown): unknown;
|
|
14
|
+
export default fabricApiResponse;
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export type { ApiGatewayV1Event, CreateLambdaHandlerOptions, FunctionUrlEvent, L
|
|
|
3
3
|
export { EXPRESS } from "./constants.js";
|
|
4
4
|
export { default as cors } from "./cors.helper.js";
|
|
5
5
|
export type { CorsConfig } from "./cors.helper.js";
|
|
6
|
+
export { default as fabricApiResponse } from "./fabricApiResponse.js";
|
|
6
7
|
export { default as expressHandler } from "./expressHandler.js";
|
|
7
8
|
export type { ExpressHandlerLocals, ExpressHandlerOptions, JaypieHandlerSetup, JaypieHandlerTeardown, JaypieHandlerValidate, } from "./expressHandler.js";
|
|
8
9
|
export { default as expressHttpCodeHandler } from "./http.handler.js";
|
package/dist/esm/index.js
CHANGED
|
@@ -1355,6 +1355,48 @@ var cors_helper = (config) => {
|
|
|
1355
1355
|
};
|
|
1356
1356
|
};
|
|
1357
1357
|
|
|
1358
|
+
//
|
|
1359
|
+
//
|
|
1360
|
+
// Helpers
|
|
1361
|
+
//
|
|
1362
|
+
function isWrappedData(value) {
|
|
1363
|
+
return (typeof value === "object" &&
|
|
1364
|
+
value !== null &&
|
|
1365
|
+
!Array.isArray(value) &&
|
|
1366
|
+
Object.keys(value).length === 1 &&
|
|
1367
|
+
"data" in value);
|
|
1368
|
+
}
|
|
1369
|
+
function isWrappedErrors(value) {
|
|
1370
|
+
return (typeof value === "object" &&
|
|
1371
|
+
value !== null &&
|
|
1372
|
+
!Array.isArray(value) &&
|
|
1373
|
+
Object.keys(value).length === 1 &&
|
|
1374
|
+
"errors" in value);
|
|
1375
|
+
}
|
|
1376
|
+
//
|
|
1377
|
+
//
|
|
1378
|
+
// Main
|
|
1379
|
+
//
|
|
1380
|
+
/**
|
|
1381
|
+
* Wrap a service return value in the canonical Jaypie API envelope.
|
|
1382
|
+
*
|
|
1383
|
+
* Rules:
|
|
1384
|
+
* - `null` / `undefined` → `{ data: null }`
|
|
1385
|
+
* - `{ data }` with exactly one key → passthrough
|
|
1386
|
+
* - `{ errors }` with exactly one key → passthrough
|
|
1387
|
+
* - everything else (including `{ data, other }`) → `{ data: value }`
|
|
1388
|
+
*
|
|
1389
|
+
* The "only one key, and that key is `data` or `errors`" check prevents
|
|
1390
|
+
* false positives where a domain object happens to contain a `data` field.
|
|
1391
|
+
*/
|
|
1392
|
+
function fabricApiResponse(result) {
|
|
1393
|
+
if (result === null || result === undefined)
|
|
1394
|
+
return { data: null };
|
|
1395
|
+
if (isWrappedData(result) || isWrappedErrors(result))
|
|
1396
|
+
return result;
|
|
1397
|
+
return { data: result };
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1358
1400
|
//
|
|
1359
1401
|
//
|
|
1360
1402
|
// Helper Functions
|
|
@@ -1717,7 +1759,7 @@ function expressHandler(handlerOrOptions, optionsOrHandler) {
|
|
|
1717
1759
|
//
|
|
1718
1760
|
// Validate
|
|
1719
1761
|
//
|
|
1720
|
-
let { chaos, locals, name, secrets, setup = [], teardown = [], unavailable, validate, } = options;
|
|
1762
|
+
let { chaos, fabric, locals, name, secrets, setup = [], teardown = [], unavailable, validate, } = options;
|
|
1721
1763
|
if (typeof handler !== "function") {
|
|
1722
1764
|
throw new BadRequestError(`Argument "${handler}" doesn't match type "function"`);
|
|
1723
1765
|
}
|
|
@@ -1871,6 +1913,9 @@ function expressHandler(handlerOrOptions, optionsOrHandler) {
|
|
|
1871
1913
|
// Process
|
|
1872
1914
|
//
|
|
1873
1915
|
response = (await jaypieFunction(req, res, ...params));
|
|
1916
|
+
if (fabric) {
|
|
1917
|
+
response = fabricApiResponse(response);
|
|
1918
|
+
}
|
|
1874
1919
|
//
|
|
1875
1920
|
//
|
|
1876
1921
|
// Error Handling
|
|
@@ -1977,19 +2022,37 @@ function expressHandler(handlerOrOptions, optionsOrHandler) {
|
|
|
1977
2022
|
res: summarizeResponse(res, extras),
|
|
1978
2023
|
});
|
|
1979
2024
|
// Construct normalized path for reporting and metrics
|
|
1980
|
-
let
|
|
1981
|
-
if (!
|
|
1982
|
-
|
|
2025
|
+
let pathWithQuery = (req.baseUrl || "") + (req.url || "");
|
|
2026
|
+
if (!pathWithQuery.startsWith("/")) {
|
|
2027
|
+
pathWithQuery = "/" + pathWithQuery;
|
|
1983
2028
|
}
|
|
2029
|
+
const queryIndex = pathWithQuery.indexOf("?");
|
|
2030
|
+
let path = queryIndex >= 0 ? pathWithQuery.slice(0, queryIndex) : pathWithQuery;
|
|
2031
|
+
const rawQuery = queryIndex >= 0 ? pathWithQuery.slice(queryIndex + 1) : "";
|
|
1984
2032
|
if (path.length > 1 && path.endsWith("/")) {
|
|
1985
2033
|
path = path.slice(0, -1);
|
|
1986
2034
|
}
|
|
1987
2035
|
// Replace UUIDs with :id for better aggregation
|
|
1988
2036
|
path = path.replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, ":id");
|
|
2037
|
+
// Normalize query params (sort alphabetically for stable aggregation)
|
|
2038
|
+
let query = "";
|
|
2039
|
+
if (rawQuery) {
|
|
2040
|
+
const params = new URLSearchParams(rawQuery);
|
|
2041
|
+
params.sort();
|
|
2042
|
+
query = params.toString();
|
|
2043
|
+
}
|
|
2044
|
+
// Capture request content metadata
|
|
2045
|
+
const headers = req.headers || {};
|
|
2046
|
+
const contentType = String(headers["content-type"] || "");
|
|
2047
|
+
const contentLengthHeader = headers["content-length"];
|
|
2048
|
+
const contentLength = contentLengthHeader ? Number(contentLengthHeader) : 0;
|
|
1989
2049
|
// Add request data to session report
|
|
1990
2050
|
logger$1.report({
|
|
1991
2051
|
method: req.method,
|
|
1992
2052
|
path,
|
|
2053
|
+
query,
|
|
2054
|
+
contentType,
|
|
2055
|
+
contentLength,
|
|
1993
2056
|
status: String(res.statusCode),
|
|
1994
2057
|
});
|
|
1995
2058
|
// Submit metric if Datadog is configured
|
|
@@ -2356,5 +2419,5 @@ const noContentRoute = routes.noContentRoute;
|
|
|
2356
2419
|
const notFoundRoute = routes.notFoundRoute;
|
|
2357
2420
|
const notImplementedRoute = routes.notImplementedRoute;
|
|
2358
2421
|
|
|
2359
|
-
export { EXPRESS, LambdaRequest, LambdaResponseBuffered, LambdaResponseStreaming, badRequestRoute, cors_helper as cors, createLambdaHandler, createLambdaStreamHandler, echoRoute, expressHandler, httpHandler as expressHttpCodeHandler, expressStreamHandler, forbiddenRoute, getCurrentInvoke, getCurrentInvokeUuid, goneRoute, methodNotAllowedRoute, noContentRoute, notFoundRoute, notImplementedRoute };
|
|
2422
|
+
export { EXPRESS, LambdaRequest, LambdaResponseBuffered, LambdaResponseStreaming, badRequestRoute, cors_helper as cors, createLambdaHandler, createLambdaStreamHandler, echoRoute, expressHandler, httpHandler as expressHttpCodeHandler, expressStreamHandler, fabricApiResponse, forbiddenRoute, getCurrentInvoke, getCurrentInvokeUuid, goneRoute, methodNotAllowedRoute, noContentRoute, notFoundRoute, notImplementedRoute };
|
|
2360
2423
|
//# sourceMappingURL=index.js.map
|