tjs-lang 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +9 -3
- package/demo/examples.test.ts +6 -2
- package/demo/src/playground-shared.ts +48 -16
- package/demo/src/playground-test-results.test.ts +112 -0
- package/demo/src/tjs-playground.ts +11 -5
- package/dist/examples/modules/dist/main.d.ts +34 -0
- package/dist/examples/modules/dist/math.d.ts +120 -0
- package/dist/index.js +115 -112
- package/dist/index.js.map +4 -4
- package/dist/src/lang/core.d.ts +1 -1
- package/dist/src/lang/dialect.d.ts +35 -0
- package/dist/src/lang/emitters/ast.d.ts +1 -1
- package/dist/src/lang/emitters/js-tests.d.ts +7 -0
- package/dist/src/lang/emitters/js.d.ts +12 -0
- package/dist/src/lang/index.d.ts +2 -1
- package/dist/src/lang/parser-types.d.ts +17 -0
- package/dist/src/lang/parser.d.ts +18 -0
- package/dist/src/lang/transpiler.d.ts +1 -0
- package/dist/src/lang/types.d.ts +18 -0
- package/dist/src/vm/runtime.d.ts +18 -0
- package/dist/src/vm/vm.d.ts +15 -1
- package/dist/tjs-batteries.js +2 -2
- package/dist/tjs-batteries.js.map +2 -2
- package/dist/tjs-eval.js +43 -43
- package/dist/tjs-eval.js.map +3 -3
- package/dist/tjs-from-ts.js +1 -1
- package/dist/tjs-from-ts.js.map +2 -2
- package/dist/tjs-lang.js +76 -73
- package/dist/tjs-lang.js.map +4 -4
- package/dist/tjs-vm.js +49 -49
- package/dist/tjs-vm.js.map +3 -3
- package/llms.txt +1 -0
- package/package.json +20 -1
- package/src/batteries/audit.ts +3 -2
- package/src/cli/commands/check.ts +3 -2
- package/src/cli/commands/emit.ts +4 -2
- package/src/cli/commands/run.ts +6 -2
- package/src/cli/commands/types.ts +2 -2
- package/src/lang/codegen.test.ts +4 -1
- package/src/lang/core.ts +6 -4
- package/src/lang/dialect.test.ts +63 -0
- package/src/lang/dialect.ts +50 -0
- package/src/lang/emitters/ast.ts +145 -2
- package/src/lang/emitters/js-tests.ts +46 -37
- package/src/lang/emitters/js.ts +19 -2
- package/src/lang/features.test.ts +6 -5
- package/src/lang/index.ts +18 -5
- package/src/lang/parser-types.ts +17 -0
- package/src/lang/parser.test.ts +12 -6
- package/src/lang/parser.ts +113 -3
- package/src/lang/subset-invariant.test.ts +90 -0
- package/src/lang/transpiler.ts +8 -0
- package/src/lang/types.ts +18 -0
- package/src/lang/wasm.test.ts +8 -2
- package/src/linalg/linalg.test.ts +8 -2
- package/src/use-cases/batteries.test.ts +4 -0
- package/src/use-cases/local-helpers.test.ts +219 -0
- package/src/use-cases/timeout-overrides.test.ts +169 -0
- package/src/vm/atoms/batteries.ts +17 -3
- package/src/vm/runtime.ts +89 -4
- package/src/vm/vm.ts +47 -9
package/CLAUDE.md
CHANGED
|
@@ -113,7 +113,11 @@ createAgent(source, vm) // Creates callable agent
|
|
|
113
113
|
|
|
114
114
|
// VM
|
|
115
115
|
const vm = new AgentVM(customAtoms)
|
|
116
|
-
await vm.run(ast, args, {
|
|
116
|
+
await vm.run(ast, args, {
|
|
117
|
+
fuel, capabilities, timeoutMs, trace,
|
|
118
|
+
costOverrides: { atomOp: 5 }, // per-atom fuel cost override
|
|
119
|
+
timeoutOverrides: { atomOp: 60_000 }, // per-atom wall-clock override (ms; 0 disables)
|
|
120
|
+
})
|
|
117
121
|
|
|
118
122
|
// Builder
|
|
119
123
|
Agent.take(schema).varSet(...).httpFetch(...).return(schema)
|
|
@@ -208,7 +212,7 @@ fn('a', 'b') // Returns { error: 'type mismatch', ... }
|
|
|
208
212
|
**Design Notes:**
|
|
209
213
|
|
|
210
214
|
- The two steps are intentionally separate for tree-shaking (TS support is optional)
|
|
211
|
-
- `fromTS` lives in a separate entry point (`
|
|
215
|
+
- `fromTS` lives in a separate entry point (`tjs-lang/lang/from-ts`)
|
|
212
216
|
- Import only what you need to keep bundle size minimal
|
|
213
217
|
- Each step is independently testable (see `src/lang/codegen.test.ts`)
|
|
214
218
|
- Constrained generics (`<T extends { id: number }>`) use the constraint as the example value instead of `any`
|
|
@@ -249,7 +253,7 @@ AJS expressions behave differently from JavaScript in several important ways:
|
|
|
249
253
|
- Unit tests alongside source files (`*.test.ts`)
|
|
250
254
|
- Integration tests in `src/use-cases/` (RAG, orchestration, malicious actors)
|
|
251
255
|
- Security tests in `src/use-cases/malicious-actor.test.ts`
|
|
252
|
-
- Language tests split across
|
|
256
|
+
- Language tests split across 18 files in `src/lang/` (lang.test.ts, features.test.ts, codegen.test.ts, parser.test.ts, from-ts.test.ts, wasm.test.ts, etc.)
|
|
253
257
|
- LLM integration tests (run via full `bun test`, skipped by `SKIP_LLM_TESTS`) need a local **LM Studio** server with a chat + embedding model loaded. Setup and the hard-won gotchas (model load failures, leaked-VRAM stray `node` worker, updating runtimes, CORS, the audit-cache parallel race) are in [`docs/lm-studio-setup.md`](docs/lm-studio-setup.md).
|
|
254
258
|
|
|
255
259
|
Coverage targets: 98% lines on `src/vm/runtime.ts` (security-critical), 80%+ overall.
|
|
@@ -303,6 +307,7 @@ Full syntax documentation is in [`CLAUDE-TJS-SYNTAX.md`](CLAUDE-TJS-SYNTAX.md).
|
|
|
303
307
|
- **Return types**: `function add(a: 0, b: 0): 0 { ... }` (colon syntax, same as TypeScript)
|
|
304
308
|
- **Safety markers**: `!` = unsafe (skip validation), `?` = safe (explicit validation)
|
|
305
309
|
- **Mode defaults**: Native TJS has all modes ON by default (`TjsEquals`, `TjsClass`, `TjsDate`, `TjsNoeval`, `TjsNoVar`, `TjsStandard`). TS-originated code (`fromTS`) and AJS/VM code get modes OFF. `TjsCompat` directive explicitly disables all modes. `TjsStrict` enables all modes (useful for TS-originated code opting in).
|
|
310
|
+
- **Source dialect (`dialect: 'js' | 'tjs'`)**: the public, programmatic way to set the modes-on/off default. `tjs(src, { dialect: 'js' })` preserves plain-JS semantics (modes OFF, `safety: 'none'`); `'tjs'` (or a bare string) is native TJS (modes ON). `dialect` is authoritative; otherwise inferred from the `fromTS` annotation / `vmTarget`. For file-based tooling, use the canonical extension→dialect helpers `dialectForFilename(filename)` / `sourceKindForFilename(filename)` from `tjs-lang/lang` (`.js`/`.mjs`/`.cjs` → `'js'`, `.tjs` → `'tjs'`, `.ts` → use `fromTS`). This upholds the **TJS ⊇ JS** invariant — see `PRINCIPLES.md`. (`.tjs` is a _better_ language, not just JS; choosing it is the opt-in to the modes.)
|
|
306
311
|
- **Bang access**: `x!.foo` — returns MonadicError if `x` is null/undefined, otherwise bare `x.foo`. Chains propagate: `x!.foo!.bar`.
|
|
307
312
|
- **Type/Generic/FunctionPredicate**: Three declaration forms for runtime type predicates
|
|
308
313
|
- **`const!`**: Compile-time immutability, zero runtime cost
|
|
@@ -478,6 +483,7 @@ The CLI (`bun src/cli/tjs.ts run`) does NOT inject the test-block `expect` harne
|
|
|
478
483
|
|
|
479
484
|
### Additional Documentation
|
|
480
485
|
|
|
486
|
+
- `PRINCIPLES.md` — **non-negotiable language invariants**: options-off TJS ⊇ JS, and TJS ⊇ AJS. A richer layer may do _more_ with the same source but must never make subset-legal code _illegal_ (e.g. un-runnable signature tests are inconclusive, not errors). Read before changing parser/transpiler acceptance or signature-test behavior; a subset violation is a bug.
|
|
481
487
|
- `llms.txt` — agent-facing navigation index (ships in npm bundle); points to docs and source entry points
|
|
482
488
|
- `guides/footguns.md` — JS footguns TJS fixes (boxed-primitive truthiness, `==` coercion, `typeof null`, uninitialized `let`, etc.). Demo: `examples/js-footguns-fixed.tjs`.
|
|
483
489
|
- `guides/playground-imports.md` — how the playground/dev-server resolves bare imports: TFS service worker, default JSDelivr `/+esm` routing, esm.sh allowlist for peer-dep packages (React), CDN hints (`jsdelivr/`, `esmsh/`, `unpkg/`, `github/`), and full-URL passthrough.
|
package/demo/examples.test.ts
CHANGED
|
@@ -448,9 +448,13 @@ describe('Playground Examples', () => {
|
|
|
448
448
|
args.imageUrl = '/test-shapes.jpg'
|
|
449
449
|
}
|
|
450
450
|
|
|
451
|
-
// Use the SAME capabilities as the playground
|
|
451
|
+
// Use the SAME capabilities as the playground.
|
|
452
|
+
// Vision inference on a local model can take a while; give it an
|
|
453
|
+
// explicit budget well above the slow-atom timeout so the test is
|
|
454
|
+
// deterministic regardless of the run-level default.
|
|
452
455
|
const runResult = await vm.run(result.ast, args, {
|
|
453
456
|
fuel: 100000,
|
|
457
|
+
timeoutMs: 180000,
|
|
454
458
|
capabilities: {
|
|
455
459
|
fetch: httpFetch,
|
|
456
460
|
llm: llmCapability || mockLLM,
|
|
@@ -466,7 +470,7 @@ describe('Playground Examples', () => {
|
|
|
466
470
|
} finally {
|
|
467
471
|
trackTestEnd()
|
|
468
472
|
}
|
|
469
|
-
},
|
|
473
|
+
}, 240000) // bun-test timeout above the vm timeoutMs (180s) + overhead
|
|
470
474
|
} else if (needsRetry) {
|
|
471
475
|
// Examples that use runCode need retry due to LLM variability
|
|
472
476
|
it(`${example.name} - runs successfully`, async () => {
|
|
@@ -255,28 +255,36 @@ export function renderConsoleMessages(
|
|
|
255
255
|
|
|
256
256
|
/**
|
|
257
257
|
* Render test results HTML with clickable error links and editor gutter markers.
|
|
258
|
-
* Returns pass/fail counts so callers can update tab indicators.
|
|
258
|
+
* Returns pass/fail/inconclusive counts so callers can update tab indicators.
|
|
259
259
|
*/
|
|
260
260
|
export function renderTestResults(
|
|
261
261
|
tests: any[],
|
|
262
262
|
outputEl: HTMLElement,
|
|
263
263
|
editor: CodeMirror,
|
|
264
264
|
goToLine: (line: number) => void
|
|
265
|
-
): { passed: number; failed: number } {
|
|
265
|
+
): { passed: number; failed: number; inconclusive: number } {
|
|
266
266
|
if (!tests || tests.length === 0) {
|
|
267
267
|
outputEl.textContent = 'No tests defined'
|
|
268
268
|
editor.clearMarkers()
|
|
269
|
-
return { passed: 0, failed: 0 }
|
|
269
|
+
return { passed: 0, failed: 0, inconclusive: 0 }
|
|
270
270
|
}
|
|
271
271
|
|
|
272
272
|
const passed = tests.filter((t: any) => t.passed).length
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
//
|
|
276
|
-
//
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
273
|
+
// Inconclusive = the test couldn't *run* (e.g. it calls an AJS atom that
|
|
274
|
+
// doesn't exist at build time). It is neither pass nor fail — surface it as
|
|
275
|
+
// a distinct warning state and keep it out of the failure count, so legal
|
|
276
|
+
// AJS doesn't read as broken in the playground. See PRINCIPLES.md.
|
|
277
|
+
const inconclusive = tests.filter(
|
|
278
|
+
(t: any) => !t.passed && t.inconclusive
|
|
279
|
+
).length
|
|
280
|
+
const failed = tests.filter((t: any) => !t.passed && !t.inconclusive).length
|
|
281
|
+
|
|
282
|
+
// Mark only genuine FAILURES in the editor. Passing tests don't need an
|
|
283
|
+
// underline (it reads as visual noise), and an inconclusive test isn't a
|
|
284
|
+
// failure — a red squiggle would wrongly imply the code is broken.
|
|
285
|
+
const failedWithLines = tests.filter(
|
|
286
|
+
(t: any) => t.line && !t.passed && !t.inconclusive
|
|
287
|
+
)
|
|
280
288
|
if (failedWithLines.length > 0) {
|
|
281
289
|
editor.setMarkers(
|
|
282
290
|
failedWithLines.map((t: any) => ({
|
|
@@ -294,19 +302,29 @@ export function renderTestResults(
|
|
|
294
302
|
if (failed > 0) {
|
|
295
303
|
html += `, <strong class="test-failed">${failed} failed</strong>`
|
|
296
304
|
}
|
|
305
|
+
if (inconclusive > 0) {
|
|
306
|
+
html += `, <strong class="test-inconclusive">${inconclusive} inconclusive</strong>`
|
|
307
|
+
}
|
|
297
308
|
html += `</div><ul class="test-list">`
|
|
298
309
|
|
|
299
310
|
for (const test of tests) {
|
|
300
|
-
const
|
|
301
|
-
|
|
311
|
+
const state = test.passed
|
|
312
|
+
? 'pass'
|
|
313
|
+
: test.inconclusive
|
|
314
|
+
? 'inconclusive'
|
|
315
|
+
: 'fail'
|
|
316
|
+
const icon = test.passed ? '✓' : test.inconclusive ? '—' : '✗'
|
|
302
317
|
const sigBadge = test.isSignatureTest
|
|
303
318
|
? ' <span class="sig-badge">signature</span>'
|
|
304
319
|
: ''
|
|
305
320
|
const dataLine = test.line ? ` data-line="${test.line}"` : ''
|
|
306
321
|
const clickable = test.line ? ' clickable-test' : ''
|
|
307
|
-
html += `<li class="
|
|
308
|
-
if (
|
|
309
|
-
|
|
322
|
+
html += `<li class="test-${state}${clickable}"${dataLine}>${icon} ${test.description}${sigBadge}`
|
|
323
|
+
if (test.error) {
|
|
324
|
+
// Inconclusive notes are warnings (couldn't run), not failures.
|
|
325
|
+
const noteCls = test.inconclusive ? 'test-note' : 'test-error'
|
|
326
|
+
const prefix = test.inconclusive ? 'could not run — ' : ''
|
|
327
|
+
html += `<div class="${noteCls}">${prefix}${test.error}</div>`
|
|
310
328
|
}
|
|
311
329
|
html += `</li>`
|
|
312
330
|
}
|
|
@@ -327,7 +345,7 @@ export function renderTestResults(
|
|
|
327
345
|
})
|
|
328
346
|
})
|
|
329
347
|
|
|
330
|
-
return { passed, failed }
|
|
348
|
+
return { passed, failed, inconclusive }
|
|
331
349
|
}
|
|
332
350
|
|
|
333
351
|
// ---------------------------------------------------------------------------
|
|
@@ -589,6 +607,20 @@ export const sharedPlaygroundStyles: Record<string, Record<string, string>> = {
|
|
|
589
607
|
fontFamily: 'var(--font-mono, monospace)',
|
|
590
608
|
},
|
|
591
609
|
|
|
610
|
+
':host .test-inconclusive': {
|
|
611
|
+
color: '#d97706',
|
|
612
|
+
},
|
|
613
|
+
|
|
614
|
+
':host .test-note': {
|
|
615
|
+
marginLeft: '20px',
|
|
616
|
+
marginTop: '4px',
|
|
617
|
+
padding: '8px',
|
|
618
|
+
background: 'rgba(217, 119, 6, 0.1)',
|
|
619
|
+
borderRadius: '4px',
|
|
620
|
+
fontSize: '13px',
|
|
621
|
+
fontFamily: 'var(--font-mono, monospace)',
|
|
622
|
+
},
|
|
623
|
+
|
|
592
624
|
':host .clickable-test': {
|
|
593
625
|
cursor: 'pointer',
|
|
594
626
|
},
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from 'bun:test'
|
|
2
|
+
import { GlobalRegistrator } from '@happy-dom/global-registrator'
|
|
3
|
+
|
|
4
|
+
beforeAll(() => GlobalRegistrator.register())
|
|
5
|
+
afterAll(() => GlobalRegistrator.unregister())
|
|
6
|
+
|
|
7
|
+
// Imported after DOM globals exist.
|
|
8
|
+
const { renderTestResults } = await import('./playground-shared')
|
|
9
|
+
|
|
10
|
+
function fakeEditor() {
|
|
11
|
+
const calls: any[] = []
|
|
12
|
+
return {
|
|
13
|
+
setMarkers: (m: any) => calls.push({ setMarkers: m }),
|
|
14
|
+
clearMarkers: () => calls.push({ clearMarkers: true }),
|
|
15
|
+
_calls: calls,
|
|
16
|
+
} as any
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe('renderTestResults — inconclusive affordance', () => {
|
|
20
|
+
it('separates inconclusive from passed and failed', () => {
|
|
21
|
+
const out = document.createElement('div')
|
|
22
|
+
const editor = fakeEditor()
|
|
23
|
+
const counts = renderTestResults(
|
|
24
|
+
[
|
|
25
|
+
{ description: 'a', passed: true },
|
|
26
|
+
{ description: 'b', passed: false, error: 'Expected 5 got 6' },
|
|
27
|
+
{
|
|
28
|
+
description: 'c signature example',
|
|
29
|
+
passed: false,
|
|
30
|
+
inconclusive: true,
|
|
31
|
+
isSignatureTest: true,
|
|
32
|
+
error: 'httpFetch is not defined',
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
out,
|
|
36
|
+
editor,
|
|
37
|
+
() => {}
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
expect(counts).toEqual({ passed: 1, failed: 1, inconclusive: 1 })
|
|
41
|
+
|
|
42
|
+
// Summary shows all three states
|
|
43
|
+
expect(out.querySelector('.test-summary')?.textContent).toContain(
|
|
44
|
+
'1 passed'
|
|
45
|
+
)
|
|
46
|
+
expect(out.querySelector('.test-summary')?.textContent).toContain(
|
|
47
|
+
'1 failed'
|
|
48
|
+
)
|
|
49
|
+
expect(out.querySelector('.test-summary')?.textContent).toContain(
|
|
50
|
+
'1 inconclusive'
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
// The inconclusive test renders with its own class + a warning note (not
|
|
54
|
+
// a red error), with the "could not run" framing.
|
|
55
|
+
const incLi = out.querySelector('li.test-inconclusive')
|
|
56
|
+
expect(incLi).not.toBeNull()
|
|
57
|
+
expect(incLi?.textContent).toContain('—') // not the ✗ failure icon
|
|
58
|
+
expect(incLi?.querySelector('.test-note')).not.toBeNull()
|
|
59
|
+
expect(incLi?.querySelector('.test-error')).toBeNull()
|
|
60
|
+
expect(incLi?.querySelector('.test-note')?.textContent).toContain(
|
|
61
|
+
'could not run'
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
// The genuine failure keeps the red error box + ✗ icon.
|
|
65
|
+
const failLi = out.querySelector('li.test-fail')
|
|
66
|
+
expect(failLi?.textContent).toContain('✗')
|
|
67
|
+
expect(failLi?.querySelector('.test-error')).not.toBeNull()
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('renders REAL transpiler output: atom-calling agent → inconclusive', async () => {
|
|
71
|
+
// End-to-end: the transpiler emits inconclusive for an un-runnable
|
|
72
|
+
// signature test; the playground must render it as such (not a failure).
|
|
73
|
+
const { tjs } = await import('../../src/lang')
|
|
74
|
+
const { testResults } = tjs(
|
|
75
|
+
`function main(url: ''): { x: '' } {\n const x = httpFetch({ url })\n return { x }\n}`
|
|
76
|
+
)
|
|
77
|
+
const out = document.createElement('div')
|
|
78
|
+
const counts = renderTestResults(
|
|
79
|
+
testResults as any,
|
|
80
|
+
out,
|
|
81
|
+
fakeEditor(),
|
|
82
|
+
() => {}
|
|
83
|
+
)
|
|
84
|
+
expect(counts.failed).toBe(0)
|
|
85
|
+
expect(counts.inconclusive).toBe(1)
|
|
86
|
+
expect(out.querySelector('li.test-inconclusive')).not.toBeNull()
|
|
87
|
+
expect(out.querySelector('li.test-fail')).toBeNull()
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('does NOT put an error marker on an inconclusive test line', () => {
|
|
91
|
+
const out = document.createElement('div')
|
|
92
|
+
const editor = fakeEditor()
|
|
93
|
+
renderTestResults(
|
|
94
|
+
[
|
|
95
|
+
{
|
|
96
|
+
description: 'sig',
|
|
97
|
+
passed: false,
|
|
98
|
+
inconclusive: true,
|
|
99
|
+
line: 3,
|
|
100
|
+
error: 'atom not defined',
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
out,
|
|
104
|
+
editor,
|
|
105
|
+
() => {}
|
|
106
|
+
)
|
|
107
|
+
// No setMarkers with an error for the inconclusive line; clearMarkers instead.
|
|
108
|
+
const markerCalls = editor._calls.filter((c: any) => c.setMarkers)
|
|
109
|
+
expect(markerCalls).toHaveLength(0)
|
|
110
|
+
expect(editor._calls.some((c: any) => c.clearMarkers)).toBe(true)
|
|
111
|
+
})
|
|
112
|
+
})
|
|
@@ -149,7 +149,7 @@ export class TJSPlayground extends Component<TJSPlaygroundParts> {
|
|
|
149
149
|
// Split mode
|
|
150
150
|
private _splitMode: null | 'code' | 'output' = null
|
|
151
151
|
private _splitChannel: BroadcastChannel | null = null
|
|
152
|
-
private _splitSessionId
|
|
152
|
+
private _splitSessionId = ''
|
|
153
153
|
|
|
154
154
|
// Build flags state
|
|
155
155
|
private buildFlags = {
|
|
@@ -860,21 +860,27 @@ export class TJSPlayground extends Component<TJSPlaygroundParts> {
|
|
|
860
860
|
|
|
861
861
|
private updateTestResults(result: any) {
|
|
862
862
|
const tests = result.testResults
|
|
863
|
-
const { passed, failed } = renderTestResults(
|
|
863
|
+
const { passed, failed, inconclusive } = renderTestResults(
|
|
864
864
|
tests,
|
|
865
865
|
this.parts.testsOutput,
|
|
866
866
|
this.parts.tjsEditor,
|
|
867
867
|
(line) => this.goToSourceLine(line)
|
|
868
868
|
)
|
|
869
|
-
this.updateTestsTabLabel(passed, failed)
|
|
869
|
+
this.updateTestsTabLabel(passed, failed, inconclusive)
|
|
870
870
|
}
|
|
871
871
|
|
|
872
|
-
private updateTestsTabLabel(
|
|
872
|
+
private updateTestsTabLabel(
|
|
873
|
+
passed: number,
|
|
874
|
+
failed: number,
|
|
875
|
+
inconclusive = 0
|
|
876
|
+
) {
|
|
873
877
|
const tabs = this.parts.outputTabs
|
|
874
878
|
if (!tabs) return
|
|
875
879
|
|
|
876
880
|
if (failed > 0) {
|
|
877
881
|
tabs.style.setProperty('--test-indicator-color', '#dc2626')
|
|
882
|
+
} else if (inconclusive > 0) {
|
|
883
|
+
tabs.style.setProperty('--test-indicator-color', '#d97706')
|
|
878
884
|
} else if (passed > 0) {
|
|
879
885
|
tabs.style.setProperty('--test-indicator-color', '#16a34a')
|
|
880
886
|
} else {
|
|
@@ -1394,7 +1400,7 @@ export class TJSPlayground extends Component<TJSPlaygroundParts> {
|
|
|
1394
1400
|
}
|
|
1395
1401
|
|
|
1396
1402
|
// Navigate to a specific line in the source editor
|
|
1397
|
-
goToSourceLine(line: number, column
|
|
1403
|
+
goToSourceLine(line: number, column = 1) {
|
|
1398
1404
|
this.parts.inputTabs.value = 0 // Switch to TJS tab (first tab)
|
|
1399
1405
|
// Wait for tab switch and editor resize before scrolling
|
|
1400
1406
|
setTimeout(() => {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export function calculate(x?: number, y?: number): any;
|
|
2
|
+
export namespace calculate {
|
|
3
|
+
namespace __tjs {
|
|
4
|
+
namespace params {
|
|
5
|
+
namespace x {
|
|
6
|
+
export namespace type {
|
|
7
|
+
let kind: string;
|
|
8
|
+
}
|
|
9
|
+
export let required: boolean;
|
|
10
|
+
let _default: null;
|
|
11
|
+
export { _default as default };
|
|
12
|
+
}
|
|
13
|
+
namespace y {
|
|
14
|
+
export namespace type_1 {
|
|
15
|
+
let kind_1: string;
|
|
16
|
+
export { kind_1 as kind };
|
|
17
|
+
}
|
|
18
|
+
export { type_1 as type };
|
|
19
|
+
let required_1: boolean;
|
|
20
|
+
export { required_1 as required };
|
|
21
|
+
let _default_1: null;
|
|
22
|
+
export { _default_1 as default };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
namespace returns {
|
|
26
|
+
export namespace type_2 {
|
|
27
|
+
let kind_2: string;
|
|
28
|
+
export { kind_2 as kind };
|
|
29
|
+
}
|
|
30
|
+
export { type_2 as type };
|
|
31
|
+
}
|
|
32
|
+
let source: string;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
export function add(a?: number, b?: number): any;
|
|
2
|
+
export namespace add {
|
|
3
|
+
namespace __tjs {
|
|
4
|
+
namespace params {
|
|
5
|
+
namespace a {
|
|
6
|
+
export namespace type {
|
|
7
|
+
let kind: string;
|
|
8
|
+
}
|
|
9
|
+
export let required: boolean;
|
|
10
|
+
let _default: null;
|
|
11
|
+
export { _default as default };
|
|
12
|
+
}
|
|
13
|
+
namespace b {
|
|
14
|
+
export namespace type_1 {
|
|
15
|
+
let kind_1: string;
|
|
16
|
+
export { kind_1 as kind };
|
|
17
|
+
}
|
|
18
|
+
export { type_1 as type };
|
|
19
|
+
let required_1: boolean;
|
|
20
|
+
export { required_1 as required };
|
|
21
|
+
let _default_1: null;
|
|
22
|
+
export { _default_1 as default };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
namespace returns {
|
|
26
|
+
export namespace type_2 {
|
|
27
|
+
let kind_2: string;
|
|
28
|
+
export { kind_2 as kind };
|
|
29
|
+
}
|
|
30
|
+
export { type_2 as type };
|
|
31
|
+
}
|
|
32
|
+
let source: string;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function subtract(a?: number, b?: number): any;
|
|
36
|
+
export namespace subtract {
|
|
37
|
+
export namespace __tjs_1 {
|
|
38
|
+
export namespace params_1 {
|
|
39
|
+
export namespace a_1 {
|
|
40
|
+
export namespace type_3 {
|
|
41
|
+
let kind_3: string;
|
|
42
|
+
export { kind_3 as kind };
|
|
43
|
+
}
|
|
44
|
+
export { type_3 as type };
|
|
45
|
+
let required_2: boolean;
|
|
46
|
+
export { required_2 as required };
|
|
47
|
+
let _default_2: null;
|
|
48
|
+
export { _default_2 as default };
|
|
49
|
+
}
|
|
50
|
+
export { a_1 as a };
|
|
51
|
+
export namespace b_1 {
|
|
52
|
+
export namespace type_4 {
|
|
53
|
+
let kind_4: string;
|
|
54
|
+
export { kind_4 as kind };
|
|
55
|
+
}
|
|
56
|
+
export { type_4 as type };
|
|
57
|
+
let required_3: boolean;
|
|
58
|
+
export { required_3 as required };
|
|
59
|
+
let _default_3: null;
|
|
60
|
+
export { _default_3 as default };
|
|
61
|
+
}
|
|
62
|
+
export { b_1 as b };
|
|
63
|
+
}
|
|
64
|
+
export { params_1 as params };
|
|
65
|
+
export namespace returns_1 {
|
|
66
|
+
export namespace type_5 {
|
|
67
|
+
let kind_5: string;
|
|
68
|
+
export { kind_5 as kind };
|
|
69
|
+
}
|
|
70
|
+
export { type_5 as type };
|
|
71
|
+
}
|
|
72
|
+
export { returns_1 as returns };
|
|
73
|
+
let source_1: string;
|
|
74
|
+
export { source_1 as source };
|
|
75
|
+
}
|
|
76
|
+
export { __tjs_1 as __tjs };
|
|
77
|
+
}
|
|
78
|
+
export function multiply(a?: number, b?: number): any;
|
|
79
|
+
export namespace multiply {
|
|
80
|
+
export namespace __tjs_2 {
|
|
81
|
+
export namespace params_2 {
|
|
82
|
+
export namespace a_2 {
|
|
83
|
+
export namespace type_6 {
|
|
84
|
+
let kind_6: string;
|
|
85
|
+
export { kind_6 as kind };
|
|
86
|
+
}
|
|
87
|
+
export { type_6 as type };
|
|
88
|
+
let required_4: boolean;
|
|
89
|
+
export { required_4 as required };
|
|
90
|
+
let _default_4: null;
|
|
91
|
+
export { _default_4 as default };
|
|
92
|
+
}
|
|
93
|
+
export { a_2 as a };
|
|
94
|
+
export namespace b_2 {
|
|
95
|
+
export namespace type_7 {
|
|
96
|
+
let kind_7: string;
|
|
97
|
+
export { kind_7 as kind };
|
|
98
|
+
}
|
|
99
|
+
export { type_7 as type };
|
|
100
|
+
let required_5: boolean;
|
|
101
|
+
export { required_5 as required };
|
|
102
|
+
let _default_5: null;
|
|
103
|
+
export { _default_5 as default };
|
|
104
|
+
}
|
|
105
|
+
export { b_2 as b };
|
|
106
|
+
}
|
|
107
|
+
export { params_2 as params };
|
|
108
|
+
export namespace returns_2 {
|
|
109
|
+
export namespace type_8 {
|
|
110
|
+
let kind_8: string;
|
|
111
|
+
export { kind_8 as kind };
|
|
112
|
+
}
|
|
113
|
+
export { type_8 as type };
|
|
114
|
+
}
|
|
115
|
+
export { returns_2 as returns };
|
|
116
|
+
let source_2: string;
|
|
117
|
+
export { source_2 as source };
|
|
118
|
+
}
|
|
119
|
+
export { __tjs_2 as __tjs };
|
|
120
|
+
}
|