import { sql } from 'drizzle-orm'; import { blob, integer, primaryKey, real, sqliteTable, text, uniqueIndex } from 'drizzle-orm/sqlite-core'; // --------------------------------------------------------------------------- // repositories // --------------------------------------------------------------------------- export const repositories = sqliteTable('repositories', { id: text('id').primaryKey(), // e.g. "/facebook/react" or "/local/my-sdk" title: text('title').notNull(), description: text('description'), source: text('source', { enum: ['github', 'local'] }).notNull(), sourceUrl: text('source_url').notNull(), // GitHub URL or absolute local path branch: text('branch').default('main'), state: text('state', { enum: ['pending', 'indexing', 'indexed', 'error'] }) .notNull() .default('pending'), totalSnippets: integer('total_snippets').default(0), totalTokens: integer('total_tokens').default(0), trustScore: real('trust_score').default(0), // 0.0–10.0 benchmarkScore: real('benchmark_score').default(0), // 0.0–100.0; reserved for future quality metrics stars: integer('stars'), // TODO: encrypt at rest in production; stored as plaintext for v1 githubToken: text('github_token'), lastIndexedAt: integer('last_indexed_at', { mode: 'timestamp' }), createdAt: integer('created_at', { mode: 'timestamp' }).notNull(), updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull() }); // --------------------------------------------------------------------------- // repository_versions // --------------------------------------------------------------------------- export const repositoryVersions = sqliteTable('repository_versions', { id: text('id').primaryKey(), // e.g. "/facebook/react/v18.3.0" repositoryId: text('repository_id') .notNull() .references(() => repositories.id, { onDelete: 'cascade' }), tag: text('tag').notNull(), // git tag or branch name title: text('title'), commitHash: text('commit_hash'), // immutable commit SHA-1 resolved from tag state: text('state', { enum: ['pending', 'indexing', 'indexed', 'error'] }) .notNull() .default('pending'), totalSnippets: integer('total_snippets').default(0), indexedAt: integer('indexed_at', { mode: 'timestamp' }), createdAt: integer('created_at', { mode: 'timestamp' }).notNull() }); // --------------------------------------------------------------------------- // documents // --------------------------------------------------------------------------- export const documents = sqliteTable('documents', { id: text('id').primaryKey(), // UUID repositoryId: text('repository_id') .notNull() .references(() => repositories.id, { onDelete: 'cascade' }), versionId: text('version_id').references(() => repositoryVersions.id, { onDelete: 'cascade' }), filePath: text('file_path').notNull(), // relative path within repo title: text('title'), language: text('language'), // e.g. "typescript", "markdown" tokenCount: integer('token_count').default(0), checksum: text('checksum').notNull(), // SHA-256 of file content indexedAt: integer('indexed_at', { mode: 'timestamp' }).notNull() }); // --------------------------------------------------------------------------- // snippets // --------------------------------------------------------------------------- export const snippets = sqliteTable('snippets', { id: text('id').primaryKey(), // UUID documentId: text('document_id') .notNull() .references(() => documents.id, { onDelete: 'cascade' }), repositoryId: text('repository_id') .notNull() .references(() => repositories.id, { onDelete: 'cascade' }), versionId: text('version_id').references(() => repositoryVersions.id, { onDelete: 'cascade' }), type: text('type', { enum: ['code', 'info'] }).notNull(), title: text('title'), content: text('content').notNull(), // searchable text / code language: text('language'), breadcrumb: text('breadcrumb'), // e.g. "Installation > Getting Started" tokenCount: integer('token_count').default(0), createdAt: integer('created_at', { mode: 'timestamp' }).notNull() }); // --------------------------------------------------------------------------- // embedding_profiles // --------------------------------------------------------------------------- export const embeddingProfiles = sqliteTable('embedding_profiles', { id: text('id').primaryKey(), providerKind: text('provider_kind').notNull(), title: text('title').notNull(), enabled: integer('enabled', { mode: 'boolean' }).notNull().default(true), isDefault: integer('is_default', { mode: 'boolean' }).notNull().default(false), model: text('model').notNull(), dimensions: integer('dimensions').notNull(), config: text('config', { mode: 'json' }).notNull().$type>(), createdAt: integer('created_at').notNull(), updatedAt: integer('updated_at').notNull() }); // --------------------------------------------------------------------------- // snippet_embeddings // --------------------------------------------------------------------------- export const snippetEmbeddings = sqliteTable( 'snippet_embeddings', { snippetId: text('snippet_id') .notNull() .references(() => snippets.id, { onDelete: 'cascade' }), profileId: text('profile_id') .notNull() .references(() => embeddingProfiles.id, { onDelete: 'cascade' }), model: text('model').notNull(), // embedding model identifier dimensions: integer('dimensions').notNull(), embedding: blob('embedding').notNull(), // Float32Array as binary blob createdAt: integer('created_at').notNull() }, (table) => [primaryKey({ columns: [table.snippetId, table.profileId] })] ); // --------------------------------------------------------------------------- // indexing_jobs // --------------------------------------------------------------------------- export const indexingJobs = sqliteTable('indexing_jobs', { id: text('id').primaryKey(), // UUID repositoryId: text('repository_id') .notNull() .references(() => repositories.id, { onDelete: 'cascade' }), versionId: text('version_id'), status: text('status', { enum: ['queued', 'running', 'paused', 'cancelled', 'done', 'failed'] }) .notNull() .default('queued'), progress: integer('progress').default(0), // 0–100 totalFiles: integer('total_files').default(0), processedFiles: integer('processed_files').default(0), error: text('error'), startedAt: integer('started_at', { mode: 'timestamp' }), completedAt: integer('completed_at', { mode: 'timestamp' }), createdAt: integer('created_at', { mode: 'timestamp' }).notNull() }); // --------------------------------------------------------------------------- // repository_configs // --------------------------------------------------------------------------- export const repositoryConfigs = sqliteTable( 'repository_configs', { repositoryId: text('repository_id') .notNull() .references(() => repositories.id, { onDelete: 'cascade' }), versionId: text('version_id'), projectTitle: text('project_title'), description: text('description'), folders: text('folders', { mode: 'json' }).$type(), excludeFolders: text('exclude_folders', { mode: 'json' }).$type(), excludeFiles: text('exclude_files', { mode: 'json' }).$type(), rules: text('rules', { mode: 'json' }).$type(), previousVersions: text('previous_versions', { mode: 'json' }).$type< { tag: string; title: string; commitHash?: string }[] >(), updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull() }, (table) => [ uniqueIndex('uniq_repo_config_base') .on(table.repositoryId) .where(sql`${table.versionId} IS NULL`), uniqueIndex('uniq_repo_config_version') .on(table.repositoryId, table.versionId) .where(sql`${table.versionId} IS NOT NULL`) ] ); // --------------------------------------------------------------------------- // settings // --------------------------------------------------------------------------- export const settings = sqliteTable('settings', { key: text('key').primaryKey(), value: text('value', { mode: 'json' }), updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull() }); // --------------------------------------------------------------------------- // Inferred TypeScript types // --------------------------------------------------------------------------- export type Repository = typeof repositories.$inferSelect; export type NewRepository = typeof repositories.$inferInsert; export type RepositoryVersion = typeof repositoryVersions.$inferSelect; export type NewRepositoryVersion = typeof repositoryVersions.$inferInsert; export type Document = typeof documents.$inferSelect; export type NewDocument = typeof documents.$inferInsert; export type Snippet = typeof snippets.$inferSelect; export type NewSnippet = typeof snippets.$inferInsert; export type EmbeddingProfile = typeof embeddingProfiles.$inferSelect; export type NewEmbeddingProfile = typeof embeddingProfiles.$inferInsert; export type SnippetEmbedding = typeof snippetEmbeddings.$inferSelect; export type NewSnippetEmbedding = typeof snippetEmbeddings.$inferInsert; export type IndexingJob = typeof indexingJobs.$inferSelect; export type NewIndexingJob = typeof indexingJobs.$inferInsert; export type RepositoryConfig = typeof repositoryConfigs.$inferSelect; export type NewRepositoryConfig = typeof repositoryConfigs.$inferInsert; export type Settings = typeof settings.$inferSelect; export type NewSettings = typeof settings.$inferInsert;