import { compare } from "semver"; import type { FumaDBAdapter, FumaDBAdapterContext } from "./adapters"; import type { Migrator } from "./migration-engine/create"; import type { AbstractQuery } from "./query"; import type { AnySchema } from "./schema"; import { createNameVariantsBuilder, type NameVariantsBuilder, } from "./schema/name-variants-builder"; import type { LibraryConfig } from "./shared/config"; export * from "./shared/config"; export * from "./shared/providers"; type Last = T extends [...infer _, infer L] ? L : T[number]; export interface FumaDB { schemas: Schemas; adapter: FumaDBAdapter; /** * Shorthand for `orm()` latest schema version * @deprecated use `orm()` directly */ readonly abstract: AbstractQuery>; /** * Get current schema version */ version: () => Promise; orm: ( version: V ) => AbstractQuery>; /** * Kysely & MongoDB only */ createMigrator: () => Migrator; /** * ORM only */ generateSchema: ( version: Schemas[number]["version"] | "latest", name?: string ) => { code: string; path: string; }; } export interface FumaDBFactory { /** * A static type-checker */ version: (target: T) => T; /** * Configure consumer-side integration */ client: (adapter: FumaDBAdapter) => FumaDB; /** * Set name variants */ names: NameVariantsBuilder>; } export type InferFumaDB> = Factory extends FumaDBFactory ? FumaDB : never; export type InferAbstractQuery< Factory extends FumaDB, Version extends string, > = Factory extends FumaDB ? AbstractQuery> & { version: Version; } : never; export function fumadb( config: LibraryConfig ): FumaDBFactory { const schemas = config.schemas.sort((a, b) => compare(a.version, b.version)); return { names: createNameVariantsBuilder(config.namespace, schemas, (schemas) => { return fumadb({ ...config, schemas, }); }), version(targetVersion) { return targetVersion; }, client(adapter) { const orms = new Map>(); const adapterContext: FumaDBAdapterContext = { ...config, }; return { adapter, schemas, orm(version) { let orm = orms.get(version); if (orm) return orm; const schema = schemas.find((schema) => schema.version === version); if (!schema) throw new Error(`unknown schema version ${version}`); orm = adapter.createORM.call(adapterContext, schema); orms.set(version, orm); return orm as any; }, async version() { const version = await adapter.getSchemaVersion.call(adapterContext); if (!version) throw new Error(`FumaDB ${config.namespace} is not initialized.`); return version; }, generateSchema(version, name = config.namespace) { if (!adapter.generateSchema) throw new Error("The adapter doesn't support schema API."); let schema: AnySchema; if (version === "latest") { schema = schemas.at(-1)!; } else { schema = schemas.find((schema) => schema.version === version)!; if (!schema) throw new Error(`Invalid version: ${version}`); } return adapter.generateSchema.call(adapterContext, schema, name); }, createMigrator() { if (!adapter.createMigrationEngine) throw new Error("The adapter doesn't support migration engine."); return adapter.createMigrationEngine.call(adapterContext); }, get abstract() { return this.orm(schemas.at(-1)!.version) as any; }, }; }, }; }