UNPKG

@mikro-orm/core

Version:

TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.

141 lines (140 loc) 5.24 kB
// Resolve a `Type<unknown> | Constructor<Type<unknown>>` to an instance. Each built-in `Type` has // `__mappedType` on its prototype so any constructed instance carries it; a missing brand means a // raw constructor that we instantiate. const toTypeInstance = (value) => { return value.__mappedType ? value : new value(); }; /** * Stored procedure or function declaration. Register instances via the `routines` config option * passed to `MikroORM.init`, then call them through `em.callRoutine(routine, args)`. * * `TArgs` and `TReturn` are inferred from the literal config, so `em.callRoutine` is fully typed * without threading generics through the call site. Reach for {@link Routine.create} when the * inferred type is too loose — typically an `object` return that should be a concrete shape * rather than `Dictionary`. * * @example * ```ts * const HashUser = new Routine({ * name: 'hash_user', * type: 'function', * params: { * name: { type: 'varchar(255)' }, * salt: { type: 'varchar(255)' }, * }, * returns: { runtimeType: 'string', columnType: 'char(40)' }, * body: 'SELECT SHA1(CONCAT(name, salt))', * }); * * await MikroORM.init({ entities: [User], routines: [HashUser] }); * * // args typed as `{ name: string; salt: string }`, result typed as `string`: * const hash = await em.callRoutine(HashUser, { name: 'jon', salt: 'pepper' }); * ``` */ export class Routine { name; type; schema; comment; security; definer; deterministic; dataAccess; language; body; expression; bodyJs; returns; returnCustomType; ignoreSchemaChanges; params; constructor(config) { this.name = config.name; this.type = config.type; this.schema = config.schema; this.comment = config.comment; this.security = config.security; this.definer = config.definer; this.deterministic = config.deterministic; this.dataAccess = config.dataAccess; this.language = config.language; this.body = config.body; this.expression = config.expression; this.bodyJs = config.bodyJs; this.returns = config.returns; this.ignoreSchemaChanges = config.ignoreSchemaChanges; this.params = Object.entries(config.params ?? {}).map(([name, opts], index) => { const explicitCustom = opts.customType ? toTypeInstance(opts.customType) : undefined; const rawType = opts.type; let type; let customType; if (rawType == null || typeof rawType === 'string') { type = rawType ?? 'string'; customType = explicitCustom; } else { // A `Type` instance or constructor at `type` both defines the column (via // `getColumnType` at schema-gen time) and acts as the default `customType` for // marshalling. An explicit `customType` on the same param overrides the marshalling side. const instance = toTypeInstance(rawType); type = instance; customType = explicitCustom ?? instance; } return { name, direction: opts.direction ?? 'in', type, runtimeType: opts.runtimeType ?? 'any', columnTypes: [typeof type === 'string' ? type : ''], customType, ref: opts.ref, length: opts.length, precision: opts.precision, scale: opts.scale, nullable: opts.nullable, defaultRaw: opts.defaultRaw, index, }; }); if (config.returns && typeof config.returns === 'object') { // `returns: { type: SomeType }` is the canonical scalar-return shape — the Type drives // both the column type (via `getColumnType` at schema-gen time) and the marshalling. if ('type' in config.returns && config.returns.type) { this.returnCustomType = toTypeInstance(config.returns.type); } else if ('customType' in config.returns && config.returns.customType) { this.returnCustomType = toTypeInstance(config.returns.customType); } } } /** @internal */ createParamMappingObject() { return this.params.reduce((o, p) => { o[p.name] = p.name; return o; }, {}); } /** * Overrides the inferred TArgs/TReturn. Omit a generic to keep inference; pass `never` in the * args slot to refine only the return type. * * @example * ```ts * interface UserStats { totalOrders: number; lastOrderAt: Date } * const GetStats = Routine.create<never, UserStats>({ * name: 'get_user_stats', * type: 'function', * params: { user_id: { type: 'int', runtimeType: 'number' } }, * returns: { runtimeType: 'object', columnType: 'json' }, * body: '...', * }); * ``` */ static create(config) { return new Routine(config); } static is(item) { return item instanceof Routine; } }