@kapeta/local-cluster-service 0.50.0 → 0.51.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 +7 -0
- package/dist/cjs/src/storm/codegen.d.ts +3 -2
- package/dist/cjs/src/storm/codegen.js +168 -25
- package/dist/cjs/src/storm/event-parser.js +0 -4
- package/dist/cjs/src/storm/events.d.ts +25 -9
- package/dist/esm/src/storm/codegen.d.ts +3 -2
- package/dist/esm/src/storm/codegen.js +168 -25
- package/dist/esm/src/storm/event-parser.js +0 -4
- package/dist/esm/src/storm/events.d.ts +25 -9
- package/package.json +1 -1
- package/src/storm/codegen.ts +189 -32
- package/src/storm/event-parser.ts +0 -5
- package/src/storm/events.ts +32 -9
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
# [0.51.0](https://github.com/kapetacom/local-cluster-service/compare/v0.50.0...v0.51.0) (2024-06-05)
|
2
|
+
|
3
|
+
|
4
|
+
### Features
|
5
|
+
|
6
|
+
* Handle file streaming events ([#164](https://github.com/kapetacom/local-cluster-service/issues/164)) ([441847a](https://github.com/kapetacom/local-cluster-service/commit/441847a12d20826d843e62627ea9bf584043effb))
|
7
|
+
|
1
8
|
# [0.50.0](https://github.com/kapetacom/local-cluster-service/compare/v0.49.0...v0.50.0) (2024-06-05)
|
2
9
|
|
3
10
|
|
@@ -18,7 +18,8 @@ export declare class StormCodegen {
|
|
18
18
|
getStream(): StormStream;
|
19
19
|
private handleTemplateFileOutput;
|
20
20
|
private handleUiOutput;
|
21
|
-
private
|
21
|
+
private handleFileEvents;
|
22
|
+
private handleFileDoneOutput;
|
22
23
|
private getBasePath;
|
23
24
|
/**
|
24
25
|
* Generates the code for a block and sends it to the AI
|
@@ -34,7 +35,7 @@ export declare class StormCodegen {
|
|
34
35
|
/**
|
35
36
|
* Emits the text-based files to the stream
|
36
37
|
*/
|
37
|
-
private
|
38
|
+
private emitStaticFiles;
|
38
39
|
private emitFile;
|
39
40
|
/**
|
40
41
|
* Sends the template to the AI and processes the response
|
@@ -42,6 +42,49 @@ const path_1 = __importStar(require("path"));
|
|
42
42
|
const node_os_1 = __importDefault(require("node:os"));
|
43
43
|
const fs_1 = require("fs");
|
44
44
|
const path_2 = __importDefault(require("path"));
|
45
|
+
const SIMULATED_DELAY = 1000;
|
46
|
+
class SimulatedFileDelay {
|
47
|
+
file;
|
48
|
+
stream;
|
49
|
+
constructor(file, stream) {
|
50
|
+
this.file = file;
|
51
|
+
this.stream = stream;
|
52
|
+
}
|
53
|
+
async start() {
|
54
|
+
const commonPayload = {
|
55
|
+
filename: this.file.payload.filename,
|
56
|
+
path: this.file.payload.path,
|
57
|
+
blockName: this.file.payload.blockName,
|
58
|
+
blockRef: this.file.payload.blockRef,
|
59
|
+
instanceId: this.file.payload.instanceId,
|
60
|
+
};
|
61
|
+
this.stream.emit('data', {
|
62
|
+
type: 'FILE_START',
|
63
|
+
created: Date.now(),
|
64
|
+
reason: 'File start',
|
65
|
+
payload: commonPayload,
|
66
|
+
});
|
67
|
+
const lines = this.file.payload.content.split('\n');
|
68
|
+
const delayPerLine = SIMULATED_DELAY / lines.length;
|
69
|
+
for (const line of lines) {
|
70
|
+
await new Promise((resolve) => {
|
71
|
+
setTimeout(() => {
|
72
|
+
this.stream.emit('data', {
|
73
|
+
type: 'FILE_CHUNK',
|
74
|
+
created: Date.now(),
|
75
|
+
reason: 'File chunk',
|
76
|
+
payload: {
|
77
|
+
...commonPayload,
|
78
|
+
content: line,
|
79
|
+
},
|
80
|
+
});
|
81
|
+
resolve();
|
82
|
+
}, delayPerLine);
|
83
|
+
});
|
84
|
+
}
|
85
|
+
this.stream.emit('data', this.file);
|
86
|
+
}
|
87
|
+
}
|
45
88
|
class StormCodegen {
|
46
89
|
userPrompt;
|
47
90
|
blocks;
|
@@ -70,42 +113,132 @@ class StormCodegen {
|
|
70
113
|
return this.out;
|
71
114
|
}
|
72
115
|
handleTemplateFileOutput(blockUri, aiName, template, data) {
|
116
|
+
if (this.handleFileEvents(blockUri, aiName, data)) {
|
117
|
+
return;
|
118
|
+
}
|
73
119
|
switch (data.type) {
|
74
|
-
case '
|
120
|
+
case 'FILE_DONE':
|
75
121
|
template.filename = data.payload.filename;
|
76
122
|
template.content = data.payload.content;
|
77
|
-
|
123
|
+
this.handleFileDoneOutput(blockUri, aiName, data);
|
124
|
+
break;
|
78
125
|
}
|
79
126
|
}
|
80
|
-
handleUiOutput(blockUri,
|
127
|
+
handleUiOutput(blockUri, blockName, data) {
|
128
|
+
const blockRef = blockUri.toNormalizedString();
|
129
|
+
const instanceId = event_parser_1.StormEventParser.toInstanceIdFromRef(blockRef);
|
130
|
+
if (this.handleFileEvents(blockUri, blockName, data)) {
|
131
|
+
return;
|
132
|
+
}
|
81
133
|
switch (data.type) {
|
82
134
|
case 'SCREEN':
|
83
|
-
const ref = blockUri.toNormalizedString();
|
84
135
|
this.out.emit('data', {
|
85
136
|
type: 'SCREEN',
|
86
137
|
reason: data.reason,
|
87
138
|
created: Date.now(),
|
88
139
|
payload: {
|
89
140
|
...data.payload,
|
90
|
-
blockName
|
91
|
-
blockRef
|
92
|
-
instanceId
|
141
|
+
blockName,
|
142
|
+
blockRef,
|
143
|
+
instanceId,
|
144
|
+
},
|
145
|
+
});
|
146
|
+
break;
|
147
|
+
case 'FILE_START':
|
148
|
+
case 'FILE_CHUNK_RESET':
|
149
|
+
this.out.emit('data', {
|
150
|
+
...data,
|
151
|
+
payload: {
|
152
|
+
...data.payload,
|
153
|
+
blockName,
|
154
|
+
blockRef,
|
155
|
+
instanceId,
|
93
156
|
},
|
94
157
|
});
|
95
|
-
|
96
|
-
|
158
|
+
break;
|
159
|
+
case 'FILE_CHUNK':
|
160
|
+
this.out.emit('data', {
|
161
|
+
...data,
|
162
|
+
payload: {
|
163
|
+
...data.payload,
|
164
|
+
blockName,
|
165
|
+
blockRef,
|
166
|
+
instanceId,
|
167
|
+
},
|
168
|
+
});
|
169
|
+
break;
|
170
|
+
case 'FILE_STATE':
|
171
|
+
this.out.emit('data', {
|
172
|
+
...data,
|
173
|
+
payload: {
|
174
|
+
...data.payload,
|
175
|
+
blockName,
|
176
|
+
blockRef,
|
177
|
+
instanceId,
|
178
|
+
},
|
179
|
+
});
|
180
|
+
break;
|
181
|
+
case 'FILE_DONE':
|
182
|
+
this.handleFileDoneOutput(blockUri, blockName, data);
|
183
|
+
break;
|
97
184
|
}
|
98
185
|
}
|
99
|
-
|
186
|
+
handleFileEvents(blockUri, blockName, data) {
|
187
|
+
const blockRef = blockUri.toNormalizedString();
|
188
|
+
const instanceId = event_parser_1.StormEventParser.toInstanceIdFromRef(blockRef);
|
189
|
+
const basePath = this.getBasePath(blockUri.fullName);
|
100
190
|
switch (data.type) {
|
101
|
-
case '
|
191
|
+
case 'FILE_START':
|
192
|
+
case 'FILE_CHUNK_RESET':
|
193
|
+
this.out.emit('data', {
|
194
|
+
...data,
|
195
|
+
payload: {
|
196
|
+
...data.payload,
|
197
|
+
path: (0, path_1.join)(basePath, data.payload.filename),
|
198
|
+
blockName,
|
199
|
+
blockRef,
|
200
|
+
instanceId,
|
201
|
+
},
|
202
|
+
});
|
203
|
+
return true;
|
204
|
+
case 'FILE_CHUNK':
|
205
|
+
this.out.emit('data', {
|
206
|
+
...data,
|
207
|
+
payload: {
|
208
|
+
...data.payload,
|
209
|
+
path: (0, path_1.join)(basePath, data.payload.filename),
|
210
|
+
blockName,
|
211
|
+
blockRef,
|
212
|
+
instanceId,
|
213
|
+
},
|
214
|
+
});
|
215
|
+
return true;
|
216
|
+
case 'FILE_STATE':
|
217
|
+
this.out.emit('data', {
|
218
|
+
...data,
|
219
|
+
payload: {
|
220
|
+
...data.payload,
|
221
|
+
path: (0, path_1.join)(basePath, data.payload.filename),
|
222
|
+
blockName,
|
223
|
+
blockRef,
|
224
|
+
instanceId,
|
225
|
+
},
|
226
|
+
});
|
227
|
+
return true;
|
228
|
+
}
|
229
|
+
return false;
|
230
|
+
}
|
231
|
+
handleFileDoneOutput(blockUri, aiName, data) {
|
232
|
+
switch (data.type) {
|
233
|
+
case 'FILE_DONE':
|
102
234
|
const ref = blockUri.toNormalizedString();
|
103
235
|
this.emitFile(blockUri, aiName, data.payload.filename, data.payload.content, data.reason);
|
104
236
|
return {
|
105
|
-
type: '
|
237
|
+
type: 'FILE_DONE',
|
106
238
|
created: Date.now(),
|
107
239
|
payload: {
|
108
240
|
filename: data.payload.filename,
|
241
|
+
path: (0, path_1.join)(this.getBasePath(blockUri.fullName), data.payload.filename),
|
109
242
|
content: data.payload.content,
|
110
243
|
blockRef: ref,
|
111
244
|
instanceId: event_parser_1.StormEventParser.toInstanceIdFromRef(ref),
|
@@ -130,7 +263,7 @@ class StormCodegen {
|
|
130
263
|
}
|
131
264
|
const allFiles = this.toStormFiles(generatedResult);
|
132
265
|
// Send all the non-ai files to the stream
|
133
|
-
this.
|
266
|
+
await this.emitStaticFiles((0, nodejs_utils_1.parseKapetaUri)(block.uri), block.aiName, allFiles);
|
134
267
|
if (this.isAborted()) {
|
135
268
|
return;
|
136
269
|
}
|
@@ -285,8 +418,8 @@ class StormCodegen {
|
|
285
418
|
/**
|
286
419
|
* Emits the text-based files to the stream
|
287
420
|
*/
|
288
|
-
|
289
|
-
files.
|
421
|
+
async emitStaticFiles(uri, aiName, files) {
|
422
|
+
const promises = files.map((file) => {
|
290
423
|
if (!file.content || typeof file.content !== 'string') {
|
291
424
|
return;
|
292
425
|
}
|
@@ -300,14 +433,30 @@ class StormCodegen {
|
|
300
433
|
// They will need to be implemented by the AI
|
301
434
|
return;
|
302
435
|
}
|
303
|
-
this.
|
436
|
+
const basePath = this.getBasePath(uri.fullName);
|
437
|
+
const ref = uri.toNormalizedString();
|
438
|
+
const fileEvent = {
|
439
|
+
type: 'FILE_DONE',
|
440
|
+
reason: 'File generated',
|
441
|
+
created: Date.now(),
|
442
|
+
payload: {
|
443
|
+
filename: file.filename,
|
444
|
+
path: (0, path_1.join)(basePath, file.filename),
|
445
|
+
content: file.content,
|
446
|
+
blockName: aiName,
|
447
|
+
blockRef: ref,
|
448
|
+
instanceId: event_parser_1.StormEventParser.toInstanceIdFromRef(ref),
|
449
|
+
},
|
450
|
+
};
|
451
|
+
return new SimulatedFileDelay(fileEvent, this.out).start();
|
304
452
|
});
|
453
|
+
return Promise.all(promises);
|
305
454
|
}
|
306
455
|
emitFile(uri, blockName, filename, content, reason = 'File generated') {
|
307
456
|
const basePath = this.getBasePath(uri.fullName);
|
308
457
|
const ref = uri.toNormalizedString();
|
309
458
|
this.out.emit('data', {
|
310
|
-
type: '
|
459
|
+
type: 'FILE_DONE',
|
311
460
|
reason,
|
312
461
|
created: Date.now(),
|
313
462
|
payload: {
|
@@ -330,21 +479,15 @@ class StormCodegen {
|
|
330
479
|
template: templateFile,
|
331
480
|
prompt: this.userPrompt,
|
332
481
|
});
|
333
|
-
const files = [];
|
334
482
|
this.out.on('aborted', () => {
|
335
483
|
stream.abort();
|
336
484
|
});
|
337
485
|
stream.on('data', (evt) => {
|
338
|
-
|
339
|
-
if (file) {
|
340
|
-
files.push(file);
|
341
|
-
}
|
486
|
+
this.handleTemplateFileOutput(blockUri, aiName, templateFile, evt);
|
342
487
|
});
|
343
488
|
await stream.waitForDone();
|
344
|
-
return files;
|
345
489
|
});
|
346
|
-
|
347
|
-
return fileChunks.flat();
|
490
|
+
await Promise.all(promises);
|
348
491
|
}
|
349
492
|
/**
|
350
493
|
* Converts the generated files to a format that can be sent to the AI
|
@@ -211,10 +211,6 @@ class StormEventParser {
|
|
211
211
|
evt.payload.toBlockId = StormEventParser.toInstanceId(handle, evt.payload.toComponent);
|
212
212
|
this.connections.push(evt.payload);
|
213
213
|
break;
|
214
|
-
default:
|
215
|
-
case 'SCREEN_CANDIDATE':
|
216
|
-
case 'FILE':
|
217
|
-
break;
|
218
214
|
}
|
219
215
|
return this.toResult(handle);
|
220
216
|
}
|
@@ -150,19 +150,35 @@ export interface StormEventScreenCandidate {
|
|
150
150
|
url: string;
|
151
151
|
};
|
152
152
|
}
|
153
|
-
export interface
|
154
|
-
|
153
|
+
export interface StormEventFileBasePayload {
|
154
|
+
filename: string;
|
155
|
+
path: string;
|
156
|
+
blockName: string;
|
157
|
+
blockRef: string;
|
158
|
+
instanceId: string;
|
159
|
+
}
|
160
|
+
export interface StormEventFileBase {
|
161
|
+
type: string;
|
155
162
|
reason: string;
|
156
163
|
created: number;
|
157
|
-
payload:
|
158
|
-
|
159
|
-
|
164
|
+
payload: StormEventFileBasePayload;
|
165
|
+
}
|
166
|
+
export interface StormEventFileLogical extends StormEventFileBase {
|
167
|
+
type: 'FILE_START' | 'FILE_CHUNK_RESET';
|
168
|
+
}
|
169
|
+
export interface StormEventFileState extends StormEventFileBase {
|
170
|
+
type: 'FILE_STATE';
|
171
|
+
payload: StormEventFileBasePayload & {
|
172
|
+
state: string;
|
173
|
+
};
|
174
|
+
}
|
175
|
+
export interface StormEventFileContent extends StormEventFileBase {
|
176
|
+
type: 'FILE_DONE' | 'FILE_CHUNK';
|
177
|
+
payload: StormEventFileBasePayload & {
|
160
178
|
content: string;
|
161
|
-
blockName: string;
|
162
|
-
blockRef: string;
|
163
|
-
instanceId: string;
|
164
179
|
};
|
165
180
|
}
|
181
|
+
export type StormEventFile = StormEventFileLogical | StormEventFileState | StormEventFileContent;
|
166
182
|
export interface StormEventBlockReady {
|
167
183
|
type: 'BLOCK_READY';
|
168
184
|
reason: string;
|
@@ -196,4 +212,4 @@ export interface StormEventPhases {
|
|
196
212
|
phaseType: StormEventPhaseType;
|
197
213
|
};
|
198
214
|
}
|
199
|
-
export type StormEvent = StormEventCreateBlock | StormEventCreateConnection | StormEventCreatePlanProperties | StormEventInvalidResponse | StormEventPlanRetry | StormEventCreateDSL | StormEventCreateDSLResource | StormEventError | StormEventScreen | StormEventScreenCandidate |
|
215
|
+
export type StormEvent = StormEventCreateBlock | StormEventCreateConnection | StormEventCreatePlanProperties | StormEventInvalidResponse | StormEventPlanRetry | StormEventCreateDSL | StormEventCreateDSLResource | StormEventError | StormEventScreen | StormEventScreenCandidate | StormEventFileLogical | StormEventFileState | StormEventFileContent | StormEventDone | StormEventDefinitionChange | StormEventErrorClassifier | StormEventCodeFix | StormEventBlockReady | StormEventPhases;
|
@@ -18,7 +18,8 @@ export declare class StormCodegen {
|
|
18
18
|
getStream(): StormStream;
|
19
19
|
private handleTemplateFileOutput;
|
20
20
|
private handleUiOutput;
|
21
|
-
private
|
21
|
+
private handleFileEvents;
|
22
|
+
private handleFileDoneOutput;
|
22
23
|
private getBasePath;
|
23
24
|
/**
|
24
25
|
* Generates the code for a block and sends it to the AI
|
@@ -34,7 +35,7 @@ export declare class StormCodegen {
|
|
34
35
|
/**
|
35
36
|
* Emits the text-based files to the stream
|
36
37
|
*/
|
37
|
-
private
|
38
|
+
private emitStaticFiles;
|
38
39
|
private emitFile;
|
39
40
|
/**
|
40
41
|
* Sends the template to the AI and processes the response
|
@@ -42,6 +42,49 @@ const path_1 = __importStar(require("path"));
|
|
42
42
|
const node_os_1 = __importDefault(require("node:os"));
|
43
43
|
const fs_1 = require("fs");
|
44
44
|
const path_2 = __importDefault(require("path"));
|
45
|
+
const SIMULATED_DELAY = 1000;
|
46
|
+
class SimulatedFileDelay {
|
47
|
+
file;
|
48
|
+
stream;
|
49
|
+
constructor(file, stream) {
|
50
|
+
this.file = file;
|
51
|
+
this.stream = stream;
|
52
|
+
}
|
53
|
+
async start() {
|
54
|
+
const commonPayload = {
|
55
|
+
filename: this.file.payload.filename,
|
56
|
+
path: this.file.payload.path,
|
57
|
+
blockName: this.file.payload.blockName,
|
58
|
+
blockRef: this.file.payload.blockRef,
|
59
|
+
instanceId: this.file.payload.instanceId,
|
60
|
+
};
|
61
|
+
this.stream.emit('data', {
|
62
|
+
type: 'FILE_START',
|
63
|
+
created: Date.now(),
|
64
|
+
reason: 'File start',
|
65
|
+
payload: commonPayload,
|
66
|
+
});
|
67
|
+
const lines = this.file.payload.content.split('\n');
|
68
|
+
const delayPerLine = SIMULATED_DELAY / lines.length;
|
69
|
+
for (const line of lines) {
|
70
|
+
await new Promise((resolve) => {
|
71
|
+
setTimeout(() => {
|
72
|
+
this.stream.emit('data', {
|
73
|
+
type: 'FILE_CHUNK',
|
74
|
+
created: Date.now(),
|
75
|
+
reason: 'File chunk',
|
76
|
+
payload: {
|
77
|
+
...commonPayload,
|
78
|
+
content: line,
|
79
|
+
},
|
80
|
+
});
|
81
|
+
resolve();
|
82
|
+
}, delayPerLine);
|
83
|
+
});
|
84
|
+
}
|
85
|
+
this.stream.emit('data', this.file);
|
86
|
+
}
|
87
|
+
}
|
45
88
|
class StormCodegen {
|
46
89
|
userPrompt;
|
47
90
|
blocks;
|
@@ -70,42 +113,132 @@ class StormCodegen {
|
|
70
113
|
return this.out;
|
71
114
|
}
|
72
115
|
handleTemplateFileOutput(blockUri, aiName, template, data) {
|
116
|
+
if (this.handleFileEvents(blockUri, aiName, data)) {
|
117
|
+
return;
|
118
|
+
}
|
73
119
|
switch (data.type) {
|
74
|
-
case '
|
120
|
+
case 'FILE_DONE':
|
75
121
|
template.filename = data.payload.filename;
|
76
122
|
template.content = data.payload.content;
|
77
|
-
|
123
|
+
this.handleFileDoneOutput(blockUri, aiName, data);
|
124
|
+
break;
|
78
125
|
}
|
79
126
|
}
|
80
|
-
handleUiOutput(blockUri,
|
127
|
+
handleUiOutput(blockUri, blockName, data) {
|
128
|
+
const blockRef = blockUri.toNormalizedString();
|
129
|
+
const instanceId = event_parser_1.StormEventParser.toInstanceIdFromRef(blockRef);
|
130
|
+
if (this.handleFileEvents(blockUri, blockName, data)) {
|
131
|
+
return;
|
132
|
+
}
|
81
133
|
switch (data.type) {
|
82
134
|
case 'SCREEN':
|
83
|
-
const ref = blockUri.toNormalizedString();
|
84
135
|
this.out.emit('data', {
|
85
136
|
type: 'SCREEN',
|
86
137
|
reason: data.reason,
|
87
138
|
created: Date.now(),
|
88
139
|
payload: {
|
89
140
|
...data.payload,
|
90
|
-
blockName
|
91
|
-
blockRef
|
92
|
-
instanceId
|
141
|
+
blockName,
|
142
|
+
blockRef,
|
143
|
+
instanceId,
|
144
|
+
},
|
145
|
+
});
|
146
|
+
break;
|
147
|
+
case 'FILE_START':
|
148
|
+
case 'FILE_CHUNK_RESET':
|
149
|
+
this.out.emit('data', {
|
150
|
+
...data,
|
151
|
+
payload: {
|
152
|
+
...data.payload,
|
153
|
+
blockName,
|
154
|
+
blockRef,
|
155
|
+
instanceId,
|
93
156
|
},
|
94
157
|
});
|
95
|
-
|
96
|
-
|
158
|
+
break;
|
159
|
+
case 'FILE_CHUNK':
|
160
|
+
this.out.emit('data', {
|
161
|
+
...data,
|
162
|
+
payload: {
|
163
|
+
...data.payload,
|
164
|
+
blockName,
|
165
|
+
blockRef,
|
166
|
+
instanceId,
|
167
|
+
},
|
168
|
+
});
|
169
|
+
break;
|
170
|
+
case 'FILE_STATE':
|
171
|
+
this.out.emit('data', {
|
172
|
+
...data,
|
173
|
+
payload: {
|
174
|
+
...data.payload,
|
175
|
+
blockName,
|
176
|
+
blockRef,
|
177
|
+
instanceId,
|
178
|
+
},
|
179
|
+
});
|
180
|
+
break;
|
181
|
+
case 'FILE_DONE':
|
182
|
+
this.handleFileDoneOutput(blockUri, blockName, data);
|
183
|
+
break;
|
97
184
|
}
|
98
185
|
}
|
99
|
-
|
186
|
+
handleFileEvents(blockUri, blockName, data) {
|
187
|
+
const blockRef = blockUri.toNormalizedString();
|
188
|
+
const instanceId = event_parser_1.StormEventParser.toInstanceIdFromRef(blockRef);
|
189
|
+
const basePath = this.getBasePath(blockUri.fullName);
|
100
190
|
switch (data.type) {
|
101
|
-
case '
|
191
|
+
case 'FILE_START':
|
192
|
+
case 'FILE_CHUNK_RESET':
|
193
|
+
this.out.emit('data', {
|
194
|
+
...data,
|
195
|
+
payload: {
|
196
|
+
...data.payload,
|
197
|
+
path: (0, path_1.join)(basePath, data.payload.filename),
|
198
|
+
blockName,
|
199
|
+
blockRef,
|
200
|
+
instanceId,
|
201
|
+
},
|
202
|
+
});
|
203
|
+
return true;
|
204
|
+
case 'FILE_CHUNK':
|
205
|
+
this.out.emit('data', {
|
206
|
+
...data,
|
207
|
+
payload: {
|
208
|
+
...data.payload,
|
209
|
+
path: (0, path_1.join)(basePath, data.payload.filename),
|
210
|
+
blockName,
|
211
|
+
blockRef,
|
212
|
+
instanceId,
|
213
|
+
},
|
214
|
+
});
|
215
|
+
return true;
|
216
|
+
case 'FILE_STATE':
|
217
|
+
this.out.emit('data', {
|
218
|
+
...data,
|
219
|
+
payload: {
|
220
|
+
...data.payload,
|
221
|
+
path: (0, path_1.join)(basePath, data.payload.filename),
|
222
|
+
blockName,
|
223
|
+
blockRef,
|
224
|
+
instanceId,
|
225
|
+
},
|
226
|
+
});
|
227
|
+
return true;
|
228
|
+
}
|
229
|
+
return false;
|
230
|
+
}
|
231
|
+
handleFileDoneOutput(blockUri, aiName, data) {
|
232
|
+
switch (data.type) {
|
233
|
+
case 'FILE_DONE':
|
102
234
|
const ref = blockUri.toNormalizedString();
|
103
235
|
this.emitFile(blockUri, aiName, data.payload.filename, data.payload.content, data.reason);
|
104
236
|
return {
|
105
|
-
type: '
|
237
|
+
type: 'FILE_DONE',
|
106
238
|
created: Date.now(),
|
107
239
|
payload: {
|
108
240
|
filename: data.payload.filename,
|
241
|
+
path: (0, path_1.join)(this.getBasePath(blockUri.fullName), data.payload.filename),
|
109
242
|
content: data.payload.content,
|
110
243
|
blockRef: ref,
|
111
244
|
instanceId: event_parser_1.StormEventParser.toInstanceIdFromRef(ref),
|
@@ -130,7 +263,7 @@ class StormCodegen {
|
|
130
263
|
}
|
131
264
|
const allFiles = this.toStormFiles(generatedResult);
|
132
265
|
// Send all the non-ai files to the stream
|
133
|
-
this.
|
266
|
+
await this.emitStaticFiles((0, nodejs_utils_1.parseKapetaUri)(block.uri), block.aiName, allFiles);
|
134
267
|
if (this.isAborted()) {
|
135
268
|
return;
|
136
269
|
}
|
@@ -285,8 +418,8 @@ class StormCodegen {
|
|
285
418
|
/**
|
286
419
|
* Emits the text-based files to the stream
|
287
420
|
*/
|
288
|
-
|
289
|
-
files.
|
421
|
+
async emitStaticFiles(uri, aiName, files) {
|
422
|
+
const promises = files.map((file) => {
|
290
423
|
if (!file.content || typeof file.content !== 'string') {
|
291
424
|
return;
|
292
425
|
}
|
@@ -300,14 +433,30 @@ class StormCodegen {
|
|
300
433
|
// They will need to be implemented by the AI
|
301
434
|
return;
|
302
435
|
}
|
303
|
-
this.
|
436
|
+
const basePath = this.getBasePath(uri.fullName);
|
437
|
+
const ref = uri.toNormalizedString();
|
438
|
+
const fileEvent = {
|
439
|
+
type: 'FILE_DONE',
|
440
|
+
reason: 'File generated',
|
441
|
+
created: Date.now(),
|
442
|
+
payload: {
|
443
|
+
filename: file.filename,
|
444
|
+
path: (0, path_1.join)(basePath, file.filename),
|
445
|
+
content: file.content,
|
446
|
+
blockName: aiName,
|
447
|
+
blockRef: ref,
|
448
|
+
instanceId: event_parser_1.StormEventParser.toInstanceIdFromRef(ref),
|
449
|
+
},
|
450
|
+
};
|
451
|
+
return new SimulatedFileDelay(fileEvent, this.out).start();
|
304
452
|
});
|
453
|
+
return Promise.all(promises);
|
305
454
|
}
|
306
455
|
emitFile(uri, blockName, filename, content, reason = 'File generated') {
|
307
456
|
const basePath = this.getBasePath(uri.fullName);
|
308
457
|
const ref = uri.toNormalizedString();
|
309
458
|
this.out.emit('data', {
|
310
|
-
type: '
|
459
|
+
type: 'FILE_DONE',
|
311
460
|
reason,
|
312
461
|
created: Date.now(),
|
313
462
|
payload: {
|
@@ -330,21 +479,15 @@ class StormCodegen {
|
|
330
479
|
template: templateFile,
|
331
480
|
prompt: this.userPrompt,
|
332
481
|
});
|
333
|
-
const files = [];
|
334
482
|
this.out.on('aborted', () => {
|
335
483
|
stream.abort();
|
336
484
|
});
|
337
485
|
stream.on('data', (evt) => {
|
338
|
-
|
339
|
-
if (file) {
|
340
|
-
files.push(file);
|
341
|
-
}
|
486
|
+
this.handleTemplateFileOutput(blockUri, aiName, templateFile, evt);
|
342
487
|
});
|
343
488
|
await stream.waitForDone();
|
344
|
-
return files;
|
345
489
|
});
|
346
|
-
|
347
|
-
return fileChunks.flat();
|
490
|
+
await Promise.all(promises);
|
348
491
|
}
|
349
492
|
/**
|
350
493
|
* Converts the generated files to a format that can be sent to the AI
|
@@ -211,10 +211,6 @@ class StormEventParser {
|
|
211
211
|
evt.payload.toBlockId = StormEventParser.toInstanceId(handle, evt.payload.toComponent);
|
212
212
|
this.connections.push(evt.payload);
|
213
213
|
break;
|
214
|
-
default:
|
215
|
-
case 'SCREEN_CANDIDATE':
|
216
|
-
case 'FILE':
|
217
|
-
break;
|
218
214
|
}
|
219
215
|
return this.toResult(handle);
|
220
216
|
}
|
@@ -150,19 +150,35 @@ export interface StormEventScreenCandidate {
|
|
150
150
|
url: string;
|
151
151
|
};
|
152
152
|
}
|
153
|
-
export interface
|
154
|
-
|
153
|
+
export interface StormEventFileBasePayload {
|
154
|
+
filename: string;
|
155
|
+
path: string;
|
156
|
+
blockName: string;
|
157
|
+
blockRef: string;
|
158
|
+
instanceId: string;
|
159
|
+
}
|
160
|
+
export interface StormEventFileBase {
|
161
|
+
type: string;
|
155
162
|
reason: string;
|
156
163
|
created: number;
|
157
|
-
payload:
|
158
|
-
|
159
|
-
|
164
|
+
payload: StormEventFileBasePayload;
|
165
|
+
}
|
166
|
+
export interface StormEventFileLogical extends StormEventFileBase {
|
167
|
+
type: 'FILE_START' | 'FILE_CHUNK_RESET';
|
168
|
+
}
|
169
|
+
export interface StormEventFileState extends StormEventFileBase {
|
170
|
+
type: 'FILE_STATE';
|
171
|
+
payload: StormEventFileBasePayload & {
|
172
|
+
state: string;
|
173
|
+
};
|
174
|
+
}
|
175
|
+
export interface StormEventFileContent extends StormEventFileBase {
|
176
|
+
type: 'FILE_DONE' | 'FILE_CHUNK';
|
177
|
+
payload: StormEventFileBasePayload & {
|
160
178
|
content: string;
|
161
|
-
blockName: string;
|
162
|
-
blockRef: string;
|
163
|
-
instanceId: string;
|
164
179
|
};
|
165
180
|
}
|
181
|
+
export type StormEventFile = StormEventFileLogical | StormEventFileState | StormEventFileContent;
|
166
182
|
export interface StormEventBlockReady {
|
167
183
|
type: 'BLOCK_READY';
|
168
184
|
reason: string;
|
@@ -196,4 +212,4 @@ export interface StormEventPhases {
|
|
196
212
|
phaseType: StormEventPhaseType;
|
197
213
|
};
|
198
214
|
}
|
199
|
-
export type StormEvent = StormEventCreateBlock | StormEventCreateConnection | StormEventCreatePlanProperties | StormEventInvalidResponse | StormEventPlanRetry | StormEventCreateDSL | StormEventCreateDSLResource | StormEventError | StormEventScreen | StormEventScreenCandidate |
|
215
|
+
export type StormEvent = StormEventCreateBlock | StormEventCreateConnection | StormEventCreatePlanProperties | StormEventInvalidResponse | StormEventPlanRetry | StormEventCreateDSL | StormEventCreateDSLResource | StormEventError | StormEventScreen | StormEventScreenCandidate | StormEventFileLogical | StormEventFileState | StormEventFileContent | StormEventDone | StormEventDefinitionChange | StormEventErrorClassifier | StormEventCodeFix | StormEventBlockReady | StormEventPhases;
|
package/package.json
CHANGED
package/src/storm/codegen.ts
CHANGED
@@ -15,7 +15,7 @@ import {
|
|
15
15
|
import { BlockDefinition } from '@kapeta/schemas';
|
16
16
|
import { codeGeneratorManager } from '../codeGeneratorManager';
|
17
17
|
import { STORM_ID, stormClient } from './stormClient';
|
18
|
-
import { StormEvent,
|
18
|
+
import { StormEvent, StormEventFileContent, StormEventFileLogical } from './events';
|
19
19
|
import { BlockDefinitionInfo, StormEventParser } from './event-parser';
|
20
20
|
import { StormFileImplementationPrompt, StormFileInfo, StormStream } from './stream';
|
21
21
|
import { KapetaURI, parseKapetaUri } from '@kapeta/nodejs-utils';
|
@@ -27,6 +27,54 @@ import Path from 'path';
|
|
27
27
|
|
28
28
|
type ImplementationGenerator = (prompt: StormFileImplementationPrompt, conversationId?: string) => Promise<StormStream>;
|
29
29
|
|
30
|
+
const SIMULATED_DELAY = 1000;
|
31
|
+
|
32
|
+
class SimulatedFileDelay {
|
33
|
+
private readonly file: StormEventFileContent;
|
34
|
+
public readonly stream;
|
35
|
+
constructor(file: StormEventFileContent, stream: StormStream) {
|
36
|
+
this.file = file;
|
37
|
+
this.stream = stream;
|
38
|
+
}
|
39
|
+
|
40
|
+
public async start() {
|
41
|
+
const commonPayload = {
|
42
|
+
filename: this.file.payload.filename,
|
43
|
+
path: this.file.payload.path,
|
44
|
+
blockName: this.file.payload.blockName,
|
45
|
+
blockRef: this.file.payload.blockRef,
|
46
|
+
instanceId: this.file.payload.instanceId,
|
47
|
+
};
|
48
|
+
this.stream.emit('data', {
|
49
|
+
type: 'FILE_START',
|
50
|
+
created: Date.now(),
|
51
|
+
reason: 'File start',
|
52
|
+
payload: commonPayload,
|
53
|
+
} satisfies StormEventFileLogical);
|
54
|
+
|
55
|
+
const lines = this.file.payload.content.split('\n');
|
56
|
+
const delayPerLine = SIMULATED_DELAY / lines.length;
|
57
|
+
for (const line of lines) {
|
58
|
+
await new Promise<void>((resolve) => {
|
59
|
+
setTimeout(() => {
|
60
|
+
this.stream.emit('data', {
|
61
|
+
type: 'FILE_CHUNK',
|
62
|
+
created: Date.now(),
|
63
|
+
reason: 'File chunk',
|
64
|
+
payload: {
|
65
|
+
...commonPayload,
|
66
|
+
content: line,
|
67
|
+
},
|
68
|
+
} satisfies StormEventFileContent);
|
69
|
+
resolve();
|
70
|
+
}, delayPerLine);
|
71
|
+
});
|
72
|
+
}
|
73
|
+
|
74
|
+
this.stream.emit('data', this.file);
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
30
78
|
export class StormCodegen {
|
31
79
|
private readonly userPrompt: string;
|
32
80
|
private readonly blocks: BlockDefinitionInfo[];
|
@@ -59,50 +107,150 @@ export class StormCodegen {
|
|
59
107
|
return this.out;
|
60
108
|
}
|
61
109
|
|
62
|
-
private handleTemplateFileOutput(
|
110
|
+
private handleTemplateFileOutput(
|
111
|
+
blockUri: KapetaURI,
|
112
|
+
aiName: string,
|
113
|
+
template: StormFileInfo,
|
114
|
+
data: StormEvent
|
115
|
+
): void {
|
116
|
+
if (this.handleFileEvents(blockUri, aiName, data)) {
|
117
|
+
return;
|
118
|
+
}
|
119
|
+
|
63
120
|
switch (data.type) {
|
64
|
-
case '
|
121
|
+
case 'FILE_DONE':
|
65
122
|
template.filename = data.payload.filename;
|
66
123
|
template.content = data.payload.content;
|
67
|
-
|
124
|
+
this.handleFileDoneOutput(blockUri, aiName, data);
|
125
|
+
break;
|
68
126
|
}
|
69
127
|
}
|
70
128
|
|
71
|
-
private handleUiOutput(blockUri: KapetaURI,
|
129
|
+
private handleUiOutput(blockUri: KapetaURI, blockName: string, data: StormEvent) {
|
130
|
+
const blockRef = blockUri.toNormalizedString();
|
131
|
+
const instanceId = StormEventParser.toInstanceIdFromRef(blockRef);
|
132
|
+
|
133
|
+
if (this.handleFileEvents(blockUri, blockName, data)) {
|
134
|
+
return;
|
135
|
+
}
|
136
|
+
|
72
137
|
switch (data.type) {
|
73
138
|
case 'SCREEN':
|
74
|
-
const ref = blockUri.toNormalizedString();
|
75
139
|
this.out.emit('data', {
|
76
140
|
type: 'SCREEN',
|
77
141
|
reason: data.reason,
|
78
142
|
created: Date.now(),
|
79
143
|
payload: {
|
80
144
|
...data.payload,
|
81
|
-
blockName
|
82
|
-
blockRef
|
83
|
-
instanceId
|
145
|
+
blockName,
|
146
|
+
blockRef,
|
147
|
+
instanceId,
|
148
|
+
},
|
149
|
+
});
|
150
|
+
break;
|
151
|
+
case 'FILE_START':
|
152
|
+
case 'FILE_CHUNK_RESET':
|
153
|
+
this.out.emit('data', {
|
154
|
+
...data,
|
155
|
+
payload: {
|
156
|
+
...data.payload,
|
157
|
+
blockName,
|
158
|
+
blockRef,
|
159
|
+
instanceId,
|
160
|
+
},
|
161
|
+
});
|
162
|
+
break;
|
163
|
+
case 'FILE_CHUNK':
|
164
|
+
this.out.emit('data', {
|
165
|
+
...data,
|
166
|
+
payload: {
|
167
|
+
...data.payload,
|
168
|
+
blockName,
|
169
|
+
blockRef,
|
170
|
+
instanceId,
|
84
171
|
},
|
85
172
|
});
|
86
|
-
|
87
|
-
|
173
|
+
break;
|
174
|
+
case 'FILE_STATE':
|
175
|
+
this.out.emit('data', {
|
176
|
+
...data,
|
177
|
+
payload: {
|
178
|
+
...data.payload,
|
179
|
+
blockName,
|
180
|
+
blockRef,
|
181
|
+
instanceId,
|
182
|
+
},
|
183
|
+
});
|
184
|
+
break;
|
185
|
+
case 'FILE_DONE':
|
186
|
+
this.handleFileDoneOutput(blockUri, blockName, data);
|
187
|
+
break;
|
188
|
+
}
|
189
|
+
}
|
190
|
+
|
191
|
+
private handleFileEvents(blockUri: KapetaURI, blockName: string, data: StormEvent) {
|
192
|
+
const blockRef = blockUri.toNormalizedString();
|
193
|
+
const instanceId = StormEventParser.toInstanceIdFromRef(blockRef);
|
194
|
+
const basePath = this.getBasePath(blockUri.fullName);
|
195
|
+
switch (data.type) {
|
196
|
+
case 'FILE_START':
|
197
|
+
case 'FILE_CHUNK_RESET':
|
198
|
+
this.out.emit('data', {
|
199
|
+
...data,
|
200
|
+
payload: {
|
201
|
+
...data.payload,
|
202
|
+
path: join(basePath, data.payload.filename),
|
203
|
+
blockName,
|
204
|
+
blockRef,
|
205
|
+
instanceId,
|
206
|
+
},
|
207
|
+
});
|
208
|
+
return true;
|
209
|
+
case 'FILE_CHUNK':
|
210
|
+
this.out.emit('data', {
|
211
|
+
...data,
|
212
|
+
payload: {
|
213
|
+
...data.payload,
|
214
|
+
path: join(basePath, data.payload.filename),
|
215
|
+
blockName,
|
216
|
+
blockRef,
|
217
|
+
instanceId,
|
218
|
+
},
|
219
|
+
});
|
220
|
+
return true;
|
221
|
+
case 'FILE_STATE':
|
222
|
+
this.out.emit('data', {
|
223
|
+
...data,
|
224
|
+
payload: {
|
225
|
+
...data.payload,
|
226
|
+
path: join(basePath, data.payload.filename),
|
227
|
+
blockName,
|
228
|
+
blockRef,
|
229
|
+
instanceId,
|
230
|
+
},
|
231
|
+
});
|
232
|
+
return true;
|
88
233
|
}
|
234
|
+
|
235
|
+
return false;
|
89
236
|
}
|
90
237
|
|
91
|
-
private
|
238
|
+
private handleFileDoneOutput(blockUri: KapetaURI, aiName: string, data: StormEvent) {
|
92
239
|
switch (data.type) {
|
93
|
-
case '
|
240
|
+
case 'FILE_DONE':
|
94
241
|
const ref = blockUri.toNormalizedString();
|
95
242
|
this.emitFile(blockUri, aiName, data.payload.filename, data.payload.content, data.reason);
|
96
243
|
return {
|
97
|
-
type: '
|
244
|
+
type: 'FILE_DONE',
|
98
245
|
created: Date.now(),
|
99
246
|
payload: {
|
100
247
|
filename: data.payload.filename,
|
248
|
+
path: join(this.getBasePath(blockUri.fullName), data.payload.filename),
|
101
249
|
content: data.payload.content,
|
102
250
|
blockRef: ref,
|
103
251
|
instanceId: StormEventParser.toInstanceIdFromRef(ref),
|
104
252
|
},
|
105
|
-
} as
|
253
|
+
} as StormEventFileContent;
|
106
254
|
}
|
107
255
|
}
|
108
256
|
|
@@ -126,7 +274,7 @@ export class StormCodegen {
|
|
126
274
|
const allFiles = this.toStormFiles(generatedResult);
|
127
275
|
|
128
276
|
// Send all the non-ai files to the stream
|
129
|
-
this.
|
277
|
+
await this.emitStaticFiles(parseKapetaUri(block.uri), block.aiName, allFiles);
|
130
278
|
|
131
279
|
if (this.isAborted()) {
|
132
280
|
return;
|
@@ -318,8 +466,8 @@ export class StormCodegen {
|
|
318
466
|
/**
|
319
467
|
* Emits the text-based files to the stream
|
320
468
|
*/
|
321
|
-
private
|
322
|
-
files.
|
469
|
+
private async emitStaticFiles(uri: KapetaURI, aiName: string, files: StormFileInfo[]) {
|
470
|
+
const promises = files.map((file) => {
|
323
471
|
if (!file.content || typeof file.content !== 'string') {
|
324
472
|
return;
|
325
473
|
}
|
@@ -336,8 +484,25 @@ export class StormCodegen {
|
|
336
484
|
return;
|
337
485
|
}
|
338
486
|
|
339
|
-
this.
|
487
|
+
const basePath = this.getBasePath(uri.fullName);
|
488
|
+
const ref = uri.toNormalizedString();
|
489
|
+
const fileEvent: StormEventFileContent = {
|
490
|
+
type: 'FILE_DONE',
|
491
|
+
reason: 'File generated',
|
492
|
+
created: Date.now(),
|
493
|
+
payload: {
|
494
|
+
filename: file.filename,
|
495
|
+
path: join(basePath, file.filename),
|
496
|
+
content: file.content,
|
497
|
+
blockName: aiName,
|
498
|
+
blockRef: ref,
|
499
|
+
instanceId: StormEventParser.toInstanceIdFromRef(ref),
|
500
|
+
},
|
501
|
+
};
|
502
|
+
return new SimulatedFileDelay(fileEvent, this.out).start();
|
340
503
|
});
|
504
|
+
|
505
|
+
return Promise.all(promises);
|
341
506
|
}
|
342
507
|
|
343
508
|
private emitFile(
|
@@ -350,7 +515,7 @@ export class StormCodegen {
|
|
350
515
|
const basePath = this.getBasePath(uri.fullName);
|
351
516
|
const ref = uri.toNormalizedString();
|
352
517
|
this.out.emit('data', {
|
353
|
-
type: '
|
518
|
+
type: 'FILE_DONE',
|
354
519
|
reason,
|
355
520
|
created: Date.now(),
|
356
521
|
payload: {
|
@@ -361,7 +526,7 @@ export class StormCodegen {
|
|
361
526
|
blockRef: ref,
|
362
527
|
instanceId: StormEventParser.toInstanceIdFromRef(ref),
|
363
528
|
},
|
364
|
-
} satisfies
|
529
|
+
} satisfies StormEventFileContent);
|
365
530
|
}
|
366
531
|
|
367
532
|
/**
|
@@ -373,7 +538,7 @@ export class StormCodegen {
|
|
373
538
|
generator: ImplementationGenerator,
|
374
539
|
templates: StormFileInfo[],
|
375
540
|
contextFiles: StormFileInfo[]
|
376
|
-
) {
|
541
|
+
): Promise<void> {
|
377
542
|
const promises = templates.map(async (templateFile) => {
|
378
543
|
const stream = await generator({
|
379
544
|
context: contextFiles,
|
@@ -381,26 +546,18 @@ export class StormCodegen {
|
|
381
546
|
prompt: this.userPrompt,
|
382
547
|
});
|
383
548
|
|
384
|
-
const files: StormEventFile[] = [];
|
385
|
-
|
386
549
|
this.out.on('aborted', () => {
|
387
550
|
stream.abort();
|
388
551
|
});
|
389
552
|
|
390
553
|
stream.on('data', (evt) => {
|
391
|
-
|
392
|
-
if (file) {
|
393
|
-
files.push(file);
|
394
|
-
}
|
554
|
+
this.handleTemplateFileOutput(blockUri, aiName, templateFile, evt);
|
395
555
|
});
|
396
556
|
|
397
557
|
await stream.waitForDone();
|
398
|
-
return files;
|
399
558
|
});
|
400
559
|
|
401
|
-
|
402
|
-
|
403
|
-
return fileChunks.flat();
|
560
|
+
await Promise.all(promises);
|
404
561
|
}
|
405
562
|
|
406
563
|
/**
|
@@ -325,11 +325,6 @@ export class StormEventParser {
|
|
325
325
|
evt.payload.toBlockId = StormEventParser.toInstanceId(handle, evt.payload.toComponent);
|
326
326
|
this.connections.push(evt.payload);
|
327
327
|
break;
|
328
|
-
|
329
|
-
default:
|
330
|
-
case 'SCREEN_CANDIDATE':
|
331
|
-
case 'FILE':
|
332
|
-
break;
|
333
328
|
}
|
334
329
|
|
335
330
|
return this.toResult(handle);
|
package/src/storm/events.ts
CHANGED
@@ -182,20 +182,41 @@ export interface StormEventScreenCandidate {
|
|
182
182
|
};
|
183
183
|
}
|
184
184
|
|
185
|
-
export interface
|
186
|
-
|
185
|
+
export interface StormEventFileBasePayload {
|
186
|
+
filename: string;
|
187
|
+
path: string;
|
188
|
+
blockName: string;
|
189
|
+
blockRef: string;
|
190
|
+
instanceId: string;
|
191
|
+
}
|
192
|
+
|
193
|
+
export interface StormEventFileBase {
|
194
|
+
type: string;
|
187
195
|
reason: string;
|
188
196
|
created: number;
|
189
|
-
payload:
|
190
|
-
|
191
|
-
|
197
|
+
payload: StormEventFileBasePayload;
|
198
|
+
}
|
199
|
+
|
200
|
+
export interface StormEventFileLogical extends StormEventFileBase {
|
201
|
+
type: 'FILE_START' | 'FILE_CHUNK_RESET';
|
202
|
+
}
|
203
|
+
|
204
|
+
export interface StormEventFileState extends StormEventFileBase {
|
205
|
+
type: 'FILE_STATE';
|
206
|
+
payload: StormEventFileBasePayload & {
|
207
|
+
state: string;
|
208
|
+
};
|
209
|
+
}
|
210
|
+
|
211
|
+
export interface StormEventFileContent extends StormEventFileBase {
|
212
|
+
type: 'FILE_DONE' | 'FILE_CHUNK';
|
213
|
+
payload: StormEventFileBasePayload & {
|
192
214
|
content: string;
|
193
|
-
blockName: string;
|
194
|
-
blockRef: string;
|
195
|
-
instanceId: string;
|
196
215
|
};
|
197
216
|
}
|
198
217
|
|
218
|
+
export type StormEventFile = StormEventFileLogical | StormEventFileState | StormEventFileContent;
|
219
|
+
|
199
220
|
export interface StormEventBlockReady {
|
200
221
|
type: 'BLOCK_READY';
|
201
222
|
reason: string;
|
@@ -245,7 +266,9 @@ export type StormEvent =
|
|
245
266
|
| StormEventError
|
246
267
|
| StormEventScreen
|
247
268
|
| StormEventScreenCandidate
|
248
|
-
|
|
269
|
+
| StormEventFileLogical
|
270
|
+
| StormEventFileState
|
271
|
+
| StormEventFileContent
|
249
272
|
| StormEventDone
|
250
273
|
| StormEventDefinitionChange
|
251
274
|
| StormEventErrorClassifier
|