@kernlang/core 3.3.9 → 3.4.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.
- package/dist/capability-matrix.d.ts +15 -0
- package/dist/capability-matrix.js +245 -0
- package/dist/capability-matrix.js.map +1 -0
- package/dist/codegen/data-layer.d.ts +1 -1
- package/dist/codegen/data-layer.js +59 -23
- package/dist/codegen/data-layer.js.map +1 -1
- package/dist/codegen/events.js +1 -1
- package/dist/codegen/events.js.map +1 -1
- package/dist/codegen/functions.js +29 -5
- package/dist/codegen/functions.js.map +1 -1
- package/dist/codegen/ground-layer.js +10 -6
- package/dist/codegen/ground-layer.js.map +1 -1
- package/dist/codegen/helpers.d.ts +3 -1
- package/dist/codegen/helpers.js +5 -1
- package/dist/codegen/helpers.js.map +1 -1
- package/dist/codegen/machines.js +4 -3
- package/dist/codegen/machines.js.map +1 -1
- package/dist/codegen/modules.d.ts +1 -0
- package/dist/codegen/modules.js +52 -1
- package/dist/codegen/modules.js.map +1 -1
- package/dist/codegen/screens.js +31 -9
- package/dist/codegen/screens.js.map +1 -1
- package/dist/codegen/stdlib-preamble.d.ts +39 -0
- package/dist/codegen/stdlib-preamble.js +213 -0
- package/dist/codegen/stdlib-preamble.js.map +1 -0
- package/dist/codegen/type-system.d.ts +113 -1
- package/dist/codegen/type-system.js +389 -30
- package/dist/codegen/type-system.js.map +1 -1
- package/dist/codegen-core.d.ts +2 -2
- package/dist/codegen-core.js +58 -10
- package/dist/codegen-core.js.map +1 -1
- package/dist/codegen-expression.d.ts +3 -0
- package/dist/codegen-expression.js +93 -0
- package/dist/codegen-expression.js.map +1 -0
- package/dist/concepts.d.ts +3 -3
- package/dist/config.d.ts +16 -0
- package/dist/config.js +13 -0
- package/dist/config.js.map +1 -1
- package/dist/decompiler.js +356 -4
- package/dist/decompiler.js.map +1 -1
- package/dist/importer.d.ts +1 -0
- package/dist/importer.js +574 -34
- package/dist/importer.js.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.js +9 -3
- package/dist/index.js.map +1 -1
- package/dist/node-props.d.ts +177 -1
- package/dist/node-props.js.map +1 -1
- package/dist/parser-core.js +30 -5
- package/dist/parser-core.js.map +1 -1
- package/dist/parser-diagnostics.js +5 -0
- package/dist/parser-diagnostics.js.map +1 -1
- package/dist/parser-expression.d.ts +17 -0
- package/dist/parser-expression.js +498 -0
- package/dist/parser-expression.js.map +1 -0
- package/dist/parser-tokenizer.d.ts +5 -3
- package/dist/parser-tokenizer.js +97 -16
- package/dist/parser-tokenizer.js.map +1 -1
- package/dist/parser-validate-effects.d.ts +21 -0
- package/dist/parser-validate-effects.js +188 -0
- package/dist/parser-validate-effects.js.map +1 -0
- package/dist/parser-validate-expressions.d.ts +6 -0
- package/dist/parser-validate-expressions.js +41 -0
- package/dist/parser-validate-expressions.js.map +1 -0
- package/dist/parser-validate-union-kind.d.ts +24 -0
- package/dist/parser-validate-union-kind.js +97 -0
- package/dist/parser-validate-union-kind.js.map +1 -0
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +373 -27
- package/dist/schema.js.map +1 -1
- package/dist/semantic-validator.js +15 -8
- package/dist/semantic-validator.js.map +1 -1
- package/dist/spec.d.ts +5 -2
- package/dist/spec.js +24 -2
- package/dist/spec.js.map +1 -1
- package/dist/types.d.ts +7 -1
- package/dist/types.js +7 -1
- package/dist/types.js.map +1 -1
- package/dist/value-ir.d.ts +69 -0
- package/dist/value-ir.js +20 -0
- package/dist/value-ir.js.map +1 -0
- package/package.json +1 -1
package/dist/schema.js
CHANGED
|
@@ -29,24 +29,46 @@ export const NODE_SCHEMAS = {
|
|
|
29
29
|
},
|
|
30
30
|
},
|
|
31
31
|
type: {
|
|
32
|
-
description: 'TypeScript type alias — union of string literals or alias to another type',
|
|
32
|
+
description: 'TypeScript type alias — union of string literals, or alias to another type (including tuple types like [string, number]). Use generics="<T>" for parameterised aliases.',
|
|
33
33
|
example: 'type name=Status values="active|inactive|banned"',
|
|
34
34
|
props: {
|
|
35
35
|
name: { required: true, kind: 'identifier' },
|
|
36
36
|
values: { kind: 'string' },
|
|
37
37
|
alias: { kind: 'rawExpr' },
|
|
38
|
+
generics: { kind: 'rawExpr' },
|
|
38
39
|
export: { kind: 'boolean' },
|
|
39
40
|
},
|
|
40
41
|
},
|
|
41
42
|
interface: {
|
|
42
|
-
description: 'TypeScript interface with typed fields',
|
|
43
|
+
description: 'TypeScript interface with typed fields. Use generics="<T>" for parameterised interfaces.',
|
|
43
44
|
example: 'interface name=User export=true\n field name=id type=string\n field name=email type=string',
|
|
44
45
|
props: {
|
|
45
46
|
name: { required: true, kind: 'identifier' },
|
|
46
47
|
extends: { kind: 'typeAnnotation' },
|
|
48
|
+
generics: { kind: 'rawExpr' },
|
|
47
49
|
export: { kind: 'boolean' },
|
|
48
50
|
},
|
|
49
|
-
allowedChildren: ['field'],
|
|
51
|
+
allowedChildren: ['field', 'indexer'],
|
|
52
|
+
},
|
|
53
|
+
indexer: {
|
|
54
|
+
description: 'Index signature for an interface — [keyName: keyType]: type',
|
|
55
|
+
example: 'indexer keyName=key keyType=string type=Value',
|
|
56
|
+
props: {
|
|
57
|
+
keyName: { kind: 'identifier' },
|
|
58
|
+
keyType: { required: true, kind: 'typeAnnotation' },
|
|
59
|
+
type: { required: true, kind: 'typeAnnotation' },
|
|
60
|
+
readonly: { kind: 'boolean' },
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
overload: {
|
|
64
|
+
description: 'Function overload signature — declared as a child of fn. Each overload emits a TS overload declaration before the implementation signature.',
|
|
65
|
+
example: 'overload params="a:number,b:number" returns=number',
|
|
66
|
+
props: {
|
|
67
|
+
params: { kind: 'string' },
|
|
68
|
+
returns: { kind: 'typeAnnotation' },
|
|
69
|
+
generics: { kind: 'rawExpr' },
|
|
70
|
+
},
|
|
71
|
+
allowedChildren: ['param'],
|
|
50
72
|
},
|
|
51
73
|
union: {
|
|
52
74
|
description: 'Discriminated union type with variants, each having their own fields',
|
|
@@ -55,9 +77,33 @@ export const NODE_SCHEMAS = {
|
|
|
55
77
|
name: { required: true, kind: 'identifier' },
|
|
56
78
|
discriminant: { required: true, kind: 'identifier' },
|
|
57
79
|
export: { kind: 'boolean' },
|
|
80
|
+
// Slice 4 — `kind=result|option` opts the union into the Result/Option
|
|
81
|
+
// shape (see docs/language/result-option-spec.md). Default (unspecified)
|
|
82
|
+
// is a regular discriminated union, identical to the slice 3 behaviour.
|
|
83
|
+
// Validated by parser-validate-union-kind.ts.
|
|
84
|
+
kind: { kind: 'string' },
|
|
58
85
|
},
|
|
59
86
|
allowedChildren: ['variant'],
|
|
60
87
|
},
|
|
88
|
+
enum: {
|
|
89
|
+
description: 'TypeScript enum — numeric (auto-incremented) via values="A|B|C", or string-valued via member children',
|
|
90
|
+
example: 'enum name=Status values="Pending|Active|Done"',
|
|
91
|
+
props: {
|
|
92
|
+
name: { required: true, kind: 'identifier' },
|
|
93
|
+
values: { kind: 'string' },
|
|
94
|
+
const: { kind: 'boolean' },
|
|
95
|
+
export: { kind: 'boolean' },
|
|
96
|
+
},
|
|
97
|
+
allowedChildren: ['member'],
|
|
98
|
+
},
|
|
99
|
+
member: {
|
|
100
|
+
description: 'Enum member with explicit value (for string-valued or computed enums)',
|
|
101
|
+
example: 'member name=Up value="UP"',
|
|
102
|
+
props: {
|
|
103
|
+
name: { required: true, kind: 'identifier' },
|
|
104
|
+
value: { kind: 'expression' },
|
|
105
|
+
},
|
|
106
|
+
},
|
|
61
107
|
variant: {
|
|
62
108
|
description: 'A case within a discriminated union. Use name= for inline variants with fields, or type= to reference an existing interface.',
|
|
63
109
|
example: 'variant name=circle\n field name=radius type=number',
|
|
@@ -74,6 +120,12 @@ export const NODE_SCHEMAS = {
|
|
|
74
120
|
name: { required: true, kind: 'identifier' },
|
|
75
121
|
type: { kind: 'typeAnnotation' },
|
|
76
122
|
optional: { kind: 'boolean' },
|
|
123
|
+
// Slice 3b — `value` is the native ValueIR-canonicalised initializer;
|
|
124
|
+
// `default` remains as the rawExpr passthrough escape hatch. `value`
|
|
125
|
+
// takes precedence when both are set. Either marks the field as having
|
|
126
|
+
// an initializer (which makes the interface-side property optional in
|
|
127
|
+
// config emit).
|
|
128
|
+
value: { kind: 'expression' },
|
|
77
129
|
default: { kind: 'rawExpr' },
|
|
78
130
|
private: { kind: 'boolean' },
|
|
79
131
|
readonly: { kind: 'boolean' },
|
|
@@ -86,6 +138,7 @@ export const NODE_SCHEMAS = {
|
|
|
86
138
|
props: {
|
|
87
139
|
name: { required: true, kind: 'identifier' },
|
|
88
140
|
implements: { kind: 'typeAnnotation' },
|
|
141
|
+
generics: { kind: 'rawExpr' },
|
|
89
142
|
export: { kind: 'boolean' },
|
|
90
143
|
},
|
|
91
144
|
allowedChildren: ['field', 'method', 'constructor', 'singleton', 'getter', 'setter'],
|
|
@@ -98,6 +151,7 @@ export const NODE_SCHEMAS = {
|
|
|
98
151
|
extends: { kind: 'typeAnnotation' },
|
|
99
152
|
implements: { kind: 'typeAnnotation' },
|
|
100
153
|
abstract: { kind: 'boolean' },
|
|
154
|
+
generics: { kind: 'rawExpr' },
|
|
101
155
|
export: { kind: 'boolean' },
|
|
102
156
|
},
|
|
103
157
|
allowedChildren: ['field', 'method', 'constructor', 'singleton', 'getter', 'setter'],
|
|
@@ -113,8 +167,9 @@ export const NODE_SCHEMAS = {
|
|
|
113
167
|
stream: { kind: 'boolean' },
|
|
114
168
|
private: { kind: 'boolean' },
|
|
115
169
|
static: { kind: 'boolean' },
|
|
170
|
+
generics: { kind: 'rawExpr' },
|
|
116
171
|
},
|
|
117
|
-
allowedChildren: ['handler'],
|
|
172
|
+
allowedChildren: ['handler', 'param'],
|
|
118
173
|
},
|
|
119
174
|
getter: {
|
|
120
175
|
description: 'A getter accessor within a class or service — emits `get name(): T { body }`.',
|
|
@@ -136,7 +191,7 @@ export const NODE_SCHEMAS = {
|
|
|
136
191
|
private: { kind: 'boolean' },
|
|
137
192
|
static: { kind: 'boolean' },
|
|
138
193
|
},
|
|
139
|
-
allowedChildren: ['handler'],
|
|
194
|
+
allowedChildren: ['handler', 'param'],
|
|
140
195
|
},
|
|
141
196
|
fn: {
|
|
142
197
|
description: 'Standalone function — the most common code unit in KERN',
|
|
@@ -149,8 +204,12 @@ export const NODE_SCHEMAS = {
|
|
|
149
204
|
stream: { kind: 'boolean' },
|
|
150
205
|
export: { kind: 'boolean' },
|
|
151
206
|
expr: { kind: 'rawExpr' },
|
|
207
|
+
generics: { kind: 'rawExpr' },
|
|
208
|
+
// Slice 6 — effects=pure declares the body has no observable side effects.
|
|
209
|
+
// Validated by parser-validate-effects.ts; see docs/language/effects-pure-spec.md.
|
|
210
|
+
effects: { kind: 'string' },
|
|
152
211
|
},
|
|
153
|
-
allowedChildren: ['handler', 'signal', 'cleanup'],
|
|
212
|
+
allowedChildren: ['handler', 'signal', 'cleanup', 'overload', 'param'],
|
|
154
213
|
},
|
|
155
214
|
machine: {
|
|
156
215
|
description: 'State machine with states and guarded transitions — 12 lines of KERN generates 140+ lines of TypeScript',
|
|
@@ -193,7 +252,7 @@ export const NODE_SCHEMAS = {
|
|
|
193
252
|
params: { kind: 'string' },
|
|
194
253
|
guard: { kind: 'rawExpr' },
|
|
195
254
|
},
|
|
196
|
-
allowedChildren: ['handler'],
|
|
255
|
+
allowedChildren: ['handler', 'param'],
|
|
197
256
|
},
|
|
198
257
|
error: {
|
|
199
258
|
description: 'Custom error class extending a base error, with typed fields',
|
|
@@ -231,8 +290,9 @@ export const NODE_SCHEMAS = {
|
|
|
231
290
|
example: 'test name="AuthService"\n describe name="login"\n it name="rejects invalid email"',
|
|
232
291
|
props: {
|
|
233
292
|
name: { required: true, kind: 'string' },
|
|
293
|
+
target: { kind: 'string' },
|
|
234
294
|
},
|
|
235
|
-
allowedChildren: ['describe', 'it', 'handler'],
|
|
295
|
+
allowedChildren: ['describe', 'it', 'expect', 'fixture', 'handler'],
|
|
236
296
|
},
|
|
237
297
|
event: {
|
|
238
298
|
description: 'Typed event with payload type children',
|
|
@@ -253,17 +313,101 @@ export const NODE_SCHEMAS = {
|
|
|
253
313
|
types: { kind: 'boolean' },
|
|
254
314
|
},
|
|
255
315
|
},
|
|
316
|
+
use: {
|
|
317
|
+
description: 'Cross-`.kern` symbol resolution. Parent of `from` children — one per imported binding. Compositional shape mirrors enum/member, class/method.',
|
|
318
|
+
example: 'use path="./helper.kern"\n from name=foo\n from name=bar as=baz',
|
|
319
|
+
props: {
|
|
320
|
+
path: { required: true, kind: 'importPath' },
|
|
321
|
+
},
|
|
322
|
+
allowedChildren: ['from'],
|
|
323
|
+
},
|
|
324
|
+
from: {
|
|
325
|
+
description: 'Single binding in a `use` block. `as=` aliases the local name; `export=true` re-exports.',
|
|
326
|
+
example: 'from name=foo as=bar export=true',
|
|
327
|
+
props: {
|
|
328
|
+
name: { required: true, kind: 'identifier' },
|
|
329
|
+
as: { kind: 'identifier' },
|
|
330
|
+
export: { kind: 'boolean' },
|
|
331
|
+
},
|
|
332
|
+
},
|
|
256
333
|
const: {
|
|
257
334
|
description: 'Constant declaration with optional type and value or handler body',
|
|
258
335
|
example: 'const name=MAX_RETRIES type=number value=3 export=true',
|
|
259
336
|
props: {
|
|
260
337
|
name: { required: true, kind: 'identifier' },
|
|
261
338
|
type: { kind: 'typeAnnotation' },
|
|
262
|
-
value: { kind: '
|
|
339
|
+
value: { kind: 'expression' },
|
|
263
340
|
export: { kind: 'boolean' },
|
|
264
341
|
},
|
|
265
342
|
allowedChildren: ['handler'],
|
|
266
343
|
},
|
|
344
|
+
destructure: {
|
|
345
|
+
description: 'Native destructuring statement — emits `const {a,b} = expr;` (object pattern with `binding` children) or `const [x,y] = expr;` (array pattern with `element` children). For complex patterns (rest `...`, defaults `=v`, nested `{a:{b}}`), use the `expr={{...}}` escape hatch which carries the raw TS statement verbatim. Slice 3d.',
|
|
346
|
+
example: 'destructure kind=const source=user\n binding name=id\n binding name=email key=mail',
|
|
347
|
+
props: {
|
|
348
|
+
kind: { kind: 'string' },
|
|
349
|
+
source: { kind: 'expression' },
|
|
350
|
+
type: { kind: 'typeAnnotation' },
|
|
351
|
+
export: { kind: 'boolean' },
|
|
352
|
+
expr: { kind: 'rawExpr' },
|
|
353
|
+
},
|
|
354
|
+
allowedChildren: ['binding', 'element'],
|
|
355
|
+
},
|
|
356
|
+
binding: {
|
|
357
|
+
description: 'Object-destructuring binding inside a `destructure` parent. `name` is the local binding; `key` is the optional property key when renaming, e.g. `{a: foo}` → `binding name=foo key=a`. Slice 3d.',
|
|
358
|
+
example: 'binding name=foo key=a',
|
|
359
|
+
props: {
|
|
360
|
+
name: { required: true, kind: 'identifier' },
|
|
361
|
+
key: { kind: 'identifier' },
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
element: {
|
|
365
|
+
description: 'Array-destructuring element inside a `destructure` parent. `index` is the ordered position (zero-based). Slice 3d.',
|
|
366
|
+
example: 'element name=first index=0',
|
|
367
|
+
props: {
|
|
368
|
+
name: { required: true, kind: 'identifier' },
|
|
369
|
+
index: { kind: 'string' },
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
mapLit: {
|
|
373
|
+
description: 'Native Map<K,V> literal — emits `const name: Type = new Map([[k1, v1], [k2, v2]]);` from `mapEntry` children. For complex shapes (computed keys, conditional entries, spread), use the `expr={{...}}` escape hatch which carries the raw TS statement verbatim. Slice 3e.',
|
|
374
|
+
example: 'mapLit name=cache type="Map<string, number>"\n mapEntry key="foo" value=1\n mapEntry key="bar" value=2',
|
|
375
|
+
props: {
|
|
376
|
+
name: { required: true, kind: 'identifier' },
|
|
377
|
+
type: { kind: 'typeAnnotation' },
|
|
378
|
+
kind: { kind: 'string' },
|
|
379
|
+
export: { kind: 'boolean' },
|
|
380
|
+
expr: { kind: 'rawExpr' },
|
|
381
|
+
},
|
|
382
|
+
allowedChildren: ['mapEntry'],
|
|
383
|
+
},
|
|
384
|
+
mapEntry: {
|
|
385
|
+
description: 'Map-literal entry inside a `mapLit` parent. `key` and `value` are both expression-typed and ValueIR-canonicalised. Slice 3e.',
|
|
386
|
+
example: 'mapEntry key="foo" value=1',
|
|
387
|
+
props: {
|
|
388
|
+
key: { required: true, kind: 'expression' },
|
|
389
|
+
value: { required: true, kind: 'expression' },
|
|
390
|
+
},
|
|
391
|
+
},
|
|
392
|
+
setLit: {
|
|
393
|
+
description: 'Native Set<T> literal — emits `const name: Type = new Set([v1, v2]);` from `setItem` children. For complex shapes (conditional members, spread), use the `expr={{...}}` escape hatch which carries the raw TS statement verbatim. Slice 3e.',
|
|
394
|
+
example: 'setLit name=allowed type="Set<string>"\n setItem value="admin"\n setItem value="user"',
|
|
395
|
+
props: {
|
|
396
|
+
name: { required: true, kind: 'identifier' },
|
|
397
|
+
type: { kind: 'typeAnnotation' },
|
|
398
|
+
kind: { kind: 'string' },
|
|
399
|
+
export: { kind: 'boolean' },
|
|
400
|
+
expr: { kind: 'rawExpr' },
|
|
401
|
+
},
|
|
402
|
+
allowedChildren: ['setItem'],
|
|
403
|
+
},
|
|
404
|
+
setItem: {
|
|
405
|
+
description: 'Set-literal item inside a `setLit` parent. `value` is expression-typed and ValueIR-canonicalised. Slice 3e.',
|
|
406
|
+
example: 'setItem value="admin"',
|
|
407
|
+
props: {
|
|
408
|
+
value: { required: true, kind: 'expression' },
|
|
409
|
+
},
|
|
410
|
+
},
|
|
267
411
|
on: {
|
|
268
412
|
description: 'Event listener — binds a handler to a named event',
|
|
269
413
|
example: 'on event=click handler=handleClick',
|
|
@@ -293,6 +437,8 @@ export const NODE_SCHEMAS = {
|
|
|
293
437
|
expr: { required: true, kind: 'rawExpr' },
|
|
294
438
|
type: { kind: 'typeAnnotation' },
|
|
295
439
|
export: { kind: 'boolean' },
|
|
440
|
+
// Slice 6 — see fn schema above.
|
|
441
|
+
effects: { kind: 'string' },
|
|
296
442
|
},
|
|
297
443
|
},
|
|
298
444
|
fmt: {
|
|
@@ -858,7 +1004,7 @@ export const NODE_SCHEMAS = {
|
|
|
858
1004
|
reversible: { kind: 'boolean' },
|
|
859
1005
|
export: { kind: 'boolean' },
|
|
860
1006
|
},
|
|
861
|
-
allowedChildren: ['handler'],
|
|
1007
|
+
allowedChildren: ['handler', 'param'],
|
|
862
1008
|
},
|
|
863
1009
|
actionRegistry: {
|
|
864
1010
|
description: 'Calls an imported registration function with a map of string-keyed async action handlers. Emits `target({ key: async (...) => body, ... })` directly — no IIFE wrapper.',
|
|
@@ -878,6 +1024,9 @@ export const NODE_SCHEMAS = {
|
|
|
878
1024
|
confidence: { kind: 'number' },
|
|
879
1025
|
kind: { kind: 'identifier' },
|
|
880
1026
|
type: { kind: 'identifier' },
|
|
1027
|
+
covers: { kind: 'string' },
|
|
1028
|
+
over: { kind: 'identifier' },
|
|
1029
|
+
union: { kind: 'identifier' },
|
|
881
1030
|
param: { kind: 'identifier' },
|
|
882
1031
|
field: { kind: 'identifier' },
|
|
883
1032
|
target: { kind: 'identifier' },
|
|
@@ -936,11 +1085,12 @@ export const NODE_SCHEMAS = {
|
|
|
936
1085
|
// separately via the `let-must-be-inside-each` semantic rule.
|
|
937
1086
|
},
|
|
938
1087
|
let: {
|
|
939
|
-
description: 'Iteration-scoped binding — emits a plain `const` inside the containing `each` callback. Use for values that depend on the iteration variable or index. Unlike `derive` (which compiles to `useMemo` and violates Rules of Hooks inside `.map`), `let` is hook-safe by construction.',
|
|
940
|
-
example: 'let name=idx
|
|
1088
|
+
description: 'Iteration-scoped binding — emits a plain `const` inside the containing `each` callback. Use for values that depend on the iteration variable or index. Unlike `derive` (which compiles to `useMemo` and violates Rules of Hooks inside `.map`), `let` is hook-safe by construction. Provide either `value=` (native expression form, ValueIR-canonicalised — slice 3a) or `expr=` (raw passthrough escape hatch).',
|
|
1089
|
+
example: 'let name=idx value=i+1',
|
|
941
1090
|
props: {
|
|
942
1091
|
name: { required: true, kind: 'identifier' },
|
|
943
|
-
|
|
1092
|
+
value: { kind: 'expression' },
|
|
1093
|
+
expr: { kind: 'rawExpr' },
|
|
944
1094
|
type: { kind: 'typeAnnotation' },
|
|
945
1095
|
},
|
|
946
1096
|
},
|
|
@@ -1041,7 +1191,7 @@ export const NODE_SCHEMAS = {
|
|
|
1041
1191
|
params: { kind: 'string' },
|
|
1042
1192
|
returns: { kind: 'typeAnnotation' },
|
|
1043
1193
|
},
|
|
1044
|
-
allowedChildren: ['handler', 'memo', 'callback', 'ref', 'effect'],
|
|
1194
|
+
allowedChildren: ['handler', 'memo', 'callback', 'ref', 'effect', 'param'],
|
|
1045
1195
|
},
|
|
1046
1196
|
effect: {
|
|
1047
1197
|
description: 'React useEffect — side effect with dependency tracking',
|
|
@@ -1330,18 +1480,42 @@ export const NODE_SCHEMAS = {
|
|
|
1330
1480
|
allowedChildren: ['param', 'handler', 'description', 'guard'],
|
|
1331
1481
|
},
|
|
1332
1482
|
param: {
|
|
1333
|
-
description: 'Parameter definition
|
|
1483
|
+
description: 'Parameter definition. Used in two contexts: (a) MCP tool/resource/prompt params (description/required/min/max apply); (b) fn/method/constructor/etc. parameter defaults via slice 3c — value flows through ValueIR canonicalisation (mirrors slice 1j const.value, 3a let.value, 3b field.value).',
|
|
1334
1484
|
example: 'param name=query type=string required=true description="Search query"',
|
|
1335
1485
|
props: {
|
|
1336
|
-
|
|
1337
|
-
|
|
1486
|
+
// Slice 3c-extension #3: `name` is required UNLESS the param carries
|
|
1487
|
+
// `binding`/`element` destructure children — destructured params encode
|
|
1488
|
+
// the LHS pattern in the children, not in `name`. The required-OR-children
|
|
1489
|
+
// invariant lives in `checkCrossProps` so `validateSchema` accepts both
|
|
1490
|
+
// forms. Keeping the schema-level `required: true` here would reject the
|
|
1491
|
+
// canonical destructured form emitted by the importer.
|
|
1492
|
+
name: { kind: 'identifier' },
|
|
1493
|
+
type: { kind: 'typeAnnotation' },
|
|
1494
|
+
value: { kind: 'expression' },
|
|
1338
1495
|
required: { kind: 'boolean' },
|
|
1496
|
+
// Slice 3c-extension: TS-style optional `?` on the LHS, distinct from MCP
|
|
1497
|
+
// `required`. When `optional=true`, codegen emits `name?: type` (with the
|
|
1498
|
+
// `?` inside the parameter list) so callers may omit the argument.
|
|
1499
|
+
optional: { kind: 'boolean' },
|
|
1500
|
+
// Slice 3c-extension: TS-style variadic `...rest`. When `variadic=true`,
|
|
1501
|
+
// codegen prepends `...` to the parameter name; the type should be an
|
|
1502
|
+
// array (e.g. `string[]`). Variadic params can't have defaults — that's
|
|
1503
|
+
// user error and TS will surface it at the call site.
|
|
1504
|
+
variadic: { kind: 'boolean' },
|
|
1339
1505
|
default: { kind: 'rawExpr' },
|
|
1340
1506
|
description: { kind: 'string' },
|
|
1341
1507
|
min: { kind: 'number' },
|
|
1342
1508
|
max: { kind: 'number' },
|
|
1343
1509
|
},
|
|
1344
|
-
|
|
1510
|
+
// Slice 3c-extension #3: TS-style destructured params via slice 3d's
|
|
1511
|
+
// `binding` (object pattern) / `element` (array pattern) children. When
|
|
1512
|
+
// present, codegen uses the pattern as the LHS instead of `name`, e.g.
|
|
1513
|
+
// param type="Point"
|
|
1514
|
+
// binding name=x
|
|
1515
|
+
// binding name=y
|
|
1516
|
+
// → `{x, y}: Point`. Same node types as slice 3d destructure — no new
|
|
1517
|
+
// node types needed. `name=` is omitted on destructured params.
|
|
1518
|
+
allowedChildren: ['guard', 'description', 'binding', 'element'],
|
|
1345
1519
|
},
|
|
1346
1520
|
prompt: {
|
|
1347
1521
|
description: 'MCP prompt template — a reusable system prompt exposed to AI agents',
|
|
@@ -1595,6 +1769,8 @@ export const NODE_SCHEMAS = {
|
|
|
1595
1769
|
props: {
|
|
1596
1770
|
name: { required: true, kind: 'identifier' },
|
|
1597
1771
|
deps: { kind: 'string' },
|
|
1772
|
+
// Slice 6 — see fn schema above.
|
|
1773
|
+
effects: { kind: 'string' },
|
|
1598
1774
|
},
|
|
1599
1775
|
allowedChildren: ['handler'],
|
|
1600
1776
|
},
|
|
@@ -1607,7 +1783,7 @@ export const NODE_SCHEMAS = {
|
|
|
1607
1783
|
deps: { kind: 'string' },
|
|
1608
1784
|
async: { kind: 'boolean' },
|
|
1609
1785
|
},
|
|
1610
|
-
allowedChildren: ['handler'],
|
|
1786
|
+
allowedChildren: ['handler', 'param'],
|
|
1611
1787
|
},
|
|
1612
1788
|
ref: {
|
|
1613
1789
|
description: 'React useRef — mutable ref object that persists across renders',
|
|
@@ -1961,8 +2137,11 @@ export const NODE_SCHEMAS = {
|
|
|
1961
2137
|
constructor: {
|
|
1962
2138
|
description: 'Constructor for a service — runs on instantiation',
|
|
1963
2139
|
example: 'constructor params="size:number"\n handler <<<\n this.data = new Map();\n >>>',
|
|
1964
|
-
props: {
|
|
1965
|
-
|
|
2140
|
+
props: {
|
|
2141
|
+
params: { kind: 'string' },
|
|
2142
|
+
generics: { kind: 'rawExpr' },
|
|
2143
|
+
},
|
|
2144
|
+
allowedChildren: ['handler', 'param'],
|
|
1966
2145
|
},
|
|
1967
2146
|
cleanup: {
|
|
1968
2147
|
description: 'Cleanup handler — runs on teardown (useEffect return, signal dispose)',
|
|
@@ -1984,13 +2163,13 @@ export const NODE_SCHEMAS = {
|
|
|
1984
2163
|
description: 'Test suite — groups related test cases',
|
|
1985
2164
|
example: 'describe name="UserService"\n it name="creates a user"\n handler <<<\n expect(createUser()).toBeDefined();\n >>>',
|
|
1986
2165
|
props: { name: { required: true, kind: 'string' } },
|
|
1987
|
-
allowedChildren: ['it', 'describe', 'handler'],
|
|
2166
|
+
allowedChildren: ['it', 'describe', 'expect', 'fixture', 'handler'],
|
|
1988
2167
|
},
|
|
1989
2168
|
it: {
|
|
1990
2169
|
description: 'Test case — single test assertion',
|
|
1991
2170
|
example: 'it name="returns 200 on success"\n handler <<<\n expect(res.status).toBe(200);\n >>>',
|
|
1992
2171
|
props: { name: { required: true, kind: 'string' } },
|
|
1993
|
-
allowedChildren: ['handler'],
|
|
2172
|
+
allowedChildren: ['expect', 'fixture', 'handler'],
|
|
1994
2173
|
},
|
|
1995
2174
|
// Ground layer — semantic reasoning
|
|
1996
2175
|
path: {
|
|
@@ -2028,9 +2207,55 @@ export const NODE_SCHEMAS = {
|
|
|
2028
2207
|
props: { fn: { kind: 'identifier' }, to: { kind: 'rawExpr' } },
|
|
2029
2208
|
},
|
|
2030
2209
|
expect: {
|
|
2031
|
-
description: 'Assertion — declare an expected condition
|
|
2032
|
-
example: 'expect expr={{items.length > 0}} message="Items must not be empty"',
|
|
2033
|
-
props: {
|
|
2210
|
+
description: 'Assertion — declare an expected runtime condition or KERN structural invariant',
|
|
2211
|
+
example: 'expect expr={{items.length > 0}} message="Items must not be empty"\nexpect machine=Order reaches=paid via=confirm,capture\nexpect machine=Order transition=capture from=confirmed to=paid\nexpect node=interface name=User child=field count=3\nexpect no=deriveCycles',
|
|
2212
|
+
props: {
|
|
2213
|
+
expr: { kind: 'rawExpr' },
|
|
2214
|
+
fn: { kind: 'identifier' },
|
|
2215
|
+
derive: { kind: 'identifier' },
|
|
2216
|
+
args: { kind: 'rawExpr' },
|
|
2217
|
+
with: { kind: 'rawExpr' },
|
|
2218
|
+
equals: { kind: 'rawExpr' },
|
|
2219
|
+
matches: { kind: 'string' },
|
|
2220
|
+
throws: { kind: 'string' },
|
|
2221
|
+
message: { kind: 'string' },
|
|
2222
|
+
preset: { kind: 'identifier' },
|
|
2223
|
+
severity: { kind: 'identifier' },
|
|
2224
|
+
node: { kind: 'identifier' },
|
|
2225
|
+
name: { kind: 'string' },
|
|
2226
|
+
within: { kind: 'string' },
|
|
2227
|
+
child: { kind: 'identifier' },
|
|
2228
|
+
childName: { kind: 'string' },
|
|
2229
|
+
prop: { kind: 'identifier' },
|
|
2230
|
+
is: { kind: 'string' },
|
|
2231
|
+
count: { kind: 'number' },
|
|
2232
|
+
machine: { kind: 'identifier' },
|
|
2233
|
+
transition: { kind: 'identifier' },
|
|
2234
|
+
from: { kind: 'identifier' },
|
|
2235
|
+
to: { kind: 'identifier' },
|
|
2236
|
+
guarded: { kind: 'boolean' },
|
|
2237
|
+
reaches: { kind: 'identifier' },
|
|
2238
|
+
through: { kind: 'string' },
|
|
2239
|
+
avoid: { kind: 'string' },
|
|
2240
|
+
avoids: { kind: 'string' },
|
|
2241
|
+
maxSteps: { kind: 'number' },
|
|
2242
|
+
via: { kind: 'string' },
|
|
2243
|
+
no: { kind: 'identifier' },
|
|
2244
|
+
guard: { kind: 'identifier' },
|
|
2245
|
+
exhaustive: { kind: 'boolean' },
|
|
2246
|
+
over: { kind: 'identifier' },
|
|
2247
|
+
union: { kind: 'identifier' },
|
|
2248
|
+
covers: { kind: 'string' },
|
|
2249
|
+
},
|
|
2250
|
+
},
|
|
2251
|
+
fixture: {
|
|
2252
|
+
description: 'Native test fixture — named runtime data available to scoped expect assertions',
|
|
2253
|
+
example: 'fixture name=paidOrder value={{({ id: "ord_1", status: "paid" })}}',
|
|
2254
|
+
props: {
|
|
2255
|
+
name: { required: true, kind: 'identifier' },
|
|
2256
|
+
value: { kind: 'rawExpr' },
|
|
2257
|
+
expr: { kind: 'rawExpr' },
|
|
2258
|
+
},
|
|
2034
2259
|
},
|
|
2035
2260
|
recover: {
|
|
2036
2261
|
description: 'Recovery handler — runs when a parent node fails',
|
|
@@ -2124,6 +2349,116 @@ function checkCrossProps(node, violations) {
|
|
|
2124
2349
|
col: node.loc?.col,
|
|
2125
2350
|
});
|
|
2126
2351
|
}
|
|
2352
|
+
if (node.type === 'param') {
|
|
2353
|
+
// Slice 3c-extension #3: `param` requires `name=` UNLESS it carries
|
|
2354
|
+
// `binding`/`element` destructure children — destructured params encode
|
|
2355
|
+
// the LHS pattern in the children. Replaces the old prop-level
|
|
2356
|
+
// `required: true` constraint which rejected the canonical destructured
|
|
2357
|
+
// form emitted by importer/decompiler.
|
|
2358
|
+
const hasName = 'name' in props;
|
|
2359
|
+
const hasDestructure = (node.children ?? []).some((c) => c.type === 'binding' || c.type === 'element');
|
|
2360
|
+
if (!hasName && !hasDestructure) {
|
|
2361
|
+
violations.push({
|
|
2362
|
+
nodeType: 'param',
|
|
2363
|
+
message: "'param' requires either 'name' or destructure children ('binding'/'element')",
|
|
2364
|
+
line: node.loc?.line,
|
|
2365
|
+
col: node.loc?.col,
|
|
2366
|
+
});
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
if (node.type === 'expect') {
|
|
2370
|
+
const hasRuntimeAssertion = 'expr' in props;
|
|
2371
|
+
const hasRuntimeBehavior = 'fn' in props || 'derive' in props;
|
|
2372
|
+
const hasPreset = 'preset' in props;
|
|
2373
|
+
const hasNodeShape = 'node' in props;
|
|
2374
|
+
const hasNegativeInvariant = 'no' in props;
|
|
2375
|
+
const hasGuardExhaustiveness = 'guard' in props;
|
|
2376
|
+
const hasMachineTransition = 'transition' in props;
|
|
2377
|
+
const hasMachineReachability = 'reaches' in props || ('machine' in props && !hasNegativeInvariant && !hasMachineTransition);
|
|
2378
|
+
if (!hasRuntimeAssertion &&
|
|
2379
|
+
!hasRuntimeBehavior &&
|
|
2380
|
+
!hasPreset &&
|
|
2381
|
+
!hasNodeShape &&
|
|
2382
|
+
!hasMachineTransition &&
|
|
2383
|
+
!hasMachineReachability &&
|
|
2384
|
+
!hasNegativeInvariant &&
|
|
2385
|
+
!hasGuardExhaustiveness) {
|
|
2386
|
+
violations.push({
|
|
2387
|
+
nodeType: 'expect',
|
|
2388
|
+
message: "'expect' requires 'expr', 'fn', 'derive', 'preset', 'node', 'machine' reachability, machine transition, 'no', or 'guard'",
|
|
2389
|
+
line: node.loc?.line,
|
|
2390
|
+
col: node.loc?.col,
|
|
2391
|
+
});
|
|
2392
|
+
}
|
|
2393
|
+
if ('fn' in props && 'derive' in props) {
|
|
2394
|
+
violations.push({
|
|
2395
|
+
nodeType: 'expect',
|
|
2396
|
+
message: "'expect' cannot combine fn=<name> and derive=<name>",
|
|
2397
|
+
line: node.loc?.line,
|
|
2398
|
+
col: node.loc?.col,
|
|
2399
|
+
});
|
|
2400
|
+
}
|
|
2401
|
+
if (hasRuntimeBehavior && hasRuntimeAssertion) {
|
|
2402
|
+
violations.push({
|
|
2403
|
+
nodeType: 'expect',
|
|
2404
|
+
message: "'expect' cannot combine fn/derive behavioral assertions with expr={{...}}",
|
|
2405
|
+
line: node.loc?.line,
|
|
2406
|
+
col: node.loc?.col,
|
|
2407
|
+
});
|
|
2408
|
+
}
|
|
2409
|
+
if (hasMachineTransition && !('machine' in props)) {
|
|
2410
|
+
violations.push({
|
|
2411
|
+
nodeType: 'expect',
|
|
2412
|
+
message: "'expect' machine transition assertions require machine=<name>",
|
|
2413
|
+
line: node.loc?.line,
|
|
2414
|
+
col: node.loc?.col,
|
|
2415
|
+
});
|
|
2416
|
+
}
|
|
2417
|
+
if (hasMachineTransition && 'reaches' in props) {
|
|
2418
|
+
violations.push({
|
|
2419
|
+
nodeType: 'expect',
|
|
2420
|
+
message: "'expect' cannot combine machine transition assertions with reaches=<state>",
|
|
2421
|
+
line: node.loc?.line,
|
|
2422
|
+
col: node.loc?.col,
|
|
2423
|
+
});
|
|
2424
|
+
}
|
|
2425
|
+
if (hasMachineReachability && (!('machine' in props) || !('reaches' in props))) {
|
|
2426
|
+
violations.push({
|
|
2427
|
+
nodeType: 'expect',
|
|
2428
|
+
message: "'expect' machine reachability requires both 'machine' and 'reaches'",
|
|
2429
|
+
line: node.loc?.line,
|
|
2430
|
+
col: node.loc?.col,
|
|
2431
|
+
});
|
|
2432
|
+
}
|
|
2433
|
+
if (hasGuardExhaustiveness && props.exhaustive !== true && props.exhaustive !== 'true') {
|
|
2434
|
+
violations.push({
|
|
2435
|
+
nodeType: 'expect',
|
|
2436
|
+
message: "'expect' guard assertions require exhaustive=true",
|
|
2437
|
+
line: node.loc?.line,
|
|
2438
|
+
col: node.loc?.col,
|
|
2439
|
+
});
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
if (node.type === 'fixture') {
|
|
2443
|
+
const hasValue = 'value' in props;
|
|
2444
|
+
const hasExpr = 'expr' in props;
|
|
2445
|
+
if (!hasValue && !hasExpr) {
|
|
2446
|
+
violations.push({
|
|
2447
|
+
nodeType: 'fixture',
|
|
2448
|
+
message: "'fixture' requires either value={{...}} or expr={{...}}",
|
|
2449
|
+
line: node.loc?.line,
|
|
2450
|
+
col: node.loc?.col,
|
|
2451
|
+
});
|
|
2452
|
+
}
|
|
2453
|
+
if (hasValue && hasExpr) {
|
|
2454
|
+
violations.push({
|
|
2455
|
+
nodeType: 'fixture',
|
|
2456
|
+
message: "'fixture' must not combine value={{...}} and expr={{...}}",
|
|
2457
|
+
line: node.loc?.line,
|
|
2458
|
+
col: node.loc?.col,
|
|
2459
|
+
});
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2127
2462
|
if (node.type === 'fmt') {
|
|
2128
2463
|
const returnMode = isTruthyProp(props.return);
|
|
2129
2464
|
if (returnMode && 'name' in props) {
|
|
@@ -2191,7 +2526,18 @@ export function exportSchemaJSON(runtime) {
|
|
|
2191
2526
|
styleShorthands: { ...STYLE_SHORTHANDS },
|
|
2192
2527
|
valueShorthands: { ...VALUE_SHORTHANDS },
|
|
2193
2528
|
multilineBlockTypes: [...rt.multilineBlockTypes],
|
|
2194
|
-
propKinds: [
|
|
2529
|
+
propKinds: [
|
|
2530
|
+
'identifier',
|
|
2531
|
+
'typeAnnotation',
|
|
2532
|
+
'importPath',
|
|
2533
|
+
'rawExpr',
|
|
2534
|
+
'rawBlock',
|
|
2535
|
+
'string',
|
|
2536
|
+
'boolean',
|
|
2537
|
+
'number',
|
|
2538
|
+
'expression',
|
|
2539
|
+
'regex',
|
|
2540
|
+
],
|
|
2195
2541
|
...(evolvedTypes.length > 0 ? { evolvedTypes } : {}),
|
|
2196
2542
|
};
|
|
2197
2543
|
}
|