serverless-event-orchestrator 2.3.0 → 2.5.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/dist/http/body-parser.d.ts.map +1 -1
- package/dist/http/body-parser.js +26 -0
- package/dist/http/body-parser.js.map +1 -1
- package/dist/tenant/helpers.js +1 -1
- package/dist/tenant/helpers.js.map +1 -1
- package/package.json +1 -1
- package/src/http/body-parser.ts +31 -6
- package/src/tenant/helpers.ts +1 -1
- package/tests/middleware/init-tenant-context.test.ts +1 -1
- package/tests/tenant/helpers.test.ts +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"body-parser.d.ts","sourceRoot":"","sources":["../../src/http/body-parser.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,eAAe,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"body-parser.d.ts","sourceRoot":"","sources":["../../src/http/body-parser.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,eAAe,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAsB7G;AAoBD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,IAAI,GAAG,SAAS,GAC5D,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAYxB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,OAAO,CAAA;CAAE,EACvF,KAAK,EAAE,CAAC,GACP,CAAC,GAAG;IAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAAE,CAKzC"}
|
package/dist/http/body-parser.js
CHANGED
|
@@ -20,6 +20,14 @@ function parseJsonBody(body, isBase64Encoded) {
|
|
|
20
20
|
if (isBase64Encoded) {
|
|
21
21
|
decodedBody = Buffer.from(body, 'base64').toString('utf-8');
|
|
22
22
|
}
|
|
23
|
+
else {
|
|
24
|
+
// API Gateway puede entregar el body interpretando bytes UTF-8 como
|
|
25
|
+
// Latin-1 cuando el Content-Type no incluye `; charset=utf-8`,
|
|
26
|
+
// produciendo mojibake (`MarÃa` en lugar de `María`). Si detectamos
|
|
27
|
+
// el patrón típico (byte UTF-8 multibyte visto como Latin-1),
|
|
28
|
+
// revertimos re-codificando latin1 → utf-8.
|
|
29
|
+
decodedBody = fixUtf8Mojibake(decodedBody);
|
|
30
|
+
}
|
|
23
31
|
const parsed = JSON.parse(decodedBody);
|
|
24
32
|
return parsed ?? {};
|
|
25
33
|
}
|
|
@@ -27,6 +35,24 @@ function parseJsonBody(body, isBase64Encoded) {
|
|
|
27
35
|
return {};
|
|
28
36
|
}
|
|
29
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Detecta mojibake UTF-8-as-Latin-1 y lo revierte. La heurística busca un
|
|
40
|
+
* primer byte UTF-8 multibyte (0xC2-0xF4) seguido de un byte de continuación
|
|
41
|
+
* (0x80-0xBF). En texto natural casi siempre indica corrupción de encoding;
|
|
42
|
+
* si no hay match, el body se considera limpio y se devuelve intacto, así
|
|
43
|
+
* los bodies ASCII puros o UTF-8 ya correctos no se ven afectados.
|
|
44
|
+
*/
|
|
45
|
+
function fixUtf8Mojibake(input) {
|
|
46
|
+
if (!MOJIBAKE_PATTERN.test(input))
|
|
47
|
+
return input;
|
|
48
|
+
try {
|
|
49
|
+
return Buffer.from(input, 'latin1').toString('utf-8');
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return input;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const MOJIBAKE_PATTERN = /[Â-ô][-¿]/;
|
|
30
56
|
/**
|
|
31
57
|
* Safely parses query string parameters
|
|
32
58
|
* Handles multi-value parameters
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"body-parser.js","sourceRoot":"","sources":["../../src/http/body-parser.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAQH,
|
|
1
|
+
{"version":3,"file":"body-parser.js","sourceRoot":"","sources":["../../src/http/body-parser.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAQH,sCAsBC;AA0BD,4CAcC;AAKD,gDAOC;AAhFD;;;;;GAKG;AACH,SAAgB,aAAa,CAAC,IAA+B,EAAE,eAAyB;IACtF,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IAErB,IAAI,CAAC;QACH,IAAI,WAAW,GAAG,IAAI,CAAC;QAEvB,IAAI,eAAe,EAAE,CAAC;YACpB,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC9D,CAAC;aAAM,CAAC;YACN,oEAAoE;YACpE,+DAA+D;YAC/D,qEAAqE;YACrE,8DAA8D;YAC9D,4CAA4C;YAC5C,WAAW,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;QAC7C,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAwB,CAAC;QAC9D,OAAO,MAAM,IAAI,EAAE,CAAC;IACtB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,SAAS,eAAe,CAAC,KAAa;IACpC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAChD,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,gBAAgB,GAAG,YAAY,CAAC;AAEtC;;;;;GAKG;AACH,SAAgB,gBAAgB,CAC9B,MAA6D;IAE7D,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAEvB,MAAM,MAAM,GAA2B,EAAE,CAAC;IAE1C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACtB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAgB,kBAAkB,CAChC,KAAQ;IAER,OAAO;QACL,GAAG,KAAK;QACR,UAAU,EAAE,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,eAAe,CAAC;KAC7D,CAAC;AACJ,CAAC"}
|
package/dist/tenant/helpers.js
CHANGED
|
@@ -31,7 +31,7 @@ function tenantInfoFromClaims(claims) {
|
|
|
31
31
|
const countryCode = getClaim(claims, 'countryCode');
|
|
32
32
|
const personProfileId = getClaim(claims, 'personProfileId');
|
|
33
33
|
const orgProfileId = getClaim(claims, 'orgProfileId');
|
|
34
|
-
const hasCRM = getClaim(claims, 'hasCRM');
|
|
34
|
+
const hasCRM = getClaim(claims, 'hasCRM') === 'true';
|
|
35
35
|
if (!tenantId || !tenantType || !userId || !countryCode) {
|
|
36
36
|
return undefined;
|
|
37
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/tenant/helpers.ts"],"names":[],"mappings":";;AAwBA,oDA2BC;AASD,sDA0BC;AASD,kDAgBC;AA9GD,yCAAkE;AAElE;;;;;GAKG;AACH,SAAS,QAAQ,CAAC,MAA2B,EAAE,GAAW;IACxD,OAAO,MAAM,CAAC,UAAU,GAAG,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC;AAChD,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,oBAAoB,CAAC,MAA2B;IAC9D,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC9C,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3D,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACpD,MAAM,eAAe,GAAG,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAC5D,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACtD,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/tenant/helpers.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "serverless-event-orchestrator",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"description": "A lightweight, type-safe event dispatcher and middleware orchestrator for AWS Lambda. Designed for hexagonal architectures with support for segmented routing (public, private, backoffice), Cognito User Pool validation, and built-in infrastructure middlewares.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
package/src/http/body-parser.ts
CHANGED
|
@@ -10,14 +10,21 @@
|
|
|
10
10
|
*/
|
|
11
11
|
export function parseJsonBody(body: string | null | undefined, isBase64Encoded?: boolean): Record<string, any> {
|
|
12
12
|
if (!body) return {};
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
try {
|
|
15
15
|
let decodedBody = body;
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
if (isBase64Encoded) {
|
|
18
18
|
decodedBody = Buffer.from(body, 'base64').toString('utf-8');
|
|
19
|
+
} else {
|
|
20
|
+
// API Gateway puede entregar el body interpretando bytes UTF-8 como
|
|
21
|
+
// Latin-1 cuando el Content-Type no incluye `; charset=utf-8`,
|
|
22
|
+
// produciendo mojibake (`MarÃa` en lugar de `María`). Si detectamos
|
|
23
|
+
// el patrón típico (byte UTF-8 multibyte visto como Latin-1),
|
|
24
|
+
// revertimos re-codificando latin1 → utf-8.
|
|
25
|
+
decodedBody = fixUtf8Mojibake(decodedBody);
|
|
19
26
|
}
|
|
20
|
-
|
|
27
|
+
|
|
21
28
|
const parsed = JSON.parse(decodedBody) as Record<string, any>;
|
|
22
29
|
return parsed ?? {};
|
|
23
30
|
} catch {
|
|
@@ -25,6 +32,24 @@ export function parseJsonBody(body: string | null | undefined, isBase64Encoded?:
|
|
|
25
32
|
}
|
|
26
33
|
}
|
|
27
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Detecta mojibake UTF-8-as-Latin-1 y lo revierte. La heurística busca un
|
|
37
|
+
* primer byte UTF-8 multibyte (0xC2-0xF4) seguido de un byte de continuación
|
|
38
|
+
* (0x80-0xBF). En texto natural casi siempre indica corrupción de encoding;
|
|
39
|
+
* si no hay match, el body se considera limpio y se devuelve intacto, así
|
|
40
|
+
* los bodies ASCII puros o UTF-8 ya correctos no se ven afectados.
|
|
41
|
+
*/
|
|
42
|
+
function fixUtf8Mojibake(input: string): string {
|
|
43
|
+
if (!MOJIBAKE_PATTERN.test(input)) return input;
|
|
44
|
+
try {
|
|
45
|
+
return Buffer.from(input, 'latin1').toString('utf-8');
|
|
46
|
+
} catch {
|
|
47
|
+
return input;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const MOJIBAKE_PATTERN = /[Â-ô][-¿]/;
|
|
52
|
+
|
|
28
53
|
/**
|
|
29
54
|
* Safely parses query string parameters
|
|
30
55
|
* Handles multi-value parameters
|
|
@@ -35,15 +60,15 @@ export function parseQueryParams(
|
|
|
35
60
|
params: Record<string, string | undefined> | null | undefined
|
|
36
61
|
): Record<string, string> {
|
|
37
62
|
if (!params) return {};
|
|
38
|
-
|
|
63
|
+
|
|
39
64
|
const result: Record<string, string> = {};
|
|
40
|
-
|
|
65
|
+
|
|
41
66
|
for (const [key, value] of Object.entries(params)) {
|
|
42
67
|
if (value !== undefined) {
|
|
43
68
|
result[key] = value;
|
|
44
69
|
}
|
|
45
70
|
}
|
|
46
|
-
|
|
71
|
+
|
|
47
72
|
return result;
|
|
48
73
|
}
|
|
49
74
|
|
package/src/tenant/helpers.ts
CHANGED
|
@@ -29,7 +29,7 @@ export function tenantInfoFromClaims(claims: Record<string, any>): TenantInfo |
|
|
|
29
29
|
const countryCode = getClaim(claims, 'countryCode');
|
|
30
30
|
const personProfileId = getClaim(claims, 'personProfileId');
|
|
31
31
|
const orgProfileId = getClaim(claims, 'orgProfileId');
|
|
32
|
-
const hasCRM = getClaim(claims, 'hasCRM');
|
|
32
|
+
const hasCRM = getClaim(claims, 'hasCRM') === 'true';
|
|
33
33
|
|
|
34
34
|
if (!tenantId || !tenantType || !userId || !countryCode) {
|
|
35
35
|
return undefined;
|
|
@@ -60,7 +60,7 @@ describe('initTenantContext', () => {
|
|
|
60
60
|
expect(result.context.tenantInfo?.tenantType).toBe('ORG');
|
|
61
61
|
expect(result.context.tenantInfo?.countryCode).toBe('CO');
|
|
62
62
|
expect(result.context.tenantInfo?.plan).toBe('PRO');
|
|
63
|
-
expect(result.context.tenantInfo?.hasCRM).toBe(
|
|
63
|
+
expect(result.context.tenantInfo?.hasCRM).toBe(true);
|
|
64
64
|
});
|
|
65
65
|
|
|
66
66
|
it('initializes TenantContext (AsyncLocalStorage)', async () => {
|
|
@@ -47,7 +47,7 @@ describe('tenantInfoFromClaims', () => {
|
|
|
47
47
|
personProfileId: 'person_001',
|
|
48
48
|
orgProfileId: 'org_abc',
|
|
49
49
|
plan: 'PRO',
|
|
50
|
-
hasCRM:
|
|
50
|
+
hasCRM: true,
|
|
51
51
|
});
|
|
52
52
|
});
|
|
53
53
|
|
|
@@ -67,7 +67,7 @@ describe('tenantInfoFromClaims', () => {
|
|
|
67
67
|
|
|
68
68
|
it('hasCRM is false if claim is not "true"', () => {
|
|
69
69
|
const result = tenantInfoFromClaims({ ...validClaims, 'custom:hasCRM': 'false' });
|
|
70
|
-
expect(result?.hasCRM).toBe(
|
|
70
|
+
expect(result?.hasCRM).toBe(false);
|
|
71
71
|
});
|
|
72
72
|
|
|
73
73
|
it('plan is undefined if claim is not a valid Plan', () => {
|
|
@@ -108,7 +108,7 @@ describe('tenantInfoFromClaims', () => {
|
|
|
108
108
|
personProfileId: 'person_001',
|
|
109
109
|
orgProfileId: 'org_abc',
|
|
110
110
|
plan: 'PRO',
|
|
111
|
-
hasCRM:
|
|
111
|
+
hasCRM: true,
|
|
112
112
|
});
|
|
113
113
|
});
|
|
114
114
|
|