@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
|
@@ -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/cjs/index.cjs
CHANGED
|
@@ -1357,6 +1357,48 @@ var cors_helper = (config) => {
|
|
|
1357
1357
|
};
|
|
1358
1358
|
};
|
|
1359
1359
|
|
|
1360
|
+
//
|
|
1361
|
+
//
|
|
1362
|
+
// Helpers
|
|
1363
|
+
//
|
|
1364
|
+
function isWrappedData(value) {
|
|
1365
|
+
return (typeof value === "object" &&
|
|
1366
|
+
value !== null &&
|
|
1367
|
+
!Array.isArray(value) &&
|
|
1368
|
+
Object.keys(value).length === 1 &&
|
|
1369
|
+
"data" in value);
|
|
1370
|
+
}
|
|
1371
|
+
function isWrappedErrors(value) {
|
|
1372
|
+
return (typeof value === "object" &&
|
|
1373
|
+
value !== null &&
|
|
1374
|
+
!Array.isArray(value) &&
|
|
1375
|
+
Object.keys(value).length === 1 &&
|
|
1376
|
+
"errors" in value);
|
|
1377
|
+
}
|
|
1378
|
+
//
|
|
1379
|
+
//
|
|
1380
|
+
// Main
|
|
1381
|
+
//
|
|
1382
|
+
/**
|
|
1383
|
+
* Wrap a service return value in the canonical Jaypie API envelope.
|
|
1384
|
+
*
|
|
1385
|
+
* Rules:
|
|
1386
|
+
* - `null` / `undefined` → `{ data: null }`
|
|
1387
|
+
* - `{ data }` with exactly one key → passthrough
|
|
1388
|
+
* - `{ errors }` with exactly one key → passthrough
|
|
1389
|
+
* - everything else (including `{ data, other }`) → `{ data: value }`
|
|
1390
|
+
*
|
|
1391
|
+
* The "only one key, and that key is `data` or `errors`" check prevents
|
|
1392
|
+
* false positives where a domain object happens to contain a `data` field.
|
|
1393
|
+
*/
|
|
1394
|
+
function fabricApiResponse(result) {
|
|
1395
|
+
if (result === null || result === undefined)
|
|
1396
|
+
return { data: null };
|
|
1397
|
+
if (isWrappedData(result) || isWrappedErrors(result))
|
|
1398
|
+
return result;
|
|
1399
|
+
return { data: result };
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1360
1402
|
//
|
|
1361
1403
|
//
|
|
1362
1404
|
// Helper Functions
|
|
@@ -1719,7 +1761,7 @@ function expressHandler(handlerOrOptions, optionsOrHandler) {
|
|
|
1719
1761
|
//
|
|
1720
1762
|
// Validate
|
|
1721
1763
|
//
|
|
1722
|
-
let { chaos, locals, name, secrets, setup = [], teardown = [], unavailable, validate, } = options;
|
|
1764
|
+
let { chaos, fabric, locals, name, secrets, setup = [], teardown = [], unavailable, validate, } = options;
|
|
1723
1765
|
if (typeof handler !== "function") {
|
|
1724
1766
|
throw new errors.BadRequestError(`Argument "${handler}" doesn't match type "function"`);
|
|
1725
1767
|
}
|
|
@@ -1873,6 +1915,9 @@ function expressHandler(handlerOrOptions, optionsOrHandler) {
|
|
|
1873
1915
|
// Process
|
|
1874
1916
|
//
|
|
1875
1917
|
response = (await jaypieFunction(req, res, ...params));
|
|
1918
|
+
if (fabric) {
|
|
1919
|
+
response = fabricApiResponse(response);
|
|
1920
|
+
}
|
|
1876
1921
|
//
|
|
1877
1922
|
//
|
|
1878
1923
|
// Error Handling
|
|
@@ -1979,19 +2024,37 @@ function expressHandler(handlerOrOptions, optionsOrHandler) {
|
|
|
1979
2024
|
res: summarizeResponse(res, extras),
|
|
1980
2025
|
});
|
|
1981
2026
|
// Construct normalized path for reporting and metrics
|
|
1982
|
-
let
|
|
1983
|
-
if (!
|
|
1984
|
-
|
|
2027
|
+
let pathWithQuery = (req.baseUrl || "") + (req.url || "");
|
|
2028
|
+
if (!pathWithQuery.startsWith("/")) {
|
|
2029
|
+
pathWithQuery = "/" + pathWithQuery;
|
|
1985
2030
|
}
|
|
2031
|
+
const queryIndex = pathWithQuery.indexOf("?");
|
|
2032
|
+
let path = queryIndex >= 0 ? pathWithQuery.slice(0, queryIndex) : pathWithQuery;
|
|
2033
|
+
const rawQuery = queryIndex >= 0 ? pathWithQuery.slice(queryIndex + 1) : "";
|
|
1986
2034
|
if (path.length > 1 && path.endsWith("/")) {
|
|
1987
2035
|
path = path.slice(0, -1);
|
|
1988
2036
|
}
|
|
1989
2037
|
// Replace UUIDs with :id for better aggregation
|
|
1990
2038
|
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");
|
|
2039
|
+
// Normalize query params (sort alphabetically for stable aggregation)
|
|
2040
|
+
let query = "";
|
|
2041
|
+
if (rawQuery) {
|
|
2042
|
+
const params = new URLSearchParams(rawQuery);
|
|
2043
|
+
params.sort();
|
|
2044
|
+
query = params.toString();
|
|
2045
|
+
}
|
|
2046
|
+
// Capture request content metadata
|
|
2047
|
+
const headers = req.headers || {};
|
|
2048
|
+
const contentType = String(headers["content-type"] || "");
|
|
2049
|
+
const contentLengthHeader = headers["content-length"];
|
|
2050
|
+
const contentLength = contentLengthHeader ? Number(contentLengthHeader) : 0;
|
|
1991
2051
|
// Add request data to session report
|
|
1992
2052
|
logger$1.report({
|
|
1993
2053
|
method: req.method,
|
|
1994
2054
|
path,
|
|
2055
|
+
query,
|
|
2056
|
+
contentType,
|
|
2057
|
+
contentLength,
|
|
1995
2058
|
status: String(res.statusCode),
|
|
1996
2059
|
});
|
|
1997
2060
|
// Submit metric if Datadog is configured
|
|
@@ -2370,6 +2433,7 @@ exports.echoRoute = echoRoute;
|
|
|
2370
2433
|
exports.expressHandler = expressHandler;
|
|
2371
2434
|
exports.expressHttpCodeHandler = httpHandler;
|
|
2372
2435
|
exports.expressStreamHandler = expressStreamHandler;
|
|
2436
|
+
exports.fabricApiResponse = fabricApiResponse;
|
|
2373
2437
|
exports.forbiddenRoute = forbiddenRoute;
|
|
2374
2438
|
exports.getCurrentInvoke = getCurrentInvoke;
|
|
2375
2439
|
exports.getCurrentInvokeUuid = getCurrentInvokeUuid;
|