Files
aperture/src/server/schemas/schemasFromPackage.ts

116 lines
3.6 KiB
TypeScript

import * as path from 'path';
import * as fs from 'fs';
export interface SchemaManifest {
roots: Record<string, string>;
fragments: Record<string, string>;
version: string;
}
export interface SchemaCollection {
roots: Record<string, any>;
fragments: Record<string, any>;
readJson: (filePath: string) => any;
toUri: (schema: any) => string;
}
export class SchemaLoader {
private packageRoot: string | null = null;
private manifest: SchemaManifest | null = null;
private cache: Map<string, any> = new Map();
constructor() {
this.initializePackageRoot();
}
private initializePackageRoot(): void {
try {
// Try to resolve the external types package
const packagePath = require.resolve('@your/openapi-types/package.json');
this.packageRoot = path.dirname(packagePath);
} catch (e) {
console.warn('Could not resolve @your/openapi-types package. Using fallback.');
// Fallback to a local path or handle gracefully
this.packageRoot = null;
}
}
async loadManifest(): Promise<SchemaManifest> {
if (this.manifest) {
return this.manifest;
}
if (!this.packageRoot) {
throw new Error('Package root not found. @your/openapi-types package is required.');
}
const manifestPath = path.join(this.packageRoot, 'schema-manifest.json');
try {
const manifestContent = fs.readFileSync(manifestPath, 'utf8');
this.manifest = JSON.parse(manifestContent);
if (!this.manifest) {
throw new Error('Manifest is null');
}
return this.manifest;
} catch (e) {
throw new Error(`Failed to load schema manifest from ${manifestPath}: ${e}`);
}
}
async getAllSchemas(): Promise<SchemaCollection> {
const manifest = await this.loadManifest();
const roots: Record<string, any> = {};
const fragments: Record<string, any> = {};
// Load root schemas
for (const [version, schemaPath] of Object.entries(manifest.roots)) {
const fullPath = path.join(this.packageRoot!, schemaPath);
roots[version] = this.readJson(fullPath);
}
// Load fragment schemas
for (const [name, schemaPath] of Object.entries(manifest.fragments)) {
const fullPath = path.join(this.packageRoot!, schemaPath);
fragments[name] = this.readJson(fullPath);
}
return {
roots,
fragments,
readJson: this.readJson.bind(this),
toUri: this.toUri.bind(this)
};
}
readJson(filePath: string): any {
if (this.cache.has(filePath)) {
return this.cache.get(filePath);
}
try {
const content = fs.readFileSync(filePath, 'utf8');
const parsed = JSON.parse(content);
this.cache.set(filePath, parsed);
return parsed;
} catch (e) {
throw new Error(`Failed to read JSON from ${filePath}: ${e}`);
}
}
toUri(schema: any): string {
// Generate a URI for the schema (used by language services)
return `aperture://schema/${Date.now()}`;
}
async getSchemaForVersion(version: string): Promise<any> {
const schemas = await this.getAllSchemas();
return schemas.roots[version] || null;
}
async getFragmentSchema(name: string): Promise<any> {
const schemas = await this.getAllSchemas();
return schemas.fragments[name] || null;
}
}