@api3/commons 0.6.2 → 0.7.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/README.md +10 -0
- package/dist/config-hash/index.d.ts +4 -0
- package/dist/config-hash/index.d.ts.map +1 -0
- package/dist/config-hash/index.js +30 -0
- package/dist/config-hash/index.js.map +1 -0
- package/dist/config-parsing/index.d.ts +15 -0
- package/dist/config-parsing/index.d.ts.map +1 -0
- package/dist/config-parsing/index.js +55 -0
- package/dist/config-parsing/index.js.map +1 -0
- package/dist/eslint/react.js +1 -1
- package/dist/eslint/react.js.map +1 -1
- package/dist/http/index.d.ts +29 -0
- package/dist/http/index.d.ts.map +1 -0
- package/dist/http/index.js +36 -0
- package/dist/http/index.js.map +1 -0
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js +15 -15
- package/dist/logger/index.js.map +1 -1
- package/dist/node-index.d.ts +2 -0
- package/dist/node-index.d.ts.map +1 -1
- package/dist/node-index.js +2 -0
- package/dist/node-index.js.map +1 -1
- package/dist/processing/unsafe-evaluate.d.ts.map +1 -1
- package/dist/processing/unsafe-evaluate.js +0 -2
- package/dist/processing/unsafe-evaluate.js.map +1 -1
- package/package.json +3 -1
- package/src/config-hash/README.md +25 -0
- package/src/config-hash/index.test.ts +93 -0
- package/src/config-hash/index.ts +25 -0
- package/src/config-parsing/README.md +48 -0
- package/src/config-parsing/index.test.ts +158 -0
- package/src/config-parsing/index.ts +75 -0
- package/src/eslint/react.js +1 -1
- package/src/http/README.md +3 -0
- package/src/http/index.test.ts +43 -0
- package/src/http/index.ts +61 -0
- package/src/logger/index.test.ts +19 -17
- package/src/logger/index.ts +15 -16
- package/src/node-index.ts +2 -0
- package/src/processing/processing.test.ts +1 -1
- package/src/processing/unsafe-evaluate.test.ts +0 -1
- package/src/processing/unsafe-evaluate.ts +0 -2
package/README.md
CHANGED
|
@@ -24,6 +24,16 @@ pnpm add @api3/commons
|
|
|
24
24
|
|
|
25
25
|
Read the documentation and sources of each module how to use it in the project.
|
|
26
26
|
|
|
27
|
+
### Modules
|
|
28
|
+
|
|
29
|
+
- [blockchain-utilities](./src/blockchain-utilities/README.md)
|
|
30
|
+
- [config-hash](./src/config-hash/README.md)
|
|
31
|
+
- [config-parsing](./src/config-parsing/README.md)
|
|
32
|
+
- [eslint](./src/eslint/README.md)
|
|
33
|
+
- [http](./src/http/README.md)
|
|
34
|
+
- [logger](./src/logger/README.md)
|
|
35
|
+
- [processing](./src/processing/README.md)
|
|
36
|
+
|
|
27
37
|
## Using the package in universal context
|
|
28
38
|
|
|
29
39
|
Some modules may be used only in both browser and Node.js context and some are Node.js only. To support this
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config-hash/index.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,yBAAyB,UAAW,GAAG,QAYnD,CAAC;AAEF,eAAO,MAAM,oBAAoB,gBAAiB,GAAG,WAGpD,CAAC;AAEF,eAAO,MAAM,gBAAgB,UAAW,MAAM,WAA4D,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createSha256Hash = exports.serializePlainObject = exports.sortObjectKeysRecursively = void 0;
|
|
7
|
+
const node_crypto_1 = require("node:crypto");
|
|
8
|
+
const isObject_1 = __importDefault(require("lodash/isObject"));
|
|
9
|
+
// We need to make sure the object is stringified in the same way every time, so we sort the keys alphabetically.
|
|
10
|
+
const sortObjectKeysRecursively = (value) => {
|
|
11
|
+
if (value === null)
|
|
12
|
+
return null;
|
|
13
|
+
if (!(0, isObject_1.default)(value) || Array.isArray(value))
|
|
14
|
+
return value;
|
|
15
|
+
const sortedKeys = Object.keys(value).sort();
|
|
16
|
+
const sortedObject = {};
|
|
17
|
+
for (const key of sortedKeys) {
|
|
18
|
+
sortedObject[key] = (0, exports.sortObjectKeysRecursively)(value[key]);
|
|
19
|
+
}
|
|
20
|
+
return sortedObject;
|
|
21
|
+
};
|
|
22
|
+
exports.sortObjectKeysRecursively = sortObjectKeysRecursively;
|
|
23
|
+
const serializePlainObject = (plainObject) => {
|
|
24
|
+
const sortedObject = (0, exports.sortObjectKeysRecursively)(plainObject);
|
|
25
|
+
return JSON.stringify(sortedObject);
|
|
26
|
+
};
|
|
27
|
+
exports.serializePlainObject = serializePlainObject;
|
|
28
|
+
const createSha256Hash = (value) => `0x${(0, node_crypto_1.createHash)('sha256').update(value).digest('hex')}`;
|
|
29
|
+
exports.createSha256Hash = createSha256Hash;
|
|
30
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config-hash/index.ts"],"names":[],"mappings":";;;;;;AAAA,6CAAyC;AAEzC,+DAAuC;AAEvC,iHAAiH;AAC1G,MAAM,yBAAyB,GAAG,CAAC,KAAU,EAAE,EAAE;IACtD,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAChC,IAAI,CAAC,IAAA,kBAAQ,EAAC,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAE3D,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7C,MAAM,YAAY,GAAQ,EAAE,CAAC;IAE7B,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE;QAC5B,YAAY,CAAC,GAAG,CAAC,GAAG,IAAA,iCAAyB,EAAE,KAAa,CAAC,GAAG,CAAC,CAAC,CAAC;KACpE;IAED,OAAO,YAAY,CAAC;AACtB,CAAC,CAAC;AAZW,QAAA,yBAAyB,6BAYpC;AAEK,MAAM,oBAAoB,GAAG,CAAC,WAAgB,EAAE,EAAE;IACvD,MAAM,YAAY,GAAG,IAAA,iCAAyB,EAAC,WAAW,CAAC,CAAC;IAC5D,OAAO,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;AACtC,CAAC,CAAC;AAHW,QAAA,oBAAoB,wBAG/B;AAEK,MAAM,gBAAgB,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,KAAK,IAAA,wBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;AAA9F,QAAA,gBAAgB,oBAA8E"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
export declare const secretNamePattern: RegExp;
|
|
4
|
+
export declare const secretNameSchema: z.ZodString;
|
|
5
|
+
export declare const secretsSchema: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
6
|
+
export declare const nonBlankSecretsSchema: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
7
|
+
export type Secrets = Record<string, string>;
|
|
8
|
+
export interface InterpolationOptions {
|
|
9
|
+
allowBlankSecretValue: boolean;
|
|
10
|
+
}
|
|
11
|
+
export type AnyObject = Record<string, unknown>;
|
|
12
|
+
export declare function interpolateSecretsIntoConfig<T = AnyObject>(config: T, secrets: unknown, options?: InterpolationOptions): T;
|
|
13
|
+
export declare const loadSecrets: (path: string) => dotenv.DotenvParseOutput;
|
|
14
|
+
export declare const loadConfig: (path: string) => any;
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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,iBAAiB,QAAqB,CAAC;AAEpD,eAAO,MAAM,gBAAgB,aAEoF,CAAC;AAElH,eAAO,MAAM,aAAa,uCAAyC,CAAC;AAEpE,eAAO,MAAM,qBAAqB,uCAGjC,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAe7C,MAAM,WAAW,oBAAoB;IACnC,qBAAqB,EAAE,OAAO,CAAC;CAChC;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,GAAE,oBAAsD,KA0BhE;AAED,eAAO,MAAM,WAAW,SAAU,MAAM,6BAA6C,CAAC;AAEtF,eAAO,MAAM,UAAU,SAAU,MAAM,QAA2C,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.loadConfig = exports.loadSecrets = exports.interpolateSecretsIntoConfig = exports.nonBlankSecretsSchema = exports.secretsSchema = exports.secretNameSchema = exports.secretNamePattern = void 0;
|
|
7
|
+
const node_fs_1 = require("node:fs");
|
|
8
|
+
const dotenv_1 = __importDefault(require("dotenv"));
|
|
9
|
+
const reduce_1 = __importDefault(require("lodash/reduce"));
|
|
10
|
+
const template_1 = __importDefault(require("lodash/template"));
|
|
11
|
+
const zod_1 = require("zod");
|
|
12
|
+
exports.secretNamePattern = /^[A-Z][\dA-Z_]*$/;
|
|
13
|
+
exports.secretNameSchema = zod_1.z
|
|
14
|
+
.string()
|
|
15
|
+
.regex(exports.secretNamePattern, `Secret name is not a valid. Secret name must match ${exports.secretNamePattern.toString()}`);
|
|
16
|
+
exports.secretsSchema = zod_1.z.record(exports.secretNameSchema, zod_1.z.string());
|
|
17
|
+
exports.nonBlankSecretsSchema = zod_1.z.record(exports.secretNameSchema, zod_1.z.string().min(1, { message: 'Secret cannot be blank' }));
|
|
18
|
+
// Regular expression that does not match anything, ensuring no escaping or interpolation happens
|
|
19
|
+
// https://github.com/lodash/lodash/blob/4.17.15/lodash.js#L199
|
|
20
|
+
// eslint-disable-next-line prefer-named-capture-group
|
|
21
|
+
const NO_MATCH_REGEXP = /($^)/;
|
|
22
|
+
// Regular expression matching ES template literal delimiter (${}) with escaping
|
|
23
|
+
// https://github.com/lodash/lodash/blob/4.17.15/lodash.js#L175
|
|
24
|
+
// eslint-disable-next-line prefer-named-capture-group
|
|
25
|
+
const ES_MATCH_REGEXP = /(?<!\\)\${([^\\}]*(?:\\.[^\\}]*)*)}/g;
|
|
26
|
+
// Regular expression matching the escaped ES template literal delimiter (${}). We need to use "\\\\" (four backslashes)
|
|
27
|
+
// because "\\" becomes "\\\\" when converted to string
|
|
28
|
+
// eslint-disable-next-line prefer-named-capture-group
|
|
29
|
+
const ESCAPED_ES_MATCH_REGEXP = /\\\\(\${([^\\}]*(?:\\.[^\\}]*)*)})/g;
|
|
30
|
+
function interpolateSecretsIntoConfig(config, secrets, options = { allowBlankSecretValue: true }) {
|
|
31
|
+
const { allowBlankSecretValue } = options;
|
|
32
|
+
const validatedSecrets = (allowBlankSecretValue ? exports.secretsSchema : exports.nonBlankSecretsSchema).parse(secrets);
|
|
33
|
+
const stringifiedSecrets = (0, reduce_1.default)(validatedSecrets, (acc, value, key) => {
|
|
34
|
+
return {
|
|
35
|
+
...acc,
|
|
36
|
+
// Convert to value to JSON to encode new lines as "\n". The resulting value will be a JSON string with quotes
|
|
37
|
+
// which are sliced off.
|
|
38
|
+
[key]: JSON.stringify(value).slice(1, -1),
|
|
39
|
+
};
|
|
40
|
+
}, {});
|
|
41
|
+
const interpolatedConfig = (0, template_1.default)(JSON.stringify(config), {
|
|
42
|
+
escape: NO_MATCH_REGEXP,
|
|
43
|
+
evaluate: NO_MATCH_REGEXP,
|
|
44
|
+
interpolate: ES_MATCH_REGEXP,
|
|
45
|
+
})(stringifiedSecrets);
|
|
46
|
+
// Un-escape the escaped config interpolations (e.g. to enable interpolation in processing snippets). Optimistically
|
|
47
|
+
// assume, the config type has not changed.
|
|
48
|
+
return JSON.parse(interpolatedConfig.replaceAll(ESCAPED_ES_MATCH_REGEXP, '$1'));
|
|
49
|
+
}
|
|
50
|
+
exports.interpolateSecretsIntoConfig = interpolateSecretsIntoConfig;
|
|
51
|
+
const loadSecrets = (path) => dotenv_1.default.parse((0, node_fs_1.readFileSync)(path, 'utf8'));
|
|
52
|
+
exports.loadSecrets = loadSecrets;
|
|
53
|
+
const loadConfig = (path) => JSON.parse((0, node_fs_1.readFileSync)(path, 'utf8'));
|
|
54
|
+
exports.loadConfig = loadConfig;
|
|
55
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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,iBAAiB,GAAG,kBAAkB,CAAC;AAEvC,QAAA,gBAAgB,GAAG,OAAC;KAC9B,MAAM,EAAE;KACR,KAAK,CAAC,yBAAiB,EAAE,sDAAsD,yBAAiB,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AAErG,QAAA,aAAa,GAAG,OAAC,CAAC,MAAM,CAAC,wBAAgB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,CAAC;AAEvD,QAAA,qBAAqB,GAAG,OAAC,CAAC,MAAM,CAC3C,wBAAgB,EAChB,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,wBAAwB,EAAE,CAAC,CACzD,CAAC;AAIF,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;AAQtE,SAAgB,4BAA4B,CAC1C,MAAS,EACT,OAAgB,EAChB,UAAgC,EAAE,qBAAqB,EAAE,IAAI,EAAE;IAE/D,MAAM,EAAE,qBAAqB,EAAE,GAAG,OAAO,CAAC;IAC1C,MAAM,gBAAgB,GAAG,CAAC,qBAAqB,CAAC,CAAC,CAAC,qBAAa,CAAC,CAAC,CAAC,6BAAqB,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAExG,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;AA7BD,oEA6BC;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/dist/eslint/react.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
2
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires, lodash/import-scope
|
|
3
3
|
const { merge } = require('lodash');
|
|
4
4
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
5
5
|
const { universalRestrictedImportsConfig, universalImportOrderConfig } = require('./internal');
|
package/dist/eslint/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.js","sourceRoot":"","sources":["../../src/eslint/react.js"],"names":[],"mappings":";AAAA,
|
|
1
|
+
{"version":3,"file":"react.js","sourceRoot":"","sources":["../../src/eslint/react.js"],"names":[],"mappings":";AAAA,mFAAmF;AACnF,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEpC,8DAA8D;AAC9D,MAAM,EAAE,gCAAgC,EAAE,0BAA0B,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;AAE/F,MAAM,CAAC,OAAO,GAAG;IACf,MAAM,EAAE,2BAA2B;IACnC,aAAa,EAAE;QACb,WAAW,EAAE,IAAI;QACjB,YAAY,EAAE;YACZ,GAAG,EAAE,IAAI,EAAE,sBAAsB;SAClC;QACD,UAAU,EAAE,QAAQ,EAAE,mCAAmC;KAC1D;IACD,QAAQ,EAAE;QACR,KAAK,EAAE;YACL,OAAO,EAAE,QAAQ,EAAE,6CAA6C;SACjE;KACF;IACD,GAAG,EAAE;QACH,IAAI,EAAE,IAAI;QACV,OAAO,EAAE,IAAI;KACd;IACD,OAAO,EAAE,CAAC,kBAAkB,EAAE,gCAAgC,CAAC;IAC/D,OAAO,EAAE,CAAC,OAAO,EAAE,oBAAoB,EAAE,QAAQ,EAAE,QAAQ,CAAC;IAC5D,KAAK,EAAE;QACL,cAAc,EAAE;YACd,OAAO;YACP,KAAK,CAAC,EAAE,EAAE,0BAA0B,EAAE;gBACpC,4BAA4B;gBAC5B,UAAU,EAAE;oBACV;wBACE,OAAO,EAAE,OAAO;wBAChB,KAAK,EAAE,SAAS;wBAChB,QAAQ,EAAE,QAAQ;qBACnB;iBACF;aACF,CAAC;SACH;QACD,kCAAkC;QAClC,gCAAgC,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,sBAAsB,EAAE,QAAQ,EAAE,CAAC;QAC3F,8BAA8B,EAAE,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QACzD,wBAAwB,EAAE,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QACnD,gCAAgC,EAAE,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,CAAC;QAC/G,yBAAyB,EAAE,KAAK;QAChC,8BAA8B,EAAE,KAAK;QACrC,yBAAyB,EAAE,KAAK;QAChC,kBAAkB,EAAE,KAAK;QACzB,wBAAwB,EAAE,KAAK;QAC/B,qBAAqB,EAAE,KAAK;QAC5B,8BAA8B,EAAE,KAAK;QACrC,mBAAmB,EAAE,KAAK;QAC1B,mBAAmB,EAAE,KAAK;QAC1B,4BAA4B,EAAE,KAAK;QACnC,uBAAuB,EAAE,KAAK;QAC9B,mCAAmC,EAAE,KAAK;QAC1C,8BAA8B,EAAE,KAAK;QACrC,sBAAsB,EAAE,KAAK;QAC7B,qBAAqB,EAAE,KAAK;QAC5B,6BAA6B,EAAE,KAAK;QACpC,4BAA4B,EAAE,KAAK;QACnC,8BAA8B,EAAE,KAAK;QACrC,kBAAkB,EAAE,KAAK;QACzB,0BAA0B,EAAE,KAAK;QACjC,6BAA6B,EAAE,KAAK;QACpC,yBAAyB,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QACrE,qCAAqC,EAAE,OAAO;QAE9C,+CAA+C;QAC/C,0CAA0C,EAAE;YAC1C,OAAO;YACP,KAAK,CAAC,EAAE,EAAE,gCAAgC,EAAE;gBAC1C,KAAK,EAAE;oBACL;wBACE,IAAI,EAAE,OAAO;wBACb,WAAW,EAAE,CAAC,SAAS,CAAC;wBACxB,OAAO,EACL,uHAAuH;qBAC1H;iBACF;aACF,CAAC;SACH;QAED,mCAAmC;QACnC,qBAAqB,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC;KAC3C;CACF,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type Method, type AxiosError, type AxiosResponse } from 'axios';
|
|
2
|
+
export interface Request {
|
|
3
|
+
readonly method: Method;
|
|
4
|
+
readonly url: string;
|
|
5
|
+
readonly headers?: Record<string, string>;
|
|
6
|
+
readonly queryParams?: Record<string, any>;
|
|
7
|
+
readonly timeout?: number;
|
|
8
|
+
readonly body?: unknown;
|
|
9
|
+
}
|
|
10
|
+
export interface ErrorResponse {
|
|
11
|
+
readonly axiosResponse: Pick<AxiosResponse, 'data' | 'headers' | 'status'> | undefined;
|
|
12
|
+
readonly message: string;
|
|
13
|
+
readonly code: string | undefined;
|
|
14
|
+
}
|
|
15
|
+
export declare const extractAxiosErrorData: (error: AxiosError) => ErrorResponse;
|
|
16
|
+
interface ExecuteRequestSuccess<T> {
|
|
17
|
+
success: true;
|
|
18
|
+
errorData: undefined;
|
|
19
|
+
data: T;
|
|
20
|
+
}
|
|
21
|
+
interface ExecuteRequestError {
|
|
22
|
+
success: false;
|
|
23
|
+
errorData: ErrorResponse;
|
|
24
|
+
data: undefined;
|
|
25
|
+
}
|
|
26
|
+
export type ExecuteRequestResult<T> = ExecuteRequestError | ExecuteRequestSuccess<T>;
|
|
27
|
+
export declare function executeRequest<T>(request: Request): Promise<ExecuteRequestResult<T>>;
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/http/index.ts"],"names":[],"mappings":"AACA,OAAc,EAAE,KAAK,MAAM,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAKhF,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC,GAAG,SAAS,CAAC;IACvF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC;AAED,eAAO,MAAM,qBAAqB,UAAW,UAAU,KAAG,aAOzD,CAAC;AAEF,UAAU,qBAAqB,CAAC,CAAC;IAC/B,OAAO,EAAE,IAAI,CAAC;IACd,SAAS,EAAE,SAAS,CAAC;IACrB,IAAI,EAAE,CAAC,CAAC;CACT;AACD,UAAU,mBAAmB;IAC3B,OAAO,EAAE,KAAK,CAAC;IACf,SAAS,EAAE,aAAa,CAAC;IACzB,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,MAAM,MAAM,oBAAoB,CAAC,CAAC,IAAI,mBAAmB,GAAG,qBAAqB,CAAC,CAAC,CAAC,CAAC;AAErF,wBAAsB,cAAc,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAiB1F"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.executeRequest = exports.extractAxiosErrorData = void 0;
|
|
7
|
+
const promise_utils_1 = require("@api3/promise-utils");
|
|
8
|
+
const axios_1 = __importDefault(require("axios"));
|
|
9
|
+
const pick_1 = __importDefault(require("lodash/pick"));
|
|
10
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
11
|
+
const extractAxiosErrorData = (error) => {
|
|
12
|
+
// Inspired by: https://axios-http.com/docs/handling_errors
|
|
13
|
+
return {
|
|
14
|
+
axiosResponse: error.response ? (0, pick_1.default)(error.response, ['data', 'status']) : undefined,
|
|
15
|
+
message: error.message,
|
|
16
|
+
code: error.code,
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
exports.extractAxiosErrorData = extractAxiosErrorData;
|
|
20
|
+
async function executeRequest(request) {
|
|
21
|
+
const { url, method, body, headers = {}, queryParams = {}, timeout = DEFAULT_TIMEOUT_MS } = request;
|
|
22
|
+
const goAxios = await (0, promise_utils_1.go)(async () => (0, axios_1.default)({
|
|
23
|
+
url,
|
|
24
|
+
method,
|
|
25
|
+
headers,
|
|
26
|
+
data: body,
|
|
27
|
+
params: queryParams,
|
|
28
|
+
timeout,
|
|
29
|
+
}));
|
|
30
|
+
if (!goAxios.success)
|
|
31
|
+
return { success: false, errorData: (0, exports.extractAxiosErrorData)(goAxios.error), data: undefined };
|
|
32
|
+
const response = goAxios.data;
|
|
33
|
+
return { success: true, errorData: undefined, data: response.data };
|
|
34
|
+
}
|
|
35
|
+
exports.executeRequest = executeRequest;
|
|
36
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/http/index.ts"],"names":[],"mappings":";;;;;;AAAA,uDAAyC;AACzC,kDAAgF;AAChF,uDAA+B;AAE/B,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAiB3B,MAAM,qBAAqB,GAAG,CAAC,KAAiB,EAAiB,EAAE;IACxE,2DAA2D;IAC3D,OAAO;QACL,aAAa,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAA,cAAI,EAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;QACpF,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,IAAI,EAAE,KAAK,CAAC,IAAI;KACA,CAAC;AACrB,CAAC,CAAC;AAPW,QAAA,qBAAqB,yBAOhC;AAeK,KAAK,UAAU,cAAc,CAAI,OAAgB;IACtD,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,EAAE,EAAE,WAAW,GAAG,EAAE,EAAE,OAAO,GAAG,kBAAkB,EAAE,GAAG,OAAO,CAAC;IAEpG,MAAM,OAAO,GAAG,MAAM,IAAA,kBAAE,EAAwC,KAAK,IAAI,EAAE,CACzE,IAAA,eAAK,EAAC;QACJ,GAAG;QACH,MAAM;QACN,OAAO;QACP,IAAI,EAAE,IAAI;QACV,MAAM,EAAE,WAAW;QACnB,OAAO;KACR,CAAC,CACH,CAAC;IACF,IAAI,CAAC,OAAO,CAAC,OAAO;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,IAAA,6BAAqB,EAAC,OAAO,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IAClH,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAE9B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC;AACtE,CAAC;AAjBD,wCAiBC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/logger/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAK9B,eAAO,MAAM,gBAAgB,6BAA8B,CAAC;AAE5D,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1D,eAAO,MAAM,eAAe,6CAA8C,CAAC;AAE3E,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAExD,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,SAAS,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAqCD,eAAO,MAAM,gBAAgB,WAAY,SAAS,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/logger/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAK9B,eAAO,MAAM,gBAAgB,6BAA8B,CAAC;AAE5D,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1D,eAAO,MAAM,eAAe,6CAA8C,CAAC;AAE3E,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAExD,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,SAAS,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAqCD,eAAO,MAAM,gBAAgB,WAAY,SAAS,mBAgBjD,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAE7C,MAAM,WAAW,MAAM;IACrB,cAAc,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3D,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,KAAK,IAAI,CAAC;IACvD,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,KAAK,IAAI,CAAC;IACtD,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,KAAK,IAAI,CAAC;IAKtD,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,KAAK,IAAI,CAAC,GACtD,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,UAAU,KAAK,IAAI,CAAC,CAAC;IAClE,KAAK,EAAE,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,MAAM,CAAC;CAC9C;AAcD,eAAO,MAAM,OAAO,WAAY,cAAc,KAAG,MA+BhD,CAAC;AAEF,eAAO,MAAM,iBAAiB,WAAY,OAAO,KAAG,SAuBnD,CAAC;AAEF,eAAO,MAAM,YAAY,WAAY,SAAS,WAG7C,CAAC"}
|
package/dist/logger/index.js
CHANGED
|
@@ -45,43 +45,43 @@ const createBaseLogger = (config) => {
|
|
|
45
45
|
return winston_1.default.createLogger({
|
|
46
46
|
level: minLevel,
|
|
47
47
|
// This format is recommended by the "winston-console-format" package.
|
|
48
|
-
format: winston_1.default.format.combine(winston_1.default.format.timestamp(), winston_1.default.format.
|
|
48
|
+
format: winston_1.default.format.combine(winston_1.default.format.timestamp(), winston_1.default.format.errors({ stack: true }), winston_1.default.format.splat(), winston_1.default.format.json()),
|
|
49
49
|
silent: !enabled,
|
|
50
50
|
exitOnError: false,
|
|
51
51
|
transports: [createConsoleTransport(config)],
|
|
52
52
|
});
|
|
53
53
|
};
|
|
54
54
|
exports.createBaseLogger = createBaseLogger;
|
|
55
|
+
const createFullContext = (localContext) => {
|
|
56
|
+
const globalContext = (0, async_storage_1.getAsyncLocalStorage)().getStore();
|
|
57
|
+
if (!globalContext && !localContext)
|
|
58
|
+
return;
|
|
59
|
+
const fullContext = { ...globalContext, ...localContext };
|
|
60
|
+
// If the context contains a `name` or `message` field, it will override the `name` and `message` fields of the log
|
|
61
|
+
// entry. To avoid this, we return the context as a separate field.
|
|
62
|
+
return { ctx: fullContext };
|
|
63
|
+
};
|
|
55
64
|
// Winston by default merges content of `context` among the rest of the fields for the JSON format.
|
|
56
65
|
// That's causing an override of fields `name` and `message` if they are present.
|
|
57
66
|
const wrapper = (logger) => {
|
|
58
67
|
return {
|
|
59
68
|
debug: (message, localContext) => {
|
|
60
|
-
|
|
61
|
-
const fullContext = globalContext || localContext ? { ...globalContext, ...localContext } : undefined;
|
|
62
|
-
logger.debug(message, fullContext);
|
|
69
|
+
logger.debug(message, createFullContext(localContext));
|
|
63
70
|
},
|
|
64
71
|
info: (message, localContext) => {
|
|
65
|
-
|
|
66
|
-
const fullContext = globalContext || localContext ? { ...globalContext, ...localContext } : undefined;
|
|
67
|
-
logger.info(message, fullContext);
|
|
72
|
+
logger.info(message, createFullContext(localContext));
|
|
68
73
|
},
|
|
69
74
|
warn: (message, localContext) => {
|
|
70
|
-
|
|
71
|
-
const fullContext = globalContext || localContext ? { ...globalContext, ...localContext } : undefined;
|
|
72
|
-
logger.warn(message, fullContext);
|
|
75
|
+
logger.warn(message, createFullContext(localContext));
|
|
73
76
|
},
|
|
74
77
|
// We need to handle both overloads of the `error` function
|
|
75
78
|
error: (message, errorOrLocalContext, localContext) => {
|
|
76
|
-
const globalContext = (0, async_storage_1.getAsyncLocalStorage)().getStore();
|
|
77
79
|
// eslint-disable-next-line lodash/prefer-lodash-typecheck
|
|
78
80
|
if (errorOrLocalContext instanceof Error) {
|
|
79
|
-
|
|
80
|
-
logger.error(message, errorOrLocalContext, fullContext);
|
|
81
|
+
logger.error(message, errorOrLocalContext, createFullContext(localContext));
|
|
81
82
|
}
|
|
82
83
|
else {
|
|
83
|
-
|
|
84
|
-
logger.error(message, fullContext);
|
|
84
|
+
logger.error(message, createFullContext(errorOrLocalContext));
|
|
85
85
|
}
|
|
86
86
|
},
|
|
87
87
|
child: (options) => (0, exports.wrapper)(logger.child(options)),
|
package/dist/logger/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/logger/index.ts"],"names":[],"mappings":";;;;;;AAAA,sDAA8B;AAC9B,mEAAuD;AAEvD,mDAAuD;AAE1C,QAAA,gBAAgB,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAU,CAAC;AAI/C,QAAA,eAAe,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAU,CAAC;AAW3E,MAAM,sBAAsB,GAAG,CAAC,MAAiB,EAAE,EAAE;IACnD,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAE7C,IAAI,CAAC,OAAO,EAAE;QACZ,OAAO,IAAI,iBAAO,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;KACzD;IAED,QAAQ,MAAM,EAAE;QACd,KAAK,MAAM,CAAC,CAAC;YACX,OAAO,IAAI,iBAAO,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,iBAAO,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;SAC1E;QACD,KAAK,QAAQ,CAAC,CAAC;YACb,MAAM,OAAO,GAAG;gBACd,QAAQ,CAAC,CAAC,CAAC,iBAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI;gBACxD,iBAAO,CAAC,MAAM,CAAC,SAAS,EAAE;gBAC1B,IAAA,sCAAa,EAAC;oBACZ,QAAQ,EAAE,IAAI;oBACd,SAAS,EAAE,EAAE;oBACb,cAAc,EAAE;wBACd,KAAK,EAAE,MAAM,CAAC,iBAAiB;wBAC/B,MAAM,EAAE,QAAQ;wBAChB,cAAc,EAAE,MAAM,CAAC,iBAAiB;wBACxC,WAAW,EAAE,GAAG;wBAChB,OAAO,EAAE,MAAM,CAAC,iBAAiB;qBAClC;iBACF,CAAC;aACH,CAAC,MAAM,CAAC,OAAO,CAA6B,CAAC;YAE9C,OAAO,IAAI,iBAAO,CAAC,UAAU,CAAC,OAAO,CAAC;gBACpC,MAAM,EAAE,iBAAO,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;aAC3C,CAAC,CAAC;SACJ;KACF;AACH,CAAC,CAAC;AAEK,MAAM,gBAAgB,GAAG,CAAC,MAAiB,EAAE,EAAE;IACpD,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IAErC,OAAO,iBAAO,CAAC,YAAY,CAAC;QAC1B,KAAK,EAAE,QAAQ;QACf,sEAAsE;QACtE,MAAM,EAAE,iBAAO,CAAC,MAAM,CAAC,OAAO,CAC5B,iBAAO,CAAC,MAAM,CAAC,SAAS,EAAE,EAC1B,iBAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/logger/index.ts"],"names":[],"mappings":"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"}
|
package/dist/node-index.d.ts
CHANGED
package/dist/node-index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-index.d.ts","sourceRoot":"","sources":["../src/node-index.ts"],"names":[],"mappings":"AACA,cAAc,mBAAmB,CAAC;AAClC,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"node-index.d.ts","sourceRoot":"","sources":["../src/node-index.ts"],"names":[],"mappings":"AACA,cAAc,mBAAmB,CAAC;AAClC,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC"}
|
package/dist/node-index.js
CHANGED
|
@@ -18,4 +18,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
18
18
|
__exportStar(require("./universal-index"), exports);
|
|
19
19
|
__exportStar(require("./logger"), exports);
|
|
20
20
|
__exportStar(require("./processing"), exports);
|
|
21
|
+
__exportStar(require("./config-parsing"), exports);
|
|
22
|
+
__exportStar(require("./config-hash"), exports);
|
|
21
23
|
//# sourceMappingURL=node-index.js.map
|
package/dist/node-index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-index.js","sourceRoot":"","sources":["../src/node-index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,iHAAiH;AACjH,oDAAkC;AAClC,2CAAyB;AACzB,+CAA6B"}
|
|
1
|
+
{"version":3,"file":"node-index.js","sourceRoot":"","sources":["../src/node-index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,iHAAiH;AACjH,oDAAkC;AAClC,2CAAyB;AACzB,+CAA6B;AAC7B,mDAAiC;AACjC,gDAA8B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unsafe-evaluate.d.ts","sourceRoot":"","sources":["../../src/processing/unsafe-evaluate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"unsafe-evaluate.d.ts","sourceRoot":"","sources":["../../src/processing/unsafe-evaluate.ts"],"names":[],"mappings":"AA+EA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,cAAc,SAAU,MAAM,mBAAmB,OAAO,MAAM,EAAE,OAAO,CAAC,WAAW,MAAM,YAarG,CAAC;AAEF,eAAO,MAAM,gBAAgB,SAAgB,MAAM,WAAW,OAAO,WAAW,MAAM,iBAmCrF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,mBAAmB,SAAgB,MAAM,mBAAmB,OAAO,MAAM,EAAE,OAAO,CAAC,WAAW,MAAM,qBAoChH,CAAC"}
|
|
@@ -33,7 +33,6 @@ const node_stream_1 = __importDefault(require("node:stream"));
|
|
|
33
33
|
const node_string_decoder_1 = __importDefault(require("node:string_decoder"));
|
|
34
34
|
const node_timers_1 = __importDefault(require("node:timers"));
|
|
35
35
|
const node_tls_1 = __importDefault(require("node:tls"));
|
|
36
|
-
const node_trace_events_1 = __importDefault(require("node:trace_events"));
|
|
37
36
|
const node_tty_1 = __importDefault(require("node:tty"));
|
|
38
37
|
const node_url_1 = __importDefault(require("node:url"));
|
|
39
38
|
const node_util_1 = __importDefault(require("node:util"));
|
|
@@ -72,7 +71,6 @@ const builtInNodeModules = {
|
|
|
72
71
|
string_decoder: node_string_decoder_1.default,
|
|
73
72
|
timers: node_timers_1.default,
|
|
74
73
|
tls: node_tls_1.default,
|
|
75
|
-
trace_events: node_trace_events_1.default,
|
|
76
74
|
tty: node_tty_1.default,
|
|
77
75
|
url: node_url_1.default,
|
|
78
76
|
util: node_util_1.default,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unsafe-evaluate.js","sourceRoot":"","sources":["../../src/processing/unsafe-evaluate.ts"],"names":[],"mappings":";;;;;;AAAA,8BAA8B;AAC9B,8DAAiC;AACjC,wEAA2C;AAC3C,8DAAiC;AACjC,4EAA+C;AAC/C,gEAAmC;AACnC,gEAAmC;AACnC,oEAAuC;AACvC,8DAAiC;AACjC,4DAA+B;AAC/B,wDAA2B;AAC3B,8DAAiC;AACjC,sDAAyB;AACzB,0DAA6B;AAC7B,4DAA+B;AAC/B,4DAA+B;AAC/B,oEAAuC;AACvC,8DAAiC;AACjC,wDAA2B;AAC3B,sDAAyB;AACzB,0DAA6B;AAC7B,sEAAyC;AACzC,gEAAmC;AACnC,kEAAqC;AACrC,0DAA6B;AAC7B,8DAAiC;AACjC,8EAAiD;AACjD,8DAAiC;AACjC,wDAA2B;AAC3B,
|
|
1
|
+
{"version":3,"file":"unsafe-evaluate.js","sourceRoot":"","sources":["../../src/processing/unsafe-evaluate.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@api3/commons",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"keywords": [],
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"engines": {
|
|
@@ -24,6 +24,8 @@
|
|
|
24
24
|
"@api3/promise-utils": "^0.4.0",
|
|
25
25
|
"@typescript-eslint/eslint-plugin": "^6.2.1",
|
|
26
26
|
"@typescript-eslint/parser": "^6.2.1",
|
|
27
|
+
"axios": "^1.6.7",
|
|
28
|
+
"dotenv": "^16.4.5",
|
|
27
29
|
"eslint-config-next": "^13.1.6",
|
|
28
30
|
"eslint-plugin-check-file": "^2.6.2",
|
|
29
31
|
"eslint-plugin-cypress": "^2.14.0",
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Config hashing
|
|
2
|
+
|
|
3
|
+
> Small Node.js module that allows creating a hash from a config object.
|
|
4
|
+
|
|
5
|
+
This module uses Node.js built-in `crypto` module to access the `sha256` hashing algorithm. A similar functionality can
|
|
6
|
+
be achieved in the browser environment using the `SubtleCrypto` API. Open an issue if you need this functionality in
|
|
7
|
+
browser.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
1. Call `serializePlainObject` to uniquely serialize a config file. This functions recursively sorts the properties of
|
|
12
|
+
an object, such that `{a: 1, b: 2}` and `{b: 2, a: 1}` will produce the same result.
|
|
13
|
+
2. Call `createSha256Hash` to create a hash from the serialized object.
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
const serializedConfig = serializePlainObject(config);
|
|
17
|
+
const hash = createSha256Hash(serializedConfig);
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
To simulate the same behavior in UNIX shell, you can use the following command:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
# Assumes jq, tr, sha256sum and awk are available
|
|
24
|
+
jq --sort-keys --compact-output . ./file.json | tr -d '\n' | sha256sum | awk '{ print "0x"$1 }'
|
|
25
|
+
```
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
import { createSha256Hash, serializePlainObject, sortObjectKeysRecursively } from '.';
|
|
6
|
+
|
|
7
|
+
describe(sortObjectKeysRecursively.name, () => {
|
|
8
|
+
it('should sort the keys alphabetically', () => {
|
|
9
|
+
const plainObject = {
|
|
10
|
+
c: 3,
|
|
11
|
+
a: 1,
|
|
12
|
+
b: 2,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
expect(sortObjectKeysRecursively(plainObject)).toStrictEqual({ a: 1, b: 2, c: 3 });
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should handle nested objects', () => {
|
|
19
|
+
const plainObject = {
|
|
20
|
+
c: 3,
|
|
21
|
+
a: 1,
|
|
22
|
+
b: {
|
|
23
|
+
e: 5,
|
|
24
|
+
d: 4,
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
expect(sortObjectKeysRecursively(plainObject)).toStrictEqual({ a: 1, b: { d: 4, e: 5 }, c: 3 });
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('handles other primitive values', () => {
|
|
32
|
+
const plainObject = {
|
|
33
|
+
c: 'c-val',
|
|
34
|
+
a: 1,
|
|
35
|
+
F: null,
|
|
36
|
+
B: false,
|
|
37
|
+
b: [2, null, 1],
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
expect(sortObjectKeysRecursively(plainObject)).toStrictEqual({
|
|
41
|
+
B: false,
|
|
42
|
+
F: null,
|
|
43
|
+
a: 1,
|
|
44
|
+
b: [2, null, 1],
|
|
45
|
+
c: 'c-val',
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe(serializePlainObject.name, () => {
|
|
51
|
+
it('creates the same serialization string for equal objects', () => {
|
|
52
|
+
const plainObject = {
|
|
53
|
+
c: 'c-val',
|
|
54
|
+
a: 1,
|
|
55
|
+
F: null,
|
|
56
|
+
B: false,
|
|
57
|
+
b: [2, null, 1],
|
|
58
|
+
};
|
|
59
|
+
const otherPlainObject = {
|
|
60
|
+
a: 1,
|
|
61
|
+
F: null,
|
|
62
|
+
B: false,
|
|
63
|
+
c: 'c-val',
|
|
64
|
+
b: [2, null, 1],
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
expect(serializePlainObject(plainObject)).toBe(serializePlainObject(otherPlainObject));
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe(createSha256Hash.name, () => {
|
|
72
|
+
it('should create same hash as SubtleCrypto in browser', () => {
|
|
73
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest#converting_a_digest_to_a_hex_string
|
|
74
|
+
const text = 'An obscure body in the S-K System, your majesty. The inhabitants refer to it as the planet Earth.';
|
|
75
|
+
|
|
76
|
+
expect(createSha256Hash(text)).toBe('0x6efd383745a964768989b9df420811abc6e5873f874fc22a76fe9258e020c2e1');
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('can produce the same hash on UNIX', () => {
|
|
81
|
+
const fixturePath = join(__dirname, '../../test/fixtures/config-hash/file.json');
|
|
82
|
+
|
|
83
|
+
// Compute the hash using this module
|
|
84
|
+
const rawConfig = JSON.parse(readFileSync(fixturePath, 'utf8'));
|
|
85
|
+
const serializedConfig = serializePlainObject(rawConfig);
|
|
86
|
+
const hash = createSha256Hash(serializedConfig);
|
|
87
|
+
|
|
88
|
+
// Compute the hash using UNIX commands
|
|
89
|
+
const unixCommand = `jq --sort-keys --compact-output . ${fixturePath} | tr -d '\n' | sha256sum | awk '{ print "0x"$1 }'`;
|
|
90
|
+
const unixCommandHash = execSync(unixCommand, { encoding: 'utf8' }).trim();
|
|
91
|
+
|
|
92
|
+
expect(hash).toBe(unixCommandHash);
|
|
93
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
import isObject from 'lodash/isObject';
|
|
4
|
+
|
|
5
|
+
// We need to make sure the object is stringified in the same way every time, so we sort the keys alphabetically.
|
|
6
|
+
export const sortObjectKeysRecursively = (value: any) => {
|
|
7
|
+
if (value === null) return null;
|
|
8
|
+
if (!isObject(value) || Array.isArray(value)) return value;
|
|
9
|
+
|
|
10
|
+
const sortedKeys = Object.keys(value).sort();
|
|
11
|
+
const sortedObject: any = {};
|
|
12
|
+
|
|
13
|
+
for (const key of sortedKeys) {
|
|
14
|
+
sortedObject[key] = sortObjectKeysRecursively((value as any)[key]);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return sortedObject;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const serializePlainObject = (plainObject: any) => {
|
|
21
|
+
const sortedObject = sortObjectKeysRecursively(plainObject);
|
|
22
|
+
return JSON.stringify(sortedObject);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const createSha256Hash = (value: string) => `0x${createHash('sha256').update(value).digest('hex')}`;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Config parsing
|
|
2
|
+
|
|
3
|
+
> Node.js module for parsing configuration files with support of interpolating secrets.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
### Parsing a configuration file and secrets
|
|
8
|
+
|
|
9
|
+
You can use the following helper functions to read the configuration and secrets file from filesystem:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
const rawConfig = loadConfig(join(__dirname, 'config.json'));
|
|
13
|
+
const rawSecrets = loadSecrets(join(__dirname, 'secrets.env'));
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### Creating the full configuration object
|
|
17
|
+
|
|
18
|
+
The module defines a `interpolateSecretsIntoConfig` function that takes a config object along with the secrets and
|
|
19
|
+
returns the config with the secrets interpolated into it.
|
|
20
|
+
|
|
21
|
+
Examples:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// Basic interpolation
|
|
25
|
+
const rawConfig = {
|
|
26
|
+
prop: 'value',
|
|
27
|
+
secret: '${SECRET}',
|
|
28
|
+
};
|
|
29
|
+
const config = interpolateSecretsIntoConfig(rawConfig, { SECRET: 'secretValue' }); // { prop: 'value', secret: 'secretValue' }
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
// Allows escaping the interpolation syntax
|
|
34
|
+
const rawConfig = {
|
|
35
|
+
prop: 'value',
|
|
36
|
+
secret: '\\${SECRET}',
|
|
37
|
+
};
|
|
38
|
+
const config = interpolateSecretsIntoConfig(rawConfig, { SECRET: 'secretValue' }); // { prop: 'value', secret: '${SECRET}' }
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
// Throws an error if something is not right
|
|
43
|
+
const rawConfig = {
|
|
44
|
+
prop: 'value',
|
|
45
|
+
secret: '${SECRET}',
|
|
46
|
+
};
|
|
47
|
+
const config = interpolateSecretsIntoConfig(rawConfig); // Error: SECRET is not defined
|
|
48
|
+
```
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { ZodError } from 'zod';
|
|
2
|
+
|
|
3
|
+
import { interpolateSecretsIntoConfig } from './index';
|
|
4
|
+
|
|
5
|
+
const rawConfig = {
|
|
6
|
+
property: 'value',
|
|
7
|
+
secretB: '${SECRET_B}',
|
|
8
|
+
secretA: '${SECRET_A}',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
describe(interpolateSecretsIntoConfig.name, () => {
|
|
12
|
+
it('interpolates secrets into config', () => {
|
|
13
|
+
const config = interpolateSecretsIntoConfig(rawConfig, {
|
|
14
|
+
SECRET_A: 'secretValueA',
|
|
15
|
+
SECRET_B: 'secretValueB',
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
expect(config).toStrictEqual({
|
|
19
|
+
property: 'value',
|
|
20
|
+
secretA: 'secretValueA',
|
|
21
|
+
secretB: 'secretValueB',
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('allows empty secrets by default', () => {
|
|
26
|
+
const config = interpolateSecretsIntoConfig(rawConfig, {
|
|
27
|
+
SECRET_A: '',
|
|
28
|
+
SECRET_B: '',
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
expect(config).toStrictEqual({
|
|
32
|
+
property: 'value',
|
|
33
|
+
secretA: '',
|
|
34
|
+
secretB: '',
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('disallows empty secrets when configured so', () => {
|
|
39
|
+
expect(() => {
|
|
40
|
+
interpolateSecretsIntoConfig(
|
|
41
|
+
rawConfig,
|
|
42
|
+
{
|
|
43
|
+
SECRET_A: '',
|
|
44
|
+
SECRET_B: '',
|
|
45
|
+
},
|
|
46
|
+
{ allowBlankSecretValue: false }
|
|
47
|
+
);
|
|
48
|
+
}).toThrow('Secret cannot be blank');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('can use "\\" to escape interpolation', () => {
|
|
52
|
+
const escapedConfig = {
|
|
53
|
+
...rawConfig,
|
|
54
|
+
secretA: '\\${SECRET_A}',
|
|
55
|
+
};
|
|
56
|
+
const config = interpolateSecretsIntoConfig(escapedConfig, {
|
|
57
|
+
SECRET_A: 'secretValueA',
|
|
58
|
+
SECRET_B: 'secretValueB',
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
expect(config).toStrictEqual({
|
|
62
|
+
property: 'value',
|
|
63
|
+
secretA: '${SECRET_A}',
|
|
64
|
+
secretB: 'secretValueB',
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('allows extraneous secrets', () => {
|
|
69
|
+
const config = interpolateSecretsIntoConfig(rawConfig, {
|
|
70
|
+
SECRET_A: 'secretValueA',
|
|
71
|
+
SECRET_B: 'secretValueB',
|
|
72
|
+
SECRET_C: 'secretValueC',
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
expect(config).toStrictEqual({
|
|
76
|
+
property: 'value',
|
|
77
|
+
secretA: 'secretValueA',
|
|
78
|
+
secretB: 'secretValueB',
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('throws an error when a secret is missing', () => {
|
|
83
|
+
expect(() => {
|
|
84
|
+
interpolateSecretsIntoConfig(rawConfig, {
|
|
85
|
+
SECRET_A: 'secretValueA',
|
|
86
|
+
});
|
|
87
|
+
}).toThrow('SECRET_B is not defined');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('allows no secrets', () => {
|
|
91
|
+
const noSecretsConfig = { value: 'no secrets' };
|
|
92
|
+
|
|
93
|
+
expect(interpolateSecretsIntoConfig(noSecretsConfig, {})).toStrictEqual(noSecretsConfig);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('throws when secret name is invalid', () => {
|
|
97
|
+
expect(() => {
|
|
98
|
+
interpolateSecretsIntoConfig(rawConfig, {
|
|
99
|
+
SECRET_A: 'secretValueA',
|
|
100
|
+
'0_SECRET_STARTING_WITH_NUMBER': 'invalid',
|
|
101
|
+
});
|
|
102
|
+
}).toThrow(
|
|
103
|
+
new ZodError([
|
|
104
|
+
{
|
|
105
|
+
validation: 'regex',
|
|
106
|
+
code: 'invalid_string',
|
|
107
|
+
message: 'Secret name is not a valid. Secret name must match /^[A-Z][\\dA-Z_]*$/',
|
|
108
|
+
path: ['0_SECRET_STARTING_WITH_NUMBER'],
|
|
109
|
+
},
|
|
110
|
+
])
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
expect(() => {
|
|
114
|
+
interpolateSecretsIntoConfig(rawConfig, {
|
|
115
|
+
SECRET_A: 'secretValueA',
|
|
116
|
+
'CANNOT-CONTAIN-HYPHEN': 'invalid',
|
|
117
|
+
});
|
|
118
|
+
}).toThrow(
|
|
119
|
+
new ZodError([
|
|
120
|
+
{
|
|
121
|
+
validation: 'regex',
|
|
122
|
+
code: 'invalid_string',
|
|
123
|
+
message: 'Secret name is not a valid. Secret name must match /^[A-Z][\\dA-Z_]*$/',
|
|
124
|
+
path: ['CANNOT-CONTAIN-HYPHEN'],
|
|
125
|
+
},
|
|
126
|
+
])
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('provides up to date README examples', () => {
|
|
131
|
+
// Basic interpolation
|
|
132
|
+
const basicInterpolationConfig = {
|
|
133
|
+
prop: 'value',
|
|
134
|
+
secret: '${SECRET}',
|
|
135
|
+
};
|
|
136
|
+
expect(interpolateSecretsIntoConfig(basicInterpolationConfig, { SECRET: 'secretValue' })).toStrictEqual({
|
|
137
|
+
prop: 'value',
|
|
138
|
+
secret: 'secretValue',
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Allows escaping the interpolation syntax
|
|
142
|
+
const escapingInterpolationConfig = {
|
|
143
|
+
prop: 'value',
|
|
144
|
+
secret: '\\${SECRET}',
|
|
145
|
+
};
|
|
146
|
+
expect(interpolateSecretsIntoConfig(escapingInterpolationConfig, { SECRET: 'secretValue' })).toStrictEqual({
|
|
147
|
+
prop: 'value',
|
|
148
|
+
secret: '${SECRET}',
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// Throws an error if something is not right
|
|
152
|
+
const missingSecretConfig = {
|
|
153
|
+
prop: 'value',
|
|
154
|
+
secret: '${SECRET}',
|
|
155
|
+
};
|
|
156
|
+
expect(() => interpolateSecretsIntoConfig(missingSecretConfig, {})).toThrow('SECRET is not defined');
|
|
157
|
+
});
|
|
158
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
|
|
3
|
+
import dotenv from 'dotenv';
|
|
4
|
+
import reduce from 'lodash/reduce';
|
|
5
|
+
import template from 'lodash/template';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
|
|
8
|
+
export const secretNamePattern = /^[A-Z][\dA-Z_]*$/;
|
|
9
|
+
|
|
10
|
+
export const secretNameSchema = z
|
|
11
|
+
.string()
|
|
12
|
+
.regex(secretNamePattern, `Secret name is not a valid. Secret name must match ${secretNamePattern.toString()}`);
|
|
13
|
+
|
|
14
|
+
export const secretsSchema = z.record(secretNameSchema, z.string());
|
|
15
|
+
|
|
16
|
+
export const nonBlankSecretsSchema = z.record(
|
|
17
|
+
secretNameSchema,
|
|
18
|
+
z.string().min(1, { message: 'Secret cannot be blank' })
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export type Secrets = Record<string, string>;
|
|
22
|
+
|
|
23
|
+
// Regular expression that does not match anything, ensuring no escaping or interpolation happens
|
|
24
|
+
// https://github.com/lodash/lodash/blob/4.17.15/lodash.js#L199
|
|
25
|
+
// eslint-disable-next-line prefer-named-capture-group
|
|
26
|
+
const NO_MATCH_REGEXP = /($^)/;
|
|
27
|
+
// Regular expression matching ES template literal delimiter (${}) with escaping
|
|
28
|
+
// https://github.com/lodash/lodash/blob/4.17.15/lodash.js#L175
|
|
29
|
+
// eslint-disable-next-line prefer-named-capture-group
|
|
30
|
+
const ES_MATCH_REGEXP = /(?<!\\)\${([^\\}]*(?:\\.[^\\}]*)*)}/g;
|
|
31
|
+
// Regular expression matching the escaped ES template literal delimiter (${}). We need to use "\\\\" (four backslashes)
|
|
32
|
+
// because "\\" becomes "\\\\" when converted to string
|
|
33
|
+
// eslint-disable-next-line prefer-named-capture-group
|
|
34
|
+
const ESCAPED_ES_MATCH_REGEXP = /\\\\(\${([^\\}]*(?:\\.[^\\}]*)*)})/g;
|
|
35
|
+
|
|
36
|
+
export interface InterpolationOptions {
|
|
37
|
+
allowBlankSecretValue: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type AnyObject = Record<string, unknown>;
|
|
41
|
+
|
|
42
|
+
export function interpolateSecretsIntoConfig<T = AnyObject>(
|
|
43
|
+
config: T,
|
|
44
|
+
secrets: unknown,
|
|
45
|
+
options: InterpolationOptions = { allowBlankSecretValue: true }
|
|
46
|
+
) {
|
|
47
|
+
const { allowBlankSecretValue } = options;
|
|
48
|
+
const validatedSecrets = (allowBlankSecretValue ? secretsSchema : nonBlankSecretsSchema).parse(secrets);
|
|
49
|
+
|
|
50
|
+
const stringifiedSecrets = reduce(
|
|
51
|
+
validatedSecrets,
|
|
52
|
+
(acc, value, key) => {
|
|
53
|
+
return {
|
|
54
|
+
...acc,
|
|
55
|
+
// Convert to value to JSON to encode new lines as "\n". The resulting value will be a JSON string with quotes
|
|
56
|
+
// which are sliced off.
|
|
57
|
+
[key]: JSON.stringify(value).slice(1, -1),
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
{} as Secrets
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const interpolatedConfig = template(JSON.stringify(config), {
|
|
64
|
+
escape: NO_MATCH_REGEXP,
|
|
65
|
+
evaluate: NO_MATCH_REGEXP,
|
|
66
|
+
interpolate: ES_MATCH_REGEXP,
|
|
67
|
+
})(stringifiedSecrets);
|
|
68
|
+
// Un-escape the escaped config interpolations (e.g. to enable interpolation in processing snippets). Optimistically
|
|
69
|
+
// assume, the config type has not changed.
|
|
70
|
+
return JSON.parse(interpolatedConfig.replaceAll(ESCAPED_ES_MATCH_REGEXP, '$1')) as T;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const loadSecrets = (path: string) => dotenv.parse(readFileSync(path, 'utf8'));
|
|
74
|
+
|
|
75
|
+
export const loadConfig = (path: string) => JSON.parse(readFileSync(path, 'utf8'));
|
package/src/eslint/react.js
CHANGED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { AxiosError, type AxiosResponse } from 'axios';
|
|
2
|
+
|
|
3
|
+
import { executeRequest, extractAxiosErrorData } from '.';
|
|
4
|
+
|
|
5
|
+
describe(extractAxiosErrorData.name, () => {
|
|
6
|
+
it('should return an error response object', () => {
|
|
7
|
+
const axiosError = new AxiosError('error message', '500', undefined, {}, {
|
|
8
|
+
data: 'error data',
|
|
9
|
+
status: 500,
|
|
10
|
+
statusText: 'Internal Server Error',
|
|
11
|
+
} as any as AxiosResponse);
|
|
12
|
+
|
|
13
|
+
expect(extractAxiosErrorData(axiosError)).toStrictEqual({
|
|
14
|
+
axiosResponse: {
|
|
15
|
+
data: 'error data',
|
|
16
|
+
status: 500,
|
|
17
|
+
},
|
|
18
|
+
code: '500',
|
|
19
|
+
message: 'error message',
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe(executeRequest.name, () => {
|
|
25
|
+
it('fails to call invalid URL', async () => {
|
|
26
|
+
const request = {
|
|
27
|
+
method: 'GET',
|
|
28
|
+
url: 'http://localhost:9999',
|
|
29
|
+
} as const;
|
|
30
|
+
|
|
31
|
+
const response = await executeRequest(request);
|
|
32
|
+
|
|
33
|
+
expect(response).toStrictEqual({
|
|
34
|
+
data: undefined,
|
|
35
|
+
errorData: {
|
|
36
|
+
axiosResponse: undefined,
|
|
37
|
+
code: 'ECONNREFUSED',
|
|
38
|
+
message: expect.any(String), // The message is empty in node@20, but "connect ECONNREFUSED ::1:9999" on node@18
|
|
39
|
+
},
|
|
40
|
+
success: false,
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { go } from '@api3/promise-utils';
|
|
2
|
+
import axios, { type Method, type AxiosError, type AxiosResponse } from 'axios';
|
|
3
|
+
import pick from 'lodash/pick';
|
|
4
|
+
|
|
5
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
6
|
+
|
|
7
|
+
export interface Request {
|
|
8
|
+
readonly method: Method;
|
|
9
|
+
readonly url: string;
|
|
10
|
+
readonly headers?: Record<string, string>;
|
|
11
|
+
readonly queryParams?: Record<string, any>;
|
|
12
|
+
readonly timeout?: number;
|
|
13
|
+
readonly body?: unknown;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ErrorResponse {
|
|
17
|
+
readonly axiosResponse: Pick<AxiosResponse, 'data' | 'headers' | 'status'> | undefined;
|
|
18
|
+
readonly message: string;
|
|
19
|
+
readonly code: string | undefined;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const extractAxiosErrorData = (error: AxiosError): ErrorResponse => {
|
|
23
|
+
// Inspired by: https://axios-http.com/docs/handling_errors
|
|
24
|
+
return {
|
|
25
|
+
axiosResponse: error.response ? pick(error.response, ['data', 'status']) : undefined,
|
|
26
|
+
message: error.message,
|
|
27
|
+
code: error.code,
|
|
28
|
+
} as ErrorResponse;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
interface ExecuteRequestSuccess<T> {
|
|
32
|
+
success: true;
|
|
33
|
+
errorData: undefined;
|
|
34
|
+
data: T;
|
|
35
|
+
}
|
|
36
|
+
interface ExecuteRequestError {
|
|
37
|
+
success: false;
|
|
38
|
+
errorData: ErrorResponse;
|
|
39
|
+
data: undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export type ExecuteRequestResult<T> = ExecuteRequestError | ExecuteRequestSuccess<T>;
|
|
43
|
+
|
|
44
|
+
export async function executeRequest<T>(request: Request): Promise<ExecuteRequestResult<T>> {
|
|
45
|
+
const { url, method, body, headers = {}, queryParams = {}, timeout = DEFAULT_TIMEOUT_MS } = request;
|
|
46
|
+
|
|
47
|
+
const goAxios = await go<Promise<AxiosResponse<T>>, AxiosError>(async () =>
|
|
48
|
+
axios({
|
|
49
|
+
url,
|
|
50
|
+
method,
|
|
51
|
+
headers,
|
|
52
|
+
data: body,
|
|
53
|
+
params: queryParams,
|
|
54
|
+
timeout,
|
|
55
|
+
})
|
|
56
|
+
);
|
|
57
|
+
if (!goAxios.success) return { success: false, errorData: extractAxiosErrorData(goAxios.error), data: undefined };
|
|
58
|
+
const response = goAxios.data;
|
|
59
|
+
|
|
60
|
+
return { success: true, errorData: undefined, data: response.data };
|
|
61
|
+
}
|
package/src/logger/index.test.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import noop from 'lodash/noop';
|
|
2
2
|
|
|
3
3
|
import { type LogConfig, createBaseLogger, wrapper } from '.';
|
|
4
4
|
|
|
@@ -29,9 +29,9 @@ describe('log context', () => {
|
|
|
29
29
|
logger.debug('parent end');
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
-
expect(baseLogger.debug).toHaveBeenCalledWith('parent start', { requestId: 'parent' });
|
|
33
|
-
expect(baseLogger.debug).toHaveBeenCalledWith('child', { requestId: 'child' });
|
|
34
|
-
expect(baseLogger.debug).toHaveBeenCalledWith('parent end', { requestId: 'parent' });
|
|
32
|
+
expect(baseLogger.debug).toHaveBeenCalledWith('parent start', { ctx: { requestId: 'parent' } });
|
|
33
|
+
expect(baseLogger.debug).toHaveBeenCalledWith('child', { ctx: { requestId: 'child' } });
|
|
34
|
+
expect(baseLogger.debug).toHaveBeenCalledWith('parent end', { ctx: { requestId: 'parent' } });
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
it('works with async functions', async () => {
|
|
@@ -48,9 +48,9 @@ describe('log context', () => {
|
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
expect(baseLogger.debug).toHaveBeenCalledTimes(3);
|
|
51
|
-
expect(baseLogger.debug).toHaveBeenCalledWith('parent start', { requestId: 'parent' });
|
|
52
|
-
expect(baseLogger.debug).toHaveBeenCalledWith('child', { requestId: 'child' });
|
|
53
|
-
expect(baseLogger.debug).toHaveBeenCalledWith('parent end', { requestId: 'parent' });
|
|
51
|
+
expect(baseLogger.debug).toHaveBeenCalledWith('parent start', { ctx: { requestId: 'parent' } });
|
|
52
|
+
expect(baseLogger.debug).toHaveBeenCalledWith('child', { ctx: { requestId: 'child' } });
|
|
53
|
+
expect(baseLogger.debug).toHaveBeenCalledWith('parent end', { ctx: { requestId: 'parent' } });
|
|
54
54
|
});
|
|
55
55
|
|
|
56
56
|
it('works with deeply nested functions', async () => {
|
|
@@ -78,14 +78,14 @@ describe('log context', () => {
|
|
|
78
78
|
});
|
|
79
79
|
|
|
80
80
|
expect(baseLogger.debug).toHaveBeenCalledTimes(8);
|
|
81
|
-
expect(baseLogger.debug).toHaveBeenCalledWith('parent start', { parent: true });
|
|
82
|
-
expect(baseLogger.debug).toHaveBeenCalledWith('A start', { parent: true, A: true });
|
|
83
|
-
expect(baseLogger.debug).toHaveBeenCalledWith('C', { parent: true, A: true, B: true });
|
|
84
|
-
expect(baseLogger.debug).toHaveBeenCalledWith('D', { parent: true, A: true, B: true });
|
|
85
|
-
expect(baseLogger.debug).toHaveBeenCalledWith('E', { parent: true, A: true, B: true });
|
|
86
|
-
expect(baseLogger.debug).toHaveBeenCalledWith('B end', { parent: true, A: true, B: true });
|
|
87
|
-
expect(baseLogger.debug).toHaveBeenCalledWith('A end', { parent: true, A: true });
|
|
88
|
-
expect(baseLogger.debug).toHaveBeenCalledWith('parent end', { parent: true });
|
|
81
|
+
expect(baseLogger.debug).toHaveBeenCalledWith('parent start', { ctx: { parent: true } });
|
|
82
|
+
expect(baseLogger.debug).toHaveBeenCalledWith('A start', { ctx: { parent: true, A: true } });
|
|
83
|
+
expect(baseLogger.debug).toHaveBeenCalledWith('C', { ctx: { parent: true, A: true, B: true } });
|
|
84
|
+
expect(baseLogger.debug).toHaveBeenCalledWith('D', { ctx: { parent: true, A: true, B: true } });
|
|
85
|
+
expect(baseLogger.debug).toHaveBeenCalledWith('E', { ctx: { parent: true, A: true, B: true } });
|
|
86
|
+
expect(baseLogger.debug).toHaveBeenCalledWith('B end', { ctx: { parent: true, A: true, B: true } });
|
|
87
|
+
expect(baseLogger.debug).toHaveBeenCalledWith('A end', { ctx: { parent: true, A: true } });
|
|
88
|
+
expect(baseLogger.debug).toHaveBeenCalledWith('parent end', { ctx: { parent: true } });
|
|
89
89
|
});
|
|
90
90
|
|
|
91
91
|
it('throws if the sync callback function throws', () => {
|
|
@@ -118,10 +118,12 @@ describe('log context', () => {
|
|
|
118
118
|
logger.error('message, error and context', new Error('some-error'), { requestId: 'parent' });
|
|
119
119
|
|
|
120
120
|
expect(baseLogger.error).toHaveBeenNthCalledWith(1, 'only message', undefined);
|
|
121
|
-
expect(baseLogger.error).toHaveBeenNthCalledWith(2, 'message and context', { requestId: 'parent' });
|
|
121
|
+
expect(baseLogger.error).toHaveBeenNthCalledWith(2, 'message and context', { ctx: { requestId: 'parent' } });
|
|
122
122
|
expect(baseLogger.error).toHaveBeenNthCalledWith(3, 'message and error', new Error('some-error'), undefined);
|
|
123
123
|
expect(baseLogger.error).toHaveBeenNthCalledWith(4, 'message, error and context', new Error('some-error'), {
|
|
124
|
-
|
|
124
|
+
ctx: {
|
|
125
|
+
requestId: 'parent',
|
|
126
|
+
},
|
|
125
127
|
});
|
|
126
128
|
});
|
|
127
129
|
});
|
package/src/logger/index.ts
CHANGED
|
@@ -61,7 +61,6 @@ export const createBaseLogger = (config: LogConfig) => {
|
|
|
61
61
|
// This format is recommended by the "winston-console-format" package.
|
|
62
62
|
format: winston.format.combine(
|
|
63
63
|
winston.format.timestamp(),
|
|
64
|
-
winston.format.ms(),
|
|
65
64
|
winston.format.errors({ stack: true }),
|
|
66
65
|
winston.format.splat(),
|
|
67
66
|
winston.format.json()
|
|
@@ -88,36 +87,36 @@ export interface Logger {
|
|
|
88
87
|
child: (options: { name: string }) => Logger;
|
|
89
88
|
}
|
|
90
89
|
|
|
90
|
+
const createFullContext = (localContext: LogContext | undefined) => {
|
|
91
|
+
const globalContext = getAsyncLocalStorage().getStore();
|
|
92
|
+
if (!globalContext && !localContext) return;
|
|
93
|
+
const fullContext = { ...globalContext, ...localContext };
|
|
94
|
+
|
|
95
|
+
// If the context contains a `name` or `message` field, it will override the `name` and `message` fields of the log
|
|
96
|
+
// entry. To avoid this, we return the context as a separate field.
|
|
97
|
+
return { ctx: fullContext };
|
|
98
|
+
};
|
|
99
|
+
|
|
91
100
|
// Winston by default merges content of `context` among the rest of the fields for the JSON format.
|
|
92
101
|
// That's causing an override of fields `name` and `message` if they are present.
|
|
93
102
|
export const wrapper = (logger: winston.Logger): Logger => {
|
|
94
103
|
return {
|
|
95
104
|
debug: (message, localContext) => {
|
|
96
|
-
|
|
97
|
-
const fullContext = globalContext || localContext ? { ...globalContext, ...localContext } : undefined;
|
|
98
|
-
logger.debug(message, fullContext);
|
|
105
|
+
logger.debug(message, createFullContext(localContext));
|
|
99
106
|
},
|
|
100
107
|
info: (message, localContext) => {
|
|
101
|
-
|
|
102
|
-
const fullContext = globalContext || localContext ? { ...globalContext, ...localContext } : undefined;
|
|
103
|
-
logger.info(message, fullContext);
|
|
108
|
+
logger.info(message, createFullContext(localContext));
|
|
104
109
|
},
|
|
105
110
|
warn: (message, localContext) => {
|
|
106
|
-
|
|
107
|
-
const fullContext = globalContext || localContext ? { ...globalContext, ...localContext } : undefined;
|
|
108
|
-
logger.warn(message, fullContext);
|
|
111
|
+
logger.warn(message, createFullContext(localContext));
|
|
109
112
|
},
|
|
110
113
|
// We need to handle both overloads of the `error` function
|
|
111
114
|
error: (message, errorOrLocalContext: Error | LogContext, localContext?: LogContext) => {
|
|
112
|
-
const globalContext = getAsyncLocalStorage().getStore();
|
|
113
115
|
// eslint-disable-next-line lodash/prefer-lodash-typecheck
|
|
114
116
|
if (errorOrLocalContext instanceof Error) {
|
|
115
|
-
|
|
116
|
-
logger.error(message, errorOrLocalContext, fullContext);
|
|
117
|
+
logger.error(message, errorOrLocalContext, createFullContext(localContext));
|
|
117
118
|
} else {
|
|
118
|
-
|
|
119
|
-
globalContext || errorOrLocalContext ? { ...globalContext, ...errorOrLocalContext } : undefined;
|
|
120
|
-
logger.error(message, fullContext);
|
|
119
|
+
logger.error(message, createFullContext(errorOrLocalContext));
|
|
121
120
|
}
|
|
122
121
|
},
|
|
123
122
|
child: (options) => wrapper(logger.child(options)),
|
package/src/node-index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable jest/prefer-strict-equal */ // Because the errors are thrown from the "vm" module (different context), they are not strictly equal.
|
|
2
2
|
import { ZodError } from 'zod';
|
|
3
3
|
|
|
4
|
-
import { createEndpoint } from '../../test/fixtures';
|
|
4
|
+
import { createEndpoint } from '../../test/fixtures/processing';
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
7
|
addReservedParameters,
|
|
@@ -27,7 +27,6 @@ import stream from 'node:stream';
|
|
|
27
27
|
import string_decoder from 'node:string_decoder';
|
|
28
28
|
import timers from 'node:timers';
|
|
29
29
|
import tls from 'node:tls';
|
|
30
|
-
import trace_events from 'node:trace_events';
|
|
31
30
|
import tty from 'node:tty';
|
|
32
31
|
import url from 'node:url';
|
|
33
32
|
import util from 'node:util';
|
|
@@ -69,7 +68,6 @@ const builtInNodeModules = {
|
|
|
69
68
|
string_decoder,
|
|
70
69
|
timers,
|
|
71
70
|
tls,
|
|
72
|
-
trace_events,
|
|
73
71
|
tty,
|
|
74
72
|
url,
|
|
75
73
|
util,
|