Skip to content

Commit dae6df0

Browse files
committed
feat : add cloudflare workers support
1 parent 8356849 commit dae6df0

File tree

10 files changed

+265
-17
lines changed

10 files changed

+265
-17
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,6 @@ public/
136136
package-lock.json
137137
data/
138138
docs/
139-
kvstorage/
139+
kvstorage/
140+
.dev.vars
141+
.wrangler/

README.md

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Create data storage that uses a simple key-value method for Node, Browser, Deno,
1515

1616
## Installation
1717

18-
NPM (node, browser, deno, bun)
18+
NPM (node, browser, deno, bun, cloudflare)
1919
```javascript
2020
npm install kv-storage
2121
```
@@ -32,7 +32,7 @@ NPM
3232
//Node & Bun CommonJS import style
3333
const {KVStorage} = require('kv-storage')
3434

35-
//Node, Browser & Bun ES Modules import style
35+
//Node, Browser, Bun & Cloudflare ES Modules import style
3636
import {KVStorage} from 'kv-storage'
3737

3838
//Deno import style
@@ -42,6 +42,12 @@ const db = await KVStorage({
4242
runtime:'node', //node | browser | deno | bun
4343
storageName:'storage'
4444
})
45+
46+
const db = await KVStorage({
47+
runtime:'cloudflare',
48+
storageName:'storage',
49+
databaseBindings:env.D1 //Cloudflare D1 database bindings env
50+
})
4551
```
4652
CDN
4753
```javascript
@@ -147,6 +153,36 @@ void async function main() {
147153
}()
148154
```
149155

156+
```javascript
157+
//Cloudflare workers example
158+
import {KVStorage} from 'kv-storage'
159+
160+
export default {
161+
async fetch(request: Request, env: Env): Promise<Response> {
162+
163+
const db = await KVStorage({
164+
runtime:'cloudflare',
165+
storageName:'storage',
166+
databaseBindings:env.D1
167+
})
168+
169+
let data = []
170+
171+
data.push(await db.put('key','value'))
172+
173+
data.push(await db.get('key'))
174+
175+
data.push(await db.has('key'))
176+
177+
data.push(await db.list())
178+
179+
data.push(await db.delete('key'))
180+
181+
return new Response(JSON.stringify(data))
182+
}
183+
}
184+
```
185+
150186
## API Reference
151187

152188
### Documentation
@@ -158,20 +194,22 @@ void async function main() {
158194
```javascript
159195
await init({
160196
runtime?:string,
161-
storageName?:string
197+
storageName?:string,
198+
databaseBindings?:any //Cloudflare only
162199
})
163200
```
164201
```
165202
runtime = Javascript runtime
166203
storageName = Alphanumeric storage name
204+
databaseBindings = Cloudflare D1 database bindings env
167205
```
168206
Supported runtime :
169207
- [x] `node`
170208
- [x] `deno` need `--allow-read --allow-write`
171209
- [x] `browser` use IndexedDB
172210
- [x] `bun`
173-
- [ ] `cloudflare-workers`
174-
- [ ] `memory`
211+
- [x] `cloudflare-workers` use D1 Database [docs](https://developers.cloudflare.com/d1/get-started/#4-bind-your-worker-to-your-d1-database)
212+
175213
### Write key-value pairs
176214

177215
```javascript

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "kv-storage",
3-
"version": "0.0.4",
3+
"version": "0.0.5",
44
"description": "Create data storage that uses a simple key-value method for Node, Browser, Deno, Bun, Cloudflare Workers",
55
"main": "dist/cjs/kv-storage.js",
66
"module": "dist/mjs/kv-storage.js",
@@ -21,6 +21,7 @@
2121
"dev-deno": "nodemon -e js,ts --watch src --watch test --exec \"deno run --allow-read --allow-write --unstable-sloppy-imports test/server-deno.ts\"",
2222
"dev": "nodemon -e js,ts --watch src --watch test --exec \"npm run build && npm start\"",
2323
"dev-mjs": "nodemon -e js,ts --watch src --watch test --exec \"npm run start-mjs\"",
24+
"dev-cf": "nodemon -e js,ts,html --watch src --watch test --exec \"npm run prepare-public && tsc -p tsconfig-browser.json && wrangler dev --env dev\"",
2425
"build-win": "npm run prepare-build-win && tsc -p tsconfig-mjs.json && tsc -p tsconfig-cjs.json && tsc -p tsconfig-umd.json && rollup -c public/config/rollup.config.umd.js && echo {\"type\": \"commonjs\"}>dist\\cjs\\package.json && echo {\"type\": \"module\"}>dist\\mjs\\package.json",
2526
"prepare-build": "if exist .\\dist (echo ok) && mkdir dist && del /S /Q .\\dist\\*",
2627
"prepare-build-win": "if not exist .\\dist (mkdir dist) else (rmdir /S /Q .\\dist\\)",
@@ -69,6 +70,7 @@
6970
"ts-node": "^10.9.2",
7071
"tslib": "^2.6.2",
7172
"typedoc": "^0.25.7",
72-
"typescript": "^5.3.3"
73+
"typescript": "^5.3.3",
74+
"wrangler": "^3.26.0"
7375
}
7476
}

src/kv-storage.ts

Lines changed: 162 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
export async function KVStorage({
22
runtime = 'node',
33
databaseName = 'kvstorage',
4-
storageName = 'storage'
4+
storageName = 'storage',
5+
databaseBindings
56
}:{
67
runtime?:string,
78
storageName?:string,
8-
databaseName?:string
9+
databaseName?:string,
10+
databaseBindings?:any
911
}): Promise<any> {
1012

1113
function isAlphanumeric(str:string) {
@@ -17,27 +19,32 @@ export async function KVStorage({
1719
}
1820

1921
if(!isAlphanumeric(runtime))showError('Runtime must be Alphanumeric')
22+
if(!isAlphanumeric(databaseName))showError('storageName must be Alphanumeric')
2023
if(!isAlphanumeric(storageName))showError('storageName must be Alphanumeric')
2124

2225
switch(runtime.toLowerCase()){
2326
case 'node':
24-
const runnode = await import('./node-kv-storage')
27+
let nodepkg = './node-kv-storage'
28+
if(typeof caches !== "undefined" && typeof global === "undefined" && typeof window === "undefined")nodepkg = ''
29+
const runnode = await import(nodepkg)
2530
const dbnode = await runnode.NodeKVStorage.init({
2631
dataDirName:databaseName,
2732
storageName
2833
})
2934
return dbnode
3035
break
3136
case 'deno':
32-
const rundeno = await import('./deno-kv-storage')
37+
let denopkg = './deno-kv-storage'
38+
const rundeno = await import(denopkg)
3339
const dbdeno = await rundeno.DenoKVStorage.init({
3440
dataDirName:databaseName,
3541
storageName
3642
})
3743
return dbdeno
3844
break
3945
case 'bun':
40-
const runbun = await import('./bun-kv-storage')
46+
let bunpkg = './bun-kv-storage'
47+
const runbun = await import(bunpkg)
4148
const dbbun = await runbun.BunKVStorage.init({
4249
dataDirName:databaseName,
4350
storageName
@@ -46,17 +53,165 @@ export async function KVStorage({
4653
break
4754
case 'browser':
4855
let browserpkg = './browser-kv-storage'
49-
if(window)browserpkg = './browser-kv-storage.js'
56+
if(typeof window !== "undefined" && typeof window.document !== "undefined")browserpkg = './browser-kv-storage.js'
5057
const runbrowser = await import(browserpkg)
51-
//const runbrowser = await import('./browser-kv-storage')
5258
const dbbrowser = await runbrowser.BrowserKVStorage.init({
5359
databaseName,
5460
storageName
5561
})
5662
return dbbrowser
5763
break
64+
case 'cloudflare':
65+
//let cloudflarepkg = 'server.js'
66+
//if(typeof caches !== "undefined" && typeof global === "undefined" && typeof window === "undefined")cloudflarepkg = './cloudflare-kv-storage'
67+
//const runcloudflare = await import(cloudflarepkg)
68+
const dbcloudflare = await CloudflareKVStorage.init({
69+
databaseBindings,
70+
storageName
71+
})
72+
return dbcloudflare
73+
break
5874
default:
5975
showError('Runtime unknown')
6076
}
6177

6278
}
79+
80+
class CloudflareKVStorage{
81+
82+
private _storageName:string
83+
private _databaseBindings:any
84+
85+
private constructor({
86+
databaseBindings,
87+
storageName
88+
}:{
89+
databaseBindings:any,
90+
storageName:string
91+
}){
92+
93+
this._databaseBindings = databaseBindings
94+
this._storageName = storageName
95+
}
96+
97+
private isAlphanumeric(str:string) {
98+
return /^[a-zA-Z0-9]+$/.test(str);
99+
}
100+
101+
private showError(msg:string = 'Error'){
102+
throw new Error(msg)
103+
}
104+
105+
public static async init({
106+
databaseBindings,
107+
storageName,
108+
}:{
109+
databaseBindings:any,
110+
storageName:string
111+
}): Promise<CloudflareKVStorage>{
112+
113+
function isAlphanumeric(str:string) {
114+
return /^[a-zA-Z0-9]+$/.test(str);
115+
}
116+
117+
function showError(msg:string = 'Error'){
118+
throw new Error(msg)
119+
}
120+
121+
if(!isAlphanumeric(storageName))showError('storageName must be Alphanumeric')
122+
123+
const stmt = databaseBindings.prepare('CREATE TABLE IF NOT EXISTS '+storageName+' (key text NOT NULL PRIMARY KEY,value text NOT NULL)')
124+
125+
const values = await stmt.run()
126+
127+
return new CloudflareKVStorage({databaseBindings,storageName})
128+
}
129+
130+
public async put(key:string,value:string){
131+
132+
if(!this.isAlphanumeric(key))this.showError('Key must be Alphanumeric')
133+
134+
const stmt = this._databaseBindings.prepare('SELECT value FROM '+this._storageName+' WHERE key = ?1').bind(key);
135+
const values = await stmt.first()
136+
if(values == null){
137+
const stmt = this._databaseBindings.prepare('INSERT INTO '+this._storageName+' (key,value) VALUES (?1,?2)').bind(key,value);
138+
const values = await stmt.run()
139+
return values.succes
140+
}else{
141+
const stmt = this._databaseBindings.prepare('UPDATE '+this._storageName+' SET value = ?2 WHERE key = ?1').bind(key,value);
142+
const values = await stmt.run()
143+
return values.success
144+
}
145+
146+
}
147+
148+
public async get(key:string){
149+
150+
if(!this.isAlphanumeric(key))this.showError('Key must be Alphanumeric')
151+
152+
const stmt = this._databaseBindings.prepare('SELECT value FROM '+this._storageName+' WHERE key = ?1').bind(key);
153+
const values = await stmt.first();
154+
let output
155+
if(values == null){
156+
output = false
157+
} else {
158+
output = values.value
159+
}
160+
return output
161+
162+
}
163+
164+
public async delete(key:string){
165+
166+
if(!this.isAlphanumeric(key))this.showError('Key must be Alphanumeric')
167+
168+
const stmt = this._databaseBindings.prepare('DELETE FROM '+this._storageName+' WHERE key = ?1').bind(key);
169+
const values = await stmt.first();
170+
let output
171+
if(values == null){
172+
output = false
173+
} else {
174+
output = true
175+
}
176+
return output
177+
178+
}
179+
180+
public async has(key:string){
181+
182+
if(!this.isAlphanumeric(key))this.showError('Key must be Alphanumeric')
183+
184+
const stmt = this._databaseBindings.prepare('SELECT value FROM '+this._storageName+' WHERE key = ?1').bind(key);
185+
const values = await stmt.first();
186+
let output
187+
if(values == null){
188+
output = false
189+
} else {
190+
output = true
191+
}
192+
return output
193+
194+
}
195+
196+
public async list(){
197+
198+
const stmt = this._databaseBindings.prepare('SELECT key FROM '+this._storageName).bind();
199+
const values = await stmt.all();
200+
let output
201+
if(values.success){
202+
let keys:Array<string> = []
203+
values.results.forEach((obj:any)=>{
204+
keys.push(obj.key)
205+
})
206+
let result = {
207+
keys:keys,
208+
complete:true
209+
}
210+
211+
output = result
212+
} else {
213+
output = false
214+
}
215+
return output
216+
}
217+
}

test/server-deno.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ void async function main() {
1414
console.log(await db.put('yes1','no1'))
1515
console.log(await db.list())
1616
console.log(await db.has('key'))
17+
console.log(typeof module)
1718
}()

test/server.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ void async function main() {
1414
console.log(await db.put('yes1','no1'))
1515
console.log(await db.list())
1616
console.log(await db.has('key'))
17+
console.log(typeof window)
18+
if (typeof window !== "undefined" && typeof window.document !== "undefined") {
19+
// browser
20+
}
1721
}()
1822

1923
/*const {KVStorage} = require('./../src/kv-storage')

test/test-cf/server-cf.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import {KVStorage} from './../../public/src/kv-storage'
2+
3+
export default {
4+
async fetch(request: Request, env: Env): Promise<Response> {
5+
6+
const db = await KVStorage({
7+
runtime:'cloudflare',
8+
storageName:'storage',
9+
databaseBindings:env.D1
10+
})
11+
12+
let data = []
13+
14+
data.push(await db.put('yes','no'))
15+
16+
data.push(await db.get('yes'))
17+
18+
data.push(await db.has('yes'))
19+
20+
data.push(await db.put('yes1','no1'))
21+
22+
data.push(await db.list())
23+
24+
data.push(await db.delete('yes'))
25+
26+
data.push(await db.list())
27+
28+
return new Response(JSON.stringify(data, null, 2))
29+
}
30+
}

0 commit comments

Comments
 (0)