tjs-lang 0.8.1 → 0.8.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 (78) hide show
  1. package/CLAUDE.md +13 -4
  2. package/demo/autocomplete.test.ts +37 -0
  3. package/demo/docs.json +698 -8
  4. package/demo/examples.test.ts +6 -2
  5. package/demo/src/autocomplete.ts +40 -2
  6. package/demo/src/introspection-bridge.ts +140 -0
  7. package/demo/src/introspection-doc.test.ts +63 -0
  8. package/demo/src/playground-shared.ts +101 -16
  9. package/demo/src/playground-test-results.test.ts +112 -0
  10. package/demo/src/tjs-playground.ts +32 -5
  11. package/dist/examples/modules/dist/main.d.ts +34 -0
  12. package/dist/examples/modules/dist/math.d.ts +120 -0
  13. package/dist/index.js +115 -112
  14. package/dist/index.js.map +4 -4
  15. package/dist/src/lang/core.d.ts +1 -1
  16. package/dist/src/lang/dialect.d.ts +35 -0
  17. package/dist/src/lang/emitters/ast.d.ts +1 -1
  18. package/dist/src/lang/emitters/js-tests.d.ts +7 -0
  19. package/dist/src/lang/emitters/js.d.ts +12 -0
  20. package/dist/src/lang/index.d.ts +2 -1
  21. package/dist/src/lang/parser-types.d.ts +17 -0
  22. package/dist/src/lang/parser.d.ts +18 -0
  23. package/dist/src/lang/transpiler.d.ts +1 -0
  24. package/dist/src/lang/types.d.ts +18 -0
  25. package/dist/src/vm/runtime.d.ts +18 -0
  26. package/dist/src/vm/vm.d.ts +15 -1
  27. package/dist/tjs-batteries.js +2 -2
  28. package/dist/tjs-batteries.js.map +2 -2
  29. package/dist/tjs-eval.js +43 -43
  30. package/dist/tjs-eval.js.map +3 -3
  31. package/dist/tjs-from-ts.js +1 -1
  32. package/dist/tjs-from-ts.js.map +2 -2
  33. package/dist/tjs-lang.js +76 -73
  34. package/dist/tjs-lang.js.map +4 -4
  35. package/dist/tjs-vm.js +49 -49
  36. package/dist/tjs-vm.js.map +3 -3
  37. package/editors/codemirror/ajs-language.ts +130 -44
  38. package/editors/codemirror/completion-source.test.ts +114 -0
  39. package/editors/introspect-value.test.ts +61 -0
  40. package/editors/introspect-value.ts +86 -0
  41. package/editors/scope-symbols.test.ts +113 -0
  42. package/editors/scope-symbols.ts +173 -0
  43. package/llms.txt +1 -0
  44. package/package.json +21 -1
  45. package/src/batteries/audit.ts +3 -2
  46. package/src/cli/commands/check.ts +3 -2
  47. package/src/cli/commands/emit.ts +4 -2
  48. package/src/cli/commands/run.ts +6 -2
  49. package/src/cli/commands/types.ts +2 -2
  50. package/src/lang/codegen.test.ts +4 -1
  51. package/src/lang/core.ts +6 -4
  52. package/src/lang/dialect.test.ts +63 -0
  53. package/src/lang/dialect.ts +50 -0
  54. package/src/lang/emitters/ast.ts +145 -2
  55. package/src/lang/emitters/js-tests.ts +46 -37
  56. package/src/lang/emitters/js.ts +19 -2
  57. package/src/lang/features.test.ts +6 -5
  58. package/src/lang/index.ts +40 -5
  59. package/src/lang/parser-types.ts +17 -0
  60. package/src/lang/parser.test.ts +12 -6
  61. package/src/lang/parser.ts +113 -3
  62. package/src/lang/predicate-schema.test.ts +97 -0
  63. package/src/lang/predicate-schema.ts +168 -0
  64. package/src/lang/predicate.test.ts +184 -0
  65. package/src/lang/predicate.ts +550 -0
  66. package/src/lang/subset-invariant.test.ts +90 -0
  67. package/src/lang/suggest.test.ts +84 -0
  68. package/src/lang/transpiler.ts +34 -0
  69. package/src/lang/types.ts +18 -0
  70. package/src/lang/wasm.test.ts +8 -2
  71. package/src/linalg/linalg.test.ts +8 -2
  72. package/src/use-cases/batteries.test.ts +4 -0
  73. package/src/use-cases/local-helpers.test.ts +219 -0
  74. package/src/use-cases/timeout-overrides.test.ts +169 -0
  75. package/src/vm/atom-effects.test.ts +72 -0
  76. package/src/vm/atoms/batteries.ts +29 -3
  77. package/src/vm/runtime.ts +140 -4
  78. package/src/vm/vm.ts +47 -9
package/CLAUDE.md CHANGED
@@ -86,6 +86,8 @@ bun run functions:serve # Local functions emulator
86
86
  - `src/lang/inference.ts` - Type inference from example values
87
87
  - `src/lang/json-schema.ts` - JSON Schema generation from TypeDescriptors and example values
88
88
  - `src/lang/linter.ts` - Static analysis (unused vars, unreachable code, no-explicit-new)
89
+ - `src/lang/predicate.ts` - Predicate-safety verifier: certifies a cluster of pure, synchronous, composable predicates (reads the atom `effects` tag via `effectfulFromAtoms`); verified predicates compile to native JS. Also `suggest()` — mines a cluster for autocomplete completions (keyword sets → `value`s, `startsWith` guards → open-ended `stub`s like `var(--`; mined values run through the compiled predicate so they're guaranteed valid). Exported from `tjs-lang/lang`. See `experiments/predicates/` (CSS torture set + perf + suggest demo)
90
+ - `src/lang/predicate-schema.ts` - Predicate-aware JSON-Schema: the `$predicate` keyword (computational types). `compilePredicateSchema`/`validatePredicateSchema` — structure for naive validators, `$predicate` for aware ones (progressive enhancement). The serializable-into-JSON-Schema endgame; exported from `tjs-lang/lang`
89
91
  - `src/lang/runtime.ts` - TJS runtime (monadic errors, type checking, wrapClass)
90
92
  - `src/lang/wasm.ts` - WASM compiler (opcodes, disassembler, bytecode generation; multi-function module composition; wasm-to-wasm `call <index>` resolution)
91
93
  - `src/lang/emitters/js-wasm.ts` - JS bootstrap emitter for compiled wasm modules (one `WebAssembly.compile` per file, name→export-index table, type-aware wrappers)
@@ -113,7 +115,11 @@ createAgent(source, vm) // Creates callable agent
113
115
 
114
116
  // VM
115
117
  const vm = new AgentVM(customAtoms)
116
- await vm.run(ast, args, { fuel, capabilities, timeoutMs, trace })
118
+ await vm.run(ast, args, {
119
+ fuel, capabilities, timeoutMs, trace,
120
+ costOverrides: { atomOp: 5 }, // per-atom fuel cost override
121
+ timeoutOverrides: { atomOp: 60_000 }, // per-atom wall-clock override (ms; 0 disables)
122
+ })
117
123
 
118
124
  // Builder
119
125
  Agent.take(schema).varSet(...).httpFetch(...).return(schema)
@@ -208,7 +214,7 @@ fn('a', 'b') // Returns { error: 'type mismatch', ... }
208
214
  **Design Notes:**
209
215
 
210
216
  - The two steps are intentionally separate for tree-shaking (TS support is optional)
211
- - `fromTS` lives in a separate entry point (`tosijs/lang/from-ts`)
217
+ - `fromTS` lives in a separate entry point (`tjs-lang/lang/from-ts`)
212
218
  - Import only what you need to keep bundle size minimal
213
219
  - Each step is independently testable (see `src/lang/codegen.test.ts`)
214
220
  - Constrained generics (`<T extends { id: number }>`) use the constraint as the example value instead of `any`
@@ -249,7 +255,7 @@ AJS expressions behave differently from JavaScript in several important ways:
249
255
  - Unit tests alongside source files (`*.test.ts`)
250
256
  - Integration tests in `src/use-cases/` (RAG, orchestration, malicious actors)
251
257
  - Security tests in `src/use-cases/malicious-actor.test.ts`
252
- - Language tests split across 17 files in `src/lang/` (lang.test.ts, features.test.ts, codegen.test.ts, parser.test.ts, from-ts.test.ts, wasm.test.ts, etc.)
258
+ - 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
259
  - 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
260
 
255
261
  Coverage targets: 98% lines on `src/vm/runtime.ts` (security-critical), 80%+ overall.
@@ -260,7 +266,7 @@ Coverage targets: 98% lines on `src/vm/runtime.ts` (security-critical), 80%+ ove
260
266
 
261
267
  ### Adding a New Atom
262
268
 
263
- 1. Define with `defineAtom(opCode, inputSchema, outputSchema, implementation, { cost, timeoutMs, docs })`
269
+ 1. Define with `defineAtom(opCode, inputSchema, outputSchema, implementation, { cost, timeoutMs, docs, effects })`
264
270
  2. Add to `src/vm/atoms/` and export from `src/vm/atoms/index.ts`
265
271
  3. Add tests
266
272
  4. Run `bun run test:fast`
@@ -270,6 +276,7 @@ Coverage targets: 98% lines on `src/vm/runtime.ts` (security-critical), 80%+ ove
270
276
  - `cost` can be static number or dynamic: `(input, ctx) => number`
271
277
  - `timeoutMs` defaults to 1000ms; use `0` for no timeout (e.g., `seq`)
272
278
  - Atoms are always async; fuel deduction is automatic in the `exec` wrapper
279
+ - `effects` defaults to `'pure'`; **set `'io'` for any atom that touches `ctx.capabilities` (fetch/store/llm/agent/code), is nondeterministic (random/uuid), or has side effects (console)**. This drives predicate-safety (a predicate may only call pure atoms — see `experiments/predicates/`). Core IO atoms are tagged centrally via `EFFECTFUL_CORE_OPS` in `runtime.ts`; the invariant is guarded by `src/vm/atom-effects.test.ts`.
273
280
 
274
281
  ### Debugging Agents
275
282
 
@@ -303,6 +310,7 @@ Full syntax documentation is in [`CLAUDE-TJS-SYNTAX.md`](CLAUDE-TJS-SYNTAX.md).
303
310
  - **Return types**: `function add(a: 0, b: 0): 0 { ... }` (colon syntax, same as TypeScript)
304
311
  - **Safety markers**: `!` = unsafe (skip validation), `?` = safe (explicit validation)
305
312
  - **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).
313
+ - **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
314
  - **Bang access**: `x!.foo` — returns MonadicError if `x` is null/undefined, otherwise bare `x.foo`. Chains propagate: `x!.foo!.bar`.
307
315
  - **Type/Generic/FunctionPredicate**: Three declaration forms for runtime type predicates
308
316
  - **`const!`**: Compile-time immutability, zero runtime cost
@@ -478,6 +486,7 @@ The CLI (`bun src/cli/tjs.ts run`) does NOT inject the test-block `expect` harne
478
486
 
479
487
  ### Additional Documentation
480
488
 
489
+ - `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
490
  - `llms.txt` — agent-facing navigation index (ships in npm bundle); points to docs and source entry points
482
491
  - `guides/footguns.md` — JS footguns TJS fixes (boxed-primitive truthiness, `==` coercion, `typeof null`, uninitialized `let`, etc.). Demo: `examples/js-footguns-fixed.tjs`.
483
492
  - `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.
@@ -40,6 +40,43 @@ describe('getCompletions', () => {
40
40
  })
41
41
  })
42
42
 
43
+ describe('destructured bindings (scope-aware)', () => {
44
+ // Regression: the old regex `const NAME =` extractor missed all
45
+ // destructuring, so the tosijs todo example suggested nothing.
46
+ const todo = `import { elements, tosi } from 'tosijs'
47
+ const { todoApp } = tosi({ todoApp: { items: [], newItem: '' } })
48
+ const { h1, ul, template, li, label, input, button } = elements
49
+ `
50
+
51
+ it('suggests destructured locals (todoApp, h1, button)', () => {
52
+ const completions = getCompletions({
53
+ source: todo,
54
+ position: todo.length,
55
+ })
56
+ const labels = completions.map((c) => c.label)
57
+ expect(labels).toContain('todoApp')
58
+ expect(labels).toContain('h1')
59
+ expect(labels).toContain('button')
60
+ })
61
+
62
+ it('shows where a destructured binding came from', () => {
63
+ const completions = getCompletions({
64
+ source: todo,
65
+ position: todo.length,
66
+ })
67
+ expect(completions.find((c) => c.label === 'h1')?.detail).toBe(
68
+ '∈ elements'
69
+ )
70
+ })
71
+
72
+ it('filters destructured locals by prefix', () => {
73
+ const src = todo + 'tod'
74
+ const completions = getCompletions({ source: src, position: src.length })
75
+ expect(completions.map((c) => c.label)).toContain('todoApp')
76
+ expect(completions.every((c) => c.label.startsWith('tod'))).toBe(true)
77
+ })
78
+ })
79
+
43
80
  describe('type completions', () => {
44
81
  it('should suggest types after colon', () => {
45
82
  const completions = getCompletions({