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 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
+ }