chore(FEEDBACK-0001): linting
This commit is contained in:
@@ -32,53 +32,53 @@ Support `trueref.json` configuration files placed in the root of a repository. T
|
||||
// src/lib/server/config/trueref-config.schema.ts
|
||||
|
||||
export interface TrueRefConfig {
|
||||
/**
|
||||
* Override the display name for this library.
|
||||
* 1–100 characters.
|
||||
*/
|
||||
projectTitle?: string;
|
||||
/**
|
||||
* Override the display name for this library.
|
||||
* 1–100 characters.
|
||||
*/
|
||||
projectTitle?: string;
|
||||
|
||||
/**
|
||||
* Description of the library for search ranking.
|
||||
* 10–500 characters.
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* Description of the library for search ranking.
|
||||
* 10–500 characters.
|
||||
*/
|
||||
description?: string;
|
||||
|
||||
/**
|
||||
* Folders to include in indexing (allowlist).
|
||||
* Each entry is a path prefix or regex string.
|
||||
* If empty/absent, all folders are included.
|
||||
* Examples: ["src/", "docs/", "^packages/core"]
|
||||
*/
|
||||
folders?: string[];
|
||||
/**
|
||||
* Folders to include in indexing (allowlist).
|
||||
* Each entry is a path prefix or regex string.
|
||||
* If empty/absent, all folders are included.
|
||||
* Examples: ["src/", "docs/", "^packages/core"]
|
||||
*/
|
||||
folders?: string[];
|
||||
|
||||
/**
|
||||
* Folders to exclude from indexing.
|
||||
* Applied after `folders` allowlist.
|
||||
* Examples: ["test/", "fixtures/", "__mocks__"]
|
||||
*/
|
||||
excludeFolders?: string[];
|
||||
/**
|
||||
* Folders to exclude from indexing.
|
||||
* Applied after `folders` allowlist.
|
||||
* Examples: ["test/", "fixtures/", "__mocks__"]
|
||||
*/
|
||||
excludeFolders?: string[];
|
||||
|
||||
/**
|
||||
* Exact filenames to exclude (no path, no regex).
|
||||
* Examples: ["README.md", "CHANGELOG.md", "jest.config.ts"]
|
||||
*/
|
||||
excludeFiles?: string[];
|
||||
/**
|
||||
* Exact filenames to exclude (no path, no regex).
|
||||
* Examples: ["README.md", "CHANGELOG.md", "jest.config.ts"]
|
||||
*/
|
||||
excludeFiles?: string[];
|
||||
|
||||
/**
|
||||
* Best practices / rules to inject at the top of every query-docs response.
|
||||
* Each rule: 5–500 characters.
|
||||
* Maximum 20 rules.
|
||||
*/
|
||||
rules?: string[];
|
||||
/**
|
||||
* Best practices / rules to inject at the top of every query-docs response.
|
||||
* Each rule: 5–500 characters.
|
||||
* Maximum 20 rules.
|
||||
*/
|
||||
rules?: string[];
|
||||
|
||||
/**
|
||||
* Previously released versions to make available for versioned queries.
|
||||
*/
|
||||
previousVersions?: Array<{
|
||||
tag: string; // git tag (e.g. "v1.2.3")
|
||||
title: string; // human-readable (e.g. "Version 1.2.3")
|
||||
}>;
|
||||
/**
|
||||
* Previously released versions to make available for versioned queries.
|
||||
*/
|
||||
previousVersions?: Array<{
|
||||
tag: string; // git tag (e.g. "v1.2.3")
|
||||
title: string; // human-readable (e.g. "Version 1.2.3")
|
||||
}>;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -88,14 +88,14 @@ export interface TrueRefConfig {
|
||||
|
||||
```typescript
|
||||
const CONFIG_CONSTRAINTS = {
|
||||
projectTitle: { minLength: 1, maxLength: 100 },
|
||||
description: { minLength: 10, maxLength: 500 },
|
||||
folders: { maxItems: 50, maxLength: 200 }, // per entry
|
||||
excludeFolders: { maxItems: 50, maxLength: 200 },
|
||||
excludeFiles: { maxItems: 100, maxLength: 200 },
|
||||
rules: { maxItems: 20, minLength: 5, maxLength: 500 },
|
||||
previousVersions: { maxItems: 50 },
|
||||
versionTag: { pattern: /^v?\d+\.\d+(\.\d+)?(-.*)?$/ },
|
||||
projectTitle: { minLength: 1, maxLength: 100 },
|
||||
description: { minLength: 10, maxLength: 500 },
|
||||
folders: { maxItems: 50, maxLength: 200 }, // per entry
|
||||
excludeFolders: { maxItems: 50, maxLength: 200 },
|
||||
excludeFiles: { maxItems: 100, maxLength: 200 },
|
||||
rules: { maxItems: 20, minLength: 5, maxLength: 500 },
|
||||
previousVersions: { maxItems: 50 },
|
||||
versionTag: { pattern: /^v?\d+\.\d+(\.\d+)?(-.*)?$/ }
|
||||
};
|
||||
```
|
||||
|
||||
@@ -107,94 +107,96 @@ const CONFIG_CONSTRAINTS = {
|
||||
// src/lib/server/config/config-parser.ts
|
||||
|
||||
export interface ParsedConfig {
|
||||
config: TrueRefConfig;
|
||||
source: 'trueref.json' | 'context7.json';
|
||||
warnings: string[];
|
||||
config: TrueRefConfig;
|
||||
source: 'trueref.json' | 'context7.json';
|
||||
warnings: string[];
|
||||
}
|
||||
|
||||
export function parseConfigFile(content: string, filename: string): ParsedConfig {
|
||||
let raw: unknown;
|
||||
let raw: unknown;
|
||||
|
||||
try {
|
||||
raw = JSON.parse(content);
|
||||
} catch (e) {
|
||||
throw new ConfigParseError(`${filename} is not valid JSON: ${(e as Error).message}`);
|
||||
}
|
||||
try {
|
||||
raw = JSON.parse(content);
|
||||
} catch (e) {
|
||||
throw new ConfigParseError(`${filename} is not valid JSON: ${(e as Error).message}`);
|
||||
}
|
||||
|
||||
if (typeof raw !== 'object' || raw === null) {
|
||||
throw new ConfigParseError(`${filename} must be a JSON object`);
|
||||
}
|
||||
if (typeof raw !== 'object' || raw === null) {
|
||||
throw new ConfigParseError(`${filename} must be a JSON object`);
|
||||
}
|
||||
|
||||
const config = raw as Record<string, unknown>;
|
||||
const validated: TrueRefConfig = {};
|
||||
const warnings: string[] = [];
|
||||
const config = raw as Record<string, unknown>;
|
||||
const validated: TrueRefConfig = {};
|
||||
const warnings: string[] = [];
|
||||
|
||||
// projectTitle
|
||||
if (config.projectTitle !== undefined) {
|
||||
if (typeof config.projectTitle !== 'string') {
|
||||
warnings.push('projectTitle must be a string, ignoring');
|
||||
} else if (config.projectTitle.length > 100) {
|
||||
validated.projectTitle = config.projectTitle.slice(0, 100);
|
||||
warnings.push('projectTitle truncated to 100 characters');
|
||||
} else {
|
||||
validated.projectTitle = config.projectTitle;
|
||||
}
|
||||
}
|
||||
// projectTitle
|
||||
if (config.projectTitle !== undefined) {
|
||||
if (typeof config.projectTitle !== 'string') {
|
||||
warnings.push('projectTitle must be a string, ignoring');
|
||||
} else if (config.projectTitle.length > 100) {
|
||||
validated.projectTitle = config.projectTitle.slice(0, 100);
|
||||
warnings.push('projectTitle truncated to 100 characters');
|
||||
} else {
|
||||
validated.projectTitle = config.projectTitle;
|
||||
}
|
||||
}
|
||||
|
||||
// description
|
||||
if (config.description !== undefined) {
|
||||
if (typeof config.description === 'string') {
|
||||
validated.description = config.description.slice(0, 500);
|
||||
}
|
||||
}
|
||||
// description
|
||||
if (config.description !== undefined) {
|
||||
if (typeof config.description === 'string') {
|
||||
validated.description = config.description.slice(0, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// folders / excludeFolders / excludeFiles — validated as string arrays
|
||||
for (const field of ['folders', 'excludeFolders', 'excludeFiles'] as const) {
|
||||
if (config[field] !== undefined) {
|
||||
if (!Array.isArray(config[field])) {
|
||||
warnings.push(`${field} must be an array, ignoring`);
|
||||
} else {
|
||||
validated[field] = (config[field] as unknown[])
|
||||
.filter((item): item is string => {
|
||||
if (typeof item !== 'string') {
|
||||
warnings.push(`${field} entry must be a string, skipping: ${item}`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.slice(0, field === 'excludeFiles' ? 100 : 50);
|
||||
}
|
||||
}
|
||||
}
|
||||
// folders / excludeFolders / excludeFiles — validated as string arrays
|
||||
for (const field of ['folders', 'excludeFolders', 'excludeFiles'] as const) {
|
||||
if (config[field] !== undefined) {
|
||||
if (!Array.isArray(config[field])) {
|
||||
warnings.push(`${field} must be an array, ignoring`);
|
||||
} else {
|
||||
validated[field] = (config[field] as unknown[])
|
||||
.filter((item): item is string => {
|
||||
if (typeof item !== 'string') {
|
||||
warnings.push(`${field} entry must be a string, skipping: ${item}`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.slice(0, field === 'excludeFiles' ? 100 : 50);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rules
|
||||
if (config.rules !== undefined) {
|
||||
if (Array.isArray(config.rules)) {
|
||||
validated.rules = (config.rules as unknown[])
|
||||
.filter((r): r is string => typeof r === 'string' && r.length >= 5)
|
||||
.map(r => r.slice(0, 500))
|
||||
.slice(0, 20);
|
||||
}
|
||||
}
|
||||
// rules
|
||||
if (config.rules !== undefined) {
|
||||
if (Array.isArray(config.rules)) {
|
||||
validated.rules = (config.rules as unknown[])
|
||||
.filter((r): r is string => typeof r === 'string' && r.length >= 5)
|
||||
.map((r) => r.slice(0, 500))
|
||||
.slice(0, 20);
|
||||
}
|
||||
}
|
||||
|
||||
// previousVersions
|
||||
if (config.previousVersions !== undefined) {
|
||||
if (Array.isArray(config.previousVersions)) {
|
||||
validated.previousVersions = (config.previousVersions as unknown[])
|
||||
.filter((v): v is { tag: string; title: string } =>
|
||||
typeof v === 'object' && v !== null &&
|
||||
typeof (v as Record<string, unknown>).tag === 'string' &&
|
||||
typeof (v as Record<string, unknown>).title === 'string'
|
||||
)
|
||||
.slice(0, 50);
|
||||
}
|
||||
}
|
||||
// previousVersions
|
||||
if (config.previousVersions !== undefined) {
|
||||
if (Array.isArray(config.previousVersions)) {
|
||||
validated.previousVersions = (config.previousVersions as unknown[])
|
||||
.filter(
|
||||
(v): v is { tag: string; title: string } =>
|
||||
typeof v === 'object' &&
|
||||
v !== null &&
|
||||
typeof (v as Record<string, unknown>).tag === 'string' &&
|
||||
typeof (v as Record<string, unknown>).title === 'string'
|
||||
)
|
||||
.slice(0, 50);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
config: validated,
|
||||
source: filename.startsWith('trueref') ? 'trueref.json' : 'context7.json',
|
||||
warnings,
|
||||
};
|
||||
return {
|
||||
config: validated,
|
||||
source: filename.startsWith('trueref') ? 'trueref.json' : 'context7.json',
|
||||
warnings
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
@@ -219,21 +221,15 @@ When `query-docs` returns results, `rules` from `repository_configs` are prepend
|
||||
|
||||
```typescript
|
||||
// In formatters.ts
|
||||
function buildContextResponse(
|
||||
snippets: Snippet[],
|
||||
config: RepositoryConfig | null
|
||||
): string {
|
||||
const parts: string[] = [];
|
||||
function buildContextResponse(snippets: Snippet[], config: RepositoryConfig | null): string {
|
||||
const parts: string[] = [];
|
||||
|
||||
if (config?.rules?.length) {
|
||||
parts.push(
|
||||
'## Library Best Practices\n' +
|
||||
config.rules.map(r => `- ${r}`).join('\n')
|
||||
);
|
||||
}
|
||||
if (config?.rules?.length) {
|
||||
parts.push('## Library Best Practices\n' + config.rules.map((r) => `- ${r}`).join('\n'));
|
||||
}
|
||||
|
||||
// ... append snippet content
|
||||
return parts.join('\n\n---\n\n');
|
||||
// ... append snippet content
|
||||
return parts.join('\n\n---\n\n');
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user