skrypt-ai 0.8.0 → 0.8.1
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/dist/auth/index.js +6 -0
- package/dist/binding/binder.d.ts +5 -0
- package/dist/binding/binder.js +63 -0
- package/dist/binding/detector.d.ts +5 -0
- package/dist/binding/detector.js +51 -0
- package/dist/binding/doc-parser.d.ts +9 -0
- package/dist/binding/doc-parser.js +138 -0
- package/dist/binding/extractor.d.ts +14 -0
- package/dist/binding/extractor.js +39 -0
- package/dist/binding/index.d.ts +5 -0
- package/dist/binding/index.js +5 -0
- package/dist/binding/types.d.ts +74 -0
- package/dist/binding/types.js +1 -0
- package/dist/claims/extractor.d.ts +13 -0
- package/dist/claims/extractor.js +138 -0
- package/dist/claims/index.d.ts +4 -0
- package/dist/claims/index.js +4 -0
- package/dist/claims/reporter.d.ts +9 -0
- package/dist/claims/reporter.js +65 -0
- package/dist/claims/store.d.ts +13 -0
- package/dist/claims/store.js +51 -0
- package/dist/claims/types.d.ts +34 -0
- package/dist/claims/types.js +1 -0
- package/dist/cli.js +516 -56
- package/dist/commands/bind.d.ts +2 -0
- package/dist/commands/bind.js +139 -0
- package/dist/commands/claims.d.ts +2 -0
- package/dist/commands/claims.js +84 -0
- package/dist/commands/coverage.d.ts +2 -0
- package/dist/commands/coverage.js +61 -0
- package/dist/commands/generate/index.js +5 -0
- package/dist/commands/generate/scan.js +33 -14
- package/dist/commands/generate/write.d.ts +7 -0
- package/dist/commands/generate/write.js +65 -1
- package/dist/commands/import.js +12 -3
- package/dist/commands/init.js +68 -5
- package/dist/commands/monitor.d.ts +15 -0
- package/dist/commands/monitor.js +2 -2
- package/dist/commands/mutate.d.ts +2 -0
- package/dist/commands/mutate.js +177 -0
- package/dist/config/types.js +2 -2
- package/dist/coverage/calculator.d.ts +7 -0
- package/dist/coverage/calculator.js +86 -0
- package/dist/coverage/index.d.ts +3 -0
- package/dist/coverage/index.js +3 -0
- package/dist/coverage/reporter.d.ts +9 -0
- package/dist/coverage/reporter.js +65 -0
- package/dist/coverage/types.d.ts +36 -0
- package/dist/coverage/types.js +1 -0
- package/dist/generator/generator.d.ts +3 -1
- package/dist/generator/generator.js +137 -23
- package/dist/generator/mdx-serializer.js +3 -2
- package/dist/generator/organizer.d.ts +5 -1
- package/dist/generator/organizer.js +29 -14
- package/dist/generator/types.d.ts +6 -0
- package/dist/generator/writer.js +7 -2
- package/dist/github/org-discovery.js +5 -0
- package/dist/importers/mintlify.js +4 -3
- package/dist/llm/anthropic-client.js +1 -0
- package/dist/llm/index.d.ts +15 -0
- package/dist/llm/index.js +148 -29
- package/dist/llm/openai-client.js +2 -0
- package/dist/mutation/index.d.ts +4 -0
- package/dist/mutation/index.js +4 -0
- package/dist/mutation/mutator.d.ts +5 -0
- package/dist/mutation/mutator.js +101 -0
- package/dist/mutation/reporter.d.ts +14 -0
- package/dist/mutation/reporter.js +66 -0
- package/dist/mutation/runner.d.ts +9 -0
- package/dist/mutation/runner.js +70 -0
- package/dist/mutation/types.d.ts +51 -0
- package/dist/mutation/types.js +1 -0
- package/dist/qa/checks.d.ts +1 -0
- package/dist/qa/checks.js +47 -0
- package/dist/qa/index.js +2 -1
- package/dist/scanner/index.js +78 -11
- package/dist/scanner/typescript.js +42 -31
- package/dist/sentry.d.ts +3 -0
- package/dist/sentry.js +28 -0
- package/dist/template/docs.json +6 -3
- package/dist/template/next.config.mjs +15 -1
- package/dist/template/package.json +4 -3
- package/dist/template/public/docs-schema.json +257 -0
- package/dist/template/sentry.client.config.ts +12 -0
- package/dist/template/sentry.edge.config.ts +7 -0
- package/dist/template/sentry.server.config.ts +7 -0
- package/dist/template/src/app/docs/[...slug]/page.tsx +11 -5
- package/dist/template/src/app/docs/layout.tsx +2 -4
- package/dist/template/src/app/global-error.tsx +60 -0
- package/dist/template/src/app/layout.tsx +7 -16
- package/dist/template/src/app/page.tsx +2 -5
- package/dist/template/src/components/ai-chat-impl.tsx +1 -1
- package/dist/template/src/components/docs-layout.tsx +1 -15
- package/dist/template/src/components/footer.tsx +95 -19
- package/dist/template/src/components/header.tsx +1 -1
- package/dist/template/src/components/search-dialog.tsx +5 -0
- package/dist/template/src/instrumentation.ts +11 -0
- package/dist/template/src/lib/docs-config.ts +235 -0
- package/dist/template/src/lib/fonts.ts +3 -3
- package/dist/testing/runner.js +8 -1
- package/package.json +2 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single mutation applied to source code
|
|
3
|
+
*/
|
|
4
|
+
export interface Mutant {
|
|
5
|
+
id: string;
|
|
6
|
+
filePath: string;
|
|
7
|
+
originalCode: string;
|
|
8
|
+
mutatedCode: string;
|
|
9
|
+
startLine: number;
|
|
10
|
+
endLine: number;
|
|
11
|
+
type: 'return_value' | 'string_literal' | 'conditional' | 'logical_operator' | 'body_removal';
|
|
12
|
+
description: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Result of testing a single mutant
|
|
16
|
+
*/
|
|
17
|
+
export interface MutantResult {
|
|
18
|
+
mutant: Mutant;
|
|
19
|
+
status: 'killed' | 'survived' | 'error' | 'timeout';
|
|
20
|
+
failedExample?: string;
|
|
21
|
+
error?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Full mutation testing report
|
|
25
|
+
*/
|
|
26
|
+
export interface MutationReport {
|
|
27
|
+
totalMutants: number;
|
|
28
|
+
killed: number;
|
|
29
|
+
survived: number;
|
|
30
|
+
errors: number;
|
|
31
|
+
timeouts: number;
|
|
32
|
+
mutationScore: number;
|
|
33
|
+
files: Array<{
|
|
34
|
+
filePath: string;
|
|
35
|
+
mutants: number;
|
|
36
|
+
killed: number;
|
|
37
|
+
survived: number;
|
|
38
|
+
score: number;
|
|
39
|
+
}>;
|
|
40
|
+
survivors: MutantResult[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Options for mutation testing
|
|
44
|
+
*/
|
|
45
|
+
export interface MutationOptions {
|
|
46
|
+
files?: string[];
|
|
47
|
+
maxMutants?: number;
|
|
48
|
+
json?: boolean;
|
|
49
|
+
minScore?: number;
|
|
50
|
+
timeout?: number;
|
|
51
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/qa/checks.d.ts
CHANGED
|
@@ -8,3 +8,4 @@ export declare function checkSecurity(content: string, relPath: string): QAIssue
|
|
|
8
8
|
export declare function checkContentQuality(content: string, relPath: string): QAIssue[];
|
|
9
9
|
export declare function checkScreenshots(content: string, relPath: string, outputDir: string): QAIssue[];
|
|
10
10
|
export declare function checkMdxSyntax(content: string, relPath: string): QAIssue[];
|
|
11
|
+
export declare function checkCodeBlockBraces(content: string, relPath: string): QAIssue[];
|
package/dist/qa/checks.js
CHANGED
|
@@ -490,3 +490,50 @@ export function checkMdxSyntax(content, relPath) {
|
|
|
490
490
|
}
|
|
491
491
|
return issues;
|
|
492
492
|
}
|
|
493
|
+
// ── Check: Unescaped curly braces inside fenced code blocks (.md files only) ──
|
|
494
|
+
// When .md files are compiled as MDX (format: 'mdx'), curly braces inside code
|
|
495
|
+
// blocks are interpreted as JSX expressions, causing "Could not parse expression
|
|
496
|
+
// with acorn" errors. The template uses format: 'md' for .md files to avoid this,
|
|
497
|
+
// but this check serves as a defensive warning layer.
|
|
498
|
+
export function checkCodeBlockBraces(content, relPath) {
|
|
499
|
+
// Only check .md files — .mdx files intentionally use JSX expressions
|
|
500
|
+
if (relPath.endsWith('.mdx'))
|
|
501
|
+
return [];
|
|
502
|
+
const issues = [];
|
|
503
|
+
const lines = content.split('\n');
|
|
504
|
+
let inCodeBlock = false;
|
|
505
|
+
let codeBlockStart = 0;
|
|
506
|
+
let braceCount = 0;
|
|
507
|
+
for (let i = 0; i < lines.length; i++) {
|
|
508
|
+
const line = lines[i];
|
|
509
|
+
if (line.startsWith('```')) {
|
|
510
|
+
if (!inCodeBlock) {
|
|
511
|
+
inCodeBlock = true;
|
|
512
|
+
codeBlockStart = i + 1;
|
|
513
|
+
braceCount = 0;
|
|
514
|
+
}
|
|
515
|
+
else {
|
|
516
|
+
// Closing fence — report if braces were found
|
|
517
|
+
if (braceCount > 0) {
|
|
518
|
+
issues.push({
|
|
519
|
+
file: relPath,
|
|
520
|
+
line: codeBlockStart,
|
|
521
|
+
severity: 'info',
|
|
522
|
+
check: 'mdx-syntax',
|
|
523
|
+
message: `Code block contains ${braceCount} unescaped curly brace${braceCount > 1 ? 's' : ''} — may break MDX compilation if format is not set to "md"`,
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
inCodeBlock = false;
|
|
527
|
+
}
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
if (inCodeBlock) {
|
|
531
|
+
// Count curly braces in code block lines
|
|
532
|
+
for (const ch of line) {
|
|
533
|
+
if (ch === '{' || ch === '}')
|
|
534
|
+
braceCount++;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return issues;
|
|
539
|
+
}
|
package/dist/qa/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readdirSync, readFileSync, writeFileSync } from 'fs';
|
|
2
2
|
import { join, relative } from 'path';
|
|
3
|
-
import { checkFrontmatter, checkHeadings, checkCodeBlocks, checkComponents, checkLinks, checkSecurity, checkContentQuality, checkMdxSyntax, checkScreenshots, } from './checks.js';
|
|
3
|
+
import { checkFrontmatter, checkHeadings, checkCodeBlocks, checkComponents, checkLinks, checkSecurity, checkContentQuality, checkMdxSyntax, checkCodeBlockBraces, checkScreenshots, } from './checks.js';
|
|
4
4
|
import { fixFrontmatter, fixMdxSyntax, fixCodeBlocks, fixSecurity, } from './fixes.js';
|
|
5
5
|
/**
|
|
6
6
|
* Recursively find all .md and .mdx files in a directory
|
|
@@ -82,6 +82,7 @@ export function runQA(outputDir) {
|
|
|
82
82
|
runCheck('security', files, (content, relPath) => checkSecurity(content, relPath)),
|
|
83
83
|
runCheck('content', files, (content, relPath) => checkContentQuality(content, relPath)),
|
|
84
84
|
runCheck('mdx-syntax', files, (content, relPath) => checkMdxSyntax(content, relPath)),
|
|
85
|
+
runCheck('mdx-code-braces', files, (content, relPath) => checkCodeBlockBraces(content, relPath)),
|
|
85
86
|
runCheck('screenshots', files, (content, relPath) => checkScreenshots(content, relPath, outputDir)),
|
|
86
87
|
];
|
|
87
88
|
// Aggregate
|
package/dist/scanner/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readdir, stat } from 'fs/promises';
|
|
2
|
+
import { statSync } from 'fs';
|
|
2
3
|
import { join, parse as parsePath } from 'path';
|
|
3
4
|
import { PythonScanner } from './python.js';
|
|
4
5
|
import { TypeScriptScanner } from './typescript.js';
|
|
@@ -52,13 +53,23 @@ function globToRegex(pattern) {
|
|
|
52
53
|
re = re.replace(/\0GLOBSTAR\0/g, '.*');
|
|
53
54
|
return new RegExp(`(^|/)${re}($|/)`);
|
|
54
55
|
}
|
|
56
|
+
/** Cache compiled glob RegExp patterns so they're created once per pattern, not once per file. */
|
|
57
|
+
const globRegexCache = new Map();
|
|
58
|
+
function getCachedGlobRegex(pattern) {
|
|
59
|
+
let regex = globRegexCache.get(pattern);
|
|
60
|
+
if (!regex) {
|
|
61
|
+
regex = globToRegex(pattern);
|
|
62
|
+
globRegexCache.set(pattern, regex);
|
|
63
|
+
}
|
|
64
|
+
return regex;
|
|
65
|
+
}
|
|
55
66
|
/**
|
|
56
67
|
* Check if a path should be excluded based on patterns
|
|
57
68
|
*/
|
|
58
69
|
function shouldExclude(filePath, excludePatterns) {
|
|
59
70
|
const normalized = filePath.replace(/\\/g, '/');
|
|
60
71
|
for (const pattern of excludePatterns) {
|
|
61
|
-
if (
|
|
72
|
+
if (getCachedGlobRegex(pattern).test(normalized))
|
|
62
73
|
return true;
|
|
63
74
|
}
|
|
64
75
|
return false;
|
|
@@ -71,13 +82,16 @@ function shouldInclude(filePath, includePatterns) {
|
|
|
71
82
|
return true;
|
|
72
83
|
const normalized = filePath.replace(/\\/g, '/');
|
|
73
84
|
for (const pattern of includePatterns) {
|
|
74
|
-
if (
|
|
85
|
+
if (getCachedGlobRegex(pattern).test(normalized))
|
|
75
86
|
return true;
|
|
76
87
|
}
|
|
77
88
|
return false;
|
|
78
89
|
}
|
|
79
90
|
const MAX_SCAN_DEPTH = 30;
|
|
80
91
|
const MAX_SCAN_FILES = 10000;
|
|
92
|
+
/** Skip files larger than 1 MB to avoid memory bloat and slow parsing */
|
|
93
|
+
const MAX_FILE_SIZE = 1024 * 1024;
|
|
94
|
+
const SCAN_CONCURRENCY = 8;
|
|
81
95
|
/**
|
|
82
96
|
* Recursively find all files in a directory
|
|
83
97
|
*/
|
|
@@ -113,6 +127,38 @@ async function findFiles(dir, include, exclude) {
|
|
|
113
127
|
await walk(dir, 0);
|
|
114
128
|
return files;
|
|
115
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* Simple concurrency limiter (pLimit-style). Limits the number of
|
|
132
|
+
* concurrently executing async functions without adding a dependency.
|
|
133
|
+
*/
|
|
134
|
+
function createScanLimiter(concurrency) {
|
|
135
|
+
let active = 0;
|
|
136
|
+
const queue = [];
|
|
137
|
+
function next() {
|
|
138
|
+
if (queue.length > 0 && active < concurrency) {
|
|
139
|
+
active++;
|
|
140
|
+
const resolve = queue.shift();
|
|
141
|
+
resolve();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return async function limit(fn) {
|
|
145
|
+
if (active >= concurrency) {
|
|
146
|
+
await new Promise((resolve) => {
|
|
147
|
+
queue.push(resolve);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
active++;
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
return await fn();
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
active--;
|
|
158
|
+
next();
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
116
162
|
/**
|
|
117
163
|
* Scan a directory (or single file) for all API elements
|
|
118
164
|
*/
|
|
@@ -129,27 +175,48 @@ export async function scanDirectory(dir, options = {}) {
|
|
|
129
175
|
else {
|
|
130
176
|
files = await findFiles(dir, include, exclude);
|
|
131
177
|
}
|
|
132
|
-
|
|
178
|
+
// Pre-allocate results array to preserve original file ordering
|
|
179
|
+
const results = new Array(files.length);
|
|
133
180
|
const allErrors = [];
|
|
134
181
|
let totalElements = 0;
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
182
|
+
let progressCounter = 0;
|
|
183
|
+
const limit = createScanLimiter(SCAN_CONCURRENCY);
|
|
184
|
+
const promises = files.map((file, i) => limit(async () => {
|
|
185
|
+
// P3: Skip files larger than MAX_FILE_SIZE
|
|
186
|
+
try {
|
|
187
|
+
const fileStats = statSync(file);
|
|
188
|
+
if (fileStats.size > MAX_FILE_SIZE) {
|
|
189
|
+
const sizeMB = (fileStats.size / (1024 * 1024)).toFixed(1);
|
|
190
|
+
console.warn(` Warning: Skipping ${file} (${sizeMB} MB exceeds 1 MB limit)`);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
// If stat fails, let scanFile handle the error
|
|
196
|
+
}
|
|
197
|
+
progressCounter++;
|
|
198
|
+
options.onProgress?.(progressCounter, files.length, file);
|
|
138
199
|
const scanner = getScannerForFile(file);
|
|
139
200
|
if (!scanner)
|
|
140
|
-
|
|
201
|
+
return;
|
|
141
202
|
try {
|
|
142
203
|
const result = await scanner.scanFile(file);
|
|
143
|
-
results
|
|
144
|
-
totalElements += result.elements.length;
|
|
145
|
-
allErrors.push(...result.errors);
|
|
204
|
+
results[i] = result;
|
|
146
205
|
}
|
|
147
206
|
catch (err) {
|
|
148
207
|
allErrors.push(`Error scanning ${file}: ${err}`);
|
|
149
208
|
}
|
|
209
|
+
}));
|
|
210
|
+
await Promise.all(promises);
|
|
211
|
+
// Collect results in original order
|
|
212
|
+
for (const result of results) {
|
|
213
|
+
if (!result)
|
|
214
|
+
continue;
|
|
215
|
+
totalElements += result.elements.length;
|
|
216
|
+
allErrors.push(...result.errors);
|
|
150
217
|
}
|
|
151
218
|
return {
|
|
152
|
-
files: results,
|
|
219
|
+
files: results.filter((r) => r !== undefined),
|
|
153
220
|
totalElements,
|
|
154
221
|
errors: allErrors
|
|
155
222
|
};
|
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
import ts from 'typescript';
|
|
2
1
|
import { readFileSync } from 'fs';
|
|
2
|
+
/**
|
|
3
|
+
* Lazy-loaded TypeScript compiler (23MB, ~221ms load). Only loaded on first TS/JS scan.
|
|
4
|
+
* `_ts` holds the runtime module; type annotations use the `import type ts` namespace.
|
|
5
|
+
*/
|
|
6
|
+
let _ts;
|
|
7
|
+
async function ensureTS() {
|
|
8
|
+
if (!_ts) {
|
|
9
|
+
_ts = (await import('typescript')).default;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
3
12
|
export class TypeScriptScanner {
|
|
4
13
|
languages = ['typescript', 'javascript'];
|
|
5
14
|
canHandle(filePath) {
|
|
@@ -9,11 +18,13 @@ export class TypeScriptScanner {
|
|
|
9
18
|
const language = filePath.endsWith('.ts') || filePath.endsWith('.tsx')
|
|
10
19
|
? 'typescript'
|
|
11
20
|
: 'javascript';
|
|
21
|
+
// Ensure TypeScript compiler is loaded (lazy, cached after first call)
|
|
22
|
+
await ensureTS();
|
|
12
23
|
try {
|
|
13
24
|
const source = readFileSync(filePath, 'utf-8');
|
|
14
|
-
const sourceFile =
|
|
15
|
-
?
|
|
16
|
-
:
|
|
25
|
+
const sourceFile = _ts.createSourceFile(filePath, source, _ts.ScriptTarget.Latest, true, filePath.endsWith('.tsx') || filePath.endsWith('.jsx')
|
|
26
|
+
? _ts.ScriptKind.TSX
|
|
27
|
+
: _ts.ScriptKind.TS);
|
|
17
28
|
const elements = [];
|
|
18
29
|
const errors = [];
|
|
19
30
|
// Extract imports for context
|
|
@@ -41,8 +52,8 @@ export class TypeScriptScanner {
|
|
|
41
52
|
*/
|
|
42
53
|
extractImports(sourceFile) {
|
|
43
54
|
const imports = [];
|
|
44
|
-
|
|
45
|
-
if (
|
|
55
|
+
_ts.forEachChild(sourceFile, node => {
|
|
56
|
+
if (_ts.isImportDeclaration(node)) {
|
|
46
57
|
imports.push(node.getText(sourceFile));
|
|
47
58
|
}
|
|
48
59
|
});
|
|
@@ -82,7 +93,7 @@ export class TypeScriptScanner {
|
|
|
82
93
|
}
|
|
83
94
|
visit(node, sourceFile, elements, filePath, source, imports, packageName, parentClass) {
|
|
84
95
|
// Function declarations
|
|
85
|
-
if (
|
|
96
|
+
if (_ts.isFunctionDeclaration(node) && node.name) {
|
|
86
97
|
const name = node.name.text;
|
|
87
98
|
if (!this.isPrivate(name) && this.isExported(node)) {
|
|
88
99
|
const element = this.extractFunction(node, sourceFile, filePath);
|
|
@@ -93,10 +104,10 @@ export class TypeScriptScanner {
|
|
|
93
104
|
}
|
|
94
105
|
}
|
|
95
106
|
// Arrow functions assigned to const (exported)
|
|
96
|
-
if (
|
|
107
|
+
if (_ts.isVariableStatement(node) && this.isExported(node)) {
|
|
97
108
|
for (const decl of node.declarationList.declarations) {
|
|
98
|
-
if (
|
|
99
|
-
if (
|
|
109
|
+
if (_ts.isIdentifier(decl.name) && decl.initializer) {
|
|
110
|
+
if (_ts.isArrowFunction(decl.initializer) || _ts.isFunctionExpression(decl.initializer)) {
|
|
100
111
|
const name = decl.name.text;
|
|
101
112
|
if (!this.isPrivate(name)) {
|
|
102
113
|
const element = this.extractArrowFunction(decl, decl.initializer, sourceFile, filePath);
|
|
@@ -110,7 +121,7 @@ export class TypeScriptScanner {
|
|
|
110
121
|
}
|
|
111
122
|
}
|
|
112
123
|
// Class declarations
|
|
113
|
-
if (
|
|
124
|
+
if (_ts.isClassDeclaration(node) && node.name) {
|
|
114
125
|
const name = node.name.text;
|
|
115
126
|
if (!this.isPrivate(name) && this.isExported(node)) {
|
|
116
127
|
const classElement = this.extractClass(node, sourceFile, filePath);
|
|
@@ -120,7 +131,7 @@ export class TypeScriptScanner {
|
|
|
120
131
|
elements.push(classElement);
|
|
121
132
|
// Extract methods
|
|
122
133
|
for (const member of node.members) {
|
|
123
|
-
if (
|
|
134
|
+
if (_ts.isMethodDeclaration(member) && member.name) {
|
|
124
135
|
const methodName = this.getPropertyName(member.name);
|
|
125
136
|
if (methodName && !this.isPrivateMember(member) && !methodName.startsWith('_')) {
|
|
126
137
|
const methodElement = this.extractMethod(member, sourceFile, filePath, name);
|
|
@@ -130,7 +141,7 @@ export class TypeScriptScanner {
|
|
|
130
141
|
elements.push(methodElement);
|
|
131
142
|
}
|
|
132
143
|
}
|
|
133
|
-
if (
|
|
144
|
+
if (_ts.isConstructorDeclaration(member)) {
|
|
134
145
|
const ctorElement = this.extractConstructor(member, sourceFile, filePath, name);
|
|
135
146
|
ctorElement.imports = imports;
|
|
136
147
|
ctorElement.packageName = packageName;
|
|
@@ -141,28 +152,28 @@ export class TypeScriptScanner {
|
|
|
141
152
|
}
|
|
142
153
|
}
|
|
143
154
|
// Recurse into children (but not into function/class bodies for top-level scan)
|
|
144
|
-
if (
|
|
145
|
-
|
|
155
|
+
if (_ts.isSourceFile(node) || _ts.isModuleBlock(node)) {
|
|
156
|
+
_ts.forEachChild(node, child => this.visit(child, sourceFile, elements, filePath, source, imports, packageName, parentClass));
|
|
146
157
|
}
|
|
147
158
|
}
|
|
148
159
|
isPrivate(name) {
|
|
149
160
|
return name.startsWith('_') && !name.startsWith('__');
|
|
150
161
|
}
|
|
151
162
|
isPrivateMember(node) {
|
|
152
|
-
const modifiers =
|
|
163
|
+
const modifiers = _ts.getModifiers(node);
|
|
153
164
|
if (modifiers) {
|
|
154
165
|
for (const mod of modifiers) {
|
|
155
|
-
if (mod.kind ===
|
|
166
|
+
if (mod.kind === _ts.SyntaxKind.PrivateKeyword)
|
|
156
167
|
return true;
|
|
157
168
|
}
|
|
158
169
|
}
|
|
159
170
|
return false;
|
|
160
171
|
}
|
|
161
172
|
isExported(node) {
|
|
162
|
-
const modifiers =
|
|
173
|
+
const modifiers = _ts.getModifiers(node);
|
|
163
174
|
if (modifiers) {
|
|
164
175
|
for (const mod of modifiers) {
|
|
165
|
-
if (mod.kind ===
|
|
176
|
+
if (mod.kind === _ts.SyntaxKind.ExportKeyword)
|
|
166
177
|
return true;
|
|
167
178
|
}
|
|
168
179
|
}
|
|
@@ -170,15 +181,15 @@ export class TypeScriptScanner {
|
|
|
170
181
|
return false;
|
|
171
182
|
}
|
|
172
183
|
getPropertyName(name) {
|
|
173
|
-
if (
|
|
184
|
+
if (_ts.isIdentifier(name))
|
|
174
185
|
return name.text;
|
|
175
|
-
if (
|
|
186
|
+
if (_ts.isStringLiteral(name))
|
|
176
187
|
return name.text;
|
|
177
188
|
return undefined;
|
|
178
189
|
}
|
|
179
190
|
extractFunction(node, sourceFile, filePath) {
|
|
180
191
|
const name = node.name?.text ?? 'anonymous';
|
|
181
|
-
const isAsync = node.modifiers?.some(m => m.kind ===
|
|
192
|
+
const isAsync = node.modifiers?.some(m => m.kind === _ts.SyntaxKind.AsyncKeyword) ?? false;
|
|
182
193
|
return {
|
|
183
194
|
kind: 'function',
|
|
184
195
|
name,
|
|
@@ -194,8 +205,8 @@ export class TypeScriptScanner {
|
|
|
194
205
|
};
|
|
195
206
|
}
|
|
196
207
|
extractArrowFunction(decl, fn, sourceFile, filePath) {
|
|
197
|
-
const name =
|
|
198
|
-
const isAsync = fn.modifiers?.some(m => m.kind ===
|
|
208
|
+
const name = _ts.isIdentifier(decl.name) ? decl.name.text : 'anonymous';
|
|
209
|
+
const isAsync = fn.modifiers?.some(m => m.kind === _ts.SyntaxKind.AsyncKeyword) ?? false;
|
|
199
210
|
return {
|
|
200
211
|
kind: 'function',
|
|
201
212
|
name,
|
|
@@ -215,10 +226,10 @@ export class TypeScriptScanner {
|
|
|
215
226
|
let signature = `class ${name}`;
|
|
216
227
|
if (node.heritageClauses) {
|
|
217
228
|
for (const clause of node.heritageClauses) {
|
|
218
|
-
if (clause.token ===
|
|
229
|
+
if (clause.token === _ts.SyntaxKind.ExtendsKeyword) {
|
|
219
230
|
signature += ` extends ${clause.types.map(t => t.getText(sourceFile)).join(', ')}`;
|
|
220
231
|
}
|
|
221
|
-
if (clause.token ===
|
|
232
|
+
if (clause.token === _ts.SyntaxKind.ImplementsKeyword) {
|
|
222
233
|
signature += ` implements ${clause.types.map(t => t.getText(sourceFile)).join(', ')}`;
|
|
223
234
|
}
|
|
224
235
|
}
|
|
@@ -237,7 +248,7 @@ export class TypeScriptScanner {
|
|
|
237
248
|
}
|
|
238
249
|
extractMethod(node, sourceFile, filePath, parentClass) {
|
|
239
250
|
const name = this.getPropertyName(node.name) ?? 'anonymous';
|
|
240
|
-
const isAsync = node.modifiers?.some(m => m.kind ===
|
|
251
|
+
const isAsync = node.modifiers?.some(m => m.kind === _ts.SyntaxKind.AsyncKeyword) ?? false;
|
|
241
252
|
return {
|
|
242
253
|
kind: 'method',
|
|
243
254
|
name,
|
|
@@ -267,7 +278,7 @@ export class TypeScriptScanner {
|
|
|
267
278
|
}
|
|
268
279
|
extractParameters(params, sourceFile) {
|
|
269
280
|
return params.map(p => {
|
|
270
|
-
const name =
|
|
281
|
+
const name = _ts.isIdentifier(p.name) ? p.name.text : p.name.getText(sourceFile);
|
|
271
282
|
return {
|
|
272
283
|
name: p.dotDotDotToken ? `...${name}` : name,
|
|
273
284
|
type: p.type ? p.type.getText(sourceFile) : undefined,
|
|
@@ -276,7 +287,7 @@ export class TypeScriptScanner {
|
|
|
276
287
|
});
|
|
277
288
|
}
|
|
278
289
|
buildSignature(node, sourceFile) {
|
|
279
|
-
const async = node.modifiers?.some(m => m.kind ===
|
|
290
|
+
const async = node.modifiers?.some(m => m.kind === _ts.SyntaxKind.AsyncKeyword) ? 'async ' : '';
|
|
280
291
|
const name = node.name.text;
|
|
281
292
|
const typeParams = node.typeParameters
|
|
282
293
|
? `<${node.typeParameters.map(t => t.getText(sourceFile)).join(', ')}>`
|
|
@@ -287,7 +298,7 @@ export class TypeScriptScanner {
|
|
|
287
298
|
}
|
|
288
299
|
buildArrowSignature(decl, fn, sourceFile) {
|
|
289
300
|
const name = decl.name.text;
|
|
290
|
-
const async = fn.modifiers?.some(m => m.kind ===
|
|
301
|
+
const async = fn.modifiers?.some(m => m.kind === _ts.SyntaxKind.AsyncKeyword) ? 'async ' : '';
|
|
291
302
|
const typeParams = fn.typeParameters
|
|
292
303
|
? `<${fn.typeParameters.map(t => t.getText(sourceFile)).join(', ')}>`
|
|
293
304
|
: '';
|
|
@@ -296,7 +307,7 @@ export class TypeScriptScanner {
|
|
|
296
307
|
return `const ${name} = ${async}${typeParams}(${params})${returnType} => ...`;
|
|
297
308
|
}
|
|
298
309
|
buildMethodSignature(node, sourceFile) {
|
|
299
|
-
const async = node.modifiers?.some(m => m.kind ===
|
|
310
|
+
const async = node.modifiers?.some(m => m.kind === _ts.SyntaxKind.AsyncKeyword) ? 'async ' : '';
|
|
300
311
|
const name = this.getPropertyName(node.name);
|
|
301
312
|
const typeParams = node.typeParameters
|
|
302
313
|
? `<${node.typeParameters.map(t => t.getText(sourceFile)).join(', ')}>`
|
package/dist/sentry.d.ts
ADDED
package/dist/sentry.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as Sentry from '@sentry/node';
|
|
2
|
+
const DSN = process.env.SENTRY_DSN;
|
|
3
|
+
export function initSentry() {
|
|
4
|
+
if (!DSN)
|
|
5
|
+
return; // Gracefully disable when no DSN configured
|
|
6
|
+
Sentry.init({
|
|
7
|
+
dsn: DSN,
|
|
8
|
+
tracesSampleRate: 0.2,
|
|
9
|
+
environment: process.env.NODE_ENV || 'production',
|
|
10
|
+
release: process.env.npm_package_version,
|
|
11
|
+
beforeSend(event) {
|
|
12
|
+
// Strip any user file paths for privacy
|
|
13
|
+
if (event.exception?.values) {
|
|
14
|
+
for (const ex of event.exception.values) {
|
|
15
|
+
if (ex.stacktrace?.frames) {
|
|
16
|
+
for (const frame of ex.stacktrace.frames) {
|
|
17
|
+
if (frame.filename && !frame.filename.includes('node_modules')) {
|
|
18
|
+
frame.filename = frame.filename.replace(/\/Users\/[^/]+/, '~');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return event;
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
export { Sentry };
|
package/dist/template/docs.json
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
+
"$schema": "./public/docs-schema.json",
|
|
2
3
|
"name": "Skrypt",
|
|
3
4
|
"description": "AI-powered documentation generation",
|
|
4
5
|
"fonts": {
|
|
@@ -6,8 +7,7 @@
|
|
|
6
7
|
"mono": "JetBrains Mono"
|
|
7
8
|
},
|
|
8
9
|
"theme": {
|
|
9
|
-
"primaryColor": "#3b82f6"
|
|
10
|
-
"accentColor": "#8b5cf6"
|
|
10
|
+
"primaryColor": "#3b82f6"
|
|
11
11
|
},
|
|
12
12
|
"navigation": [
|
|
13
13
|
{
|
|
@@ -33,6 +33,9 @@
|
|
|
33
33
|
"links": [
|
|
34
34
|
{ "title": "GitHub", "url": "https://github.com/debgotwired/skrypt" },
|
|
35
35
|
{ "title": "npm", "url": "https://www.npmjs.com/package/skrypt-ai" }
|
|
36
|
-
]
|
|
36
|
+
],
|
|
37
|
+
"social": {
|
|
38
|
+
"github": "https://github.com/debgotwired/skrypt"
|
|
39
|
+
}
|
|
37
40
|
}
|
|
38
41
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import createMDX from '@next/mdx'
|
|
2
2
|
import remarkGfm from 'remark-gfm'
|
|
3
3
|
import remarkFrontmatter from 'remark-frontmatter'
|
|
4
|
+
import { withSentryConfig } from '@sentry/nextjs'
|
|
4
5
|
|
|
5
6
|
const withMDX = createMDX({
|
|
6
7
|
extension: /\.mdx?$/,
|
|
@@ -47,4 +48,17 @@ const nextConfig = {
|
|
|
47
48
|
},
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
export default withMDX(nextConfig)
|
|
51
|
+
export default withSentryConfig(withMDX(nextConfig), {
|
|
52
|
+
// Suppress Sentry build logs
|
|
53
|
+
silent: true,
|
|
54
|
+
|
|
55
|
+
// Source maps: disabled for now — enable per-customer later
|
|
56
|
+
sourcemaps: {
|
|
57
|
+
disable: true,
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
// Tunnel Sentry events through Next.js to bypass ad-blockers
|
|
61
|
+
tunnelRoute: '/monitoring',
|
|
62
|
+
|
|
63
|
+
// Do not set org/project — each customer configures their own via env vars
|
|
64
|
+
})
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
"@mdx-js/react": "^3.1.0",
|
|
18
18
|
"@next/mdx": "^15.3.0",
|
|
19
19
|
"@orama/orama": "^3.1.0",
|
|
20
|
+
"@sentry/nextjs": "^10.45.0",
|
|
21
|
+
"ai": "^4.0.0",
|
|
20
22
|
"clsx": "^2.1.0",
|
|
21
23
|
"gray-matter": "^4.0.3",
|
|
22
24
|
"lucide-react": "^0.500.0",
|
|
@@ -26,7 +28,6 @@
|
|
|
26
28
|
"react-dom": "^19.0.0",
|
|
27
29
|
"remark-frontmatter": "^5.0.0",
|
|
28
30
|
"remark-gfm": "^4.0.0",
|
|
29
|
-
"ai": "^4.0.0",
|
|
30
31
|
"shiki": "^3.0.0",
|
|
31
32
|
"streamdown": "^1.0.0",
|
|
32
33
|
"tailwind-merge": "^3.0.0",
|
|
@@ -34,8 +35,8 @@
|
|
|
34
35
|
},
|
|
35
36
|
"optionalDependencies": {
|
|
36
37
|
"@codesandbox/sandpack-react": "^2.20.0",
|
|
37
|
-
"
|
|
38
|
-
"
|
|
38
|
+
"@scalar/nextjs-api-reference": "^0.4.0",
|
|
39
|
+
"mermaid": "^11.13.0"
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
|
41
42
|
"@tailwindcss/postcss": "^4.1.0",
|