diff --git a/lokijs/lokijs-tests.ts b/lokijs/lokijs-tests.ts
new file mode 100644
index 000000000..433a41092
--- /dev/null
+++ b/lokijs/lokijs-tests.ts
@@ -0,0 +1,103 @@
+///
+
+import Loki = require("lokijs");
+
+module LokijsTest {
+
+ class Ant {
+ static uniqueId = 1;
+
+ id: number;
+ dob: Date;
+ health: number; // range [0.0, 1.0]
+ lengthMm: number; // in millimeters
+ weightMg: number; // in milligrams
+
+ constructor(id: number) {
+ this.id = id;
+ }
+
+ static createAnt() {
+ return new Ant(Ant.uniqueId++);
+ }
+
+ }
+
+
+ class QueenAnt extends Ant {
+ eggsBirthed: number;
+
+ constructor(id: number) {
+ super(id);
+ }
+
+ static createQueenAnt() {
+ return new QueenAnt(Ant.uniqueId++);
+ }
+
+ }
+
+
+ class AntColony {
+ ants: LokiCollection;
+ queens: LokiCollection;
+
+ constructor(ants?: LokiCollection, queens?: LokiCollection) {
+ this.ants = ants;
+ this.queens = queens;
+ }
+ }
+
+
+ export class Test {
+
+ static runAllTests() {
+ var lokiInst = new Loki("ant-colony", { autosave: false });
+ var colony = Test.createAntColony(lokiInst, 250);
+ var topAnts = colony.ants.addDynamicView("top-ants");
+ var topAntAry = topAnts.applyFind({ $gt: { health: 0.75 } }).data();
+
+ if (colony.ants.binaryIndices["id"] == null) {
+ throw new Error("missing 'id' binary index");
+ }
+
+ if (lokiInst.getCollection("ants") != colony.ants) {
+ throw new Error("ant collections don't match");
+ }
+
+ var collNames = lokiInst.listCollections().map((coll) => coll.name);
+ if (collNames.length != 2 || collNames.indexOf("ants") < 0 || collNames.indexOf("queenAnts") < 0) {
+ throw new Error("collections [" + collNames + "] does not equal expected ['ants', 'queenAnts']");
+ }
+
+ var firstQueenId = (colony.queens.findOne({}))["$loki"];
+ if (firstQueenId == null || colony.queens.get(firstQueenId) == null) {
+ throw new Error("queen object's '.$loki' property lookup failed");
+ }
+
+ var anotherColl = new Loki.Collection("anotherCollection");
+
+ }
+
+
+ static createAntColony(lokiInst: Loki, antCount: number, queenCount: number = 1) {
+ var ants = lokiInst.addCollection("ants", { indices: "id" });
+ var queens = lokiInst.addCollection("queenAnts", { indices: "id" });
+ var antColony = new AntColony(ants, queens);
+
+ for (var i = 0; i < antCount; i++) {
+ ants.add(Ant.createAnt());
+ }
+
+ for (var i = 0; i < antCount; i++) {
+ queens.add(QueenAnt.createQueenAnt());
+ }
+
+ return antColony;
+ }
+
+ }
+
+}
+
+export = LokijsTest;
diff --git a/lokijs/lokijs.d.ts b/lokijs/lokijs.d.ts
new file mode 100644
index 000000000..1e93bb370
--- /dev/null
+++ b/lokijs/lokijs.d.ts
@@ -0,0 +1,1189 @@
+// Type definitions for lokijs v1.2.5
+// Project: https://github.com/techfort/LokiJS
+// 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
+
+/** LokiJS
+ * A lightweight document oriented javascript database
+ * @author Joe Minichino
+ */
+
+
+/** Loki: The main database class
+ * @constructor
+ * @param {string} filename - name of the file to be saved to
+ * @param {object} options - config object
+ */
+interface Loki extends LokiEventEmitter {
+ filename: string;
+ 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
+ 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.
+ // 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[];
+ };
+
+ new (filename: string, options: LokiConfigureOptions): Loki;
+
+ /** 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} });
+ * @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
+ */
+ anonym(docs: T | T[], indexesArray?: LokiCollectionOptions): LokiCollection;
+
+ addCollection(name: string, options?: LokiCollectionOptions): LokiCollection;
+
+ loadCollection(collection: LokiCollection): void;
+
+ getCollection(collectionName: string): LokiCollection;
+
+ listCollections(): { name: string; type: string; count: number }[];
+
+ removeCollection(collectionName: string): void;
+
+ getName(): string;
+
+ /** serializeReplacer - used to prevent certain properties from being serialized, may return null */
+ serializeReplacer(key: "autosaveHandle", value: T): T;
+ serializeReplacer(key: "persistenceAdapter", value: T): T;
+ serializeReplacer(key: "constraints", value: T): T;
+ serializeReplacer(key: string, value: T): T;
+
+ // toJson
+ serialize(): string;
+
+ // alias of serialize
+ 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;
+
+ /** 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 |
+ +--------------------------*/
+
+ /** The Changes API enables the tracking the changes occurred in the collections since the beginning of the session,
+ * so it's possible to create a differential dataset for synchronization purposes (possibly to a remote db)
+ */
+
+ /** 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.
+ * @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
+ */
+ generateChangesNotification(arrayOfCollectionNames?: string[]): LokiCollectionChange[];
+
+ /** serializeChanges() - stringify changes for network transmission
+ * @returns {string} string representation of the changes
+ */
+ serializeChanges(collectionNamesArray?: string[]): string;
+
+ /** 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).
+ * @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;
+
+ /** 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).
+ * @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;
+
+ // alias
+ save(callback: (err: Error) => 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.
+ */
+ autosaveDirty(): boolean;
+
+ /** autosaveClearFlags - resets dirty flags on all collections.
+ * Called from saveDatabase() after db is saved.
+ */
+ autosaveClearFlags(): void;
+
+ /** autosaveEnable - begin a javascript interval to periodically save the database. */
+ autosaveEnable(): void;
+
+ /** autosaveDisable - stop the autosave interval timer. */
+ autosaveDisable(): void;
+
+}
+
+
+/** 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)[] };
+
+ new (): LokiEventEmitter;
+
+ /** @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
+ * @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;
+
+ /** @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;
+
+ /** @prop remove() - removes the listener at position 'index' from the event 'eventName' */
+ removeListener(eventName: string, listener: (...args: any[]) => void): void;
+}
+
+
+
+
+/*------------------+
+| PERSISTENCE |
+-------------------*/
+
+/** there are two build in persistence adapters for internal use
+ * fs for use in Nodejs type environments
+ * localStorage for use in browser environment
+ * defined as helper classes here so its easy and clean to use
+ */
+interface LokiFsAdapter {
+ //fs; // 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;
+
+ /** 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;
+}
+
+
+
+
+/** constructor for local storage */
+interface LokiLocalStorageAdapter {
+
+ /** 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;
+
+ /** 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;
+}
+
+
+
+
+/** 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().
+ */
+interface LokiResultset {
+ collection: LokiCollection;
+ searchIsChained: boolean;
+ filteredrows: string[]; // technically number[] (e.g. = Object.keys(this.collection.data))
+ filterInitialized: boolean;
+
+ new (collection: LokiCollection, queryObj: LokiQuery, queryFunc: (obj: E) => boolean, firstOnly?: boolean): 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.
+ * @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()
+ branch(): 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;
+ * });
+ * @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)
+ * @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;
+
+ /** 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
+ */
+ calculateRange(op: "$eq", 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*/];
+ calculateRange(op: "$lte", prop: string, val: any): [number/*start*/, number/*end*/];
+ 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.
+ * @param {array} expressionArray - array of expressions
+ * @returns {Resultset} this resultset for further chain ops.
+ */
+ findOr(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.
+ * @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;
+
+ /** 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?: 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;
+
+ /** data() - Terminates the chain and returns array of filtered documents
+ * @returns {array} Array of documents in the resultset
+ */
+ data(): 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;
+
+ /** 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
+ */
+ mapReduce(mapFunction: (value: E, index: number, array: E[]) => T, reduceFunction: (array: T[]) => U): U;
+
+ /** eqJoin() - Left joining two sets of data. Join keys can be defined or calculated properties
+ * eqJoin expects the right join key values to be unique. Otherwise left data will be joined on the last joinData object with that key
+ * @param {Array} joinData - Data array to join to.
+ * @param {String,function} leftJoinKey - Property name in this result set to join on or a function to produce a value to join on
+ * @param {String,function} rightJoinKey - Property name in the joinData to join on or a function to produce a value to join on
+ * @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;
+
+}
+
+
+
+
+/** 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.
+ */
+interface LokiDynamicView extends LokiEventEmitter {
+ name: string;
+ collection: LokiCollection;
+ persistent: 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()
+ sortFunction: (a: E, b: E) => number;
+ sortCriteria: [string, boolean][]; // TODO type
+ 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
+ };
+
+ new (collection: LokiCollection, name: string, persistent?: boolean): 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.
+ * @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;
+
+ /** 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.
+ * @returns {Resultset} A copy of the internal resultset for branched queries.
+ */
+ branchResultset(): LokiResultset;
+
+ /** toJSON() - Override of toJSON to avoid circular references */
+ toJSON(): LokiDynamicView;
+
+ /** 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.
+ */
+ 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)
+ * @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;
+
+ /** 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.
+ */
+ applyFind(query: LokiQuery): LokiDynamicView;
+
+ /** applyWhere() - Adds a javascript filter function to the DynamicView filter pipeline
+ * @param {function} fun - A javascript filter function to apply to pipeline
+ * @returns {DynamicView} this DynamicView object, for further chain ops.
+ */
+ applyWhere(fun: (obj: E) => boolean): LokiDynamicView;
+
+ /** 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[];
+
+ /** */
+ queueSortPhase(): void;
+
+ /** performSortPhase() - invoked synchronously or asynchronously to perform final sort phase (if needed) */
+ performSortPhase(): void;
+
+ /** evaluateDocument() - internal method for (re)evaluating document inclusion.
+ * Called by : collection.insert() and collection.update().
+ * @param {int} objIndex - index of document to (re)run through filter pipeline.
+ */
+ evaluateDocument(objIndex: number): void;
+
+ /** 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;
+
+}
+
+
+
+
+/** 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[];
+ };
+ // 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;
+ disableChangesApi: boolean;
+ setChangesApi: (enabled: boolean) => void;
+
+ new (name: string, options?: LokiCollectionOptions): LokiCollection;
+
+ getChanges(): LokiCollectionChange[];
+
+ flushChanges(): void;
+
+ byExample(template: any): { $and: any[] };
+
+ findObject(template: any): E;
+
+ findObjects(template: any): E[];
+
+
+ /*----------------------------+
+ | INDEXING |
+ +----------------------------*/
+
+ /** Ensure binary index on a certain field */
+ ensureIndex(property: string, force?: boolean): void;
+
+ ensureUniqueIndex(field: string): void;
+
+ /** Ensure all binary indices */
+ ensureAllIndexes(force?: boolean): void;
+
+ flagBinaryIndexesDirty(): void;
+
+ count(): number;
+
+ /** Rebuild idIndex */
+ ensureId(): void;
+
+ /** 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;
+
+ removeDynamicView(name: string): void;
+
+ getDynamicView(name: string): LokiDynamicView;
+
+ /** find and update: pass a filtering function to select elements to be updated
+ * and apply the updatefunctino to those elements iteratively
+ */
+ 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)
+ * @returns document or documents (if passed an array of objects)
+ */
+ insert(doc: E): E;
+ insert(doc: E[]): E[];
+
+ clear(): void;
+
+ /** Update method */
+ update(doc: E): void;
+
+ /** 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;
+
+
+ /*---------------------+
+ | Finding methods |
+ +----------------------*/
+
+ /** 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];
+
+ by(field: string): (value: any) => E;
+ by(field: string, value?: string): E;
+
+ /** 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.
+ */
+ chain(): LokiResultset;
+
+ /** 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 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 */
+ startTransaction(): void;
+
+ /** commit the transation */
+ commit(): void;
+
+ /** roll back the transation */
+ rollback(): void;
+
+ // async executor. This is only to enable callbacks at the end of the execution.
+ async(fun: () => void, callback: () => void): void;
+
+ /** 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;
+
+
+ /* ------ STAGING API -------- */
+ /** stages: a map of uniquely identified 'stages', which hold copies of objects to be
+ * manipulated without affecting the data in the original collection
+ */
+ stages: { [id: string]: any };
+
+ /** create a stage and/or retrieve it */
+ getStage(name: string): any;
+
+ /** a collection of objects recording the changes applied through a commmitStage */
+ commitLog: { timestamp: number; message: any; data: any }[];
+
+ /** create a copy of an object and insert it into a stage */
+ stage(stageName: string, obj: T): T;
+
+ /** 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
+ */
+ commitStage(stageName: string, message: any): void;
+
+ no_op(): void;
+
+ extract(field: string): any[];
+
+ max(field: string): number;
+
+ min(field: string): number;
+
+ maxRecord(field: string): { index: number; value: any; };
+
+ minRecord(field: string): { index: number; value: any; };
+
+ extractNumerical(field: string): number[];
+
+ avg(field: string): number;
+
+ stdDev(field: string): number;
+
+ mode(field: 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;
+*/
+
+
+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;
+}
+
+
+interface LokiBinarySearch {
+ (array: T[], item: T, fun: (a: T, b: T) => number): { found: boolean; index: number; };
+}
+
+
+interface LokiBSonSort {
+ (fun: (a: T, b: T) => number): (array: T[], item: T) => { found: boolean; index: number; };
+}
+
+
+
+
+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;
+}
+
+
+
+
+interface LokiUniqueIndex {
+ field: string;
+ keyMap: { [id: string]: E };
+ lokiMap: { [id: number]: any }; // 'field' map
+
+ 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 {
+ 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 ==== */
+interface LokiIndexedAdapter {
+ app: string;
+ catalog: LokiCatalog; // TODO type
+
+ /** IndexedAdapter - Loki persistence adapter class for indexedDb.
+ * This class fulfills abstract adapter interface which can be applied to other storage methods
+ * Utilizes the included LokiCatalog app/key/value database for actual database persistence.
+ * @param {string} appname - Application name context can be used to distinguish subdomains or just 'loki'
+ */
+ new (appname: string): LokiIndexedAdapter;
+
+ /** checkAvailability - used to check if adapter is available
+ * @returns {boolean} true if indexeddb is available, false if not.
+ */
+ checkAvailability(): boolean;
+
+ /** loadDatabase() - Retrieves a serialized db string from the catalog.
+ * @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?: (data: any) => void): void;
+
+ // alias for loadDatabase
+ loadKey(dbname: string, callback?: (data: any) => void): void;
+
+ /** 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?: (res: { success: boolean }) => void): void;
+
+ // alias for saveDatabase
+ saveKey(dbname: string, dbstring: string, callback?: (res: { success: boolean }) => void): void;
+
+ /** deleteDatabase() - Deletes a serialized db from the catalog.
+ * @param {string} dbname - the name of the database to delete from the catalog.
+ */
+ deleteDatabase(dbname: string): void;
+
+ // alias for deleteDatabase
+ deleteKey(dbname: string): void;
+
+ /** getDatabaseList() - Retrieves object array of catalog entries for current app.
+ * @param {function} callback - should accept array of database names in the catalog for current app.
+ */
+ getDatabaseList(callback: (names: string[]) => void): void;
+
+ // alias for getDatabaseList
+ getKeyList(callback: (names: string[]) => void): void;
+
+ /** getCatalogSummary - allows retrieval of list of all keys in catalog along with size
+ * @param {function} callback - (Optional) callback to accept result array.
+ */
+ getCatalogSummary(callback: (entries: { app: string; key: string; size: number; }) => void): void;
+
+}
+
+
+/** LokiCatalog - underlying App/Key/Value catalog persistence
+ * This non-interface class implements the actual persistence.
+ * Used by the IndexedAdapter class.
+ */
+interface LokiCatalog {
+ db: IDBDatabase;
+
+ new (callback: (cat: LokiCatalog) => void): LokiCatalog;
+
+ initializeLokiCatalog(callback: (cat: LokiCatalog) => void): void;
+
+ getAppKey(app: string, key: string, callback: (resObj: any) => void): void;
+
+ getAppKeyById(id: any, callback: (result: any, data: T) => void, data: T): void;
+
+ setAppKey(app: string, key: string, val: any, callback: (res: { success: boolean }) => void): void;
+
+ deleteAppKey(id: any, callback: (res: { success: boolean; }) => void): void;
+
+ getAppKeys(app: string, callback: (data: any[]) => void): void;
+
+ // Hide 'cursoring' and return array of { id: id, key: key }
+ getAllKeys(callback: (data: any[]) => void): void;
+
+}
+/* ==== 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;
+}
+
+
+interface LokiCryptedFileAdapter {
+ secret: string;
+
+ /** The constructor is automatically called on `require` , see examples below
+ * @constructor
+ */
+ 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');
+
+ *
+ * @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;
+
+ /**
+ @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
+
+ * 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;
+
+}
+/* ==== END loki-crypted-file-adapter.js ==== */
+
+
+
+
+/* ==== loki-angular.js ==== */
+/* introduces a angular module named lokijs that returns the 'lokijs' module
+ var module = angular.module('lokijs', [])
+ .factory('Loki', function Loki() {
+ return lokijs;
+ });
+ return module;
+*/
+/* ==== END loki-angular.js ==== */
+
+
+
+
+/* ==== jquery-sync-adapter.js ==== */
+/** 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
+
+ new (options: any): JquerySyncAdapter;
+
+ 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;
+
+}
+/* ==== END jquery-sync-adapter.js ==== */
+
+
+
+
+declare var LokiConstructor: {
+ new (filename: string, options?: LokiConfigureOptions): Loki;
+ Collection: LokiCollection;
+ KeyValueStore: LokiKeyValueStore;
+}
+
+
+declare module "lokijs" {
+ export = LokiConstructor;
+}