/** * ISO-VARIANCE: * * Some functions defined in isomorphic code are reported as being invalid when called from * client/server code due to them being interpreted contravariantly instead of bivariantly. * Typescript does not yet support variance casting so we resort to any. * * Example bivariant hack seen in react code: * type RefCallback = { bivarianceHack(instance: T | null): void }["bivarianceHack"]; */ export namespace Tyr { /** * This is meant as a trigger to indicate that you are typing something * as "any" for now but which might have a better type for it but you do not * have time at the moment to figure out the right type. * * For example, read: * * ... = (something as Tyr.anny); * * as: * * ... = (something as any); // TODO: figure out a better type */ export type anny = any; export interface MongoDocument { [key: string]: any; } export interface MongoQuery { [key: string]: any; } export interface MongoProjection { [key: string]: number; } export interface Class { new (...args: any[]): T; } export type Numbering = | 'lowercase' | 'uppercase' | 'zero-based' | 'one-based' | 'ordinal' | 'roman' | 'roman-lowercase'; export type ActionTraitType = 'entrance' | 'exit' | 'builtin'; export type ActionTrait = // entrance actions | 'create' | 'edit' | 'view' | 'search' // exit actions | 'save' | 'cancel' // built in actions | 'filter' | 'config' // not used, but like filter control | 'import' | 'export'; export interface AccessResult { allowed: boolean; reason: string; fields?: { effect: 'allow' | 'deny'; names: string[]; }; } export type Metadata = | CollectionInstance | FieldInstance | PathInstance | Document; export interface ErrorOptions { message?: string; suffix?: string; technical?: string; rowNumber?: number; lineNumber?: number; columnNumber?: number; } export interface AppErrorStatic { new (opts?: string | ErrorOptions): UserError; } export const AppError: AppErrorStatic; export interface AppError { message: string; field?: FieldInstance; technical?: string; rowNumber?: number; lineNumber?: number; columnNumber?: number; toString(): string; } export interface SecureErrorStatic { new (opts?: string | ErrorOptions): SecureError; } export const SecureError: SecureErrorStatic; export interface SecureError { message: string; field?: FieldInstance; technical?: string; rowNumber?: number; lineNumber?: number; columnNumber?: number; toString(): string; } export interface UserErrorStatic { new (opts: string | ErrorOptions): UserError; } export const UserError: UserErrorStatic; export interface UserError { message: string; field?: FieldInstance; technical?: string; rowNumber?: number; lineNumber?: number; columnNumber?: number; toString(): string; } export interface TypeStatic { byName: { [key: string]: TypeInstance }; new (...args: any[]): TypeInstance; } export interface TypeDefinition { name: string; typescript?: string; width?: number; } export interface TypeInstance { name: string; def: TypeDefinition; create(field: FieldInstance): any; compare(field: FieldInstance, a: any, b: any): number; format(field: FieldInstance, value: any): string; width?: number; } export interface FieldDefinition< D extends Document = Document > { [key: string]: any; is?: string; unique?: boolean; client?: boolean | (() => boolean); custom?: boolean; db?: boolean; aux?: boolean; historical?: boolean; defaultValue?: any; //inverse?: boolean; label?: string | (() => string); help?: string; placeholder?: string; deprecated?: string | boolean; note?: string; required?: boolean; validateSearch?: boolean; // this function needs to be bivariant, NOT contravariant -- so defining it like a method rather than a callback validate?( this: any, // D -- ISO-VARIANCE field: any // FieldInstance // D -- ISO-VARIANCE ): Promise | string | false | undefined; of?: string | FieldDefinition; cardinality?: string; fields?: { [key: string]: FieldDefinition }; keys?: string | FieldDefinition; numbering?: Numbering; denormal?: MongoDocument; // NestedMongoProjection link?: string; relate?: 'owns' | 'ownedBy' | 'associate'; where?: MongoQuery | ((this: D, opts: any) => MongoQuery); pathLabel?: string; in?: string; min?: number; max?: number; step?: number; labelField?: boolean | 'alternate' | { uses: string[] }; labelImageField?: boolean; orderField?: boolean; pattern?: RegExp; minlength?: number; maxlength?: number; granularity?: string; computed?: boolean; generated?: boolean; width?: number; mentionFeed?: { marker: string; feed: string[]; minimumCharacters: number; }; get?(this: D): any; getClient?(this: D): any; getServer?(this: D): any; set?(this: D, val: any): void; setClient?(this: D, val: any): void; setServer?(this: D, val: any): void; } export interface FieldStatic { new (...args: any[]): FieldInstance; } export interface FieldInstance< D extends Document = Document > { $metaType: 'field'; collection: Tyr.CollectionInstance; // ISO-VARIANCE aux: boolean; computed: boolean; generated: boolean; db: boolean; def: FieldDefinition; dynamicSchema?: any; name: string; path: PathInstance; of?: FieldInstance; parent?: FieldInstance; pathLabel: string; pathName: string; readonly: boolean; spath: string; in: any; keys?: FieldInstance; label: string | (() => string); link?: CollectionInstance; mediaType?: Tyr.MediaTypeId; numbering?: Numbering; relate?: 'owns' | 'ownedBy' | 'associate'; type: TypeInstance; fields?: { [key: string]: FieldInstance }; method: string; populateName?: string; width?: number; format(value: any): string; isId(): boolean; labelify(value: any): Promise; labels( doc: Document, text?: string, opts?: { labelField?: string } ): Promise; validate( obj: {}, opts?: { trait?: ActionTrait } ): Promise | string | false | undefined; } export interface PathStatic { new (...args: any[]): PathInstance; decode(path: string): string; encode(path: string): string; populateNameFor(name: string, denormal?: boolean): string; resolve( collection: CollectionInstance, parentPath?: PathInstance, path?: PathInstance | string ): PathInstance; } export interface PathInstance { $metaType: 'path'; collection: CollectionInstance; detail: FieldInstance; name: string; identifier: string; path: string[]; spath: string; spathArr: string; fields: FieldInstance[]; label: string; pathLabel: string; tail: Tyr.FieldInstance; denormal: MongoDocument | undefined; groupCount: number; groupRange(groupNumber: number): [number, number]; groupLabel(groupNumber: number): string; parsePath(path: string): this; pathName(idx: number): string; projectify(projection: MongoProjection): void; uniq(obj: any): any[]; get(obj: any): any; set( obj: Document, value: any, opts?: { create?: boolean; ignore?: boolean } ): void; walk(path: string | number): this; } export type IdType> = D extends Document< infer ID > ? ID : D extends Inserted ? ID : never; export interface CollectionStatic {} export interface CollectionInstance< D extends Document = Document > extends Class { $metaType: 'collection'; byId(id: IdType, opts?: any): Promise; byIds(ids: IdType[], opts?: any): Promise; byLabel(label: string): Promise; count(opts: any): Promise; def: any /* collection def */; exists(opts: any): Promise; fields: { [fieldName: string]: FieldInstance }; fieldsFor(opts: { match?: MongoDocument; query?: MongoQuery; custom?: boolean; static?: boolean; }): Promise<{ [key: string]: Tyr.FieldInstance }>; findAll(args: any): Promise; findOne(args: any): Promise; id: string; idToLabel(id: IdType): Promise; idToUid(id: IdType | string): string; insert(docs: A, opts?: any): Promise; insert(doc: I): Promise; insert(doc: any): Promise; isAux(): boolean; isDb(): boolean; isSingleton(): boolean; isStatic(): boolean; isUid(uid: string): boolean; label: string; labelField: any; alternateLabelFields?: any; labelImageField: any; orderField: any; labelFor(doc: D | object, opts?: { labelField: string }): string; labelProjection(labelField?: string): any; // Mongo Projection labels(text: string, opts?: { labelField?: string }): Promise; labels(ids: string[], opts?: { labelField?: string }): Promise; labels(_: any): Promise; on(opts: any): () => void; parsePath(text: string): PathInstance; paths: { [fieldPathName: string]: FieldInstance }; push(id: IdType, path: string, value: any, opts: any): Promise; remove(id: IdType, justOne: boolean): Promise; remove( query: any /* MongoDB-style query */, justOne: boolean ): Promise; save(doc: D | object): Promise; save(doc: D[] | object[]): Promise; save(doc: any): Promise; subscribe(query: MongoQuery, cancel: boolean): Promise; updateDoc(doc: D | MongoDocument, opts: any): Promise; values: D[]; } export interface Document { $access?: AccessResult; $clone(): this; $cloneDeep(): this; $id: ID; $isNew: boolean; $label: string; $metaType: 'document'; $model: CollectionInstance; // this -- ISO-VARIANCE $remove(opts: any): Promise; $save(opts: any): Promise; $slice(path: string, opts: any): Promise; $toPlain(): object; $tyr: typeof Tyr; $uid: string; $update(opts: any): Promise; } export interface Inserted extends Document { _id: ID; } export type LogOption = string | Error | BaseTyrLog; export function trace(...args: LogOption[]): Promise; export function log(...args: LogOption[]): Promise; export function info(...args: LogOption[]): Promise; export function warn(...args: LogOption[]): Promise; export function error(...args: LogOption[]): Promise; export function fatal(...args: LogOption[]): Promise; }