Skip to content

Commit f87e9ac

Browse files
committed
feat : initial version support for node runtime
1 parent d5bffac commit f87e9ac

File tree

10 files changed

+433
-0
lines changed

10 files changed

+433
-0
lines changed

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,11 @@ dist
128128
.yarn/build-state.yml
129129
.yarn/install-state.gz
130130
.pnp.*
131+
132+
#hook
133+
test/cjs/node_modules/
134+
test/mjs/node_modules/
135+
public/
136+
package-lock.json
137+
data/
138+
docs/

README.md

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,130 @@
11
# kv-storage
22
Create data storage that uses a simple key-value method for Node, Browser, Deno, Bun, Cloudflare Workers
3+
4+
## Features
5+
6+
* ✅ 0 Dependencies
7+
* ✅ NoSQL Database
8+
* ✅ Lightwight
9+
10+
## Demo
11+
12+
[https://codesandbox.io/p/devbox/simple-kv-storage-pzr9ld](https://codesandbox.io/p/devbox/simple-kv-storage-pzr9ld)
13+
14+
## Installation
15+
16+
```javascript
17+
npm install kv-storage
18+
```
19+
20+
## Initialization
21+
22+
```javascript
23+
//ES Modules import style
24+
import {KVStorage} from 'kv-storage'
25+
26+
or
27+
28+
//CommonJS import style
29+
const {KVStorage} = require('kv-storage')
30+
31+
const db = await KVStorage.init({
32+
runtime:'node',
33+
storageName:'storage'
34+
})
35+
```
36+
37+
## Example Usage
38+
39+
```javascript
40+
import {KVStorage} from 'kv-storage'
41+
42+
void async function main() {
43+
const db = await KVStorage.init({
44+
storageName:'storage'
45+
})
46+
47+
console.log(await db.put('key','value'))
48+
console.log(await db.get('key'))
49+
console.log(await db.list())
50+
console.log(await db.delete('key'))
51+
}()
52+
```
53+
54+
```javascript
55+
const {KVStorage} = require('kv-storage')
56+
57+
void async function main() {
58+
const db = await KVStorage.init({
59+
storageName:'storage'
60+
})
61+
62+
console.log(await db.put('key','value'))
63+
console.log(await db.get('key'))
64+
console.log(await db.list())
65+
console.log(await db.delete('key'))
66+
}()
67+
```
68+
69+
## API Reference
70+
71+
### Init Parameters
72+
73+
```javascript
74+
await init({
75+
runtime?:string,
76+
storageName?:string
77+
})
78+
```
79+
```
80+
runtime = Javascript runtime
81+
storageName = Alphanumeric name of storage
82+
```
83+
Supported runtime :
84+
- [x] `node`
85+
- [ ] `browser`
86+
- [ ] `deno`
87+
- [ ] `bun`
88+
- [ ] `cloudflare-workers`
89+
- [ ] `memory`
90+
### Write key-value pairs
91+
92+
```javascript
93+
await put(key:string,value:string)
94+
```
95+
The put() method returns a Promise that you should await on to verify a successful update which resolves with a boolean :
96+
* true = Update successful
97+
* false = Update failed
98+
### Read key-value pairs
99+
100+
```javascript
101+
await get(key:string)
102+
```
103+
The get() method returns a promise you can await on to get the value which resolves with:
104+
* null = The key is not found
105+
* data = Get the value successful
106+
* false = Get the value failed
107+
108+
### List keys
109+
110+
```javascript
111+
await list()
112+
```
113+
Use a list operation to view all the keys that live in a given storage, return a promise which resolves with an object consist of:
114+
* keys = Array of keys
115+
* complete = True if operation complete
116+
117+
### Delete key-value pairs
118+
119+
```javascript
120+
await delete(key:string)
121+
```
122+
123+
To delete a key-value pair, call the delete() method, return a promise which resolves with:
124+
* null = The key is not found
125+
* true = Delete successful
126+
* false = Delete failed
127+
128+
## License
129+
130+
MIT

package.json

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
{
2+
"name": "kv-storage",
3+
"version": "0.0.1",
4+
"description": "Create data storage that uses a simple key-value method for Node, Browser, Deno, Bun, Cloudflare Workers",
5+
"main": "dist/cjs/kv-storage.js",
6+
"module": "dist/mjs/kv-storage.js",
7+
"exports": {
8+
".": {
9+
"import": "./dist/mjs/kv-storage.js",
10+
"require": "./dist/cjs/kv-storage.js"
11+
}
12+
},
13+
"scripts": {
14+
"test": "echo \"Error: no test specified\" && exit 1",
15+
"build": "tsc",
16+
"start": "node public/test/server.js",
17+
"start-mjs": "npm run build && node public/test/server-mjs.js",
18+
"dev-ts": "nodemon -e js,ts --watch src --watch test --exec \"ts-node test/server\"",
19+
"dev": "nodemon -e js,ts --watch src --watch test --exec \"npm run build && npm start\"",
20+
"dev-mjs": "nodemon -e js,ts --watch src --watch test --exec \"npm run start-mjs\"",
21+
"build-win": "npm run prepare-build-win && tsc -p tsconfig-mjs.json && tsc -p tsconfig-cjs.json && echo {\"type\": \"commonjs\"}>dist\\cjs\\package.json && echo {\"type\": \"module\"}>dist\\mjs\\package.json",
22+
"prepare-build": "if exist .\\dist (echo ok) && mkdir dist && del /S /Q .\\dist\\*",
23+
"prepare-build-win": "if not exist .\\dist (mkdir dist) else (rmdir /S /Q .\\dist\\)"
24+
},
25+
"files": [
26+
"dist/",
27+
"LICENSE"
28+
],
29+
"repository": {
30+
"type": "git",
31+
"url": "git+https://github.com/nuzulul/kv-storage.git"
32+
},
33+
"keywords": [
34+
"nodejs",
35+
"nosql",
36+
"data-storage",
37+
"data-store",
38+
"node-js",
39+
"nosql-database",
40+
"file-storage",
41+
"kv-storage",
42+
"key-value-storage",
43+
"node-key-value-storage",
44+
"node-kv-storage",
45+
"browser-kv-storage",
46+
"deno-kv-storage",
47+
"bun-kv-storage",
48+
"cloudflare-workers"
49+
],
50+
"author": "Nuzulul Zulkarnain",
51+
"license": "MIT",
52+
"bugs": {
53+
"url": "https://github.com/nuzulul/kv-storage/issues"
54+
},
55+
"homepage": "https://github.com/nuzulul/kv-storage#readme",
56+
"devDependencies": {
57+
"nodemon": "^3.0.3",
58+
"ts-node": "^10.9.2",
59+
"typedoc": "^0.25.7",
60+
"typescript": "^5.3.3"
61+
}
62+
}

src/kv-storage.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
export async function KVStorage({
2+
runtime = 'node',
3+
storageName = 'storage'
4+
}:{
5+
runtime?:string,
6+
storageName?:string
7+
}): Promise<any> {
8+
9+
function isAlphanumeric(str:string) {
10+
return /^[a-zA-Z0-9]+$/.test(str);
11+
}
12+
13+
function showError(msg:string = 'Error'){
14+
throw new Error(msg)
15+
}
16+
17+
if(!isAlphanumeric(runtime))showError('Runtime must be Alphanumeric')
18+
if(!isAlphanumeric(storageName))showError('storageName must be Alphanumeric')
19+
20+
switch(runtime.toLowerCase()){
21+
case 'node':
22+
const runtime = await import('./node-kv-storage')
23+
const db = await runtime.NodeKVStorage.init({
24+
storageName
25+
})
26+
return db
27+
break
28+
case 'browser':
29+
break
30+
default:
31+
showError('Runtime unknown')
32+
}
33+
34+
}

src/node-kv-storage.ts

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import fs from 'fs'
2+
3+
export class NodeKVStorage{
4+
5+
_storageDir:string
6+
7+
private constructor({
8+
storageDir
9+
}:{
10+
11+
storageDir:string
12+
}){
13+
14+
this._storageDir = storageDir
15+
}
16+
17+
private isAlphanumeric(str:string) {
18+
return /^[a-zA-Z0-9]+$/.test(str);
19+
}
20+
21+
private showError(msg:string = 'Error'){
22+
throw new Error(msg)
23+
}
24+
25+
public static async init({
26+
dataDirName = "data",
27+
storageName
28+
}:{
29+
dataDirName?:string,
30+
storageName:string
31+
}): Promise<NodeKVStorage>{
32+
33+
function isAlphanumeric(str:string) {
34+
return /^[a-zA-Z0-9]+$/.test(str);
35+
}
36+
37+
function showError(msg:string = 'Error'){
38+
throw new Error(msg)
39+
}
40+
41+
async function makeDir(dir:string): Promise<boolean>{
42+
return new Promise((resolve) => {
43+
fs.stat(dir, (err)=>{
44+
if (err) {
45+
fs.mkdir(dir, { recursive: true }, (err,path) => {
46+
if (err) {resolve(false)}else{resolve(true)}
47+
})
48+
}else{resolve(true)}
49+
})
50+
})
51+
}
52+
53+
if(!isAlphanumeric(dataDirName))showError('dataDirName must be Alphanumeric')
54+
if(!isAlphanumeric(storageName))showError('storageName must be Alphanumeric')
55+
let _dataDirName = "./"+dataDirName
56+
let storageDir = _dataDirName+'/'+storageName
57+
await makeDir(storageDir)
58+
return new NodeKVStorage({storageDir})
59+
}
60+
61+
62+
private async makeDirSync(dir:string){
63+
if (!fs.existsSync(dir)) {
64+
fs.mkdirSync(dir, {
65+
recursive: true
66+
})
67+
}
68+
}
69+
70+
private async makeDir(dir:string): Promise<boolean>{
71+
return new Promise((resolve) => {
72+
fs.stat(dir, (err)=>{
73+
if (err) {
74+
fs.mkdir(dir, { recursive: true }, (err,path) => {
75+
if (err) {resolve(false)}else{resolve(true)}
76+
})
77+
}else{resolve(true)}
78+
})
79+
})
80+
}
81+
82+
public async put(key:string,value:string){
83+
return new Promise((resolve) => {
84+
if(!this.isAlphanumeric(key))this.showError('Key must be Alphanumeric')
85+
const keyFilePath = this._storageDir+'/'+key
86+
fs.writeFile(keyFilePath, value,(err)=>{
87+
if (err) {resolve(false)}else{resolve(true)}
88+
});
89+
})
90+
}
91+
92+
public async get(key:string){
93+
return new Promise((resolve) => {
94+
if(!this.isAlphanumeric(key))this.showError('Key must be Alphanumeric')
95+
const keyFilePath = this._storageDir+'/'+key
96+
fs.stat(keyFilePath, (err)=>{
97+
if (err) {resolve(null)}else{
98+
fs.readFile(keyFilePath,(err,data)=>{
99+
if (err) {resolve(false)}else{resolve(data.toString('utf8', 0, data.length))}
100+
});
101+
}
102+
})
103+
})
104+
}
105+
106+
public async delete(key:string){
107+
return new Promise((resolve) => {
108+
if(!this.isAlphanumeric(key))this.showError('Key must be Alphanumeric')
109+
const keyFilePath = this._storageDir+'/'+key
110+
fs.stat(keyFilePath, (err)=>{
111+
if (err) {resolve(null)}else{
112+
fs.unlink(keyFilePath,(err)=>{
113+
if (err) {resolve(false)}else{resolve(true)}
114+
});
115+
}
116+
})
117+
})
118+
}
119+
120+
public async list(){
121+
return new Promise((resolve) => {
122+
fs.readdir(this._storageDir,(err,files)=>{
123+
if (err) {throw err}else{
124+
let result = {
125+
keys:files,
126+
complete:true
127+
}
128+
resolve(result)
129+
}
130+
});
131+
})
132+
}
133+
}

test/server.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import {KVStorage} from './../src/kv-storage'
2+
3+
void async function main() {
4+
5+
const db = await KVStorage({
6+
runtime:'node',
7+
storageName:'storage'
8+
})
9+
10+
console.log(await db.put('yes','no'))
11+
console.log(await db.get('yes'))
12+
console.log(await db.delete('yes'))
13+
console.log(await db.get('yes'))
14+
console.log(await db.put('yes1','no1'))
15+
console.log(await db.list())
16+
}()

0 commit comments

Comments
 (0)