@adminide-stack/form-builder-core 5.1.4-alpha.62 → 5.1.4-alpha.66

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.
@@ -0,0 +1,432 @@
1
+ /**
2
+ * Monaco Editor Integration for JavaScript Autocomplete
3
+ *
4
+ * Integrates with the js-autocomplete-extension via command execution
5
+ * to provide intelligent code completions in step function editors.
6
+ */
7
+
8
+ import { getStepCompletionContext, getStepTypeDefinitions } from './stepGenerator';
9
+ import type { InngestStep } from './interfaces/types';
10
+
11
+ export interface MonacoAutocompleteOptions {
12
+ /** Enable Inngest step functions context */
13
+ enableInngestContext?: boolean;
14
+ /** Custom scope variables */
15
+ customScope?: string[];
16
+ /** Enable type info on hover */
17
+ enableHoverInfo?: boolean;
18
+ /** Command service for executing extension commands */
19
+ commandService?: any;
20
+ }
21
+
22
+ /**
23
+ * Setup JavaScript autocomplete for Monaco Editor using extension commands
24
+ *
25
+ * @param monaco Monaco editor instance
26
+ * @param options Configuration options
27
+ * @param commandService Service for executing extension commands
28
+ */
29
+ export async function setupMonacoAutocomplete(
30
+ monaco: any,
31
+ options: MonacoAutocompleteOptions = {},
32
+ commandService?: any,
33
+ ) {
34
+ const { enableInngestContext = true, customScope = [], enableHoverInfo = true } = options;
35
+
36
+ const commands = commandService || options.commandService;
37
+
38
+ if (!commands) {
39
+ console.warn('No command service provided, autocomplete will not be available');
40
+ return;
41
+ }
42
+
43
+ // Build scope context
44
+ const scope = [...customScope];
45
+ if (enableInngestContext) {
46
+ scope.push('step', 'event', 'run', 'sleep', 'sendEvent', 'waitForEvent');
47
+ }
48
+
49
+ // Configure TypeScript/JavaScript compiler options to prevent worker errors
50
+ const compilerOptions = {
51
+ target: monaco.languages.typescript.ScriptTarget.ES2020,
52
+ allowNonTsExtensions: true,
53
+ moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
54
+ module: monaco.languages.typescript.ModuleKind.CommonJS,
55
+ noEmit: true,
56
+ esModuleInterop: true,
57
+ allowJs: true,
58
+ checkJs: false,
59
+ noSemanticValidation: false,
60
+ noSyntaxValidation: false,
61
+ };
62
+
63
+ // Set compiler options for both JavaScript and TypeScript
64
+ monaco.languages.typescript.javascriptDefaults.setCompilerOptions(compilerOptions);
65
+ monaco.languages.typescript.typescriptDefaults.setCompilerOptions(compilerOptions);
66
+
67
+ // Configure diagnostics options to prevent unnecessary worker calls
68
+ monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
69
+ noSemanticValidation: false,
70
+ noSyntaxValidation: false,
71
+ diagnosticCodesToIgnore: [1108], // Ignore "A 'return' statement can only be used within a function body"
72
+ });
73
+
74
+ monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
75
+ noSemanticValidation: false,
76
+ noSyntaxValidation: false,
77
+ });
78
+
79
+ // Disable eager model sync to prevent worker trying to access non-existent models
80
+ monaco.languages.typescript.javascriptDefaults.setEagerModelSync(false);
81
+ monaco.languages.typescript.typescriptDefaults.setEagerModelSync(false);
82
+
83
+ // Register completion provider for JavaScript
84
+ monaco.languages.registerCompletionItemProvider('javascript', {
85
+ triggerCharacters: ['.', ' '],
86
+
87
+ provideCompletionItems: async (model: any, position: any) => {
88
+ try {
89
+ const text = model.getValue();
90
+ const offset = model.getOffsetAt(position);
91
+
92
+ // Call the autocomplete extension command
93
+ const result = await commands.executeCommand({
94
+ command: 'jsAutocomplete.getCompletions',
95
+ arguments: [text, offset, scope.length > 0 ? scope : undefined],
96
+ });
97
+
98
+ if (!result || !result.completions) {
99
+ return { suggestions: [] };
100
+ }
101
+
102
+ // Convert to Monaco completion items
103
+ const suggestions = result.completions.map((completion: any) => ({
104
+ label: completion.name,
105
+ kind: determineCompletionKind(completion.type, monaco),
106
+ documentation: formatDocumentation(completion),
107
+ insertText: completion.name,
108
+ detail: completion.type || 'any',
109
+ sortText: getSortText(completion),
110
+ }));
111
+
112
+ return {
113
+ suggestions,
114
+ incomplete: result.isIncomplete || false,
115
+ };
116
+ } catch (error) {
117
+ console.error('Autocomplete error:', error);
118
+ return { suggestions: [] };
119
+ }
120
+ },
121
+
122
+ resolveCompletionItem: async (item: any) => {
123
+ if (!enableHoverInfo) {
124
+ return item;
125
+ }
126
+
127
+ try {
128
+ // Get detailed type info for the completion
129
+ const typeInfo = await commands.executeCommand({
130
+ command: 'jsAutocomplete.getTypeInfo',
131
+ arguments: [item.label, scope.length > 0 ? scope : undefined],
132
+ });
133
+
134
+ if (typeInfo) {
135
+ item.documentation = {
136
+ value: formatDetailedDocumentation(item.label, typeInfo),
137
+ isTrusted: true,
138
+ };
139
+ }
140
+ } catch (error) {
141
+ console.error('Type info resolution error:', error);
142
+ }
143
+
144
+ return item;
145
+ },
146
+ });
147
+
148
+ // Register completion provider for TypeScript
149
+ monaco.languages.registerCompletionItemProvider('typescript', {
150
+ triggerCharacters: ['.', ' '],
151
+
152
+ provideCompletionItems: async (model: any, position: any) => {
153
+ try {
154
+ const text = model.getValue();
155
+ const offset = model.getOffsetAt(position);
156
+
157
+ // Call the autocomplete extension command
158
+ const result = await commands.executeCommand({
159
+ command: 'jsAutocomplete.getCompletions',
160
+ arguments: [text, offset, scope.length > 0 ? scope : undefined],
161
+ });
162
+
163
+ if (!result || !result.completions) {
164
+ return { suggestions: [] };
165
+ }
166
+
167
+ // Convert to Monaco completion items
168
+ const suggestions = result.completions.map((completion: any) => ({
169
+ label: completion.name,
170
+ kind: determineCompletionKind(completion.type, monaco),
171
+ documentation: formatDocumentation(completion),
172
+ insertText: completion.name,
173
+ detail: completion.type || 'any',
174
+ sortText: getSortText(completion),
175
+ }));
176
+
177
+ return {
178
+ suggestions,
179
+ incomplete: result.isIncomplete || false,
180
+ };
181
+ } catch (error) {
182
+ console.error('TypeScript autocomplete error:', error);
183
+ return { suggestions: [] };
184
+ }
185
+ },
186
+ });
187
+
188
+ // Register hover provider for type information
189
+ if (enableHoverInfo) {
190
+ monaco.languages.registerHoverProvider('javascript', {
191
+ provideHover: async (model: any, position: any) => {
192
+ try {
193
+ const word = model.getWordAtPosition(position);
194
+ if (!word) {
195
+ return null;
196
+ }
197
+
198
+ // Get type info via extension command
199
+ const typeInfo = await commands.executeCommand({
200
+ command: 'jsAutocomplete.getTypeInfo',
201
+ arguments: [word.word, scope.length > 0 ? scope : undefined],
202
+ });
203
+
204
+ if (!typeInfo) {
205
+ return null;
206
+ }
207
+
208
+ return {
209
+ range: new monaco.Range(
210
+ position.lineNumber,
211
+ word.startColumn,
212
+ position.lineNumber,
213
+ word.endColumn,
214
+ ),
215
+ contents: [
216
+ { value: `**${word.word}**: \`${typeInfo.type || 'any'}\`` },
217
+ { value: typeInfo.doc || 'No documentation available' },
218
+ ],
219
+ };
220
+ } catch (error) {
221
+ console.error('Hover provider error:', error);
222
+ return null;
223
+ }
224
+ },
225
+ });
226
+
227
+ monaco.languages.registerHoverProvider('typescript', {
228
+ provideHover: async (model: any, position: any) => {
229
+ try {
230
+ const word = model.getWordAtPosition(position);
231
+ if (!word) {
232
+ return null;
233
+ }
234
+
235
+ const typeInfo = await commands.executeCommand({
236
+ command: 'jsAutocomplete.getTypeInfo',
237
+ arguments: [word.word, scope.length > 0 ? scope : undefined],
238
+ });
239
+
240
+ if (!typeInfo) {
241
+ return null;
242
+ }
243
+
244
+ return {
245
+ range: new monaco.Range(
246
+ position.lineNumber,
247
+ word.startColumn,
248
+ position.lineNumber,
249
+ word.endColumn,
250
+ ),
251
+ contents: [
252
+ { value: `**${word.word}**: \`${typeInfo.type || 'any'}\`` },
253
+ { value: typeInfo.doc || 'No documentation available' },
254
+ ],
255
+ };
256
+ } catch (error) {
257
+ console.error('TypeScript hover provider error:', error);
258
+ return null;
259
+ }
260
+ },
261
+ });
262
+ }
263
+
264
+ console.log('Monaco autocomplete integration setup complete');
265
+ }
266
+
267
+ /**
268
+ * Setup autocomplete for a specific Inngest step
269
+ * Provides step-specific context and completions
270
+ */
271
+ export async function setupStepAutocomplete(monaco: any, step: InngestStep, code: string, commandService: any) {
272
+ const context = getStepCompletionContext(step, code);
273
+
274
+ await setupMonacoAutocomplete(
275
+ monaco,
276
+ {
277
+ enableInngestContext: true,
278
+ customScope: [...context.scope, ...context.variables],
279
+ enableHoverInfo: true,
280
+ commandService,
281
+ },
282
+ commandService,
283
+ );
284
+
285
+ // Add step-specific type definitions
286
+ const typeDefinitions = getStepTypeDefinitions();
287
+ monaco.languages.typescript.javascriptDefaults.addExtraLib(typeDefinitions, 'file:///inngest-step-types.d.ts');
288
+ }
289
+
290
+ /**
291
+ * Install a library for autocomplete
292
+ */
293
+ export async function installLibraryForAutocomplete(
294
+ commandService: any,
295
+ url: string,
296
+ accessors: string[],
297
+ ): Promise<boolean> {
298
+ try {
299
+ await commandService.executeCommand({
300
+ command: 'jsAutocomplete.installLibrary',
301
+ arguments: [url, accessors],
302
+ });
303
+ console.log(`Library installed: ${accessors.join(', ')}`);
304
+ return true;
305
+ } catch (error) {
306
+ console.error('Failed to install library:', error);
307
+ return false;
308
+ }
309
+ }
310
+
311
+ /**
312
+ * Update autocomplete configuration
313
+ */
314
+ export async function updateAutocompleteConfig(
315
+ commandService: any,
316
+ config: {
317
+ includeDefaults?: boolean;
318
+ includeLibraries?: boolean;
319
+ enableDynamicLibraries?: boolean;
320
+ customDefinitions?: string[];
321
+ },
322
+ ): Promise<boolean> {
323
+ try {
324
+ await commandService.executeCommand({
325
+ command: 'jsAutocomplete.updateConfig',
326
+ arguments: [config],
327
+ });
328
+ console.log('Autocomplete config updated');
329
+ return true;
330
+ } catch (error) {
331
+ console.error('Failed to update autocomplete config:', error);
332
+ return false;
333
+ }
334
+ }
335
+
336
+ /**
337
+ * Get available type definitions
338
+ */
339
+ export async function getAvailableDefinitions(commandService: any): Promise<string[]> {
340
+ try {
341
+ const definitions = await commandService.executeCommand({
342
+ command: 'jsAutocomplete.getAvailableDefinitions',
343
+ arguments: [],
344
+ });
345
+ return definitions || [];
346
+ } catch (error) {
347
+ console.error('Failed to get available definitions:', error);
348
+ return [];
349
+ }
350
+ }
351
+
352
+ // Helper functions
353
+
354
+ function determineCompletionKind(type: string | undefined, monaco: any): number {
355
+ if (!type) {
356
+ return monaco.languages.CompletionItemKind.Variable;
357
+ }
358
+
359
+ if (type.includes('fn(') || type.includes('=>')) {
360
+ return monaco.languages.CompletionItemKind.Function;
361
+ }
362
+
363
+ if (type.startsWith('class ') || type.includes('{}')) {
364
+ return monaco.languages.CompletionItemKind.Class;
365
+ }
366
+
367
+ if (type.includes('[]')) {
368
+ return monaco.languages.CompletionItemKind.Field;
369
+ }
370
+
371
+ if (type === 'number' || type === 'string' || type === 'boolean') {
372
+ return monaco.languages.CompletionItemKind.Keyword;
373
+ }
374
+
375
+ return monaco.languages.CompletionItemKind.Variable;
376
+ }
377
+
378
+ function formatDocumentation(completion: { name: string; type?: string; doc?: string; url?: string }): string {
379
+ const parts: string[] = [];
380
+
381
+ if (completion.type) {
382
+ parts.push(`\`${completion.type}\``);
383
+ }
384
+
385
+ if (completion.doc) {
386
+ parts.push(completion.doc);
387
+ }
388
+
389
+ if (completion.url) {
390
+ parts.push(`[Documentation](${completion.url})`);
391
+ }
392
+
393
+ return parts.join('\n\n') || completion.name;
394
+ }
395
+
396
+ function formatDetailedDocumentation(name: string, typeInfo: { type?: string; doc?: string; url?: string }): string {
397
+ const parts: string[] = [];
398
+
399
+ parts.push(`## ${name}`);
400
+ parts.push('');
401
+
402
+ if (typeInfo.type) {
403
+ parts.push('```typescript');
404
+ parts.push(typeInfo.type);
405
+ parts.push('```');
406
+ parts.push('');
407
+ }
408
+
409
+ if (typeInfo.doc) {
410
+ parts.push(typeInfo.doc);
411
+ parts.push('');
412
+ }
413
+
414
+ if (typeInfo.url) {
415
+ parts.push(`[View Documentation](${typeInfo.url})`);
416
+ }
417
+
418
+ return parts.join('\n');
419
+ }
420
+
421
+ function getSortText(completion: { name: string; type?: string }): string {
422
+ // Prioritize functions and commonly used types
423
+ if (completion.type?.includes('fn(') || completion.type?.includes('=>')) {
424
+ return `0_${completion.name}`;
425
+ }
426
+
427
+ if (completion.type?.startsWith('class ')) {
428
+ return `1_${completion.name}`;
429
+ }
430
+
431
+ return `2_${completion.name}`;
432
+ }
@@ -6,8 +6,191 @@ export type { ExtractedFunction } from './interfaces/types';
6
6
  /**
7
7
  * Step function generation utilities for unified code generation
8
8
  * Used by both browser editor and server execution
9
+ *
10
+ * Integrates with js-autocomplete-extension for intelligent code completion
9
11
  */
10
12
 
13
+ /**
14
+ * Get scope context for code completion in step functions
15
+ * Returns available variables and their types for autocomplete
16
+ */
17
+ export function getStepCompletionScope(): string[] {
18
+ return [
19
+ 'step',
20
+ 'event',
21
+ 'run',
22
+ 'sleep',
23
+ 'sendEvent',
24
+ 'waitForEvent',
25
+ 'Promise',
26
+ 'console',
27
+ 'Date',
28
+ 'JSON',
29
+ 'Array',
30
+ 'Object',
31
+ ];
32
+ }
33
+
34
+ /**
35
+ * Get type definitions for step function context
36
+ * Used by Monaco editor for enhanced IntelliSense
37
+ */
38
+ export function getStepTypeDefinitions(): string {
39
+ return `
40
+ // Inngest Step API
41
+ declare const step: {
42
+ run<T>(id: string, fn: (event: any, step: any) => Promise<T> | T): Promise<T>;
43
+ sendEvent(id: string, input: { name: string; data?: any }): Promise<void>;
44
+ waitForEvent<T = any>(id: string, opts: { event: string; timeout?: string | number }): Promise<T>;
45
+ sleep(id: string, ms: string | number): Promise<void>;
46
+ };
47
+
48
+ // Event context
49
+ declare const event: {
50
+ data: any;
51
+ id: string;
52
+ name: string;
53
+ ts: number;
54
+ user?: any;
55
+ };
56
+
57
+ // Common utilities available in step functions
58
+ declare const console: {
59
+ log(...args: any[]): void;
60
+ error(...args: any[]): void;
61
+ warn(...args: any[]): void;
62
+ info(...args: any[]): void;
63
+ };
64
+
65
+ // Promise and async utilities
66
+ declare class Promise<T> {
67
+ then<TResult>(onfulfilled?: (value: T) => TResult | Promise<TResult>): Promise<TResult>;
68
+ catch<TResult>(onrejected?: (reason: any) => TResult | Promise<TResult>): Promise<TResult>;
69
+ finally(onfinally?: () => void): Promise<T>;
70
+ static all<T>(promises: Promise<T>[]): Promise<T[]>;
71
+ static race<T>(promises: Promise<T>[]): Promise<T>;
72
+ static resolve<T>(value: T): Promise<T>;
73
+ static reject(reason?: any): Promise<never>;
74
+ }
75
+
76
+ // Date utilities
77
+ declare class Date {
78
+ constructor();
79
+ constructor(value: number | string);
80
+ toISOString(): string;
81
+ getTime(): number;
82
+ static now(): number;
83
+ }
84
+ `;
85
+ }
86
+
87
+ /**
88
+ * Get autocomplete suggestions for a specific step type
89
+ * Returns context-specific completions based on step type
90
+ */
91
+ export function getStepTypeCompletions(stepType: string): Array<{ name: string; snippet: string; doc: string }> {
92
+ const completions: Record<string, Array<{ name: string; snippet: string; doc: string }>> = {
93
+ sleep: [
94
+ {
95
+ name: 'step.sleep',
96
+ snippet: "await step.sleep('${1:step-id}', '${2:5s}');",
97
+ doc: 'Sleep for a specified duration. Accepts time strings like "5s", "1m", "1h"',
98
+ },
99
+ ],
100
+ sendEvent: [
101
+ {
102
+ name: 'step.sendEvent',
103
+ snippet: `await step.sendEvent('\${1:step-id}', {
104
+ name: '\${2:event.name}',
105
+ data: {
106
+ \${3:key}: \${4:value}
107
+ }
108
+ });`,
109
+ doc: 'Send an event to trigger other workflows',
110
+ },
111
+ ],
112
+ waitForEvent: [
113
+ {
114
+ name: 'step.waitForEvent',
115
+ snippet: `const \${1:result} = await step.waitForEvent('\${2:step-id}', {
116
+ event: '\${3:event.name}',
117
+ timeout: '\${4:60s}'
118
+ });`,
119
+ doc: 'Wait for a specific event before continuing',
120
+ },
121
+ ],
122
+ run: [
123
+ {
124
+ name: 'step.run',
125
+ snippet: `const \${1:result} = await step.run('\${2:step-id}', async () => {
126
+ \${3:// Your logic here}
127
+ return { success: true };
128
+ });`,
129
+ doc: 'Run a unit of work with automatic retries and error handling',
130
+ },
131
+ ],
132
+ parallel: [
133
+ {
134
+ name: 'Promise.all',
135
+ snippet: `const results = await Promise.all([
136
+ \${1:step.run('step-1', async () => { return 1; })},
137
+ \${2:step.run('step-2', async () => { return 2; })}
138
+ ]);`,
139
+ doc: 'Execute multiple steps in parallel',
140
+ },
141
+ ],
142
+ };
143
+
144
+ return completions[stepType] || completions.run;
145
+ }
146
+
147
+ /**
148
+ * Extract variable names from step code for completion context
149
+ * Returns all declared variables in the step function
150
+ */
151
+ export function extractVariablesFromCode(code: string): string[] {
152
+ const variables: string[] = [];
153
+
154
+ // Match const/let/var declarations
155
+ const declRegex = /(?:const|let|var)\s+(\w+)/g;
156
+ let match;
157
+
158
+ while ((match = declRegex.exec(code)) !== null) {
159
+ variables.push(match[1]);
160
+ }
161
+
162
+ // Match function parameters
163
+ const paramRegex = /(?:async\s+)?\(([^)]*)\)\s*=>/g;
164
+ while ((match = paramRegex.exec(code)) !== null) {
165
+ const params = match[1].split(',').map((p) => p.trim().split(/\s+/)[0]);
166
+ variables.push(...params.filter((p) => p && p !== ''));
167
+ }
168
+
169
+ return [...new Set(variables)]; // Remove duplicates
170
+ }
171
+
172
+ /**
173
+ * Get completion context for a step at a specific position
174
+ * Used by Monaco editor to provide context-aware completions
175
+ */
176
+ export interface CompletionContext {
177
+ scope: string[];
178
+ typeDefinitions: string;
179
+ snippets: Array<{ name: string; snippet: string; doc: string }>;
180
+ variables: string[];
181
+ }
182
+
183
+ export function getStepCompletionContext(step: InngestStep, code: string): CompletionContext {
184
+ const stepType = step.type || 'run';
185
+
186
+ return {
187
+ scope: getStepCompletionScope(),
188
+ typeDefinitions: getStepTypeDefinitions(),
189
+ snippets: getStepTypeCompletions(stepType),
190
+ variables: extractVariablesFromCode(code),
191
+ };
192
+ }
193
+
11
194
  // Pass through code without any cleaning/validation
12
195
  export function cleanStepCode(code: string): string {
13
196
  return code;