tjs-lang 0.8.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +15 -5
- package/CONTEXT.md +4 -0
- package/demo/docs.json +12 -690
- package/demo/examples.test.ts +6 -2
- package/demo/src/playground-shared.ts +48 -16
- package/demo/src/playground-test-results.test.ts +112 -0
- package/demo/src/tjs-playground.ts +11 -5
- package/dist/eslint.config.d.ts +2 -0
- package/dist/index.js +146 -141
- package/dist/index.js.map +4 -4
- package/dist/src/lang/core.d.ts +1 -1
- package/dist/src/lang/dialect.d.ts +35 -0
- package/dist/src/lang/emitters/ast.d.ts +1 -1
- package/dist/src/lang/emitters/js-tests.d.ts +7 -0
- package/dist/src/lang/emitters/js-wasm.d.ts +5 -1
- package/dist/src/lang/emitters/js.d.ts +21 -0
- package/dist/src/lang/index.d.ts +3 -1
- package/dist/src/lang/module-loader.d.ts +125 -0
- package/dist/src/lang/parser-transforms.d.ts +79 -0
- package/dist/src/lang/parser-types.d.ts +50 -0
- package/dist/src/lang/parser.d.ts +18 -0
- package/dist/src/lang/transpiler.d.ts +1 -0
- package/dist/src/lang/types.d.ts +18 -0
- package/dist/src/lang/wasm.d.ts +67 -1
- package/dist/src/vm/runtime.d.ts +18 -0
- package/dist/src/vm/vm.d.ts +15 -1
- package/dist/tjs-batteries.js +2 -2
- package/dist/tjs-batteries.js.map +2 -2
- package/dist/tjs-eval.js +43 -41
- package/dist/tjs-eval.js.map +3 -3
- package/dist/tjs-from-ts.js +2 -2
- package/dist/tjs-from-ts.js.map +2 -2
- package/dist/tjs-lang.js +107 -104
- package/dist/tjs-lang.js.map +4 -4
- package/dist/tjs-vm.js +53 -51
- package/dist/tjs-vm.js.map +3 -3
- package/docs/README.md +2 -0
- package/docs/lm-studio-setup.md +143 -0
- package/docs/universal-endpoint.md +122 -0
- package/llms.txt +9 -2
- package/package.json +24 -4
- package/src/batteries/audit.ts +6 -5
- package/src/batteries/llm.ts +8 -3
- package/src/builder.ts +0 -3
- package/src/cli/commands/check.ts +3 -2
- package/src/cli/commands/emit.ts +4 -2
- package/src/cli/commands/run.ts +6 -2
- package/src/cli/commands/test.ts +1 -1
- package/src/cli/commands/types.ts +2 -2
- package/src/lang/codegen.test.ts +4 -1
- package/src/lang/core.ts +6 -4
- package/src/lang/dialect.test.ts +63 -0
- package/src/lang/dialect.ts +50 -0
- package/src/lang/docs.test.ts +0 -1
- package/src/lang/emitters/ast.ts +145 -2
- package/src/lang/emitters/from-ts.ts +1 -1
- package/src/lang/emitters/js-tests.ts +46 -37
- package/src/lang/emitters/js.ts +19 -3
- package/src/lang/features.test.ts +6 -5
- package/src/lang/index.ts +18 -5
- package/src/lang/linter.ts +1 -1
- package/src/lang/module-loader.test.ts +9 -5
- package/src/lang/module-loader.ts +4 -5
- package/src/lang/parser-params.ts +1 -1
- package/src/lang/parser-transforms.ts +12 -18
- package/src/lang/parser-types.ts +17 -0
- package/src/lang/parser.test.ts +12 -6
- package/src/lang/parser.ts +113 -3
- package/src/lang/perf.test.ts +10 -4
- package/src/lang/runtime.ts +0 -1
- package/src/lang/subset-invariant.test.ts +90 -0
- package/src/lang/transpiler.ts +8 -0
- package/src/lang/types.ts +18 -0
- package/src/lang/wasm.test.ts +22 -16
- package/src/linalg/linalg.test.ts +16 -4
- package/src/linalg/vector-search.bench.test.ts +31 -10
- package/src/types/Type.ts +6 -6
- package/src/use-cases/asymmetric-client-server.test.ts +0 -2
- package/src/use-cases/batteries.test.ts +4 -0
- package/src/use-cases/client-server.test.ts +1 -1
- package/src/use-cases/local-helpers.test.ts +219 -0
- package/src/use-cases/timeout-overrides.test.ts +169 -0
- package/src/use-cases/unbundled-imports.test.ts +0 -1
- package/src/vm/atoms/batteries.ts +17 -3
- package/src/vm/runtime.ts +92 -7
- package/src/vm/vm.ts +50 -10
package/dist/src/lang/core.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type { SeqNode } from '../builder';
|
|
|
8
8
|
import type { TranspileOptions, TranspileResult, FunctionSignature } from './types';
|
|
9
9
|
import { type TJSTranspileResult, type TJSTranspileOptions } from './emitters/js';
|
|
10
10
|
export * from './types';
|
|
11
|
-
export { parse, preprocess, extractTDoc, validateSingleFunction, } from './parser';
|
|
11
|
+
export { parse, preprocess, extractTDoc, validateSingleFunction, extractFunctions, } from './parser';
|
|
12
12
|
export { transformFunction } from './emitters/ast';
|
|
13
13
|
/**
|
|
14
14
|
* Transpile JavaScript source code to Agent99 AST
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dialect resolution for file-based tooling.
|
|
3
|
+
*
|
|
4
|
+
* The subset invariant (see PRINCIPLES.md) says plain JavaScript must transpile
|
|
5
|
+
* through TJS without changing meaning, while native `.tjs` gets the full
|
|
6
|
+
* footgun-removal feature set. The unit of opt-in is the *dialect* — and for a
|
|
7
|
+
* file, the dialect is its extension. This is the one canonical mapping that
|
|
8
|
+
* CLIs, bundler plugins, module loaders, and doc systems should all share so
|
|
9
|
+
* `.js` is never silently "improved" into different semantics.
|
|
10
|
+
*/
|
|
11
|
+
/** Source dialect understood by `tjs(src, { dialect })`. */
|
|
12
|
+
export type Dialect = 'js' | 'tjs';
|
|
13
|
+
/** How a file should be transpiled, based on its extension. */
|
|
14
|
+
export type SourceKind = 'js' | 'ts' | 'tjs';
|
|
15
|
+
/**
|
|
16
|
+
* Classify a file by extension into the source kind that determines its
|
|
17
|
+
* transpile path:
|
|
18
|
+
* - `'tjs'` → native TJS: `tjs(src)` (all modes ON — the better language)
|
|
19
|
+
* - `'js'` → plain JS: `tjs(src, { dialect: 'js' })` (semantics preserved)
|
|
20
|
+
* - `'ts'` → TypeScript: route through `fromTS` (TS → TJS → JS)
|
|
21
|
+
*
|
|
22
|
+
* Unknown extensions default to `'tjs'` (the native language). `.d.ts` is
|
|
23
|
+
* treated as TypeScript.
|
|
24
|
+
*/
|
|
25
|
+
export declare function sourceKindForFilename(filename: string): SourceKind;
|
|
26
|
+
/**
|
|
27
|
+
* Map a filename to the `dialect` option for `tjs()`. `.js`/`.mjs`/`.cjs` ⇒
|
|
28
|
+
* `'js'` (plain JavaScript, semantics preserved); everything else ⇒ `'tjs'`
|
|
29
|
+
* (native TJS).
|
|
30
|
+
*
|
|
31
|
+
* NOTE: TypeScript files are NOT a `tjs()` dialect — transpile them with
|
|
32
|
+
* `fromTS` first. Use {@link sourceKindForFilename} when you need to tell
|
|
33
|
+
* `.ts` apart from `.js`/`.tjs`.
|
|
34
|
+
*/
|
|
35
|
+
export declare function dialectForFilename(filename: string): Dialect;
|
|
@@ -9,7 +9,7 @@ import type { TransformContext, TranspileOptions, FunctionSignature, TranspileWa
|
|
|
9
9
|
/**
|
|
10
10
|
* Transform a function declaration into Agent99 AST
|
|
11
11
|
*/
|
|
12
|
-
export declare function transformFunction(func: FunctionDeclaration, source: string, returnTypeAnnotation: string | undefined, options?: TranspileOptions, requiredParamsFromPreprocess?: Set<string>): {
|
|
12
|
+
export declare function transformFunction(func: FunctionDeclaration, source: string, returnTypeAnnotation: string | undefined, options?: TranspileOptions, requiredParamsFromPreprocess?: Set<string>, helpers?: Map<string, FunctionDeclaration>): {
|
|
13
13
|
ast: BaseNode;
|
|
14
14
|
signature: FunctionSignature;
|
|
15
15
|
warnings: TranspileWarning[];
|
|
@@ -13,6 +13,13 @@ export interface TestResult {
|
|
|
13
13
|
error?: string;
|
|
14
14
|
/** Whether this was an implicit signature test */
|
|
15
15
|
isSignatureTest?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* The test could not be *run* (e.g. it references a name TJS can't resolve at
|
|
18
|
+
* build time — like an AJS atom — or the harness couldn't execute the module).
|
|
19
|
+
* Inconclusive ≠ failed: it never blocks transpilation. Surfaced as a warning
|
|
20
|
+
* (e.g. in the playground). See PRINCIPLES.md ("more, never illegal").
|
|
21
|
+
*/
|
|
22
|
+
inconclusive?: boolean;
|
|
16
23
|
/** Source line number (1-indexed) where the test or error occurred */
|
|
17
24
|
line?: number;
|
|
18
25
|
/** Source column number (1-indexed) */
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* TJS WASM Bootstrap Generation
|
|
3
3
|
*
|
|
4
|
-
* Compiles
|
|
4
|
+
* Compiles the file's WASM blocks into a single WebAssembly.Module with
|
|
5
|
+
* one exported function per block, then emits JavaScript that compiles
|
|
6
|
+
* and instantiates the module once at startup. This is the foundation
|
|
7
|
+
* for cross-file wasm composition (see wasm-library-plan.md, Phase 3) —
|
|
8
|
+
* once everything's in one module, intra-module calls cost nothing.
|
|
5
9
|
*/
|
|
6
10
|
import type { WasmBlock } from '../parser';
|
|
7
11
|
export declare function generateWasmBootstrap(blocks: WasmBlock[]): {
|
|
@@ -54,6 +54,18 @@ export interface TJSTranspileOptions {
|
|
|
54
54
|
sourceMap?: boolean;
|
|
55
55
|
/** Mode: 'dev' | 'strict' | 'production' */
|
|
56
56
|
mode?: 'dev' | 'strict' | 'production';
|
|
57
|
+
/**
|
|
58
|
+
* Source dialect — what kind of source this string is:
|
|
59
|
+
* - `'tjs'` (default for a bare string): native TJS, footgun-removal modes ON
|
|
60
|
+
* (structural `==`, `TjsStandard`, etc.).
|
|
61
|
+
* - `'js'`: plain JavaScript — modes OFF, `safety: 'none'`; the source's own
|
|
62
|
+
* semantics are preserved (no `==`→`Eq`, no truthiness rewrite, no input
|
|
63
|
+
* validation). Use this when feeding tjs() a vanilla `.js` string so it
|
|
64
|
+
* transpiles without changing meaning. See PRINCIPLES.md (TJS ⊇ JS).
|
|
65
|
+
*
|
|
66
|
+
* For TypeScript, use the `fromTS` entry point (TS → TJS → JS).
|
|
67
|
+
*/
|
|
68
|
+
dialect?: 'js' | 'tjs';
|
|
57
69
|
/**
|
|
58
70
|
* Test execution mode:
|
|
59
71
|
* - true (default): run tests at transpile time, throw on failure
|
|
@@ -74,6 +86,15 @@ export interface TJSTranspileOptions {
|
|
|
74
86
|
* Used when tests depend on imported modules.
|
|
75
87
|
*/
|
|
76
88
|
resolvedImports?: Record<string, string>;
|
|
89
|
+
/**
|
|
90
|
+
* Optional ModuleLoader for cross-file `wasm function` composition (Phase 3
|
|
91
|
+
* of the wasm-library plan). When provided, `import { dot } from
|
|
92
|
+
* 'tjs-lang/linalg'`-style imports are resolved at transpile time and any
|
|
93
|
+
* matching `wasm function` declarations are composed into this file's
|
|
94
|
+
* consolidated WebAssembly.Module. When omitted, imports are preserved
|
|
95
|
+
* verbatim (default behavior — runtime resolves them).
|
|
96
|
+
*/
|
|
97
|
+
moduleLoader?: any;
|
|
77
98
|
}
|
|
78
99
|
/** Result of running tests at transpile time */
|
|
79
100
|
export type { TestResult } from './js-tests';
|
package/dist/src/lang/index.d.ts
CHANGED
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
import type { SeqNode } from '../builder';
|
|
24
24
|
import type { TranspileOptions, TranspileResult, FunctionSignature } from './types';
|
|
25
25
|
export * from './types';
|
|
26
|
-
export { parse, preprocess, extractTDoc } from './parser';
|
|
26
|
+
export { parse, preprocess, extractTDoc, validateSingleFunction, extractFunctions, } from './parser';
|
|
27
|
+
export { dialectForFilename, sourceKindForFilename, type Dialect, type SourceKind, } from './dialect';
|
|
27
28
|
export { transformFunction } from './emitters/ast';
|
|
28
29
|
export { transpileToJS, stripModuleSyntax, stripTjsPreamble, type TJSTranspileOptions, type TJSTranspileResult, type TJSTypeInfo, } from './emitters/js';
|
|
29
30
|
export { generateDTS, typeDescriptorToTS, type GenerateDTSOptions, } from './emitters/dts';
|
|
@@ -34,6 +35,7 @@ export { typeDescriptorToJSONSchema, exampleToJSONSchema, functionMetaToJSONSche
|
|
|
34
35
|
export { MetadataCache, getGlobalCache, setGlobalCache } from './metadata-cache';
|
|
35
36
|
export { lint, type LintResult, type LintDiagnostic, type LintOptions, } from './linter';
|
|
36
37
|
export { generateDocs, generateDocsMarkdown, type DocResult, type DocItem, type FunctionTypeInfo, type ParamTypeInfo, } from './docs';
|
|
38
|
+
export { ModuleLoader, inMemoryFileSystem, type ModuleLoaderOptions, type FileSystem, type LoadedModule, type ImportEntry, type ExportEntry, } from './module-loader';
|
|
37
39
|
export { extractTests, assertFunction, expectFunction, testUtils, type ExtractedTest, type ExtractedMock, type TestExtractionResult, } from './tests';
|
|
38
40
|
export { runtime, installRuntime, isError, error, typeOf, checkType, validateArgs, wrap, emitRuntimeWrapper, TJS_VERSION, type TJSError, } from './runtime';
|
|
39
41
|
export { compileToWasm, instantiateWasm, registerWasmBlock, compileWasmBlocks, type WasmCompileResult, } from './wasm';
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transpile-time module loader.
|
|
3
|
+
*
|
|
4
|
+
* Until now the transpiler has preserved import statements verbatim — runtime
|
|
5
|
+
* resolvers (the bun plugin, the playground service worker, the browser ESM
|
|
6
|
+
* loader) handle them on demand. That's the right default for regular JS
|
|
7
|
+
* interop, but Phase 3 of the wasm-library plan needs *static* visibility into
|
|
8
|
+
* imported sources: given `import { dot } from 'tjs-lang/linalg'`, we have to
|
|
9
|
+
* read linalg's source at transpile time so cross-file `wasm function`
|
|
10
|
+
* declarations can be composed into the consumer's wasm module.
|
|
11
|
+
*
|
|
12
|
+
* This loader is the foundation for that work. It's also useful as a building
|
|
13
|
+
* block for any future cross-file static analysis (type flow, dead code, etc.)
|
|
14
|
+
* — hence the name "module loader" rather than "wasm import resolver."
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* const loader = new ModuleLoader({ baseDir: '/path/to/project' })
|
|
18
|
+
* const mod = loader.load('./math.tjs', '/path/to/project/app.tjs')
|
|
19
|
+
* if (mod) { console.log(mod.exports) }
|
|
20
|
+
*
|
|
21
|
+
* Resolution rules (in order):
|
|
22
|
+
* - URL specifiers (http://, https://, data:): not loadable, returns null
|
|
23
|
+
* - Relative paths (./foo, ../bar): resolved against importer's dir
|
|
24
|
+
* - Absolute paths (/foo/bar): used as-is
|
|
25
|
+
* - Bare specifiers (foo, foo/bar): walk up importer looking for
|
|
26
|
+
* node_modules/<spec>; also
|
|
27
|
+
* try bareSpecifierRoots
|
|
28
|
+
*
|
|
29
|
+
* For each candidate, we try extensions in order: `.tjs`, `.ts`, `.js`.
|
|
30
|
+
* (Index files: `<dir>/index.<ext>`.)
|
|
31
|
+
*
|
|
32
|
+
* The loader does NOT mutate transpiler behavior. It's an additive capability;
|
|
33
|
+
* Phase 3 (cross-file wasm composition) is the first caller.
|
|
34
|
+
*/
|
|
35
|
+
import { parse as parseTjs } from './parser';
|
|
36
|
+
/** Pluggable filesystem hook. Default uses node:fs. */
|
|
37
|
+
export interface FileSystem {
|
|
38
|
+
/** Return source text for an absolute path, or null if it doesn't exist. */
|
|
39
|
+
readFile(path: string): string | null;
|
|
40
|
+
/** Return true if the path exists and is a file. */
|
|
41
|
+
exists(path: string): boolean;
|
|
42
|
+
}
|
|
43
|
+
export interface ModuleLoaderOptions {
|
|
44
|
+
/** Filesystem hook (default: node:fs based) */
|
|
45
|
+
fs?: FileSystem;
|
|
46
|
+
/** Where to resolve bare specifiers from when no importer is given. */
|
|
47
|
+
baseDir?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Extra roots tried for bare specifiers BEFORE the standard node_modules
|
|
50
|
+
* walk. Useful for monorepos or tests that want to point at a virtual
|
|
51
|
+
* package directory. Each root is treated as if it were a `node_modules/`.
|
|
52
|
+
*/
|
|
53
|
+
bareSpecifierRoots?: string[];
|
|
54
|
+
/** Cache size cap. 0 disables caching. (default 256) */
|
|
55
|
+
cacheLimit?: number;
|
|
56
|
+
}
|
|
57
|
+
/** A single import / re-export specifier extracted from the AST */
|
|
58
|
+
export interface ImportEntry {
|
|
59
|
+
/** Original module specifier (e.g. './math.tjs', 'tjs-lang/linalg') */
|
|
60
|
+
specifier: string;
|
|
61
|
+
/** Local name in the importing module */
|
|
62
|
+
local: string;
|
|
63
|
+
/** Imported name in the source module ('default' for default imports) */
|
|
64
|
+
imported: string;
|
|
65
|
+
/** True if this came from `import * as X` */
|
|
66
|
+
namespace: boolean;
|
|
67
|
+
}
|
|
68
|
+
/** A top-level export from a module */
|
|
69
|
+
export interface ExportEntry {
|
|
70
|
+
/** Exported name (or 'default' for default exports) */
|
|
71
|
+
name: string;
|
|
72
|
+
/** Kind of declaration being exported */
|
|
73
|
+
kind: 'function' | 'class' | 'variable' | 're-export' | 'unknown';
|
|
74
|
+
/** For re-exports, the source specifier */
|
|
75
|
+
fromSpecifier?: string;
|
|
76
|
+
}
|
|
77
|
+
export interface LoadedModule {
|
|
78
|
+
/** Resolved absolute path */
|
|
79
|
+
path: string;
|
|
80
|
+
/** Original source text */
|
|
81
|
+
source: string;
|
|
82
|
+
/** AST + tjs preprocessing output (lazy — only computed once per module) */
|
|
83
|
+
parseResult: ReturnType<typeof parseTjs>;
|
|
84
|
+
/** Imports declared by this module */
|
|
85
|
+
imports: ImportEntry[];
|
|
86
|
+
/** Top-level exports */
|
|
87
|
+
exports: ExportEntry[];
|
|
88
|
+
}
|
|
89
|
+
export declare class ModuleLoader {
|
|
90
|
+
private cache;
|
|
91
|
+
private fs;
|
|
92
|
+
private baseDir;
|
|
93
|
+
private bareSpecifierRoots;
|
|
94
|
+
private cacheLimit;
|
|
95
|
+
constructor(options?: ModuleLoaderOptions);
|
|
96
|
+
/**
|
|
97
|
+
* Resolve a specifier to an absolute path. Returns null when the specifier
|
|
98
|
+
* is not loadable as a local TJS/TS/JS file (URLs, missing files, unknown
|
|
99
|
+
* bare specifiers all return null — the caller falls back to verbatim
|
|
100
|
+
* import preservation).
|
|
101
|
+
*/
|
|
102
|
+
resolve(specifier: string, importerPath?: string): string | null;
|
|
103
|
+
/**
|
|
104
|
+
* Load a module by specifier. Returns null if not resolvable (caller treats
|
|
105
|
+
* this as "leave the import statement alone").
|
|
106
|
+
*
|
|
107
|
+
* Repeated calls with the same specifier+importer combination hit the cache.
|
|
108
|
+
*/
|
|
109
|
+
load(specifier: string, importerPath?: string): LoadedModule | null;
|
|
110
|
+
/** Drop all cached modules. */
|
|
111
|
+
clearCache(): void;
|
|
112
|
+
/** Try each supported extension; return the first existing path or null. */
|
|
113
|
+
private tryExtensions;
|
|
114
|
+
/**
|
|
115
|
+
* Resolve a bare specifier (e.g. `tjs-lang/linalg`, `lodash`) by walking
|
|
116
|
+
* up the importer's directory tree looking for `node_modules/<spec>`.
|
|
117
|
+
* Also checks each configured `bareSpecifierRoots` entry first.
|
|
118
|
+
*/
|
|
119
|
+
private resolveBare;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Helper for tests / advanced use: build a FileSystem from a plain
|
|
123
|
+
* `Map<string, string>`. Keys must be absolute paths.
|
|
124
|
+
*/
|
|
125
|
+
export declare function inMemoryFileSystem(files: Map<string, string> | Record<string, string>): FileSystem;
|
|
@@ -32,6 +32,85 @@ export declare function extractWasmBlocks(source: string): {
|
|
|
32
32
|
source: string;
|
|
33
33
|
blocks: WasmBlock[];
|
|
34
34
|
};
|
|
35
|
+
/**
|
|
36
|
+
* Extract top-level `wasm function NAME(params): RetType { body }` declarations.
|
|
37
|
+
*
|
|
38
|
+
* Unlike `extractWasmBlocks` (which finds `wasm { ... }` blocks nested inside
|
|
39
|
+
* regular tjs functions with auto-captured variables), this extractor finds
|
|
40
|
+
* top-level wasm function declarations with explicit parameters and an
|
|
41
|
+
* optional return type. The body is the wasm-subset source.
|
|
42
|
+
*
|
|
43
|
+
* Each declaration becomes a `WasmBlock` whose `captures` array holds the
|
|
44
|
+
* function's parameters with their type annotations (e.g. `['a: Float32Array',
|
|
45
|
+
* 'b: Float32Array', 'n: i32']`). The block ID is derived from the function
|
|
46
|
+
* name (`__tjs_wasm_<name>`), so the JS-side wrapper can reference it.
|
|
47
|
+
*
|
|
48
|
+
* The declaration is replaced in source with a regular JS function that
|
|
49
|
+
* forwards its args to the wasm export, preserving the `export` modifier
|
|
50
|
+
* if present:
|
|
51
|
+
*
|
|
52
|
+
* (export)? wasm function dot(a: Float32Array, b: Float32Array, n: f64): f64 {
|
|
53
|
+
* <wasm-source>
|
|
54
|
+
* }
|
|
55
|
+
*
|
|
56
|
+
* becomes:
|
|
57
|
+
*
|
|
58
|
+
* (export)? function dot(a, b, n) { return globalThis.__tjs_wasm_dot(a, b, n) }
|
|
59
|
+
*
|
|
60
|
+
* This runs BEFORE `extractWasmBlocks` so its output (a regular JS function
|
|
61
|
+
* wrapper) isn't disturbed by the inline-block scanner.
|
|
62
|
+
*
|
|
63
|
+
* Return-type note: the underlying wasm bytecode builder currently emits f64
|
|
64
|
+
* or void return types only. The declared `: RetType` annotation is parsed
|
|
65
|
+
* and preserved on the block, but not yet validated against the backend's
|
|
66
|
+
* capabilities — `: f64` and omitted-return work today; other types (`: i32`,
|
|
67
|
+
* `: f32`, `: v128`) will be supported when the bytecode builder grows
|
|
68
|
+
* per-function return-type encoding.
|
|
69
|
+
*/
|
|
70
|
+
export declare function extractWasmFunctions(source: string): {
|
|
71
|
+
source: string;
|
|
72
|
+
blocks: WasmBlock[];
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Phase 3: cross-file wasm-function composition.
|
|
76
|
+
*
|
|
77
|
+
* Scans the consumer's source for `import { name1, name2, ... } from 'spec'`
|
|
78
|
+
* statements. For each, resolves the spec via the supplied ModuleLoader; if
|
|
79
|
+
* the imported file is tjs/ts and one of the imported names corresponds to a
|
|
80
|
+
* `wasm function` declared there, the function's WasmBlock is pulled into the
|
|
81
|
+
* consumer's compilation and replaced in source by a local JS wrapper. The
|
|
82
|
+
* import statement is rewritten to remove the satisfied names (or removed
|
|
83
|
+
* entirely if every imported name was wasm-composed).
|
|
84
|
+
*
|
|
85
|
+
* This is the heart of the cross-file composition story: imported wasm
|
|
86
|
+
* functions become local functions in the consumer's single WebAssembly.Module
|
|
87
|
+
* (via the Phase 0.5 consolidated-module path). The library's transpiled .js
|
|
88
|
+
* is NOT involved — the source is consumed at transpile time.
|
|
89
|
+
*
|
|
90
|
+
* Caller is responsible for providing a configured ModuleLoader. When no
|
|
91
|
+
* loader is supplied (the common case before Phase 3 is fully wired up), this
|
|
92
|
+
* function returns the source unchanged with an empty blocks array.
|
|
93
|
+
*
|
|
94
|
+
* @param source the consumer's source (after extractWasmFunctions on its own
|
|
95
|
+
* wasm functions, before transformParenExpressions)
|
|
96
|
+
* @param options.loader the ModuleLoader to resolve imports through
|
|
97
|
+
* @param options.importerPath the path of the file being transpiled (used as
|
|
98
|
+
* the resolver's importer context); optional
|
|
99
|
+
* @returns updated source + the list of imported wasm function blocks
|
|
100
|
+
*/
|
|
101
|
+
export declare function composeImportedWasmFunctions(source: string, options: {
|
|
102
|
+
loader?: {
|
|
103
|
+
load(specifier: string, importerPath?: string): {
|
|
104
|
+
parseResult: {
|
|
105
|
+
wasmBlocks: WasmBlock[];
|
|
106
|
+
};
|
|
107
|
+
} | null;
|
|
108
|
+
};
|
|
109
|
+
importerPath?: string;
|
|
110
|
+
}): {
|
|
111
|
+
source: string;
|
|
112
|
+
blocks: WasmBlock[];
|
|
113
|
+
};
|
|
35
114
|
/**
|
|
36
115
|
* Transform Is/IsNot infix operators to function calls
|
|
37
116
|
*
|
|
@@ -14,6 +14,28 @@ export interface ParseOptions {
|
|
|
14
14
|
* When true, skips == to Is() transformation since the VM handles == correctly.
|
|
15
15
|
*/
|
|
16
16
|
vmTarget?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Source dialect — tells the transpiler what kind of source this string is:
|
|
19
|
+
* - `'tjs'`: native TJS, all footgun-removal modes ON (structural `==`,
|
|
20
|
+
* `TjsStandard`, etc.). This is the default for a bare string.
|
|
21
|
+
* - `'js'`: plain JavaScript — modes OFF and `safety: 'none'`, so the source's
|
|
22
|
+
* own semantics are preserved (no `==`→`Eq`, no truthiness rewrite, etc.).
|
|
23
|
+
*
|
|
24
|
+
* Authoritative when set; otherwise the dialect is inferred (the fromTS
|
|
25
|
+
* annotation and `vmTarget` ⇒ JS-compatible). See PRINCIPLES.md (TJS ⊇ JS).
|
|
26
|
+
*/
|
|
27
|
+
dialect?: 'js' | 'tjs';
|
|
28
|
+
/**
|
|
29
|
+
* Optional ModuleLoader for cross-file `wasm function` composition (Phase 3).
|
|
30
|
+
* When provided, imports are resolved at transpile time and matching wasm
|
|
31
|
+
* functions are composed into the consumer's WebAssembly.Module. When
|
|
32
|
+
* omitted, imports are preserved verbatim (the default — runtime resolves
|
|
33
|
+
* them as before).
|
|
34
|
+
*
|
|
35
|
+
* Type is left as `any` here to avoid a circular import with module-loader.ts;
|
|
36
|
+
* callers should pass a `ModuleLoader` instance.
|
|
37
|
+
*/
|
|
38
|
+
moduleLoader?: any;
|
|
17
39
|
}
|
|
18
40
|
/**
|
|
19
41
|
* A WASM block extracted from source
|
|
@@ -35,6 +57,21 @@ export interface ParseOptions {
|
|
|
35
57
|
export interface WasmBlock {
|
|
36
58
|
/** Unique ID for this block */
|
|
37
59
|
id: string;
|
|
60
|
+
/**
|
|
61
|
+
* Declared function name (only set for top-level `wasm function NAME(...)`
|
|
62
|
+
* declarations — Phase 1+). Used by Phase 3 cross-file composition to
|
|
63
|
+
* match an imported symbol against a wasm function declaration. Inline
|
|
64
|
+
* `wasm {}` blocks have no name and don't participate in composition.
|
|
65
|
+
*/
|
|
66
|
+
name?: string;
|
|
67
|
+
/**
|
|
68
|
+
* Declared return-type annotation, e.g. `'f64'`. Only set for top-level
|
|
69
|
+
* `wasm function NAME(...): RetType` declarations; presence/absence is
|
|
70
|
+
* used to determine `hasReturn` BEFORE the body is compiled, so the
|
|
71
|
+
* function index map can be built up-front for wasm-to-wasm calls.
|
|
72
|
+
* Inline blocks have no declared return type.
|
|
73
|
+
*/
|
|
74
|
+
returnType?: string;
|
|
38
75
|
/** The body (JS subset that compiles to WASM, also used as fallback) */
|
|
39
76
|
body: string;
|
|
40
77
|
/** Explicit fallback body (only if different from body) */
|
|
@@ -79,6 +116,19 @@ export interface PreprocessOptions {
|
|
|
79
116
|
* Default: false (transform == to Is() for TJS code running in regular JS)
|
|
80
117
|
*/
|
|
81
118
|
vmTarget?: boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Source dialect: `'js'` ⇒ modes OFF / `safety: 'none'` (preserve plain-JS
|
|
121
|
+
* semantics); `'tjs'` ⇒ native modes ON. Authoritative when set; otherwise
|
|
122
|
+
* inferred from the fromTS annotation / `vmTarget`. See ParseOptions.dialect.
|
|
123
|
+
*/
|
|
124
|
+
dialect?: 'js' | 'tjs';
|
|
125
|
+
/**
|
|
126
|
+
* Optional ModuleLoader for cross-file `wasm function` composition (Phase 3).
|
|
127
|
+
* See ParseOptions.moduleLoader for details.
|
|
128
|
+
*/
|
|
129
|
+
moduleLoader?: any;
|
|
130
|
+
/** Path of the file being preprocessed (used as importer context). */
|
|
131
|
+
filename?: string;
|
|
82
132
|
}
|
|
83
133
|
/**
|
|
84
134
|
* Tokenizer state for tracking context during source transformation
|
|
@@ -53,6 +53,24 @@ export declare function parse(source: string, options?: ParseOptions): {
|
|
|
53
53
|
* Validate that the source contains exactly one function declaration
|
|
54
54
|
*/
|
|
55
55
|
export declare function validateSingleFunction(ast: Program, filename?: string): FunctionDeclaration;
|
|
56
|
+
/**
|
|
57
|
+
* Extract top-level function declarations from the parsed program.
|
|
58
|
+
*
|
|
59
|
+
* Returns `{ entry, helpers }` where:
|
|
60
|
+
* - `entry` is the last function declaration (the agent's entry point)
|
|
61
|
+
* - `helpers` are all preceding function declarations, looked up by name
|
|
62
|
+
*
|
|
63
|
+
* This matches the natural "helpers first, agent last" pattern, including
|
|
64
|
+
* the TOOL_LIBRARY use case where helper async wrappers are prepended
|
|
65
|
+
* before the user-supplied agent function.
|
|
66
|
+
*
|
|
67
|
+
* Same top-level construct restrictions as `validateSingleFunction`:
|
|
68
|
+
* imports, exports, and classes are rejected.
|
|
69
|
+
*/
|
|
70
|
+
export declare function extractFunctions(ast: Program, filename?: string): {
|
|
71
|
+
entry: FunctionDeclaration;
|
|
72
|
+
helpers: Map<string, FunctionDeclaration>;
|
|
73
|
+
};
|
|
56
74
|
/**
|
|
57
75
|
* Extract TDoc comment from before a function
|
|
58
76
|
*
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
export { transpile, ajs, tjs, createAgent, getToolDefinitions } from './core';
|
|
13
13
|
export { parse, preprocess, extractTDoc } from './parser';
|
|
14
|
+
export { dialectForFilename, sourceKindForFilename, type Dialect, type SourceKind, } from './dialect';
|
|
14
15
|
export { transformFunction } from './emitters/ast';
|
|
15
16
|
export { transpileToJS } from './emitters/js';
|
|
16
17
|
export type { TJSTranspileOptions, TJSTranspileResult, TJSTypeInfo, } from './emitters/js';
|
package/dist/src/lang/types.d.ts
CHANGED
|
@@ -143,6 +143,24 @@ export interface TransformContext {
|
|
|
143
143
|
filename: string;
|
|
144
144
|
/** Options */
|
|
145
145
|
options: TranspileOptions;
|
|
146
|
+
/**
|
|
147
|
+
* Helper functions (top-level functions declared before the entry function).
|
|
148
|
+
* Calls to these names emit `callLocal` instead of treating them as atom calls.
|
|
149
|
+
*/
|
|
150
|
+
helpers?: Map<string, any>;
|
|
151
|
+
/**
|
|
152
|
+
* Cache of transformed helper bodies. Lazily populated as helpers are
|
|
153
|
+
* referenced from the entry function (or from other helpers).
|
|
154
|
+
*/
|
|
155
|
+
helperSteps?: Map<string, {
|
|
156
|
+
steps: any[];
|
|
157
|
+
paramNames: string[];
|
|
158
|
+
}>;
|
|
159
|
+
/**
|
|
160
|
+
* Names of helpers currently being transformed. Used to detect direct or
|
|
161
|
+
* transitive recursion.
|
|
162
|
+
*/
|
|
163
|
+
helperTransforming?: Set<string>;
|
|
146
164
|
}
|
|
147
165
|
/** Create a child context for nested scopes */
|
|
148
166
|
export declare function createChildContext(parent: TransformContext): TransformContext;
|
package/dist/src/lang/wasm.d.ts
CHANGED
|
@@ -15,6 +15,29 @@
|
|
|
15
15
|
* audio processing, image manipulation, and physics simulations.
|
|
16
16
|
*/
|
|
17
17
|
import type { WasmBlock } from './parser';
|
|
18
|
+
/** TJS type that maps to WASM */
|
|
19
|
+
type WasmValueType = 'i32' | 'i64' | 'f32' | 'f64' | 'v128';
|
|
20
|
+
/** Parameter with type annotation */
|
|
21
|
+
interface TypedParam {
|
|
22
|
+
name: string;
|
|
23
|
+
type: WasmValueType;
|
|
24
|
+
isArray?: boolean;
|
|
25
|
+
arrayType?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Signature of a wasm function in the current module. Used to resolve
|
|
29
|
+
* cross-function calls (wasm-to-wasm `call <index>` instructions) without
|
|
30
|
+
* routing through JS. Populated by compileBlocksToModule before compiling
|
|
31
|
+
* any individual body, so forward references and mutual recursion work.
|
|
32
|
+
*/
|
|
33
|
+
export interface ModuleFunctionSig {
|
|
34
|
+
/** Function index in the composed module */
|
|
35
|
+
index: number;
|
|
36
|
+
/** Parameter types (used for arg-type checking + auto-conversion) */
|
|
37
|
+
params: TypedParam[];
|
|
38
|
+
/** Whether this function returns a value (f64) or is void */
|
|
39
|
+
hasReturn: boolean;
|
|
40
|
+
}
|
|
18
41
|
/** Compile result */
|
|
19
42
|
export interface WasmCompileResult {
|
|
20
43
|
/** The compiled WebAssembly module bytes */
|
|
@@ -31,9 +54,51 @@ export interface WasmCompileResult {
|
|
|
31
54
|
wat?: string;
|
|
32
55
|
}
|
|
33
56
|
/**
|
|
34
|
-
* Compile a WASM block to WebAssembly
|
|
57
|
+
* Compile a single WASM block to a complete WebAssembly module.
|
|
58
|
+
* The module exports a single function named `compute`.
|
|
35
59
|
*/
|
|
36
60
|
export declare function compileToWasm(block: WasmBlock): WasmCompileResult;
|
|
61
|
+
/** Per-export metadata produced by compileBlocksToModule */
|
|
62
|
+
export interface BlockExport {
|
|
63
|
+
/** Original block ID (assigned by the parser) */
|
|
64
|
+
id: string;
|
|
65
|
+
/** Export name in the composed module (e.g. 'compute_0') */
|
|
66
|
+
exportName: string;
|
|
67
|
+
/** Capture annotations (preserved for runtime wrapper) */
|
|
68
|
+
captures: string[];
|
|
69
|
+
/** Whether this function reads/writes memory */
|
|
70
|
+
needsMemory: boolean;
|
|
71
|
+
/** WAT disassembly */
|
|
72
|
+
wat: string;
|
|
73
|
+
}
|
|
74
|
+
/** Result of composing multiple blocks into one module */
|
|
75
|
+
export interface MultiBlockCompileResult {
|
|
76
|
+
/** The composed module bytes, or empty if all blocks failed */
|
|
77
|
+
bytes: Uint8Array;
|
|
78
|
+
/** Per-block compile status (preserves input order) */
|
|
79
|
+
results: {
|
|
80
|
+
id: string;
|
|
81
|
+
success: boolean;
|
|
82
|
+
error?: string;
|
|
83
|
+
/** Index into `exports` (only when success === true) */
|
|
84
|
+
exportIndex?: number;
|
|
85
|
+
}[];
|
|
86
|
+
/** Successfully-compiled exports (in module-index order) */
|
|
87
|
+
exports: BlockExport[];
|
|
88
|
+
/** True if any included function needs memory */
|
|
89
|
+
needsMemory: boolean;
|
|
90
|
+
/** Aggregated warnings from all blocks */
|
|
91
|
+
warnings: string[];
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Compile N WASM blocks into a single WebAssembly module with N exports.
|
|
95
|
+
* Failed blocks are skipped (their slot in `results` records the error)
|
|
96
|
+
* but do not abort compilation of the rest.
|
|
97
|
+
*
|
|
98
|
+
* Exports are named `compute_0`, `compute_1`, ... in input order, skipping
|
|
99
|
+
* indices that correspond to failed blocks.
|
|
100
|
+
*/
|
|
101
|
+
export declare function compileBlocksToModule(blocks: WasmBlock[]): MultiBlockCompileResult;
|
|
37
102
|
/**
|
|
38
103
|
* Instantiate a compiled WASM module
|
|
39
104
|
*/
|
|
@@ -81,3 +146,4 @@ export declare function compileWasmBlocksForIframe(blocks: WasmBlock[]): Promise
|
|
|
81
146
|
* This code should be injected into the iframe's script
|
|
82
147
|
*/
|
|
83
148
|
export declare function generateWasmInstantiationCode(compiledBlocks: CompiledWasmData[]): string;
|
|
149
|
+
export {};
|
package/dist/src/vm/runtime.d.ts
CHANGED
|
@@ -60,6 +60,8 @@ export interface TraceEvent {
|
|
|
60
60
|
}
|
|
61
61
|
/** Cost override: static number or dynamic function */
|
|
62
62
|
export type CostOverride = number | ((input: any, ctx: RuntimeContext) => number);
|
|
63
|
+
/** Timeout override: static number (ms) or dynamic function. 0 disables. */
|
|
64
|
+
export type TimeoutOverride = number | ((input: any, ctx: RuntimeContext) => number);
|
|
63
65
|
export interface RuntimeContext {
|
|
64
66
|
fuel: {
|
|
65
67
|
current: number;
|
|
@@ -76,8 +78,15 @@ export interface RuntimeContext {
|
|
|
76
78
|
warnings?: string[];
|
|
77
79
|
signal?: AbortSignal;
|
|
78
80
|
costOverrides?: Record<string, CostOverride>;
|
|
81
|
+
timeoutOverrides?: Record<string, TimeoutOverride>;
|
|
79
82
|
context?: Record<string, any>;
|
|
80
83
|
runCodeDepth?: number;
|
|
84
|
+
localCall?: boolean;
|
|
85
|
+
helpers?: Record<string, {
|
|
86
|
+
steps: any[];
|
|
87
|
+
paramNames: string[];
|
|
88
|
+
}>;
|
|
89
|
+
callDepth?: number;
|
|
81
90
|
}
|
|
82
91
|
export type AtomExec = (step: any, ctx: RuntimeContext) => Promise<void>;
|
|
83
92
|
export interface AtomDef {
|
|
@@ -218,6 +227,14 @@ export declare const varsImport: Atom<Record<string, any>, void>;
|
|
|
218
227
|
export declare const varsLet: Atom<Record<string, any>, void>;
|
|
219
228
|
export declare const varsExport: Atom<Record<string, any>, Record<string, any>>;
|
|
220
229
|
export declare const scope: Atom<Record<string, any>, void>;
|
|
230
|
+
/**
|
|
231
|
+
* Maximum helper-call nesting depth. Fuel + timeout bound the total *work* a
|
|
232
|
+
* recursive helper can do, but deeply-nested `await seq.exec` would overflow
|
|
233
|
+
* the host JS stack (an uncatchable RangeError) before fuel runs out. This cap
|
|
234
|
+
* converts runaway recursion into a clean monadic error instead.
|
|
235
|
+
*/
|
|
236
|
+
export declare const MAX_CALL_DEPTH = 256;
|
|
237
|
+
export declare const callLocal: Atom<Record<string, any>, any>;
|
|
221
238
|
export declare const map: Atom<Record<string, any>, any[]>;
|
|
222
239
|
export declare const filter: Atom<Record<string, any>, any[]>;
|
|
223
240
|
export declare const reduce: Atom<Record<string, any>, any>;
|
|
@@ -281,6 +298,7 @@ export declare const coreAtoms: {
|
|
|
281
298
|
varsLet: Atom<Record<string, any>, void>;
|
|
282
299
|
varsExport: Atom<Record<string, any>, Record<string, any>>;
|
|
283
300
|
scope: Atom<Record<string, any>, void>;
|
|
301
|
+
callLocal: Atom<Record<string, any>, any>;
|
|
284
302
|
map: Atom<Record<string, any>, any[]>;
|
|
285
303
|
filter: Atom<Record<string, any>, any[]>;
|
|
286
304
|
reduce: Atom<Record<string, any>, any>;
|
package/dist/src/vm/vm.d.ts
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
|
-
import { type Atom, type Capabilities, type RunResult, type CostOverride, coreAtoms } from './runtime';
|
|
1
|
+
import { type Atom, type Capabilities, type RunResult, type CostOverride, type TimeoutOverride, coreAtoms } from './runtime';
|
|
2
2
|
import { type BaseNode, type BuilderType } from '../builder';
|
|
3
3
|
export declare class AgentVM<M extends Record<string, Atom<any, any>>> {
|
|
4
4
|
readonly atoms: typeof coreAtoms & M;
|
|
5
|
+
private _defaultRunTimeout?;
|
|
5
6
|
constructor(customAtoms?: M);
|
|
7
|
+
/**
|
|
8
|
+
* Default run-level wall-clock timeout when `run()` is given no explicit
|
|
9
|
+
* `timeoutMs`. Derived as `max(per-atom timeoutMs) × 2` over the registered
|
|
10
|
+
* atoms (with headroom for an agent that chains a couple of slow calls), so
|
|
11
|
+
* the run-level backstop can never be shorter than the slowest single atom's
|
|
12
|
+
* own budget — otherwise that per-atom budget would be dead config (e.g.
|
|
13
|
+
* `llmVision`/`llmPredictBattery` are 120s; a fixed 60s run default would kill
|
|
14
|
+
* them mid-call). Atoms with `timeoutMs: 0` (no timeout, e.g. `seq`) are
|
|
15
|
+
* excluded; the result is floored at {@link MIN_DEFAULT_RUN_TIMEOUT_MS}.
|
|
16
|
+
* Self-adjusting: registering a slower custom atom raises the default.
|
|
17
|
+
*/
|
|
18
|
+
get defaultRunTimeout(): number;
|
|
6
19
|
get builder(): BuilderType<typeof coreAtoms & M>;
|
|
7
20
|
get Agent(): BuilderType<typeof coreAtoms & M>;
|
|
8
21
|
/** @deprecated Use `Agent` instead */
|
|
@@ -23,6 +36,7 @@ export declare class AgentVM<M extends Record<string, Atom<any, any>>> {
|
|
|
23
36
|
timeoutMs?: number;
|
|
24
37
|
signal?: AbortSignal;
|
|
25
38
|
costOverrides?: Record<string, CostOverride>;
|
|
39
|
+
timeoutOverrides?: Record<string, TimeoutOverride>;
|
|
26
40
|
context?: Record<string, any>;
|
|
27
41
|
}): Promise<RunResult>;
|
|
28
42
|
}
|