Skip to content

Instantly share code, notes, and snippets.

@taxilian
Last active May 23, 2016 18:55
Show Gist options
  • Save taxilian/4c26b8af70120e1d18027c07b688a174 to your computer and use it in GitHub Desktop.
Save taxilian/4c26b8af70120e1d18027c07b688a174 to your computer and use it in GitHub Desktop.
better mongoose.d.ts
// Type definitions for Mongoose 3.8.5
// Project: http://mongoosejs.com/
// Definitions by: horiuchi <https://github.com/horiuchi/>
// Definitions: https://github.com/borisyankov/DefinitelyTyped
// tslint:disable
///<reference path="../../typings/main.d.ts" />
///<reference path="../mongodb/mongodb.d.ts" />
declare module "mongoose" {
import * as Promise from 'bluebird';
function connect(uri: string, options?: ConnectionOptions , callback?: (err: any) => void): Mongoose;
function createConnection(): Connection;
function createConnection(uri: string, options?: ConnectionOptions): Connection;
function createConnection(host: string, database_name: string, port?: number, options?: ConnectionOptions): Connection;
function disconnect(callback?: (err?: any) => void): Mongoose;
function model<T extends Document>(name: string, schema?: Schema, collection?: string, skipInit?: boolean): Model<T>;
function modelNames(): string[];
function plugin(plugin: (schema: Schema, options?: any) => void, options?: any): Mongoose;
function get(key: string): any;
function set(key: string, value: any): void;
var mongo: any;
var mquery: any;
var version: string;
var connection: Connection;
var models: any;
export class Mongoose {
connect(uri: string, options?: ConnectOpenOptionsBase, callback?: (err: any) => void): Mongoose;
createConnection(): Connection;
createConnection(uri: string, options?: any): Connection;
createConnection(host: string, database_name: string, port?: number, options?: ConnectOpenOptionsBase): Connection;
disconnect(callback?: (err?: any) => void): Mongoose;
get(key: string): any;
model<T extends Document>(name: string, schema?: Schema, collection?: string, skipInit?: boolean): Model<T>;
modelNames(): string[];
plugin(plugin: (schema: Schema, options?: any) => void, options?: any): Mongoose;
set(key: string, value: any): void;
mongo: any;
mquery: any;
version: string;
connection: Connection;
}
export interface Connection extends NodeJS.EventEmitter {
constructor(base: Mongoose): Connection;
close(callback?: (err: any) => void): Connection;
collection(name: string, options?: any): Collection;
model<T extends Document>(name: string, schema?: Schema, collection?: string): Model<T>;
modelNames(): string[];
open(host: string, database?: string, port?: number, options?: OpenSetConnectionOptions, callback?: (err: any) => void): Connection;
openSet(uris: string, database?: string, options?: OpenSetConnectionOptions, callback?: (err: any) => void): Connection;
db: any;
collections: {[index: string]: Collection};
readyState: number;
}
export interface ConnectOpenOptionsBase {
db?: DbCreateOptions;
server?: ServerOptions;
replset?: ReplicaSetOptions;
mongos?: MongosOptions;
/** Username for authentication if not supplied in the URI. */
user?: string;
/** Password for authentication if not supplied in the URI. */
pass?: string;
/** Options for authentication */
auth?: any;
}
export interface ConnectionOptions extends ConnectOpenOptionsBase {
/** Passed to the underlying driver's Mongos instance. */
mongos?: MongosOptions;
server?: ServerOptions;
replSet?: ReplicaSetOptions;
promiseLibrary: any;
}
interface OpenSetConnectionOptions extends ConnectOpenOptionsBase {
/** If true, enables High Availability support for mongos */
mongos?: boolean;
}
export interface DbCreateOptions {
// If the database authentication is dependent on another databaseName.
authSource?: string;
// the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = ‘majority’ or tag acknowledges the write.
w?: number|string;
// set the timeout for waiting for write concern to finish (combines with w option).
wtimeout?: number;
// Specify a journal write concern.
j?: boolean;
// use c++ bson parser. default:false.
native_parser?: boolean;
// force server to create _id fields instead of client. default:false.
forceServerObjectId?: boolean;
// serialize functions. default:false.
serializeFunctions?: boolean;
ignoreUndefined?: boolean;
// peform operations using raw bson buffers. default:false.
raw?: boolean;
// when deserializing a Long will fit it into a Number if it’s smaller than 53 bits. default:true.
promoteLongs?: boolean;
bufferMaxEntries?: number;
// the prefered read preference. use 'ReadPreference' class.
readPreference?: ReadPreference|string;
// custom primary key factory to generate _id values (see Custom primary keys).
pkFactory?: any;
// default: (ES6 Promise or polyfill). A Promise library class the application wishes to use
// such as Bluebird, must be ES6 compatible
promiseLibrary?:PromiseConstructor;
// Specify a read concern for the collection. (only MongoDB 3.2 or higher supported)
readConcern?: {
// Specify a read concern level for the collection operations, one of [local|majority]. (only MongoDB 3.2 or higher supported)
level: string;
};
}
export interface SocketOptions {
//= set seconds before connection times out default:0
connectTimeoutMS?: number;
//= TCP Socket timeout setting
socketTimeoutMS?: number;
//= Disables the Nagle algorithm default:true
noDelay?: boolean;
//= Set if keepAlive is used default:0 , which means no keepAlive, set higher than 0 for keepAlive
keepAlive?: number;
}
export class ReadPreference {
public static PRIMARY: string;
public static PRIMARY_PREFERRED: string;
public static SECONDARY: string;
public static SECONDARY_PREFERRED: string;
public static NEAREST: string;
}
export interface BasicServerOptions {
// - specify the number of connections in the pool default:5
poolSize?: number;
// - Use ssl connection (needs to have a mongod server with ssl support)
ssl?: boolean;
// - Validate mongod server certificate against ca (needs to have a mongod server with ssl support, 2.4 or higher)
sslValidate?: boolean;
// - Array of valid certificates either as Buffers or Strings (needs to have a mongod server with ssl support, 2.4 or higher)
sslCA?: string|Buffer|string[]|Buffer[];
// - String or buffer containing the certificate we wish to present (needs to have a mongod server with ssl support, 2.4 or higher)
sslCert?: string|Buffer;
// - String or buffer containing the certificate private key we wish to present (needs to have a mongod server with ssl support, 2.4 or higher)
sslKey?: string|Buffer;
// - String or buffer containing the certificate password (needs to have a mongod server with ssl support, 2.4 or higher)
sslPass?: string|Buffer;
// - a collection of pr socket settings
socketOptions?: SocketOptions;
}
// from http://mongodb.github.io/node-mongodb-native/2.0/api/Server.html
interface ServerOptions extends BasicServerOptions {
// - Server attempt to reconnect #times (default 30)
reconnectTries?:number;
// - Server will wait # milliseconds between retries (default 1000)
reconnectInterval?:number;
}
// from http://mongodb.github.io/node-mongodb-native/2.0/api/ReplSet.html
interface ReplicaSetOptions extends BasicServerOptions {
// - Turn on high availability monitoring.
ha?: boolean;
// - Time between each replicaset status check.
haInterval?: number;
// - (no default) The name of the replicaset to connect to.
replicaSet?: string;
// - (15) Sets the range of servers to pick when using NEAREST (lowest ping ms + the latency fence, ex: range of 1 to (1 + 15) ms)
secondaryAcceptableLatencyMS?: number;
// - (false) Sets if the driver should connect even if no primary is available
connectWithNoPrimary?: boolean;
}
// from http://mongodb.github.io/node-mongodb-native/2.0/api/Mongos.html
interface MongosOptions extends BasicServerOptions {
// - Turn on high availability monitoring.
ha?: boolean;
// - Time between each replicaset status check.
haInterval?: number;
}
export interface Collection {
}
export class SchemaType { }
export class VirtualType {
get(fn: Function): VirtualType;
set(fn: Function): VirtualType;
}
export module Types {
export class ObjectId {
constructor(id?: string|number);
toHexString(): string;
equals(other: ObjectId|string): boolean;
getTimestamp(): Date;
isValid(): boolean;
static createFromTime(time: number): ObjectId;
static createFromHexString(hexString: string): ObjectId;
}
}
interface IMongooseObjectID {
new (id?: string | number): Types.ObjectId;
schemaName: string;
}
type HookCallback = (next:Function, obj?:any) => void;
export class Schema {
static Types: {
String: String;
ObjectId: IMongooseObjectID;
OId: IMongooseObjectID;
Mixed: any;
};
static ObjectId: IMongooseObjectID;
constructor(schema?: any, options?: any);
add(obj: any, prefix?: string): void;
eachPath(fn: (path: string, type: any) => void): Schema;
get(key: string): any;
index(fields: any, options?: any): Schema;
indexes(): void;
method(name: string, fn: Function): Schema;
method(method: any): Schema;
path(path: string): any;
path(path: string, constructor: any): Schema;
pathType(path: string): string;
plugin(plugin: (schema: Schema, options?: any) => void, options?: any): Schema;
post(method: string, fn: HookCallback): Schema;
pre(method: string, callback: HookCallback): Schema;
requiredPaths(): string[];
set(key: string, value: any): void;
static(name: string, fn: Function): Schema;
statics: any;
methods: any;
paths: any;
virtual(name: string, options?: any): VirtualType;
virtualpath(name: string): VirtualType;
}
export interface SchemaOption {
autoIndex?: boolean;
bufferCommands?: boolean;
capped?: boolean;
collection?: string;
id?: boolean;
_id?: boolean;
minimize?: boolean;
read?: string;
safe?: boolean;
shardKey?: boolean;
strict?: boolean;
toJSON?: any;
toObject?: any;
versionKey?: boolean;
}
export interface Model<T extends Document> extends NodeJS.EventEmitter {
new(doc?: any): T;
aggregate(...aggregations: any[]): Aggregate<T[]>;
aggregate(aggregation: any, callback: (err: any, res: T[]) => void): Promise<T[]>;
aggregate(aggregation1: any, aggregation2: any, callback: (err: any, res: T[]) => void): Promise<T[]>;
aggregate(aggregation1: any, aggregation2: any, aggregation3: any, callback: (err: any, res: T[]) => void): Promise<T[]>;
count(conditions: any, callback?: (err: any, count: number) => void): Query<number, number>;
create(doc: any, fn?: (err: any, res: T) => void): Promise<T>;
create(doc1: any, doc2: any, fn?: (err: any, res1: T, res2: T) => void): Promise<T[]>;
create(doc1: any, doc2: any, doc3: any, fn?: (err: any, res1: T, res2: T, res3: T) => void): Promise<T[]>;
discriminator<U extends Document>(name: string, schema: Schema): Model<U>;
distinct(field: string, callback?: (err: any, res: T[]) => void): Query<T[], T>;
distinct(field: string, conditions: any, callback?: (err: any, res: T[]) => void): Query<T[], T>;
ensureIndexes(callback: (err: any) => void): Promise<T>;
find(cond?: any, callback?: (err: any, res: T[]) => void): Query<T[], T>;
find(cond: any, fields: any, callback?: (err: any, res: T[]) => void): Query<T[], T>;
find(cond: any, fields: any, options: any, callback?: (err: any, res: T[]) => void): Query<T[], T>;
findById(id: string, callback?: (err: any, res: T) => void): Query<T, T>;
findById(id: string, fields: any, callback?: (err: any, res: T) => void): Query<T, T>;
findById(id: string, fields: any, options: any, callback?: (err: any, res: T) => void): Query<T, T>;
findByIdAndRemove(id: string, callback?: (err: any, res: T) => void): Query<T, T>;
findByIdAndRemove(id: string, options: any, callback?: (err: any, res: T) => void): Query<T, T>;
findByIdAndUpdate(id: string, update: any, callback?: (err: any, res: T) => void): Query<T, T>;
findByIdAndUpdate(id: string, update: any, options: FindAndUpdateOption, callback?: (err: any, res: T) => void): Query<T, T>;
findOne(cond?: any, callback?: (err: any, res: T) => void): Query<T, T>;
findOne(cond: any, fields: any, callback?: (err: any, res: T) => void): Query<T, T>;
findOne(cond: any, fields: any, options: any, callback?: (err: any, res: T) => void): Query<T, T>;
findOneAndRemove(cond: any, callback?: (err: any, res: T) => void): Query<T, T>;
findOneAndRemove(cond: any, options: any, callback?: (err: any, res: T) => void): Query<T, T>;
findOneAndUpdate(cond: any, update: any, callback?: (err: any, res: T) => void): Query<T, T>;
findOneAndUpdate(cond: any, update: any, options: FindAndUpdateOption, callback?: (err: any, res: T) => void): Query<T, T>;
geoNear(point: { type: string; coordinates: number[] }, options: any, callback?: (err: any, res: T[]) => void): Query<T[], T>;
geoNear(point: number[], options: any, callback?: (err: any, res: T[]) => void): Query<T[], T>;
geoSearch(cond: any, options: GeoSearchOption, callback?: (err: any, res: T[]) => void): Query<T[], T>;
increment(): T;
mapReduce<K, V>(options: MapReduceOption<T, K, V>, callback?: (err: any, res: MapReduceResult<K, V>[]) => void): Promise<MapReduceResult<K, V>[]>;
mapReduce<K, V>(options: MapReduceOption2<T, K, V>, callback?: (err: any, res: MapReduceResult<K, V>[]) => void): Promise<MapReduceResult<K, V>[]>;
model<U extends Document>(name: string): Model<U>;
populate<U>(doc: U, options: any, callback?: (err: any, res: U) => void): Promise<U>;
populate<U>(doc: U[], options: any, callback?: (err: any, res: U[]) => void): Promise<U[]>;
update(cond: any, update: any, callback?: (err: any, affectedRows: number, raw: any) => void): Query<T, T>;
update(cond: any, update: any, options: any, callback?: (err: any, affectedRows: number, raw: any) => void): Query<T, T>;
remove(cond: any, callback?: (err: any) => void): Query<{}, {}>;
save(callback?: (err: any, result: T, numberAffected: number) => void): Query<T, T>;
where(path: string, val?: any): Query<T[], T>;
$where(argument: string): Query<T[], T>;
$where(argument: Function): Query<T[], T>;
base: Mongoose;
collection: Collection;
db: any;
discriminators: any;
modelName: string;
schema: Schema;
}
export interface DocumentArray<T> extends Array<T> {
addToSet?(...members: T[]) : T[];
create?(obj?:T) : T;
isMongooseDocumentArray?: boolean;
isMongooseArray?: boolean;
pull?(val:any) : DocumentArray<T>;
remove?(val:any) : DocumentArray<T>;
set?(index:number, val:T) : DocumentArray<T>;
toObject?(options?:{depopulate?:boolean}) : T[];
inspect?(): string;
indexOf(obj:T) : number;
nonAtomicPush?(...objects: T[]) : T[];
hasAtomics?:number;
id?: Types.ObjectId;
}
export interface FindAndUpdateOption {
new?: boolean;
upsert?: boolean;
sort?: any;
select?: any;
}
export interface GeoSearchOption {
near: number[];
maxDistance: number;
limit?: number;
lean?: boolean;
}
export interface MapReduceOption<T extends Document, Key, Val> {
map: () => void;
reduce: (key: Key, vals: T[]) => Val;
query?: any;
limit?: number;
keeptemp?: boolean;
finalize?: (key: Key, val: Val) => Val;
scope?: any;
jsMode?: boolean;
verbose?: boolean;
out?: {
inline?: number;
replace?: string;
reduce?: string;
merge?: string;
};
}
export interface MapReduceOption2<T extends Document, Key, Val> {
map: string;
reduce: (key: Key, vals: T[]) => Val;
query?: any;
limit?: number;
keeptemp?: boolean;
finalize?: (key: Key, val: Val) => Val;
scope?: any;
jsMode?: boolean;
verbose?: boolean;
out?: {
inline?: number;
replace?: string;
reduce?: string;
merge?: string;
};
}
export interface MapReduceResult<Key, Val> {
_id: Key;
value: Val;
}
export class Query<T, O> {
exec(callback?: (err: any, res: T) => void): Promise<T>;
exec(operation: string, callback?: (err: any, res: T) => void): Promise<T>;
exec(operation: Function, callback?: (err: any, res: T) => void): Promise<T>;
all(val: number): Query<T, O>;
all(path: string, val: number): Query<T, O>;
and(array: any[]): Query<T, O>;
box(val: any): Query<T, O>;
box(a: number[], b: number[]): Query<T, O>;
batchSize(val: number): Query<T, O>;
cast<U extends Document>(model: Model<U>, obj: any): U;
//center(): Query<T>;
//centerSphere(path: string, val: any): Query<T>;
circle(area: any): Query<T, O>;
circle(path: string, area: any): Query<T, O>;
comment(val: any): Query<T, O>;
count(callback?: (err: any, count: number) => void): Query<number, number>;
count(criteria: any, callback?: (err: any, count: number) => void): Query<number, number>;
distinct(callback?: (err: any, res: T) => void): Query<T, O>;
distinct(field: string, callback?: (err: any, res: T) => void): Query<T, O>;
distinct(criteria: any, field: string, callback?: (err: any, res: T) => void): Query<T, O>;
distinct(criteria: Query<T, O>, field: string, callback?: (err: any, res: T) => void): Query<T, O>;
elemMatch(criteria: any): Query<T, O>;
elemMatch(criteria: (elem: Query<T, O>) => void): Query<T, O>;
elemMatch(path: string, criteria: any): Query<T, O>;
elemMatch(path: string, criteria: (elem: Query<T, O>) => void): Query<T, O>;
equals(val: any): Query<T, O>;
exists(val?: boolean): Query<T, O>;
exists(path: string, val?: boolean): Query<T, O>;
find(callback?: (err: any, res: T) => void): Query<T, O>;
find(criteria: any, callback?: (err: any, res: T) => void): Query<T, O>;
findOne(callback?: (err: any, res: T) => void): Query<O, O>;
findOne(criteria: any, callback?: (err: any, res: T) => void): Query<O, O>;
findOneAndRemove(callback?: (err: any, res: T) => void): Query<O, O>;
findOneAndRemove(cond: any, callback?: (err: any, res: T) => void): Query<O, O>;
findOneAndRemove(cond: any, options: any, callback?: (err: any, res: T) => void): Query<O, O>;
findOneAndUpdate(callback?: (err: any, res: T) => void): Query<O, O>;
findOneAndUpdate(update: any, callback?: (err: any, res: T) => void): Query<O, O>;
findOneAndUpdate(cond: any, update: any, callback?: (err: any, res: T) => void): Query<O, O>;
findOneAndUpdate(cond: any, update: any, options: FindAndUpdateOption, callback?: (err: any, res: T) => void): Query<O, O>;
geometry(object: any): Query<T, O>;
gt(val: number): Query<T, O>;
gt(path: string, val: number): Query<T, O>;
gte(val: number): Query<T, O>;
gte(path: string, val: number): Query<T, O>;
hint(val: any): Query<T, O>;
in(val: any[]): Query<T, O>;
in(path: string, val: any[]): Query<T, O>;
intersects(arg?: any): Query<T, O>;
lean(bool?: boolean): Query<T, O>;
limit(val: number): Query<T, O>;
lt(val: number): Query<T, O>;
lt(path: string, val: number): Query<T, O>;
lte(val: number): Query<T, O>;
lte(path: string, val: number): Query<T, O>;
maxDistance(val: number): Query<T, O>;
maxDistance(path: string, val: number): Query<T, O>;
maxScan(val: number): Query<T, O>;
merge(source: Query<T, O>): Query<T, O>;
merge(source: any): Query<T, O>;
mod(val: number[]): Query<T, O>;
mod(path: string, val: number[]): Query<T, O>;
ne(val: any): Query<T, O>;
ne(path: string, val: any): Query<T, O>;
near(val: any): Query<T, O>;
near(path: string, val: any): Query<T, O>;
nearSphere(val: any): Query<T, O>;
nearSphere(path: string, val: any): Query<T, O>;
nin(val: any[]): Query<T, O>;
nin(path: string, val: any[]): Query<T, O>;
nor(array: any[]): Query<T, O>;
or(array: any[]): Query<T, O>;
polygon(...coordinatePairs: number[][]): Query<T, O>;
polygon(path: string, ...coordinatePairs: number[][]): Query<T, O>;
populate(path: string, select?: string, match?: any, options?: any): Query<T, O>;
populate(path: string, select: string, model: string, match?: any, options?: any): Query<T, O>;
populate(opt: PopulateOption): Query<T, O>;
read(pref: string, tags?: any[]): Query<T, O>;
regex(val: RegExp): Query<T, O>;
regex(path: string, val: RegExp): Query<T, O>;
remove(callback?: (err: any, res: T) => void): Query<T, O>;
remove(criteria: any, callback?: (err: any, res: T) => void): Query<T, O>;
select(arg: string): Query<T, O>;
select(arg: any): Query<T, O>;
setOptions(options: any): Query<T, O>;
size(val: number): Query<T, O>;
size(path: string, val: number): Query<T, O>;
skip(val: number): Query<T, O>;
slaveOk(v?: boolean): Query<T, O>;
slice(val: number): Query<T, O>;
slice(val: number[]): Query<T, O>;
slice(path: string, val: number): Query<T, O>;
slice(path: string, val: number[]): Query<T, O>;
snapshot(v?: boolean): Query<T, O>;
sort(arg: any): Query<T, O>;
sort(arg: string): Query<T, O>;
stream(options?: { transform?: Function; }): QueryStream;
tailable(v?: boolean): Query<T, O>;
toConstructor(): Query<T, O>;
update(callback?: (err: any, affectedRows: number, doc: T) => void): Query<T, O>;
update(doc: any, callback?: (err: any, affectedRows: number, doc: T) => void): Query<T, O>;
update(criteria: any, doc: any, callback?: (err: any, affectedRows: number, doc: T) => void): Query<T, O>;
update(criteria: any, doc: any, options: any, callback?: (err: any, affectedRows: number, doc: T) => void): Query<T, O>;
where(path?: string, val?: any): Query<T, O>;
where(path?: any, val?: any): Query<T, O>;
within(val?: any): Query<T, O>;
within(coordinate: number[], ...coordinatePairs: number[][]): Query<T, O>;
$where(argument: string): Query<T, O>;
$where(argument: Function): Query<T, O>;
static use$geoWithin: boolean;
}
export interface PopulateOption {
path: string;
select?: string;
model?: string;
match?: any;
options?: any;
}
export interface QueryStream extends NodeJS.EventEmitter {
destory(err?: any): void;
pause(): void;
resume(): void;
pipe<T extends NodeJS.WritableStream>(destination: T, options?: { end?: boolean; }): T;
paused: number;
readable: boolean;
}
export interface Document {
id?: string;
_id?: Types.ObjectId;
__v?: number;
equals?(doc: Document): boolean;
get?(path: string, type?: new(...args: any[]) => any): any;
inspect?(options?: any): string;
invalidate?(path: string, errorMsg: string, value: any): void;
invalidate?(path: string, error: Error, value: any): void;
isDirectModified?(path: string): boolean;
isInit?(path: string): boolean;
isModified?(path?: string): boolean;
isSelected?(path: string): boolean;
markModified?(path: string): void;
modifiedPaths?(): string[];
populate?<T>(callback?: (err: any, res: T) => void): Document;
populate?<T>(path?: string, callback?: (err: any, res: T) => void): Document;
populate?<T>(opt: PopulateOption, callback?: (err: any, res: T) => void): Document;
populated?(path: string): any;
remove?<T>(callback?: (err: any) => void): Query<T, T>;
save?<T>(callback?: (err: any, res: T) => void): Promise<T>;
set?(path: string, val: any, type?: new(...args: any[]) => any, options?: any): void;
set?(path: string, val: any, options?: any): void;
set?(value: any): void;
toJSON?(options?: any): any;
toObject?(options?: any): any;
toany?(options?: any): any;
toString?(): string;
update?<T>(doc: any, options: any, callback: (err: any, affectedRows: number, raw: any) => void): Query<T, T>;
validate?(cb?: (err: any) => void): Promise<void>;
isNew?: boolean;
errors?: any;
schema?: any;
}
export class Aggregate<T> {
constructor(...options: any[]);
append(...options: any[]): Aggregate<T>;
group(arg: any): Aggregate<T>;
limit(num: number): Aggregate<T>;
match(arg: any): Aggregate<T>;
near(parameters: any): Aggregate<T>;
project(arg: string): Aggregate<T>;
project(arg: any): Aggregate<T>;
select(filter: string): Aggregate<T>;
skip(num: number): Aggregate<T>;
sort(arg: string): Aggregate<T>;
sort(arg: any): Aggregate<T>;
unwind(fiels: string, ...rest: string[]): Aggregate<T>;
exec(callback?: (err: any, result: T) => void): Promise<T>;
read(pref: string, ...tags: any[]): Aggregate<T>;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment