@theshelf/database 0.0.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,42 +11,45 @@ This integration is based on simple CRUD operations and purposely does NOT suppo
11
11
  npm install @theshelf/database
12
12
  ```
13
13
 
14
- ## Implementations
14
+ ## Drivers
15
15
 
16
- Currently, there are two implementations:
16
+ Currently, there are two drivers available:
17
17
 
18
- * **Memory** - non-persistent in memory storage (suited for testing).
18
+ * **Memory** - non-persistent in memory storage (suited for testing).
19
19
  * **MongoDB** - persistent document storage.
20
20
 
21
- ## Configuration
21
+ ## How to use
22
22
 
23
- The used implementation needs to be configured in the `.env` file.
23
+ The basic set up looks like this.
24
24
 
25
- ```env
26
- DATABASE_IMPLEMENTATION="mongodb" # (memory | mongodb)
27
- ```
25
+ ```ts
26
+ import Database, { MemoryDriver | MongoDBDriver as SelectedDriver } from '@theshelf/database';
28
27
 
29
- In case of MongoDB, additional configuration is required.
28
+ const driver = new SelectedDriver(/* configuration */);
29
+ const database = new Database(driver);
30
30
 
31
- ```env
32
- MONGODB_CONNECTION_STRING="mongodb://username:password@address:27017"
33
- MONGODB_DATABASE_NAME="mydb"
31
+ // Perform operations with the database instance
34
32
  ```
35
33
 
36
- ## How to use
34
+ ### Configuration
37
35
 
38
- An instance of the configured implementation can be imported for performing database operations.
36
+ #### Memory driver
39
37
 
40
- ```ts
41
- import database from '@theshelf/database';
38
+ No configuration options.
42
39
 
43
- // Perform operations with the database instance
40
+ #### MongoDB driver
41
+
42
+ ```ts
43
+ type MongoDBConfiguration = {
44
+ connectionString: string; // e.g. "mongodb://development:development@localhost:27017"
45
+ databaseName: string; // e.g. "mydb"
46
+ };
44
47
  ```
45
48
 
46
49
  ### Operations
47
50
 
48
51
  ```ts
49
- import database, { RecordData, RecordQuery, RecordSort, SortDirections } from '@theshelf/database';
52
+ import { RecordData, RecordQuery, RecordSort, SortDirections } from '@theshelf/database';
50
53
 
51
54
  // Open connection
52
55
  await database.connect();
@@ -0,0 +1,9 @@
1
+ import type { ConnectionState } from './definitions/constants.js';
2
+ import type { Driver } from './definitions/interfaces.js';
3
+ export default class ConnectionManager {
4
+ #private;
5
+ constructor(driver: Driver);
6
+ get state(): ConnectionState;
7
+ connect(): Promise<void>;
8
+ disconnect(): Promise<void>;
9
+ }
@@ -0,0 +1,53 @@
1
+ import { ConnectionStates } from './definitions/constants.js';
2
+ export default class ConnectionManager {
3
+ #driver;
4
+ #state = ConnectionStates.DISCONNECTED;
5
+ #connectPromise;
6
+ #disconnectPromise;
7
+ constructor(driver) {
8
+ this.#driver = driver;
9
+ }
10
+ get state() { return this.#state; }
11
+ async connect() {
12
+ if (this.#connectPromise !== undefined) {
13
+ return this.#connectPromise;
14
+ }
15
+ if (this.#state !== ConnectionStates.DISCONNECTED) {
16
+ return;
17
+ }
18
+ this.#state = ConnectionStates.CONNECTING;
19
+ try {
20
+ this.#connectPromise = this.#driver.connect();
21
+ await this.#connectPromise;
22
+ this.#state = ConnectionStates.CONNECTED;
23
+ }
24
+ catch (error) {
25
+ this.#state = ConnectionStates.DISCONNECTED;
26
+ throw error;
27
+ }
28
+ finally {
29
+ this.#connectPromise = undefined;
30
+ }
31
+ }
32
+ async disconnect() {
33
+ if (this.#disconnectPromise !== undefined) {
34
+ return this.#disconnectPromise;
35
+ }
36
+ if (this.#state !== ConnectionStates.CONNECTED) {
37
+ return;
38
+ }
39
+ this.#state = ConnectionStates.DISCONNECTING;
40
+ try {
41
+ this.#disconnectPromise = this.#driver.disconnect();
42
+ await this.#disconnectPromise;
43
+ this.#state = ConnectionStates.DISCONNECTED;
44
+ }
45
+ catch (error) {
46
+ this.#state = ConnectionStates.CONNECTED;
47
+ throw error;
48
+ }
49
+ finally {
50
+ this.#disconnectPromise = undefined;
51
+ }
52
+ }
53
+ }
@@ -1,8 +1,10 @@
1
+ import type { ConnectionState } from './definitions/constants.js';
1
2
  import type { Driver } from './definitions/interfaces.js';
2
3
  import type { RecordData, RecordField, RecordId, RecordQuery, RecordSort, RecordType } from './definitions/types.js';
3
4
  export default class Database implements Driver {
4
5
  #private;
5
6
  constructor(driver: Driver);
7
+ get connectionState(): ConnectionState;
6
8
  get connected(): boolean;
7
9
  connect(): Promise<void>;
8
10
  disconnect(): Promise<void>;
@@ -13,5 +15,4 @@ export default class Database implements Driver {
13
15
  updateRecords(type: RecordType, query: RecordQuery, data: RecordData): Promise<number>;
14
16
  deleteRecord(type: RecordType, query: RecordQuery): Promise<number>;
15
17
  deleteRecords(type: RecordType, query: RecordQuery): Promise<number>;
16
- clear(): Promise<void>;
17
18
  }
package/dist/Database.js CHANGED
@@ -1,15 +1,24 @@
1
1
  import sanitize from './utilities/sanitize.js';
2
+ import { ConnectionStates } from './definitions/constants.js';
3
+ import ConnectionManager from './ConnectionManager.js';
2
4
  export default class Database {
3
5
  #driver;
6
+ #connectionManager;
4
7
  constructor(driver) {
5
8
  this.#driver = driver;
9
+ this.#connectionManager = new ConnectionManager(driver);
10
+ }
11
+ get connectionState() {
12
+ return this.#connectionManager.state;
13
+ }
14
+ get connected() {
15
+ return this.connectionState === ConnectionStates.CONNECTED;
6
16
  }
7
- get connected() { return this.#driver.connected; }
8
17
  connect() {
9
- return this.#driver.connect();
18
+ return this.#connectionManager.connect();
10
19
  }
11
20
  disconnect() {
12
- return this.#driver.disconnect();
21
+ return this.#connectionManager.disconnect();
13
22
  }
14
23
  createRecord(type, data) {
15
24
  const cleanData = sanitize(data);
@@ -35,7 +44,4 @@ export default class Database {
35
44
  deleteRecords(type, query) {
36
45
  return this.#driver.deleteRecords(type, query);
37
46
  }
38
- clear() {
39
- return this.#driver.clear();
40
- }
41
47
  }
@@ -1,22 +1,29 @@
1
1
  export declare const ID = "id";
2
2
  export declare const LogicalOperators: {
3
- AND: string;
4
- OR: string;
3
+ readonly AND: "AND";
4
+ readonly OR: "OR";
5
5
  };
6
6
  export declare const SortDirections: {
7
- ASCENDING: string;
8
- DESCENDING: string;
7
+ readonly ASCENDING: "ASCENDING";
8
+ readonly DESCENDING: "DESCENDING";
9
9
  };
10
10
  export declare const QueryOperators: {
11
- EQUALS: string;
12
- NOT_EQUALS: string;
13
- LESS_THAN: string;
14
- LESS_THAN_OR_EQUALS: string;
15
- GREATER_THAN: string;
16
- GREATER_THAN_OR_EQUALS: string;
17
- IN: string;
18
- NOT_IN: string;
19
- CONTAINS: string;
20
- STARTS_WITH: string;
21
- ENDS_WITH: string;
11
+ readonly EQUALS: "EQUALS";
12
+ readonly NOT_EQUALS: "NOT_EQUALS";
13
+ readonly LESS_THAN: "LESS_THAN";
14
+ readonly LESS_THAN_OR_EQUALS: "LESS_THAN_OR_EQUALS";
15
+ readonly GREATER_THAN: "GREATER_THAN";
16
+ readonly GREATER_THAN_OR_EQUALS: "GREATER_THAN_OR_EQUALS";
17
+ readonly IN: "IN";
18
+ readonly NOT_IN: "NOT_IN";
19
+ readonly CONTAINS: "CONTAINS";
20
+ readonly STARTS_WITH: "STARTS_WITH";
21
+ readonly ENDS_WITH: "ENDS_WITH";
22
22
  };
23
+ export declare const ConnectionStates: {
24
+ readonly DISCONNECTED: "DISCONNECTED";
25
+ readonly DISCONNECTING: "DISCONNECTING";
26
+ readonly CONNECTING: "CONNECTING";
27
+ readonly CONNECTED: "CONNECTED";
28
+ };
29
+ export type ConnectionState = typeof ConnectionStates[keyof typeof ConnectionStates];
@@ -20,6 +20,9 @@ export const QueryOperators = {
20
20
  STARTS_WITH: 'STARTS_WITH', // "LIKE%"
21
21
  ENDS_WITH: 'ENDS_WITH' // "%LIKE"
22
22
  };
23
- Object.freeze(LogicalOperators);
24
- Object.freeze(SortDirections);
25
- Object.freeze(QueryOperators);
23
+ export const ConnectionStates = {
24
+ DISCONNECTED: 'DISCONNECTED',
25
+ DISCONNECTING: 'DISCONNECTING',
26
+ CONNECTING: 'CONNECTING',
27
+ CONNECTED: 'CONNECTED'
28
+ };
@@ -10,5 +10,4 @@ export interface Driver {
10
10
  updateRecords(type: RecordType, query: RecordQuery, data: RecordData): Promise<number>;
11
11
  deleteRecord(type: RecordType, query: RecordQuery): Promise<number>;
12
12
  deleteRecords(type: RecordType, query: RecordQuery): Promise<number>;
13
- clear(): Promise<void>;
14
13
  }
@@ -1,9 +1,9 @@
1
- import type { Driver } from '../../definitions/interfaces.js';
2
- import type { RecordData, RecordQuery, RecordSort } from '../../definitions/types.js';
1
+ import type { Driver } from '../definitions/interfaces.js';
2
+ import type { RecordData, RecordQuery, RecordSort } from '../definitions/types.js';
3
3
  export default class Memory implements Driver {
4
4
  #private;
5
- recordId: number;
6
5
  get connected(): boolean;
6
+ get memory(): Map<string, RecordData[]>;
7
7
  connect(): Promise<void>;
8
8
  disconnect(): Promise<void>;
9
9
  createRecord(type: string, data: RecordData): Promise<string>;
@@ -13,5 +13,5 @@ export default class Memory implements Driver {
13
13
  updateRecords(type: string, query: RecordQuery, data: RecordData): Promise<number>;
14
14
  deleteRecord(type: string, query: RecordQuery): Promise<number>;
15
15
  deleteRecords(type: string, query: RecordQuery): Promise<number>;
16
- clear(): Promise<void>;
16
+ clear(): void;
17
17
  }
@@ -1,5 +1,5 @@
1
- import { LogicalOperators, QueryOperators, SortDirections } from '../../definitions/constants.js';
2
- import NotConnected from '../../errors/NotConnected.js';
1
+ import { LogicalOperators, QueryOperators, SortDirections } from '../definitions/constants.js';
2
+ import NotConnected from '../errors/NotConnected.js';
3
3
  const OPERATORS = {
4
4
  [QueryOperators.EQUALS]: '==',
5
5
  [QueryOperators.GREATER_THAN]: '>',
@@ -15,13 +15,20 @@ const LOGICAL_OPERATORS = {
15
15
  export default class Memory {
16
16
  #memory = new Map();
17
17
  #connected = false;
18
- recordId = 0;
18
+ #recordId = 0;
19
19
  get connected() { return this.#connected; }
20
+ get memory() {
21
+ if (this.#connected === false) {
22
+ throw new NotConnected();
23
+ }
24
+ return this.#memory;
25
+ }
20
26
  async connect() {
21
27
  this.#connected = true;
22
28
  }
23
29
  async disconnect() {
24
30
  this.#connected = false;
31
+ this.#memory.clear();
25
32
  }
26
33
  async createRecord(type, data) {
27
34
  const collection = this.#getCollection(type);
@@ -73,8 +80,8 @@ export default class Memory {
73
80
  indexes.forEach(index => collection.splice(index, 1));
74
81
  return indexes.length;
75
82
  }
76
- async clear() {
77
- this.#memory.clear();
83
+ clear() {
84
+ this.memory.clear();
78
85
  }
79
86
  #fetchRecord(type, query) {
80
87
  const collection = this.#getCollection(type);
@@ -168,16 +175,14 @@ export default class Memory {
168
175
  return `record.${key} ${codeOperator} ${codeValue}`;
169
176
  }
170
177
  #createId() {
171
- return (++this.recordId).toString().padStart(8, '0');
178
+ return (++this.#recordId).toString().padStart(8, '0');
172
179
  }
173
180
  #getCollection(type) {
174
- if (this.#memory === undefined) {
175
- throw new NotConnected();
176
- }
177
- let collection = this.#memory.get(type);
181
+ const memory = this.memory;
182
+ let collection = memory.get(type);
178
183
  if (collection === undefined) {
179
184
  collection = [];
180
- this.#memory.set(type, collection);
185
+ memory.set(type, collection);
181
186
  }
182
187
  return collection;
183
188
  }
@@ -1,8 +1,12 @@
1
- import type { Driver } from '../../definitions/interfaces.js';
2
- import type { RecordData, RecordField, RecordId, RecordQuery, RecordSort, RecordType } from '../../definitions/types.js';
1
+ import type { Driver } from '../definitions/interfaces.js';
2
+ import type { RecordData, RecordField, RecordId, RecordQuery, RecordSort, RecordType } from '../definitions/types.js';
3
+ type MongoDBConfiguration = {
4
+ connectionString: string;
5
+ databaseName: string;
6
+ };
3
7
  export default class MongoDB implements Driver {
4
8
  #private;
5
- constructor(connectionString: string, databaseName: string);
9
+ constructor(configuration: MongoDBConfiguration);
6
10
  get connected(): boolean;
7
11
  connect(): Promise<void>;
8
12
  disconnect(): Promise<void>;
@@ -13,5 +17,5 @@ export default class MongoDB implements Driver {
13
17
  updateRecords(type: RecordType, query: RecordQuery, data: RecordData): Promise<number>;
14
18
  deleteRecord(type: RecordType, query: RecordQuery): Promise<number>;
15
19
  deleteRecords(type: RecordType, query: RecordQuery): Promise<number>;
16
- clear(): Promise<void>;
17
20
  }
21
+ export {};
@@ -1,8 +1,8 @@
1
1
  /* eslint @typescript-eslint/no-explicit-any: "off" */
2
2
  import { MongoClient } from 'mongodb';
3
- import { ID, LogicalOperators, QueryOperators, SortDirections } from '../../definitions/constants.js';
4
- import DatabaseError from '../../errors/DatabaseError.js';
5
- import NotConnected from '../../errors/NotConnected.js';
3
+ import { ID, LogicalOperators, QueryOperators, SortDirections } from '../definitions/constants.js';
4
+ import DatabaseError from '../errors/DatabaseError.js';
5
+ import NotConnected from '../errors/NotConnected.js';
6
6
  const UNKNOWN_ERROR = 'Unknown error';
7
7
  const OPERATORS = {
8
8
  [QueryOperators.EQUALS]: '$eq',
@@ -28,9 +28,9 @@ export default class MongoDB {
28
28
  #client;
29
29
  #database;
30
30
  #connected = false;
31
- constructor(connectionString, databaseName) {
32
- this.#connectionString = connectionString;
33
- this.#databaseName = databaseName;
31
+ constructor(configuration) {
32
+ this.#connectionString = configuration.connectionString;
33
+ this.#databaseName = configuration.databaseName;
34
34
  }
35
35
  get connected() { return this.#connected; }
36
36
  async connect() {
@@ -106,9 +106,6 @@ export default class MongoDB {
106
106
  const result = await collection.deleteMany(mongoQuery);
107
107
  return result.deletedCount;
108
108
  }
109
- async clear() {
110
- return; // Deliberately not implemented
111
- }
112
109
  #buildMongoQuery(query) {
113
110
  const mongoQuery = {};
114
111
  const multiStatements = query;
@@ -1,2 +1,3 @@
1
1
  export default class DatabaseError extends Error {
2
+ constructor(message?: string);
2
3
  }
@@ -1,2 +1,9 @@
1
1
  export default class DatabaseError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = this.constructor.name;
5
+ if (Error.captureStackTrace) {
6
+ Error.captureStackTrace(this, this.constructor);
7
+ }
8
+ }
2
9
  }
@@ -1,4 +1,4 @@
1
1
  import DatabaseError from './DatabaseError.js';
2
2
  export default class NotConnected extends DatabaseError {
3
- constructor(message?: string);
3
+ constructor();
4
4
  }
@@ -1,6 +1,6 @@
1
1
  import DatabaseError from './DatabaseError.js';
2
2
  export default class NotConnected extends DatabaseError {
3
- constructor(message) {
4
- super(message ?? 'Database not connected');
3
+ constructor() {
4
+ super('Database not connected');
5
5
  }
6
6
  }
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import Database from './Database.js';
2
- declare const database: Database;
3
1
  export * from './definitions/constants.js';
4
- export * from './definitions/types.js';
2
+ export type * from './definitions/interfaces.js';
3
+ export type * from './definitions/types.js';
5
4
  export { default as DatabaseError } from './errors/DatabaseError.js';
6
5
  export { default as NotConnected } from './errors/NotConnected.js';
7
- export type { Database };
8
- export default database;
6
+ export { default as MemoryDriver } from './drivers/Memory.js';
7
+ export { default as MongoDBDriver } from './drivers/MongoDB.js';
8
+ export { default } from './Database.js';
package/dist/index.js CHANGED
@@ -1,8 +1,6 @@
1
- import Database from './Database.js';
2
- import implementation from './implementation.js';
3
- const database = new Database(implementation);
4
1
  export * from './definitions/constants.js';
5
- export * from './definitions/types.js';
6
2
  export { default as DatabaseError } from './errors/DatabaseError.js';
7
3
  export { default as NotConnected } from './errors/NotConnected.js';
8
- export default database;
4
+ export { default as MemoryDriver } from './drivers/Memory.js';
5
+ export { default as MongoDBDriver } from './drivers/MongoDB.js';
6
+ export { default } from './Database.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@theshelf/database",
3
3
  "private": false,
4
- "version": "0.0.4",
4
+ "version": "0.2.0",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "url": "https://github.com/MaskingTechnology/theshelf"
@@ -1,4 +0,0 @@
1
- import DatabaseError from './DatabaseError.js';
2
- export default class UnknownImplementation extends DatabaseError {
3
- constructor(name: string);
4
- }
@@ -1,6 +0,0 @@
1
- import DatabaseError from './DatabaseError.js';
2
- export default class UnknownImplementation extends DatabaseError {
3
- constructor(name) {
4
- super(`Unknown database implementation: ${name}`);
5
- }
6
- }
@@ -1,3 +0,0 @@
1
- import type { Driver } from './definitions/interfaces.js';
2
- declare const _default: Driver;
3
- export default _default;
@@ -1,14 +0,0 @@
1
- import UnknownImplementation from './errors/UnknownImplementation.js';
2
- import createMemoryDb from './implementations/memory/create.js';
3
- import createMongoDb from './implementations/mongodb/create.js';
4
- const implementations = new Map([
5
- ['memory', createMemoryDb],
6
- ['mongodb', createMongoDb],
7
- ]);
8
- const DEFAULT_DATABASE_IMPLEMENTATION = 'memory';
9
- const implementationName = process.env.DATABASE_IMPLEMENTATION ?? DEFAULT_DATABASE_IMPLEMENTATION;
10
- const creator = implementations.get(implementationName.toLowerCase());
11
- if (creator === undefined) {
12
- throw new UnknownImplementation(implementationName);
13
- }
14
- export default creator();
@@ -1,2 +0,0 @@
1
- import Memory from './Memory.js';
2
- export default function create(): Memory;
@@ -1,4 +0,0 @@
1
- import Memory from './Memory.js';
2
- export default function create() {
3
- return new Memory();
4
- }
@@ -1,2 +0,0 @@
1
- import MongoDb from './MongoDb.js';
2
- export default function create(): MongoDb;
@@ -1,6 +0,0 @@
1
- import MongoDb from './MongoDb.js';
2
- export default function create() {
3
- const connectionString = process.env.MONGODB_CONNECTION_STRING ?? 'undefined';
4
- const databaseName = process.env.MONGODB_DATABASE_NAME ?? 'undefined';
5
- return new MongoDb(connectionString, databaseName);
6
- }