tova 0.2.9 → 0.3.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.
@@ -0,0 +1,191 @@
1
+ // Client-specific analyzer methods for the Tova language
2
+ // Extracted from analyzer.js for lazy loading — only loaded when client { } blocks are encountered.
3
+
4
+ import { Symbol } from './scope.js';
5
+
6
+ export function installClientAnalyzer(AnalyzerClass) {
7
+ if (AnalyzerClass.prototype._clientAnalyzerInstalled) return;
8
+ AnalyzerClass.prototype._clientAnalyzerInstalled = true;
9
+
10
+ AnalyzerClass.prototype.visitClientBlock = function(node) {
11
+ const prevScope = this.currentScope;
12
+ this.currentScope = this.currentScope.child('client');
13
+ try {
14
+ for (const stmt of node.body) {
15
+ this.visitNode(stmt);
16
+ }
17
+ } finally {
18
+ this.currentScope = prevScope;
19
+ }
20
+ };
21
+
22
+ AnalyzerClass.prototype.visitStateDeclaration = function(node) {
23
+ const ctx = this.currentScope.getContext();
24
+ if (ctx !== 'client') {
25
+ this.error(`'state' can only be used inside a client block`, node.loc, "move this inside a client { } block", { code: 'E302' });
26
+ }
27
+ try {
28
+ this.currentScope.define(node.name,
29
+ new Symbol(node.name, 'state', node.typeAnnotation, true, node.loc));
30
+ } catch (e) {
31
+ this.error(e.message);
32
+ }
33
+ this.visitExpression(node.initialValue);
34
+ };
35
+
36
+ AnalyzerClass.prototype.visitComputedDeclaration = function(node) {
37
+ const ctx = this.currentScope.getContext();
38
+ if (ctx !== 'client') {
39
+ this.error(`'computed' can only be used inside a client block`, node.loc, "move this inside a client { } block", { code: 'E302' });
40
+ }
41
+ try {
42
+ this.currentScope.define(node.name,
43
+ new Symbol(node.name, 'computed', null, false, node.loc));
44
+ } catch (e) {
45
+ this.error(e.message);
46
+ }
47
+ this.visitExpression(node.expression);
48
+ };
49
+
50
+ AnalyzerClass.prototype.visitEffectDeclaration = function(node) {
51
+ const ctx = this.currentScope.getContext();
52
+ if (ctx !== 'client') {
53
+ this.error(`'effect' can only be used inside a client block`, node.loc, "move this inside a client { } block", { code: 'E302' });
54
+ }
55
+ this.visitNode(node.body);
56
+ };
57
+
58
+ AnalyzerClass.prototype.visitComponentDeclaration = function(node) {
59
+ const ctx = this.currentScope.getContext();
60
+ if (ctx !== 'client') {
61
+ this.error(`'component' can only be used inside a client block`, node.loc, "move this inside a client { } block", { code: 'E302' });
62
+ }
63
+ this._checkNamingConvention(node.name, 'component', node.loc);
64
+ try {
65
+ this.currentScope.define(node.name,
66
+ new Symbol(node.name, 'component', null, false, node.loc));
67
+ } catch (e) {
68
+ this.error(e.message);
69
+ }
70
+
71
+ const prevScope = this.currentScope;
72
+ this.currentScope = this.currentScope.child('function');
73
+ for (const param of node.params) {
74
+ try {
75
+ this.currentScope.define(param.name,
76
+ new Symbol(param.name, 'parameter', param.typeAnnotation, false, param.loc));
77
+ } catch (e) {
78
+ this.error(e.message);
79
+ }
80
+ }
81
+ try {
82
+ for (const child of node.body) {
83
+ this.visitNode(child);
84
+ }
85
+ } finally {
86
+ this.currentScope = prevScope;
87
+ }
88
+ };
89
+
90
+ AnalyzerClass.prototype.visitStoreDeclaration = function(node) {
91
+ const ctx = this.currentScope.getContext();
92
+ if (ctx !== 'client') {
93
+ this.error(`'store' can only be used inside a client block`, node.loc, "move this inside a client { } block", { code: 'E302' });
94
+ }
95
+ this._checkNamingConvention(node.name, 'store', node.loc);
96
+ try {
97
+ this.currentScope.define(node.name,
98
+ new Symbol(node.name, 'variable', null, false, node.loc));
99
+ } catch (e) {
100
+ this.error(e.message);
101
+ }
102
+
103
+ const prevScope = this.currentScope;
104
+ this.currentScope = this.currentScope.child('block');
105
+ try {
106
+ for (const child of node.body) {
107
+ this.visitNode(child);
108
+ }
109
+ } finally {
110
+ this.currentScope = prevScope;
111
+ }
112
+ };
113
+
114
+ AnalyzerClass.prototype.visitJSXElement = function(node) {
115
+ for (const attr of node.attributes) {
116
+ if (attr.type === 'JSXSpreadAttribute') {
117
+ this.visitExpression(attr.expression);
118
+ } else {
119
+ this.visitExpression(attr.value);
120
+ }
121
+ }
122
+ for (const child of node.children) {
123
+ if (child.type === 'JSXElement') {
124
+ this.visitJSXElement(child);
125
+ } else if (child.type === 'JSXFragment') {
126
+ this.visitJSXFragment(child);
127
+ } else if (child.type === 'JSXExpression') {
128
+ this.visitExpression(child.expression);
129
+ } else if (child.type === 'JSXFor') {
130
+ this.visitJSXFor(child);
131
+ } else if (child.type === 'JSXIf') {
132
+ this.visitJSXIf(child);
133
+ }
134
+ }
135
+ };
136
+
137
+ AnalyzerClass.prototype.visitJSXFragment = function(node) {
138
+ for (const child of node.children) {
139
+ if (child.type === 'JSXElement') {
140
+ this.visitJSXElement(child);
141
+ } else if (child.type === 'JSXFragment') {
142
+ this.visitJSXFragment(child);
143
+ } else if (child.type === 'JSXExpression') {
144
+ this.visitExpression(child.expression);
145
+ } else if (child.type === 'JSXFor') {
146
+ this.visitJSXFor(child);
147
+ } else if (child.type === 'JSXIf') {
148
+ this.visitJSXIf(child);
149
+ }
150
+ }
151
+ };
152
+
153
+ AnalyzerClass.prototype.visitJSXFor = function(node) {
154
+ const prevScope = this.currentScope;
155
+ this.currentScope = this.currentScope.child('block');
156
+ try {
157
+ this.visitExpression(node.iterable);
158
+ try {
159
+ this.currentScope.define(node.variable,
160
+ new Symbol(node.variable, 'variable', null, false, node.loc));
161
+ } catch (e) {
162
+ this.error(e.message);
163
+ }
164
+ for (const child of node.body) {
165
+ this.visitNode(child);
166
+ }
167
+ } finally {
168
+ this.currentScope = prevScope;
169
+ }
170
+ };
171
+
172
+ AnalyzerClass.prototype.visitJSXIf = function(node) {
173
+ this.visitExpression(node.condition);
174
+ for (const child of node.consequent) {
175
+ this.visitNode(child);
176
+ }
177
+ if (node.alternates) {
178
+ for (const alt of node.alternates) {
179
+ this.visitExpression(alt.condition);
180
+ for (const child of alt.body) {
181
+ this.visitNode(child);
182
+ }
183
+ }
184
+ }
185
+ if (node.alternate) {
186
+ for (const child of node.alternate) {
187
+ this.visitNode(child);
188
+ }
189
+ }
190
+ };
191
+ }
@@ -0,0 +1,467 @@
1
+ // Server-specific analyzer methods for the Tova language
2
+ // Extracted from analyzer.js for lazy loading — only loaded when server { } blocks are encountered.
3
+
4
+ import { Symbol } from './scope.js';
5
+
6
+ export function collectServerBlockFunctions(ast) {
7
+ const serverBlockFunctions = new Map();
8
+ const collectFns = (stmts) => {
9
+ const fns = [];
10
+ for (const stmt of stmts) {
11
+ if (stmt.type === 'FunctionDeclaration') {
12
+ fns.push(stmt.name);
13
+ } else if (stmt.type === 'RouteGroupDeclaration') {
14
+ fns.push(...collectFns(stmt.body));
15
+ }
16
+ }
17
+ return fns;
18
+ };
19
+ for (const node of ast.body) {
20
+ if (node.type === 'ServerBlock' && node.name) {
21
+ const fns = collectFns(node.body);
22
+ if (serverBlockFunctions.has(node.name)) {
23
+ serverBlockFunctions.get(node.name).push(...fns);
24
+ } else {
25
+ serverBlockFunctions.set(node.name, fns);
26
+ }
27
+ }
28
+ }
29
+ return serverBlockFunctions;
30
+ }
31
+
32
+ export function installServerAnalyzer(AnalyzerClass) {
33
+ if (AnalyzerClass.prototype._serverAnalyzerInstalled) return;
34
+ AnalyzerClass.prototype._serverAnalyzerInstalled = true;
35
+
36
+ AnalyzerClass.prototype.visitServerBlock = function(node) {
37
+ const prevScope = this.currentScope;
38
+ const prevServerBlockName = this._currentServerBlockName;
39
+ this._currentServerBlockName = node.name || null;
40
+ this.currentScope = this.currentScope.child('server');
41
+
42
+ try {
43
+ // Register peer server block names as valid identifiers in this scope
44
+ if (node.name && this.serverBlockFunctions.size > 0) {
45
+ for (const [peerName] of this.serverBlockFunctions) {
46
+ if (peerName !== node.name) {
47
+ try {
48
+ this.currentScope.define(peerName,
49
+ new Symbol(peerName, 'builtin', null, false, { line: 0, column: 0, file: '<peer-server>' }));
50
+ } catch (e) {
51
+ // Ignore if already defined
52
+ }
53
+ }
54
+ }
55
+ }
56
+
57
+ // Register AI provider names as variables (named: claude, gpt, etc.; default: ai)
58
+ for (const stmt of node.body) {
59
+ if (stmt.type === 'AiConfigDeclaration') {
60
+ const aiName = stmt.name || 'ai';
61
+ try {
62
+ this.currentScope.define(aiName,
63
+ new Symbol(aiName, 'builtin', null, false, stmt.loc));
64
+ } catch (e) {
65
+ // Ignore if already defined
66
+ }
67
+ }
68
+ }
69
+
70
+ for (const stmt of node.body) {
71
+ this.visitNode(stmt);
72
+ }
73
+ } finally {
74
+ this.currentScope = prevScope;
75
+ this._currentServerBlockName = prevServerBlockName;
76
+ }
77
+ };
78
+
79
+ AnalyzerClass.prototype.visitRouteDeclaration = function(node) {
80
+ const ctx = this.currentScope.getContext();
81
+ if (ctx !== 'server') {
82
+ this.error(`'route' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
83
+ }
84
+ this.visitExpression(node.handler);
85
+
86
+ // Validate body type annotation is only used with POST/PUT/PATCH
87
+ if (node.bodyType && !['POST', 'PUT', 'PATCH'].includes(node.method.toUpperCase())) {
88
+ this.warn(`body type annotation on ${node.method} route is ignored — only POST, PUT, and PATCH routes parse request bodies`, node.loc);
89
+ }
90
+
91
+ // Route param ↔ handler signature type safety
92
+ if (node.handler.type === 'Identifier') {
93
+ const handlerName = node.handler.name;
94
+ // Find the function declaration in the current server block scope
95
+ const fnSym = this.currentScope.lookup(handlerName);
96
+ if (fnSym && fnSym.kind === 'function' && fnSym._params) {
97
+ const pathParams = new Set();
98
+ const pathStr = node.path || '';
99
+ const paramMatches = pathStr.match(/:([a-zA-Z_][a-zA-Z0-9_]*)/g);
100
+ if (paramMatches) {
101
+ for (const m of paramMatches) pathParams.add(m.slice(1));
102
+ }
103
+ const handlerParams = fnSym._params.filter(p => p !== 'req');
104
+ for (const hp of handlerParams) {
105
+ if (pathParams.size > 0 && !pathParams.has(hp) && node.method.toUpperCase() === 'GET') {
106
+ // For GET routes, params not in path come from query — this is fine, just a warning
107
+ this.warn(`Handler '${handlerName}' param '${hp}' not in route path '${pathStr}' — will be extracted from query string`, node.loc);
108
+ }
109
+ }
110
+ }
111
+ }
112
+ };
113
+
114
+ AnalyzerClass.prototype.visitMiddlewareDeclaration = function(node) {
115
+ const ctx = this.currentScope.getContext();
116
+ if (ctx !== 'server') {
117
+ this.error(`'middleware' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
118
+ }
119
+ try {
120
+ this.currentScope.define(node.name,
121
+ new Symbol(node.name, 'function', null, false, node.loc));
122
+ } catch (e) {
123
+ this.error(e.message);
124
+ }
125
+ const prevScope = this.currentScope;
126
+ this.currentScope = this.currentScope.child('function');
127
+ for (const param of node.params) {
128
+ try {
129
+ this.currentScope.define(param.name,
130
+ new Symbol(param.name, 'parameter', param.typeAnnotation, false, param.loc));
131
+ } catch (e) {
132
+ this.error(e.message);
133
+ }
134
+ }
135
+ try {
136
+ this.visitNode(node.body);
137
+ } finally {
138
+ this.currentScope = prevScope;
139
+ }
140
+ };
141
+
142
+ AnalyzerClass.prototype.visitHealthCheckDeclaration = function(node) {
143
+ const ctx = this.currentScope.getContext();
144
+ if (ctx !== 'server') {
145
+ this.error(`'health' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
146
+ }
147
+ };
148
+
149
+ AnalyzerClass.prototype.visitCorsDeclaration = function(node) {
150
+ const ctx = this.currentScope.getContext();
151
+ if (ctx !== 'server') {
152
+ this.error(`'cors' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
153
+ }
154
+ for (const value of Object.values(node.config)) {
155
+ this.visitExpression(value);
156
+ }
157
+ };
158
+
159
+ AnalyzerClass.prototype.visitErrorHandlerDeclaration = function(node) {
160
+ const ctx = this.currentScope.getContext();
161
+ if (ctx !== 'server') {
162
+ this.error(`'on_error' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
163
+ }
164
+ const prevScope = this.currentScope;
165
+ this.currentScope = this.currentScope.child('function');
166
+ for (const param of node.params) {
167
+ try {
168
+ this.currentScope.define(param.name,
169
+ new Symbol(param.name, 'parameter', param.typeAnnotation, false, param.loc));
170
+ } catch (e) {
171
+ this.error(e.message);
172
+ }
173
+ }
174
+ try {
175
+ this.visitNode(node.body);
176
+ } finally {
177
+ this.currentScope = prevScope;
178
+ }
179
+ };
180
+
181
+ AnalyzerClass.prototype.visitWebSocketDeclaration = function(node) {
182
+ const ctx = this.currentScope.getContext();
183
+ if (ctx !== 'server') {
184
+ this.error(`'ws' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
185
+ }
186
+ for (const [, handler] of Object.entries(node.handlers)) {
187
+ if (!handler) continue;
188
+ const prevScope = this.currentScope;
189
+ this.currentScope = this.currentScope.child('function');
190
+ for (const param of handler.params) {
191
+ try {
192
+ this.currentScope.define(param.name,
193
+ new Symbol(param.name, 'parameter', param.typeAnnotation, false, param.loc));
194
+ } catch (e) {
195
+ this.error(e.message);
196
+ }
197
+ }
198
+ try {
199
+ this.visitNode(handler.body);
200
+ } finally {
201
+ this.currentScope = prevScope;
202
+ }
203
+ }
204
+ };
205
+
206
+ AnalyzerClass.prototype.visitStaticDeclaration = function(node) {
207
+ const ctx = this.currentScope.getContext();
208
+ if (ctx !== 'server') {
209
+ this.error(`'static' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
210
+ }
211
+ };
212
+
213
+ AnalyzerClass.prototype.visitDiscoverDeclaration = function(node) {
214
+ const ctx = this.currentScope.getContext();
215
+ if (ctx !== 'server') {
216
+ this.error(`'discover' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
217
+ }
218
+ this.visitExpression(node.urlExpression);
219
+ };
220
+
221
+ AnalyzerClass.prototype.visitAuthDeclaration = function(node) {
222
+ const ctx = this.currentScope.getContext();
223
+ if (ctx !== 'server') {
224
+ this.error(`'auth' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
225
+ }
226
+ for (const value of Object.values(node.config)) {
227
+ this.visitExpression(value);
228
+ }
229
+ };
230
+
231
+ AnalyzerClass.prototype.visitMaxBodyDeclaration = function(node) {
232
+ const ctx = this.currentScope.getContext();
233
+ if (ctx !== 'server') {
234
+ this.error(`'max_body' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
235
+ }
236
+ this.visitExpression(node.limit);
237
+ };
238
+
239
+ AnalyzerClass.prototype.visitRouteGroupDeclaration = function(node) {
240
+ const ctx = this.currentScope.getContext();
241
+ if (ctx !== 'server') {
242
+ this.error(`'routes' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
243
+ }
244
+ for (const stmt of node.body) {
245
+ this.visitNode(stmt);
246
+ }
247
+ };
248
+
249
+ AnalyzerClass.prototype.visitRateLimitDeclaration = function(node) {
250
+ const ctx = this.currentScope.getContext();
251
+ if (ctx !== 'server') {
252
+ this.error(`'rate_limit' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
253
+ }
254
+ for (const value of Object.values(node.config)) {
255
+ this.visitExpression(value);
256
+ }
257
+ };
258
+
259
+ AnalyzerClass.prototype.visitLifecycleHookDeclaration = function(node) {
260
+ const ctx = this.currentScope.getContext();
261
+ if (ctx !== 'server') {
262
+ this.error(`'on_${node.hook}' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
263
+ }
264
+ const prevScope = this.currentScope;
265
+ this.currentScope = this.currentScope.child('function');
266
+ for (const param of node.params) {
267
+ try {
268
+ this.currentScope.define(param.name,
269
+ new Symbol(param.name, 'parameter', param.typeAnnotation, false, param.loc));
270
+ } catch (e) {
271
+ this.error(e.message);
272
+ }
273
+ }
274
+ try {
275
+ this.visitNode(node.body);
276
+ } finally {
277
+ this.currentScope = prevScope;
278
+ }
279
+ };
280
+
281
+ AnalyzerClass.prototype.visitSubscribeDeclaration = function(node) {
282
+ const ctx = this.currentScope.getContext();
283
+ if (ctx !== 'server') {
284
+ this.error(`'subscribe' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
285
+ }
286
+ const prevScope = this.currentScope;
287
+ this.currentScope = this.currentScope.child('function');
288
+ for (const param of node.params) {
289
+ try {
290
+ this.currentScope.define(param.name,
291
+ new Symbol(param.name, 'parameter', param.typeAnnotation, false, param.loc));
292
+ } catch (e) {
293
+ this.error(e.message);
294
+ }
295
+ }
296
+ try {
297
+ this.visitNode(node.body);
298
+ } finally {
299
+ this.currentScope = prevScope;
300
+ }
301
+ };
302
+
303
+ AnalyzerClass.prototype.visitEnvDeclaration = function(node) {
304
+ const ctx = this.currentScope.getContext();
305
+ if (ctx !== 'server') {
306
+ this.error(`'env' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
307
+ }
308
+ try {
309
+ this.currentScope.define(node.name,
310
+ new Symbol(node.name, 'variable', node.typeAnnotation, false, node.loc));
311
+ } catch (e) {
312
+ this.error(e.message);
313
+ }
314
+ if (node.defaultValue) {
315
+ this.visitExpression(node.defaultValue);
316
+ }
317
+ };
318
+
319
+ AnalyzerClass.prototype.visitScheduleDeclaration = function(node) {
320
+ const ctx = this.currentScope.getContext();
321
+ if (ctx !== 'server') {
322
+ this.error(`'schedule' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
323
+ }
324
+ if (node.name) {
325
+ try {
326
+ this.currentScope.define(node.name,
327
+ new Symbol(node.name, 'function', null, false, node.loc));
328
+ } catch (e) {
329
+ this.error(e.message);
330
+ }
331
+ }
332
+ const prevScope = this.currentScope;
333
+ this.currentScope = this.currentScope.child('function');
334
+ for (const param of node.params) {
335
+ try {
336
+ this.currentScope.define(param.name,
337
+ new Symbol(param.name, 'parameter', param.typeAnnotation, false, param.loc));
338
+ } catch (e) {
339
+ this.error(e.message);
340
+ }
341
+ }
342
+ try {
343
+ this.visitNode(node.body);
344
+ } finally {
345
+ this.currentScope = prevScope;
346
+ }
347
+ };
348
+
349
+ AnalyzerClass.prototype.visitUploadDeclaration = function(node) {
350
+ const ctx = this.currentScope.getContext();
351
+ if (ctx !== 'server') {
352
+ this.error(`'upload' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
353
+ }
354
+ for (const value of Object.values(node.config)) {
355
+ this.visitExpression(value);
356
+ }
357
+ };
358
+
359
+ AnalyzerClass.prototype.visitSessionDeclaration = function(node) {
360
+ const ctx = this.currentScope.getContext();
361
+ if (ctx !== 'server') {
362
+ this.error(`'session' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
363
+ }
364
+ for (const value of Object.values(node.config)) {
365
+ this.visitExpression(value);
366
+ }
367
+ };
368
+
369
+ AnalyzerClass.prototype.visitDbDeclaration = function(node) {
370
+ const ctx = this.currentScope.getContext();
371
+ if (ctx !== 'server') {
372
+ this.error(`'db' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
373
+ }
374
+ for (const value of Object.values(node.config)) {
375
+ this.visitExpression(value);
376
+ }
377
+ };
378
+
379
+ AnalyzerClass.prototype.visitTlsDeclaration = function(node) {
380
+ const ctx = this.currentScope.getContext();
381
+ if (ctx !== 'server') {
382
+ this.error(`'tls' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
383
+ }
384
+ for (const value of Object.values(node.config)) {
385
+ this.visitExpression(value);
386
+ }
387
+ };
388
+
389
+ AnalyzerClass.prototype.visitCompressionDeclaration = function(node) {
390
+ const ctx = this.currentScope.getContext();
391
+ if (ctx !== 'server') {
392
+ this.error(`'compression' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
393
+ }
394
+ for (const value of Object.values(node.config)) {
395
+ this.visitExpression(value);
396
+ }
397
+ };
398
+
399
+ AnalyzerClass.prototype.visitBackgroundJobDeclaration = function(node) {
400
+ const ctx = this.currentScope.getContext();
401
+ if (ctx !== 'server') {
402
+ this.error(`'background' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
403
+ }
404
+ try {
405
+ this.currentScope.define(node.name,
406
+ new Symbol(node.name, 'function', null, false, node.loc));
407
+ } catch (e) {
408
+ this.error(e.message);
409
+ }
410
+ const prevScope = this.currentScope;
411
+ this.currentScope = this.currentScope.child('function');
412
+ for (const param of node.params) {
413
+ try {
414
+ this.currentScope.define(param.name,
415
+ new Symbol(param.name, 'parameter', param.typeAnnotation, false, param.loc));
416
+ } catch (e) {
417
+ this.error(e.message);
418
+ }
419
+ }
420
+ try {
421
+ this.visitNode(node.body);
422
+ } finally {
423
+ this.currentScope = prevScope;
424
+ }
425
+ };
426
+
427
+ AnalyzerClass.prototype.visitCacheDeclaration = function(node) {
428
+ const ctx = this.currentScope.getContext();
429
+ if (ctx !== 'server') {
430
+ this.error(`'cache' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
431
+ }
432
+ for (const value of Object.values(node.config)) {
433
+ this.visitExpression(value);
434
+ }
435
+ };
436
+
437
+ AnalyzerClass.prototype.visitSseDeclaration = function(node) {
438
+ const ctx = this.currentScope.getContext();
439
+ if (ctx !== 'server') {
440
+ this.error(`'sse' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
441
+ }
442
+ const prevScope = this.currentScope;
443
+ this.currentScope = this.currentScope.child('block');
444
+ for (const p of node.params) {
445
+ this.currentScope.define(p.name, { kind: 'param' });
446
+ }
447
+ try {
448
+ for (const stmt of node.body.body || []) {
449
+ this.visitNode(stmt);
450
+ }
451
+ } finally {
452
+ this.currentScope = prevScope;
453
+ }
454
+ };
455
+
456
+ AnalyzerClass.prototype.visitModelDeclaration = function(node) {
457
+ const ctx = this.currentScope.getContext();
458
+ if (ctx !== 'server') {
459
+ this.error(`'model' can only be used inside a server block`, node.loc, "move this inside a server { } block", { code: 'E303' });
460
+ }
461
+ if (node.config) {
462
+ for (const value of Object.values(node.config)) {
463
+ this.visitExpression(value);
464
+ }
465
+ }
466
+ };
467
+ }