-
-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathvalidate-stream.ts
More file actions
130 lines (109 loc) · 3.63 KB
/
validate-stream.ts
File metadata and controls
130 lines (109 loc) · 3.63 KB
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Copyright 2023-present Eser Ozvataf and other contributors. All rights reserved. Apache-2.0 license.
/**
* Streaming validator run via the Go FFI bridge.
*
* Returns an `AsyncIterable<ValidatorResult>` backed by `EserAjanCodebaseValidateFilesStream*`.
* Each yielded item is one validator's result. Supports `await using` / `Symbol.asyncDispose`.
*
* @module
*/
import { ensureLib, getLib } from "./ffi-client.ts";
import type { ValidatorResult } from "./validation/types.ts";
// =============================================================================
// Types
// =============================================================================
export type ValidateStreamOptions = {
readonly dir?: string;
readonly validators?: readonly string[];
readonly extensions?: readonly string[];
readonly validatorOptions?: Record<string, unknown>;
readonly gitAware?: boolean;
};
// =============================================================================
// ValidateStream
// =============================================================================
export class ValidateStream
implements AsyncIterable<ValidatorResult>, AsyncDisposable {
readonly #handle: string;
constructor(handle: string) {
this.#handle = handle;
}
async *[Symbol.asyncIterator](): AsyncGenerator<ValidatorResult> {
const lib = getLib();
if (lib === null) {
throw new Error(
"FFI library unavailable — cannot iterate validate stream",
);
}
while (true) {
const raw = lib.symbols.EserAjanCodebaseValidateFilesStreamRead(
this.#handle,
);
if (raw === "null") {
break;
}
const parsed = JSON.parse(raw) as {
name?: string;
passed?: boolean;
issues?: Array<{
severity?: string;
file?: string;
line?: number;
message?: string;
}>;
filesChecked?: number;
error?: string;
};
if (parsed.error !== undefined) {
throw new Error(`validate stream error: ${parsed.error}`);
}
yield {
name: parsed.name ?? "",
passed: parsed.passed ?? true,
issues: (parsed.issues ?? []).map((iss) => ({
severity: (iss.severity ?? "error") as "error" | "warning",
message: iss.message ?? "",
file: iss.file,
line: iss.line,
})),
stats: { filesChecked: parsed.filesChecked ?? 0 },
};
}
}
async [Symbol.asyncDispose](): Promise<void> {
const lib = getLib();
if (lib !== null) {
lib.symbols.EserAjanCodebaseValidateFilesStreamClose(this.#handle);
}
}
}
// =============================================================================
// Factory
// =============================================================================
export const validateFilesStream = async (
options: ValidateStreamOptions = {},
): Promise<ValidateStream> => {
await ensureLib();
const lib = getLib();
if (lib === null) {
throw new Error(
"FFI library unavailable — cannot create validate stream",
);
}
const raw = lib.symbols.EserAjanCodebaseValidateFilesStreamCreate(
JSON.stringify({
dir: options.dir ?? ".",
validators: options.validators,
extensions: options.extensions,
validatorOptions: options.validatorOptions,
gitAware: options.gitAware ?? false,
}),
);
const parsed = JSON.parse(raw) as { handle?: string; error?: string };
if (parsed.error !== undefined || parsed.handle === undefined) {
throw new Error(
`validate stream create failed: ${parsed.error ?? "no handle"}`,
);
}
return new ValidateStream(parsed.handle);
};