@razinshafayet/typedjs 0.1.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.
@@ -0,0 +1,15 @@
1
+ {
2
+ "eslint.options": {
3
+ "overrideConfig": {
4
+ "languageOptions": {
5
+ "parser": "/home/razin/.antigravity/extensions/razinshafayet.typedjs-vscode-0.0.2-universal/server/parser.js"
6
+ },
7
+ "rules": {
8
+ "no-op": "error"
9
+ }
10
+ },
11
+ "rulePaths": [
12
+ "/home/razin/.antigravity/extensions/razinshafayet.typedjs-vscode-0.0.2-universal/server/rules"
13
+ ]
14
+ }
15
+ }
package/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # TypedJS
2
+
3
+ A lightweight, runtime-checkable type system for JavaScript. It brings the safety of TypeScript with the simplicity of running directly in Node.js (via a transparent runtime).
4
+
5
+ ## Features
6
+
7
+ - **Runtime Type Checking**: Catches type errors as your code runs (Development Mode).
8
+ - **Fast Production Mode**: Strips checks for raw JavaScript speed (matching TypeScript performance).
9
+ - **No Build Step**: Run `.js` files directly with the `typedjs` CLI.
10
+ - **Support for Modern Types**: Includes Maps, Sets, Tuples, and Unions.
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install -g typedjs
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ### 1. Development Mode (Safe)
21
+ Runs your code with full runtime type checking. Great for debugging.
22
+
23
+ ```bash
24
+ typedjs app.js
25
+ ```
26
+
27
+ ### 2. Production Mode (Fast)
28
+ Strips all types and runtime checks. Executes as fast as raw JavaScript. Use this for deployment.
29
+
30
+ ```bash
31
+ typedjs app.js --prod
32
+ ```
33
+
34
+ ## Supported Type Annotations
35
+
36
+ TypedJS supports a subset of TypeScript syntax, focused on runtime-validatable types.
37
+
38
+ ### Primitives
39
+ ```javascript
40
+ let name: string = "Razin";
41
+ let age: number = 25;
42
+ let isActive: boolean = true;
43
+ let empty: null = null;
44
+ let notDefined: undefined = undefined;
45
+ let big: bigint = 9007199254740991n;
46
+ let sym: symbol = Symbol("id");
47
+ ```
48
+
49
+ ### Interfaces & Objects
50
+ ```javascript
51
+ interface User {
52
+ id: number;
53
+ name: string;
54
+ email?: string; // Optional property
55
+ }
56
+
57
+ let user: User = {
58
+ id: 1,
59
+ name: "Razin"
60
+ };
61
+ ```
62
+
63
+ ### Type Aliases & Unions
64
+ ```javascript
65
+ type ID = string | number;
66
+ type Status = "active" | "inactive" | "pending"; // Literal Unions
67
+
68
+ let myId: ID = 123;
69
+ let status: Status = "active";
70
+ ```
71
+
72
+ ### Arrays & Tuples
73
+ ```javascript
74
+ let scores: Array<number> = [10, 20, 30];
75
+ let point: [number, number] = [10, 20]; // Tuple
76
+ ```
77
+
78
+ ### Maps & Sets
79
+ ```javascript
80
+ let map: Map<string, number> = new Map();
81
+ let set: Set<string> = new Set();
82
+ ```
83
+
84
+ ### Functions
85
+ ```javascript
86
+ function add(a: number, b: number): number {
87
+ return a + b;
88
+ }
89
+ ```
90
+
91
+ ## How It Works
92
+
93
+ TypedJS includes a smart parser and runtime generator:
94
+ 1. **Parser**: Reads your TypedJS syntax (using `acorn-typescript`).
95
+ 2. **Analyzer**: Performs static analysis to catch obvious errors early.
96
+ 3. **Generator**:
97
+ * **Dev Mode**: Injects `__checkType__` calls around every variable assignment and function call.
98
+ * **Prod Mode**: Removes all types and checks, outputting pure optimized JavaScript.
@@ -0,0 +1,23 @@
1
+ const parser = require("/home/razin/.vscode/extensions/razinshafayet.typedjs-vscode-0.0.1/server/parser.js");
2
+ const noOp = require("/home/razin/.vscode/extensions/razinshafayet.typedjs-vscode-0.0.1/server/rules/no-op.js");
3
+
4
+ const typedjsPlugin = {
5
+ rules: {
6
+ "no-op": noOp
7
+ }
8
+ };
9
+
10
+ module.exports = [
11
+ {
12
+ files: ["**/*.js"],
13
+ languageOptions: {
14
+ parser: parser
15
+ },
16
+ plugins: {
17
+ typedjs: typedjsPlugin
18
+ },
19
+ rules: {
20
+ "typedjs/no-op": "error"
21
+ }
22
+ }
23
+ ];
package/example.js ADDED
@@ -0,0 +1,14 @@
1
+ interface User {
2
+ name: string;
3
+ age: number;
4
+ }
5
+
6
+ let person1: User = {
7
+ name: "Razin",
8
+ age: 20,
9
+ };
10
+
11
+ let person2: User = {
12
+ name: "Shafayet",
13
+ age: 21,
14
+ };
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@razinshafayet/typedjs",
3
+ "version": "0.1.0",
4
+ "description": "Runtime type checking for JavaScript",
5
+ "main": "src/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "typedjs": "./src/cli.js"
9
+ },
10
+ "keywords": [
11
+ "typescript",
12
+ "javascript",
13
+ "types",
14
+ "runtime",
15
+ "validation",
16
+ "type-checking",
17
+ "type-system"
18
+ ],
19
+ "author": "Razin Shafayet <curiostymaster77@gmail.com>",
20
+ "license": "MIT",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/RazinShafayet2007/typedjs.git"
24
+ },
25
+ "bugs": {
26
+ "url": "https://github.com/RazinShafayet2007/typedjs/issues"
27
+ },
28
+ "homepage": "https://github.com/RazinShafayet2007/typedjs#readme",
29
+ "engines": {
30
+ "node": ">=14.0.0"
31
+ },
32
+ "dependencies": {
33
+ "acorn": "^8.15.0",
34
+ "acorn-typescript": "^1.4.13",
35
+ "escodegen": "^2.1.0",
36
+ "estree-walker": "^3.0.3"
37
+ },
38
+ "devDependencies": {
39
+ "eslint": "^9.39.2"
40
+ }
41
+ }
@@ -0,0 +1,159 @@
1
+ // Updated src/analyzer/analyzer.js - Fixed ESM import + proper walk for enclosing function (no this.parent)
2
+
3
+ import { walk } from 'estree-walker';
4
+
5
+ export function staticAnalyze(typeRegistry, ast) {
6
+ const errors = [];
7
+
8
+ // Helper to check if value matches type (for literals/constants)
9
+ function matchesType(value, type) {
10
+ if (Array.isArray(type)) { // Union
11
+ return type.some(member => matchesType(value, member));
12
+ }
13
+ if (type.kind === 'literal') {
14
+ return value === type.value;
15
+ }
16
+ if (typeof type === 'string') {
17
+ if (type === 'any' || type === 'unknown') return true;
18
+ if (type === 'string') return typeof value === 'string';
19
+ if (type === 'number') return typeof value === 'number';
20
+ if (type === 'boolean') return typeof value === 'boolean';
21
+ if (type === 'null') return value === null;
22
+ if (type === 'undefined') return value === undefined;
23
+ if (type === 'bigint') return typeof value === 'bigint';
24
+ if (type === 'symbol') return typeof value === 'symbol';
25
+ return false;
26
+ }
27
+ // Structured types: skip static check for basic version
28
+ return true;
29
+ }
30
+
31
+ function checkType(name, valueNode, type, errors) {
32
+ // 1. Literal Check
33
+ if (valueNode.type === 'Literal' || valueNode.type === 'BigIntLiteral') {
34
+ const value = valueNode.value ?? valueNode.bigint;
35
+ if (!matchesType(value, type)) {
36
+ errors.push(`[Static type error] Property '${name}' got ${JSON.stringify(value)}, expected ${typeToString(type)}`);
37
+ }
38
+ return;
39
+ }
40
+
41
+ // 2. Object Check
42
+ if (valueNode.type === 'ObjectExpression' && typeof type === 'object' && type.kind !== 'literal') {
43
+ checkObject(name, valueNode, type, errors);
44
+ return;
45
+ }
46
+
47
+ // 3. Array Check (Simple)
48
+ if (valueNode.type === 'ArrayExpression' && type.kind === 'array') {
49
+ valueNode.elements.forEach((elem, i) => {
50
+ if (elem) checkType(`${name}[${i}]`, elem, type.elementType, errors);
51
+ });
52
+ return;
53
+ }
54
+ }
55
+
56
+ function checkObject(name, objectExpr, type, errors) {
57
+ if (typeof type !== 'object' || type === null) return; // Can't check primitive vs object here easily without more logic
58
+
59
+ // Convert AST ObjectExpression props to a map for easy lookup
60
+ const props = {};
61
+ objectExpr.properties.forEach(p => {
62
+ if (p.type === 'Property' && p.key.type === 'Identifier') {
63
+ props[p.key.name] = p.value;
64
+ }
65
+ });
66
+
67
+ // Check against interface definition (type is the shape object)
68
+ for (const [key, expectedType] of Object.entries(type)) {
69
+ const isOptional = expectedType.kind === 'optional';
70
+ const actualType = isOptional ? expectedType.type : expectedType;
71
+
72
+ if (!(key in props)) {
73
+ if (!isOptional) {
74
+ errors.push(`[Static type error] Property '${key}' is missing in object '${name}' (expected type ${typeToString(type)})`);
75
+ }
76
+ continue;
77
+ }
78
+
79
+ const valueNode = props[key];
80
+ checkType(`${name}.${key}`, valueNode, actualType, errors);
81
+ }
82
+ }
83
+
84
+ // Map variable names to types
85
+ const varTypes = {};
86
+ typeRegistry.forEach(entry => {
87
+ if (entry.kind === 'variable') {
88
+ varTypes[entry.name] = entry.type;
89
+ }
90
+ });
91
+
92
+ // Map function names to return types
93
+ const funcReturnTypes = {};
94
+ typeRegistry.forEach(entry => {
95
+ if (entry.kind === 'function' && entry.returnType !== 'any') {
96
+ funcReturnTypes[entry.name] = entry.returnType;
97
+ }
98
+ });
99
+
100
+ let currentFuncName = null;
101
+
102
+ walk(ast, {
103
+ enter(node) {
104
+ // Track current function
105
+ if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
106
+ currentFuncName = node.id?.name || null;
107
+ }
108
+
109
+ // Check variable initializer (only simple literals)
110
+ if (node.type === 'VariableDeclarator' && node.init && varTypes[node.id.name]) {
111
+ if (node.init.type === 'Literal' || node.init.type === 'BigIntLiteral') {
112
+ const value = node.init.value ?? node.init.bigint;
113
+ const declaredType = varTypes[node.id.name];
114
+ if (!matchesType(value, declaredType)) {
115
+ errors.push(`[Static type error] Variable '${node.id.name}' initializer ${JSON.stringify(value)} does not match declared type ${typeToString(declaredType)}`);
116
+ }
117
+ } else if (node.init.type === 'ObjectExpression') {
118
+ const declaredType = varTypes[node.id.name];
119
+ checkObject(node.id.name, node.init, declaredType, errors);
120
+ }
121
+ }
122
+
123
+ // Check literal return
124
+ if (node.type === 'ReturnStatement' && node.argument && (node.argument.type === 'Literal' || node.argument.type === 'BigIntLiteral') && currentFuncName && funcReturnTypes[currentFuncName]) {
125
+ const value = node.argument.value ?? node.argument.bigint;
126
+ const returnType = funcReturnTypes[currentFuncName];
127
+ if (!matchesType(value, returnType)) {
128
+ errors.push(`[Static type error] Function '${currentFuncName}' returns ${JSON.stringify(value)} which does not match return type ${typeToString(returnType)}`);
129
+ }
130
+ }
131
+ },
132
+ leave(node) {
133
+ // Clear current function on leave
134
+ if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
135
+ currentFuncName = null;
136
+ }
137
+ }
138
+ });
139
+
140
+ // Helper for nice type strings in errors (reuse from generator or simple)
141
+ function typeToString(t) {
142
+ if (Array.isArray(t)) return t.map(typeToString).join(' | ');
143
+ if (typeof t === 'object' && t?.kind === 'literal') return JSON.stringify(t.value);
144
+ return t;
145
+ }
146
+
147
+ // Print static errors
148
+ if (errors.length > 0) {
149
+ console.log('\n--- Static Type Errors ---');
150
+ errors.forEach(err => console.log(err));
151
+ console.log('--- End Static Errors ---\n');
152
+ }
153
+
154
+ return errors.length === 0;
155
+ }
156
+
157
+ export function analyze(typeRegistry) {
158
+ return typeRegistry;
159
+ }
package/src/cli.js ADDED
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { parseCode } from "./parser/parser.js";
5
+ import { staticAnalyze, analyze } from "./analyzer/analyzer.js"; // Add staticAnalyze
6
+ import { generate } from "./generator/generator.js";
7
+
8
+ const args = process.argv.slice(2);
9
+
10
+ const isProd = args.includes('--prod');
11
+ const fileArg = args.find(a => !a.startsWith('--'));
12
+
13
+ if (!fileArg) {
14
+ console.error("Usage: typedjs <file.js> [--prod]");
15
+ process.exit(1);
16
+ }
17
+
18
+ const filePath = path.resolve(fileArg);
19
+ const source = fs.readFileSync(filePath, "utf-8");
20
+
21
+ const { ast, typeRegistry } = parseCode(source);
22
+
23
+ // Static analysis (Always run it, but in prod it's CRITICAL)
24
+ const staticErrors = staticAnalyze(typeRegistry, ast);
25
+ if (!staticErrors && isProd) { // staticAnalyze returns false if errors found (wait, logic check)
26
+ // staticAnalyze returns boolean? Let's check.
27
+ // It returns `errors.length === 0`. So true means OK.
28
+ console.error("Build failed due to static type errors.");
29
+ process.exit(1);
30
+ }
31
+
32
+ // Old minimal analyze (deprecated but keeping for now)
33
+ analyze(typeRegistry);
34
+
35
+ const output = generate(ast, typeRegistry, isProd ? 'production' : 'development');
36
+
37
+ const tmpFile = path.resolve("./typedjs_temp.js");
38
+ fs.writeFileSync(tmpFile, output);
39
+
40
+ await import(tmpFile);
41
+
42
+ fs.unlinkSync(tmpFile);
@@ -0,0 +1,261 @@
1
+ // Updated src/generator/generator.js - Added runtime checks for null, undefined, bigint, symbol
2
+
3
+ import escodegen from 'escodegen';
4
+ import { walk } from 'estree-walker';
5
+
6
+ function transformAst(ast, typeRegistry, mode) {
7
+ // Remove compile-time TS nodes
8
+ if (ast.body) {
9
+ ast.body = ast.body.filter(node =>
10
+ node.type !== 'TSInterfaceDeclaration' &&
11
+ node.type !== 'TSTypeAliasDeclaration'
12
+ );
13
+ }
14
+
15
+ // If production mode, stop here! (No runtime checks)
16
+ if (mode === 'production') {
17
+ return ast;
18
+ }
19
+
20
+ // Helpers for check injection
21
+ function createCheckCall(name, valueExpr, type) {
22
+ return {
23
+ type: 'CallExpression',
24
+ callee: { type: 'Identifier', name: '__checkType__' },
25
+ arguments: [
26
+ { type: 'Literal', value: name },
27
+ valueExpr,
28
+ { type: 'Literal', value: JSON.stringify(type) }
29
+ ]
30
+ };
31
+ }
32
+
33
+ function createReturnCheckCall(valueExpr, type) {
34
+ return {
35
+ type: 'CallExpression',
36
+ callee: { type: 'Identifier', name: '__checkReturnType__' },
37
+ arguments: [
38
+ valueExpr,
39
+ { type: 'Literal', value: JSON.stringify(type) }
40
+ ]
41
+ };
42
+ }
43
+
44
+ const funcTypes = {};
45
+ typeRegistry.forEach(entry => {
46
+ if (entry.kind === 'function') {
47
+ funcTypes[entry.name] = entry;
48
+ }
49
+ });
50
+
51
+ walk(ast, {
52
+ enter(node) {
53
+ if (node.typeAnnotation) node.typeAnnotation = null;
54
+ if (node.returnType) node.returnType = null;
55
+
56
+ if (node.type === 'FunctionDeclaration' && node.id && funcTypes[node.id.name]?.params?.length > 0) {
57
+ const params = funcTypes[node.id.name].params;
58
+ const checkStmts = params.map(p => ({
59
+ type: 'VariableDeclaration',
60
+ kind: 'const',
61
+ declarations: [{
62
+ type: 'VariableDeclarator',
63
+ id: { type: 'Identifier', name: p.name },
64
+ init: createCheckCall(p.name, { type: 'Identifier', name: `__arg_${p.name}` }, p.type)
65
+ }]
66
+ }));
67
+
68
+ node.params = node.params.map((param, i) => {
69
+ if (params[i]) param.name = `__arg_${param.name}`;
70
+ return param;
71
+ });
72
+
73
+ node.body.body = [...checkStmts, ...node.body.body];
74
+ }
75
+ },
76
+ leave(node, parent) {
77
+ if (node.type === 'ReturnStatement' && parent?.type === 'BlockStatement' && parent.parent?.type === 'FunctionDeclaration' && parent.parent.id) {
78
+ const funcName = parent.parent.id.name;
79
+ const returnType = funcTypes[funcName]?.returnType;
80
+ if (returnType && returnType !== 'any' && node.argument) {
81
+ node.argument = createReturnCheckCall(node.argument, returnType);
82
+ }
83
+ }
84
+ }
85
+ });
86
+
87
+ return ast;
88
+ }
89
+
90
+ export function generate(ast, typeRegistry, mode = 'development') {
91
+ const transformedAst = transformAst(ast, typeRegistry, mode);
92
+
93
+ let transformed = escodegen.generate(transformedAst, {
94
+ format: { indent: { style: ' ' } },
95
+ comment: true,
96
+ });
97
+
98
+ if (mode === 'production') {
99
+ return transformed;
100
+ }
101
+
102
+ const helpers = `
103
+ function typeToString(t) {
104
+ if (Array.isArray(t)) {
105
+ return t.map(typeToString).join(' | ');
106
+ }
107
+ if (typeof t === 'object' && t !== null) {
108
+ if (t.kind === 'literal') return JSON.stringify(t.value);
109
+ if (t.kind === 'array') return 'Array<' + typeToString(t.elementType) + '>';
110
+ if (t.kind === 'map') return 'Map<' + typeToString(t.keyType) + ', ' + typeToString(t.valueType) + '>';
111
+ if (t.kind === 'set') return 'Set<' + typeToString(t.elementType) + '>';
112
+ if (t.kind === 'tuple') return '[' + t.elements.map(typeToString).join(', ') + ']';
113
+ if (t.kind === 'optional') return typeToString(t.type) + '?';
114
+ return '{' + Object.keys(t).map(k => k + ': ' + typeToString(t[k])).join(', ') + '}';
115
+ }
116
+ return t;
117
+ }
118
+
119
+ function __checkType__(name, value, typeJson) {
120
+ const type = JSON.parse(typeJson);
121
+ const typeStr = typeToString(type);
122
+
123
+ // Union
124
+ if (Array.isArray(type)) {
125
+ const ok = type.some(member => {
126
+ if (typeof member === 'string') {
127
+ if (member === 'string') return typeof value === 'string';
128
+ if (member === 'number') return typeof value === 'number';
129
+ if (member === 'boolean') return typeof value === 'boolean';
130
+ if (member === 'null') return value === null;
131
+ if (member === 'undefined') return value === undefined;
132
+ if (member === 'bigint') return typeof value === 'bigint';
133
+ if (member === 'symbol') return typeof value === 'symbol';
134
+ if (member === 'unknown') return true;
135
+ return false;
136
+ }
137
+ if (member.kind === 'literal') {
138
+ return value === member.value;
139
+ }
140
+ return false;
141
+ });
142
+ if (!ok) {
143
+ console.warn('[Type warning] ' + name + ' expected ' + typeStr + ', got ' + JSON.stringify(value));
144
+ }
145
+ return value;
146
+ }
147
+
148
+ // Single literal
149
+ if (type.kind === 'literal') {
150
+ if (value !== type.value) {
151
+ console.warn('[Type warning] ' + name + ' expected ' + typeStr + ', got ' + JSON.stringify(value));
152
+ }
153
+ return value;
154
+ }
155
+
156
+ // Primitive (including new ones)
157
+ if (typeof type === 'string') {
158
+ if (type === 'any') return value;
159
+ const allowed = type.split('|').map(s => s.trim());
160
+ const ok = allowed.some(a => {
161
+ if (a === 'string') return typeof value === 'string';
162
+ if (a === 'number') return typeof value === 'number';
163
+ if (a === 'boolean') return typeof value === 'boolean';
164
+ if (a === 'null') return value === null;
165
+ if (a === 'undefined') return value === undefined;
166
+ if (a === 'bigint') return typeof value === 'bigint';
167
+ if (a === 'symbol') return typeof value === 'symbol';
168
+ if (a === 'unknown') return true;
169
+ return false;
170
+ });
171
+ if (!ok) console.warn('[Type warning] ' + name + ' expected ' + typeStr + ', got ' + typeof value);
172
+ return value;
173
+ }
174
+
175
+ // Structured types (unchanged)
176
+ if (typeof type === 'object' && type !== null) {
177
+ if (type.kind === 'array') {
178
+ if (!Array.isArray(value)) {
179
+ console.warn('[Type warning] ' + name + ' expected ' + typeStr + ', got ' + typeof value);
180
+ return value;
181
+ }
182
+ value.forEach((item, i) => {
183
+ __checkType__(name + '[' + i + ']', item, JSON.stringify(type.elementType));
184
+ });
185
+ return value;
186
+ }
187
+
188
+ if (type.kind === 'map') {
189
+ if (!(value instanceof Map)) {
190
+ console.warn('[Type warning] ' + name + ' expected ' + typeStr + ', got ' + typeof value);
191
+ return value;
192
+ }
193
+ for (const [k, v] of value.entries()) {
194
+ __checkType__(name + '.key', k, JSON.stringify(type.keyType));
195
+ __checkType__(name + '.value', v, JSON.stringify(type.valueType));
196
+ }
197
+ return value;
198
+ }
199
+
200
+ if (type.kind === 'set') {
201
+ if (!(value instanceof Set)) {
202
+ console.warn('[Type warning] ' + name + ' expected ' + typeStr + ', got ' + typeof value);
203
+ return value;
204
+ }
205
+ let i = 0;
206
+ for (const item of value) {
207
+ __checkType__(name + '[#' + i + ']', item, JSON.stringify(type.elementType));
208
+ i++;
209
+ }
210
+ return value;
211
+ }
212
+
213
+ if (type.kind === 'tuple') {
214
+ if (!Array.isArray(value)) {
215
+ console.warn('[Type warning] ' + name + ' expected ' + typeStr + ', got ' + typeof value);
216
+ return value;
217
+ }
218
+ if (value.length !== type.elements.length) {
219
+ console.warn('[Type warning] ' + name + ' expected tuple length ' + type.elements.length + ', got ' + value.length);
220
+ }
221
+ type.elements.forEach((et, i) => {
222
+ if (i < value.length) {
223
+ __checkType__(name + '[' + i + ']', value[i], JSON.stringify(et));
224
+ }
225
+ });
226
+ return value;
227
+ }
228
+
229
+ if (type.kind === 'optional') {
230
+ if (value === undefined) return value;
231
+ return __checkType__(name, value, JSON.stringify(type.type));
232
+ }
233
+
234
+ // Object shape
235
+ if (typeof value !== 'object' || value === null) {
236
+ console.warn('[Type warning] ' + name + ' expected ' + typeStr + ', got ' + typeof value);
237
+ return value;
238
+ }
239
+ for (const [prop, pt] of Object.entries(type)) {
240
+ const actualType = pt.kind === 'optional' ? pt.type : pt;
241
+ if (pt.kind === 'optional' && value[prop] === undefined) continue;
242
+ __checkType__(name + '.' + prop, value[prop], JSON.stringify(actualType));
243
+ }
244
+ return value;
245
+ }
246
+
247
+ return value;
248
+ }
249
+
250
+ function __checkReturnType__(value, typeJson) {
251
+ return __checkType__('return', value, typeJson);
252
+ }
253
+ `;
254
+
255
+ const varChecks = typeRegistry
256
+ .filter(e => e.kind === 'variable')
257
+ .map(({ name, type }) => `__checkType__('${name}', ${name}, ${JSON.stringify(JSON.stringify(type))});`)
258
+ .join('\n');
259
+
260
+ return `${helpers}\n${transformed}\n${varChecks}`;
261
+ }
@@ -0,0 +1,136 @@
1
+ // Updated src/parser/parser.js - Added support for null, undefined, bigint, symbol
2
+
3
+ import { Parser } from "acorn";
4
+ import ts from "acorn-typescript";
5
+ import { walk } from 'estree-walker';
6
+
7
+ /**
8
+ * Convert a TypeScript AST type node into structured representation
9
+ */
10
+ function tsTypeToString(typeNode, registry = []) {
11
+ switch (typeNode.type) {
12
+ case "TSNullKeyword":
13
+ return "null";
14
+ case "TSUndefinedKeyword":
15
+ return "undefined";
16
+ case "TSBigIntKeyword":
17
+ return "bigint";
18
+ case "TSSymbolKeyword":
19
+ return "symbol";
20
+ case "TSStringKeyword":
21
+ return "string";
22
+ case "TSNumberKeyword":
23
+ return "number";
24
+ case "TSBooleanKeyword":
25
+ return "boolean";
26
+ case "TSLiteralType":
27
+ return { kind: 'literal', value: typeNode.literal.value };
28
+ case "TSTypeReference":
29
+ const refName = typeNode.typeName.name;
30
+ if (refName === 'Array' && typeNode.typeParameters?.params?.length > 0) {
31
+ return { kind: 'array', elementType: tsTypeToString(typeNode.typeParameters.params[0], registry) };
32
+ }
33
+ if (refName === 'Map' && typeNode.typeParameters?.params?.length === 2) {
34
+ return { kind: 'map', keyType: tsTypeToString(typeNode.typeParameters.params[0], registry), valueType: tsTypeToString(typeNode.typeParameters.params[1], registry) };
35
+ }
36
+ if (refName === 'Set' && typeNode.typeParameters?.params?.length > 0) {
37
+ return { kind: 'set', elementType: tsTypeToString(typeNode.typeParameters.params[0], registry) };
38
+ }
39
+ const iface = registry.find(e => e.kind === 'interface' && e.name === refName);
40
+ return iface ? { ...iface.shape } : refName;
41
+ case "TSUnionType":
42
+ return typeNode.types.map(t => tsTypeToString(t, registry));
43
+ case "TSTypeLiteral":
44
+ const shape = {};
45
+ typeNode.members.forEach(m => {
46
+ if (m.type === 'TSPropertySignature') {
47
+ const name = m.key.name;
48
+ const optional = !!m.optional;
49
+ const propType = tsTypeToString(m.typeAnnotation.typeAnnotation, registry);
50
+ shape[name] = optional ? { kind: 'optional', type: propType } : propType;
51
+ }
52
+ });
53
+ return shape;
54
+ case "TSTupleType":
55
+ return { kind: 'tuple', elements: typeNode.elementTypes.map(et => tsTypeToString(et, registry)) };
56
+ default:
57
+ return "unknown";
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Parse code and collect type registry
63
+ */
64
+ export function parseCode(source) {
65
+ const parser = Parser.extend(ts());
66
+ const ast = parser.parse(source, { ecmaVersion: 2024, sourceType: "module" });
67
+
68
+ const typeRegistry = [];
69
+
70
+ // Pass 1: Interfaces + type aliases
71
+ walk(ast, {
72
+ enter(node) {
73
+ if (node.type === "TSInterfaceDeclaration") {
74
+ const shape = {};
75
+ node.body.body.forEach(m => {
76
+ if (m.type === 'TSPropertySignature') {
77
+ const name = m.key.name;
78
+ const optional = !!m.optional;
79
+ const propType = tsTypeToString(m.typeAnnotation.typeAnnotation, typeRegistry);
80
+ shape[name] = optional ? { kind: 'optional', type: propType } : propType;
81
+ }
82
+ });
83
+ typeRegistry.push({ kind: "interface", name: node.id.name, shape });
84
+ } else if (node.type === "TSTypeAliasDeclaration") {
85
+ const aliasType = tsTypeToString(node.typeAnnotation, typeRegistry);
86
+ typeRegistry.push({ kind: "typeAlias", name: node.id.name, type: aliasType });
87
+ }
88
+ }
89
+ });
90
+
91
+ // Pass 2: Variables + functions (resolve aliases)
92
+ walk(ast, {
93
+ enter(node) {
94
+ if (node.type === "VariableDeclaration") {
95
+ for (const decl of node.declarations) {
96
+ if (decl.id.typeAnnotation) {
97
+ let type = tsTypeToString(decl.id.typeAnnotation.typeAnnotation, typeRegistry);
98
+ if (typeof type === 'string') {
99
+ const alias = typeRegistry.find(e => e.kind === 'typeAlias' && e.name === type);
100
+ if (alias) type = alias.type;
101
+ }
102
+ typeRegistry.push({ kind: "variable", name: decl.id.name, type });
103
+ }
104
+ }
105
+ } else if (node.type === "FunctionDeclaration") {
106
+ const funcName = node.id.name;
107
+ const params = node.params.map(p => {
108
+ if (p.typeAnnotation) {
109
+ let type = tsTypeToString(p.typeAnnotation.typeAnnotation, typeRegistry);
110
+ if (typeof type === 'string') {
111
+ const alias = typeRegistry.find(e => e.kind === 'typeAlias' && e.name === type);
112
+ if (alias) type = alias.type;
113
+ }
114
+ return { name: p.name, type };
115
+ }
116
+ return null;
117
+ }).filter(Boolean);
118
+
119
+ let returnType = "any";
120
+ if (node.returnType) {
121
+ returnType = tsTypeToString(node.returnType.typeAnnotation, typeRegistry);
122
+ if (typeof returnType === 'string') {
123
+ const alias = typeRegistry.find(e => e.kind === 'typeAlias' && e.name === returnType);
124
+ if (alias) returnType = alias.type;
125
+ }
126
+ }
127
+
128
+ if (params.length > 0 || returnType !== "any") {
129
+ typeRegistry.push({ kind: "function", name: funcName, params, returnType });
130
+ }
131
+ }
132
+ }
133
+ });
134
+
135
+ return { ast, typeRegistry };
136
+ }