@middy/input-output-logger 5.0.3 → 5.2.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.
Files changed (2) hide show
  1. package/index.js +157 -100
  2. package/package.json +4 -3
package/index.js CHANGED
@@ -1,107 +1,164 @@
1
+ import { Transform } from 'node:stream'
2
+
1
3
  const defaults = {
2
- logger: console.log,
3
- awsContext: false,
4
- omitPaths: [],
5
- mask: undefined,
6
- replacer: undefined
7
- };
8
- const inputOutputLoggerMiddleware = (opts = {})=>{
9
- const { logger, awsContext, omitPaths, mask, replacer } = {
10
- ...defaults,
11
- ...opts
12
- };
13
- if (typeof logger !== 'function') {
14
- throw new Error('logger must be a function', {
15
- cause: {
16
- package: '@middy/input-output-logger'
17
- }
18
- });
4
+ logger: console.log,
5
+ awsContext: false,
6
+ omitPaths: [],
7
+ mask: undefined,
8
+ replacer: undefined
9
+ }
10
+
11
+ const inputOutputLoggerMiddleware = (opts = {}) => {
12
+ const { logger, awsContext, omitPaths, mask, replacer } = {
13
+ ...defaults,
14
+ ...opts
15
+ }
16
+
17
+ if (typeof logger !== 'function') {
18
+ throw new Error('logger must be a function', {
19
+ cause: {
20
+ package: '@middy/input-output-logger'
21
+ }
22
+ })
23
+ }
24
+
25
+ const omitPathTree = buildPathTree(omitPaths)
26
+ // needs `omitPathTree`, `logger`
27
+ const omitAndLog = (param, request) => {
28
+ const message = { [param]: request[param] }
29
+
30
+ if (awsContext) {
31
+ message.context = pick(request.context, awsContextKeys)
19
32
  }
20
- const omitPathTree = buildPathTree(omitPaths);
21
- const omitAndLog = (param, request)=>{
22
- const message = {
23
- [param]: request[param]
24
- };
25
- if (awsContext) {
26
- message.context = pick(request.context, awsContextKeys);
27
- }
28
- let cloneMessage = message;
29
- if (omitPaths.length) {
30
- cloneMessage = structuredClone(message, replacer);
31
- omit(cloneMessage, {
32
- [param]: omitPathTree[param]
33
- });
34
- }
35
- logger(cloneMessage);
36
- };
37
- const omit = (obj, pathTree = {})=>{
38
- if (Array.isArray(obj) && pathTree['[]']) {
39
- for(let i = 0, l = obj.length; i < l; i++){
40
- omit(obj[i], pathTree['[]']);
41
- }
42
- } else if (isObject(obj)) {
43
- for(const key in pathTree){
44
- if (pathTree[key] === true) {
45
- if (mask) {
46
- obj[key] = mask;
47
- } else {
48
- delete obj[key];
49
- }
50
- } else {
51
- omit(obj[key], pathTree[key]);
52
- }
53
- }
33
+
34
+ let cloneMessage = message
35
+ if (omitPaths.length) {
36
+ cloneMessage = structuredClone(message, replacer) // Full clone to prevent nested mutations
37
+ omit(cloneMessage, { [param]: omitPathTree[param] })
38
+ }
39
+ logger(cloneMessage)
40
+ }
41
+
42
+ // needs `mask`
43
+ const omit = (obj, pathTree = {}) => {
44
+ if (Array.isArray(obj) && pathTree['[]']) {
45
+ for (let i = 0, l = obj.length; i < l; i++) {
46
+ omit(obj[i], pathTree['[]'])
47
+ }
48
+ } else if (isObject(obj)) {
49
+ for (const key in pathTree) {
50
+ if (pathTree[key] === true) {
51
+ if (mask) {
52
+ obj[key] = mask
53
+ } else {
54
+ delete obj[key]
55
+ }
56
+ } else {
57
+ omit(obj[key], pathTree[key])
54
58
  }
55
- };
56
- const inputOutputLoggerMiddlewareBefore = async (request)=>omitAndLog('event', request);
57
- const inputOutputLoggerMiddlewareAfter = async (request)=>omitAndLog('response', request);
58
- const inputOutputLoggerMiddlewareOnError = async (request)=>{
59
- if (request.response === undefined) return;
60
- omitAndLog('response', request);
61
- };
62
- return {
63
- before: inputOutputLoggerMiddlewareBefore,
64
- after: inputOutputLoggerMiddlewareAfter,
65
- onError: inputOutputLoggerMiddlewareOnError
66
- };
67
- };
59
+ }
60
+ }
61
+ }
62
+
63
+ const inputOutputLoggerMiddlewareBefore = async (request) => {
64
+ omitAndLog('event', request)
65
+ }
66
+ const inputOutputLoggerMiddlewareAfter = async (request) => {
67
+ if (
68
+ request.response?._readableState ??
69
+ request.response?.body?._readableState
70
+ ) {
71
+ passThrough(request, omitAndLog)
72
+ } else {
73
+ omitAndLog('response', request)
74
+ }
75
+ }
76
+ const inputOutputLoggerMiddlewareOnError = async (request) => {
77
+ if (request.response === undefined) return
78
+ inputOutputLoggerMiddlewareAfter(request)
79
+ }
80
+
81
+ return {
82
+ before: inputOutputLoggerMiddlewareBefore,
83
+ after: inputOutputLoggerMiddlewareAfter,
84
+ onError: inputOutputLoggerMiddlewareOnError
85
+ }
86
+ }
87
+
88
+ // https://docs.aws.amazon.com/lambda/latest/dg/nodejs-context.html
68
89
  const awsContextKeys = [
69
- 'functionName',
70
- 'functionVersion',
71
- 'invokedFunctionArn',
72
- 'memoryLimitInMB',
73
- 'awsRequestId',
74
- 'logGroupName',
75
- 'logStreamName',
76
- 'identity',
77
- 'clientContext',
78
- 'callbackWaitsForEmptyEventLoop'
79
- ];
80
- const pick = (originalObject = {}, keysToPick = [])=>{
81
- const newObject = {};
82
- for (const path of keysToPick){
83
- if (originalObject[path] !== undefined) {
84
- newObject[path] = originalObject[path];
85
- }
90
+ 'functionName',
91
+ 'functionVersion',
92
+ 'invokedFunctionArn',
93
+ 'memoryLimitInMB',
94
+ 'awsRequestId',
95
+ 'logGroupName',
96
+ 'logStreamName',
97
+ 'identity',
98
+ 'clientContext',
99
+ 'callbackWaitsForEmptyEventLoop'
100
+ ]
101
+
102
+ // move to util, if ever used elsewhere
103
+ const pick = (originalObject = {}, keysToPick = []) => {
104
+ const newObject = {}
105
+ for (const path of keysToPick) {
106
+ // only supports first level
107
+ if (originalObject[path] !== undefined) {
108
+ newObject[path] = originalObject[path]
86
109
  }
87
- return newObject;
88
- };
89
- const buildPathTree = (paths)=>{
90
- const tree = {};
91
- for (let path of paths.sort().reverse()){
92
- if (!Array.isArray(path)) path = path.split('.');
93
- if (path.includes('__proto__')) continue;
94
- path.slice(0).reduce((a, b, idx)=>{
95
- if (idx < path.length - 1) {
96
- a[b] ??= {};
97
- return a[b];
98
- }
99
- a[b] = true;
100
- return true;
101
- }, tree);
110
+ }
111
+ return newObject
112
+ }
113
+
114
+ const isObject = (value) =>
115
+ value && typeof value === 'object' && value.constructor === Object
116
+
117
+ const buildPathTree = (paths) => {
118
+ const tree = {}
119
+ for (let path of paths.sort().reverse()) {
120
+ // reverse to ensure conflicting paths don't cause issues
121
+ if (!Array.isArray(path)) path = path.split('.')
122
+ if (path.includes('__proto__')) continue
123
+ path
124
+ .slice(0) // clone
125
+ .reduce((a, b, idx) => {
126
+ if (idx < path.length - 1) {
127
+ a[b] ??= {}
128
+ return a[b]
129
+ }
130
+ a[b] = true
131
+ return true
132
+ }, tree)
133
+ }
134
+ return tree
135
+ }
136
+
137
+ const passThrough = (request, omitAndLog) => {
138
+ // required because `core` remove body before `flush` is triggered
139
+ const hasBody = request.response?.body
140
+ let body = ''
141
+ const listen = new Transform({
142
+ objectMode: false,
143
+ transform (chunk, encoding, callback) {
144
+ body += chunk
145
+ this.push(chunk, encoding)
146
+ callback()
147
+ },
148
+ flush (callback) {
149
+ if (hasBody) {
150
+ omitAndLog('response', { response: { ...request.response, body } })
151
+ } else {
152
+ omitAndLog('response', { response: body })
153
+ }
154
+ callback()
102
155
  }
103
- return tree;
104
- };
105
- const isObject = (value)=>value && typeof value === 'object' && value.constructor === Object;
106
- export default inputOutputLoggerMiddleware;
156
+ })
157
+ if (hasBody) {
158
+ request.response.body = request.response.body.pipe(listen)
159
+ } else {
160
+ request.response = request.response.pipe(listen)
161
+ }
162
+ }
107
163
 
164
+ export default inputOutputLoggerMiddleware
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@middy/input-output-logger",
3
- "version": "5.0.3",
3
+ "version": "5.2.0",
4
4
  "description": "Input and output logger middleware for the middy framework",
5
5
  "type": "module",
6
6
  "engines": {
@@ -60,8 +60,9 @@
60
60
  "url": "https://github.com/sponsors/willfarrell"
61
61
  },
62
62
  "devDependencies": {
63
- "@middy/core": "5.0.3",
63
+ "@datastream/core": "0.0.35",
64
+ "@middy/core": "5.2.0",
64
65
  "@types/node": "^20.0.0"
65
66
  },
66
- "gitHead": "87660575a7ac2b52e4153c407a4c63c9449dcd0d"
67
+ "gitHead": "2d9096a49cd8fb62359517be96d6c93609df41f0"
67
68
  }