tjs-lang 0.6.44 → 0.7.3

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 (86) hide show
  1. package/CLAUDE.md +85 -422
  2. package/README.md +15 -82
  3. package/bin/benchmarks.ts +7 -7
  4. package/bin/dev.ts +2 -1
  5. package/demo/autocomplete.test.ts +1 -1
  6. package/demo/docs.json +744 -48
  7. package/demo/src/demo-nav.ts +5 -5
  8. package/demo/src/index.ts +28 -36
  9. package/demo/src/module-sw.ts +1 -1
  10. package/demo/src/playground-shared.ts +17 -17
  11. package/demo/src/playground.ts +13 -1
  12. package/demo/src/style.ts +4 -1
  13. package/demo/src/tjs-playground.ts +5 -5
  14. package/demo/src/user-store.ts +2 -1
  15. package/demo/static/favicon.svg +17 -24
  16. package/demo/static/tosi-platform.json +9304 -0
  17. package/dist/index.js +158 -156
  18. package/dist/index.js.map +14 -13
  19. package/dist/scripts/compat-effect.d.ts +16 -0
  20. package/dist/scripts/compat-kysely.d.ts +13 -0
  21. package/dist/scripts/compat-radash.d.ts +13 -0
  22. package/dist/scripts/compat-superstruct.d.ts +13 -0
  23. package/dist/scripts/compat-ts-pattern.d.ts +13 -0
  24. package/dist/scripts/compat-zod.d.ts +12 -0
  25. package/dist/src/lang/emitters/from-ts.d.ts +1 -1
  26. package/dist/src/lang/emitters/js-tests.d.ts +4 -0
  27. package/dist/src/lang/emitters/js.d.ts +2 -2
  28. package/dist/src/lang/index.d.ts +1 -0
  29. package/dist/src/lang/json-schema.d.ts +40 -0
  30. package/dist/src/lang/parser-transforms.d.ts +14 -0
  31. package/dist/src/lang/runtime.d.ts +39 -6
  32. package/dist/src/types/Type.d.ts +5 -0
  33. package/dist/tjs-full.js +158 -156
  34. package/dist/tjs-full.js.map +14 -13
  35. package/dist/tjs-vm.js +44 -43
  36. package/dist/tjs-vm.js.map +5 -5
  37. package/docs/README.md +21 -20
  38. package/docs/WASM-QUICKSTART.md +283 -0
  39. package/docs/diagrams/architecture-shift.svg +117 -0
  40. package/docs/diagrams/compile-runtime.svg +130 -0
  41. package/docs/diagrams/icon-riff-1.svg +55 -0
  42. package/docs/diagrams/icon-riff-2.svg +62 -0
  43. package/docs/diagrams/icon-riff-3.svg +61 -0
  44. package/docs/diagrams/platform-overview.svg +114 -0
  45. package/docs/diagrams/safe-eval.svg +147 -0
  46. package/docs/eval-v4/arch-comparison.svg +277 -0
  47. package/docs/eval-v4/bundler-tree.svg +250 -0
  48. package/docs/eval-v4/http-lifecycle.svg +148 -0
  49. package/docs/function-predicate-design.md +8 -8
  50. package/docs/native-engine-integration.md +2 -2
  51. package/editors/codemirror/autocomplete.test.ts +29 -29
  52. package/package.json +10 -4
  53. package/src/cli/commands/convert.test.ts +11 -8
  54. package/src/cli/tjs.ts +1 -1
  55. package/src/lang/codegen.test.ts +117 -112
  56. package/src/lang/docs.test.ts +22 -22
  57. package/src/lang/docs.ts +5 -8
  58. package/src/lang/emitters/dts.test.ts +13 -13
  59. package/src/lang/emitters/from-ts.ts +36 -9
  60. package/src/lang/emitters/js-tests.ts +143 -28
  61. package/src/lang/emitters/js.ts +49 -28
  62. package/src/lang/features.test.ts +259 -43
  63. package/src/lang/from-ts.test.ts +3 -3
  64. package/src/lang/function-predicate.test.ts +1 -1
  65. package/src/lang/index.ts +8 -47
  66. package/src/lang/json-schema.test.ts +261 -0
  67. package/src/lang/json-schema.ts +167 -0
  68. package/src/lang/parser-params.ts +28 -44
  69. package/src/lang/parser-transforms.ts +255 -0
  70. package/src/lang/parser.test.ts +32 -13
  71. package/src/lang/parser.ts +49 -11
  72. package/src/lang/perf.test.ts +11 -11
  73. package/src/lang/roundtrip.test.ts +3 -3
  74. package/src/lang/runtime.test.ts +167 -0
  75. package/src/lang/runtime.ts +234 -46
  76. package/src/lang/transpiler.test.ts +21 -21
  77. package/src/lang/typescript-syntax.test.ts +11 -9
  78. package/src/types/Type.ts +38 -1
  79. package/src/use-cases/bootstrap.test.ts +7 -7
  80. package/src/use-cases/client-server.test.ts +1 -1
  81. package/src/use-cases/malicious-actor.test.ts +1 -1
  82. package/src/use-cases/rag-processor.test.ts +1 -1
  83. package/src/use-cases/sophisticated-agents.test.ts +2 -2
  84. package/src/use-cases/transpiler-llm.test.ts +1 -1
  85. package/src/use-cases/unbundled-imports.test.ts +9 -9
  86. package/tjs-lang.svg +17 -25
@@ -94,28 +94,28 @@ describe('TS → TJS conversion quality', () => {
94
94
  const ts = `function getName(): string { return 'test' }`
95
95
  const { code } = fromTS(ts, { emitTJS: true })
96
96
 
97
- expect(code).toContain("-! ''")
97
+ expect(code).toContain(":! ''")
98
98
  })
99
99
 
100
100
  it('converts number return type to -! syntax', () => {
101
101
  const ts = `function getCount(): number { return 42 }`
102
102
  const { code } = fromTS(ts, { emitTJS: true })
103
103
 
104
- expect(code).toContain('-! 0')
104
+ expect(code).toContain(':! 0')
105
105
  })
106
106
 
107
107
  it('converts boolean return type to -! syntax', () => {
108
108
  const ts = `function isValid(): boolean { return true }`
109
109
  const { code } = fromTS(ts, { emitTJS: true })
110
110
 
111
- expect(code).toContain('-! false')
111
+ expect(code).toContain(':! false')
112
112
  })
113
113
 
114
114
  it('converts object return type to -! syntax', () => {
115
115
  const ts = `function getUser(): { name: string; age: number } { return { name: '', age: 0 } }`
116
116
  const { code } = fromTS(ts, { emitTJS: true })
117
117
 
118
- expect(code).toContain('-!')
118
+ expect(code).toContain(':!')
119
119
  expect(code).toContain("name: ''")
120
120
  expect(code).toContain('age: 0')
121
121
  })
@@ -124,22 +124,22 @@ describe('TS → TJS conversion quality', () => {
124
124
  const ts = `function getItems(): string[] { return [] }`
125
125
  const { code } = fromTS(ts, { emitTJS: true })
126
126
 
127
- expect(code).toContain("-! ['']")
127
+ expect(code).toContain(":! ['']")
128
128
  })
129
129
 
130
130
  it('omits void return type', () => {
131
131
  const ts = `function doSomething(): void { console.log('done') }`
132
132
  const { code } = fromTS(ts, { emitTJS: true })
133
133
 
134
- expect(code).not.toContain('-!')
135
- expect(code).not.toContain('->')
134
+ expect(code).not.toContain(':!')
135
+ expect(code).not.toMatch(/\)\s*:/)
136
136
  })
137
137
 
138
138
  it('handles Promise return types by unwrapping', () => {
139
139
  const ts = `async function fetchData(): Promise<string> { return 'data' }`
140
140
  const { code } = fromTS(ts, { emitTJS: true })
141
141
 
142
- expect(code).toContain("-! ''")
142
+ expect(code).toContain(":! ''")
143
143
  expect(code).not.toContain('Promise')
144
144
  })
145
145
  })
@@ -236,7 +236,7 @@ class Calculator {
236
236
  `
237
237
  const { code } = fromTS(ts, { emitTJS: true })
238
238
 
239
- expect(code).toContain('add(a: 0.0, b: 0.0) -! 0.0')
239
+ expect(code).toContain('add(a: 0.0, b: 0.0):! 0.0')
240
240
  })
241
241
 
242
242
  it('converts getters and setters', () => {
@@ -277,7 +277,7 @@ class MathUtils {
277
277
  `
278
278
  const { code } = fromTS(ts, { emitTJS: true })
279
279
 
280
- expect(code).toContain('static double(x: 0.0) -! 0.0')
280
+ expect(code).toContain('static double(x: 0.0):! 0.0')
281
281
  })
282
282
 
283
283
  it('converts async methods', () => {
@@ -291,7 +291,7 @@ class Api {
291
291
  const { code } = fromTS(ts, { emitTJS: true })
292
292
 
293
293
  expect(code).toContain('async fetch')
294
- expect(code).toContain("-! ''")
294
+ expect(code).toContain(":! ''")
295
295
  })
296
296
  })
297
297
 
@@ -344,7 +344,7 @@ class Api {
344
344
 
345
345
  expect(code).toContain('function double')
346
346
  expect(code).toContain('x: 0')
347
- expect(code).toContain('-! 0')
347
+ expect(code).toContain(':! 0')
348
348
  })
349
349
 
350
350
  it('converts arrow function with block body', () => {
@@ -399,7 +399,7 @@ describe('TJS → JS transpilation quality', () => {
399
399
 
400
400
  describe('__tjs metadata', () => {
401
401
  it('includes param types in metadata', () => {
402
- const source = `function greet(name: 'World') -> 'World' { return name }`
402
+ const source = `function greet(name: 'World'): 'World' { return name }`
403
403
  const { code, types } = tjs(source)
404
404
 
405
405
  expect(code).toContain('__tjs')
@@ -408,7 +408,7 @@ describe('TJS → JS transpilation quality', () => {
408
408
  })
409
409
 
410
410
  it('includes return type in metadata', () => {
411
- const source = `function double(x: 0) -> 0 { return x * 2 }`
411
+ const source = `function double(x: 0): 0 { return x * 2 }`
412
412
  const { code, types } = tjs(source)
413
413
 
414
414
  expect(code).toContain('__tjs')
@@ -430,15 +430,15 @@ describe('TJS → JS transpilation quality', () => {
430
430
  describe('documentation generation quality', () => {
431
431
  describe('function signatures', () => {
432
432
  it('preserves original signature in markdown', () => {
433
- const source = `function greet(name: 'World') -> '' { return name }`
433
+ const source = `function greet(name: 'World'): '' { return name }`
434
434
  const { markdown } = generateDocs(source)
435
435
 
436
436
  // Signature is preserved as-is - the types ARE the docs
437
- expect(markdown).toContain("function greet(name: 'World') -> ''")
437
+ expect(markdown).toContain("function greet(name: 'World'): ''")
438
438
  })
439
439
 
440
440
  it('preserves optional params with defaults', () => {
441
- const source = `function greet(name = 'World') -> '' { return name }`
441
+ const source = `function greet(name = 'World'): '' { return name }`
442
442
  const { markdown } = generateDocs(source)
443
443
 
444
444
  expect(markdown).toContain("name = 'World'")
@@ -447,7 +447,7 @@ describe('documentation generation quality', () => {
447
447
 
448
448
  describe('signature as documentation', () => {
449
449
  it('shows params in signature', () => {
450
- const source = `function add(a: 0, b: 0) -> 0 { return a + b }`
450
+ const source = `function add(a: 0, b: 0): 0 { return a + b }`
451
451
  const { markdown } = generateDocs(source)
452
452
 
453
453
  expect(markdown).toContain('a: 0')
@@ -455,10 +455,10 @@ describe('documentation generation quality', () => {
455
455
  })
456
456
 
457
457
  it('shows return type in signature', () => {
458
- const source = `function double(x: 0) -> 0 { return x * 2 }`
458
+ const source = `function double(x: 0): 0 { return x * 2 }`
459
459
  const { markdown } = generateDocs(source)
460
460
 
461
- expect(markdown).toContain('-> 0')
461
+ expect(markdown).toContain(': 0')
462
462
  })
463
463
  })
464
464
  })
@@ -577,10 +577,10 @@ function greet(name: string): string {
577
577
  `
578
578
  const { code } = fromTS(ts, { emitTJS: true })
579
579
 
580
- // All functions should be present (TS transpiler uses -! to skip signature tests)
581
- expect(code).toContain('function add(a: 0.0, b: 0.0) -! 0.0')
582
- expect(code).toContain('function multiply(a: 0.0, b: 0.0) -! 0.0')
583
- expect(code).toContain("function greet(name: '') -! ''")
580
+ // All functions should be present (TS transpiler uses :! to skip signature tests)
581
+ expect(code).toContain('function add(a: 0.0, b: 0.0):! 0.0')
582
+ expect(code).toContain('function multiply(a: 0.0, b: 0.0):! 0.0')
583
+ expect(code).toContain("function greet(name: ''):! ''")
584
584
 
585
585
  // Should be valid TJS (no TypeScript syntax remaining)
586
586
  expect(code).not.toContain(': number')
@@ -653,7 +653,7 @@ console.log(second())
653
653
  const ts = `function test(): number { return 42 }`
654
654
  const { code } = fromTS(ts, { emitTJS: true })
655
655
 
656
- expect(code).toContain('-! 0')
656
+ expect(code).toContain(':! 0')
657
657
  expect(code).not.toContain(': number')
658
658
  })
659
659
 
@@ -709,15 +709,15 @@ describe('Pipeline Step 2: TJS → JS', () => {
709
709
  it('transpiles multiple functions correctly', () => {
710
710
  // Use -! to skip signature tests (we're testing transpilation, not return values)
711
711
  const tjsSource = `
712
- function add(a: 0, b: 0) -! 0 {
712
+ function add(a: 0, b: 0):! 0 {
713
713
  return a + b
714
714
  }
715
715
 
716
- function multiply(a: 0, b: 0) -! 0 {
716
+ function multiply(a: 0, b: 0):! 0 {
717
717
  return a * b
718
718
  }
719
719
 
720
- function greet(name: '') -! '' {
720
+ function greet(name: ''):! '' {
721
721
  return 'Hello, ' + name
722
722
  }
723
723
  `
@@ -742,11 +742,11 @@ function greet(name: '') -! '' {
742
742
  it('each function has correct metadata', () => {
743
743
  // Use -! to skip signature tests
744
744
  const tjsSource = `
745
- function add(a: 0, b: 0) -! 0 {
745
+ function add(a: 0, b: 0):! 0 {
746
746
  return a + b
747
747
  }
748
748
 
749
- function greet(name: '', excited = false) -! '' {
749
+ function greet(name: '', excited = false):! '' {
750
750
  return excited ? name + '!' : name
751
751
  }
752
752
  `
@@ -768,7 +768,7 @@ function greet(name: '', excited = false) -! '' {
768
768
  it('produces executable JavaScript', () => {
769
769
  // Use -! to skip signature test
770
770
  const tjsSource = `
771
- function double(x: 0) -! 0 {
771
+ function double(x: 0):! 0 {
772
772
  return x * 2
773
773
  }
774
774
  `
@@ -782,7 +782,7 @@ function double(x: 0) -! 0 {
782
782
  it('includes runtime validation', () => {
783
783
  // Use -! to skip signature test
784
784
  const tjsSource = `
785
- function greet(name: '') -! '' {
785
+ function greet(name: ''):! '' {
786
786
  return 'Hello, ' + name
787
787
  }
788
788
  `
@@ -799,7 +799,7 @@ function greet(name: '') -! '' {
799
799
  it('__tjs metadata is valid JSON structure', () => {
800
800
  // Use -! to skip signature test
801
801
  const tjsSource = `
802
- function test(a: 0, b: '') -! true {
802
+ function test(a: 0, b: ''):! true {
803
803
  return a > 0
804
804
  }
805
805
  `
@@ -819,7 +819,7 @@ function test(a: 0, b: '') -! true {
819
819
  it('runs inline tests during transpilation', () => {
820
820
  // Use -! to skip signature test - we only want to count explicit tests
821
821
  const tjsSource = `
822
- function add(a: 0, b: 0) -! 0 {
822
+ function add(a: 0, b: 0):! 0 {
823
823
  return a + b
824
824
  }
825
825
 
@@ -841,7 +841,7 @@ test 'add works' {
841
841
  // TjsEquals directive enables structural equality transformation.
842
842
 
843
843
  it('preserves == as-is by default (JS semantics)', () => {
844
- const tjsSource = `function isEqual(a: {x: 0}, b: {x: 0}) -! true { return a == b }`
844
+ const tjsSource = `function isEqual(a: {x: 0}, b: {x: 0}):! true { return a == b }`
845
845
  const { code } = tjs(tjsSource)
846
846
 
847
847
  // Without TjsEquals, == is NOT transformed
@@ -851,7 +851,7 @@ test 'add works' {
851
851
 
852
852
  it('transforms == to Eq() with TjsEquals directive', () => {
853
853
  const tjsSource = `TjsEquals
854
- function isEqual(a: {x: 0}, b: {x: 0}) -! true { return a == b }`
854
+ function isEqual(a: {x: 0}, b: {x: 0}):! true { return a == b }`
855
855
  const { code } = tjs(tjsSource)
856
856
 
857
857
  // Should transform == to Eq()
@@ -862,7 +862,7 @@ function isEqual(a: {x: 0}, b: {x: 0}) -! true { return a == b }`
862
862
 
863
863
  it('transforms != to NotEq() with TjsEquals directive', () => {
864
864
  const tjsSource = `TjsEquals
865
- function notEqual(a: {x: 0}, b: {x: 0}) -! true { return a != b }`
865
+ function notEqual(a: {x: 0}, b: {x: 0}):! true { return a != b }`
866
866
  const { code } = tjs(tjsSource)
867
867
 
868
868
  // Should transform != to NotEq()
@@ -872,7 +872,7 @@ function notEqual(a: {x: 0}, b: {x: 0}) -! true { return a != b }`
872
872
 
873
873
  it('preserves === for identity comparison', () => {
874
874
  const tjsSource = `TjsEquals
875
- function isSame(a: {x: 0}, b: {x: 0}) -! true { return a === b }`
875
+ function isSame(a: {x: 0}, b: {x: 0}):! true { return a === b }`
876
876
  const { code } = tjs(tjsSource)
877
877
 
878
878
  // Should preserve === unchanged
@@ -883,7 +883,7 @@ function isSame(a: {x: 0}, b: {x: 0}) -! true { return a === b }`
883
883
 
884
884
  it('does NOT add Is/IsNot imports when not needed', () => {
885
885
  // Use unsafe (!) to skip all validation, then no __tjs needed
886
- const tjsSource = `function add(! a: 0, b: 0) -! 0 { return a + b }`
886
+ const tjsSource = `function add(! a: 0, b: 0):! 0 { return a + b }`
887
887
  const { code } = tjs(tjsSource)
888
888
 
889
889
  // No equality ops and fully unsafe = no __tjs reference
@@ -895,7 +895,7 @@ function isSame(a: {x: 0}, b: {x: 0}) -! true { return a === b }`
895
895
 
896
896
  it('adds only Eq when only == is used with TjsEquals', () => {
897
897
  const tjsSource = `TjsEquals
898
- function eq(a: 0, b: 0) -! true { return a == b }`
898
+ function eq(a: 0, b: 0):! true { return a == b }`
899
899
  const { code } = tjs(tjsSource)
900
900
 
901
901
  expect(code).toContain('Eq(')
@@ -905,7 +905,7 @@ function eq(a: 0, b: 0) -! true { return a == b }`
905
905
 
906
906
  it('adds only NotEq when only != is used with TjsEquals', () => {
907
907
  const tjsSource = `TjsEquals
908
- function neq(a: 0, b: 0) -! true { return a != b }`
908
+ function neq(a: 0, b: 0):! true { return a != b }`
909
909
  const { code } = tjs(tjsSource)
910
910
 
911
911
  expect(code).toContain('NotEq(')
@@ -914,7 +914,7 @@ function neq(a: 0, b: 0) -! true { return a != b }`
914
914
 
915
915
  it('adds both Eq and NotEq when both == and != are used with TjsEquals', () => {
916
916
  const tjsSource = `TjsEquals
917
- function test(a: 0, b: 0) -! true { return a == b || a != b }`
917
+ function test(a: 0, b: 0):! true { return a == b || a != b }`
918
918
  const { code } = tjs(tjsSource)
919
919
 
920
920
  expect(code).toContain('Eq(')
@@ -923,7 +923,7 @@ function test(a: 0, b: 0) -! true { return a == b || a != b }`
923
923
 
924
924
  it('does NOT add imports for === only even with TjsEquals', () => {
925
925
  const tjsSource = `TjsEquals
926
- function strict(a: 0, b: 0) -! true { return a === b }`
926
+ function strict(a: 0, b: 0):! true { return a === b }`
927
927
  const { code } = tjs(tjsSource)
928
928
 
929
929
  expect(code).not.toContain('const { Is')
@@ -935,7 +935,7 @@ function strict(a: 0, b: 0) -! true { return a === b }`
935
935
  installRuntime()
936
936
 
937
937
  const tjsSource = `TjsEquals
938
- function isEqual(! a: null, b: null) -! true { return a == b }`
938
+ function isEqual(! a: null, b: null):! true { return a == b }`
939
939
  const { code } = tjs(tjsSource)
940
940
 
941
941
  const isEqual = new Function(code + '; return isEqual')()
@@ -952,7 +952,7 @@ function isEqual(! a: null, b: null) -! true { return a == b }`
952
952
  describe('TjsStandard (ASI protection)', () => {
953
953
  it('inserts semicolon before IIFE to prevent footgun', () => {
954
954
  const tjsSource = `TjsStandard
955
- function test() -! 0 {
955
+ function test():! 0 {
956
956
  const x = 1
957
957
  (() => console.log('iife'))()
958
958
  return x
@@ -965,7 +965,7 @@ function test() -! 0 {
965
965
 
966
966
  it('inserts semicolon before array literal on new line', () => {
967
967
  const tjsSource = `TjsStandard
968
- function test() -! 0 {
968
+ function test():! 0 {
969
969
  const x = 1
970
970
  [1, 2, 3].forEach(console.log)
971
971
  return x
@@ -978,7 +978,7 @@ function test() -! 0 {
978
978
 
979
979
  it('does NOT insert semicolon when previous line has operator', () => {
980
980
  const tjsSource = `TjsStandard
981
- function test() -! 0 {
981
+ function test():! 0 {
982
982
  const result = 1 +
983
983
  (2 + 3)
984
984
  return result
@@ -994,7 +994,7 @@ function test() -! 0 {
994
994
  it('does NOT insert semicolon after opening brace', () => {
995
995
  // Test that we don't add ; after [ or {
996
996
  const tjsSource = `TjsStandard
997
- function test() -! 0 {
997
+ function test():! 0 {
998
998
  const arr = [
999
999
  (x => x + 1)
1000
1000
  ]
@@ -1009,7 +1009,7 @@ function test() -! 0 {
1009
1009
 
1010
1010
  it('does NOT insert semicolon after return keyword', () => {
1011
1011
  const tjsSource = `TjsStandard
1012
- function test() -! 0 {
1012
+ function test():! 0 {
1013
1013
  return (
1014
1014
  1 + 2
1015
1015
  )
@@ -1023,7 +1023,7 @@ function test() -! 0 {
1023
1023
 
1024
1024
  it('does NOT insert semicolon after comma (multi-line array)', () => {
1025
1025
  const tjsSource = `TjsStandard
1026
- function test() -! [] {
1026
+ function test():! [] {
1027
1027
  return [
1028
1028
  1,
1029
1029
  (2 + 3),
@@ -1038,7 +1038,7 @@ function test() -! [] {
1038
1038
 
1039
1039
  it('TjsStrict enables TjsStandard', () => {
1040
1040
  const tjsSource = `TjsStrict
1041
- function test() -! 0 {
1041
+ function test():! 0 {
1042
1042
  const x = 1
1043
1043
  (() => console.log('iife'))()
1044
1044
  return x
@@ -1051,7 +1051,7 @@ function test() -! 0 {
1051
1051
 
1052
1052
  it('works correctly at runtime', () => {
1053
1053
  const tjsSource = `TjsStandard
1054
- function test() -! 0 {
1054
+ function test():! 0 {
1055
1055
  let result = 42
1056
1056
  (() => { result = result + 1 })()
1057
1057
  return result
@@ -1066,9 +1066,9 @@ function test() -! 0 {
1066
1066
  it('without TjsStandard, IIFE would be footgun (JS behavior)', () => {
1067
1067
  // This demonstrates the footgun that TjsStandard prevents
1068
1068
  // Without the directive, the code passes through unchanged
1069
- const tjsSource = `
1070
- function getNumber() -! 0 { return 42 }
1071
- function test() -! 0 {
1069
+ const tjsSource = `TjsCompat
1070
+ function getNumber():! 0 { return 42 }
1071
+ function test():! 0 {
1072
1072
  const x = getNumber
1073
1073
  (() => {})()
1074
1074
  return 1
@@ -1098,7 +1098,7 @@ function calculate(a: number, b: number, operation: string): number {
1098
1098
  expect(tjsCode).toContain('a: 0')
1099
1099
  expect(tjsCode).toContain('b: 0')
1100
1100
  expect(tjsCode).toContain("operation: ''")
1101
- expect(tjsCode).toContain('-! 0') // TS transpiler uses -! to skip signature tests
1101
+ expect(tjsCode).toContain(':! 0') // TS transpiler uses :! to skip signature tests
1102
1102
 
1103
1103
  // Step 3: TJS → JS (already has -! from TS transpiler)
1104
1104
  const { code: jsCode, types } = tjs(tjsCode)
@@ -1195,7 +1195,7 @@ function greet(user: { name: string; age: number }): string {
1195
1195
  `
1196
1196
  const { code: tjsCode } = fromTS(ts, { emitTJS: true })
1197
1197
  // Already has -! from TS transpiler
1198
- const { code: jsCode } = tjs(tjsCode)
1198
+ const { code: jsCode } = tjs('safety inputs\n' + tjsCode)
1199
1199
 
1200
1200
  const greet = new Function(jsCode + '; return greet')()
1201
1201
 
@@ -1221,7 +1221,7 @@ function add(a: number, b: number): number {
1221
1221
  `
1222
1222
  const { code: tjsCode } = fromTS(ts, { emitTJS: true })
1223
1223
  // Already has -! from TS transpiler
1224
- const { code: jsCode } = tjs(tjsCode)
1224
+ const { code: jsCode } = tjs('safety inputs\n' + tjsCode)
1225
1225
 
1226
1226
  const add = new Function(jsCode + '; return add')()
1227
1227
 
@@ -1287,7 +1287,7 @@ describe('Monadic error handling', () => {
1287
1287
  describe('error pass-through (monadic propagation)', () => {
1288
1288
  it('passes through Error input without processing', () => {
1289
1289
  const tjsSource = `
1290
- function double(x: 0) -! 0 {
1290
+ function double(x: 0):! 0 {
1291
1291
  return x * 2
1292
1292
  }
1293
1293
  `
@@ -1305,7 +1305,7 @@ function double(x: 0) -! 0 {
1305
1305
 
1306
1306
  it('passes through error in multi-param function', () => {
1307
1307
  const tjsSource = `
1308
- function add(a: 0, b: 0) -! 0 {
1308
+ function add(a: 0, b: 0):! 0 {
1309
1309
  return a + b
1310
1310
  }
1311
1311
  `
@@ -1323,15 +1323,15 @@ function add(a: 0, b: 0) -! 0 {
1323
1323
 
1324
1324
  it('propagates error through function chain', () => {
1325
1325
  const tjsSource = `
1326
- function step1(x: 0) -! 0 {
1326
+ function step1(x: 0):! 0 {
1327
1327
  return x * 2
1328
1328
  }
1329
1329
 
1330
- function step2(x: 0) -! 0 {
1330
+ function step2(x: 0):! 0 {
1331
1331
  return x + 10
1332
1332
  }
1333
1333
 
1334
- function step3(x: 0) -! 0 {
1334
+ function step3(x: 0):! 0 {
1335
1335
  return x / 2
1336
1336
  }
1337
1337
  `
@@ -1350,7 +1350,7 @@ function step3(x: 0) -! 0 {
1350
1350
  describe('type error emission', () => {
1351
1351
  it('returns MonadicError on type mismatch', () => {
1352
1352
  const tjsSource = `
1353
- function greet(name: '') -! '' {
1353
+ function greet(name: ''):! '' {
1354
1354
  return 'Hello, ' + name
1355
1355
  }
1356
1356
  `
@@ -1370,7 +1370,7 @@ function greet(name: '') -! '' {
1370
1370
 
1371
1371
  it('includes path for nested params', () => {
1372
1372
  const tjsSource = `
1373
- function process({ name: '', age: 0 }) -! '' {
1373
+ function process({ name: '', age: 0 }):! '' {
1374
1374
  return name + ' is ' + age
1375
1375
  }
1376
1376
  `
@@ -1387,7 +1387,7 @@ function process({ name: '', age: 0 }) -! '' {
1387
1387
 
1388
1388
  it('user code cannot accidentally process error as data', () => {
1389
1389
  const tjsSource = `
1390
- function getData(id: 0) -! { value: 0 } {
1390
+ function getData(id: 0):! { value: 0 } {
1391
1391
  return { value: id * 10 }
1392
1392
  }
1393
1393
  `
@@ -1411,7 +1411,7 @@ function getData(id: 0) -! { value: 0 } {
1411
1411
  describe('error vs valid value distinction', () => {
1412
1412
  it('valid values pass through normally', () => {
1413
1413
  const tjsSource = `
1414
- function double(x: 0) -! 0 {
1414
+ function double(x: 0):! 0 {
1415
1415
  return x * 2
1416
1416
  }
1417
1417
  `
@@ -1425,7 +1425,7 @@ function double(x: 0) -! 0 {
1425
1425
 
1426
1426
  it('distinguishes Error from error-like objects', () => {
1427
1427
  const tjsSource = `
1428
- function process(data: { error: false }) -! { error: false } {
1428
+ function process(data: { error: false }):! { error: false } {
1429
1429
  return data
1430
1430
  }
1431
1431
  `
@@ -1451,7 +1451,7 @@ function process(data: { error: false }) -! { error: false } {
1451
1451
  describe('unsafe functions skip validation entirely', () => {
1452
1452
  it('unsafe function skips all validation including error pass-through', () => {
1453
1453
  const tjsSource = `
1454
- function fastDouble(! x: 0) -! 0 {
1454
+ function fastDouble(! x: 0):! 0 {
1455
1455
  return x * 2
1456
1456
  }
1457
1457
  `
@@ -1473,7 +1473,7 @@ function fastDouble(! x: 0) -! 0 {
1473
1473
  describe('source location tracking', () => {
1474
1474
  it('error includes source file and line (no debug mode)', () => {
1475
1475
  const tjsSource = `
1476
- function greet(name: '') -! '' {
1476
+ function greet(name: ''):! '' {
1477
1477
  return 'Hello, ' + name
1478
1478
  }
1479
1479
  `
@@ -1510,8 +1510,10 @@ function transform(value: string): string {
1510
1510
  filename: 'src/processors/data.ts',
1511
1511
  })
1512
1512
 
1513
- // TJS → JS
1514
- const { code: jsCode } = tjs(tjsCode)
1513
+ // TJS → JS — inject safety directive after the tjs annotation line
1514
+ const { code: jsCode } = tjs(
1515
+ tjsCode.replace(/(\/\* tjs <- [^*]+ \*\/)/, '$1\nsafety inputs')
1516
+ )
1515
1517
 
1516
1518
  // Execute and trigger errors
1517
1519
  const fns = new Function(
@@ -1538,9 +1540,10 @@ function transform(value: string): string {
1538
1540
  it('preserves line annotations through TJS intermediate', () => {
1539
1541
  // TJS with explicit line annotations (as if from TS transpilation)
1540
1542
  const tjsSource = `/* tjs <- lib/utils.ts */
1543
+ safety inputs
1541
1544
 
1542
1545
  /* line 15 */
1543
- function helper(x: 0) -! 0 {
1546
+ function helper(x: 0):! 0 {
1544
1547
  return x + 1
1545
1548
  }
1546
1549
  `
@@ -1555,34 +1558,35 @@ function helper(x: 0) -! 0 {
1555
1558
  expect(err.path).toBe('lib/utils.ts:15:helper.x')
1556
1559
  })
1557
1560
 
1558
- it('captures TJS call stack in debug mode', () => {
1561
+ it('captures TJS call stack with callStacks enabled', () => {
1559
1562
  const { configure } = require('./runtime')
1560
1563
 
1561
- // Enable debug mode
1562
- configure({ debug: true })
1564
+ // Enable call stack tracking
1565
+ configure({ callStacks: true })
1563
1566
 
1564
1567
  try {
1565
1568
  const tjsSource = `/* tjs <- src/chain.ts */
1569
+ safety inputs
1566
1570
 
1567
1571
  /* line 10 */
1568
- function outer(x: 0) -! 0 {
1572
+ function outer(x: 0):! 0 {
1569
1573
  return middle(x * 2)
1570
1574
  }
1571
1575
 
1572
1576
  /* line 20 */
1573
- function middle(x: 0) -! 0 {
1577
+ function middle(x: 0):! 0 {
1574
1578
  return inner(x + 10)
1575
1579
  }
1576
1580
 
1577
1581
  /* line 30 */
1578
- function inner(x: '') -! '' {
1582
+ function inner(x: ''):! '' {
1579
1583
  return x.toUpperCase()
1580
1584
  }
1581
1585
  `
1582
1586
  const { code } = tjs(tjsSource)
1583
1587
  const fns = new Function(code + '; return { outer, middle, inner }')()
1584
1588
 
1585
- // outer(5) -> middle(10) -> inner(20) fails (20 is not a string)
1589
+ // outer(5): middle(10): inner(20) fails (20 is not a string)
1586
1590
  const err = fns.outer(5)
1587
1591
 
1588
1592
  expect(err).toBeInstanceOf(MonadicError)
@@ -1594,8 +1598,7 @@ function inner(x: '') -! '' {
1594
1598
  expect(err.callStack).toContain('src/chain.ts:20:middle')
1595
1599
  expect(err.callStack).toContain('src/chain.ts:30:inner')
1596
1600
  } finally {
1597
- // Reset debug mode
1598
- configure({ debug: false })
1601
+ configure({ debug: false, callStacks: false })
1599
1602
  }
1600
1603
  })
1601
1604
 
@@ -1606,7 +1609,7 @@ function inner(x: '') -! '' {
1606
1609
  configure({ debug: false })
1607
1610
 
1608
1611
  const tjsSource = `
1609
- function test(x: 0) -! 0 {
1612
+ function test(x: 0):! 0 {
1610
1613
  return x * 2
1611
1614
  }
1612
1615
  `
@@ -1628,7 +1631,7 @@ function test(x: 0) -! 0 {
1628
1631
 
1629
1632
  try {
1630
1633
  const { code } = tjs(`
1631
- function add(a: 0, b: 0) -! 0 {
1634
+ function add(a: 0, b: 0):! 0 {
1632
1635
  return a + b
1633
1636
  }
1634
1637
  `)
@@ -1652,12 +1655,13 @@ function add(a: 0, b: 0) -! 0 {
1652
1655
 
1653
1656
  try {
1654
1657
  const { code } = tjs(`/* tjs <- src/app.ts */
1658
+ safety inputs
1655
1659
  /* line 1 */
1656
- function outer(x: 0) -! 0 {
1660
+ function outer(x: 0):! 0 {
1657
1661
  return inner(x)
1658
1662
  }
1659
1663
  /* line 5 */
1660
- function inner(x: '') -! '' {
1664
+ function inner(x: ''):! '' {
1661
1665
  return x.toUpperCase()
1662
1666
  }
1663
1667
  `)
@@ -1679,7 +1683,7 @@ function inner(x: '') -! '' {
1679
1683
 
1680
1684
  try {
1681
1685
  const { code } = tjs(`
1682
- function greet(name: '') -! '' {
1686
+ function greet(name: ''):! '' {
1683
1687
  return 'Hello, ' + name
1684
1688
  }
1685
1689
  `)
@@ -1710,14 +1714,15 @@ function greet(name: '') -! '' {
1710
1714
 
1711
1715
  try {
1712
1716
  const { code } = tjs(`/* tjs <- src/pipeline.ts */
1717
+ safety inputs
1713
1718
  /* line 1 */
1714
- function a(x: 0) -! 0 { return b(x) }
1719
+ function a(x: 0):! 0 { return b(x) }
1715
1720
  /* line 3 */
1716
- function b(x: 0) -! 0 { return c(x) }
1721
+ function b(x: 0):! 0 { return c(x) }
1717
1722
  /* line 5 */
1718
- function c(x: 0) -! 0 { return d(x) }
1723
+ function c(x: 0):! 0 { return d(x) }
1719
1724
  /* line 7 */
1720
- function d(x: '') -! '' { return x.toUpperCase() }
1725
+ function d(x: ''):! '' { return x.toUpperCase() }
1721
1726
  `)
1722
1727
  const fns = new Function(code + '; return { a, b, c, d }')()
1723
1728
  const err = fns.a(99)
@@ -1741,11 +1746,11 @@ function d(x: '') -! '' { return x.toUpperCase() }
1741
1746
  describe('input-side error propagation', () => {
1742
1747
  it('error from inner function caught by outer input check', () => {
1743
1748
  const { code } = tjs(`
1744
- function step1(x: '') -! '' {
1749
+ function step1(x: ''):! '' {
1745
1750
  return x.toUpperCase()
1746
1751
  }
1747
1752
 
1748
- function step2(x: '') -! '' {
1753
+ function step2(x: ''):! '' {
1749
1754
  return x + '!'
1750
1755
  }
1751
1756
  `)
@@ -1761,9 +1766,9 @@ function step2(x: '') -! '' {
1761
1766
 
1762
1767
  it('error identity preserved through chain', () => {
1763
1768
  const { code } = tjs(`
1764
- function a(x: '') -! '' { return x }
1765
- function b(x: '') -! '' { return x }
1766
- function c(x: '') -! '' { return x }
1769
+ function a(x: ''):! '' { return x }
1770
+ function b(x: ''):! '' { return x }
1771
+ function c(x: ''):! '' { return x }
1767
1772
  `)
1768
1773
  const fns = new Function(code + '; return { a, b, c }')()
1769
1774
 
@@ -1781,9 +1786,9 @@ function c(x: '') -! '' { return x }
1781
1786
 
1782
1787
  it('multi-level nested call propagation', () => {
1783
1788
  const { code } = tjs(`
1784
- function validate(x: '') -! '' { return x }
1785
- function transform(x: '') -! '' { return x.toUpperCase() }
1786
- function format(x: '') -! '' { return x + '!' }
1789
+ function validate(x: ''):! '' { return x }
1790
+ function transform(x: ''):! '' { return x.toUpperCase() }
1791
+ function format(x: ''):! '' { return x + '!' }
1787
1792
  `)
1788
1793
  const fns = new Function(
1789
1794
  code + '; return { validate, transform, format }'
@@ -1801,7 +1806,7 @@ function format(x: '') -! '' { return x + '!' }
1801
1806
  const bodyExecuted = false
1802
1807
 
1803
1808
  const { code } = tjs(`
1804
- function process(x: '') -! '' {
1809
+ function process(x: ''):! '' {
1805
1810
  globalThis.__test_body_ran = true
1806
1811
  return x.toUpperCase()
1807
1812
  }
@@ -1828,7 +1833,7 @@ function process(x: '') -! '' {
1828
1833
  describe('return type default keys', () => {
1829
1834
  it('signature test passes when optional key is absent', () => {
1830
1835
  const result = tjs(`
1831
- function divide(a: 10, b: 2) -> { value: 5, error = '' } {
1836
+ function divide(a: 10, b: 2): { value: 5, error = '' } {
1832
1837
  return { value: a / b }
1833
1838
  }
1834
1839
  `)
@@ -1839,7 +1844,7 @@ function divide(a: 10, b: 2) -> { value: 5, error = '' } {
1839
1844
 
1840
1845
  it('signature test passes when optional key is present', () => {
1841
1846
  const result = tjs(`
1842
- function divide(a: 10, b: 0) -> { value: 0, error = 'Division by zero' } {
1847
+ function divide(a: 10, b: 0): { value: 0, error = 'Division by zero' } {
1843
1848
  if (b === 0) return { value: 0, error: 'Division by zero' }
1844
1849
  return { value: a / b }
1845
1850
  }
@@ -1851,7 +1856,7 @@ function divide(a: 10, b: 0) -> { value: 0, error = 'Division by zero' } {
1851
1856
 
1852
1857
  it('works with non-string defaults', () => {
1853
1858
  const result = tjs(`
1854
- function lookup(key: 'x') -> { value: 'found', count = 0 } {
1859
+ function lookup(key: 'x'): { value: 'found', count = 0 } {
1855
1860
  return { value: 'found' }
1856
1861
  }
1857
1862
  `)
@@ -1864,7 +1869,7 @@ function lookup(key: 'x') -> { value: 'found', count = 0 } {
1864
1869
  // Transpiler throws on signature test failure, so we catch it
1865
1870
  expect(() =>
1866
1871
  tjs(`
1867
- function broken(x: 0) -> { value: 0, error = '' } {
1872
+ function broken(x: 0): { value: 0, error = '' } {
1868
1873
  return { error: 'oops' }
1869
1874
  }
1870
1875
  `)
@@ -1873,7 +1878,7 @@ function broken(x: 0) -> { value: 0, error = '' } {
1873
1878
 
1874
1879
  it('inline tests can check default keys', () => {
1875
1880
  const result = tjs(`
1876
- function divide(a: 10, b: 2) -> { value: 5, error = '' } {
1881
+ function divide(a: 10, b: 2): { value: 5, error = '' } {
1877
1882
  if (b === 0) return { value: NaN, error: 'Division by zero' }
1878
1883
  return { value: a / b }
1879
1884
  }
@@ -1895,7 +1900,7 @@ test 'normal division works' {
1895
1900
 
1896
1901
  it('type metadata parses return type with defaults', () => {
1897
1902
  const result = tjs(`
1898
- function divide(a: 10, b: 2) -> { value: 5, error = '' } {
1903
+ function divide(a: 10, b: 2): { value: 5, error = '' } {
1899
1904
  return { value: a / b }
1900
1905
  }
1901
1906
  `)
@@ -1908,7 +1913,7 @@ function divide(a: 10, b: 2) -> { value: 5, error = '' } {
1908
1913
 
1909
1914
  it('-? runtime validation passes when optional key is absent', () => {
1910
1915
  const result = tjs(`
1911
- function divide(a: 10, b: 2) -? { value: 5, error = '' } {
1916
+ function divide(a: 10, b: 2):? { value: 5, error = '' } {
1912
1917
  return { value: a / b }
1913
1918
  }
1914
1919
  `)
@@ -1923,7 +1928,7 @@ function divide(a: 10, b: 2) -? { value: 5, error = '' } {
1923
1928
  it('-? with simple return type rejects wrong type at runtime', () => {
1924
1929
  // Use a simple return type (string) where checkType works
1925
1930
  const result = tjs(`
1926
- function greet(name: 'World') -? 'Hello, World' {
1931
+ function greet(name: 'World'):? 'Hello, World' {
1927
1932
  return 'Hello, ' + name
1928
1933
  }
1929
1934
  `)
@@ -1937,7 +1942,7 @@ function greet(name: 'World') -? 'Hello, World' {
1937
1942
 
1938
1943
  it('__tjs metadata includes return defaults', () => {
1939
1944
  const result = tjs(`
1940
- function divide(a: 10, b: 2) -? { value: 5, error = '' } {
1945
+ function divide(a: 10, b: 2):? { value: 5, error = '' } {
1941
1946
  return { value: a / b }
1942
1947
  }
1943
1948
  `)
@@ -2046,7 +2051,7 @@ describe('TS overloads → TJS → JS full pipeline', () => {
2046
2051
 
2047
2052
  describe('rest parameter metadata', () => {
2048
2053
  it('should capture typed rest param in metadata', () => {
2049
- const result = tjs(`function sum(...nums: [0]) -> 0 { return 0 }`, {
2054
+ const result = tjs(`function sum(...nums: [0]): 0 { return 0 }`, {
2050
2055
  runTests: false,
2051
2056
  })
2052
2057
  const info = result.types.sum
@@ -2058,7 +2063,7 @@ describe('rest parameter metadata', () => {
2058
2063
 
2059
2064
  it('should capture rest param with float array type', () => {
2060
2065
  const result = tjs(
2061
- `function mean(...values: [1.0, 2.0]) -> 0.0 { return 0 }`,
2066
+ `function mean(...values: [1.0, 2.0]): 0.0 { return 0 }`,
2062
2067
  { runTests: false }
2063
2068
  )
2064
2069
  const info = result.types.mean
@@ -2069,7 +2074,7 @@ describe('rest parameter metadata', () => {
2069
2074
 
2070
2075
  it('should capture heterogeneous rest param as union', () => {
2071
2076
  const result = tjs(
2072
- `function log(...args: ['hello', 42, true]) -> 0 { return 0 }`,
2077
+ `function log(...args: ['hello', 42, true]): 0 { return 0 }`,
2073
2078
  { runTests: false }
2074
2079
  )
2075
2080
  const info = result.types.log