diff --git a/package.json b/package.json index 09035eb..364b1b4 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "uuid": ">=8 <10" }, "devDependencies": { + "@types/lodash": "^4.17.23", "@types/node": "^22.10.7", "@typescript-eslint/eslint-plugin": "^7.16.0", "@typescript-eslint/parser": "^7.16.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d10e29d..8612f8c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -33,6 +33,9 @@ importers: specifier: '>=8 <10' version: 9.0.1 devDependencies: + '@types/lodash': + specifier: ^4.17.23 + version: 4.17.23 '@types/node': specifier: ^22.10.7 version: 22.13.1 @@ -475,6 +478,9 @@ packages: '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/lodash@4.17.23': + resolution: {integrity: sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA==} + '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} @@ -2785,6 +2791,8 @@ snapshots: '@types/estree@1.0.6': {} + '@types/lodash@4.17.23': {} + '@types/mdast@4.0.4': dependencies: '@types/unist': 3.0.3 diff --git a/src/DB/DB.js b/src/DB/DB.js index 07a9b30..1b6d4d5 100644 --- a/src/DB/DB.js +++ b/src/DB/DB.js @@ -1,6 +1,6 @@ import {sortBy} from 'lodash' import debug from 'debug' -import SQLite, {sql} from './SQLite' +import SQLiteImpl, {sql} from './SQLite' import {DEV, deprecated} from '../lib/warning' const dbg = debug('strato-db/DB') @@ -45,7 +45,7 @@ const _markMigration = async (db, runKey, up) => { * * @implements {DB} */ -class DBImpl extends SQLite { +class DBImpl extends SQLiteImpl { /** @param {DBOptions} options */ constructor({migrations = [], onBeforeMigrations, ...options} = {}) { const onDidOpen = options.readOnly @@ -71,7 +71,7 @@ class DBImpl extends SQLite { * database. The model should use the given `db` instance at creation time. * * @param {Object} Model - A class. - * @param {Object} options - Options passed during Model creation. + * @param {Object} [options] - Options passed during Model creation. * @returns {Object} - The created Model instance. */ addModel(Model, options) { @@ -92,7 +92,7 @@ class DBImpl extends SQLite { * * - The name under which to register these migrations. * - * @param {Record>} migrations + * @param {DBMigrations} migrations * * - The migrations object. * diff --git a/src/DB/SQLite.js b/src/DB/SQLite.js index 60903e9..f97c882 100644 --- a/src/DB/SQLite.js +++ b/src/DB/SQLite.js @@ -123,7 +123,9 @@ class SQLiteImpl extends EventEmitter { vacuumInterval, vacuumPageCount, } + /** @type {Promise} */ this.dbP = new Promise(resolve => { + /** @type {((v: any) => void) | null} */ this._resolveDbP = resolve }) this._sema = new Sema(1) @@ -265,6 +267,7 @@ class SQLiteImpl extends EventEmitter { open() { const {_resolveDbP} = this if (_resolveDbP) { + /** @type {Promise | null} */ this._openingDbP = this._openDB().finally(() => { this._openingDbP = null }) diff --git a/src/DB/Statement.js b/src/DB/Statement.js index cfe26f1..aa98015 100644 --- a/src/DB/Statement.js +++ b/src/DB/Statement.js @@ -17,6 +17,7 @@ class StatementImpl { this.name = `${db.name}${this._name}` } + /** @type {true} */ get isStatement() { return true } @@ -65,17 +66,19 @@ class StatementImpl { if (!_stmt) return Promise.resolve() return this._wrap( () => - new Promise((resolve, reject) => { - delete this._stmt - _stmt.finalize(err => { - if (err) { - if (!this._stmt) this._stmt = _stmt - return reject(err) - } - dbg(`${this.name} finalized`) - resolve() + /** @type {Promise} */ ( + new Promise((resolve, reject) => { + delete this._stmt + _stmt.finalize(err => { + if (err) { + if (!this._stmt) this._stmt = _stmt + return reject(err) + } + dbg(`${this.name} finalized`) + resolve() + }) }) - }) + ) ) } diff --git a/src/EventSourcingDB/EventSourcingDB.js b/src/EventSourcingDB/EventSourcingDB.js index 82cc2b5..ab16167 100644 --- a/src/EventSourcingDB/EventSourcingDB.js +++ b/src/EventSourcingDB/EventSourcingDB.js @@ -150,11 +150,13 @@ const makeDispatcher = (name, fn) => (typeOrEvent, data, ts) => { * works in React. * * @augments EventEmitter + * @implements EventSourcingDB */ // eslint-disable-next-line unicorn/prefer-event-target class EventSourcingDB extends EventEmitter { MAX_RETRY = 38 // this is an hour + /** @param {ESDBOptions} options */ constructor({ queue, models, diff --git a/src/JsonModel/makeMigrations.js b/src/JsonModel/makeMigrations.js index 7988c5e..b2be1ff 100644 --- a/src/JsonModel/makeMigrations.js +++ b/src/JsonModel/makeMigrations.js @@ -71,7 +71,9 @@ export const makeMigrations = ({ ) } // Wrap the migration functions to provide their arguments + /** @type {DBMigrations} */ const wrappedMigrations = {} + /** @type {(fn: Function) => DBMigration} */ const wrap = fn => fn && (writeableDb => { diff --git a/types.d.ts b/types.d.ts index f6ceaae..74cd0cb 100644 --- a/types.d.ts +++ b/types.d.ts @@ -70,8 +70,9 @@ type SQLiteOptions = { * - 'finally': transaction finished * - 'call': call to SQLite completed, includes data and duration */ -interface SQLite extends EventEmitter { - new (options?: SQLiteOptions) +// eslint-disable-next-line unicorn/prefer-event-target +declare class SQLite extends EventEmitter { + constructor(options?: SQLiteOptions) /** Holding space for models */ store: object @@ -86,7 +87,7 @@ interface SQLite extends EventEmitter { * is converted to `db.all('select * from "foo" where t = ? and json = ?', [bar, * JSON.stringify(obj)])` */ - sql(): {quoteId: (id: SQLiteParam) => string} & SqlTag + sql: {quoteId: (id: SQLiteParam) => string} & SqlTag /** `true` if an sqlite connection was set up. Mostly useful for tests. */ isOpen: boolean /** @@ -195,8 +196,8 @@ interface SQLite extends EventEmitter { */ withTransaction(fn: () => Promise): Promise } - -type DBMigration = {up: DBCallback} | DBCallback +type DBUpMigration = {up: DBCallback} +type DBMigration = DBUpMigration | DBCallback /** Migrations are marked completed by their name in the `{sdb} migrations` table */ type DBMigrations = Record interface DBModel { @@ -205,7 +206,7 @@ interface DBModel { type DBOptions = { /** Open the DB read-only */ readOnly?: boolean - migrations?: DBMigrations + migrations?: (DBUpMigration & {runKey: string})[] /** Called before migrations run. Not called for read-only */ onBeforeMigrations?: (...params: any[]) => any /** @@ -396,7 +397,7 @@ type JMColums = JMColumns type JMOptions< T extends {[x: string]: any}, IDCol extends string = 'id', - Columns extends JMColums = {[id in IDCol]?: {type: 'TEXT'}}, + Columns extends JMColumns = {[id in IDCol]?: {type: 'TEXT'}}, > = { /** A DB instance, normally passed by DB */ db: DB @@ -474,7 +475,7 @@ declare class JsonModel< ? RealItem : RealItem & {[id in IDCol]: IDValue}, Config = ConfigOrID extends string ? object : ConfigOrID, - Columns extends JMColums = Config extends {columns: object} + Columns extends JMColumns = Config extends {columns: object} ? Config['columns'] : // If we didn't get a config, assume all keys are columns Item, @@ -763,7 +764,7 @@ interface EventQueue< Item extends {[x: string]: any} = RealItem extends {[id in IDCol]?: unknown} ? RealItem : RealItem & {[id in IDCol]: IDValue}, - Columns extends JMColums = Config extends {columns: object} + Columns extends JMColumns = Config extends {columns: object} ? Config['columns'] : // If we didn't get a config, assume all keys are columns {[colName in keyof Item]: object}, @@ -902,8 +903,13 @@ interface ESDBModel { } type ESDBOptions = DBOptions & { + /** + * @deprecated 'db' is no longer an option, pass the db options instead, e.g. + * file, verbose, readOnly + */ + db?: never models: {[name: string]: ESDBModel} - queue?: InstanceType + queue?: EventQueue queueFile?: string withViews?: boolean onWillOpen?: DBCallback @@ -986,7 +992,7 @@ interface ESModel< ? RealItem : RealItem & {[id in IDCol]: IDValue}, Config = ConfigOrID extends string ? object : ConfigOrID, - Columns extends JMColums = Config extends {columns: object} + Columns extends JMColumns = Config extends {columns: object} ? Config['columns'] : // If we didn't get a config, assume all keys are columns Item,