tova 0.9.13 → 0.9.15
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/package.json +1 -1
- package/src/analyzer/type-registry.js +164 -0
- package/src/codegen/codegen.js +17 -48
- package/src/codegen/edge-codegen.js +1 -5
- package/src/lsp/server.js +30 -0
- package/src/version.js +1 -1
package/package.json
CHANGED
|
@@ -88,4 +88,168 @@ export class TypeRegistry {
|
|
|
88
88
|
}
|
|
89
89
|
return [];
|
|
90
90
|
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Extract the base type name from a composite type string.
|
|
94
|
+
* "[Int]" → "Array", "Result<Int, String>" → "Result", "Option<String>" → "Option", "String" → "String"
|
|
95
|
+
*/
|
|
96
|
+
static extractBaseType(typeName) {
|
|
97
|
+
if (!typeName) return null;
|
|
98
|
+
if (typeName.startsWith('[') && typeName.endsWith(']')) return 'Array';
|
|
99
|
+
if (typeName.startsWith('Result<') || typeName === 'Result') return 'Result';
|
|
100
|
+
if (typeName.startsWith('Option<') || typeName === 'Option') return 'Option';
|
|
101
|
+
if (typeName.startsWith('Map<') || typeName === 'Map') return 'Map';
|
|
102
|
+
if (typeName.startsWith('Set<') || typeName === 'Set') return 'Set';
|
|
103
|
+
return typeName;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get built-in members (fields + methods) for a built-in type.
|
|
108
|
+
* Returns { fields: Map, methods: [] } or null if not a built-in type.
|
|
109
|
+
*/
|
|
110
|
+
getBuiltinMembers(typeName) {
|
|
111
|
+
const base = TypeRegistry.extractBaseType(typeName);
|
|
112
|
+
const descriptors = TypeRegistry.BUILTIN_MEMBERS[base];
|
|
113
|
+
if (!descriptors) return null;
|
|
114
|
+
|
|
115
|
+
const fields = new Map();
|
|
116
|
+
const methods = [];
|
|
117
|
+
for (const d of descriptors) {
|
|
118
|
+
if (d.kind === 'field') {
|
|
119
|
+
fields.set(d.name, d.returnType);
|
|
120
|
+
} else {
|
|
121
|
+
methods.push(d);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return { fields, methods };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ─── Built-in type member descriptors ──────────────────────────
|
|
129
|
+
|
|
130
|
+
function field(name, returnType, doc) {
|
|
131
|
+
return { kind: 'field', name, returnType, doc };
|
|
91
132
|
}
|
|
133
|
+
|
|
134
|
+
function method(name, params, returnType, doc) {
|
|
135
|
+
return { kind: 'method', name, params, returnType, doc };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
TypeRegistry.BUILTIN_MEMBERS = {
|
|
139
|
+
String: [
|
|
140
|
+
field('length', 'Int', 'Number of characters'),
|
|
141
|
+
method('slice', ['start: Int', 'end?: Int'], 'String', 'Extract a section of the string'),
|
|
142
|
+
method('includes', ['search: String'], 'Bool', 'Check if string contains substring'),
|
|
143
|
+
method('indexOf', ['search: String'], 'Int', 'First index of substring, or -1'),
|
|
144
|
+
method('lastIndexOf', ['search: String'], 'Int', 'Last index of substring, or -1'),
|
|
145
|
+
method('startsWith', ['prefix: String'], 'Bool', 'Check if string starts with prefix'),
|
|
146
|
+
method('endsWith', ['suffix: String'], 'Bool', 'Check if string ends with suffix'),
|
|
147
|
+
method('trim', [], 'String', 'Remove whitespace from both ends'),
|
|
148
|
+
method('trimStart', [], 'String', 'Remove whitespace from start'),
|
|
149
|
+
method('trimEnd', [], 'String', 'Remove whitespace from end'),
|
|
150
|
+
method('toUpperCase', [], 'String', 'Convert to uppercase'),
|
|
151
|
+
method('toLowerCase', [], 'String', 'Convert to lowercase'),
|
|
152
|
+
method('split', ['separator: String'], '[String]', 'Split into array of substrings'),
|
|
153
|
+
method('replace', ['search: String', 'replacement: String'], 'String', 'Replace first occurrence'),
|
|
154
|
+
method('replaceAll', ['search: String', 'replacement: String'], 'String', 'Replace all occurrences'),
|
|
155
|
+
method('repeat', ['count: Int'], 'String', 'Repeat the string n times'),
|
|
156
|
+
method('charAt', ['index: Int'], 'String', 'Character at index'),
|
|
157
|
+
method('charCodeAt', ['index: Int'], 'Int', 'Character code at index'),
|
|
158
|
+
method('concat', ['other: String'], 'String', 'Concatenate strings'),
|
|
159
|
+
method('padStart', ['length: Int', 'fill?: String'], 'String', 'Pad from start to length'),
|
|
160
|
+
method('padEnd', ['length: Int', 'fill?: String'], 'String', 'Pad from end to length'),
|
|
161
|
+
method('match', ['pattern: String'], 'Option', 'Match against regex pattern'),
|
|
162
|
+
method('search', ['pattern: String'], 'Int', 'Search for regex pattern'),
|
|
163
|
+
method('toString', [], 'String', 'Convert to string'),
|
|
164
|
+
method('substring', ['start: Int', 'end?: Int'], 'String', 'Extract characters between indices'),
|
|
165
|
+
method('at', ['index: Int'], 'String', 'Character at index (supports negative)'),
|
|
166
|
+
],
|
|
167
|
+
Array: [
|
|
168
|
+
field('length', 'Int', 'Number of elements'),
|
|
169
|
+
method('push', ['item: T'], 'Int', 'Add element to end, returns new length'),
|
|
170
|
+
method('pop', [], 'T', 'Remove and return last element'),
|
|
171
|
+
method('shift', [], 'T', 'Remove and return first element'),
|
|
172
|
+
method('unshift', ['item: T'], 'Int', 'Add element to start, returns new length'),
|
|
173
|
+
method('splice', ['start: Int', 'deleteCount?: Int'], '[T]', 'Remove/replace elements'),
|
|
174
|
+
method('slice', ['start?: Int', 'end?: Int'], '[T]', 'Extract a section of the array'),
|
|
175
|
+
method('concat', ['other: [T]'], '[T]', 'Merge arrays'),
|
|
176
|
+
method('join', ['separator?: String'], 'String', 'Join elements into string'),
|
|
177
|
+
method('reverse', [], '[T]', 'Reverse array in place'),
|
|
178
|
+
method('sort', ['compareFn?: fn(T, T) -> Int'], '[T]', 'Sort array in place'),
|
|
179
|
+
method('map', ['fn: fn(T) -> U'], '[U]', 'Transform each element'),
|
|
180
|
+
method('filter', ['fn: fn(T) -> Bool'], '[T]', 'Keep elements matching predicate'),
|
|
181
|
+
method('reduce', ['fn: fn(acc, T) -> U', 'initial: U'], 'U', 'Reduce to single value'),
|
|
182
|
+
method('find', ['fn: fn(T) -> Bool'], 'Option', 'Find first matching element'),
|
|
183
|
+
method('findIndex', ['fn: fn(T) -> Bool'], 'Int', 'Index of first match, or -1'),
|
|
184
|
+
method('some', ['fn: fn(T) -> Bool'], 'Bool', 'Check if any element matches'),
|
|
185
|
+
method('every', ['fn: fn(T) -> Bool'], 'Bool', 'Check if all elements match'),
|
|
186
|
+
method('includes', ['item: T'], 'Bool', 'Check if array contains element'),
|
|
187
|
+
method('indexOf', ['item: T'], 'Int', 'First index of element, or -1'),
|
|
188
|
+
method('flat', [], '[T]', 'Flatten one level of nesting'),
|
|
189
|
+
method('flatMap', ['fn: fn(T) -> [U]'], '[U]', 'Map then flatten'),
|
|
190
|
+
method('fill', ['value: T', 'start?: Int', 'end?: Int'], '[T]', 'Fill with value'),
|
|
191
|
+
method('forEach', ['fn: fn(T) -> Nil'], 'Nil', 'Execute function for each element'),
|
|
192
|
+
method('at', ['index: Int'], 'T', 'Element at index (supports negative)'),
|
|
193
|
+
],
|
|
194
|
+
Result: [
|
|
195
|
+
method('map', ['fn: fn(T) -> U'], 'Result', 'Transform Ok value'),
|
|
196
|
+
method('flatMap', ['fn: fn(T) -> Result'], 'Result', 'Chain Result-returning function'),
|
|
197
|
+
method('andThen', ['fn: fn(T) -> Result'], 'Result', 'Alias for flatMap'),
|
|
198
|
+
method('unwrap', [], 'T', 'Get Ok value or throw on Err'),
|
|
199
|
+
method('unwrapOr', ['default: T'], 'T', 'Get Ok value or return default'),
|
|
200
|
+
method('expect', ['msg: String'], 'T', 'Get Ok value or throw with message'),
|
|
201
|
+
method('isOk', [], 'Bool', 'Check if Result is Ok'),
|
|
202
|
+
method('isErr', [], 'Bool', 'Check if Result is Err'),
|
|
203
|
+
method('mapErr', ['fn: fn(E) -> F'], 'Result', 'Transform Err value'),
|
|
204
|
+
method('unwrapErr', [], 'E', 'Get Err value or throw on Ok'),
|
|
205
|
+
method('or', ['other: Result'], 'Result', 'Return self if Ok, otherwise other'),
|
|
206
|
+
method('and', ['other: Result'], 'Result', 'Return other if Ok, otherwise self'),
|
|
207
|
+
method('context', ['msg: String'], 'Result', 'Add context to Err'),
|
|
208
|
+
],
|
|
209
|
+
Option: [
|
|
210
|
+
method('map', ['fn: fn(T) -> U'], 'Option', 'Transform Some value'),
|
|
211
|
+
method('flatMap', ['fn: fn(T) -> Option'], 'Option', 'Chain Option-returning function'),
|
|
212
|
+
method('andThen', ['fn: fn(T) -> Option'], 'Option', 'Alias for flatMap'),
|
|
213
|
+
method('unwrap', [], 'T', 'Get Some value or throw on None'),
|
|
214
|
+
method('unwrapOr', ['default: T'], 'T', 'Get Some value or return default'),
|
|
215
|
+
method('expect', ['msg: String'], 'T', 'Get Some value or throw with message'),
|
|
216
|
+
method('isSome', [], 'Bool', 'Check if Option is Some'),
|
|
217
|
+
method('isNone', [], 'Bool', 'Check if Option is None'),
|
|
218
|
+
method('or', ['other: Option'], 'Option', 'Return self if Some, otherwise other'),
|
|
219
|
+
method('and', ['other: Option'], 'Option', 'Return other if Some, otherwise self'),
|
|
220
|
+
method('filter', ['fn: fn(T) -> Bool'], 'Option', 'Keep Some if predicate matches'),
|
|
221
|
+
],
|
|
222
|
+
Map: [
|
|
223
|
+
field('size', 'Int', 'Number of key-value pairs'),
|
|
224
|
+
method('get', ['key: K'], 'V', 'Get value by key'),
|
|
225
|
+
method('set', ['key: K', 'value: V'], 'Map', 'Set key-value pair'),
|
|
226
|
+
method('has', ['key: K'], 'Bool', 'Check if key exists'),
|
|
227
|
+
method('delete', ['key: K'], 'Bool', 'Remove key-value pair'),
|
|
228
|
+
method('clear', [], 'Nil', 'Remove all entries'),
|
|
229
|
+
method('keys', [], '[K]', 'Get all keys'),
|
|
230
|
+
method('values', [], '[V]', 'Get all values'),
|
|
231
|
+
method('entries', [], '[(K, V)]', 'Get all key-value pairs'),
|
|
232
|
+
method('forEach', ['fn: fn(V, K) -> Nil'], 'Nil', 'Execute function for each entry'),
|
|
233
|
+
],
|
|
234
|
+
Set: [
|
|
235
|
+
field('size', 'Int', 'Number of elements'),
|
|
236
|
+
method('add', ['value: T'], 'Set', 'Add element'),
|
|
237
|
+
method('has', ['value: T'], 'Bool', 'Check if element exists'),
|
|
238
|
+
method('delete', ['value: T'], 'Bool', 'Remove element'),
|
|
239
|
+
method('clear', [], 'Nil', 'Remove all elements'),
|
|
240
|
+
method('keys', [], '[T]', 'Get all values (alias)'),
|
|
241
|
+
method('values', [], '[T]', 'Get all values'),
|
|
242
|
+
method('entries', [], '[(T, T)]', 'Get all value-value pairs'),
|
|
243
|
+
method('forEach', ['fn: fn(T) -> Nil'], 'Nil', 'Execute function for each element'),
|
|
244
|
+
],
|
|
245
|
+
Int: [
|
|
246
|
+
method('toString', [], 'String', 'Convert to string'),
|
|
247
|
+
method('toFixed', ['digits?: Int'], 'String', 'Format with fixed decimal places'),
|
|
248
|
+
method('toPrecision', ['precision?: Int'], 'String', 'Format to specified precision'),
|
|
249
|
+
],
|
|
250
|
+
Float: [
|
|
251
|
+
method('toString', [], 'String', 'Convert to string'),
|
|
252
|
+
method('toFixed', ['digits?: Int'], 'String', 'Format with fixed decimal places'),
|
|
253
|
+
method('toPrecision', ['precision?: Int'], 'String', 'Format to specified precision'),
|
|
254
|
+
],
|
|
255
|
+
};
|
package/src/codegen/codegen.js
CHANGED
|
@@ -2,57 +2,26 @@
|
|
|
2
2
|
// Supports named multi-blocks: server "api" { }, server "ws" { }
|
|
3
3
|
// Blocks with the same name are merged; different names produce separate output files.
|
|
4
4
|
|
|
5
|
-
import { createRequire } from 'module';
|
|
6
5
|
import { SharedCodegen } from './shared-codegen.js';
|
|
7
6
|
import { BUILTIN_NAMES } from '../stdlib/inline.js';
|
|
8
7
|
import { BlockRegistry } from '../registry/register-all.js';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
function getBrowserCodegen() {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function
|
|
25
|
-
|
|
26
|
-
return _SecurityCodegen;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function getCliCodegen() {
|
|
30
|
-
if (!_CliCodegen) _CliCodegen = _require('./cli-codegen.js').CliCodegen;
|
|
31
|
-
return _CliCodegen;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function getEdgeCodegen() {
|
|
35
|
-
if (!_EdgeCodegen) _EdgeCodegen = _require('./edge-codegen.js').EdgeCodegen;
|
|
36
|
-
return _EdgeCodegen;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
let _DeployCodegen;
|
|
40
|
-
function getDeployCodegen() {
|
|
41
|
-
if (!_DeployCodegen) _DeployCodegen = _require('./deploy-codegen.js').DeployCodegen;
|
|
42
|
-
return _DeployCodegen;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
let _ThemeCodegen;
|
|
46
|
-
function getThemeCodegen() {
|
|
47
|
-
if (!_ThemeCodegen) _ThemeCodegen = _require('./theme-codegen.js').ThemeCodegen;
|
|
48
|
-
return _ThemeCodegen;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
let _AuthCodegen;
|
|
52
|
-
function getAuthCodegen() {
|
|
53
|
-
if (!_AuthCodegen) _AuthCodegen = _require('./auth-codegen.js').AuthCodegen;
|
|
54
|
-
return _AuthCodegen;
|
|
55
|
-
}
|
|
8
|
+
import { ServerCodegen } from './server-codegen.js';
|
|
9
|
+
import { BrowserCodegen } from './browser-codegen.js';
|
|
10
|
+
import { SecurityCodegen } from './security-codegen.js';
|
|
11
|
+
import { CliCodegen } from './cli-codegen.js';
|
|
12
|
+
import { EdgeCodegen } from './edge-codegen.js';
|
|
13
|
+
import { DeployCodegen } from './deploy-codegen.js';
|
|
14
|
+
import { ThemeCodegen } from './theme-codegen.js';
|
|
15
|
+
import { AuthCodegen } from './auth-codegen.js';
|
|
16
|
+
|
|
17
|
+
function getServerCodegen() { return ServerCodegen; }
|
|
18
|
+
function getBrowserCodegen() { return BrowserCodegen; }
|
|
19
|
+
function getSecurityCodegen() { return SecurityCodegen; }
|
|
20
|
+
function getCliCodegen() { return CliCodegen; }
|
|
21
|
+
function getEdgeCodegen() { return EdgeCodegen; }
|
|
22
|
+
function getDeployCodegen() { return DeployCodegen; }
|
|
23
|
+
function getThemeCodegen() { return ThemeCodegen; }
|
|
24
|
+
function getAuthCodegen() { return AuthCodegen; }
|
|
56
25
|
|
|
57
26
|
export class CodeGenerator {
|
|
58
27
|
constructor(ast, filename = '<stdin>', options = {}) {
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
// Edge/serverless code generator for the Tova language
|
|
2
2
|
// Produces deployment-ready code for Cloudflare Workers, Deno Deploy, Vercel Edge, AWS Lambda, or Bun.
|
|
3
3
|
|
|
4
|
-
import { createRequire } from 'module';
|
|
5
4
|
import { BaseCodegen } from './base-codegen.js';
|
|
6
|
-
|
|
7
|
-
const _require = createRequire(import.meta.url);
|
|
8
|
-
let _SecurityCodegen;
|
|
5
|
+
import { SecurityCodegen as _SecurityCodegen } from './security-codegen.js';
|
|
9
6
|
|
|
10
7
|
const DEFAULT_TARGET = 'cloudflare';
|
|
11
8
|
|
|
@@ -295,7 +292,6 @@ export class EdgeCodegen extends BaseCodegen {
|
|
|
295
292
|
const noSec = { hasAuth: false, hasProtect: false, hasAutoSanitize: false };
|
|
296
293
|
if (!securityConfig) return noSec;
|
|
297
294
|
|
|
298
|
-
if (!_SecurityCodegen) _SecurityCodegen = _require('./security-codegen.js').SecurityCodegen;
|
|
299
295
|
const secGen = new _SecurityCodegen();
|
|
300
296
|
const fragments = secGen.generateServerSecurity(securityConfig);
|
|
301
297
|
|
package/src/lsp/server.js
CHANGED
|
@@ -629,6 +629,36 @@ class TovaLanguageServer {
|
|
|
629
629
|
});
|
|
630
630
|
}
|
|
631
631
|
}
|
|
632
|
+
|
|
633
|
+
// Fall back to built-in type members if no user-defined members found
|
|
634
|
+
if (items.length === 0) {
|
|
635
|
+
const builtin = typeRegistry.getBuiltinMembers(typeName);
|
|
636
|
+
if (builtin) {
|
|
637
|
+
for (const [fieldName, fieldType] of builtin.fields) {
|
|
638
|
+
if (!partial || fieldName.startsWith(partial)) {
|
|
639
|
+
items.push({
|
|
640
|
+
label: fieldName,
|
|
641
|
+
kind: 5, // Field
|
|
642
|
+
detail: fieldType || 'field',
|
|
643
|
+
sortText: `0${fieldName}`,
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
for (const m of builtin.methods) {
|
|
648
|
+
if (!partial || m.name.startsWith(partial)) {
|
|
649
|
+
const paramStr = (m.params || []).join(', ');
|
|
650
|
+
const retStr = m.returnType ? ` -> ${m.returnType}` : '';
|
|
651
|
+
items.push({
|
|
652
|
+
label: m.name,
|
|
653
|
+
kind: 2, // Method
|
|
654
|
+
detail: `fn(${paramStr})${retStr}`,
|
|
655
|
+
documentation: m.doc,
|
|
656
|
+
sortText: `1${m.name}`,
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
632
662
|
}
|
|
633
663
|
}
|
|
634
664
|
|
package/src/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated by scripts/embed-runtime.js — do not edit
|
|
2
|
-
export const VERSION = "0.9.
|
|
2
|
+
export const VERSION = "0.9.15";
|