@kapeta/local-cluster-service 0.30.1 → 0.32.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/CHANGELOG.md +14 -0
- package/dist/cjs/index.js +2 -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/codeGeneratorManager.js +4 -3
- package/dist/cjs/src/containerManager.js +7 -1
- package/dist/cjs/src/filesystem/routes.js +16 -0
- package/dist/cjs/src/filesystemManager.d.ts +4 -0
- package/dist/cjs/src/filesystemManager.js +14 -0
- package/dist/cjs/src/utils/utils.d.ts +1 -0
- package/dist/cjs/src/utils/utils.js +7 -1
- package/dist/esm/index.js +2 -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/codeGeneratorManager.js +4 -3
- package/dist/esm/src/containerManager.js +7 -1
- package/dist/esm/src/filesystem/routes.js +16 -0
- package/dist/esm/src/filesystemManager.d.ts +4 -0
- package/dist/esm/src/filesystemManager.js +14 -0
- package/dist/esm/src/utils/utils.d.ts +1 -0
- package/dist/esm/src/utils/utils.js +7 -1
- package/index.ts +2 -0
- package/package.json +1 -1
- 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/codeGeneratorManager.ts +6 -4
- package/src/containerManager.ts +7 -1
- package/src/filesystem/routes.ts +22 -0
- package/src/filesystemManager.ts +18 -0
- package/src/utils/utils.ts +6 -0
package/src/ai/routes.ts
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright 2023 Kapeta Inc.
|
3
|
+
* SPDX-License-Identifier: BUSL-1.1
|
4
|
+
*/
|
5
|
+
|
6
|
+
import Router from 'express-promise-router';
|
7
|
+
import { Response } from 'express';
|
8
|
+
|
9
|
+
import { corsHandler } from '../middleware/cors';
|
10
|
+
|
11
|
+
import { stringBody, StringBodyRequest } from '../middleware/stringBody';
|
12
|
+
import { aiClient, AIRequest } from './aiClient';
|
13
|
+
import { KapetaBodyRequest } from '../types';
|
14
|
+
import YAML from 'yaml';
|
15
|
+
|
16
|
+
const router = Router();
|
17
|
+
|
18
|
+
router.use('/', corsHandler);
|
19
|
+
router.use('/', stringBody);
|
20
|
+
|
21
|
+
router.post('/prompt/:handle', async (req: KapetaBodyRequest, res: Response) => {
|
22
|
+
const handle = req.params.handle;
|
23
|
+
try {
|
24
|
+
const aiRequest: AIRequest = JSON.parse(req.stringBody ?? '{}');
|
25
|
+
const result = await aiClient.sendPrompt(handle, aiRequest);
|
26
|
+
if (req.accepts('application/yaml')) {
|
27
|
+
res.set('Content-Type', 'application/yaml');
|
28
|
+
res.send(YAML.stringify(result));
|
29
|
+
} else {
|
30
|
+
res.json(result);
|
31
|
+
}
|
32
|
+
} catch (err: any) {
|
33
|
+
console.error('Failed to send prompt', err);
|
34
|
+
res.status(400).send({ error: err.message });
|
35
|
+
return;
|
36
|
+
}
|
37
|
+
});
|
38
|
+
|
39
|
+
export default router;
|
@@ -0,0 +1,275 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright 2023 Kapeta Inc.
|
3
|
+
* SPDX-License-Identifier: BUSL-1.1
|
4
|
+
*/
|
5
|
+
import { BlockDefinition, Plan } from '@kapeta/schemas';
|
6
|
+
import { Application } from './types';
|
7
|
+
import { definitionsManager } from '../definitionsManager';
|
8
|
+
import { normalizeKapetaUri, parseKapetaUri } from '@kapeta/nodejs-utils';
|
9
|
+
import uuid from 'node-uuid';
|
10
|
+
|
11
|
+
export type PlanContext = {
|
12
|
+
plan: Plan;
|
13
|
+
blocks: BlockDefinition[];
|
14
|
+
};
|
15
|
+
|
16
|
+
async function getFreeName(name: string) {
|
17
|
+
let currentName = name;
|
18
|
+
let iteration = 1;
|
19
|
+
do {
|
20
|
+
const found = await definitionsManager.getLatestDefinition(currentName);
|
21
|
+
if (!found) {
|
22
|
+
return currentName;
|
23
|
+
}
|
24
|
+
currentName = name + '_' + iteration++;
|
25
|
+
} while (true);
|
26
|
+
}
|
27
|
+
|
28
|
+
export const transformToPlan = async (handle: string, application: Application): Promise<PlanContext> => {
|
29
|
+
const blockTypeService = await definitionsManager.getLatestDefinition('kapeta/block-type-service');
|
30
|
+
const blockTypeFrontend = await definitionsManager.getLatestDefinition('kapeta/block-type-frontend');
|
31
|
+
const mongoDbResource = await definitionsManager.getLatestDefinition('kapeta/resource-type-mongodb');
|
32
|
+
const postgresResource = await definitionsManager.getLatestDefinition('kapeta/resource-type-postgresql');
|
33
|
+
const webPageResource = await definitionsManager.getLatestDefinition('kapeta/resource-type-web-page');
|
34
|
+
const webFragmentResource = await definitionsManager.getLatestDefinition('kapeta/resource-type-web-fragment');
|
35
|
+
const restApiResource = await definitionsManager.getLatestDefinition('kapeta/resource-type-rest-api');
|
36
|
+
const restClientResource = await definitionsManager.getLatestDefinition('kapeta/resource-type-rest-client');
|
37
|
+
const javaLanguage = await definitionsManager.getLatestDefinition('kapeta/language-target-java-spring-boot');
|
38
|
+
const nodejsLanguage = await definitionsManager.getLatestDefinition('kapeta/language-target-nodejs');
|
39
|
+
const reactLanguage = await definitionsManager.getLatestDefinition('kapeta/language-target-react-ts');
|
40
|
+
|
41
|
+
if (
|
42
|
+
!blockTypeService ||
|
43
|
+
!blockTypeFrontend ||
|
44
|
+
!mongoDbResource ||
|
45
|
+
!postgresResource ||
|
46
|
+
!javaLanguage ||
|
47
|
+
!nodejsLanguage ||
|
48
|
+
!reactLanguage ||
|
49
|
+
!webPageResource ||
|
50
|
+
!restApiResource ||
|
51
|
+
!restClientResource ||
|
52
|
+
!webFragmentResource
|
53
|
+
) {
|
54
|
+
throw new Error('Missing definitions');
|
55
|
+
}
|
56
|
+
|
57
|
+
const plan: Plan = {
|
58
|
+
kind: 'core/plan',
|
59
|
+
metadata: {
|
60
|
+
name: await getFreeName(`${handle}/${application.name}`),
|
61
|
+
title: application.title,
|
62
|
+
description: application.description,
|
63
|
+
visibility: 'private',
|
64
|
+
},
|
65
|
+
spec: {
|
66
|
+
blocks: [],
|
67
|
+
connections: [],
|
68
|
+
},
|
69
|
+
};
|
70
|
+
|
71
|
+
const blocks: BlockDefinition[] = [];
|
72
|
+
|
73
|
+
const addToPlan = (ref: string, name: string) => {
|
74
|
+
const top = 100 + Math.floor(plan.spec.blocks.length / 3) * 300;
|
75
|
+
const left = 200 + (plan.spec.blocks.length % 3) * 450;
|
76
|
+
|
77
|
+
plan.spec.blocks.push({
|
78
|
+
block: {
|
79
|
+
ref,
|
80
|
+
},
|
81
|
+
name,
|
82
|
+
id: uuid.v4(),
|
83
|
+
dimensions: {
|
84
|
+
top,
|
85
|
+
left,
|
86
|
+
width: -1,
|
87
|
+
height: -1,
|
88
|
+
},
|
89
|
+
});
|
90
|
+
};
|
91
|
+
|
92
|
+
const nameMapper = new Map<string, string>();
|
93
|
+
|
94
|
+
for (const backend of application.backends) {
|
95
|
+
const blockName = `${handle}/${backend.name}`;
|
96
|
+
const blockRealName = await getFreeName(blockName);
|
97
|
+
nameMapper.set(blockName, blockRealName);
|
98
|
+
|
99
|
+
const language = backend.targetLanguage === 'java' ? javaLanguage : nodejsLanguage;
|
100
|
+
const databaseInfo = backend.databases?.[0];
|
101
|
+
const database = databaseInfo?.type === 'mongodb' ? mongoDbResource : postgresResource;
|
102
|
+
|
103
|
+
const blockRef = normalizeKapetaUri(blockRealName + ':local');
|
104
|
+
let targetOptions = {};
|
105
|
+
if (backend.targetLanguage === 'java') {
|
106
|
+
targetOptions = {
|
107
|
+
basePackage: `${handle}.${application.name}`.toLowerCase().replace(/-/g, '_'),
|
108
|
+
groupId: `${handle}.${application.name}`.toLowerCase().replace(/-/g, '_'),
|
109
|
+
artifactId: backend.name.toLowerCase().replace(/-/g, '_'),
|
110
|
+
};
|
111
|
+
}
|
112
|
+
blocks.push({
|
113
|
+
kind: normalizeKapetaUri(`${blockTypeService.definition.metadata.name}:${blockTypeService.version}`),
|
114
|
+
metadata: {
|
115
|
+
name: blockRealName,
|
116
|
+
title: backend.title,
|
117
|
+
description: backend.description,
|
118
|
+
visibility: 'private',
|
119
|
+
},
|
120
|
+
spec: {
|
121
|
+
target: {
|
122
|
+
kind: normalizeKapetaUri(`${language.definition.metadata.name}:${language.version}`),
|
123
|
+
options: targetOptions,
|
124
|
+
},
|
125
|
+
consumers: [
|
126
|
+
{
|
127
|
+
kind: normalizeKapetaUri(`${database.definition.metadata.name}:${database.version}`),
|
128
|
+
metadata: {
|
129
|
+
name: (databaseInfo?.name ?? 'main').replace(/-/g, ''),
|
130
|
+
},
|
131
|
+
spec: {
|
132
|
+
port: {
|
133
|
+
type: database.definition.spec.ports[0].type,
|
134
|
+
},
|
135
|
+
},
|
136
|
+
},
|
137
|
+
],
|
138
|
+
providers: [
|
139
|
+
{
|
140
|
+
kind: normalizeKapetaUri(
|
141
|
+
`${restApiResource.definition.metadata.name}:${restApiResource.version}`
|
142
|
+
),
|
143
|
+
metadata: {
|
144
|
+
name: 'main',
|
145
|
+
},
|
146
|
+
spec: {
|
147
|
+
port: {
|
148
|
+
type: restApiResource.definition.spec.ports[0].type,
|
149
|
+
},
|
150
|
+
},
|
151
|
+
},
|
152
|
+
],
|
153
|
+
},
|
154
|
+
});
|
155
|
+
|
156
|
+
addToPlan(blockRef, backend.name);
|
157
|
+
}
|
158
|
+
|
159
|
+
for (const frontend of application.frontends) {
|
160
|
+
const blockName = `${handle}/${frontend.name}`;
|
161
|
+
const blockRealName = await getFreeName(blockName);
|
162
|
+
nameMapper.set(blockName, blockRealName);
|
163
|
+
|
164
|
+
const language = reactLanguage;
|
165
|
+
|
166
|
+
const blockRef = normalizeKapetaUri(blockRealName + ':local');
|
167
|
+
blocks.push({
|
168
|
+
kind: normalizeKapetaUri(`${blockTypeFrontend.definition.metadata.name}:${blockTypeFrontend.version}`),
|
169
|
+
metadata: {
|
170
|
+
name: blockRealName,
|
171
|
+
title: frontend.title,
|
172
|
+
description: frontend.description,
|
173
|
+
visibility: 'private',
|
174
|
+
},
|
175
|
+
spec: {
|
176
|
+
target: {
|
177
|
+
kind: normalizeKapetaUri(`${language.definition.metadata.name}:${language.version}`),
|
178
|
+
options: {},
|
179
|
+
},
|
180
|
+
consumers: [],
|
181
|
+
providers: [
|
182
|
+
{
|
183
|
+
kind: normalizeKapetaUri(
|
184
|
+
`${webPageResource.definition.metadata.name}:${webPageResource.version}`
|
185
|
+
),
|
186
|
+
metadata: {
|
187
|
+
name: 'main',
|
188
|
+
},
|
189
|
+
spec: {
|
190
|
+
port: {
|
191
|
+
type: webPageResource.definition.spec.ports[0].type,
|
192
|
+
},
|
193
|
+
},
|
194
|
+
},
|
195
|
+
],
|
196
|
+
},
|
197
|
+
});
|
198
|
+
|
199
|
+
addToPlan(blockRef, frontend.name);
|
200
|
+
}
|
201
|
+
|
202
|
+
application.connections?.forEach((connection) => {
|
203
|
+
const fullProviderName = nameMapper.get(`${handle}/${connection.provider.name}`) as string;
|
204
|
+
const fullConsumerName = nameMapper.get(`${handle}/${connection.consumer.name}`) as string;
|
205
|
+
const consumerResourceName = connection.provider.name;
|
206
|
+
const providerRef = normalizeKapetaUri(`${fullProviderName}:local`);
|
207
|
+
const consumerRef = normalizeKapetaUri(`${fullConsumerName}:local`);
|
208
|
+
|
209
|
+
const instanceProvider = plan.spec.blocks.find((b) => b.block.ref === providerRef)!;
|
210
|
+
const instanceConsumer = plan.spec.blocks.find((b) => b.block.ref === consumerRef)!;
|
211
|
+
const consumerBlock = blocks.find((block) => block.metadata.name === fullConsumerName);
|
212
|
+
const providerBlock = blocks.find((block) => block.metadata.name === fullProviderName);
|
213
|
+
if (!consumerBlock) {
|
214
|
+
throw new Error('Missing consumer block: ' + fullConsumerName);
|
215
|
+
}
|
216
|
+
|
217
|
+
if (!providerBlock) {
|
218
|
+
throw new Error('Missing provider block: ' + fullProviderName);
|
219
|
+
}
|
220
|
+
|
221
|
+
const portType = parseKapetaUri(providerBlock.kind).fullName === 'kapeta/block-type-service' ? 'rest' : 'web';
|
222
|
+
|
223
|
+
if (portType === 'rest') {
|
224
|
+
consumerBlock.spec.consumers!.push({
|
225
|
+
kind: normalizeKapetaUri(
|
226
|
+
`${restClientResource.definition.metadata.name}:${restClientResource.version}`
|
227
|
+
),
|
228
|
+
metadata: {
|
229
|
+
name: consumerResourceName,
|
230
|
+
},
|
231
|
+
spec: {
|
232
|
+
port: {
|
233
|
+
type: 'rest',
|
234
|
+
},
|
235
|
+
},
|
236
|
+
});
|
237
|
+
} else {
|
238
|
+
consumerBlock.spec.consumers!.push({
|
239
|
+
kind: normalizeKapetaUri(
|
240
|
+
`${webFragmentResource.definition.metadata.name}:${webFragmentResource.version}`
|
241
|
+
),
|
242
|
+
metadata: {
|
243
|
+
name: consumerResourceName,
|
244
|
+
},
|
245
|
+
spec: {
|
246
|
+
port: {
|
247
|
+
type: 'web',
|
248
|
+
},
|
249
|
+
},
|
250
|
+
});
|
251
|
+
}
|
252
|
+
|
253
|
+
plan.spec.connections.push({
|
254
|
+
provider: {
|
255
|
+
blockId: instanceProvider.id,
|
256
|
+
resourceName: 'main',
|
257
|
+
port: {
|
258
|
+
type: portType,
|
259
|
+
},
|
260
|
+
},
|
261
|
+
consumer: {
|
262
|
+
blockId: instanceConsumer.id,
|
263
|
+
resourceName: consumerResourceName,
|
264
|
+
port: {
|
265
|
+
type: portType,
|
266
|
+
},
|
267
|
+
},
|
268
|
+
});
|
269
|
+
});
|
270
|
+
|
271
|
+
return {
|
272
|
+
plan,
|
273
|
+
blocks,
|
274
|
+
};
|
275
|
+
};
|
package/src/ai/types.ts
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright 2023 Kapeta Inc.
|
3
|
+
* SPDX-License-Identifier: BUSL-1.1
|
4
|
+
*/
|
5
|
+
export interface Database {
|
6
|
+
name: string;
|
7
|
+
type: 'mongodb' | 'postgres';
|
8
|
+
}
|
9
|
+
|
10
|
+
export interface BackendService {
|
11
|
+
name: string;
|
12
|
+
title: string;
|
13
|
+
description: string;
|
14
|
+
targetLanguage: 'java' | 'node';
|
15
|
+
databases: Database[] | null;
|
16
|
+
}
|
17
|
+
|
18
|
+
export interface FrontendService {
|
19
|
+
name: string;
|
20
|
+
title: string;
|
21
|
+
description: string;
|
22
|
+
targetLanguage: 'react';
|
23
|
+
}
|
24
|
+
|
25
|
+
export interface Endpoint {
|
26
|
+
type: 'backend' | 'frontend';
|
27
|
+
name: string;
|
28
|
+
}
|
29
|
+
|
30
|
+
export interface Connection {
|
31
|
+
provider: Endpoint;
|
32
|
+
consumer: Endpoint;
|
33
|
+
}
|
34
|
+
|
35
|
+
export interface Application {
|
36
|
+
kind: 'core/plan';
|
37
|
+
name: string;
|
38
|
+
title: string;
|
39
|
+
description: string;
|
40
|
+
backends: BackendService[];
|
41
|
+
frontends: FrontendService[];
|
42
|
+
connections: Connection[];
|
43
|
+
explanation: string;
|
44
|
+
response: string;
|
45
|
+
}
|
@@ -9,7 +9,7 @@ import { BlockDefinition } from '@kapeta/schemas';
|
|
9
9
|
import { definitionsManager } from './definitionsManager';
|
10
10
|
import { Definition } from '@kapeta/local-cluster-config';
|
11
11
|
import { assetManager } from './assetManager';
|
12
|
-
import { normalizeKapetaUri } from '@kapeta/nodejs-utils';
|
12
|
+
import { normalizeKapetaUri, parseKapetaUri } from '@kapeta/nodejs-utils';
|
13
13
|
import { repositoryManager } from './repositoryManager';
|
14
14
|
|
15
15
|
const TARGET_KIND = 'core/language-target';
|
@@ -61,16 +61,18 @@ class CodeGeneratorManager {
|
|
61
61
|
}
|
62
62
|
|
63
63
|
async canGenerateCode(yamlContent: Definition): Promise<boolean> {
|
64
|
-
if (!yamlContent.spec
|
64
|
+
if (!yamlContent.spec?.target?.kind || !yamlContent.kind) {
|
65
65
|
//Not all block types have targets
|
66
66
|
return false;
|
67
67
|
}
|
68
68
|
|
69
|
+
const kindUri = parseKapetaUri(yamlContent.kind);
|
70
|
+
|
69
71
|
const blockTypes = await definitionsManager.getDefinitions(BLOCK_TYPE_KIND);
|
70
72
|
const blockTypeKinds = blockTypes.map(
|
71
|
-
(blockType) => blockType.definition.metadata.name
|
73
|
+
(blockType) => parseKapetaUri(blockType.definition.metadata.name).fullName
|
72
74
|
);
|
73
|
-
return
|
75
|
+
return blockTypeKinds.includes(kindUri.fullName);
|
74
76
|
}
|
75
77
|
|
76
78
|
async generate(yamlFile: string, yamlContent: Definition) {
|
package/src/containerManager.ts
CHANGED
@@ -166,11 +166,17 @@ class ContainerManager {
|
|
166
166
|
];
|
167
167
|
for (const opts of connectOptions) {
|
168
168
|
try {
|
169
|
+
const testClient = new Docker({
|
170
|
+
...opts,
|
171
|
+
timeout: 1000, // 1 secs should be enough for a ping
|
172
|
+
});
|
173
|
+
await testClient.ping();
|
174
|
+
// If we get here - we have a working connection
|
175
|
+
// Now create a client with a longer timeout for all other operations
|
169
176
|
const client = new Docker({
|
170
177
|
...opts,
|
171
178
|
timeout: 15 * 60 * 1000, //15 minutes should be enough for any operation
|
172
179
|
});
|
173
|
-
await client.ping();
|
174
180
|
this._docker = client;
|
175
181
|
const versionInfo: any = await client.version();
|
176
182
|
this._version = versionInfo.Server?.Version ?? versionInfo.Version;
|
package/src/filesystem/routes.ts
CHANGED
@@ -50,6 +50,28 @@ router.post('/release-channel', (req: StringBodyRequest, res: Response) => {
|
|
50
50
|
res.sendStatus(204);
|
51
51
|
});
|
52
52
|
|
53
|
+
router.get('/show-pixel-grid', (req: Request, res: Response) => {
|
54
|
+
res.send(filesystemManager.getShowPixelGrid());
|
55
|
+
});
|
56
|
+
|
57
|
+
router.use('/show-pixel-grid', stringBody);
|
58
|
+
|
59
|
+
router.post('/show-pixel-grid', (req: StringBodyRequest, res: Response) => {
|
60
|
+
filesystemManager.setShowPixelGrid(req?.stringBody === 'true' ?? false);
|
61
|
+
res.sendStatus(204);
|
62
|
+
});
|
63
|
+
|
64
|
+
router.get('/snap-to-pixel-grid', (req: Request, res: Response) => {
|
65
|
+
res.send(filesystemManager.getSnapToPixelGrid());
|
66
|
+
});
|
67
|
+
|
68
|
+
router.use('/snap-to-pixel-grid', stringBody);
|
69
|
+
|
70
|
+
router.post('/snap-to-pixel-grid', (req: StringBodyRequest, res: Response) => {
|
71
|
+
filesystemManager.setSnapToPixelGrid(req?.stringBody === 'true' ?? false);
|
72
|
+
res.sendStatus(204);
|
73
|
+
});
|
74
|
+
|
53
75
|
router.use('/', (req: Request, res: Response, next: NextFunction) => {
|
54
76
|
if (!req.query.path) {
|
55
77
|
res.status(400).send({ error: 'Missing required query parameter "path"' });
|
package/src/filesystemManager.ts
CHANGED
@@ -12,6 +12,8 @@ const SECTION_ID = 'filesystem';
|
|
12
12
|
const PROJECT_ROOT = 'project_root';
|
13
13
|
const EDITOR = 'editor';
|
14
14
|
const RELEASE_CHANNEL = 'release_channel';
|
15
|
+
const SHOW_PIXEL_GRID = 'show_pixel_grid';
|
16
|
+
const SNAP_TO_PIXEL_GRID = 'snap_to_pixel_grid';
|
15
17
|
|
16
18
|
function isFile(path: string) {
|
17
19
|
try {
|
@@ -110,6 +112,22 @@ class FilesystemManager {
|
|
110
112
|
setReleaseChannel(channel: string) {
|
111
113
|
storageService.put('app', RELEASE_CHANNEL, channel);
|
112
114
|
}
|
115
|
+
|
116
|
+
getShowPixelGrid() {
|
117
|
+
return storageService.get<boolean>('app', SHOW_PIXEL_GRID, false);
|
118
|
+
}
|
119
|
+
|
120
|
+
setShowPixelGrid(show: boolean) {
|
121
|
+
storageService.put('app', SHOW_PIXEL_GRID, show);
|
122
|
+
}
|
123
|
+
|
124
|
+
getSnapToPixelGrid() {
|
125
|
+
return storageService.get<boolean>('app', SNAP_TO_PIXEL_GRID, false);
|
126
|
+
}
|
127
|
+
|
128
|
+
setSnapToPixelGrid(snap: boolean) {
|
129
|
+
storageService.put('app', SNAP_TO_PIXEL_GRID, snap);
|
130
|
+
}
|
113
131
|
}
|
114
132
|
|
115
133
|
export const filesystemManager = new FilesystemManager();
|
package/src/utils/utils.ts
CHANGED
@@ -10,11 +10,17 @@ import md5 from 'md5';
|
|
10
10
|
import { EntityList } from '@kapeta/schemas';
|
11
11
|
import _ from 'lodash';
|
12
12
|
import { AnyMap } from '../types';
|
13
|
+
import ClusterConfiguration from '@kapeta/local-cluster-config';
|
13
14
|
|
14
15
|
export function getBlockInstanceContainerName(systemId: string, instanceId: string) {
|
15
16
|
return `kapeta-block-instance-${md5(systemId + instanceId)}`;
|
16
17
|
}
|
17
18
|
|
19
|
+
export function getRemoteUrl(id: string, defautValue: string) {
|
20
|
+
const remoteConfig = ClusterConfiguration.getClusterConfig().remote;
|
21
|
+
return remoteConfig?.[id] ?? defautValue;
|
22
|
+
}
|
23
|
+
|
18
24
|
export function readYML(path: string) {
|
19
25
|
const rawYaml = FS.readFileSync(path);
|
20
26
|
|