@mondaydotcomorg/atp-compiler 0.18.2 → 0.18.4-rc.1

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 (93) hide show
  1. package/__tests__/unit/default-compiler.test.ts +259 -0
  2. package/__tests__/unit/plugin-system.test.ts +401 -0
  3. package/dist/atp-compiler/src/index.d.ts +8 -0
  4. package/dist/atp-compiler/src/index.d.ts.map +1 -1
  5. package/dist/atp-compiler/src/index.js +9 -0
  6. package/dist/atp-compiler/src/index.js.map +1 -1
  7. package/dist/atp-compiler/src/plugin-system/create-default-compiler.d.ts +40 -0
  8. package/dist/atp-compiler/src/plugin-system/create-default-compiler.d.ts.map +1 -0
  9. package/dist/atp-compiler/src/plugin-system/create-default-compiler.js +40 -0
  10. package/dist/atp-compiler/src/plugin-system/create-default-compiler.js.map +1 -0
  11. package/dist/atp-compiler/src/plugin-system/default-plugins/array-transformer-plugin.d.ts +18 -0
  12. package/dist/atp-compiler/src/plugin-system/default-plugins/array-transformer-plugin.d.ts.map +1 -0
  13. package/dist/atp-compiler/src/plugin-system/default-plugins/array-transformer-plugin.js +45 -0
  14. package/dist/atp-compiler/src/plugin-system/default-plugins/array-transformer-plugin.js.map +1 -0
  15. package/dist/atp-compiler/src/plugin-system/default-plugins/detection-plugin.d.ts +17 -0
  16. package/dist/atp-compiler/src/plugin-system/default-plugins/detection-plugin.d.ts.map +1 -0
  17. package/dist/atp-compiler/src/plugin-system/default-plugins/detection-plugin.js +33 -0
  18. package/dist/atp-compiler/src/plugin-system/default-plugins/detection-plugin.js.map +1 -0
  19. package/dist/atp-compiler/src/plugin-system/default-plugins/index.d.ts +11 -0
  20. package/dist/atp-compiler/src/plugin-system/default-plugins/index.d.ts.map +1 -0
  21. package/dist/atp-compiler/src/plugin-system/default-plugins/index.js +11 -0
  22. package/dist/atp-compiler/src/plugin-system/default-plugins/index.js.map +1 -0
  23. package/dist/atp-compiler/src/plugin-system/default-plugins/loop-transformer-plugin.d.ts +17 -0
  24. package/dist/atp-compiler/src/plugin-system/default-plugins/loop-transformer-plugin.d.ts.map +1 -0
  25. package/dist/atp-compiler/src/plugin-system/default-plugins/loop-transformer-plugin.js +36 -0
  26. package/dist/atp-compiler/src/plugin-system/default-plugins/loop-transformer-plugin.js.map +1 -0
  27. package/dist/atp-compiler/src/plugin-system/default-plugins/promise-transformer-plugin.d.ts +19 -0
  28. package/dist/atp-compiler/src/plugin-system/default-plugins/promise-transformer-plugin.d.ts.map +1 -0
  29. package/dist/atp-compiler/src/plugin-system/default-plugins/promise-transformer-plugin.js +49 -0
  30. package/dist/atp-compiler/src/plugin-system/default-plugins/promise-transformer-plugin.js.map +1 -0
  31. package/dist/atp-compiler/src/plugin-system/examples/loop-transformer-plugin.d.ts +31 -0
  32. package/dist/atp-compiler/src/plugin-system/examples/loop-transformer-plugin.d.ts.map +1 -0
  33. package/dist/atp-compiler/src/plugin-system/examples/loop-transformer-plugin.js +60 -0
  34. package/dist/atp-compiler/src/plugin-system/examples/loop-transformer-plugin.js.map +1 -0
  35. package/dist/atp-compiler/src/plugin-system/examples/security-validator-plugin.d.ts +48 -0
  36. package/dist/atp-compiler/src/plugin-system/examples/security-validator-plugin.d.ts.map +1 -0
  37. package/dist/atp-compiler/src/plugin-system/examples/security-validator-plugin.js +108 -0
  38. package/dist/atp-compiler/src/plugin-system/examples/security-validator-plugin.js.map +1 -0
  39. package/dist/atp-compiler/src/plugin-system/examples/timeout-plugin.d.ts +53 -0
  40. package/dist/atp-compiler/src/plugin-system/examples/timeout-plugin.d.ts.map +1 -0
  41. package/dist/atp-compiler/src/plugin-system/examples/timeout-plugin.js +106 -0
  42. package/dist/atp-compiler/src/plugin-system/examples/timeout-plugin.js.map +1 -0
  43. package/dist/atp-compiler/src/plugin-system/index.d.ts +14 -0
  44. package/dist/atp-compiler/src/plugin-system/index.d.ts.map +1 -0
  45. package/dist/atp-compiler/src/plugin-system/index.js +16 -0
  46. package/dist/atp-compiler/src/plugin-system/index.js.map +1 -0
  47. package/dist/atp-compiler/src/plugin-system/pluggable-compiler.d.ts +102 -0
  48. package/dist/atp-compiler/src/plugin-system/pluggable-compiler.d.ts.map +1 -0
  49. package/dist/atp-compiler/src/plugin-system/pluggable-compiler.js +280 -0
  50. package/dist/atp-compiler/src/plugin-system/pluggable-compiler.js.map +1 -0
  51. package/dist/atp-compiler/src/plugin-system/plugin-api.d.ts +165 -0
  52. package/dist/atp-compiler/src/plugin-system/plugin-api.d.ts.map +1 -0
  53. package/dist/atp-compiler/src/plugin-system/plugin-api.js +180 -0
  54. package/dist/atp-compiler/src/plugin-system/plugin-api.js.map +1 -0
  55. package/dist/atp-compiler/src/transformer/index.d.ts +15 -1
  56. package/dist/atp-compiler/src/transformer/index.d.ts.map +1 -1
  57. package/dist/atp-compiler/src/transformer/index.js +17 -0
  58. package/dist/atp-compiler/src/transformer/index.js.map +1 -1
  59. package/dist/atp-compiler/src/types/compiler-interface.d.ts +61 -0
  60. package/dist/atp-compiler/src/types/compiler-interface.d.ts.map +1 -0
  61. package/dist/atp-compiler/src/types/compiler-interface.js +18 -0
  62. package/dist/atp-compiler/src/types/compiler-interface.js.map +1 -0
  63. package/dist/runtime/src/approval/index.js +2 -1
  64. package/dist/runtime/src/approval/index.js.map +1 -1
  65. package/dist/runtime/src/index.d.ts +1 -1
  66. package/dist/runtime/src/index.d.ts.map +1 -1
  67. package/dist/runtime/src/index.js +1 -1
  68. package/dist/runtime/src/index.js.map +1 -1
  69. package/dist/runtime/src/llm/index.d.ts +1 -1
  70. package/dist/runtime/src/llm/index.d.ts.map +1 -1
  71. package/dist/runtime/src/llm/index.js +1 -1
  72. package/dist/runtime/src/llm/index.js.map +1 -1
  73. package/dist/runtime/src/llm/replay.d.ts +75 -0
  74. package/dist/runtime/src/llm/replay.d.ts.map +1 -1
  75. package/dist/runtime/src/llm/replay.js +187 -5
  76. package/dist/runtime/src/llm/replay.js.map +1 -1
  77. package/dist/tsconfig.tsbuildinfo +1 -1
  78. package/package.json +13 -5
  79. package/src/index.ts +26 -0
  80. package/src/plugin-system/create-default-compiler.ts +57 -0
  81. package/src/plugin-system/default-plugins/array-transformer-plugin.ts +57 -0
  82. package/src/plugin-system/default-plugins/detection-plugin.ts +41 -0
  83. package/src/plugin-system/default-plugins/index.ts +12 -0
  84. package/src/plugin-system/default-plugins/loop-transformer-plugin.ts +47 -0
  85. package/src/plugin-system/default-plugins/promise-transformer-plugin.ts +63 -0
  86. package/src/plugin-system/examples/loop-transformer-plugin.ts +76 -0
  87. package/src/plugin-system/examples/security-validator-plugin.ts +168 -0
  88. package/src/plugin-system/examples/timeout-plugin.ts +158 -0
  89. package/src/plugin-system/index.ts +19 -0
  90. package/src/plugin-system/pluggable-compiler.ts +318 -0
  91. package/src/plugin-system/plugin-api.ts +330 -0
  92. package/src/transformer/index.ts +21 -1
  93. package/src/types/compiler-interface.ts +79 -0
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Tests to verify createDefaultCompiler() provides identical functionality to ATPCompiler
3
+ */
4
+
5
+ import { ATPCompiler } from '../../src/transformer/index.js';
6
+ import { createDefaultCompiler } from '../../src/plugin-system/create-default-compiler.js';
7
+ import type { TransformationPlugin } from '../../src/plugin-system/plugin-api.js';
8
+
9
+ describe('Default Compiler - Functional Equivalence', () => {
10
+ describe('Detection', () => {
11
+ it('should detect for-of-await patterns like ATPCompiler', async () => {
12
+ const code = `
13
+ for (const item of items) {
14
+ await processItem(item);
15
+ }
16
+ `;
17
+
18
+ const atpCompiler = new ATPCompiler();
19
+ const pluggableCompiler = createDefaultCompiler();
20
+
21
+ const atpResult = atpCompiler.detect(code);
22
+ const pluggableResult = await pluggableCompiler.detect(code);
23
+
24
+ expect(pluggableResult.needsTransform).toBe(atpResult.needsTransform);
25
+ expect(pluggableResult.patterns).toEqual(expect.arrayContaining(atpResult.patterns));
26
+ });
27
+
28
+ it('should detect map-async patterns like ATPCompiler', async () => {
29
+ const code = `
30
+ const results = items.map(async (item) => {
31
+ return await processItem(item);
32
+ });
33
+ `;
34
+
35
+ const atpCompiler = new ATPCompiler();
36
+ const pluggableCompiler = createDefaultCompiler();
37
+
38
+ const atpResult = atpCompiler.detect(code);
39
+ const pluggableResult = await pluggableCompiler.detect(code);
40
+
41
+ expect(pluggableResult.needsTransform).toBe(atpResult.needsTransform);
42
+ expect(pluggableResult.patterns).toEqual(expect.arrayContaining(atpResult.patterns));
43
+ });
44
+
45
+ it('should detect Promise.all patterns like ATPCompiler', async () => {
46
+ const code = `
47
+ const results = await Promise.all(promises);
48
+ `;
49
+
50
+ const atpCompiler = new ATPCompiler();
51
+ const pluggableCompiler = createDefaultCompiler();
52
+
53
+ const atpResult = atpCompiler.detect(code);
54
+ const pluggableResult = await pluggableCompiler.detect(code);
55
+
56
+ expect(pluggableResult.needsTransform).toBe(atpResult.needsTransform);
57
+ });
58
+ });
59
+
60
+ describe('Transformation', () => {
61
+ it('should transform for-of loops like ATPCompiler', async () => {
62
+ const code = `
63
+ for (const item of items) {
64
+ await processItem(item);
65
+ }
66
+ `;
67
+
68
+ const atpCompiler = new ATPCompiler();
69
+ const pluggableCompiler = createDefaultCompiler();
70
+
71
+ const atpResult = atpCompiler.transform(code);
72
+ const pluggableResult = await pluggableCompiler.transform(code);
73
+
74
+ // Both should transform
75
+ expect(pluggableResult.transformed).toBe(true);
76
+ expect(atpResult.transformed).toBe(true);
77
+
78
+ // Both should produce transformed code (not exact match due to formatting)
79
+ expect(pluggableResult.code).toContain('__runtime.resumableForOf');
80
+ expect(atpResult.code).toContain('__runtime.resumableForOf');
81
+
82
+ // Metadata should be similar
83
+ expect(pluggableResult.metadata.loopCount).toBeGreaterThan(0);
84
+ expect(atpResult.metadata.loopCount).toBeGreaterThan(0);
85
+ });
86
+
87
+ it('should transform array methods like ATPCompiler', async () => {
88
+ const code = `
89
+ const results = items.map(async (item) => {
90
+ return await processItem(item);
91
+ });
92
+ `;
93
+
94
+ const atpCompiler = new ATPCompiler();
95
+ const pluggableCompiler = createDefaultCompiler();
96
+
97
+ const atpResult = atpCompiler.transform(code);
98
+ const pluggableResult = await pluggableCompiler.transform(code);
99
+
100
+ expect(pluggableResult.transformed).toBe(true);
101
+ expect(atpResult.transformed).toBe(true);
102
+
103
+ // Code should contain transformation
104
+ expect(pluggableResult.code).toContain('map');
105
+ expect(atpResult.code).toContain('map');
106
+
107
+ // Metadata counts should match
108
+ expect(pluggableResult.metadata).toMatchObject({
109
+ loopCount: atpResult.metadata.loopCount,
110
+ arrayMethodCount: atpResult.metadata.arrayMethodCount,
111
+ parallelCallCount: atpResult.metadata.parallelCallCount,
112
+ });
113
+ });
114
+
115
+ it('should handle code that needs no transformation', async () => {
116
+ const code = `
117
+ const x = 1 + 1;
118
+ console.log(x);
119
+ `;
120
+
121
+ const atpCompiler = new ATPCompiler();
122
+ const pluggableCompiler = createDefaultCompiler();
123
+
124
+ const atpResult = atpCompiler.transform(code);
125
+ const pluggableResult = await pluggableCompiler.transform(code);
126
+
127
+ expect(pluggableResult.transformed).toBe(false);
128
+ expect(atpResult.transformed).toBe(false);
129
+
130
+ // Code should be unchanged
131
+ expect(pluggableResult.code).toBe(code);
132
+ expect(atpResult.code).toBe(code);
133
+ });
134
+ });
135
+
136
+ describe('Configuration', () => {
137
+ it('should respect enableBatchParallel config', async () => {
138
+ const code = `
139
+ const results = await Promise.all([
140
+ fetch('/api/1'),
141
+ fetch('/api/2'),
142
+ ]);
143
+ `;
144
+
145
+ const compilerWithBatch = createDefaultCompiler({ enableBatchParallel: true });
146
+ const compilerWithoutBatch = createDefaultCompiler({ enableBatchParallel: false });
147
+
148
+ const resultWith = await compilerWithBatch.transform(code);
149
+ const resultWithout = await compilerWithoutBatch.transform(code);
150
+
151
+ // Both should transform
152
+ expect(resultWith.transformed).toBe(true);
153
+ expect(resultWithout.transformed).toBe(true);
154
+
155
+ // The code output might differ based on batch config
156
+ // (exact differences depend on implementation)
157
+ });
158
+
159
+ it('should respect batchSizeThreshold config', async () => {
160
+ const code = `
161
+ for (const item of items) {
162
+ await processItem(item);
163
+ }
164
+ `;
165
+
166
+ const compilerSmall = createDefaultCompiler({ batchSizeThreshold: 5 });
167
+ const compilerLarge = createDefaultCompiler({ batchSizeThreshold: 100 });
168
+
169
+ const resultSmall = await compilerSmall.transform(code);
170
+ const resultLarge = await compilerLarge.transform(code);
171
+
172
+ // Both should transform
173
+ expect(resultSmall.transformed).toBe(true);
174
+ expect(resultLarge.transformed).toBe(true);
175
+ });
176
+ });
177
+
178
+ describe('Extensibility', () => {
179
+ it('should allow adding custom plugins on top of defaults', async () => {
180
+ const compiler = createDefaultCompiler();
181
+
182
+ let customPluginExecuted = false;
183
+
184
+ // Add custom transformation plugin that counts identifiers
185
+ const customPlugin: TransformationPlugin = {
186
+ name: 'custom-plugin',
187
+ version: '1.0.0',
188
+ priority: 50, // Lower priority to run after defaults
189
+ getVisitor: () => ({
190
+ Identifier: (path: any) => {
191
+ if (path.node.name === 'item') {
192
+ customPluginExecuted = true;
193
+ }
194
+ },
195
+ }),
196
+ getMetadata: () => ({}),
197
+ reset: () => {
198
+ customPluginExecuted = false;
199
+ },
200
+ };
201
+
202
+ compiler.use(customPlugin);
203
+
204
+ // Use code that will trigger transformation
205
+ const code = `
206
+ for (const item of items) {
207
+ await processItem(item);
208
+ }
209
+ `;
210
+ const result = await compiler.transform(code);
211
+
212
+ // Verify plugin was registered and executed
213
+ expect(customPluginExecuted).toBe(true);
214
+ expect(result).toHaveProperty('code');
215
+ expect(result.transformed).toBe(true);
216
+ });
217
+
218
+ it('should work with AST caching', async () => {
219
+ const compiler = createDefaultCompiler();
220
+
221
+ const code = `const x = 42;`;
222
+
223
+ // First call - parses and caches
224
+ await compiler.detect(code);
225
+ expect(compiler.getCacheStats().size).toBe(1);
226
+
227
+ // Second call - uses cache
228
+ await compiler.transform(code);
229
+ expect(compiler.getCacheStats().size).toBe(1);
230
+ });
231
+ });
232
+
233
+ describe('Drop-in Replacement', () => {
234
+ it('should be usable as ATPCompilerLike type', async () => {
235
+ // This test verifies the type compatibility
236
+ const compiler = createDefaultCompiler({
237
+ enableBatchParallel: true,
238
+ batchSizeThreshold: 10,
239
+ });
240
+
241
+ const code = `
242
+ for (const item of items) {
243
+ await processItem(item);
244
+ }
245
+ `;
246
+
247
+ // Should work with same API
248
+ const detection = await compiler.detect(code);
249
+ expect(detection).toHaveProperty('needsTransform');
250
+ expect(detection).toHaveProperty('patterns');
251
+
252
+ const result = await compiler.transform(code);
253
+ expect(result).toHaveProperty('code');
254
+ expect(result).toHaveProperty('transformed');
255
+ expect(result).toHaveProperty('metadata');
256
+ });
257
+ });
258
+ });
259
+
@@ -0,0 +1,401 @@
1
+ /**
2
+ * Unit tests for the Plugin System
3
+ */
4
+
5
+ import { PluginRegistry } from '../../src/plugin-system/plugin-api.js';
6
+ import { PluggableCompiler } from '../../src/plugin-system/pluggable-compiler.js';
7
+ import type {
8
+ DetectionPlugin,
9
+ TransformationPlugin,
10
+ OptimizerPlugin,
11
+ ValidatorPlugin,
12
+ BabelVisitor,
13
+ } from '../../src/plugin-system/plugin-api.js';
14
+ import type { CompilerConfig, TransformMetadata, DetectionResult } from '../../src/types.js';
15
+ import * as t from '@babel/types';
16
+
17
+ describe('PluginRegistry', () => {
18
+ let registry: PluginRegistry;
19
+
20
+ beforeEach(() => {
21
+ registry = new PluginRegistry();
22
+ });
23
+
24
+ describe('Plugin Registration', () => {
25
+ it('should register a detection plugin', () => {
26
+ const plugin: DetectionPlugin = {
27
+ name: 'test-detector',
28
+ version: '1.0.0',
29
+ priority: 100,
30
+ patterns: ['for-of-await'],
31
+ detect: async (code: string, ast: t.File): Promise<DetectionResult> => ({
32
+ needsTransform: true,
33
+ patterns: ['for-of-await'],
34
+ batchableParallel: false,
35
+ }),
36
+ };
37
+
38
+ registry.register(plugin);
39
+ const plugins = registry.getDetectors();
40
+ expect(plugins).toHaveLength(1);
41
+ expect(plugins[0]!.name).toBe('test-detector');
42
+ });
43
+
44
+ it('should register a transformation plugin', () => {
45
+ const plugin: TransformationPlugin = {
46
+ name: 'test-transformer',
47
+ version: '1.0.0',
48
+ priority: 100,
49
+ getVisitor: (config: CompilerConfig): BabelVisitor => ({}),
50
+ getMetadata: () => ({ loopCount: 0 }),
51
+ reset: () => {},
52
+ };
53
+
54
+ registry.register(plugin);
55
+ const plugins = registry.getTransformers();
56
+ expect(plugins).toHaveLength(1);
57
+ expect(plugins[0]!.name).toBe('test-transformer');
58
+ });
59
+
60
+ it('should throw error when registering duplicate plugin names', () => {
61
+ const plugin1: DetectionPlugin = {
62
+ name: 'duplicate',
63
+ version: '1.0.0',
64
+ priority: 100,
65
+ patterns: ['for-of-await'],
66
+ detect: async () => ({ needsTransform: false, patterns: [], batchableParallel: false }),
67
+ };
68
+
69
+ const plugin2: DetectionPlugin = {
70
+ name: 'duplicate',
71
+ version: '2.0.0',
72
+ priority: 50,
73
+ patterns: ['map-async'],
74
+ detect: async () => ({ needsTransform: false, patterns: [], batchableParallel: false }),
75
+ };
76
+
77
+ registry.register(plugin1);
78
+ expect(() => registry.register(plugin2)).toThrow('Plugin "duplicate" is already registered');
79
+ });
80
+
81
+ it('should sort plugins by priority (higher first)', () => {
82
+ const plugin1: DetectionPlugin = {
83
+ name: 'low-priority',
84
+ version: '1.0.0',
85
+ priority: 10,
86
+ patterns: [],
87
+ detect: async () => ({ needsTransform: false, patterns: [], batchableParallel: false }),
88
+ };
89
+
90
+ const plugin2: DetectionPlugin = {
91
+ name: 'high-priority',
92
+ version: '1.0.0',
93
+ priority: 100,
94
+ patterns: [],
95
+ detect: async () => ({ needsTransform: false, patterns: [], batchableParallel: false }),
96
+ };
97
+
98
+ registry.register(plugin1);
99
+ registry.register(plugin2);
100
+
101
+ const plugins = registry.getDetectors();
102
+ expect(plugins[0]!.name).toBe('high-priority');
103
+ expect(plugins[1]!.name).toBe('low-priority');
104
+ });
105
+ });
106
+
107
+ describe('Plugin Unregistration', () => {
108
+ it('should unregister a plugin by name', () => {
109
+ const plugin: DetectionPlugin = {
110
+ name: 'to-remove',
111
+ version: '1.0.0',
112
+ priority: 100,
113
+ patterns: [],
114
+ detect: async () => ({ needsTransform: false, patterns: [], batchableParallel: false }),
115
+ };
116
+
117
+ registry.register(plugin);
118
+ expect(registry.getDetectors()).toHaveLength(1);
119
+
120
+ const removed = registry.unregister('to-remove');
121
+ expect(removed).toBe(true);
122
+ expect(registry.getDetectors()).toHaveLength(0);
123
+ });
124
+
125
+ it('should return false when unregistering non-existent plugin', () => {
126
+ const removed = registry.unregister('non-existent');
127
+ expect(removed).toBe(false);
128
+ });
129
+ });
130
+
131
+ describe('Plugin Retrieval', () => {
132
+ it('should find plugin by name', () => {
133
+ const plugin: DetectionPlugin = {
134
+ name: 'findable',
135
+ version: '1.0.0',
136
+ priority: 100,
137
+ patterns: [],
138
+ detect: async () => ({ needsTransform: false, patterns: [], batchableParallel: false }),
139
+ };
140
+
141
+ registry.register(plugin);
142
+ const found = registry.findPlugin('findable');
143
+ expect(found).toBeDefined();
144
+ expect(found?.name).toBe('findable');
145
+ });
146
+
147
+ it('should return undefined for non-existent plugin', () => {
148
+ const found = registry.findPlugin('non-existent');
149
+ expect(found).toBeUndefined();
150
+ });
151
+ });
152
+ });
153
+
154
+ describe('PluggableCompiler', () => {
155
+ let compiler: PluggableCompiler;
156
+
157
+ beforeEach(() => {
158
+ compiler = new PluggableCompiler();
159
+ });
160
+
161
+ describe('Plugin Management', () => {
162
+ it('should register plugin via use() method (chainable)', () => {
163
+ const plugin: DetectionPlugin = {
164
+ name: 'test-plugin',
165
+ version: '1.0.0',
166
+ patterns: [],
167
+ detect: async () => ({ needsTransform: false, patterns: [], batchableParallel: false }),
168
+ };
169
+
170
+ const result = compiler.use(plugin);
171
+ expect(result).toBe(compiler); // Chainable
172
+ });
173
+
174
+ it('should unregister plugin via remove() method', () => {
175
+ const plugin: DetectionPlugin = {
176
+ name: 'removable',
177
+ version: '1.0.0',
178
+ patterns: [],
179
+ detect: async () => ({ needsTransform: false, patterns: [], batchableParallel: false }),
180
+ };
181
+
182
+ compiler.use(plugin);
183
+ const removed = compiler.remove('removable');
184
+ expect(removed).toBe(true);
185
+ });
186
+ });
187
+
188
+ describe('Detection', () => {
189
+ it('should detect patterns using registered plugins', async () => {
190
+ const plugin: DetectionPlugin = {
191
+ name: 'for-of-detector',
192
+ version: '1.0.0',
193
+ priority: 100,
194
+ patterns: ['for-of-await'],
195
+ detect: async (code: string): Promise<DetectionResult> => {
196
+ return code.includes('for')
197
+ ? { needsTransform: true, patterns: ['for-of-await'], batchableParallel: false }
198
+ : { needsTransform: false, patterns: [], batchableParallel: false };
199
+ },
200
+ };
201
+
202
+ compiler.use(plugin);
203
+
204
+ const code = `
205
+ for (const item of items) {
206
+ console.log(item);
207
+ }
208
+ `;
209
+
210
+ const result = await compiler.detect(code);
211
+ expect(result.needsTransform).toBe(true);
212
+ expect(result.patterns).toContain('for-of-await');
213
+ });
214
+
215
+ it('should handle detection errors gracefully', async () => {
216
+ const plugin: DetectionPlugin = {
217
+ name: 'failing-detector',
218
+ version: '1.0.0',
219
+ priority: 100,
220
+ patterns: [],
221
+ detect: async () => {
222
+ throw new Error('Detection failed');
223
+ },
224
+ };
225
+
226
+ compiler.use(plugin);
227
+
228
+ const result = await compiler.detect('some code');
229
+ expect(result.needsTransform).toBe(false);
230
+ expect(result.patterns).toEqual([]);
231
+ });
232
+ });
233
+
234
+ describe('Transformation', () => {
235
+ it('should execute transform with registered transformation plugins', async () => {
236
+ const plugin: TransformationPlugin = {
237
+ name: 'simple-transformer',
238
+ version: '1.0.0',
239
+ priority: 100,
240
+ getVisitor: (): BabelVisitor => ({}),
241
+ getMetadata: () => ({ loopCount: 1 }),
242
+ reset: () => {},
243
+ };
244
+
245
+ compiler.use(plugin);
246
+
247
+ const code = `const foo = 42;`;
248
+ const result = await compiler.transform(code);
249
+ expect(result.code).toBeTruthy(); // Code was processed
250
+ expect(result.metadata).toBeDefined(); // Metadata is present
251
+ });
252
+ });
253
+
254
+ describe('Lifecycle Management', () => {
255
+ it('should initialize plugins automatically on first use', async () => {
256
+ let initialized = false;
257
+
258
+ const plugin: DetectionPlugin = {
259
+ name: 'lifecycle-plugin',
260
+ version: '1.0.0',
261
+ priority: 100,
262
+ patterns: [],
263
+ detect: async () => ({ needsTransform: false, patterns: [], batchableParallel: false }),
264
+ initialize: async () => {
265
+ initialized = true;
266
+ },
267
+ };
268
+
269
+ compiler.use(plugin);
270
+ await compiler.detect('test code');
271
+
272
+ expect(initialized).toBe(true);
273
+ });
274
+
275
+ it('should dispose plugins on compiler disposal', async () => {
276
+ let disposed = false;
277
+
278
+ const plugin: DetectionPlugin = {
279
+ name: 'disposable-plugin',
280
+ version: '1.0.0',
281
+ patterns: [],
282
+ detect: async () => ({ needsTransform: false, patterns: [], batchableParallel: false }),
283
+ dispose: async () => {
284
+ disposed = true;
285
+ },
286
+ };
287
+
288
+ compiler.use(plugin);
289
+ await compiler.dispose();
290
+
291
+ expect(disposed).toBe(true);
292
+ });
293
+ });
294
+ });
295
+
296
+ describe('AST Caching', () => {
297
+ it('should cache AST between detect() and transform()', async () => {
298
+ const compiler = new PluggableCompiler();
299
+
300
+ const code = `const x = 42;`;
301
+
302
+ // First call - should parse and cache
303
+ await compiler.detect(code);
304
+ expect(compiler.getCacheStats().size).toBe(1);
305
+
306
+ // Second call - should use cache
307
+ await compiler.transform(code);
308
+ expect(compiler.getCacheStats().size).toBe(1); // Still 1, not 2
309
+ });
310
+
311
+ it('should cache different code snippets separately', async () => {
312
+ const compiler = new PluggableCompiler();
313
+
314
+ const code1 = `const x = 1;`;
315
+ const code2 = `const y = 2;`;
316
+
317
+ await compiler.detect(code1);
318
+ await compiler.detect(code2);
319
+
320
+ expect(compiler.getCacheStats().size).toBe(2);
321
+ });
322
+
323
+ it('should clear cache on clearCache() call', async () => {
324
+ const compiler = new PluggableCompiler();
325
+
326
+ await compiler.detect(`const x = 1;`);
327
+ expect(compiler.getCacheStats().size).toBe(1);
328
+
329
+ compiler.clearCache();
330
+ expect(compiler.getCacheStats().size).toBe(0);
331
+ });
332
+
333
+ it('should clear cache on dispose()', async () => {
334
+ const compiler = new PluggableCompiler();
335
+
336
+ await compiler.detect(`const x = 1;`);
337
+ expect(compiler.getCacheStats().size).toBe(1);
338
+
339
+ await compiler.dispose();
340
+ expect(compiler.getCacheStats().size).toBe(0);
341
+ });
342
+
343
+ it('should return cache statistics', () => {
344
+ const compiler = new PluggableCompiler();
345
+
346
+ const stats = compiler.getCacheStats();
347
+ expect(stats).toEqual({
348
+ size: 0,
349
+ enabled: true,
350
+ });
351
+ });
352
+ });
353
+
354
+ describe('Plugin Integration', () => {
355
+ it('should work with detection + transformation plugins', async () => {
356
+ const compiler = new PluggableCompiler();
357
+
358
+ // Detection plugin
359
+ compiler.use({
360
+ name: 'detector',
361
+ version: '1.0.0',
362
+ priority: 100,
363
+ patterns: ['for-of-await'],
364
+ detect: async (code: string) =>
365
+ code.includes('for')
366
+ ? { needsTransform: true, patterns: ['for-of-await'], batchableParallel: false }
367
+ : { needsTransform: false, patterns: [], batchableParallel: false },
368
+ } as DetectionPlugin);
369
+
370
+ // Transformation plugin
371
+ let loopCount = 0;
372
+ compiler.use({
373
+ name: 'transformer',
374
+ version: '1.0.0',
375
+ priority: 100,
376
+ getVisitor: () => ({
377
+ ForOfStatement: (path: any) => {
378
+ loopCount++;
379
+ },
380
+ }),
381
+ getMetadata: () => ({ loopCount }),
382
+ reset: () => {
383
+ loopCount = 0;
384
+ },
385
+ } as TransformationPlugin);
386
+
387
+ const code = `
388
+ for (const item of items) {
389
+ console.log(item);
390
+ }
391
+ `;
392
+
393
+ const detection = await compiler.detect(code);
394
+ expect(detection.needsTransform).toBe(true);
395
+
396
+ const result = await compiler.transform(code);
397
+ expect(result.transformed).toBe(true);
398
+ expect(result.metadata.loopCount).toBeGreaterThan(0);
399
+ });
400
+ });
401
+
@@ -1,6 +1,14 @@
1
1
  export * from './types.js';
2
2
  export * from './transformer/index.js';
3
3
  export * from './runtime/index.js';
4
+ export * from './types/compiler-interface.js';
5
+ export * from './plugin-system/index.js';
4
6
  export { ATPCompiler } from './transformer/index.js';
5
7
  export { initializeRuntime, cleanupRuntime } from './runtime/index.js';
8
+ export { PluggableCompiler } from './plugin-system/pluggable-compiler.js';
9
+ export { PluginRegistry } from './plugin-system/plugin-api.js';
10
+ export { createDefaultCompiler } from './plugin-system/create-default-compiler.js';
11
+ export type { CompilerPlugin, DetectionPlugin, TransformationPlugin, OptimizerPlugin, ValidatorPlugin, PluginContext, BabelVisitor, } from './plugin-system/plugin-api.js';
12
+ export type { ATPCompilerLike } from './plugin-system/create-default-compiler.js';
13
+ export type { ICompiler, CacheStats } from './types/compiler-interface.js';
6
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AAEnC,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAC;AAC3B,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,+BAA+B,CAAC;AAG9C,cAAc,0BAA0B,CAAC;AAGzC,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AAGnF,YAAY,EACX,cAAc,EACd,eAAe,EACf,oBAAoB,EACpB,eAAe,EACf,eAAe,EACf,aAAa,EACb,YAAY,GACZ,MAAM,+BAA+B,CAAC;AACvC,YAAY,EAAE,eAAe,EAAE,MAAM,4CAA4C,CAAC;AAGlF,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC"}
@@ -1,6 +1,15 @@
1
+ // Core exports
1
2
  export * from './types.js';
2
3
  export * from './transformer/index.js';
3
4
  export * from './runtime/index.js';
5
+ // Compiler interface exports
6
+ export * from './types/compiler-interface.js';
7
+ // Plugin system exports
8
+ export * from './plugin-system/index.js';
9
+ // Main exports
4
10
  export { ATPCompiler } from './transformer/index.js';
5
11
  export { initializeRuntime, cleanupRuntime } from './runtime/index.js';
12
+ export { PluggableCompiler } from './plugin-system/pluggable-compiler.js';
13
+ export { PluginRegistry } from './plugin-system/plugin-api.js';
14
+ export { createDefaultCompiler } from './plugin-system/create-default-compiler.js';
6
15
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AAEnC,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,eAAe;AACf,cAAc,YAAY,CAAC;AAC3B,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AAEnC,6BAA6B;AAC7B,cAAc,+BAA+B,CAAC;AAE9C,wBAAwB;AACxB,cAAc,0BAA0B,CAAC;AAEzC,eAAe;AACf,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC"}