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
|
@@ -123,7 +123,12 @@ export function SearchDialog({ open, onClose }: SearchDialogProps) {
|
|
|
123
123
|
setSelectedIndex(0)
|
|
124
124
|
setRecentSearches(getRecentSearches())
|
|
125
125
|
|
|
126
|
+
// Lock body scroll while dialog is open
|
|
127
|
+
const previousOverflow = document.body.style.overflow
|
|
128
|
+
document.body.style.overflow = 'hidden'
|
|
129
|
+
|
|
126
130
|
return () => {
|
|
131
|
+
document.body.style.overflow = previousOverflow
|
|
127
132
|
previouslyFocused?.focus()
|
|
128
133
|
}
|
|
129
134
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export async function register() {
|
|
2
|
+
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
|
3
|
+
await import('../sentry.server.config')
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
if (process.env.NEXT_RUNTIME === 'edge') {
|
|
7
|
+
await import('../sentry.edge.config')
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export { captureRequestError as onRequestError } from '@sentry/nextjs'
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized docs.json config loader with validation.
|
|
3
|
+
*
|
|
4
|
+
* Instead of raw JSON.parse scattered across files, all config
|
|
5
|
+
* access goes through loadDocsConfig() which validates, warns
|
|
6
|
+
* on bad values, and falls back to safe defaults.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { readFileSync } from 'fs'
|
|
10
|
+
import { join } from 'path'
|
|
11
|
+
import { SUPPORTED_SANS_FONTS, SUPPORTED_MONO_FONTS } from './fonts'
|
|
12
|
+
import type { Navigation } from './navigation'
|
|
13
|
+
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Types
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
export interface DocsConfig {
|
|
19
|
+
$schema?: string
|
|
20
|
+
name: string
|
|
21
|
+
description?: string
|
|
22
|
+
favicon?: string
|
|
23
|
+
siteUrl?: string
|
|
24
|
+
logo?: string
|
|
25
|
+
headerLinks?: Array<{ title: string; path: string }>
|
|
26
|
+
fonts?: {
|
|
27
|
+
sans?: string
|
|
28
|
+
mono?: string
|
|
29
|
+
}
|
|
30
|
+
theme?: {
|
|
31
|
+
primaryColor?: string
|
|
32
|
+
font?: string // legacy
|
|
33
|
+
}
|
|
34
|
+
navigation: Navigation
|
|
35
|
+
footer?: {
|
|
36
|
+
links?: Array<{ title: string; url: string }>
|
|
37
|
+
social?: {
|
|
38
|
+
github?: string
|
|
39
|
+
twitter?: string
|
|
40
|
+
discord?: string
|
|
41
|
+
linkedin?: string
|
|
42
|
+
}
|
|
43
|
+
copyright?: string
|
|
44
|
+
}
|
|
45
|
+
editLink?: {
|
|
46
|
+
repoUrl?: string
|
|
47
|
+
branch?: string
|
|
48
|
+
docsPath?: string
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Defaults
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
const DEFAULT_CONFIG: DocsConfig = {
|
|
57
|
+
name: 'Documentation',
|
|
58
|
+
navigation: [],
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Validation helpers
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
const HEX_COLOR_RE = /^#[0-9a-fA-F]{3}(?:[0-9a-fA-F]{3})?$/
|
|
66
|
+
|
|
67
|
+
function isNonEmptyString(value: unknown): value is string {
|
|
68
|
+
return typeof value === 'string' && value.trim().length > 0
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function isHexColor(value: unknown): value is string {
|
|
72
|
+
return typeof value === 'string' && HEX_COLOR_RE.test(value)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function isArray(value: unknown): value is unknown[] {
|
|
76
|
+
return Array.isArray(value)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// Loader
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
/** Module-level cache so we only read + validate once per build */
|
|
84
|
+
let _cached: DocsConfig | null = null
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Load and validate docs.json. Returns a typed DocsConfig with safe defaults.
|
|
88
|
+
*
|
|
89
|
+
* - Missing or unreadable file: returns defaults, logs warning.
|
|
90
|
+
* - Invalid JSON: returns defaults, logs warning.
|
|
91
|
+
* - Invalid field values: uses defaults for that field, logs warning.
|
|
92
|
+
*
|
|
93
|
+
* Result is cached for the lifetime of the process (one Next.js build).
|
|
94
|
+
*/
|
|
95
|
+
export function loadDocsConfig(): DocsConfig {
|
|
96
|
+
if (_cached) return _cached
|
|
97
|
+
|
|
98
|
+
const configPath = join(process.cwd(), 'docs.json')
|
|
99
|
+
|
|
100
|
+
// --- Read file ---
|
|
101
|
+
let raw: string
|
|
102
|
+
try {
|
|
103
|
+
raw = readFileSync(configPath, 'utf-8')
|
|
104
|
+
} catch {
|
|
105
|
+
console.warn('[docs.json] File not found or unreadable at', configPath, '— using defaults.')
|
|
106
|
+
_cached = { ...DEFAULT_CONFIG }
|
|
107
|
+
return _cached
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// --- Parse JSON ---
|
|
111
|
+
let parsed: Record<string, unknown>
|
|
112
|
+
try {
|
|
113
|
+
parsed = JSON.parse(raw)
|
|
114
|
+
} catch (err) {
|
|
115
|
+
const message = err instanceof Error ? err.message : String(err)
|
|
116
|
+
console.warn(`[docs.json] Invalid JSON: ${message} — using defaults.`)
|
|
117
|
+
_cached = { ...DEFAULT_CONFIG }
|
|
118
|
+
return _cached
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
122
|
+
console.warn('[docs.json] Expected a JSON object, got', typeof parsed, '— using defaults.')
|
|
123
|
+
_cached = { ...DEFAULT_CONFIG }
|
|
124
|
+
return _cached
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// --- Validate individual fields ---
|
|
128
|
+
const warnings: string[] = []
|
|
129
|
+
|
|
130
|
+
// name (required string)
|
|
131
|
+
let name = DEFAULT_CONFIG.name
|
|
132
|
+
if (isNonEmptyString(parsed.name)) {
|
|
133
|
+
name = parsed.name as string
|
|
134
|
+
} else if (parsed.name !== undefined) {
|
|
135
|
+
warnings.push(`"name" must be a non-empty string, got ${JSON.stringify(parsed.name)} — using "${DEFAULT_CONFIG.name}".`)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// description (optional string)
|
|
139
|
+
const description = isNonEmptyString(parsed.description) ? (parsed.description as string) : undefined
|
|
140
|
+
|
|
141
|
+
// favicon (optional string)
|
|
142
|
+
const favicon = isNonEmptyString(parsed.favicon) ? (parsed.favicon as string) : undefined
|
|
143
|
+
|
|
144
|
+
// siteUrl (optional string)
|
|
145
|
+
const siteUrl = isNonEmptyString(parsed.siteUrl) ? (parsed.siteUrl as string) : undefined
|
|
146
|
+
|
|
147
|
+
// logo (optional string)
|
|
148
|
+
const logo = isNonEmptyString(parsed.logo) ? (parsed.logo as string) : undefined
|
|
149
|
+
|
|
150
|
+
// headerLinks (optional array)
|
|
151
|
+
const headerLinks = isArray(parsed.headerLinks)
|
|
152
|
+
? (parsed.headerLinks as Array<{ title: string; path: string }>)
|
|
153
|
+
: undefined
|
|
154
|
+
|
|
155
|
+
// navigation (required non-empty array)
|
|
156
|
+
let navigation: Navigation = DEFAULT_CONFIG.navigation
|
|
157
|
+
if (isArray(parsed.navigation) && parsed.navigation.length > 0) {
|
|
158
|
+
navigation = parsed.navigation as Navigation
|
|
159
|
+
} else if (!isArray(parsed.navigation)) {
|
|
160
|
+
warnings.push(`"navigation" must be an array, got ${typeof parsed.navigation} — using empty navigation.`)
|
|
161
|
+
} else if (parsed.navigation.length === 0) {
|
|
162
|
+
warnings.push('"navigation" is empty — the sidebar will have no links.')
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// theme (optional object)
|
|
166
|
+
let theme: DocsConfig['theme'] = undefined
|
|
167
|
+
if (parsed.theme && typeof parsed.theme === 'object' && !Array.isArray(parsed.theme)) {
|
|
168
|
+
const rawTheme = parsed.theme as Record<string, unknown>
|
|
169
|
+
const primaryColor = isHexColor(rawTheme.primaryColor) ? rawTheme.primaryColor : undefined
|
|
170
|
+
const font = isNonEmptyString(rawTheme.font) ? (rawTheme.font as string) : undefined
|
|
171
|
+
|
|
172
|
+
if (rawTheme.primaryColor !== undefined && !primaryColor) {
|
|
173
|
+
warnings.push(`"theme.primaryColor" must be a valid hex color (e.g. "#171717"), got ${JSON.stringify(rawTheme.primaryColor)} — ignoring.`)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
theme = { primaryColor, font }
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// fonts (optional object with known font names)
|
|
180
|
+
let fonts: DocsConfig['fonts'] = undefined
|
|
181
|
+
if (parsed.fonts && typeof parsed.fonts === 'object' && !Array.isArray(parsed.fonts)) {
|
|
182
|
+
const rawFonts = parsed.fonts as Record<string, unknown>
|
|
183
|
+
let sans: string | undefined
|
|
184
|
+
let mono: string | undefined
|
|
185
|
+
|
|
186
|
+
if (isNonEmptyString(rawFonts.sans)) {
|
|
187
|
+
if (rawFonts.sans in SUPPORTED_SANS_FONTS) {
|
|
188
|
+
sans = rawFonts.sans as string
|
|
189
|
+
} else {
|
|
190
|
+
warnings.push(`"fonts.sans" value "${rawFonts.sans}" is not a supported font — falling back to default. Supported: ${Object.keys(SUPPORTED_SANS_FONTS).join(', ')}.`)
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (isNonEmptyString(rawFonts.mono)) {
|
|
195
|
+
if (rawFonts.mono in SUPPORTED_MONO_FONTS) {
|
|
196
|
+
mono = rawFonts.mono as string
|
|
197
|
+
} else {
|
|
198
|
+
warnings.push(`"fonts.mono" value "${rawFonts.mono}" is not a supported font — falling back to default. Supported: ${Object.keys(SUPPORTED_MONO_FONTS).join(', ')}.`)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
fonts = { sans, mono }
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// footer (optional)
|
|
206
|
+
const footer = parsed.footer && typeof parsed.footer === 'object' && !Array.isArray(parsed.footer)
|
|
207
|
+
? (parsed.footer as DocsConfig['footer'])
|
|
208
|
+
: undefined
|
|
209
|
+
|
|
210
|
+
// editLink (optional)
|
|
211
|
+
const editLink = parsed.editLink && typeof parsed.editLink === 'object' && !Array.isArray(parsed.editLink)
|
|
212
|
+
? (parsed.editLink as DocsConfig['editLink'])
|
|
213
|
+
: undefined
|
|
214
|
+
|
|
215
|
+
// --- Log warnings ---
|
|
216
|
+
for (const w of warnings) {
|
|
217
|
+
console.warn(`[docs.json] ${w}`)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
_cached = {
|
|
221
|
+
name,
|
|
222
|
+
description,
|
|
223
|
+
favicon,
|
|
224
|
+
siteUrl,
|
|
225
|
+
logo,
|
|
226
|
+
headerLinks,
|
|
227
|
+
fonts,
|
|
228
|
+
theme,
|
|
229
|
+
navigation,
|
|
230
|
+
footer,
|
|
231
|
+
editLink,
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return _cached
|
|
235
|
+
}
|
|
@@ -83,9 +83,9 @@ function sanitize(name: string): string {
|
|
|
83
83
|
*
|
|
84
84
|
* Unrecognized font names fall back to defaults.
|
|
85
85
|
*/
|
|
86
|
-
export function resolveFonts(docsConfig:
|
|
87
|
-
const fonts = docsConfig.fonts
|
|
88
|
-
const legacyFont =
|
|
86
|
+
export function resolveFonts(docsConfig: { fonts?: { sans?: string; mono?: string }; theme?: { font?: string } }): ResolvedFonts {
|
|
87
|
+
const fonts = docsConfig.fonts
|
|
88
|
+
const legacyFont = docsConfig.theme?.font
|
|
89
89
|
|
|
90
90
|
// Resolve sans font
|
|
91
91
|
const rawSans = fonts?.sans || legacyFont || DEFAULT_SANS
|
package/dist/testing/runner.js
CHANGED
|
@@ -159,7 +159,7 @@ async function installNodeDeps(deps, tempDir, envVars) {
|
|
|
159
159
|
}
|
|
160
160
|
writeFileSync(join(tempDir, 'package.json'), JSON.stringify(pkg));
|
|
161
161
|
const env = buildCleanEnv(envVars);
|
|
162
|
-
await executeWithTimeout('npm', ['install', '--prefix', tempDir, '--no-audit', '--no-fund'], 30000, tempDir, env);
|
|
162
|
+
await executeWithTimeout('npm', ['install', '--prefix', tempDir, '--no-audit', '--no-fund', '--ignore-scripts'], 30000, tempDir, env);
|
|
163
163
|
}
|
|
164
164
|
/**
|
|
165
165
|
* Install Python dependencies in a temp directory using pip
|
|
@@ -184,6 +184,7 @@ function executeWithTimeout(command, args, timeoutMs, cwd, env) {
|
|
|
184
184
|
let stdout = '';
|
|
185
185
|
let stderr = '';
|
|
186
186
|
let timedOut = false;
|
|
187
|
+
let resolved = false;
|
|
187
188
|
const MAX_BUFFER = 1024 * 1024; // 1MB cap to prevent OOM
|
|
188
189
|
const timeout = setTimeout(() => {
|
|
189
190
|
timedOut = true;
|
|
@@ -204,6 +205,9 @@ function executeWithTimeout(command, args, timeoutMs, cwd, env) {
|
|
|
204
205
|
}
|
|
205
206
|
});
|
|
206
207
|
proc.on('close', (code) => {
|
|
208
|
+
if (resolved)
|
|
209
|
+
return;
|
|
210
|
+
resolved = true;
|
|
207
211
|
clearTimeout(timeout);
|
|
208
212
|
resolve({
|
|
209
213
|
exitCode: timedOut ? 1 : (code ?? 1),
|
|
@@ -213,6 +217,9 @@ function executeWithTimeout(command, args, timeoutMs, cwd, env) {
|
|
|
213
217
|
});
|
|
214
218
|
});
|
|
215
219
|
proc.on('error', (err) => {
|
|
220
|
+
if (resolved)
|
|
221
|
+
return;
|
|
222
|
+
resolved = true;
|
|
216
223
|
clearTimeout(timeout);
|
|
217
224
|
resolve({
|
|
218
225
|
exitCode: 1,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skrypt-ai",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "AI-powered documentation generator with code examples",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/cli.js",
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"license": "Elastic-2.0",
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@anthropic-ai/sdk": "^0.78.0",
|
|
51
|
+
"@sentry/node": "^10.45.0",
|
|
51
52
|
"chokidar": "^5.0.0",
|
|
52
53
|
"commander": "^14.0.3",
|
|
53
54
|
"js-yaml": "^4.1.1",
|