@objectql/core 4.0.2 → 4.0.4

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.
Files changed (98) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/CHANGELOG.md +18 -0
  3. package/README.md +4 -4
  4. package/dist/app.d.ts +9 -6
  5. package/dist/app.js +151 -29
  6. package/dist/app.js.map +1 -1
  7. package/dist/index.d.ts +6 -9
  8. package/dist/index.js +2 -5
  9. package/dist/index.js.map +1 -1
  10. package/dist/optimizations/CompiledHookManager.d.ts +55 -0
  11. package/dist/optimizations/CompiledHookManager.js +164 -0
  12. package/dist/optimizations/CompiledHookManager.js.map +1 -0
  13. package/dist/optimizations/DependencyGraph.d.ts +82 -0
  14. package/dist/optimizations/DependencyGraph.js +211 -0
  15. package/dist/optimizations/DependencyGraph.js.map +1 -0
  16. package/dist/optimizations/GlobalConnectionPool.d.ts +89 -0
  17. package/dist/optimizations/GlobalConnectionPool.js +193 -0
  18. package/dist/optimizations/GlobalConnectionPool.js.map +1 -0
  19. package/dist/optimizations/LazyMetadataLoader.d.ts +75 -0
  20. package/dist/optimizations/LazyMetadataLoader.js +149 -0
  21. package/dist/optimizations/LazyMetadataLoader.js.map +1 -0
  22. package/dist/optimizations/OptimizedMetadataRegistry.d.ts +26 -0
  23. package/dist/optimizations/OptimizedMetadataRegistry.js +117 -0
  24. package/dist/optimizations/OptimizedMetadataRegistry.js.map +1 -0
  25. package/dist/optimizations/OptimizedValidationEngine.d.ts +73 -0
  26. package/dist/optimizations/OptimizedValidationEngine.js +141 -0
  27. package/dist/optimizations/OptimizedValidationEngine.js.map +1 -0
  28. package/dist/optimizations/QueryCompiler.d.ts +51 -0
  29. package/dist/optimizations/QueryCompiler.js +216 -0
  30. package/dist/optimizations/QueryCompiler.js.map +1 -0
  31. package/dist/optimizations/SQLQueryOptimizer.d.ts +96 -0
  32. package/dist/optimizations/SQLQueryOptimizer.js +265 -0
  33. package/dist/optimizations/SQLQueryOptimizer.js.map +1 -0
  34. package/dist/optimizations/index.d.ts +32 -0
  35. package/dist/optimizations/index.js +44 -0
  36. package/dist/optimizations/index.js.map +1 -0
  37. package/dist/plugin.d.ts +8 -7
  38. package/dist/plugin.js +57 -22
  39. package/dist/plugin.js.map +1 -1
  40. package/dist/query/query-analyzer.js.map +1 -1
  41. package/dist/query/query-builder.d.ts +6 -1
  42. package/dist/query/query-builder.js +21 -5
  43. package/dist/query/query-builder.js.map +1 -1
  44. package/dist/query/query-service.js.map +1 -1
  45. package/dist/repository.d.ts +2 -0
  46. package/dist/repository.js +15 -9
  47. package/dist/repository.js.map +1 -1
  48. package/jest.config.js +3 -3
  49. package/package.json +8 -5
  50. package/src/app.ts +173 -47
  51. package/src/index.ts +8 -9
  52. package/src/optimizations/CompiledHookManager.ts +185 -0
  53. package/src/optimizations/DependencyGraph.ts +255 -0
  54. package/src/optimizations/GlobalConnectionPool.ts +251 -0
  55. package/src/optimizations/LazyMetadataLoader.ts +180 -0
  56. package/src/optimizations/OptimizedMetadataRegistry.ts +132 -0
  57. package/src/optimizations/OptimizedValidationEngine.ts +172 -0
  58. package/src/optimizations/QueryCompiler.ts +242 -0
  59. package/src/optimizations/SQLQueryOptimizer.ts +329 -0
  60. package/src/optimizations/index.ts +34 -0
  61. package/src/plugin.ts +71 -28
  62. package/src/query/query-analyzer.ts +1 -1
  63. package/src/query/query-builder.ts +21 -7
  64. package/src/query/query-service.ts +1 -1
  65. package/src/repository.ts +25 -13
  66. package/test/__mocks__/@objectstack/runtime.ts +8 -8
  67. package/test/app.test.ts +9 -7
  68. package/test/optimizations.test.ts +440 -0
  69. package/test/plugin-integration.test.ts +30 -19
  70. package/tsconfig.json +4 -6
  71. package/tsconfig.tsbuildinfo +1 -1
  72. package/dist/ai-agent.d.ts +0 -176
  73. package/dist/ai-agent.js +0 -722
  74. package/dist/ai-agent.js.map +0 -1
  75. package/dist/formula-engine.d.ts +0 -102
  76. package/dist/formula-engine.js +0 -433
  77. package/dist/formula-engine.js.map +0 -1
  78. package/dist/formula-plugin.d.ts +0 -52
  79. package/dist/formula-plugin.js +0 -107
  80. package/dist/formula-plugin.js.map +0 -1
  81. package/dist/validator-plugin.d.ts +0 -56
  82. package/dist/validator-plugin.js +0 -106
  83. package/dist/validator-plugin.js.map +0 -1
  84. package/dist/validator.d.ts +0 -80
  85. package/dist/validator.js +0 -625
  86. package/dist/validator.js.map +0 -1
  87. package/src/ai-agent.ts +0 -868
  88. package/src/formula-engine.ts +0 -572
  89. package/src/formula-plugin.ts +0 -141
  90. package/src/validator-plugin.ts +0 -140
  91. package/src/validator.ts +0 -743
  92. package/test/formula-engine.test.ts +0 -725
  93. package/test/formula-integration.test.ts +0 -286
  94. package/test/formula-plugin.test.ts +0 -197
  95. package/test/formula-spec-compliance.test.ts +0 -258
  96. package/test/validation-spec-compliance.test.ts +0 -440
  97. package/test/validator-plugin.test.ts +0 -126
  98. package/test/validator.test.ts +0 -440
package/dist/ai-agent.js DELETED
@@ -1,722 +0,0 @@
1
- "use strict";
2
- /**
3
- * ObjectQL
4
- * Copyright (c) 2026-present ObjectStack Inc.
5
- *
6
- * This source code is licensed under the MIT license found in the
7
- * LICENSE file in the root directory of this source tree.
8
- */
9
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- var desc = Object.getOwnPropertyDescriptor(m, k);
12
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
- desc = { enumerable: true, get: function() { return m[k]; } };
14
- }
15
- Object.defineProperty(o, k2, desc);
16
- }) : (function(o, m, k, k2) {
17
- if (k2 === undefined) k2 = k;
18
- o[k2] = m[k];
19
- }));
20
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
- Object.defineProperty(o, "default", { enumerable: true, value: v });
22
- }) : function(o, v) {
23
- o["default"] = v;
24
- });
25
- var __importStar = (this && this.__importStar) || (function () {
26
- var ownKeys = function(o) {
27
- ownKeys = Object.getOwnPropertyNames || function (o) {
28
- var ar = [];
29
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
- return ar;
31
- };
32
- return ownKeys(o);
33
- };
34
- return function (mod) {
35
- if (mod && mod.__esModule) return mod;
36
- var result = {};
37
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
- __setModuleDefault(result, mod);
39
- return result;
40
- };
41
- })();
42
- var __importDefault = (this && this.__importDefault) || function (mod) {
43
- return (mod && mod.__esModule) ? mod : { "default": mod };
44
- };
45
- Object.defineProperty(exports, "__esModule", { value: true });
46
- exports.ObjectQLAgent = void 0;
47
- exports.createAgent = createAgent;
48
- /**
49
- * ObjectQL AI Agent - Programmatic API for AI-powered application generation
50
- *
51
- * This module provides a high-level API for using AI to generate and validate
52
- * ObjectQL metadata programmatically.
53
- */
54
- const openai_1 = __importDefault(require("openai"));
55
- const yaml = __importStar(require("js-yaml"));
56
- const validator_1 = require("./validator");
57
- /**
58
- * Regular expression patterns for parsing AI responses
59
- */
60
- const AI_RESPONSE_PATTERNS = {
61
- /**
62
- * Matches YAML file blocks with explicit headers in the format:
63
- * # filename.object.yml or File: filename.object.yml
64
- * followed by a YAML code block
65
- *
66
- * Groups:
67
- * 1. filename (e.g., "user.object.yml")
68
- * 2. YAML content
69
- */
70
- FILE_BLOCK_YAML: /(?:^|\n)(?:#|File:)\s*([a-zA-Z0-9_-]+\.[a-z]+\.yml)\s*\n```(?:yaml|yml)?\n([\s\S]*?)```/gi,
71
- /**
72
- * Matches TypeScript file blocks with explicit headers in the format:
73
- * // filename.action.ts or File: filename.hook.ts or filename.test.ts
74
- * followed by a TypeScript code block
75
- *
76
- * Groups:
77
- * 1. filename (e.g., "approve_order.action.ts", "user.hook.ts", "user.test.ts")
78
- * 2. TypeScript content
79
- */
80
- FILE_BLOCK_TS: /(?:^|\n)(?:\/\/|File:)\s*([a-zA-Z0-9_-]+\.(?:action|hook|test|spec)\.ts)\s*\n```(?:typescript|ts)?\n([\s\S]*?)```/gi,
81
- /**
82
- * Matches generic YAML/YML code blocks without explicit headers
83
- *
84
- * Groups:
85
- * 1. YAML content
86
- */
87
- CODE_BLOCK_YAML: /```(?:yaml|yml)\n([\s\S]*?)```/g,
88
- /**
89
- * Matches generic TypeScript code blocks without explicit headers
90
- *
91
- * Groups:
92
- * 1. TypeScript content
93
- */
94
- CODE_BLOCK_TS: /```(?:typescript|ts)\n([\s\S]*?)```/g,
95
- };
96
- /**
97
- * ObjectQL AI Agent for programmatic application generation and validation
98
- */
99
- class ObjectQLAgent {
100
- constructor(config) {
101
- var _a;
102
- this.config = {
103
- apiKey: config.apiKey,
104
- model: config.model || 'gpt-4',
105
- temperature: (_a = config.temperature) !== null && _a !== void 0 ? _a : 0.7,
106
- language: config.language || 'en',
107
- };
108
- this.openai = new openai_1.default({ apiKey: this.config.apiKey });
109
- this.validator = new validator_1.Validator({ language: this.config.language });
110
- }
111
- /**
112
- * Generate application metadata from natural language description
113
- */
114
- async generateApp(options) {
115
- var _a, _b;
116
- const systemPrompt = this.getSystemPrompt();
117
- const userPrompt = this.buildGenerationPrompt(options);
118
- try {
119
- const completion = await this.openai.chat.completions.create({
120
- model: this.config.model,
121
- messages: [
122
- { role: 'system', content: systemPrompt },
123
- { role: 'user', content: userPrompt }
124
- ],
125
- temperature: this.config.temperature,
126
- max_tokens: options.maxTokens || 4000,
127
- });
128
- const response = (_b = (_a = completion.choices[0]) === null || _a === void 0 ? void 0 : _a.message) === null || _b === void 0 ? void 0 : _b.content;
129
- if (!response) {
130
- return {
131
- success: false,
132
- files: [],
133
- errors: ['No response from AI model'],
134
- };
135
- }
136
- // Parse response and extract files
137
- const files = this.parseGenerationResponse(response);
138
- return {
139
- success: files.length > 0,
140
- files,
141
- rawResponse: response,
142
- errors: files.length === 0 ? ['Failed to extract metadata files from response'] : undefined,
143
- };
144
- }
145
- catch (error) {
146
- return {
147
- success: false,
148
- files: [],
149
- errors: [error instanceof Error ? error.message : 'Unknown error'],
150
- };
151
- }
152
- }
153
- /**
154
- * Validate metadata using AI
155
- */
156
- async validateMetadata(options) {
157
- var _a, _b;
158
- // Parse metadata if it's a string
159
- let parsedMetadata;
160
- if (typeof options.metadata === 'string') {
161
- try {
162
- parsedMetadata = yaml.load(options.metadata);
163
- }
164
- catch (error) {
165
- return {
166
- valid: false,
167
- errors: [{
168
- message: `YAML parsing error: ${error instanceof Error ? error.message : 'Invalid YAML'}`,
169
- location: 'root',
170
- }],
171
- warnings: [],
172
- info: [],
173
- };
174
- }
175
- }
176
- else {
177
- parsedMetadata = options.metadata;
178
- }
179
- // Build validation prompt
180
- const validationPrompt = this.buildValidationPrompt(options);
181
- try {
182
- const completion = await this.openai.chat.completions.create({
183
- model: this.config.model,
184
- messages: [
185
- { role: 'system', content: this.getValidationSystemPrompt() },
186
- { role: 'user', content: validationPrompt }
187
- ],
188
- temperature: 0.3, // Lower temperature for more consistent validation
189
- max_tokens: 2000,
190
- });
191
- const feedback = ((_b = (_a = completion.choices[0]) === null || _a === void 0 ? void 0 : _a.message) === null || _b === void 0 ? void 0 : _b.content) || '';
192
- // Parse feedback into structured result
193
- return this.parseFeedback(feedback);
194
- }
195
- catch (error) {
196
- return {
197
- valid: false,
198
- errors: [{
199
- message: `Validation error: ${error instanceof Error ? error.message : 'Unknown error'}`,
200
- }],
201
- warnings: [],
202
- info: [],
203
- };
204
- }
205
- }
206
- /**
207
- * Refine existing metadata based on feedback
208
- */
209
- async refineMetadata(metadata, feedback, iterations = 1) {
210
- var _a, _b;
211
- const systemPrompt = this.getSystemPrompt();
212
- let currentMetadata = metadata;
213
- let messages = [
214
- { role: 'system', content: systemPrompt },
215
- { role: 'user', content: `Here is the current metadata:\n\n${metadata}\n\nPlease refine it based on this feedback: ${feedback}` }
216
- ];
217
- for (let i = 0; i < iterations; i++) {
218
- try {
219
- const completion = await this.openai.chat.completions.create({
220
- model: this.config.model,
221
- messages,
222
- temperature: 0.5,
223
- max_tokens: 4000,
224
- });
225
- const response = (_b = (_a = completion.choices[0]) === null || _a === void 0 ? void 0 : _a.message) === null || _b === void 0 ? void 0 : _b.content;
226
- if (!response)
227
- break;
228
- currentMetadata = response;
229
- messages.push({ role: 'assistant', content: response });
230
- // If this isn't the last iteration, validate and continue
231
- if (i < iterations - 1) {
232
- const validation = await this.validateMetadata({
233
- metadata: response,
234
- checkBusinessLogic: true,
235
- });
236
- if (validation.valid) {
237
- break; // Stop if validation passes
238
- }
239
- // Add validation feedback for next iteration
240
- const validationFeedback = [
241
- ...validation.errors.map(e => `ERROR: ${e.message}`),
242
- ...validation.warnings.map(w => `WARNING: ${w.message}`)
243
- ].join('\n');
244
- messages.push({
245
- role: 'user',
246
- content: `Please address these issues:\n${validationFeedback}`
247
- });
248
- }
249
- }
250
- catch (error) {
251
- return {
252
- success: false,
253
- files: [],
254
- errors: [error instanceof Error ? error.message : 'Unknown error'],
255
- };
256
- }
257
- }
258
- const files = this.parseGenerationResponse(currentMetadata);
259
- return {
260
- success: files.length > 0,
261
- files,
262
- rawResponse: currentMetadata,
263
- };
264
- }
265
- /**
266
- * Conversational generation with step-by-step refinement
267
- * This allows users to iteratively improve the application through dialogue
268
- */
269
- async generateConversational(options) {
270
- var _a, _b;
271
- const systemPrompt = this.getSystemPrompt();
272
- // Initialize or continue conversation
273
- const messages = [
274
- { role: 'system', content: systemPrompt }
275
- ];
276
- // Add conversation history if provided
277
- if (options.conversationHistory) {
278
- messages.push(...options.conversationHistory.filter(m => m.role !== 'system'));
279
- }
280
- // Build the user message
281
- let userMessage = options.message;
282
- // If there's a current app state, include it in context
283
- if (options.currentApp && options.currentApp.files.length > 0) {
284
- const currentState = options.currentApp.files
285
- .map(f => `# ${f.filename}\n${f.content}`)
286
- .join('\n\n---\n\n');
287
- userMessage = `Current application state:\n\n${currentState}\n\n---\n\nUser request: ${options.message}\n\nPlease update the application according to the user's request. Provide the complete updated files.`;
288
- }
289
- messages.push({ role: 'user', content: userMessage });
290
- try {
291
- const completion = await this.openai.chat.completions.create({
292
- model: this.config.model,
293
- messages,
294
- temperature: this.config.temperature,
295
- max_tokens: 4000,
296
- });
297
- const response = (_b = (_a = completion.choices[0]) === null || _a === void 0 ? void 0 : _a.message) === null || _b === void 0 ? void 0 : _b.content;
298
- if (!response) {
299
- return {
300
- success: false,
301
- files: [],
302
- conversationHistory: [...(options.conversationHistory || []),
303
- { role: 'user', content: options.message }],
304
- errors: ['No response from AI model'],
305
- };
306
- }
307
- // Parse the response
308
- const files = this.parseGenerationResponse(response);
309
- // Update conversation history
310
- const updatedHistory = [
311
- ...(options.conversationHistory || []),
312
- { role: 'user', content: options.message },
313
- { role: 'assistant', content: response }
314
- ];
315
- // Generate suggestions for next steps
316
- const suggestions = this.generateSuggestions(files, options.currentApp);
317
- return {
318
- success: files.length > 0,
319
- files,
320
- rawResponse: response,
321
- conversationHistory: updatedHistory,
322
- suggestions,
323
- errors: files.length === 0 ? ['Failed to extract metadata files from response'] : undefined,
324
- };
325
- }
326
- catch (error) {
327
- return {
328
- success: false,
329
- files: [],
330
- conversationHistory: [...(options.conversationHistory || []),
331
- { role: 'user', content: options.message }],
332
- errors: [error instanceof Error ? error.message : 'Unknown error'],
333
- };
334
- }
335
- }
336
- /**
337
- * Generate suggestions for next steps based on current application state
338
- */
339
- generateSuggestions(currentFiles, previousApp) {
340
- const suggestions = [];
341
- // Check what metadata types are missing
342
- const fileTypes = new Set(currentFiles.map(f => f.type));
343
- const allTypes = [
344
- 'object', 'validation', 'action', 'hook', 'permission', 'workflow', 'data'
345
- ];
346
- const missingTypes = allTypes.filter(t => !fileTypes.has(t));
347
- if (missingTypes.length > 0) {
348
- suggestions.push(`Consider adding: ${missingTypes.join(', ')}`);
349
- }
350
- if (!fileTypes.has('permission')) {
351
- suggestions.push('Add permissions to control access');
352
- }
353
- if (!fileTypes.has('workflow') && fileTypes.has('object')) {
354
- suggestions.push('Add workflows for approval processes');
355
- }
356
- return suggestions;
357
- }
358
- /**
359
- * Get system prompt for metadata generation
360
- */
361
- getSystemPrompt() {
362
- return `You are an expert ObjectQL architect. Generate valid ObjectQL metadata in YAML format AND TypeScript implementation files for business logic.
363
-
364
- Follow ObjectQL metadata standards for ALL metadata types:
365
-
366
- **1. Core Data Layer:**
367
- - Objects (*.object.yml): entities, fields, relationships, indexes
368
- - Validations (*.validation.yml): validation rules, business constraints
369
- - Data (*.data.yml): seed data and initial records
370
-
371
- **2. Business Logic Layer (YAML + TypeScript):**
372
- - Actions (*.action.yml + *.action.ts): custom RPC operations with TypeScript implementation
373
- - Hooks (*.hook.yml + *.hook.ts): lifecycle triggers with TypeScript implementation
374
- - beforeCreate, afterCreate, beforeUpdate, afterUpdate, beforeDelete, afterDelete
375
- - Workflows (*.workflow.yml): approval processes, automation
376
-
377
- **3. Security Layer:**
378
- - Permissions (*.permission.yml): access control rules
379
- - Application (*.application.yml): app-level configuration
380
-
381
- **Field Types:** text, number, boolean, select, date, datetime, lookup, currency, email, phone, url, textarea, formula, file, image
382
-
383
- **For Actions - Generate BOTH files:**
384
- Example:
385
- # approve_order.action.yml
386
- \`\`\`yaml
387
- label: Approve Order
388
- type: record
389
- params:
390
- comment:
391
- type: textarea
392
- label: Comment
393
- \`\`\`
394
-
395
- # approve_order.action.ts
396
- \`\`\`typescript
397
- import { ActionContext } from '@objectql/types';
398
-
399
- export default async function approveOrder(context: ActionContext) {
400
- const { recordId, params, user, app } = context;
401
-
402
- // Business logic here
403
- const record = await app.findOne('orders', { _id: recordId });
404
-
405
- if (!record) {
406
- throw new Error('Order not found');
407
- }
408
-
409
- await app.update('orders', recordId, {
410
- status: 'approved',
411
- approved_by: user.id,
412
- approved_at: new Date(),
413
- approval_comment: params.comment
414
- });
415
-
416
- return { success: true, message: 'Order approved successfully' };
417
- }
418
- \`\`\`
419
-
420
- **For Hooks - Generate BOTH files:**
421
- Example:
422
- # user.hook.yml
423
- \`\`\`yaml
424
- triggers:
425
- - beforeCreate
426
- - beforeUpdate
427
- \`\`\`
428
-
429
- # user.hook.ts
430
- \`\`\`typescript
431
- import { HookContext } from '@objectql/types';
432
-
433
- export async function beforeCreate(context: HookContext) {
434
- const { data, user } = context;
435
-
436
- // Auto-assign creator
437
- data.created_by = user.id;
438
- data.created_at = new Date();
439
-
440
- // Validate email uniqueness (example)
441
- const existing = await context.app.findOne('users', { email: data.email });
442
- if (existing) {
443
- throw new Error('Email already exists');
444
- }
445
- }
446
-
447
- export async function beforeUpdate(context: HookContext) {
448
- const { data, previousData, user } = context;
449
-
450
- data.updated_by = user.id;
451
- data.updated_at = new Date();
452
- }
453
- \`\`\`
454
-
455
- **For Tests - Generate test files:**
456
- Example:
457
- // approve_order.test.ts
458
- \`\`\`typescript
459
- import { describe, it, expect, beforeEach } from '@jest/globals';
460
- import { ObjectQL } from '@objectql/core';
461
- import approveOrder from './approve_order.action';
462
-
463
- describe('approve_order action', () => {
464
- let app: ObjectQL;
465
- let testUser: any;
466
-
467
- beforeEach(async () => {
468
- // Setup test environment
469
- app = new ObjectQL(/* test config */);
470
- await app.connect();
471
- testUser = { id: 'user123', name: 'Test User' };
472
- });
473
-
474
- it('should approve an order successfully', async () => {
475
- // Create test order
476
- const order = await app.create('orders', {
477
- status: 'pending',
478
- total: 100
479
- });
480
-
481
- // Execute action
482
- const result = await approveOrder({
483
- recordId: order.id,
484
- params: { comment: 'Approved' },
485
- user: testUser,
486
- app
487
- });
488
-
489
- // Verify
490
- expect(result.success).toBe(true);
491
- const updated = await app.findOne('orders', { _id: order.id });
492
- expect(updated.status).toBe('approved');
493
- });
494
-
495
- it('should reject if order not found', async () => {
496
- await expect(approveOrder({
497
- recordId: 'invalid_id',
498
- params: { comment: 'Test' },
499
- user: testUser,
500
- app
501
- })).rejects.toThrow('Order not found');
502
- });
503
- });
504
- \`\`\`
505
-
506
- **Best Practices:**
507
- - Use snake_case for names
508
- - Clear, business-friendly labels
509
- - Include validation rules
510
- - Add help text for clarity
511
- - Define proper relationships
512
- - Consider security from the start
513
- - Implement actual business logic in TypeScript files
514
- - Include error handling in implementations
515
- - Add comments explaining complex logic
516
- - Write comprehensive tests for all business logic
517
- - Test both success and failure cases
518
-
519
- Output format: Provide each file in code blocks with filename headers (e.g., "# filename.object.yml" or "// filename.action.ts").`;
520
- }
521
- /**
522
- * Get system prompt for validation
523
- */
524
- getValidationSystemPrompt() {
525
- return `You are an expert ObjectQL metadata validator. Analyze metadata for:
526
- 1. YAML structure and syntax
527
- 2. ObjectQL specification compliance
528
- 3. Business logic consistency
529
- 4. Data modeling best practices
530
- 5. Security considerations
531
- 6. Performance implications
532
-
533
- Provide feedback in this format:
534
- - [ERROR] Location: Issue description
535
- - [WARNING] Location: Issue description
536
- - [INFO] Location: Suggestion`;
537
- }
538
- /**
539
- * Build generation prompt based on options
540
- */
541
- buildGenerationPrompt(options) {
542
- const { description, type = 'custom' } = options;
543
- switch (type) {
544
- case 'basic':
545
- return `Generate a minimal ObjectQL application for: ${description}
546
-
547
- Include:
548
- - 2-3 core objects with essential fields
549
- - Basic relationships between objects
550
- - Simple validation rules
551
- - At least one action with TypeScript implementation
552
- - At least one hook with TypeScript implementation
553
-
554
- Output: Provide each file separately with clear filename headers (e.g., "# filename.object.yml" or "// filename.action.ts").`;
555
- case 'complete':
556
- return `Generate a complete ObjectQL enterprise application for: ${description}
557
-
558
- Include ALL necessary metadata types WITH implementations:
559
- 1. **Objects**: All entities with comprehensive fields
560
- 2. **Validations**: Business rules and constraints
561
- 3. **Actions WITH TypeScript implementations**: Common operations (approve, export, etc.) - Generate BOTH .yml metadata AND .action.ts implementation files
562
- 4. **Hooks WITH TypeScript implementations**: Lifecycle triggers - Generate .hook.ts implementation files
563
- 5. **Permissions**: Basic access control
564
- 6. **Data**: Sample seed data (optional)
565
- 7. **Workflows**: Approval processes if applicable
566
- 8. **Tests**: Generate test files (.test.ts) for actions and hooks to validate business logic
567
-
568
- Consider:
569
- - Security and permissions from the start
570
- - Business processes and workflows
571
- - Data integrity and validation
572
- - Complete TypeScript implementations for all actions and hooks
573
- - Test coverage for business logic
574
-
575
- Output: Provide each file separately with clear filename headers (e.g., "# filename.object.yml" or "// filename.action.ts").`;
576
- default:
577
- return `Generate ObjectQL metadata for: ${description}
578
-
579
- Analyze the requirements and create appropriate metadata across ALL relevant types:
580
- - Objects, Validations, Forms, Views, Pages, Menus, Actions, Hooks, Permissions, Workflows, Reports, Data, Application
581
- - For Actions and Hooks: Generate BOTH YAML metadata AND TypeScript implementation files
582
- - Include test files to validate business logic
583
-
584
- Output: Provide each file separately with clear filename headers (e.g., "# filename.object.yml" or "// filename.action.ts").`;
585
- }
586
- }
587
- /**
588
- * Build validation prompt
589
- */
590
- buildValidationPrompt(options) {
591
- const metadataStr = typeof options.metadata === 'string'
592
- ? options.metadata
593
- : yaml.dump(options.metadata);
594
- const checks = [];
595
- if (options.checkBusinessLogic !== false)
596
- checks.push('Business logic consistency');
597
- if (options.checkPerformance)
598
- checks.push('Performance considerations');
599
- if (options.checkSecurity)
600
- checks.push('Security issues');
601
- return `Validate this ObjectQL metadata file:
602
-
603
- ${options.filename ? `Filename: ${options.filename}\n` : ''}
604
- Content:
605
- \`\`\`yaml
606
- ${metadataStr}
607
- \`\`\`
608
-
609
- Check for:
610
- - YAML syntax and structure
611
- - ObjectQL specification compliance
612
- ${checks.length > 0 ? '- ' + checks.join('\n- ') : ''}
613
-
614
- Provide feedback in the specified format.`;
615
- }
616
- /**
617
- * Parse generation response and extract files
618
- */
619
- parseGenerationResponse(response) {
620
- const files = [];
621
- let match;
622
- // Extract YAML files with explicit headers
623
- while ((match = AI_RESPONSE_PATTERNS.FILE_BLOCK_YAML.exec(response)) !== null) {
624
- const filename = match[1];
625
- const content = match[2].trim();
626
- const type = this.inferFileType(filename);
627
- files.push({ filename, content, type });
628
- }
629
- // Extract TypeScript files with explicit headers
630
- while ((match = AI_RESPONSE_PATTERNS.FILE_BLOCK_TS.exec(response)) !== null) {
631
- const filename = match[1];
632
- const content = match[2].trim();
633
- const type = this.inferFileType(filename);
634
- files.push({ filename, content, type });
635
- }
636
- // Fallback: Generic code blocks if no explicit headers found
637
- if (files.length === 0) {
638
- let yamlIndex = 0;
639
- let tsIndex = 0;
640
- // Try to extract generic YAML blocks
641
- while ((match = AI_RESPONSE_PATTERNS.CODE_BLOCK_YAML.exec(response)) !== null) {
642
- const content = match[1].trim();
643
- const filename = `generated_${yamlIndex}.object.yml`;
644
- files.push({ filename, content, type: 'object' });
645
- yamlIndex++;
646
- }
647
- // Try to extract generic TypeScript blocks
648
- while ((match = AI_RESPONSE_PATTERNS.CODE_BLOCK_TS.exec(response)) !== null) {
649
- const content = match[1].trim();
650
- // Use generic .ts extension since we can't determine the specific type
651
- const filename = `generated_${tsIndex}.ts`;
652
- files.push({ filename, content, type: 'typescript' });
653
- tsIndex++;
654
- }
655
- }
656
- return files;
657
- }
658
- /**
659
- * Parse validation feedback into structured result
660
- */
661
- parseFeedback(feedback) {
662
- const errors = [];
663
- const warnings = [];
664
- const info = [];
665
- const lines = feedback.split('\n');
666
- for (let i = 0; i < lines.length; i++) {
667
- const line = lines[i];
668
- if (line.includes('[ERROR]')) {
669
- const message = line.replace(/^\s*-?\s*\[ERROR\]\s*/, '');
670
- errors.push({ message });
671
- }
672
- else if (line.includes('[WARNING]')) {
673
- const message = line.replace(/^\s*-?\s*\[WARNING\]\s*/, '');
674
- warnings.push({ message });
675
- }
676
- else if (line.includes('[INFO]')) {
677
- const message = line.replace(/^\s*-?\s*\[INFO\]\s*/, '');
678
- info.push({ message });
679
- }
680
- }
681
- return {
682
- valid: errors.length === 0,
683
- errors,
684
- warnings,
685
- info,
686
- };
687
- }
688
- /**
689
- * Infer file type from filename
690
- */
691
- inferFileType(filename) {
692
- if (filename.includes('.object.yml'))
693
- return 'object';
694
- if (filename.includes('.validation.yml'))
695
- return 'validation';
696
- if (filename.includes('.action.yml'))
697
- return 'action';
698
- if (filename.includes('.hook.yml'))
699
- return 'hook';
700
- if (filename.includes('.permission.yml'))
701
- return 'permission';
702
- if (filename.includes('.workflow.yml'))
703
- return 'workflow';
704
- if (filename.includes('.data.yml'))
705
- return 'data';
706
- if (filename.includes('.application.yml') || filename.includes('.app.yml'))
707
- return 'application';
708
- if (filename.includes('.action.ts') || filename.includes('.hook.ts'))
709
- return 'typescript';
710
- if (filename.includes('.test.ts') || filename.includes('.spec.ts'))
711
- return 'test';
712
- return 'other';
713
- }
714
- }
715
- exports.ObjectQLAgent = ObjectQLAgent;
716
- /**
717
- * Convenience function to create an agent instance
718
- */
719
- function createAgent(apiKey, options) {
720
- return new ObjectQLAgent({ apiKey, ...options });
721
- }
722
- //# sourceMappingURL=ai-agent.js.map