From 502650dae55852401ce8484614085a8500dab9cc Mon Sep 17 00:00:00 2001 From: R-Rajaneesh Date: Thu, 17 Nov 2022 07:17:24 +0530 Subject: [PATCH] feat: @quipodb/json & @quipodb/steno --- build.js | 7 ++ package.json | 7 +- packages/core/dist/index.d.ts | 72 +++++++++++-- packages/core/dist/index.js | 187 ++++++++++++++++++++++++++++++-- packages/core/index.ts | 189 +++++++++++++++++++++++++++++++-- packages/json/dist/index.d.ts | 23 ++++ packages/json/dist/index.js | 57 ++++++++++ packages/json/index.ts | 67 ++++++++++++ packages/json/package.json | 39 +++++++ packages/steno/dist/index.d.ts | 5 + packages/steno/dist/index.js | 66 ++++++++++++ packages/steno/index.ts | 78 ++++++++++++++ packages/steno/package.json | 24 +++++ 13 files changed, 792 insertions(+), 29 deletions(-) create mode 100644 build.js create mode 100644 packages/json/dist/index.d.ts create mode 100644 packages/json/dist/index.js create mode 100644 packages/json/index.ts create mode 100644 packages/json/package.json create mode 100644 packages/steno/dist/index.d.ts create mode 100644 packages/steno/dist/index.js create mode 100644 packages/steno/index.ts create mode 100644 packages/steno/package.json diff --git a/build.js b/build.js new file mode 100644 index 0000000..0d7b36a --- /dev/null +++ b/build.js @@ -0,0 +1,7 @@ +import fs from "fs-extra"; +import { exec } from "child_process"; +fs.readdirSync("packages").forEach((dir) => { + exec(`cd packages/${dir} && npm run build`, (error, stdout, stderr) => { + console.log(stdout); + }); +}); diff --git a/package.json b/package.json index aaa2f4c..6b53e98 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,7 @@ "version": "0.0.1-beta.0", "type": "module", "scripts": { - "build": "tsc index.ts -t esnext -m esnext -d --allowSyntheticDefaultImports --moduleResolution node --outDir ./dist", - "build-packages": "cd packages/core && npm run build && cd.. && cd.. && cd packages/firestore && npm run build && cd.. && cd.. && cd packages/sqlite && npm run build && cd.. && cd..", + "build": "node build.js", "publish-beta-public": "npm publish --tag beta --registry=https://registry.npmjs.com --workspaces", "publish-beta-private": "npm publish --tag beta --workspaces", "publish-public": "npm publish --registry=https://registry.npmjs.com --workspaces", @@ -30,11 +29,8 @@ "/packages/*" ], "dependencies": { - "@firebase/firestore": "^3.6.0", - "@hapi/hapi": "^20.2.2", "axios": "^1.1.3", "better-sqlite3": "^7.6.2", - "bson": "^4.7.0", "firebase": "^9.11.0", "firebase-admin": "^11.0.1", "fs-extra": "^10.1.0", @@ -48,6 +44,7 @@ "@types/fs-extra": "^9.0.13", "@types/lodash": "^4.14.186", "@types/node": "^18.11.3", + "globby": "^13.1.2", "typescript": "^4.8.4" } } diff --git a/packages/core/dist/index.d.ts b/packages/core/dist/index.d.ts index ace9006..54e96f0 100644 --- a/packages/core/dist/index.d.ts +++ b/packages/core/dist/index.d.ts @@ -1,5 +1,6 @@ interface constructorOptions { providers: providers[]; + cache: Boolean; } interface providers { createCollectionProvider(collectionName: String, cb?: Function): Promise | any; @@ -18,9 +19,10 @@ interface storage { interface document { [key: string]: string | number | Object | Array | any; } -export default class QuipoDB { +export declare class QuipoDB { options: constructorOptions; storage: storage; + cache: Boolean; collectionName: String; Docs: typeof Docs; providers: providers[]; @@ -31,11 +33,14 @@ export default class QuipoDB { interface DocsOptions { providers: providers[]; collectionName: String; + storage: storage; + cache: Boolean; } declare class Docs { private options; private providers; private collectionName; + private storage; constructor(options: DocsOptions); createDoc(data: document | document[], cb?: Function): Promise; deleteDoc(data: document | fn, cb?: Function): Promise; @@ -46,10 +51,65 @@ declare class Docs { [x: string]: any; save: Function; }>; - private $add; - private $subtract; - private $multiply; - private $divide; - private $push; + private _$add; + private _$subtract; + private _$multiply; + private _$divide; + private _$push; +} +export declare class Query { + private data; + private key; + private current; + private _limit; + constructor(Data: any[]); + add(val: number): this; + /** + * Gets back to the top level on the data + */ + clearQuery(): this; + delete(key: string): this; + divide(val: number): this; + equals(val: any): this; + /** + * Check if the data exists on every object + */ + exists(key: string): boolean; + find(key: string, val: any): void; + gt(val: any): this; + gte(val: any): this; + limit(val: number): this; + lt(val: any): this; + lte(val: any): this; + multiply(val: number): this; + push(arr: any[]): this; + /** + * Get the raw data + */ + raw(): any[]; + update(val: number): this; + /** + * Get into a deeper object + * @param {String} key + */ + select(key: string): this; + subtract(val: number): this; + /** + * Saves the queries on the data + */ + save(): this; + /** + * Get the Latest data + */ + toJSON(): any[]; + /** + * Get the last selected query + */ + toValue(): any[]; + /** + * Select a object to query next + * @param {string} key + */ + where(key: string): this; } export {}; diff --git a/packages/core/dist/index.js b/packages/core/dist/index.js index c9a0f9d..bcbc7e9 100644 --- a/packages/core/dist/index.js +++ b/packages/core/dist/index.js @@ -1,8 +1,9 @@ import _ from "lodash"; import { mergeAndConcat } from "merge-anything"; -export default class QuipoDB { +export class QuipoDB { options; storage; + cache; collectionName; Docs; providers; @@ -18,11 +19,13 @@ export default class QuipoDB { this.providers.forEach(async (provider) => { await provider.createCollectionProvider(collectionName, cb); }); + if (this.options.cache) + this.storage[`${collectionName}`] = []; } catch (error) { error; } - const docs = new this.Docs({ providers: this.providers, collectionName: this.collectionName }); + const docs = new this.Docs({ providers: this.providers, collectionName: this.collectionName, storage: this.storage, cache: this.options.cache }); cb(docs); return docs; } @@ -31,6 +34,8 @@ export default class QuipoDB { this.providers.forEach(async (provider) => { await provider.deleteCollectionProvider(collectionName); }); + if (this.options.cache) + delete this.storage[`${collectionName}`]; } catch (error) { error; @@ -43,10 +48,12 @@ class Docs { options; providers; collectionName; + storage; constructor(options) { this.options = options; this.providers = options.providers; this.collectionName = options.collectionName; + this.storage = options.storage; } async createDoc(data, cb = () => { }) { if (Array.isArray(data)) { @@ -54,12 +61,16 @@ class Docs { this.providers.forEach(async (provider) => { await provider.createDocProvider(doc); }); + if (this.options.cache) + this.storage[`${this.collectionName}`].push(doc); }); } try { this.providers.forEach(async (provider) => { await provider.createDocProvider(data); }); + if (this.options.cache) + this.storage[`${this.collectionName}`].push(data); } catch (error) { error; @@ -74,6 +85,9 @@ class Docs { this.providers.forEach(async (provider) => { await provider.deleteDocProvider(data); }); + if (this.options.cache) { + this.storage[`${this.collectionName}`].splice(_.findIndex(this.storage[`${this.collectionName}`], data), 1); + } } catch (error) { error; @@ -86,7 +100,8 @@ class Docs { if (typeof data === "function") data = data(await this.providers[0].getCollectionProvider(this.collectionName)) ?? {}; try { - result = await this.providers[0].getDocProvider(data); + const Data = await this.providers[0].getDocProvider(data); + result = this.options.cache ? _.find(this.storage[`${this.collectionName}`], data) ?? Data : Data; } catch (error) { error; @@ -112,12 +127,13 @@ class Docs { Object.keys(data) .filter((v) => v.startsWith("$")) .forEach((atomic, i) => { - data = _.defaultsDeep(this[atomic](oldDoc, data[atomic]), oldDoc); + data = _.defaultsDeep(this[`_${atomic}`](oldDoc, data[atomic]), oldDoc); }); try { this.providers.forEach(async (provider) => { await provider.updateDocProvider(refData, data); }); + this.storage[`${this.collectionName}`][_.findIndex(this.storage[`${this.collectionName}`], refData)] = data; } catch (error) { error; @@ -138,6 +154,7 @@ class Docs { try { delete this.save; await self.updateDoc(refData, this); + this.storage[`${this.collectionName}`][_.findIndex(this.storage[`${this.collectionName}`], refData)] = this; } catch (error) { error; @@ -147,7 +164,7 @@ class Docs { cb(func); return func; } - $add(...data) { + _$add(...data) { const deepMerge = (oldData, newData) => { return Object.keys(oldData).reduce((acc, key) => { if (typeof newData[key] === "object") { @@ -164,7 +181,7 @@ class Docs { const result = data.reduce((acc, obj) => (acc = deepMerge(acc, obj))); return result; } - $subtract(...data) { + _$subtract(...data) { const deepMerge = (oldData, newData) => { return Object.keys(oldData).reduce((acc, key) => { if (typeof newData[key] === "object") { @@ -181,7 +198,7 @@ class Docs { const result = data.reduce((acc, obj) => (acc = deepMerge(acc, obj))); return result; } - $multiply(...data) { + _$multiply(...data) { const deepMerge = (oldData, newData) => { return Object.keys(oldData).reduce((acc, key) => { if (typeof newData[key] === "object") { @@ -198,7 +215,7 @@ class Docs { const result = data.reduce((acc, obj) => (acc = deepMerge(acc, obj))); return result; } - $divide(...data) { + _$divide(...data) { const deepMerge = (oldData, newData) => { return Object.keys(oldData).reduce((acc, key) => { if (typeof newData[key] === "object") { @@ -215,7 +232,159 @@ class Docs { const result = data.reduce((acc, obj) => (acc = deepMerge(acc, obj))); return result; } - $push(oldval, newVal) { + _$push(oldval, newVal) { return mergeAndConcat(oldval, newVal); } } +export class Query { + data; + key; + current; + _limit; + constructor(Data) { + this.data = Data; + this.key = ""; + this.current = this.data.map((v) => ({ ...v, _$current: v })); + } + add(val) { + this.current.forEach((d) => { + d._$current[this.key] += val; + }); + return this; + } + /** + * Gets back to the top level on the data + */ + clearQuery() { + this.current = this.data.map((v) => ({ ...v, _$current: v })); + return this; + } + delete(key) { + this.current = this.current.map((v, i) => { + delete v._$current[key]; + return (v = v); + }); + return this; + } + divide(val) { + this.current.forEach((d) => { + d._$current[this.key] /= val; + }); + return this; + } + equals(val) { + this.current = this.current.filter((v, i) => v._$current[this.key] === val); + return this; + } + /** + * Check if the data exists on every object + */ + exists(key) { + return this.current.every((v) => v[key]); + } + find(key, val) { + const res = []; + return this.current.forEach((v) => { + if (v._$current[key] === val) + res.push(v); + }); + } + gt(val) { + this.current = this.current.filter((v, i) => v._$current[this.key] > val); + return this; + } + gte(val) { + this.current = this.current.filter((v, i) => v._$current[this.key] >= val); + return this; + } + limit(val) { + this._limit = val + 1; + return this; + } + lt(val) { + this.current = this.current.filter((v, i) => v._$current[this.key] < val); + return this; + } + lte(val) { + this.current = this.current.filter((v, i) => v._$current[this.key] <= val); + return this; + } + multiply(val) { + this.current.forEach((d) => { + d._$current[this.key] *= val; + }); + return this; + } + push(arr) { + arr.forEach((val) => { + this.current.forEach((d) => { + if (Array.isArray(d._$current[this.key])) + d._$current[this.key].push(val); + }); + }); + return this; + } + /** + * Get the raw data + */ + raw() { + const data = this.current.map((v) => (v = v._$current)); + return data; + } + update(val) { + this.current.forEach((d) => { + d._$current[this.key] = val; + }); + return this; + } + /** + * Get into a deeper object + * @param {String} key + */ + select(key) { + this.current.map((v, i) => { + this.current[i]._$current = v[key]; + }); + return this; + } + subtract(val) { + this.current.forEach((d) => { + d._$current[this.key] -= val; + }); + return this; + } + /** + * Saves the queries on the data + */ + save() { + this.current = this.current.map((v) => (v = v._$current)); + this.data = this.current; + return this; + } + /** + * Get the Latest data + */ + toJSON() { + return this.data; + } + /** + * Get the last selected query + */ + toValue() { + return this.current.map((v, i) => { + if (this._limit < i) + return; + if (v._$current) + delete v._$current; + return (v = v); + }); + } + /** + * Select a object to query next + * @param {string} key + */ + where(key) { + this.key = `${key}`; + return this; + } +} diff --git a/packages/core/index.ts b/packages/core/index.ts index 62b2c17..380d4df 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -3,6 +3,7 @@ import { mergeAndConcat } from "merge-anything"; interface constructorOptions { providers: providers[]; + cache: Boolean; } interface providers { createCollectionProvider(collectionName: String, cb?: Function): Promise | any; @@ -33,9 +34,10 @@ interface storage { interface document { [key: string]: string | number | Object | Array | any; } -export default class QuipoDB { +export class QuipoDB { public options: constructorOptions; public storage: storage; + public cache: Boolean; public collectionName: String; public Docs: typeof Docs; public providers: providers[]; @@ -51,10 +53,11 @@ export default class QuipoDB { this.providers.forEach(async (provider) => { await provider.createCollectionProvider(collectionName, cb); }); + if (this.options.cache) this.storage[`${collectionName}`] = []; } catch (error) { error; } - const docs = new this.Docs({ providers: this.providers, collectionName: this.collectionName }); + const docs = new this.Docs({ providers: this.providers, collectionName: this.collectionName, storage: this.storage, cache: this.options.cache }); cb(docs); return docs; @@ -64,6 +67,7 @@ export default class QuipoDB { this.providers.forEach(async (provider) => { await provider.deleteCollectionProvider(collectionName); }); + if (this.options.cache) delete this.storage[`${collectionName}`]; } catch (error) { error; } @@ -74,15 +78,19 @@ export default class QuipoDB { interface DocsOptions { providers: providers[]; collectionName: String; + storage: storage; + cache: Boolean; } class Docs { private options: any; private providers: providers[]; private collectionName: String; + private storage: storage; constructor(options: DocsOptions) { this.options = options; this.providers = options.providers; this.collectionName = options.collectionName; + this.storage = options.storage; } public async createDoc(data: document | document[], cb: Function = () => {}) { if (Array.isArray(data)) { @@ -90,12 +98,14 @@ class Docs { this.providers.forEach(async (provider) => { await provider.createDocProvider(doc); }); + if (this.options.cache) this.storage[`${this.collectionName}`].push(doc); }); } try { this.providers.forEach(async (provider) => { await provider.createDocProvider(data); }); + if (this.options.cache) this.storage[`${this.collectionName}`].push(data); } catch (error) { error; } @@ -109,6 +119,10 @@ class Docs { this.providers.forEach(async (provider) => { await provider.deleteDocProvider(data); }); + + if (this.options.cache) { + this.storage[`${this.collectionName}`].splice(_.findIndex(this.storage[`${this.collectionName}`], data), 1); + } } catch (error) { error; } @@ -119,7 +133,8 @@ class Docs { let result: object = {}; if (typeof data === "function") data = data(await this.providers[0].getCollectionProvider(this.collectionName)) ?? {}; try { - result = await this.providers[0].getDocProvider(data); + const Data = await this.providers[0].getDocProvider(data); + result = this.options.cache ? _.find(this.storage[`${this.collectionName}`], data) ?? Data : Data; } catch (error) { error; } @@ -147,12 +162,13 @@ class Docs { Object.keys(data) .filter((v) => v.startsWith("$")) .forEach((atomic, i) => { - data = _.defaultsDeep(this[atomic](oldDoc, data[atomic]), oldDoc); + data = _.defaultsDeep(this[`_${atomic}`](oldDoc, data[atomic]), oldDoc); }); try { this.providers.forEach(async (provider) => { await provider.updateDocProvider(refData, data); }); + this.storage[`${this.collectionName}`][_.findIndex(this.storage[`${this.collectionName}`], refData)] = data; } catch (error) { error; } @@ -172,6 +188,7 @@ class Docs { try { delete this.save; await self.updateDoc(refData, this); + this.storage[`${this.collectionName}`][_.findIndex(this.storage[`${this.collectionName}`], refData)] = this; } catch (error) { error; } @@ -181,7 +198,7 @@ class Docs { return func; } - private $add(...data: Object[]) { + private _$add(...data: Object[]) { const deepMerge = (oldData: any, newData: any) => { return Object.keys(oldData).reduce((acc, key) => { if (typeof newData[key] === "object") { @@ -198,7 +215,7 @@ class Docs { return result; } - private $subtract(...data: Object[]) { + private _$subtract(...data: Object[]) { const deepMerge = (oldData: any, newData: any) => { return Object.keys(oldData).reduce((acc, key) => { if (typeof newData[key] === "object") { @@ -215,7 +232,7 @@ class Docs { return result; } - private $multiply(...data: Object[]) { + private _$multiply(...data: Object[]) { const deepMerge = (oldData: any, newData: any) => { return Object.keys(oldData).reduce((acc, key) => { if (typeof newData[key] === "object") { @@ -232,7 +249,7 @@ class Docs { return result; } - private $divide(...data: Object[]) { + private _$divide(...data: Object[]) { const deepMerge = (oldData: any, newData: any) => { return Object.keys(oldData).reduce((acc, key) => { if (typeof newData[key] === "object") { @@ -249,7 +266,161 @@ class Docs { return result; } - private $push(oldval: any, newVal: any) { + private _$push(oldval: any, newVal: any) { return mergeAndConcat(oldval, newVal); } } + +export class Query { + private data: any[]; + private key: string; + private current: any[]; + private _limit: number; + constructor(Data: any[]) { + this.data = Data; + this.key = ""; + this.current = this.data.map((v) => ({ ...v, _$current: v })); + } + public add(val: number) { + this.current.forEach((d) => { + d._$current[this.key] += val; + }); + return this; + } + /** + * Gets back to the top level on the data + */ + public clearQuery() { + this.current = this.data.map((v) => ({ ...v, _$current: v })); + return this; + } + public delete(key: string) { + this.current = this.current.map((v, i) => { + delete v._$current[key]; + return (v = v); + }); + return this; + } + public divide(val: number) { + this.current.forEach((d) => { + d._$current[this.key] /= val; + }); + return this; + } + public equals(val: any) { + this.current = this.current.filter((v, i) => v._$current[this.key] === val); + return this; + } + /** + * Check if the data exists on every object + */ + public exists(key: string) { + return this.current.every((v) => v[key]); + } + public find(key: string, val: any) { + const res: any[] = []; + return this.current.forEach((v) => { + if (v._$current[key] === val) res.push(v); + }); + + } + public gt(val: any) { + this.current = this.current.filter((v, i) => v._$current[this.key] > val); + return this; + } + public gte(val: any) { + this.current = this.current.filter((v, i) => v._$current[this.key] >= val); + return this; + } + public limit(val: number) { + this._limit = val + 1; + return this; + } + + public lt(val: any) { + this.current = this.current.filter((v, i) => v._$current[this.key] < val); + return this; + } + + public lte(val: any) { + this.current = this.current.filter((v, i) => v._$current[this.key] <= val); + return this; + } + + public multiply(val: number) { + this.current.forEach((d) => { + d._$current[this.key] *= val; + }); + return this; + } + public push(arr: any[]) { + arr.forEach((val) => { + this.current.forEach((d) => { + if (Array.isArray(d._$current[this.key])) d._$current[this.key].push(val); + }); + }); + return this; + } + /** + * Get the raw data + */ + public raw() { + const data = this.current.map((v) => (v = v._$current)); + return data; + } + public update(val: number) { + this.current.forEach((d) => { + d._$current[this.key] = val; + }); + return this; + } + /** + * Get into a deeper object + * @param {String} key + */ + public select(key: string) { + this.current.map((v, i) => { + this.current[i]._$current = v[key]; + }); + + return this; + } + public subtract(val: number) { + this.current.forEach((d) => { + d._$current[this.key] -= val; + }); + return this; + } + /** + * Saves the queries on the data + */ + public save() { + this.current = this.current.map((v) => (v = v._$current)); + this.data = this.current; + return this; + } + /** + * Get the Latest data + */ + public toJSON() { + return this.data; + } + /** + * Get the last selected query + */ + public toValue() { + return this.current.map((v, i) => { + if (this._limit < i) return; + if (v._$current) delete v._$current; + return (v = v); + }); + } + /** + * Select a object to query next + * @param {string} key + */ + public where(key: string) { + this.key = `${key}`; + return this; + } +} diff --git a/packages/json/dist/index.d.ts b/packages/json/dist/index.d.ts new file mode 100644 index 0000000..e314015 --- /dev/null +++ b/packages/json/dist/index.d.ts @@ -0,0 +1,23 @@ +interface document { + [key: string]: string | number | Object | Array | any; +} +interface storage { + [collectioName: string]: document[]; +} +interface JsonStoreOptions { + path: string; +} +export declare class JsonStore { + private collectionName; + private storage; + constructor(options: JsonStoreOptions); + createCollectionProvider(collectionName: string, cb?: Function): Promise; + createDocProvider(data: document, cb?: Function): Promise; + deleteCollectionProvider(collectionName: string, cb?: Function): Promise; + deleteDocProvider(data: document, cb?: Function): Promise; + getCollectionProvider(collectionName: string, cb?: Function): Promise; + getCollectionsProvider(cb?: Function): Promise; + getDocProvider(data: document, cb?: Function): Promise; + updateDocProvider(refData: document, data: document, cb?: Function): Promise; +} +export {}; diff --git a/packages/json/dist/index.js b/packages/json/dist/index.js new file mode 100644 index 0000000..dd82b65 --- /dev/null +++ b/packages/json/dist/index.js @@ -0,0 +1,57 @@ +import _ from "lodash"; +import fs from "fs-extra"; +export class JsonStore { + collectionName; + storage; + constructor(options) { + this.storage = {}; + fs.ensureDirSync(options.path.replace(/.+(\.).+$/g, "")); + fs.ensureFileSync(options.path); + setInterval(() => { + fs.writeFileSync(options.path, JSON.stringify(this.storage)); + }, 1000); + } + async createCollectionProvider(collectionName, cb = () => { }) { + this.collectionName = collectionName; + this.storage[collectionName] = []; + cb(this.storage[collectionName]); + return this.storage[collectionName]; + } + async createDocProvider(data, cb = () => { }) { + this.storage[this.collectionName].push(data); + cb(data); + return data; + } + async deleteCollectionProvider(collectionName, cb = () => { }) { + delete this.storage[collectionName]; + cb(); + return; + } + async deleteDocProvider(data, cb = () => { }) { + this.storage[this.collectionName].splice(_.findIndex(this.storage[this.collectionName], data), 1); + cb(); + return; + } + async getCollectionProvider(collectionName, cb = () => { }) { + const data = this.storage[collectionName]; + cb(data); + return data; + } + async getCollectionsProvider(cb = () => { }) { + const data = this.storage; + cb(data); + return data; + } + async getDocProvider(data, cb = () => { }) { + const Data = _.find(this.storage[this.collectionName], data); + cb(Data); + return Data; + } + async updateDocProvider(refData, data, cb = () => { }) { + const docIndex = _.findIndex(this.storage[this.collectionName], refData); + const Data = _.defaultsDeep(this.storage[this.collectionName][docIndex], data); + this.storage[this.collectionName][docIndex] = Data; + cb(Data); + return Data; + } +} diff --git a/packages/json/index.ts b/packages/json/index.ts new file mode 100644 index 0000000..5b0620a --- /dev/null +++ b/packages/json/index.ts @@ -0,0 +1,67 @@ +import _ from "lodash"; +import fs from "fs-extra"; +type fn = (data: Object[] | Object) => void; +interface document { + [key: string]: string | number | Object | Array | any; +} +interface storage { + [collectioName: string]: document[]; +} +interface JsonStoreOptions { + path: string; +} +export class JsonStore { + private collectionName: string; + private storage: storage; + constructor(options: JsonStoreOptions) { + this.storage = {}; + fs.ensureDirSync(options.path.replace(/.+(\.).+$/g, "")); + fs.ensureFileSync(options.path); + setInterval(() => { + fs.writeFileSync(options.path, JSON.stringify(this.storage)); + }, 1000); + } + public async createCollectionProvider(collectionName: string, cb: Function = () => {}) { + this.collectionName = collectionName; + this.storage[collectionName] = []; + cb(this.storage[collectionName]); + return this.storage[collectionName]; + } + public async createDocProvider(data: document, cb: Function = () => {}) { + this.storage[this.collectionName].push(data); + cb(data); + return data; + } + public async deleteCollectionProvider(collectionName: string, cb: Function = () => {}) { + delete this.storage[collectionName]; + cb(); + return; + } + public async deleteDocProvider(data: document, cb: Function = () => {}) { + this.storage[this.collectionName].splice(_.findIndex(this.storage[this.collectionName], data), 1); + cb(); + return; + } + public async getCollectionProvider(collectionName: string, cb: Function = () => {}) { + const data = this.storage[collectionName]; + cb(data); + return data; + } + public async getCollectionsProvider(cb: Function = () => {}) { + const data = this.storage; + cb(data); + return data; + } + public async getDocProvider(data: document, cb: Function = () => {}) { + const Data = _.find(this.storage[this.collectionName], data); + cb(Data); + return Data; + } + public async updateDocProvider(refData: document, data: document, cb: Function = () => {}) { + const docIndex = _.findIndex(this.storage[this.collectionName], refData); + const Data = _.defaultsDeep(this.storage[this.collectionName][docIndex], data); + this.storage[this.collectionName][docIndex] = Data; + cb(Data); + return Data; + } +} diff --git a/packages/json/package.json b/packages/json/package.json new file mode 100644 index 0000000..97dc155 --- /dev/null +++ b/packages/json/package.json @@ -0,0 +1,39 @@ +{ + "name": "@quipodb/json", + "version": "1.0.0", + "type": "module", + "scripts": { + "build": "tsc index.ts -t esnext -m esnext -d --allowSyntheticDefaultImports --moduleResolution node --outDir ./dist", + "build-packages": "cd packages/core && npm run build && cd.. && cd.. && cd packages/firestore && npm run build && cd.. && cd.. && cd packages/sqlite && npm run build && cd.. && cd..", + "publish-beta-public": "npm publish --tag beta --registry=https://registry.npmjs.com --workspaces", + "publish-beta-private": "npm publish --tag beta --workspaces", + "publish-public": "npm publish --registry=https://registry.npmjs.com --workspaces", + "publish-private": "npm publish --workspaces" + }, + "author": { + "name": "Rajaneesh R", + "email": "rajaneeshr@proton.me", + "url": "https://r-rajaneesh.vercel.app" + }, + "keywords": [ + "quipodb", + "db", + "database", + "sqlite", + "ttl", + "key", + "value", + "store" + ], + "license": "ISC", + "dependencies": { + "fs-extra": "^10.1.0", + "lodash": "^4.17.21" + }, + "devDependencies": { + "@types/fs-extra": "^9.0.13", + "@types/lodash": "^4.14.186", + "@types/node": "^18.11.3", + "typescript": "^4.8.4" + } +} diff --git a/packages/steno/dist/index.d.ts b/packages/steno/dist/index.d.ts new file mode 100644 index 0000000..08221fb --- /dev/null +++ b/packages/steno/dist/index.d.ts @@ -0,0 +1,5 @@ +export declare class Writer { + #private; + constructor(filename: string); + write(data: string): Promise; +} diff --git a/packages/steno/dist/index.js b/packages/steno/dist/index.js new file mode 100644 index 0000000..b82d0f8 --- /dev/null +++ b/packages/steno/dist/index.js @@ -0,0 +1,66 @@ +import { rename, writeFile } from "node:fs/promises"; +import { basename, dirname, join } from "node:path"; +// Returns a temporary file +// Example: for /some/file will return /some/.file.tmp +function getTempFilename(file) { + return join(dirname(file), "." + basename(file) + ".tmp"); +} +export class Writer { + #filename; + #tempFilename; + #locked = false; + #prev = null; + #next = null; + #nextPromise = null; + #nextData = null; + // File is locked, add data for later + #add(data) { + // Only keep most recent data + this.#nextData = data; + // Create a singleton promise to resolve all next promises once next data is written + this.#nextPromise ||= new Promise((resolve, reject) => { + this.#next = [resolve, reject]; + }); + // Return a promise that will resolve at the same time as next promise + return new Promise((resolve, reject) => { + this.#nextPromise?.then(resolve).catch(reject); + }); + } + // File isn't locked, write data + async #write(data) { + // Lock file + this.#locked = true; + try { + // Atomic write + await writeFile(this.#tempFilename, data, "utf-8"); + await rename(this.#tempFilename, this.#filename); + // Call resolve + this.#prev?.[0](); + } + catch (err) { + // Call reject + if (err instanceof Error) { + this.#prev?.[1](err); + } + throw err; + } + finally { + // Unlock file + this.#locked = false; + this.#prev = this.#next; + this.#next = this.#nextPromise = null; + if (this.#nextData !== null) { + const nextData = this.#nextData; + this.#nextData = null; + await this.write(nextData); + } + } + } + constructor(filename) { + this.#filename = filename; + this.#tempFilename = getTempFilename(filename); + } + async write(data) { + return this.#locked ? this.#add(data) : this.#write(data); + } +} diff --git a/packages/steno/index.ts b/packages/steno/index.ts new file mode 100644 index 0000000..30ce1d8 --- /dev/null +++ b/packages/steno/index.ts @@ -0,0 +1,78 @@ +import { rename, writeFile } from "node:fs/promises"; +import { basename, dirname, join } from "node:path"; + +// Returns a temporary file +// Example: for /some/file will return /some/.file.tmp +function getTempFilename(file: string): string { + return join(dirname(file), "." + basename(file) + ".tmp"); +} + +type Resolve = () => void; +type Reject = (error: Error) => void; + +export class Writer { + #filename: string; + #tempFilename: string; + #locked = false; + #prev: [Resolve, Reject] | null = null; + #next: [Resolve, Reject] | null = null; + #nextPromise: Promise | null = null; + #nextData: string | null = null; + + // File is locked, add data for later + #add(data: string): Promise { + // Only keep most recent data + this.#nextData = data; + + // Create a singleton promise to resolve all next promises once next data is written + this.#nextPromise ||= new Promise((resolve, reject) => { + this.#next = [resolve, reject]; + }); + + // Return a promise that will resolve at the same time as next promise + return new Promise((resolve, reject) => { + this.#nextPromise?.then(resolve).catch(reject); + }); + } + + // File isn't locked, write data + async #write(data: string): Promise { + // Lock file + this.#locked = true; + try { + // Atomic write + await writeFile(this.#tempFilename, data, "utf-8"); + await rename(this.#tempFilename, this.#filename); + + // Call resolve + this.#prev?.[0](); + } catch (err) { + // Call reject + if (err instanceof Error) { + this.#prev?.[1](err); + } + throw err; + } finally { + // Unlock file + this.#locked = false; + + this.#prev = this.#next; + this.#next = this.#nextPromise = null; + + if (this.#nextData !== null) { + const nextData = this.#nextData; + this.#nextData = null; + await this.write(nextData); + } + } + } + + constructor(filename: string) { + this.#filename = filename; + this.#tempFilename = getTempFilename(filename); + } + + async write(data: string): Promise { + return this.#locked ? this.#add(data) : this.#write(data); + } +} diff --git a/packages/steno/package.json b/packages/steno/package.json new file mode 100644 index 0000000..b17fcbf --- /dev/null +++ b/packages/steno/package.json @@ -0,0 +1,24 @@ +{ + "name": "@quipodb/steno", + "version": "0.0.1-beta.0", + "type": "module", + "private":true, + "scripts": { + "build": "tsc index.ts -t esnext -m esnext -d --allowSyntheticDefaultImports --moduleResolution node --outDir ./dist", + "build-packages": "cd packages/core && npm run build && cd.. && cd.. && cd packages/firestore && npm run build && cd.. && cd.. && cd packages/sqlite && npm run build && cd.. && cd..", + "publish-beta-public": "npm publish --tag beta --registry=https://registry.npmjs.com --workspaces", + "publish-beta-private": "npm publish --tag beta --workspaces", + "publish-public": "npm publish --registry=https://registry.npmjs.com --workspaces", + "publish-private": "npm publish --workspaces" + }, + "author": { + "name": "Rajaneesh R", + "email": "rajaneeshr@proton.me", + "url": "https://r-rajaneesh.vercel.app" + }, + "license": "ISC", + "devDependencies": { + "@types/node": "^18.11.3", + "typescript": "^4.8.4" + } +}