tova 0.5.1 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/bin/tova.js +261 -60
  2. package/package.json +1 -1
  3. package/src/analyzer/analyzer.js +351 -11
  4. package/src/analyzer/{client-analyzer.js → browser-analyzer.js} +20 -17
  5. package/src/analyzer/deploy-analyzer.js +44 -0
  6. package/src/analyzer/form-analyzer.js +113 -0
  7. package/src/analyzer/scope.js +2 -2
  8. package/src/codegen/base-codegen.js +1160 -10
  9. package/src/codegen/{client-codegen.js → browser-codegen.js} +444 -5
  10. package/src/codegen/codegen.js +119 -28
  11. package/src/codegen/deploy-codegen.js +49 -0
  12. package/src/codegen/edge-codegen.js +1351 -0
  13. package/src/codegen/form-codegen.js +553 -0
  14. package/src/codegen/security-codegen.js +5 -5
  15. package/src/codegen/server-codegen.js +88 -7
  16. package/src/codegen/shared-codegen.js +5 -0
  17. package/src/codegen/wasm-codegen.js +6 -0
  18. package/src/config/edit-toml.js +6 -2
  19. package/src/config/git-resolver.js +128 -0
  20. package/src/config/lock-file.js +57 -0
  21. package/src/config/module-cache.js +58 -0
  22. package/src/config/module-entry.js +37 -0
  23. package/src/config/module-path.js +31 -0
  24. package/src/config/pkg-errors.js +62 -0
  25. package/src/config/resolve.js +17 -0
  26. package/src/config/resolver.js +139 -0
  27. package/src/config/search.js +28 -0
  28. package/src/config/semver.js +72 -0
  29. package/src/config/toml.js +48 -5
  30. package/src/deploy/deploy.js +217 -0
  31. package/src/deploy/infer.js +218 -0
  32. package/src/deploy/provision.js +311 -0
  33. package/src/diagnostics/error-codes.js +1 -1
  34. package/src/docs/generator.js +1 -1
  35. package/src/formatter/formatter.js +4 -4
  36. package/src/lexer/tokens.js +12 -2
  37. package/src/lsp/server.js +483 -1
  38. package/src/parser/ast.js +60 -5
  39. package/src/parser/{client-ast.js → browser-ast.js} +3 -3
  40. package/src/parser/{client-parser.js → browser-parser.js} +42 -15
  41. package/src/parser/concurrency-ast.js +15 -0
  42. package/src/parser/concurrency-parser.js +236 -0
  43. package/src/parser/deploy-ast.js +37 -0
  44. package/src/parser/deploy-parser.js +132 -0
  45. package/src/parser/edge-ast.js +83 -0
  46. package/src/parser/edge-parser.js +262 -0
  47. package/src/parser/form-ast.js +80 -0
  48. package/src/parser/form-parser.js +206 -0
  49. package/src/parser/parser.js +82 -14
  50. package/src/parser/select-ast.js +39 -0
  51. package/src/registry/plugins/browser-plugin.js +30 -0
  52. package/src/registry/plugins/concurrency-plugin.js +32 -0
  53. package/src/registry/plugins/deploy-plugin.js +33 -0
  54. package/src/registry/plugins/edge-plugin.js +32 -0
  55. package/src/registry/register-all.js +8 -2
  56. package/src/runtime/ssr.js +2 -2
  57. package/src/stdlib/inline.js +38 -6
  58. package/src/stdlib/runtime-bridge.js +152 -0
  59. package/src/version.js +1 -1
  60. package/src/registry/plugins/client-plugin.js +0 -30
package/src/lsp/server.js CHANGED
@@ -121,6 +121,7 @@ class TovaLanguageServer {
121
121
  case 'textDocument/codeAction': return this._onCodeAction(msg);
122
122
  case 'textDocument/references': return this._onReferences(msg);
123
123
  case 'textDocument/inlayHint': return this._onInlayHint(msg);
124
+ case 'textDocument/semanticTokens/full': return this._onSemanticTokensFull(msg);
124
125
  case 'workspace/symbol': return this._onWorkspaceSymbol(msg);
125
126
  default: return this._respondError(msg.id, -32601, `Method not found: ${method}`);
126
127
  }
@@ -165,6 +166,48 @@ class TovaLanguageServer {
165
166
  referencesProvider: true,
166
167
  inlayHintProvider: true,
167
168
  workspaceSymbolProvider: true,
169
+ semanticTokensProvider: {
170
+ legend: {
171
+ tokenTypes: [
172
+ 'namespace', // 0
173
+ 'type', // 1
174
+ 'class', // 2
175
+ 'enum', // 3
176
+ 'interface', // 4
177
+ 'struct', // 5
178
+ 'typeParameter', // 6
179
+ 'parameter', // 7
180
+ 'variable', // 8
181
+ 'property', // 9
182
+ 'enumMember', // 10
183
+ 'event', // 11
184
+ 'function', // 12
185
+ 'method', // 13
186
+ 'macro', // 14
187
+ 'keyword', // 15
188
+ 'modifier', // 16
189
+ 'comment', // 17
190
+ 'string', // 18
191
+ 'number', // 19
192
+ 'regexp', // 20
193
+ 'decorator', // 21
194
+ ],
195
+ tokenModifiers: [
196
+ 'declaration', // 0
197
+ 'definition', // 1
198
+ 'readonly', // 2
199
+ 'static', // 3
200
+ 'deprecated', // 4
201
+ 'abstract', // 5
202
+ 'async', // 6
203
+ 'modification', // 7
204
+ 'documentation', // 8
205
+ 'defaultLibrary', // 9
206
+ ],
207
+ },
208
+ full: true,
209
+ range: false,
210
+ },
168
211
  },
169
212
  });
170
213
  }
@@ -475,7 +518,7 @@ class TovaLanguageServer {
475
518
  const keywords = [
476
519
  'fn', 'let', 'if', 'elif', 'else', 'for', 'while', 'loop', 'when', 'in',
477
520
  'return', 'match', 'type', 'import', 'from', 'true', 'false',
478
- 'nil', 'server', 'client', 'shared', 'pub', 'mut',
521
+ 'nil', 'server', 'browser', 'client', 'shared', 'pub', 'mut',
479
522
  'try', 'catch', 'finally', 'break', 'continue', 'async', 'await',
480
523
  'guard', 'interface', 'derive', 'route', 'model', 'db',
481
524
  ];
@@ -1901,6 +1944,445 @@ class TovaLanguageServer {
1901
1944
  return positions;
1902
1945
  }
1903
1946
 
1947
+ // ─── Semantic Tokens ─────────────────────────────────────
1948
+
1949
+ _onSemanticTokensFull(msg) {
1950
+ const { textDocument } = msg.params;
1951
+ const cached = this._diagnosticsCache.get(textDocument.uri);
1952
+ if (!cached?.ast || !cached?.analyzer) {
1953
+ return this._respond(msg.id, { data: [] });
1954
+ }
1955
+
1956
+ const tokens = [];
1957
+ try {
1958
+ this._walkASTForSemanticTokens(cached.ast, cached.analyzer, tokens);
1959
+ } catch (e) {
1960
+ this._logError(`Semantic tokens error: ${e.message}`);
1961
+ }
1962
+
1963
+ // Sort by position (line first, then character)
1964
+ tokens.sort((a, b) => a.line - b.line || a.char - b.char);
1965
+
1966
+ // Delta-encode into flat array [deltaLine, deltaChar, length, tokenType, tokenModifiers]
1967
+ const data = [];
1968
+ let prevLine = 0;
1969
+ let prevChar = 0;
1970
+
1971
+ for (const tok of tokens) {
1972
+ const deltaLine = tok.line - prevLine;
1973
+ const deltaChar = deltaLine === 0 ? tok.char - prevChar : tok.char;
1974
+ data.push(deltaLine, deltaChar, tok.length, tok.tokenType, tok.modifiers);
1975
+ prevLine = tok.line;
1976
+ prevChar = tok.char;
1977
+ }
1978
+
1979
+ this._respond(msg.id, { data });
1980
+ }
1981
+
1982
+ _walkASTForSemanticTokens(node, analyzer, tokens) {
1983
+ if (!node || typeof node !== 'object') return;
1984
+
1985
+ // Token type indices (must match legend in capabilities)
1986
+ const TT_NAMESPACE = 0;
1987
+ const TT_TYPE = 1;
1988
+ const TT_INTERFACE = 4;
1989
+ const TT_PARAMETER = 7;
1990
+ const TT_VARIABLE = 8;
1991
+ const TT_PROPERTY = 9;
1992
+ const TT_ENUM_MEMBER = 10;
1993
+ const TT_FUNCTION = 12;
1994
+ const TT_DECORATOR = 21;
1995
+
1996
+ // Token modifier bitmasks
1997
+ const TM_DECLARATION = 1 << 0;
1998
+ const TM_READONLY = 1 << 2;
1999
+ const TM_ASYNC = 1 << 6;
2000
+ const TM_DEFAULT_LIBRARY = 1 << 9;
2001
+
2002
+ const loc = node.loc || node.location;
2003
+
2004
+ switch (node.type) {
2005
+ case 'FunctionDeclaration': {
2006
+ // Emit function name
2007
+ if (node.name && loc) {
2008
+ const nameOffset = node.async ? 'async fn '.length : 'fn '.length;
2009
+ let modifiers = TM_DECLARATION;
2010
+ if (node.async) modifiers |= TM_ASYNC;
2011
+ tokens.push({
2012
+ line: (loc.line || 1) - 1,
2013
+ char: (loc.column || 1) - 1 + nameOffset,
2014
+ length: node.name.length,
2015
+ tokenType: TT_FUNCTION,
2016
+ modifiers,
2017
+ });
2018
+ }
2019
+ // Walk parameters
2020
+ if (node.params) {
2021
+ for (const param of node.params) {
2022
+ this._walkASTForSemanticTokens(param, analyzer, tokens);
2023
+ }
2024
+ }
2025
+ // Walk return type
2026
+ if (node.returnType) {
2027
+ this._walkTypeAnnotation(node.returnType, tokens);
2028
+ }
2029
+ // Walk body
2030
+ if (node.body) {
2031
+ this._walkASTForSemanticTokens(node.body, analyzer, tokens);
2032
+ }
2033
+ return;
2034
+ }
2035
+
2036
+ case 'Parameter': {
2037
+ if (node.name && loc) {
2038
+ tokens.push({
2039
+ line: (loc.line || 1) - 1,
2040
+ char: (loc.column || 1) - 1,
2041
+ length: node.name.length,
2042
+ tokenType: TT_PARAMETER,
2043
+ modifiers: TM_DECLARATION,
2044
+ });
2045
+ }
2046
+ if (node.typeAnnotation) {
2047
+ this._walkTypeAnnotation(node.typeAnnotation, tokens);
2048
+ }
2049
+ if (node.default) {
2050
+ this._walkASTForSemanticTokens(node.default, analyzer, tokens);
2051
+ }
2052
+ return;
2053
+ }
2054
+
2055
+ case 'TypeDeclaration': {
2056
+ if (node.name && loc) {
2057
+ const nameOffset = 'type '.length;
2058
+ tokens.push({
2059
+ line: (loc.line || 1) - 1,
2060
+ char: (loc.column || 1) - 1 + nameOffset,
2061
+ length: node.name.length,
2062
+ tokenType: TT_TYPE,
2063
+ modifiers: TM_DECLARATION,
2064
+ });
2065
+ }
2066
+ if (node.variants) {
2067
+ for (const v of node.variants) {
2068
+ this._walkASTForSemanticTokens(v, analyzer, tokens);
2069
+ }
2070
+ }
2071
+ if (node.fields) {
2072
+ for (const f of node.fields) {
2073
+ this._walkASTForSemanticTokens(f, analyzer, tokens);
2074
+ }
2075
+ }
2076
+ return;
2077
+ }
2078
+
2079
+ case 'TypeVariant': {
2080
+ if (node.name && loc) {
2081
+ tokens.push({
2082
+ line: (loc.line || 1) - 1,
2083
+ char: (loc.column || 1) - 1,
2084
+ length: node.name.length,
2085
+ tokenType: TT_ENUM_MEMBER,
2086
+ modifiers: TM_DECLARATION,
2087
+ });
2088
+ }
2089
+ if (node.fields) {
2090
+ for (const f of node.fields) {
2091
+ this._walkASTForSemanticTokens(f, analyzer, tokens);
2092
+ }
2093
+ }
2094
+ return;
2095
+ }
2096
+
2097
+ case 'TypeField': {
2098
+ if (node.name && loc) {
2099
+ tokens.push({
2100
+ line: (loc.line || 1) - 1,
2101
+ char: (loc.column || 1) - 1,
2102
+ length: node.name.length,
2103
+ tokenType: TT_PROPERTY,
2104
+ modifiers: TM_DECLARATION,
2105
+ });
2106
+ }
2107
+ if (node.typeAnnotation) {
2108
+ this._walkTypeAnnotation(node.typeAnnotation, tokens);
2109
+ }
2110
+ return;
2111
+ }
2112
+
2113
+ case 'InterfaceDeclaration':
2114
+ case 'TraitDeclaration': {
2115
+ if (node.name && loc) {
2116
+ const keyword = node.type === 'InterfaceDeclaration' ? 'interface ' : 'trait ';
2117
+ tokens.push({
2118
+ line: (loc.line || 1) - 1,
2119
+ char: (loc.column || 1) - 1 + keyword.length,
2120
+ length: node.name.length,
2121
+ tokenType: TT_INTERFACE,
2122
+ modifiers: TM_DECLARATION,
2123
+ });
2124
+ }
2125
+ if (node.methods) {
2126
+ for (const m of node.methods) {
2127
+ this._walkASTForSemanticTokens(m, analyzer, tokens);
2128
+ }
2129
+ }
2130
+ if (node.body) {
2131
+ this._walkASTForSemanticTokens(node.body, analyzer, tokens);
2132
+ }
2133
+ return;
2134
+ }
2135
+
2136
+ case 'ComponentDeclaration': {
2137
+ if (node.name && loc) {
2138
+ const nameOffset = 'component '.length;
2139
+ tokens.push({
2140
+ line: (loc.line || 1) - 1,
2141
+ char: (loc.column || 1) - 1 + nameOffset,
2142
+ length: node.name.length,
2143
+ tokenType: TT_TYPE,
2144
+ modifiers: TM_DECLARATION,
2145
+ });
2146
+ }
2147
+ if (node.body) {
2148
+ this._walkASTForSemanticTokens(node.body, analyzer, tokens);
2149
+ }
2150
+ return;
2151
+ }
2152
+
2153
+ case 'StateDeclaration':
2154
+ case 'ComputedDeclaration': {
2155
+ if (node.name && loc) {
2156
+ const keyword = node.type === 'StateDeclaration' ? 'state ' : 'computed ';
2157
+ tokens.push({
2158
+ line: (loc.line || 1) - 1,
2159
+ char: (loc.column || 1) - 1 + keyword.length,
2160
+ length: node.name.length,
2161
+ tokenType: TT_VARIABLE,
2162
+ modifiers: TM_DECLARATION,
2163
+ });
2164
+ }
2165
+ if (node.value) {
2166
+ this._walkASTForSemanticTokens(node.value, analyzer, tokens);
2167
+ }
2168
+ if (node.body) {
2169
+ this._walkASTForSemanticTokens(node.body, analyzer, tokens);
2170
+ }
2171
+ return;
2172
+ }
2173
+
2174
+ case 'Assignment': {
2175
+ if (node.name && loc) {
2176
+ tokens.push({
2177
+ line: (loc.line || 1) - 1,
2178
+ char: (loc.column || 1) - 1,
2179
+ length: node.name.length,
2180
+ tokenType: TT_VARIABLE,
2181
+ modifiers: TM_DECLARATION | TM_READONLY,
2182
+ });
2183
+ }
2184
+ if (node.typeAnnotation) {
2185
+ this._walkTypeAnnotation(node.typeAnnotation, tokens);
2186
+ }
2187
+ if (node.value) {
2188
+ this._walkASTForSemanticTokens(node.value, analyzer, tokens);
2189
+ }
2190
+ return;
2191
+ }
2192
+
2193
+ case 'VarDeclaration': {
2194
+ if (node.name && loc) {
2195
+ const nameOffset = 'var '.length;
2196
+ tokens.push({
2197
+ line: (loc.line || 1) - 1,
2198
+ char: (loc.column || 1) - 1 + nameOffset,
2199
+ length: node.name.length,
2200
+ tokenType: TT_VARIABLE,
2201
+ modifiers: TM_DECLARATION,
2202
+ });
2203
+ }
2204
+ if (node.typeAnnotation) {
2205
+ this._walkTypeAnnotation(node.typeAnnotation, tokens);
2206
+ }
2207
+ if (node.value) {
2208
+ this._walkASTForSemanticTokens(node.value, analyzer, tokens);
2209
+ }
2210
+ return;
2211
+ }
2212
+
2213
+ case 'CallExpression': {
2214
+ // Emit callee as function
2215
+ if (node.callee) {
2216
+ if (node.callee.type === 'Identifier' && node.callee.name) {
2217
+ const calleeLoc = node.callee.loc || node.callee.location;
2218
+ if (calleeLoc) {
2219
+ let modifiers = 0;
2220
+ if (BUILTIN_NAMES.has(node.callee.name)) {
2221
+ modifiers |= TM_DEFAULT_LIBRARY;
2222
+ }
2223
+ tokens.push({
2224
+ line: (calleeLoc.line || 1) - 1,
2225
+ char: (calleeLoc.column || 1) - 1,
2226
+ length: node.callee.name.length,
2227
+ tokenType: TT_FUNCTION,
2228
+ modifiers,
2229
+ });
2230
+ }
2231
+ } else {
2232
+ this._walkASTForSemanticTokens(node.callee, analyzer, tokens);
2233
+ }
2234
+ }
2235
+ // Walk arguments
2236
+ if (node.args) {
2237
+ for (const arg of node.args) {
2238
+ this._walkASTForSemanticTokens(arg, analyzer, tokens);
2239
+ }
2240
+ }
2241
+ if (node.arguments) {
2242
+ for (const arg of node.arguments) {
2243
+ this._walkASTForSemanticTokens(arg, analyzer, tokens);
2244
+ }
2245
+ }
2246
+ return;
2247
+ }
2248
+
2249
+ case 'MemberExpression': {
2250
+ // Walk object
2251
+ if (node.object) {
2252
+ this._walkASTForSemanticTokens(node.object, analyzer, tokens);
2253
+ }
2254
+ // Emit property
2255
+ if (node.property && typeof node.property === 'object' && node.property.name) {
2256
+ const propLoc = node.property.loc || node.property.location;
2257
+ if (propLoc) {
2258
+ tokens.push({
2259
+ line: (propLoc.line || 1) - 1,
2260
+ char: (propLoc.column || 1) - 1,
2261
+ length: node.property.name.length,
2262
+ tokenType: TT_PROPERTY,
2263
+ modifiers: 0,
2264
+ });
2265
+ }
2266
+ }
2267
+ return;
2268
+ }
2269
+
2270
+ case 'ServerBlock':
2271
+ case 'BrowserBlock':
2272
+ case 'SharedBlock':
2273
+ case 'ClientBlock': {
2274
+ if (loc) {
2275
+ const keyword = node.type.replace('Block', '').toLowerCase();
2276
+ tokens.push({
2277
+ line: (loc.line || 1) - 1,
2278
+ char: (loc.column || 1) - 1,
2279
+ length: keyword.length,
2280
+ tokenType: TT_NAMESPACE,
2281
+ modifiers: 0,
2282
+ });
2283
+ }
2284
+ if (node.body) {
2285
+ if (Array.isArray(node.body)) {
2286
+ for (const child of node.body) {
2287
+ this._walkASTForSemanticTokens(child, analyzer, tokens);
2288
+ }
2289
+ } else {
2290
+ this._walkASTForSemanticTokens(node.body, analyzer, tokens);
2291
+ }
2292
+ }
2293
+ return;
2294
+ }
2295
+
2296
+ case 'Identifier': {
2297
+ if (node.name && loc) {
2298
+ const sym = this._findSymbolInScopes(analyzer, node.name);
2299
+ if (sym) {
2300
+ let tokenType = TT_VARIABLE;
2301
+ let modifiers = 0;
2302
+
2303
+ if (sym.kind === 'function') tokenType = TT_FUNCTION;
2304
+ else if (sym.kind === 'type') tokenType = TT_TYPE;
2305
+ else if (sym.kind === 'parameter') tokenType = TT_PARAMETER;
2306
+
2307
+ if (BUILTIN_NAMES.has(node.name)) {
2308
+ modifiers |= TM_DEFAULT_LIBRARY;
2309
+ }
2310
+
2311
+ tokens.push({
2312
+ line: (loc.line || 1) - 1,
2313
+ char: (loc.column || 1) - 1,
2314
+ length: node.name.length,
2315
+ tokenType,
2316
+ modifiers,
2317
+ });
2318
+ }
2319
+ }
2320
+ return;
2321
+ }
2322
+
2323
+ case 'Decorator': {
2324
+ if (node.name && loc) {
2325
+ tokens.push({
2326
+ line: (loc.line || 1) - 1,
2327
+ char: (loc.column || 1) - 1,
2328
+ length: node.name.length + 1, // include @
2329
+ tokenType: TT_DECORATOR,
2330
+ modifiers: 0,
2331
+ });
2332
+ }
2333
+ return;
2334
+ }
2335
+ }
2336
+
2337
+ // Default: recursively walk all children that have .type (AST nodes)
2338
+ for (const key of Object.keys(node)) {
2339
+ if (key === 'loc' || key === 'location' || key === 'type') continue;
2340
+ const child = node[key];
2341
+ if (Array.isArray(child)) {
2342
+ for (const item of child) {
2343
+ if (item && typeof item === 'object' && item.type) {
2344
+ this._walkASTForSemanticTokens(item, analyzer, tokens);
2345
+ }
2346
+ }
2347
+ } else if (child && typeof child === 'object' && child.type) {
2348
+ this._walkASTForSemanticTokens(child, analyzer, tokens);
2349
+ }
2350
+ }
2351
+ }
2352
+
2353
+ _walkTypeAnnotation(typeNode, tokens) {
2354
+ if (!typeNode || typeof typeNode !== 'object') return;
2355
+ const loc = typeNode.loc || typeNode.location;
2356
+
2357
+ // Named type reference
2358
+ if (typeNode.name && loc) {
2359
+ tokens.push({
2360
+ line: (loc.line || 1) - 1,
2361
+ char: (loc.column || 1) - 1,
2362
+ length: typeNode.name.length,
2363
+ tokenType: 1, // TT_TYPE
2364
+ modifiers: 0,
2365
+ });
2366
+ }
2367
+
2368
+ // Generic type parameters: e.g. List<Int>
2369
+ if (typeNode.typeArgs) {
2370
+ for (const arg of typeNode.typeArgs) {
2371
+ this._walkTypeAnnotation(arg, tokens);
2372
+ }
2373
+ }
2374
+ if (typeNode.params) {
2375
+ for (const p of typeNode.params) {
2376
+ this._walkTypeAnnotation(p, tokens);
2377
+ }
2378
+ }
2379
+ // Union types, function types, etc.
2380
+ if (typeNode.left) this._walkTypeAnnotation(typeNode.left, tokens);
2381
+ if (typeNode.right) this._walkTypeAnnotation(typeNode.right, tokens);
2382
+ if (typeNode.returnType) this._walkTypeAnnotation(typeNode.returnType, tokens);
2383
+ if (typeNode.elementType) this._walkTypeAnnotation(typeNode.elementType, tokens);
2384
+ }
2385
+
1904
2386
  // ─── Utilities ────────────────────────────────────────────
1905
2387
 
1906
2388
  _uriToPath(uri) {
package/src/parser/ast.js CHANGED
@@ -27,14 +27,16 @@ export class ServerBlock {
27
27
  }
28
28
  }
29
29
 
30
- export class ClientBlock {
30
+ export class BrowserBlock {
31
31
  constructor(body, loc, name = null) {
32
- this.type = 'ClientBlock';
32
+ this.type = 'BrowserBlock';
33
33
  this.name = name;
34
34
  this.body = body;
35
35
  this.loc = loc;
36
36
  }
37
37
  }
38
+ // Deprecated alias for backward compatibility
39
+ export { BrowserBlock as ClientBlock };
38
40
 
39
41
  export class SharedBlock {
40
42
  constructor(body, loc, name = null) {
@@ -62,6 +64,25 @@ export class CliBlock {
62
64
  }
63
65
  }
64
66
 
67
+ export class EdgeBlock {
68
+ constructor(body, loc, name = null) {
69
+ this.type = 'EdgeBlock';
70
+ this.name = name; // optional string name for named edge blocks
71
+ this.body = body; // Array of edge statements (config, bindings, routes, functions, etc.)
72
+ this.loc = loc;
73
+ }
74
+ }
75
+
76
+ export class ConcurrentBlock {
77
+ constructor(mode, timeout, body, loc) {
78
+ this.type = 'ConcurrentBlock';
79
+ this.mode = mode; // "all" | "cancel_on_error" | "first"
80
+ this.timeout = timeout; // Expression | null
81
+ this.body = body; // Array of statements
82
+ this.loc = loc;
83
+ }
84
+ }
85
+
65
86
  // ============================================================
66
87
  // Declarations
67
88
  // ============================================================
@@ -137,11 +158,12 @@ export class TypeVariant {
137
158
  }
138
159
 
139
160
  export class TypeField {
140
- constructor(name, typeAnnotation, loc) {
161
+ constructor(name, typeAnnotation, loc, validators) {
141
162
  this.type = 'TypeField';
142
163
  this.name = name;
143
164
  this.typeAnnotation = typeAnnotation;
144
165
  this.loc = loc;
166
+ this.validators = validators || [];
145
167
  }
146
168
  }
147
169
 
@@ -674,7 +696,7 @@ export class RangePattern {
674
696
  }
675
697
 
676
698
  // ============================================================
677
- // Client-specific nodes (lazy-loaded from client-ast.js, re-exported for backward compat)
699
+ // Browser-specific nodes (lazy-loaded from browser-ast.js, re-exported for backward compat)
678
700
  // ============================================================
679
701
 
680
702
  export {
@@ -682,7 +704,7 @@ export {
682
704
  ComponentDeclaration, ComponentStyleBlock, StoreDeclaration,
683
705
  JSXElement, JSXAttribute, JSXSpreadAttribute, JSXFragment,
684
706
  JSXText, JSXExpression, JSXFor, JSXIf, JSXMatch,
685
- } from './client-ast.js';
707
+ } from './browser-ast.js';
686
708
 
687
709
  // ============================================================
688
710
  // Server-specific nodes (lazy-loaded from server-ast.js, re-exported for backward compat)
@@ -713,6 +735,39 @@ export {
713
735
  SecurityTrustProxyDeclaration, SecurityHstsDeclaration,
714
736
  } from './security-ast.js';
715
737
 
738
+ // ============================================================
739
+ // Edge-specific nodes (lazy-loaded from edge-ast.js, re-exported for backward compat)
740
+ // ============================================================
741
+
742
+ export {
743
+ EdgeConfigField, EdgeKVDeclaration, EdgeSQLDeclaration,
744
+ EdgeStorageDeclaration, EdgeQueueDeclaration, EdgeEnvDeclaration,
745
+ EdgeSecretDeclaration, EdgeScheduleDeclaration, EdgeConsumeDeclaration,
746
+ } from './edge-ast.js';
747
+
748
+ // ============================================================
749
+ // Deploy-specific nodes (lazy-loaded from deploy-ast.js, re-exported for backward compat)
750
+ // ============================================================
751
+
752
+ export {
753
+ DeployBlock, DeployConfigField, DeployEnvBlock, DeployDbBlock,
754
+ } from './deploy-ast.js';
755
+
756
+ // ============================================================
757
+ // Form-specific nodes (lazy-loaded from form-ast.js, re-exported for backward compat)
758
+ // ============================================================
759
+
760
+ export {
761
+ FormDeclaration, FormFieldDeclaration, FormGroupDeclaration,
762
+ FormArrayDeclaration, FormValidator, FormStepsDeclaration, FormStep,
763
+ } from './form-ast.js';
764
+
765
+ // ============================================================
766
+ // Select-specific nodes (lazy-loaded from select-ast.js, re-exported for backward compat)
767
+ // ============================================================
768
+
769
+ export { SelectStatement, SelectCase } from './select-ast.js';
770
+
716
771
  export class TestBlock {
717
772
  constructor(name, body, loc, options = {}) {
718
773
  this.type = 'TestBlock';
@@ -1,8 +1,8 @@
1
- // Client-specific AST Node definitions for the Tova language
2
- // Extracted from ast.js for lazy loading — only loaded when client { } blocks are used.
1
+ // Browser-specific AST Node definitions for the Tova language
2
+ // Extracted from ast.js for lazy loading — only loaded when browser { } blocks are used.
3
3
 
4
4
  // ============================================================
5
- // Client-specific nodes
5
+ // Browser-specific nodes
6
6
  // ============================================================
7
7
 
8
8
  export class StateDeclaration {