@hahnpro/hpc-api 4.0.1 → 4.0.2
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/asset.service.js +1 -1
- package/dist/content.interface.js +1 -1
- package/dist/content.service.js +1 -1
- package/dist/flow-deployment.service.js +1 -2
- package/dist/flow-module.service.js +1 -1
- package/dist/http.service.js +8 -9
- package/dist/mock/api.mock.js +92 -20
- package/dist/mock/asset.mock.service.js +3 -3
- package/dist/mock/assetTypes.mock.service.js +2 -2
- package/dist/mock/content.mock.service.js +1 -1
- package/dist/mock/data.mock.service.js +1 -1
- package/dist/mock/flow-deployment.mock.service.js +2 -3
- package/dist/mock/flow-functions.mock.service.js +1 -1
- package/dist/mock/flow.mock.service.js +3 -3
- package/dist/mock/secret.mock.service.js +1 -1
- package/dist/mock/task.mock.service.js +1 -1
- package/dist/mock/timeseries.mock.service.js +4 -4
- package/dist/notification.interface.d.ts +3 -1
- package/package.json +2 -2
package/dist/asset.service.js
CHANGED
|
@@ -15,7 +15,7 @@ class AssetService extends BaseService {
|
|
|
15
15
|
constructor(httpClient) {
|
|
16
16
|
super(httpClient, '/assets');
|
|
17
17
|
this.addAttachment = (id, form) => {
|
|
18
|
-
const headers =
|
|
18
|
+
const headers = { ...form.getHeaders() };
|
|
19
19
|
return this.httpClient.post(`${this.basePath}/${id}/attachment`, form, {
|
|
20
20
|
headers,
|
|
21
21
|
maxBodyLength: Infinity,
|
|
@@ -9,4 +9,4 @@ var ReturnType;
|
|
|
9
9
|
ReturnType[ReturnType["BLOB"] = 3] = "BLOB";
|
|
10
10
|
ReturnType[ReturnType["ARRAYBUFFER"] = 4] = "ARRAYBUFFER";
|
|
11
11
|
ReturnType[ReturnType["NODESTREAM"] = 5] = "NODESTREAM";
|
|
12
|
-
})(ReturnType
|
|
12
|
+
})(ReturnType || (exports.ReturnType = ReturnType = {}));
|
package/dist/content.service.js
CHANGED
|
@@ -16,7 +16,7 @@ class ContentService extends BaseService {
|
|
|
16
16
|
constructor(httpClient) {
|
|
17
17
|
super(httpClient, '/contents');
|
|
18
18
|
this.upload = (form) => {
|
|
19
|
-
const headers =
|
|
19
|
+
const headers = { ...form.getHeaders() };
|
|
20
20
|
return this.httpClient.post(`${this.basePath}`, form, { headers, maxBodyLength: Infinity, maxContentLength: Infinity });
|
|
21
21
|
};
|
|
22
22
|
}
|
|
@@ -33,8 +33,7 @@ class FlowDeploymentService extends BaseService {
|
|
|
33
33
|
return this.httpClient.get(`${this.basePath}/${id}/logs`);
|
|
34
34
|
}
|
|
35
35
|
resolveReferences(id, recursive = true, types) {
|
|
36
|
-
|
|
37
|
-
const params = { recursive, types: (_a = types === null || types === void 0 ? void 0 : types.join(',')) !== null && _a !== void 0 ? _a : undefined };
|
|
36
|
+
const params = { recursive, types: types?.join(',') ?? undefined };
|
|
38
37
|
return this.httpClient.get(`${this.basePath}/${id}/references`, { params });
|
|
39
38
|
}
|
|
40
39
|
updateStatus(id, desiredStatus) {
|
package/dist/http.service.js
CHANGED
|
@@ -17,7 +17,7 @@ class HttpClient {
|
|
|
17
17
|
this.clientId = clientId;
|
|
18
18
|
this.clientSecret = clientSecret;
|
|
19
19
|
this.eventSourcesMap = new Map();
|
|
20
|
-
this.getQueueStats = () =>
|
|
20
|
+
this.getQueueStats = () => this.requestQueue?.getStats();
|
|
21
21
|
this.delete = (url, config) => this.request('DELETE', url, config);
|
|
22
22
|
this.get = (url, config) => this.request('GET', url, config);
|
|
23
23
|
this.post = (url, data, config) => this.request('POST', url, config, data);
|
|
@@ -26,8 +26,8 @@ class HttpClient {
|
|
|
26
26
|
return this.requestQueue.add(() => new Promise((resolve, reject) => {
|
|
27
27
|
this.getAccessToken()
|
|
28
28
|
.then((token) => {
|
|
29
|
-
const headers =
|
|
30
|
-
return this.axiosInstance.request(
|
|
29
|
+
const headers = { Authorization: `Bearer ${token}`, ...config.headers };
|
|
30
|
+
return this.axiosInstance.request({ ...config, headers, method, url, data });
|
|
31
31
|
})
|
|
32
32
|
.then((response) => resolve(response.data))
|
|
33
33
|
.catch(reject);
|
|
@@ -74,10 +74,9 @@ class HttpClient {
|
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
validateIssuer(issuer) {
|
|
77
|
-
var _a, _b;
|
|
78
77
|
if (!issuer.issuer ||
|
|
79
|
-
!
|
|
80
|
-
!
|
|
78
|
+
!issuer.grant_types_supported?.includes('client_credentials') ||
|
|
79
|
+
!issuer.token_endpoint_auth_methods_supported?.includes('client_secret_jwt')) {
|
|
81
80
|
throw new Error('Issuer does not support client_secret_jwt');
|
|
82
81
|
}
|
|
83
82
|
return issuer;
|
|
@@ -91,7 +90,6 @@ class HttpClient {
|
|
|
91
90
|
return this.validateIssuer(issuerResponse.data);
|
|
92
91
|
}
|
|
93
92
|
async requestAccessToken() {
|
|
94
|
-
var _a, _b;
|
|
95
93
|
const issuer = await this.discoverIssuer(`${this.authBaseURL}/realms/${this.realm}`);
|
|
96
94
|
const timestamp = Date.now() / 1000;
|
|
97
95
|
const audience = [...new Set([issuer.issuer, issuer.token_endpoint].filter(Boolean))];
|
|
@@ -104,7 +102,8 @@ class HttpClient {
|
|
|
104
102
|
aud: audience,
|
|
105
103
|
};
|
|
106
104
|
const supportedAlgos = issuer.token_endpoint_auth_signing_alg_values_supported;
|
|
107
|
-
const alg =
|
|
105
|
+
const alg = issuer.token_endpoint_auth_signing_alg ??
|
|
106
|
+
(Array.isArray(supportedAlgos) && supportedAlgos.find((signAlg) => /^HS(?:256|384|512)/.test(signAlg)));
|
|
108
107
|
if (!alg) {
|
|
109
108
|
throw new Error('Issuer has to support HS256, HS384 or HS512');
|
|
110
109
|
}
|
|
@@ -120,7 +119,7 @@ class HttpClient {
|
|
|
120
119
|
const authResponse = await this.authAxiosInstance.post(issuer.token_endpoint, (0, querystring_1.stringify)(opts), {
|
|
121
120
|
headers: { Accept: 'application/json', 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
122
121
|
});
|
|
123
|
-
if (
|
|
122
|
+
if (authResponse?.data?.access_token && authResponse.data.expires_in) {
|
|
124
123
|
this.tokenSet = new token_set_1.TokenSet(authResponse.data.access_token, authResponse.data.expires_in);
|
|
125
124
|
return authResponse.data.access_token;
|
|
126
125
|
}
|
package/dist/mock/api.mock.js
CHANGED
|
@@ -38,13 +38,30 @@ class MockAPI {
|
|
|
38
38
|
uiSchema: {},
|
|
39
39
|
};
|
|
40
40
|
});
|
|
41
|
-
const assets1 = assets.map((v, index) => (
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
const assets1 = assets.map((v, index) => ({
|
|
42
|
+
...v,
|
|
43
|
+
readPermissions: [],
|
|
44
|
+
readWritePermissions: [],
|
|
45
|
+
type: assetTypes[index],
|
|
46
|
+
}));
|
|
47
|
+
const assetRevisions1 = assetRevisions.map((v, index) => ({
|
|
48
|
+
...v,
|
|
49
|
+
readPermissions: [],
|
|
50
|
+
readWritePermissions: [],
|
|
51
|
+
type: assetTypes[index],
|
|
52
|
+
}));
|
|
53
|
+
const contents1 = contents.map((v) => ({
|
|
54
|
+
...v,
|
|
55
|
+
readPermissions: [],
|
|
56
|
+
readWritePermissions: [],
|
|
57
|
+
size: 0,
|
|
58
|
+
fileId: '',
|
|
59
|
+
mimetype: v.mimetype || '',
|
|
60
|
+
}));
|
|
44
61
|
const contentData = contents.map((v) => {
|
|
45
62
|
return v.data ? v.data : (0, fs_1.readFileSync)((0, path_1.join)(v.filePath, v.filename));
|
|
46
63
|
});
|
|
47
|
-
const secrets1 = secrets.map((v) => (
|
|
64
|
+
const secrets1 = secrets.map((v) => ({ ...v, readPermissions: [], readWritePermissions: [] }));
|
|
48
65
|
const timeSeries1 = timeSeries.map((value) => ({
|
|
49
66
|
id: value.id,
|
|
50
67
|
name: value.name,
|
|
@@ -99,22 +116,77 @@ class MockAPI {
|
|
|
99
116
|
createdAt: v.createdAt,
|
|
100
117
|
}));
|
|
101
118
|
const timeseriesValues = timeSeries.map((v) => v.values);
|
|
102
|
-
const diagrams1 = diagrams.map((v) => (
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
});
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
119
|
+
const diagrams1 = diagrams.map((v) => ({
|
|
120
|
+
...v,
|
|
121
|
+
json: '',
|
|
122
|
+
author: 'nobody',
|
|
123
|
+
}));
|
|
124
|
+
const flows1 = flows.map((v) => ({
|
|
125
|
+
...v,
|
|
126
|
+
readPermissions: [],
|
|
127
|
+
readWritePermissions: [],
|
|
128
|
+
diagram: diagrams.find((v1) => v1.flow === v.id).id,
|
|
129
|
+
name: `flow-${v.id}`,
|
|
130
|
+
deployments: [],
|
|
131
|
+
}));
|
|
132
|
+
const flowRevisions1 = flowRevisions.map((v) => ({
|
|
133
|
+
...v,
|
|
134
|
+
readPermissions: [],
|
|
135
|
+
readWritePermissions: [],
|
|
136
|
+
diagram: diagrams.find((v1) => v1.flow === v.originalId).id,
|
|
137
|
+
name: `flow-${v.id}`,
|
|
138
|
+
deployments: [],
|
|
139
|
+
}));
|
|
140
|
+
const deployments1 = deployments.map((v) => ({
|
|
141
|
+
...v,
|
|
142
|
+
readPermissions: [],
|
|
143
|
+
readWritePermissions: [],
|
|
144
|
+
diagram: v.diagram ?? '',
|
|
145
|
+
artifact: null,
|
|
146
|
+
flowModel: { connections: [], elements: [] },
|
|
147
|
+
desiredStatus: 'running',
|
|
148
|
+
actualStatus: 'generating queued',
|
|
149
|
+
target: 'executor',
|
|
150
|
+
name: `deployment-${v.id}`,
|
|
151
|
+
}));
|
|
152
|
+
const functions1 = functions.map((v) => ({
|
|
153
|
+
...v,
|
|
154
|
+
category: 'task',
|
|
155
|
+
readPermissions: [],
|
|
156
|
+
readWritePermissions: [],
|
|
157
|
+
author: 'nobody',
|
|
158
|
+
}));
|
|
159
|
+
const functionRevisions1 = functionRevisions.map((v) => ({
|
|
160
|
+
...v,
|
|
161
|
+
category: 'task',
|
|
162
|
+
readPermissions: [],
|
|
163
|
+
readWritePermissions: [],
|
|
164
|
+
author: 'nobody',
|
|
165
|
+
}));
|
|
166
|
+
const modules1 = modules.map((v, index) => ({
|
|
167
|
+
...v,
|
|
168
|
+
artifacts: modules[index].artifacts.map((art) => ({
|
|
169
|
+
...art,
|
|
170
|
+
version: '0.0.0',
|
|
171
|
+
id: (0, crypto_1.randomUUID)(),
|
|
172
|
+
mimetype: '',
|
|
173
|
+
size: 0,
|
|
174
|
+
createdAt: '' + Date.now(),
|
|
175
|
+
})) ?? [],
|
|
176
|
+
author: 'nobody',
|
|
177
|
+
functions: [],
|
|
178
|
+
readPermissions: [],
|
|
179
|
+
readWritePermissions: [],
|
|
180
|
+
}));
|
|
181
|
+
const labels1 = labels.map((label) => ({
|
|
182
|
+
...label,
|
|
183
|
+
color: '',
|
|
184
|
+
description: '',
|
|
185
|
+
readPermissions: [],
|
|
186
|
+
readWritePermissions: [],
|
|
187
|
+
}));
|
|
188
|
+
const vaultSecrets1 = vault.map((v) => ({ ...v, readPermissions: [], readWritePermissions: [] }));
|
|
189
|
+
const notifications1 = notifications.map((n) => ({ ...n, link: '', description: '', read: false }));
|
|
118
190
|
this.assets = new asset_mock_service_1.AssetMockService(this, assets1, assetRevisions1);
|
|
119
191
|
this.contents = new content_mock_service_1.ContentMockService(contents1, contentData);
|
|
120
192
|
this.endpoints = new endpoint_mock_service_1.EndpointMockService(endpoint1);
|
|
@@ -27,12 +27,12 @@ class AssetMockService extends BaseService {
|
|
|
27
27
|
return page;
|
|
28
28
|
}
|
|
29
29
|
addOne(dto) {
|
|
30
|
-
this.revisions.push(
|
|
30
|
+
this.revisions.push({ ...dto, originalId: dto.id });
|
|
31
31
|
return super.addOne(dto);
|
|
32
32
|
}
|
|
33
33
|
deleteOne(assetId, force = false) {
|
|
34
34
|
const asset = this.data.find((v) => v.id === assetId);
|
|
35
|
-
if (!
|
|
35
|
+
if (!asset?.deletedAt && !force) {
|
|
36
36
|
asset.deletedAt = new Date().toISOString();
|
|
37
37
|
return Promise.resolve(asset);
|
|
38
38
|
}
|
|
@@ -61,7 +61,7 @@ class AssetMockService extends BaseService {
|
|
|
61
61
|
}
|
|
62
62
|
async getAttachments(assetId) {
|
|
63
63
|
const contents = await this.api.contents.getMany();
|
|
64
|
-
const docs = contents.docs.filter((c) =>
|
|
64
|
+
const docs = contents.docs.filter((c) => c.assets?.includes?.(assetId));
|
|
65
65
|
return { docs, total: docs.length, limit: 0 };
|
|
66
66
|
}
|
|
67
67
|
getChildren(assetId, params = {}) {
|
|
@@ -23,12 +23,12 @@ class AssetTypesMockService extends BaseService {
|
|
|
23
23
|
}
|
|
24
24
|
addOne(dto) {
|
|
25
25
|
const id = (0, crypto_1.randomUUID)();
|
|
26
|
-
this.revisions.push(
|
|
26
|
+
this.revisions.push({ ...dto, id, originalId: dto.id });
|
|
27
27
|
return super.addOne(dto);
|
|
28
28
|
}
|
|
29
29
|
deleteOne(id, force = false) {
|
|
30
30
|
const assettype = this.data.find((v) => v.id === id);
|
|
31
|
-
if (!
|
|
31
|
+
if (!assettype?.deletedAt && !force) {
|
|
32
32
|
assettype.deletedAt = new Date().toISOString();
|
|
33
33
|
return Promise.resolve(assettype);
|
|
34
34
|
}
|
|
@@ -67,7 +67,7 @@ class ContentMockService extends BaseService {
|
|
|
67
67
|
}
|
|
68
68
|
deleteOne(contentId, force = false) {
|
|
69
69
|
const content = this.data.find((v) => v.id === contentId);
|
|
70
|
-
if (!
|
|
70
|
+
if (!content?.deletedAt && !force) {
|
|
71
71
|
content.deletedAt = new Date().toISOString();
|
|
72
72
|
return Promise.resolve(content);
|
|
73
73
|
}
|
|
@@ -32,7 +32,7 @@ class DataMockService extends data_service_1.DataService {
|
|
|
32
32
|
}
|
|
33
33
|
async getManyFiltered(filter, params = {}) {
|
|
34
34
|
const paginated = await this.getMany(params);
|
|
35
|
-
const newData = paginated.docs.filter((v) =>
|
|
35
|
+
const newData = paginated.docs.filter((v) => filter.parent === v.parent || filter.tags?.some((tag) => v.tags?.contains(tag)) || filter.type === v.tag);
|
|
36
36
|
const page = {
|
|
37
37
|
docs: newData,
|
|
38
38
|
limit: paginated.limit || Number.MAX_SAFE_INTEGER,
|
|
@@ -36,9 +36,8 @@ class FlowDeploymentMockService extends BaseService {
|
|
|
36
36
|
return Promise.resolve(deployment);
|
|
37
37
|
}
|
|
38
38
|
async resolveReferences(id, recursive, types) {
|
|
39
|
-
var _a;
|
|
40
39
|
const depl = await this.getOne(id);
|
|
41
|
-
return
|
|
40
|
+
return depl.refs ?? [];
|
|
42
41
|
}
|
|
43
42
|
getDeploymentStatistics(id) {
|
|
44
43
|
return Promise.resolve({
|
|
@@ -100,7 +99,7 @@ class FlowDeploymentMockService extends BaseService {
|
|
|
100
99
|
target: 'executor',
|
|
101
100
|
refs,
|
|
102
101
|
};
|
|
103
|
-
await this.api.flows.updateOne(flow.id,
|
|
102
|
+
await this.api.flows.updateOne(flow.id, { ...flow, deployments: [...flow.deployments, id] });
|
|
104
103
|
return super.addOne(newDepl);
|
|
105
104
|
}
|
|
106
105
|
async deleteOne(id) {
|
|
@@ -11,7 +11,7 @@ class FlowFunctionsMockService extends data_mock_service_1.DataMockService {
|
|
|
11
11
|
}
|
|
12
12
|
addOne(dto) {
|
|
13
13
|
const id = (0, crypto_1.randomUUID)();
|
|
14
|
-
this.revisions.push(
|
|
14
|
+
this.revisions.push({ ...dto, id, originalId: dto.fqn });
|
|
15
15
|
return super.addOne(dto);
|
|
16
16
|
}
|
|
17
17
|
deleteOne(fqn) {
|
|
@@ -20,7 +20,7 @@ class FlowMockService extends BaseService {
|
|
|
20
20
|
}
|
|
21
21
|
deleteOne(id, force = false) {
|
|
22
22
|
const flow = this.data.find((v) => v.id === id);
|
|
23
|
-
if (!
|
|
23
|
+
if (!flow?.deletedAt && !force) {
|
|
24
24
|
flow.deletedAt = new Date().toISOString();
|
|
25
25
|
return Promise.resolve(flow);
|
|
26
26
|
}
|
|
@@ -34,7 +34,7 @@ class FlowMockService extends BaseService {
|
|
|
34
34
|
}
|
|
35
35
|
addOne(dto) {
|
|
36
36
|
const id = (0, crypto_1.randomUUID)();
|
|
37
|
-
this.revisions.push(
|
|
37
|
+
this.revisions.push({ ...dto, id, originalId: dto.id });
|
|
38
38
|
return super.addOne(dto);
|
|
39
39
|
}
|
|
40
40
|
async updateOne(id, dto) {
|
|
@@ -57,7 +57,7 @@ class FlowMockService extends BaseService {
|
|
|
57
57
|
async getMany(params) {
|
|
58
58
|
const flows = this.getItems(params, false);
|
|
59
59
|
return {
|
|
60
|
-
docs: flows.docs.map((v) => (
|
|
60
|
+
docs: flows.docs.map((v) => ({ ...v, diagram: this.diagrams.find((v1) => v1.id === v.diagram) })),
|
|
61
61
|
total: 0,
|
|
62
62
|
limit: 0,
|
|
63
63
|
};
|
|
@@ -17,7 +17,7 @@ class SecretMockService extends BaseService {
|
|
|
17
17
|
}
|
|
18
18
|
deleteOne(contentId, force = false) {
|
|
19
19
|
const content = this.data.find((v) => v.id === contentId);
|
|
20
|
-
if (!
|
|
20
|
+
if (!content?.deletedAt && !force) {
|
|
21
21
|
content.deletedAt = new Date().toISOString();
|
|
22
22
|
return Promise.resolve(content);
|
|
23
23
|
}
|
|
@@ -21,7 +21,7 @@ class TaskMockService extends BaseService {
|
|
|
21
21
|
}
|
|
22
22
|
deleteOne(taskId, force = false) {
|
|
23
23
|
const task = this.data.find((v) => v.id === taskId);
|
|
24
|
-
if (!
|
|
24
|
+
if (!task?.deletedAt && !force) {
|
|
25
25
|
task.deletedAt = new Date().toISOString();
|
|
26
26
|
return Promise.resolve(task);
|
|
27
27
|
}
|
|
@@ -13,12 +13,12 @@ BaseService = tslib_1.__decorate([
|
|
|
13
13
|
], BaseService);
|
|
14
14
|
class TimeseriesMockService extends BaseService {
|
|
15
15
|
constructor(timeseries, timeseriesValues) {
|
|
16
|
-
const data = timeseries.map((value, index) => (
|
|
16
|
+
const data = timeseries.map((value, index) => ({ ...value, data: timeseriesValues[index] }));
|
|
17
17
|
super(data);
|
|
18
18
|
}
|
|
19
19
|
deleteOne(tsmId, force = false) {
|
|
20
20
|
const tsm = this.data.find((v) => v.id === tsmId);
|
|
21
|
-
if (!
|
|
21
|
+
if (!tsm?.deletedAt && !force) {
|
|
22
22
|
tsm.deletedAt = new Date().toISOString();
|
|
23
23
|
return Promise.resolve(tsm);
|
|
24
24
|
}
|
|
@@ -32,7 +32,7 @@ class TimeseriesMockService extends BaseService {
|
|
|
32
32
|
const ts = this.data.find((v) => v.assetRef === assetId);
|
|
33
33
|
const data = Object.entries(values).map(([timestamp, value]) => {
|
|
34
34
|
if (value !== null && typeof value === 'object') {
|
|
35
|
-
return
|
|
35
|
+
return { timestamp, ...value };
|
|
36
36
|
}
|
|
37
37
|
else {
|
|
38
38
|
return { timestamp, value };
|
|
@@ -60,7 +60,7 @@ class TimeseriesMockService extends BaseService {
|
|
|
60
60
|
const ts = await this.getOne(id, {});
|
|
61
61
|
for (const [timestamp, v] of Object.entries(value)) {
|
|
62
62
|
if (v !== null && typeof v === 'object') {
|
|
63
|
-
ts.data.push(
|
|
63
|
+
ts.data.push({ timestamp, ...v });
|
|
64
64
|
}
|
|
65
65
|
else {
|
|
66
66
|
ts.data.push({ timestamp: parseInt(timestamp, 10), value: v });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hahnpro/hpc-api",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.2",
|
|
4
4
|
"description": "Module for easy access to the HahnPRO API",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/eventsource": "1.1.11",
|
|
38
|
-
"axios-mock-adapter": "^1.21.
|
|
38
|
+
"axios-mock-adapter": "^1.21.5"
|
|
39
39
|
},
|
|
40
40
|
"engines": {
|
|
41
41
|
"node": ">=v14.13"
|