diff --git a/README.md b/README.md index 7f50fac..35c6b67 100644 --- a/README.md +++ b/README.md @@ -503,6 +503,16 @@ The optional `options` object may contain: - `signal`: an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) to abort the deferred operation. When aborted (now or later) the `fn` function will not be called, and the promise returned by `deferAsync()` will be rejected with a [`LEVEL_ABORTED`](#level_aborted) error. +### `db.attachResource(resource)` + +Keep track of the given `resource` in order to call its `close()` method when the database is closed. Once successfully closed, the resource will no longer be tracked, to the same effect as manually calling [`db.detachResource()`](#dbdetachresourceresource). When given multiple resources, the database will close them in parallel. Resources are kept in a [set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) so that the same object will not be attached (and closed) twice. + +Intended for objects that rely on an open database. Used internally for built-in resources like iterators and sublevels, and is publicly exposed for custom resources. + +### `db.detachResource(resource)` + +Stop tracking the given `resource`. + ### `iterator` An iterator allows one to lazily read a range of entries stored in the database. The entries will be sorted by keys in [lexicographic order](https://en.wikipedia.org/wiki/Lexicographic_order) (in other words: byte order) which in short means key `'a'` comes before `'b'` and key `'10'` comes before `'2'`. diff --git a/abstract-level.js b/abstract-level.js index 496a9d6..32e1a62 100644 --- a/abstract-level.js +++ b/abstract-level.js @@ -973,7 +973,6 @@ class AbstractLevel extends EventEmitter { }) } - // TODO: docs and types attachResource (resource) { if (typeof resource !== 'object' || resource === null || typeof resource.close !== 'function') { @@ -983,7 +982,6 @@ class AbstractLevel extends EventEmitter { this.#resources.add(resource) } - // TODO: docs and types detachResource (resource) { this.#resources.delete(resource) } diff --git a/index.d.ts b/index.d.ts index ad07aa6..2e438dc 100644 --- a/index.d.ts +++ b/index.d.ts @@ -45,7 +45,8 @@ export { } from './types/abstract-snapshot' export { - AbstractReadOptions + AbstractReadOptions, + AbstractResource } from './types/interfaces' export * as Transcoder from 'level-transcoder' diff --git a/types/abstract-chained-batch.d.ts b/types/abstract-chained-batch.d.ts index ae6c8f6..613b9f5 100644 --- a/types/abstract-chained-batch.d.ts +++ b/types/abstract-chained-batch.d.ts @@ -1,8 +1,9 @@ import * as Transcoder from 'level-transcoder' import { AbstractSublevel } from './abstract-sublevel' +import { AbstractResource } from './interfaces' export class AbstractChainedBatch - implements AsyncDisposable { + implements AbstractResource { constructor (db: TDatabase) /** diff --git a/types/abstract-iterator.d.ts b/types/abstract-iterator.d.ts index 74316e8..ac81b42 100644 --- a/types/abstract-iterator.d.ts +++ b/types/abstract-iterator.d.ts @@ -1,5 +1,5 @@ import * as Transcoder from 'level-transcoder' -import { AbstractReadOptions, RangeOptions } from './interfaces' +import { AbstractReadOptions, AbstractResource, RangeOptions } from './interfaces' declare interface CommonIteratorOptions extends AbstractReadOptions { /** @@ -60,7 +60,7 @@ export interface AbstractValueIteratorOptions extends RangeOptions, Com * @template TDatabase Type of the database that created this iterator. * @template T Type of items yielded. Items can be entries, keys or values. */ -declare class CommonIterator implements AsyncDisposable { +declare class CommonIterator implements AbstractResource { /** * A reference to the database that created this iterator. */ diff --git a/types/abstract-level.d.ts b/types/abstract-level.d.ts index 597ec68..625d13b 100644 --- a/types/abstract-level.d.ts +++ b/types/abstract-level.d.ts @@ -14,7 +14,7 @@ import { AbstractValueIteratorOptions } from './abstract-iterator' -import { AbstractReadOptions, RangeOptions } from './interfaces' +import { AbstractReadOptions, AbstractResource, RangeOptions } from './interfaces' /** * Abstract class for a lexicographically sorted key-value database. @@ -24,7 +24,7 @@ import { AbstractReadOptions, RangeOptions } from './interfaces' * @template VDefault The default type of values if not overridden on operations. */ declare class AbstractLevel - extends EventEmitter implements AsyncDisposable { + extends EventEmitter implements AbstractResource { /** * Private database constructor. * @@ -307,6 +307,24 @@ declare class AbstractLevel * @returns A promise for the result of {@link fn}. */ deferAsync (fn: () => Promise, options?: AbstractDeferOptions | undefined): Promise + + /** + * Keep track of the given {@link resource} in order to call its `close()` method when + * the database is closed. Once successfully closed, the resource will no longer be + * tracked, to the same effect as manually calling {@link detachResource}. When given + * multiple resources, the database will close them in parallel. Resources are kept in + * a {@link Set} so that the same object will not be attached (and closed) twice. + * + * Intended for objects that rely on an open database. Used internally for built-in + * resources like iterators and sublevels, and is publicly exposed for custom + * resources. + */ + attachResource(resource: AbstractResource): void + + /** + * Stop tracking the given {@link resource}. + */ + detachResource(resource: AbstractResource): void } export { AbstractLevel } diff --git a/types/abstract-snapshot.d.ts b/types/abstract-snapshot.d.ts index 71299dc..a7df9dd 100644 --- a/types/abstract-snapshot.d.ts +++ b/types/abstract-snapshot.d.ts @@ -1,8 +1,10 @@ +import { AbstractResource } from './interfaces' + /** * A lightweight token that represents a version of a database at a particular point in * time. */ -export class AbstractSnapshot implements AsyncDisposable { +export class AbstractSnapshot implements AbstractResource { /** * Increment reference count, to register work that should delay closing until * {@link unref} is called an equal amount of times. The promise that will be returned diff --git a/types/interfaces.d.ts b/types/interfaces.d.ts index 438b6a1..702037b 100644 --- a/types/interfaces.d.ts +++ b/types/interfaces.d.ts @@ -20,3 +20,23 @@ export interface AbstractReadOptions { */ snapshot?: AbstractSnapshot | undefined } + +/** + * Represents a stateful resource that can be closed. + */ +export interface AbstractResource extends AsyncDisposable { + /** + * Close the resource. + * + * Note for implementors: if the resource is exposed to the user and can also be closed + * in an automated fashion - through `db.attachResource()` or other - then the + * `close()` method should be idempotent such that calling it twice will make no + * difference. + */ + close (): Promise + + /** + * Close the resource. Identical in functionality to {@link close}. + */ + [Symbol.asyncDispose](): Promise +}