tjs-lang 0.7.5 → 0.7.7
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 +33 -15
- package/demo/docs.json +4 -4
- package/dist/index.js +110 -106
- package/dist/index.js.map +3 -3
- package/dist/src/lang/parser.d.ts +6 -0
- package/dist/tjs-eval.js +37 -34
- package/dist/tjs-eval.js.map +3 -3
- package/dist/tjs-from-ts.js +1 -1
- package/dist/tjs-from-ts.js.map +1 -1
- package/dist/tjs-lang.js +76 -72
- package/dist/tjs-lang.js.map +3 -3
- package/dist/tjs-vm.js +44 -41
- package/dist/tjs-vm.js.map +3 -3
- package/package.json +1 -1
- package/src/lang/codegen.test.ts +40 -0
- package/src/lang/emitters/js-tests.ts +7 -0
- package/src/lang/emitters/js.ts +26 -0
- package/src/lang/features.test.ts +22 -0
- package/src/lang/parser-transforms.ts +238 -7
- package/src/lang/parser.ts +51 -0
- package/src/lang/tests.ts +21 -8
package/CLAUDE.md
CHANGED
|
@@ -12,6 +12,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|
|
12
12
|
- **AJS** — Agent language that compiles to JSON AST for safe, sandboxed execution with fuel limits and injected capabilities. Code travels to data.
|
|
13
13
|
- **Toolchain** — Compresses transpilation, linting, testing, and documentation generation into one pass. Includes inline WASM with SIMD, polymorphic dispatch, local class extensions, and a browser-based playground.
|
|
14
14
|
|
|
15
|
+
> **TJS syntax is NOT TypeScript.** Full reference: [`CLAUDE-TJS-SYNTAX.md`](CLAUDE-TJS-SYNTAX.md). The single most common LLM mistake is treating `function foo(x: 'default')` as a TypeScript string-literal type. It is _not_ — the colon value is an **example**, and `'default'` widens to `string`. See the syntax doc before writing or modifying TJS source.
|
|
16
|
+
|
|
15
17
|
## Common Commands
|
|
16
18
|
|
|
17
19
|
```bash
|
|
@@ -72,15 +74,15 @@ bun run functions:serve # Local functions emulator
|
|
|
72
74
|
### Two-Layer Design
|
|
73
75
|
|
|
74
76
|
1. **Builder Layer** (`src/builder.ts`): Fluent API that constructs AST nodes. Contains no execution logic.
|
|
75
|
-
2. **Runtime Layer** (`src/vm/runtime.ts`): Executes AST nodes. Contains all atom implementations (~
|
|
77
|
+
2. **Runtime Layer** (`src/vm/runtime.ts`): Executes AST nodes. Contains all atom implementations (~3024 lines, security-critical).
|
|
76
78
|
|
|
77
79
|
### Key Source Files
|
|
78
80
|
|
|
79
81
|
- `src/index.ts` - Main entry, re-exports everything
|
|
80
|
-
- `src/vm/runtime.ts` - All atom implementations, expression evaluation, fuel charging (~
|
|
81
|
-
- `src/vm/vm.ts` - AgentVM class (~
|
|
82
|
+
- `src/vm/runtime.ts` - All atom implementations, expression evaluation, fuel charging (~3024 lines, security-critical)
|
|
83
|
+
- `src/vm/vm.ts` - AgentVM class (~247 lines)
|
|
82
84
|
- `src/vm/atoms/batteries.ts` - Battery atoms (vector search, LLM, store operations)
|
|
83
|
-
- `src/builder.ts` - TypedBuilder fluent API (~19KB)
|
|
85
|
+
- `src/builder.ts` - TypedBuilder fluent API (~757 lines / ~19KB)
|
|
84
86
|
- `src/lang/parser.ts` - TJS parser with colon shorthand, unsafe markers, return type extraction
|
|
85
87
|
- `src/lang/parser-transforms.ts` - Type, Generic, and FunctionPredicate block/function form transforms
|
|
86
88
|
- `src/lang/emitters/ast.ts` - Emits Agent99 AST from parsed source
|
|
@@ -413,22 +415,38 @@ The `docs/` directory contains real documentation (markdown), not build artifact
|
|
|
413
415
|
- `TJS-FOR-JS.md` — TJS guide for JavaScript developers (syntax differences, gotchas)
|
|
414
416
|
- `TJS-FOR-TS.md` — TJS guide for TypeScript developers (migration, interop)
|
|
415
417
|
- `CONTEXT.md` — Architecture deep dive
|
|
416
|
-
- `AGENTS.md` — Agent workflow instructions (
|
|
418
|
+
- `AGENTS.md` — Agent workflow instructions (session-completion checklist, push-before-done rule)
|
|
419
|
+
- `TODO.md` — Open work, organized by area; move items to the **Completed** section when done
|
|
417
420
|
- `PLAN.md` — Roadmap
|
|
418
421
|
|
|
419
|
-
###
|
|
422
|
+
### Tracking Work
|
|
420
423
|
|
|
421
|
-
|
|
424
|
+
Work is tracked in plain markdown — no external issue tracker. Open items live in `TODO.md` (organized by area). When you start a task, find or add the relevant entry; when you finish, check the box and (for substantial work) move it to the Completed section with a short note.
|
|
422
425
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
426
|
+
### Landing the Plane (Session Completion Checklist)
|
|
427
|
+
|
|
428
|
+
When ending a work session that touched code, complete **all** steps below in order. Canonical version lives in `AGENTS.md`.
|
|
429
|
+
|
|
430
|
+
1. **Update `TODO.md`** — check off completed items, add follow-ups for anything left undone, note blockers.
|
|
431
|
+
2. **Run quality gates** — if code changed: `bun run format`, `bun run typecheck`, `bun run test:fast` (or `bun test` for full suite).
|
|
432
|
+
3. **Commit** — focused commits with clear messages. Don't bundle unrelated changes.
|
|
433
|
+
4. **Push to remote** — mandatory:
|
|
434
|
+
```bash
|
|
435
|
+
git pull --rebase
|
|
436
|
+
git push
|
|
437
|
+
git status # MUST show "up to date with 'origin/...'"
|
|
438
|
+
```
|
|
439
|
+
5. **Clean up** — clear stale stashes, prune merged remote branches if appropriate.
|
|
440
|
+
6. **Verify** — working tree clean AND branch up to date with origin.
|
|
441
|
+
7. **Hand off** — leave a brief summary so the next session can pick up cold.
|
|
442
|
+
|
|
443
|
+
**Hard rules:**
|
|
430
444
|
|
|
431
|
-
|
|
445
|
+
- Work is NOT complete until `git push` succeeds.
|
|
446
|
+
- Never stop before pushing — leaving work stranded locally is leaving it lost.
|
|
447
|
+
- Never say "ready to push when you are" — push it yourself.
|
|
448
|
+
- If push fails, resolve the cause (rebase conflicts, hook failures, auth) and retry until it succeeds.
|
|
449
|
+
- Never `--no-verify` to bypass hooks. Fix the underlying issue.
|
|
432
450
|
|
|
433
451
|
### Known Gotcha: `tjs()` Returns an Object, Not a String
|
|
434
452
|
|
package/demo/docs.json
CHANGED
|
@@ -1147,7 +1147,7 @@
|
|
|
1147
1147
|
"title": "Agent Instructions",
|
|
1148
1148
|
"filename": "AGENTS.md",
|
|
1149
1149
|
"path": "AGENTS.md",
|
|
1150
|
-
"text": "# Agent Instructions\n\nThis project
|
|
1150
|
+
"text": "# Agent Instructions\n\nThis project tracks work in plain markdown files — no external issue tracker.\n\n## Where Work Lives\n\n- **`TODO.md`** — open work, organized by area (Playground, Language Features, Editor, Documentation, Infrastructure). Move items to the **Completed** section when done.\n- **`PLAN.md`** — roadmap and longer-term direction.\n- **`CLAUDE.md`** — repo conventions, commands, architecture for AI assistants.\n\nWhen you start a task, find or add the relevant entry in `TODO.md`. When you finish, check the box and (for substantial work) move it to the Completed section with a short note.\n\n## Landing the Plane (Session Completion)\n\nWhen ending a work session that touched code, complete **all** steps below in order. Work is NOT complete until `git push` succeeds.\n\n1. **Update `TODO.md`** — check off completed items, add follow-ups for anything left undone, note blockers.\n2. **Run quality gates** — if code changed:\n ```bash\n bun run format # ESLint fix + Prettier\n bun run typecheck # tsc --noEmit\n bun run test:fast # core tests (or `bun test` for full suite)\n ```\n3. **Commit** — focused commits with clear messages. Don't bundle unrelated changes.\n4. **Push to remote** — mandatory:\n ```bash\n git pull --rebase\n git push\n git status # MUST show \"up to date with 'origin/...'\"\n ```\n5. **Clean up** — clear stale stashes, prune merged remote branches if appropriate.\n6. **Verify** — working tree clean AND branch up to date with origin.\n7. **Hand off** — leave a brief summary so the next session can pick up cold.\n\n## Hard Rules\n\n- Work is NOT complete until `git push` succeeds.\n- Never stop before pushing — leaving work stranded locally is leaving it lost.\n- Never say \"ready to push when you are\" — push it yourself.\n- If push fails, resolve the cause (rebase conflicts, hook failures, auth) and retry until it succeeds.\n- Never `--no-verify` to bypass hooks. Fix the underlying issue.\n"
|
|
1151
1151
|
},
|
|
1152
1152
|
{
|
|
1153
1153
|
"title": "AGENTS.md",
|
|
@@ -1219,7 +1219,7 @@
|
|
|
1219
1219
|
"title": "CLAUDE.md",
|
|
1220
1220
|
"filename": "CLAUDE.md",
|
|
1221
1221
|
"path": "CLAUDE.md",
|
|
1222
|
-
"text": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\n**tjs-lang** (npm: `tjs-lang`) is a typed JavaScript platform — a language, runtime, and toolchain that transpiles TypeScript and TJS to JavaScript with runtime type validation, inline WASM, monadic errors, and safe eval. It also includes AJS, a gas-metered VM for executing untrusted agent code in any JavaScript environment.\n\n**Three pillars:**\n\n- **TJS** — TypeScript-like syntax where types are examples that survive to runtime as contracts, documentation, and tests. Transpiles TS → TJS → JS in a single fast pass.\n- **AJS** — Agent language that compiles to JSON AST for safe, sandboxed execution with fuel limits and injected capabilities. Code travels to data.\n- **Toolchain** — Compresses transpilation, linting, testing, and documentation generation into one pass. Includes inline WASM with SIMD, polymorphic dispatch, local class extensions, and a browser-based playground.\n\n## Common Commands\n\n```bash\n# Development\nbun run format # ESLint fix + Prettier\nbun run test:fast # Core tests (skips LLM & benchmarks)\nbun run make # Full build (clean, format, grammars, tsc, esbuild)\nbun run dev # Development server with file watcher\nbun run start # Build demo + start dev server\nbun run latest # Clean reinstall (rm node_modules + bun install)\n\n# Testing (framework: bun:test — describe/it/expect)\nbun test # Full test suite\nbun test src/path/to/file.test.ts # Single test file\nbun test --test-name-pattern \"pattern\" # Run tests matching pattern\nSKIP_LLM_TESTS=1 bun test # Skip LLM integration tests\nbun test --coverage # With coverage report\n\n# Efficient test debugging - capture once, query multiple times\nbun test 2>&1 | tee /tmp/test-results.txt | tail -20 # Run and save\ngrep -E \"^\\(fail\\)\" /tmp/test-results.txt # List failures\ngrep -A10 \"test name\" /tmp/test-results.txt # See specific error\n\n# CLI tools\nbun src/cli/tjs.ts check <file> # Parse and type check TJS file\nbun src/cli/tjs.ts run <file> # Transpile and execute\nbun src/cli/tjs.ts types <file> # Output type metadata as JSON\nbun src/cli/tjs.ts emit <file> # Output transpiled JavaScript\nbun src/cli/tjs.ts convert <file> # Convert TypeScript to TJS (--emit-tjs) or JS\nbun src/cli/tjs.ts test <file> # Run inline tests in a TJS file\n\n# Type checking & other\nbun run typecheck # tsc --noEmit (type check without emitting)\nbun run test:llm # LM Studio integration tests\nbun run bench # Vector search benchmarks\nbun run docs # Generate documentation\n\n# Build standalone CLI binaries\nbun run build:cli # Compiles tjs + tjsx to dist/\n\n# Compatibility testing (transpile popular TS libraries with fromTS)\nbun scripts/compat-zod.ts # Zod validation library\nbun scripts/compat-effect.ts # Effect (HKTs, intersections)\nbun scripts/compat-radash.ts # Radash utilities\nbun scripts/compat-superstruct.ts # Superstruct validation\nbun scripts/compat-ts-pattern.ts # ts-pattern matching\nbun scripts/compat-kysely.ts # Kysely SQL builder\n\n# Deployment (Firebase)\nbun run deploy # Build demo + deploy functions + hosting\nbun run deploy:hosting # Hosting only (serves from .demo/)\nbun run functions:deploy # Cloud functions only\nbun run functions:serve # Local functions emulator\n```\n\n## Architecture\n\n### Two-Layer Design\n\n1. **Builder Layer** (`src/builder.ts`): Fluent API that constructs AST nodes. Contains no execution logic.\n2. **Runtime Layer** (`src/vm/runtime.ts`): Executes AST nodes. Contains all atom implementations (~2900 lines, security-critical).\n\n### Key Source Files\n\n- `src/index.ts` - Main entry, re-exports everything\n- `src/vm/runtime.ts` - All atom implementations, expression evaluation, fuel charging (~3000 lines, security-critical)\n- `src/vm/vm.ts` - AgentVM class (~226 lines)\n- `src/vm/atoms/batteries.ts` - Battery atoms (vector search, LLM, store operations)\n- `src/builder.ts` - TypedBuilder fluent API (~19KB)\n- `src/lang/parser.ts` - TJS parser with colon shorthand, unsafe markers, return type extraction\n- `src/lang/parser-transforms.ts` - Type, Generic, and FunctionPredicate block/function form transforms\n- `src/lang/emitters/ast.ts` - Emits Agent99 AST from parsed source\n- `src/lang/emitters/js.ts` - Emits JavaScript with `__tjs` metadata\n- `src/lang/emitters/from-ts.ts` - TypeScript to TJS/JS transpiler with class metadata extraction\n- `src/lang/emitters/dts.ts` - .d.ts declaration file generator from TJS transpilation results\n- `src/lang/inference.ts` - Type inference from example values\n- `src/lang/json-schema.ts` - JSON Schema generation from TypeDescriptors and example values\n- `src/lang/linter.ts` - Static analysis (unused vars, unreachable code, no-explicit-new)\n- `src/lang/runtime.ts` - TJS runtime (monadic errors, type checking, wrapClass)\n- `src/lang/wasm.ts` - WASM compiler (opcodes, disassembler, bytecode generation)\n- `src/types/` - Type system definitions (Type.ts, Generic.ts)\n- `src/transpiler/` - AJS transpiler (source → AST)\n- `src/batteries/` - LM Studio integration (lazy init, model audit, vector search)\n- `src/store/` - Store implementations for persistence\n- `src/rbac/` - Role-based access control\n- `src/use-cases/` - Integration tests and real-world examples (28 test files)\n- `src/cli/tjs.ts` - CLI tool for check/run/types/emit/convert/test commands\n- `src/cli/tjsx.ts` - JSX/component runner\n- `src/cli/playground.ts` - Local playground server\n- `src/cli/create-app.ts` - Project scaffolding tool\n\n### Core APIs\n\n```typescript\n// Language\najs`...` // Parse AJS to AST\ntjs`...` // Parse TypeScript variant with type metadata\ntranspile(source, options) // Full transpilation with signature extraction\ncreateAgent(source, vm) // Creates callable agent\n\n// VM\nconst vm = new AgentVM(customAtoms)\nawait vm.run(ast, args, { fuel, capabilities, timeoutMs, trace })\n\n// Builder\nAgent.take(schema).varSet(...).httpFetch(...).return(schema)\nvm.Agent // Builder with custom atoms included\n\n// JSON Schema\nType('user', { name: '', age: 0 }).toJSONSchema() // → JSON Schema object\nType('user', { name: '', age: 0 }).strip(value) // → strip extra fields\nfunctionMetaToJSONSchema(fn.__tjs) // → { input, output } schemas\n\n// Error History (on by default, zero cost on happy path)\n__tjs.errors() // → recent MonadicErrors (ring buffer, max 64)\n__tjs.clearErrors() // → returns and clears\n__tjs.getErrorCount() // → total since last clear\n```\n\n### Package Entry Points\n\n```typescript\nimport { Agent, AgentVM, ajs, tjs } from 'tjs-lang' // Main entry\nimport { Eval, SafeFunction } from 'tjs-lang/eval' // Safe eval utilities\nimport { tjs, transpile } from 'tjs-lang/lang' // Language tools only\nimport { fromTS } from 'tjs-lang/lang/from-ts' // TypeScript transpilation\n```\n\n### Transpiler Chain (TS → TJS → JS)\n\nTJS supports transpiling TypeScript to JavaScript with runtime type validation. The pipeline has two distinct, independently testable steps:\n\n**Step 1: TypeScript → TJS** (`fromTS`)\n\n```typescript\nimport { fromTS } from 'tjs-lang/lang/from-ts'\n\nconst tsSource = `\nfunction greet(name: string): string {\n return \\`Hello, \\${name}!\\`\n}\n`\n\nconst result = fromTS(tsSource, { emitTJS: true })\n// result.code contains TJS:\n// function greet(name: ''): '' {\n// return \\`Hello, \\${name}!\\`\n// }\n```\n\n**Step 2: TJS → JavaScript** (`tjs`)\n\n```typescript\nimport { tjs } from 'tjs-lang/lang'\n\nconst tjsSource = `\nfunction greet(name: ''): '' {\n return \\`Hello, \\${name}!\\`\n}\n`\n\nconst jsResult = tjs(tjsSource)\n// jsResult.code contains JavaScript with __tjs metadata for runtime validation\n```\n\n**Full Chain Example:**\n\n```typescript\nimport { fromTS } from 'tjs-lang/lang/from-ts'\nimport { tjs } from 'tjs-lang/lang'\n\n// TypeScript source with type annotations\nconst tsSource = `\nfunction add(a: number, b: number): number {\n return a + b\n}\n`\n\n// Step 1: TS → TJS\nconst tjsResult = fromTS(tsSource, { emitTJS: true })\n\n// Step 2: TJS → JS (with runtime validation)\nconst jsCode = tjs(tjsResult.code)\n\n// Execute the result\nconst fn = new Function('__tjs', jsCode + '; return add')(__tjs_runtime)\nfn(1, 2) // Returns 3\nfn('a', 'b') // Returns { error: 'type mismatch', ... }\n```\n\n**Design Notes:**\n\n- The two steps are intentionally separate for tree-shaking (TS support is optional)\n- `fromTS` lives in a separate entry point (`tosijs/lang/from-ts`)\n- Import only what you need to keep bundle size minimal\n- Each step is independently testable (see `src/lang/codegen.test.ts`)\n- Constrained generics (`<T extends { id: number }>`) use the constraint as the example value instead of `any`\n- Generic defaults (`<T = string>`) use the default as the example value\n- Unconstrained generics (`<T>`) degrade to `any` — there's no information to use\n\n### Security Model\n\n- **Capability-based**: VM has zero IO by default; inject `fetch`, `store`, `llm` via capabilities\n- **Fuel metering**: Every atom has a cost; execution stops when fuel exhausted\n- **Timeout enforcement**: Default `fuel × 10ms`; explicit `timeoutMs` overrides\n- **Monadic errors**: Errors wrapped in `AgentError` (VM) / `MonadicError` (TJS), not thrown (prevents exception exploits). Use `isMonadicError()` to check — `isError()` is deprecated\n- **Expression sandboxing**: ExprNode AST evaluation, blocked prototype access\n\n### Expression Evaluation\n\nExpressions use AST nodes (`$expr`), not strings:\n\n```typescript\n{ $expr: 'binary', op: '+', left: {...}, right: {...} }\n{ $expr: 'ident', name: 'varName' }\n{ $expr: 'member', object: {...}, property: 'foo' }\n```\n\nEach node costs 0.01 fuel. Forbidden: function calls, `new`, `this`, `__proto__`, `constructor`.\n\n## AJS Expression Gotchas\n\nAJS expressions behave differently from JavaScript in several important ways:\n\n- **Null member access is safe by default**: `null.foo.bar` returns `undefined` silently (uses `?.` semantics internally). This differs from JavaScript which would throw `TypeError`.\n- **No computed member access with variables**: `items[i]` fails at transpile time with \"Computed member access with variables not yet supported\". Literal indices work (`items[0]`, `obj[\"key\"]`). Workaround: use `.map`/`.reduce` atoms instead.\n- **Unknown atom errors**: When an atom doesn't exist, the error is `\"Unknown Atom: <name>\"` with no listing of available atoms.\n- **TJS parameter syntax is NOT TypeScript**: `function foo(x: 'default')` means \"required param, example value 'default'\" — not a TypeScript string literal type. LLMs consistently generate `function foo(x: string)` which is wrong. The colon value is an _example_, not a _type annotation_.\n\n## Testing Strategy\n\n- Unit tests alongside source files (`*.test.ts`)\n- Integration tests in `src/use-cases/` (RAG, orchestration, malicious actors)\n- Security tests in `src/use-cases/malicious-actor.test.ts`\n- Language tests split across 14 files in `src/lang/` (lang.test.ts, features.test.ts, codegen.test.ts, parser.test.ts, from-ts.test.ts, wasm.test.ts, etc.)\n\nCoverage targets: 98% lines on `src/vm/runtime.ts` (security-critical), 80%+ overall.\n\n**Bug fix rule:** Always create a reproduction test case before fixing a bug.\n\n## Key Patterns\n\n### Adding a New Atom\n\n1. Define with `defineAtom(opCode, inputSchema, outputSchema, implementation, { cost, timeoutMs, docs })`\n2. Add to `src/vm/atoms/` and export from `src/vm/atoms/index.ts`\n3. Add tests\n4. Run `bun run test:fast`\n\n**Atom implementation notes:**\n\n- `cost` can be static number or dynamic: `(input, ctx) => number`\n- `timeoutMs` defaults to 1000ms; use `0` for no timeout (e.g., `seq`)\n- Atoms are always async; fuel deduction is automatic in the `exec` wrapper\n\n### Debugging Agents\n\nEnable tracing: `vm.run(ast, args, { trace: true })` returns `TraceEvent[]` with execution path, fuel consumption, and state changes.\n\n### Custom Atoms Must\n\n- Be non-blocking (no synchronous CPU-heavy work)\n- Respect `ctx.signal` for cancellation\n- Access IO only via `ctx.capabilities`\n\n### Value Resolution\n\nThe `resolveValue()` function handles multiple input patterns:\n\n- `{ $kind: 'arg', path: 'varName' }` → lookup in `ctx.args`\n- `{ $expr: ... }` → evaluate ExprNode via `evaluateExpr()`\n- String with dots `'obj.foo.bar'` → traverse state with forbidden property checks\n- Bare strings → lookup in state, else return literal\n\n### Monadic Error Flow\n\nWhen `ctx.error` is set, subsequent atoms in a `seq` skip execution. Errors are wrapped in `AgentError`, not thrown. This prevents exception-based exploits.\n\n### TJS Syntax Reference\n\nFull syntax documentation is in [`CLAUDE-TJS-SYNTAX.md`](CLAUDE-TJS-SYNTAX.md). Key concepts:\n\n- **Colon shorthand**: `function foo(x: 'hello')` — colon value is an _example_, not a type. This is the most common LLM mistake.\n- **Numeric narrowing**: `3.14` = float, `42` = integer, `+0` = non-negative integer\n- **Return types**: `function add(a: 0, b: 0): 0 { ... }` (colon syntax, same as TypeScript)\n- **Safety markers**: `!` = unsafe (skip validation), `?` = safe (explicit validation)\n- **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).\n- **Bang access**: `x!.foo` — returns MonadicError if `x` is null/undefined, otherwise bare `x.foo`. Chains propagate: `x!.foo!.bar`.\n- **Type/Generic/FunctionPredicate**: Three declaration forms for runtime type predicates\n- **`const!`**: Compile-time immutability, zero runtime cost\n- **Equality**: `==`/`!=` = structural equality by default in native TJS (via `Is`/`IsNot`), `===`/`!==` = identity\n- **Polymorphic functions**: Multiple same-name declarations merge into arity/type dispatcher\n- **`extend` blocks**: Local class extensions without prototype pollution\n- **WASM blocks**: Inline WebAssembly compiled at transpile time, with SIMD intrinsics and `wasmBuffer()` zero-copy arrays\n- **`@tjs` annotations**: `/* @tjs ... */` comments in TS files enrich TJS output\n\n#### Runtime Configuration\n\n```typescript\nimport { configure } from 'tjs-lang/lang'\n\nconfigure({ logTypeErrors: true }) // Log type errors to console\nconfigure({ throwTypeErrors: true }) // Throw instead of return (debugging)\nconfigure({ callStacks: true }) // Track call stacks in errors (~2x overhead)\nconfigure({ trackErrors: false }) // Disable error history (on by default)\n```\n\n#### Error History\n\nType errors are tracked in a ring buffer (on by default, zero cost on happy path):\n\n```typescript\n__tjs.errors() // → recent MonadicErrors (newest last, max 64)\n__tjs.clearErrors() // → returns and clears the buffer\n__tjs.getErrorCount() // → total since last clear (survives buffer wrap)\n```\n\nUse for debugging (find silent failures), testing (`clearErrors()` → run → check), and monitoring.\n\n#### Standalone JS Output\n\nEmitted `.js` files work without any runtime setup. Each file includes an inline\nminimal runtime as fallback — only the functions actually used are included (~500\nbytes for a basic validated function). If `globalThis.__tjs` exists (shared runtime),\nit's used instead.\n\n## Dependencies\n\nRuntime (shipped): `acorn` (JS parser, ~30KB), `tosijs-schema` (validation, ~5KB). Both have zero transitive dependencies.\n\n## Forbidden Properties (Security)\n\nThe following property names are blocked in expression evaluation to prevent prototype pollution:\n\n- `__proto__`, `constructor`, `prototype`\n\nThese are hardcoded in `runtime.ts` and checked during member access in `evaluateExpr()`.\n\n## Batteries System\n\nThe batteries (`src/batteries/`) provide zero-config local AI development:\n\n- **Lazy initialization**: First import audits LM Studio models (cached 24 hours)\n- **HTTPS detection**: Blocks local LLM calls from HTTPS contexts (security)\n- **Capabilities interface**: `fetch`, `store` (KV + vector), `llmBattery` (predict/embed)\n\nRegister battery atoms: `new AgentVM(batteryAtoms)` then pass `{ capabilities: batteries }` to `run()`.\n\n### Capability Key Naming\n\nThe base `Capabilities` interface (`runtime.ts`) uses `llm` with `{ predict, embed? }`, but the battery atoms access capabilities via different keys:\n\n| Capability key | Used by | Contains |\n| -------------- | -------------------------------------------------------- | -------------------------------------------- |\n| `llmBattery` | `llmPredictBattery`, `llmVision` | Full `LLMCapability` (`predict` + `embed`) |\n| `vector` | `storeVectorize` | Just `{ embed }` (extracted from llmBattery) |\n| `store` | `storeSearch`, `storeCreateCollection`, `storeVectorAdd` | KV + vector store ops |\n\nBoth `llmBattery` and `vector` can be `undefined`/`null` if LM Studio isn't available or HTTPS is detected.\n\n### Battery Atom Return Types\n\n- **`llmPredictBattery`**: Returns OpenAI message format `{ role?, content?, tool_calls? }` — NOT a plain string\n- **`storeVectorize`**: Returns `number[]` (embedding vector)\n- **`storeSearch`**: Returns `any[]` (matched documents)\n\n## Development Configuration\n\n### Bun Plugin\n\n`bunfig.toml` preloads `src/bun-plugin/tjs-plugin.ts` which enables importing `.tjs` files directly in bun (transpiled on-the-fly). It also aliases `tjs-lang` to `./src/index.ts` for local development, so `import { tjs } from 'tjs-lang'` resolves to the source tree without needing `npm link` or a published package.\n\n### Code Style\n\n- **Prettier**: Single quotes, no semicolons, 2-space indentation, 80 char width, es5 trailing commas\n- Prefix unused variables with `_` (enforced by ESLint: `argsIgnorePattern: '^_'`)\n- `any` types are allowed (`@typescript-eslint/no-explicit-any: 0`)\n- Module type is ESM (`\"type\": \"module\"` in package.json)\n- Build output goes to `dist/` (declaration files only via `tsconfig.build.json`, bundles via `scripts/build.ts`)\n- Run `bun run format` before committing (ESLint fix + Prettier)\n\n### Firebase Deployment\n\nThe playground is hosted on Firebase (`tjs-platform.web.app`). Demo build output goes to `.demo/` (gitignored) which is the Firebase hosting root. Cloud Functions live in `functions/` with their own build process (`functions/src/*.tjs` → transpile → bundle). Firebase config: `firebase.json`, `.firebaserc`, `firestore.rules`.\n\nThe `docs/` directory contains real documentation (markdown), not build artifacts. See `docs/README.md` for the documentation index.\n\n### Additional Directories\n\n- `tjs-src/` — TJS runtime written in TJS itself (self-hosting)\n- `guides/` — Usage patterns, benchmarks, examples (`patterns.md`, `benchmarks.md`, `tjs-examples.md`)\n- `examples/` — Standalone TJS example files (`hello.tjs`, `datetime.tjs`, `generic-demo.tjs`)\n- `editors/` — Syntax highlighting for Monaco, CodeMirror, Ace, VSCode\n\n### Additional Documentation\n\n- `DOCS-TJS.md` — TJS language guide\n- `DOCS-AJS.md` — AJS runtime guide\n- `TJS-FOR-JS.md` — TJS guide for JavaScript developers (syntax differences, gotchas)\n- `TJS-FOR-TS.md` — TJS guide for TypeScript developers (migration, interop)\n- `CONTEXT.md` — Architecture deep dive\n- `AGENTS.md` — Agent workflow instructions (issue tracking with `bd`, mandatory push-before-done)\n- `PLAN.md` — Roadmap\n\n### Issue Tracking with `bd`\n\nThe project uses `bd` (beads) for issue tracking. Key commands:\n\n```bash\nbd ready # Find available work\nbd show <id> # View issue details\nbd update <id> --status in_progress # Claim work\nbd close <id> # Complete work\nbd sync # Sync with git\n```\n\n**Critical rule**: Work is NOT complete until `git push` succeeds. Never stop before pushing — work stranded locally is work lost. If push fails, resolve and retry.\n\n### Known Gotcha: `tjs()` Returns an Object, Not a String\n\n`tjs(source)` returns `{ code, types, metadata, testResults, ... }`. Use `.code` to get the transpiled JavaScript string. This is a common mistake.\n\n### Running Emitted TJS Code\n\nEmitted JS works standalone — no setup required. Each file includes an inline\nruntime fallback. If you want the shared runtime (e.g. for `isMonadicError` to\nwork across files), install it first:\n\n```typescript\nimport { installRuntime, createRuntime } from '../lang/runtime'\ninstallRuntime() // or: globalThis.__tjs = createRuntime()\n\nconst fn = new Function(result.code + '\\nreturn fnName')()\nfn('valid') // works\nfn(42) // returns MonadicError (not thrown)\n```\n"
|
|
1222
|
+
"text": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\n**tjs-lang** (npm: `tjs-lang`) is a typed JavaScript platform — a language, runtime, and toolchain that transpiles TypeScript and TJS to JavaScript with runtime type validation, inline WASM, monadic errors, and safe eval. It also includes AJS, a gas-metered VM for executing untrusted agent code in any JavaScript environment.\n\n**Three pillars:**\n\n- **TJS** — TypeScript-like syntax where types are examples that survive to runtime as contracts, documentation, and tests. Transpiles TS → TJS → JS in a single fast pass.\n- **AJS** — Agent language that compiles to JSON AST for safe, sandboxed execution with fuel limits and injected capabilities. Code travels to data.\n- **Toolchain** — Compresses transpilation, linting, testing, and documentation generation into one pass. Includes inline WASM with SIMD, polymorphic dispatch, local class extensions, and a browser-based playground.\n\n> **TJS syntax is NOT TypeScript.** Full reference: [`CLAUDE-TJS-SYNTAX.md`](CLAUDE-TJS-SYNTAX.md). The single most common LLM mistake is treating `function foo(x: 'default')` as a TypeScript string-literal type. It is _not_ — the colon value is an **example**, and `'default'` widens to `string`. See the syntax doc before writing or modifying TJS source.\n\n## Common Commands\n\n```bash\n# Development\nbun run format # ESLint fix + Prettier\nbun run test:fast # Core tests (skips LLM & benchmarks)\nbun run make # Full build (clean, format, grammars, tsc, esbuild)\nbun run dev # Development server with file watcher\nbun run start # Build demo + start dev server\nbun run latest # Clean reinstall (rm node_modules + bun install)\n\n# Testing (framework: bun:test — describe/it/expect)\nbun test # Full test suite\nbun test src/path/to/file.test.ts # Single test file\nbun test --test-name-pattern \"pattern\" # Run tests matching pattern\nSKIP_LLM_TESTS=1 bun test # Skip LLM integration tests\nbun test --coverage # With coverage report\n\n# Efficient test debugging - capture once, query multiple times\nbun test 2>&1 | tee /tmp/test-results.txt | tail -20 # Run and save\ngrep -E \"^\\(fail\\)\" /tmp/test-results.txt # List failures\ngrep -A10 \"test name\" /tmp/test-results.txt # See specific error\n\n# CLI tools\nbun src/cli/tjs.ts check <file> # Parse and type check TJS file\nbun src/cli/tjs.ts run <file> # Transpile and execute\nbun src/cli/tjs.ts types <file> # Output type metadata as JSON\nbun src/cli/tjs.ts emit <file> # Output transpiled JavaScript\nbun src/cli/tjs.ts convert <file> # Convert TypeScript to TJS (--emit-tjs) or JS\nbun src/cli/tjs.ts test <file> # Run inline tests in a TJS file\n\n# Type checking & other\nbun run typecheck # tsc --noEmit (type check without emitting)\nbun run test:llm # LM Studio integration tests\nbun run bench # Vector search benchmarks\nbun run docs # Generate documentation\n\n# Build standalone CLI binaries\nbun run build:cli # Compiles tjs + tjsx to dist/\n\n# Compatibility testing (transpile popular TS libraries with fromTS)\nbun scripts/compat-zod.ts # Zod validation library\nbun scripts/compat-effect.ts # Effect (HKTs, intersections)\nbun scripts/compat-radash.ts # Radash utilities\nbun scripts/compat-superstruct.ts # Superstruct validation\nbun scripts/compat-ts-pattern.ts # ts-pattern matching\nbun scripts/compat-kysely.ts # Kysely SQL builder\n\n# Deployment (Firebase)\nbun run deploy # Build demo + deploy functions + hosting\nbun run deploy:hosting # Hosting only (serves from .demo/)\nbun run functions:deploy # Cloud functions only\nbun run functions:serve # Local functions emulator\n```\n\n## Architecture\n\n### Two-Layer Design\n\n1. **Builder Layer** (`src/builder.ts`): Fluent API that constructs AST nodes. Contains no execution logic.\n2. **Runtime Layer** (`src/vm/runtime.ts`): Executes AST nodes. Contains all atom implementations (~3024 lines, security-critical).\n\n### Key Source Files\n\n- `src/index.ts` - Main entry, re-exports everything\n- `src/vm/runtime.ts` - All atom implementations, expression evaluation, fuel charging (~3024 lines, security-critical)\n- `src/vm/vm.ts` - AgentVM class (~247 lines)\n- `src/vm/atoms/batteries.ts` - Battery atoms (vector search, LLM, store operations)\n- `src/builder.ts` - TypedBuilder fluent API (~757 lines / ~19KB)\n- `src/lang/parser.ts` - TJS parser with colon shorthand, unsafe markers, return type extraction\n- `src/lang/parser-transforms.ts` - Type, Generic, and FunctionPredicate block/function form transforms\n- `src/lang/emitters/ast.ts` - Emits Agent99 AST from parsed source\n- `src/lang/emitters/js.ts` - Emits JavaScript with `__tjs` metadata\n- `src/lang/emitters/from-ts.ts` - TypeScript to TJS/JS transpiler with class metadata extraction\n- `src/lang/emitters/dts.ts` - .d.ts declaration file generator from TJS transpilation results\n- `src/lang/inference.ts` - Type inference from example values\n- `src/lang/json-schema.ts` - JSON Schema generation from TypeDescriptors and example values\n- `src/lang/linter.ts` - Static analysis (unused vars, unreachable code, no-explicit-new)\n- `src/lang/runtime.ts` - TJS runtime (monadic errors, type checking, wrapClass)\n- `src/lang/wasm.ts` - WASM compiler (opcodes, disassembler, bytecode generation)\n- `src/types/` - Type system definitions (Type.ts, Generic.ts)\n- `src/transpiler/` - AJS transpiler (source → AST)\n- `src/batteries/` - LM Studio integration (lazy init, model audit, vector search)\n- `src/store/` - Store implementations for persistence\n- `src/rbac/` - Role-based access control\n- `src/use-cases/` - Integration tests and real-world examples (28 test files)\n- `src/cli/tjs.ts` - CLI tool for check/run/types/emit/convert/test commands\n- `src/cli/tjsx.ts` - JSX/component runner\n- `src/cli/playground.ts` - Local playground server\n- `src/cli/create-app.ts` - Project scaffolding tool\n\n### Core APIs\n\n```typescript\n// Language\najs`...` // Parse AJS to AST\ntjs`...` // Parse TypeScript variant with type metadata\ntranspile(source, options) // Full transpilation with signature extraction\ncreateAgent(source, vm) // Creates callable agent\n\n// VM\nconst vm = new AgentVM(customAtoms)\nawait vm.run(ast, args, { fuel, capabilities, timeoutMs, trace })\n\n// Builder\nAgent.take(schema).varSet(...).httpFetch(...).return(schema)\nvm.Agent // Builder with custom atoms included\n\n// JSON Schema\nType('user', { name: '', age: 0 }).toJSONSchema() // → JSON Schema object\nType('user', { name: '', age: 0 }).strip(value) // → strip extra fields\nfunctionMetaToJSONSchema(fn.__tjs) // → { input, output } schemas\n\n// Error History (on by default, zero cost on happy path)\n__tjs.errors() // → recent MonadicErrors (ring buffer, max 64)\n__tjs.clearErrors() // → returns and clears\n__tjs.getErrorCount() // → total since last clear\n```\n\n### Package Entry Points\n\n```typescript\nimport { Agent, AgentVM, ajs, tjs } from 'tjs-lang' // Main entry\nimport { Eval, SafeFunction } from 'tjs-lang/eval' // Safe eval utilities\nimport { tjs, transpile } from 'tjs-lang/lang' // Language tools only\nimport { fromTS } from 'tjs-lang/lang/from-ts' // TypeScript transpilation\n```\n\n### Transpiler Chain (TS → TJS → JS)\n\nTJS supports transpiling TypeScript to JavaScript with runtime type validation. The pipeline has two distinct, independently testable steps:\n\n**Step 1: TypeScript → TJS** (`fromTS`)\n\n```typescript\nimport { fromTS } from 'tjs-lang/lang/from-ts'\n\nconst tsSource = `\nfunction greet(name: string): string {\n return \\`Hello, \\${name}!\\`\n}\n`\n\nconst result = fromTS(tsSource, { emitTJS: true })\n// result.code contains TJS:\n// function greet(name: ''): '' {\n// return \\`Hello, \\${name}!\\`\n// }\n```\n\n**Step 2: TJS → JavaScript** (`tjs`)\n\n```typescript\nimport { tjs } from 'tjs-lang/lang'\n\nconst tjsSource = `\nfunction greet(name: ''): '' {\n return \\`Hello, \\${name}!\\`\n}\n`\n\nconst jsResult = tjs(tjsSource)\n// jsResult.code contains JavaScript with __tjs metadata for runtime validation\n```\n\n**Full Chain Example:**\n\n```typescript\nimport { fromTS } from 'tjs-lang/lang/from-ts'\nimport { tjs } from 'tjs-lang/lang'\n\n// TypeScript source with type annotations\nconst tsSource = `\nfunction add(a: number, b: number): number {\n return a + b\n}\n`\n\n// Step 1: TS → TJS\nconst tjsResult = fromTS(tsSource, { emitTJS: true })\n\n// Step 2: TJS → JS (with runtime validation)\nconst jsCode = tjs(tjsResult.code)\n\n// Execute the result\nconst fn = new Function('__tjs', jsCode + '; return add')(__tjs_runtime)\nfn(1, 2) // Returns 3\nfn('a', 'b') // Returns { error: 'type mismatch', ... }\n```\n\n**Design Notes:**\n\n- The two steps are intentionally separate for tree-shaking (TS support is optional)\n- `fromTS` lives in a separate entry point (`tosijs/lang/from-ts`)\n- Import only what you need to keep bundle size minimal\n- Each step is independently testable (see `src/lang/codegen.test.ts`)\n- Constrained generics (`<T extends { id: number }>`) use the constraint as the example value instead of `any`\n- Generic defaults (`<T = string>`) use the default as the example value\n- Unconstrained generics (`<T>`) degrade to `any` — there's no information to use\n\n### Security Model\n\n- **Capability-based**: VM has zero IO by default; inject `fetch`, `store`, `llm` via capabilities\n- **Fuel metering**: Every atom has a cost; execution stops when fuel exhausted\n- **Timeout enforcement**: Default `fuel × 10ms`; explicit `timeoutMs` overrides\n- **Monadic errors**: Errors wrapped in `AgentError` (VM) / `MonadicError` (TJS), not thrown (prevents exception exploits). Use `isMonadicError()` to check — `isError()` is deprecated\n- **Expression sandboxing**: ExprNode AST evaluation, blocked prototype access\n\n### Expression Evaluation\n\nExpressions use AST nodes (`$expr`), not strings:\n\n```typescript\n{ $expr: 'binary', op: '+', left: {...}, right: {...} }\n{ $expr: 'ident', name: 'varName' }\n{ $expr: 'member', object: {...}, property: 'foo' }\n```\n\nEach node costs 0.01 fuel. Forbidden: function calls, `new`, `this`, `__proto__`, `constructor`.\n\n## AJS Expression Gotchas\n\nAJS expressions behave differently from JavaScript in several important ways:\n\n- **Null member access is safe by default**: `null.foo.bar` returns `undefined` silently (uses `?.` semantics internally). This differs from JavaScript which would throw `TypeError`.\n- **No computed member access with variables**: `items[i]` fails at transpile time with \"Computed member access with variables not yet supported\". Literal indices work (`items[0]`, `obj[\"key\"]`). Workaround: use `.map`/`.reduce` atoms instead.\n- **Unknown atom errors**: When an atom doesn't exist, the error is `\"Unknown Atom: <name>\"` with no listing of available atoms.\n- **TJS parameter syntax is NOT TypeScript**: `function foo(x: 'default')` means \"required param, example value 'default'\" — not a TypeScript string literal type. LLMs consistently generate `function foo(x: string)` which is wrong. The colon value is an _example_, not a _type annotation_.\n\n## Testing Strategy\n\n- Unit tests alongside source files (`*.test.ts`)\n- Integration tests in `src/use-cases/` (RAG, orchestration, malicious actors)\n- Security tests in `src/use-cases/malicious-actor.test.ts`\n- Language tests split across 14 files in `src/lang/` (lang.test.ts, features.test.ts, codegen.test.ts, parser.test.ts, from-ts.test.ts, wasm.test.ts, etc.)\n\nCoverage targets: 98% lines on `src/vm/runtime.ts` (security-critical), 80%+ overall.\n\n**Bug fix rule:** Always create a reproduction test case before fixing a bug.\n\n## Key Patterns\n\n### Adding a New Atom\n\n1. Define with `defineAtom(opCode, inputSchema, outputSchema, implementation, { cost, timeoutMs, docs })`\n2. Add to `src/vm/atoms/` and export from `src/vm/atoms/index.ts`\n3. Add tests\n4. Run `bun run test:fast`\n\n**Atom implementation notes:**\n\n- `cost` can be static number or dynamic: `(input, ctx) => number`\n- `timeoutMs` defaults to 1000ms; use `0` for no timeout (e.g., `seq`)\n- Atoms are always async; fuel deduction is automatic in the `exec` wrapper\n\n### Debugging Agents\n\nEnable tracing: `vm.run(ast, args, { trace: true })` returns `TraceEvent[]` with execution path, fuel consumption, and state changes.\n\n### Custom Atoms Must\n\n- Be non-blocking (no synchronous CPU-heavy work)\n- Respect `ctx.signal` for cancellation\n- Access IO only via `ctx.capabilities`\n\n### Value Resolution\n\nThe `resolveValue()` function handles multiple input patterns:\n\n- `{ $kind: 'arg', path: 'varName' }` → lookup in `ctx.args`\n- `{ $expr: ... }` → evaluate ExprNode via `evaluateExpr()`\n- String with dots `'obj.foo.bar'` → traverse state with forbidden property checks\n- Bare strings → lookup in state, else return literal\n\n### Monadic Error Flow\n\nWhen `ctx.error` is set, subsequent atoms in a `seq` skip execution. Errors are wrapped in `AgentError`, not thrown. This prevents exception-based exploits.\n\n### TJS Syntax Reference\n\nFull syntax documentation is in [`CLAUDE-TJS-SYNTAX.md`](CLAUDE-TJS-SYNTAX.md). Key concepts:\n\n- **Colon shorthand**: `function foo(x: 'hello')` — colon value is an _example_, not a type. This is the most common LLM mistake.\n- **Numeric narrowing**: `3.14` = float, `42` = integer, `+0` = non-negative integer\n- **Return types**: `function add(a: 0, b: 0): 0 { ... }` (colon syntax, same as TypeScript)\n- **Safety markers**: `!` = unsafe (skip validation), `?` = safe (explicit validation)\n- **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).\n- **Bang access**: `x!.foo` — returns MonadicError if `x` is null/undefined, otherwise bare `x.foo`. Chains propagate: `x!.foo!.bar`.\n- **Type/Generic/FunctionPredicate**: Three declaration forms for runtime type predicates\n- **`const!`**: Compile-time immutability, zero runtime cost\n- **Equality**: `==`/`!=` = structural equality by default in native TJS (via `Is`/`IsNot`), `===`/`!==` = identity\n- **Polymorphic functions**: Multiple same-name declarations merge into arity/type dispatcher\n- **`extend` blocks**: Local class extensions without prototype pollution\n- **WASM blocks**: Inline WebAssembly compiled at transpile time, with SIMD intrinsics and `wasmBuffer()` zero-copy arrays\n- **`@tjs` annotations**: `/* @tjs ... */` comments in TS files enrich TJS output\n\n#### Runtime Configuration\n\n```typescript\nimport { configure } from 'tjs-lang/lang'\n\nconfigure({ logTypeErrors: true }) // Log type errors to console\nconfigure({ throwTypeErrors: true }) // Throw instead of return (debugging)\nconfigure({ callStacks: true }) // Track call stacks in errors (~2x overhead)\nconfigure({ trackErrors: false }) // Disable error history (on by default)\n```\n\n#### Error History\n\nType errors are tracked in a ring buffer (on by default, zero cost on happy path):\n\n```typescript\n__tjs.errors() // → recent MonadicErrors (newest last, max 64)\n__tjs.clearErrors() // → returns and clears the buffer\n__tjs.getErrorCount() // → total since last clear (survives buffer wrap)\n```\n\nUse for debugging (find silent failures), testing (`clearErrors()` → run → check), and monitoring.\n\n#### Standalone JS Output\n\nEmitted `.js` files work without any runtime setup. Each file includes an inline\nminimal runtime as fallback — only the functions actually used are included (~500\nbytes for a basic validated function). If `globalThis.__tjs` exists (shared runtime),\nit's used instead.\n\n## Dependencies\n\nRuntime (shipped): `acorn` (JS parser, ~30KB), `tosijs-schema` (validation, ~5KB). Both have zero transitive dependencies.\n\n## Forbidden Properties (Security)\n\nThe following property names are blocked in expression evaluation to prevent prototype pollution:\n\n- `__proto__`, `constructor`, `prototype`\n\nThese are hardcoded in `runtime.ts` and checked during member access in `evaluateExpr()`.\n\n## Batteries System\n\nThe batteries (`src/batteries/`) provide zero-config local AI development:\n\n- **Lazy initialization**: First import audits LM Studio models (cached 24 hours)\n- **HTTPS detection**: Blocks local LLM calls from HTTPS contexts (security)\n- **Capabilities interface**: `fetch`, `store` (KV + vector), `llmBattery` (predict/embed)\n\nRegister battery atoms: `new AgentVM(batteryAtoms)` then pass `{ capabilities: batteries }` to `run()`.\n\n### Capability Key Naming\n\nThe base `Capabilities` interface (`runtime.ts`) uses `llm` with `{ predict, embed? }`, but the battery atoms access capabilities via different keys:\n\n| Capability key | Used by | Contains |\n| -------------- | -------------------------------------------------------- | -------------------------------------------- |\n| `llmBattery` | `llmPredictBattery`, `llmVision` | Full `LLMCapability` (`predict` + `embed`) |\n| `vector` | `storeVectorize` | Just `{ embed }` (extracted from llmBattery) |\n| `store` | `storeSearch`, `storeCreateCollection`, `storeVectorAdd` | KV + vector store ops |\n\nBoth `llmBattery` and `vector` can be `undefined`/`null` if LM Studio isn't available or HTTPS is detected.\n\n### Battery Atom Return Types\n\n- **`llmPredictBattery`**: Returns OpenAI message format `{ role?, content?, tool_calls? }` — NOT a plain string\n- **`storeVectorize`**: Returns `number[]` (embedding vector)\n- **`storeSearch`**: Returns `any[]` (matched documents)\n\n## Development Configuration\n\n### Bun Plugin\n\n`bunfig.toml` preloads `src/bun-plugin/tjs-plugin.ts` which enables importing `.tjs` files directly in bun (transpiled on-the-fly). It also aliases `tjs-lang` to `./src/index.ts` for local development, so `import { tjs } from 'tjs-lang'` resolves to the source tree without needing `npm link` or a published package.\n\n### Code Style\n\n- **Prettier**: Single quotes, no semicolons, 2-space indentation, 80 char width, es5 trailing commas\n- Prefix unused variables with `_` (enforced by ESLint: `argsIgnorePattern: '^_'`)\n- `any` types are allowed (`@typescript-eslint/no-explicit-any: 0`)\n- Module type is ESM (`\"type\": \"module\"` in package.json)\n- Build output goes to `dist/` (declaration files only via `tsconfig.build.json`, bundles via `scripts/build.ts`)\n- Run `bun run format` before committing (ESLint fix + Prettier)\n\n### Firebase Deployment\n\nThe playground is hosted on Firebase (`tjs-platform.web.app`). Demo build output goes to `.demo/` (gitignored) which is the Firebase hosting root. Cloud Functions live in `functions/` with their own build process (`functions/src/*.tjs` → transpile → bundle). Firebase config: `firebase.json`, `.firebaserc`, `firestore.rules`.\n\nThe `docs/` directory contains real documentation (markdown), not build artifacts. See `docs/README.md` for the documentation index.\n\n### Additional Directories\n\n- `tjs-src/` — TJS runtime written in TJS itself (self-hosting)\n- `guides/` — Usage patterns, benchmarks, examples (`patterns.md`, `benchmarks.md`, `tjs-examples.md`)\n- `examples/` — Standalone TJS example files (`hello.tjs`, `datetime.tjs`, `generic-demo.tjs`)\n- `editors/` — Syntax highlighting for Monaco, CodeMirror, Ace, VSCode\n\n### Additional Documentation\n\n- `DOCS-TJS.md` — TJS language guide\n- `DOCS-AJS.md` — AJS runtime guide\n- `TJS-FOR-JS.md` — TJS guide for JavaScript developers (syntax differences, gotchas)\n- `TJS-FOR-TS.md` — TJS guide for TypeScript developers (migration, interop)\n- `CONTEXT.md` — Architecture deep dive\n- `AGENTS.md` — Agent workflow instructions (session-completion checklist, push-before-done rule)\n- `TODO.md` — Open work, organized by area; move items to the **Completed** section when done\n- `PLAN.md` — Roadmap\n\n### Tracking Work\n\nWork is tracked in plain markdown — no external issue tracker. Open items live in `TODO.md` (organized by area). When you start a task, find or add the relevant entry; when you finish, check the box and (for substantial work) move it to the Completed section with a short note.\n\n### Landing the Plane (Session Completion Checklist)\n\nWhen ending a work session that touched code, complete **all** steps below in order. Canonical version lives in `AGENTS.md`.\n\n1. **Update `TODO.md`** — check off completed items, add follow-ups for anything left undone, note blockers.\n2. **Run quality gates** — if code changed: `bun run format`, `bun run typecheck`, `bun run test:fast` (or `bun test` for full suite).\n3. **Commit** — focused commits with clear messages. Don't bundle unrelated changes.\n4. **Push to remote** — mandatory:\n ```bash\n git pull --rebase\n git push\n git status # MUST show \"up to date with 'origin/...'\"\n ```\n5. **Clean up** — clear stale stashes, prune merged remote branches if appropriate.\n6. **Verify** — working tree clean AND branch up to date with origin.\n7. **Hand off** — leave a brief summary so the next session can pick up cold.\n\n**Hard rules:**\n\n- Work is NOT complete until `git push` succeeds.\n- Never stop before pushing — leaving work stranded locally is leaving it lost.\n- Never say \"ready to push when you are\" — push it yourself.\n- If push fails, resolve the cause (rebase conflicts, hook failures, auth) and retry until it succeeds.\n- Never `--no-verify` to bypass hooks. Fix the underlying issue.\n\n### Known Gotcha: `tjs()` Returns an Object, Not a String\n\n`tjs(source)` returns `{ code, types, metadata, testResults, ... }`. Use `.code` to get the transpiled JavaScript string. This is a common mistake.\n\n### Running Emitted TJS Code\n\nEmitted JS works standalone — no setup required. Each file includes an inline\nruntime fallback. If you want the shared runtime (e.g. for `isMonadicError` to\nwork across files), install it first:\n\n```typescript\nimport { installRuntime, createRuntime } from '../lang/runtime'\ninstallRuntime() // or: globalThis.__tjs = createRuntime()\n\nconst fn = new Function(result.code + '\\nreturn fnName')()\nfn('valid') // works\nfn(42) // returns MonadicError (not thrown)\n```\n"
|
|
1223
1223
|
},
|
|
1224
1224
|
{
|
|
1225
1225
|
"title": "Context: Working with tosijs-schema",
|
|
@@ -1543,7 +1543,7 @@
|
|
|
1543
1543
|
"title": "TJS-Lang TODO",
|
|
1544
1544
|
"filename": "TODO.md",
|
|
1545
1545
|
"path": "TODO.md",
|
|
1546
|
-
"text": "# TJS-Lang TODO\n\n## Playground - Error Navigation\n\n- [ ] Test errors: click should navigate to source location\n- [ ] Console errors: click should navigate to source location\n- [ ] Error in imported module: click through to source\n\n## Playground - Module Management\n\n- [ ] Import one example from another in playground\n- [ ] Save/Load TS examples (consistency with TJS examples)\n- [ ] File name should be linked to example name\n- [ ] New example button in playground\n- [ ] UI for managing stored modules (browse/delete IndexedDB)\n- [ ] Auto-discover and build local dependencies in module resolution\n\n## Language Features\n\n- [ ] Portable Type predicates - expression-only AJS subset (no loops, no async, serializable)\n- [ ] Sync AJS / AJS-to-JS compilation - for type-checked AJS that passes static analysis, transpile to native JS with fuel injection points. Enables both type safety guarantees AND native performance for RBAC rules, predicates, etc.\n- [ ] Self-contained transpiler output (no runtime dependency)\n - Currently transpiled code references `globalThis.__tjs` for pushStack/popStack, typeError, Is/IsNot\n - Requires runtime to be installed or a stub (see playground's manual \\_\\_tjs stub)\n - Goal: TJS produces completely independent code, only needing semantic dependencies\n - Options: inline minimal runtime (~1KB), `{ standalone: true }` option, or tree-shake\n - See: src/lang/emitters/js.ts TODO comment for details\n- [x] WASM compilation at transpile time (not runtime)\n - [x] Compile wasm {} blocks during transpilation\n - [x] Embed base64-encoded WASM bytes in output\n - [x] Include WAT disassembly as comment for debugging/learning\n - [x] Self-contained async instantiation (no separate compileWasmBlocksForIframe)\n- [x] Expand WASM support beyond POC\n - [x] For loops with numeric bounds\n - [x] Conditionals (if/else)\n - [x] Local variables within block\n - [x] Typed array access (Float32Array, Float64Array, Int32Array, Uint8Array)\n - [x] Memory operations\n - [x] Continue/break statements\n - [x] Logical expressions (&& / ||)\n - [x] Math functions (sqrt, abs, floor, ceil, min, max, sin, cos, log, exp, pow)\n- [x] WASM SIMD support (v128/f32x4)\n - 12 f32x4 intrinsics: load, store, splat, extract_lane, replace_lane, add, sub, mul, div, neg, sqrt\n - Explicit intrinsic approach (users call f32x4\\_\\* in wasm blocks)\n - Disassembler handles 0xfd prefix with LEB128 sub-opcodes\n - 16-byte aligned memory for v128 loads/stores\n - Demos: starfield SIMD rotation, vector search cosine similarity\n- [ ] WASM SIMD vector search (batteries)\n - Replace JS vectorSearch battery with WASM SIMD implementation\n - SIMD cosine similarity demonstrated in vector search demo\n - TODO: integrate as a battery atom with auto-detect + fallback\n\n## Editor\n\n- [ ] Embedded AJS syntax highlighting\n\n## Documentation / Examples\n\n- [ ] Create an endpoint example\n- [ ] Fold docs and tests into one panel, with passing tests collapsed by default (ts -> tjs inserts test; tjs -> js turns test blocks into documentation along with outcomes).\n- [ ] Dim/hide the preview tab if nothing ever changed it\n- [ ] Single source of truth for version number. I note the badge displayed in console is not matching the version. No hardwired versions -- version number is pulled from package.json and written to version.ts somewhere and that is the single source of truth.\n\n## Infrastructure\n\n- [ ] Make playground components reusable for others\n- [ ] Web worker for transpiles (freezer - not needed yet)\n- [x] Retarget Firebase as host platform (vs GitHub Pages)\n- [ ] Universal LLM endpoint with real LLMs (OpenAI, Anthropic, etc.)\n- [ ] ESM-as-a-service: versioned library endpoints\n- [ ] User accounts (Google sign-in) for API key storage\n- [ ] AJS-based Firestore and Storage security rules\n- [ ] npx tjs-playground - run playground locally with LM Studio\n- [ ] Virtual subdomains for user apps (yourapp.tjs.land)\n - [ ] Wildcard DNS to Firebase\n - [ ] Subdomain routing in Cloud Function\n - [ ] Deploy button in playground\n - [ ] Public/private visibility toggle\n- [ ] Rate limiting / abuse prevention for LLM endpoint\n- [ ] Usage tracking / billing foundation (for future paid tiers)\n\n---\n\n## Completed (this session)\n\n### Project Rename\n\n- [x] Rename from tosijs-agent to tjs-lang\n- [x] Update all references in package.json, docs, scripts\n
|
|
1546
|
+
"text": "# TJS-Lang TODO\n\n## Playground - Error Navigation\n\n- [ ] Test errors: click should navigate to source location\n- [ ] Console errors: click should navigate to source location\n- [ ] Error in imported module: click through to source\n\n## Playground - Module Management\n\n- [ ] Import one example from another in playground\n- [ ] Save/Load TS examples (consistency with TJS examples)\n- [ ] File name should be linked to example name\n- [ ] New example button in playground\n- [ ] UI for managing stored modules (browse/delete IndexedDB)\n- [ ] Auto-discover and build local dependencies in module resolution\n\n## Language Features\n\n- [ ] Portable Type predicates - expression-only AJS subset (no loops, no async, serializable)\n- [ ] Sync AJS / AJS-to-JS compilation - for type-checked AJS that passes static analysis, transpile to native JS with fuel injection points. Enables both type safety guarantees AND native performance for RBAC rules, predicates, etc.\n- [ ] Self-contained transpiler output (no runtime dependency)\n - Currently transpiled code references `globalThis.__tjs` for pushStack/popStack, typeError, Is/IsNot\n - Requires runtime to be installed or a stub (see playground's manual \\_\\_tjs stub)\n - Goal: TJS produces completely independent code, only needing semantic dependencies\n - Options: inline minimal runtime (~1KB), `{ standalone: true }` option, or tree-shake\n - See: src/lang/emitters/js.ts TODO comment for details\n- [x] WASM compilation at transpile time (not runtime)\n - [x] Compile wasm {} blocks during transpilation\n - [x] Embed base64-encoded WASM bytes in output\n - [x] Include WAT disassembly as comment for debugging/learning\n - [x] Self-contained async instantiation (no separate compileWasmBlocksForIframe)\n- [x] Expand WASM support beyond POC\n - [x] For loops with numeric bounds\n - [x] Conditionals (if/else)\n - [x] Local variables within block\n - [x] Typed array access (Float32Array, Float64Array, Int32Array, Uint8Array)\n - [x] Memory operations\n - [x] Continue/break statements\n - [x] Logical expressions (&& / ||)\n - [x] Math functions (sqrt, abs, floor, ceil, min, max, sin, cos, log, exp, pow)\n- [x] WASM SIMD support (v128/f32x4)\n - 12 f32x4 intrinsics: load, store, splat, extract_lane, replace_lane, add, sub, mul, div, neg, sqrt\n - Explicit intrinsic approach (users call f32x4\\_\\* in wasm blocks)\n - Disassembler handles 0xfd prefix with LEB128 sub-opcodes\n - 16-byte aligned memory for v128 loads/stores\n - Demos: starfield SIMD rotation, vector search cosine similarity\n- [ ] WASM SIMD vector search (batteries)\n - Replace JS vectorSearch battery with WASM SIMD implementation\n - SIMD cosine similarity demonstrated in vector search demo\n - TODO: integrate as a battery atom with auto-detect + fallback\n\n## Editor\n\n- [ ] Embedded AJS syntax highlighting\n\n## Documentation / Examples\n\n- [ ] Create an endpoint example\n- [ ] Fold docs and tests into one panel, with passing tests collapsed by default (ts -> tjs inserts test; tjs -> js turns test blocks into documentation along with outcomes).\n- [ ] Dim/hide the preview tab if nothing ever changed it\n- [ ] Single source of truth for version number. I note the badge displayed in console is not matching the version. No hardwired versions -- version number is pulled from package.json and written to version.ts somewhere and that is the single source of truth.\n\n## Infrastructure\n\n- [ ] Make playground components reusable for others\n- [ ] Web worker for transpiles (freezer - not needed yet)\n- [x] Retarget Firebase as host platform (vs GitHub Pages)\n- [ ] Universal LLM endpoint with real LLMs (OpenAI, Anthropic, etc.)\n- [ ] ESM-as-a-service: versioned library endpoints\n- [ ] User accounts (Google sign-in) for API key storage\n- [ ] AJS-based Firestore and Storage security rules\n- [ ] npx tjs-playground - run playground locally with LM Studio\n- [ ] Virtual subdomains for user apps (yourapp.tjs.land)\n - [ ] Wildcard DNS to Firebase\n - [ ] Subdomain routing in Cloud Function\n - [ ] Deploy button in playground\n - [ ] Public/private visibility toggle\n- [ ] Rate limiting / abuse prevention for LLM endpoint\n- [ ] Usage tracking / billing foundation (for future paid tiers)\n\n---\n\n## Completed (this session)\n\n### Project Rename\n\n- [x] Rename from tosijs-agent to tjs-lang\n- [x] Update all references in package.json, docs, scripts\n\n### Timestamp & LegalDate Utilities\n\n- [x] Timestamp - pure functions, 1-based months, no Date warts (53 tests)\n - now, from, parse, tryParse\n - addDays/Hours/Minutes/Seconds/Weeks/Months/Years\n - diff, diffSeconds/Minutes/Hours/Days\n - year/month/day/hour/minute/second/millisecond/dayOfWeek\n - toLocal, format, formatDate, formatTime, toDate\n - isBefore/isAfter/isEqual/min/max\n - startOf/endOf Day/Month/Year\n- [x] LegalDate - pure functions, YYYY-MM-DD strings (55 tests)\n - today, todayIn, from, parse, tryParse\n - addDays/Weeks/Months/Years\n - diff, diffMonths, diffYears\n - year/month/day/dayOfWeek/weekOfYear/dayOfYear/quarter\n - isLeapYear, daysInMonth, daysInYear\n - toTimestamp, toUnix, fromUnix\n - format, formatLong, formatShort\n - isBefore/isAfter/isEqual/min/max/isBetween\n - startOf/endOf Month/Quarter/Year/Week\n- [x] Portable predicate helpers: isValidUrl, isValidTimestamp, isValidLegalDate\n\n### TJS Mode System (native TJS has all modes ON by default; TS-originated code defaults to OFF)\n\n- [x] Invert mode system - native TJS enables all modes; TS-originated/AJS code defaults to JS semantics\n- [x] TjsEquals directive - structural == and != (null == undefined)\n- [x] TjsClass directive - classes callable without new\n- [x] TjsDate directive - bans Date constructor/methods\n- [x] TjsNoeval directive - bans eval() and new Function()\n- [x] TjsStrict directive - enables all of the above\n- [x] TjsSafeEval directive - includes Eval/SafeFunction for dynamic code execution\n- [x] Updated Is() for nullish equality (null == undefined)\n- [x] Added Is/IsNot tests (structural equality, nullish handling)\n- [x] TjsStandard directive - newlines as statement terminators (prevents ASI footguns)\n- [x] WASM POC - wasm {} blocks with parsing, fallback mechanism, basic numeric compilation\n- [x] Eval/SafeFunction - proper VM-backed implementation with fuel metering and capabilities\n\n### Bundle Size Optimization\n\n- [x] Separated Eval/SafeFunction into standalone module (eval.ts)\n- [x] Created core.ts - AJS transpiler without TypeScript dependency\n- [x] Fixed tjs-transpiler bundle: 4.14MB → 88.9KB (27KB gzipped)\n- [x] Runtime is now ~5KB gzipped (just Is/IsNot, wrap, Type, etc.)\n- [x] Eval adds ~27KB gzipped (VM + AJS transpiler, no TypeScript)\n- [x] TypeScript only bundled in playground (5.8MB) for real-time TS transpilation\n"
|
|
1547
1547
|
},
|
|
1548
1548
|
{
|
|
1549
1549
|
"title": "TJS: Typed JavaScript",
|
|
@@ -1619,4 +1619,4 @@
|
|
|
1619
1619
|
"navTitle": "TJS for TS Devs",
|
|
1620
1620
|
"text": "<!--{\"section\": \"tjs-for-ts\", \"group\": \"docs\", \"order\": 0, \"navTitle\": \"TJS for TS Devs\"}-->\n\n# TJS for TypeScript Programmers\n\n_What if your types didn't disappear at runtime?_\n\n---\n\nTypeScript is great. It catches bugs at compile time, makes refactoring safer,\nand gives you autocomplete. But it has a fundamental limitation: types are\nfiction. They exist only in your editor, and they vanish completely at runtime.\n\nTJS starts from a different premise: **types are example values that survive\nto runtime**. This gives you everything TypeScript gives you, plus runtime\nvalidation, reflection, documentation, inline tests of private methods, and traceability of errors back to source code -- from a single source of truth.\n\nThis guide is split into two paths:\n\n1. **[Using TJS from TypeScript](#part-1-using-tjs-from-typescript)** -- Keep your TS codebase, use TJS for safe eval and agent execution\n2. **[Migrating to TJS](#part-2-migrating-to-tjs)** -- Convert your codebase from TypeScript to TJS\n\n---\n\n# Part 1: Using TJS from TypeScript\n\nYou don't have to rewrite anything. TJS provides tools you can use directly\nfrom your TypeScript codebase.\n\n## Safe Eval\n\nThe most common reason to reach for TJS from TypeScript: running untrusted\ncode safely.\n\n```typescript\nimport { Eval, SafeFunction } from 'tjs-lang/eval'\n\n// Run user-provided code with a gas limit\nconst { result, fuelUsed } = await Eval({\n code: userCode,\n context: { items: data, threshold: 10 },\n fuel: 1000,\n capabilities: {\n fetch: sandboxedFetch, // your whitelist-wrapped fetch\n },\n})\n\n// Or create a reusable safe function\nconst transform = await SafeFunction({\n body: 'return items.filter(x => x.price < budget)',\n params: ['items', 'budget'],\n fuel: 500,\n})\n\nconst { result } = await transform(products, 100)\n```\n\nNo `eval()`. No CSP violations. No Docker containers. The code runs in a\nfuel-metered sandbox with only the capabilities you inject.\n\n## Agent VM\n\nBuild and execute JSON-serializable agents:\n\n```typescript\nimport { ajs, AgentVM } from 'tjs-lang'\n\n// Parse agent source to JSON AST\nconst agent = ajs`\n function analyze({ data, query }) {\n let filtered = data.filter(x => x.score > 0.5)\n let summary = llmPredict({\n prompt: 'Summarize findings for: ' + query,\n data: filtered\n })\n return { query, summary, count: filtered.length }\n }\n`\n\n// Execute with resource limits\nconst vm = new AgentVM()\nconst { result } = await vm.run(\n agent,\n { data, query },\n {\n fuel: 1000,\n timeoutMs: 5000,\n capabilities: { fetch: myFetch, llm: myLlm },\n }\n)\n```\n\nThe agent AST is JSON. You can store it in a database, send it over the\nnetwork, version it, diff it, audit it.\n\n## Type-Safe Builder\n\nConstruct agents programmatically with full TypeScript support:\n\n```typescript\nimport { Agent, AgentVM, s } from 'tjs-lang'\n\nconst pipeline = Agent.take(s.object({ url: s.string, maxResults: s.number }))\n .httpFetch({ url: { $kind: 'arg', path: 'url' } })\n .as('response')\n .varSet({\n key: 'results',\n value: {\n $expr: 'member',\n object: { $expr: 'ident', name: 'response' },\n property: 'items',\n },\n })\n .return(s.object({ results: s.array(s.any) }))\n\nconst vm = new AgentVM()\nconst { result } = await vm.run(\n pipeline.toJSON(),\n { url, maxResults: 10 },\n {\n fuel: 500,\n capabilities: { fetch },\n }\n)\n```\n\n## TypeScript Entry Points\n\nTJS is tree-shakeable. Import only what you need:\n\n```typescript\nimport { Agent, AgentVM, ajs, tjs } from 'tjs-lang' // Everything\nimport { Eval, SafeFunction } from 'tjs-lang/eval' // Safe eval only\nimport { tjs, transpile } from 'tjs-lang/lang' // Language tools\nimport { fromTS } from 'tjs-lang/lang/from-ts' // TS -> TJS converter\n```\n\n## When to Stay in TypeScript\n\nIf your codebase is TypeScript and you're happy with it, you probably only\nneed TJS for:\n\n- Running user-provided or LLM-generated code safely\n- Building agents that travel over the network\n- Adding runtime type validation at system boundaries\n- Eval without `eval()`\n\nYou don't need to migrate anything. The libraries work from TypeScript.\n\n---\n\n# Part 2: Migrating to TJS\n\nIf you want the full TJS experience -- runtime types, structural equality,\nmonadic errors, inline tests -- here's how to convert.\n\n## The Core Idea: Types as Examples\n\nTypeScript describes types abstractly. TJS describes them concretely:\n\n```typescript\n// TypeScript: what TYPE is this?\nfunction greet(name: string): string { ... }\n\n// TJS: what's an EXAMPLE of this?\nfunction greet(name: 'World'): '' { ... }\n```\n\n`'World'` tells TJS: this is a string, it's required, and here's a valid\nexample. The example doubles as documentation and test data.\n\n## Conversion Reference\n\n### Primitives\n\n```typescript\n// TypeScript // TJS\nname: string name: ''\ncount: number count: 0.0 // float (any number)\nindex: number index: 0 // integer\nage: number age: +0 // non-negative integer\nflag: boolean flag: true\nitems: string[] items: ['']\nnested: number[][] nested: [[0]]\n```\n\n**Important:** The example value determines the _type_, not a literal\nconstraint. `name: 'World'` means \"required string\" -- not \"must be the\nstring `'World'`.\" Any string passes validation. The example is there for\ndocumentation, testing, and type inference. Think of it as `string` with\na built-in `@example` tag.\n\n**Numeric precision:** TJS distinguishes three numeric types using valid\nJavaScript syntax that JS itself ignores:\n\n| You Write | TJS Type | Runtime Check |\n| --------- | ---------------------- | ------------------------------- |\n| `3.14` | `number` (float) | Any number |\n| `0.0` | `number` (float) | Any number |\n| `42` | `integer` | `Number.isInteger(x)` |\n| `0` | `integer` | `Number.isInteger(x)` |\n| `+20` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n| `+0` | `non-negative integer` | `Number.isInteger(x) && x >= 0` |\n\nTypeScript's `number` is a single type. TJS gives you three levels of\nprecision -- all using expressions that are already legal JavaScript.\nThe automatic converter maps TypeScript `number` to `0.0` (float) to\npreserve the widest behavior; you can then narrow manually to `0` (integer)\nor `+0` (non-negative integer) where appropriate.\n\n### Optional Parameters\n\n```typescript\n// TypeScript // TJS\nfunction f(x?: string) {}\nfunction f(x = '') {}\nfunction f(x: string = 'hi') {}\nfunction f(x = 'hi') {}\n```\n\nIn TypeScript, `?` means optional with type `string | undefined`.\nIn TJS, `= value` means optional with that default. Same semantics, less syntax.\n\n### Object Shapes\n\n```typescript\n// TypeScript\nfunction createUser(opts: { name: string; age: number; email?: string }) {}\n\n// TJS\nfunction createUser(opts: { name: '', age: 0, email = '' }) {}\n```\n\nRequired properties use `:`, optional ones use `=`.\n\n### Return Types\n\n```typescript\n// TypeScript // TJS\nfunction add(a: number, b: number): number function add(a: 0, b: 0): 0\nfunction getUser(): { name: string } function getUser(): { name: '' }\nfunction fetchData(): Promise<string[]> function fetchData(): ['']\n```\n\nThe `fromTS` converter unwraps `Promise<T>` in return type annotations --\nyou annotate the resolved type, not the wrapper. This only applies when\nconverting from TypeScript; in native TJS you just write normal\n`async`/`await` and annotate what the function resolves to.\n\nThe return annotation also generates an automatic test: `add(0, 0)` must\nreturn a number. If it doesn't, you get an error at transpile time.\n\n### Interfaces and Type Aliases\n\n```typescript\n// TypeScript\ninterface User {\n name: string\n age: number\n email?: string\n}\n\ntype Status = 'active' | 'inactive' | 'banned'\n\n// TJS\nType User {\n description: 'a registered user'\n example: { name: '', age: 0, email = '' }\n}\n\nUnion Status 'account status' 'active' | 'inactive' | 'banned'\n```\n\n### Enums\n\n```typescript\n// TypeScript\nenum Color {\n Red = 'red',\n Green = 'green',\n Blue = 'blue',\n}\n\n// TJS\nEnum Color 'CSS color' {\n Red = 'red'\n Green = 'green'\n Blue = 'blue'\n}\n```\n\n### Classes\n\n```typescript\n// TypeScript\nclass Point {\n private x: number\n private y: number\n\n constructor(x: number, y: number) {\n this.x = x\n this.y = y\n }\n\n distanceTo(other: Point): number {\n return Math.sqrt((this.x - other.x) ** 2 + (this.y - other.y) ** 2)\n }\n}\n\nconst p = new Point(10, 20)\n\n// TJS\nclass Point {\n #x\n #y\n\n constructor(x: 0, y: 0) {\n this.#x = x\n this.#y = y\n }\n\n distanceTo(other: Point): 0 {\n return Math.sqrt((this.#x - other.#x) ** 2 + (this.#y - other.#y) ** 2)\n }\n}\n\nconst p = Point(10, 20) // no 'new' needed\n```\n\nKey differences:\n\n- `private` is stripped by default (TS `private` is compile-time only).\n With `TjsClass` (on by default in native TJS, add via `/* @tjs TjsClass */` for TS-originated code), `private` converts to `#` (true JS runtime privacy).\n- Type annotations become example values\n- With `TjsClass`, `new` is optional (linter warns against it)\n\n### Generics\n\nTJS takes a different approach to generics. TypeScript has function-level\ntype parameters (`<T>`) that vanish at runtime. TJS has `Generic` declarations\nthat produce runtime-checkable type constructors:\n\n```typescript\n// TypeScript -- compile-time only, gone at runtime\nfunction identity<T>(x: T): T { return x }\ninterface Box<T> { value: T }\n\n// TJS -- no function-level generics; use Generic for container types\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// Usage: Box(Number) is a runtime type checker\nconst isNumberBox = Box(Number)\nisNumberBox({ value: 42 }) // true\nisNumberBox({ value: 'nope' }) // false\n```\n\nFor simple generic functions like `identity` or `first`, you don't need\ngenerics at all -- just skip the type parameter. TJS validates the\nconcrete types at call sites, not the abstract relationship between them.\n\n```javascript\n// Simple -- no generics needed, the function just works\nfunction first(arr: [0]) { return arr[0] }\n\n// If you need runtime-checked containers, use Generic\nGeneric Pair<T, U> {\n description: 'a typed pair'\n predicate(x, T, U) { return T(x[0]) && U(x[1]) }\n}\n```\n\nWhen converting from TypeScript, the `fromTS` converter preserves generic\nmetadata but types become `any`. This is a place where manual review helps.\n\n### Nullability\n\n```typescript\n// TypeScript\nfunction find(id: number): User | null { ... }\n\n// TJS\nfunction find(id: 0): { name: '', age: 0 } || null { ... }\n```\n\nTJS distinguishes `null` from `undefined` -- they're different types, just\nas `typeOf(null)` returns `'null'` and `typeOf(undefined)` returns\n`'undefined'`. Writing `|| null` means the value can be the base type or\n`null`, but not `undefined`. Optional parameters (using `=`) accept\n`undefined` because that's what you get when the caller omits the argument.\n\n## What TypeScript Has That TJS Doesn't\n\nTJS intentionally skips TypeScript features that don't survive to runtime\nor add complexity without proportional value:\n\n| TypeScript Feature | TJS Equivalent |\n| --------------------------- | ------------------------------------------- |\n| `interface` | `Type` with example |\n| `type` aliases | `Type`, `Union`, or `Enum` |\n| Conditional types | Preserved in `.d.ts` via declaration blocks |\n| Mapped types | Preserved in `.d.ts` via declaration blocks |\n| `keyof`, `typeof` | Use runtime `Object.keys()`, `typeOf()` |\n| `Partial<T>`, `Pick<T>` | Define the shape you need directly |\n| Declaration files (`.d.ts`) | Generated from `__tjs` metadata (`--dts`) |\n| `as` type assertions | Not needed (values are checked) |\n| `any` escape hatch | `safety none` per-module or `!` per-fn |\n| Decorators | Not supported |\n| `namespace` | Use modules |\n\nThe philosophy: if a type feature doesn't do something at runtime, it's\ncomplexity without payoff.\n\n### But What About Narrowing?\n\nTypeScript's type system is Turing-complete. You can express astonishing\nconstraints -- `Pick<Omit<T, K>, Extract<keyof T, string>>` -- but the\nresulting types are often harder to understand than the code they describe.\nAnd they vanish at runtime, so they can't protect you from bad API data.\n\nTJS takes the opposite approach: `Type()` gives you a predicate function.\nIf you can write a boolean expression, you can define a type. No type-level\nprogramming language to learn.\n\n```javascript\n// TypeScript: branded types + manual validation\ntype Email = string & { __brand: 'email' }\nfunction isEmail(s: string): s is Email {\n return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(s)\n}\nfunction validateEmail(input: string): Email {\n if (!isEmail(input)) throw new Error('Invalid email')\n return input\n}\n\n// TJS: one line, works at runtime\nType Email {\n description: 'email address'\n example: 'user@example.com'\n predicate(v) { return typeof v === 'string' && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(v) }\n}\n```\n\nThe TJS `Type()` built-in handles everything from simple shapes to\nsophisticated domain constraints:\n\n```javascript\n// Simple -- infer from example\nType Name 'Alice' // string\n\n// Constrained -- predicate narrows beyond the base type\nType PositiveInt {\n description: 'a positive integer'\n example: 1\n predicate(v) { return typeof v === 'number' && Number.isInteger(v) && v > 0 }\n}\n\n// Domain-specific -- readable business rules\nType USZipCode {\n description: '5-digit US zip code'\n example: '90210'\n predicate(v) { return typeof v === 'string' && /^\\d{5}$/.test(v) }\n}\n\n// Combinators -- compose types\nType OptionalEmail Nullable(Email) // Email | null\n\n// Schema-based -- use tosijs-schema for structured validation\nType AgeRange {\n description: 'valid age'\n example: 25\n predicate(v) { return typeof v === 'number' && v >= 0 && v <= 150 }\n}\n```\n\nCompare the TypeScript equivalents:\n\n| What you want | TypeScript | TJS |\n| ------------------- | ----------------------------------------------------------- | -------------------------------------- |\n| String with format | Branded type + type guard + validation function | `Type Email { predicate(v) {...} }` |\n| Number in range | Branded type + manual check | `Type Age { predicate(v) {...} }` |\n| Non-empty string | Template literal type (compile-only) | `Type NonEmpty { predicate(v) {...} }` |\n| Nullable variant | `T \\| null` (compile-only) | `Nullable(MyType)` (runtime-checked) |\n| Union of literals | `'a' \\| 'b' \\| 'c'` (compile-only) | `Union Status 'a' \\| 'b' \\| 'c'` |\n| Discriminated union | `type Shape = { kind: 'circle' } \\| ...` + manual narrowing | `Union('kind', { circle: {...} })` |\n| Generic container | `interface Box<T>` (compile-only) | `Generic Box<T> { predicate(...) }` |\n\nEvery row in the TypeScript column is compile-time fiction that disappears\nwhen your code runs. Every row in the TJS column is a runtime check that\nactually catches bugs in production. And the TJS versions are shorter,\nbecause a predicate is just a function -- not a type-level program.\n\nTJS also ships common types out of the box: `TString`, `TNumber`,\n`TBoolean`, `TInteger`, `TPositiveInt`, `TNonEmptyString`, `TEmail`,\n`TUrl`, `TUuid`, `Timestamp`, `LegalDate`. No imports from a validation\nlibrary needed.\n\n### Tooling Comparison\n\n| Concern | TypeScript | TJS |\n| ------------------- | ---------------------------------- | ---------------------------------------------------------------------- |\n| **Type checking** | `tsc` (compile-time only) | Runtime validation (survives build) |\n| **Runtime schemas** | Zod / io-ts / Ajv (separate) | Built-in (types _are_ schemas) |\n| **Linting** | ESLint + plugins | Built-in linter (unused vars, unreachable code, no-explicit-new) |\n| **Testing** | Vitest / Jest (separate files) | Inline `test` blocks (transpile-time) |\n| **Equality** | Reference-based only | Honest `==` (no coercion), `Is`/`IsNot` (structural), `===` (identity) |\n| **Build toolchain** | tsc + bundler (webpack/Vite/etc) | Transpiles in-browser, no build step |\n| **Debugging** | Source maps (brittle, build bloat) | Functions carry source identity via `__tjs` metadata |\n| **Documentation** | JSDoc / TypeDoc (manual) | Generated from `__tjs` metadata |\n| **Editor support** | Mature (VSCode, etc) | Monaco/CodeMirror/Ace + VSCode/Cursor extensions |\n\n## What TJS Has That TypeScript Doesn't\n\n### Runtime Validation\n\nTypeScript:\n\n```typescript\n// Types are a promise. A lie, if the data comes from outside.\nfunction processOrder(order: Order) {\n // If order came from an API, nothing guarantees it matches Order.\n // You need Zod/io-ts/ajv AND the TypeScript type AND keep them in sync.\n}\n```\n\nTJS:\n\n```javascript\n// Types are checked at runtime. One source of truth.\nfunction processOrder(order: { items: [{ id: 0, qty: 0 }], total: 0 }): {\n status: '',\n} {\n // If order doesn't match, caller gets a MonadicError -- no crash.\n}\n```\n\n### Honest Equality\n\nTypeScript inherits JavaScript's broken equality. Native TJS fixes this by default. For TS-originated code, add the `TjsEquals` directive (or use `/* @tjs TjsEquals */` in the source `.ts` file):\n\n```javascript\nTjsEquals // needed for TS-originated code; native TJS has this on by default\n\n// == is honest: no coercion, unwraps boxed primitives\n0 == '' // false (JS: true!)\n[] == ![] // false (JS: true!)\nnew String('foo') == 'foo' // true (unwraps boxed)\nnull == undefined // true (useful pattern preserved)\ntypeof null // 'null' (JS: 'object')\n\n// == is fast: O(1) reference equality for objects/arrays\n{a: 1} == {a: 1} // false (different refs)\n[1, 2] == [1, 2] // false (different refs)\n\n// Is/IsNot for explicit deep structural comparison (O(n))\n{a: 1} Is {a: 1} // true\n[1, 2, 3] Is [1, 2, 3] // true\nnew Set([1,2]) Is new Set([2,1]) // true (Sets are order-independent)\n\n// === unchanged: identity check\nobj === obj // true (same reference)\n```\n\n`==` fixes coercion without the performance cost of deep comparison.\nUse `Is`/`IsNot` when you explicitly need structural comparison.\n\n### Monadic Errors\n\nTypeScript uses exceptions. TJS uses values:\n\n```javascript\n// TypeScript -- you have to remember to try/catch\nfunction divide(a: number, b: number): number {\n if (b === 0) throw new Error('Division by zero')\n return a / b\n}\n// Caller forgets try/catch? Crash.\n\n// TJS -- errors flow through the pipeline\nfunction divide(a: 0, b: 0): 0 {\n if (b === 0) return MonadicError('Division by zero')\n return a / b\n}\n// Caller gets an error value. No crash. Ever.\n```\n\n**How the caller handles it:**\n\n```javascript\n// Option 1: Check the result\nconst result = divide(10, 0)\nif (result instanceof Error) {\n console.log(result.message) // 'Division by zero'\n} else {\n useResult(result)\n}\n\n// Option 2: Just keep going -- errors propagate automatically\nconst a = divide(10, 0) // MonadicError\nconst b = double(a) // Receives error, returns it immediately (skips execution)\nconst c = format(b) // Same -- error flows through the whole chain\n// c is still the original MonadicError from divide()\n```\n\nIf you've used Rust's `Result<T, E>` or Haskell's `Either`, the pattern\nis familiar. The key difference from TypeScript: you never have to guess\nwhether a function might throw. Type errors and validation failures are\nalways values, never exceptions.\n\n### Inline Tests\n\n```javascript\nfunction fibonacci(n: 0): 0 {\n if (n <= 1) return n\n return fibonacci(n - 1) + fibonacci(n - 2)\n}\n\ntest 'fibonacci sequence' {\n expect(fibonacci(0)).toBe(0)\n expect(fibonacci(1)).toBe(1)\n expect(fibonacci(10)).toBe(55)\n}\n```\n\nTests run at transpile time. They're stripped from production output.\nNo separate test files, no test runner configuration.\n\n### Safety Controls\n\n```javascript\nsafety none // This module: skip all validation (performance)\nsafety inputs // This module: validate inputs only (default)\nsafety all // This module: validate everything (debug)\n\n// Per-function overrides\nfunction hot(! x: 0) {} // Skip validation even if module says 'inputs'\nfunction safe(? x: 0) {} // Force validation even if module says 'none'\n```\n\nUse `!` (skip validation) only in hot loops where every microsecond counts\nand the data source is already trusted. In all other cases, the ~1.5x\noverhead of `safety inputs` is negligible compared to the bugs it catches.\n\n#### Additional Safety Features\n\n```javascript\nTjsNoVar // var declarations are syntax errors\nconst! config = {} // Compile-time immutability (zero runtime cost)\n\n// Debug mode: make type errors visible\nimport { configure } from 'tjs-lang/lang'\nconfigure({ logTypeErrors: true }) // console.error on every type error\nconfigure({ throwTypeErrors: true }) // throw instead of returning MonadicError\n```\n\nTypeScript has no equivalent to most of these. You're either all-in on\ntypes or you use `as any` to escape.\n\n---\n\n## Automatic Conversion\n\nTJS includes a TypeScript-to-TJS converter that has been validated against\nthe tosijs production codebase (35 files, 523 tests passing):\n\n```bash\n# Convert a single file\nbun src/cli/tjs.ts convert input.ts --emit-tjs > output.tjs\n\n# Convert a directory (produces .js + .d.ts + .md per file)\nbun src/cli/tjs.ts convert src/ -o tjs-out/\n\n# Emit JavaScript directly\nbun src/cli/tjs.ts convert input.ts > output.js\n```\n\nFrom code:\n\n```typescript\nimport { fromTS } from 'tjs-lang/lang/from-ts'\n\nconst result = fromTS(tsSource, { emitTJS: true })\nconsole.log(result.code) // TJS source\n```\n\n### What `fromTS` Handles\n\n- Primitive annotations (`string`, `number`, `boolean`) → example values\n- Interfaces → `Type` declarations with declaration blocks for `.d.ts` round-tripping\n- Generic interfaces → `Generic` with predicates + declaration blocks\n- Conditional/mapped types → preserved verbatim in declaration blocks\n- Function type aliases → `FunctionPredicate` declarations (including generics)\n- String literal unions → `Union`\n- Enums → `Enum`\n- Rest parameters (`...args: T[]`) → preserved with `...` prefix\n- Nullable types (`T | null`) → proper null guards in runtime checks\n- Optional params, default values\n- `private` → stripped (or `#` with `TjsClass`)\n- Static getters/setters → `static` keyword preserved\n- `Promise<T>` → unwrapped return types\n- DOM types (130+) → `{}` (opaque object, keeps params annotated)\n- JSDoc comments → TDoc comments\n- Exported constants → type-inferred `.d.ts` entries\n\n### `@tjs` Annotations in TypeScript\n\nAnnotate your `.ts` files with `/* @tjs ... */` comments to enrich\nthe TJS output. The TS compiler ignores them.\n\n```typescript\n/* @tjs TjsClass TjsEquals */ // Enable TJS modes (off by default in TS-originated code)\n\n/* @tjs-skip */ // Skip this type declaration\nexport type Unboxed<T> = T extends { value: infer U } ? U : T\n\n/* @tjs predicate(x, T) { return typeof x === 'object' && T(x.value) } */\nexport interface Box<T> {\n value: T\n}\n\n/* @tjs example: { name: 'Alice', age: 30 } */\nexport interface User {\n name: string\n age: number\n}\n\n/* @tjs declaration { value: T; path: string } */\nexport interface BoxedProxy<T> {\n /* complex conditional type */\n}\n```\n\n### `.d.ts` Generation\n\nThe DTS emitter produces TypeScript declarations from TJS transpilation:\n\n```bash\nbun src/cli/tjs.ts emit input.tjs # emits .js, .d.ts, .md\n```\n\n- **Interfaces with declaration blocks** → `export interface Name<T> { ... }`\n- **Conditional/mapped types** → `export type Name<T> = ...` (verbatim TS body)\n- **Function types** → `export type Name = (...) => T`\n- **Simple type aliases** → `export type Name = original TS body`\n- **Constants** → `export declare const Name: type`\n- **Functions** → `export declare function Name(params): returnType`\n- **Classes** → callable function + class declaration\n\n### Constrained Generics\n\nWhen the converter encounters a constrained generic like\n`<T extends { id: number }>`, it uses the constraint shape as the\nexample value instead of falling back to `any`. This means:\n\n```typescript\n// TypeScript\nfunction first<T extends { id: number }>(items: T[]): T {\n return items[0]\n}\n\n// Converted TJS — uses constraint shape, not 'any'\nfunction first(items: [{ id: 0.0 }]):! { id: 0.0 } { ... }\n```\n\nGeneric defaults also work: `<T = string>` uses `string` as the example.\nUnconstrained generics (`<T>` with no `extends` or default) still degrade\nto `any` — there's genuinely no information about what T is.\n\n### What `fromTS` Can't Fully Express\n\nTJS types are example values, not abstract type algebra. Some TypeScript\npatterns have no direct TJS equivalent — but most now preserve their\noriginal TS body for `.d.ts` round-tripping:\n\n| TypeScript Pattern | What Happens |\n| ------------------------------------------- | ----------------------------------------------------- |\n| Conditional types (`T extends U ? X : Y`) | TS body preserved verbatim in `.d.ts` |\n| Mapped types (`{ [K in keyof T]: ... }`) | TS body preserved verbatim in `.d.ts` |\n| Intersection types (`A & B`) | TS body preserved verbatim in `.d.ts` |\n| `Partial<T>`, `Required<T>`, `Pick`, `Omit` | Emits warning, uses base shape |\n| `ReturnType<T>`, `Parameters<T>` | Drops to `any` |\n| Template literal types (`` `${A}-${B}` ``) | Becomes `string` |\n| Deeply nested generics (`Foo<Bar<U>>`) | Inner params become `any` |\n| `readonly`, `as const` | Stripped (use `const!` for compile-time immutability) |\n\nThe key improvement: complex types that can't be expressed as runtime\npredicates are still preserved in the `.d.ts` output via declaration blocks.\nThe runtime code works with `any`, but TypeScript consumers of your library\nget the full type information.\n\n## Migration Strategy\n\n### Incremental Adoption\n\nYou don't have to convert everything at once:\n\n1. **Start at boundaries.** Convert API handlers and validation layers\n first -- these benefit most from runtime types.\n2. **Convert hot modules.** Modules with frequent type-related bugs are\n good candidates.\n3. **Leave internals for last.** Pure computational code that's already\n well-tested benefits least from migration.\n\n### The Bun Plugin\n\nIf you use Bun, `.tjs` files work alongside `.ts` files with zero config:\n\n```javascript\n// bunfig.toml already preloads the TJS plugin\nimport { processOrder } from './orders.tjs' // just works\nimport { validateUser } from './users.ts' // also works\n```\n\n### What to Watch For\n\n**Example values matter.** `count: 0` means \"number, example is 0.\" If\nyour function breaks on 0 (division, array index), the automatic signature\ntest will catch it immediately. Choose examples that exercise the\nhappy path.\n\n**Return types generate tests.** `: 0` means TJS will call your function\nwith the parameter examples and check the result. If your function has\nside effects or requires setup, use `:! 0` to skip the signature test.\n\n**Structural equality changes behavior.** If your code relies on `==`\nfor type coercion (comparing numbers to strings, etc.), you'll need to\nupdate those comparisons. This is almost always a bug fix.\n\n---\n\n## Side-by-Side: A Complete Example\n\n### TypeScript\n\n```typescript\ninterface Product {\n id: string\n name: string\n price: number\n tags: string[]\n}\n\ninterface CartItem {\n product: Product\n quantity: number\n}\n\nfunction calculateTotal(items: CartItem[], taxRate: number = 0.1): number {\n const subtotal = items.reduce(\n (sum, item) => sum + item.product.price * item.quantity,\n 0\n )\n return Math.round(subtotal * (1 + taxRate) * 100) / 100\n}\n\nfunction applyDiscount(\n total: number,\n code: string | null\n): { final: number; discount: number } {\n const discounts: Record<string, number> = {\n SAVE10: 0.1,\n SAVE20: 0.2,\n }\n const rate = code ? discounts[code] ?? 0 : 0\n return {\n final: Math.round(total * (1 - rate) * 100) / 100,\n discount: rate,\n }\n}\n```\n\n### TJS\n\n```javascript\nType Product {\n description: 'a product in the catalog'\n example: { id: '', name: '', price: 0.0, tags: [''] }\n}\n\nType CartItem {\n description: 'a product with quantity'\n example: { product: { id: '', name: '', price: 0.0, tags: [''] }, quantity: +0 }\n}\n\nfunction calculateTotal(items: [CartItem], taxRate = 0.1): 0.0 {\n const subtotal = items.reduce(\n (sum, item) => sum + item.product.price * item.quantity,\n 0\n )\n return Math.round(subtotal * (1 + taxRate) * 100) / 100\n}\n\nfunction applyDiscount(total: 0.0, code: '' || null): { final: 0.0, discount: 0.0 } {\n const discounts = {\n SAVE10: 0.1,\n SAVE20: 0.2,\n }\n const rate = code ? discounts[code] ?? 0 : 0\n return {\n final: Math.round(total * (1 - rate) * 100) / 100,\n discount: rate,\n }\n}\n\ntest 'cart calculation' {\n const items = [\n { product: { id: '1', name: 'Widget', price: 10, tags: [] }, quantity: 3 }\n ]\n expect(calculateTotal(items, 0)).toBe(30)\n expect(calculateTotal(items, 0.1)).toBe(33)\n}\n\ntest 'discount codes' {\n expect(applyDiscount(100, 'SAVE10')).toEqual({ final: 90, discount: 0.1 })\n expect(applyDiscount(100, null)).toEqual({ final: 100, discount: 0 })\n expect(applyDiscount(100, 'INVALID')).toEqual({ final: 100, discount: 0 })\n}\n```\n\nThe TJS version is about the same length, but the types exist at runtime,\nthe tests live with the code, and invalid inputs return errors instead\nof crashing.\n\n---\n\n## Traceability: The Death of Source Maps\n\nTypeScript debugging relies on source maps -- external files that try to\nmap minified, transpiled JavaScript back to your original code. They're\nbrittle, often out of sync, and fail entirely in complex build pipelines.\n\nTJS eliminates source maps. Every function carries its source identity\nin `__tjs` metadata:\n\n```javascript\nfunction add(a: 0, b: 0): 0 {\n return a + b\n}\n\nadd.__tjs.source // \"mymodule.tjs:3\"\n```\n\n- **Zero-config debugging:** If a function fails validation, the error\n points to the exact line in your `.tjs` source, not a generated `.js` file.\n- **Transparent eval:** Even code run via `Eval()` or the `AgentVM`\n provides clear traces because AST and source metadata are preserved.\n- **No build bloat:** You don't ship `.map` files to production just to\n know why your app crashed.\n\n---\n\n## FAQ\n\n### How do I use TJS with existing NPM packages?\n\nTJS is a superset of JavaScript. Import any NPM package as usual:\n\n```javascript\nimport lodash from 'https://esm.sh/lodash@4.17.21'\nimport { z } from 'zod' // works, though you won't need it\n```\n\nWhen you import a vanilla JS library, its exports have no TJS metadata.\nYou can wrap them in a TJS boundary to get runtime safety:\n\n```javascript\nimport { rawGeocode } from 'legacy-geo-pkg'\n\n// Wrap to validate at your system boundary\nfunction geocode(addr: ''): { lat: 0.0, lon: 0.0 } {\n return rawGeocode(addr)\n}\n```\n\nThe untyped library code runs freely. Your TJS wrapper validates the\nresult before it enters your typed world.\n\n### Do Proxies, WeakMaps, and other advanced patterns work?\n\nYes. TJS is purely additive — it adds inline type checks and metadata\nproperties but does not wrap, intercept, or modify JavaScript runtime\nbehavior. Specifically:\n\n- **Proxies** work identically to plain JS. TJS attaches `.__tjs` as a\n plain property on function objects, which doesn't trigger Proxy traps.\n If your Proxy needs custom equality, use the `[tjsEquals]` symbol protocol.\n- **WeakMap/WeakSet** are unaffected. TJS doesn't inspect collection contents.\n- **Symbols** work normally. TJS reserves `Symbol.for('tjs.equals')` for\n custom equality but doesn't interfere with other symbols.\n- **`Object.defineProperty`**, getters/setters, non-enumerable properties\n — all work as expected. TJS validation checks value types, not property\n descriptors.\n- **Prototype chains** are preserved. `instanceof` works correctly with\n TJS-wrapped classes.\n\nIf you're building a Proxy-heavy library (reactive state, ORMs, etc.),\nTJS will not interfere. The transpiled output is plain JavaScript with\nsome `typeof` checks at function entry points.\n\n### Does structural equality (`==`) handle circular references?\n\nNo. Circular structures will cause infinite recursion. Use identity\ncomparison (`===`) for objects that might be circular, or define a\ncustom `.Equals` method on the class.\n\n### What happens to TypeScript's `strict` mode checks?\n\nTJS doesn't have `strictNullChecks` or `noImplicitAny` because the\nproblems they solve don't exist:\n\n- **Null safety:** `|| null` explicitly marks nullable parameters.\n Functions without it reject null at runtime.\n- **Implicit any:** Every TJS parameter has an example value that\n determines its type. There's nothing to be implicit about.\n- **Strict property access:** Runtime validation catches missing\n properties with a clear error message instead of `undefined`.\n\n---\n\n## Learn More\n\n- [TJS Language Reference](DOCS-TJS.md) -- Full syntax and features\n- [TJS for JavaScript Programmers](TJS-FOR-JS.md) -- Coming from vanilla JS?\n- [AJS Agent Language](DOCS-AJS.md) -- The sandboxed agent VM\n- [Playground](https://tjs-platform.web.app) -- Try it live\n"
|
|
1621
1621
|
}
|
|
1622
|
-
]
|
|
1622
|
+
]
|