@oxog/npm-llms 1.0.0
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/LICENSE +21 -0
- package/README.md +286 -0
- package/dist/cli/index.cjs +5154 -0
- package/dist/cli/index.cjs.map +1 -0
- package/dist/cli/index.d.cts +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +5152 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.cjs +3589 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +427 -0
- package/dist/index.d.ts +427 -0
- package/dist/index.js +3558 -0
- package/dist/index.js.map +1 -0
- package/dist/kernel-I4Zn2uXv.d.cts +559 -0
- package/dist/kernel-I4Zn2uXv.d.ts +559 -0
- package/dist/plugins/index.cjs +4171 -0
- package/dist/plugins/index.cjs.map +1 -0
- package/dist/plugins/index.d.cts +452 -0
- package/dist/plugins/index.d.ts +452 -0
- package/dist/plugins/index.js +4133 -0
- package/dist/plugins/index.js.map +1 -0
- package/package.json +88 -0
|
@@ -0,0 +1,4171 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var zlib = require('zlib');
|
|
4
|
+
|
|
5
|
+
// src/errors.ts
|
|
6
|
+
var NpmLlmsError = class extends Error {
|
|
7
|
+
/**
|
|
8
|
+
* Create a new NpmLlmsError
|
|
9
|
+
* @param message - Error message
|
|
10
|
+
* @param code - Error code
|
|
11
|
+
* @param details - Additional error details
|
|
12
|
+
*/
|
|
13
|
+
constructor(message, code, details) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.code = code;
|
|
16
|
+
this.details = details;
|
|
17
|
+
this.name = "NpmLlmsError";
|
|
18
|
+
if (Error.captureStackTrace) {
|
|
19
|
+
Error.captureStackTrace(this, this.constructor);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Convert error to JSON
|
|
24
|
+
*/
|
|
25
|
+
toJSON() {
|
|
26
|
+
return {
|
|
27
|
+
name: this.name,
|
|
28
|
+
message: this.message,
|
|
29
|
+
code: this.code,
|
|
30
|
+
details: this.details
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
var PackageNotFoundError = class extends NpmLlmsError {
|
|
35
|
+
constructor(packageName) {
|
|
36
|
+
super(`Package "${packageName}" not found on npm`, "PACKAGE_NOT_FOUND", { packageName });
|
|
37
|
+
this.name = "PackageNotFoundError";
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
var VersionNotFoundError = class extends NpmLlmsError {
|
|
41
|
+
constructor(packageName, version) {
|
|
42
|
+
super(`Version "${version}" not found for package "${packageName}"`, "VERSION_NOT_FOUND", {
|
|
43
|
+
packageName,
|
|
44
|
+
version
|
|
45
|
+
});
|
|
46
|
+
this.name = "VersionNotFoundError";
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var DownloadError = class extends NpmLlmsError {
|
|
50
|
+
constructor(message, url) {
|
|
51
|
+
super(message, "DOWNLOAD_FAILED", { url });
|
|
52
|
+
this.name = "DownloadError";
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
var AIError = class extends NpmLlmsError {
|
|
56
|
+
constructor(message, provider) {
|
|
57
|
+
super(message, "AI_ERROR", { provider });
|
|
58
|
+
this.name = "AIError";
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
var PluginError = class extends NpmLlmsError {
|
|
62
|
+
constructor(message, pluginName) {
|
|
63
|
+
super(message, "PLUGIN_ERROR", { pluginName });
|
|
64
|
+
this.name = "PluginError";
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
var TarError = class extends NpmLlmsError {
|
|
68
|
+
constructor(message, offset) {
|
|
69
|
+
super(message, "TAR_ERROR", { offset });
|
|
70
|
+
this.name = "TarError";
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
var TimeoutError = class extends NpmLlmsError {
|
|
74
|
+
constructor(message, timeout) {
|
|
75
|
+
super(message, "TIMEOUT_ERROR", { timeout });
|
|
76
|
+
this.name = "TimeoutError";
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
var ValidationError = class extends NpmLlmsError {
|
|
80
|
+
constructor(message, field) {
|
|
81
|
+
super(message, "VALIDATION_ERROR", { field });
|
|
82
|
+
this.name = "ValidationError";
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
var DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
86
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
87
|
+
function createTimeoutController(timeout) {
|
|
88
|
+
const controller = new AbortController();
|
|
89
|
+
const timeoutId = setTimeout(() => {
|
|
90
|
+
controller.abort();
|
|
91
|
+
}, timeout);
|
|
92
|
+
return {
|
|
93
|
+
controller,
|
|
94
|
+
cleanup: () => clearTimeout(timeoutId)
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
async function fetchJson(url, options = {}) {
|
|
98
|
+
const timeout = options.timeout ?? DEFAULT_TIMEOUT;
|
|
99
|
+
const { controller, cleanup } = createTimeoutController(timeout);
|
|
100
|
+
try {
|
|
101
|
+
const response = await fetch(url, {
|
|
102
|
+
method: "GET",
|
|
103
|
+
headers: {
|
|
104
|
+
Accept: "application/json",
|
|
105
|
+
...options.headers
|
|
106
|
+
},
|
|
107
|
+
signal: controller.signal
|
|
108
|
+
});
|
|
109
|
+
if (!response.ok) {
|
|
110
|
+
throw new DownloadError(`HTTP ${response.status}: ${response.statusText}`, url);
|
|
111
|
+
}
|
|
112
|
+
const data = await response.json();
|
|
113
|
+
return {
|
|
114
|
+
data,
|
|
115
|
+
status: response.status,
|
|
116
|
+
headers: response.headers
|
|
117
|
+
};
|
|
118
|
+
} catch (error) {
|
|
119
|
+
if (error instanceof DownloadError) {
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
if (error instanceof Error) {
|
|
123
|
+
if (error.name === "AbortError") {
|
|
124
|
+
throw new TimeoutError(`Request timed out after ${timeout}ms`, timeout);
|
|
125
|
+
}
|
|
126
|
+
throw new DownloadError(error.message, url);
|
|
127
|
+
}
|
|
128
|
+
throw new DownloadError("Unknown fetch error", url);
|
|
129
|
+
} finally {
|
|
130
|
+
cleanup();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
async function fetchBinary(url, options = {}) {
|
|
134
|
+
const timeout = options.timeout ?? DEFAULT_TIMEOUT;
|
|
135
|
+
const { controller, cleanup } = createTimeoutController(timeout);
|
|
136
|
+
try {
|
|
137
|
+
const response = await fetch(url, {
|
|
138
|
+
method: "GET",
|
|
139
|
+
headers: {
|
|
140
|
+
Accept: "application/octet-stream",
|
|
141
|
+
...options.headers
|
|
142
|
+
},
|
|
143
|
+
signal: controller.signal
|
|
144
|
+
});
|
|
145
|
+
if (!response.ok) {
|
|
146
|
+
throw new DownloadError(`HTTP ${response.status}: ${response.statusText}`, url);
|
|
147
|
+
}
|
|
148
|
+
const data = await response.arrayBuffer();
|
|
149
|
+
return {
|
|
150
|
+
data,
|
|
151
|
+
status: response.status,
|
|
152
|
+
headers: response.headers
|
|
153
|
+
};
|
|
154
|
+
} catch (error) {
|
|
155
|
+
if (error instanceof DownloadError) {
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
if (error instanceof Error) {
|
|
159
|
+
if (error.name === "AbortError") {
|
|
160
|
+
throw new TimeoutError(`Request timed out after ${timeout}ms`, timeout);
|
|
161
|
+
}
|
|
162
|
+
throw new DownloadError(error.message, url);
|
|
163
|
+
}
|
|
164
|
+
throw new DownloadError("Unknown fetch error", url);
|
|
165
|
+
} finally {
|
|
166
|
+
cleanup();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
async function fetchGzipped(url, options = {}) {
|
|
170
|
+
const { data: gzipped } = await fetchBinary(url, options);
|
|
171
|
+
try {
|
|
172
|
+
return zlib.gunzipSync(Buffer.from(gzipped));
|
|
173
|
+
} catch (error) {
|
|
174
|
+
const message = error instanceof Error ? error.message : "Unknown decompression error";
|
|
175
|
+
throw new DownloadError(`Failed to decompress gzip: ${message}`, url);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function encodePackageName(name) {
|
|
179
|
+
if (name.startsWith("@")) {
|
|
180
|
+
const slashIndex = name.indexOf("/");
|
|
181
|
+
if (slashIndex === -1) {
|
|
182
|
+
return encodeURIComponent(name);
|
|
183
|
+
}
|
|
184
|
+
const scope = name.slice(0, slashIndex);
|
|
185
|
+
const pkg = name.slice(slashIndex + 1);
|
|
186
|
+
return `${scope}%2F${encodeURIComponent(pkg)}`;
|
|
187
|
+
}
|
|
188
|
+
return encodeURIComponent(name);
|
|
189
|
+
}
|
|
190
|
+
function buildPackageUrl(name, version, registry = DEFAULT_REGISTRY) {
|
|
191
|
+
const encodedName = encodePackageName(name);
|
|
192
|
+
const base = `${registry}/${encodedName}`;
|
|
193
|
+
return version ? `${base}/${version}` : base;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// src/utils/tar.ts
|
|
197
|
+
var BLOCK_SIZE = 512;
|
|
198
|
+
var TAR_TYPES = {
|
|
199
|
+
FILE: "0",
|
|
200
|
+
FILE_ALT: "",
|
|
201
|
+
GNU_LONGNAME: "L"};
|
|
202
|
+
var decoder = new TextDecoder("utf-8");
|
|
203
|
+
function isZeroBlock(buffer, offset) {
|
|
204
|
+
const end = Math.min(offset + BLOCK_SIZE, buffer.length);
|
|
205
|
+
for (let i = offset; i < end; i++) {
|
|
206
|
+
if (buffer[i] !== 0) return false;
|
|
207
|
+
}
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
function parseOctal(buffer, offset, length) {
|
|
211
|
+
const bytes = buffer.slice(offset, offset + length);
|
|
212
|
+
const str = decoder.decode(bytes).replace(/\0/g, "").trim();
|
|
213
|
+
if (!str) return 0;
|
|
214
|
+
return parseInt(str, 8) || 0;
|
|
215
|
+
}
|
|
216
|
+
function parseString(buffer, offset, length) {
|
|
217
|
+
const bytes = buffer.slice(offset, offset + length);
|
|
218
|
+
const str = decoder.decode(bytes);
|
|
219
|
+
const nullIndex = str.indexOf("\0");
|
|
220
|
+
return nullIndex >= 0 ? str.slice(0, nullIndex) : str;
|
|
221
|
+
}
|
|
222
|
+
function calculateChecksum(buffer, offset) {
|
|
223
|
+
let sum = 0;
|
|
224
|
+
for (let i = 0; i < BLOCK_SIZE; i++) {
|
|
225
|
+
if (i >= 148 && i < 156) {
|
|
226
|
+
sum += 32;
|
|
227
|
+
} else {
|
|
228
|
+
sum += buffer[offset + i] ?? 0;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return sum;
|
|
232
|
+
}
|
|
233
|
+
function parseTarHeader(buffer, offset) {
|
|
234
|
+
if (isZeroBlock(buffer, offset)) {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
if (offset + BLOCK_SIZE > buffer.length) {
|
|
238
|
+
throw new TarError("Unexpected end of tar archive", offset);
|
|
239
|
+
}
|
|
240
|
+
const name = parseString(buffer, offset, 100);
|
|
241
|
+
const mode = parseOctal(buffer, offset + 100, 8);
|
|
242
|
+
const uid = parseOctal(buffer, offset + 108, 8);
|
|
243
|
+
const gid = parseOctal(buffer, offset + 116, 8);
|
|
244
|
+
const size = parseOctal(buffer, offset + 124, 12);
|
|
245
|
+
const mtime = parseOctal(buffer, offset + 136, 12);
|
|
246
|
+
const checksum = parseOctal(buffer, offset + 148, 8);
|
|
247
|
+
const type = parseString(buffer, offset + 156, 1);
|
|
248
|
+
const linkname = parseString(buffer, offset + 157, 100);
|
|
249
|
+
const calculatedChecksum = calculateChecksum(buffer, offset);
|
|
250
|
+
if (checksum !== 0 && checksum !== calculatedChecksum) {
|
|
251
|
+
throw new TarError(`Invalid tar header checksum: expected ${checksum}, got ${calculatedChecksum}`, offset);
|
|
252
|
+
}
|
|
253
|
+
const magic = parseString(buffer, offset + 257, 6);
|
|
254
|
+
let fullName = name;
|
|
255
|
+
if (magic === "ustar" || magic === "ustar ") {
|
|
256
|
+
const prefix = parseString(buffer, offset + 345, 155);
|
|
257
|
+
if (prefix) {
|
|
258
|
+
fullName = `${prefix}/${name}`;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
name: fullName,
|
|
263
|
+
mode,
|
|
264
|
+
uid,
|
|
265
|
+
gid,
|
|
266
|
+
size,
|
|
267
|
+
mtime,
|
|
268
|
+
checksum,
|
|
269
|
+
type,
|
|
270
|
+
linkname
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
function sanitizePath(path) {
|
|
274
|
+
let normalized = path.replace(/\\/g, "/");
|
|
275
|
+
if (normalized.startsWith("/")) {
|
|
276
|
+
normalized = normalized.slice(1);
|
|
277
|
+
}
|
|
278
|
+
const parts = normalized.split("/");
|
|
279
|
+
const safeParts = [];
|
|
280
|
+
for (const part of parts) {
|
|
281
|
+
if (part === "..") {
|
|
282
|
+
throw new TarError(`Path traversal detected: ${path}`);
|
|
283
|
+
}
|
|
284
|
+
if (part && part !== ".") {
|
|
285
|
+
safeParts.push(part);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return safeParts.join("/");
|
|
289
|
+
}
|
|
290
|
+
function removePackagePrefix(path) {
|
|
291
|
+
const prefixes = ["package/", "package\\"];
|
|
292
|
+
for (const prefix of prefixes) {
|
|
293
|
+
if (path.startsWith(prefix)) {
|
|
294
|
+
return path.slice(prefix.length);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return path;
|
|
298
|
+
}
|
|
299
|
+
function getEntryType(_type) {
|
|
300
|
+
return "file";
|
|
301
|
+
}
|
|
302
|
+
function shouldExtract(path) {
|
|
303
|
+
const lower = path.toLowerCase();
|
|
304
|
+
if (lower.endsWith(".d.ts") || lower.endsWith(".d.mts") || lower.endsWith(".d.cts")) {
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
if (lower.endsWith(".ts") || lower.endsWith(".tsx") || lower.endsWith(".js") || lower.endsWith(".mjs") || lower.endsWith(".cjs") || lower.endsWith(".jsx")) {
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
if (lower.endsWith(".md") || lower.endsWith(".txt") || lower.endsWith(".json") || lower.endsWith(".yaml") || lower.endsWith(".yml")) {
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
const filename = path.split("/").pop() ?? "";
|
|
314
|
+
if (filename === "package.json" || filename === "README.md" || filename === "CHANGELOG.md" || filename === "LICENSE" || filename === "LICENSE.md") {
|
|
315
|
+
return true;
|
|
316
|
+
}
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
function* extractTarSync(buffer) {
|
|
320
|
+
const data = buffer instanceof Buffer ? new Uint8Array(buffer) : new Uint8Array(buffer);
|
|
321
|
+
let offset = 0;
|
|
322
|
+
let gnuLongName = null;
|
|
323
|
+
while (offset < data.length) {
|
|
324
|
+
const header = parseTarHeader(data, offset);
|
|
325
|
+
if (!header) {
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
offset += BLOCK_SIZE;
|
|
329
|
+
if (header.type === TAR_TYPES.GNU_LONGNAME) {
|
|
330
|
+
const contentBytes = data.slice(offset, offset + header.size);
|
|
331
|
+
gnuLongName = decoder.decode(contentBytes).replace(/\0/g, "");
|
|
332
|
+
offset += Math.ceil(header.size / BLOCK_SIZE) * BLOCK_SIZE;
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
const entryName = gnuLongName ?? header.name;
|
|
336
|
+
gnuLongName = null;
|
|
337
|
+
if (header.type !== TAR_TYPES.FILE && header.type !== TAR_TYPES.FILE_ALT) {
|
|
338
|
+
offset += Math.ceil(header.size / BLOCK_SIZE) * BLOCK_SIZE;
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
let path;
|
|
342
|
+
try {
|
|
343
|
+
path = sanitizePath(entryName);
|
|
344
|
+
path = removePackagePrefix(path);
|
|
345
|
+
} catch {
|
|
346
|
+
offset += Math.ceil(header.size / BLOCK_SIZE) * BLOCK_SIZE;
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
if (!shouldExtract(path)) {
|
|
350
|
+
offset += Math.ceil(header.size / BLOCK_SIZE) * BLOCK_SIZE;
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
const content = data.slice(offset, offset + header.size);
|
|
354
|
+
const textContent = decoder.decode(content);
|
|
355
|
+
yield {
|
|
356
|
+
path,
|
|
357
|
+
content: textContent,
|
|
358
|
+
size: header.size,
|
|
359
|
+
type: getEntryType(header.type)
|
|
360
|
+
};
|
|
361
|
+
offset += Math.ceil(header.size / BLOCK_SIZE) * BLOCK_SIZE;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function extractTarToMap(buffer) {
|
|
365
|
+
const files = /* @__PURE__ */ new Map();
|
|
366
|
+
for (const entry of extractTarSync(buffer)) {
|
|
367
|
+
if (entry.type === "file") {
|
|
368
|
+
files.set(entry.path, entry.content);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return files;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// src/core/fetcher.ts
|
|
375
|
+
function validatePackageName(name) {
|
|
376
|
+
if (!name || typeof name !== "string") {
|
|
377
|
+
throw new ValidationError("Package name is required", "name");
|
|
378
|
+
}
|
|
379
|
+
const validPattern = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
|
|
380
|
+
if (!validPattern.test(name)) {
|
|
381
|
+
throw new ValidationError(
|
|
382
|
+
`Invalid package name: "${name}". Package names must be lowercase and may contain letters, numbers, hyphens, dots, and underscores.`,
|
|
383
|
+
"name"
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
if (name.length > 214) {
|
|
387
|
+
throw new ValidationError("Package name cannot exceed 214 characters", "name");
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
function parsePackageSpec(spec) {
|
|
391
|
+
if (!spec || typeof spec !== "string") {
|
|
392
|
+
throw new ValidationError("Package specifier is required", "spec");
|
|
393
|
+
}
|
|
394
|
+
const trimmed = spec.trim();
|
|
395
|
+
if (trimmed.startsWith("@")) {
|
|
396
|
+
const firstSlash = trimmed.indexOf("/");
|
|
397
|
+
if (firstSlash === -1) {
|
|
398
|
+
throw new ValidationError(`Invalid scoped package: "${spec}"`, "spec");
|
|
399
|
+
}
|
|
400
|
+
const afterScope = trimmed.slice(firstSlash + 1);
|
|
401
|
+
const versionSep = afterScope.lastIndexOf("@");
|
|
402
|
+
if (versionSep > 0) {
|
|
403
|
+
const name = trimmed.slice(0, firstSlash + 1 + versionSep);
|
|
404
|
+
const version = afterScope.slice(versionSep + 1);
|
|
405
|
+
return { name, version: version || void 0 };
|
|
406
|
+
}
|
|
407
|
+
return { name: trimmed };
|
|
408
|
+
}
|
|
409
|
+
const atIndex = trimmed.lastIndexOf("@");
|
|
410
|
+
if (atIndex > 0) {
|
|
411
|
+
const name = trimmed.slice(0, atIndex);
|
|
412
|
+
const version = trimmed.slice(atIndex + 1);
|
|
413
|
+
return { name, version: version || void 0 };
|
|
414
|
+
}
|
|
415
|
+
return { name: trimmed };
|
|
416
|
+
}
|
|
417
|
+
function resolveVersion(packageName, versions, distTags, requested) {
|
|
418
|
+
if (!requested) {
|
|
419
|
+
const latest = distTags["latest"];
|
|
420
|
+
if (latest && versions.includes(latest)) {
|
|
421
|
+
return latest;
|
|
422
|
+
}
|
|
423
|
+
return versions.sort(compareVersions).pop();
|
|
424
|
+
}
|
|
425
|
+
if (distTags[requested]) {
|
|
426
|
+
return distTags[requested];
|
|
427
|
+
}
|
|
428
|
+
if (versions.includes(requested)) {
|
|
429
|
+
return requested;
|
|
430
|
+
}
|
|
431
|
+
const matching = versions.filter((v) => v.startsWith(requested));
|
|
432
|
+
if (matching.length > 0) {
|
|
433
|
+
return matching.sort(compareVersions).pop();
|
|
434
|
+
}
|
|
435
|
+
throw new VersionNotFoundError(packageName, requested);
|
|
436
|
+
}
|
|
437
|
+
function compareVersions(a, b) {
|
|
438
|
+
const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
|
|
439
|
+
const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
|
|
440
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
441
|
+
const na = pa[i] || 0;
|
|
442
|
+
const nb = pb[i] || 0;
|
|
443
|
+
if (na !== nb) return na - nb;
|
|
444
|
+
}
|
|
445
|
+
return 0;
|
|
446
|
+
}
|
|
447
|
+
async function fetchPackageMetadata(name, version, options = {}) {
|
|
448
|
+
validatePackageName(name);
|
|
449
|
+
const registry = options.registry ?? DEFAULT_REGISTRY;
|
|
450
|
+
const httpOptions = { timeout: options.timeout };
|
|
451
|
+
if (version && !["latest", "next", "beta", "alpha"].includes(version)) {
|
|
452
|
+
try {
|
|
453
|
+
const url2 = buildPackageUrl(name, version, registry);
|
|
454
|
+
const { data } = await fetchJson(url2, httpOptions);
|
|
455
|
+
return extractMetadata(data);
|
|
456
|
+
} catch (error) {
|
|
457
|
+
if (!(error instanceof DownloadError) || !error.message.includes("404")) {
|
|
458
|
+
throw error;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
const url = buildPackageUrl(name, void 0, registry);
|
|
463
|
+
try {
|
|
464
|
+
const { data } = await fetchJson(url, httpOptions);
|
|
465
|
+
const versions = Object.keys(data.versions ?? {});
|
|
466
|
+
if (versions.length === 0) {
|
|
467
|
+
throw new PackageNotFoundError(name);
|
|
468
|
+
}
|
|
469
|
+
const resolvedVersion = resolveVersion(
|
|
470
|
+
name,
|
|
471
|
+
versions,
|
|
472
|
+
data["dist-tags"] ?? {},
|
|
473
|
+
version
|
|
474
|
+
);
|
|
475
|
+
const versionData = data.versions[resolvedVersion];
|
|
476
|
+
if (!versionData) {
|
|
477
|
+
throw new VersionNotFoundError(name, resolvedVersion);
|
|
478
|
+
}
|
|
479
|
+
return extractMetadata(versionData);
|
|
480
|
+
} catch (error) {
|
|
481
|
+
if (error instanceof DownloadError && error.message.includes("404")) {
|
|
482
|
+
throw new PackageNotFoundError(name);
|
|
483
|
+
}
|
|
484
|
+
throw error;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
function extractMetadata(data) {
|
|
488
|
+
if (!data.dist?.tarball) {
|
|
489
|
+
throw new DownloadError("Package metadata missing tarball URL");
|
|
490
|
+
}
|
|
491
|
+
let repository;
|
|
492
|
+
if (data.repository) {
|
|
493
|
+
if (typeof data.repository === "string") {
|
|
494
|
+
repository = { type: "git", url: data.repository };
|
|
495
|
+
} else {
|
|
496
|
+
repository = data.repository;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
return {
|
|
500
|
+
name: data.name,
|
|
501
|
+
version: data.version,
|
|
502
|
+
description: data.description,
|
|
503
|
+
tarball: data.dist.tarball,
|
|
504
|
+
types: data.types ?? data.typings,
|
|
505
|
+
main: data.main,
|
|
506
|
+
exports: data.exports,
|
|
507
|
+
repository,
|
|
508
|
+
keywords: data.keywords,
|
|
509
|
+
author: data.author,
|
|
510
|
+
license: data.license,
|
|
511
|
+
homepage: data.homepage
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
async function downloadPackageFiles(tarballUrl, options = {}) {
|
|
515
|
+
const buffer = await fetchGzipped(tarballUrl, options);
|
|
516
|
+
return extractTarToMap(buffer);
|
|
517
|
+
}
|
|
518
|
+
async function fetchPackage(spec, options = {}) {
|
|
519
|
+
const { name, version } = parsePackageSpec(spec);
|
|
520
|
+
const metadata = await fetchPackageMetadata(name, version, options);
|
|
521
|
+
const files = await downloadPackageFiles(metadata.tarball, {
|
|
522
|
+
timeout: options.timeout
|
|
523
|
+
});
|
|
524
|
+
return {
|
|
525
|
+
...metadata,
|
|
526
|
+
files
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// src/plugins/core/types-resolver.ts
|
|
531
|
+
function hasDtsFiles(files) {
|
|
532
|
+
for (const path of files.keys()) {
|
|
533
|
+
if (path.endsWith(".d.ts")) {
|
|
534
|
+
return true;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return false;
|
|
538
|
+
}
|
|
539
|
+
function getTypesPackageName(packageName) {
|
|
540
|
+
if (packageName.startsWith("@")) {
|
|
541
|
+
const withoutAt = packageName.slice(1);
|
|
542
|
+
return `@types/${withoutAt.replace("/", "__")}`;
|
|
543
|
+
}
|
|
544
|
+
return `@types/${packageName}`;
|
|
545
|
+
}
|
|
546
|
+
function isTypesPackage(packageName) {
|
|
547
|
+
return packageName.startsWith("@types/");
|
|
548
|
+
}
|
|
549
|
+
var typesResolverPlugin = {
|
|
550
|
+
name: "types-resolver",
|
|
551
|
+
version: "1.0.0",
|
|
552
|
+
category: "parser",
|
|
553
|
+
install(kernel) {
|
|
554
|
+
kernel.on("package:fetched", async (context) => {
|
|
555
|
+
const { files, name } = context.package;
|
|
556
|
+
if (isTypesPackage(name)) {
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
if (hasDtsFiles(files)) {
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
const typesPackageName = getTypesPackageName(name);
|
|
563
|
+
try {
|
|
564
|
+
const typesPackage = await fetchPackage(typesPackageName);
|
|
565
|
+
let mergedCount = 0;
|
|
566
|
+
for (const [path, content] of typesPackage.files) {
|
|
567
|
+
if (path.endsWith(".d.ts")) {
|
|
568
|
+
const newPath = `__types__/${path}`;
|
|
569
|
+
files.set(newPath, content);
|
|
570
|
+
mergedCount++;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
if (typesPackage.types && !context.package.types) {
|
|
574
|
+
context.package.types = `__types__/${typesPackage.types}`;
|
|
575
|
+
}
|
|
576
|
+
if (mergedCount > 0) {
|
|
577
|
+
console.error(
|
|
578
|
+
`[types-resolver] Merged ${mergedCount} .d.ts files from ${typesPackageName}`
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
} catch (error) {
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
// src/parsers/jsdoc.ts
|
|
588
|
+
var TAG_PATTERNS = {
|
|
589
|
+
/** @param {type} name - description */
|
|
590
|
+
param: /@param\s+(?:\{([^}]*)\})?\s*(\[?\w+(?:\.\w+)*\]?)\s*(?:-\s*)?(.*)$/gm,
|
|
591
|
+
/** @returns {type} description */
|
|
592
|
+
returns: /@returns?\s+(?:\{([^}]*)\})?\s*(.*)/g,
|
|
593
|
+
/** @example ... */
|
|
594
|
+
example: /@example\s*([\s\S]*?)(?=@\w|$)/g,
|
|
595
|
+
/** @deprecated reason */
|
|
596
|
+
deprecated: /@deprecated\s*(.*)/g,
|
|
597
|
+
/** @since version */
|
|
598
|
+
since: /@since\s*(.*)/g,
|
|
599
|
+
/** @see reference */
|
|
600
|
+
see: /@see\s*(.*)/g,
|
|
601
|
+
/** @throws {type} description */
|
|
602
|
+
throws: /@throws?\s+(?:\{([^}]*)\})?\s*(.*)/g,
|
|
603
|
+
/** @template T - description */
|
|
604
|
+
typeParam: /@template\s+(\w+)\s*(?:-\s*)?(.*)/g};
|
|
605
|
+
function cleanJSDocComment(comment) {
|
|
606
|
+
return comment.replace(/^\/\*\*\s*/, "").replace(/\s*\*\/$/, "").replace(/^\s*\*\s?/gm, "").trim();
|
|
607
|
+
}
|
|
608
|
+
function extractDescription(content) {
|
|
609
|
+
const tagMatch = content.match(/@\w+/);
|
|
610
|
+
if (!tagMatch) {
|
|
611
|
+
return content.trim() || void 0;
|
|
612
|
+
}
|
|
613
|
+
const description = content.slice(0, tagMatch.index).trim();
|
|
614
|
+
return description || void 0;
|
|
615
|
+
}
|
|
616
|
+
function parseParams(content) {
|
|
617
|
+
const params = [];
|
|
618
|
+
const pattern = new RegExp(TAG_PATTERNS.param.source, "gm");
|
|
619
|
+
let match;
|
|
620
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
621
|
+
const [, type, rawName, description] = match;
|
|
622
|
+
if (!rawName) continue;
|
|
623
|
+
const name = rawName;
|
|
624
|
+
const isOptional = name.startsWith("[") && name.endsWith("]");
|
|
625
|
+
const cleanName = isOptional ? name.slice(1, -1) : name;
|
|
626
|
+
params.push({
|
|
627
|
+
name: cleanName,
|
|
628
|
+
type: type?.trim(),
|
|
629
|
+
description: description?.trim() || void 0,
|
|
630
|
+
optional: isOptional
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
return params;
|
|
634
|
+
}
|
|
635
|
+
function parseReturns(content) {
|
|
636
|
+
const pattern = new RegExp(TAG_PATTERNS.returns.source, "g");
|
|
637
|
+
const match = pattern.exec(content);
|
|
638
|
+
if (!match) return void 0;
|
|
639
|
+
const [, type, description] = match;
|
|
640
|
+
return {
|
|
641
|
+
type: type?.trim(),
|
|
642
|
+
description: description?.trim() || void 0
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
function parseExamples(content) {
|
|
646
|
+
const examples = [];
|
|
647
|
+
const pattern = new RegExp(TAG_PATTERNS.example.source, "g");
|
|
648
|
+
let match;
|
|
649
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
650
|
+
const example = match[1]?.trim();
|
|
651
|
+
if (example) {
|
|
652
|
+
examples.push(example);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
return examples;
|
|
656
|
+
}
|
|
657
|
+
function parseDeprecated(content) {
|
|
658
|
+
const pattern = new RegExp(TAG_PATTERNS.deprecated.source, "g");
|
|
659
|
+
const match = pattern.exec(content);
|
|
660
|
+
return match ? match[1]?.trim() || "true" : void 0;
|
|
661
|
+
}
|
|
662
|
+
function parseSince(content) {
|
|
663
|
+
const pattern = new RegExp(TAG_PATTERNS.since.source, "g");
|
|
664
|
+
const match = pattern.exec(content);
|
|
665
|
+
return match ? match[1]?.trim() : void 0;
|
|
666
|
+
}
|
|
667
|
+
function parseSee(content) {
|
|
668
|
+
const refs = [];
|
|
669
|
+
const pattern = new RegExp(TAG_PATTERNS.see.source, "g");
|
|
670
|
+
let match;
|
|
671
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
672
|
+
const ref = match[1]?.trim();
|
|
673
|
+
if (ref) {
|
|
674
|
+
refs.push(ref);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
return refs;
|
|
678
|
+
}
|
|
679
|
+
function parseThrows(content) {
|
|
680
|
+
const throws = [];
|
|
681
|
+
const pattern = new RegExp(TAG_PATTERNS.throws.source, "g");
|
|
682
|
+
let match;
|
|
683
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
684
|
+
const [, type, description] = match;
|
|
685
|
+
throws.push({
|
|
686
|
+
type: type?.trim(),
|
|
687
|
+
description: description?.trim() || void 0
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
return throws;
|
|
691
|
+
}
|
|
692
|
+
function parseTypeParams(content) {
|
|
693
|
+
const params = [];
|
|
694
|
+
const pattern = new RegExp(TAG_PATTERNS.typeParam.source, "g");
|
|
695
|
+
let match;
|
|
696
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
697
|
+
const [, name, description] = match;
|
|
698
|
+
if (!name) continue;
|
|
699
|
+
params.push({
|
|
700
|
+
name: name.trim(),
|
|
701
|
+
description: description?.trim() || void 0
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
return params;
|
|
705
|
+
}
|
|
706
|
+
function parseJSDoc(comment) {
|
|
707
|
+
const content = cleanJSDocComment(comment);
|
|
708
|
+
return {
|
|
709
|
+
description: extractDescription(content),
|
|
710
|
+
params: parseParams(content).map((p) => ({
|
|
711
|
+
name: p.name,
|
|
712
|
+
type: p.type,
|
|
713
|
+
description: p.description,
|
|
714
|
+
optional: p.optional,
|
|
715
|
+
defaultValue: p.defaultValue
|
|
716
|
+
})),
|
|
717
|
+
returns: parseReturns(content),
|
|
718
|
+
examples: parseExamples(content),
|
|
719
|
+
deprecated: parseDeprecated(content),
|
|
720
|
+
since: parseSince(content),
|
|
721
|
+
see: parseSee(content),
|
|
722
|
+
throws: parseThrows(content),
|
|
723
|
+
typeParams: parseTypeParams(content)
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
function extractJSDocComments(source) {
|
|
727
|
+
const comments = [];
|
|
728
|
+
const pattern = /\/\*\*[\s\S]*?\*\//g;
|
|
729
|
+
let match;
|
|
730
|
+
while ((match = pattern.exec(source)) !== null) {
|
|
731
|
+
comments.push({
|
|
732
|
+
comment: match[0],
|
|
733
|
+
start: match.index,
|
|
734
|
+
end: match.index + match[0].length
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
return comments;
|
|
738
|
+
}
|
|
739
|
+
function findPrecedingJSDoc(source, position, comments) {
|
|
740
|
+
const allComments = comments ?? extractJSDocComments(source);
|
|
741
|
+
for (let i = allComments.length - 1; i >= 0; i--) {
|
|
742
|
+
const comment = allComments[i];
|
|
743
|
+
if (!comment || comment.end > position) continue;
|
|
744
|
+
const between = source.slice(comment.end, position);
|
|
745
|
+
if (/^\s*$/.test(between)) {
|
|
746
|
+
return parseJSDoc(comment.comment);
|
|
747
|
+
}
|
|
748
|
+
if (comment.end < position - 500) break;
|
|
749
|
+
}
|
|
750
|
+
return void 0;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// src/parsers/dts.ts
|
|
754
|
+
var PATTERNS = {
|
|
755
|
+
/** Export function declaration */
|
|
756
|
+
exportFunction: /export\s+(?:declare\s+)?function\s+(\w+)\s*(<[^>]*>)?\s*\(([^)]*)\)\s*:\s*([^;{]+)/g,
|
|
757
|
+
/** Export const declaration */
|
|
758
|
+
exportConst: /export\s+(?:declare\s+)?const\s+(\w+)\s*:\s*([^;=]+)/g,
|
|
759
|
+
/** Export class declaration */
|
|
760
|
+
exportClass: /export\s+(?:declare\s+)?(?:abstract\s+)?class\s+(\w+)(?:\s*<([^>]*)>)?(?:\s+extends\s+([^\s{]+))?(?:\s+implements\s+([^{]+))?\s*\{/g,
|
|
761
|
+
/** Export interface declaration */
|
|
762
|
+
exportInterface: /export\s+(?:declare\s+)?interface\s+(\w+)(?:\s*<([^>]*)>)?(?:\s+extends\s+([^{]+))?\s*\{/g,
|
|
763
|
+
/** Export type declaration */
|
|
764
|
+
exportType: /export\s+(?:declare\s+)?type\s+(\w+)(?:\s*<([^>]*)>)?\s*=\s*([^;]+)/g,
|
|
765
|
+
/** Export enum declaration */
|
|
766
|
+
exportEnum: /export\s+(?:declare\s+)?(?:const\s+)?enum\s+(\w+)\s*\{/g,
|
|
767
|
+
/** Import statement */
|
|
768
|
+
importStatement: /import\s*(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)\s*from\s*['"]([^'"]+)['"]/g,
|
|
769
|
+
/** Declare module (module augmentation) */
|
|
770
|
+
declareModule: /declare\s+module\s+['"]([^'"]+)['"]\s*\{/g};
|
|
771
|
+
function parseParameters(paramStr) {
|
|
772
|
+
if (!paramStr.trim()) return [];
|
|
773
|
+
const params = [];
|
|
774
|
+
const parts = splitParameters(paramStr);
|
|
775
|
+
for (const part of parts) {
|
|
776
|
+
const trimmed = part.trim();
|
|
777
|
+
if (!trimmed) continue;
|
|
778
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
779
|
+
const colonIndex = findTypeColonIndex(trimmed);
|
|
780
|
+
if (colonIndex > 0) {
|
|
781
|
+
params.push({
|
|
782
|
+
name: "options",
|
|
783
|
+
type: trimmed.slice(colonIndex + 1).trim(),
|
|
784
|
+
optional: trimmed.includes("?")
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
789
|
+
const match = trimmed.match(/^(\w+)(\?)?\s*:\s*(.+?)(?:\s*=\s*(.+))?$/);
|
|
790
|
+
if (match) {
|
|
791
|
+
const [, name, optional, type, defaultValue] = match;
|
|
792
|
+
if (name && type) {
|
|
793
|
+
params.push({
|
|
794
|
+
name,
|
|
795
|
+
type: type.trim(),
|
|
796
|
+
optional: !!optional,
|
|
797
|
+
defaultValue: defaultValue?.trim()
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
} else {
|
|
801
|
+
const nameMatch = trimmed.match(/^(\w+)(\?)?$/);
|
|
802
|
+
if (nameMatch && nameMatch[1]) {
|
|
803
|
+
params.push({
|
|
804
|
+
name: nameMatch[1],
|
|
805
|
+
type: "unknown",
|
|
806
|
+
optional: !!nameMatch[2]
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
return params;
|
|
812
|
+
}
|
|
813
|
+
function splitParameters(paramStr) {
|
|
814
|
+
const parts = [];
|
|
815
|
+
let current = "";
|
|
816
|
+
let depth = 0;
|
|
817
|
+
for (let i = 0; i < paramStr.length; i++) {
|
|
818
|
+
const char = paramStr[i];
|
|
819
|
+
if (char === "<" || char === "{" || char === "[" || char === "(") {
|
|
820
|
+
depth++;
|
|
821
|
+
current += char;
|
|
822
|
+
} else if (char === ">" || char === "}" || char === "]" || char === ")") {
|
|
823
|
+
depth--;
|
|
824
|
+
current += char;
|
|
825
|
+
} else if (char === "," && depth === 0) {
|
|
826
|
+
parts.push(current);
|
|
827
|
+
current = "";
|
|
828
|
+
} else {
|
|
829
|
+
current += char;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
if (current.trim()) {
|
|
833
|
+
parts.push(current);
|
|
834
|
+
}
|
|
835
|
+
return parts;
|
|
836
|
+
}
|
|
837
|
+
function findTypeColonIndex(str) {
|
|
838
|
+
let depth = 0;
|
|
839
|
+
for (let i = 0; i < str.length; i++) {
|
|
840
|
+
const char = str[i];
|
|
841
|
+
if (char === "{" || char === "[" || char === "<" || char === "(") {
|
|
842
|
+
depth++;
|
|
843
|
+
} else if (char === "}" || char === "]" || char === ">" || char === ")") {
|
|
844
|
+
depth--;
|
|
845
|
+
} else if (char === ":" && depth === 0) {
|
|
846
|
+
return i;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
return -1;
|
|
850
|
+
}
|
|
851
|
+
function extractBody(source, startIndex) {
|
|
852
|
+
let depth = 0;
|
|
853
|
+
let i = startIndex;
|
|
854
|
+
while (i < source.length) {
|
|
855
|
+
if (source[i] === "{") depth++;
|
|
856
|
+
else if (source[i] === "}") {
|
|
857
|
+
depth--;
|
|
858
|
+
if (depth === 0) break;
|
|
859
|
+
}
|
|
860
|
+
i++;
|
|
861
|
+
}
|
|
862
|
+
return source.slice(startIndex + 1, i);
|
|
863
|
+
}
|
|
864
|
+
function parseClassMembers(body, comments, bodyOffset) {
|
|
865
|
+
const methods = [];
|
|
866
|
+
const properties = [];
|
|
867
|
+
const methodPattern = /(?:(?:public|private|protected|static|readonly)\s+)*(\w+)\s*(<[^>]*>)?\s*\(([^)]*)\)\s*:\s*([^;{]+)/g;
|
|
868
|
+
let match;
|
|
869
|
+
while ((match = methodPattern.exec(body)) !== null) {
|
|
870
|
+
const [fullMatch, name, generics, params, returnType] = match;
|
|
871
|
+
if (!name || !returnType) continue;
|
|
872
|
+
const jsdoc = findPrecedingJSDoc(body, match.index, comments.map((c) => ({
|
|
873
|
+
...c,
|
|
874
|
+
start: c.start - bodyOffset,
|
|
875
|
+
end: c.end - bodyOffset
|
|
876
|
+
})));
|
|
877
|
+
methods.push({
|
|
878
|
+
kind: "function",
|
|
879
|
+
name,
|
|
880
|
+
signature: `${name}${generics || ""}(${params || ""}): ${returnType.trim()}`,
|
|
881
|
+
description: jsdoc?.description,
|
|
882
|
+
params: mergeParamDocs(parseParameters(params || ""), jsdoc?.params),
|
|
883
|
+
returns: {
|
|
884
|
+
type: returnType.trim(),
|
|
885
|
+
description: jsdoc?.returns?.description
|
|
886
|
+
},
|
|
887
|
+
examples: jsdoc?.examples
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
const propPattern = /(?:(?:public|private|protected|static|readonly)\s+)*(\w+)(\?)?\s*:\s*([^;]+)/g;
|
|
891
|
+
while ((match = propPattern.exec(body)) !== null) {
|
|
892
|
+
const [, name, optional, type] = match;
|
|
893
|
+
if (!name || !type) continue;
|
|
894
|
+
if (type.includes("=>") || type.includes("(")) continue;
|
|
895
|
+
properties.push({
|
|
896
|
+
kind: "constant",
|
|
897
|
+
name,
|
|
898
|
+
signature: `${name}${optional || ""}: ${type.trim()}`,
|
|
899
|
+
description: findPrecedingJSDoc(body, match.index)?.description
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
return { methods, properties };
|
|
903
|
+
}
|
|
904
|
+
function mergeParamDocs(sigParams, jsdocParams) {
|
|
905
|
+
if (!jsdocParams || jsdocParams.length === 0) return sigParams;
|
|
906
|
+
return sigParams.map((param) => {
|
|
907
|
+
const jsdocParam = jsdocParams.find((p) => p.name === param.name);
|
|
908
|
+
if (jsdocParam) {
|
|
909
|
+
return {
|
|
910
|
+
...param,
|
|
911
|
+
description: jsdocParam.description || param.description,
|
|
912
|
+
type: param.type || jsdocParam.type,
|
|
913
|
+
optional: param.optional || jsdocParam.optional,
|
|
914
|
+
defaultValue: param.defaultValue || jsdocParam.defaultValue
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
return param;
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
function parseEnumMembers(body) {
|
|
921
|
+
const members = [];
|
|
922
|
+
const pattern = /(\w+)\s*(?:=\s*([^,}]+))?/g;
|
|
923
|
+
let match;
|
|
924
|
+
while ((match = pattern.exec(body)) !== null) {
|
|
925
|
+
const [, name, value] = match;
|
|
926
|
+
if (!name) continue;
|
|
927
|
+
let parsedValue;
|
|
928
|
+
if (value !== void 0) {
|
|
929
|
+
const trimmed = value.trim();
|
|
930
|
+
const num = Number(trimmed);
|
|
931
|
+
if (!isNaN(num)) {
|
|
932
|
+
parsedValue = num;
|
|
933
|
+
} else {
|
|
934
|
+
parsedValue = trimmed.replace(/^['"]|['"]$/g, "");
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
members.push({ name, value: parsedValue });
|
|
938
|
+
}
|
|
939
|
+
return members;
|
|
940
|
+
}
|
|
941
|
+
function parseDts(content, filePath) {
|
|
942
|
+
const exports$1 = [];
|
|
943
|
+
const imports = [];
|
|
944
|
+
let moduleName;
|
|
945
|
+
const comments = extractJSDocComments(content);
|
|
946
|
+
const moduleMatch = PATTERNS.declareModule.exec(content);
|
|
947
|
+
if (moduleMatch) {
|
|
948
|
+
moduleName = moduleMatch[1];
|
|
949
|
+
}
|
|
950
|
+
let match;
|
|
951
|
+
PATTERNS.exportFunction.lastIndex = 0;
|
|
952
|
+
while ((match = PATTERNS.exportFunction.exec(content)) !== null) {
|
|
953
|
+
const [fullMatch, name, generics, params, returnType] = match;
|
|
954
|
+
if (!name || !returnType) continue;
|
|
955
|
+
const jsdoc = findPrecedingJSDoc(content, match.index, comments);
|
|
956
|
+
exports$1.push({
|
|
957
|
+
kind: "function",
|
|
958
|
+
name,
|
|
959
|
+
signature: `function ${name}${generics || ""}(${params || ""}): ${returnType.trim()}`,
|
|
960
|
+
description: jsdoc?.description,
|
|
961
|
+
params: mergeParamDocs(parseParameters(params || ""), jsdoc?.params),
|
|
962
|
+
returns: {
|
|
963
|
+
type: returnType.trim(),
|
|
964
|
+
description: jsdoc?.returns?.description
|
|
965
|
+
},
|
|
966
|
+
examples: jsdoc?.examples,
|
|
967
|
+
deprecated: jsdoc?.deprecated ? jsdoc.deprecated : void 0,
|
|
968
|
+
since: jsdoc?.since,
|
|
969
|
+
see: jsdoc?.see,
|
|
970
|
+
sourceFile: filePath
|
|
971
|
+
});
|
|
972
|
+
}
|
|
973
|
+
PATTERNS.exportConst.lastIndex = 0;
|
|
974
|
+
while ((match = PATTERNS.exportConst.exec(content)) !== null) {
|
|
975
|
+
const [, name, type] = match;
|
|
976
|
+
if (!name || !type) continue;
|
|
977
|
+
const jsdoc = findPrecedingJSDoc(content, match.index, comments);
|
|
978
|
+
exports$1.push({
|
|
979
|
+
kind: "constant",
|
|
980
|
+
name,
|
|
981
|
+
signature: `const ${name}: ${type.trim()}`,
|
|
982
|
+
description: jsdoc?.description,
|
|
983
|
+
deprecated: jsdoc?.deprecated ? jsdoc.deprecated : void 0,
|
|
984
|
+
sourceFile: filePath
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
PATTERNS.exportClass.lastIndex = 0;
|
|
988
|
+
while ((match = PATTERNS.exportClass.exec(content)) !== null) {
|
|
989
|
+
const [, name, generics, extendsClause, implementsClause] = match;
|
|
990
|
+
if (!name) continue;
|
|
991
|
+
const jsdoc = findPrecedingJSDoc(content, match.index, comments);
|
|
992
|
+
const bodyStart = match.index + match[0].length - 1;
|
|
993
|
+
const body = extractBody(content, bodyStart);
|
|
994
|
+
const { methods, properties } = parseClassMembers(body, comments, bodyStart);
|
|
995
|
+
exports$1.push({
|
|
996
|
+
kind: "class",
|
|
997
|
+
name,
|
|
998
|
+
signature: `class ${name}${generics ? `<${generics}>` : ""}`,
|
|
999
|
+
description: jsdoc?.description,
|
|
1000
|
+
extends: extendsClause ? [extendsClause.trim()] : void 0,
|
|
1001
|
+
implements: implementsClause ? implementsClause.split(",").map((s) => s.trim()) : void 0,
|
|
1002
|
+
methods,
|
|
1003
|
+
properties,
|
|
1004
|
+
typeParams: generics ? generics.split(",").map((s) => s.trim()) : void 0,
|
|
1005
|
+
deprecated: jsdoc?.deprecated ? jsdoc.deprecated : void 0,
|
|
1006
|
+
sourceFile: filePath
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
PATTERNS.exportInterface.lastIndex = 0;
|
|
1010
|
+
while ((match = PATTERNS.exportInterface.exec(content)) !== null) {
|
|
1011
|
+
const [, name, generics, extendsClause] = match;
|
|
1012
|
+
if (!name) continue;
|
|
1013
|
+
const jsdoc = findPrecedingJSDoc(content, match.index, comments);
|
|
1014
|
+
const bodyStart = match.index + match[0].length - 1;
|
|
1015
|
+
const body = extractBody(content, bodyStart);
|
|
1016
|
+
const { methods, properties } = parseClassMembers(body, comments, bodyStart);
|
|
1017
|
+
exports$1.push({
|
|
1018
|
+
kind: "interface",
|
|
1019
|
+
name,
|
|
1020
|
+
signature: `interface ${name}${generics ? `<${generics}>` : ""}`,
|
|
1021
|
+
description: jsdoc?.description,
|
|
1022
|
+
extends: extendsClause ? extendsClause.split(",").map((s) => s.trim()) : void 0,
|
|
1023
|
+
methods,
|
|
1024
|
+
properties,
|
|
1025
|
+
typeParams: generics ? generics.split(",").map((s) => s.trim()) : void 0,
|
|
1026
|
+
deprecated: jsdoc?.deprecated ? jsdoc.deprecated : void 0,
|
|
1027
|
+
sourceFile: filePath
|
|
1028
|
+
});
|
|
1029
|
+
}
|
|
1030
|
+
PATTERNS.exportType.lastIndex = 0;
|
|
1031
|
+
while ((match = PATTERNS.exportType.exec(content)) !== null) {
|
|
1032
|
+
const [, name, generics, definition] = match;
|
|
1033
|
+
if (!name || !definition) continue;
|
|
1034
|
+
const jsdoc = findPrecedingJSDoc(content, match.index, comments);
|
|
1035
|
+
exports$1.push({
|
|
1036
|
+
kind: "type",
|
|
1037
|
+
name,
|
|
1038
|
+
signature: `type ${name}${generics ? `<${generics}>` : ""} = ${definition.trim()}`,
|
|
1039
|
+
description: jsdoc?.description,
|
|
1040
|
+
typeParams: generics ? generics.split(",").map((s) => s.trim()) : void 0,
|
|
1041
|
+
deprecated: jsdoc?.deprecated ? jsdoc.deprecated : void 0,
|
|
1042
|
+
sourceFile: filePath
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
PATTERNS.exportEnum.lastIndex = 0;
|
|
1046
|
+
while ((match = PATTERNS.exportEnum.exec(content)) !== null) {
|
|
1047
|
+
const [, name] = match;
|
|
1048
|
+
if (!name) continue;
|
|
1049
|
+
const jsdoc = findPrecedingJSDoc(content, match.index, comments);
|
|
1050
|
+
const bodyStart = match.index + match[0].length - 1;
|
|
1051
|
+
const body = extractBody(content, bodyStart);
|
|
1052
|
+
const members = parseEnumMembers(body);
|
|
1053
|
+
exports$1.push({
|
|
1054
|
+
kind: "enum",
|
|
1055
|
+
name,
|
|
1056
|
+
signature: `enum ${name}`,
|
|
1057
|
+
description: jsdoc?.description,
|
|
1058
|
+
members,
|
|
1059
|
+
deprecated: jsdoc?.deprecated ? jsdoc.deprecated : void 0,
|
|
1060
|
+
sourceFile: filePath
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
PATTERNS.importStatement.lastIndex = 0;
|
|
1064
|
+
while ((match = PATTERNS.importStatement.exec(content)) !== null) {
|
|
1065
|
+
if (match[1]) {
|
|
1066
|
+
imports.push(match[1]);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
PATTERNS.declareModule.lastIndex = 0;
|
|
1070
|
+
while ((match = PATTERNS.declareModule.exec(content)) !== null) {
|
|
1071
|
+
const moduleBodyStart = match.index + match[0].length - 1;
|
|
1072
|
+
const moduleBody = extractBody(content, moduleBodyStart);
|
|
1073
|
+
const moduleExports = parseModuleAugmentation(moduleBody, comments, moduleBodyStart, filePath);
|
|
1074
|
+
for (const entry of moduleExports) {
|
|
1075
|
+
exports$1.push(entry);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
return { exports: exports$1, imports, moduleName };
|
|
1079
|
+
}
|
|
1080
|
+
function parseModuleAugmentation(body, _comments, _bodyOffset, filePath) {
|
|
1081
|
+
const entries = [];
|
|
1082
|
+
const moduleComments = extractJSDocComments(body);
|
|
1083
|
+
const interfacePattern = /interface\s+(\w+)(?:\s*<([^>]*)>)?(?:\s+extends\s+([^{]+))?\s*\{/g;
|
|
1084
|
+
let match;
|
|
1085
|
+
while ((match = interfacePattern.exec(body)) !== null) {
|
|
1086
|
+
const [, interfaceName, generics, extendsClause] = match;
|
|
1087
|
+
if (!interfaceName) continue;
|
|
1088
|
+
const ifaceBodyStart = match.index + match[0].length - 1;
|
|
1089
|
+
const ifaceBody = extractBody(body, ifaceBodyStart);
|
|
1090
|
+
if (interfaceName === "LoDashStatic" || interfaceName.endsWith("Static")) {
|
|
1091
|
+
const methods = parseInterfaceMethodsAsExports(
|
|
1092
|
+
ifaceBody,
|
|
1093
|
+
moduleComments,
|
|
1094
|
+
ifaceBodyStart,
|
|
1095
|
+
filePath
|
|
1096
|
+
);
|
|
1097
|
+
for (const method of methods) {
|
|
1098
|
+
entries.push(method);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
return entries;
|
|
1103
|
+
}
|
|
1104
|
+
function parseInterfaceMethodsAsExports(body, _comments, _bodyOffset, filePath) {
|
|
1105
|
+
const methods = [];
|
|
1106
|
+
const bodyComments = extractJSDocComments(body);
|
|
1107
|
+
const methodPattern = /(\w+)\s*(<[^>]*>)?\s*\(([^)]*)\)\s*:\s*([^;]+);/g;
|
|
1108
|
+
let match;
|
|
1109
|
+
while ((match = methodPattern.exec(body)) !== null) {
|
|
1110
|
+
const [fullMatch, name, generics, params, returnType] = match;
|
|
1111
|
+
if (!name || !returnType) continue;
|
|
1112
|
+
if (["constructor", "toString", "valueOf", "toJSON"].includes(name)) continue;
|
|
1113
|
+
const jsdoc = findPrecedingJSDoc(body, match.index, bodyComments);
|
|
1114
|
+
methods.push({
|
|
1115
|
+
kind: "function",
|
|
1116
|
+
name,
|
|
1117
|
+
signature: `function ${name}${generics || ""}(${params || ""}): ${returnType.trim()}`,
|
|
1118
|
+
description: jsdoc?.description,
|
|
1119
|
+
params: mergeParamDocs(parseParameters(params || ""), jsdoc?.params),
|
|
1120
|
+
returns: {
|
|
1121
|
+
type: returnType.trim(),
|
|
1122
|
+
description: jsdoc?.returns?.description
|
|
1123
|
+
},
|
|
1124
|
+
examples: jsdoc?.examples,
|
|
1125
|
+
deprecated: jsdoc?.deprecated ? jsdoc.deprecated : void 0,
|
|
1126
|
+
since: jsdoc?.since,
|
|
1127
|
+
see: jsdoc?.see,
|
|
1128
|
+
sourceFile: filePath
|
|
1129
|
+
});
|
|
1130
|
+
}
|
|
1131
|
+
return methods;
|
|
1132
|
+
}
|
|
1133
|
+
function sortExports(exports$1) {
|
|
1134
|
+
const kindOrder = {
|
|
1135
|
+
function: 1,
|
|
1136
|
+
class: 2,
|
|
1137
|
+
interface: 3,
|
|
1138
|
+
type: 4,
|
|
1139
|
+
enum: 5,
|
|
1140
|
+
constant: 6
|
|
1141
|
+
};
|
|
1142
|
+
return [...exports$1].sort((a, b) => {
|
|
1143
|
+
const kindDiff = kindOrder[a.kind] - kindOrder[b.kind];
|
|
1144
|
+
if (kindDiff !== 0) return kindDiff;
|
|
1145
|
+
return a.name.localeCompare(b.name);
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
1148
|
+
function findMainDtsFile(files, typesField) {
|
|
1149
|
+
if (typesField) {
|
|
1150
|
+
const normalized = typesField.replace(/^\.\//, "");
|
|
1151
|
+
if (files.has(normalized)) return normalized;
|
|
1152
|
+
}
|
|
1153
|
+
const priorities = [
|
|
1154
|
+
"index.d.ts",
|
|
1155
|
+
"dist/index.d.ts",
|
|
1156
|
+
"lib/index.d.ts",
|
|
1157
|
+
"types/index.d.ts",
|
|
1158
|
+
"src/index.d.ts",
|
|
1159
|
+
// @types packages merged via types-resolver plugin
|
|
1160
|
+
"__types__/index.d.ts",
|
|
1161
|
+
"__types__/dist/index.d.ts",
|
|
1162
|
+
"__types__/lib/index.d.ts",
|
|
1163
|
+
"__types__/types/index.d.ts"
|
|
1164
|
+
];
|
|
1165
|
+
for (const path of priorities) {
|
|
1166
|
+
if (files.has(path)) return path;
|
|
1167
|
+
}
|
|
1168
|
+
for (const path of files.keys()) {
|
|
1169
|
+
if (path.endsWith(".d.ts") && !path.startsWith("__types__/")) {
|
|
1170
|
+
return path;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
for (const path of files.keys()) {
|
|
1174
|
+
if (path.endsWith(".d.ts") && path.startsWith("__types__/")) {
|
|
1175
|
+
return path;
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
return void 0;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
// src/plugins/core/dts-parser.ts
|
|
1182
|
+
var dtsParserPlugin = {
|
|
1183
|
+
name: "dts-parser",
|
|
1184
|
+
version: "1.0.0",
|
|
1185
|
+
category: "parser",
|
|
1186
|
+
install(kernel) {
|
|
1187
|
+
kernel.on("parse:start", async (context) => {
|
|
1188
|
+
const { files } = context.package;
|
|
1189
|
+
const mainDts = findMainDtsFile(files, context.package.types);
|
|
1190
|
+
if (!mainDts) {
|
|
1191
|
+
return;
|
|
1192
|
+
}
|
|
1193
|
+
const dtsFiles = Array.from(files.entries()).filter(([path]) => path.endsWith(".d.ts")).sort(([a], [b]) => {
|
|
1194
|
+
if (a === mainDts) return -1;
|
|
1195
|
+
if (b === mainDts) return 1;
|
|
1196
|
+
if (a.includes("index.d.ts")) return -1;
|
|
1197
|
+
if (b.includes("index.d.ts")) return 1;
|
|
1198
|
+
return a.localeCompare(b);
|
|
1199
|
+
});
|
|
1200
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
1201
|
+
for (const [path, content] of dtsFiles) {
|
|
1202
|
+
try {
|
|
1203
|
+
const result = parseDts(content, path);
|
|
1204
|
+
for (const entry of result.exports) {
|
|
1205
|
+
if (!seenNames.has(entry.name)) {
|
|
1206
|
+
seenNames.add(entry.name);
|
|
1207
|
+
context.api.push(entry);
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
} catch (error) {
|
|
1211
|
+
context.errors.push(error instanceof Error ? error : new Error(String(error)));
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
context.api = sortExports(context.api);
|
|
1215
|
+
});
|
|
1216
|
+
}
|
|
1217
|
+
};
|
|
1218
|
+
|
|
1219
|
+
// src/parsers/typescript.ts
|
|
1220
|
+
var PATTERNS2 = {
|
|
1221
|
+
/** Export function */
|
|
1222
|
+
exportFunction: /export\s+(?:async\s+)?function\s+(\w+)\s*(<[^>]*>)?\s*\(([^)]*)\)(?:\s*:\s*([^{]+))?\s*\{/g,
|
|
1223
|
+
/** Export arrow function */
|
|
1224
|
+
exportArrow: /export\s+const\s+(\w+)\s*(?::\s*([^=]+))?\s*=\s*(?:async\s*)?\([^)]*\)\s*(?::\s*([^=]+))?\s*=>/g,
|
|
1225
|
+
/** Export class */
|
|
1226
|
+
exportClass: /export\s+(?:abstract\s+)?class\s+(\w+)(?:\s*<([^>]*)>)?(?:\s+extends\s+([^\s{]+))?(?:\s+implements\s+([^{]+))?\s*\{/g,
|
|
1227
|
+
/** Export interface */
|
|
1228
|
+
exportInterface: /export\s+interface\s+(\w+)(?:\s*<([^>]*)>)?(?:\s+extends\s+([^{]+))?\s*\{/g,
|
|
1229
|
+
/** Export type */
|
|
1230
|
+
exportType: /export\s+type\s+(\w+)(?:\s*<([^>]*)>)?\s*=\s*([^;]+)/g,
|
|
1231
|
+
/** Export const/let */
|
|
1232
|
+
exportConst: /export\s+(?:const|let)\s+(\w+)(?:\s*:\s*([^=]+))?\s*=/g,
|
|
1233
|
+
/** Export enum */
|
|
1234
|
+
exportEnum: /export\s+(?:const\s+)?enum\s+(\w+)\s*\{/g};
|
|
1235
|
+
function parseSourceParams(paramStr) {
|
|
1236
|
+
if (!paramStr.trim()) return [];
|
|
1237
|
+
const params = [];
|
|
1238
|
+
const parts = splitParams(paramStr);
|
|
1239
|
+
for (const part of parts) {
|
|
1240
|
+
const trimmed = part.trim();
|
|
1241
|
+
if (!trimmed) continue;
|
|
1242
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
1243
|
+
const colonIndex = findBalancedColon(trimmed);
|
|
1244
|
+
if (colonIndex > 0) {
|
|
1245
|
+
params.push({
|
|
1246
|
+
name: "options",
|
|
1247
|
+
type: trimmed.slice(colonIndex + 1).trim(),
|
|
1248
|
+
optional: trimmed.includes("?")
|
|
1249
|
+
});
|
|
1250
|
+
} else {
|
|
1251
|
+
params.push({
|
|
1252
|
+
name: "options",
|
|
1253
|
+
type: "object"
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
continue;
|
|
1257
|
+
}
|
|
1258
|
+
const match = trimmed.match(/^(\w+)(\?)?\s*(?::\s*(.+?))?(?:\s*=\s*(.+))?$/);
|
|
1259
|
+
if (match && match[1]) {
|
|
1260
|
+
const [, name, optional, type, defaultValue] = match;
|
|
1261
|
+
params.push({
|
|
1262
|
+
name,
|
|
1263
|
+
type: type?.trim() || inferTypeFromDefault(defaultValue),
|
|
1264
|
+
optional: !!optional || !!defaultValue,
|
|
1265
|
+
defaultValue: defaultValue?.trim()
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
return params;
|
|
1270
|
+
}
|
|
1271
|
+
function splitParams(str) {
|
|
1272
|
+
const parts = [];
|
|
1273
|
+
let current = "";
|
|
1274
|
+
let depth = 0;
|
|
1275
|
+
for (const char of str) {
|
|
1276
|
+
if (char === "<" || char === "{" || char === "[" || char === "(") {
|
|
1277
|
+
depth++;
|
|
1278
|
+
current += char;
|
|
1279
|
+
} else if (char === ">" || char === "}" || char === "]" || char === ")") {
|
|
1280
|
+
depth--;
|
|
1281
|
+
current += char;
|
|
1282
|
+
} else if (char === "," && depth === 0) {
|
|
1283
|
+
parts.push(current);
|
|
1284
|
+
current = "";
|
|
1285
|
+
} else {
|
|
1286
|
+
current += char;
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
if (current.trim()) parts.push(current);
|
|
1290
|
+
return parts;
|
|
1291
|
+
}
|
|
1292
|
+
function findBalancedColon(str) {
|
|
1293
|
+
let depth = 0;
|
|
1294
|
+
for (let i = 0; i < str.length; i++) {
|
|
1295
|
+
const char = str[i];
|
|
1296
|
+
if (char === "{" || char === "[" || char === "<" || char === "(") {
|
|
1297
|
+
depth++;
|
|
1298
|
+
} else if (char === "}" || char === "]" || char === ">" || char === ")") {
|
|
1299
|
+
depth--;
|
|
1300
|
+
} else if (char === ":" && depth === 0) {
|
|
1301
|
+
return i;
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
return -1;
|
|
1305
|
+
}
|
|
1306
|
+
function inferTypeFromDefault(defaultValue) {
|
|
1307
|
+
if (!defaultValue) return "unknown";
|
|
1308
|
+
const trimmed = defaultValue.trim();
|
|
1309
|
+
if (trimmed === "true" || trimmed === "false") return "boolean";
|
|
1310
|
+
if (trimmed.startsWith("'") || trimmed.startsWith('"') || trimmed.startsWith("`")) {
|
|
1311
|
+
return "string";
|
|
1312
|
+
}
|
|
1313
|
+
if (/^-?\d+(\.\d+)?$/.test(trimmed)) return "number";
|
|
1314
|
+
if (trimmed === "null") return "null";
|
|
1315
|
+
if (trimmed === "undefined") return "undefined";
|
|
1316
|
+
if (trimmed.startsWith("[")) return "unknown[]";
|
|
1317
|
+
if (trimmed.startsWith("{")) return "object";
|
|
1318
|
+
if (trimmed.startsWith("()") || trimmed.includes("=>")) return "Function";
|
|
1319
|
+
return "unknown";
|
|
1320
|
+
}
|
|
1321
|
+
function parseTypeScript(content, filePath) {
|
|
1322
|
+
const exports$1 = [];
|
|
1323
|
+
let hasTypes = false;
|
|
1324
|
+
hasTypes = /:\s*\w+/.test(content) || content.includes("interface ") || content.includes("type ");
|
|
1325
|
+
const comments = extractJSDocComments(content);
|
|
1326
|
+
let match;
|
|
1327
|
+
PATTERNS2.exportFunction.lastIndex = 0;
|
|
1328
|
+
while ((match = PATTERNS2.exportFunction.exec(content)) !== null) {
|
|
1329
|
+
const [, name, generics, params, returnType] = match;
|
|
1330
|
+
if (!name) continue;
|
|
1331
|
+
const jsdoc = findPrecedingJSDoc(content, match.index, comments);
|
|
1332
|
+
const paramStr = params || "";
|
|
1333
|
+
exports$1.push({
|
|
1334
|
+
kind: "function",
|
|
1335
|
+
name,
|
|
1336
|
+
signature: `function ${name}${generics || ""}(${paramStr})${returnType ? `: ${returnType.trim()}` : ""}`,
|
|
1337
|
+
description: jsdoc?.description,
|
|
1338
|
+
params: mergeParams(parseSourceParams(paramStr), jsdoc?.params),
|
|
1339
|
+
returns: returnType ? { type: returnType.trim(), description: jsdoc?.returns?.description } : void 0,
|
|
1340
|
+
examples: jsdoc?.examples,
|
|
1341
|
+
deprecated: jsdoc?.deprecated ? jsdoc.deprecated : void 0,
|
|
1342
|
+
sourceFile: filePath
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
PATTERNS2.exportArrow.lastIndex = 0;
|
|
1346
|
+
while ((match = PATTERNS2.exportArrow.exec(content)) !== null) {
|
|
1347
|
+
const [fullMatch, name, typeAnnotation, returnType] = match;
|
|
1348
|
+
if (!name || !fullMatch) continue;
|
|
1349
|
+
const jsdoc = findPrecedingJSDoc(content, match.index, comments);
|
|
1350
|
+
const paramsStart = fullMatch.indexOf("(");
|
|
1351
|
+
const paramsEnd = fullMatch.indexOf(")", paramsStart);
|
|
1352
|
+
const params = fullMatch.slice(paramsStart + 1, paramsEnd);
|
|
1353
|
+
exports$1.push({
|
|
1354
|
+
kind: "function",
|
|
1355
|
+
name,
|
|
1356
|
+
signature: typeAnnotation ? `const ${name}: ${typeAnnotation.trim()}` : `const ${name} = (${params})${returnType ? ` => ${returnType.trim()}` : ""}`,
|
|
1357
|
+
description: jsdoc?.description,
|
|
1358
|
+
params: mergeParams(parseSourceParams(params), jsdoc?.params),
|
|
1359
|
+
returns: returnType ? { type: returnType.trim(), description: jsdoc?.returns?.description } : void 0,
|
|
1360
|
+
examples: jsdoc?.examples,
|
|
1361
|
+
deprecated: jsdoc?.deprecated ? jsdoc.deprecated : void 0,
|
|
1362
|
+
sourceFile: filePath
|
|
1363
|
+
});
|
|
1364
|
+
}
|
|
1365
|
+
PATTERNS2.exportClass.lastIndex = 0;
|
|
1366
|
+
while ((match = PATTERNS2.exportClass.exec(content)) !== null) {
|
|
1367
|
+
const [, name, generics, extendsClause, implementsClause] = match;
|
|
1368
|
+
if (!name) continue;
|
|
1369
|
+
const jsdoc = findPrecedingJSDoc(content, match.index, comments);
|
|
1370
|
+
exports$1.push({
|
|
1371
|
+
kind: "class",
|
|
1372
|
+
name,
|
|
1373
|
+
signature: `class ${name}${generics ? `<${generics}>` : ""}`,
|
|
1374
|
+
description: jsdoc?.description,
|
|
1375
|
+
extends: extendsClause ? [extendsClause.trim()] : void 0,
|
|
1376
|
+
implements: implementsClause ? implementsClause.split(",").map((s) => s.trim()) : void 0,
|
|
1377
|
+
deprecated: jsdoc?.deprecated ? jsdoc.deprecated : void 0,
|
|
1378
|
+
sourceFile: filePath
|
|
1379
|
+
});
|
|
1380
|
+
}
|
|
1381
|
+
PATTERNS2.exportInterface.lastIndex = 0;
|
|
1382
|
+
while ((match = PATTERNS2.exportInterface.exec(content)) !== null) {
|
|
1383
|
+
const [, name, generics, extendsClause] = match;
|
|
1384
|
+
if (!name) continue;
|
|
1385
|
+
const jsdoc = findPrecedingJSDoc(content, match.index, comments);
|
|
1386
|
+
exports$1.push({
|
|
1387
|
+
kind: "interface",
|
|
1388
|
+
name,
|
|
1389
|
+
signature: `interface ${name}${generics ? `<${generics}>` : ""}`,
|
|
1390
|
+
description: jsdoc?.description,
|
|
1391
|
+
extends: extendsClause ? extendsClause.split(",").map((s) => s.trim()) : void 0,
|
|
1392
|
+
deprecated: jsdoc?.deprecated ? jsdoc.deprecated : void 0,
|
|
1393
|
+
sourceFile: filePath
|
|
1394
|
+
});
|
|
1395
|
+
}
|
|
1396
|
+
PATTERNS2.exportType.lastIndex = 0;
|
|
1397
|
+
while ((match = PATTERNS2.exportType.exec(content)) !== null) {
|
|
1398
|
+
const [, name, generics, definition] = match;
|
|
1399
|
+
if (!name || !definition) continue;
|
|
1400
|
+
const jsdoc = findPrecedingJSDoc(content, match.index, comments);
|
|
1401
|
+
exports$1.push({
|
|
1402
|
+
kind: "type",
|
|
1403
|
+
name,
|
|
1404
|
+
signature: `type ${name}${generics ? `<${generics}>` : ""} = ${definition.trim()}`,
|
|
1405
|
+
description: jsdoc?.description,
|
|
1406
|
+
deprecated: jsdoc?.deprecated ? jsdoc.deprecated : void 0,
|
|
1407
|
+
sourceFile: filePath
|
|
1408
|
+
});
|
|
1409
|
+
}
|
|
1410
|
+
PATTERNS2.exportConst.lastIndex = 0;
|
|
1411
|
+
while ((match = PATTERNS2.exportConst.exec(content)) !== null) {
|
|
1412
|
+
const [, name, type] = match;
|
|
1413
|
+
if (!name) continue;
|
|
1414
|
+
if (content.slice(match.index, match.index + 200).includes("=>")) continue;
|
|
1415
|
+
const jsdoc = findPrecedingJSDoc(content, match.index, comments);
|
|
1416
|
+
exports$1.push({
|
|
1417
|
+
kind: "constant",
|
|
1418
|
+
name,
|
|
1419
|
+
signature: `const ${name}${type ? `: ${type.trim()}` : ""}`,
|
|
1420
|
+
description: jsdoc?.description,
|
|
1421
|
+
deprecated: jsdoc?.deprecated ? jsdoc.deprecated : void 0,
|
|
1422
|
+
sourceFile: filePath
|
|
1423
|
+
});
|
|
1424
|
+
}
|
|
1425
|
+
PATTERNS2.exportEnum.lastIndex = 0;
|
|
1426
|
+
while ((match = PATTERNS2.exportEnum.exec(content)) !== null) {
|
|
1427
|
+
const [, name] = match;
|
|
1428
|
+
if (!name) continue;
|
|
1429
|
+
const jsdoc = findPrecedingJSDoc(content, match.index, comments);
|
|
1430
|
+
exports$1.push({
|
|
1431
|
+
kind: "enum",
|
|
1432
|
+
name,
|
|
1433
|
+
signature: `enum ${name}`,
|
|
1434
|
+
description: jsdoc?.description,
|
|
1435
|
+
deprecated: jsdoc?.deprecated ? jsdoc.deprecated : void 0,
|
|
1436
|
+
sourceFile: filePath
|
|
1437
|
+
});
|
|
1438
|
+
}
|
|
1439
|
+
return { exports: exports$1, hasTypes };
|
|
1440
|
+
}
|
|
1441
|
+
function mergeParams(sourceParams, jsdocParams) {
|
|
1442
|
+
if (!jsdocParams || jsdocParams.length === 0) return sourceParams;
|
|
1443
|
+
return sourceParams.map((param) => {
|
|
1444
|
+
const jsdocParam = jsdocParams.find((p) => p.name === param.name);
|
|
1445
|
+
if (jsdocParam) {
|
|
1446
|
+
return {
|
|
1447
|
+
...param,
|
|
1448
|
+
description: jsdocParam.description || param.description,
|
|
1449
|
+
type: param.type !== "unknown" ? param.type : jsdocParam.type
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
return param;
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
function findTypeScriptFiles(files, mainField) {
|
|
1456
|
+
const tsFiles = [];
|
|
1457
|
+
const priorities = [];
|
|
1458
|
+
if (mainField) {
|
|
1459
|
+
const base = mainField.replace(/^\.\//, "").replace(/\.[jt]sx?$/, "");
|
|
1460
|
+
priorities.push(
|
|
1461
|
+
`${base}.ts`,
|
|
1462
|
+
`${base}.tsx`,
|
|
1463
|
+
`src/${base}.ts`,
|
|
1464
|
+
`src/${base}.tsx`
|
|
1465
|
+
);
|
|
1466
|
+
}
|
|
1467
|
+
priorities.push(
|
|
1468
|
+
"src/index.ts",
|
|
1469
|
+
"src/index.tsx",
|
|
1470
|
+
"index.ts",
|
|
1471
|
+
"index.tsx",
|
|
1472
|
+
"lib/index.ts",
|
|
1473
|
+
"src/main.ts"
|
|
1474
|
+
);
|
|
1475
|
+
for (const path of priorities) {
|
|
1476
|
+
if (files.has(path) && !tsFiles.includes(path)) {
|
|
1477
|
+
tsFiles.push(path);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
for (const path of files.keys()) {
|
|
1481
|
+
if ((path.endsWith(".ts") || path.endsWith(".tsx")) && !path.endsWith(".d.ts")) {
|
|
1482
|
+
if (!tsFiles.includes(path)) {
|
|
1483
|
+
tsFiles.push(path);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
return tsFiles;
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
// src/plugins/core/ts-source-parser.ts
|
|
1491
|
+
var tsSourceParserPlugin = {
|
|
1492
|
+
name: "ts-source-parser",
|
|
1493
|
+
version: "1.0.0",
|
|
1494
|
+
category: "parser",
|
|
1495
|
+
install(kernel) {
|
|
1496
|
+
kernel.on("parse:start", async (context) => {
|
|
1497
|
+
if (context.api.length > 0) {
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
const { files } = context.package;
|
|
1501
|
+
const tsFiles = findTypeScriptFiles(files, context.package.main);
|
|
1502
|
+
if (tsFiles.length === 0) {
|
|
1503
|
+
return;
|
|
1504
|
+
}
|
|
1505
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
1506
|
+
for (const path of tsFiles) {
|
|
1507
|
+
const content = files.get(path);
|
|
1508
|
+
if (!content) continue;
|
|
1509
|
+
try {
|
|
1510
|
+
const result = parseTypeScript(content, path);
|
|
1511
|
+
for (const entry of result.exports) {
|
|
1512
|
+
if (!seenNames.has(entry.name)) {
|
|
1513
|
+
seenNames.add(entry.name);
|
|
1514
|
+
context.api.push(entry);
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
} catch (error) {
|
|
1518
|
+
context.errors.push(error instanceof Error ? error : new Error(String(error)));
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
context.api = sortExports(context.api);
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
};
|
|
1525
|
+
|
|
1526
|
+
// src/parsers/readme.ts
|
|
1527
|
+
var SECTION_PATTERNS = {
|
|
1528
|
+
installation: /^#+\s*(installation|install|setup|getting started)/i,
|
|
1529
|
+
quickStart: /^#+\s*(quick\s*start|usage|getting\s*started|basic\s*usage)/i,
|
|
1530
|
+
api: /^#+\s*(api|api\s*reference|reference|methods|functions)/i,
|
|
1531
|
+
examples: /^#+\s*(examples?|code\s*examples?)/i};
|
|
1532
|
+
var BADGE_PATTERN = /\[!\[([^\]]*)\]\(([^)]+)\)\]\([^)]+\)|\!\[([^\]]*)\]\(([^)]+)\)/g;
|
|
1533
|
+
var CODE_BLOCK_PATTERN = /```(\w+)?\s*\n([\s\S]*?)```/g;
|
|
1534
|
+
function parseReadme(content) {
|
|
1535
|
+
const sections = extractSections(content);
|
|
1536
|
+
const badges = extractBadges(content);
|
|
1537
|
+
const { title, description } = extractTitleAndDescription(content, sections);
|
|
1538
|
+
const installation = findSection(sections, SECTION_PATTERNS.installation);
|
|
1539
|
+
const quickStart = findSection(sections, SECTION_PATTERNS.quickStart);
|
|
1540
|
+
const api = findSection(sections, SECTION_PATTERNS.api);
|
|
1541
|
+
const examples = findExampleSections(sections);
|
|
1542
|
+
return {
|
|
1543
|
+
title,
|
|
1544
|
+
description,
|
|
1545
|
+
badges,
|
|
1546
|
+
installation: installation?.content,
|
|
1547
|
+
quickStart: quickStart?.content || findQuickStartFromCode(content),
|
|
1548
|
+
examples,
|
|
1549
|
+
api: api?.content,
|
|
1550
|
+
sections
|
|
1551
|
+
};
|
|
1552
|
+
}
|
|
1553
|
+
function extractSections(content) {
|
|
1554
|
+
const sections = [];
|
|
1555
|
+
const lines = content.split("\n");
|
|
1556
|
+
let currentSection = null;
|
|
1557
|
+
let currentContent = [];
|
|
1558
|
+
let charIndex = 0;
|
|
1559
|
+
function finishSection() {
|
|
1560
|
+
if (currentSection) {
|
|
1561
|
+
currentSection.content = currentContent.join("\n").trim();
|
|
1562
|
+
currentSection.endIndex = charIndex;
|
|
1563
|
+
sections.push(currentSection);
|
|
1564
|
+
currentContent = [];
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
for (const line of lines) {
|
|
1568
|
+
const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
|
|
1569
|
+
if (headingMatch && headingMatch[1] && headingMatch[2]) {
|
|
1570
|
+
finishSection();
|
|
1571
|
+
const level = headingMatch[1].length;
|
|
1572
|
+
const title = headingMatch[2].trim();
|
|
1573
|
+
currentSection = {
|
|
1574
|
+
title,
|
|
1575
|
+
content: "",
|
|
1576
|
+
level,
|
|
1577
|
+
startIndex: charIndex,
|
|
1578
|
+
endIndex: 0
|
|
1579
|
+
};
|
|
1580
|
+
currentContent = [];
|
|
1581
|
+
} else if (currentSection) {
|
|
1582
|
+
currentContent.push(line);
|
|
1583
|
+
}
|
|
1584
|
+
charIndex += line.length + 1;
|
|
1585
|
+
}
|
|
1586
|
+
finishSection();
|
|
1587
|
+
return sections;
|
|
1588
|
+
}
|
|
1589
|
+
function findSection(sections, pattern) {
|
|
1590
|
+
return sections.find((s) => pattern.test(`# ${s.title}`));
|
|
1591
|
+
}
|
|
1592
|
+
function findExampleSections(sections) {
|
|
1593
|
+
const examples = [];
|
|
1594
|
+
for (const section of sections) {
|
|
1595
|
+
if (SECTION_PATTERNS.examples.test(`# ${section.title}`)) {
|
|
1596
|
+
const codeBlocks = extractCodeBlocks(section.content);
|
|
1597
|
+
examples.push(...codeBlocks);
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
return examples;
|
|
1601
|
+
}
|
|
1602
|
+
function extractCodeBlocks(content) {
|
|
1603
|
+
const blocks = [];
|
|
1604
|
+
const pattern = new RegExp(CODE_BLOCK_PATTERN.source, "g");
|
|
1605
|
+
let match;
|
|
1606
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
1607
|
+
const [, language, code] = match;
|
|
1608
|
+
if (code && code.trim()) {
|
|
1609
|
+
blocks.push(language ? `\`\`\`${language}
|
|
1610
|
+
${code.trim()}
|
|
1611
|
+
\`\`\`` : code.trim());
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
return blocks;
|
|
1615
|
+
}
|
|
1616
|
+
function extractBadges(content) {
|
|
1617
|
+
const badges = [];
|
|
1618
|
+
const pattern = new RegExp(BADGE_PATTERN.source, "g");
|
|
1619
|
+
let match;
|
|
1620
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
1621
|
+
const altText = match[1] || match[3] || "";
|
|
1622
|
+
if (altText) {
|
|
1623
|
+
badges.push(altText);
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
return badges;
|
|
1627
|
+
}
|
|
1628
|
+
function extractTitleAndDescription(content, sections) {
|
|
1629
|
+
let title;
|
|
1630
|
+
let description;
|
|
1631
|
+
const firstHeading = sections[0];
|
|
1632
|
+
if (firstHeading?.level === 1) {
|
|
1633
|
+
title = firstHeading.title;
|
|
1634
|
+
} else {
|
|
1635
|
+
const lines = content.split("\n");
|
|
1636
|
+
for (const line of lines) {
|
|
1637
|
+
const trimmed = line.trim();
|
|
1638
|
+
if (!trimmed) continue;
|
|
1639
|
+
if (trimmed.startsWith("!") || trimmed.startsWith("[!")) continue;
|
|
1640
|
+
if (trimmed.startsWith("#")) {
|
|
1641
|
+
title = trimmed.replace(/^#+\s*/, "");
|
|
1642
|
+
break;
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
const afterTitle = content.split("\n").slice(1);
|
|
1647
|
+
let foundContent = false;
|
|
1648
|
+
for (const line of afterTitle) {
|
|
1649
|
+
const trimmed = line.trim();
|
|
1650
|
+
if (trimmed.startsWith("!") || trimmed.startsWith("[!")) continue;
|
|
1651
|
+
if (trimmed.startsWith("#")) break;
|
|
1652
|
+
if (!trimmed && !foundContent) continue;
|
|
1653
|
+
if (!trimmed && foundContent) break;
|
|
1654
|
+
if (trimmed) {
|
|
1655
|
+
foundContent = true;
|
|
1656
|
+
if (!description) {
|
|
1657
|
+
description = trimmed;
|
|
1658
|
+
} else {
|
|
1659
|
+
description += " " + trimmed;
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
if (description) {
|
|
1664
|
+
description = description.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").trim();
|
|
1665
|
+
}
|
|
1666
|
+
return { title, description };
|
|
1667
|
+
}
|
|
1668
|
+
function findQuickStartFromCode(content) {
|
|
1669
|
+
const installMatch = content.match(/installation|install|setup/i);
|
|
1670
|
+
const startIndex = installMatch?.index ?? 0;
|
|
1671
|
+
const searchArea = content.slice(startIndex, startIndex + 2e3);
|
|
1672
|
+
const codeBlocks = extractCodeBlocks(searchArea);
|
|
1673
|
+
for (const block of codeBlocks) {
|
|
1674
|
+
if (block.startsWith("```javascript") || block.startsWith("```typescript") || block.startsWith("```js") || block.startsWith("```ts") || block.startsWith("```jsx") || block.startsWith("```tsx")) {
|
|
1675
|
+
return block;
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
return codeBlocks[0];
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
// src/plugins/core/readme-parser.ts
|
|
1682
|
+
var readmeParserPlugin = {
|
|
1683
|
+
name: "readme-parser",
|
|
1684
|
+
version: "1.0.0",
|
|
1685
|
+
category: "parser",
|
|
1686
|
+
install(kernel) {
|
|
1687
|
+
kernel.on("parse:start", async (context) => {
|
|
1688
|
+
const { files } = context.package;
|
|
1689
|
+
let readmeContent;
|
|
1690
|
+
const readmePatterns = [
|
|
1691
|
+
"README.md",
|
|
1692
|
+
"readme.md",
|
|
1693
|
+
"Readme.md",
|
|
1694
|
+
"README.MD",
|
|
1695
|
+
"README",
|
|
1696
|
+
"readme"
|
|
1697
|
+
];
|
|
1698
|
+
for (const pattern of readmePatterns) {
|
|
1699
|
+
if (files.has(pattern)) {
|
|
1700
|
+
readmeContent = files.get(pattern);
|
|
1701
|
+
break;
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
if (!readmeContent) {
|
|
1705
|
+
for (const [path, content] of files) {
|
|
1706
|
+
if (path.toLowerCase().includes("readme")) {
|
|
1707
|
+
readmeContent = content;
|
|
1708
|
+
break;
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
if (!readmeContent) {
|
|
1713
|
+
return;
|
|
1714
|
+
}
|
|
1715
|
+
try {
|
|
1716
|
+
context.readme = parseReadme(readmeContent);
|
|
1717
|
+
if (!context.package.description && context.readme.description) {
|
|
1718
|
+
context.package.description = context.readme.description;
|
|
1719
|
+
}
|
|
1720
|
+
} catch (error) {
|
|
1721
|
+
context.errors.push(error instanceof Error ? error : new Error(String(error)));
|
|
1722
|
+
}
|
|
1723
|
+
});
|
|
1724
|
+
}
|
|
1725
|
+
};
|
|
1726
|
+
|
|
1727
|
+
// src/core/tokens.ts
|
|
1728
|
+
var CHARS_PER_TOKEN = {
|
|
1729
|
+
/** English prose: ~4 chars per token */
|
|
1730
|
+
prose: 4,
|
|
1731
|
+
/** Code blocks: ~3 chars per token (more special tokens) */
|
|
1732
|
+
code: 3};
|
|
1733
|
+
var PRIORITY_ORDER = {
|
|
1734
|
+
functions: 100,
|
|
1735
|
+
examples: 90,
|
|
1736
|
+
classes: 80,
|
|
1737
|
+
interfaces: 70,
|
|
1738
|
+
types: 60,
|
|
1739
|
+
readme: 50
|
|
1740
|
+
};
|
|
1741
|
+
function countTokens(text) {
|
|
1742
|
+
if (!text) return 0;
|
|
1743
|
+
let tokens = 0;
|
|
1744
|
+
const codeBlockRegex = /```[\s\S]*?```/g;
|
|
1745
|
+
const parts = [];
|
|
1746
|
+
let lastIndex = 0;
|
|
1747
|
+
let match;
|
|
1748
|
+
while ((match = codeBlockRegex.exec(text)) !== null) {
|
|
1749
|
+
if (match.index > lastIndex) {
|
|
1750
|
+
parts.push({
|
|
1751
|
+
text: text.slice(lastIndex, match.index),
|
|
1752
|
+
isCode: false
|
|
1753
|
+
});
|
|
1754
|
+
}
|
|
1755
|
+
parts.push({
|
|
1756
|
+
text: match[0],
|
|
1757
|
+
isCode: true
|
|
1758
|
+
});
|
|
1759
|
+
lastIndex = match.index + match[0].length;
|
|
1760
|
+
}
|
|
1761
|
+
if (lastIndex < text.length) {
|
|
1762
|
+
parts.push({
|
|
1763
|
+
text: text.slice(lastIndex),
|
|
1764
|
+
isCode: false
|
|
1765
|
+
});
|
|
1766
|
+
}
|
|
1767
|
+
for (const part of parts) {
|
|
1768
|
+
const charsPerToken = part.isCode ? CHARS_PER_TOKEN.code : CHARS_PER_TOKEN.prose;
|
|
1769
|
+
tokens += Math.ceil(part.text.length / charsPerToken);
|
|
1770
|
+
}
|
|
1771
|
+
return tokens;
|
|
1772
|
+
}
|
|
1773
|
+
function parseSections(text) {
|
|
1774
|
+
const sections = [];
|
|
1775
|
+
const lines = text.split("\n");
|
|
1776
|
+
let currentSection = null;
|
|
1777
|
+
let currentContent = [];
|
|
1778
|
+
function finishSection() {
|
|
1779
|
+
if (currentSection) {
|
|
1780
|
+
currentSection.content = currentContent.join("\n").trim();
|
|
1781
|
+
currentSection.tokens = countTokens(currentSection.content);
|
|
1782
|
+
sections.push(currentSection);
|
|
1783
|
+
currentContent = [];
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
for (const line of lines) {
|
|
1787
|
+
const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
|
|
1788
|
+
if (headingMatch && headingMatch[2]) {
|
|
1789
|
+
finishSection();
|
|
1790
|
+
const title = headingMatch[2].trim();
|
|
1791
|
+
const priority = inferPriority(title);
|
|
1792
|
+
currentSection = {
|
|
1793
|
+
id: title.toLowerCase().replace(/[^a-z0-9]+/g, "-"),
|
|
1794
|
+
title,
|
|
1795
|
+
content: "",
|
|
1796
|
+
priority,
|
|
1797
|
+
tokens: 0
|
|
1798
|
+
};
|
|
1799
|
+
currentContent = [line];
|
|
1800
|
+
} else if (currentSection) {
|
|
1801
|
+
currentContent.push(line);
|
|
1802
|
+
} else {
|
|
1803
|
+
if (line.trim()) {
|
|
1804
|
+
if (!currentSection) {
|
|
1805
|
+
currentSection = {
|
|
1806
|
+
id: "intro",
|
|
1807
|
+
title: "Introduction",
|
|
1808
|
+
content: "",
|
|
1809
|
+
priority: "readme",
|
|
1810
|
+
tokens: 0
|
|
1811
|
+
};
|
|
1812
|
+
}
|
|
1813
|
+
currentContent.push(line);
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
finishSection();
|
|
1818
|
+
return sections;
|
|
1819
|
+
}
|
|
1820
|
+
function inferPriority(title) {
|
|
1821
|
+
const lower = title.toLowerCase();
|
|
1822
|
+
if (lower.includes("function") || lower.includes("method") || lower.includes("api")) {
|
|
1823
|
+
return "functions";
|
|
1824
|
+
}
|
|
1825
|
+
if (lower.includes("example") || lower.includes("usage") || lower.includes("quick start")) {
|
|
1826
|
+
return "examples";
|
|
1827
|
+
}
|
|
1828
|
+
if (lower.includes("class")) {
|
|
1829
|
+
return "classes";
|
|
1830
|
+
}
|
|
1831
|
+
if (lower.includes("interface")) {
|
|
1832
|
+
return "interfaces";
|
|
1833
|
+
}
|
|
1834
|
+
if (lower.includes("type")) {
|
|
1835
|
+
return "types";
|
|
1836
|
+
}
|
|
1837
|
+
return "readme";
|
|
1838
|
+
}
|
|
1839
|
+
function prioritizeSections(sections, priorities = ["functions", "examples"]) {
|
|
1840
|
+
const customOrder = { ...PRIORITY_ORDER };
|
|
1841
|
+
priorities.forEach((p, i) => {
|
|
1842
|
+
customOrder[p] = 1e3 - i;
|
|
1843
|
+
});
|
|
1844
|
+
return [...sections].sort((a, b) => {
|
|
1845
|
+
return (customOrder[b.priority] ?? 0) - (customOrder[a.priority] ?? 0);
|
|
1846
|
+
});
|
|
1847
|
+
}
|
|
1848
|
+
function truncateToTokenLimit(text, limit, priorities = ["functions", "examples"]) {
|
|
1849
|
+
const currentTokens = countTokens(text);
|
|
1850
|
+
if (currentTokens <= limit) {
|
|
1851
|
+
return {
|
|
1852
|
+
text,
|
|
1853
|
+
truncated: false,
|
|
1854
|
+
tokenCount: currentTokens,
|
|
1855
|
+
includedSections: [],
|
|
1856
|
+
removedSections: []
|
|
1857
|
+
};
|
|
1858
|
+
}
|
|
1859
|
+
const sections = parseSections(text);
|
|
1860
|
+
const prioritized = prioritizeSections(sections, priorities);
|
|
1861
|
+
const included = [];
|
|
1862
|
+
const removed = [];
|
|
1863
|
+
let usedTokens = 0;
|
|
1864
|
+
for (const section of prioritized) {
|
|
1865
|
+
if (usedTokens + section.tokens <= limit) {
|
|
1866
|
+
included.push(section);
|
|
1867
|
+
usedTokens += section.tokens;
|
|
1868
|
+
} else {
|
|
1869
|
+
const remaining = limit - usedTokens;
|
|
1870
|
+
if (remaining > 50 && section.tokens > 0) {
|
|
1871
|
+
const truncated = truncateSectionContent(section, remaining);
|
|
1872
|
+
if (truncated) {
|
|
1873
|
+
included.push(truncated);
|
|
1874
|
+
usedTokens += truncated.tokens;
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
removed.push(section);
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
const sectionOrder = sections.map((s) => s.id);
|
|
1881
|
+
included.sort((a, b) => sectionOrder.indexOf(a.id) - sectionOrder.indexOf(b.id));
|
|
1882
|
+
const output = included.map((s) => s.content).join("\n\n");
|
|
1883
|
+
return {
|
|
1884
|
+
text: output.trim(),
|
|
1885
|
+
truncated: true,
|
|
1886
|
+
tokenCount: countTokens(output),
|
|
1887
|
+
includedSections: included.map((s) => s.title),
|
|
1888
|
+
removedSections: removed.map((s) => s.title)
|
|
1889
|
+
};
|
|
1890
|
+
}
|
|
1891
|
+
function truncateSectionContent(section, maxTokens) {
|
|
1892
|
+
const lines = section.content.split("\n");
|
|
1893
|
+
const truncatedLines = [];
|
|
1894
|
+
let tokens = 0;
|
|
1895
|
+
for (const line of lines) {
|
|
1896
|
+
const lineTokens = countTokens(line);
|
|
1897
|
+
if (tokens + lineTokens <= maxTokens) {
|
|
1898
|
+
truncatedLines.push(line);
|
|
1899
|
+
tokens += lineTokens;
|
|
1900
|
+
} else {
|
|
1901
|
+
break;
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
if (truncatedLines.length === 0) {
|
|
1905
|
+
return null;
|
|
1906
|
+
}
|
|
1907
|
+
truncatedLines.push("\n...(truncated)");
|
|
1908
|
+
return {
|
|
1909
|
+
...section,
|
|
1910
|
+
content: truncatedLines.join("\n"),
|
|
1911
|
+
tokens: countTokens(truncatedLines.join("\n"))
|
|
1912
|
+
};
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
// src/outputs/llms.ts
|
|
1916
|
+
var DEFAULT_LLMS_TOKEN_LIMIT = Infinity;
|
|
1917
|
+
function generateLlmsTxt(context, options = {}) {
|
|
1918
|
+
const {
|
|
1919
|
+
tokenLimit = DEFAULT_LLMS_TOKEN_LIMIT,
|
|
1920
|
+
includeInstall = true,
|
|
1921
|
+
includeQuickStart = true,
|
|
1922
|
+
maxFunctions = Infinity,
|
|
1923
|
+
maxClasses = Infinity
|
|
1924
|
+
} = options;
|
|
1925
|
+
const { package: pkg, api, readme } = context;
|
|
1926
|
+
const sections = [];
|
|
1927
|
+
sections.push(`# ${pkg.name}`);
|
|
1928
|
+
if (pkg.description) {
|
|
1929
|
+
sections.push(`
|
|
1930
|
+
> ${pkg.description}`);
|
|
1931
|
+
}
|
|
1932
|
+
sections.push("");
|
|
1933
|
+
if (includeInstall) {
|
|
1934
|
+
sections.push("## Install\n");
|
|
1935
|
+
sections.push("```bash");
|
|
1936
|
+
sections.push(`npm install ${pkg.name}`);
|
|
1937
|
+
sections.push("```\n");
|
|
1938
|
+
}
|
|
1939
|
+
if (includeQuickStart && readme?.quickStart) {
|
|
1940
|
+
sections.push("## Quick Start\n");
|
|
1941
|
+
sections.push(readme.quickStart);
|
|
1942
|
+
sections.push("");
|
|
1943
|
+
}
|
|
1944
|
+
sections.push("## API\n");
|
|
1945
|
+
const functions = api.filter((e) => e.kind === "function");
|
|
1946
|
+
const classes = api.filter((e) => e.kind === "class");
|
|
1947
|
+
const interfaces = api.filter((e) => e.kind === "interface");
|
|
1948
|
+
const types = api.filter((e) => e.kind === "type");
|
|
1949
|
+
const constants = api.filter((e) => e.kind === "constant");
|
|
1950
|
+
if (functions.length > 0) {
|
|
1951
|
+
sections.push("### Functions\n");
|
|
1952
|
+
const limit = Math.min(functions.length, maxFunctions);
|
|
1953
|
+
for (let i = 0; i < limit; i++) {
|
|
1954
|
+
sections.push(formatFunctionBrief(functions[i]));
|
|
1955
|
+
}
|
|
1956
|
+
if (functions.length > maxFunctions && maxFunctions < Infinity) {
|
|
1957
|
+
sections.push(`
|
|
1958
|
+
...and ${functions.length - maxFunctions} more functions.`);
|
|
1959
|
+
}
|
|
1960
|
+
sections.push("");
|
|
1961
|
+
}
|
|
1962
|
+
if (classes.length > 0) {
|
|
1963
|
+
sections.push("### Classes\n");
|
|
1964
|
+
const limit = Math.min(classes.length, maxClasses);
|
|
1965
|
+
for (let i = 0; i < limit; i++) {
|
|
1966
|
+
sections.push(formatClassBrief(classes[i]));
|
|
1967
|
+
}
|
|
1968
|
+
if (classes.length > maxClasses && maxClasses < Infinity) {
|
|
1969
|
+
sections.push(`
|
|
1970
|
+
...and ${classes.length - maxClasses} more classes.`);
|
|
1971
|
+
}
|
|
1972
|
+
sections.push("");
|
|
1973
|
+
}
|
|
1974
|
+
if (interfaces.length > 0 || types.length > 0) {
|
|
1975
|
+
sections.push("### Types\n");
|
|
1976
|
+
const allTypes = [...interfaces, ...types];
|
|
1977
|
+
for (const t of allTypes) {
|
|
1978
|
+
const desc = t.description ? ` - ${truncate(t.description, 80)}` : "";
|
|
1979
|
+
sections.push(`- \`${t.name}\`${desc}`);
|
|
1980
|
+
}
|
|
1981
|
+
sections.push("");
|
|
1982
|
+
}
|
|
1983
|
+
if (constants.length > 0) {
|
|
1984
|
+
sections.push("### Constants\n");
|
|
1985
|
+
for (const c of constants) {
|
|
1986
|
+
const desc = c.description ? ` - ${truncate(c.description, 80)}` : "";
|
|
1987
|
+
sections.push(`- \`${c.name}\`${desc}`);
|
|
1988
|
+
}
|
|
1989
|
+
sections.push("");
|
|
1990
|
+
}
|
|
1991
|
+
let output = sections.join("\n");
|
|
1992
|
+
const currentTokens = countTokens(output);
|
|
1993
|
+
if (currentTokens > tokenLimit) {
|
|
1994
|
+
const result = truncateToTokenLimit(output, tokenLimit, ["functions", "examples"]);
|
|
1995
|
+
output = result.text;
|
|
1996
|
+
context.truncated = result.truncated;
|
|
1997
|
+
}
|
|
1998
|
+
context.tokenCount = countTokens(output);
|
|
1999
|
+
return output;
|
|
2000
|
+
}
|
|
2001
|
+
function formatFunctionBrief(fn) {
|
|
2002
|
+
const params = fn.params?.map((p) => `${p.name}${p.optional ? "?" : ""}`).join(", ") || "";
|
|
2003
|
+
const returnType = fn.returns?.type ? `: ${simplifyType(fn.returns.type)}` : "";
|
|
2004
|
+
const desc = fn.description ? ` - ${truncate(fn.description, 60)}` : "";
|
|
2005
|
+
return `- \`${fn.name}(${params})${returnType}\`${desc}`;
|
|
2006
|
+
}
|
|
2007
|
+
function formatClassBrief(cls) {
|
|
2008
|
+
const lines = [];
|
|
2009
|
+
const desc = cls.description ? ` - ${truncate(cls.description, 60)}` : "";
|
|
2010
|
+
lines.push(`- \`${cls.name}\`${desc}`);
|
|
2011
|
+
if (cls.methods && cls.methods.length > 0) {
|
|
2012
|
+
const keyMethods = cls.methods.slice(0, 3);
|
|
2013
|
+
for (const method of keyMethods) {
|
|
2014
|
+
lines.push(` - \`${method.name}()\``);
|
|
2015
|
+
}
|
|
2016
|
+
if (cls.methods.length > 3) {
|
|
2017
|
+
lines.push(` - ...${cls.methods.length - 3} more methods`);
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
return lines.join("\n");
|
|
2021
|
+
}
|
|
2022
|
+
function simplifyType(type) {
|
|
2023
|
+
if (type.length > 30) {
|
|
2024
|
+
const genericStart = type.indexOf("<");
|
|
2025
|
+
if (genericStart > 0) {
|
|
2026
|
+
return type.slice(0, genericStart) + "<...>";
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
return type;
|
|
2030
|
+
}
|
|
2031
|
+
function truncate(text, maxLength) {
|
|
2032
|
+
if (text.length <= maxLength) return text;
|
|
2033
|
+
return text.slice(0, maxLength - 3) + "...";
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
// src/plugins/core/llms-output.ts
|
|
2037
|
+
var llmsOutputPlugin = {
|
|
2038
|
+
name: "llms-output",
|
|
2039
|
+
version: "1.0.0",
|
|
2040
|
+
category: "output",
|
|
2041
|
+
install(kernel) {
|
|
2042
|
+
kernel.on("output:start", async (context) => {
|
|
2043
|
+
if (!context.options.formats?.includes("llms")) {
|
|
2044
|
+
return;
|
|
2045
|
+
}
|
|
2046
|
+
try {
|
|
2047
|
+
const tokenLimit = context.options.llmsTokenLimit ?? DEFAULT_LLMS_TOKEN_LIMIT;
|
|
2048
|
+
const output = generateLlmsTxt(context, {
|
|
2049
|
+
tokenLimit,
|
|
2050
|
+
includeInstall: true,
|
|
2051
|
+
includeQuickStart: true
|
|
2052
|
+
});
|
|
2053
|
+
context.outputs.set("llms", output);
|
|
2054
|
+
} catch (error) {
|
|
2055
|
+
context.errors.push(error instanceof Error ? error : new Error(String(error)));
|
|
2056
|
+
}
|
|
2057
|
+
});
|
|
2058
|
+
}
|
|
2059
|
+
};
|
|
2060
|
+
|
|
2061
|
+
// src/outputs/llms-full.ts
|
|
2062
|
+
function generateLlmsFullTxt(context, options = {}) {
|
|
2063
|
+
const {
|
|
2064
|
+
includeExamples = true,
|
|
2065
|
+
includeParamDescriptions = true,
|
|
2066
|
+
includeSourceLocations = false,
|
|
2067
|
+
includeDeprecations = true
|
|
2068
|
+
} = options;
|
|
2069
|
+
const { package: pkg, api, readme } = context;
|
|
2070
|
+
const sections = [];
|
|
2071
|
+
sections.push(`# ${pkg.name} v${pkg.version}`);
|
|
2072
|
+
if (pkg.description) {
|
|
2073
|
+
sections.push(`
|
|
2074
|
+
> ${pkg.description}`);
|
|
2075
|
+
}
|
|
2076
|
+
sections.push("");
|
|
2077
|
+
sections.push("## Package Info\n");
|
|
2078
|
+
sections.push(`- **Version:** ${pkg.version}`);
|
|
2079
|
+
if (pkg.license) {
|
|
2080
|
+
sections.push(`- **License:** ${pkg.license}`);
|
|
2081
|
+
}
|
|
2082
|
+
if (pkg.homepage) {
|
|
2083
|
+
sections.push(`- **Homepage:** ${pkg.homepage}`);
|
|
2084
|
+
}
|
|
2085
|
+
if (pkg.repository) {
|
|
2086
|
+
sections.push(`- **Repository:** ${pkg.repository.url}`);
|
|
2087
|
+
}
|
|
2088
|
+
sections.push("");
|
|
2089
|
+
sections.push("## Installation\n");
|
|
2090
|
+
sections.push("```bash");
|
|
2091
|
+
sections.push(`# npm`);
|
|
2092
|
+
sections.push(`npm install ${pkg.name}`);
|
|
2093
|
+
sections.push("");
|
|
2094
|
+
sections.push(`# yarn`);
|
|
2095
|
+
sections.push(`yarn add ${pkg.name}`);
|
|
2096
|
+
sections.push("");
|
|
2097
|
+
sections.push(`# pnpm`);
|
|
2098
|
+
sections.push(`pnpm add ${pkg.name}`);
|
|
2099
|
+
sections.push("```\n");
|
|
2100
|
+
if (readme?.quickStart) {
|
|
2101
|
+
sections.push("## Quick Start\n");
|
|
2102
|
+
sections.push(readme.quickStart);
|
|
2103
|
+
sections.push("");
|
|
2104
|
+
}
|
|
2105
|
+
sections.push("## API Reference\n");
|
|
2106
|
+
const functions = api.filter((e) => e.kind === "function");
|
|
2107
|
+
const classes = api.filter((e) => e.kind === "class");
|
|
2108
|
+
const interfaces = api.filter((e) => e.kind === "interface");
|
|
2109
|
+
const types = api.filter((e) => e.kind === "type");
|
|
2110
|
+
const constants = api.filter((e) => e.kind === "constant");
|
|
2111
|
+
const enums = api.filter((e) => e.kind === "enum");
|
|
2112
|
+
if (functions.length > 0) {
|
|
2113
|
+
sections.push("### Functions\n");
|
|
2114
|
+
for (const fn of functions) {
|
|
2115
|
+
sections.push(formatFunction(fn, { includeExamples, includeParamDescriptions, includeDeprecations, includeSourceLocations }));
|
|
2116
|
+
sections.push("");
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
if (classes.length > 0) {
|
|
2120
|
+
sections.push("### Classes\n");
|
|
2121
|
+
for (const cls of classes) {
|
|
2122
|
+
sections.push(formatClass(cls, { includeExamples, includeDeprecations}));
|
|
2123
|
+
sections.push("");
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
if (interfaces.length > 0) {
|
|
2127
|
+
sections.push("### Interfaces\n");
|
|
2128
|
+
for (const iface of interfaces) {
|
|
2129
|
+
sections.push(formatInterface(iface, { includeDeprecations}));
|
|
2130
|
+
sections.push("");
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
if (types.length > 0) {
|
|
2134
|
+
sections.push("### Types\n");
|
|
2135
|
+
for (const type of types) {
|
|
2136
|
+
sections.push(formatType(type, { includeDeprecations }));
|
|
2137
|
+
sections.push("");
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
if (enums.length > 0) {
|
|
2141
|
+
sections.push("### Enums\n");
|
|
2142
|
+
for (const enumEntry of enums) {
|
|
2143
|
+
sections.push(formatEnum(enumEntry));
|
|
2144
|
+
sections.push("");
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
if (constants.length > 0) {
|
|
2148
|
+
sections.push("### Constants\n");
|
|
2149
|
+
for (const constant of constants) {
|
|
2150
|
+
sections.push(formatConstant(constant));
|
|
2151
|
+
sections.push("");
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
const output = sections.join("\n");
|
|
2155
|
+
context.tokenCount = countTokens(output);
|
|
2156
|
+
return output;
|
|
2157
|
+
}
|
|
2158
|
+
function formatFunction(fn, options) {
|
|
2159
|
+
const lines = [];
|
|
2160
|
+
lines.push(`#### \`${fn.name}\``);
|
|
2161
|
+
if (options.includeDeprecations && fn.deprecated) {
|
|
2162
|
+
lines.push(`
|
|
2163
|
+
> \u26A0\uFE0F **Deprecated:** ${typeof fn.deprecated === "string" ? fn.deprecated : "This function is deprecated."}`);
|
|
2164
|
+
}
|
|
2165
|
+
if (fn.description) {
|
|
2166
|
+
lines.push(`
|
|
2167
|
+
${fn.description}`);
|
|
2168
|
+
}
|
|
2169
|
+
lines.push("\n**Signature:**");
|
|
2170
|
+
lines.push("```typescript");
|
|
2171
|
+
lines.push(fn.signature);
|
|
2172
|
+
lines.push("```");
|
|
2173
|
+
if (fn.params && fn.params.length > 0 && options.includeParamDescriptions) {
|
|
2174
|
+
lines.push("\n**Parameters:**\n");
|
|
2175
|
+
for (const param of fn.params) {
|
|
2176
|
+
const optional = param.optional ? " (optional)" : "";
|
|
2177
|
+
const defaultVal = param.defaultValue ? ` = \`${param.defaultValue}\`` : "";
|
|
2178
|
+
lines.push(`- \`${param.name}: ${param.type || "unknown"}\`${optional}${defaultVal}`);
|
|
2179
|
+
if (param.description) {
|
|
2180
|
+
lines.push(` - ${param.description}`);
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
if (fn.returns) {
|
|
2185
|
+
lines.push("\n**Returns:**");
|
|
2186
|
+
lines.push(`- \`${fn.returns.type}\`${fn.returns.description ? ` - ${fn.returns.description}` : ""}`);
|
|
2187
|
+
}
|
|
2188
|
+
if (options.includeExamples && fn.examples && fn.examples.length > 0) {
|
|
2189
|
+
lines.push("\n**Examples:**");
|
|
2190
|
+
for (const example of fn.examples) {
|
|
2191
|
+
if (example.includes("```")) {
|
|
2192
|
+
lines.push(example);
|
|
2193
|
+
} else {
|
|
2194
|
+
lines.push("```typescript");
|
|
2195
|
+
lines.push(example);
|
|
2196
|
+
lines.push("```");
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
if (options.includeSourceLocations && fn.sourceFile) {
|
|
2201
|
+
lines.push(`
|
|
2202
|
+
*Source: ${fn.sourceFile}${fn.line ? `:${fn.line}` : ""}*`);
|
|
2203
|
+
}
|
|
2204
|
+
return lines.join("\n");
|
|
2205
|
+
}
|
|
2206
|
+
function formatClass(cls, options) {
|
|
2207
|
+
const lines = [];
|
|
2208
|
+
lines.push(`#### \`${cls.name}\``);
|
|
2209
|
+
if (options.includeDeprecations && cls.deprecated) {
|
|
2210
|
+
lines.push(`
|
|
2211
|
+
> \u26A0\uFE0F **Deprecated:** ${typeof cls.deprecated === "string" ? cls.deprecated : "This class is deprecated."}`);
|
|
2212
|
+
}
|
|
2213
|
+
if (cls.description) {
|
|
2214
|
+
lines.push(`
|
|
2215
|
+
${cls.description}`);
|
|
2216
|
+
}
|
|
2217
|
+
lines.push("\n**Signature:**");
|
|
2218
|
+
lines.push("```typescript");
|
|
2219
|
+
let sig = cls.signature;
|
|
2220
|
+
if (cls.extends && cls.extends.length > 0) {
|
|
2221
|
+
sig += ` extends ${cls.extends.join(", ")}`;
|
|
2222
|
+
}
|
|
2223
|
+
if (cls.implements && cls.implements.length > 0) {
|
|
2224
|
+
sig += ` implements ${cls.implements.join(", ")}`;
|
|
2225
|
+
}
|
|
2226
|
+
lines.push(sig);
|
|
2227
|
+
lines.push("```");
|
|
2228
|
+
if (cls.properties && cls.properties.length > 0) {
|
|
2229
|
+
lines.push("\n**Properties:**\n");
|
|
2230
|
+
for (const prop of cls.properties) {
|
|
2231
|
+
lines.push(`- \`${prop.name}\`: \`${prop.signature.split(":").slice(1).join(":").trim() || "unknown"}\``);
|
|
2232
|
+
if (prop.description) {
|
|
2233
|
+
lines.push(` - ${prop.description}`);
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
if (cls.methods && cls.methods.length > 0) {
|
|
2238
|
+
lines.push("\n**Methods:**\n");
|
|
2239
|
+
for (const method of cls.methods) {
|
|
2240
|
+
const params = method.params?.map((p) => `${p.name}: ${p.type || "unknown"}`).join(", ") || "";
|
|
2241
|
+
const returnType = method.returns?.type || "void";
|
|
2242
|
+
lines.push(`- \`${method.name}(${params}): ${returnType}\``);
|
|
2243
|
+
if (method.description) {
|
|
2244
|
+
lines.push(` - ${method.description}`);
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
if (options.includeExamples && cls.examples && cls.examples.length > 0) {
|
|
2249
|
+
lines.push("\n**Examples:**");
|
|
2250
|
+
for (const example of cls.examples) {
|
|
2251
|
+
if (example.includes("```")) {
|
|
2252
|
+
lines.push(example);
|
|
2253
|
+
} else {
|
|
2254
|
+
lines.push("```typescript");
|
|
2255
|
+
lines.push(example);
|
|
2256
|
+
lines.push("```");
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
return lines.join("\n");
|
|
2261
|
+
}
|
|
2262
|
+
function formatInterface(iface, options) {
|
|
2263
|
+
const lines = [];
|
|
2264
|
+
lines.push(`#### \`${iface.name}\``);
|
|
2265
|
+
if (options.includeDeprecations && iface.deprecated) {
|
|
2266
|
+
lines.push(`
|
|
2267
|
+
> \u26A0\uFE0F **Deprecated**`);
|
|
2268
|
+
}
|
|
2269
|
+
if (iface.description) {
|
|
2270
|
+
lines.push(`
|
|
2271
|
+
${iface.description}`);
|
|
2272
|
+
}
|
|
2273
|
+
lines.push("\n```typescript");
|
|
2274
|
+
let sig = iface.signature;
|
|
2275
|
+
if (iface.extends && iface.extends.length > 0) {
|
|
2276
|
+
sig += ` extends ${iface.extends.join(", ")}`;
|
|
2277
|
+
}
|
|
2278
|
+
lines.push(sig + " {");
|
|
2279
|
+
if (iface.properties && iface.properties.length > 0) {
|
|
2280
|
+
for (const prop of iface.properties) {
|
|
2281
|
+
lines.push(` ${prop.signature};`);
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2284
|
+
if (iface.methods && iface.methods.length > 0) {
|
|
2285
|
+
for (const method of iface.methods) {
|
|
2286
|
+
lines.push(` ${method.signature};`);
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
lines.push("}");
|
|
2290
|
+
lines.push("```");
|
|
2291
|
+
return lines.join("\n");
|
|
2292
|
+
}
|
|
2293
|
+
function formatType(type, options) {
|
|
2294
|
+
const lines = [];
|
|
2295
|
+
lines.push(`#### \`${type.name}\``);
|
|
2296
|
+
if (options.includeDeprecations && type.deprecated) {
|
|
2297
|
+
lines.push(`
|
|
2298
|
+
> \u26A0\uFE0F **Deprecated**`);
|
|
2299
|
+
}
|
|
2300
|
+
if (type.description) {
|
|
2301
|
+
lines.push(`
|
|
2302
|
+
${type.description}`);
|
|
2303
|
+
}
|
|
2304
|
+
lines.push("\n```typescript");
|
|
2305
|
+
lines.push(type.signature);
|
|
2306
|
+
lines.push("```");
|
|
2307
|
+
return lines.join("\n");
|
|
2308
|
+
}
|
|
2309
|
+
function formatEnum(enumEntry) {
|
|
2310
|
+
const lines = [];
|
|
2311
|
+
lines.push(`#### \`${enumEntry.name}\``);
|
|
2312
|
+
if (enumEntry.description) {
|
|
2313
|
+
lines.push(`
|
|
2314
|
+
${enumEntry.description}`);
|
|
2315
|
+
}
|
|
2316
|
+
lines.push("\n```typescript");
|
|
2317
|
+
lines.push(`enum ${enumEntry.name} {`);
|
|
2318
|
+
if (enumEntry.members) {
|
|
2319
|
+
for (const member of enumEntry.members) {
|
|
2320
|
+
if (member.value !== void 0) {
|
|
2321
|
+
lines.push(` ${member.name} = ${JSON.stringify(member.value)},`);
|
|
2322
|
+
} else {
|
|
2323
|
+
lines.push(` ${member.name},`);
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
lines.push("}");
|
|
2328
|
+
lines.push("```");
|
|
2329
|
+
return lines.join("\n");
|
|
2330
|
+
}
|
|
2331
|
+
function formatConstant(constant) {
|
|
2332
|
+
const lines = [];
|
|
2333
|
+
lines.push(`#### \`${constant.name}\``);
|
|
2334
|
+
if (constant.description) {
|
|
2335
|
+
lines.push(`
|
|
2336
|
+
${constant.description}`);
|
|
2337
|
+
}
|
|
2338
|
+
lines.push("\n```typescript");
|
|
2339
|
+
lines.push(constant.signature);
|
|
2340
|
+
lines.push("```");
|
|
2341
|
+
return lines.join("\n");
|
|
2342
|
+
}
|
|
2343
|
+
|
|
2344
|
+
// src/plugins/core/llms-full-output.ts
|
|
2345
|
+
var llmsFullOutputPlugin = {
|
|
2346
|
+
name: "llms-full-output",
|
|
2347
|
+
version: "1.0.0",
|
|
2348
|
+
category: "output",
|
|
2349
|
+
install(kernel) {
|
|
2350
|
+
kernel.on("output:start", async (context) => {
|
|
2351
|
+
if (!context.options.formats?.includes("llms-full")) {
|
|
2352
|
+
return;
|
|
2353
|
+
}
|
|
2354
|
+
try {
|
|
2355
|
+
const output = generateLlmsFullTxt(context, {
|
|
2356
|
+
includeExamples: true,
|
|
2357
|
+
includeParamDescriptions: true,
|
|
2358
|
+
includeDeprecations: true
|
|
2359
|
+
});
|
|
2360
|
+
context.outputs.set("llms-full", output);
|
|
2361
|
+
} catch (error) {
|
|
2362
|
+
context.errors.push(error instanceof Error ? error : new Error(String(error)));
|
|
2363
|
+
}
|
|
2364
|
+
});
|
|
2365
|
+
}
|
|
2366
|
+
};
|
|
2367
|
+
|
|
2368
|
+
// src/outputs/markdown.ts
|
|
2369
|
+
function generateMarkdown(context, options = {}) {
|
|
2370
|
+
const {
|
|
2371
|
+
includeToc = true,
|
|
2372
|
+
includeParamTables = true,
|
|
2373
|
+
includeBadges = true,
|
|
2374
|
+
repositoryUrl
|
|
2375
|
+
} = options;
|
|
2376
|
+
const { package: pkg, api, readme } = context;
|
|
2377
|
+
const sections = [];
|
|
2378
|
+
sections.push(`# ${pkg.name}`);
|
|
2379
|
+
sections.push("");
|
|
2380
|
+
if (includeBadges) {
|
|
2381
|
+
const badges = [];
|
|
2382
|
+
badges.push(`})`);
|
|
2383
|
+
if (pkg.license) {
|
|
2384
|
+
badges.push(`})`);
|
|
2385
|
+
}
|
|
2386
|
+
if (badges.length > 0) {
|
|
2387
|
+
sections.push(badges.join(" "));
|
|
2388
|
+
sections.push("");
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
if (pkg.description) {
|
|
2392
|
+
sections.push(`> ${pkg.description}`);
|
|
2393
|
+
sections.push("");
|
|
2394
|
+
}
|
|
2395
|
+
if (includeToc) {
|
|
2396
|
+
sections.push("## Table of Contents\n");
|
|
2397
|
+
sections.push("- [Installation](#installation)");
|
|
2398
|
+
if (readme?.quickStart) {
|
|
2399
|
+
sections.push("- [Quick Start](#quick-start)");
|
|
2400
|
+
}
|
|
2401
|
+
sections.push("- [API Reference](#api-reference)");
|
|
2402
|
+
const functions2 = api.filter((e) => e.kind === "function");
|
|
2403
|
+
const classes2 = api.filter((e) => e.kind === "class");
|
|
2404
|
+
const interfaces2 = api.filter((e) => e.kind === "interface");
|
|
2405
|
+
const types2 = api.filter((e) => e.kind === "type");
|
|
2406
|
+
if (functions2.length > 0) {
|
|
2407
|
+
sections.push(" - [Functions](#functions)");
|
|
2408
|
+
}
|
|
2409
|
+
if (classes2.length > 0) {
|
|
2410
|
+
sections.push(" - [Classes](#classes)");
|
|
2411
|
+
}
|
|
2412
|
+
if (interfaces2.length > 0) {
|
|
2413
|
+
sections.push(" - [Interfaces](#interfaces)");
|
|
2414
|
+
}
|
|
2415
|
+
if (types2.length > 0) {
|
|
2416
|
+
sections.push(" - [Types](#types)");
|
|
2417
|
+
}
|
|
2418
|
+
sections.push("");
|
|
2419
|
+
}
|
|
2420
|
+
sections.push("## Installation\n");
|
|
2421
|
+
sections.push("```bash");
|
|
2422
|
+
sections.push(`npm install ${pkg.name}`);
|
|
2423
|
+
sections.push("```\n");
|
|
2424
|
+
sections.push("Or with yarn:\n");
|
|
2425
|
+
sections.push("```bash");
|
|
2426
|
+
sections.push(`yarn add ${pkg.name}`);
|
|
2427
|
+
sections.push("```\n");
|
|
2428
|
+
if (readme?.quickStart) {
|
|
2429
|
+
sections.push("## Quick Start\n");
|
|
2430
|
+
sections.push(readme.quickStart);
|
|
2431
|
+
sections.push("");
|
|
2432
|
+
}
|
|
2433
|
+
sections.push("## API Reference\n");
|
|
2434
|
+
const functions = api.filter((e) => e.kind === "function");
|
|
2435
|
+
const classes = api.filter((e) => e.kind === "class");
|
|
2436
|
+
const interfaces = api.filter((e) => e.kind === "interface");
|
|
2437
|
+
const types = api.filter((e) => e.kind === "type");
|
|
2438
|
+
const enums = api.filter((e) => e.kind === "enum");
|
|
2439
|
+
const constants = api.filter((e) => e.kind === "constant");
|
|
2440
|
+
if (functions.length > 0) {
|
|
2441
|
+
sections.push("### Functions\n");
|
|
2442
|
+
for (const fn of functions) {
|
|
2443
|
+
sections.push(formatFunctionMd(fn, { includeParamTables, repositoryUrl }));
|
|
2444
|
+
sections.push("");
|
|
2445
|
+
sections.push("---\n");
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
if (classes.length > 0) {
|
|
2449
|
+
sections.push("### Classes\n");
|
|
2450
|
+
for (const cls of classes) {
|
|
2451
|
+
sections.push(formatClassMd(cls));
|
|
2452
|
+
sections.push("");
|
|
2453
|
+
sections.push("---\n");
|
|
2454
|
+
}
|
|
2455
|
+
}
|
|
2456
|
+
if (interfaces.length > 0) {
|
|
2457
|
+
sections.push("### Interfaces\n");
|
|
2458
|
+
for (const iface of interfaces) {
|
|
2459
|
+
sections.push(formatInterfaceMd(iface));
|
|
2460
|
+
sections.push("");
|
|
2461
|
+
sections.push("---\n");
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
if (types.length > 0) {
|
|
2465
|
+
sections.push("### Types\n");
|
|
2466
|
+
for (const type of types) {
|
|
2467
|
+
sections.push(formatTypeMd(type));
|
|
2468
|
+
sections.push("");
|
|
2469
|
+
}
|
|
2470
|
+
}
|
|
2471
|
+
if (enums.length > 0) {
|
|
2472
|
+
sections.push("### Enums\n");
|
|
2473
|
+
for (const enumEntry of enums) {
|
|
2474
|
+
sections.push(formatEnumMd(enumEntry));
|
|
2475
|
+
sections.push("");
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
if (constants.length > 0) {
|
|
2479
|
+
sections.push("### Constants\n");
|
|
2480
|
+
for (const constant of constants) {
|
|
2481
|
+
sections.push(formatConstantMd(constant));
|
|
2482
|
+
}
|
|
2483
|
+
sections.push("");
|
|
2484
|
+
}
|
|
2485
|
+
const output = sections.join("\n");
|
|
2486
|
+
context.tokenCount = countTokens(output);
|
|
2487
|
+
return output;
|
|
2488
|
+
}
|
|
2489
|
+
function formatFunctionMd(fn, options) {
|
|
2490
|
+
const lines = [];
|
|
2491
|
+
const anchor = fn.name.toLowerCase();
|
|
2492
|
+
lines.push(`<a name="${anchor}"></a>`);
|
|
2493
|
+
lines.push(`#### \`${fn.name}()\``);
|
|
2494
|
+
if (fn.deprecated) {
|
|
2495
|
+
lines.push(`
|
|
2496
|
+
> \u26A0\uFE0F **Deprecated:** ${typeof fn.deprecated === "string" ? fn.deprecated : "This function is deprecated."}
|
|
2497
|
+
`);
|
|
2498
|
+
}
|
|
2499
|
+
if (fn.description) {
|
|
2500
|
+
lines.push(`
|
|
2501
|
+
${fn.description}
|
|
2502
|
+
`);
|
|
2503
|
+
}
|
|
2504
|
+
lines.push("**Signature:**\n");
|
|
2505
|
+
lines.push("```typescript");
|
|
2506
|
+
lines.push(fn.signature);
|
|
2507
|
+
lines.push("```\n");
|
|
2508
|
+
if (fn.params && fn.params.length > 0 && options.includeParamTables) {
|
|
2509
|
+
lines.push("**Parameters:**\n");
|
|
2510
|
+
lines.push("| Name | Type | Required | Description |");
|
|
2511
|
+
lines.push("|------|------|----------|-------------|");
|
|
2512
|
+
for (const param of fn.params) {
|
|
2513
|
+
const required = param.optional ? "No" : "Yes";
|
|
2514
|
+
const desc = param.description || "-";
|
|
2515
|
+
const defaultVal = param.defaultValue ? ` (default: \`${param.defaultValue}\`)` : "";
|
|
2516
|
+
lines.push(`| \`${param.name}\` | \`${param.type || "unknown"}\` | ${required} | ${desc}${defaultVal} |`);
|
|
2517
|
+
}
|
|
2518
|
+
lines.push("");
|
|
2519
|
+
}
|
|
2520
|
+
if (fn.returns) {
|
|
2521
|
+
lines.push("**Returns:**\n");
|
|
2522
|
+
lines.push(`\`${fn.returns.type}\`${fn.returns.description ? ` - ${fn.returns.description}` : ""}
|
|
2523
|
+
`);
|
|
2524
|
+
}
|
|
2525
|
+
if (fn.examples && fn.examples.length > 0) {
|
|
2526
|
+
lines.push("**Example:**\n");
|
|
2527
|
+
for (const example of fn.examples) {
|
|
2528
|
+
if (example.includes("```")) {
|
|
2529
|
+
lines.push(example);
|
|
2530
|
+
} else {
|
|
2531
|
+
lines.push("```typescript");
|
|
2532
|
+
lines.push(example);
|
|
2533
|
+
lines.push("```");
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2536
|
+
lines.push("");
|
|
2537
|
+
}
|
|
2538
|
+
if (options.repositoryUrl && fn.sourceFile) {
|
|
2539
|
+
const sourceUrl = `${options.repositoryUrl}/blob/main/${fn.sourceFile}${fn.line ? `#L${fn.line}` : ""}`;
|
|
2540
|
+
lines.push(`[View source](${sourceUrl})
|
|
2541
|
+
`);
|
|
2542
|
+
}
|
|
2543
|
+
return lines.join("\n");
|
|
2544
|
+
}
|
|
2545
|
+
function formatClassMd(cls, options) {
|
|
2546
|
+
const lines = [];
|
|
2547
|
+
const anchor = cls.name.toLowerCase();
|
|
2548
|
+
lines.push(`<a name="${anchor}"></a>`);
|
|
2549
|
+
lines.push(`#### \`${cls.name}\``);
|
|
2550
|
+
if (cls.deprecated) {
|
|
2551
|
+
lines.push(`
|
|
2552
|
+
> \u26A0\uFE0F **Deprecated**
|
|
2553
|
+
`);
|
|
2554
|
+
}
|
|
2555
|
+
if (cls.description) {
|
|
2556
|
+
lines.push(`
|
|
2557
|
+
${cls.description}
|
|
2558
|
+
`);
|
|
2559
|
+
}
|
|
2560
|
+
if (cls.extends && cls.extends.length > 0) {
|
|
2561
|
+
lines.push(`**Extends:** ${cls.extends.map((e) => `\`${e}\``).join(", ")}
|
|
2562
|
+
`);
|
|
2563
|
+
}
|
|
2564
|
+
if (cls.implements && cls.implements.length > 0) {
|
|
2565
|
+
lines.push(`**Implements:** ${cls.implements.map((e) => `\`${e}\``).join(", ")}
|
|
2566
|
+
`);
|
|
2567
|
+
}
|
|
2568
|
+
const constructor = cls.methods?.find((m) => m.name === "constructor");
|
|
2569
|
+
if (constructor) {
|
|
2570
|
+
lines.push("**Constructor:**\n");
|
|
2571
|
+
lines.push("```typescript");
|
|
2572
|
+
lines.push(`new ${cls.name}(${constructor.params?.map((p) => `${p.name}: ${p.type}`).join(", ") || ""})`);
|
|
2573
|
+
lines.push("```\n");
|
|
2574
|
+
}
|
|
2575
|
+
if (cls.properties && cls.properties.length > 0) {
|
|
2576
|
+
lines.push("**Properties:**\n");
|
|
2577
|
+
lines.push("| Name | Type | Description |");
|
|
2578
|
+
lines.push("|------|------|-------------|");
|
|
2579
|
+
for (const prop of cls.properties) {
|
|
2580
|
+
const type = prop.signature.split(":").slice(1).join(":").trim() || "unknown";
|
|
2581
|
+
lines.push(`| \`${prop.name}\` | \`${type}\` | ${prop.description || "-"} |`);
|
|
2582
|
+
}
|
|
2583
|
+
lines.push("");
|
|
2584
|
+
}
|
|
2585
|
+
const methods = cls.methods?.filter((m) => m.name !== "constructor") || [];
|
|
2586
|
+
if (methods.length > 0) {
|
|
2587
|
+
lines.push("**Methods:**\n");
|
|
2588
|
+
lines.push("| Method | Returns | Description |");
|
|
2589
|
+
lines.push("|--------|---------|-------------|");
|
|
2590
|
+
for (const method of methods) {
|
|
2591
|
+
const params = method.params?.map((p) => `${p.name}`).join(", ") || "";
|
|
2592
|
+
const returnType = method.returns?.type || "void";
|
|
2593
|
+
lines.push(`| \`${method.name}(${params})\` | \`${returnType}\` | ${method.description || "-"} |`);
|
|
2594
|
+
}
|
|
2595
|
+
lines.push("");
|
|
2596
|
+
}
|
|
2597
|
+
return lines.join("\n");
|
|
2598
|
+
}
|
|
2599
|
+
function formatInterfaceMd(iface) {
|
|
2600
|
+
const lines = [];
|
|
2601
|
+
lines.push(`#### \`${iface.name}\``);
|
|
2602
|
+
if (iface.description) {
|
|
2603
|
+
lines.push(`
|
|
2604
|
+
${iface.description}
|
|
2605
|
+
`);
|
|
2606
|
+
}
|
|
2607
|
+
if (iface.extends && iface.extends.length > 0) {
|
|
2608
|
+
lines.push(`**Extends:** ${iface.extends.map((e) => `\`${e}\``).join(", ")}
|
|
2609
|
+
`);
|
|
2610
|
+
}
|
|
2611
|
+
if (iface.properties && iface.properties.length > 0) {
|
|
2612
|
+
lines.push("| Property | Type | Description |");
|
|
2613
|
+
lines.push("|----------|------|-------------|");
|
|
2614
|
+
for (const prop of iface.properties) {
|
|
2615
|
+
const type = prop.signature.split(":").slice(1).join(":").trim() || "unknown";
|
|
2616
|
+
lines.push(`| \`${prop.name}\` | \`${type}\` | ${prop.description || "-"} |`);
|
|
2617
|
+
}
|
|
2618
|
+
lines.push("");
|
|
2619
|
+
}
|
|
2620
|
+
lines.push("<details>");
|
|
2621
|
+
lines.push("<summary>Full Definition</summary>\n");
|
|
2622
|
+
lines.push("```typescript");
|
|
2623
|
+
lines.push(`interface ${iface.name} {`);
|
|
2624
|
+
if (iface.properties) {
|
|
2625
|
+
for (const prop of iface.properties) {
|
|
2626
|
+
lines.push(` ${prop.signature};`);
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
2629
|
+
if (iface.methods) {
|
|
2630
|
+
for (const method of iface.methods) {
|
|
2631
|
+
lines.push(` ${method.signature};`);
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
lines.push("}");
|
|
2635
|
+
lines.push("```");
|
|
2636
|
+
lines.push("</details>");
|
|
2637
|
+
return lines.join("\n");
|
|
2638
|
+
}
|
|
2639
|
+
function formatTypeMd(type) {
|
|
2640
|
+
const lines = [];
|
|
2641
|
+
lines.push(`#### \`${type.name}\``);
|
|
2642
|
+
if (type.description) {
|
|
2643
|
+
lines.push(`
|
|
2644
|
+
${type.description}
|
|
2645
|
+
`);
|
|
2646
|
+
}
|
|
2647
|
+
lines.push("```typescript");
|
|
2648
|
+
lines.push(type.signature);
|
|
2649
|
+
lines.push("```");
|
|
2650
|
+
return lines.join("\n");
|
|
2651
|
+
}
|
|
2652
|
+
function formatEnumMd(enumEntry) {
|
|
2653
|
+
const lines = [];
|
|
2654
|
+
lines.push(`#### \`${enumEntry.name}\``);
|
|
2655
|
+
if (enumEntry.description) {
|
|
2656
|
+
lines.push(`
|
|
2657
|
+
${enumEntry.description}
|
|
2658
|
+
`);
|
|
2659
|
+
}
|
|
2660
|
+
if (enumEntry.members && enumEntry.members.length > 0) {
|
|
2661
|
+
lines.push("| Member | Value |");
|
|
2662
|
+
lines.push("|--------|-------|");
|
|
2663
|
+
for (const member of enumEntry.members) {
|
|
2664
|
+
const value = member.value !== void 0 ? `\`${JSON.stringify(member.value)}\`` : "-";
|
|
2665
|
+
lines.push(`| \`${member.name}\` | ${value} |`);
|
|
2666
|
+
}
|
|
2667
|
+
lines.push("");
|
|
2668
|
+
}
|
|
2669
|
+
return lines.join("\n");
|
|
2670
|
+
}
|
|
2671
|
+
function formatConstantMd(constant) {
|
|
2672
|
+
const lines = [];
|
|
2673
|
+
lines.push(`- **\`${constant.name}\`**: \`${constant.signature.split(":").slice(1).join(":").trim() || "unknown"}\``);
|
|
2674
|
+
if (constant.description) {
|
|
2675
|
+
lines.push(` - ${constant.description}`);
|
|
2676
|
+
}
|
|
2677
|
+
return lines.join("\n");
|
|
2678
|
+
}
|
|
2679
|
+
|
|
2680
|
+
// src/plugins/core/markdown-output.ts
|
|
2681
|
+
var markdownOutputPlugin = {
|
|
2682
|
+
name: "markdown-output",
|
|
2683
|
+
version: "1.0.0",
|
|
2684
|
+
category: "output",
|
|
2685
|
+
install(kernel) {
|
|
2686
|
+
kernel.on("output:start", async (context) => {
|
|
2687
|
+
if (!context.options.formats?.includes("markdown")) {
|
|
2688
|
+
return;
|
|
2689
|
+
}
|
|
2690
|
+
try {
|
|
2691
|
+
const repositoryUrl = context.package.repository?.url?.replace(/^git\+/, "").replace(/\.git$/, "");
|
|
2692
|
+
const output = generateMarkdown(context, {
|
|
2693
|
+
includeToc: true,
|
|
2694
|
+
includeParamTables: true,
|
|
2695
|
+
includeBadges: true,
|
|
2696
|
+
repositoryUrl
|
|
2697
|
+
});
|
|
2698
|
+
context.outputs.set("markdown", output);
|
|
2699
|
+
} catch (error) {
|
|
2700
|
+
context.errors.push(error instanceof Error ? error : new Error(String(error)));
|
|
2701
|
+
}
|
|
2702
|
+
});
|
|
2703
|
+
}
|
|
2704
|
+
};
|
|
2705
|
+
|
|
2706
|
+
// src/outputs/json.ts
|
|
2707
|
+
function generateJson(context, options = {}) {
|
|
2708
|
+
const { pretty = true, includeEmpty = false, includeSourceLocations = true } = options;
|
|
2709
|
+
const output = generateJsonObject(context, { includeEmpty, includeSourceLocations });
|
|
2710
|
+
return pretty ? JSON.stringify(output, null, 2) : JSON.stringify(output);
|
|
2711
|
+
}
|
|
2712
|
+
function generateJsonObject(context, options = {}) {
|
|
2713
|
+
const { includeEmpty = false, includeSourceLocations = true } = options;
|
|
2714
|
+
const { package: pkg, api } = context;
|
|
2715
|
+
const functions = api.filter((e) => e.kind === "function");
|
|
2716
|
+
const classes = api.filter((e) => e.kind === "class");
|
|
2717
|
+
const interfaces = api.filter((e) => e.kind === "interface");
|
|
2718
|
+
const types = api.filter((e) => e.kind === "type");
|
|
2719
|
+
const enums = api.filter((e) => e.kind === "enum");
|
|
2720
|
+
const constants = api.filter((e) => e.kind === "constant");
|
|
2721
|
+
const documented = api.filter((e) => e.description).length;
|
|
2722
|
+
const withExamples = api.filter((e) => e.examples && e.examples.length > 0).length;
|
|
2723
|
+
const output = {
|
|
2724
|
+
$schema: "https://npm-llms.oxog.dev/schema/v1.json",
|
|
2725
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2726
|
+
package: {
|
|
2727
|
+
name: pkg.name,
|
|
2728
|
+
version: pkg.version,
|
|
2729
|
+
description: pkg.description,
|
|
2730
|
+
license: pkg.license,
|
|
2731
|
+
homepage: pkg.homepage,
|
|
2732
|
+
repository: pkg.repository?.url,
|
|
2733
|
+
keywords: pkg.keywords
|
|
2734
|
+
},
|
|
2735
|
+
api: {
|
|
2736
|
+
functions: functions.map((fn) => transformFunction(fn, includeSourceLocations)),
|
|
2737
|
+
classes: classes.map((cls) => transformClass(cls, includeSourceLocations)),
|
|
2738
|
+
interfaces: interfaces.map(transformInterface),
|
|
2739
|
+
types: types.map(transformType),
|
|
2740
|
+
enums: enums.map(transformEnum),
|
|
2741
|
+
constants: constants.map(transformConstant)
|
|
2742
|
+
},
|
|
2743
|
+
stats: {
|
|
2744
|
+
totalExports: api.length,
|
|
2745
|
+
functions: functions.length,
|
|
2746
|
+
classes: classes.length,
|
|
2747
|
+
interfaces: interfaces.length,
|
|
2748
|
+
types: types.length,
|
|
2749
|
+
enums: enums.length,
|
|
2750
|
+
constants: constants.length,
|
|
2751
|
+
documented,
|
|
2752
|
+
withExamples
|
|
2753
|
+
}
|
|
2754
|
+
};
|
|
2755
|
+
if (!includeEmpty) {
|
|
2756
|
+
const apiObj = output.api;
|
|
2757
|
+
for (const [key, value] of Object.entries(apiObj)) {
|
|
2758
|
+
if (Array.isArray(value) && value.length === 0) {
|
|
2759
|
+
delete apiObj[key];
|
|
2760
|
+
}
|
|
2761
|
+
}
|
|
2762
|
+
}
|
|
2763
|
+
return output;
|
|
2764
|
+
}
|
|
2765
|
+
function transformFunction(entry, includeSource) {
|
|
2766
|
+
const result = {
|
|
2767
|
+
name: entry.name,
|
|
2768
|
+
signature: entry.signature,
|
|
2769
|
+
description: entry.description,
|
|
2770
|
+
params: (entry.params || []).map((p) => ({
|
|
2771
|
+
name: p.name,
|
|
2772
|
+
type: p.type || "unknown",
|
|
2773
|
+
optional: p.optional ?? false,
|
|
2774
|
+
defaultValue: p.defaultValue,
|
|
2775
|
+
description: p.description
|
|
2776
|
+
})),
|
|
2777
|
+
returns: entry.returns ? {
|
|
2778
|
+
type: entry.returns.type,
|
|
2779
|
+
description: entry.returns.description
|
|
2780
|
+
} : void 0,
|
|
2781
|
+
examples: entry.examples || [],
|
|
2782
|
+
deprecated: entry.deprecated,
|
|
2783
|
+
since: entry.since,
|
|
2784
|
+
see: entry.see
|
|
2785
|
+
};
|
|
2786
|
+
if (includeSource) {
|
|
2787
|
+
result.sourceFile = entry.sourceFile;
|
|
2788
|
+
result.line = entry.line;
|
|
2789
|
+
}
|
|
2790
|
+
return result;
|
|
2791
|
+
}
|
|
2792
|
+
function transformClass(entry, includeSource) {
|
|
2793
|
+
const result = {
|
|
2794
|
+
name: entry.name,
|
|
2795
|
+
signature: entry.signature,
|
|
2796
|
+
description: entry.description,
|
|
2797
|
+
extends: entry.extends,
|
|
2798
|
+
implements: entry.implements,
|
|
2799
|
+
typeParams: entry.typeParams,
|
|
2800
|
+
properties: (entry.properties || []).map((p) => ({
|
|
2801
|
+
name: p.name,
|
|
2802
|
+
type: p.signature.split(":").slice(1).join(":").trim() || "unknown",
|
|
2803
|
+
description: p.description
|
|
2804
|
+
})),
|
|
2805
|
+
methods: (entry.methods || []).map((m) => transformFunction(m, includeSource)),
|
|
2806
|
+
deprecated: entry.deprecated
|
|
2807
|
+
};
|
|
2808
|
+
if (includeSource) {
|
|
2809
|
+
result.sourceFile = entry.sourceFile;
|
|
2810
|
+
}
|
|
2811
|
+
return result;
|
|
2812
|
+
}
|
|
2813
|
+
function transformInterface(entry) {
|
|
2814
|
+
return {
|
|
2815
|
+
name: entry.name,
|
|
2816
|
+
signature: entry.signature,
|
|
2817
|
+
description: entry.description,
|
|
2818
|
+
extends: entry.extends,
|
|
2819
|
+
typeParams: entry.typeParams,
|
|
2820
|
+
properties: (entry.properties || []).map((p) => ({
|
|
2821
|
+
name: p.name,
|
|
2822
|
+
type: p.signature.split(":").slice(1).join(":").trim() || "unknown",
|
|
2823
|
+
optional: p.signature.includes("?:"),
|
|
2824
|
+
description: p.description
|
|
2825
|
+
})),
|
|
2826
|
+
methods: (entry.methods || []).map((m) => ({
|
|
2827
|
+
name: m.name,
|
|
2828
|
+
signature: m.signature,
|
|
2829
|
+
description: m.description
|
|
2830
|
+
})),
|
|
2831
|
+
deprecated: entry.deprecated
|
|
2832
|
+
};
|
|
2833
|
+
}
|
|
2834
|
+
function transformType(entry) {
|
|
2835
|
+
return {
|
|
2836
|
+
name: entry.name,
|
|
2837
|
+
signature: entry.signature,
|
|
2838
|
+
description: entry.description,
|
|
2839
|
+
typeParams: entry.typeParams,
|
|
2840
|
+
deprecated: entry.deprecated
|
|
2841
|
+
};
|
|
2842
|
+
}
|
|
2843
|
+
function transformEnum(entry) {
|
|
2844
|
+
return {
|
|
2845
|
+
name: entry.name,
|
|
2846
|
+
description: entry.description,
|
|
2847
|
+
members: entry.members || []
|
|
2848
|
+
};
|
|
2849
|
+
}
|
|
2850
|
+
function transformConstant(entry) {
|
|
2851
|
+
return {
|
|
2852
|
+
name: entry.name,
|
|
2853
|
+
type: entry.signature.split(":").slice(1).join(":").trim() || "unknown",
|
|
2854
|
+
description: entry.description
|
|
2855
|
+
};
|
|
2856
|
+
}
|
|
2857
|
+
|
|
2858
|
+
// src/plugins/core/json-output.ts
|
|
2859
|
+
var jsonOutputPlugin = {
|
|
2860
|
+
name: "json-output",
|
|
2861
|
+
version: "1.0.0",
|
|
2862
|
+
category: "output",
|
|
2863
|
+
install(kernel) {
|
|
2864
|
+
kernel.on("output:start", async (context) => {
|
|
2865
|
+
if (!context.options.formats?.includes("json")) {
|
|
2866
|
+
return;
|
|
2867
|
+
}
|
|
2868
|
+
try {
|
|
2869
|
+
const output = generateJson(context, {
|
|
2870
|
+
pretty: true,
|
|
2871
|
+
includeEmpty: false,
|
|
2872
|
+
includeSourceLocations: true
|
|
2873
|
+
});
|
|
2874
|
+
context.outputs.set("json", output);
|
|
2875
|
+
} catch (error) {
|
|
2876
|
+
context.errors.push(error instanceof Error ? error : new Error(String(error)));
|
|
2877
|
+
}
|
|
2878
|
+
});
|
|
2879
|
+
}
|
|
2880
|
+
};
|
|
2881
|
+
|
|
2882
|
+
// src/plugins/core/index.ts
|
|
2883
|
+
var coreParserPlugins = [
|
|
2884
|
+
typesResolverPlugin,
|
|
2885
|
+
dtsParserPlugin,
|
|
2886
|
+
tsSourceParserPlugin,
|
|
2887
|
+
readmeParserPlugin
|
|
2888
|
+
];
|
|
2889
|
+
var coreOutputPlugins = [
|
|
2890
|
+
llmsOutputPlugin,
|
|
2891
|
+
llmsFullOutputPlugin,
|
|
2892
|
+
markdownOutputPlugin,
|
|
2893
|
+
jsonOutputPlugin
|
|
2894
|
+
];
|
|
2895
|
+
var corePlugins = [
|
|
2896
|
+
...coreParserPlugins,
|
|
2897
|
+
...coreOutputPlugins
|
|
2898
|
+
];
|
|
2899
|
+
|
|
2900
|
+
// src/plugins/optional/ai-base.ts
|
|
2901
|
+
var DEFAULT_OPTIONS = {
|
|
2902
|
+
tasks: ["descriptions", "examples"],
|
|
2903
|
+
maxTokensPerRequest: 1e3,
|
|
2904
|
+
temperature: 0.3,
|
|
2905
|
+
batchSize: 5,
|
|
2906
|
+
skipExisting: true
|
|
2907
|
+
};
|
|
2908
|
+
var SYSTEM_PROMPTS = {
|
|
2909
|
+
descriptions: `You are a technical documentation expert. Generate concise, clear descriptions for API entries.
|
|
2910
|
+
Rules:
|
|
2911
|
+
- Be specific about what the function/class does
|
|
2912
|
+
- Mention important parameters and return values
|
|
2913
|
+
- Keep descriptions under 100 words
|
|
2914
|
+
- Use present tense ("Returns..." not "Will return...")
|
|
2915
|
+
- Don't include example code in descriptions`,
|
|
2916
|
+
examples: `You are a technical documentation expert. Generate practical usage examples for API entries.
|
|
2917
|
+
Rules:
|
|
2918
|
+
- Create 1-2 realistic examples
|
|
2919
|
+
- Show common use cases
|
|
2920
|
+
- Include proper TypeScript/JavaScript syntax
|
|
2921
|
+
- Keep examples concise but complete
|
|
2922
|
+
- Use meaningful variable names`,
|
|
2923
|
+
summary: `You are a technical documentation expert. Generate a summary of an npm package.
|
|
2924
|
+
Rules:
|
|
2925
|
+
- Describe the main purpose and use cases
|
|
2926
|
+
- Mention key features
|
|
2927
|
+
- Keep it under 150 words
|
|
2928
|
+
- Be objective and informative`,
|
|
2929
|
+
params: `You are a technical documentation expert. Generate parameter descriptions.
|
|
2930
|
+
Rules:
|
|
2931
|
+
- Be specific about expected values
|
|
2932
|
+
- Mention valid ranges or formats
|
|
2933
|
+
- Note if parameter is optional
|
|
2934
|
+
- Keep each description under 20 words`,
|
|
2935
|
+
returns: `You are a technical documentation expert. Generate return value descriptions.
|
|
2936
|
+
Rules:
|
|
2937
|
+
- Describe what is returned
|
|
2938
|
+
- Mention possible values or states
|
|
2939
|
+
- Keep descriptions under 30 words`
|
|
2940
|
+
};
|
|
2941
|
+
function generateEntryPrompt(entry, task) {
|
|
2942
|
+
const parts = [`${entry.kind}: ${entry.name}`, `Signature: ${entry.signature}`];
|
|
2943
|
+
if (entry.description) {
|
|
2944
|
+
parts.push(`Current description: ${entry.description}`);
|
|
2945
|
+
}
|
|
2946
|
+
if (entry.params && entry.params.length > 0) {
|
|
2947
|
+
const paramList = entry.params.map((p) => `- ${p.name}: ${p.type || "unknown"}`).join("\n");
|
|
2948
|
+
parts.push(`Parameters:
|
|
2949
|
+
${paramList}`);
|
|
2950
|
+
}
|
|
2951
|
+
if (entry.returns) {
|
|
2952
|
+
parts.push(`Returns: ${entry.returns.type}`);
|
|
2953
|
+
}
|
|
2954
|
+
switch (task) {
|
|
2955
|
+
case "descriptions":
|
|
2956
|
+
parts.push("\nGenerate a clear description for this API entry:");
|
|
2957
|
+
break;
|
|
2958
|
+
case "examples":
|
|
2959
|
+
parts.push("\nGenerate a practical usage example:");
|
|
2960
|
+
break;
|
|
2961
|
+
}
|
|
2962
|
+
return parts.join("\n");
|
|
2963
|
+
}
|
|
2964
|
+
function generateSummaryPrompt(context) {
|
|
2965
|
+
const { package: pkg, api } = context;
|
|
2966
|
+
const functionCount = api.filter((e) => e.kind === "function").length;
|
|
2967
|
+
const classCount = api.filter((e) => e.kind === "class").length;
|
|
2968
|
+
const typeCount = api.filter((e) => e.kind === "type" || e.kind === "interface").length;
|
|
2969
|
+
return `
|
|
2970
|
+
Package: ${pkg.name}@${pkg.version}
|
|
2971
|
+
Description: ${pkg.description || "No description available"}
|
|
2972
|
+
Functions: ${functionCount}
|
|
2973
|
+
Classes: ${classCount}
|
|
2974
|
+
Types: ${typeCount}
|
|
2975
|
+
|
|
2976
|
+
Top exports:
|
|
2977
|
+
${api.slice(0, 10).map((e) => `- ${e.kind} ${e.name}`).join("\n")}
|
|
2978
|
+
|
|
2979
|
+
Generate a summary of this package that explains its purpose and main features:
|
|
2980
|
+
`.trim();
|
|
2981
|
+
}
|
|
2982
|
+
function parseDescriptionResponse(response) {
|
|
2983
|
+
return response.trim().replace(/^Description:\s*/i, "").replace(/^["']|["']$/g, "");
|
|
2984
|
+
}
|
|
2985
|
+
function parseExampleResponse(response) {
|
|
2986
|
+
const examples = [];
|
|
2987
|
+
const codeBlockRegex = /```(?:\w+)?\n?([\s\S]*?)```/g;
|
|
2988
|
+
let match;
|
|
2989
|
+
while ((match = codeBlockRegex.exec(response)) !== null) {
|
|
2990
|
+
if (match[1]?.trim()) {
|
|
2991
|
+
examples.push(match[1].trim());
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
if (examples.length === 0 && response.trim()) {
|
|
2995
|
+
examples.push(response.trim());
|
|
2996
|
+
}
|
|
2997
|
+
return examples;
|
|
2998
|
+
}
|
|
2999
|
+
function createAIEnrichmentPlugin(provider, options = {}) {
|
|
3000
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
3001
|
+
return {
|
|
3002
|
+
name: `ai-enrichment-${provider.name}`,
|
|
3003
|
+
version: "1.0.0",
|
|
3004
|
+
category: "ai",
|
|
3005
|
+
install(kernel) {
|
|
3006
|
+
kernel.on("ai:enrich", async (context) => {
|
|
3007
|
+
if (!provider.isAvailable()) {
|
|
3008
|
+
throw new AIError(`AI provider ${provider.name} is not available`, provider.name);
|
|
3009
|
+
}
|
|
3010
|
+
if (opts.tasks.includes("descriptions") || opts.tasks.includes("examples")) {
|
|
3011
|
+
await enrichEntries(context, provider, opts);
|
|
3012
|
+
}
|
|
3013
|
+
if (opts.tasks.includes("summary")) {
|
|
3014
|
+
await enrichSummary(context, provider, opts);
|
|
3015
|
+
}
|
|
3016
|
+
});
|
|
3017
|
+
}
|
|
3018
|
+
};
|
|
3019
|
+
}
|
|
3020
|
+
async function enrichEntries(context, provider, options) {
|
|
3021
|
+
const { api } = context;
|
|
3022
|
+
const { tasks, batchSize, skipExisting, maxTokensPerRequest, temperature } = options;
|
|
3023
|
+
const entriesToEnrich = api.filter((entry) => {
|
|
3024
|
+
if (skipExisting) {
|
|
3025
|
+
if (tasks.includes("descriptions") && entry.description) return false;
|
|
3026
|
+
if (tasks.includes("examples") && entry.examples && entry.examples.length > 0) return false;
|
|
3027
|
+
}
|
|
3028
|
+
return entry.kind === "function" || entry.kind === "class";
|
|
3029
|
+
});
|
|
3030
|
+
for (let i = 0; i < entriesToEnrich.length; i += batchSize) {
|
|
3031
|
+
const batch = entriesToEnrich.slice(i, i + batchSize);
|
|
3032
|
+
await Promise.all(
|
|
3033
|
+
batch.map(async (entry) => {
|
|
3034
|
+
try {
|
|
3035
|
+
if (tasks.includes("descriptions") && (!skipExisting || !entry.description)) {
|
|
3036
|
+
const prompt = generateEntryPrompt(entry, "descriptions");
|
|
3037
|
+
const response = await provider.complete(prompt, {
|
|
3038
|
+
maxTokens: maxTokensPerRequest,
|
|
3039
|
+
temperature,
|
|
3040
|
+
systemPrompt: SYSTEM_PROMPTS.descriptions
|
|
3041
|
+
});
|
|
3042
|
+
entry.description = parseDescriptionResponse(response);
|
|
3043
|
+
}
|
|
3044
|
+
if (tasks.includes("examples") && (!skipExisting || !entry.examples?.length)) {
|
|
3045
|
+
const prompt = generateEntryPrompt(entry, "examples");
|
|
3046
|
+
const response = await provider.complete(prompt, {
|
|
3047
|
+
maxTokens: maxTokensPerRequest,
|
|
3048
|
+
temperature,
|
|
3049
|
+
systemPrompt: SYSTEM_PROMPTS.examples
|
|
3050
|
+
});
|
|
3051
|
+
entry.examples = parseExampleResponse(response);
|
|
3052
|
+
}
|
|
3053
|
+
} catch (error) {
|
|
3054
|
+
context.errors.push(
|
|
3055
|
+
error instanceof Error ? error : new Error(`AI enrichment failed for ${entry.name}`)
|
|
3056
|
+
);
|
|
3057
|
+
}
|
|
3058
|
+
})
|
|
3059
|
+
);
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
async function enrichSummary(context, provider, options) {
|
|
3063
|
+
try {
|
|
3064
|
+
const prompt = generateSummaryPrompt(context);
|
|
3065
|
+
const response = await provider.complete(prompt, {
|
|
3066
|
+
maxTokens: options.maxTokensPerRequest,
|
|
3067
|
+
temperature: options.temperature,
|
|
3068
|
+
systemPrompt: SYSTEM_PROMPTS.summary
|
|
3069
|
+
});
|
|
3070
|
+
if (context.readme) {
|
|
3071
|
+
if (!context.readme.description) {
|
|
3072
|
+
context.readme.description = response.trim();
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
} catch (error) {
|
|
3076
|
+
context.errors.push(
|
|
3077
|
+
error instanceof Error ? error : new Error("AI summary generation failed")
|
|
3078
|
+
);
|
|
3079
|
+
}
|
|
3080
|
+
}
|
|
3081
|
+
function createSimpleProvider(name, completeFn, isAvailableFn = () => true) {
|
|
3082
|
+
return {
|
|
3083
|
+
name,
|
|
3084
|
+
isAvailable: isAvailableFn,
|
|
3085
|
+
complete: completeFn
|
|
3086
|
+
};
|
|
3087
|
+
}
|
|
3088
|
+
|
|
3089
|
+
// src/plugins/optional/claude-ai.ts
|
|
3090
|
+
var DEFAULT_CONFIG = {
|
|
3091
|
+
apiKey: "",
|
|
3092
|
+
model: "claude-haiku-4-5-20251001",
|
|
3093
|
+
baseUrl: "https://api.anthropic.com",
|
|
3094
|
+
timeout: 3e4
|
|
3095
|
+
};
|
|
3096
|
+
function createClaudeProvider(config = {}) {
|
|
3097
|
+
const cfg = {
|
|
3098
|
+
...DEFAULT_CONFIG,
|
|
3099
|
+
...config,
|
|
3100
|
+
apiKey: config.apiKey || process.env["ANTHROPIC_API_KEY"] || ""
|
|
3101
|
+
};
|
|
3102
|
+
return {
|
|
3103
|
+
name: "claude",
|
|
3104
|
+
isAvailable() {
|
|
3105
|
+
return !!cfg.apiKey;
|
|
3106
|
+
},
|
|
3107
|
+
async complete(prompt, options) {
|
|
3108
|
+
if (!cfg.apiKey) {
|
|
3109
|
+
throw new AIError("ANTHROPIC_API_KEY not set", "claude");
|
|
3110
|
+
}
|
|
3111
|
+
const requestBody = {
|
|
3112
|
+
model: cfg.model,
|
|
3113
|
+
max_tokens: options?.maxTokens ?? 1e3,
|
|
3114
|
+
messages: [{ role: "user", content: prompt }],
|
|
3115
|
+
temperature: options?.temperature ?? 0.3
|
|
3116
|
+
};
|
|
3117
|
+
if (options?.systemPrompt) {
|
|
3118
|
+
requestBody.system = options.systemPrompt;
|
|
3119
|
+
}
|
|
3120
|
+
const controller = new AbortController();
|
|
3121
|
+
const timeoutId = setTimeout(() => controller.abort(), cfg.timeout);
|
|
3122
|
+
try {
|
|
3123
|
+
const response = await fetch(`${cfg.baseUrl}/v1/messages`, {
|
|
3124
|
+
method: "POST",
|
|
3125
|
+
headers: {
|
|
3126
|
+
"Content-Type": "application/json",
|
|
3127
|
+
"x-api-key": cfg.apiKey,
|
|
3128
|
+
"anthropic-version": "2023-06-01"
|
|
3129
|
+
},
|
|
3130
|
+
body: JSON.stringify(requestBody),
|
|
3131
|
+
signal: controller.signal
|
|
3132
|
+
});
|
|
3133
|
+
clearTimeout(timeoutId);
|
|
3134
|
+
if (!response.ok) {
|
|
3135
|
+
const errorText = await response.text();
|
|
3136
|
+
throw new AIError(
|
|
3137
|
+
`Claude API error: ${response.status} ${response.statusText} - ${errorText}`,
|
|
3138
|
+
"claude"
|
|
3139
|
+
);
|
|
3140
|
+
}
|
|
3141
|
+
const data = await response.json();
|
|
3142
|
+
const textContent = data.content.find((c) => c.type === "text");
|
|
3143
|
+
if (!textContent) {
|
|
3144
|
+
throw new AIError("No text content in Claude response", "claude");
|
|
3145
|
+
}
|
|
3146
|
+
return textContent.text;
|
|
3147
|
+
} catch (error) {
|
|
3148
|
+
clearTimeout(timeoutId);
|
|
3149
|
+
if (error instanceof AIError) {
|
|
3150
|
+
throw error;
|
|
3151
|
+
}
|
|
3152
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
3153
|
+
throw new AIError("Claude API request timed out", "claude");
|
|
3154
|
+
}
|
|
3155
|
+
throw new AIError(
|
|
3156
|
+
`Claude API request failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
3157
|
+
"claude"
|
|
3158
|
+
);
|
|
3159
|
+
}
|
|
3160
|
+
}
|
|
3161
|
+
};
|
|
3162
|
+
}
|
|
3163
|
+
function createClaudePlugin(config = {}, enrichmentOptions) {
|
|
3164
|
+
const provider = createClaudeProvider(config);
|
|
3165
|
+
return createAIEnrichmentPlugin(provider, enrichmentOptions);
|
|
3166
|
+
}
|
|
3167
|
+
|
|
3168
|
+
// src/plugins/optional/openai-ai.ts
|
|
3169
|
+
var OPENAI_COMPATIBLE_PRESETS = {
|
|
3170
|
+
openai: {
|
|
3171
|
+
name: "openai",
|
|
3172
|
+
baseUrl: "https://api.openai.com/v1",
|
|
3173
|
+
envKey: "OPENAI_API_KEY",
|
|
3174
|
+
defaultModel: "gpt-4.1-nano"
|
|
3175
|
+
},
|
|
3176
|
+
xai: {
|
|
3177
|
+
name: "xai",
|
|
3178
|
+
baseUrl: "https://api.x.ai/v1",
|
|
3179
|
+
envKey: "XAI_API_KEY",
|
|
3180
|
+
defaultModel: "grok-3-mini-fast"
|
|
3181
|
+
},
|
|
3182
|
+
zai: {
|
|
3183
|
+
name: "zai",
|
|
3184
|
+
baseUrl: "https://api.z.ai/api/paas/v4",
|
|
3185
|
+
envKey: "ZAI_API_KEY",
|
|
3186
|
+
defaultModel: "glm-4.7"
|
|
3187
|
+
},
|
|
3188
|
+
together: {
|
|
3189
|
+
name: "together",
|
|
3190
|
+
baseUrl: "https://api.together.xyz/v1",
|
|
3191
|
+
envKey: "TOGETHER_API_KEY",
|
|
3192
|
+
defaultModel: "meta-llama/Llama-3.3-70B-Instruct-Turbo"
|
|
3193
|
+
},
|
|
3194
|
+
perplexity: {
|
|
3195
|
+
name: "perplexity",
|
|
3196
|
+
baseUrl: "https://api.perplexity.ai",
|
|
3197
|
+
envKey: "PERPLEXITY_API_KEY",
|
|
3198
|
+
defaultModel: "sonar-pro"
|
|
3199
|
+
},
|
|
3200
|
+
openrouter: {
|
|
3201
|
+
name: "openrouter",
|
|
3202
|
+
baseUrl: "https://openrouter.ai/api/v1",
|
|
3203
|
+
envKey: "OPENROUTER_API_KEY",
|
|
3204
|
+
defaultModel: "anthropic/claude-3.5-sonnet"
|
|
3205
|
+
},
|
|
3206
|
+
deepseek: {
|
|
3207
|
+
name: "deepseek",
|
|
3208
|
+
baseUrl: "https://api.deepseek.com/v1",
|
|
3209
|
+
envKey: "DEEPSEEK_API_KEY",
|
|
3210
|
+
defaultModel: "deepseek-chat"
|
|
3211
|
+
},
|
|
3212
|
+
mistral: {
|
|
3213
|
+
name: "mistral",
|
|
3214
|
+
baseUrl: "https://api.mistral.ai/v1",
|
|
3215
|
+
envKey: "MISTRAL_API_KEY",
|
|
3216
|
+
defaultModel: "mistral-small-latest"
|
|
3217
|
+
}
|
|
3218
|
+
};
|
|
3219
|
+
var DEFAULT_CONFIG2 = {
|
|
3220
|
+
apiKey: "",
|
|
3221
|
+
model: "gpt-4.1-nano",
|
|
3222
|
+
baseUrl: "https://api.openai.com/v1",
|
|
3223
|
+
timeout: 3e4,
|
|
3224
|
+
organization: void 0,
|
|
3225
|
+
preset: void 0
|
|
3226
|
+
};
|
|
3227
|
+
function createOpenAIProvider(config = {}) {
|
|
3228
|
+
const preset = config.preset ? OPENAI_COMPATIBLE_PRESETS[config.preset] : null;
|
|
3229
|
+
const cfg = {
|
|
3230
|
+
...DEFAULT_CONFIG2,
|
|
3231
|
+
...preset && {
|
|
3232
|
+
baseUrl: preset.baseUrl,
|
|
3233
|
+
model: preset.defaultModel
|
|
3234
|
+
},
|
|
3235
|
+
...config,
|
|
3236
|
+
apiKey: config.apiKey || process.env[preset?.envKey ?? "OPENAI_API_KEY"] || ""
|
|
3237
|
+
};
|
|
3238
|
+
const providerName = preset?.name ?? "openai";
|
|
3239
|
+
return {
|
|
3240
|
+
name: providerName,
|
|
3241
|
+
isAvailable() {
|
|
3242
|
+
return !!cfg.apiKey;
|
|
3243
|
+
},
|
|
3244
|
+
async complete(prompt, options) {
|
|
3245
|
+
if (!cfg.apiKey) {
|
|
3246
|
+
throw new AIError(`${preset?.envKey ?? "OPENAI_API_KEY"} not set`, providerName);
|
|
3247
|
+
}
|
|
3248
|
+
const messages = [];
|
|
3249
|
+
if (options?.systemPrompt) {
|
|
3250
|
+
messages.push({ role: "system", content: options.systemPrompt });
|
|
3251
|
+
}
|
|
3252
|
+
messages.push({ role: "user", content: prompt });
|
|
3253
|
+
const requestBody = {
|
|
3254
|
+
model: cfg.model,
|
|
3255
|
+
messages,
|
|
3256
|
+
max_tokens: options?.maxTokens ?? 1e3,
|
|
3257
|
+
temperature: options?.temperature ?? 0.3
|
|
3258
|
+
};
|
|
3259
|
+
const headers = {
|
|
3260
|
+
"Content-Type": "application/json",
|
|
3261
|
+
Authorization: `Bearer ${cfg.apiKey}`
|
|
3262
|
+
};
|
|
3263
|
+
if (cfg.organization) {
|
|
3264
|
+
headers["OpenAI-Organization"] = cfg.organization;
|
|
3265
|
+
}
|
|
3266
|
+
const controller = new AbortController();
|
|
3267
|
+
const timeoutId = setTimeout(() => controller.abort(), cfg.timeout);
|
|
3268
|
+
try {
|
|
3269
|
+
const response = await fetch(`${cfg.baseUrl}/chat/completions`, {
|
|
3270
|
+
method: "POST",
|
|
3271
|
+
headers,
|
|
3272
|
+
body: JSON.stringify(requestBody),
|
|
3273
|
+
signal: controller.signal
|
|
3274
|
+
});
|
|
3275
|
+
clearTimeout(timeoutId);
|
|
3276
|
+
if (!response.ok) {
|
|
3277
|
+
const errorText = await response.text();
|
|
3278
|
+
throw new AIError(
|
|
3279
|
+
`${providerName} API error: ${response.status} ${response.statusText} - ${errorText}`,
|
|
3280
|
+
providerName
|
|
3281
|
+
);
|
|
3282
|
+
}
|
|
3283
|
+
const data = await response.json();
|
|
3284
|
+
const choice = data.choices[0];
|
|
3285
|
+
if (!choice || !choice.message.content) {
|
|
3286
|
+
throw new AIError(`No content in ${providerName} response`, providerName);
|
|
3287
|
+
}
|
|
3288
|
+
return choice.message.content;
|
|
3289
|
+
} catch (error) {
|
|
3290
|
+
clearTimeout(timeoutId);
|
|
3291
|
+
if (error instanceof AIError) {
|
|
3292
|
+
throw error;
|
|
3293
|
+
}
|
|
3294
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
3295
|
+
throw new AIError(`${providerName} API request timed out`, providerName);
|
|
3296
|
+
}
|
|
3297
|
+
throw new AIError(
|
|
3298
|
+
`${providerName} API request failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
3299
|
+
providerName
|
|
3300
|
+
);
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
3303
|
+
};
|
|
3304
|
+
}
|
|
3305
|
+
function createOpenAIPlugin(config = {}, enrichmentOptions) {
|
|
3306
|
+
const provider = createOpenAIProvider(config);
|
|
3307
|
+
return createAIEnrichmentPlugin(provider, enrichmentOptions);
|
|
3308
|
+
}
|
|
3309
|
+
|
|
3310
|
+
// src/plugins/optional/gemini-ai.ts
|
|
3311
|
+
var DEFAULT_CONFIG3 = {
|
|
3312
|
+
model: "gemini-3-flash-preview",
|
|
3313
|
+
baseUrl: "https://generativelanguage.googleapis.com/v1beta",
|
|
3314
|
+
timeout: 6e4,
|
|
3315
|
+
maxTokens: 1024,
|
|
3316
|
+
temperature: 0.3
|
|
3317
|
+
};
|
|
3318
|
+
function createGeminiProvider(config = {}) {
|
|
3319
|
+
const cfg = {
|
|
3320
|
+
...DEFAULT_CONFIG3,
|
|
3321
|
+
...config,
|
|
3322
|
+
apiKey: config.apiKey || process.env["GOOGLE_API_KEY"] || process.env["GEMINI_API_KEY"] || ""
|
|
3323
|
+
};
|
|
3324
|
+
return {
|
|
3325
|
+
name: "gemini",
|
|
3326
|
+
isAvailable() {
|
|
3327
|
+
return !!cfg.apiKey;
|
|
3328
|
+
},
|
|
3329
|
+
async complete(prompt, options) {
|
|
3330
|
+
if (!cfg.apiKey) {
|
|
3331
|
+
throw new Error("Gemini API key not configured. Set GOOGLE_API_KEY or GEMINI_API_KEY environment variable.");
|
|
3332
|
+
}
|
|
3333
|
+
const url = `${cfg.baseUrl}/models/${cfg.model}:generateContent?key=${cfg.apiKey}`;
|
|
3334
|
+
const contents = [];
|
|
3335
|
+
if (options?.systemPrompt) {
|
|
3336
|
+
contents.push({
|
|
3337
|
+
role: "user",
|
|
3338
|
+
parts: [{ text: `System instruction: ${options.systemPrompt}
|
|
3339
|
+
|
|
3340
|
+
Now respond to the following:` }]
|
|
3341
|
+
});
|
|
3342
|
+
contents.push({
|
|
3343
|
+
role: "model",
|
|
3344
|
+
parts: [{ text: "Understood. I will follow those instructions." }]
|
|
3345
|
+
});
|
|
3346
|
+
}
|
|
3347
|
+
contents.push({
|
|
3348
|
+
role: "user",
|
|
3349
|
+
parts: [{ text: prompt }]
|
|
3350
|
+
});
|
|
3351
|
+
const body = {
|
|
3352
|
+
contents,
|
|
3353
|
+
generationConfig: {
|
|
3354
|
+
maxOutputTokens: options?.maxTokens ?? cfg.maxTokens,
|
|
3355
|
+
temperature: options?.temperature ?? cfg.temperature
|
|
3356
|
+
}
|
|
3357
|
+
};
|
|
3358
|
+
const controller = new AbortController();
|
|
3359
|
+
const timeoutId = setTimeout(() => controller.abort(), cfg.timeout);
|
|
3360
|
+
try {
|
|
3361
|
+
const response = await fetch(url, {
|
|
3362
|
+
method: "POST",
|
|
3363
|
+
headers: {
|
|
3364
|
+
"Content-Type": "application/json"
|
|
3365
|
+
},
|
|
3366
|
+
body: JSON.stringify(body),
|
|
3367
|
+
signal: controller.signal
|
|
3368
|
+
});
|
|
3369
|
+
clearTimeout(timeoutId);
|
|
3370
|
+
if (!response.ok) {
|
|
3371
|
+
const errorText = await response.text();
|
|
3372
|
+
throw new Error(`Gemini API error (${response.status}): ${errorText}`);
|
|
3373
|
+
}
|
|
3374
|
+
const data = await response.json();
|
|
3375
|
+
if (data.error) {
|
|
3376
|
+
throw new Error(`Gemini API error: ${data.error.message}`);
|
|
3377
|
+
}
|
|
3378
|
+
const text = data.candidates?.[0]?.content?.parts?.[0]?.text;
|
|
3379
|
+
if (!text) {
|
|
3380
|
+
throw new Error("No response generated from Gemini");
|
|
3381
|
+
}
|
|
3382
|
+
return text;
|
|
3383
|
+
} catch (error) {
|
|
3384
|
+
clearTimeout(timeoutId);
|
|
3385
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
3386
|
+
throw new Error(`Gemini request timed out after ${cfg.timeout}ms`);
|
|
3387
|
+
}
|
|
3388
|
+
throw error;
|
|
3389
|
+
}
|
|
3390
|
+
}
|
|
3391
|
+
};
|
|
3392
|
+
}
|
|
3393
|
+
function createGeminiPlugin(config = {}, enrichmentOptions) {
|
|
3394
|
+
const provider = createGeminiProvider(config);
|
|
3395
|
+
return createAIEnrichmentPlugin(provider, enrichmentOptions);
|
|
3396
|
+
}
|
|
3397
|
+
function checkGeminiAvailable() {
|
|
3398
|
+
return !!(process.env["GOOGLE_API_KEY"] || process.env["GEMINI_API_KEY"]);
|
|
3399
|
+
}
|
|
3400
|
+
|
|
3401
|
+
// src/plugins/optional/ollama-ai.ts
|
|
3402
|
+
var DEFAULT_CONFIG4 = {
|
|
3403
|
+
model: "llama3.2",
|
|
3404
|
+
baseUrl: "http://localhost:11434",
|
|
3405
|
+
timeout: 12e4
|
|
3406
|
+
// Longer timeout for local models
|
|
3407
|
+
};
|
|
3408
|
+
function createOllamaProvider(config = {}) {
|
|
3409
|
+
const cfg = { ...DEFAULT_CONFIG4, ...config };
|
|
3410
|
+
let available = null;
|
|
3411
|
+
return {
|
|
3412
|
+
name: "ollama",
|
|
3413
|
+
isAvailable() {
|
|
3414
|
+
if (available !== null) {
|
|
3415
|
+
return available;
|
|
3416
|
+
}
|
|
3417
|
+
return true;
|
|
3418
|
+
},
|
|
3419
|
+
async complete(prompt, options) {
|
|
3420
|
+
const requestBody = {
|
|
3421
|
+
model: cfg.model,
|
|
3422
|
+
prompt,
|
|
3423
|
+
stream: false,
|
|
3424
|
+
options: {
|
|
3425
|
+
temperature: options?.temperature ?? 0.3,
|
|
3426
|
+
num_predict: options?.maxTokens ?? 1e3
|
|
3427
|
+
}
|
|
3428
|
+
};
|
|
3429
|
+
if (options?.systemPrompt) {
|
|
3430
|
+
requestBody.system = options.systemPrompt;
|
|
3431
|
+
}
|
|
3432
|
+
const controller = new AbortController();
|
|
3433
|
+
const timeoutId = setTimeout(() => controller.abort(), cfg.timeout);
|
|
3434
|
+
try {
|
|
3435
|
+
const response = await fetch(`${cfg.baseUrl}/api/generate`, {
|
|
3436
|
+
method: "POST",
|
|
3437
|
+
headers: {
|
|
3438
|
+
"Content-Type": "application/json"
|
|
3439
|
+
},
|
|
3440
|
+
body: JSON.stringify(requestBody),
|
|
3441
|
+
signal: controller.signal
|
|
3442
|
+
});
|
|
3443
|
+
clearTimeout(timeoutId);
|
|
3444
|
+
if (!response.ok) {
|
|
3445
|
+
available = false;
|
|
3446
|
+
const errorText = await response.text();
|
|
3447
|
+
throw new AIError(
|
|
3448
|
+
`Ollama API error: ${response.status} ${response.statusText} - ${errorText}`,
|
|
3449
|
+
"ollama"
|
|
3450
|
+
);
|
|
3451
|
+
}
|
|
3452
|
+
available = true;
|
|
3453
|
+
const data = await response.json();
|
|
3454
|
+
if (!data.response) {
|
|
3455
|
+
throw new AIError("No response from Ollama", "ollama");
|
|
3456
|
+
}
|
|
3457
|
+
return data.response;
|
|
3458
|
+
} catch (error) {
|
|
3459
|
+
clearTimeout(timeoutId);
|
|
3460
|
+
if (error instanceof AIError) {
|
|
3461
|
+
throw error;
|
|
3462
|
+
}
|
|
3463
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
3464
|
+
throw new AIError("Ollama request timed out", "ollama");
|
|
3465
|
+
}
|
|
3466
|
+
if (error instanceof Error && error.message.includes("ECONNREFUSED")) {
|
|
3467
|
+
available = false;
|
|
3468
|
+
throw new AIError("Ollama is not running. Start it with: ollama serve", "ollama");
|
|
3469
|
+
}
|
|
3470
|
+
throw new AIError(
|
|
3471
|
+
`Ollama request failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
3472
|
+
"ollama"
|
|
3473
|
+
);
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3476
|
+
};
|
|
3477
|
+
}
|
|
3478
|
+
function createOllamaPlugin(config = {}, enrichmentOptions) {
|
|
3479
|
+
const provider = createOllamaProvider(config);
|
|
3480
|
+
return createAIEnrichmentPlugin(provider, enrichmentOptions);
|
|
3481
|
+
}
|
|
3482
|
+
async function checkOllamaAvailable(baseUrl = "http://localhost:11434") {
|
|
3483
|
+
try {
|
|
3484
|
+
const response = await fetch(`${baseUrl}/api/tags`, {
|
|
3485
|
+
method: "GET",
|
|
3486
|
+
signal: AbortSignal.timeout(5e3)
|
|
3487
|
+
});
|
|
3488
|
+
return response.ok;
|
|
3489
|
+
} catch {
|
|
3490
|
+
return false;
|
|
3491
|
+
}
|
|
3492
|
+
}
|
|
3493
|
+
async function listOllamaModels(baseUrl = "http://localhost:11434") {
|
|
3494
|
+
try {
|
|
3495
|
+
const response = await fetch(`${baseUrl}/api/tags`, {
|
|
3496
|
+
method: "GET",
|
|
3497
|
+
signal: AbortSignal.timeout(5e3)
|
|
3498
|
+
});
|
|
3499
|
+
if (!response.ok) {
|
|
3500
|
+
return [];
|
|
3501
|
+
}
|
|
3502
|
+
const data = await response.json();
|
|
3503
|
+
return data.models.map((m) => m.name);
|
|
3504
|
+
} catch {
|
|
3505
|
+
return [];
|
|
3506
|
+
}
|
|
3507
|
+
}
|
|
3508
|
+
|
|
3509
|
+
// src/plugins/optional/groq-ai.ts
|
|
3510
|
+
var DEFAULT_CONFIG5 = {
|
|
3511
|
+
apiKey: "",
|
|
3512
|
+
model: "llama-3.1-8b-instant",
|
|
3513
|
+
baseUrl: "https://api.groq.com/openai",
|
|
3514
|
+
timeout: 3e4
|
|
3515
|
+
};
|
|
3516
|
+
function createGroqProvider(config = {}) {
|
|
3517
|
+
const cfg = {
|
|
3518
|
+
...DEFAULT_CONFIG5,
|
|
3519
|
+
...config,
|
|
3520
|
+
apiKey: config.apiKey || process.env["GROQ_API_KEY"] || ""
|
|
3521
|
+
};
|
|
3522
|
+
return {
|
|
3523
|
+
name: "groq",
|
|
3524
|
+
isAvailable() {
|
|
3525
|
+
return !!cfg.apiKey;
|
|
3526
|
+
},
|
|
3527
|
+
async complete(prompt, options) {
|
|
3528
|
+
if (!cfg.apiKey) {
|
|
3529
|
+
throw new AIError("GROQ_API_KEY not set", "groq");
|
|
3530
|
+
}
|
|
3531
|
+
const messages = [];
|
|
3532
|
+
if (options?.systemPrompt) {
|
|
3533
|
+
messages.push({ role: "system", content: options.systemPrompt });
|
|
3534
|
+
}
|
|
3535
|
+
messages.push({ role: "user", content: prompt });
|
|
3536
|
+
const requestBody = {
|
|
3537
|
+
model: cfg.model,
|
|
3538
|
+
messages,
|
|
3539
|
+
max_tokens: options?.maxTokens ?? 1e3,
|
|
3540
|
+
temperature: options?.temperature ?? 0.3
|
|
3541
|
+
};
|
|
3542
|
+
const controller = new AbortController();
|
|
3543
|
+
const timeoutId = setTimeout(() => controller.abort(), cfg.timeout);
|
|
3544
|
+
try {
|
|
3545
|
+
const response = await fetch(`${cfg.baseUrl}/v1/chat/completions`, {
|
|
3546
|
+
method: "POST",
|
|
3547
|
+
headers: {
|
|
3548
|
+
"Content-Type": "application/json",
|
|
3549
|
+
Authorization: `Bearer ${cfg.apiKey}`
|
|
3550
|
+
},
|
|
3551
|
+
body: JSON.stringify(requestBody),
|
|
3552
|
+
signal: controller.signal
|
|
3553
|
+
});
|
|
3554
|
+
clearTimeout(timeoutId);
|
|
3555
|
+
if (!response.ok) {
|
|
3556
|
+
const errorText = await response.text();
|
|
3557
|
+
throw new AIError(
|
|
3558
|
+
`Groq API error: ${response.status} ${response.statusText} - ${errorText}`,
|
|
3559
|
+
"groq"
|
|
3560
|
+
);
|
|
3561
|
+
}
|
|
3562
|
+
const data = await response.json();
|
|
3563
|
+
const choice = data.choices[0];
|
|
3564
|
+
if (!choice || !choice.message.content) {
|
|
3565
|
+
throw new AIError("No content in Groq response", "groq");
|
|
3566
|
+
}
|
|
3567
|
+
return choice.message.content;
|
|
3568
|
+
} catch (error) {
|
|
3569
|
+
clearTimeout(timeoutId);
|
|
3570
|
+
if (error instanceof AIError) {
|
|
3571
|
+
throw error;
|
|
3572
|
+
}
|
|
3573
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
3574
|
+
throw new AIError("Groq API request timed out", "groq");
|
|
3575
|
+
}
|
|
3576
|
+
throw new AIError(
|
|
3577
|
+
`Groq API request failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
3578
|
+
"groq"
|
|
3579
|
+
);
|
|
3580
|
+
}
|
|
3581
|
+
}
|
|
3582
|
+
};
|
|
3583
|
+
}
|
|
3584
|
+
function createGroqPlugin(config = {}, enrichmentOptions) {
|
|
3585
|
+
const provider = createGroqProvider(config);
|
|
3586
|
+
return createAIEnrichmentPlugin(provider, enrichmentOptions);
|
|
3587
|
+
}
|
|
3588
|
+
|
|
3589
|
+
// src/kernel.ts
|
|
3590
|
+
function createKernel() {
|
|
3591
|
+
const plugins = /* @__PURE__ */ new Map();
|
|
3592
|
+
const eventHandlers = /* @__PURE__ */ new Map();
|
|
3593
|
+
const handlerToPlugin = /* @__PURE__ */ new Map();
|
|
3594
|
+
function handlePluginError(error, pluginName) {
|
|
3595
|
+
const plugin = pluginName ? plugins.get(pluginName) : void 0;
|
|
3596
|
+
if (plugin?.onError) {
|
|
3597
|
+
try {
|
|
3598
|
+
plugin.onError(error);
|
|
3599
|
+
} catch {
|
|
3600
|
+
}
|
|
3601
|
+
}
|
|
3602
|
+
}
|
|
3603
|
+
function use(plugin) {
|
|
3604
|
+
if (!plugin.name) {
|
|
3605
|
+
throw new PluginError("Plugin must have a name");
|
|
3606
|
+
}
|
|
3607
|
+
if (plugins.has(plugin.name)) {
|
|
3608
|
+
throw new PluginError(`Plugin "${plugin.name}" is already registered`, plugin.name);
|
|
3609
|
+
}
|
|
3610
|
+
if (plugin.dependencies) {
|
|
3611
|
+
for (const dep of plugin.dependencies) {
|
|
3612
|
+
if (!plugins.has(dep)) {
|
|
3613
|
+
throw new PluginError(
|
|
3614
|
+
`Plugin "${plugin.name}" depends on "${dep}" which is not registered`,
|
|
3615
|
+
plugin.name
|
|
3616
|
+
);
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3619
|
+
}
|
|
3620
|
+
try {
|
|
3621
|
+
plugin.install(kernel);
|
|
3622
|
+
plugins.set(plugin.name, plugin);
|
|
3623
|
+
} catch (error) {
|
|
3624
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
3625
|
+
throw new PluginError(
|
|
3626
|
+
`Failed to install plugin "${plugin.name}": ${err.message}`,
|
|
3627
|
+
plugin.name
|
|
3628
|
+
);
|
|
3629
|
+
}
|
|
3630
|
+
return kernel;
|
|
3631
|
+
}
|
|
3632
|
+
function unregister(name) {
|
|
3633
|
+
const plugin = plugins.get(name);
|
|
3634
|
+
if (!plugin) {
|
|
3635
|
+
return false;
|
|
3636
|
+
}
|
|
3637
|
+
for (const [, p] of plugins) {
|
|
3638
|
+
if (p.dependencies?.includes(name)) {
|
|
3639
|
+
throw new PluginError(
|
|
3640
|
+
`Cannot unregister "${name}": plugin "${p.name}" depends on it`,
|
|
3641
|
+
name
|
|
3642
|
+
);
|
|
3643
|
+
}
|
|
3644
|
+
}
|
|
3645
|
+
for (const [event, handlers] of eventHandlers) {
|
|
3646
|
+
const toRemove = [];
|
|
3647
|
+
for (const entry of handlers) {
|
|
3648
|
+
if (entry.pluginName === name) {
|
|
3649
|
+
toRemove.push(entry);
|
|
3650
|
+
handlerToPlugin.delete(entry.handler);
|
|
3651
|
+
}
|
|
3652
|
+
}
|
|
3653
|
+
for (const entry of toRemove) {
|
|
3654
|
+
handlers.delete(entry);
|
|
3655
|
+
}
|
|
3656
|
+
if (handlers.size === 0) {
|
|
3657
|
+
eventHandlers.delete(event);
|
|
3658
|
+
}
|
|
3659
|
+
}
|
|
3660
|
+
try {
|
|
3661
|
+
plugin.onDestroy?.();
|
|
3662
|
+
} catch {
|
|
3663
|
+
}
|
|
3664
|
+
plugins.delete(name);
|
|
3665
|
+
return true;
|
|
3666
|
+
}
|
|
3667
|
+
function on(event, handler) {
|
|
3668
|
+
if (!eventHandlers.has(event)) {
|
|
3669
|
+
eventHandlers.set(event, /* @__PURE__ */ new Set());
|
|
3670
|
+
}
|
|
3671
|
+
const pluginName = getCurrentPluginContext() ?? "unknown";
|
|
3672
|
+
const entry = { handler, pluginName };
|
|
3673
|
+
eventHandlers.get(event).add(entry);
|
|
3674
|
+
handlerToPlugin.set(handler, pluginName);
|
|
3675
|
+
}
|
|
3676
|
+
function off(event, handler) {
|
|
3677
|
+
const handlers = eventHandlers.get(event);
|
|
3678
|
+
if (!handlers) return;
|
|
3679
|
+
for (const entry of handlers) {
|
|
3680
|
+
if (entry.handler === handler) {
|
|
3681
|
+
handlers.delete(entry);
|
|
3682
|
+
handlerToPlugin.delete(handler);
|
|
3683
|
+
break;
|
|
3684
|
+
}
|
|
3685
|
+
}
|
|
3686
|
+
if (handlers.size === 0) {
|
|
3687
|
+
eventHandlers.delete(event);
|
|
3688
|
+
}
|
|
3689
|
+
}
|
|
3690
|
+
async function emit(event, context, ...args) {
|
|
3691
|
+
const handlers = eventHandlers.get(event);
|
|
3692
|
+
if (!handlers || handlers.size === 0) return;
|
|
3693
|
+
for (const entry of handlers) {
|
|
3694
|
+
try {
|
|
3695
|
+
await entry.handler(context, ...args);
|
|
3696
|
+
} catch (error) {
|
|
3697
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
3698
|
+
handlePluginError(err, entry.pluginName);
|
|
3699
|
+
}
|
|
3700
|
+
}
|
|
3701
|
+
}
|
|
3702
|
+
function listPlugins() {
|
|
3703
|
+
return Array.from(plugins.values()).map((p) => ({
|
|
3704
|
+
name: p.name,
|
|
3705
|
+
version: p.version,
|
|
3706
|
+
category: p.category,
|
|
3707
|
+
dependencies: p.dependencies ?? []
|
|
3708
|
+
}));
|
|
3709
|
+
}
|
|
3710
|
+
function getPlugin(name) {
|
|
3711
|
+
return plugins.get(name);
|
|
3712
|
+
}
|
|
3713
|
+
function hasPlugin(name) {
|
|
3714
|
+
return plugins.has(name);
|
|
3715
|
+
}
|
|
3716
|
+
async function destroy() {
|
|
3717
|
+
const pluginNames = Array.from(plugins.keys()).reverse();
|
|
3718
|
+
for (const name of pluginNames) {
|
|
3719
|
+
const plugin = plugins.get(name);
|
|
3720
|
+
if (plugin?.onDestroy) {
|
|
3721
|
+
try {
|
|
3722
|
+
await plugin.onDestroy();
|
|
3723
|
+
} catch {
|
|
3724
|
+
}
|
|
3725
|
+
}
|
|
3726
|
+
}
|
|
3727
|
+
plugins.clear();
|
|
3728
|
+
eventHandlers.clear();
|
|
3729
|
+
handlerToPlugin.clear();
|
|
3730
|
+
}
|
|
3731
|
+
let currentPluginContext = null;
|
|
3732
|
+
function getCurrentPluginContext() {
|
|
3733
|
+
return currentPluginContext;
|
|
3734
|
+
}
|
|
3735
|
+
const originalUse = use;
|
|
3736
|
+
function wrappedUse(plugin) {
|
|
3737
|
+
currentPluginContext = plugin.name;
|
|
3738
|
+
try {
|
|
3739
|
+
originalUse(plugin);
|
|
3740
|
+
return kernel;
|
|
3741
|
+
} finally {
|
|
3742
|
+
currentPluginContext = null;
|
|
3743
|
+
}
|
|
3744
|
+
}
|
|
3745
|
+
const kernel = {
|
|
3746
|
+
use: wrappedUse,
|
|
3747
|
+
unregister,
|
|
3748
|
+
on,
|
|
3749
|
+
off,
|
|
3750
|
+
emit,
|
|
3751
|
+
listPlugins,
|
|
3752
|
+
getPlugin,
|
|
3753
|
+
hasPlugin,
|
|
3754
|
+
destroy
|
|
3755
|
+
};
|
|
3756
|
+
return kernel;
|
|
3757
|
+
}
|
|
3758
|
+
function definePlugin(definition) {
|
|
3759
|
+
return definition;
|
|
3760
|
+
}
|
|
3761
|
+
function composePlugins(name, plugins) {
|
|
3762
|
+
return {
|
|
3763
|
+
name,
|
|
3764
|
+
version: "1.0.0",
|
|
3765
|
+
category: "utility",
|
|
3766
|
+
dependencies: plugins.flatMap((p) => p.dependencies ?? []),
|
|
3767
|
+
install(kernel) {
|
|
3768
|
+
for (const plugin of plugins) {
|
|
3769
|
+
kernel.use(plugin);
|
|
3770
|
+
}
|
|
3771
|
+
},
|
|
3772
|
+
async onDestroy() {
|
|
3773
|
+
}
|
|
3774
|
+
};
|
|
3775
|
+
}
|
|
3776
|
+
|
|
3777
|
+
// src/plugins/optional/changelog-parser.ts
|
|
3778
|
+
function parseChangelog(content) {
|
|
3779
|
+
const versions = [];
|
|
3780
|
+
const lines = content.split("\n");
|
|
3781
|
+
let currentVersion = null;
|
|
3782
|
+
let currentType = "other";
|
|
3783
|
+
const versionRegex = /^##\s+\[?(\d+\.\d+\.\d+(?:-[\w.]+)?)\]?(?:\s*[-–(]\s*(\d{4}-\d{2}-\d{2}))?/;
|
|
3784
|
+
const typeMap = {
|
|
3785
|
+
added: "added",
|
|
3786
|
+
changed: "changed",
|
|
3787
|
+
deprecated: "deprecated",
|
|
3788
|
+
removed: "removed",
|
|
3789
|
+
fixed: "fixed",
|
|
3790
|
+
security: "security"
|
|
3791
|
+
};
|
|
3792
|
+
for (const line of lines) {
|
|
3793
|
+
const versionMatch = versionRegex.exec(line);
|
|
3794
|
+
if (versionMatch) {
|
|
3795
|
+
if (currentVersion) {
|
|
3796
|
+
versions.push(currentVersion);
|
|
3797
|
+
}
|
|
3798
|
+
currentVersion = {
|
|
3799
|
+
version: versionMatch[1],
|
|
3800
|
+
date: versionMatch[2],
|
|
3801
|
+
changes: []
|
|
3802
|
+
};
|
|
3803
|
+
currentType = "other";
|
|
3804
|
+
continue;
|
|
3805
|
+
}
|
|
3806
|
+
if (line.startsWith("### ")) {
|
|
3807
|
+
const typeText = line.slice(4).toLowerCase().trim();
|
|
3808
|
+
currentType = typeMap[typeText] || "other";
|
|
3809
|
+
continue;
|
|
3810
|
+
}
|
|
3811
|
+
const itemMatch = /^[-*]\s+(.+)/.exec(line);
|
|
3812
|
+
if (itemMatch && currentVersion) {
|
|
3813
|
+
currentVersion.changes.push({
|
|
3814
|
+
type: currentType,
|
|
3815
|
+
description: itemMatch[1].trim()
|
|
3816
|
+
});
|
|
3817
|
+
}
|
|
3818
|
+
}
|
|
3819
|
+
if (currentVersion) {
|
|
3820
|
+
versions.push(currentVersion);
|
|
3821
|
+
}
|
|
3822
|
+
return { versions };
|
|
3823
|
+
}
|
|
3824
|
+
function findChangelog(files) {
|
|
3825
|
+
const names = [
|
|
3826
|
+
"CHANGELOG.md",
|
|
3827
|
+
"changelog.md",
|
|
3828
|
+
"CHANGELOG",
|
|
3829
|
+
"changelog",
|
|
3830
|
+
"HISTORY.md",
|
|
3831
|
+
"history.md",
|
|
3832
|
+
"CHANGES.md",
|
|
3833
|
+
"changes.md"
|
|
3834
|
+
];
|
|
3835
|
+
for (const name of names) {
|
|
3836
|
+
const content = files.get(name) || files.get(`package/${name}`);
|
|
3837
|
+
if (content) {
|
|
3838
|
+
return content;
|
|
3839
|
+
}
|
|
3840
|
+
}
|
|
3841
|
+
return void 0;
|
|
3842
|
+
}
|
|
3843
|
+
function getLatestVersion(changelog) {
|
|
3844
|
+
return changelog.versions[0];
|
|
3845
|
+
}
|
|
3846
|
+
function getVersion(changelog, version) {
|
|
3847
|
+
return changelog.versions.find((v) => v.version === version);
|
|
3848
|
+
}
|
|
3849
|
+
function formatChangelogEntry(entry) {
|
|
3850
|
+
const lines = [];
|
|
3851
|
+
lines.push(`## ${entry.version}${entry.date ? ` (${entry.date})` : ""}`);
|
|
3852
|
+
lines.push("");
|
|
3853
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
3854
|
+
for (const change of entry.changes) {
|
|
3855
|
+
const existing = grouped.get(change.type) || [];
|
|
3856
|
+
existing.push(change.description);
|
|
3857
|
+
grouped.set(change.type, existing);
|
|
3858
|
+
}
|
|
3859
|
+
const typeLabels = {
|
|
3860
|
+
added: "Added",
|
|
3861
|
+
changed: "Changed",
|
|
3862
|
+
deprecated: "Deprecated",
|
|
3863
|
+
removed: "Removed",
|
|
3864
|
+
fixed: "Fixed",
|
|
3865
|
+
security: "Security",
|
|
3866
|
+
other: "Other"
|
|
3867
|
+
};
|
|
3868
|
+
for (const [type, changes] of grouped) {
|
|
3869
|
+
lines.push(`### ${typeLabels[type] || type}`);
|
|
3870
|
+
for (const change of changes) {
|
|
3871
|
+
lines.push(`- ${change}`);
|
|
3872
|
+
}
|
|
3873
|
+
lines.push("");
|
|
3874
|
+
}
|
|
3875
|
+
return lines.join("\n");
|
|
3876
|
+
}
|
|
3877
|
+
function createChangelogParserPlugin() {
|
|
3878
|
+
return definePlugin({
|
|
3879
|
+
name: "changelog-parser",
|
|
3880
|
+
version: "1.0.0",
|
|
3881
|
+
category: "parser",
|
|
3882
|
+
install(kernel) {
|
|
3883
|
+
kernel.on("parse:changelog", async (ctx) => {
|
|
3884
|
+
const changelogContent = findChangelog(ctx.package.files);
|
|
3885
|
+
if (changelogContent) {
|
|
3886
|
+
ctx.changelog = parseChangelog(changelogContent);
|
|
3887
|
+
}
|
|
3888
|
+
});
|
|
3889
|
+
}
|
|
3890
|
+
});
|
|
3891
|
+
}
|
|
3892
|
+
|
|
3893
|
+
// src/plugins/optional/html-output.ts
|
|
3894
|
+
var DEFAULT_OPTIONS2 = {
|
|
3895
|
+
includeStyles: true,
|
|
3896
|
+
syntaxHighlighting: true,
|
|
3897
|
+
darkMode: true
|
|
3898
|
+
};
|
|
3899
|
+
function escapeHTML(str) {
|
|
3900
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
3901
|
+
}
|
|
3902
|
+
function generateStyles(options) {
|
|
3903
|
+
if (!options.includeStyles) return "";
|
|
3904
|
+
let css = `
|
|
3905
|
+
:root {
|
|
3906
|
+
--bg: #ffffff;
|
|
3907
|
+
--text: #1a1a1a;
|
|
3908
|
+
--code-bg: #f5f5f5;
|
|
3909
|
+
--border: #e5e5e5;
|
|
3910
|
+
--link: #0066cc;
|
|
3911
|
+
--heading: #000000;
|
|
3912
|
+
--param-type: #0550ae;
|
|
3913
|
+
--deprecated: #d93025;
|
|
3914
|
+
}
|
|
3915
|
+
${options.darkMode ? `
|
|
3916
|
+
@media (prefers-color-scheme: dark) {
|
|
3917
|
+
:root {
|
|
3918
|
+
--bg: #1a1a1a;
|
|
3919
|
+
--text: #e5e5e5;
|
|
3920
|
+
--code-bg: #2d2d2d;
|
|
3921
|
+
--border: #404040;
|
|
3922
|
+
--link: #58a6ff;
|
|
3923
|
+
--heading: #ffffff;
|
|
3924
|
+
--param-type: #79c0ff;
|
|
3925
|
+
--deprecated: #f85149;
|
|
3926
|
+
}
|
|
3927
|
+
}` : ""}
|
|
3928
|
+
* { box-sizing: border-box; }
|
|
3929
|
+
body {
|
|
3930
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
3931
|
+
line-height: 1.6;
|
|
3932
|
+
color: var(--text);
|
|
3933
|
+
background: var(--bg);
|
|
3934
|
+
max-width: 900px;
|
|
3935
|
+
margin: 0 auto;
|
|
3936
|
+
padding: 2rem;
|
|
3937
|
+
}
|
|
3938
|
+
h1, h2, h3, h4 { color: var(--heading); margin-top: 2rem; }
|
|
3939
|
+
h1 { border-bottom: 2px solid var(--border); padding-bottom: 0.5rem; }
|
|
3940
|
+
code {
|
|
3941
|
+
font-family: 'SFMono-Regular', Consolas, monospace;
|
|
3942
|
+
font-size: 0.9em;
|
|
3943
|
+
background: var(--code-bg);
|
|
3944
|
+
padding: 0.2em 0.4em;
|
|
3945
|
+
border-radius: 3px;
|
|
3946
|
+
}
|
|
3947
|
+
pre {
|
|
3948
|
+
background: var(--code-bg);
|
|
3949
|
+
padding: 1rem;
|
|
3950
|
+
overflow-x: auto;
|
|
3951
|
+
border-radius: 6px;
|
|
3952
|
+
border: 1px solid var(--border);
|
|
3953
|
+
}
|
|
3954
|
+
pre code { background: none; padding: 0; }
|
|
3955
|
+
a { color: var(--link); text-decoration: none; }
|
|
3956
|
+
a:hover { text-decoration: underline; }
|
|
3957
|
+
.api-entry {
|
|
3958
|
+
margin: 2rem 0;
|
|
3959
|
+
padding: 1.5rem;
|
|
3960
|
+
border: 1px solid var(--border);
|
|
3961
|
+
border-radius: 8px;
|
|
3962
|
+
}
|
|
3963
|
+
.api-entry h3 { margin-top: 0; }
|
|
3964
|
+
.signature { font-size: 0.95rem; }
|
|
3965
|
+
.param-type { color: var(--param-type); }
|
|
3966
|
+
.deprecated { color: var(--deprecated); font-style: italic; }
|
|
3967
|
+
.params-table {
|
|
3968
|
+
width: 100%;
|
|
3969
|
+
border-collapse: collapse;
|
|
3970
|
+
margin: 1rem 0;
|
|
3971
|
+
}
|
|
3972
|
+
.params-table th, .params-table td {
|
|
3973
|
+
text-align: left;
|
|
3974
|
+
padding: 0.5rem;
|
|
3975
|
+
border-bottom: 1px solid var(--border);
|
|
3976
|
+
}
|
|
3977
|
+
.params-table th { font-weight: 600; }
|
|
3978
|
+
.toc { background: var(--code-bg); padding: 1rem; border-radius: 6px; }
|
|
3979
|
+
.toc ul { margin: 0; padding-left: 1.5rem; }
|
|
3980
|
+
.badge {
|
|
3981
|
+
display: inline-block;
|
|
3982
|
+
padding: 0.2em 0.5em;
|
|
3983
|
+
font-size: 0.75rem;
|
|
3984
|
+
font-weight: 600;
|
|
3985
|
+
border-radius: 4px;
|
|
3986
|
+
margin-left: 0.5rem;
|
|
3987
|
+
}
|
|
3988
|
+
.badge-function { background: #ddf4ff; color: #0550ae; }
|
|
3989
|
+
.badge-class { background: #fff8c5; color: #735c0f; }
|
|
3990
|
+
.badge-interface { background: #dafbe1; color: #116329; }
|
|
3991
|
+
.badge-type { background: #f6e8ff; color: #8250df; }
|
|
3992
|
+
.badge-constant { background: #ffebe9; color: #cf222e; }
|
|
3993
|
+
.badge-enum { background: #fdf4ff; color: #bf3989; }
|
|
3994
|
+
`;
|
|
3995
|
+
if (options.customCSS) {
|
|
3996
|
+
css += options.customCSS;
|
|
3997
|
+
}
|
|
3998
|
+
return `<style>${css}</style>`;
|
|
3999
|
+
}
|
|
4000
|
+
function kindBadge(kind) {
|
|
4001
|
+
return `<span class="badge badge-${kind}">${kind}</span>`;
|
|
4002
|
+
}
|
|
4003
|
+
function generateTOC(entries) {
|
|
4004
|
+
if (entries.length === 0) return "";
|
|
4005
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
4006
|
+
for (const entry of entries) {
|
|
4007
|
+
const list = grouped.get(entry.kind) || [];
|
|
4008
|
+
list.push(entry);
|
|
4009
|
+
grouped.set(entry.kind, list);
|
|
4010
|
+
}
|
|
4011
|
+
let html = '<nav class="toc"><h2>Table of Contents</h2>';
|
|
4012
|
+
for (const [kind, items] of grouped) {
|
|
4013
|
+
html += `<h4>${kind.charAt(0).toUpperCase() + kind.slice(1)}s</h4><ul>`;
|
|
4014
|
+
for (const item of items) {
|
|
4015
|
+
const id = `${kind}-${item.name}`.toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
4016
|
+
html += `<li><a href="#${id}">${escapeHTML(item.name)}</a></li>`;
|
|
4017
|
+
}
|
|
4018
|
+
html += "</ul>";
|
|
4019
|
+
}
|
|
4020
|
+
html += "</nav>";
|
|
4021
|
+
return html;
|
|
4022
|
+
}
|
|
4023
|
+
function generateParams(entry) {
|
|
4024
|
+
if (!entry.params || entry.params.length === 0) return "";
|
|
4025
|
+
let html = '<h4>Parameters</h4><table class="params-table"><thead><tr>';
|
|
4026
|
+
html += "<th>Name</th><th>Type</th><th>Description</th></tr></thead><tbody>";
|
|
4027
|
+
for (const param of entry.params) {
|
|
4028
|
+
const name = param.optional ? `${param.name}?` : param.name;
|
|
4029
|
+
const type = param.type ? `<code class="param-type">${escapeHTML(param.type)}</code>` : "-";
|
|
4030
|
+
const desc = param.description ? escapeHTML(param.description) : "-";
|
|
4031
|
+
html += `<tr><td><code>${escapeHTML(name)}</code></td><td>${type}</td><td>${desc}</td></tr>`;
|
|
4032
|
+
}
|
|
4033
|
+
html += "</tbody></table>";
|
|
4034
|
+
return html;
|
|
4035
|
+
}
|
|
4036
|
+
function generateExamples(examples) {
|
|
4037
|
+
if (examples.length === 0) return "";
|
|
4038
|
+
let html = "<h4>Examples</h4>";
|
|
4039
|
+
for (const example of examples) {
|
|
4040
|
+
html += `<pre><code>${escapeHTML(example)}</code></pre>`;
|
|
4041
|
+
}
|
|
4042
|
+
return html;
|
|
4043
|
+
}
|
|
4044
|
+
function generateEntry(entry) {
|
|
4045
|
+
const id = `${entry.kind}-${entry.name}`.toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
4046
|
+
let html = `<div class="api-entry" id="${id}">`;
|
|
4047
|
+
html += `<h3>${escapeHTML(entry.name)} ${kindBadge(entry.kind)}</h3>`;
|
|
4048
|
+
if (entry.deprecated) {
|
|
4049
|
+
const reason = typeof entry.deprecated === "string" ? entry.deprecated : "";
|
|
4050
|
+
html += `<p class="deprecated">Deprecated${reason ? `: ${escapeHTML(reason)}` : ""}</p>`;
|
|
4051
|
+
}
|
|
4052
|
+
if (entry.description) {
|
|
4053
|
+
html += `<p>${escapeHTML(entry.description)}</p>`;
|
|
4054
|
+
}
|
|
4055
|
+
html += `<pre class="signature"><code>${escapeHTML(entry.signature)}</code></pre>`;
|
|
4056
|
+
html += generateParams(entry);
|
|
4057
|
+
if (entry.returns) {
|
|
4058
|
+
html += "<h4>Returns</h4>";
|
|
4059
|
+
html += `<p><code class="param-type">${escapeHTML(entry.returns.type)}</code>`;
|
|
4060
|
+
if (entry.returns.description) {
|
|
4061
|
+
html += ` - ${escapeHTML(entry.returns.description)}`;
|
|
4062
|
+
}
|
|
4063
|
+
html += "</p>";
|
|
4064
|
+
}
|
|
4065
|
+
if (entry.examples && entry.examples.length > 0) {
|
|
4066
|
+
html += generateExamples(entry.examples);
|
|
4067
|
+
}
|
|
4068
|
+
html += "</div>";
|
|
4069
|
+
return html;
|
|
4070
|
+
}
|
|
4071
|
+
function generateHeader(name, version, description, readme) {
|
|
4072
|
+
let html = `<header>`;
|
|
4073
|
+
html += `<h1>${escapeHTML(name)} <small>v${escapeHTML(version)}</small></h1>`;
|
|
4074
|
+
if (description) {
|
|
4075
|
+
html += `<p>${escapeHTML(description)}</p>`;
|
|
4076
|
+
}
|
|
4077
|
+
if (readme?.installation) {
|
|
4078
|
+
html += "<h2>Installation</h2>";
|
|
4079
|
+
html += `<pre><code>${escapeHTML(readme.installation)}</code></pre>`;
|
|
4080
|
+
}
|
|
4081
|
+
html += "</header>";
|
|
4082
|
+
return html;
|
|
4083
|
+
}
|
|
4084
|
+
function generateHTML(ctx, options = {}) {
|
|
4085
|
+
const opts = { ...DEFAULT_OPTIONS2, ...options };
|
|
4086
|
+
const title = opts.title || `${ctx.package.name} API Documentation`;
|
|
4087
|
+
let html = '<!DOCTYPE html>\n<html lang="en">\n<head>\n';
|
|
4088
|
+
html += '<meta charset="UTF-8">\n';
|
|
4089
|
+
html += '<meta name="viewport" content="width=device-width, initial-scale=1.0">\n';
|
|
4090
|
+
html += `<title>${escapeHTML(title)}</title>
|
|
4091
|
+
`;
|
|
4092
|
+
html += generateStyles(opts);
|
|
4093
|
+
html += "</head>\n<body>\n";
|
|
4094
|
+
html += generateHeader(ctx.package.name, ctx.package.version, ctx.package.description, ctx.readme);
|
|
4095
|
+
html += generateTOC(ctx.api);
|
|
4096
|
+
html += "<main>";
|
|
4097
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
4098
|
+
for (const entry of ctx.api) {
|
|
4099
|
+
const list = grouped.get(entry.kind) || [];
|
|
4100
|
+
list.push(entry);
|
|
4101
|
+
grouped.set(entry.kind, list);
|
|
4102
|
+
}
|
|
4103
|
+
const kindOrder = ["function", "class", "interface", "type", "constant", "enum"];
|
|
4104
|
+
for (const kind of kindOrder) {
|
|
4105
|
+
const entries = grouped.get(kind);
|
|
4106
|
+
if (!entries || entries.length === 0) continue;
|
|
4107
|
+
html += `<section><h2>${kind.charAt(0).toUpperCase() + kind.slice(1)}s</h2>`;
|
|
4108
|
+
for (const entry of entries) {
|
|
4109
|
+
html += generateEntry(entry);
|
|
4110
|
+
}
|
|
4111
|
+
html += "</section>";
|
|
4112
|
+
}
|
|
4113
|
+
html += "</main>\n";
|
|
4114
|
+
html += '<footer><p>Generated by <a href="https://github.com/oxog/npm-llms">@oxog/npm-llms</a></p></footer>\n';
|
|
4115
|
+
html += "</body>\n</html>";
|
|
4116
|
+
return html;
|
|
4117
|
+
}
|
|
4118
|
+
function createHTMLOutputPlugin(options = {}) {
|
|
4119
|
+
return definePlugin({
|
|
4120
|
+
name: "html-output",
|
|
4121
|
+
version: "1.0.0",
|
|
4122
|
+
category: "output",
|
|
4123
|
+
install(kernel) {
|
|
4124
|
+
kernel.on("output:generate", async (ctx) => {
|
|
4125
|
+
if (!ctx.options.formats?.includes("html")) return;
|
|
4126
|
+
const html = generateHTML(ctx, options);
|
|
4127
|
+
ctx.outputs.set("html", html);
|
|
4128
|
+
});
|
|
4129
|
+
}
|
|
4130
|
+
});
|
|
4131
|
+
}
|
|
4132
|
+
|
|
4133
|
+
exports.checkGeminiAvailable = checkGeminiAvailable;
|
|
4134
|
+
exports.checkOllamaAvailable = checkOllamaAvailable;
|
|
4135
|
+
exports.composePlugins = composePlugins;
|
|
4136
|
+
exports.coreOutputPlugins = coreOutputPlugins;
|
|
4137
|
+
exports.coreParserPlugins = coreParserPlugins;
|
|
4138
|
+
exports.corePlugins = corePlugins;
|
|
4139
|
+
exports.createAIEnrichmentPlugin = createAIEnrichmentPlugin;
|
|
4140
|
+
exports.createChangelogParserPlugin = createChangelogParserPlugin;
|
|
4141
|
+
exports.createClaudePlugin = createClaudePlugin;
|
|
4142
|
+
exports.createClaudeProvider = createClaudeProvider;
|
|
4143
|
+
exports.createGeminiPlugin = createGeminiPlugin;
|
|
4144
|
+
exports.createGeminiProvider = createGeminiProvider;
|
|
4145
|
+
exports.createGroqPlugin = createGroqPlugin;
|
|
4146
|
+
exports.createGroqProvider = createGroqProvider;
|
|
4147
|
+
exports.createHTMLOutputPlugin = createHTMLOutputPlugin;
|
|
4148
|
+
exports.createKernel = createKernel;
|
|
4149
|
+
exports.createOllamaPlugin = createOllamaPlugin;
|
|
4150
|
+
exports.createOllamaProvider = createOllamaProvider;
|
|
4151
|
+
exports.createOpenAIPlugin = createOpenAIPlugin;
|
|
4152
|
+
exports.createOpenAIProvider = createOpenAIProvider;
|
|
4153
|
+
exports.createSimpleProvider = createSimpleProvider;
|
|
4154
|
+
exports.definePlugin = definePlugin;
|
|
4155
|
+
exports.dtsParserPlugin = dtsParserPlugin;
|
|
4156
|
+
exports.findChangelog = findChangelog;
|
|
4157
|
+
exports.formatChangelogEntry = formatChangelogEntry;
|
|
4158
|
+
exports.generateHTML = generateHTML;
|
|
4159
|
+
exports.getLatestVersion = getLatestVersion;
|
|
4160
|
+
exports.getVersion = getVersion;
|
|
4161
|
+
exports.jsonOutputPlugin = jsonOutputPlugin;
|
|
4162
|
+
exports.listOllamaModels = listOllamaModels;
|
|
4163
|
+
exports.llmsFullOutputPlugin = llmsFullOutputPlugin;
|
|
4164
|
+
exports.llmsOutputPlugin = llmsOutputPlugin;
|
|
4165
|
+
exports.markdownOutputPlugin = markdownOutputPlugin;
|
|
4166
|
+
exports.parseChangelog = parseChangelog;
|
|
4167
|
+
exports.readmeParserPlugin = readmeParserPlugin;
|
|
4168
|
+
exports.tsSourceParserPlugin = tsSourceParserPlugin;
|
|
4169
|
+
exports.typesResolverPlugin = typesResolverPlugin;
|
|
4170
|
+
//# sourceMappingURL=index.cjs.map
|
|
4171
|
+
//# sourceMappingURL=index.cjs.map
|