jotdb 0.1.0
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/LICENSE +21 -0
- package/README.md +211 -0
- package/bun.lock +217 -0
- package/jotdb.tests.ts +63 -0
- package/package.json +17 -0
- package/src/index.ts +331 -0
- package/tsconfig.json +30 -0
- package/wrangler.jsonc +30 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 JotDB
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# JotDB
|
|
2
|
+
|
|
3
|
+
## 🚀 Quick Start: Using JotDB in Your Cloudflare Worker
|
|
4
|
+
|
|
5
|
+
### 1. **Install JotDB**
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add jotdb
|
|
9
|
+
# or
|
|
10
|
+
npm install jotdb
|
|
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);
|
|
35
|
+
|
|
36
|
+
// Use RPC (recommended, requires extends DurableObject)
|
|
37
|
+
await db.set("key", "value");
|
|
38
|
+
const value = await db.get("key");
|
|
39
|
+
|
|
40
|
+
return new Response(`Value: ${value}`);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 4. **Deploy or run locally**
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
wrangler dev
|
|
49
|
+
# or
|
|
50
|
+
wrangler deploy
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 📝 Notes
|
|
56
|
+
|
|
57
|
+
- **RPC support:** JotDB uses Cloudflare's new JavaScript-native RPC. You can call methods directly on the stub (e.g., `db.set(...)`, `db.get(...)`).
|
|
58
|
+
- **No fetch needed:** You do not need to use HTTP fetch to communicate with your Durable Object—just call methods!
|
|
59
|
+
- **TypeScript:** Use `DurableObjectNamespace<JotDB>` for full type safety.
|
|
60
|
+
- **See the API section below for all available methods.**
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 📚 Full Example
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { JotDB } from 'jotdb';
|
|
68
|
+
|
|
69
|
+
export interface Env {
|
|
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
|
+
});
|
|
83
|
+
|
|
84
|
+
await db.setAll({
|
|
85
|
+
name: "Alice",
|
|
86
|
+
age: 42,
|
|
87
|
+
email: "alice@example.com"
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const all = await db.getAll();
|
|
91
|
+
|
|
92
|
+
return new Response(JSON.stringify(all, null, 2), {
|
|
93
|
+
headers: { "Content-Type": "application/json" }
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
A lightweight, schema-validated key-value store built on Cloudflare Durable Objects.
|
|
102
|
+
|
|
103
|
+
## Features
|
|
104
|
+
|
|
105
|
+
- Schema validation using Zod
|
|
106
|
+
- Automatic schema inference
|
|
107
|
+
- Audit logging
|
|
108
|
+
- TypeScript support
|
|
109
|
+
- Read-only mode
|
|
110
|
+
- Auto-strip mode for schema validation
|
|
111
|
+
|
|
112
|
+
## Installation
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
npm install jotdb
|
|
116
|
+
# or
|
|
117
|
+
bun add jotdb
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Usage
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
import { JotDB } from 'jotdb';
|
|
124
|
+
|
|
125
|
+
// In your Worker
|
|
126
|
+
export interface Env {
|
|
127
|
+
JOTDB: DurableObjectNamespace;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export default {
|
|
131
|
+
async fetch(request: Request, env: Env) {
|
|
132
|
+
const id = env.JOTDB.idFromName("my-db");
|
|
133
|
+
const db = env.JOTDB.get(id);
|
|
134
|
+
|
|
135
|
+
// Set a value
|
|
136
|
+
await db.set("key", "value");
|
|
137
|
+
|
|
138
|
+
// Get a value
|
|
139
|
+
const value = await db.get("key");
|
|
140
|
+
|
|
141
|
+
// Set schema
|
|
142
|
+
await db.setSchema({
|
|
143
|
+
name: "string",
|
|
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"
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## API
|
|
159
|
+
|
|
160
|
+
### Methods
|
|
161
|
+
|
|
162
|
+
- `get<T>(key: string): Promise<T | undefined>`
|
|
163
|
+
- `set<T>(key: string, value: T): Promise<void>`
|
|
164
|
+
- `getAll(): Promise<Record<string, unknown>>`
|
|
165
|
+
- `setAll(obj: Record<string, unknown>): Promise<void>`
|
|
166
|
+
- `delete(key: string): Promise<void>`
|
|
167
|
+
- `clear(): Promise<void>`
|
|
168
|
+
- `keys(): Promise<string[]>`
|
|
169
|
+
- `has(key: string): Promise<boolean>`
|
|
170
|
+
- `getSchema(): Promise<SchemaDefinition>`
|
|
171
|
+
- `setSchema(schema: SchemaDefinition): Promise<void>`
|
|
172
|
+
- `getOptions(): Promise<JotDBOptions>`
|
|
173
|
+
- `setOptions(opts: Partial<JotDBOptions>): Promise<void>`
|
|
174
|
+
- `getAuditLog(): Promise<AuditLogEntry[]>`
|
|
175
|
+
- `clearAuditLog(): Promise<void>`
|
|
176
|
+
|
|
177
|
+
### Types
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
type SchemaType = "string" | "number" | "boolean" | "email" | "array" | "object" | "any";
|
|
181
|
+
type SchemaDefinition = Record<string, SchemaType>;
|
|
182
|
+
|
|
183
|
+
interface JotDBOptions {
|
|
184
|
+
autoStrip: boolean;
|
|
185
|
+
readOnly: boolean;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
interface AuditLogEntry {
|
|
189
|
+
timestamp: number;
|
|
190
|
+
action: string;
|
|
191
|
+
keys: string[];
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## License
|
|
196
|
+
|
|
197
|
+
MIT
|
|
198
|
+
|
|
199
|
+
## Contributing
|
|
200
|
+
|
|
201
|
+
1. Fork the repository
|
|
202
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
203
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
204
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
205
|
+
5. Open a Pull Request
|
|
206
|
+
|
|
207
|
+
## Testing
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
bun test
|
|
211
|
+
```
|
package/bun.lock
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lockfileVersion": 1,
|
|
3
|
+
"workspaces": {
|
|
4
|
+
"": {
|
|
5
|
+
"name": "jotdb",
|
|
6
|
+
"devDependencies": {
|
|
7
|
+
"@cloudflare/workers-types": "^4.20250517.0",
|
|
8
|
+
"hono": "^4.7.10",
|
|
9
|
+
"wrangler": "^4.15.2",
|
|
10
|
+
"zod": "^3.24.4",
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
"packages": {
|
|
15
|
+
"@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.0", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA=="],
|
|
16
|
+
|
|
17
|
+
"@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.3.1", "", { "peerDependencies": { "unenv": "2.0.0-rc.15", "workerd": "^1.20250320.0" }, "optionalPeers": ["workerd"] }, "sha512-Xq57Qd+ADpt6hibcVBO0uLG9zzRgyRhfCUgBT9s+g3+3Ivg5zDyVgLFy40ES1VdNcu8rPNSivm9A+kGP5IVaPg=="],
|
|
18
|
+
|
|
19
|
+
"@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250508.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-9x09MrA9Y5RQs3zqWvWns8xHgM2pVNXWpeJ+3hQYu4PrwPFZXtTD6b/iMmOnlYKzINlREq1RGeEybMFyWEUlUg=="],
|
|
20
|
+
|
|
21
|
+
"@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250508.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0Ili+nE2LLRzYue/yPc1pepSyNNg6LxR3/ng/rlQzVQUxPXIXldHFkJ/ynsYwQnAcf6OxasSi/kbTm6yvDoSAQ=="],
|
|
22
|
+
|
|
23
|
+
"@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250508.0", "", { "os": "linux", "cpu": "x64" }, "sha512-5saVrZ3uVwYxvBa7BaonXjeqB6X0YF3ak05qvBaWcmZ3FNmnarMm2W8842cnbhnckDVBpB/iDo51Sy6Y7y1jcw=="],
|
|
24
|
+
|
|
25
|
+
"@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250508.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-muQe1pkxRi3eaq1Q417xvfGd2SlktbLTzNhT5Yftsx8OecWrYuB8i4ttR6Nr5ER06bfEj0FqQjqJJhcp6wLLUQ=="],
|
|
26
|
+
|
|
27
|
+
"@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250508.0", "", { "os": "win32", "cpu": "x64" }, "sha512-EJj8iTWFMqjgvZUxxNvzK7frA1JMFi3y/9eDIdZPL/OaQh3cmk5Lai5DCXsKYUxfooMBZWYTp53zOLrvuJI8VQ=="],
|
|
28
|
+
|
|
29
|
+
"@cloudflare/workers-types": ["@cloudflare/workers-types@4.20250517.0", "", {}, "sha512-DYttpORVcvKGXI+zQDHBO6r0pDsAc8fkmJg0x1PK7+t0ijfc6nMVnUF/IVJIllScOWWHHCBknCbCmV0eGq2njA=="],
|
|
30
|
+
|
|
31
|
+
"@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="],
|
|
32
|
+
|
|
33
|
+
"@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="],
|
|
34
|
+
|
|
35
|
+
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q=="],
|
|
36
|
+
|
|
37
|
+
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.4", "", { "os": "android", "cpu": "arm" }, "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ=="],
|
|
38
|
+
|
|
39
|
+
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.4", "", { "os": "android", "cpu": "arm64" }, "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A=="],
|
|
40
|
+
|
|
41
|
+
"@esbuild/android-x64": ["@esbuild/android-x64@0.25.4", "", { "os": "android", "cpu": "x64" }, "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ=="],
|
|
42
|
+
|
|
43
|
+
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g=="],
|
|
44
|
+
|
|
45
|
+
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A=="],
|
|
46
|
+
|
|
47
|
+
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.4", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ=="],
|
|
48
|
+
|
|
49
|
+
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ=="],
|
|
50
|
+
|
|
51
|
+
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.4", "", { "os": "linux", "cpu": "arm" }, "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ=="],
|
|
52
|
+
|
|
53
|
+
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ=="],
|
|
54
|
+
|
|
55
|
+
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.4", "", { "os": "linux", "cpu": "ia32" }, "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ=="],
|
|
56
|
+
|
|
57
|
+
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA=="],
|
|
58
|
+
|
|
59
|
+
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg=="],
|
|
60
|
+
|
|
61
|
+
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag=="],
|
|
62
|
+
|
|
63
|
+
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA=="],
|
|
64
|
+
|
|
65
|
+
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g=="],
|
|
66
|
+
|
|
67
|
+
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.4", "", { "os": "linux", "cpu": "x64" }, "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA=="],
|
|
68
|
+
|
|
69
|
+
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.4", "", { "os": "none", "cpu": "arm64" }, "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ=="],
|
|
70
|
+
|
|
71
|
+
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.4", "", { "os": "none", "cpu": "x64" }, "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw=="],
|
|
72
|
+
|
|
73
|
+
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.4", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A=="],
|
|
74
|
+
|
|
75
|
+
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.4", "", { "os": "openbsd", "cpu": "x64" }, "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw=="],
|
|
76
|
+
|
|
77
|
+
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.4", "", { "os": "sunos", "cpu": "x64" }, "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q=="],
|
|
78
|
+
|
|
79
|
+
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ=="],
|
|
80
|
+
|
|
81
|
+
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg=="],
|
|
82
|
+
|
|
83
|
+
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.4", "", { "os": "win32", "cpu": "x64" }, "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ=="],
|
|
84
|
+
|
|
85
|
+
"@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="],
|
|
86
|
+
|
|
87
|
+
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="],
|
|
88
|
+
|
|
89
|
+
"@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="],
|
|
90
|
+
|
|
91
|
+
"@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="],
|
|
92
|
+
|
|
93
|
+
"@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="],
|
|
94
|
+
|
|
95
|
+
"@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="],
|
|
96
|
+
|
|
97
|
+
"@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="],
|
|
98
|
+
|
|
99
|
+
"@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="],
|
|
100
|
+
|
|
101
|
+
"@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="],
|
|
102
|
+
|
|
103
|
+
"@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="],
|
|
104
|
+
|
|
105
|
+
"@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="],
|
|
106
|
+
|
|
107
|
+
"@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="],
|
|
108
|
+
|
|
109
|
+
"@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="],
|
|
110
|
+
|
|
111
|
+
"@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="],
|
|
112
|
+
|
|
113
|
+
"@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="],
|
|
114
|
+
|
|
115
|
+
"@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="],
|
|
116
|
+
|
|
117
|
+
"@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="],
|
|
118
|
+
|
|
119
|
+
"@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="],
|
|
120
|
+
|
|
121
|
+
"@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="],
|
|
122
|
+
|
|
123
|
+
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="],
|
|
124
|
+
|
|
125
|
+
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
|
126
|
+
|
|
127
|
+
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="],
|
|
128
|
+
|
|
129
|
+
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="],
|
|
130
|
+
|
|
131
|
+
"acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="],
|
|
132
|
+
|
|
133
|
+
"acorn-walk": ["acorn-walk@8.3.2", "", {}, "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A=="],
|
|
134
|
+
|
|
135
|
+
"as-table": ["as-table@1.0.55", "", { "dependencies": { "printable-characters": "^1.0.42" } }, "sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ=="],
|
|
136
|
+
|
|
137
|
+
"blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="],
|
|
138
|
+
|
|
139
|
+
"color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="],
|
|
140
|
+
|
|
141
|
+
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
|
142
|
+
|
|
143
|
+
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
|
144
|
+
|
|
145
|
+
"color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="],
|
|
146
|
+
|
|
147
|
+
"cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
|
|
148
|
+
|
|
149
|
+
"data-uri-to-buffer": ["data-uri-to-buffer@2.0.2", "", {}, "sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA=="],
|
|
150
|
+
|
|
151
|
+
"defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
|
|
152
|
+
|
|
153
|
+
"detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="],
|
|
154
|
+
|
|
155
|
+
"esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="],
|
|
156
|
+
|
|
157
|
+
"exit-hook": ["exit-hook@2.2.1", "", {}, "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw=="],
|
|
158
|
+
|
|
159
|
+
"exsolve": ["exsolve@1.0.5", "", {}, "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg=="],
|
|
160
|
+
|
|
161
|
+
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
|
162
|
+
|
|
163
|
+
"get-source": ["get-source@2.0.12", "", { "dependencies": { "data-uri-to-buffer": "^2.0.0", "source-map": "^0.6.1" } }, "sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w=="],
|
|
164
|
+
|
|
165
|
+
"glob-to-regexp": ["glob-to-regexp@0.4.1", "", {}, "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="],
|
|
166
|
+
|
|
167
|
+
"hono": ["hono@4.7.10", "", {}, "sha512-QkACju9MiN59CKSY5JsGZCYmPZkA6sIW6OFCUp7qDjZu6S6KHtJHhAc9Uy9mV9F8PJ1/HQ3ybZF2yjCa/73fvQ=="],
|
|
168
|
+
|
|
169
|
+
"is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="],
|
|
170
|
+
|
|
171
|
+
"mime": ["mime@3.0.0", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="],
|
|
172
|
+
|
|
173
|
+
"miniflare": ["miniflare@4.20250508.2", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "stoppable": "1.1.0", "undici": "^5.28.5", "workerd": "1.20250508.0", "ws": "8.18.0", "youch": "3.3.4", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-+2XoHLSbY49LNQgZoAJRX+SyUwC767Cz46pgx4T/j1YGKSrMzAxCOk59b12QoFNnN50Gtd9HkT3ukZn2nzrIVw=="],
|
|
174
|
+
|
|
175
|
+
"mustache": ["mustache@4.2.0", "", { "bin": { "mustache": "bin/mustache" } }, "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ=="],
|
|
176
|
+
|
|
177
|
+
"ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="],
|
|
178
|
+
|
|
179
|
+
"path-to-regexp": ["path-to-regexp@6.3.0", "", {}, "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="],
|
|
180
|
+
|
|
181
|
+
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
|
182
|
+
|
|
183
|
+
"printable-characters": ["printable-characters@1.0.42", "", {}, "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ=="],
|
|
184
|
+
|
|
185
|
+
"semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
|
186
|
+
|
|
187
|
+
"sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="],
|
|
188
|
+
|
|
189
|
+
"simple-swizzle": ["simple-swizzle@0.2.2", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="],
|
|
190
|
+
|
|
191
|
+
"source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
|
192
|
+
|
|
193
|
+
"stacktracey": ["stacktracey@2.1.8", "", { "dependencies": { "as-table": "^1.0.36", "get-source": "^2.0.12" } }, "sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw=="],
|
|
194
|
+
|
|
195
|
+
"stoppable": ["stoppable@1.1.0", "", {}, "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw=="],
|
|
196
|
+
|
|
197
|
+
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
|
198
|
+
|
|
199
|
+
"ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="],
|
|
200
|
+
|
|
201
|
+
"undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="],
|
|
202
|
+
|
|
203
|
+
"unenv": ["unenv@2.0.0-rc.15", "", { "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.4", "ohash": "^2.0.11", "pathe": "^2.0.3", "ufo": "^1.5.4" } }, "sha512-J/rEIZU8w6FOfLNz/hNKsnY+fFHWnu9MH4yRbSZF3xbbGHovcetXPs7sD+9p8L6CeNC//I9bhRYAOsBt2u7/OA=="],
|
|
204
|
+
|
|
205
|
+
"workerd": ["workerd@1.20250508.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250508.0", "@cloudflare/workerd-darwin-arm64": "1.20250508.0", "@cloudflare/workerd-linux-64": "1.20250508.0", "@cloudflare/workerd-linux-arm64": "1.20250508.0", "@cloudflare/workerd-windows-64": "1.20250508.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-ffLxe7dXSuGoA6jb3Qx2SClIV1aLHfJQ6RhGhzYHjQgv7dL6fdUOSIIGgzmu2mRKs+WFSujp6c8WgKquco6w3w=="],
|
|
206
|
+
|
|
207
|
+
"wrangler": ["wrangler@4.15.2", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.3.1", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20250508.2", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.15", "workerd": "1.20250508.0" }, "optionalDependencies": { "fsevents": "~2.3.2", "sharp": "^0.33.5" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250508.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-Rv7zP61DOVzIS3af+/1UzJkRVpqu6VDRi6uIVMiwD1LkXG5Ov08tv94jgnE9bSjVf0paQg3dl0E89h+wQ0x/Bw=="],
|
|
208
|
+
|
|
209
|
+
"ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="],
|
|
210
|
+
|
|
211
|
+
"youch": ["youch@3.3.4", "", { "dependencies": { "cookie": "^0.7.1", "mustache": "^4.2.0", "stacktracey": "^2.1.8" } }, "sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg=="],
|
|
212
|
+
|
|
213
|
+
"zod": ["zod@3.24.4", "", {}, "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg=="],
|
|
214
|
+
|
|
215
|
+
"miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="],
|
|
216
|
+
}
|
|
217
|
+
}
|
package/jotdb.tests.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { assert, describe, it, beforeEach } from "bun:test";
|
|
2
|
+
import { JotDB } from "./src/JotDB.ts";
|
|
3
|
+
|
|
4
|
+
// Fake DurableObjectState stub for unit testing
|
|
5
|
+
function createFakeState(): any {
|
|
6
|
+
let store: Record<string, any> = {};
|
|
7
|
+
return {
|
|
8
|
+
storage: {
|
|
9
|
+
get: async (k: string) => store[k],
|
|
10
|
+
put: async (k: string, v: any) => { store[k] = v },
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe("JotDB", () => {
|
|
16
|
+
let jot: JotDB;
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
jot = new JotDB(createFakeState());
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("should set and get a value", async () => {
|
|
23
|
+
await jot.set("foo", "bar");
|
|
24
|
+
const val = await jot.get("foo");
|
|
25
|
+
assert(val === "bar");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("should support setAll and getAll", async () => {
|
|
29
|
+
await jot.setAll({ a: 1, b: true });
|
|
30
|
+
const all = await jot.getAll();
|
|
31
|
+
assert(all.a === 1);
|
|
32
|
+
assert(all.b === true);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("should enforce schema after inference", async () => {
|
|
36
|
+
await jot.setAll({ x: "yes", y: 2 });
|
|
37
|
+
await assert.rejects(() => jot.set("y", "not a number"));
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("should strip unknowns if autoStrip is on", async () => {
|
|
41
|
+
await jot.setAll({ known: "yes" });
|
|
42
|
+
await jot.setOptions({ autoStrip: true });
|
|
43
|
+
await jot.setAll({ known: "ok", extra: "skip" });
|
|
44
|
+
const data = await jot.getAll();
|
|
45
|
+
assert(data.known === "ok");
|
|
46
|
+
assert(!("extra" in data));
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("should block writes if readOnly is on", async () => {
|
|
50
|
+
await jot.setAll({ z: 9 });
|
|
51
|
+
await jot.setOptions({ readOnly: true });
|
|
52
|
+
await assert.rejects(() => jot.set("z", 10));
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("should track audit logs", async () => {
|
|
56
|
+
await jot.set("a", 1);
|
|
57
|
+
await jot.setAll({ b: 2, c: 3 });
|
|
58
|
+
const log = await jot.getAuditLog();
|
|
59
|
+
assert(log.length >= 2);
|
|
60
|
+
assert(log[0].action === "setAll");
|
|
61
|
+
assert(log[1].action === "set");
|
|
62
|
+
});
|
|
63
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "jotdb",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "wrangler dev --port 5173 --remote",
|
|
7
|
+
"test": "bun test jotdb.tests.ts",
|
|
8
|
+
"deploy": "wrangler deploy"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"@cloudflare/workers-types": "^4.20250517.0",
|
|
13
|
+
"hono": "^4.7.10",
|
|
14
|
+
"wrangler": "^4.15.2",
|
|
15
|
+
"zod": "^3.24.4"
|
|
16
|
+
}
|
|
17
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import { z, ZodTypeAny, ZodObject } from "zod";
|
|
2
|
+
import type { DurableObjectState } from "@cloudflare/workers-types";
|
|
3
|
+
import { Hono } from 'hono';
|
|
4
|
+
import { cors } from 'hono/cors';
|
|
5
|
+
import { prettyJSON } from 'hono/pretty-json';
|
|
6
|
+
import { DurableObject } from "cloudflare:workers";
|
|
7
|
+
|
|
8
|
+
// Type definitions
|
|
9
|
+
type SchemaType = "string" | "number" | "boolean" | "email" | "array" | "object" | "any";
|
|
10
|
+
type SchemaDefinition = Record<string, SchemaType>;
|
|
11
|
+
|
|
12
|
+
interface JotDBOptions {
|
|
13
|
+
autoStrip: boolean;
|
|
14
|
+
readOnly: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface AuditLogEntry {
|
|
18
|
+
timestamp: number;
|
|
19
|
+
action: string;
|
|
20
|
+
keys: string[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class JotDB extends DurableObject {
|
|
24
|
+
private ctx: DurableObjectState;
|
|
25
|
+
private data: Record<string, unknown> = {};
|
|
26
|
+
private rawSchema: SchemaDefinition = {};
|
|
27
|
+
private zodSchema: ZodObject<any> | null = null;
|
|
28
|
+
private options: JotDBOptions = {
|
|
29
|
+
autoStrip: false,
|
|
30
|
+
readOnly: false,
|
|
31
|
+
};
|
|
32
|
+
private auditLog: AuditLogEntry[] = [];
|
|
33
|
+
|
|
34
|
+
constructor(state: DurableObjectState, env: Env) {
|
|
35
|
+
super(state, env);
|
|
36
|
+
this.ctx = state;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async load(): Promise<void> {
|
|
40
|
+
if (Object.keys(this.data).length === 0) {
|
|
41
|
+
this.data = (await this.ctx.storage.get("data")) || {};
|
|
42
|
+
}
|
|
43
|
+
if (Object.keys(this.rawSchema).length === 0) {
|
|
44
|
+
this.rawSchema = (await this.ctx.storage.get("__schema__")) || {};
|
|
45
|
+
if (Object.keys(this.rawSchema).length > 0) {
|
|
46
|
+
this.zodSchema = this.buildZodSchema(this.rawSchema);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const storedOptions = await this.ctx.storage.get("__options__") as JotDBOptions | null;
|
|
50
|
+
if (storedOptions) this.options = storedOptions;
|
|
51
|
+
|
|
52
|
+
this.auditLog = (await this.ctx.storage.get("__audit__")) || [];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async save(): Promise<void> {
|
|
56
|
+
await this.ctx.storage.put("data", this.data);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async logAudit(action: string, keys: string[] | string): Promise<void> {
|
|
60
|
+
const entry: AuditLogEntry = {
|
|
61
|
+
timestamp: Date.now(),
|
|
62
|
+
action,
|
|
63
|
+
keys: Array.isArray(keys) ? keys : [keys],
|
|
64
|
+
};
|
|
65
|
+
this.auditLog.unshift(entry);
|
|
66
|
+
await this.ctx.storage.put("__audit__", this.auditLog.slice(0, 100)); // keep max 100 entries
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async get<T = unknown>(key: string): Promise<T | undefined> {
|
|
70
|
+
await this.load();
|
|
71
|
+
return this.data[key] as T;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async getAll(): Promise<Record<string, unknown>> {
|
|
75
|
+
await this.load();
|
|
76
|
+
return this.data;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async set<T>(key: string, value: T): Promise<void> {
|
|
80
|
+
await this.load();
|
|
81
|
+
if (this.options.readOnly) throw new Error("JotDB is in read-only mode");
|
|
82
|
+
|
|
83
|
+
if (this.zodSchema) {
|
|
84
|
+
const partialSchema = this.zodSchema.pick({ [key]: true });
|
|
85
|
+
partialSchema.parse({ [key]: value });
|
|
86
|
+
}
|
|
87
|
+
this.data[key] = value;
|
|
88
|
+
await this.save();
|
|
89
|
+
await this.logAudit("set", key);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async setAll(obj: Record<string, unknown>): Promise<void> {
|
|
93
|
+
await this.load();
|
|
94
|
+
if (this.options.readOnly) throw new Error("JotDB is in read-only mode");
|
|
95
|
+
|
|
96
|
+
const typeOfValue = (v: any): string => {
|
|
97
|
+
if (Array.isArray(v)) return "array";
|
|
98
|
+
switch (typeof v) {
|
|
99
|
+
case "string": return "string";
|
|
100
|
+
case "number": return "number";
|
|
101
|
+
case "boolean": return "boolean";
|
|
102
|
+
case "object": return "object";
|
|
103
|
+
default: return "any";
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
if (!this.zodSchema) {
|
|
108
|
+
const inferred: SchemaDefinition = {};
|
|
109
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
110
|
+
inferred[k] = typeOfValue(v) as SchemaType;
|
|
111
|
+
}
|
|
112
|
+
await this.setSchema(inferred);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (this.zodSchema) {
|
|
116
|
+
if (this.options.autoStrip) {
|
|
117
|
+
obj = this.zodSchema.parse(obj); // returns stripped
|
|
118
|
+
} else {
|
|
119
|
+
this.zodSchema.parse(obj); // strict match
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
Object.assign(this.data, obj);
|
|
124
|
+
await this.save();
|
|
125
|
+
await this.logAudit("setAll", Object.keys(obj));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async delete(key: string): Promise<void> {
|
|
129
|
+
await this.load();
|
|
130
|
+
delete this.data[key];
|
|
131
|
+
await this.save();
|
|
132
|
+
await this.logAudit("delete", key);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async clear(): Promise<void> {
|
|
136
|
+
this.data = {};
|
|
137
|
+
await this.save();
|
|
138
|
+
await this.logAudit("clear", []);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async keys(): Promise<string[]> {
|
|
142
|
+
await this.load();
|
|
143
|
+
return Object.keys(this.data);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async has(key: string): Promise<boolean> {
|
|
147
|
+
await this.load();
|
|
148
|
+
return key in this.data;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async getSchema(): Promise<SchemaDefinition> {
|
|
152
|
+
await this.load();
|
|
153
|
+
return this.rawSchema;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private warnSchemaDiff(newSchema: SchemaDefinition): void {
|
|
157
|
+
const current = this.rawSchema;
|
|
158
|
+
for (const key in newSchema) {
|
|
159
|
+
if (!(key in current)) console.warn(`[JotDB] New key added: ${key}`);
|
|
160
|
+
else if (newSchema[key] !== current[key]) {
|
|
161
|
+
console.warn(
|
|
162
|
+
`[JotDB] Type changed for "${key}": ${current[key]} → ${newSchema[key]}`
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
for (const key in current) {
|
|
167
|
+
if (!(key in newSchema)) {
|
|
168
|
+
console.warn(`[JotDB] Key removed: ${key}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async setSchema(schemaObj: SchemaDefinition): Promise<void> {
|
|
174
|
+
await this.load();
|
|
175
|
+
if (Object.keys(this.rawSchema).length > 0) {
|
|
176
|
+
this.warnSchemaDiff(schemaObj);
|
|
177
|
+
}
|
|
178
|
+
this.rawSchema = schemaObj;
|
|
179
|
+
this.zodSchema = this.buildZodSchema(schemaObj);
|
|
180
|
+
await this.ctx.storage.put("__schema__", schemaObj);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async setOptions(opts: Partial<JotDBOptions>): Promise<void> {
|
|
184
|
+
await this.load();
|
|
185
|
+
Object.assign(this.options, opts);
|
|
186
|
+
await this.ctx.storage.put("__options__", this.options);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async getOptions(): Promise<JotDBOptions> {
|
|
190
|
+
await this.load();
|
|
191
|
+
return this.options;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async getAuditLog(): Promise<AuditLogEntry[]> {
|
|
195
|
+
await this.load();
|
|
196
|
+
return this.auditLog;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async clearAuditLog(): Promise<void> {
|
|
200
|
+
this.auditLog = [];
|
|
201
|
+
await this.ctx.storage.put("__audit__", []);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private buildZodSchema(schema: SchemaDefinition): ZodObject<any> {
|
|
205
|
+
const shape: Record<string, ZodTypeAny> = {};
|
|
206
|
+
for (const [key, type] of Object.entries(schema)) {
|
|
207
|
+
switch (type) {
|
|
208
|
+
case "string":
|
|
209
|
+
shape[key] = z.string();
|
|
210
|
+
break;
|
|
211
|
+
case "number":
|
|
212
|
+
shape[key] = z.number();
|
|
213
|
+
break;
|
|
214
|
+
case "boolean":
|
|
215
|
+
shape[key] = z.boolean();
|
|
216
|
+
break;
|
|
217
|
+
case "email":
|
|
218
|
+
shape[key] = z.string().email();
|
|
219
|
+
break;
|
|
220
|
+
case "array":
|
|
221
|
+
shape[key] = z.array(z.any());
|
|
222
|
+
break;
|
|
223
|
+
case "object":
|
|
224
|
+
shape[key] = z.record(z.any());
|
|
225
|
+
break;
|
|
226
|
+
default:
|
|
227
|
+
shape[key] = z.any();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return z.object(shape);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export interface Env {
|
|
235
|
+
JOTDB: DurableObjectNamespace;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const app = new Hono<{ Bindings: Env }>();
|
|
239
|
+
|
|
240
|
+
// Middleware
|
|
241
|
+
app.use('*', cors());
|
|
242
|
+
app.use('*', prettyJSON());
|
|
243
|
+
|
|
244
|
+
// Test endpoint
|
|
245
|
+
app.get('/test', async (c) => {
|
|
246
|
+
const id = c.env.JOTDB.idFromName("test-db");
|
|
247
|
+
const db = c.env.JOTDB.get(id) as unknown as JotDB;
|
|
248
|
+
|
|
249
|
+
const results = {
|
|
250
|
+
timestamp: Date.now(),
|
|
251
|
+
tests: [] as any[],
|
|
252
|
+
auditLog: [] as any[]
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
// Test 1: Basic set/get
|
|
257
|
+
await db.set("test1", "hello");
|
|
258
|
+
const value1 = await db.get("test1");
|
|
259
|
+
results.tests.push({
|
|
260
|
+
name: "Basic set/get",
|
|
261
|
+
passed: value1 === "hello",
|
|
262
|
+
value: value1
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// Test 2: Schema validation
|
|
266
|
+
await db.setSchema({
|
|
267
|
+
name: "string",
|
|
268
|
+
age: "number",
|
|
269
|
+
email: "email"
|
|
270
|
+
});
|
|
271
|
+
await db.setAll({
|
|
272
|
+
name: "John",
|
|
273
|
+
age: 30,
|
|
274
|
+
email: "john@example.com"
|
|
275
|
+
});
|
|
276
|
+
const all = await db.getAll();
|
|
277
|
+
results.tests.push({
|
|
278
|
+
name: "Schema validation",
|
|
279
|
+
passed: all.name === "John" && all.age === 30,
|
|
280
|
+
value: all
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// Test 3: Read-only mode
|
|
284
|
+
await db.setOptions({ readOnly: true });
|
|
285
|
+
try {
|
|
286
|
+
await db.set("test3", "should fail");
|
|
287
|
+
results.tests.push({
|
|
288
|
+
name: "Read-only mode",
|
|
289
|
+
passed: false,
|
|
290
|
+
error: "Should have thrown"
|
|
291
|
+
});
|
|
292
|
+
} catch (e) {
|
|
293
|
+
results.tests.push({
|
|
294
|
+
name: "Read-only mode",
|
|
295
|
+
passed: true,
|
|
296
|
+
error: e instanceof Error ? e.message : String(e)
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Test 4: Auto-strip mode
|
|
301
|
+
await db.setOptions({ readOnly: false, autoStrip: true });
|
|
302
|
+
await db.setAll({
|
|
303
|
+
name: "Jane",
|
|
304
|
+
age: 25,
|
|
305
|
+
email: "jane@example.com",
|
|
306
|
+
extra: "should be stripped"
|
|
307
|
+
});
|
|
308
|
+
const stripped = await db.getAll();
|
|
309
|
+
results.tests.push({
|
|
310
|
+
name: "Auto-strip mode",
|
|
311
|
+
passed: !("extra" in stripped),
|
|
312
|
+
value: stripped
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// Get audit log
|
|
316
|
+
results.auditLog = await db.getAuditLog();
|
|
317
|
+
|
|
318
|
+
return c.json(results);
|
|
319
|
+
} catch (error) {
|
|
320
|
+
return c.json({
|
|
321
|
+
error: error instanceof Error ? error.message : String(error),
|
|
322
|
+
tests: results.tests,
|
|
323
|
+
auditLog: results.auditLog
|
|
324
|
+
}, 500);
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
// Health check endpoint
|
|
329
|
+
app.get('/', (c) => c.text('JotDB Durable Object'));
|
|
330
|
+
|
|
331
|
+
export default app;
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ES2020",
|
|
5
|
+
"lib": [
|
|
6
|
+
"ES2020"
|
|
7
|
+
],
|
|
8
|
+
"types": [
|
|
9
|
+
"@cloudflare/workers-types"
|
|
10
|
+
],
|
|
11
|
+
"moduleResolution": "node",
|
|
12
|
+
"strict": true,
|
|
13
|
+
"noImplicitAny": true,
|
|
14
|
+
"strictNullChecks": true,
|
|
15
|
+
"strictFunctionTypes": true,
|
|
16
|
+
"strictBindCallApply": true,
|
|
17
|
+
"strictPropertyInitialization": true,
|
|
18
|
+
"noImplicitThis": true,
|
|
19
|
+
"alwaysStrict": true,
|
|
20
|
+
"esModuleInterop": true,
|
|
21
|
+
"skipLibCheck": true,
|
|
22
|
+
"forceConsistentCasingInFileNames": true
|
|
23
|
+
},
|
|
24
|
+
"include": [
|
|
25
|
+
"src/**/*"
|
|
26
|
+
],
|
|
27
|
+
"exclude": [
|
|
28
|
+
"node_modules"
|
|
29
|
+
]
|
|
30
|
+
}
|
package/wrangler.jsonc
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "node_modules/wrangler/config-schema.json",
|
|
3
|
+
"name": "jotdb",
|
|
4
|
+
"main": "src/index.ts",
|
|
5
|
+
"account_id": "bfcb6ac5b3ceaf42a09607f6f7925823",
|
|
6
|
+
"compatibility_date": "2024-05-01",
|
|
7
|
+
"compatibility_flags": [
|
|
8
|
+
"nodejs_compat"
|
|
9
|
+
],
|
|
10
|
+
"durable_objects": {
|
|
11
|
+
"bindings": [
|
|
12
|
+
{
|
|
13
|
+
"name": "JOTDB",
|
|
14
|
+
"class_name": "JotDB"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
"migrations": [
|
|
19
|
+
{
|
|
20
|
+
"tag": "v1",
|
|
21
|
+
"new_classes": [
|
|
22
|
+
"JotDB"
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"minify": true,
|
|
27
|
+
"observability": {
|
|
28
|
+
"enabled": true
|
|
29
|
+
}
|
|
30
|
+
}
|