@kapeta/local-cluster-service 0.54.11 → 0.55.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 +21 -0
- package/definitions.d.ts +11 -0
- package/dist/cjs/src/storm/archetype.d.ts +12 -0
- package/dist/cjs/src/storm/archetype.js +98 -0
- package/dist/cjs/src/storm/codegen.d.ts +2 -0
- package/dist/cjs/src/storm/codegen.js +28 -4
- package/dist/cjs/src/storm/event-parser.d.ts +7 -4
- package/dist/cjs/src/storm/event-parser.js +190 -160
- package/dist/cjs/src/storm/events.d.ts +1 -0
- package/dist/cjs/src/storm/predefined.d.ts +27 -0
- package/dist/cjs/src/storm/predefined.js +64 -0
- package/dist/cjs/src/storm/routes.js +23 -15
- package/dist/cjs/test/storm/codegen.test.d.ts +5 -0
- package/dist/cjs/test/storm/codegen.test.js +41 -0
- package/dist/cjs/test/storm/event-parser.test.d.ts +25 -1
- package/dist/cjs/test/storm/event-parser.test.js +34 -15
- package/dist/cjs/test/storm/predefined-user-events.json +13 -0
- package/dist/esm/src/storm/archetype.d.ts +12 -0
- package/dist/esm/src/storm/archetype.js +98 -0
- package/dist/esm/src/storm/codegen.d.ts +2 -0
- package/dist/esm/src/storm/codegen.js +28 -4
- package/dist/esm/src/storm/event-parser.d.ts +7 -4
- package/dist/esm/src/storm/event-parser.js +190 -160
- package/dist/esm/src/storm/events.d.ts +1 -0
- package/dist/esm/src/storm/predefined.d.ts +27 -0
- package/dist/esm/src/storm/predefined.js +64 -0
- package/dist/esm/src/storm/routes.js +23 -15
- package/dist/esm/test/storm/codegen.test.d.ts +5 -0
- package/dist/esm/test/storm/codegen.test.js +41 -0
- package/dist/esm/test/storm/event-parser.test.d.ts +25 -1
- package/dist/esm/test/storm/event-parser.test.js +34 -15
- package/dist/esm/test/storm/predefined-user-events.json +13 -0
- package/package.json +6 -1
- package/src/storm/archetype.ts +85 -0
- package/src/storm/codegen.ts +34 -4
- package/src/storm/event-parser.ts +200 -159
- package/src/storm/events.ts +1 -0
- package/src/storm/predefined.ts +52 -0
- package/src/storm/routes.ts +25 -18
- package/test/storm/codegen.test.ts +46 -0
- package/test/storm/event-parser.test.ts +32 -10
- package/test/storm/predefined-user-events.json +13 -0
@@ -7,9 +7,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
7
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
8
8
|
};
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
10
|
+
exports.parserOptions = void 0;
|
10
11
|
const event_parser_1 = require("../../src/storm/event-parser");
|
11
12
|
const simple_blog_events_json_1 = __importDefault(require("./simple-blog-events.json"));
|
12
|
-
const
|
13
|
+
const predefined_user_events_json_1 = __importDefault(require("./predefined-user-events.json"));
|
14
|
+
exports.parserOptions = {
|
13
15
|
serviceKind: 'kapeta/block-service:local',
|
14
16
|
serviceLanguage: 'kapeta/language-target-nodejs-ts:local',
|
15
17
|
frontendKind: 'kapeta/block-type-frontend:local',
|
@@ -129,10 +131,12 @@ const events = [
|
|
129
131
|
},
|
130
132
|
];
|
131
133
|
describe('event-parser', () => {
|
132
|
-
it('it can parse events into a plan and blocks with proper layout', () => {
|
133
|
-
const parser = new event_parser_1.StormEventParser(parserOptions);
|
134
|
-
|
135
|
-
|
134
|
+
it('it can parse events into a plan and blocks with proper layout', async () => {
|
135
|
+
const parser = new event_parser_1.StormEventParser(exports.parserOptions);
|
136
|
+
for (const event of events) {
|
137
|
+
await parser.processEvent('kapeta', event);
|
138
|
+
}
|
139
|
+
const result = await parser.toResult('kapeta');
|
136
140
|
expect(result.plan.metadata.name).toBe('kapeta/my-plan');
|
137
141
|
expect(result.plan.metadata.description).toBe('my plan description');
|
138
142
|
expect(result.blocks.length).toBe(2);
|
@@ -146,10 +150,10 @@ describe('event-parser', () => {
|
|
146
150
|
expect(clientResource).toBeDefined();
|
147
151
|
expect(dbResource).toBeDefined();
|
148
152
|
expect(pageResource).toBeDefined();
|
149
|
-
expect(apiResource?.kind).toBe(parserOptions.apiKind);
|
150
|
-
expect(clientResource?.kind).toBe(parserOptions.clientKind);
|
151
|
-
expect(dbResource?.kind).toBe(parserOptions.databaseKind);
|
152
|
-
expect(pageResource?.kind).toBe(parserOptions.webPageKind);
|
153
|
+
expect(apiResource?.kind).toBe(exports.parserOptions.apiKind);
|
154
|
+
expect(clientResource?.kind).toBe(exports.parserOptions.clientKind);
|
155
|
+
expect(dbResource?.kind).toBe(exports.parserOptions.databaseKind);
|
156
|
+
expect(pageResource?.kind).toBe(exports.parserOptions.webPageKind);
|
153
157
|
expect(apiResource?.spec).toEqual(clientResource?.spec);
|
154
158
|
expect(dbResource?.spec.source.value).toContain('type Entity');
|
155
159
|
const serviceBlockInstance = result.plan.spec.blocks[0];
|
@@ -162,18 +166,33 @@ describe('event-parser', () => {
|
|
162
166
|
expect(result.plan.spec.connections[0].provider.blockId).toBe(serviceBlockInstance.id);
|
163
167
|
expect(result.plan.spec.connections[0].provider.resourceName).toBe(apiResource?.metadata.name);
|
164
168
|
});
|
165
|
-
it('it will split api into correct provider', () => {
|
169
|
+
it('it will split api into correct provider', async () => {
|
166
170
|
const events = simple_blog_events_json_1.default;
|
167
|
-
const parser = new event_parser_1.StormEventParser(parserOptions);
|
168
|
-
|
169
|
-
|
171
|
+
const parser = new event_parser_1.StormEventParser(exports.parserOptions);
|
172
|
+
for (const event of events) {
|
173
|
+
await parser.processEvent('kapeta', event);
|
174
|
+
}
|
175
|
+
const result = await parser.toResult('kapeta');
|
170
176
|
const blogService = result.blocks.find((block) => block.aiName === 'blog-service');
|
171
177
|
expect(blogService).toBeDefined();
|
172
178
|
expect(blogService?.content).toBeDefined();
|
173
179
|
const apiProviders = blogService?.content?.spec?.providers?.filter((provider) => provider.kind === 'kapeta/block-type-api:local');
|
174
180
|
expect(apiProviders).toBeDefined();
|
175
181
|
expect(apiProviders.length).toBe(2);
|
176
|
-
expect(apiProviders[
|
177
|
-
expect(apiProviders[
|
182
|
+
expect(apiProviders['0'].spec.source.value).not.toBe('');
|
183
|
+
expect(apiProviders['1'].spec.source.value).not.toBe('');
|
184
|
+
});
|
185
|
+
it('predefined components', async () => {
|
186
|
+
const events = predefined_user_events_json_1.default;
|
187
|
+
const parser = new event_parser_1.StormEventParser(exports.parserOptions);
|
188
|
+
for (const event of events) {
|
189
|
+
await parser.processEvent('kapeta', event);
|
190
|
+
}
|
191
|
+
const result = await parser.toResult('kapeta');
|
192
|
+
expect(result.blocks.length).toBe(1);
|
193
|
+
expect(result.blocks[0].content.metadata.title).toBe('user-service');
|
194
|
+
expect(result.blocks[0].content.metadata.title).toBe('user-service');
|
195
|
+
expect(result.blocks[0].content.metadata.name).toBe('kapeta/user-service');
|
196
|
+
expect(result.blocks[0].archetype).toBeDefined();
|
178
197
|
});
|
179
198
|
});
|
@@ -0,0 +1,13 @@
|
|
1
|
+
[
|
2
|
+
{
|
3
|
+
"type": "CREATE_BLOCK",
|
4
|
+
"reason": "Handles user registration, authentication, and authorization.",
|
5
|
+
"payload": {
|
6
|
+
"name": "user-service",
|
7
|
+
"resources": [],
|
8
|
+
"type": "BACKEND",
|
9
|
+
"archetype": "USER_SERVICE"
|
10
|
+
},
|
11
|
+
"created": 1720597268038
|
12
|
+
}
|
13
|
+
]
|
@@ -0,0 +1,12 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright 2023 Kapeta Inc.
|
3
|
+
* SPDX-License-Identifier: BUSL-1.1
|
4
|
+
*/
|
5
|
+
import { PredefinedBlock } from './predefined';
|
6
|
+
import { GeneratedResult } from '@kapeta/codegen';
|
7
|
+
export declare class Archetype {
|
8
|
+
cloneRepository(predefinedBlock: PredefinedBlock, targetDir: string, basePath: string): Promise<GeneratedResult>;
|
9
|
+
private copyToBasePathAndRemoveDownload;
|
10
|
+
private convertToGeneratedResult;
|
11
|
+
private downloadRepo;
|
12
|
+
}
|
@@ -0,0 +1,98 @@
|
|
1
|
+
"use strict";
|
2
|
+
/**
|
3
|
+
* Copyright 2023 Kapeta Inc.
|
4
|
+
* SPDX-License-Identifier: BUSL-1.1
|
5
|
+
*/
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
7
|
+
if (k2 === undefined) k2 = k;
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
11
|
+
}
|
12
|
+
Object.defineProperty(o, k2, desc);
|
13
|
+
}) : (function(o, m, k, k2) {
|
14
|
+
if (k2 === undefined) k2 = k;
|
15
|
+
o[k2] = m[k];
|
16
|
+
}));
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
19
|
+
}) : function(o, v) {
|
20
|
+
o["default"] = v;
|
21
|
+
});
|
22
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
23
|
+
if (mod && mod.__esModule) return mod;
|
24
|
+
var result = {};
|
25
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
26
|
+
__setModuleDefault(result, mod);
|
27
|
+
return result;
|
28
|
+
};
|
29
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
30
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
31
|
+
};
|
32
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
33
|
+
exports.Archetype = void 0;
|
34
|
+
const download_git_repo_1 = __importDefault(require("download-git-repo"));
|
35
|
+
const fs = __importStar(require("node:fs"));
|
36
|
+
const path_1 = __importDefault(require("path"));
|
37
|
+
class Archetype {
|
38
|
+
async cloneRepository(predefinedBlock, targetDir, basePath) {
|
39
|
+
const downloadDirectory = `${targetDir}/download`;
|
40
|
+
await this.downloadRepo(predefinedBlock.getGitRepo().owner + '/' + predefinedBlock.getGitRepo().repo, downloadDirectory);
|
41
|
+
await this.copyToBasePathAndRemoveDownload(downloadDirectory, predefinedBlock.getGitRepo().path, basePath);
|
42
|
+
return this.convertToGeneratedResult(basePath);
|
43
|
+
}
|
44
|
+
async copyToBasePathAndRemoveDownload(downloadDirectory, subPath, targetDir) {
|
45
|
+
const fullSourcePath = path_1.default.resolve(`${downloadDirectory}/${subPath}`);
|
46
|
+
const fullDestinationPath = path_1.default.resolve(targetDir);
|
47
|
+
try {
|
48
|
+
await fs.promises.cp(fullSourcePath, fullDestinationPath, { recursive: true });
|
49
|
+
console.log(`Directory copied: ${fullSourcePath} -> ${fullDestinationPath}`);
|
50
|
+
}
|
51
|
+
catch (err) {
|
52
|
+
console.error(`Error copying directory: ${err}`);
|
53
|
+
}
|
54
|
+
finally {
|
55
|
+
await fs.promises.rm(downloadDirectory, { recursive: true, force: true });
|
56
|
+
}
|
57
|
+
}
|
58
|
+
async convertToGeneratedResult(basePath) {
|
59
|
+
const generatedFiles = [];
|
60
|
+
async function traverse(currentPath) {
|
61
|
+
const files = await fs.promises.readdir(currentPath, { withFileTypes: true });
|
62
|
+
for (const file of files) {
|
63
|
+
const fullPath = path_1.default.join(currentPath, file.name);
|
64
|
+
if (file.isFile()) {
|
65
|
+
const stats = await fs.promises.stat(fullPath);
|
66
|
+
const mode = stats.mode.toString(8);
|
67
|
+
const permissions = stats.mode.toString(8).slice(-3);
|
68
|
+
const content = await fs.promises.readFile(fullPath);
|
69
|
+
const generatedFile = {
|
70
|
+
filename: file.name,
|
71
|
+
content: content.toString(),
|
72
|
+
mode: mode,
|
73
|
+
permissions: permissions,
|
74
|
+
};
|
75
|
+
generatedFiles.push(generatedFile);
|
76
|
+
}
|
77
|
+
else if (file.isDirectory()) {
|
78
|
+
await traverse(fullPath);
|
79
|
+
}
|
80
|
+
}
|
81
|
+
}
|
82
|
+
await traverse(basePath);
|
83
|
+
return { files: generatedFiles };
|
84
|
+
}
|
85
|
+
downloadRepo(url, dest) {
|
86
|
+
return new Promise((resolve, reject) => {
|
87
|
+
(0, download_git_repo_1.default)(url, dest, function (err) {
|
88
|
+
if (err) {
|
89
|
+
reject(err);
|
90
|
+
}
|
91
|
+
else {
|
92
|
+
resolve();
|
93
|
+
}
|
94
|
+
});
|
95
|
+
});
|
96
|
+
}
|
97
|
+
}
|
98
|
+
exports.Archetype = Archetype;
|
@@ -26,6 +26,7 @@ export declare class StormCodegen {
|
|
26
26
|
* Generates the code for a block and sends it to the AI
|
27
27
|
*/
|
28
28
|
private processBlockCode;
|
29
|
+
private emitBlockStatusDone;
|
29
30
|
private emitBlockStatus;
|
30
31
|
private verifyAndFixCode;
|
31
32
|
private tryToFixFile;
|
@@ -55,5 +56,6 @@ export declare class StormCodegen {
|
|
55
56
|
* Generates the code using codegen for a given block.
|
56
57
|
*/
|
57
58
|
private generateBlock;
|
59
|
+
private cloneArchetype;
|
58
60
|
abort(): void;
|
59
61
|
}
|
@@ -44,6 +44,9 @@ const path_2 = __importStar(require("path"));
|
|
44
44
|
const node_os_1 = __importDefault(require("node:os"));
|
45
45
|
const fs_1 = require("fs");
|
46
46
|
const yaml_1 = __importDefault(require("yaml"));
|
47
|
+
const predefined_1 = require("./predefined");
|
48
|
+
const archetype_1 = require("./archetype");
|
49
|
+
const lodash_1 = __importDefault(require("lodash"));
|
47
50
|
const SIMULATED_DELAY = 1000;
|
48
51
|
const ENABLE_SIMULATED_DELAY = false;
|
49
52
|
class SimulatedFileDelay {
|
@@ -106,7 +109,12 @@ class StormCodegen {
|
|
106
109
|
}
|
107
110
|
async process() {
|
108
111
|
const promises = this.blocks.map((block) => {
|
109
|
-
|
112
|
+
if (block.archetype) {
|
113
|
+
return this.cloneArchetype(block);
|
114
|
+
}
|
115
|
+
else {
|
116
|
+
return this.processBlockCode(block);
|
117
|
+
}
|
110
118
|
});
|
111
119
|
await Promise.all(promises);
|
112
120
|
this.out.end();
|
@@ -391,11 +399,14 @@ class StormCodegen {
|
|
391
399
|
const blockRef = block.uri;
|
392
400
|
this.emitBlockStatus(blockUri, block.aiName, events_1.StormEventBlockStatusType.QA);
|
393
401
|
/* TODO: temporarily disabled - enable again when codegen is more stable
|
394
|
-
|
395
|
-
|
396
|
-
|
402
|
+
const filesToBeFixed = serviceFiles.concat(contextFiles).concat(screenFilesConverted);
|
403
|
+
const codeGenerator = new BlockCodeGenerator(blockDefinition);
|
404
|
+
*/
|
397
405
|
this.emitBlockStatus(blockUri, block.aiName, events_1.StormEventBlockStatusType.BUILDING);
|
398
406
|
// await this.verifyAndFixCode(blockUri, block.aiName, codeGenerator, basePath, filesToBeFixed, allFiles);
|
407
|
+
this.emitBlockStatusDone(basePath, block, blockRef);
|
408
|
+
}
|
409
|
+
emitBlockStatusDone(basePath, block, blockRef) {
|
399
410
|
this.out.emit('data', {
|
400
411
|
type: 'BLOCK_READY',
|
401
412
|
reason: 'Block ready',
|
@@ -784,6 +795,19 @@ class StormCodegen {
|
|
784
795
|
new codegen_1.CodeWriter(basePath).write(generatedResult);
|
785
796
|
return generatedResult;
|
786
797
|
}
|
798
|
+
async cloneArchetype(block) {
|
799
|
+
const predefinedBlock = predefined_1.PREDEFINED_BLOCKS.get(block.archetype);
|
800
|
+
let blockDefinition = await predefinedBlock.getBlockDefinition();
|
801
|
+
const kapetaURI = new nodejs_utils_1.KapetaURI(block.uri);
|
802
|
+
lodash_1.default.set(blockDefinition, ['metadata', 'name'], kapetaURI.fullName);
|
803
|
+
lodash_1.default.set(blockDefinition, ['metadata', 'title'], kapetaURI.name);
|
804
|
+
const basePath = this.getBasePath(blockDefinition.metadata.name);
|
805
|
+
let archetype = new archetype_1.Archetype();
|
806
|
+
const generatedResult = await archetype.cloneRepository(predefinedBlock, this.tmpDir, basePath);
|
807
|
+
await this.emitStaticFiles(kapetaURI, block.aiName, this.toStormFiles(generatedResult));
|
808
|
+
const blockRef = block.uri;
|
809
|
+
this.emitBlockStatusDone(basePath, block, blockRef);
|
810
|
+
}
|
787
811
|
abort() {
|
788
812
|
this.out.abort();
|
789
813
|
}
|
@@ -9,6 +9,7 @@ export interface BlockDefinitionInfo {
|
|
9
9
|
uri: string;
|
10
10
|
content: BlockDefinition;
|
11
11
|
aiName: string;
|
12
|
+
archetype?: string;
|
12
13
|
}
|
13
14
|
export interface StormDefinitions {
|
14
15
|
plan: Plan;
|
@@ -61,18 +62,20 @@ export declare class StormEventParser {
|
|
61
62
|
/**
|
62
63
|
* Builds plan and block definitions - and enriches events with relevant refs and ids
|
63
64
|
*/
|
64
|
-
processEvent(handle: string, evt: StormEvent): StormDefinitions
|
65
|
+
processEvent(handle: string, evt: StormEvent): Promise<StormDefinitions>;
|
65
66
|
getEvents(): StormEvent[];
|
66
67
|
isValid(): boolean;
|
67
68
|
getError(): string;
|
68
|
-
toResult(handle: string): StormDefinitions
|
69
|
-
toBlockDefinitions(handle: string): {
|
69
|
+
toResult(handle: string): Promise<StormDefinitions>;
|
70
|
+
toBlockDefinitions(handle: string): Promise<{
|
70
71
|
[key: string]: BlockDefinitionInfo;
|
71
|
-
}
|
72
|
+
}>;
|
73
|
+
private createBlockDefinitionInfo;
|
72
74
|
private toResourceKind;
|
73
75
|
private toBlockKind;
|
74
76
|
private toConnectionMapping;
|
75
77
|
private toPortType;
|
76
78
|
private toBlockTarget;
|
77
79
|
private toBlockTargetKind;
|
80
|
+
private resolveArchetypeBlockDefinition;
|
78
81
|
}
|