jotdb 0.1.4 โ 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +109 -154
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,203 +1,156 @@
|
|
|
1
1
|
# JotDB
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://deploy.workers.cloudflare.com/?url=https://github.com/acoyfellow/jotdb)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
A lightweight, schema-less database built on Cloudflare Durable Objects. Think of it as Firestore's security rules, but with Zod validation built-in. Perfect for both internal and external APIs, with automatic type safety and validation.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
### 2. **Bind the Durable Object in your wrangler.toml or wrangler.json**
|
|
14
|
-
|
|
15
|
-
```toml
|
|
16
|
-
[[durable_objects.bindings]]
|
|
17
|
-
name = "JOTDB"
|
|
18
|
-
class_name = "JotDB"
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
### 3. **Register the Durable Object in your Worker**
|
|
22
|
-
|
|
23
|
-
```ts
|
|
24
|
-
import { JotDB } from 'jotdb';
|
|
25
|
-
|
|
26
|
-
export interface Env {
|
|
27
|
-
JOTDB: DurableObjectNamespace<JotDB>;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export default {
|
|
31
|
-
async fetch(request: Request, env: Env) {
|
|
32
|
-
// Get a stub for your JotDB instance
|
|
33
|
-
const id = env.JOTDB.idFromName("my-db");
|
|
34
|
-
const db = env.JOTDB.get(id);
|
|
7
|
+
> **Cloudflare Products**: JotDB works with any Cloudflare product that supports Durable Objects:
|
|
8
|
+
> - Cloudflare Workers
|
|
9
|
+
> - Cloudflare Pages (with Functions)
|
|
10
|
+
> - Cloudflare Workflows
|
|
11
|
+
> - Cloudflare Queues
|
|
12
|
+
> - Cloudflare Cron Triggers
|
|
35
13
|
|
|
36
|
-
|
|
37
|
-
await db.set("key", "value");
|
|
38
|
-
const value = await db.get("key");
|
|
14
|
+
## Why JotDB?
|
|
39
15
|
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
```
|
|
16
|
+
JotDB combines the best of both worlds: the simplicity of NoSQL with the safety of schema validation. Here's what makes it special:
|
|
44
17
|
|
|
45
|
-
|
|
18
|
+
- **Built-in Type Safety**: Automatic Zod validation ensures your data is always in the right shape
|
|
19
|
+
- **Edge-Native**: Runs directly on Cloudflare's edge network, with sub-millisecond latency
|
|
20
|
+
- **RPC-First**: Direct method calls instead of HTTP endpoints (though you can easily wrap it in HTTP)
|
|
21
|
+
- **Durable Storage**: Built on Durable Objects for reliable, consistent storage
|
|
22
|
+
- **Zero Setup**: No database configuration, no connection strings, just instantiate and go
|
|
23
|
+
- **Perfect for APIs**: Use it as an internal database or wrap it with auth for external APIs
|
|
24
|
+
- **Real-time Ready**: Durable Objects provide strong consistency guarantees
|
|
46
25
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
26
|
+
Perfect for:
|
|
27
|
+
- Quick prototypes that need data validation
|
|
28
|
+
- Small to medium applications that need reliable storage
|
|
29
|
+
- Serverless environments where you want type safety
|
|
30
|
+
- Real-time data storage with strong consistency
|
|
31
|
+
- Collaborative applications that need data validation
|
|
32
|
+
- APIs that need both flexibility and safety
|
|
52
33
|
|
|
53
|
-
|
|
34
|
+
## Design Patterns
|
|
54
35
|
|
|
55
|
-
|
|
36
|
+
JotDB uses Cloudflare Durable Objects under the hood, which means you can organize your data in several ways:
|
|
56
37
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
38
|
+
1. **Global Store**: Use a single instance for your entire application
|
|
39
|
+
```typescript
|
|
40
|
+
const db = env.JOTDB.get(env.JOTDB.idFromName("global"));
|
|
41
|
+
```
|
|
61
42
|
|
|
62
|
-
|
|
43
|
+
2. **Per-User Store**: Create a separate instance for each user
|
|
44
|
+
```typescript
|
|
45
|
+
const userDb = env.JOTDB.get(env.JOTDB.idFromName(`user:${userId}`));
|
|
46
|
+
```
|
|
63
47
|
|
|
64
|
-
|
|
48
|
+
3. **Per-Event Store**: Create temporary stores for events or sessions
|
|
49
|
+
```typescript
|
|
50
|
+
const eventDb = env.JOTDB.get(env.JOTDB.idFromName(`event:${eventId}`));
|
|
51
|
+
```
|
|
65
52
|
|
|
66
|
-
|
|
67
|
-
import { JotDB } from 'jotdb';
|
|
53
|
+
Each instance is isolated and can have its own schema and options. This follows the Actor Model pattern, where each instance is an independent actor that manages its own state.
|
|
68
54
|
|
|
69
|
-
|
|
70
|
-
JOTDB: DurableObjectNamespace<JotDB>;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export default {
|
|
74
|
-
async fetch(request: Request, env: Env) {
|
|
75
|
-
const id = env.JOTDB.idFromName("my-db");
|
|
76
|
-
const db = env.JOTDB.get(id);
|
|
77
|
-
|
|
78
|
-
await db.setSchema({
|
|
79
|
-
name: "string",
|
|
80
|
-
age: "number",
|
|
81
|
-
email: "email"
|
|
82
|
-
});
|
|
55
|
+
## Installation
|
|
83
56
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
email: "alice@example.com"
|
|
88
|
-
});
|
|
57
|
+
```bash
|
|
58
|
+
# Using npm
|
|
59
|
+
npm install jotdb
|
|
89
60
|
|
|
90
|
-
|
|
61
|
+
# Using yarn
|
|
62
|
+
yarn add jotdb
|
|
91
63
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
};
|
|
64
|
+
# Using pnpm
|
|
65
|
+
pnpm add jotdb
|
|
97
66
|
```
|
|
98
67
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
## Installation
|
|
113
|
-
|
|
114
|
-
```bash
|
|
115
|
-
npm install jotdb
|
|
116
|
-
# or
|
|
117
|
-
bun add jotdb
|
|
68
|
+
### Configure wrangler.jsonc
|
|
69
|
+
|
|
70
|
+
```jsonc
|
|
71
|
+
{
|
|
72
|
+
"durable_objects": {
|
|
73
|
+
"bindings": [
|
|
74
|
+
{
|
|
75
|
+
"name": "JOTDB",
|
|
76
|
+
"class_name": "JotDB"
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
}
|
|
118
81
|
```
|
|
119
82
|
|
|
120
|
-
##
|
|
83
|
+
## Full Example
|
|
121
84
|
|
|
122
85
|
```typescript
|
|
123
86
|
import { JotDB } from 'jotdb';
|
|
124
87
|
|
|
125
|
-
// In your Worker
|
|
126
88
|
export interface Env {
|
|
127
89
|
JOTDB: DurableObjectNamespace;
|
|
128
90
|
}
|
|
129
91
|
|
|
130
92
|
export default {
|
|
131
93
|
async fetch(request: Request, env: Env) {
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
//
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
age: "number",
|
|
145
|
-
email: "email"
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
// Set multiple values
|
|
149
|
-
await db.setAll({
|
|
150
|
-
name: "John",
|
|
151
|
-
age: 30,
|
|
152
|
-
email: "john@example.com"
|
|
94
|
+
// Initialize the database
|
|
95
|
+
const jotId = env.JOTDB.idFromName("my-db");
|
|
96
|
+
const db = env.JOTDB.get(jotId);
|
|
97
|
+
|
|
98
|
+
// Example operations
|
|
99
|
+
await db.set("user:123", { name: "John", age: 30 });
|
|
100
|
+
const user = await db.get("user:123");
|
|
101
|
+
await db.delete("user:123");
|
|
102
|
+
|
|
103
|
+
// Return the result
|
|
104
|
+
return new Response(JSON.stringify({ user }), {
|
|
105
|
+
headers: { 'Content-Type': 'application/json' }
|
|
153
106
|
});
|
|
154
107
|
}
|
|
155
108
|
};
|
|
156
109
|
```
|
|
157
110
|
|
|
158
|
-
## API
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
111
|
+
## API Reference
|
|
112
|
+
|
|
113
|
+
| Method | Description | Parameters | Returns |
|
|
114
|
+
|--------|-------------|------------|---------|
|
|
115
|
+
| `set(key, value)` | Store a value | `key: string`, `value: any` | `Promise<void>` |
|
|
116
|
+
| `get(key)` | Retrieve a value | `key: string` | `Promise<any>` |
|
|
117
|
+
| `delete(key)` | Remove a value | `key: string` | `Promise<void>` |
|
|
118
|
+
| `clear()` | Remove all values | none | `Promise<void>` |
|
|
119
|
+
| `keys()` | Get all keys | none | `Promise<string[]>` |
|
|
120
|
+
| `has(key)` | Check if key exists | `key: string` | `Promise<boolean>` |
|
|
121
|
+
| `getAll()` | Get all data | none | `Promise<Record<string, unknown> \| unknown[]>` |
|
|
122
|
+
| `setAll(objOrArr)` | Set all data at once | `objOrArr: Record<string, unknown> \| unknown[]` | `Promise<void>` |
|
|
123
|
+
| `push(item)` | Add item to array | `item: unknown` | `Promise<void>` |
|
|
124
|
+
| `getSchema()` | Get current schema | none | `Promise<SchemaDefinition>` |
|
|
125
|
+
| `setSchema(schema)` | Set data schema | `schema: SchemaDefinition` | `Promise<void>` |
|
|
126
|
+
| `getOptions()` | Get current options | none | `Promise<JotDBOptions>` |
|
|
127
|
+
| `setOptions(opts)` | Set database options | `opts: Partial<JotDBOptions>` | `Promise<void>` |
|
|
128
|
+
| `getAuditLog()` | Get audit log entries | none | `Promise<AuditLogEntry[]>` |
|
|
129
|
+
| `clearAuditLog()` | Clear audit log | none | `Promise<void>` |
|
|
130
|
+
|
|
131
|
+
### Options
|
|
178
132
|
|
|
179
133
|
```typescript
|
|
180
|
-
type SchemaType = "string" | "number" | "boolean" | "email" | "array" | "object" | "any";
|
|
181
|
-
type SchemaDefinition = Record<string, SchemaType>;
|
|
182
|
-
|
|
183
134
|
interface JotDBOptions {
|
|
184
|
-
autoStrip: boolean;
|
|
185
|
-
readOnly: boolean;
|
|
135
|
+
autoStrip: boolean; // Automatically strip unknown fields
|
|
136
|
+
readOnly: boolean; // Enable read-only mode
|
|
186
137
|
}
|
|
138
|
+
```
|
|
187
139
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
140
|
+
### Schema Types
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
type SchemaType = "string" | "number" | "boolean" | "email" | "array" | "object" | "any";
|
|
193
144
|
```
|
|
194
145
|
|
|
195
146
|
## License
|
|
196
147
|
|
|
197
|
-
MIT
|
|
148
|
+
MIT License - feel free to use this in your own projects!
|
|
198
149
|
|
|
199
150
|
## Contributing
|
|
200
151
|
|
|
152
|
+
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
|
|
153
|
+
|
|
201
154
|
1. Fork the repository
|
|
202
155
|
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
203
156
|
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
@@ -206,6 +159,8 @@ MIT
|
|
|
206
159
|
|
|
207
160
|
## Testing
|
|
208
161
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
162
|
+
Currently, testing is done manually in production. We're working on adding a comprehensive test suite. For now, you can test the functionality by:
|
|
163
|
+
|
|
164
|
+
1. Deploying to Cloudflare Workers
|
|
165
|
+
2. Using the example endpoints
|
|
166
|
+
3. Verifying data persistence
|