@api3/commons 0.7.0 → 0.7.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.
- package/dist/config-parsing/index.d.ts +5 -5
- package/dist/config-parsing/index.d.ts.map +1 -1
- package/dist/config-parsing/index.js +10 -9
- package/dist/config-parsing/index.js.map +1 -1
- package/package.json +1 -1
- package/src/config-parsing/index.test.ts +32 -0
- package/src/config-parsing/index.ts +14 -13
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import dotenv from 'dotenv';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
export declare const
|
|
4
|
-
export declare const
|
|
5
|
-
export declare const
|
|
6
|
-
export declare const nonBlankSecretsSchema: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
3
|
+
export declare const strictSecretNamePattern: RegExp;
|
|
4
|
+
export declare const strictSecretNameSchema: z.ZodString;
|
|
5
|
+
export declare const nonBlankSecretValueSchema: z.ZodString;
|
|
7
6
|
export type Secrets = Record<string, string>;
|
|
8
7
|
export interface InterpolationOptions {
|
|
9
|
-
allowBlankSecretValue
|
|
8
|
+
allowBlankSecretValue?: boolean;
|
|
9
|
+
validateSecretName?: boolean;
|
|
10
10
|
}
|
|
11
11
|
export type AnyObject = Record<string, unknown>;
|
|
12
12
|
export declare function interpolateSecretsIntoConfig<T = AnyObject>(config: T, secrets: unknown, options?: InterpolationOptions): T;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config-parsing/index.ts"],"names":[],"mappings":"AAEA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAG5B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config-parsing/index.ts"],"names":[],"mappings":"AAEA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAG5B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,uBAAuB,QAAqB,CAAC;AAE1D,eAAO,MAAM,sBAAsB,aAKhC,CAAC;AAEJ,eAAO,MAAM,yBAAyB,aAA2D,CAAC;AAElG,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAe7C,MAAM,WAAW,oBAAoB;IACnC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhD,wBAAgB,4BAA4B,CAAC,CAAC,GAAG,SAAS,EACxD,MAAM,EAAE,CAAC,EACT,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,oBAAoB,KA4B/B;AAED,eAAO,MAAM,WAAW,SAAU,MAAM,6BAA6C,CAAC;AAEtF,eAAO,MAAM,UAAU,SAAU,MAAM,QAA2C,CAAC"}
|
|
@@ -3,18 +3,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.loadConfig = exports.loadSecrets = exports.interpolateSecretsIntoConfig = exports.
|
|
6
|
+
exports.loadConfig = exports.loadSecrets = exports.interpolateSecretsIntoConfig = exports.nonBlankSecretValueSchema = exports.strictSecretNameSchema = exports.strictSecretNamePattern = void 0;
|
|
7
7
|
const node_fs_1 = require("node:fs");
|
|
8
8
|
const dotenv_1 = __importDefault(require("dotenv"));
|
|
9
9
|
const reduce_1 = __importDefault(require("lodash/reduce"));
|
|
10
10
|
const template_1 = __importDefault(require("lodash/template"));
|
|
11
11
|
const zod_1 = require("zod");
|
|
12
|
-
exports.
|
|
13
|
-
exports.
|
|
12
|
+
exports.strictSecretNamePattern = /^[A-Z][\dA-Z_]*$/;
|
|
13
|
+
exports.strictSecretNameSchema = zod_1.z
|
|
14
14
|
.string()
|
|
15
|
-
.regex(exports.
|
|
16
|
-
exports.
|
|
17
|
-
exports.nonBlankSecretsSchema = zod_1.z.record(exports.secretNameSchema, zod_1.z.string().min(1, { message: 'Secret cannot be blank' }));
|
|
15
|
+
.regex(exports.strictSecretNamePattern, `Secret name is not a valid. Secret name must match ${exports.strictSecretNamePattern.toString()}`);
|
|
16
|
+
exports.nonBlankSecretValueSchema = zod_1.z.string().min(1, { message: 'Secret cannot be blank' });
|
|
18
17
|
// Regular expression that does not match anything, ensuring no escaping or interpolation happens
|
|
19
18
|
// https://github.com/lodash/lodash/blob/4.17.15/lodash.js#L199
|
|
20
19
|
// eslint-disable-next-line prefer-named-capture-group
|
|
@@ -27,9 +26,11 @@ const ES_MATCH_REGEXP = /(?<!\\)\${([^\\}]*(?:\\.[^\\}]*)*)}/g;
|
|
|
27
26
|
// because "\\" becomes "\\\\" when converted to string
|
|
28
27
|
// eslint-disable-next-line prefer-named-capture-group
|
|
29
28
|
const ESCAPED_ES_MATCH_REGEXP = /\\\\(\${([^\\}]*(?:\\.[^\\}]*)*)})/g;
|
|
30
|
-
function interpolateSecretsIntoConfig(config, secrets, options
|
|
31
|
-
const { allowBlankSecretValue } = options;
|
|
32
|
-
const
|
|
29
|
+
function interpolateSecretsIntoConfig(config, secrets, options) {
|
|
30
|
+
const { allowBlankSecretValue = true, validateSecretName = true } = options ?? {};
|
|
31
|
+
const secretNameSchema = validateSecretName ? exports.strictSecretNameSchema : zod_1.z.string();
|
|
32
|
+
const secretValueSchema = allowBlankSecretValue ? zod_1.z.string() : exports.nonBlankSecretValueSchema;
|
|
33
|
+
const validatedSecrets = zod_1.z.record(secretNameSchema, secretValueSchema).parse(secrets);
|
|
33
34
|
const stringifiedSecrets = (0, reduce_1.default)(validatedSecrets, (acc, value, key) => {
|
|
34
35
|
return {
|
|
35
36
|
...acc,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config-parsing/index.ts"],"names":[],"mappings":";;;;;;AAAA,qCAAuC;AAEvC,oDAA4B;AAC5B,2DAAmC;AACnC,+DAAuC;AACvC,6BAAwB;AAEX,QAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config-parsing/index.ts"],"names":[],"mappings":";;;;;;AAAA,qCAAuC;AAEvC,oDAA4B;AAC5B,2DAAmC;AACnC,+DAAuC;AACvC,6BAAwB;AAEX,QAAA,uBAAuB,GAAG,kBAAkB,CAAC;AAE7C,QAAA,sBAAsB,GAAG,OAAC;KACpC,MAAM,EAAE;KACR,KAAK,CACJ,+BAAuB,EACvB,sDAAsD,+BAAuB,CAAC,QAAQ,EAAE,EAAE,CAC3F,CAAC;AAES,QAAA,yBAAyB,GAAG,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,wBAAwB,EAAE,CAAC,CAAC;AAIlG,iGAAiG;AACjG,+DAA+D;AAC/D,sDAAsD;AACtD,MAAM,eAAe,GAAG,MAAM,CAAC;AAC/B,gFAAgF;AAChF,+DAA+D;AAC/D,sDAAsD;AACtD,MAAM,eAAe,GAAG,sCAAsC,CAAC;AAC/D,wHAAwH;AACxH,uDAAuD;AACvD,sDAAsD;AACtD,MAAM,uBAAuB,GAAG,qCAAqC,CAAC;AAStE,SAAgB,4BAA4B,CAC1C,MAAS,EACT,OAAgB,EAChB,OAA8B;IAE9B,MAAM,EAAE,qBAAqB,GAAG,IAAI,EAAE,kBAAkB,GAAG,IAAI,EAAE,GAAG,OAAO,IAAI,EAAE,CAAC;IAClF,MAAM,gBAAgB,GAAG,kBAAkB,CAAC,CAAC,CAAC,8BAAsB,CAAC,CAAC,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC;IAClF,MAAM,iBAAiB,GAAG,qBAAqB,CAAC,CAAC,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,iCAAyB,CAAC;IACzF,MAAM,gBAAgB,GAAG,OAAC,CAAC,MAAM,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAEtF,MAAM,kBAAkB,GAAG,IAAA,gBAAM,EAC/B,gBAAgB,EAChB,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAClB,OAAO;YACL,GAAG,GAAG;YACN,8GAA8G;YAC9G,wBAAwB;YACxB,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;SAC1C,CAAC;IACJ,CAAC,EACD,EAAa,CACd,CAAC;IAEF,MAAM,kBAAkB,GAAG,IAAA,kBAAQ,EAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE;QAC1D,MAAM,EAAE,eAAe;QACvB,QAAQ,EAAE,eAAe;QACzB,WAAW,EAAE,eAAe;KAC7B,CAAC,CAAC,kBAAkB,CAAC,CAAC;IACvB,oHAAoH;IACpH,2CAA2C;IAC3C,OAAO,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,UAAU,CAAC,uBAAuB,EAAE,IAAI,CAAC,CAAM,CAAC;AACvF,CAAC;AA/BD,oEA+BC;AAEM,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,gBAAM,CAAC,KAAK,CAAC,IAAA,sBAAY,EAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;AAAzE,QAAA,WAAW,eAA8D;AAE/E,MAAM,UAAU,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAA,sBAAY,EAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;AAAtE,QAAA,UAAU,cAA4D"}
|
package/package.json
CHANGED
|
@@ -127,6 +127,38 @@ describe(interpolateSecretsIntoConfig.name, () => {
|
|
|
127
127
|
);
|
|
128
128
|
});
|
|
129
129
|
|
|
130
|
+
it('allows parsing secrets without validating secret names', () => {
|
|
131
|
+
const rawSecrets = {
|
|
132
|
+
SECRET_A: 'secretValueA',
|
|
133
|
+
SECRET_B: 'secretValueB',
|
|
134
|
+
'CANNOT-CONTAIN-HYPHEN': 'invalid',
|
|
135
|
+
lowercasedSecret: 'valid',
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
expect(() => interpolateSecretsIntoConfig(rawConfig, rawSecrets)).toThrow(
|
|
139
|
+
new ZodError([
|
|
140
|
+
{
|
|
141
|
+
validation: 'regex',
|
|
142
|
+
code: 'invalid_string',
|
|
143
|
+
message: 'Secret name is not a valid. Secret name must match /^[A-Z][\\dA-Z_]*$/',
|
|
144
|
+
path: ['CANNOT-CONTAIN-HYPHEN'],
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
validation: 'regex',
|
|
148
|
+
code: 'invalid_string',
|
|
149
|
+
message: 'Secret name is not a valid. Secret name must match /^[A-Z][\\dA-Z_]*$/',
|
|
150
|
+
path: ['lowercasedSecret'],
|
|
151
|
+
},
|
|
152
|
+
])
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
expect(interpolateSecretsIntoConfig(rawConfig, rawSecrets, { validateSecretName: false })).toStrictEqual({
|
|
156
|
+
property: 'value',
|
|
157
|
+
secretA: 'secretValueA',
|
|
158
|
+
secretB: 'secretValueB',
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
130
162
|
it('provides up to date README examples', () => {
|
|
131
163
|
// Basic interpolation
|
|
132
164
|
const basicInterpolationConfig = {
|
|
@@ -5,18 +5,16 @@ import reduce from 'lodash/reduce';
|
|
|
5
5
|
import template from 'lodash/template';
|
|
6
6
|
import { z } from 'zod';
|
|
7
7
|
|
|
8
|
-
export const
|
|
8
|
+
export const strictSecretNamePattern = /^[A-Z][\dA-Z_]*$/;
|
|
9
9
|
|
|
10
|
-
export const
|
|
10
|
+
export const strictSecretNameSchema = z
|
|
11
11
|
.string()
|
|
12
|
-
.regex(
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
.regex(
|
|
13
|
+
strictSecretNamePattern,
|
|
14
|
+
`Secret name is not a valid. Secret name must match ${strictSecretNamePattern.toString()}`
|
|
15
|
+
);
|
|
15
16
|
|
|
16
|
-
export const
|
|
17
|
-
secretNameSchema,
|
|
18
|
-
z.string().min(1, { message: 'Secret cannot be blank' })
|
|
19
|
-
);
|
|
17
|
+
export const nonBlankSecretValueSchema = z.string().min(1, { message: 'Secret cannot be blank' });
|
|
20
18
|
|
|
21
19
|
export type Secrets = Record<string, string>;
|
|
22
20
|
|
|
@@ -34,7 +32,8 @@ const ES_MATCH_REGEXP = /(?<!\\)\${([^\\}]*(?:\\.[^\\}]*)*)}/g;
|
|
|
34
32
|
const ESCAPED_ES_MATCH_REGEXP = /\\\\(\${([^\\}]*(?:\\.[^\\}]*)*)})/g;
|
|
35
33
|
|
|
36
34
|
export interface InterpolationOptions {
|
|
37
|
-
allowBlankSecretValue
|
|
35
|
+
allowBlankSecretValue?: boolean;
|
|
36
|
+
validateSecretName?: boolean;
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
export type AnyObject = Record<string, unknown>;
|
|
@@ -42,10 +41,12 @@ export type AnyObject = Record<string, unknown>;
|
|
|
42
41
|
export function interpolateSecretsIntoConfig<T = AnyObject>(
|
|
43
42
|
config: T,
|
|
44
43
|
secrets: unknown,
|
|
45
|
-
options
|
|
44
|
+
options?: InterpolationOptions
|
|
46
45
|
) {
|
|
47
|
-
const { allowBlankSecretValue } = options;
|
|
48
|
-
const
|
|
46
|
+
const { allowBlankSecretValue = true, validateSecretName = true } = options ?? {};
|
|
47
|
+
const secretNameSchema = validateSecretName ? strictSecretNameSchema : z.string();
|
|
48
|
+
const secretValueSchema = allowBlankSecretValue ? z.string() : nonBlankSecretValueSchema;
|
|
49
|
+
const validatedSecrets = z.record(secretNameSchema, secretValueSchema).parse(secrets);
|
|
49
50
|
|
|
50
51
|
const stringifiedSecrets = reduce(
|
|
51
52
|
validatedSecrets,
|