@unrdf/kgc-runtime 26.4.2
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/IMPLEMENTATION_SUMMARY.json +150 -0
- package/PLUGIN_SYSTEM_SUMMARY.json +149 -0
- package/README.md +98 -0
- package/TRANSACTION_IMPLEMENTATION.json +119 -0
- package/capability-map.md +93 -0
- package/docs/api-stability.md +269 -0
- package/docs/extensions/plugin-development.md +382 -0
- package/package.json +40 -0
- package/plugins/registry.json +35 -0
- package/src/admission-gate.mjs +414 -0
- package/src/api-version.mjs +373 -0
- package/src/atomic-admission.mjs +310 -0
- package/src/bounds.mjs +289 -0
- package/src/bulkhead-manager.mjs +280 -0
- package/src/capsule.mjs +524 -0
- package/src/crdt.mjs +361 -0
- package/src/enhanced-bounds.mjs +614 -0
- package/src/executor.mjs +73 -0
- package/src/freeze-restore.mjs +521 -0
- package/src/index.mjs +62 -0
- package/src/materialized-views.mjs +371 -0
- package/src/merge.mjs +472 -0
- package/src/plugin-isolation.mjs +392 -0
- package/src/plugin-manager.mjs +441 -0
- package/src/projections-api.mjs +336 -0
- package/src/projections-cli.mjs +238 -0
- package/src/projections-docs.mjs +300 -0
- package/src/projections-ide.mjs +278 -0
- package/src/receipt.mjs +340 -0
- package/src/rollback.mjs +258 -0
- package/src/saga-orchestrator.mjs +355 -0
- package/src/schemas.mjs +1330 -0
- package/src/storage-optimization.mjs +359 -0
- package/src/tool-registry.mjs +272 -0
- package/src/transaction.mjs +466 -0
- package/src/validators.mjs +485 -0
- package/src/work-item.mjs +449 -0
- package/templates/plugin-template/README.md +58 -0
- package/templates/plugin-template/index.mjs +162 -0
- package/templates/plugin-template/plugin.json +19 -0
- package/test/admission-gate.test.mjs +583 -0
- package/test/api-version.test.mjs +74 -0
- package/test/atomic-admission.test.mjs +155 -0
- package/test/bounds.test.mjs +341 -0
- package/test/bulkhead-manager.test.mjs +236 -0
- package/test/capsule.test.mjs +625 -0
- package/test/crdt.test.mjs +215 -0
- package/test/enhanced-bounds.test.mjs +487 -0
- package/test/freeze-restore.test.mjs +472 -0
- package/test/materialized-views.test.mjs +243 -0
- package/test/merge.test.mjs +665 -0
- package/test/plugin-isolation.test.mjs +109 -0
- package/test/plugin-manager.test.mjs +208 -0
- package/test/projections-api.test.mjs +293 -0
- package/test/projections-cli.test.mjs +204 -0
- package/test/projections-docs.test.mjs +173 -0
- package/test/projections-ide.test.mjs +230 -0
- package/test/receipt.test.mjs +295 -0
- package/test/rollback.test.mjs +132 -0
- package/test/saga-orchestrator.test.mjs +279 -0
- package/test/schemas.test.mjs +716 -0
- package/test/storage-optimization.test.mjs +503 -0
- package/test/tool-registry.test.mjs +341 -0
- package/test/transaction.test.mjs +189 -0
- package/test/validators.test.mjs +463 -0
- package/test/work-item.test.mjs +548 -0
- package/test/work-item.test.mjs.bak +548 -0
- package/var/kgc/test-atomic-log.json +519 -0
- package/var/kgc/test-cascading-log.json +145 -0
- package/vitest.config.mjs +18 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for CLI Projections (Π_cli)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from 'vitest';
|
|
6
|
+
import {
|
|
7
|
+
projectReceiptToCLI,
|
|
8
|
+
projectWorkItemsToCLI,
|
|
9
|
+
projectObjectToTree,
|
|
10
|
+
projectArrayToList,
|
|
11
|
+
flattenForCLI,
|
|
12
|
+
CLIProjectionSchema,
|
|
13
|
+
} from '../src/projections-cli.mjs';
|
|
14
|
+
|
|
15
|
+
describe('Π_cli - CLI Projections', () => {
|
|
16
|
+
describe('projectReceiptToCLI', () => {
|
|
17
|
+
it('should project receipt to CLI format with colors', () => {
|
|
18
|
+
const receipt = {
|
|
19
|
+
id: 'receipt-001',
|
|
20
|
+
timestamp: '2024-01-01T00:00:00Z',
|
|
21
|
+
operation: 'test-operation',
|
|
22
|
+
inputs: { x: 1 },
|
|
23
|
+
outputs: { y: 2 },
|
|
24
|
+
hash: '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const projection = projectReceiptToCLI(receipt, true);
|
|
28
|
+
|
|
29
|
+
expect(projection.type).toBe('cli');
|
|
30
|
+
expect(projection.format).toBe('summary');
|
|
31
|
+
expect(projection.colored).toBe(true);
|
|
32
|
+
expect(projection.content).toContain('receipt-001');
|
|
33
|
+
expect(projection.content).toContain('test-operation');
|
|
34
|
+
expect(projection.metadata.receiptId).toBe('receipt-001');
|
|
35
|
+
|
|
36
|
+
// Validate schema
|
|
37
|
+
CLIProjectionSchema.parse(projection);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should project receipt without colors', () => {
|
|
41
|
+
const receipt = {
|
|
42
|
+
id: 'receipt-002',
|
|
43
|
+
timestamp: '2024-01-01T00:00:00Z',
|
|
44
|
+
operation: 'test-op',
|
|
45
|
+
inputs: {},
|
|
46
|
+
outputs: {},
|
|
47
|
+
hash: 'a'.repeat(64),
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const projection = projectReceiptToCLI(receipt, false);
|
|
51
|
+
|
|
52
|
+
expect(projection.colored).toBe(false);
|
|
53
|
+
expect(projection.content).not.toContain('\x1b['); // No ANSI codes
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should include parent hash if present', () => {
|
|
57
|
+
const receipt = {
|
|
58
|
+
id: 'receipt-003',
|
|
59
|
+
timestamp: '2024-01-01T00:00:00Z',
|
|
60
|
+
operation: 'test',
|
|
61
|
+
inputs: {},
|
|
62
|
+
outputs: {},
|
|
63
|
+
hash: 'a'.repeat(64),
|
|
64
|
+
parentHash: 'b'.repeat(64),
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const projection = projectReceiptToCLI(receipt, false);
|
|
68
|
+
|
|
69
|
+
expect(projection.content).toContain('Parent:');
|
|
70
|
+
expect(projection.content).toContain('bbbbbbbbbbbbbbbb');
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('projectWorkItemsToCLI', () => {
|
|
75
|
+
it('should project work items to table format', () => {
|
|
76
|
+
const workItems = [
|
|
77
|
+
{ id: 'item-001', goal: 'Process data', state: 'completed', priority: 1 },
|
|
78
|
+
{ id: 'item-002', goal: 'Generate report', state: 'running', priority: 2 },
|
|
79
|
+
{ id: 'item-003', goal: 'Send notification', state: 'failed', priority: 0 },
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
const projection = projectWorkItemsToCLI(workItems, false);
|
|
83
|
+
|
|
84
|
+
expect(projection.type).toBe('cli');
|
|
85
|
+
expect(projection.format).toBe('table');
|
|
86
|
+
expect(projection.content).toContain('ID');
|
|
87
|
+
expect(projection.content).toContain('Goal');
|
|
88
|
+
expect(projection.content).toContain('State');
|
|
89
|
+
expect(projection.content).toContain('Priority');
|
|
90
|
+
expect(projection.metadata.count).toBe(3);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should handle empty work items array', () => {
|
|
94
|
+
const projection = projectWorkItemsToCLI([], false);
|
|
95
|
+
|
|
96
|
+
expect(projection.content).toContain('No work items');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should use colors for different states', () => {
|
|
100
|
+
const workItems = [
|
|
101
|
+
{ id: 'item-001', goal: 'Task', state: 'completed', priority: 1 },
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
const projection = projectWorkItemsToCLI(workItems, true);
|
|
105
|
+
|
|
106
|
+
expect(projection.colored).toBe(true);
|
|
107
|
+
expect(projection.content).toContain('\x1b['); // Has ANSI codes
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe('projectObjectToTree', () => {
|
|
112
|
+
it('should project nested object to tree format', () => {
|
|
113
|
+
const obj = {
|
|
114
|
+
root: {
|
|
115
|
+
child1: {
|
|
116
|
+
grandchild: 'value',
|
|
117
|
+
},
|
|
118
|
+
child2: 'simple',
|
|
119
|
+
},
|
|
120
|
+
array: [1, 2, 3],
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const projection = projectObjectToTree(obj, false);
|
|
124
|
+
|
|
125
|
+
expect(projection.type).toBe('cli');
|
|
126
|
+
expect(projection.format).toBe('tree');
|
|
127
|
+
expect(projection.content).toContain('root');
|
|
128
|
+
expect(projection.content).toContain('child1');
|
|
129
|
+
expect(projection.content).toContain('grandchild');
|
|
130
|
+
expect(projection.content).toContain('└──');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('should handle arrays in tree', () => {
|
|
134
|
+
const obj = {
|
|
135
|
+
items: [1, 2, 3],
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const projection = projectObjectToTree(obj, false);
|
|
139
|
+
|
|
140
|
+
expect(projection.content).toContain('[3 items]');
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe('projectArrayToList', () => {
|
|
145
|
+
it('should project string array to numbered list', () => {
|
|
146
|
+
const items = ['First item', 'Second item', 'Third item'];
|
|
147
|
+
|
|
148
|
+
const projection = projectArrayToList(items, false);
|
|
149
|
+
|
|
150
|
+
expect(projection.type).toBe('cli');
|
|
151
|
+
expect(projection.format).toBe('list');
|
|
152
|
+
expect(projection.content).toContain('1. First item');
|
|
153
|
+
expect(projection.content).toContain('2. Second item');
|
|
154
|
+
expect(projection.content).toContain('3. Third item');
|
|
155
|
+
expect(projection.metadata.count).toBe(3);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('should project object array with labels and descriptions', () => {
|
|
159
|
+
const items = [
|
|
160
|
+
{ label: 'Task 1', description: 'Do something' },
|
|
161
|
+
{ label: 'Task 2', description: 'Do something else' },
|
|
162
|
+
];
|
|
163
|
+
|
|
164
|
+
const projection = projectArrayToList(items, false);
|
|
165
|
+
|
|
166
|
+
expect(projection.content).toContain('Task 1');
|
|
167
|
+
expect(projection.content).toContain('Do something');
|
|
168
|
+
expect(projection.content).toContain('Task 2');
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
describe('flattenForCLI', () => {
|
|
173
|
+
it('should flatten nested object with dot notation', () => {
|
|
174
|
+
const obj = {
|
|
175
|
+
user: {
|
|
176
|
+
name: 'Alice',
|
|
177
|
+
address: {
|
|
178
|
+
city: 'NYC',
|
|
179
|
+
zip: '10001',
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
count: 42,
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const flattened = flattenForCLI(obj);
|
|
186
|
+
|
|
187
|
+
expect(flattened['user.name']).toBe('Alice');
|
|
188
|
+
expect(flattened['user.address.city']).toBe('NYC');
|
|
189
|
+
expect(flattened['user.address.zip']).toBe('10001');
|
|
190
|
+
expect(flattened['count']).toBe(42);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('should handle arrays without flattening', () => {
|
|
194
|
+
const obj = {
|
|
195
|
+
items: [1, 2, 3],
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const flattened = flattenForCLI(obj);
|
|
199
|
+
|
|
200
|
+
expect(Array.isArray(flattened.items)).toBe(true);
|
|
201
|
+
expect(flattened.items).toEqual([1, 2, 3]);
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
});
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Documentation Projections (Π_docs)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from 'vitest';
|
|
6
|
+
import {
|
|
7
|
+
projectReceiptToDocs,
|
|
8
|
+
projectSchemaToDocs,
|
|
9
|
+
projectFunctionToDocs,
|
|
10
|
+
projectWorkflowToDocs,
|
|
11
|
+
generateCrossRefs,
|
|
12
|
+
generateTableOfContents,
|
|
13
|
+
DocsProjectionSchema,
|
|
14
|
+
} from '../src/projections-docs.mjs';
|
|
15
|
+
import { z } from 'zod';
|
|
16
|
+
|
|
17
|
+
describe('Π_docs - Documentation Projections', () => {
|
|
18
|
+
describe('projectReceiptToDocs', () => {
|
|
19
|
+
it('should project receipt to markdown documentation', () => {
|
|
20
|
+
const receipt = {
|
|
21
|
+
id: 'receipt-001',
|
|
22
|
+
timestamp: '2024-01-01T00:00:00Z',
|
|
23
|
+
operation: 'test-operation',
|
|
24
|
+
inputs: { x: 1, y: 2 },
|
|
25
|
+
outputs: { result: 3 },
|
|
26
|
+
hash: '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const projection = projectReceiptToDocs(receipt);
|
|
30
|
+
|
|
31
|
+
expect(projection.type).toBe('docs');
|
|
32
|
+
expect(projection.format).toBe('markdown');
|
|
33
|
+
expect(projection.content).toContain('# Receipt Documentation');
|
|
34
|
+
expect(projection.content).toContain('receipt-001');
|
|
35
|
+
expect(projection.content).toContain('test-operation');
|
|
36
|
+
expect(projection.frontMatter.id).toBe('receipt-001');
|
|
37
|
+
expect(projection.sections).toHaveLength(4);
|
|
38
|
+
|
|
39
|
+
// Validate schema
|
|
40
|
+
DocsProjectionSchema.parse(projection);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should include parent hash if present', () => {
|
|
44
|
+
const receipt = {
|
|
45
|
+
id: 'receipt-002',
|
|
46
|
+
timestamp: '2024-01-01T00:00:00Z',
|
|
47
|
+
operation: 'test',
|
|
48
|
+
inputs: {},
|
|
49
|
+
outputs: {},
|
|
50
|
+
hash: 'a'.repeat(64),
|
|
51
|
+
parentHash: 'b'.repeat(64),
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const projection = projectReceiptToDocs(receipt);
|
|
55
|
+
|
|
56
|
+
expect(projection.content).toContain('Parent Hash');
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('projectSchemaToDocs', () => {
|
|
61
|
+
it('should project Zod schema to documentation', () => {
|
|
62
|
+
const TestSchema = z.object({
|
|
63
|
+
name: z.string(),
|
|
64
|
+
age: z.number(),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const example = { name: 'Alice', age: 30 };
|
|
68
|
+
|
|
69
|
+
const projection = projectSchemaToDocs('TestSchema', TestSchema, example);
|
|
70
|
+
|
|
71
|
+
expect(projection.type).toBe('docs');
|
|
72
|
+
expect(projection.content).toContain('# TestSchema Documentation');
|
|
73
|
+
expect(projection.content).toContain('```json');
|
|
74
|
+
expect(projection.content).toContain('Alice');
|
|
75
|
+
expect(projection.content).toContain('## Example');
|
|
76
|
+
expect(projection.content).toContain('## Usage');
|
|
77
|
+
expect(projection.frontMatter.schema).toBe('TestSchema');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should generate docs without example', () => {
|
|
81
|
+
const TestSchema = z.string();
|
|
82
|
+
|
|
83
|
+
const projection = projectSchemaToDocs('SimpleSchema', TestSchema);
|
|
84
|
+
|
|
85
|
+
expect(projection.content).toContain('# SimpleSchema Documentation');
|
|
86
|
+
expect(projection.content).not.toContain('## Example');
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('projectFunctionToDocs', () => {
|
|
91
|
+
it('should project function to API documentation', () => {
|
|
92
|
+
const fnInfo = {
|
|
93
|
+
name: 'calculateSum',
|
|
94
|
+
description: 'Calculates the sum of two numbers',
|
|
95
|
+
params: [
|
|
96
|
+
{ name: 'a', type: 'number', description: 'First number' },
|
|
97
|
+
{ name: 'b', type: 'number', description: 'Second number' },
|
|
98
|
+
],
|
|
99
|
+
returns: { type: 'number', description: 'The sum of a and b' },
|
|
100
|
+
example: 'const result = calculateSum(1, 2); // 3',
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const projection = projectFunctionToDocs(null, fnInfo);
|
|
104
|
+
|
|
105
|
+
expect(projection.type).toBe('docs');
|
|
106
|
+
expect(projection.content).toContain('# calculateSum()');
|
|
107
|
+
expect(projection.content).toContain('## Description');
|
|
108
|
+
expect(projection.content).toContain('## Parameters');
|
|
109
|
+
expect(projection.content).toContain('## Returns');
|
|
110
|
+
expect(projection.content).toContain('## Example');
|
|
111
|
+
expect(projection.content).toContain('First number');
|
|
112
|
+
expect(projection.frontMatter.function).toBe('calculateSum');
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe('projectWorkflowToDocs', () => {
|
|
117
|
+
it('should project workflow to documentation', () => {
|
|
118
|
+
const workflow = {
|
|
119
|
+
id: 'wf-001',
|
|
120
|
+
name: 'Data Processing Workflow',
|
|
121
|
+
description: 'Processes incoming data through multiple stages',
|
|
122
|
+
steps: [
|
|
123
|
+
{ id: 'step-1', type: 'validate', description: 'Validate input data' },
|
|
124
|
+
{ id: 'step-2', type: 'transform', description: 'Transform data format' },
|
|
125
|
+
{ id: 'step-3', type: 'store', description: 'Store in database' },
|
|
126
|
+
],
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const projection = projectWorkflowToDocs(workflow);
|
|
130
|
+
|
|
131
|
+
expect(projection.type).toBe('docs');
|
|
132
|
+
expect(projection.content).toContain('# Data Processing Workflow');
|
|
133
|
+
expect(projection.content).toContain('## Workflow Steps');
|
|
134
|
+
expect(projection.content).toContain('1. **validate**');
|
|
135
|
+
expect(projection.content).toContain('Validate input data');
|
|
136
|
+
expect(projection.crossRefs).toHaveLength(3);
|
|
137
|
+
expect(projection.frontMatter.workflowId).toBe('wf-001');
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
describe('generateCrossRefs', () => {
|
|
142
|
+
it('should generate markdown links for references', () => {
|
|
143
|
+
const text = 'See Receipt and WorkItem for details';
|
|
144
|
+
const refMap = new Map([
|
|
145
|
+
['Receipt', '/docs/receipt'],
|
|
146
|
+
['WorkItem', '/docs/work-item'],
|
|
147
|
+
]);
|
|
148
|
+
|
|
149
|
+
const result = generateCrossRefs(text, refMap);
|
|
150
|
+
|
|
151
|
+
expect(result).toContain('[Receipt](/docs/receipt)');
|
|
152
|
+
expect(result).toContain('[WorkItem](/docs/work-item)');
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe('generateTableOfContents', () => {
|
|
157
|
+
it('should generate TOC from sections', () => {
|
|
158
|
+
const sections = [
|
|
159
|
+
{ title: 'Introduction', level: 1 },
|
|
160
|
+
{ title: 'Getting Started', level: 2 },
|
|
161
|
+
{ title: 'Installation', level: 3 },
|
|
162
|
+
{ title: 'Usage', level: 2 },
|
|
163
|
+
];
|
|
164
|
+
|
|
165
|
+
const toc = generateTableOfContents(sections);
|
|
166
|
+
|
|
167
|
+
expect(toc).toContain('## Table of Contents');
|
|
168
|
+
expect(toc).toContain('[Introduction](#introduction)');
|
|
169
|
+
expect(toc).toContain(' [Getting Started](#getting-started)');
|
|
170
|
+
expect(toc).toContain(' [Installation](#installation)');
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
});
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for IDE Metadata Projections (Π_ide)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from 'vitest';
|
|
6
|
+
import {
|
|
7
|
+
projectFunctionToHover,
|
|
8
|
+
projectSchemaToCompletions,
|
|
9
|
+
projectToDefinition,
|
|
10
|
+
projectToDiagnostic,
|
|
11
|
+
projectToSymbol,
|
|
12
|
+
projectToCodeAction,
|
|
13
|
+
projectToCodeLens,
|
|
14
|
+
projectToSignatureHelp,
|
|
15
|
+
projectToSemanticTokens,
|
|
16
|
+
IDEProjectionSchema,
|
|
17
|
+
} from '../src/projections-ide.mjs';
|
|
18
|
+
|
|
19
|
+
describe('Π_ide - IDE Metadata Projections', () => {
|
|
20
|
+
describe('projectFunctionToHover', () => {
|
|
21
|
+
it('should project function to LSP hover information', () => {
|
|
22
|
+
const fnInfo = {
|
|
23
|
+
name: 'calculateSum',
|
|
24
|
+
signature: 'function calculateSum(a: number, b: number): number',
|
|
25
|
+
documentation: 'Calculates the sum of two numbers',
|
|
26
|
+
parameters: [
|
|
27
|
+
{ name: 'a', type: 'number', description: 'First number' },
|
|
28
|
+
{ name: 'b', type: 'number', description: 'Second number' },
|
|
29
|
+
],
|
|
30
|
+
returns: { type: 'number', description: 'The sum' },
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const projection = projectFunctionToHover(fnInfo);
|
|
34
|
+
|
|
35
|
+
expect(projection.type).toBe('ide');
|
|
36
|
+
expect(projection.format).toBe('hover');
|
|
37
|
+
expect(projection.content.kind).toBe('markdown');
|
|
38
|
+
expect(projection.content.value).toContain('calculateSum');
|
|
39
|
+
expect(projection.content.value).toContain('@param');
|
|
40
|
+
expect(projection.content.value).toContain('@returns');
|
|
41
|
+
|
|
42
|
+
// Validate schema
|
|
43
|
+
IDEProjectionSchema.parse(projection);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('projectSchemaToCompletions', () => {
|
|
48
|
+
it('should project schema to LSP completions', () => {
|
|
49
|
+
const fields = {
|
|
50
|
+
name: { type: 'string', description: 'User name' },
|
|
51
|
+
age: { type: 'number', description: 'User age' },
|
|
52
|
+
email: { type: 'string', description: 'User email address' },
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const projection = projectSchemaToCompletions('UserSchema', fields);
|
|
56
|
+
|
|
57
|
+
expect(projection.type).toBe('ide');
|
|
58
|
+
expect(projection.format).toBe('completion');
|
|
59
|
+
expect(projection.content.items).toHaveLength(3);
|
|
60
|
+
expect(projection.content.items[0].label).toBe('name');
|
|
61
|
+
expect(projection.content.items[0].kind).toBe(10); // Property
|
|
62
|
+
expect(projection.content.isIncomplete).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe('projectToDefinition', () => {
|
|
67
|
+
it('should project to LSP definition location', () => {
|
|
68
|
+
const location = {
|
|
69
|
+
uri: 'file:///home/user/project/src/main.mjs',
|
|
70
|
+
range: {
|
|
71
|
+
start: { line: 10, character: 5 },
|
|
72
|
+
end: { line: 10, character: 20 },
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const projection = projectToDefinition(location);
|
|
77
|
+
|
|
78
|
+
expect(projection.type).toBe('ide');
|
|
79
|
+
expect(projection.format).toBe('definition');
|
|
80
|
+
expect(projection.uri).toBe(location.uri);
|
|
81
|
+
expect(projection.range).toEqual(location.range);
|
|
82
|
+
expect(projection.content.uri).toBe(location.uri);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe('projectToDiagnostic', () => {
|
|
87
|
+
it('should project to LSP diagnostic (error)', () => {
|
|
88
|
+
const issue = {
|
|
89
|
+
message: 'Variable not defined',
|
|
90
|
+
severity: 'error',
|
|
91
|
+
range: {
|
|
92
|
+
start: { line: 5, character: 10 },
|
|
93
|
+
end: { line: 5, character: 15 },
|
|
94
|
+
},
|
|
95
|
+
code: 'E001',
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const diagnostic = projectToDiagnostic(issue);
|
|
99
|
+
|
|
100
|
+
expect(diagnostic.message).toBe('Variable not defined');
|
|
101
|
+
expect(diagnostic.severity).toBe(1); // Error
|
|
102
|
+
expect(diagnostic.code).toBe('E001');
|
|
103
|
+
expect(diagnostic.source).toBe('kgc-runtime');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should handle warning severity', () => {
|
|
107
|
+
const issue = {
|
|
108
|
+
message: 'Unused variable',
|
|
109
|
+
severity: 'warning',
|
|
110
|
+
range: {
|
|
111
|
+
start: { line: 0, character: 0 },
|
|
112
|
+
end: { line: 0, character: 1 },
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const diagnostic = projectToDiagnostic(issue);
|
|
117
|
+
|
|
118
|
+
expect(diagnostic.severity).toBe(2); // Warning
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe('projectToSymbol', () => {
|
|
123
|
+
it('should project to LSP document symbol', () => {
|
|
124
|
+
const symbolInfo = {
|
|
125
|
+
name: 'calculateSum',
|
|
126
|
+
kind: 'function',
|
|
127
|
+
containerName: 'math.utils',
|
|
128
|
+
location: {
|
|
129
|
+
uri: 'file:///project/math.mjs',
|
|
130
|
+
range: {
|
|
131
|
+
start: { line: 10, character: 0 },
|
|
132
|
+
end: { line: 15, character: 1 },
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const symbol = projectToSymbol(symbolInfo);
|
|
138
|
+
|
|
139
|
+
expect(symbol.name).toBe('calculateSum');
|
|
140
|
+
expect(symbol.kind).toBe(12); // Function
|
|
141
|
+
expect(symbol.containerName).toBe('math.utils');
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
describe('projectToCodeAction', () => {
|
|
146
|
+
it('should project to LSP code action', () => {
|
|
147
|
+
const actionInfo = {
|
|
148
|
+
title: 'Fix import statement',
|
|
149
|
+
kind: 'quickfix',
|
|
150
|
+
edit: {
|
|
151
|
+
changes: {
|
|
152
|
+
'file:///project/main.mjs': [
|
|
153
|
+
{
|
|
154
|
+
range: { start: { line: 0, character: 0 }, end: { line: 0, character: 10 } },
|
|
155
|
+
newText: 'import { x } from "./utils.mjs"',
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
isPreferred: true,
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const codeAction = projectToCodeAction(actionInfo);
|
|
164
|
+
|
|
165
|
+
expect(codeAction.title).toBe('Fix import statement');
|
|
166
|
+
expect(codeAction.kind).toBe('quickfix');
|
|
167
|
+
expect(codeAction.isPreferred).toBe(true);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe('projectToCodeLens', () => {
|
|
172
|
+
it('should project work item to code lens', () => {
|
|
173
|
+
const workItem = {
|
|
174
|
+
id: 'item-001',
|
|
175
|
+
goal: 'Run tests',
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const range = {
|
|
179
|
+
start: { line: 5, character: 0 },
|
|
180
|
+
end: { line: 5, character: 20 },
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const codeLens = projectToCodeLens(workItem, range);
|
|
184
|
+
|
|
185
|
+
expect(codeLens.range).toEqual(range);
|
|
186
|
+
expect(codeLens.command.title).toContain('Run tests');
|
|
187
|
+
expect(codeLens.command.command).toBe('kgc.executeWorkItem');
|
|
188
|
+
expect(codeLens.command.arguments).toEqual(['item-001']);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe('projectToSignatureHelp', () => {
|
|
193
|
+
it('should project to LSP signature help', () => {
|
|
194
|
+
const fnInfo = {
|
|
195
|
+
name: 'processData',
|
|
196
|
+
documentation: 'Processes data with options',
|
|
197
|
+
parameters: [
|
|
198
|
+
{ name: 'data', type: 'any', description: 'Data to process' },
|
|
199
|
+
{ name: 'options', type: 'object', description: 'Processing options' },
|
|
200
|
+
],
|
|
201
|
+
returns: { type: 'Promise<any>', description: 'Processed data' },
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const projection = projectToSignatureHelp(fnInfo, 1);
|
|
205
|
+
|
|
206
|
+
expect(projection.type).toBe('ide');
|
|
207
|
+
expect(projection.format).toBe('lsp');
|
|
208
|
+
expect(projection.content.signatures).toHaveLength(1);
|
|
209
|
+
expect(projection.content.activeParameter).toBe(1);
|
|
210
|
+
expect(projection.content.signatures[0].parameters).toHaveLength(2);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
describe('projectToSemanticTokens', () => {
|
|
215
|
+
it('should project to LSP semantic tokens', () => {
|
|
216
|
+
const tokens = [
|
|
217
|
+
{ line: 0, char: 0, length: 6, tokenType: 'function' },
|
|
218
|
+
{ line: 0, char: 7, length: 4, tokenType: 'variable' },
|
|
219
|
+
{ line: 1, char: 2, length: 5, tokenType: 'class', modifiers: ['declaration'] },
|
|
220
|
+
];
|
|
221
|
+
|
|
222
|
+
const semanticTokens = projectToSemanticTokens(tokens);
|
|
223
|
+
|
|
224
|
+
expect(Array.isArray(semanticTokens.data)).toBe(true);
|
|
225
|
+
expect(semanticTokens.data.length).toBeGreaterThan(0);
|
|
226
|
+
// Delta encoding: [deltaLine, deltaChar, length, typeIndex, modifierBits]
|
|
227
|
+
expect(semanticTokens.data.length % 5).toBe(0);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
});
|