tina4-nodejs 3.13.100 → 3.13.103
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 +3 -3
- package/README.md +16 -0
- package/package.json +2 -2
- package/packages/cli/dist/bin.js +572 -764
- package/packages/cli/src/bin.ts +0 -6
- package/packages/core/dist/index.js +561 -622
- package/packages/core/public/js/tina4-dev-admin.min.js +2 -2
- package/packages/core/src/aiClient.ts +288 -0
- package/packages/core/src/devAdmin.ts +1 -2
- package/packages/core/src/index.ts +2 -0
- package/packages/core/src/metrics.ts +80 -632
- package/packages/frond/dist/index.js +113 -109
- package/packages/frond/src/engine.ts +128 -130
- package/packages/orm/dist/index.js +562 -629
- package/types/core/src/aiClient.d.ts +66 -0
- package/types/core/src/index.d.ts +2 -0
- package/types/core/src/metrics.d.ts +0 -35
- package/types/frond/src/engine.d.ts +4 -2
- package/packages/cli/src/commands/metrics.ts +0 -160
- package/types/cli/src/commands/metrics.d.ts +0 -6
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export declare class AiError extends Error {
|
|
2
|
+
}
|
|
3
|
+
export declare class AiConfigError extends AiError {
|
|
4
|
+
}
|
|
5
|
+
export declare class AiTimeoutError extends AiError {
|
|
6
|
+
}
|
|
7
|
+
export declare class AiParseError extends AiError {
|
|
8
|
+
}
|
|
9
|
+
export declare class AiHTTPError extends AiError {
|
|
10
|
+
readonly status: number | null;
|
|
11
|
+
constructor(message: string, status?: number | null);
|
|
12
|
+
}
|
|
13
|
+
export interface ChatResponse {
|
|
14
|
+
text: string;
|
|
15
|
+
model: string;
|
|
16
|
+
usage: {
|
|
17
|
+
promptTokens: number;
|
|
18
|
+
completionTokens: number;
|
|
19
|
+
totalTokens: number;
|
|
20
|
+
};
|
|
21
|
+
finishReason: string | null;
|
|
22
|
+
raw: Record<string, unknown>;
|
|
23
|
+
}
|
|
24
|
+
export interface AiMessage {
|
|
25
|
+
role: "system" | "user" | "assistant";
|
|
26
|
+
content: string;
|
|
27
|
+
}
|
|
28
|
+
export interface AiChatOptions {
|
|
29
|
+
model?: string;
|
|
30
|
+
temperature?: number;
|
|
31
|
+
maxTokens?: number;
|
|
32
|
+
stream?: boolean;
|
|
33
|
+
timeout?: number;
|
|
34
|
+
provider?: "local" | "openai" | "anthropic";
|
|
35
|
+
}
|
|
36
|
+
export interface AiEmbedOptions {
|
|
37
|
+
model?: string;
|
|
38
|
+
timeout?: number;
|
|
39
|
+
provider?: "local" | "openai" | "anthropic";
|
|
40
|
+
}
|
|
41
|
+
export declare class Ai {
|
|
42
|
+
static chat(messages: AiMessage[], options: AiChatOptions & {
|
|
43
|
+
stream: true;
|
|
44
|
+
}): AsyncGenerator<string>;
|
|
45
|
+
static chat(messages: AiMessage[], options?: AiChatOptions & {
|
|
46
|
+
stream?: false;
|
|
47
|
+
}): Promise<ChatResponse>;
|
|
48
|
+
static complete(prompt: string, options?: Omit<AiChatOptions, "stream">): Promise<string>;
|
|
49
|
+
static embed(textOrTexts: string | string[], options?: AiEmbedOptions): Promise<number[] | number[][]>;
|
|
50
|
+
private static validateMessages;
|
|
51
|
+
private static number;
|
|
52
|
+
private static config;
|
|
53
|
+
private static endpoint;
|
|
54
|
+
private static headers;
|
|
55
|
+
private static chatBody;
|
|
56
|
+
private static open;
|
|
57
|
+
private static readBody;
|
|
58
|
+
private static retryDelay;
|
|
59
|
+
private static requestJson;
|
|
60
|
+
private static normalizeChat;
|
|
61
|
+
private static chatResponse;
|
|
62
|
+
private static streamDelta;
|
|
63
|
+
private static streamData;
|
|
64
|
+
private static streamError;
|
|
65
|
+
private static streamRequest;
|
|
66
|
+
}
|
|
@@ -55,6 +55,8 @@ export { HtmlElement, htmlElement, addHtmlHelpers, Raw, SafeString } from "./htm
|
|
|
55
55
|
export { renderErrorOverlay, isDebugMode } from "./errorOverlay.js";
|
|
56
56
|
export { AI_TOOLS, isInstalled, showMenu, installSelected, installAll, generateContext } from "./ai.js";
|
|
57
57
|
export type { AiTool } from "./ai.js";
|
|
58
|
+
export { Ai, AiError, AiConfigError, AiHTTPError, AiTimeoutError, AiParseError } from "./aiClient.js";
|
|
59
|
+
export type { ChatResponse, AiMessage, AiChatOptions, AiEmbedOptions } from "./aiClient.js";
|
|
58
60
|
export type { ImapMessage, ImapFullMessage, ImapAttachment } from "./messenger.js";
|
|
59
61
|
export { LiteBackend } from "./queueBackends/liteBackend.js";
|
|
60
62
|
export { RabbitMQBackend, parseAmqpUrl } from "./queueBackends/rabbitmqBackend.js";
|
|
@@ -1,41 +1,6 @@
|
|
|
1
|
-
export declare function quickMetrics(root?: string): Record<string, any>;
|
|
2
|
-
/**
|
|
3
|
-
* The native metrics engine could not produce a payload.
|
|
4
|
-
*
|
|
5
|
-
* Thrown instead of falling back to a second implementation.
|
|
6
|
-
*/
|
|
7
1
|
export declare class MetricsEngineError extends Error {
|
|
8
2
|
constructor(message: string);
|
|
9
3
|
}
|
|
10
|
-
export declare const SEVERITY_RANK: Record<string, number>;
|
|
11
|
-
/**
|
|
12
|
-
* Return [directory to scan, scanMode] for any metrics producer.
|
|
13
|
-
*
|
|
14
|
-
* The engine is language-agnostic and cannot know which directory holds a
|
|
15
|
-
* framework package, so root resolution and the "framework" label stay here,
|
|
16
|
-
* shared by the census and the engine adapter so the two never disagree.
|
|
17
|
-
*/
|
|
18
|
-
export declare function resolveScanTarget(root?: string): [string, string];
|
|
19
|
-
/** Absolute path to the tina4 CLI binary, or null when it is not installed. */
|
|
20
4
|
export declare function enginePath(): string | null;
|
|
21
|
-
/** Full code analysis from the native engine, shaped for the dashboard. */
|
|
22
5
|
export declare function fullAnalysis(root?: string): Record<string, any>;
|
|
23
|
-
export interface OffendersResult {
|
|
24
|
-
offenders: Record<string, any>[];
|
|
25
|
-
summary: Record<string, any>;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Top code-health offenders from the native engine.
|
|
29
|
-
*
|
|
30
|
-
* The engine ranks and severity-tags them, and its own --fail-on gate reads the
|
|
31
|
-
* same list, so the CLI and the dashboard can never disagree about what counts
|
|
32
|
-
* as an offender.
|
|
33
|
-
*/
|
|
34
|
-
export declare function offenders(root?: string, top?: number): OffendersResult;
|
|
35
|
-
/**
|
|
36
|
-
* Per-file metrics from the native engine.
|
|
37
|
-
*
|
|
38
|
-
* The engine accepts a single file for --path, so one code path serves both the
|
|
39
|
-
* whole-tree scan and one file.
|
|
40
|
-
*/
|
|
41
6
|
export declare function fileDetail(filePath: string): Record<string, any>;
|
|
@@ -36,8 +36,8 @@ export declare const pathParseCache: Map<string, [string[], boolean[]]>;
|
|
|
36
36
|
*/
|
|
37
37
|
export declare const TEMPLATE_CACHE_MAX = 256;
|
|
38
38
|
/**
|
|
39
|
-
* Hard cap on every per-expression memo cache — `filterChainCache
|
|
40
|
-
* `pathParseCache` (ADR-0004, parity with PHP's MEMO_CACHE_MAX and the
|
|
39
|
+
* Hard cap on every per-expression memo cache — `filterChainCache`,
|
|
40
|
+
* `pathParseCache`, and `expressionFormCache` (ADR-0004, parity with PHP's MEMO_CACHE_MAX and the
|
|
41
41
|
* Python master's `@lru_cache(maxsize=1024)` on the equivalent module-level
|
|
42
42
|
* parsers). Deliberately higher than TEMPLATE_CACHE_MAX: one entry here is a
|
|
43
43
|
* small parsed-path array, orders of magnitude smaller than a token list.
|
|
@@ -47,6 +47,8 @@ export declare const TEMPLATE_CACHE_MAX = 256;
|
|
|
47
47
|
* string, the same order of magnitude as a compiled template.
|
|
48
48
|
*/
|
|
49
49
|
export declare const MEMO_CACHE_MAX = 1024;
|
|
50
|
+
/** Cached expression dispatcher branch; exported only for cache-bound verification. */
|
|
51
|
+
export declare const expressionFormCache: Map<string, number>;
|
|
50
52
|
/**
|
|
51
53
|
* Set the session ID used by formToken() / form_token() for CSRF session binding.
|
|
52
54
|
*/
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLI command: metrics — Rank top code-quality offenders.
|
|
3
|
-
*
|
|
4
|
-
* Exposes the existing static analyzer (cyclomatic complexity, maintainability,
|
|
5
|
-
* large-file, too-many-functions, and the now-precise has_tests) on the CLI as
|
|
6
|
-
* a self-monitoring tool.
|
|
7
|
-
*
|
|
8
|
-
* tina4nodejs metrics # human report, scans src/ (or framework)
|
|
9
|
-
* tina4nodejs metrics --top 10 # only the worst 10
|
|
10
|
-
* tina4nodejs metrics --path packages/core/src # scan a specific directory
|
|
11
|
-
* tina4nodejs metrics --json # machine-readable for CI (ONLY json printed)
|
|
12
|
-
* tina4nodejs metrics --fail-on warn # exit 1 if any warn/error offender
|
|
13
|
-
* tina4nodejs metrics --fail-on error # exit 1 only on error-severity
|
|
14
|
-
*
|
|
15
|
-
* Returns the intended process exit code (the caller exits). Splitting "compute
|
|
16
|
-
* exit code" from "exit the process" keeps the handler unit-testable.
|
|
17
|
-
*/
|
|
18
|
-
import { offenders, MetricsEngineError } from "../../../core/src/metrics.js";
|
|
19
|
-
|
|
20
|
-
type Flags = {
|
|
21
|
-
top: number;
|
|
22
|
-
json: boolean;
|
|
23
|
-
path: string;
|
|
24
|
-
failOn: "warn" | "error" | null;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
/** Parse `--top N`, `--json`, `--fail-on warn|error`, `--path DIR` out of argv. */
|
|
28
|
-
function parseFlags(args: string[]): Flags | { error: string } {
|
|
29
|
-
const flags: Flags = { top: 20, json: false, path: "src", failOn: null };
|
|
30
|
-
|
|
31
|
-
for (let i = 0; i < args.length; i++) {
|
|
32
|
-
const a = args[i];
|
|
33
|
-
switch (a) {
|
|
34
|
-
case "--json":
|
|
35
|
-
flags.json = true;
|
|
36
|
-
break;
|
|
37
|
-
case "--top": {
|
|
38
|
-
const v = args[++i];
|
|
39
|
-
if (v === undefined || !/^\d+$/.test(v)) {
|
|
40
|
-
return { error: `--top expects a number (got '${v ?? ""}')` };
|
|
41
|
-
}
|
|
42
|
-
flags.top = parseInt(v, 10);
|
|
43
|
-
break;
|
|
44
|
-
}
|
|
45
|
-
case "--path": {
|
|
46
|
-
const v = args[++i];
|
|
47
|
-
if (v === undefined) return { error: "--path expects a directory" };
|
|
48
|
-
flags.path = v;
|
|
49
|
-
break;
|
|
50
|
-
}
|
|
51
|
-
case "--fail-on": {
|
|
52
|
-
const v = args[++i];
|
|
53
|
-
if (v !== "warn" && v !== "error") {
|
|
54
|
-
return { error: `invalid --fail-on '${v ?? ""}' (use warn or error)` };
|
|
55
|
-
}
|
|
56
|
-
flags.failOn = v;
|
|
57
|
-
break;
|
|
58
|
-
}
|
|
59
|
-
default:
|
|
60
|
-
return { error: `unknown option '${a}'` };
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return flags;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Run the metrics report. Returns the process exit code; does NOT call
|
|
69
|
-
* process.exit (the bin wrapper does). 0 = ok / below threshold, 1 = gated
|
|
70
|
-
* failure, 2 = bad arguments / analysis error.
|
|
71
|
-
*/
|
|
72
|
-
export function runMetrics(args: string[] = []): number {
|
|
73
|
-
const parsed = parseFlags(args);
|
|
74
|
-
if ("error" in parsed) {
|
|
75
|
-
console.log(` ${parsed.error}`);
|
|
76
|
-
return 2;
|
|
77
|
-
}
|
|
78
|
-
const { top, json, path, failOn } = parsed;
|
|
79
|
-
|
|
80
|
-
// ONE engine run. Ask for every offender and slice for display: the gate must
|
|
81
|
-
// read the FULL set, not the printed top-N, and the old second call re-ran the
|
|
82
|
-
// whole analysis (its "mtime-cached" comment stopped being true when the
|
|
83
|
-
// in-process analyzer and its cache were deleted -- ADR-0002).
|
|
84
|
-
let result;
|
|
85
|
-
try {
|
|
86
|
-
result = offenders(path, Number.MAX_SAFE_INTEGER);
|
|
87
|
-
} catch (e) {
|
|
88
|
-
if (e instanceof MetricsEngineError) {
|
|
89
|
-
console.error(` metrics error: ${e.message}`);
|
|
90
|
-
return 2;
|
|
91
|
-
}
|
|
92
|
-
throw e;
|
|
93
|
-
}
|
|
94
|
-
const summary = result.summary;
|
|
95
|
-
const allOffenders = result.offenders;
|
|
96
|
-
const found = allOffenders.slice(0, top);
|
|
97
|
-
|
|
98
|
-
const severities = new Set(allOffenders.map((o) => o.severity));
|
|
99
|
-
let exitCode = 0;
|
|
100
|
-
if (failOn === "warn" && (severities.has("warn") || severities.has("error"))) {
|
|
101
|
-
exitCode = 1;
|
|
102
|
-
} else if (failOn === "error" && severities.has("error")) {
|
|
103
|
-
exitCode = 1;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (json) {
|
|
107
|
-
// Print ONLY the JSON — machine-readable for CI / tooling.
|
|
108
|
-
console.log(JSON.stringify({ summary, offenders: found }, null, 2));
|
|
109
|
-
return exitCode;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// ── Human report ──────────────────────────────────────────────────
|
|
113
|
-
const useColor = Boolean(process.stdout.isTTY);
|
|
114
|
-
const c = (text: string, code: string): string =>
|
|
115
|
-
useColor ? `\x1b[${code}m${text}\x1b[0m` : text;
|
|
116
|
-
const sevColor: Record<string, string> = { error: "31", warn: "33", info: "2" }; // red / yellow / dim
|
|
117
|
-
|
|
118
|
-
console.log("");
|
|
119
|
-
console.log(` Tina4 Metrics — ${summary.scan_mode} scan (${summary.scan_root})`);
|
|
120
|
-
console.log(
|
|
121
|
-
` files: ${summary.files_analyzed} ` +
|
|
122
|
-
`functions: ${summary.total_functions} ` +
|
|
123
|
-
`avg complexity: ${summary.avg_complexity} ` +
|
|
124
|
-
`avg maintainability: ${summary.avg_maintainability}`
|
|
125
|
-
);
|
|
126
|
-
console.log(
|
|
127
|
-
` offenders: ${summary.total_offenders} total` +
|
|
128
|
-
(found.length ? ` (showing top ${found.length})` : "")
|
|
129
|
-
);
|
|
130
|
-
console.log("");
|
|
131
|
-
|
|
132
|
-
if (found.length === 0) {
|
|
133
|
-
console.log(" " + c("✓ no offenders — clean", "32"));
|
|
134
|
-
console.log("");
|
|
135
|
-
return exitCode;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Column widths so the table lines up.
|
|
139
|
-
const locs = found.map((o) => `${o.file}:${o.line}`);
|
|
140
|
-
const locW = Math.max("FILE:LINE".length, ...locs.map((s) => s.length));
|
|
141
|
-
const kindW = Math.max("KIND".length, ...found.map((o) => o.kind.length));
|
|
142
|
-
|
|
143
|
-
const pad = (s: string, w: number) => s.padEnd(w);
|
|
144
|
-
const header = ` ${pad("#", 3)} ${pad("SEVERITY", 8)} ${pad("KIND", kindW)} ${pad(
|
|
145
|
-
"FILE:LINE",
|
|
146
|
-
locW
|
|
147
|
-
)} DETAIL`;
|
|
148
|
-
console.log(c(header, "1"));
|
|
149
|
-
console.log(" " + "-".repeat(header.length - 2));
|
|
150
|
-
found.forEach((o, idx) => {
|
|
151
|
-
const i = idx + 1;
|
|
152
|
-
const sevCell = c(pad(o.severity, 8), sevColor[o.severity]);
|
|
153
|
-
console.log(
|
|
154
|
-
` ${String(i).padStart(3)} ${sevCell} ${pad(o.kind, kindW)} ` +
|
|
155
|
-
`${pad(locs[idx], locW)} ${o.detail}`
|
|
156
|
-
);
|
|
157
|
-
});
|
|
158
|
-
console.log("");
|
|
159
|
-
return exitCode;
|
|
160
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Run the metrics report. Returns the process exit code; does NOT call
|
|
3
|
-
* process.exit (the bin wrapper does). 0 = ok / below threshold, 1 = gated
|
|
4
|
-
* failure, 2 = bad arguments / analysis error.
|
|
5
|
-
*/
|
|
6
|
-
export declare function runMetrics(args?: string[]): number;
|