diff --git a/lokijs/lokijs.d.ts b/lokijs/lokijs.d.ts index 5b6251a652..3db0964a23 100644 --- a/lokijs/lokijs.d.ts +++ b/lokijs/lokijs.d.ts @@ -3,7 +3,7 @@ // Definitions by: TeamworkGuy2 // Definitions: https://github.com/borisyankov/DefinitelyTyped -// NOTE: definition last updated (2015-6-6) based on latest code as of https://github.com/techfort/LokiJS/commit/4ffdda188c59ac06b760575b4ca41ad591ca8b0d +// NOTE: definition last updated (2016-3-13) based on latest code as of https://github.com/techfort/LokiJS/commit/3d2cf9546cd22556444deeabc4df314f227ecf5c /** LokiJS * A lightweight document oriented javascript database @@ -17,45 +17,50 @@ * @param {object} options - config object */ interface Loki extends LokiEventEmitter { - filename: string; + // autosave support (disabled by default) + autosave: boolean; + autosaveInterval: number; // milliseconds between auto-saves + autosaveHandle: number; // ID from setInterval(...) collections: LokiCollection[]; databaseVersion: number; engineVersion: number; - // autosave support (disabled by default) - // pass autosave: true, autosaveInterval: 6000 in options to set 6 second autosave - autosave: boolean; - autosaveInterval: number; - autosaveHandle: number; // from setInterval() call + ENV: string;/*NODEJS, CORDOVA, BROWSER*/ + events: { [id: string]: ((...args: any[]) => void)[] }; /*{ + 'init': ((...args) => void)[]; + 'loaded': ((...args) => void)[]; + 'flushChanges': ((...args) => void)[]; + 'close': ((...args) => void)[]; + 'changes': ((...args) => void)[]; + 'warning': ((...args) => void)[]; + };*/ + filename: string; options: LokiConfigureOptions; - // currently keeping persistenceMethod and persistenceAdapter as loki level properties that - // will not or cannot be deserialized. You are required to configure persistence every time - // you instantiate a loki object (or use default environment detection) in order to load the database anyways. + persistenceAdapter: LokiPersistenceInterface; // persistenceMethod could be 'fs', 'localStorage', or 'adapter' // this is optional option param, otherwise environment detection will be used // if user passes their own adapter we will force this method to 'adapter' later, so no need to pass method option. - persistenceMethod: string; - // retain reference to optional (non-serializable) persistenceAdapter 'instance' - persistenceAdapter: LokiPersistenceInterface; - ENV: string; - events: { - 'init': any[]; - 'flushChanges': any[]; - 'close': any[]; - 'changes': any[]; - 'warning': any[]; - }; + persistenceMethod: string; /*'fs', 'localStorage', 'adapter'*/ + verbose: boolean; new (filename: string, options: LokiConfigureOptions): Loki; + // experimental support for browserify's abstract syntax scan to pick up dependency of indexed adapter. + // Hopefully, once this hits npm a browserify require of lokijs should scan the main file and detect this indexed adapter reference. + getIndexedAdapter(): LokiPersistenceInterface; // require("./loki-indexed-adapter.js") + + /** configureOptions - allows reconfiguring database options + * * @param {object} options - configuration options to apply to loki db object * @param {boolean} initialConfig - (optional) if this is a reconfig, don't pass this */ configureOptions(options: LokiConfigureOptions, initialConfig?: boolean): void; /** anonym() - shorthand method for quickly creating and populating an anonymous collection. - * This collection is not referenced internally so upon losing scope it will be garbage collected. - * Example : var results = new loki().anonym(myDocArray).find({'age': {'$gt': 30} }); + * This collection is not referenced internally so upon losing scope it will be garbage collected. + * + * Example : var results = new loki().anonym(myDocArray).find({'age': {'$gt': 30} }); + * * @param {Array} docs - document array to initialize the anonymous collection with * @param {Array} indexesArray - (Optional) array of property names to index * @returns {Collection} New collection which you can query or chain @@ -74,7 +79,8 @@ interface Loki extends LokiEventEmitter { getName(): string; - /** serializeReplacer - used to prevent certain properties from being serialized, may return null */ + /** serializeReplacer - used to prevent certain properties from being serialized + */ serializeReplacer(key: "autosaveHandle", value: T): T; serializeReplacer(key: "persistenceAdapter", value: T): T; serializeReplacer(key: "constraints", value: T): T; @@ -87,17 +93,24 @@ interface Loki extends LokiEventEmitter { toJson(): string; /** loadJSON - inflates a loki database from a serialized JSON string + * * @param {string} serializedDb - a serialized loki database string * @param {object} options - apply or override collection level settings */ - loadJSON(serializedDb: string, options: { [id: string]: { inflate?: (a: any, b: any) => void; proto: any; } }): void; + loadJSON(serializedDb: string, options?: { [collectionName: string]: { inflate?: (src: any, dst: any) => void; proto: any; } }): void; + + /** loadJSONObject - inflates a loki database from a JS object + * + * @param {object} dbObject - a serialized loki database string + * @param {object} options - apply or override collection level settings + */ + loadJSONObject(dbObject: Loki, options?: { [collectionName: string]: { inflate?: (src: any, dst: any) => void; proto: any; } }): void; /** close(callback) - emits the close event with an optional callback. Does not actually destroy the db * but useful from an API perspective */ close(callback?: (...args: any[]) => void): void; - /**-------------------------+ | Changes API | +--------------------------*/ @@ -107,8 +120,9 @@ interface Loki extends LokiEventEmitter { */ /** generateChangesNotification() - takes all the changes stored in each - * collection and creates a single array for the entire database. If an array of names - * of collections is passed then only the included collections will be tracked. + * collection and creates a single array for the entire database. If an array of names + * of collections is passed then only the included collections will be tracked. + * * @param {array} optional array of collection names. No arg means all collections are processed. * @returns {array} array of changes * @see private method createChange() in Collection @@ -120,27 +134,40 @@ interface Loki extends LokiEventEmitter { */ serializeChanges(collectionNamesArray?: string[]): string; - /** clearChanges() - clears all the changes in all collections. */ + /** clearChanges() - clears all the changes in all collections. + */ clearChanges(): void; /** loadDatabase - Handles loading from file system, local storage, or adapter (indexeddb) - * This method utilizes loki configuration options (if provided) to determine which - * persistence method to use, or environment detection (if configuration was not provided). + * This method utilizes loki configuration options (if provided) to determine which + * persistence method to use, or environment detection (if configuration was not provided). + * * @param {object} options - not currently used (remove or allow overrides?) * @param {function} callback - (Optional) user supplied async callback / error handler */ - loadDatabase(options: { [id: string]: { inflate?: (a: any, b: any) => void; proto: any; } }, callback: (err: Error | string, data: any) => void): void; + loadDatabase(options: { [collectionName: string]: { inflate?: (src: any, dst: any) => void; proto: any; } }, callback?: (err: any, data: any) => void): void; /** saveDatabase - Handles saving to file system, local storage, or adapter (indexeddb) - * This method utilizes loki configuration options (if provided) to determine which - * persistence method to use, or environment detection (if configuration was not provided). + * This method utilizes loki configuration options (if provided) to determine which + * persistence method to use, or environment detection (if configuration was not provided). + * * @param {object} options - not currently used (remove or allow overrides?) * @param {function} callback - (Optional) user supplied async callback / error handler */ - saveDatabase(callback: (err: Error) => void): void; + saveDatabase(callback?: (err: any) => void): void; - // alias - save(callback: (err: Error) => void): void; + // alias for saveDatabase + save(callback ?: (err: any) => void): void; + + /** deleteDatabase - Handles deleting a database from file system, local + * storage, or adapter (indexeddb) + * This method utilizes loki configuration options (if provided) to determine which + * persistence method to use, or environment detection (if configuration was not provided). + * + * @param {object} options - not currently used (remove or allow overrides?) + * @param {function} callback - user supplied async callback / error handler + */ + deleteDatabase(options: any, callback: (err: any, data: any) => void): void; /** autosaveDirty - check whether any collections are 'dirty' meaning we need to save (entire) database * @returns {boolean} - true if database has changed since last autosave, false if not. @@ -148,49 +175,63 @@ interface Loki extends LokiEventEmitter { autosaveDirty(): boolean; /** autosaveClearFlags - resets dirty flags on all collections. - * Called from saveDatabase() after db is saved. + * Called from saveDatabase() after db is saved. */ autosaveClearFlags(): void; - /** autosaveEnable - begin a javascript interval to periodically save the database. */ - autosaveEnable(): void; + /** autosaveEnable - begin a javascript interval to periodically save the database. + * + * @param {object} options - not currently used (remove or allow overrides?) + * @param {function} callback - (Optional) user supplied async callback + */ + autosaveEnable(options?: LokiConfigureOptions, callback?: (err: any) => void): void; - /** autosaveDisable - stop the autosave interval timer. */ + /** autosaveDisable - stop the autosave interval timer. + */ autosaveDisable(): void; - } -/** LokiEventEmitter is a minimalist version of EventEmitter. It enables any + + +/** + * LokiEventEmitter is a minimalist version of EventEmitter. It enables any * constructor that inherits EventEmitter to emit events and trigger * listeners that have been added to the event through the on(event, callback) method */ interface LokiEventEmitter { - /** @prop Events property is a hashmap, with each property being an array of callbacks */ - events: {} //{ [id: string]: ((...args) => void)[] }; + /** + * @prop Events property is a hashmap, with each property being an array of callbacks + */ + events: { [eventName: string]: ((...args: any[]) => void)[] }; new (): LokiEventEmitter; - /** @prop asyncListeners - boolean determines whether or not the callbacks associated with each event + /** + * @prop asyncListeners - boolean determines whether or not the callbacks associated with each event * should happen in an async fashion or not * Default is false, which means events are synchronous */ asyncListeners: boolean; - /** @prop on(eventName, listener) - adds a listener to the queue of callbacks associated to an event + /** + * @prop on(eventName, listener) - adds a listener to the queue of callbacks associated to an event * @returns {int} the index of the callback in the array of listeners for a particular event */ - on(eventName: string, listener: (...args: any[]) => void): (...args: any[]) => void; + on void>(eventName: string, listener: U): U; - /** @propt emit(eventName, data) - emits a particular event + /** + * @propt emit(eventName, data) - emits a particular event * with the option of passing optional parameters which are going to be processed by the callback * provided signatures match (i.e. if passing emit(event, arg0, arg1) the listener should take two parameters) * @param {string} eventName - the name of the event * @param {object} data - optional object passed with the event */ - emit(eventName: string, data: any): void; + emit(eventName: string, data?: any): void; - /** @prop remove() - removes the listener at position 'index' from the event 'eventName' */ + /** + * @prop remove() - removes the listener at position 'index' from the event 'eventName' + */ removeListener(eventName: string, listener: (...args: any[]) => void): void; } @@ -206,135 +247,184 @@ interface LokiEventEmitter { * localStorage for use in browser environment * defined as helper classes here so its easy and clean to use */ -interface LokiFsAdapter { - //fs; // require('fs'); + +interface LokiPersistenceInterface { + loadDatabase(dbname: string, callback: (dataOrErr: string | Error) => void): void; + saveDatabase(dbname: string, dbstring: string, callback: (resOrErr: void | Error) => void): void; + deleteDatabase(dbname: string, callback?: (resOrErr: void | Error) => void): void; + // optional + mode?: string; // 'reference' + // filename may seem redundant but loadDatabase will need to expect this same filename + exportDatabase?(filename: string, param: any, callback?: (err: any) => void): void; +} + + +/** constructor for fs + */ +interface LokiFsAdapter extends LokiPersistenceInterface { + fs: any; //require('fs'); /** loadDatabase() - Load data from file, will throw an error if the file does not exist * @param {string} dbname - the filename of the database to load * @param {function} callback - the callback to handle the result */ - loadDatabase(dbname: string, callback: (data: string | Error) => void): void; + loadDatabase(dbname: string, callback: (err: Error, data: string) => void): void; /** saveDatabase() - save data to file, will throw an error if the file can't be saved * might want to expand this to avoid dataloss on partial save * @param {string} dbname - the filename of the database to load * @param {function} callback - the callback to handle the result */ - saveDatabase(dbname: string, dbstring: string, callback: (error: any) => void): void; + saveDatabase(dbname: string, dbstring: string, callback: (err: any) => void): void; + + /** deleteDatabase() - delete the database file, will throw an error if the + * file can't be deleted + * @param {string} dbname - the filename of the database to delete + * @param {function} callback - the callback to handle the result + */ + deleteDatabase(dbname: string, callback: (resOrErr: void | Error) => void): void; } - - -/** constructor for local storage */ -interface LokiLocalStorageAdapter { +/** constructor for local storage + */ +interface LokiLocalStorageAdapter extends LokiPersistenceInterface { /** loadDatabase() - Load data from localstorage * @param {string} dbname - the name of the database to load * @param {function} callback - the callback to handle the result */ - loadDatabase(dbname: string, callback: (data: string | Error) => void): void; + loadDatabase(dbname: string, callback: (dataOrErr: string | Error) => void): void; /** saveDatabase() - save data to localstorage, will throw an error if the file can't be saved * might want to expand this to avoid dataloss on partial save * @param {string} dbname - the filename of the database to load * @param {function} callback - the callback to handle the result */ - saveDatabase(dbname: string, dbstring: string, callback: (error: Error) => void): void; + saveDatabase(dbname: string, dbstring: string, callback: (resOrErr: void | Error) => void): void; + + /** deleteDatabase() - delete the database from localstorage, will throw an error if it + * can't be deleted + * @param {string} dbname - the filename of the database to delete + * @param {function} callback - the callback to handle the result + */ + deleteDatabase(dbname: string, callback: (resOrErr: void | Error) => void): void; } /** Resultset class allowing chainable queries. Intended to be instanced internally. - * Collection.find(), Collection.where(), and Collection.chain() instantiate this. - * Example: - * mycollection.chain() - * .find({ 'doors' : 4 }) - * .where(function(obj) { return obj.name === 'Toyota' }) - * .data(); - * @param {Collection} collection - The collection which this Resultset will query against. - * @param {string} queryObj - Optional mongo-style query object to initialize resultset with. - * @param {function} queryFunc - Optional javascript filter function to initialize resultset with. - * @param {bool} firstOnly - Optional boolean used by collection.findOne(). + * Collection.find(), Collection.where(), and Collection.chain() instantiate this. + * + * Example: + * mycollection.chain() + * .find({ 'doors' : 4 }) + * .where(function(obj) { return obj.name === 'Toyota' }) + * .data(); */ interface LokiResultset { + // retain reference to collection we are querying against collection: LokiCollection; - searchIsChained: boolean; - filteredrows: string[]; // technically number[] (e.g. = Object.keys(this.collection.data)) filterInitialized: boolean; + filteredrows: string[]; // technically number[] (e.g. = Object.keys(this.collection.data)) + options: LokiResultsetOptions; + searchIsChained: boolean; - new (collection: LokiCollection, queryObj: LokiQuery, queryFunc: (obj: E) => boolean, firstOnly?: boolean): LokiResultset; - /** toJSON() - Override of toJSON to avoid circular references */ + /** + * @constructor + * @param {Collection} collection - The collection which this Resultset will query against. + * @param {Object} options - Object containing one or more options. + * @param {string} options.queryObj - Optional mongo-style query object to initialize resultset with. + * @param {function} options.queryFunc - Optional javascript filter function to initialize resultset with. + * @param {bool} options.firstOnly - Optional boolean used by collection.findOne(). + */ + new (collection: LokiCollection, options: LokiResultsetOptions): LokiResultset | E[]; + + /** reset() - Reset the resultset to its initial state. + * + * @returns {Resultset} Reference to this resultset, for future chain operations. + */ + reset(): LokiResultset; + + /** toJSON() - Override of toJSON to avoid circular references + */ toJSON(): LokiResultset; /** limit() - Allows you to limit the number of documents passed to next chain operation. - * A resultset copy() is made to avoid altering original resultset. + * A resultset copy() is made to avoid altering original resultset. + * * @param {int} qty - The number of documents to return. * @returns {Resultset} Returns a copy of the resultset, limited by qty, for subsequent chain ops. */ limit(qty: number): LokiResultset; /** offset() - Used for skipping 'pos' number of documents in the resultset. + * * @param {int} pos - Number of documents to skip; all preceding documents are filtered out. * @returns {Resultset} Returns a copy of the resultset, containing docs starting at 'pos' for subsequent chain ops. */ offset(pos: number): LokiResultset; /** copy() - To support reuse of resultset in branched query situations. + * * @returns {Resultset} Returns a copy of the resultset (set) but the underlying document references will be the same. */ copy(): LokiResultset; - - // add branch() as alias of copy() + // alias of copy() branch(): LokiResultset; + /** + * transform() - executes a named collection transform or raw array of transform steps against the resultset. + * + * @param transform {string|array} : (Optional) name of collection transform or raw transform array + * @param parameters {object} : (Optional) object property hash of parameters, if the transform requires them. + * @returns {Resultset} : either (this) resultset or a clone of of this resultset (depending on steps) + */ + transform(transform?: string | any[], parameters?: any): LokiResultset; + /** sort() - User supplied compare function is provided two documents to compare. (chainable) - * Example: - * rslt.sort(function(obj1, obj2) { - * if (obj1.name === obj2.name) return 0; - * if (obj1.name > obj2.name) return 1; - * if (obj1.name < obj2.name) return -1; - * }); + * Example: + * rslt.sort(function(obj1, obj2) { + * if (obj1.name === obj2.name) return 0; + * if (obj1.name > obj2.name) return 1; + * if (obj1.name < obj2.name) return -1; + * }); + * * @param {function} comparefun - A javascript compare function used for sorting. * @returns {Resultset} Reference to this resultset, sorted, for future chain operations. */ sort(comparefun: (a: E, b: E) => number): LokiResultset; /** simplesort() - Simpler, loose evaluation for user to sort based on a property name. (chainable) + * * @param {string} propname - name of property to sort by. * @param {bool} isdesc - (Optional) If true, the property will be sorted in descending order * @returns {Resultset} Reference to this resultset, sorted, for future chain operations. */ simplesort(propname: string, isdesc?: boolean): LokiResultset; - /** compoundeval() - helper method for compoundsort(), performing individual object comparisons - * @param {array} properties - array of property names, in order, by which to evaluate sort order - * @param {object} obj1 - first object to compare - * @param {object} obj2 - second object to compare - * @returns {integer} 0, -1, or 1 to designate if identical (sortwise) or which should be first - */ - compoundeval(properties: any[], obj1: any, obj2: any): number; - /** compoundsort() - Allows sorting a resultset based on multiple columns. - * Example : rs.compoundsort(['age', 'name']); to sort by age and then name (both ascending) - * Example : rs.compoundsort(['age', ['name', true]); to sort by age (ascending) and then by name (descending) + * Example : rs.compoundsort(['age', 'name']); to sort by age and then name (both ascending) + * Example : rs.compoundsort(['age', ['name', true]); to sort by age (ascending) and then by name (descending) + * * @param {array} properties - array of property names or subarray of [propertyname, isdesc] used evaluate sort order * @returns {Resultset} Reference to this resultset, sorted, for future chain operations. */ - compoundsort(properties: any[]): LokiResultset; + compoundsort(properties: ([string, boolean] | [string])[]): LokiResultset; /** calculateRange() - Binary Search utility method to find range/segment of values matching criteria. - * this is used for collection.find() and first find filter of resultset/dynview - * slightly different than get() binary search in that get() hones in on 1 value, - * but we have to hone in on many (range) - * @param {string} op - operation, such as $eq - * @param {string} prop - name of property to calculate range for - * @param {object} val - value to use for range calculation. - * @returns {array} [start, end] index array positions - */ + * this is used for collection.find() and first find filter of resultset/dynview + * slightly different than get() binary search in that get() hones in on 1 value, + * but we have to hone in on many (range) + * @param {string} op - operation, such as $eq + * @param {string} prop - name of property to calculate range for + * @param {object} val - value to use for range calculation. + * @returns {array} [start, end] index array positions + */ calculateRange(op: "$eq", prop: string, val: any): [number/*start*/, number/*end*/]; + calculateRange(op: "$dteq", prop: string, val: any): [number/*start*/, number/*end*/]; calculateRange(op: "$gt", prop: string, val: any): [number/*start*/, number/*end*/]; calculateRange(op: "$gte", prop: string, val: any): [number/*start*/, number/*end*/]; calculateRange(op: "$lt", prop: string, val: any): [number/*start*/, number/*end*/]; @@ -342,57 +432,78 @@ interface LokiResultset { calculateRange(op: string, prop: string, val: any): [number/*start*/, number/*end*/]; /** findOr() - oversee the operation of OR'ed query expressions. - * OR'ed expression evaluation runs each expression individually against the full collection, - * and finally does a set OR on each expression's results. - * Each evaluation can utilize a binary index to prevent multiple linear array scans. + * OR'ed expression evaluation runs each expression individually against the full collection, + * and finally does a set OR on each expression's results. + * Each evaluation can utilize a binary index to prevent multiple linear array scans. + * * @param {array} expressionArray - array of expressions * @returns {Resultset} this resultset for further chain ops. */ findOr(expressionArray: LokiQuery[]): LokiResultset; + $or(expressionArray: LokiQuery[]): LokiResultset; /** findAnd() - oversee the operation of AND'ed query expressions. - * AND'ed expression evaluation runs each expression progressively against the full collection, - * internally utilizing existing chained resultset functionality. - * Only the first filter can utilize a binary index. + * AND'ed expression evaluation runs each expression progressively against the full collection, + * internally utilizing existing chained resultset functionality. + * Only the first filter can utilize a binary index. + * * @param {array} expressionArray - array of expressions * @returns {Resultset} this resultset for further chain ops. */ findAnd(expressionArray: LokiQuery[]): LokiResultset; - - /** dotSubScan - helper function used for dot notation queries. */ - dotSubScan(root: any, property: string, fun: (a: any, b: any) => boolean, value: any): boolean; + $and(expressionArray: LokiQuery[]): LokiResultset; /** find() - Used for querying via a mongo-style query object. + * * @param {object} query - A mongo-style query object used for filtering current results. * @param {boolean} firstOnly - (Optional) Used by collection.findOne() * @returns {Resultset} this resultset for further chain ops. */ + //find(query: LokiQuery, firstOnly: boolean): E; //find(query?: any, firstOnly?: boolean): E[]; find(query: LokiQuery, firstOnly?: boolean): LokiResultset; /** where() - Used for filtering via a javascript filter function. + * * @param {function} fun - A javascript function used for filtering current results by. * @returns {Resultset} this resultset for further chain ops. */ where(fun: (obj: E) => boolean): LokiResultset; + /** count() - returns the number of documents in the resultset. + * + * @returns {number} The number of documents in the resultset. + */ + count(): number; + /** data() - Terminates the chain and returns array of filtered documents + * + * @param options {object} : allows specifying 'forceClones' and 'forceCloneMethod' options. + * options : + * forceClones {boolean} : Allows forcing the return of cloned objects even when + * the collection is not configured for clone object. + * forceCloneMethod {string} : Allows overriding the default or collection specified cloning method. + * Possible values include 'parse-stringify', 'jquery-extend-deep', and 'shallow' + * * @returns {array} Array of documents in the resultset */ - data(): E[]; + data(options?: { forceClones?: string; forceCloneMethod?: string; }): E[]; /** update() - used to run an update operation on all documents currently in the resultset. - * @param {function} updateFunction - User supplied updateFunction(obj) will be executed for each document object. - * @returns {Resultset} this resultset for further chain ops. - */ - update(updateFunction: (obj: E) => U): LokiResultset; + * + * @param {function} updateFunction - User supplied updateFunction(obj) will be executed for each document object. + * @returns {Resultset} this resultset for further chain ops. + */ + update(updateFunction: (obj: E) => void): LokiResultset; /** remove() - removes all document objects which are currently in resultset from collection (as well as resultset) + * * @returns {Resultset} this (empty) resultset for further chain ops. */ remove(): LokiResultset; /** mapReduce() - data transformation via user supplied functions + * * @param {function} mapFunction - this function accepts a single document for you to transform and return * @param {function} reduceFunction - this function accepts many (array of map outputs) and returns single value * @returns The output of your reduceFunction @@ -407,76 +518,94 @@ interface LokiResultset { * @param {function} (optional) mapFun - A function that receives each matching pair and maps them into output objects - function(left,right){return joinedObject} * @returns {Resultset} A resultset with data in the format [{left: leftObj, right: rightObj}] */ - eqJoin(joinData: T[]| LokiResultset, leftJoinKey: string | ((obj: T) => string), rightJoinKey: string | ((obj: E) => string)): LokiResultset<{ left: E; right: T; }>; - eqJoin(joinData: T[]| LokiResultset, leftJoinKey: string | ((obj: T) => string), rightJoinKey: string | ((obj: E) => string), mapFun?: (a: E, b: T) => U): LokiResultset; - - map(mapFun: (currentValue: E, index: number, array: E[]) => U): LokiResultset; + eqJoin(joinData: T[] | LokiResultset, leftJoinKey: string | ((obj: E) => string), rightJoinKey: string | ((obj: T) => string)): LokiResultset<{ left: E; right: T; }>; + eqJoin(joinData: T[] | LokiResultset, leftJoinKey: string | ((obj: E) => string), rightJoinKey: string | ((obj: T) => string), mapFun?: (a: E, b: T) => U): LokiResultset; + map(mapFun: (currentValue: E, index: number, array: E[]) => T): LokiResultset; } /** DynamicView class is a versatile 'live' view class which can have filters and sorts applied. - * Collection.addDynamicView(name) instantiates this DynamicView object and notifies it - * whenever documents are add/updated/removed so it can remain up-to-date. (chainable) - * Examples: - * var mydv = mycollection.addDynamicView('test'); // default is non-persistent - * mydv.applyWhere(function(obj) { return obj.name === 'Toyota'; }); - * mydv.applyFind({ 'doors' : 4 }); - * var results = mydv.data(); - * @param {Collection} collection - A reference to the collection to work against - * @param {string} name - The name of this dynamic view - * @param {boolean} persistent - (Optional) If true, the results will be copied into an internal array for read efficiency or binding to. + * Collection.addDynamicView(name) instantiates this DynamicView object and notifies it + * whenever documents are add/updated/removed so it can remain up-to-date. (chainable) + * + * Examples: + * var mydv = mycollection.addDynamicView('test'); // default is non-persistent + * mydv.applyWhere(function(obj) { return obj.name === 'Toyota'; }); + * mydv.applyFind({ 'doors' : 4 }); + * var results = mydv.data(); + * */ interface LokiDynamicView extends LokiEventEmitter { - name: string; + cachedresultset: LokiResultset; collection: LokiCollection; + events: { [id: string]: ((...args: any[]) => void)[] }; /*{ + 'rebuild': ((...args) => void)[]; + };*/ + // keep ordered filter pipeline + filterPipeline: LokiFilter[]; + minRebuildInterval: number; + name: string; + options: LokiDynamicViewOptions; persistent: boolean; + rebuildPending: boolean; resultset: LokiResultset; resultdata: E[]; resultsdirty: boolean; - cachedresultset: LokiResultset; // TODO type - // keep ordered filter pipeline - filterPipeline: { type: string/*'find', 'where'*/; value: LokiQuery | ((element: E, index: number, array: E[]) => boolean) }[]; // TODO type - // sorting member variables - we only support one active search, applied using applySort() or applySimpleSort() + // sorting member variables, we only support one active search, applied using applySort() or applySimpleSort() sortFunction: (a: E, b: E) => number; - sortCriteria: [string, boolean][]; // TODO type + sortCriteria: ([string, boolean] | [string])[]; sortDirty: boolean; - // for now just have 1 event for when we finally rebuilt lazy view - // once we refactor transactions, i will tie in certain transactional events - events: { - "rebuild": any[]; // TODO type - }; + sortPriority: string; // 'persistentSortPriority', 'passive' (will defer the sort phase until they call data(). most efficient overall), 'active' (will sort async whenever next idle. prioritizes read speeds) - new (collection: LokiCollection, name: string, persistent?: boolean): LokiDynamicView; + /** + * @constructor + * @param {Collection} collection - A reference to the collection to work against + * @param {string} name - The name of this dynamic view + * @param {object} options - (Optional) Pass in object with 'persistent' and/or 'sortPriority' options. + */ + new (collection: LokiCollection, name: string, options?: LokiDynamicViewOptions): LokiDynamicView; /** rematerialize() - intended for use immediately after deserialization (loading) - * This will clear out and reapply filterPipeline ops, recreating the view. - * Since where filters do not persist correctly, this method allows - * restoring the view to state where user can re-apply those where filters. + * This will clear out and reapply filterPipeline ops, recreating the view. + * Since where filters do not persist correctly, this method allows + * restoring the view to state where user can re-apply those where filters. + * * @param {Object} options - (Optional) allows specification of 'removeWhereFilters' option * @returns {DynamicView} This dynamic view for further chained ops. */ - rematerialize(options: { removeWhereFilters?: any/*boolean - if prop exists, action occurs*/; }): LokiDynamicView; + rematerialize(options?: { removeWhereFilters?: boolean; }): LokiDynamicView; /** branchResultset() - Makes a copy of the internal resultset for branched queries. - * Unlike this dynamic view, the branched resultset will not be 'live' updated, - * so your branched query should be immediately resolved and not held for future evaluation. + * Unlike this dynamic view, the branched resultset will not be 'live' updated, + * so your branched query should be immediately resolved and not held for future evaluation. + * + * @param {string|array} transform: Optional name of collection transform, or an array of transform steps + * @param {object} parameters: optional parameters (if optional transform requires them) * @returns {Resultset} A copy of the internal resultset for branched queries. */ - branchResultset(): LokiResultset; + branchResultset(transform?: string | any[], parameters?: any): LokiResultset; - /** toJSON() - Override of toJSON to avoid circular references */ + /** toJSON() - Override of toJSON to avoid circular references + */ toJSON(): LokiDynamicView; + /** removeFilters() - Used to clear pipeline and reset dynamic view to initial state. + * Existing options should be retained. + */ + removeFilters(): void; + /** applySort() - Used to apply a sort to the dynamic view + * * @param {function} comparefun - a javascript compare function used for sorting * @returns {DynamicView} this DynamicView object, for further chain ops. */ applySort(comparefun: (a: E, b: E) => number): LokiDynamicView; /** applySimpleSort() - Used to specify a property used for view translation. + * * @param {string} propname - Name of property by which to sort. * @param {boolean} isdesc - (Optional) If true, the sort will be in descending order. * @returns {DynamicView} this DynamicView object, for further chain ops. @@ -484,156 +613,279 @@ interface LokiDynamicView extends LokiEventEmitter { applySimpleSort(propname: string, isdesc?: boolean): LokiDynamicView; /** applySortCriteria() - Allows sorting a resultset based on multiple columns. - * Example : dv.applySortCriteria(['age', 'name']); to sort by age and then name (both ascending) - * Example : dv.applySortCriteria(['age', ['name', true]); to sort by age (ascending) and then by name (descending) - * Example : dv.applySortCriteria(['age', true], ['name', true]); to sort by age (descending) and then by name (descending) + * Example : dv.applySortCriteria(['age', 'name']); to sort by age and then name (both ascending) + * Example : dv.applySortCriteria(['age', ['name', true]); to sort by age (ascending) and then by name (descending) + * Example : dv.applySortCriteria(['age', true], ['name', true]); to sort by age (descending) and then by name (descending) + * * @param {array} properties - array of property names or subarray of [propertyname, isdesc] used evaluate sort order * @returns {DynamicView} Reference to this DynamicView, sorted, for future chain operations. */ - applySortCriteria(criteria: string | any[]): LokiDynamicView; + applySortCriteria(criteria: ([string, boolean] | [string])[]): LokiDynamicView; /** startTransaction() - marks the beginning of a transaction. + * * @returns {DynamicView} this DynamicView object, for further chain ops. */ startTransaction(): LokiDynamicView; /** commit() - commits a transaction. + * * @returns {DynamicView} this DynamicView object, for further chain ops. */ commit(): LokiDynamicView; /** rollback() - rolls back a transaction. + * * @returns {DynamicView} this DynamicView object, for further chain ops. */ rollback(): LokiDynamicView; - /** applyFind() - Adds a mongo-style query option to the DynamicView filter pipeline - * @param {object} query - A mongo-style query object to apply to pipeline - * @returns {DynamicView} this DynamicView object, for further chain ops. + /** Implementation detail. + * _indexOfFilterWithId() - Find the index of a filter in the pipeline, by that filter's ID. + * + * @param {string|number} uid - The unique ID of the filter. + * @returns {number}: index of the referenced filter in the pipeline; -1 if not found. */ - applyFind(query: LokiQuery): LokiDynamicView; + _indexOfFilterWithId(uid: string | number): number; - /** applyWhere() - Adds a javascript filter function to the DynamicView filter pipeline - * @param {function} fun - A javascript filter function to apply to pipeline + /** Implementation detail. + * _addFilter() - Add the filter object to the end of view's filter pipeline and apply the filter to the resultset. + * + * @param {object} filter - The filter object. Refer to applyFilter() for extra details. + */ + _addFilter(filter: LokiFilter): void; + + /** reapplyFilters() - Reapply all the filters in the current pipeline. + * * @returns {DynamicView} this DynamicView object, for further chain ops. */ - applyWhere(fun: (obj: E) => boolean): LokiDynamicView; + reapplyFilters(): LokiDynamicView; + + /** applyFilter() - Adds or updates a filter in the DynamicView filter pipeline + * + * @param {object} filter - A filter object to add to the pipeline. + * The object is in the format { 'type': filter_type, 'val', filter_param, 'uid', optional_filter_id } + * @returns {DynamicView} this DynamicView object, for further chain ops. + */ + applyFilter(filter: LokiFilter): LokiDynamicView; + + /** applyFind() - Adds or updates a mongo-style query option in the DynamicView filter pipeline + * + * @param {object} query - A mongo-style query object to apply to pipeline + * @param {string|number} uid - Optional: The unique ID of this filter, to reference it in the future. + * @returns {DynamicView} this DynamicView object, for further chain ops. + */ + applyFind(query: LokiQuery, uid?: string | number): LokiDynamicView; + + /** applyWhere() - Adds or updates a javascript filter function in the DynamicView filter pipeline + * + * @param {function} fun - A javascript filter function to apply to pipeline + * @param {string|number} uid - Optional: The unique ID of this filter, to reference it in the future. + * @returns {DynamicView} this DynamicView object, for further chain ops. + */ + applyWhere(fun: (obj: E) => boolean, uid?: string | number): LokiDynamicView; + + /** removeFilter() - Remove the specified filter from the DynamicView filter pipeline + * + * @param {string|number} uid - The unique ID of the filter to be removed. + * @returns {DynamicView} this DynamicView object, for further chain ops. + */ + removeFilter(uid: string | number): LokiDynamicView; + + /** count() - returns the number of documents representing the current DynamicView contents. + * + * @returns {number} The number of documents representing the current DynamicView contents. + */ + count(): number; /** data() - resolves and pending filtering and sorting, then returns document array as result. + * * @returns {array} An array of documents representing the current DynamicView contents. */ data(): E[]; - /** */ + /** queueRebuildEvent() - When the view is not sorted we may still wish to be notified of rebuild events. + * This event will throttle and queue a single rebuild event when batches of updates affect the view. + */ + queueRebuildEvent(): void; + + /** queueSortPhase : If the view is sorted we will throttle sorting to either : + * (1) passive - when the user calls data(), or + * (2) active - once they stop updating and yield js thread control + */ queueSortPhase(): void; - /** performSortPhase() - invoked synchronously or asynchronously to perform final sort phase (if needed) */ - performSortPhase(): void; + /** performSortPhase() - invoked synchronously or asynchronously to perform final sort phase (if needed) + */ + performSortPhase(options?: { suppressRebuildEvent?: boolean; }): void; /** evaluateDocument() - internal method for (re)evaluating document inclusion. - * Called by : collection.insert() and collection.update(). + * Called by : collection.insert() and collection.update(). + * * @param {int} objIndex - index of document to (re)run through filter pipeline. + * @param {bool} isNew - true if the document was just added to the collection. */ - evaluateDocument(objIndex: number): void; + evaluateDocument(objIndex: number, isNew?: boolean): void; - /** removeDocument() - internal function called on collection.delete() */ + /** removeDocument() - internal function called on collection.delete() + */ removeDocument(objIndex: number): void; /** mapReduce() - data transformation via user supplied functions + * * @param {function} mapFunction - this function accepts a single document for you to transform and return * @param {function} reduceFunction - this function accepts many (array of map outputs) and returns single value * @returns The output of your reduceFunction */ - mapReduce(mapFunction: (value: E, index: number, array: E[]) => T, reduceFunction: (array: T[]) => U): U; - + mapReduce(mapFunction: (item: E, index: number, array: E[]) => T, reduceFunction: (array: T[]) => U): U; } /** Collection class that handles documents of same type - * @param {stirng} collection name - * @param {array} array of property names to be indicized - * @param {object} configuration object */ interface LokiCollection extends LokiEventEmitter { - // the name of the collection - name: string; - // the data held by the collection - data: E[]; - idIndex: number[]; // index of id - binaryIndices: { [id: string]: { name: string; dirty: boolean; values: number[] } }; // user defined indexes - constraints: { unique: { [id: string]: LokiUniqueIndex }; exact: { [id: string]: LokiExactIndex }; } - // the object type of the collection - objType: string; - // currentMaxId - change manually at your own peril! - maxId: number; - DynamicViews: LokiDynamicView[]; // TODO type - events: { - 'insert': any[]; - 'update': any[]; - 'pre-insert': any[]; - 'pre-update': any[]; - 'close': any[]; - 'flushbuffer': any[]; - 'error': any[]; - 'delete': any[]; - 'warning': any[]; + // option to observe objects and update them automatically, ignored if Object.observe is not supported + autoupdate: boolean; + // option to make event listeners async, default is sync + asyncListeners: boolean; + binaryIndices: { [id: string]: { name: string; dirty: boolean; values: number[] } }; + + cachedIndex: number[]; + cachedBinaryIndex: { [id: string]: { name: string; dirty: boolean; values: number[] } }; + cachedData: E[]; + // changes are tracked by collection and aggregated by the db + changes: LokiCollectionChange[]; + // default clone method (if enabled) is parse-stringify + cloneMethod: string; // 'parse-stringify' + // options to clone objects when inserting them + cloneObjects: boolean; + console: { + log: () => void; + warn: () => void; + error: () => void; }; + constraints: { + unique: { [id: string]: LokiUniqueIndex }; + exact: { [id: string]: LokiExactIndex }; + }; + data: E[]; // in autosave scenarios we will use collection level dirty flags to determine whether save is needed. // currently, if any collection is dirty we will autosave the whole database if autosave is configured. // defaulting to true since this is called from addCollection and adding a collection should trigger save dirty: boolean; - // changes are tracked by collection and aggregated by the db - changes: { name: string; operation: string/*'I', 'U', 'R'*/; obj: any }[]; - // private holders for cached data - cachedIndex: any; // TODO type - cachedBinaryIndex: any; // TODO type - cachedData: any; // TODO type - // options - transactional: boolean; - cloneObjects: boolean; - asyncListeners: boolean; + // disable track changes disableChangesApi: boolean; - setChangesApi: (enabled: boolean) => void; + DynamicViews: LokiDynamicView[]; + events: { [id: string]: ((...args: any[]) => void)[] }; /*{ + 'insert': ((...args) => void)[]; + 'update': ((...args) => void)[]; + 'pre-insert': ((...args) => void)[]; + 'pre-update': ((...args) => void)[]; + 'close': ((...args) => void)[]; + 'flushbuffer': ((...args) => void)[]; + 'error': ((...args) => void)[]; + 'delete': ((...args) => void)[]; + 'warning': ((...args) => void)[]; + };*/ + idIndex: number[]; + maxId: number; // currentMaxId - change manually at your own peril! + name: string; + // is collection transactional + transactional: boolean; + objType: string; + // transforms will be used to store frequently used query chains as a series of steps + // which itself can be stored along with the database. + transforms: { [id: string]: any }; + // unique contraints contain duplicate object references, so they are not persisted. + // we will keep track of properties which have unique contraint applied here, and regenerate on load + uniqueNames: string[]; - new (name: string, options?: LokiCollectionOptions): LokiCollection; + options: LokiCollectionOptions; + + // option to activate a cleaner daemon - clears "aged" documents at set intervals. + ttl: { + age: number; + ttlInterval: number; + daemon: number; + }; + + /** Collection class that handles documents of same type + * @constructor + * @param {string} collection name + * @param {array} array of property names to be indicized + * @param {object} configuration object + */ + new (name: string, options?: LokiCollectionOptions): LokiCollection; getChanges(): LokiCollectionChange[]; + setChangesApi(enabled: boolean): void; + flushChanges(): void; - byExample(template: any): { $and: any[] }; + observerCallback: (changes: { object: any }[]) => void; + + addAutoUpdateObserver(object: any): void; + + removeAutoUpdateObserver(object: any): void; + + addTransform(name: string, transform: any): void; + + setTransform(name: string, transform: any): void; + + removeTransform(name: string): void; + + byExample(template: any): { '$and': any[] }; findObject(template: any): E; findObjects(template: any): E[]; + /*----------------------------+ + | TTL daemon | + +----------------------------*/ + ttlDaemonFuncGen(): () => void; + + setTTL(age: number, interval: number): void; /*----------------------------+ | INDEXING | +----------------------------*/ - /** Ensure binary index on a certain field */ + /** + * create a row filter that covers all documents in the collection + */ + prepareFullDocIndex(): number[]; + + /** Ensure binary index on a certain field + */ ensureIndex(property: string, force?: boolean): void; - ensureUniqueIndex(field: string): void; + ensureUniqueIndex(field: string): LokiUniqueIndex; - /** Ensure all binary indices */ + /** Ensure all binary indices + */ ensureAllIndexes(force?: boolean): void; flagBinaryIndexesDirty(): void; - count(): number; + flagBinaryIndexDirty(index: string): void; - /** Rebuild idIndex */ + count(query?: LokiQuery): number; + + /** Rebuild idIndex + */ ensureId(): void; - /** Rebuild idIndex async with callback - useful for background syncing with a remote server */ + /** Rebuild idIndex async with callback - useful for background syncing with a remote server + */ ensureIdAsync(callback: () => void): void; - /** Each collection maintains a list of DynamicViews associated with it */ - addDynamicView(name: string, persistent?: boolean): LokiDynamicView; + /** Each collection maintains a list of DynamicViews associated with it + **/ + addDynamicView(name: string, options?: LokiDynamicViewOptions): LokiDynamicView; removeDynamicView(name: string): void; @@ -644,61 +896,78 @@ interface LokiCollection extends LokiEventEmitter { */ findAndUpdate(filterFunction: (obj: E) => boolean, updateFunction: (obj: E) => E): void; - /** generate document method - ensure objects have id and objType properties - * @param {object} the document to be inserted (or an array of objects) + /** generate document method - ensure object(s) have meta properties, clone it if necessary, etc. + * @param {object} doc: the document to be inserted (or an array of objects) * @returns document or documents (if passed an array of objects) */ insert(doc: E): E; insert(doc: E[]): E[]; + /** generate document method - ensure object has meta properties, clone it if necessary, etc. + * @param {object} the document to be inserted + * @returns document or 'undefined' if there was a problem inserting it + */ + insertOne(doc: E): E; + clear(): void; - /** Update method */ - update(doc: E): void; + /** Update method + */ + update(doc: E): E; + update(doc: E[]): void; - /** Add object to collection */ + /** Add object to collection + */ add(obj: E): E; removeWhere(query: ((obj: E) => boolean) | LokiQuery): void; removeDataOnly(): void; - /** delete wrapped */ - remove(doc: E | E[]| number | number[]): E; - + /** delete wrapped + */ + remove(doc: E): E; + remove(doc: number): E; + remove(doc: number[]): void; + remove(doc: E[]): void; /*---------------------+ | Finding methods | +----------------------*/ - /** Get by Id - faster than other methods because of the searching algorithm */ + /** Get by Id - faster than other methods because of the searching algorithm + */ get(id: number | string): E; - //get(id: number | string, returnPosition: true): [E, number]; - get(id: number | string, returnPosition: boolean): E |[E, number]; + get(id: number | string, returnPosition?: boolean): E | [E, number]; by(field: string): (value: any) => E; - by(field: string, value?: string): E; + by(field: string, value: string): E; - /** Find one object by index property, by property equal to value */ + /** Find one object by index property, by property equal to value + */ findOne(query: LokiQuery): E; /** Chain method, used for beginning a series of chained find() and/or view() operations * on a collection. + * + * @param {array} transform : Ordered array of transform step objects similar to chain + * @param {object} parameters: Object containing properties representing parameters to substitute + * @returns {Resultset} : (or data array if any map or join functions where called) */ - chain(): LokiResultset; + chain(transform?: string | any[], parameters?: any): LokiResultset; - /** Find method, api is similar to mongodb except for now it only supports one search parameter. + /** + * Find method, api is similar to mongodb except for now it only supports one search parameter. * for more complex queries use view() and storeView() */ - find(): LokiResultset; - find(query: LokiQuery): E[]; + find(): E[]; + find(query: LokiQuery): LokiResultset; /** Find object by unindexed field by property equal to value, * simply iterates and returns the first element matching the query */ findOneUnindexed(prop: string, value: any): E; - /** Transaction methods */ /** start the transation */ @@ -713,16 +982,18 @@ interface LokiCollection extends LokiEventEmitter { // async executor. This is only to enable callbacks at the end of the execution. async(fun: () => void, callback: () => void): void; - /** Create view function - filter */ + /** Create view function - filter + */ where(fun: (obj: E) => boolean): LokiResultset; - /** Map Reduce */ - mapReduce(mapFunction: (value: E, index: number, array: E[]) => T, reduceFunction: (previousValue: U, currentValue: T, index: number, array: T[]) => U): U; - - /** eqJoin - Join two collections on specified properties */ - eqJoin(joinData: T[]| LokiResultset, leftJoinProp: string | ((obj: T) => string), rightJoinProp: string | ((obj: E) => string)): LokiResultset<{ left: E; right: T; }>; - eqJoin(joinData: T[]| LokiResultset, leftJoinProp: string | ((obj: T) => string), rightJoinProp: string | ((obj: E) => string), mapFun?: (a: E, b: T) => U): LokiResultset; + /** Map Reduce + */ + mapReduce(mapFunction: (item: E, index: number, array: E[]) => U, reduceFunction: (array: U[]) => V): V; + /** eqJoin - Join two collections on specified properties + */ + eqJoin(joinData: T[] | LokiResultset, leftJoinProp: string | ((obj: E) => string), rightJoinProp: string | ((obj: T) => string)): LokiResultset<{ left: E; right: T; }>; + eqJoin(joinData: T[] | LokiResultset, leftJoinProp: string | ((obj: E) => string), rightJoinProp: string | ((obj: T) => string), mapFun?: (a: E, b: T) => U): LokiResultset; /* ------ STAGING API -------- */ /** stages: a map of uniquely identified 'stages', which hold copies of objects to be @@ -730,14 +1001,21 @@ interface LokiCollection extends LokiEventEmitter { */ stages: { [id: string]: any }; - /** create a stage and/or retrieve it */ - getStage(name: string): any; + /** create a stage and/or retrieve it + */ + getStage(name: string): E[]; - /** a collection of objects recording the changes applied through a commmitStage */ - commitLog: { timestamp: number; message: any; data: any }[]; + /** a collection of objects recording the changes applied through a commmitStage + */ + commitLog: { + timestamp: number; // timestamp (i.e. new Date().getTime()) + message: any; + data: E; + }[]; - /** create a copy of an object and insert it into a stage */ - stage(stageName: string, obj: T): T; + /** create a copy of an object and insert it into a stage + */ + stage(stageName: string, obj: E): E; /** re-attach all objects to the original collection, so indexes and views can be rebuilt * then create a message to be inserted in the commitlog @@ -762,80 +1040,187 @@ interface LokiCollection extends LokiEventEmitter { stdDev(field: string): number; - mode(field: string): number; + mode(field: string): string | number; median(field: string): number; } -/* -interface Utils { - copyProperties(src, dest): void; -} - -// Sort helper that support null and undefined -function ltHelper(prop1, prop2, equal?: boolean): boolean; - -function gtHelper(prop1, prop2, equal?: boolean): boolean; - -function sortHelper(prop1, prop2, desc?: boolean): number; - -function containsCheckFn(a: T[], b): (curr: T) => boolean; - -function containsCheckFn(a: string, b): (curr: string) => boolean; - -function containsCheckFn(a: T, b): (curr: string) => boolean; - -function clone(data: T, method?: string): T; - -function localStorageAvailable(): boolean; -*/ - - -/** General utils, including statistical functions */ -/* -function isDeepProperty(field: string): boolean; - -function parseBase10(num: number | string): number; - -function isNotUndefined(obj: any): boolean; - -function add(a: number, b: number): number; - -function sub(a: number, b: number): number; - -function median(values: number[]): number; - -function average(array: number[]): number; - -function standardDeviation(values: number[]): number; -*/ +/** comparison operators + * a is the value in the collection + * b is the query value + */ interface LokiOps { - // comparison operators - $eq: (a: any, b: any) => boolean; - $gt: (a: any, b: any) => boolean; - $gte: (a: any, b: any) => boolean; - $lt: (a: any, b: any) => boolean; - $lte: (a: any, b: any) => boolean; - $ne: (a: any, b: any) => boolean; - $regex: (a: string, b: RegExp) => boolean; - $in: (a: any, b: { indexOf: (value: any) => number }) => boolean; - $containsAny: (a: any, b: any[]| any) => boolean; - $contains: (a: any, b: any[]| any) => boolean; -} - -//declare var operators: LokiOps; - - -interface LokiDeepProperty { - (obj: any, property: string, isDeep?: boolean): any; + $eq(a: any, b: any): boolean; + $ne(a: any, b: any): boolean; + $dteq(a: any, b: any): boolean; + $gt(a: any, b: any): boolean; + $gte(a: any, b: any): boolean; + $lt(a: any, b: any): boolean; + $lte(a: any, b: any): boolean; + $in(a: any, b: { indexOf: (value: any) => boolean }): boolean; + $nin(a: any, b: { indexOf: (value: any) => boolean }): boolean; + $keyin(a: string, b: any): boolean; + $nkeyin(a: string, b: any): boolean; + $definedin(a: any, b: any): boolean; + $undefinedin(a: any, b: any): boolean; + $regex(a: any, b: RegExp | { test: (str: string) => boolean }): boolean; + $containsString(a: string | any, b: string): boolean; + $containsNone(a: any, b: any): boolean; + $containsAny(a: any, b: any | any[]): boolean; + $contains(a: any, b: any | any[]): boolean; + $type(a: any, b: any): boolean; + $size(a: any, b: any): boolean; + $len(a: any, b: any): boolean; + // field-level logical operators + // a is the value in the collection + // b is the nested query operation (for '$not') + // or an array of nested query operations (for '$and' and '$or') + $not(a: any, b: any): boolean; + $and(a: any, b: any[]): boolean; + $or(a: any, b: any[]): boolean; } -interface LokiBinarySearch { - (array: T[], item: T, fun: (a: T, b: T) => number): { found: boolean; index: number; }; +interface LokiKeyValueStore { + keys: K[]; + values: V[]; + + sort(a: any, b: any): number; + setSort(fun: (a: K, b: K) => number): void; + bs(): LokiBSonSort; + set(key: K, value: V): void; + get(key: K): V; +} + + +interface LokiUniqueIndex { + field: string; + keyMap: { [id: string]: E }; + lokiMap: { [id: number]: any }; + + new (uniqueField: string): LokiUniqueIndex; + + set(obj: E): void; + get(key: string): E; + byId(id: number): E; + update(obj: E): void; + remove(key: string): void; + clear(): void; +} + + +interface LokiExactIndex { + index: { [id: string]: E[] }; + field: string; + + new (exactField: string): LokiExactIndex + + /** add the value you want returned to the key in the index */ + set(key: string, val: E): void; + /** remove the value from the index, if the value was the last one, remove the key */ + remove(key: string, val: E): void; + /** get the values related to the key, could be more than one */ + get(key: string): E[]; + /** clear will zap the index */ + clear(key?: any): void; +} + + +interface LokiSortedIndex { + field: string; + keys: K[]; + values: V[][]; + + new (sortedField: string): LokiSortedIndex; + + // set the default sort + sort(a: any, b: any): number; + bs(): LokiBSonSort; + // and allow override of the default sort + setSort(fun: (a: any, b: any) => number): void; + // add the value you want returned to the key in the index + set(key: K, value: V): void; + // get all values which have a key == the given key + get(key: K): V[]; + // get all values which have a key < the given key + getLt(key: K): V[]; + // get all values which have a key > the given key + getGt(key: K): V[]; + // get all vals from start to end + getAll(key: K, start: number, end: number): V[]; + // just in case someone wants to do something smart with ranges + getPos(key: K): { found: boolean; index: number; }; + // remove the value from the index, if the value was the last one, remove the key + remove(key: K, value: V): void; + // clear will zap the index + clear(): void; +} + + +interface LokiConfigureOptions { + adapter?: LokiPersistenceInterface; + autoload?: boolean; + autoloadCallback?: (dataOrErr: any | Error) => void; + autosave?: boolean; + autosaveCallback?: (err: any) => void; + autosaveInterval?: number; // milliseconds between auto-saves + env?: string; /*'NODEJS', 'BROWSER', 'CORDOVA'*/ + persistenceMethod?: string; /*'fs', 'localStorage', 'adapter'*/ + verbose?: boolean; +} + + +interface LokiCollectionOptions { + asyncListeners?: boolean; + autoupdate?: boolean; + clone?: boolean; + cloneMethod?: string; + disableChangesApi?: boolean; + exact?: string[]; + indices?: string | string[]; + transactional?: boolean; + unique?: string | string[]; +} + + +interface LokiDynamicViewOptions { + minRebuildInterval?: number; + persistent?: boolean; + sortPriority: string; /*'active', 'passive'*/ +} + + +interface LokiResultsetOptions { + firstOnly?: boolean; + queryObj?: LokiQuery; + queryFunc?: (item: E) => boolean; +} + + +interface LokiQuery { +} + + +interface LokiFilter { + type: string; /*'find', 'where'*/ + val: LokiQuery | ((obj: E, index: number, array: E[]) => boolean); + uid: number | string; +} + + +interface LokiElementMetaData { + created: number; // unix style timestamp (i.e. new Date().getTime()) + revision: number; +} + + +interface LokiCollectionChange { + name: string; + operation: string;/*'I', 'R', 'U'*/ + obj: any; } @@ -844,140 +1229,82 @@ interface LokiBSonSort { } +/* +interface LokiUtils { + copyProperties(src: any, dest: any): void; + // used to recursively scan hierarchical transform step object for param substitution + resolveTransformObject(subObj: U, params: any, depth?: number): U; -interface LokiKeyValueStore { - keys: K[]; - values: V[]; - - sort(a: K, b: K): number; - setSort(fun: (a: K, b: K) => number): void; - bs(): LokiBSonSort; - set(key: K, value: V): void; - get(key: K): V; + // top level utility to resolve an entire (single) transform (array of steps) for parameter substitution + resolveTransformParams(transform: U[], params: any): U[]; } +// Sort helper that support null and undefined +declare function ltHelper(prop1: any, prop2: any, equal?: boolean): boolean; +declare function gtHelper(prop1: any, prop2: any, equal?: boolean): boolean; +declare function sortHelper(prop1: any, prop2: any, desc?: boolean): number; -interface LokiUniqueIndex { - field: string; - keyMap: { [id: string]: E }; - lokiMap: { [id: number]: any }; // 'field' map +declare function doQueryOp(val: any, op: any): boolean; - new (uniqueField: string): LokiUniqueIndex; +declare function containsCheckFn(a: T[], b): (curr: T) => boolean; +declare function containsCheckFn(a: string, b): (curr: string) => boolean; +declare function containsCheckFn(a: T, b): (curr: string) => boolean; +*/ - set(obj: E): void; +/** General utils, including statistical functions + */ +/* +declare function isDeepProperty(field: string): boolean; - get(key: string): E; +declare function parseBase10(num: string | number): number; - byId(id: number): E; +declare function isNotUndefined(obj: any): boolean; - update(obj: E): void; +declare function add(a: string | number, b: string | number): number; - remove(key: string): void; +declare function sub(a: string | number, b: string | number): number; - clear(): void; -} +declare function median(values: number[]): number; + +declare function average(array: (string | number)[]); + +declare function standardDeviation(values: (string | number)[]): number; + +declare function deepProperty(obj: any, property: string, isDeep?: boolean): any; + +declare function binarySearch(array: U[], item: U, fun: (a: U, b: U) => number): { found: boolean; index: number; }; + +// compoundeval() - helper function for compoundsort(), performing individual object comparisons +// +// @param {array} properties - array of property names, in order, by which to evaluate sort order +// @param {object} obj1 - first object to compare +// @param {object} obj2 - second object to compare +// @returns {integer} 0, -1, or 1 to designate if identical (sortwise) or which should be first +declare function compoundeval(properties: ([string, boolean] | [string])[], obj1: any, obj2: any): number; + +// dotSubScan - helper function used for dot notation queries. +declare function dotSubScan(root: any | any[], propPath: string[], fun: (root, value: V) => boolean, value: V): boolean; + +// making indexing opt-in... our range function knows how to deal with these ops : +//var indexedOpsList = ['$eq', '$dteq', '$gt', '$gte', '$lt', '$lte']; + +declare function clone(data: U, method?: string): U; // stage: 'parse-stringify', 'jquery-extend-deep', 'shallow' + +declare function cloneObjectArray(objarray: U[], method?: string): U; // stage: 'parse-stringify', 'jquery-extend-deep', 'shallow' + +declare function localStorageAvailable(): boolean; +*/ -interface LokiExactIndex { - index: { [id: string]: E[] }; - field: string; - - new (exactField: string): LokiExactIndex; - - // add the value you want returned to the key in the index - set(key: string, val: E): void; - - // remove the value from the index, if the value was the last one, remove the key - remove(key: string, val: E): void; - - // get the values related to the key, could be more than one - get(key: string): E[]; - - // clear will zap the index - clear(key?: any): void; -} - - - -interface LokiSortedIndex { - keys: K[]; - values: V[]; - - new (sortedField: string): LokiSortedIndex; - - // set the default sort - sort(a: K, b: K): number; - bs(): LokiBSonSort; - // and allow override of the default sort - setSort(fun: (a: K, b: K) => number): void; - // add the value you want returned to the key in the index - set(key: K, value: V): void; - // get all values which have a key == the given key - get(key: K): V | V[]; - // get all values which have a key < the given key - getLt(key: K): V[]; - // get all values which have a key > the given key - getGt(key: K): V[]; - - // get all vals from start to end - getAll(key: K, start: number, end: number): V[]; - // just in case someone wants to do something smart with ranges - getPos(key: K): boolean; - // remove the value from the index, if the value was the last one, remove the key - remove(key: K, value: V): void; - // clear will zap the index - clear(key?: any): void; -} - - -interface LokiPersistenceInterface { - loadDatabase: (fileName: string, func: (dbString: string) => void) => void; - saveDatabase: (fileName: string, content: string, func: () => void) => void; -} - - -interface LokiConfigureOptions { - env?: string/*'NODEJS', 'BROWSER', 'CORDOVA'*/; - persistenceMethod?: string/*'fs', 'localStorage'*/; - adapter?: LokiPersistenceInterface; - autoload?: any; - autoloadCallback?: (err: Error | string, data: any) => void; - autosave?: boolean; - autosaveInterval?: number; -} - - -interface LokiQuery { -} - - -interface LokiCollectionChange { - name: string; - operation: string/*'I', 'U', 'R'*/; - obj: any -} - - -interface LokiCollectionOptions { - transactional?: boolean; - clone?: boolean; - asyncListeners?: boolean; - disableChangesApi?: boolean; - indices?: string | string[]; -} - - - - -/* ==== loki-indexed-adapter.js ==== */ +/* ======== loki-indexed-adapter.js ======== */ interface LokiIndexedAdapter { app: string; - catalog: LokiCatalog; // TODO type + catalog: LokiCatalog; /** IndexedAdapter - Loki persistence adapter class for indexedDb. * This class fulfills abstract adapter interface which can be applied to other storage methods @@ -1005,10 +1332,10 @@ interface LokiIndexedAdapter { * @param {string} dbstring - the serialized db string to save. * @param {function} callback - (Optional) callback passed obj.success with true or false */ - saveDatabase(dbname: string, dbstring: string, callback?: (res: { success: boolean }) => void): void; + saveDatabase(dbname: string, dbstring: string, callback?: (err: Error | void) => void): void; // alias for saveDatabase - saveKey(dbname: string, dbstring: string, callback?: (res: { success: boolean }) => void): void; + saveKey(dbname: string, dbstring: string, callback?: (err: Error | void) => void): void; /** deleteDatabase() - Deletes a serialized db from the catalog. * @param {string} dbname - the name of the database to delete from the catalog. @@ -1030,7 +1357,6 @@ interface LokiIndexedAdapter { * @param {function} callback - (Optional) callback to accept result array. */ getCatalogSummary(callback: (entries: { app: string; key: string; size: number; }) => void): void; - } @@ -1057,24 +1383,30 @@ interface LokiCatalog { // Hide 'cursoring' and return array of { id: id, key: key } getAllKeys(callback: (data: any[]) => void): void; - } -/* ==== END loki-indexed-adapter.js ==== */ +/* ======== END loki-indexed-adapter.js ======== */ -/* ==== loki-crypted-file-adapter.js ==== */ -interface LokiCryptedFileAdapterEncryptResult { - cipher: string; - keyDerivation: string; - keyLength: number; - iterations: number; - iv: string; - salt: string; - value: string; -} +/* ======== loki-crypted-file-adapter.js ======== */ +/** + * @file lokiCryptedFileAdapter.js + * @author Hans Klunder + */ +/** require libs */ +//var fs = require('fs'); +//var cryptoLib = require('crypto'); +//var isError = require('util').isError; +/* The default Loki File adapter uses plain text JSON files. This adapter crypts the database string and wraps the result +* in a JSON including enough info to be able to decrypt it (except for the 'secret' of course !) +* +* The idea is that the 'secret' does not reside in your source code but is supplied by some other source (e.g. the user in node-webkit) +* +* The idea + encrypt/decrypt routines are borrowed from https://github.com/mmoulton/krypt/blob/develop/lib/krypt.js +* not using the krypt module to avoid third party dependencies +*/ interface LokiCryptedFileAdapter { secret: string; @@ -1084,56 +1416,69 @@ interface LokiCryptedFileAdapter { new (): LokiCryptedFileAdapter; /** setSecret() - set the secret to be used during encryption and decryption + * * @param {string} secret - the secret to be used */ setSecret(secret: string): void; /** loadDatabase() - Retrieves a serialized db string from the catalog. + * * @example - // LOAD - var cryptedFileAdapter = require('./lokiCryptedFileAdapter'); - ptedFileAdapter.setSecret('mySecret'); // you should change 'mySecret' to something supplied by the user - var db = new loki('test.crypted', { adapter: cryptedFileAdapter }); //you can use any name, not just '*.crypted' - db.loadDatabase(function(result) { - e.log('done'); - + // LOAD + var cryptedFileAdapter = require('./lokiCryptedFileAdapter'); + cryptedFileAdapter.setSecret('mySecret'); // you should change 'mySecret' to something supplied by the user + var db = new loki('test.crypted', { adapter: cryptedFileAdapter }); //you can use any name, not just '*.crypted' + db.loadDatabase(function(result) { + console.log('done'); + }); * * @param {string} dbname - the name of the database to retrieve. * @param {function} callback - callback should accept string param containing serialized db string. */ - loadDatabase(dbname: string, callback?: (res: string | Error) => void): void; + loadDatabase(dbname: string, callback: (decryptedDataOrErr: string | any) => void): void; /** + * @example - // SAVE : will save database in 'test.crypted' - cryptedFileAdapter = require('./lokiCryptedFileAdapter'); - ptedFileAdapter.setSecret('mySecret'); // you should change 'mySecret' to something supplied by the user - loki=require('lokijs'); - db = new loki('test.crypted',{ adapter: cryptedFileAdapter }); //you can use any name, not just '*.crypted' - coll = db.addCollection('testColl'); - l.insert({test: 'val'}); - saveDatabase(); // could pass callback if needed for async complete - - ample - // if you have the krypt module installed you can use: - pt --decrypt test.crypted --secret mySecret - to view the contents of the database - + // SAVE : will save database in 'test.crypted' + var cryptedFileAdapter = require('./lokiCryptedFileAdapter'); + cryptedFileAdapter.setSecret('mySecret'); // you should change 'mySecret' to something supplied by the user + var loki=require('lokijs'); + var db = new loki('test.crypted',{ adapter: cryptedFileAdapter }); //you can use any name, not just '*.crypted' + var coll = db.addCollection('testColl'); + coll.insert({test: 'val'}); + db.saveDatabase(); // could pass callback if needed for async complete + + @example + // if you have the krypt module installed you can use: + krypt --decrypt test.crypted --secret mySecret + to view the contents of the database + * saveDatabase() - Saves a serialized db to the catalog. * * @param {string} dbname - the name to give the serialized database within the catalog. * @param {string} dbstring - the serialized db string to save. * @param {function} callback - (Optional) callback passed obj.success with true or false */ - saveDatabase(dbname: string, dbstring: string, callback?: (err?: any | LokiCryptedFileAdapterEncryptResult) => void): void; - + saveDatabase(dbname: string, dbstring: string, callback: (err: any) => void): void; } -/* ==== END loki-crypted-file-adapter.js ==== */ + + +interface LokiCryptedFileAdapterEncryptResult { + cipher: string; + keyDerivation: string; + keyLength: number; + iterations: number; + iv: string; + salt: string; + value: string; +} +/* ======== END loki-crypted-file-adapter.js ======== */ -/* ==== loki-angular.js ==== */ +/* ======== loki-angular.js ======== */ /* introduces a angular module named lokijs that returns the 'lokijs' module var module = angular.module('lokijs', []) .factory('Loki', function Loki() { @@ -1141,49 +1486,86 @@ interface LokiCryptedFileAdapter { }); return module; */ -/* ==== END loki-angular.js ==== */ +/* ======== END loki-angular.js ======== */ -/* ==== jquery-sync-adapter.js ==== */ +/* ======== jquery-sync-adapter.js ======== */ + +/** LokiJS JquerySyncAdapter + * A remote sync adapter example for LokiJS + * @author Joe Minichino + */ + /** this adapter assumes an object options is passed, * containing the following properties: * ajaxLib: jquery or compatible ajax library * save: { url: the url to save to, dataType [optional]: json|xml|etc., type [optional]: POST|GET|PUT} * load: { url: the url to load from, dataType [optional]: json|xml| etc., type [optional]: POST|GET|PUT } */ -interface JquerySyncAdapter { - options: { - ajaxLib: { - ajax(options: any): any; - }; - save: { - type?: string/*'GET', 'POST, 'DELETE', etc.*/; - dataType?: string/*'json', 'xml', etc.*/ - url?: string - }; - }; // TODO type +interface LokiJquerySyncAdapter { + options: LokiJquerySyncAdapterOptions - new (options: any): JquerySyncAdapter; + new (options: LokiJquerySyncAdapterOptions): LokiJquerySyncAdapter; - saveDatabase(name: any, data: any, callback?: (data: any, textStatus: string, jqXHR: XMLHttpRequest/*JQueryXHR*/) => any): void; - - loadDatabase(name: any, callback?: (data: any, textStatus: string, jqXHR: XMLHttpRequest/*JQueryXHR*/) => any): void; + saveDatabase(name: string, data: any, callback?: (data: any, textStatus: string, xhr: XMLHttpRequest) => any): void; + loadDatabase(name: string, callback?: (data: any, textStatus: string, xhr: XMLHttpRequest) => any): void; } -/* ==== END jquery-sync-adapter.js ==== */ +interface LokiJquerySyncAdapterOptions { + ajaxLib: { ajax(options: any): any; }; + save: { + url: any; + type?: string; /*'GET', 'POST, 'DELETE', etc.*/ + dataType?: string; /*'json', 'xml', etc.*/ + }; + load: { + url: any; + type?: string; /*'GET', 'POST, 'DELETE', etc.*/ + dataType?: string; /*'json', 'xml', etc.*/ + }; +} + + +interface LokiJquerySyncAdapterError extends Error { + name: string; // "JquerySyncAdapterError" + message: any; + + new (message: any): LokiJquerySyncAdapterError; +} +/* ======== END jquery-sync-adapter.js ======== */ + + + + +declare var LokiCryptedFileAdapterConstructor: { + new (): LokiCryptedFileAdapter; +} + +declare module "lokiCryptedFileAdapter" { + export = LokiCryptedFileAdapterConstructor; +} + + +declare var LokiIndexedAdapterConstructor: { + new (filename: string): LokiIndexedAdapter; +} + +declare module "loki-indexed-adapter" { + export = LokiIndexedAdapterConstructor; +} declare var LokiConstructor: { new (filename: string, options?: LokiConfigureOptions): Loki; + LokiOps: LokiOps; Collection: LokiCollection; KeyValueStore: LokiKeyValueStore; } - declare module "lokijs" { export = LokiConstructor; }