UNPKG

17.6 kBTypeScriptView Raw
1import { EventEmitter } from 'events';
2
3declare namespace NeDB {
4 type Query = {
5 [key: string]: any;
6 }
7
8 type Update = {
9 [key: string]: any;
10 }
11
12 type Projection<TSchema> = {
13 [p in keyof TSchema]?: number;
14 }
15
16 interface Persistence {
17 /**
18 * Under the hood, NeDB's persistence uses an append-only format, meaning
19 * that all updates and deletes actually result in lines added at the end
20 * of the datafile, for performance reasons. The database is automatically
21 * compacted (i.e. put back in the one-line-per-document format) every
22 * time you load each database within your application.
23 *
24 * You can manually call the compaction function with
25 * `datastore.persistence.compactDatafile` which takes no argument. It
26 * queues a compaction of the datafile in the executor, to be executed
27 * sequentially after all pending operations. The datastore will fire a
28 * compaction.done event once compaction is finished.
29 */
30 compactDatafile(): void;
31
32 /**
33 * Set automatic compaction at a regular `interval` in milliseconds (a
34 * minimum of 5s is enforced).
35 */
36 setAutocompactionInterval(interval: number): void;
37
38 /**
39 * Stop automatic compaction with
40 * `datastore.persistence.stopAutocompaction()`.
41 */
42 stopAutocompaction(): void;
43 }
44
45 interface AbstractCursor<TSchema> {
46 /**
47 * Sort the queried documents.
48 *
49 * See: https://github.com/louischatriot/nedb#sorting-and-paginating
50 */
51 sort(query: any): this;
52
53 /**
54 * Skip some of the queried documents.
55 *
56 * See: https://github.com/louischatriot/nedb#sorting-and-paginating
57 */
58 skip(n: number): this;
59
60 /**
61 * Limit the queried documents.
62 *
63 * See: https://github.com/louischatriot/nedb#sorting-and-paginating
64 */
65 limit(n: number): this;
66
67 /**
68 * Set the document projection.
69 *
70 * See: https://github.com/louischatriot/nedb#projections
71 */
72 project(projection: Projection<TSchema>): this;
73 }
74
75 interface FindCursor<TSchema> extends AbstractCursor<TSchema>, Promise<TSchema[]> {
76 /**
77 * Execute the cursor.
78 *
79 * Since the Cursor has a `then` and a `catch` method
80 * JavaScript identifies it as a thenable object
81 * thus you can await it in async functions.
82 *
83 * @example
84 * // in an async function
85 * await datastore.find(...)
86 * .sort(...)
87 * .limit(...)
88 *
89 * @example
90 * // the previous is the same as:
91 * await datastore.find(...)
92 * .sort(...)
93 * .limit(...)
94 * .exec()
95 */
96 exec(): Promise<TSchema[]>;
97 }
98
99 interface FindOneCursor<TSchema> extends AbstractCursor<TSchema>, Promise<TSchema | null> {
100 /**
101 * Execute the cursor.
102 *
103 * Since the Cursor has a `then` and a `catch` method
104 * JavaScript identifies it as a thenable object
105 * thus you can await it in async functions.
106 *
107 * @example
108 * // in an async function
109 * await datastore.find(...)
110 * .sort(...)
111 * .limit(...)
112 *
113 * @example
114 * // the previous is the same as:
115 * await datastore.find(...)
116 * .sort(...)
117 * .limit(...)
118 * .exec()
119 */
120 exec(): Promise<TSchema | null>;
121 }
122
123 type DatastoreOptions = {
124 /**
125 * Path to the file where the data is persisted. If left blank, the
126 * datastore is automatically considered in-memory only. It cannot end
127 * with a `~` which is used in the temporary files NeDB uses to perform
128 * crash-safe writes.
129 */
130 filename?: string;
131
132 /**
133 * As the name implies...
134 *
135 * Defaults to `false`.
136 */
137 inMemoryOnly?: boolean;
138
139 /**
140 * Timestamp the insertion and last update of all documents, with the
141 * fields createdAt and updatedAt. User-specified values override
142 * automatic generation, usually useful for testing.
143 *
144 * Defaults to `false`.
145 */
146 timestampData?: boolean;
147
148 /**
149 * If used, the database will automatically be loaded from the datafile
150 * upon creation (you don't need to call `load`). Any command issued
151 * before load is finished is buffered and will be executed when load is
152 * done.
153 *
154 * Defaults to `false`.
155 */
156 autoload?: boolean;
157
158 /**
159 * If you use autoloading, this is the handler called after `load`. It
160 * takes one error argument. If you use autoloading without specifying
161 * this handler, and an error happens during load, an error will be
162 * thrown.
163 */
164 onload?(error: Error): any;
165
166 /**
167 * Hook you can use to transform data after it was serialized and before
168 * it is written to disk. Can be used for example to encrypt data before
169 * writing database to disk. This function takes a string as parameter
170 * (one line of an NeDB data file) and outputs the transformed string,
171 * which must absolutely not contain a `\n` character (or data will be
172 * lost).
173 */
174 afterSerialization?(line: string): string;
175
176 /**
177 * Inverse of afterSerialization. Make sure to include both and not just
178 * one or you risk data loss. For the same reason, make sure both
179 * functions are inverses of one another.
180 *
181 * Some failsafe mechanisms are in place to prevent data loss if you
182 * misuse the serialization hooks: NeDB checks that never one is declared
183 * without the other, and checks that they are reverse of one another by
184 * testing on random strings of various lengths. In addition, if too much
185 * data is detected as corrupt, NeDB will refuse to start as it could mean
186 * you're not using the deserialization hook corresponding to the
187 * serialization hook used before.
188 */
189 beforeDeserialization?(line: string): string;
190
191 /**
192 * Between 0 and 1, defaults to 10%. NeDB will refuse to start if more
193 * than this percentage of the datafile is corrupt. 0 means you don't
194 * tolerate any corruption, 1 means you don't care.
195 */
196 corruptAlertThreshold?: number;
197
198 /**
199 * Compares strings `a` and `b` and returns -1, 0 or 1. If specified, it
200 * overrides default string comparison which is not well adapted to non-US
201 * characters in particular accented letters. Native `localCompare` will
202 * most of the time be the right choice.
203 */
204 compareStrings?(a: string, b: string): number;
205
206 /**
207 * If you are using NeDB from whithin a Node Webkit app, specify its name
208 * (the same one you use in the package.json) in this field and the
209 * filename will be relative to the directory Node Webkit uses to store
210 * the rest of the application's data (local storage etc.). It works on
211 * Linux, OS X and Windows. Now that you can use
212 * `require('nw.gui').App.dataPath` in Node Webkit to get the path to the
213 * data directory for your application, you should not use this option
214 * anymore and it will be removed.
215 * @deprecated
216 */
217 nodeWebkitAppName?: string;
218 }
219
220 type UpdateOptions = {
221 /**
222 * Allows the modification of several documents if set to `true`.
223 *
224 * Defaults to `false`.
225 */
226 multi?: boolean;
227
228 /**
229 * If you want to insert a new document corresponding to the `update` rules
230 * if your `query` doesn't match anything. If your `update` is a simple object
231 * with no modifiers, it is the inserted document. In the other case, the
232 * `query` is stripped from all operator recursively, and the `update` is
233 * applied to it.
234 *
235 * Defaults to `false`.
236 */
237 upsert?: boolean;
238
239 /**
240 * (Not MongoDB-compatible) If set to true and update is not an upsert,
241 * will return the document or the array of documents (when multi is set
242 * to `true`) matched by the find query and updated. Updated documents
243 * will be returned even if the update did not actually modify them.
244 *
245 * Defaults to `false`.
246 */
247 returnUpdatedDocs?: boolean;
248 }
249
250 type RemoveOptions = {
251 /**
252 * Allows the removal of multiple documents if set to true.
253 *
254 * Defaults to `false`.
255 */
256 multi?: boolean;
257 }
258
259 type IndexOptions = {
260 /**
261 * The name of the field to index. Use the dot notation to index a field
262 * in a nested document.
263 */
264 fieldName: string;
265
266 /**
267 * Enforce field uniqueness. Note that a unique index will raise an error
268 * if you try to index two documents for which the field is not defined.
269 */
270 unique?: boolean;
271
272 /**
273 * Don't index documents for which the field is not defined. Use this
274 * option along with `unique` if you want to accept multiple documents for
275 * which it is not defined.
276 */
277 sparse?: boolean;
278
279 /**
280 * If set, the created index is a TTL (time to live) index, that will
281 * automatically remove documents when the system date becomes larger than
282 * the date on the indexed field plus `expireAfterSeconds`. Documents where
283 * the indexed field is not specified or not a Date object are ignored.
284 */
285 expireAfterSeconds?: number;
286 }
287
288 /**
289 * @summary
290 * As of v2.0.0 the Datastore class extends node's built
291 * in EventEmitter class and implements each method as an event
292 * plus additional error events. It also inherits the `compaction.done`
293 * event from nedb but for consistency, in this library the event
294 * was renamed to `compactionDone`.
295 *
296 * All event callbacks will be passed the same type of values,
297 * the first being the datastore, then the operation result (if there is any)
298 * and then the arguments of the called method. (Check out the first example!)
299 *
300 * All events have a matching error event that goes by the name of `${method}Error`,
301 * for example `findError` or `loadError`. The callbacks of these events will receive
302 * the same parameters as the normal event handlers except that instead of the
303 * operation result there will be an operation error. (Check out the second example!)
304 *
305 * A generic `__error__` event is also available. This event will be emitted at any of
306 * the above error events. The callbacks of this event will receive the same parameters
307 * as the specific error event handlers except that there will be one more parameter
308 * passed between the datastore and the error object, that being the name of the method
309 * that failed. (Check out the third example!)
310 *
311 * @example
312 * let datastore = Datastore.create()
313 * datastore.on('update', (datastore, result, query, update, options) => {
314 * })
315 * datastore.on('load', (datastore) => {
316 * // this event doesn't have a result
317 * })
318 * datastore.on('ensureIndex', (datastore, options) => {
319 * // this event doesn't have a result
320 * // but it has the options argument which will be passed to the
321 * // event handlers
322 * })
323 * datastore.on('compactionDone', (datastore) => {
324 * // inherited from nedb's compaction.done event
325 * })
326 *
327 * @example
328 * let datastore = Datastore.create()
329 * datastore.on('updateError', (datastore, error, query, update, options) => {
330 * })
331 * datastore.on('loadError', (datastore, error) => {
332 * })
333 * datastore.on('ensureIndexError', (datastore, error, options) => {
334 * })
335 *
336 * @example
337 * let datastore = Datastore.create()
338 * datastore.on('__error__', (datastore, event, error, ...args) => {
339 * // for example
340 * // datastore, 'find', error, [{ foo: 'bar' }, {}]
341 * })
342 *
343 * @class
344 */
345 class Datastore<TDocument> extends EventEmitter {
346 persistence: Persistence;
347
348 private constructor();
349
350 /**
351 * Create a database instance.
352 *
353 * Use this over `new Datastore(...)` to access original nedb datastore
354 * properties, such as `datastore.persistence`.
355 *
356 * For more information visit:
357 * https://github.com/louischatriot/nedb#creatingloading-a-database
358 */
359 static create(
360 pathOrOptions: DatastoreOptions & { timestampData: true },
361 ): Datastore<{ _id: string, createdAt: Date, updatedAt: Date }>;
362 /**
363 * Create a database instance.
364 *
365 * Use this over `new Datastore(...)` to access original nedb datastore
366 * properties, such as `datastore.persistence`.
367 *
368 * For more information visit:
369 * https://github.com/louischatriot/nedb#creatingloading-a-database
370 */
371 static create(
372 pathOrOptions?: string | DatastoreOptions,
373 ): Datastore<{ _id: string }>;
374
375 /**
376 * Load the datastore.
377 *
378 * Note that you don't necessarily have to call
379 * this method to load the datastore as it will
380 * automatically be called and awaited on any
381 * operation issued against the datastore
382 * (i.e.: `find`, `findOne`, etc.).
383 */
384 load(): Promise<void>;
385
386 /**
387 * Find documents that match a query.
388 *
389 * It's basically the same as the original:
390 * https://github.com/louischatriot/nedb#finding-documents
391 *
392 * There are differences minor in how the cursor works though.
393 *
394 * @example
395 * datastore.find({ ... }).sort({ ... }).exec().then(...)
396 *
397 * @example
398 * datastore.find({ ... }).sort({ ... }).then(...)
399 *
400 * @example
401 * // in an async function
402 * await datastore.find({ ... }).sort({ ... })
403 */
404 find<TSchema>(
405 query: Query,
406 projection?: Projection<TDocument & TSchema>,
407 ): FindCursor<TDocument & TSchema>;
408
409 /**
410 * Find a document that matches a query.
411 *
412 * It's basically the same as the original:
413 * https://github.com/louischatriot/nedb#finding-documents
414 *
415 * @example
416 * datastore.findOne({ ... }).then(...)
417 *
418 * @example
419 * // in an async function
420 * await datastore.findOne({ ... }).sort({ ... })
421 */
422 findOne<TSchema>(
423 query: Query,
424 projection?: Projection<TDocument & TSchema>,
425 ): FindOneCursor<TDocument & TSchema>;
426
427 /**
428 * Insert a document.
429 *
430 * It's basically the same as the original:
431 * https://github.com/louischatriot/nedb#inserting-documents
432 */
433 insert<TSchema>(
434 docs: TSchema,
435 ): Promise<TDocument & TSchema>;
436 /**
437 * Insert an array of documents.
438 *
439 * It's basically the same as the original:
440 * https://github.com/louischatriot/nedb#inserting-documents
441 */
442 insert<TSchema>(
443 docs: TSchema[],
444 ): Promise<(TDocument & TSchema)[]>;
445
446 /**
447 * Update a document that matches a query.
448 * Insert a new document corresponding to the `update` rules
449 * if no matching document was found.
450 *
451 * It's basically the same as the original:
452 * https://github.com/louischatriot/nedb#updating-documents
453 */
454 update<TSchema>(
455 query: Query,
456 update: Update,
457 options: UpdateOptions & { returnUpdatedDocs: true; upsert: true; multi?: false },
458 ): Promise<TDocument & TSchema>;
459 /**
460 * Update documents that match a query.
461 * Insert a new document corresponding to the `update` rules
462 * if no matching document was found.
463 *
464 * It's basically the same as the original:
465 * https://github.com/louischatriot/nedb#updating-documents
466 */
467 update<TSchema>(
468 query: Query,
469 update: Update,
470 options: UpdateOptions & { returnUpdatedDocs: true; upsert: true; multi: true },
471 ): Promise<(TDocument & TSchema)[] | (TDocument & TSchema)>;
472 /**
473 * Update a document that matches a query.
474 *
475 * It's basically the same as the original:
476 * https://github.com/louischatriot/nedb#updating-documents
477 */
478 update<TSchema>(
479 query: Query,
480 update: Update,
481 options: UpdateOptions & { returnUpdatedDocs: true; upsert?: false; multi?: false },
482 ): Promise<(TDocument & TSchema) | null>;
483 /**
484 * Update documents that match a query.
485 *
486 * It's basically the same as the original:
487 * https://github.com/louischatriot/nedb#updating-documents
488 */
489 update<TSchema>(
490 query: Query,
491 update: Update,
492 options: UpdateOptions & { returnUpdatedDocs: true; multi: true },
493 ): Promise<(TDocument & TSchema)[]>;
494 /**
495 * Update documents that match a query.
496 *
497 * It's basically the same as the original:
498 * https://github.com/louischatriot/nedb#updating-documents
499 */
500 update<TSchema>(
501 query: Query,
502 update: Update,
503 options?: UpdateOptions,
504 ): Promise<number>;
505 /**
506 * Update documents that match a query.
507 *
508 * It's basically the same as the original:
509 * https://github.com/louischatriot/nedb#updating-documents
510 */
511 update(
512 query: Query,
513 update: Update,
514 options?: UpdateOptions,
515 ): Promise<number>;
516
517 /**
518 * Remove documents that match a query.
519 *
520 * It's basically the same as the original:
521 * https://github.com/louischatriot/nedb#removing-documents
522 */
523 remove(query: Query, options: RemoveOptions): Promise<number>;
524
525 /**
526 * Count all documents matching the query.
527 */
528 count(query: Query): Promise<number>;
529
530 /**
531 * Ensure an index is kept for this field. Same parameters as lib/indexes
532 * For now this function is synchronous, we need to test how much time it
533 * takes We use an async API for consistency with the rest of the code.
534 */
535 ensureIndex(options: IndexOptions): Promise<void>;
536
537 /**
538 * Remove an index.
539 */
540 removeIndex(fieldName: string): Promise<void>;
541 }
542}
543
544export = NeDB.Datastore;