1 | import { EventEmitter } from 'events';
|
2 |
|
3 | declare 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 |
|
544 | export = NeDB.Datastore;
|