@kapeta/local-cluster-service 0.0.0-96f91ef
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/.eslintrc.cjs +25 -0
- package/.github/workflows/check-license.yml +17 -0
- package/.github/workflows/main.yml +26 -0
- package/.prettierignore +4 -0
- package/.vscode/launch.json +19 -0
- package/CHANGELOG.md +920 -0
- package/LICENSE +38 -0
- package/README.md +36 -0
- package/definitions.d.ts +35 -0
- package/dist/cjs/index.d.ts +34 -0
- package/dist/cjs/index.js +263 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/src/RepositoryWatcher.d.ts +30 -0
- package/dist/cjs/src/RepositoryWatcher.js +332 -0
- package/dist/cjs/src/ai/aiClient.d.ts +20 -0
- package/dist/cjs/src/ai/aiClient.js +74 -0
- package/dist/cjs/src/ai/routes.d.ts +7 -0
- package/dist/cjs/src/ai/routes.js +37 -0
- package/dist/cjs/src/ai/transform.d.ts +11 -0
- package/dist/cjs/src/ai/transform.js +239 -0
- package/dist/cjs/src/ai/types.d.ts +40 -0
- package/dist/cjs/src/ai/types.js +2 -0
- package/dist/cjs/src/api.d.ts +7 -0
- package/dist/cjs/src/api.js +29 -0
- package/dist/cjs/src/assetManager.d.ts +41 -0
- package/dist/cjs/src/assetManager.js +274 -0
- package/dist/cjs/src/assets/routes.d.ts +7 -0
- package/dist/cjs/src/assets/routes.js +165 -0
- package/dist/cjs/src/attachments/routes.d.ts +7 -0
- package/dist/cjs/src/attachments/routes.js +72 -0
- package/dist/cjs/src/authManager.d.ts +16 -0
- package/dist/cjs/src/authManager.js +64 -0
- package/dist/cjs/src/cacheManager.d.ts +20 -0
- package/dist/cjs/src/cacheManager.js +51 -0
- package/dist/cjs/src/clusterService.d.ts +44 -0
- package/dist/cjs/src/clusterService.js +120 -0
- package/dist/cjs/src/codeGeneratorManager.d.ts +14 -0
- package/dist/cjs/src/codeGeneratorManager.js +93 -0
- package/dist/cjs/src/config/routes.d.ts +7 -0
- package/dist/cjs/src/config/routes.js +160 -0
- package/dist/cjs/src/configManager.d.ts +42 -0
- package/dist/cjs/src/configManager.js +136 -0
- package/dist/cjs/src/containerManager.d.ts +148 -0
- package/dist/cjs/src/containerManager.js +958 -0
- package/dist/cjs/src/definitionsManager.d.ts +20 -0
- package/dist/cjs/src/definitionsManager.js +171 -0
- package/dist/cjs/src/filesystem/routes.d.ts +7 -0
- package/dist/cjs/src/filesystem/routes.js +105 -0
- package/dist/cjs/src/filesystemManager.d.ts +27 -0
- package/dist/cjs/src/filesystemManager.js +118 -0
- package/dist/cjs/src/identities/routes.d.ts +7 -0
- package/dist/cjs/src/identities/routes.js +37 -0
- package/dist/cjs/src/instanceManager.d.ts +69 -0
- package/dist/cjs/src/instanceManager.js +910 -0
- package/dist/cjs/src/instances/routes.d.ts +7 -0
- package/dist/cjs/src/instances/routes.js +179 -0
- package/dist/cjs/src/middleware/cors.d.ts +6 -0
- package/dist/cjs/src/middleware/cors.js +14 -0
- package/dist/cjs/src/middleware/kapeta.d.ts +15 -0
- package/dist/cjs/src/middleware/kapeta.js +28 -0
- package/dist/cjs/src/middleware/stringBody.d.ts +9 -0
- package/dist/cjs/src/middleware/stringBody.js +18 -0
- package/dist/cjs/src/networkManager.d.ts +37 -0
- package/dist/cjs/src/networkManager.js +119 -0
- package/dist/cjs/src/operatorManager.d.ts +41 -0
- package/dist/cjs/src/operatorManager.js +211 -0
- package/dist/cjs/src/progressListener.d.ts +31 -0
- package/dist/cjs/src/progressListener.js +133 -0
- package/dist/cjs/src/providerManager.d.ts +11 -0
- package/dist/cjs/src/providerManager.js +84 -0
- package/dist/cjs/src/providers/routes.d.ts +7 -0
- package/dist/cjs/src/providers/routes.js +46 -0
- package/dist/cjs/src/proxy/routes.d.ts +7 -0
- package/dist/cjs/src/proxy/routes.js +115 -0
- package/dist/cjs/src/proxy/types/rest.d.ts +10 -0
- package/dist/cjs/src/proxy/types/rest.js +123 -0
- package/dist/cjs/src/proxy/types/web.d.ts +8 -0
- package/dist/cjs/src/proxy/types/web.js +61 -0
- package/dist/cjs/src/repositoryManager.d.ts +35 -0
- package/dist/cjs/src/repositoryManager.js +247 -0
- package/dist/cjs/src/serviceManager.d.ts +36 -0
- package/dist/cjs/src/serviceManager.js +106 -0
- package/dist/cjs/src/socketManager.d.ts +32 -0
- package/dist/cjs/src/socketManager.js +125 -0
- package/dist/cjs/src/storageService.d.ts +21 -0
- package/dist/cjs/src/storageService.js +81 -0
- package/dist/cjs/src/taskManager.d.ts +70 -0
- package/dist/cjs/src/taskManager.js +181 -0
- package/dist/cjs/src/tasks/routes.d.ts +7 -0
- package/dist/cjs/src/tasks/routes.js +39 -0
- package/dist/cjs/src/traffic/routes.d.ts +7 -0
- package/dist/cjs/src/traffic/routes.js +22 -0
- package/dist/cjs/src/types.d.ts +99 -0
- package/dist/cjs/src/types.js +39 -0
- package/dist/cjs/src/utils/BlockInstanceRunner.d.ts +28 -0
- package/dist/cjs/src/utils/BlockInstanceRunner.js +432 -0
- package/dist/cjs/src/utils/DefaultProviderInstaller.d.ts +15 -0
- package/dist/cjs/src/utils/DefaultProviderInstaller.js +136 -0
- package/dist/cjs/src/utils/InternalConfigProvider.d.ts +38 -0
- package/dist/cjs/src/utils/InternalConfigProvider.js +146 -0
- package/dist/cjs/src/utils/LogData.d.ts +23 -0
- package/dist/cjs/src/utils/LogData.js +46 -0
- package/dist/cjs/src/utils/commandLineUtils.d.ts +8 -0
- package/dist/cjs/src/utils/commandLineUtils.js +39 -0
- package/dist/cjs/src/utils/pathTemplateParser.d.ts +30 -0
- package/dist/cjs/src/utils/pathTemplateParser.js +135 -0
- package/dist/cjs/src/utils/utils.d.ts +40 -0
- package/dist/cjs/src/utils/utils.js +148 -0
- package/dist/cjs/start.d.ts +5 -0
- package/dist/cjs/start.js +17 -0
- package/dist/cjs/test/proxy/types/rest.test.d.ts +5 -0
- package/dist/cjs/test/proxy/types/rest.test.js +48 -0
- package/dist/cjs/test/utils/pathTemplateParser.test.d.ts +5 -0
- package/dist/cjs/test/utils/pathTemplateParser.test.js +27 -0
- package/dist/esm/index.d.ts +34 -0
- package/dist/esm/index.js +263 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/src/RepositoryWatcher.d.ts +30 -0
- package/dist/esm/src/RepositoryWatcher.js +332 -0
- package/dist/esm/src/ai/aiClient.d.ts +20 -0
- package/dist/esm/src/ai/aiClient.js +74 -0
- package/dist/esm/src/ai/routes.d.ts +7 -0
- package/dist/esm/src/ai/routes.js +37 -0
- package/dist/esm/src/ai/transform.d.ts +11 -0
- package/dist/esm/src/ai/transform.js +239 -0
- package/dist/esm/src/ai/types.d.ts +40 -0
- package/dist/esm/src/ai/types.js +2 -0
- package/dist/esm/src/api.d.ts +7 -0
- package/dist/esm/src/api.js +29 -0
- package/dist/esm/src/assetManager.d.ts +41 -0
- package/dist/esm/src/assetManager.js +274 -0
- package/dist/esm/src/assets/routes.d.ts +7 -0
- package/dist/esm/src/assets/routes.js +165 -0
- package/dist/esm/src/attachments/routes.d.ts +7 -0
- package/dist/esm/src/attachments/routes.js +72 -0
- package/dist/esm/src/authManager.d.ts +16 -0
- package/dist/esm/src/authManager.js +64 -0
- package/dist/esm/src/cacheManager.d.ts +20 -0
- package/dist/esm/src/cacheManager.js +51 -0
- package/dist/esm/src/clusterService.d.ts +44 -0
- package/dist/esm/src/clusterService.js +120 -0
- package/dist/esm/src/codeGeneratorManager.d.ts +14 -0
- package/dist/esm/src/codeGeneratorManager.js +93 -0
- package/dist/esm/src/config/routes.d.ts +7 -0
- package/dist/esm/src/config/routes.js +160 -0
- package/dist/esm/src/configManager.d.ts +42 -0
- package/dist/esm/src/configManager.js +136 -0
- package/dist/esm/src/containerManager.d.ts +148 -0
- package/dist/esm/src/containerManager.js +958 -0
- package/dist/esm/src/definitionsManager.d.ts +20 -0
- package/dist/esm/src/definitionsManager.js +171 -0
- package/dist/esm/src/filesystem/routes.d.ts +7 -0
- package/dist/esm/src/filesystem/routes.js +105 -0
- package/dist/esm/src/filesystemManager.d.ts +27 -0
- package/dist/esm/src/filesystemManager.js +118 -0
- package/dist/esm/src/identities/routes.d.ts +7 -0
- package/dist/esm/src/identities/routes.js +37 -0
- package/dist/esm/src/instanceManager.d.ts +69 -0
- package/dist/esm/src/instanceManager.js +910 -0
- package/dist/esm/src/instances/routes.d.ts +7 -0
- package/dist/esm/src/instances/routes.js +179 -0
- package/dist/esm/src/middleware/cors.d.ts +6 -0
- package/dist/esm/src/middleware/cors.js +14 -0
- package/dist/esm/src/middleware/kapeta.d.ts +15 -0
- package/dist/esm/src/middleware/kapeta.js +28 -0
- package/dist/esm/src/middleware/stringBody.d.ts +9 -0
- package/dist/esm/src/middleware/stringBody.js +18 -0
- package/dist/esm/src/networkManager.d.ts +37 -0
- package/dist/esm/src/networkManager.js +119 -0
- package/dist/esm/src/operatorManager.d.ts +41 -0
- package/dist/esm/src/operatorManager.js +211 -0
- package/dist/esm/src/progressListener.d.ts +31 -0
- package/dist/esm/src/progressListener.js +133 -0
- package/dist/esm/src/providerManager.d.ts +11 -0
- package/dist/esm/src/providerManager.js +84 -0
- package/dist/esm/src/providers/routes.d.ts +7 -0
- package/dist/esm/src/providers/routes.js +46 -0
- package/dist/esm/src/proxy/routes.d.ts +7 -0
- package/dist/esm/src/proxy/routes.js +115 -0
- package/dist/esm/src/proxy/types/rest.d.ts +10 -0
- package/dist/esm/src/proxy/types/rest.js +123 -0
- package/dist/esm/src/proxy/types/web.d.ts +8 -0
- package/dist/esm/src/proxy/types/web.js +61 -0
- package/dist/esm/src/repositoryManager.d.ts +35 -0
- package/dist/esm/src/repositoryManager.js +247 -0
- package/dist/esm/src/serviceManager.d.ts +36 -0
- package/dist/esm/src/serviceManager.js +106 -0
- package/dist/esm/src/socketManager.d.ts +32 -0
- package/dist/esm/src/socketManager.js +125 -0
- package/dist/esm/src/storageService.d.ts +21 -0
- package/dist/esm/src/storageService.js +81 -0
- package/dist/esm/src/taskManager.d.ts +70 -0
- package/dist/esm/src/taskManager.js +181 -0
- package/dist/esm/src/tasks/routes.d.ts +7 -0
- package/dist/esm/src/tasks/routes.js +39 -0
- package/dist/esm/src/traffic/routes.d.ts +7 -0
- package/dist/esm/src/traffic/routes.js +22 -0
- package/dist/esm/src/types.d.ts +99 -0
- package/dist/esm/src/types.js +39 -0
- package/dist/esm/src/utils/BlockInstanceRunner.d.ts +28 -0
- package/dist/esm/src/utils/BlockInstanceRunner.js +432 -0
- package/dist/esm/src/utils/DefaultProviderInstaller.d.ts +15 -0
- package/dist/esm/src/utils/DefaultProviderInstaller.js +136 -0
- package/dist/esm/src/utils/InternalConfigProvider.d.ts +38 -0
- package/dist/esm/src/utils/InternalConfigProvider.js +146 -0
- package/dist/esm/src/utils/LogData.d.ts +23 -0
- package/dist/esm/src/utils/LogData.js +46 -0
- package/dist/esm/src/utils/commandLineUtils.d.ts +8 -0
- package/dist/esm/src/utils/commandLineUtils.js +39 -0
- package/dist/esm/src/utils/pathTemplateParser.d.ts +30 -0
- package/dist/esm/src/utils/pathTemplateParser.js +135 -0
- package/dist/esm/src/utils/utils.d.ts +40 -0
- package/dist/esm/src/utils/utils.js +148 -0
- package/dist/esm/start.d.ts +5 -0
- package/dist/esm/start.js +17 -0
- package/dist/esm/test/proxy/types/rest.test.d.ts +5 -0
- package/dist/esm/test/proxy/types/rest.test.js +48 -0
- package/dist/esm/test/utils/pathTemplateParser.test.d.ts +5 -0
- package/dist/esm/test/utils/pathTemplateParser.test.js +27 -0
- package/index.ts +280 -0
- package/jest.config.js +8 -0
- package/package.json +134 -0
- package/src/RepositoryWatcher.ts +363 -0
- package/src/ai/aiClient.ts +93 -0
- package/src/ai/routes.ts +39 -0
- package/src/ai/transform.ts +275 -0
- package/src/ai/types.ts +45 -0
- package/src/api.ts +32 -0
- package/src/assetManager.ts +355 -0
- package/src/assets/routes.ts +183 -0
- package/src/attachments/routes.ts +79 -0
- package/src/authManager.ts +67 -0
- package/src/cacheManager.ts +59 -0
- package/src/clusterService.ts +142 -0
- package/src/codeGeneratorManager.ts +109 -0
- package/src/config/routes.ts +201 -0
- package/src/configManager.ts +180 -0
- package/src/containerManager.ts +1178 -0
- package/src/definitionsManager.ts +212 -0
- package/src/filesystem/routes.ts +123 -0
- package/src/filesystemManager.ts +133 -0
- package/src/identities/routes.ts +38 -0
- package/src/instanceManager.ts +1160 -0
- package/src/instances/routes.ts +203 -0
- package/src/middleware/cors.ts +14 -0
- package/src/middleware/kapeta.ts +41 -0
- package/src/middleware/stringBody.ts +21 -0
- package/src/networkManager.ts +148 -0
- package/src/operatorManager.ts +294 -0
- package/src/progressListener.ts +151 -0
- package/src/providerManager.ts +97 -0
- package/src/providers/routes.ts +51 -0
- package/src/proxy/routes.ts +153 -0
- package/src/proxy/types/rest.ts +172 -0
- package/src/proxy/types/web.ts +70 -0
- package/src/repositoryManager.ts +291 -0
- package/src/serviceManager.ts +133 -0
- package/src/socketManager.ts +138 -0
- package/src/storageService.ts +97 -0
- package/src/taskManager.ts +247 -0
- package/src/tasks/routes.ts +43 -0
- package/src/traffic/routes.ts +23 -0
- package/src/types.ts +112 -0
- package/src/utils/BlockInstanceRunner.ts +577 -0
- package/src/utils/DefaultProviderInstaller.ts +150 -0
- package/src/utils/InternalConfigProvider.ts +214 -0
- package/src/utils/LogData.ts +50 -0
- package/src/utils/commandLineUtils.ts +45 -0
- package/src/utils/pathTemplateParser.ts +157 -0
- package/src/utils/utils.ts +155 -0
- package/start.ts +14 -0
- package/test/proxy/types/rest.test.ts +54 -0
- package/test/utils/pathTemplateParser.test.ts +29 -0
- package/tsconfig.json +15 -0
@@ -0,0 +1,153 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright 2023 Kapeta Inc.
|
3
|
+
* SPDX-License-Identifier: BUSL-1.1
|
4
|
+
*/
|
5
|
+
|
6
|
+
import Router from 'express-promise-router';
|
7
|
+
import { Request, Response } from 'express';
|
8
|
+
import { Resource } from '@kapeta/schemas';
|
9
|
+
import { proxyRestRequest } from './types/rest';
|
10
|
+
import { proxyHttpRequest } from './types/web';
|
11
|
+
import { ProxyRequestHandler } from '../types';
|
12
|
+
import { stringBody, StringBodyRequest } from '../middleware/stringBody';
|
13
|
+
import { serviceManager } from '../serviceManager';
|
14
|
+
import { clusterService } from '../clusterService';
|
15
|
+
import { assetManager } from '../assetManager';
|
16
|
+
|
17
|
+
import _ from 'lodash';
|
18
|
+
|
19
|
+
const router = Router();
|
20
|
+
/**
|
21
|
+
* @var {{[key:string]:ProxyRequestHandler}}
|
22
|
+
*/
|
23
|
+
const TYPE_HANDLERS: { [p: string]: ProxyRequestHandler } = {
|
24
|
+
rest: proxyRestRequest,
|
25
|
+
http: proxyHttpRequest,
|
26
|
+
};
|
27
|
+
|
28
|
+
function getResource(resources: Resource[], resourceName: string) {
|
29
|
+
return resources.find((resource) => {
|
30
|
+
return resource.metadata.name.toLowerCase() === resourceName.toLowerCase();
|
31
|
+
});
|
32
|
+
}
|
33
|
+
|
34
|
+
router.use('/:systemId/:consumerInstanceId/:consumerResourceName', stringBody);
|
35
|
+
|
36
|
+
router.all(
|
37
|
+
'/:systemId/:consumerInstanceId/:consumerResourceName/:type/*',
|
38
|
+
async (req: StringBodyRequest, res: Response) => {
|
39
|
+
try {
|
40
|
+
let typeHandler = TYPE_HANDLERS[req.params.type.toLowerCase()];
|
41
|
+
if (!typeHandler) {
|
42
|
+
// Default to http
|
43
|
+
typeHandler = TYPE_HANDLERS['http'];
|
44
|
+
}
|
45
|
+
|
46
|
+
const plan = await assetManager.getPlan(req.params.systemId);
|
47
|
+
|
48
|
+
// We can find the connection by the consumer information alone since
|
49
|
+
// only 1 provider can be connected to a consumer resource at a time
|
50
|
+
const connection = plan.spec.connections.find((connection) => {
|
51
|
+
return (
|
52
|
+
connection.consumer.blockId.toLowerCase() === req.params.consumerInstanceId.toLowerCase() &&
|
53
|
+
connection.consumer.resourceName.toLowerCase() === req.params.consumerResourceName.toLowerCase()
|
54
|
+
);
|
55
|
+
});
|
56
|
+
|
57
|
+
if (!connection) {
|
58
|
+
res.status(401).send({
|
59
|
+
error: `No connection found for consumer "${req.params.consumerInstanceId}::${req.params.consumerResourceName}"`,
|
60
|
+
});
|
61
|
+
return;
|
62
|
+
}
|
63
|
+
|
64
|
+
const toBlockInstance = _.find(plan.spec.blocks, (blockInstance) => {
|
65
|
+
return blockInstance.id.toLowerCase() === connection.consumer.blockId.toLowerCase();
|
66
|
+
});
|
67
|
+
|
68
|
+
if (!toBlockInstance) {
|
69
|
+
res.status(401).send({ error: `Block instance not found "${req.params.consumerInstanceId}` });
|
70
|
+
return;
|
71
|
+
}
|
72
|
+
|
73
|
+
const toBlockAsset = await assetManager.getAsset(toBlockInstance.block.ref);
|
74
|
+
|
75
|
+
if (!toBlockAsset) {
|
76
|
+
res.status(401).send({ error: `Block asset not found "${toBlockInstance.block.ref}` });
|
77
|
+
return;
|
78
|
+
}
|
79
|
+
|
80
|
+
const consumerResource = getResource(toBlockAsset.data.spec.consumers, req.params.consumerResourceName);
|
81
|
+
|
82
|
+
if (!consumerResource) {
|
83
|
+
res.status(401).send({
|
84
|
+
error: `Block resource not found "${req.params.consumerInstanceId}::${req.params.consumerResourceName}`,
|
85
|
+
});
|
86
|
+
return;
|
87
|
+
}
|
88
|
+
|
89
|
+
const basePath = clusterService.getProxyPath(
|
90
|
+
req.params.systemId,
|
91
|
+
req.params.consumerInstanceId,
|
92
|
+
req.params.consumerResourceName,
|
93
|
+
req.params.type
|
94
|
+
);
|
95
|
+
|
96
|
+
const fromBlockInstance = _.find(plan.spec.blocks, (blockInstance) => {
|
97
|
+
return blockInstance.id.toLowerCase() === connection.provider.blockId.toLowerCase();
|
98
|
+
});
|
99
|
+
|
100
|
+
if (!fromBlockInstance) {
|
101
|
+
res.status(401).send({ error: `Block instance not found "${connection.provider.blockId}` });
|
102
|
+
return;
|
103
|
+
}
|
104
|
+
|
105
|
+
const fromBlockAsset = await assetManager.getAsset(fromBlockInstance.block.ref);
|
106
|
+
|
107
|
+
if (!fromBlockAsset) {
|
108
|
+
res.status(401).send({ error: `Block asset not found "${fromBlockInstance.block.ref}` });
|
109
|
+
return;
|
110
|
+
}
|
111
|
+
|
112
|
+
const providerResource = getResource(fromBlockAsset.data.spec.providers, connection.provider.resourceName);
|
113
|
+
|
114
|
+
if (!providerResource) {
|
115
|
+
res.status(401).send({
|
116
|
+
error: `Block resource not found "${connection.provider.blockId}::${connection.provider.resourceName}`,
|
117
|
+
});
|
118
|
+
return;
|
119
|
+
}
|
120
|
+
|
121
|
+
//Get target address
|
122
|
+
let address = await serviceManager.getProviderAddress(
|
123
|
+
req.params.systemId,
|
124
|
+
connection.provider.blockId,
|
125
|
+
req.params.type
|
126
|
+
);
|
127
|
+
|
128
|
+
while (address.endsWith('/')) {
|
129
|
+
address = address.substring(0, address.length - 1);
|
130
|
+
}
|
131
|
+
|
132
|
+
/*
|
133
|
+
Get the path the consumer requested.
|
134
|
+
Note that this might not match the path the destination is expecting so we need to identify the method
|
135
|
+
that is being called and identify the destination path from the connection.
|
136
|
+
*/
|
137
|
+
const consumerPath = req.originalUrl.substring(basePath.length - 1);
|
138
|
+
|
139
|
+
typeHandler(req, res, {
|
140
|
+
consumerPath,
|
141
|
+
address,
|
142
|
+
consumerResource,
|
143
|
+
providerResource,
|
144
|
+
connection,
|
145
|
+
});
|
146
|
+
} catch (err: any) {
|
147
|
+
console.warn('Failed to process proxy request', err);
|
148
|
+
res.status(400).send({ error: err.message });
|
149
|
+
}
|
150
|
+
}
|
151
|
+
);
|
152
|
+
|
153
|
+
export default router;
|
@@ -0,0 +1,172 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright 2023 Kapeta Inc.
|
3
|
+
* SPDX-License-Identifier: BUSL-1.1
|
4
|
+
*/
|
5
|
+
|
6
|
+
import _ from 'lodash';
|
7
|
+
import request from 'request';
|
8
|
+
import Path from 'path';
|
9
|
+
import { pathTemplateParser } from '../../utils/pathTemplateParser';
|
10
|
+
import { networkManager } from '../../networkManager';
|
11
|
+
|
12
|
+
import { socketManager } from '../../socketManager';
|
13
|
+
import { Request, Response } from 'express';
|
14
|
+
import { ProxyRequestInfo, SimpleRequest, StringMap } from '../../types';
|
15
|
+
import { StringBodyRequest } from '../../middleware/stringBody';
|
16
|
+
import { Resource } from '@kapeta/schemas';
|
17
|
+
import { stringify } from 'qs';
|
18
|
+
|
19
|
+
export function getRestMethodId(restResource: Resource, httpMethod: string, httpPath: string) {
|
20
|
+
return _.findKey(restResource.spec.methods, (method) => {
|
21
|
+
let methodType = method.method ? method.method.toUpperCase() : 'GET';
|
22
|
+
|
23
|
+
if (methodType.toUpperCase() !== httpMethod.toUpperCase()) {
|
24
|
+
return false;
|
25
|
+
}
|
26
|
+
|
27
|
+
let path = method.path;
|
28
|
+
|
29
|
+
if (restResource.spec.basePath) {
|
30
|
+
path = Path.join(restResource.spec.basePath, path);
|
31
|
+
}
|
32
|
+
|
33
|
+
const pathTemplate = pathTemplateParser(path);
|
34
|
+
|
35
|
+
return pathTemplate.matches(httpPath);
|
36
|
+
});
|
37
|
+
}
|
38
|
+
|
39
|
+
/**
|
40
|
+
*
|
41
|
+
* @param req {Request}
|
42
|
+
* @param opts {ProxyRequestInfo}
|
43
|
+
* @return {{consumerMethod: *, providerMethod: *}}
|
44
|
+
*/
|
45
|
+
function resolveMethods(req: Request, opts: ProxyRequestInfo) {
|
46
|
+
const consumerMethodId = getRestMethodId(opts.consumerResource, req.method, opts.consumerPath);
|
47
|
+
|
48
|
+
if (!consumerMethodId) {
|
49
|
+
throw new Error(
|
50
|
+
`Consumer method not found for path "${req.method} ${opts.consumerPath}" in resource "${req.params.consumerInstanceId}::${req.params.consumerResourceName}`
|
51
|
+
);
|
52
|
+
}
|
53
|
+
|
54
|
+
const consumerMethod = _.cloneDeep(opts.consumerResource.spec.methods[consumerMethodId]);
|
55
|
+
|
56
|
+
if (!consumerMethod) {
|
57
|
+
throw new Error(
|
58
|
+
`Consumer method not found for path "${req.method} ${opts.consumerPath}" in resource "${req.params.consumerInstanceId}::${req.params.consumerResourceName}`
|
59
|
+
);
|
60
|
+
}
|
61
|
+
|
62
|
+
consumerMethod.id = consumerMethodId;
|
63
|
+
|
64
|
+
const providerMethodId = _.findKey(opts.connection.mapping, (mapping) => {
|
65
|
+
return mapping.targetId === consumerMethodId;
|
66
|
+
});
|
67
|
+
|
68
|
+
if (!providerMethodId) {
|
69
|
+
throw new Error(`Connection contained no mapping for consumer method "${consumerMethodId}`);
|
70
|
+
}
|
71
|
+
|
72
|
+
const providerMethod = _.cloneDeep(opts.providerResource.spec.methods[providerMethodId]);
|
73
|
+
|
74
|
+
if (!providerMethod) {
|
75
|
+
throw new Error(
|
76
|
+
`Provider method not found "${providerMethodId}" in resource "${opts.connection.provider.blockId}::${opts.connection.provider.resourceName}`
|
77
|
+
);
|
78
|
+
}
|
79
|
+
|
80
|
+
providerMethod.id = providerMethodId;
|
81
|
+
|
82
|
+
return {
|
83
|
+
consumerMethod,
|
84
|
+
providerMethod,
|
85
|
+
};
|
86
|
+
}
|
87
|
+
|
88
|
+
export function proxyRestRequest(req: StringBodyRequest, res: Response, opts: ProxyRequestInfo) {
|
89
|
+
let { consumerMethod, providerMethod } = resolveMethods(req, opts);
|
90
|
+
|
91
|
+
const consumerPathTemplate = pathTemplateParser(consumerMethod.path);
|
92
|
+
const providerPathTemplate = pathTemplateParser(providerMethod.path);
|
93
|
+
|
94
|
+
const pathVariables = consumerPathTemplate.parse(opts.consumerPath);
|
95
|
+
if (!pathVariables) {
|
96
|
+
res.status(400).send({
|
97
|
+
error: `Path did not match any patterns: "${opts.consumerPath}"`,
|
98
|
+
});
|
99
|
+
return;
|
100
|
+
}
|
101
|
+
|
102
|
+
let providerPath = providerPathTemplate.create(pathVariables);
|
103
|
+
|
104
|
+
if (!providerPath.startsWith('/')) {
|
105
|
+
providerPath = '/' + providerPath;
|
106
|
+
}
|
107
|
+
|
108
|
+
if (!_.isEmpty(req.query)) {
|
109
|
+
providerPath += '?' + stringify(req.query, { arrayFormat: 'repeat' });
|
110
|
+
}
|
111
|
+
|
112
|
+
const requestHeaders = _.clone(req.headers);
|
113
|
+
|
114
|
+
delete requestHeaders['content-length'];
|
115
|
+
delete requestHeaders['content-encoding'];
|
116
|
+
delete requestHeaders['connection'];
|
117
|
+
delete requestHeaders['host'];
|
118
|
+
delete requestHeaders['origin'];
|
119
|
+
|
120
|
+
console.log('Proxy request to provider: %s => %s [rest]', opts.consumerPath, opts.address + providerPath);
|
121
|
+
|
122
|
+
const reqOpts: SimpleRequest = {
|
123
|
+
method: providerMethod.method || 'GET',
|
124
|
+
url: opts.address + providerPath,
|
125
|
+
body: req.stringBody,
|
126
|
+
headers: requestHeaders as StringMap,
|
127
|
+
};
|
128
|
+
|
129
|
+
const traffic = networkManager.addRequest(
|
130
|
+
req.params.systemId,
|
131
|
+
opts.connection,
|
132
|
+
reqOpts,
|
133
|
+
consumerMethod.id,
|
134
|
+
providerMethod.id
|
135
|
+
);
|
136
|
+
|
137
|
+
socketManager.emit(traffic.connectionId, 'traffic_start', traffic);
|
138
|
+
|
139
|
+
request(reqOpts, function (err, response, responseBody) {
|
140
|
+
if (err) {
|
141
|
+
traffic.asError(err);
|
142
|
+
socketManager.emit(traffic.connectionId, 'traffic_end', traffic);
|
143
|
+
|
144
|
+
res.status(500).send({ error: '' + err });
|
145
|
+
return;
|
146
|
+
}
|
147
|
+
|
148
|
+
const responseHeaders = _.clone(response.headers);
|
149
|
+
|
150
|
+
delete responseHeaders['content-length'];
|
151
|
+
delete responseHeaders['content-encoding'];
|
152
|
+
delete responseHeaders['connection'];
|
153
|
+
|
154
|
+
res.set(responseHeaders);
|
155
|
+
|
156
|
+
res.status(response.statusCode);
|
157
|
+
|
158
|
+
traffic.withResponse({
|
159
|
+
code: response.statusCode,
|
160
|
+
headers: response.headers as StringMap,
|
161
|
+
body: responseBody,
|
162
|
+
});
|
163
|
+
|
164
|
+
socketManager.emit(traffic.connectionId, 'traffic_end', traffic);
|
165
|
+
|
166
|
+
if (responseBody) {
|
167
|
+
res.write(responseBody);
|
168
|
+
}
|
169
|
+
|
170
|
+
res.end();
|
171
|
+
});
|
172
|
+
}
|
@@ -0,0 +1,70 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright 2023 Kapeta Inc.
|
3
|
+
* SPDX-License-Identifier: BUSL-1.1
|
4
|
+
*/
|
5
|
+
|
6
|
+
import request from 'request';
|
7
|
+
import _ from 'lodash';
|
8
|
+
import { networkManager } from '../../networkManager';
|
9
|
+
import { socketManager } from '../../socketManager';
|
10
|
+
import { Response } from 'express';
|
11
|
+
import { ProxyRequestInfo, SimpleRequest, StringMap } from '../../types';
|
12
|
+
import { StringBodyRequest } from '../../middleware/stringBody';
|
13
|
+
import { stringify } from 'qs';
|
14
|
+
|
15
|
+
export function proxyHttpRequest(req: StringBodyRequest, res: Response, opts: ProxyRequestInfo) {
|
16
|
+
const requestHeaders = _.clone(req.headers);
|
17
|
+
|
18
|
+
delete requestHeaders['content-length'];
|
19
|
+
delete requestHeaders['content-encoding'];
|
20
|
+
delete requestHeaders['connection'];
|
21
|
+
delete requestHeaders['host'];
|
22
|
+
delete requestHeaders['origin'];
|
23
|
+
|
24
|
+
const sourceBasePath = opts.consumerResource.spec.path;
|
25
|
+
const targetBasePath = opts.providerResource.spec.path;
|
26
|
+
let path = opts.consumerPath;
|
27
|
+
if (opts.consumerPath.startsWith(sourceBasePath)) {
|
28
|
+
path = path.replace(sourceBasePath, targetBasePath);
|
29
|
+
}
|
30
|
+
|
31
|
+
if (!_.isEmpty(req.query)) {
|
32
|
+
path += '?' + stringify(req.query, { arrayFormat: 'repeat' });
|
33
|
+
}
|
34
|
+
|
35
|
+
console.log('Proxy request to provider: %s => %s%s [http]', opts.consumerPath, opts.address, path);
|
36
|
+
|
37
|
+
const reqOpts: SimpleRequest = {
|
38
|
+
method: req.method,
|
39
|
+
url: opts.address + path,
|
40
|
+
headers: requestHeaders as StringMap,
|
41
|
+
body: req.stringBody,
|
42
|
+
};
|
43
|
+
|
44
|
+
const traffic = networkManager.addRequest(req.params.systemId, opts.connection, reqOpts);
|
45
|
+
|
46
|
+
socketManager.emit(traffic.connectionId, 'traffic_start', traffic);
|
47
|
+
const proxyReq = request(reqOpts);
|
48
|
+
|
49
|
+
proxyReq.on('error', function (err) {
|
50
|
+
traffic.asError(err);
|
51
|
+
socketManager.emit(traffic.connectionId, 'traffic_end', traffic);
|
52
|
+
if (!res.headersSent) {
|
53
|
+
res.status(500).send({ error: '' + err });
|
54
|
+
}
|
55
|
+
});
|
56
|
+
|
57
|
+
proxyReq.on('response', function (response) {
|
58
|
+
//TODO: Include the response body in the traffic object when it is not a stream
|
59
|
+
traffic.withResponse({
|
60
|
+
code: response.statusCode,
|
61
|
+
headers: response.headers as StringMap,
|
62
|
+
body: null,
|
63
|
+
});
|
64
|
+
|
65
|
+
socketManager.emit(traffic.connectionId, 'traffic_end', traffic);
|
66
|
+
});
|
67
|
+
|
68
|
+
//We need to pipe the proxy response to the client response to handle sockets and event streams
|
69
|
+
proxyReq.pipe(res);
|
70
|
+
}
|
@@ -0,0 +1,291 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright 2023 Kapeta Inc.
|
3
|
+
* SPDX-License-Identifier: BUSL-1.1
|
4
|
+
*/
|
5
|
+
|
6
|
+
import os from 'node:os';
|
7
|
+
import { socketManager } from './socketManager';
|
8
|
+
import { DependencyReference, Dependency, resolveDependencies } from '@kapeta/schemas';
|
9
|
+
import { Actions, AssetVersion, Config, RegistryService } from '@kapeta/nodejs-registry-utils';
|
10
|
+
import { definitionsManager } from './definitionsManager';
|
11
|
+
import { Task, taskManager } from './taskManager';
|
12
|
+
import { normalizeKapetaUri, parseKapetaUri, parseVersion } from '@kapeta/nodejs-utils';
|
13
|
+
import { TaskProgressListener } from './progressListener';
|
14
|
+
import { RepositoryWatcher } from './RepositoryWatcher';
|
15
|
+
import { SourceOfChange } from './types';
|
16
|
+
import { cacheManager } from './cacheManager';
|
17
|
+
import { EventEmitter } from 'node:events';
|
18
|
+
import { DefinitionInfo } from '@kapeta/local-cluster-config';
|
19
|
+
|
20
|
+
const EVENT_DEFAULT_PROVIDERS_START = 'default-providers-start';
|
21
|
+
const EVENT_DEFAULT_PROVIDERS_END = 'default-providers-end';
|
22
|
+
|
23
|
+
const DEFAULT_PROVIDERS = [
|
24
|
+
'kapeta/block-type-service',
|
25
|
+
'kapeta/block-type-frontend',
|
26
|
+
'kapeta/block-type-gateway-http',
|
27
|
+
'kapeta/resource-type-rest-api',
|
28
|
+
'kapeta/resource-type-rest-client',
|
29
|
+
'kapeta/resource-type-web-page',
|
30
|
+
'kapeta/resource-type-web-fragment',
|
31
|
+
'kapeta/resource-type-mongodb',
|
32
|
+
'kapeta/resource-type-postgresql',
|
33
|
+
'kapeta/resource-type-smtp-client',
|
34
|
+
'kapeta/language-target-react-ts',
|
35
|
+
'kapeta/language-target-nodejs',
|
36
|
+
];
|
37
|
+
|
38
|
+
class RepositoryManager extends EventEmitter {
|
39
|
+
private _registryService: RegistryService;
|
40
|
+
private watcher: RepositoryWatcher;
|
41
|
+
|
42
|
+
constructor() {
|
43
|
+
super();
|
44
|
+
this._registryService = new RegistryService(Config.data.registry.url);
|
45
|
+
this.watcher = new RepositoryWatcher();
|
46
|
+
this.listenForChanges();
|
47
|
+
|
48
|
+
this.watcher.on('change', (file: string, source: SourceOfChange) => {
|
49
|
+
this.emit('change', file, source);
|
50
|
+
});
|
51
|
+
}
|
52
|
+
|
53
|
+
listenForChanges() {
|
54
|
+
this.watcher.watch();
|
55
|
+
}
|
56
|
+
|
57
|
+
async stopListening() {
|
58
|
+
return this.watcher.unwatch();
|
59
|
+
}
|
60
|
+
|
61
|
+
/**
|
62
|
+
* Setting the source of change helps us know
|
63
|
+
* how to react to changes in the UI.
|
64
|
+
*/
|
65
|
+
setSourceOfChangeFor(file: string, source: SourceOfChange) {
|
66
|
+
return this.watcher.setSourceOfChangeFor(file, source);
|
67
|
+
}
|
68
|
+
|
69
|
+
clearSourceOfChangeFor(file: string) {
|
70
|
+
return this.watcher.clearSourceOfChangeFor(file);
|
71
|
+
}
|
72
|
+
|
73
|
+
public async ensureDefaultProviders(): Promise<void> {
|
74
|
+
socketManager.emitGlobal(EVENT_DEFAULT_PROVIDERS_START, { providers: DEFAULT_PROVIDERS });
|
75
|
+
const tasks = await this.scheduleInstallation(DEFAULT_PROVIDERS);
|
76
|
+
Promise.allSettled(tasks.map((t) => t.wait())).then(() => {
|
77
|
+
socketManager.emitGlobal(EVENT_DEFAULT_PROVIDERS_END, {});
|
78
|
+
});
|
79
|
+
}
|
80
|
+
|
81
|
+
/**
|
82
|
+
* Will go through all available assets and get a list of
|
83
|
+
* providers that are not referenced anywhere.
|
84
|
+
*
|
85
|
+
* It will also make sure to not include the latest version of an asset.
|
86
|
+
*
|
87
|
+
*/
|
88
|
+
public async getUnusedProviders(): Promise<string[]> {
|
89
|
+
const allDefinitions: DefinitionInfo[] = await definitionsManager.getDefinitions();
|
90
|
+
const blocks: DefinitionInfo[] = [];
|
91
|
+
const plans: DefinitionInfo[] = [];
|
92
|
+
const providerMap = new Map<string, DefinitionInfo>();
|
93
|
+
const providerVersions: { [name: string]: Set<string> } = {};
|
94
|
+
const unusedProviders = new Set<string>();
|
95
|
+
allDefinitions.forEach((d) => {
|
96
|
+
if (d.definition.kind === 'core/plan') {
|
97
|
+
plans.push(d);
|
98
|
+
return;
|
99
|
+
}
|
100
|
+
|
101
|
+
if (d.definition.kind.startsWith('core/')) {
|
102
|
+
const ref = normalizeKapetaUri(`${d.definition.metadata.name}:${d.version}`);
|
103
|
+
providerMap.set(ref, d);
|
104
|
+
if (!providerVersions[d.definition.metadata.name]) {
|
105
|
+
providerVersions[d.definition.metadata.name] = new Set<string>();
|
106
|
+
}
|
107
|
+
providerVersions[d.definition.metadata.name].add(d.version);
|
108
|
+
unusedProviders.add(ref);
|
109
|
+
return;
|
110
|
+
}
|
111
|
+
blocks.push(d);
|
112
|
+
});
|
113
|
+
|
114
|
+
const latestVersions: { [name: string]: string } = {};
|
115
|
+
Object.entries(providerVersions).forEach(([name, versions]) => {
|
116
|
+
const versionArray = Array.from(versions);
|
117
|
+
versionArray.sort((a, b) => {
|
118
|
+
return parseVersion(a).compareTo(parseVersion(b)) * -1;
|
119
|
+
});
|
120
|
+
latestVersions[name] = versionArray[0];
|
121
|
+
});
|
122
|
+
|
123
|
+
function markDependencyAsUsed(dep: DependencyReference) {
|
124
|
+
const uri = parseKapetaUri(dep.name);
|
125
|
+
const ref = uri.toNormalizedString();
|
126
|
+
if (unusedProviders.has(ref)) {
|
127
|
+
unusedProviders.delete(ref);
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
131
|
+
plans.forEach((plan) => {
|
132
|
+
const dependencies = resolveDependencies(plan.definition);
|
133
|
+
dependencies.forEach(markDependencyAsUsed);
|
134
|
+
});
|
135
|
+
|
136
|
+
blocks.forEach((block) => {
|
137
|
+
const blockTypeKind = normalizeKapetaUri(block.definition.kind);
|
138
|
+
unusedProviders.delete(blockTypeKind);
|
139
|
+
const blockTypeProvider = providerMap.get(blockTypeKind);
|
140
|
+
if (!blockTypeProvider) {
|
141
|
+
console.warn('No provider found for block type', block.definition.kind);
|
142
|
+
return;
|
143
|
+
}
|
144
|
+
const dependencies = resolveDependencies(block.definition, blockTypeProvider.definition);
|
145
|
+
dependencies.forEach(markDependencyAsUsed);
|
146
|
+
});
|
147
|
+
|
148
|
+
return Array.from(unusedProviders).filter((ref) => {
|
149
|
+
const uri = parseKapetaUri(ref);
|
150
|
+
if (uri.version == 'local') {
|
151
|
+
// Don't delete local assets
|
152
|
+
return false;
|
153
|
+
}
|
154
|
+
|
155
|
+
// Don't delete the latest version of an asset
|
156
|
+
return latestVersions[uri.fullName] !== uri.version;
|
157
|
+
});
|
158
|
+
}
|
159
|
+
|
160
|
+
public async getUpdatableAssets(allNames: string[]): Promise<string[]> {
|
161
|
+
const names = Array.from(new Set<string>(allNames));
|
162
|
+
|
163
|
+
const currentVersions = await Promise.all(
|
164
|
+
names.map((name) => definitionsManager.getLatestDefinition(name).catch(() => undefined))
|
165
|
+
);
|
166
|
+
|
167
|
+
const latestVersions = await Promise.all(
|
168
|
+
names.map((name) => this._registryService.getLatestVersion(name).catch(() => undefined))
|
169
|
+
);
|
170
|
+
|
171
|
+
return names
|
172
|
+
.map((name, index) => {
|
173
|
+
const currentVersion: DefinitionInfo | undefined = currentVersions[index];
|
174
|
+
const latestVersion: AssetVersion | undefined = latestVersions[index];
|
175
|
+
if (!currentVersion || !latestVersion) {
|
176
|
+
// Shouldn't happen unless the registry is down or an asset was deleted
|
177
|
+
return undefined;
|
178
|
+
}
|
179
|
+
|
180
|
+
const ref = normalizeKapetaUri(`${name}:${latestVersion.version}`);
|
181
|
+
|
182
|
+
if (currentVersion.version === latestVersion.version) {
|
183
|
+
return undefined;
|
184
|
+
}
|
185
|
+
|
186
|
+
return ref;
|
187
|
+
})
|
188
|
+
.filter((ref) => !!ref) as string[];
|
189
|
+
}
|
190
|
+
|
191
|
+
private async scheduleInstallation(refs: string[]): Promise<Task[]> {
|
192
|
+
//We make sure to only install one asset at a time - otherwise unexpected things might happen
|
193
|
+
const createInstaller = (ref: string) => {
|
194
|
+
return async (task: Task) => {
|
195
|
+
if (await definitionsManager.exists(ref)) {
|
196
|
+
return;
|
197
|
+
}
|
198
|
+
|
199
|
+
const progressListener = new TaskProgressListener(task);
|
200
|
+
//console.log(`Installing asset: ${ref}`);
|
201
|
+
//Auto-install missing asset
|
202
|
+
try {
|
203
|
+
//We change to a temp dir to avoid issues with the current working directory
|
204
|
+
process.chdir(os.tmpdir());
|
205
|
+
await Actions.install(progressListener, [ref], {});
|
206
|
+
} catch (e) {
|
207
|
+
console.error(`Failed to install asset: ${ref}`, e);
|
208
|
+
throw e;
|
209
|
+
}
|
210
|
+
cacheManager.flush();
|
211
|
+
if (await definitionsManager.exists(ref)) {
|
212
|
+
return;
|
213
|
+
}
|
214
|
+
throw new Error(`Failed to find asset after installation: ${ref}. Please try again`);
|
215
|
+
};
|
216
|
+
};
|
217
|
+
|
218
|
+
const tasks: Task[] = [];
|
219
|
+
|
220
|
+
while (refs.length > 0) {
|
221
|
+
let ref = refs.shift();
|
222
|
+
if (!ref) {
|
223
|
+
continue;
|
224
|
+
}
|
225
|
+
ref = normalizeKapetaUri(ref);
|
226
|
+
|
227
|
+
if (await definitionsManager.exists(ref)) {
|
228
|
+
continue;
|
229
|
+
}
|
230
|
+
|
231
|
+
const task = taskManager.add(`asset:install:${ref}`, createInstaller(ref), {
|
232
|
+
name: `Installing ${ref}`,
|
233
|
+
group: 'asset:install:', //Group prevents multiple tasks from running at the same time
|
234
|
+
});
|
235
|
+
|
236
|
+
tasks.push(task);
|
237
|
+
}
|
238
|
+
|
239
|
+
return tasks;
|
240
|
+
}
|
241
|
+
|
242
|
+
async ensureAsset(
|
243
|
+
handle: string,
|
244
|
+
name: string,
|
245
|
+
version: string,
|
246
|
+
wait: boolean = true
|
247
|
+
): Promise<undefined | Task[]> {
|
248
|
+
const fullName = `${handle}/${name}`;
|
249
|
+
const ref = `${fullName}:${version}`;
|
250
|
+
|
251
|
+
if (version === 'local') {
|
252
|
+
//TODO: Get dependencies for local asset
|
253
|
+
return;
|
254
|
+
}
|
255
|
+
|
256
|
+
const installedAsset = await definitionsManager.getDefinition(`${fullName}:${version}`);
|
257
|
+
|
258
|
+
let assetVersion;
|
259
|
+
try {
|
260
|
+
assetVersion = await this._registryService.getVersion(fullName, version);
|
261
|
+
if (!assetVersion) {
|
262
|
+
return;
|
263
|
+
}
|
264
|
+
} catch (e) {
|
265
|
+
console.warn(`Unable to resolve asset: ${ref}`, e);
|
266
|
+
if (installedAsset) {
|
267
|
+
return;
|
268
|
+
}
|
269
|
+
throw e;
|
270
|
+
}
|
271
|
+
|
272
|
+
let tasks: Task[] | undefined = undefined;
|
273
|
+
if (!installedAsset) {
|
274
|
+
tasks = await this.scheduleInstallation([ref]);
|
275
|
+
} else {
|
276
|
+
//Ensure dependencies are installed
|
277
|
+
const refs = assetVersion.dependencies.map((dep: Dependency) => dep.name);
|
278
|
+
if (refs.length > 0) {
|
279
|
+
tasks = await this.scheduleInstallation(refs);
|
280
|
+
}
|
281
|
+
}
|
282
|
+
|
283
|
+
if (tasks && wait) {
|
284
|
+
await Promise.all(tasks.map((t) => t.wait()));
|
285
|
+
}
|
286
|
+
|
287
|
+
return tasks;
|
288
|
+
}
|
289
|
+
}
|
290
|
+
|
291
|
+
export const repositoryManager = new RepositoryManager();
|