tjs-lang 0.8.2 → 0.8.4

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 (45) hide show
  1. package/CLAUDE.md +5 -2
  2. package/demo/autocomplete.test.ts +37 -0
  3. package/demo/docs.json +701 -11
  4. package/demo/src/autocomplete.ts +40 -2
  5. package/demo/src/introspection-bridge.ts +140 -0
  6. package/demo/src/introspection-doc.test.ts +63 -0
  7. package/demo/src/playground-shared.ts +53 -0
  8. package/demo/src/tjs-playground.ts +21 -0
  9. package/dist/experiments/predicates/css.predicates.d.ts +13 -0
  10. package/dist/experiments/predicates/verify.d.ts +39 -0
  11. package/dist/index.js +101 -97
  12. package/dist/index.js.map +4 -4
  13. package/dist/src/lang/index.d.ts +2 -0
  14. package/dist/src/lang/predicate-schema.d.ts +39 -0
  15. package/dist/src/lang/predicate.d.ts +103 -0
  16. package/dist/src/lang/transpiler.d.ts +2 -0
  17. package/dist/src/vm/runtime.d.ts +22 -0
  18. package/dist/tjs-eval.js +29 -29
  19. package/dist/tjs-eval.js.map +3 -3
  20. package/dist/tjs-from-ts.js +1 -1
  21. package/dist/tjs-from-ts.js.map +2 -2
  22. package/dist/tjs-lang.js +86 -82
  23. package/dist/tjs-lang.js.map +4 -4
  24. package/dist/tjs-vm.js +45 -45
  25. package/dist/tjs-vm.js.map +3 -3
  26. package/editors/codemirror/ajs-language.ts +130 -44
  27. package/editors/codemirror/completion-source.test.ts +114 -0
  28. package/editors/introspect-value.test.ts +61 -0
  29. package/editors/introspect-value.ts +86 -0
  30. package/editors/scope-symbols.test.ts +113 -0
  31. package/editors/scope-symbols.ts +173 -0
  32. package/package.json +2 -1
  33. package/src/lang/index.ts +22 -0
  34. package/src/lang/predicate-schema.test.ts +97 -0
  35. package/src/lang/predicate-schema.ts +168 -0
  36. package/src/lang/predicate.test.ts +184 -0
  37. package/src/lang/predicate.ts +550 -0
  38. package/src/lang/runtime.test.ts +46 -0
  39. package/src/lang/suggest.test.ts +84 -0
  40. package/src/lang/transpiler.ts +26 -0
  41. package/src/runtime.test.ts +6 -6
  42. package/src/vm/atom-effects.test.ts +72 -0
  43. package/src/vm/atoms/batteries.ts +12 -0
  44. package/src/vm/equality.test.ts +59 -0
  45. package/src/vm/runtime.ts +77 -59
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)
@@ -264,7 +266,7 @@ Coverage targets: 98% lines on `src/vm/runtime.ts` (security-critical), 80%+ ove
264
266
 
265
267
  ### Adding a New Atom
266
268
 
267
- 1. Define with `defineAtom(opCode, inputSchema, outputSchema, implementation, { cost, timeoutMs, docs })`
269
+ 1. Define with `defineAtom(opCode, inputSchema, outputSchema, implementation, { cost, timeoutMs, docs, effects })`
268
270
  2. Add to `src/vm/atoms/` and export from `src/vm/atoms/index.ts`
269
271
  3. Add tests
270
272
  4. Run `bun run test:fast`
@@ -274,6 +276,7 @@ Coverage targets: 98% lines on `src/vm/runtime.ts` (security-critical), 80%+ ove
274
276
  - `cost` can be static number or dynamic: `(input, ctx) => number`
275
277
  - `timeoutMs` defaults to 1000ms; use `0` for no timeout (e.g., `seq`)
276
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`.
277
280
 
278
281
  ### Debugging Agents
279
282
 
@@ -311,7 +314,7 @@ Full syntax documentation is in [`CLAUDE-TJS-SYNTAX.md`](CLAUDE-TJS-SYNTAX.md).
311
314
  - **Bang access**: `x!.foo` — returns MonadicError if `x` is null/undefined, otherwise bare `x.foo`. Chains propagate: `x!.foo!.bar`.
312
315
  - **Type/Generic/FunctionPredicate**: Three declaration forms for runtime type predicates
313
316
  - **`const!`**: Compile-time immutability, zero runtime cost
314
- - **Equality**: `==`/`!=` = structural equality by default in native TJS (via `Is`/`IsNot`), `===`/`!==` = identity
317
+ - **Equality**: `==`/`!=` in native TJS (via `Eq`/`NotEq` under `TjsEquals`) are **footgun-free `===`** — they unwrap boxed primitives (`new Boolean(false) == false`) and treat `null`/`undefined` as equal, but do **NOT** coerce types (`'5' != 5`, `'' != false`) and are **NOT structural** (distinct objects/arrays are distinct: `{a:1} != {a:1}` — a real distinction, and structural `==` would be a silent O(n) hit). For deep structural comparison use the `Is`/`IsNot` function (or a type's `.Equals` hook). `===`/`!==` = strict identity.
315
318
  - **Polymorphic functions**: Multiple same-name declarations merge into arity/type dispatcher
316
319
  - **`extend` blocks**: Local class extensions without prototype pollution
317
320
  - **WASM blocks**: Inline WebAssembly compiled at transpile time, with SIMD intrinsics and `wasmBuffer()` zero-copy arrays
@@ -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({