@middy/ssm 4.6.5 → 5.0.0-alpha.1

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 (4) hide show
  1. package/index.d.ts +28 -16
  2. package/index.js +191 -139
  3. package/package.json +6 -13
  4. package/index.cjs +0 -153
package/index.d.ts CHANGED
@@ -2,27 +2,39 @@ import middy from '@middy/core'
2
2
  import { Options as MiddyOptions } from '@middy/util'
3
3
  import { Context as LambdaContext } from 'aws-lambda'
4
4
  import { SSMClient, SSMClientConfig } from '@aws-sdk/client-ssm'
5
- import { JsonValue } from 'type-fest'
6
5
 
7
- interface Options<AwsSSMClient = SSMClient>
8
- extends MiddyOptions<AwsSSMClient, SSMClientConfig> {}
6
+ export type ParamType<T> = string & { __returnType?: T }
7
+ export declare function ssmParam<T> (path: string): ParamType<T>
9
8
 
10
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
11
- type ExtractSingles<T> = T extends `/${infer _}` ? never : T
12
-
13
- export type Context<TOptions extends Options | undefined> = TOptions extends {
14
- setToContext: true
9
+ export interface SSMOptions<AwsSSMClient = SSMClient>
10
+ extends Omit<MiddyOptions<AwsSSMClient, SSMClientConfig>, 'fetchData'> {
11
+ fetchData?: { [key: string]: string | ParamType<unknown> }
15
12
  }
16
- ? LambdaContext &
17
- Record<ExtractSingles<keyof TOptions['fetchData']>, JsonValue> &
18
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
19
- (keyof TOptions['fetchData'] extends `${infer _P}/${infer _S}`
20
- ? Record<string, JsonValue>
21
- : unknown)
13
+
14
+ export type Context<TOptions extends SSMOptions | undefined> =
15
+ TOptions extends { setToContext: true }
16
+ ? TOptions extends { fetchData: infer TFetchData }
17
+ ? LambdaContext & {
18
+ [Key in keyof TFetchData]: TFetchData[Key] extends ParamType<infer T>
19
+ ? T
20
+ : unknown
21
+ }
22
+ : never
22
23
  : LambdaContext
23
24
 
24
- declare function ssm<TOptions extends Options> (
25
+ export type Internal<TOptions extends SSMOptions | undefined> =
26
+ TOptions extends SSMOptions
27
+ ? TOptions extends { fetchData: infer TFetchData }
28
+ ? {
29
+ [Key in keyof TFetchData]: TFetchData[Key] extends ParamType<infer T>
30
+ ? T
31
+ : unknown
32
+ }
33
+ : {}
34
+ : {}
35
+
36
+ declare function ssm<TOptions extends SSMOptions> (
25
37
  options?: TOptions
26
- ): middy.MiddlewareObj<unknown, any, Error, Context<TOptions>>
38
+ ): middy.MiddlewareObj<unknown, any, Error, Context<TOptions>, Internal<TOptions>>
27
39
 
28
40
  export default ssm
package/index.js CHANGED
@@ -1,143 +1,195 @@
1
- import { canPrefetch, createPrefetchClient, createClient, processCache, getCache, modifyCache, jsonSafeParse, getInternal, sanitizeKey } from '@middy/util';
2
- import { SSMClient, GetParametersCommand, GetParametersByPathCommand } from '@aws-sdk/client-ssm';
3
- const awsRequestLimit = 10;
1
+ import {
2
+ canPrefetch,
3
+ createPrefetchClient,
4
+ createClient,
5
+ processCache,
6
+ getCache,
7
+ modifyCache,
8
+ jsonSafeParse,
9
+ getInternal,
10
+ sanitizeKey
11
+ } from '@middy/util'
12
+ import {
13
+ SSMClient,
14
+ GetParametersCommand,
15
+ GetParametersByPathCommand
16
+ } from '@aws-sdk/client-ssm'
17
+
18
+ const awsRequestLimit = 10
4
19
  const defaults = {
5
- AwsClient: SSMClient,
6
- awsClientOptions: {},
7
- awsClientAssumeRole: undefined,
8
- awsClientCapture: undefined,
9
- fetchData: {},
10
- disablePrefetch: false,
11
- cacheKey: 'ssm',
12
- cacheKeyExpiry: {},
13
- cacheExpiry: -1,
14
- setToContext: false
15
- };
16
- const ssmMiddleware = (opts = {})=>{
17
- const options = {
18
- ...defaults,
19
- ...opts
20
- };
21
- const fetch = (request, cachedValues)=>{
22
- return {
23
- ...fetchSingle(request, cachedValues),
24
- ...fetchByPath(request, cachedValues)
25
- };
26
- };
27
- const fetchSingle = (request, cachedValues = {})=>{
28
- const values = {};
29
- let batchReq = null;
30
- let batchInternalKeys = [];
31
- let batchFetchKeys = [];
32
- const namedKeys = [];
33
- const internalKeys = Object.keys(options.fetchData);
34
- const fetchKeys = Object.values(options.fetchData);
35
- for (const internalKey of internalKeys){
36
- if (cachedValues[internalKey]) continue;
37
- if (options.fetchData[internalKey].substr(-1) === '/') continue; // Skip path passed in
38
- namedKeys.push(internalKey);
39
- }
40
- for (const [idx, internalKey] of namedKeys.entries()){
41
- const fetchKey = options.fetchData[internalKey];
42
- batchInternalKeys.push(internalKey);
43
- batchFetchKeys.push(fetchKey);
44
- // from the first to the batch size skip, unless it's the last entry
45
- if ((!idx || (idx + 1) % awsRequestLimit !== 0) && !(idx + 1 === namedKeys.length)) {
46
- continue;
47
- }
48
- batchReq = client.send(new GetParametersCommand({
49
- Names: batchFetchKeys,
50
- WithDecryption: true
51
- })).then((resp)=>{
52
- // Don't sanitize key, mapped to set value in options
53
- return Object.assign(...(resp.InvalidParameters ?? []).map((fetchKey)=>{
54
- return {
55
- [fetchKey]: new Promise(()=>{
56
- const internalKey = internalKeys[fetchKeys.indexOf(fetchKey)];
57
- const value = getCache(options.cacheKey).value ?? {};
58
- value[internalKey] = undefined;
59
- modifyCache(options.cacheKey, value);
60
- throw new Error('[ssm] InvalidParameter ' + fetchKey);
61
- })
62
- };
63
- }), ...(resp.Parameters ?? []).map((param)=>{
64
- return {
65
- [param.Name]: parseValue(param)
66
- };
67
- }));
68
- }).catch((e)=>{
69
- const value = getCache(options.cacheKey).value ?? {};
70
- value[internalKey] = undefined;
71
- modifyCache(options.cacheKey, value);
72
- throw e;
73
- });
74
- for (const internalKey of batchInternalKeys){
75
- values[internalKey] = batchReq.then((params)=>{
76
- return params[options.fetchData[internalKey]];
77
- });
78
- }
79
- batchInternalKeys = [];
80
- batchFetchKeys = [];
81
- batchReq = null;
82
- }
83
- return values;
84
- };
85
- const fetchByPath = (request, cachedValues = {})=>{
86
- const values = {};
87
- for(const internalKey in options.fetchData){
88
- if (cachedValues[internalKey]) continue;
89
- const fetchKey = options.fetchData[internalKey];
90
- if (fetchKey.substr(-1) !== '/') continue; // Skip not path passed in
91
- values[internalKey] = fetchPath(fetchKey).catch((e)=>{
92
- const value = getCache(options.cacheKey).value ?? {};
93
- value[internalKey] = undefined;
94
- modifyCache(options.cacheKey, value);
95
- throw e;
96
- });
97
- }
98
- return values;
99
- };
100
- const fetchPath = (path, nextToken, values = {})=>{
101
- return client.send(new GetParametersByPathCommand({
102
- Path: path,
103
- NextToken: nextToken,
104
- Recursive: true,
20
+ AwsClient: SSMClient, // Allow for XRay
21
+ awsClientOptions: {},
22
+ awsClientAssumeRole: undefined,
23
+ awsClientCapture: undefined,
24
+ fetchData: {}, // { contextKey: fetchKey, contextPrefix: fetchPath/ }
25
+ disablePrefetch: false,
26
+ cacheKey: 'ssm',
27
+ cacheKeyExpiry: {},
28
+ cacheExpiry: -1,
29
+ setToContext: false
30
+ }
31
+
32
+ const ssmMiddleware = (opts = {}) => {
33
+ const options = { ...defaults, ...opts }
34
+
35
+ const fetch = (request, cachedValues) => {
36
+ return {
37
+ ...fetchSingle(request, cachedValues),
38
+ ...fetchByPath(request, cachedValues)
39
+ }
40
+ }
41
+
42
+ const fetchSingle = (request, cachedValues = {}) => {
43
+ const values = {}
44
+ let batchReq = null
45
+ let batchInternalKeys = []
46
+ let batchFetchKeys = []
47
+ const namedKeys = []
48
+
49
+ const internalKeys = Object.keys(options.fetchData)
50
+ const fetchKeys = Object.values(options.fetchData)
51
+ for (const internalKey of internalKeys) {
52
+ if (cachedValues[internalKey]) continue
53
+ if (options.fetchData[internalKey].substr(-1) === '/') continue // Skip path passed in
54
+ namedKeys.push(internalKey)
55
+ }
56
+
57
+ for (const [idx, internalKey] of namedKeys.entries()) {
58
+ const fetchKey = options.fetchData[internalKey]
59
+ batchInternalKeys.push(internalKey)
60
+ batchFetchKeys.push(fetchKey)
61
+ // from the first to the batch size skip, unless it's the last entry
62
+ if (
63
+ (!idx || (idx + 1) % awsRequestLimit !== 0) &&
64
+ !(idx + 1 === namedKeys.length)
65
+ ) {
66
+ continue
67
+ }
68
+
69
+ batchReq = client
70
+ .send(
71
+ new GetParametersCommand({
72
+ Names: batchFetchKeys,
105
73
  WithDecryption: true
106
- })).then((resp)=>{
107
- Object.assign(values, ...resp.Parameters.map((param)=>{
108
- return {
109
- [sanitizeKey(param.Name.replace(path, ''))]: parseValue(param)
110
- };
111
- }));
112
- if (resp.NextToken) return fetchPath(path, resp.NextToken, values);
113
- return values;
114
- });
115
- };
116
- const parseValue = (param)=>{
117
- if (param.Type === 'StringList') {
118
- return param.Value.split(',');
119
- }
120
- return jsonSafeParse(param.Value);
121
- };
122
- let client;
123
- if (canPrefetch(options)) {
124
- client = createPrefetchClient(options);
125
- processCache(options, fetch);
74
+ })
75
+ )
76
+ .then((resp) => {
77
+ // Don't sanitize key, mapped to set value in options
78
+ return Object.assign(
79
+ ...(resp.InvalidParameters ?? []).map((fetchKey) => {
80
+ return {
81
+ [fetchKey]: new Promise(() => {
82
+ const internalKey = internalKeys[fetchKeys.indexOf(fetchKey)]
83
+ const value = getCache(options.cacheKey).value ?? {}
84
+ value[internalKey] = undefined
85
+ modifyCache(options.cacheKey, value)
86
+ throw new Error('InvalidParameter ' + fetchKey, {
87
+ cause: { package: '@middy/ssm' }
88
+ })
89
+ })
90
+ }
91
+ }),
92
+ ...(resp.Parameters ?? []).map((param) => {
93
+ return { [param.Name]: parseValue(param) }
94
+ })
95
+ )
96
+ })
97
+ .catch((e) => {
98
+ const value = getCache(options.cacheKey).value ?? {}
99
+ value[internalKey] = undefined
100
+ modifyCache(options.cacheKey, value)
101
+ throw e
102
+ })
103
+
104
+ for (const internalKey of batchInternalKeys) {
105
+ values[internalKey] = batchReq.then((params) => {
106
+ return params[options.fetchData[internalKey]]
107
+ })
108
+ }
109
+
110
+ batchInternalKeys = []
111
+ batchFetchKeys = []
112
+ batchReq = null
126
113
  }
127
- const ssmMiddlewareBefore = async (request)=>{
128
- if (!client) {
129
- client = await createClient(options, request);
130
- }
131
- const { value } = processCache(options, fetch, request);
132
- Object.assign(request.internal, value);
133
- if (options.setToContext) {
134
- const data = await getInternal(Object.keys(options.fetchData), request);
135
- Object.assign(request.context, data);
136
- }
137
- };
138
- return {
139
- before: ssmMiddlewareBefore
140
- };
141
- };
142
- export default ssmMiddleware;
114
+ return values
115
+ }
116
+
117
+ const fetchByPath = (request, cachedValues = {}) => {
118
+ const values = {}
119
+ for (const internalKey in options.fetchData) {
120
+ if (cachedValues[internalKey]) continue
121
+ const fetchKey = options.fetchData[internalKey]
122
+ if (fetchKey.substr(-1) !== '/') continue // Skip not path passed in
123
+ values[internalKey] = fetchPath(fetchKey).catch((e) => {
124
+ const value = getCache(options.cacheKey).value ?? {}
125
+ value[internalKey] = undefined
126
+ modifyCache(options.cacheKey, value)
127
+ throw e
128
+ })
129
+ }
130
+ return values
131
+ }
132
+
133
+ const fetchPath = (path, nextToken, values = {}) => {
134
+ return client
135
+ .send(
136
+ new GetParametersByPathCommand({
137
+ Path: path,
138
+ NextToken: nextToken,
139
+ Recursive: true,
140
+ WithDecryption: true
141
+ })
142
+ )
143
+ .then((resp) => {
144
+ Object.assign(
145
+ values,
146
+ ...resp.Parameters.map((param) => {
147
+ return {
148
+ [sanitizeKey(param.Name.replace(path, ''))]: parseValue(param)
149
+ }
150
+ })
151
+ )
152
+ if (resp.NextToken) return fetchPath(path, resp.NextToken, values)
153
+ return values
154
+ })
155
+ }
156
+
157
+ const parseValue = (param) => {
158
+ if (param.Type === 'StringList') {
159
+ return param.Value.split(',')
160
+ }
161
+ return jsonSafeParse(param.Value)
162
+ }
163
+
164
+ let client
165
+ if (canPrefetch(options)) {
166
+ client = createPrefetchClient(options)
167
+ processCache(options, fetch)
168
+ }
169
+
170
+ const ssmMiddlewareBefore = async (request) => {
171
+ if (!client) {
172
+ client = await createClient(options, request)
173
+ }
174
+
175
+ const { value } = processCache(options, fetch, request)
176
+
177
+ Object.assign(request.internal, value)
178
+
179
+ if (options.setToContext) {
180
+ const data = await getInternal(Object.keys(options.fetchData), request)
181
+ Object.assign(request.context, data)
182
+ }
183
+ }
184
+
185
+ return {
186
+ before: ssmMiddlewareBefore
187
+ }
188
+ }
189
+
190
+ export default ssmMiddleware
143
191
 
192
+ // used for TS type inference (see index.d.ts)
193
+ export function ssmParam (name) {
194
+ return name
195
+ }
package/package.json CHANGED
@@ -1,33 +1,27 @@
1
1
  {
2
2
  "name": "@middy/ssm",
3
- "version": "4.6.5",
3
+ "version": "5.0.0-alpha.1",
4
4
  "description": "SSM (EC2 Systems Manager) parameters middleware for the middy framework",
5
5
  "type": "module",
6
6
  "engines": {
7
- "node": ">=16"
7
+ "node": ">=18"
8
8
  },
9
9
  "engineStrict": true,
10
10
  "publishConfig": {
11
11
  "access": "public"
12
12
  },
13
- "main": "./index.cjs",
14
13
  "module": "./index.js",
15
14
  "exports": {
16
15
  ".": {
17
16
  "import": {
18
17
  "types": "./index.d.ts",
19
18
  "default": "./index.js"
20
- },
21
- "require": {
22
- "types": "./index.d.ts",
23
- "default": "./index.cjs"
24
19
  }
25
20
  }
26
21
  },
27
22
  "types": "index.d.ts",
28
23
  "files": [
29
24
  "index.js",
30
- "index.cjs",
31
25
  "index.d.ts"
32
26
  ],
33
27
  "scripts": {
@@ -66,14 +60,13 @@
66
60
  "url": "https://github.com/sponsors/willfarrell"
67
61
  },
68
62
  "dependencies": {
69
- "@middy/util": "4.6.5"
63
+ "@middy/util": "5.0.0-alpha.1"
70
64
  },
71
65
  "devDependencies": {
72
66
  "@aws-sdk/client-ssm": "^3.0.0",
73
- "@middy/core": "4.6.5",
67
+ "@middy/core": "5.0.0-alpha.1",
74
68
  "@types/aws-lambda": "^8.10.101",
75
- "aws-xray-sdk": "^3.3.3",
76
- "type-fest": "^4.0.0"
69
+ "aws-xray-sdk": "^3.3.3"
77
70
  },
78
- "gitHead": "573d7b0bb243d8c5a9bcb00cf29d031aa7a0c606"
71
+ "gitHead": "ebce8d5df8783077fa49ba62ee9be20e8486a7f1"
79
72
  }
package/index.cjs DELETED
@@ -1,153 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- Object.defineProperty(module, "exports", {
6
- enumerable: true,
7
- get: function() {
8
- return _default;
9
- }
10
- });
11
- const _util = require("@middy/util");
12
- const _clientssm = require("@aws-sdk/client-ssm");
13
- const awsRequestLimit = 10;
14
- const defaults = {
15
- AwsClient: _clientssm.SSMClient,
16
- awsClientOptions: {},
17
- awsClientAssumeRole: undefined,
18
- awsClientCapture: undefined,
19
- fetchData: {},
20
- disablePrefetch: false,
21
- cacheKey: 'ssm',
22
- cacheKeyExpiry: {},
23
- cacheExpiry: -1,
24
- setToContext: false
25
- };
26
- const ssmMiddleware = (opts = {})=>{
27
- const options = {
28
- ...defaults,
29
- ...opts
30
- };
31
- const fetch = (request, cachedValues)=>{
32
- return {
33
- ...fetchSingle(request, cachedValues),
34
- ...fetchByPath(request, cachedValues)
35
- };
36
- };
37
- const fetchSingle = (request, cachedValues = {})=>{
38
- const values = {};
39
- let batchReq = null;
40
- let batchInternalKeys = [];
41
- let batchFetchKeys = [];
42
- const namedKeys = [];
43
- const internalKeys = Object.keys(options.fetchData);
44
- const fetchKeys = Object.values(options.fetchData);
45
- for (const internalKey of internalKeys){
46
- if (cachedValues[internalKey]) continue;
47
- if (options.fetchData[internalKey].substr(-1) === '/') continue; // Skip path passed in
48
- namedKeys.push(internalKey);
49
- }
50
- for (const [idx, internalKey] of namedKeys.entries()){
51
- const fetchKey = options.fetchData[internalKey];
52
- batchInternalKeys.push(internalKey);
53
- batchFetchKeys.push(fetchKey);
54
- // from the first to the batch size skip, unless it's the last entry
55
- if ((!idx || (idx + 1) % awsRequestLimit !== 0) && !(idx + 1 === namedKeys.length)) {
56
- continue;
57
- }
58
- batchReq = client.send(new _clientssm.GetParametersCommand({
59
- Names: batchFetchKeys,
60
- WithDecryption: true
61
- })).then((resp)=>{
62
- // Don't sanitize key, mapped to set value in options
63
- return Object.assign(...(resp.InvalidParameters ?? []).map((fetchKey)=>{
64
- return {
65
- [fetchKey]: new Promise(()=>{
66
- const internalKey = internalKeys[fetchKeys.indexOf(fetchKey)];
67
- const value = (0, _util.getCache)(options.cacheKey).value ?? {};
68
- value[internalKey] = undefined;
69
- (0, _util.modifyCache)(options.cacheKey, value);
70
- throw new Error('[ssm] InvalidParameter ' + fetchKey);
71
- })
72
- };
73
- }), ...(resp.Parameters ?? []).map((param)=>{
74
- return {
75
- [param.Name]: parseValue(param)
76
- };
77
- }));
78
- }).catch((e)=>{
79
- const value = (0, _util.getCache)(options.cacheKey).value ?? {};
80
- value[internalKey] = undefined;
81
- (0, _util.modifyCache)(options.cacheKey, value);
82
- throw e;
83
- });
84
- for (const internalKey of batchInternalKeys){
85
- values[internalKey] = batchReq.then((params)=>{
86
- return params[options.fetchData[internalKey]];
87
- });
88
- }
89
- batchInternalKeys = [];
90
- batchFetchKeys = [];
91
- batchReq = null;
92
- }
93
- return values;
94
- };
95
- const fetchByPath = (request, cachedValues = {})=>{
96
- const values = {};
97
- for(const internalKey in options.fetchData){
98
- if (cachedValues[internalKey]) continue;
99
- const fetchKey = options.fetchData[internalKey];
100
- if (fetchKey.substr(-1) !== '/') continue; // Skip not path passed in
101
- values[internalKey] = fetchPath(fetchKey).catch((e)=>{
102
- const value = (0, _util.getCache)(options.cacheKey).value ?? {};
103
- value[internalKey] = undefined;
104
- (0, _util.modifyCache)(options.cacheKey, value);
105
- throw e;
106
- });
107
- }
108
- return values;
109
- };
110
- const fetchPath = (path, nextToken, values = {})=>{
111
- return client.send(new _clientssm.GetParametersByPathCommand({
112
- Path: path,
113
- NextToken: nextToken,
114
- Recursive: true,
115
- WithDecryption: true
116
- })).then((resp)=>{
117
- Object.assign(values, ...resp.Parameters.map((param)=>{
118
- return {
119
- [(0, _util.sanitizeKey)(param.Name.replace(path, ''))]: parseValue(param)
120
- };
121
- }));
122
- if (resp.NextToken) return fetchPath(path, resp.NextToken, values);
123
- return values;
124
- });
125
- };
126
- const parseValue = (param)=>{
127
- if (param.Type === 'StringList') {
128
- return param.Value.split(',');
129
- }
130
- return (0, _util.jsonSafeParse)(param.Value);
131
- };
132
- let client;
133
- if ((0, _util.canPrefetch)(options)) {
134
- client = (0, _util.createPrefetchClient)(options);
135
- (0, _util.processCache)(options, fetch);
136
- }
137
- const ssmMiddlewareBefore = async (request)=>{
138
- if (!client) {
139
- client = await (0, _util.createClient)(options, request);
140
- }
141
- const { value } = (0, _util.processCache)(options, fetch, request);
142
- Object.assign(request.internal, value);
143
- if (options.setToContext) {
144
- const data = await (0, _util.getInternal)(Object.keys(options.fetchData), request);
145
- Object.assign(request.context, data);
146
- }
147
- };
148
- return {
149
- before: ssmMiddlewareBefore
150
- };
151
- };
152
- const _default = ssmMiddleware;
153
-