diff --git a/packages/mql-typescript/tests/real.ts b/packages/mql-typescript/tests/real.ts new file mode 100644 index 00000000..36d4e18e --- /dev/null +++ b/packages/mql-typescript/tests/real.ts @@ -0,0 +1,155 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import type * as schema from '../out/schema'; +import type { Document } from 'bson'; + +interface GenericCollectionSchema { + schema: Document; +} +interface GenericDatabaseSchema { + [key: string]: GenericCollectionSchema; +} +interface GenericServerSideSchema { + [key: string]: GenericDatabaseSchema; +} +type StringKey = keyof T & string; + +class Mongo {} + +type CollectionWithSchema< + M extends GenericServerSideSchema = GenericServerSideSchema, + D extends GenericDatabaseSchema = M[keyof M], + C extends GenericCollectionSchema = D[keyof D], + N extends StringKey = StringKey, +> = Collection & { + [k in StringKey as k extends `${N}.${infer S}` ? S : never]: Collection< + M, + D, + D[k], + k + >; +}; + +class Collection< + M extends GenericServerSideSchema = GenericServerSideSchema, + D extends GenericDatabaseSchema = M[keyof M], + C extends GenericCollectionSchema = D[keyof D], + N extends StringKey = StringKey, +> { + _mongo: Mongo; + _database: DatabaseWithSchema; + _name: N; + constructor( + mongo: Mongo, + database: DatabaseWithSchema | Database, + name: N, + ) { + this._mongo = mongo; + this._database = database as DatabaseWithSchema; + this._name = name; + } + getName(): N { + return this._name; + } + async find( + query?: schema.Query, + projection?: Document, + options: Document = {}, + ): Promise | undefined> { + return Promise.resolve(query); + } +} + +type DatabaseWithSchema< + M extends GenericServerSideSchema = GenericServerSideSchema, + D extends GenericDatabaseSchema = GenericDatabaseSchema, +> = Database & { + [k in StringKey]: Collection; +}; + +function isValidCollectionName(name: string): boolean { + return !!name && !/[$\0]/.test(name); +} + +export class Database< + M extends GenericServerSideSchema = GenericServerSideSchema, + D extends GenericDatabaseSchema = GenericDatabaseSchema, +> { + _mongo: Mongo; + _name: StringKey; + _collections: Record, CollectionWithSchema>; + + constructor(mongo: Mongo, name: StringKey) { + this._mongo = mongo; + this._name = name; + const collections: Record< + string, + CollectionWithSchema + > = Object.create(null); + this._collections = collections; + + const proxy = new Proxy(this, { + get: (target, prop): any => { + if (prop in target) { + return (target as any)[prop]; + } + + if ( + typeof prop !== 'string' || + prop.startsWith('_') || + !isValidCollectionName(prop) + ) { + return; + } + + if (!collections[prop]) { + collections[prop] = new Collection( + mongo, + proxy, + prop, + ) as CollectionWithSchema; + } + + return collections[prop]; + }, + }); + return proxy; + } + + getCollection>( + coll: K, + ): CollectionWithSchema { + const collection = new Collection( + this._mongo, + this, + 'myCollection', + ); + + return collection as CollectionWithSchema; + } +} + +async function run() { + const serverSchema = { + myDatabase: { + myCollection: { + schema: { + _id: 'ObjectId', + name: 'string', + age: 'number', + }, + }, + }, + }; + const mongo = new Mongo(); + const db = new Database< + typeof serverSchema, + (typeof serverSchema)['myDatabase'] + >(mongo, 'myDatabase') as DatabaseWithSchema< + typeof serverSchema, + (typeof serverSchema)['myDatabase'] + >; + const query = await db.myCollection.find({ name: 'foo' }); + console.log(query); +} + +run().catch(console.error);