From 345d298c4760b109b77e0a696ffec7d365c0a446 Mon Sep 17 00:00:00 2001 From: Vitalij Ryndin Date: Tue, 7 Feb 2023 21:29:42 +0800 Subject: [PATCH 1/5] feat(packages): reworked api --- README.md | 14 ++--- examples/with-node-lodash/src/index.ts | 17 +++--- examples/with-node/src/index.ts | 17 +++--- packages/node/README.md | 14 ++--- packages/node/src/NodeDatabase.ts | 30 ----------- packages/node/src/adapter/AsyncAdapter.ts | 41 +++++++++++++++ packages/node/src/adapter/AsyncWriter.ts | 52 ------------------- packages/node/src/adapter/BaseAdapter.ts | 41 +++++++++++++++ packages/node/src/adapter/BaseWriter.ts | 23 -------- packages/node/src/adapter/SyncAdapter.ts | 39 ++++++++++++++ packages/node/src/adapter/SyncWriter.ts | 48 ----------------- packages/node/src/index.ts | 6 +-- packages/node/src/provider/AsyncProvider.ts | 20 +++---- packages/node/src/provider/BaseProvider.ts | 17 +++--- .../src/{ => provider}/DirectoryProvider.ts | 8 +-- packages/node/src/provider/NodeProvider.ts | 42 +++++++++++++++ packages/node/src/provider/SyncProvider.ts | 21 +++----- packages/node/src/types.ts | 12 +++-- 18 files changed, 233 insertions(+), 229 deletions(-) delete mode 100644 packages/node/src/NodeDatabase.ts create mode 100644 packages/node/src/adapter/AsyncAdapter.ts delete mode 100644 packages/node/src/adapter/AsyncWriter.ts create mode 100644 packages/node/src/adapter/BaseAdapter.ts delete mode 100644 packages/node/src/adapter/BaseWriter.ts create mode 100644 packages/node/src/adapter/SyncAdapter.ts delete mode 100644 packages/node/src/adapter/SyncWriter.ts rename packages/node/src/{ => provider}/DirectoryProvider.ts (84%) create mode 100644 packages/node/src/provider/NodeProvider.ts diff --git a/README.md b/README.md index aa09a13..e0fe2a9 100644 --- a/README.md +++ b/README.md @@ -72,18 +72,18 @@ export class Post { import 'reflect-metadata' import { dirname, resolve } from 'node:path' import { fileURLToPath } from 'node:url' -import { AsyncWriter, NodeDatabase } from '@stenodb/node' +import { AsyncAdapter, NodeProvider } from '@stenodb/node' import { Users, User, Post } from './entities.js' const path = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'database') -const adapter = new AsyncWriter('users', Users) const initialData = new Users(new User('John Doe')) -const database = new NodeDatabase(path) -const databaseUsers = database.create(adapter, initialData) +const adapter = new AsyncAdapter('users', Users, initialData) +const provider = new NodeProvider(path) +const database = provider.createAsync(adapter) -await databaseUsers.read() -databaseUsers.data?.users[0]?.addPost(new Post('Lorem ipsum')) -await databaseUsers.write() +await database.read() +database.data?.users[0]?.addPost(new Post('Lorem ipsum')) +await database.write() ``` ### `@stenodb/browser` diff --git a/examples/with-node-lodash/src/index.ts b/examples/with-node-lodash/src/index.ts index 4fc551c..2eed3e6 100644 --- a/examples/with-node-lodash/src/index.ts +++ b/examples/with-node-lodash/src/index.ts @@ -1,15 +1,15 @@ import 'reflect-metadata' import { dirname, resolve } from 'node:path' import { fileURLToPath } from 'node:url' -import { AsyncWriter, NodeDatabase } from '@stenodb/node' +import { AsyncAdapter, NodeProvider } from '@stenodb/node' import lodash from 'lodash' import { User, Users } from './entities.js' -import type { NodeProvider } from '@stenodb/node/types' +import type { Steno } from '@stenodb/node/types' export class NodeWithLodash { chain: lodash.ExpChain - constructor(private readonly provider: NodeProvider) { + constructor(private readonly provider: Steno.NodeProvider) { this.chain = lodash.chain(provider).get('data') } @@ -27,15 +27,16 @@ export class NodeWithLodash { } const path = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'database') -const adapter = new AsyncWriter('users', Users) const initialData = new Users(new User(1, 'John Doe')) -const database = new NodeDatabase(path) +const adapter = new AsyncAdapter('users', Users, initialData) +const provider = new NodeProvider(path) -const usersDatabase = new NodeWithLodash(database.create(adapter, initialData)) -await usersDatabase.read() +const database = new NodeWithLodash(provider.createAsync(adapter)) +await database.read() +await database.write() function findUserById(id: number) { - return usersDatabase.chain.get('users').find({ id }).value() + return database.chain.get('users').find({ id }).value() } console.log(findUserById(1)) diff --git a/examples/with-node/src/index.ts b/examples/with-node/src/index.ts index 7848883..6f1480a 100644 --- a/examples/with-node/src/index.ts +++ b/examples/with-node/src/index.ts @@ -1,19 +1,18 @@ import 'reflect-metadata' import { dirname, resolve } from 'node:path' import { fileURLToPath } from 'node:url' -import { AsyncWriter, NodeDatabase } from '@stenodb/node' +import { AsyncAdapter, NodeProvider } from '@stenodb/node' import { Post, User, Users } from './entities.js' const path = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'database') -const adapter = new AsyncWriter('users', Users) const initialData = new Users(new User('John Doe')) -const database = new NodeDatabase(path) - -const usersDatabase = database.create(adapter, initialData) -await usersDatabase.read() +const adapter = new AsyncAdapter('users', Users, initialData) +const provider = new NodeProvider(path) +const database = provider.createAsync(adapter) +await database.read() const post = new Post('Hello world') -usersDatabase.data?.users[0]?.addPost(post) -await usersDatabase.write() +database.data?.users[0]?.addPost(post) +await database.write() -console.log(usersDatabase.data) +console.log(database.data) diff --git a/packages/node/README.md b/packages/node/README.md index 617190b..2d60837 100644 --- a/packages/node/README.md +++ b/packages/node/README.md @@ -64,18 +64,18 @@ export class Post { import 'reflect-metadata' import { dirname, resolve } from 'node:path' import { fileURLToPath } from 'node:url' -import { AsyncWriter, NodeDatabase } from '@stenodb/node' +import { AsyncAdapter, NodeProvider } from '@stenodb/node' import { Users, User, Post } from './entities.js' const path = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'database') -const adapter = new AsyncWriter('users', Users) const initialData = new Users(new User('John Doe')) -const database = new NodeDatabase(path) -const databaseUsers = database.create(adapter, initialData) +const adapter = new AsyncAdapter('users', Users, initialData) +const provider = new NodeProvider(path) +const database = provider.createAsync(adapter) -await databaseUsers.read() -databaseUsers.data?.users[0]?.addPost(new Post('Lorem ipsum')) -await databaseUsers.write() +await database.read() +database.data?.users[0]?.addPost(new Post('Lorem ipsum')) +await database.write() ``` ## Credits diff --git a/packages/node/src/NodeDatabase.ts b/packages/node/src/NodeDatabase.ts deleted file mode 100644 index bd7b90c..0000000 --- a/packages/node/src/NodeDatabase.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { AsyncWriter } from './adapter/AsyncWriter.js' -import { SyncWriter } from './adapter/SyncWriter.js' -import { DirectoryProvider } from './DirectoryProvider.js' -import { AsyncProvider } from './provider/AsyncProvider.js' -import { SyncProvider } from './provider/SyncProvider.js' -import type { NodeAdapter, NodeProvider } from './types.js' - -export class NodeDatabase { - #directory: DirectoryProvider - - constructor(path: string) { - this.#directory = new DirectoryProvider(path) - } - - create(adapter: SyncWriter, initialData?: T): SyncProvider - create(adapter: AsyncWriter, initialData?: T): AsyncProvider - create(adapter: NodeAdapter, initialData?: T): NodeProvider { - adapter.setDirectory(this.#directory) - - if (adapter instanceof SyncWriter) { - return new SyncProvider(adapter, initialData) - } - - if (adapter instanceof AsyncWriter) { - return new AsyncProvider(adapter, initialData) - } - - throw new Error('Invalid adapter') - } -} diff --git a/packages/node/src/adapter/AsyncAdapter.ts b/packages/node/src/adapter/AsyncAdapter.ts new file mode 100644 index 0000000..a5c58ce --- /dev/null +++ b/packages/node/src/adapter/AsyncAdapter.ts @@ -0,0 +1,41 @@ +import { readFile } from 'node:fs/promises' +import { parseData } from '@stenodb/utils' +import { BaseAdapter } from './BaseAdapter.js' +import type { Steno } from '../types.js' + +export class AsyncAdapter extends BaseAdapter { + constructor(fileName: string, entity: Steno.Entity, initialData?: T) { + super(fileName, entity, initialData) + } + + async read(): Promise { + try { + const file = await readFile(this.filePath, 'utf-8') + this.data = this.plainData(file) + } catch (err) { + console.log(err) + } + } + + async write(): Promise { + await this.writer.write(parseData(this.data).toString()) + } + + async reset(): Promise { + if (!this.initialData) return + + try { + await this.directory + .createTemporaryFile(this.fileName, this.data) + ?.writeAsync() + this.data = this.initialData + await this.write() + } catch (err) { + console.log(err) + } + } + + async exists(): Promise { + return (await this.read()) !== null + } +} diff --git a/packages/node/src/adapter/AsyncWriter.ts b/packages/node/src/adapter/AsyncWriter.ts deleted file mode 100644 index 807c007..0000000 --- a/packages/node/src/adapter/AsyncWriter.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { readFile } from 'node:fs/promises' -import { parseData } from '@stenodb/utils' -import { BaseWriter } from './BaseWriter.js' -import type { Entity } from '../types.js' - -interface AsyncAdapter { - read(): Promise - write(data: T | null): Promise - reset(initialData: T): Promise - exists(): Promise -} - -export class AsyncWriter extends BaseWriter implements AsyncAdapter { - constructor(name: string, entity: Entity) { - super(name, entity) - } - - async read(): Promise { - try { - const data = await readFile(this.file, 'utf-8') - return parseData(data).toJSON() - } catch (err) { - if ((err as NodeJS.ErrnoException).code === 'ENOENT') { - return null - } - - throw err - } - } - - async write(data: T | null): Promise { - await this.writer.write(parseData(data).toString()) - } - - async reset(initialData: T): Promise { - try { - const data = await this.read() - await this.directory.createTemporaryFile(this.name, data)?.writeAsync() - await this.write(initialData) - } catch (err) { - if ((err as NodeJS.ErrnoException).code === 'ENOENT') { - this.write(initialData) - } - - throw err - } - } - - async exists(): Promise { - return (await this.read()) !== null - } -} diff --git a/packages/node/src/adapter/BaseAdapter.ts b/packages/node/src/adapter/BaseAdapter.ts new file mode 100644 index 0000000..05d8734 --- /dev/null +++ b/packages/node/src/adapter/BaseAdapter.ts @@ -0,0 +1,41 @@ +import { parseData } from '@stenodb/utils' +import { plainToClass } from 'class-transformer' +import { Writer } from 'steno' +import type { DirectoryProvider } from '../provider/DirectoryProvider.js' +import type { Steno } from '../types.js' + +export class BaseAdapter { + fileName: string + entity: Steno.Entity + + directory: DirectoryProvider + filePath: string + writer: Writer + + data: T | null = null + initialData: T | null = null + + constructor(fileName: string, entity: Steno.Entity, initialData?: T) { + this.fileName = fileName + this.entity = entity + + if (initialData) { + this.initialData = initialData + } + } + + plainData(data: T | string | null = this.data): T | null { + if (!data) return null + + const parsedData = + typeof data === 'string' ? parseData(data).toJSON() : data + + return plainToClass(this.entity, parsedData) + } + + registerDirectory(directory: DirectoryProvider): void { + this.directory = directory + this.filePath = this.directory.databaseFilePath(this.fileName) + this.writer = new Writer(this.filePath) + } +} diff --git a/packages/node/src/adapter/BaseWriter.ts b/packages/node/src/adapter/BaseWriter.ts deleted file mode 100644 index baa8f1b..0000000 --- a/packages/node/src/adapter/BaseWriter.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Writer } from 'steno' -import type { DirectoryProvider } from '../DirectoryProvider.js' -import type { Entity } from '../types.js' - -export class BaseWriter { - name: string - entity: Entity - - file: string - directory: DirectoryProvider - writer: Writer - - constructor(name: string, entity: Entity) { - this.name = name - this.entity = entity - } - - setDirectory(directory: DirectoryProvider): void { - this.directory = directory - this.file = this.directory.databaseFilePath(this.name) - this.writer = new Writer(this.file) - } -} diff --git a/packages/node/src/adapter/SyncAdapter.ts b/packages/node/src/adapter/SyncAdapter.ts new file mode 100644 index 0000000..61a897b --- /dev/null +++ b/packages/node/src/adapter/SyncAdapter.ts @@ -0,0 +1,39 @@ +import { readFileSync } from 'node:fs' +import { parseData } from '@stenodb/utils' +import { BaseAdapter } from './BaseAdapter.js' +import type { Steno } from '../types.js' + +export class SyncAdapter extends BaseAdapter { + constructor(fileName: string, entity: Steno.Entity, initialData?: T) { + super(fileName, entity, initialData) + } + + read(): void { + try { + const file = readFileSync(this.filePath, 'utf-8') + this.data = this.plainData(file) + } catch (err) { + console.log(err) + } + } + + write(): void { + this.writer.write(parseData(this.data).toString()) + } + + reset(): void { + if (!this.initialData) return + + try { + this.directory.createTemporaryFile(this.fileName, this.data)?.write() + this.data = this.initialData + this.write() + } catch (err) { + throw err + } + } + + exists(): boolean { + return this.read() !== null + } +} diff --git a/packages/node/src/adapter/SyncWriter.ts b/packages/node/src/adapter/SyncWriter.ts deleted file mode 100644 index d5121ea..0000000 --- a/packages/node/src/adapter/SyncWriter.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { readFileSync } from 'node:fs' -import { parseData } from '@stenodb/utils' -import { BaseWriter } from './BaseWriter.js' -import type { Entity } from '../types.js' - -interface SyncAdapter { - read(): T | null - write(data: T | null): void - reset(initialData: T): void - exists(): boolean -} - -export class SyncWriter extends BaseWriter implements SyncAdapter { - constructor(name: string, entity: Entity) { - super(name, entity) - } - - read(): T | null { - try { - const data = readFileSync(this.file, 'utf-8') - return parseData(data).toJSON() - } catch (err) { - if ((err as NodeJS.ErrnoException).code === 'ENOENT') { - return null - } - - throw err - } - } - - write(data: T | null): void { - this.writer.write(parseData(data).toString()) - } - - reset(initialData: T): void { - try { - const data = this.read() - this.directory.createTemporaryFile(this.name, data)?.write() - this.write(initialData) - } catch (err) { - throw err - } - } - - exists(): boolean { - return this.read() !== null - } -} diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 7183f32..06fbdfe 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -1,3 +1,3 @@ -export * from './adapter/AsyncWriter.js' -export * from './adapter/SyncWriter.js' -export * from './NodeDatabase.js' +export * from './adapter/AsyncAdapter.js' +export * from './adapter/SyncAdapter.js' +export * from './provider/NodeProvider.js' diff --git a/packages/node/src/provider/AsyncProvider.ts b/packages/node/src/provider/AsyncProvider.ts index 2758010..28656c8 100644 --- a/packages/node/src/provider/AsyncProvider.ts +++ b/packages/node/src/provider/AsyncProvider.ts @@ -1,36 +1,32 @@ -import { plainToClass } from 'class-transformer' -import { AsyncWriter } from '../index.js' +import { AsyncAdapter } from '../index.js' import { BaseProvider } from './BaseProvider.js' export class AsyncProvider extends BaseProvider { - #adapter: AsyncWriter + #adapter: AsyncAdapter - constructor(adapter: AsyncWriter, initialData?: T) { - super() + constructor(adapter: AsyncAdapter) { + super(adapter) this.#adapter = adapter - this.initialData = initialData } async read(): Promise { - this.data = await this.#adapter.read() + await this.#adapter.read() if (!this.data) { await this.reset() } else { - this.data = plainToClass(this.#adapter.entity, this.data) + this.data = this.#adapter.plainData() } return this.data } async write(): Promise { - await this.#adapter.write(this.data) + await this.#adapter.write() } async reset(): Promise { - if (!this.initialData) return - this.data = plainToClass(this.#adapter.entity, this.initialData) - await this.#adapter.reset(this.data) + await this.#adapter.reset() } async exists(): Promise { diff --git a/packages/node/src/provider/BaseProvider.ts b/packages/node/src/provider/BaseProvider.ts index d0ea3bf..4792fa6 100644 --- a/packages/node/src/provider/BaseProvider.ts +++ b/packages/node/src/provider/BaseProvider.ts @@ -1,21 +1,26 @@ +import type { Steno } from '../types.js' + export class BaseProvider { - #data: T | null = null - #initialData: T | null = null + #adapter: Steno.NodeAdapter + + constructor(adapter: Steno.NodeAdapter) { + this.#adapter = adapter + } get data(): T | null { - return this.#data + return this.#adapter.data } set data(data: T | null) { - this.#data = data + this.#adapter.data = data } get initialData(): T | null { - return this.#initialData + return this.#adapter.initialData } set initialData(data: T | undefined | null) { if (!data) return - this.#initialData = data + this.#adapter.initialData = this.#adapter.plainData(data) } } diff --git a/packages/node/src/DirectoryProvider.ts b/packages/node/src/provider/DirectoryProvider.ts similarity index 84% rename from packages/node/src/DirectoryProvider.ts rename to packages/node/src/provider/DirectoryProvider.ts index 08e05f3..ce9149d 100644 --- a/packages/node/src/DirectoryProvider.ts +++ b/packages/node/src/provider/DirectoryProvider.ts @@ -1,4 +1,4 @@ -import { lstatSync, mkdir, readFile, rmSync } from 'node:fs' +import { mkdir, readFile, rmSync } from 'node:fs' import { join } from 'node:path' import { parseData } from '@stenodb/utils' import { Writer } from 'steno' @@ -8,10 +8,6 @@ export class DirectoryProvider { temporaryPath: string constructor(path: string) { - // if (!lstatSync(path).isDirectory()) { - // throw new Error('Path must be a directory') - // } - this.databasePath = path this.temporaryPath = join(this.databasePath, 'temp') this.createDatabaseDir() @@ -41,7 +37,7 @@ export class DirectoryProvider { return join(this.temporaryPath, `${filename}-${Date.now()}.json`) } - createTemporaryFile(filename: string, data: T | null) { + createTemporaryFile(filename: string, data: T | string | null) { if (!data) return const file = this.temporaryFilePath(filename) diff --git a/packages/node/src/provider/NodeProvider.ts b/packages/node/src/provider/NodeProvider.ts new file mode 100644 index 0000000..54f7d59 --- /dev/null +++ b/packages/node/src/provider/NodeProvider.ts @@ -0,0 +1,42 @@ +import { AsyncAdapter } from '../adapter/AsyncAdapter.js' +import { SyncAdapter } from '../adapter/SyncAdapter.js' +import { AsyncProvider } from './AsyncProvider.js' +import { DirectoryProvider } from './DirectoryProvider.js' +import { SyncProvider } from './SyncProvider.js' +import type { Steno } from '../types.js' + +export class NodeProvider { + #directory: DirectoryProvider + + constructor(path: string) { + this.#directory = new DirectoryProvider(path) + } + + private registerDirectory(adapter: Steno.NodeAdapter) { + adapter.registerDirectory(this.#directory) + } + + create(adapter: SyncAdapter): SyncProvider + create(adapter: AsyncAdapter): AsyncProvider + create(adapter: Steno.NodeAdapter): Steno.NodeProvider { + if (adapter instanceof SyncAdapter) { + return this.createSync(adapter) + } + + if (adapter instanceof AsyncAdapter) { + return this.createAsync(adapter) + } + + throw new Error('Invalid adapter') + } + + createSync(adapter: SyncAdapter): SyncProvider { + this.registerDirectory(adapter) + return new SyncProvider(adapter) + } + + createAsync(adapter: AsyncAdapter): AsyncProvider { + this.registerDirectory(adapter) + return new AsyncProvider(adapter) + } +} diff --git a/packages/node/src/provider/SyncProvider.ts b/packages/node/src/provider/SyncProvider.ts index d841ece..03cbf70 100644 --- a/packages/node/src/provider/SyncProvider.ts +++ b/packages/node/src/provider/SyncProvider.ts @@ -1,37 +1,32 @@ -import { plainToClass } from 'class-transformer' -import { SyncWriter } from '../index.js' +import { SyncAdapter } from '../index.js' import { BaseProvider } from './BaseProvider.js' export class SyncProvider extends BaseProvider { - #adapter: SyncWriter - - constructor(adapter: SyncWriter, initialData?: T) { - super() + #adapter: SyncAdapter + constructor(adapter: SyncAdapter) { + super(adapter) this.#adapter = adapter - this.initialData = initialData } read(): T | null { - this.data = this.#adapter.read() + this.#adapter.read() if (!this.data) { this.reset() } else { - this.data = plainToClass(this.#adapter.entity, this.data) + this.data = this.#adapter.plainData() } return this.data } write(): void { - this.#adapter.write(this.data) + this.#adapter.write() } reset(): void { - if (!this.initialData) return - this.data = plainToClass(this.#adapter.entity, this.initialData) - this.#adapter.reset(this.data) + this.#adapter.reset() } exists(): boolean { diff --git a/packages/node/src/types.ts b/packages/node/src/types.ts index 35fc652..d90be02 100644 --- a/packages/node/src/types.ts +++ b/packages/node/src/types.ts @@ -1,9 +1,11 @@ -import type { AsyncWriter } from './adapter/AsyncWriter.js' -import type { SyncWriter } from './adapter/SyncWriter.js' +import type { AsyncAdapter } from './adapter/AsyncAdapter.js' +import type { SyncAdapter } from './adapter/SyncAdapter.js' import type { AsyncProvider } from './provider/AsyncProvider.js' import type { SyncProvider } from './provider/SyncProvider.js' import type { ClassConstructor } from 'class-transformer' -export type Entity = ClassConstructor -export type NodeAdapter = AsyncWriter | SyncWriter -export type NodeProvider = AsyncProvider | SyncProvider +export namespace Steno { + export type Entity = ClassConstructor + export type NodeAdapter = AsyncAdapter | SyncAdapter + export type NodeProvider = AsyncProvider | SyncProvider +} From ac2f977ce277d163daca45154c1ef8289b9a5bef Mon Sep 17 00:00:00 2001 From: Vitalij Ryndin Date: Tue, 7 Feb 2023 22:07:18 +0800 Subject: [PATCH 2/5] feat(packages/browser): reworked api --- README.md | 13 ++-- examples/with-browser-lodash/src/storage.ts | 33 +++++++--- examples/with-browser/src/storage.ts | 8 ++- examples/with-node-lodash/src/index.ts | 8 ++- packages/browser/README.md | 13 ++-- packages/browser/src/BrowserDatabase.ts | 9 --- packages/browser/src/BrowserProvider.ts | 55 ----------------- packages/browser/src/adapter/LocalStorage.ts | 6 +- .../browser/src/adapter/SessionStorage.ts | 6 +- packages/browser/src/adapter/WebStorage.ts | 60 ++++++++++++------- packages/browser/src/index.ts | 2 +- packages/browser/src/provider/BaseProvider.ts | 26 ++++++++ .../browser/src/provider/BrowserProvider.ts | 8 +++ .../browser/src/provider/StorageProvider.ts | 35 +++++++++++ packages/browser/src/types.ts | 8 ++- packages/node/src/provider/BaseProvider.ts | 2 +- 16 files changed, 172 insertions(+), 120 deletions(-) delete mode 100644 packages/browser/src/BrowserDatabase.ts delete mode 100644 packages/browser/src/BrowserProvider.ts create mode 100644 packages/browser/src/provider/BaseProvider.ts create mode 100644 packages/browser/src/provider/BrowserProvider.ts create mode 100644 packages/browser/src/provider/StorageProvider.ts diff --git a/README.md b/README.md index e0fe2a9..fbcc52c 100644 --- a/README.md +++ b/README.md @@ -89,16 +89,17 @@ await database.write() ### `@stenodb/browser` ```typescript import 'reflect-metadata' -import { LocalStorage, BrowserDatabase } from '@stenodb/browser' +import { LocalStorage, BrowserProvider } from '@stenodb/browser' import { Users, User, Post } from './entities.js' -const adapter = new LocalStorage('users', Users) const initialData = new Users(new User('John Doe')) -const databaseUsers = new BrowserDatabase(adapter, initialData) +const adapter = new LocalStorage('users', Users, initialData) +const provider = new BrowserProvider() +const storage = provider.create(adapter) -databaseUsers.read() -databaseUsers.data?.users[0]?.addPost(new Post('Lorem ipsum')) -databaseUsers.write() +storage.read() +storage.data?.users[0]?.addPost(new Post('Lorem ipsum')) +storage.write() ``` ## Credits diff --git a/examples/with-browser-lodash/src/storage.ts b/examples/with-browser-lodash/src/storage.ts index 2cf0f21..6e7eb3c 100644 --- a/examples/with-browser-lodash/src/storage.ts +++ b/examples/with-browser-lodash/src/storage.ts @@ -1,20 +1,37 @@ -import { BrowserDatabase, LocalStorage } from '@stenodb/browser' +import { BrowserProvider, LocalStorage } from '@stenodb/browser' +import { Steno } from '@stenodb/browser/types' import lodash from 'lodash' import { User, Users } from './entities.js' -import type { BrowserAdapter } from '@stenodb/browser/types' -class StorageWithLodash extends BrowserDatabase { - chain: lodash.ExpChain = lodash.chain(this).get('data') +export class BrowserWithLodash { + chain: lodash.ExpChain - constructor(adapter: BrowserAdapter, initialData: T) { - super(adapter, initialData) + constructor(private readonly provider: Steno.BrowserProvider) { + this.chain = lodash.chain(provider).get('data') + } + + get data(): T | null { + return this.provider.data + } + + read(): T | null { + return this.provider.read() + } + + write(): void { + this.provider.write() + } + + reset(): void { + this.provider.reset() } } -const adapter = new LocalStorage('users', Users) const initialData = new Users(new User(1, 'John')) +const adapter = new LocalStorage('users', Users, initialData) +const provider = new BrowserProvider() -export const storage = new StorageWithLodash(adapter, initialData) +export const storage = new BrowserWithLodash(provider.create(adapter)) storage.read() export function addUser(user: User): void { diff --git a/examples/with-browser/src/storage.ts b/examples/with-browser/src/storage.ts index 03c593c..17c6fa3 100644 --- a/examples/with-browser/src/storage.ts +++ b/examples/with-browser/src/storage.ts @@ -1,7 +1,9 @@ -import { BrowserDatabase, LocalStorage } from '@stenodb/browser' +import { BrowserProvider, LocalStorage } from '@stenodb/browser' import { User, Users } from './entities.js' -const adapter = new LocalStorage('users', Users) const initialData = new Users(new User(1, 'John')) -export const storage = new BrowserDatabase(adapter, initialData) +const adapter = new LocalStorage('users', Users, initialData) +const provider = new BrowserProvider() + +export const storage = provider.create(adapter) storage.read() diff --git a/examples/with-node-lodash/src/index.ts b/examples/with-node-lodash/src/index.ts index 2eed3e6..0de795f 100644 --- a/examples/with-node-lodash/src/index.ts +++ b/examples/with-node-lodash/src/index.ts @@ -17,13 +17,17 @@ export class NodeWithLodash { return this.provider.data } - async read(): Promise { - await this.provider.read() + async read(): Promise { + return await this.provider.read() } async write(): Promise { await this.provider.write() } + + async reset(): Promise { + await this.provider.reset() + } } const path = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'database') diff --git a/packages/browser/README.md b/packages/browser/README.md index ed58a2a..94a7931 100644 --- a/packages/browser/README.md +++ b/packages/browser/README.md @@ -62,16 +62,17 @@ export class Post { ```typescript // index.ts import 'reflect-metadata' -import { LocalStorage, BrowserDatabase } from '@stenodb/browser' +import { LocalStorage, BrowserProvider } from '@stenodb/browser' import { Users, User, Post } from './entities.js' -const adapter = new LocalStorage('users', Users) const initialData = new Users(new User('John Doe')) -const databaseUsers = new BrowserDatabase(adapter, initialData) +const adapter = new LocalStorage('users', Users, initialData) +const provider = new BrowserProvider() +const storage = provider.create(adapter) -databaseUsers.read() -databaseUsers.data?.users[0]?.addPost(new Post('Lorem ipsum')) -databaseUsers.write() +storage.read() +storage.data?.users[0]?.addPost(new Post('Lorem ipsum')) +storage.write() ``` ## Credits diff --git a/packages/browser/src/BrowserDatabase.ts b/packages/browser/src/BrowserDatabase.ts deleted file mode 100644 index 0c46bfd..0000000 --- a/packages/browser/src/BrowserDatabase.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { BrowserProvider } from './BrowserProvider.js' -import type { BrowserAdapter } from './types.js' - -export class BrowserDatabase extends BrowserProvider { - constructor(adapter: BrowserAdapter, initialData?: T) { - super(adapter) - this.initialData = initialData - } -} diff --git a/packages/browser/src/BrowserProvider.ts b/packages/browser/src/BrowserProvider.ts deleted file mode 100644 index e78ab62..0000000 --- a/packages/browser/src/BrowserProvider.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { plainToClass } from 'class-transformer' -import type { BrowserAdapter } from './types.js' - -export class BrowserProvider { - #adapter: BrowserAdapter - #data: T | null = null - #initialData: T | null = null - - constructor(adapter: BrowserAdapter) { - this.#adapter = adapter - } - - get data(): T | null { - return this.#data - } - - set data(data: T | null) { - this.#data = data - } - - get initialData(): T | null { - return this.#initialData - } - - set initialData(data: T | undefined | null) { - if (!data) return - this.#initialData = data - } - - read(): T | null { - this.#data = this.#adapter.read() - - if (!this.#data) { - this.reset() - } else { - this.#data = plainToClass(this.#adapter.entity, this.#data) - } - - return this.#data - } - - write(): void { - this.#adapter.write(this.#data) - } - - reset(): void { - if (!this.#initialData) return - this.#data = plainToClass(this.#adapter.entity, this.#initialData) - this.#adapter.write(this.#data) - } - - exists(): boolean { - return this.#adapter.exists() - } -} diff --git a/packages/browser/src/adapter/LocalStorage.ts b/packages/browser/src/adapter/LocalStorage.ts index 868e63c..74fc95b 100644 --- a/packages/browser/src/adapter/LocalStorage.ts +++ b/packages/browser/src/adapter/LocalStorage.ts @@ -1,8 +1,8 @@ import { BrowserStorage } from './WebStorage.js' -import type { Entity } from '../types.js' +import type { Steno } from '../types.js' export class LocalStorage extends BrowserStorage { - constructor(name: string, entity: Entity) { - super(name, localStorage, entity) + constructor(name: string, entity: Steno.Entity, initialData?: T) { + super(name, localStorage, entity, initialData) } } diff --git a/packages/browser/src/adapter/SessionStorage.ts b/packages/browser/src/adapter/SessionStorage.ts index e3c102f..910b746 100644 --- a/packages/browser/src/adapter/SessionStorage.ts +++ b/packages/browser/src/adapter/SessionStorage.ts @@ -1,8 +1,8 @@ import { BrowserStorage } from './WebStorage.js' -import type { Entity } from '../types.js' +import type { Steno } from '../types.js' export class SessionStorage extends BrowserStorage { - constructor(name: string, entity: Entity) { - super(name, sessionStorage, entity) + constructor(name: string, entity: Steno.Entity, initialData?: T) { + super(name, sessionStorage, entity, initialData) } } diff --git a/packages/browser/src/adapter/WebStorage.ts b/packages/browser/src/adapter/WebStorage.ts index 1be65ca..31be9b2 100644 --- a/packages/browser/src/adapter/WebStorage.ts +++ b/packages/browser/src/adapter/WebStorage.ts @@ -1,34 +1,52 @@ import { parseData } from '@stenodb/utils' -import type { Entity } from '../types.js' +import { plainToClass } from 'class-transformer' +import type { Steno } from '../types.js' -interface SyncAdapter { - read(): T | null - write(data: T | null): void - exists(): boolean -} +export class BrowserStorage { + name: string + storage: Storage + entity: Steno.Entity + + data: T | null = null + initialData: T | null = null + + constructor( + name: string, + storage: Storage, + entity: Steno.Entity, + initialData?: T + ) { + this.name = name + this.storage = storage + this.entity = entity + + if (initialData) { + this.initialData = initialData + } + } + + plainData(data: T | string | null = this.data): T | null { + if (!data) return null -export class BrowserStorage implements SyncAdapter { - #name: string - #storage: Storage - #entity: Entity + const parsedData = + typeof data === 'string' ? parseData(data).toJSON() : data - constructor(name: string, storage: Storage, entity: Entity) { - this.#name = name - this.#storage = storage - this.#entity = entity + return plainToClass(this.entity, parsedData) } - get entity(): Entity { - return this.#entity + read(): void { + const data = this.storage.getItem(this.name) + this.data = data ? this.plainData(data) : null } - read(): T | null { - const data = this.#storage.getItem(this.#name) - return data ? parseData(data).toJSON() : null + write(): void { + this.storage.setItem(this.name, parseData(this.data).toString()) } - write(data: T | null): void { - this.#storage.setItem(this.#name, parseData(data).toString()) + reset(): void { + if (!this.initialData) return + this.data = plainToClass(this.entity, this.initialData) + this.write() } exists(): boolean { diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts index c4a0554..f08e94e 100644 --- a/packages/browser/src/index.ts +++ b/packages/browser/src/index.ts @@ -1,3 +1,3 @@ export * from './adapter/LocalStorage.js' export * from './adapter/SessionStorage.js' -export * from './BrowserDatabase.js' +export * from './provider/BrowserProvider.js' diff --git a/packages/browser/src/provider/BaseProvider.ts b/packages/browser/src/provider/BaseProvider.ts new file mode 100644 index 0000000..de01c94 --- /dev/null +++ b/packages/browser/src/provider/BaseProvider.ts @@ -0,0 +1,26 @@ +import type { Steno } from '../types.js' + +export class BaseProvider { + #adapter: Steno.BrowserAdapter + + constructor(adapter: Steno.BrowserAdapter) { + this.#adapter = adapter + } + + get data(): T | null { + return this.#adapter.data + } + + set data(data: T | null) { + this.#adapter.data = data + } + + get initialData(): T | null { + return this.#adapter.initialData + } + + set initialData(data: T | undefined | null) { + if (!data) return + this.#adapter.initialData = data + } +} diff --git a/packages/browser/src/provider/BrowserProvider.ts b/packages/browser/src/provider/BrowserProvider.ts new file mode 100644 index 0000000..9b99aa5 --- /dev/null +++ b/packages/browser/src/provider/BrowserProvider.ts @@ -0,0 +1,8 @@ +import { StorageProvider } from './StorageProvider.js' +import type { Steno } from '../types.js' + +export class BrowserProvider { + create(adapter: Steno.BrowserAdapter): StorageProvider { + return new StorageProvider(adapter) + } +} diff --git a/packages/browser/src/provider/StorageProvider.ts b/packages/browser/src/provider/StorageProvider.ts new file mode 100644 index 0000000..c1b04c4 --- /dev/null +++ b/packages/browser/src/provider/StorageProvider.ts @@ -0,0 +1,35 @@ +import { BaseProvider } from './BaseProvider.js' +import type { Steno } from '../types.js' + +export class StorageProvider extends BaseProvider { + #adapter: Steno.BrowserAdapter + + constructor(adapter: Steno.BrowserAdapter) { + super(adapter) + this.#adapter = adapter + } + + read(): T | null { + this.#adapter.read() + + if (!this.data) { + this.reset() + } else { + this.data = this.#adapter.plainData() + } + + return this.data + } + + write(): void { + this.#adapter.write() + } + + reset(): void { + this.#adapter.reset() + } + + exists(): boolean { + return this.#adapter.exists() + } +} diff --git a/packages/browser/src/types.ts b/packages/browser/src/types.ts index a670d4c..3cca074 100644 --- a/packages/browser/src/types.ts +++ b/packages/browser/src/types.ts @@ -1,6 +1,10 @@ import type { LocalStorage } from './adapter/LocalStorage.js' import type { SessionStorage } from './adapter/SessionStorage.js' +import type { StorageProvider } from './provider/StorageProvider.js' import type { ClassConstructor } from 'class-transformer' -export type Entity = ClassConstructor -export type BrowserAdapter = LocalStorage | SessionStorage +export namespace Steno { + export type Entity = ClassConstructor + export type BrowserAdapter = LocalStorage | SessionStorage + export type BrowserProvider = StorageProvider +} diff --git a/packages/node/src/provider/BaseProvider.ts b/packages/node/src/provider/BaseProvider.ts index 4792fa6..91d7541 100644 --- a/packages/node/src/provider/BaseProvider.ts +++ b/packages/node/src/provider/BaseProvider.ts @@ -21,6 +21,6 @@ export class BaseProvider { set initialData(data: T | undefined | null) { if (!data) return - this.#adapter.initialData = this.#adapter.plainData(data) + this.#adapter.initialData = data } } From d1d6fb0ef1ccd620ef55e553727feb3373262c9c Mon Sep 17 00:00:00 2001 From: Vitalij Ryndin Date: Tue, 7 Feb 2023 22:07:49 +0800 Subject: [PATCH 3/5] docs: update readme --- packages/stenodb/README.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/packages/stenodb/README.md b/packages/stenodb/README.md index aa09a13..fbcc52c 100644 --- a/packages/stenodb/README.md +++ b/packages/stenodb/README.md @@ -72,33 +72,34 @@ export class Post { import 'reflect-metadata' import { dirname, resolve } from 'node:path' import { fileURLToPath } from 'node:url' -import { AsyncWriter, NodeDatabase } from '@stenodb/node' +import { AsyncAdapter, NodeProvider } from '@stenodb/node' import { Users, User, Post } from './entities.js' const path = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'database') -const adapter = new AsyncWriter('users', Users) const initialData = new Users(new User('John Doe')) -const database = new NodeDatabase(path) -const databaseUsers = database.create(adapter, initialData) +const adapter = new AsyncAdapter('users', Users, initialData) +const provider = new NodeProvider(path) +const database = provider.createAsync(adapter) -await databaseUsers.read() -databaseUsers.data?.users[0]?.addPost(new Post('Lorem ipsum')) -await databaseUsers.write() +await database.read() +database.data?.users[0]?.addPost(new Post('Lorem ipsum')) +await database.write() ``` ### `@stenodb/browser` ```typescript import 'reflect-metadata' -import { LocalStorage, BrowserDatabase } from '@stenodb/browser' +import { LocalStorage, BrowserProvider } from '@stenodb/browser' import { Users, User, Post } from './entities.js' -const adapter = new LocalStorage('users', Users) const initialData = new Users(new User('John Doe')) -const databaseUsers = new BrowserDatabase(adapter, initialData) +const adapter = new LocalStorage('users', Users, initialData) +const provider = new BrowserProvider() +const storage = provider.create(adapter) -databaseUsers.read() -databaseUsers.data?.users[0]?.addPost(new Post('Lorem ipsum')) -databaseUsers.write() +storage.read() +storage.data?.users[0]?.addPost(new Post('Lorem ipsum')) +storage.write() ``` ## Credits From 348a1e038ffdad01018846e420eeebf396f43ac0 Mon Sep 17 00:00:00 2001 From: Vitalij Ryndin Date: Tue, 7 Feb 2023 22:10:07 +0800 Subject: [PATCH 4/5] 3.0.0 --- package.json | 2 +- packages/browser/package.json | 4 ++-- packages/node/package.json | 4 ++-- packages/stenodb/package.json | 6 +++--- packages/utils/package.json | 2 +- pnpm-lock.yaml | 8 ++++---- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 8664236..9b220b6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@stenodb/workspace", - "version": "2.0.0", + "version": "3.0.0", "type": "module", "private": true, "workspaces": [ diff --git a/packages/browser/package.json b/packages/browser/package.json index 7f055d3..6baff50 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -1,7 +1,7 @@ { "name": "@stenodb/browser", "description": "✍ Easy to use local JSON database", - "version": "2.0.0", + "version": "3.0.0", "type": "module", "files": [ "dist" @@ -49,7 +49,7 @@ "prepublishOnly": "pnpm build" }, "dependencies": { - "@stenodb/utils": "workspace:2.0.0", + "@stenodb/utils": "workspace:3.0.0", "class-transformer": "0.5.1" }, "peerDependencies": { diff --git a/packages/node/package.json b/packages/node/package.json index 0f540cf..59881b4 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -1,7 +1,7 @@ { "name": "@stenodb/node", "description": "✍ Easy to use local JSON database", - "version": "2.0.0", + "version": "3.0.0", "type": "module", "files": [ "dist" @@ -46,7 +46,7 @@ "prepublishOnly": "pnpm build" }, "dependencies": { - "@stenodb/utils": "workspace:2.0.0", + "@stenodb/utils": "workspace:3.0.0", "class-transformer": "0.5.1", "steno": "3.0.0" }, diff --git a/packages/stenodb/package.json b/packages/stenodb/package.json index f5fc843..e4b1fe9 100644 --- a/packages/stenodb/package.json +++ b/packages/stenodb/package.json @@ -1,7 +1,7 @@ { "name": "stenodb", "description": "✍ Easy to use local JSON database", - "version": "2.0.0", + "version": "3.0.0", "type": "module", "files": [ "dist" @@ -61,8 +61,8 @@ "prepublishOnly": "pnpm build" }, "dependencies": { - "@stenodb/browser": "workspace:2.0.0", - "@stenodb/node": "workspace:2.0.0" + "@stenodb/browser": "workspace:3.0.0", + "@stenodb/node": "workspace:3.0.0" }, "peerDependencies": { "class-transformer": ">=0.5.0" diff --git a/packages/utils/package.json b/packages/utils/package.json index 439e898..eca78fb 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@stenodb/utils", - "version": "2.0.0", + "version": "3.0.0", "type": "module", "files": [ "dist" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e9e67ff..5aaa564 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -94,7 +94,7 @@ importers: packages/browser: specifiers: - '@stenodb/utils': workspace:2.0.0 + '@stenodb/utils': workspace:3.0.0 class-transformer: 0.5.1 dependencies: '@stenodb/utils': link:../utils @@ -102,7 +102,7 @@ importers: packages/node: specifiers: - '@stenodb/utils': workspace:2.0.0 + '@stenodb/utils': workspace:3.0.0 '@types/node': 18.11.19 class-transformer: 0.5.1 steno: 3.0.0 @@ -115,8 +115,8 @@ importers: packages/stenodb: specifiers: - '@stenodb/browser': workspace:2.0.0 - '@stenodb/node': workspace:2.0.0 + '@stenodb/browser': workspace:3.0.0 + '@stenodb/node': workspace:3.0.0 class-transformer: '>=0.5.0' dependencies: '@stenodb/browser': link:../browser From c8bc4957d92d5de2aa3b71af27b8991e73910a84 Mon Sep 17 00:00:00 2001 From: Vitalij Ryndin Date: Tue, 7 Feb 2023 22:10:33 +0800 Subject: [PATCH 5/5] docs: update readme --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index fbcc52c..ebcbae6 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,6 @@ pnpm add stenodb | Package | Version | Platform | | ------- | ------ | ----------- | -| [stenodb](./packages/stenodb) | [![](https://img.shields.io/npm/v/stenodb)](https://npm.im/stenodb) | Reexports packages | | [@stenodb/node](./packages/node) | [![](https://img.shields.io/npm/v/@stenodb/node)](https://npm.im/@stenodb/node) | Node.js | | [@stenodb/browser](./packages/browser) | [![](https://img.shields.io/npm/v/@stenodb/browser)](https://npm.im/@stenodb/browser) | Browser |