vladx 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +256 -0
  2. package/bin/cli.js +486 -0
  3. package/bin/vlad.js +539 -0
  4. package/bin/vladpm.js +710 -0
  5. package/bin/vladx.js +491 -0
  6. package/package.json +57 -0
  7. package/src/engine/jit-compiler.js +285 -0
  8. package/src/engine/vladx-engine.js +941 -0
  9. package/src/index.js +44 -0
  10. package/src/interpreter/interpreter.js +2114 -0
  11. package/src/lexer/lexer.js +658 -0
  12. package/src/lexer/optimized-lexer.js +106 -0
  13. package/src/lexer/regex-cache.js +83 -0
  14. package/src/parser/ast-nodes.js +472 -0
  15. package/src/parser/parser.js +1408 -0
  16. package/src/runtime/advanced-type-system.js +209 -0
  17. package/src/runtime/async-manager.js +252 -0
  18. package/src/runtime/builtins.js +143 -0
  19. package/src/runtime/bundler.js +422 -0
  20. package/src/runtime/cache-manager.js +126 -0
  21. package/src/runtime/data-structures.js +612 -0
  22. package/src/runtime/debugger.js +260 -0
  23. package/src/runtime/enhanced-module-system.js +196 -0
  24. package/src/runtime/environment-enhanced.js +272 -0
  25. package/src/runtime/environment.js +140 -0
  26. package/src/runtime/event-emitter.js +232 -0
  27. package/src/runtime/formatter.js +280 -0
  28. package/src/runtime/functional.js +359 -0
  29. package/src/runtime/io-operations.js +390 -0
  30. package/src/runtime/linter.js +374 -0
  31. package/src/runtime/logging.js +314 -0
  32. package/src/runtime/minifier.js +242 -0
  33. package/src/runtime/module-system.js +377 -0
  34. package/src/runtime/network-operations.js +373 -0
  35. package/src/runtime/profiler.js +295 -0
  36. package/src/runtime/repl.js +336 -0
  37. package/src/runtime/security-manager.js +244 -0
  38. package/src/runtime/source-map-generator.js +208 -0
  39. package/src/runtime/test-runner.js +394 -0
  40. package/src/runtime/transformer.js +277 -0
  41. package/src/runtime/type-system.js +244 -0
  42. package/src/runtime/vladx-object.js +250 -0
@@ -0,0 +1,208 @@
1
+ /**
2
+ * SourceMapGenerator — Генерация source maps
3
+ */
4
+
5
+ export class SourceMapGenerator {
6
+ constructor(options = {}) {
7
+ this.file = options.file || '';
8
+ this.sourceRoot = options.sourceRoot || '';
9
+ this.mappings = [];
10
+ this.sources = new Set();
11
+ this.names = new Set();
12
+ this.generatedMappings = [];
13
+ this.generatedLine = 1;
14
+ this.generatedColumn = 0;
15
+ this.lastGeneratedLine = 0;
16
+ this.lastGeneratedColumn = 0;
17
+ this.lastSourceLine = 0;
18
+ this.lastSourceColumn = 0;
19
+ this.lastNameIndex = 0;
20
+ this.lastSourceIndex = 0;
21
+ }
22
+
23
+ /**
24
+ * Добавить mapping
25
+ */
26
+ addMapping(mapping) {
27
+ if (!mapping.generated) {
28
+ throw new Error('Generated position is required');
29
+ }
30
+
31
+ if (!mapping.original && !mapping.name) {
32
+ this.mappings.push(mapping);
33
+ return;
34
+ }
35
+
36
+ if (!mapping.source) {
37
+ throw new Error('Source is required for mapping with original or name');
38
+ }
39
+
40
+ this.sources.add(mapping.source);
41
+ if (mapping.name) {
42
+ this.names.add(mapping.name);
43
+ }
44
+
45
+ this.mappings.push({
46
+ generated: {
47
+ line: mapping.generated.line,
48
+ column: mapping.generated.column
49
+ },
50
+ original: mapping.original ? {
51
+ line: mapping.original.line,
52
+ column: mapping.original.column
53
+ } : null,
54
+ source: mapping.source,
55
+ name: mapping.name || null
56
+ });
57
+ }
58
+
59
+ /**
60
+ * Установить исходный контент
61
+ */
62
+ setSourceContent(source, content) {
63
+ this.sourceContent = this.sourceContent || {};
64
+ this.sourceContent[source] = content;
65
+ }
66
+
67
+ /**
68
+ * Добавить source
69
+ */
70
+ addSource(source) {
71
+ this.sources.add(source);
72
+ }
73
+
74
+ /**
75
+ * Добавить имя
76
+ */
77
+ addName(name) {
78
+ this.names.add(name);
79
+ }
80
+
81
+ /**
82
+ * Сгенерировать source map
83
+ */
84
+ toString() {
85
+ return JSON.stringify(this.toJSON());
86
+ }
87
+
88
+ /**
89
+ * Получить JSON
90
+ */
91
+ toJSON() {
92
+ const sources = Array.from(this.sources);
93
+ const names = Array.from(this.names);
94
+
95
+ const map = {
96
+ version: 3,
97
+ file: this.file,
98
+ sourceRoot: this.sourceRoot,
99
+ sources,
100
+ names,
101
+ mappings: this.encodeMappings(this.mappings)
102
+ };
103
+
104
+ if (this.sourceContent) {
105
+ map.sourcesContent = sources.map(source =>
106
+ this.sourceContent[source] || null
107
+ );
108
+ }
109
+
110
+ return map;
111
+ }
112
+
113
+ /**
114
+ * Кодировать mappings в base64 VLQ
115
+ */
116
+ encodeMappings(mappings) {
117
+ let result = '';
118
+ let lastGeneratedLine = 0;
119
+ let lastGeneratedColumn = 0;
120
+ let lastSourceIndex = 0;
121
+ let lastSourceLine = 0;
122
+ let lastSourceColumn = 0;
123
+ let lastNameIndex = 0;
124
+
125
+ mappings.sort((a, b) => {
126
+ if (a.generated.line !== b.generated.line) {
127
+ return a.generated.line - b.generated.line;
128
+ }
129
+ return a.generated.column - b.generated.column;
130
+ });
131
+
132
+ for (const mapping of mappings) {
133
+ if (mapping.generated.line !== lastGeneratedLine) {
134
+ const linesDiff = mapping.generated.line - lastGeneratedLine;
135
+ for (let i = 0; i < linesDiff; i++) {
136
+ result += ';';
137
+ }
138
+ lastGeneratedLine = mapping.generated.line;
139
+ lastGeneratedColumn = 0;
140
+ } else if (result.length > 0) {
141
+ result += ',';
142
+ }
143
+
144
+ // Generated column
145
+ result += this.encodeVLQ(mapping.generated.column - lastGeneratedColumn);
146
+ lastGeneratedColumn = mapping.generated.column;
147
+
148
+ if (mapping.original) {
149
+ const sourceIndex = this.sources.indexOf(mapping.source);
150
+ result += this.encodeVLQ(sourceIndex - lastSourceIndex);
151
+ lastSourceIndex = sourceIndex;
152
+
153
+ result += this.encodeVLQ(mapping.original.line - 1 - lastSourceLine);
154
+ lastSourceLine = mapping.original.line - 1;
155
+
156
+ result += this.encodeVLQ(mapping.original.column - lastSourceColumn);
157
+ lastSourceColumn = mapping.original.column;
158
+
159
+ if (mapping.name !== null) {
160
+ const nameIndex = this.names.indexOf(mapping.name);
161
+ result += this.encodeVLQ(nameIndex - lastNameIndex);
162
+ lastNameIndex = nameIndex;
163
+ }
164
+ }
165
+ }
166
+
167
+ return result;
168
+ }
169
+
170
+ /**
171
+ * Кодировать число в VLQ
172
+ */
173
+ encodeVLQ(value) {
174
+ let VLQ_BASE_SHIFT = 5;
175
+ let VLQ_BASE = 1 << VLQ_BASE_SHIFT;
176
+ let VLQ_BASE_MASK = VLQ_BASE - 1;
177
+ let VLQ_CONTINUATION_BIT = VLQ_BASE;
178
+
179
+ let encoded = '';
180
+ let sign = value < 0 ? 1 : 0;
181
+ value = Math.abs(value);
182
+
183
+ value = (value << 1) | sign;
184
+
185
+ do {
186
+ let digit = value & VLQ_BASE_MASK;
187
+ value >>>= VLQ_BASE_SHIFT;
188
+
189
+ if (value > 0) {
190
+ digit |= VLQ_CONTINUATION_BIT;
191
+ }
192
+
193
+ encoded += this.base64Encode(digit);
194
+ } while (value > 0);
195
+
196
+ return encoded;
197
+ }
198
+
199
+ /**
200
+ * Base64 кодирование
201
+ */
202
+ base64Encode(value) {
203
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
204
+ return chars[value];
205
+ }
206
+ }
207
+
208
+ export default SourceMapGenerator;
@@ -0,0 +1,394 @@
1
+ /**
2
+ * TestRunner — Фреймворк для тестирования
3
+ */
4
+
5
+ import { VladXObject } from './vladx-object.js';
6
+
7
+ export class TestRunner {
8
+ constructor() {
9
+ this.suites = [];
10
+ this.currentSuite = null;
11
+ this.results = {
12
+ total: 0,
13
+ passed: 0,
14
+ failed: 0,
15
+ skipped: 0,
16
+ suites: []
17
+ };
18
+ this.assertions = this.createAssertions();
19
+ }
20
+
21
+ /**
22
+ * Создать группу тестов
23
+ */
24
+ describe(name, callback) {
25
+ const suite = {
26
+ name,
27
+ tests: [],
28
+ beforeEach: [],
29
+ afterEach: []
30
+ };
31
+
32
+ this.suites.push(suite);
33
+ const parentSuite = this.currentSuite;
34
+ this.currentSuite = suite;
35
+
36
+ callback();
37
+
38
+ this.currentSuite = parentSuite;
39
+ }
40
+
41
+ /**
42
+ * Создать тест
43
+ */
44
+ it(name, callback, options = {}) {
45
+ if (!this.currentSuite) {
46
+ throw new Error('Test must be inside a describe block');
47
+ }
48
+
49
+ const test = {
50
+ name,
51
+ callback,
52
+ skip: options.skip || false,
53
+ only: options.only || false,
54
+ timeout: options.timeout || 5000
55
+ };
56
+
57
+ this.currentSuite.tests.push(test);
58
+ }
59
+
60
+ /**
61
+ * Пропустить тест
62
+ */
63
+ skip(name, callback) {
64
+ this.it(name, callback, { skip: true });
65
+ }
66
+
67
+ /**
68
+ * Запустить только этот тест
69
+ */
70
+ only(name, callback) {
71
+ this.it(name, callback, { only: true });
72
+ }
73
+
74
+ /**
75
+ * Before each
76
+ */
77
+ beforeEach(callback) {
78
+ if (!this.currentSuite) {
79
+ throw new Error('beforeEach must be inside a describe block');
80
+ }
81
+ this.currentSuite.beforeEach.push(callback);
82
+ }
83
+
84
+ /**
85
+ * After each
86
+ */
87
+ afterEach(callback) {
88
+ if (!this.currentSuite) {
89
+ throw new Error('afterEach must be inside a describe block');
90
+ }
91
+ this.currentSuite.afterEach.push(callback);
92
+ }
93
+
94
+ /**
95
+ * Запустить все тесты
96
+ */
97
+ async run() {
98
+ this.results = {
99
+ total: 0,
100
+ passed: 0,
101
+ failed: 0,
102
+ skipped: 0,
103
+ suites: []
104
+ };
105
+
106
+ // Запустить только тесты с .only
107
+ const onlyTests = this.findOnlyTests();
108
+ const suitesToRun = onlyTests.length > 0 ? this.getSuitesWithOnly() : this.suites;
109
+
110
+ for (const suite of suitesToRun) {
111
+ await this.runSuite(suite);
112
+ }
113
+
114
+ return this.results;
115
+ }
116
+
117
+ /**
118
+ * Запустить suite
119
+ */
120
+ async runSuite(suite) {
121
+ const suiteResult = {
122
+ name: suite.name,
123
+ tests: []
124
+ };
125
+
126
+ console.log(`\n${suite.name}`);
127
+
128
+ for (const test of suite.tests) {
129
+ const testResult = await this.runTest(suite, test);
130
+ suiteResult.tests.push(testResult);
131
+
132
+ if (testResult.skipped) {
133
+ this.results.skipped++;
134
+ console.log(` ⊘ ${test.name} (skipped)`);
135
+ } else if (testResult.passed) {
136
+ this.results.passed++;
137
+ console.log(` ✓ ${test.name}`);
138
+ } else {
139
+ this.results.failed++;
140
+ console.log(` ✗ ${test.name}`);
141
+ if (testResult.error) {
142
+ console.log(` ${testResult.error.message}`);
143
+ }
144
+ }
145
+
146
+ this.results.total++;
147
+ }
148
+
149
+ this.results.suites.push(suiteResult);
150
+ }
151
+
152
+ /**
153
+ * Запустить тест
154
+ */
155
+ async runTest(suite, test) {
156
+ const result = {
157
+ name: test.name,
158
+ passed: false,
159
+ failed: false,
160
+ skipped: test.skip,
161
+ error: null
162
+ };
163
+
164
+ if (test.skip) {
165
+ return result;
166
+ }
167
+
168
+ try {
169
+ // Before each hooks
170
+ for (const hook of suite.beforeEach) {
171
+ await hook();
172
+ }
173
+
174
+ // Run test with timeout
175
+ await Promise.race([
176
+ test.callback(this.assertions),
177
+ new Promise((_, reject) =>
178
+ setTimeout(() => reject(new Error(`Timeout: ${test.timeout}ms`)), test.timeout)
179
+ )
180
+ ]);
181
+
182
+ result.passed = true;
183
+ } catch (error) {
184
+ result.failed = true;
185
+ result.error = error;
186
+ } finally {
187
+ // After each hooks
188
+ for (const hook of suite.afterEach) {
189
+ try {
190
+ await hook();
191
+ } catch (e) {
192
+ console.error('Error in afterEach:', e);
193
+ }
194
+ }
195
+ }
196
+
197
+ return result;
198
+ }
199
+
200
+ /**
201
+ * Создать assertions
202
+ */
203
+ createAssertions() {
204
+ const assert = (condition, message) => {
205
+ if (!condition) {
206
+ throw new Error(message || 'Assertion failed');
207
+ }
208
+ };
209
+
210
+ return {
211
+ assert,
212
+
213
+ equal: (actual, expected, message) => {
214
+ assert(actual === expected, message || `Expected ${actual} to equal ${expected}`);
215
+ },
216
+
217
+ deepEqual: (actual, expected, message) => {
218
+ assert(
219
+ JSON.stringify(actual) === JSON.stringify(expected),
220
+ message || `Expected ${JSON.stringify(actual)} to deeply equal ${JSON.stringify(expected)}`
221
+ );
222
+ },
223
+
224
+ notEqual: (actual, expected, message) => {
225
+ assert(actual !== expected, message || `Expected ${actual} to not equal ${expected}`);
226
+ },
227
+
228
+ throws: async (fn, message) => {
229
+ let threw = false;
230
+ try {
231
+ await fn();
232
+ } catch (e) {
233
+ threw = true;
234
+ }
235
+ assert(threw, message || 'Expected function to throw');
236
+ },
237
+
238
+ doesNotThrow: async (fn, message) => {
239
+ let threw = false;
240
+ let error;
241
+ try {
242
+ await fn();
243
+ } catch (e) {
244
+ threw = true;
245
+ error = e;
246
+ }
247
+ assert(!threw, message || `Expected function not to throw, but threw: ${error?.message}`);
248
+ },
249
+
250
+ isTrue: (value, message) => {
251
+ assert(value === true, message || `Expected ${value} to be true`);
252
+ },
253
+
254
+ isFalse: (value, message) => {
255
+ assert(value === false, message || `Expected ${value} to be false`);
256
+ },
257
+
258
+ isNull: (value, message) => {
259
+ assert(value === null, message || `Expected ${value} to be null`);
260
+ },
261
+
262
+ isNotNull: (value, message) => {
263
+ assert(value !== null, message || `Expected ${value} to not be null`);
264
+ },
265
+
266
+ isUndefined: (value, message) => {
267
+ assert(value === undefined, message || `Expected ${value} to be undefined`);
268
+ },
269
+
270
+ isDefined: (value, message) => {
271
+ assert(value !== undefined, message || `Expected ${value} to be defined`);
272
+ },
273
+
274
+ contains: (array, item, message) => {
275
+ assert(array.includes(item), message || `Expected ${array} to contain ${item}`);
276
+ },
277
+
278
+ notContains: (array, item, message) => {
279
+ assert(!array.includes(item), message || `Expected ${array} to not contain ${item}`);
280
+ },
281
+
282
+ hasProperty: (object, property, message) => {
283
+ assert(property in object, message || `Expected object to have property ${property}`);
284
+ },
285
+
286
+ typeOf: (value, type, message) => {
287
+ assert(
288
+ typeof value === type,
289
+ message || `Expected ${value} to be of type ${type}`
290
+ );
291
+ },
292
+
293
+ instanceOf: (value, constructor, message) => {
294
+ assert(
295
+ value instanceof constructor,
296
+ message || `Expected ${value} to be instance of ${constructor.name}`
297
+ );
298
+ },
299
+
300
+ lengthOf: (array, length, message) => {
301
+ assert(
302
+ array.length === length,
303
+ message || `Expected array to have length ${length}`
304
+ );
305
+ },
306
+
307
+ greaterThan: (value, expected, message) => {
308
+ assert(value > expected, message || `Expected ${value} to be greater than ${expected}`);
309
+ },
310
+
311
+ lessThan: (value, expected, message) => {
312
+ assert(value < expected, message || `Expected ${value} to be less than ${expected}`);
313
+ },
314
+
315
+ matches: (string, regex, message) => {
316
+ assert(regex.test(string), message || `Expected ${string} to match ${regex}`);
317
+ },
318
+
319
+ fail: (message) => {
320
+ throw new Error(message || 'Test failed');
321
+ },
322
+
323
+ snapshot: (value, name) => {
324
+ // Simplified snapshot testing
325
+ const snapshotKey = `${name}`;
326
+ console.log(`Snapshot: ${snapshotKey}`, JSON.stringify(value, null, 2));
327
+ }
328
+ };
329
+ }
330
+
331
+ /**
332
+ * Найти тесты с .only
333
+ */
334
+ findOnlyTests() {
335
+ const onlyTests = [];
336
+ for (const suite of this.suites) {
337
+ for (const test of suite.tests) {
338
+ if (test.only) {
339
+ onlyTests.push(test);
340
+ }
341
+ }
342
+ }
343
+ return onlyTests;
344
+ }
345
+
346
+ /**
347
+ * Получить suites с тестами .only
348
+ */
349
+ getSuitesWithOnly() {
350
+ const suitesWithOnly = [];
351
+ for (const suite of this.suites) {
352
+ if (suite.tests.some(test => test.only)) {
353
+ suitesWithOnly.push(suite);
354
+ }
355
+ }
356
+ return suitesWithOnly;
357
+ }
358
+
359
+ /**
360
+ * Получить результаты в формате JSON
361
+ */
362
+ getResultsJSON() {
363
+ return JSON.stringify(this.results, null, 2);
364
+ }
365
+
366
+ /**
367
+ * Получить результаты в формате JUnit XML
368
+ */
369
+ getResultsJUnit() {
370
+ let xml = '<?xml version="1.0" encoding="UTF-8"?>\n<testsuites>\n';
371
+
372
+ for (const suite of this.results.suites) {
373
+ const suiteFailed = suite.tests.some(t => t.failed);
374
+ xml += ` <testsuite name="${suite.name}" tests="${suite.tests.length}" failures="${suite.tests.filter(t => t.failed).length}" skipped="${suite.tests.filter(t => t.skipped).length}">\n`;
375
+
376
+ for (const test of suite.tests) {
377
+ if (test.skipped) {
378
+ xml += ` <testcase name="${test.name}">\n <skipped/>\n </testcase>\n`;
379
+ } else if (test.failed) {
380
+ xml += ` <testcase name="${test.name}">\n <failure message="${test.error?.message || 'Test failed'}"/>\n </testcase>\n`;
381
+ } else {
382
+ xml += ` <testcase name="${test.name}"/>\n`;
383
+ }
384
+ }
385
+
386
+ xml += ' </testsuite>\n';
387
+ }
388
+
389
+ xml += '</testsuites>';
390
+ return xml;
391
+ }
392
+ }
393
+
394
+ export default TestRunner;