Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 5x 5x 5x 5x 5x 1x 1x 1x 1x 1x 1x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 1071x 5x 379x 6x 6x 379x 379x 63x 1x 1x 63x 62x 62x 379x 379x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 1x 1x 1x 1x 1x 1x 1x 1x 6x 6x 1x 1x 1x 1x 1x 1x 1x 1x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 1x 1x 1x 1x 1x 1x | "use strict";
/**
* Type-check agent TypeScript against the exposed `sdk` binding.
*
* Type checking is a correctness aid that returns useful diagnostics before
* code runs; it is not an authorisation boundary.
*
* @module executor/typecheck
*/
import fs from "node:fs";
import path from "node:path";
import ts from "typescript";
import { sdkCompilerOptions } from "../prepare/bundle.js";
import { mapDiagnostic, wrapForTypecheck } from "./code-policy.js";
import type { Diagnostic } from "./types.js";
/**
* A persistent language service over the prepared SDK declarations.
*/
export class AgentTypeChecker {
private readonly service: ts.LanguageService;
private readonly agentFile: string;
private readonly libDir: string;
private version = 0;
private current = "";
private readonly options: ts.CompilerOptions;
/**
* @param {String} sdkDir - workspace sdk/ directory (contains types/ and sdk-binding.d.ts)
* @param {String} libDir - directory with TypeScript lib files
*/
constructor(sdkDir: string, libDir: string) {
this.libDir = libDir;
this.agentFile = path.join(sdkDir, "__agent__.ts");
const bindingFile = path.join(sdkDir, "sdk-binding.d.ts");
this.options = {
...sdkCompilerOptions(),
noEmit: true,
strict: true,
noImplicitAny: false,
noUnusedLocals: false,
noUnusedParameters: false,
noImplicitReturns: false,
allowUnreachableCode: true,
};
const snapshots = new Map<string, ts.IScriptSnapshot>();
const host: ts.LanguageServiceHost = {
getCompilationSettings: () => this.options,
getScriptFileNames: () => [bindingFile, this.agentFile],
getScriptVersion: (f) =>
f === this.agentFile ? String(this.version) : "0",
getScriptSnapshot: (f) => {
if (f === this.agentFile) {
return ts.ScriptSnapshot.fromString(this.current);
}
let snap = snapshots.get(f);
if (!snap) {
if (!fs.existsSync(f)) {
return undefined;
}
snap = ts.ScriptSnapshot.fromString(fs.readFileSync(f, "utf8"));
snapshots.set(f, snap);
}
return snap;
},
getCurrentDirectory: () => sdkDir,
getDefaultLibFileName: () => path.join(this.libDir, "lib.es2022.d.ts"),
fileExists: (f) => f === this.agentFile || fs.existsSync(f),
readFile: (f) => this._readFile(f),
readDirectory: ts.sys.readDirectory,
directoryExists: ts.sys.directoryExists,
getDirectories: ts.sys.getDirectories,
};
this.service = ts.createLanguageService(host, ts.createDocumentRegistry());
}
/**
* The agent code being checked, or a file from disk (undefined when
* missing).
*
* @private
*/
private _readFile(f: string): string | undefined {
return f === this.agentFile ? this.current : ts.sys.readFile(f);
}
/**
* Type-check agent code.
*
* @param {String} code - agent TypeScript (function body)
* @return {Array} diagnostics (empty when the code type-checks)
*/
check(code: string): Diagnostic[] {
this.current = wrapForTypecheck(code);
this.version++;
const diags = [
...this.service.getSyntacticDiagnostics(this.agentFile),
...this.service.getSemanticDiagnostics(this.agentFile),
].filter((d) => d.category === ts.DiagnosticCategory.Error);
return diags
.map(mapDiagnostic)
.filter((d): d is Diagnostic => Boolean(d))
.slice(0, 10);
}
/** Warm the language service so the first agent call is fast. */
warmUp(): void {
this.check("return 1;");
}
}
|