diff --git a/bun.lock b/bun.lock index 87c72dc..93539b3 100644 --- a/bun.lock +++ b/bun.lock @@ -4,12 +4,27 @@ "": { "name": "workshop", }, + "packages/attache": { + "name": "attache", + "dependencies": { + "@workshop/shared": "workspace:*", + "hono": "^4.8.0", + "nanoid": "^5.1.5", + }, + "devDependencies": { + "@types/bun": "latest", + }, + "peerDependencies": { + "typescript": "^5", + }, + }, "packages/http": { "name": "@workshop/http", "dependencies": { "@workshop/nano-remix": "workspace:*", "@workshop/shared": "workspace:*", - "hono": "^4.7.11", + "@workshop/todo": "workspace:*", + "hono": "catalog:", }, "devDependencies": { "@types/bun": "latest", @@ -30,7 +45,7 @@ "packages/nano-remix": { "name": "@workshop/nano-remix", "dependencies": { - "hono": "^4.7.11", + "hono": "catalog:", }, "devDependencies": { "@types/bun": "latest", @@ -77,8 +92,8 @@ "typescript": "^5", }, }, - "packages/text-do": { - "name": "text-do", + "packages/todo": { + "name": "@workshop/todo", "dependencies": { "@codemirror/commands": "^6.8.1", "@codemirror/language": "^6.11.1", @@ -87,17 +102,21 @@ "@lezer/generator": "^1.7.3", "@lezer/highlight": "^1.2.1", "@lezer/lr": "^1.4.2", - "hono": "^4.8.0", + "hono": "catalog:", "luxon": "^3.6.1", }, "devDependencies": { "@types/bun": "latest", }, "peerDependencies": { + "hono": "^4", "typescript": "^5", }, }, }, + "catalog": { + "hono": "^4.8.0", + }, "packages": { "@codemirror/commands": ["@codemirror/commands@6.8.1", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.4.0", "@codemirror/view": "^6.27.0", "@lezer/common": "^1.1.0" } }, "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw=="], @@ -113,11 +132,11 @@ "@discordjs/formatters": ["@discordjs/formatters@0.6.1", "", { "dependencies": { "discord-api-types": "^0.38.1" } }, "sha512-5cnX+tASiPCqCWtFcFslxBVUaCetB0thvM/JyavhbXInP1HJIEU+Qv/zMrnuwSsX3yWH2lVXNJZeDK3EiP4HHg=="], - "@discordjs/rest": ["@discordjs/rest@2.5.0", "", { "dependencies": { "@discordjs/collection": "^2.1.1", "@discordjs/util": "^1.1.1", "@sapphire/async-queue": "^1.5.3", "@sapphire/snowflake": "^3.5.3", "@vladfrangu/async_event_emitter": "^2.4.6", "discord-api-types": "^0.38.1", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.1" } }, "sha512-PWhchxTzpn9EV3vvPRpwS0EE2rNYB9pvzDU/eLLW3mByJl0ZHZjHI2/wA8EbH2gRMQV7nu+0FoDF84oiPl8VAQ=="], + "@discordjs/rest": ["@discordjs/rest@2.5.1", "", { "dependencies": { "@discordjs/collection": "^2.1.1", "@discordjs/util": "^1.1.1", "@sapphire/async-queue": "^1.5.3", "@sapphire/snowflake": "^3.5.3", "@vladfrangu/async_event_emitter": "^2.4.6", "discord-api-types": "^0.38.1", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.3" } }, "sha512-Tg9840IneBcbrAjcGaQzHUJWFNq1MMWZjTdjJ0WS/89IffaNKc++iOvffucPxQTF/gviO9+9r8kEPea1X5J2Dw=="], "@discordjs/util": ["@discordjs/util@1.1.1", "", {}, "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g=="], - "@discordjs/ws": ["@discordjs/ws@1.2.2", "", { "dependencies": { "@discordjs/collection": "^2.1.0", "@discordjs/rest": "^2.5.0", "@discordjs/util": "^1.1.0", "@sapphire/async-queue": "^1.5.2", "@types/ws": "^8.5.10", "@vladfrangu/async_event_emitter": "^2.2.4", "discord-api-types": "^0.38.1", "tslib": "^2.6.2", "ws": "^8.17.0" } }, "sha512-dyfq7yn0wO0IYeYOs3z79I6/HumhmKISzFL0Z+007zQJMtAFGtt3AEoq1nuLXtcunUE5YYYQqgKvybXukAK8/w=="], + "@discordjs/ws": ["@discordjs/ws@1.2.3", "", { "dependencies": { "@discordjs/collection": "^2.1.0", "@discordjs/rest": "^2.5.1", "@discordjs/util": "^1.1.0", "@sapphire/async-queue": "^1.5.2", "@types/ws": "^8.5.10", "@vladfrangu/async_event_emitter": "^2.2.4", "discord-api-types": "^0.38.1", "tslib": "^2.6.2", "ws": "^8.17.0" } }, "sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw=="], "@lezer/common": ["@lezer/common@1.2.3", "", {}, "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA=="], @@ -129,7 +148,7 @@ "@marijn/find-cluster-break": ["@marijn/find-cluster-break@1.0.2", "", {}, "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g=="], - "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.12.3", "", { "dependencies": { "ajv": "^6.12.6", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-DyVYSOafBvk3/j1Oka4z5BWT8o4AFmoNyZY9pALOm7Lh3GZglR71Co4r4dEUoqDWdDazIZQHBe7J2Nwkg6gHgQ=="], + "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.13.0", "", { "dependencies": { "ajv": "^6.12.6", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-P5FZsXU0kY881F6Hbk9GhsYx02/KgWK1DYf7/tyE/1lcFKhDYPQR9iYjhQXJn+Sg6hQleMo3DB7h7+p4wgp2Lw=="], "@openai/agents": ["@openai/agents@0.0.8", "", { "dependencies": { "@openai/agents-core": "0.0.8", "@openai/agents-openai": "0.0.8", "@openai/agents-realtime": "0.0.8", "debug": "^4.4.0", "openai": "^5.0.1" } }, "sha512-HAPP4QM47kWeWw70uxCzr5zjqHuDIvQ8Obx+98J66lcEeIZzMChHN60k5ew8DITScmzDVAVuwdzfAImSyq002w=="], @@ -139,7 +158,7 @@ "@openai/agents-realtime": ["@openai/agents-realtime@0.0.8", "", { "dependencies": { "@openai/agents-core": "0.0.8", "@openai/zod": "npm:zod@^3.25.40", "@types/ws": "^8.18.1", "debug": "^4.4.0", "ws": "^8.18.1" } }, "sha512-f+CxHICIFvCwbMCznop+bz+TTgnFfFpscN+9OTfiU5ITnaohRf+qbyU8PRgQZnSbsxRZyTOgqFoJ+2wWxM5tHA=="], - "@openai/zod": ["zod@3.25.64", "", {}, "sha512-hbP9FpSZf7pkS7hRVUrOjhwKJNyampPgtXKc3AN6DsWtoHsg2Sb4SQaS4Tcay380zSwd2VPo9G9180emBACp5g=="], + "@openai/zod": ["zod@3.25.67", "", {}, "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw=="], "@planetscale/database": ["@planetscale/database@1.19.0", "", {}, "sha512-Tv4jcFUFAFjOWrGSio49H6R2ijALv0ZzVBfJKIdm+kl9X046Fh4LLawrF9OMsglVbK6ukqMJsUCeucGAFTBcMA=="], @@ -149,11 +168,11 @@ "@sapphire/snowflake": ["@sapphire/snowflake@3.5.3", "", {}, "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ=="], - "@types/bun": ["@types/bun@1.2.16", "", { "dependencies": { "bun-types": "1.2.16" } }, "sha512-1aCZJ/6nSiViw339RsaNhkNoEloLaPzZhxMOYEa7OzRzO41IGg5n/7I43/ZIAW/c+Q6cT12Vf7fOZOoVIzb5BQ=="], + "@types/bun": ["@types/bun@1.2.17", "", { "dependencies": { "bun-types": "1.2.17" } }, "sha512-l/BYs/JYt+cXA/0+wUhulYJB6a6p//GTPiJ7nV+QHa8iiId4HZmnu/3J/SowP5g0rTiERY2kfGKXEK5Ehltx4Q=="], "@types/luxon": ["@types/luxon@3.6.2", "", {}, "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw=="], - "@types/node": ["@types/node@24.0.1", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-MX4Zioh39chHlDJbKmEgydJDS3tspMP/lnQC67G3SWsTnb9NeYVWOjkxpOSy4oMfPs4StcWHwBrvUb4ybfnuaw=="], + "@types/node": ["@types/node@24.0.3", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg=="], "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], @@ -171,13 +190,17 @@ "@workshop/spike": ["@workshop/spike@workspace:packages/spike"], + "@workshop/todo": ["@workshop/todo@workspace:packages/todo"], + "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], + "attache": ["attache@workspace:packages/attache"], + "body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="], - "bun-types": ["bun-types@1.2.16", "", { "dependencies": { "@types/node": "*" } }, "sha512-ciXLrHV4PXax9vHvUrkvun9VPVGOVwbbbBF/Ev1cXz12lyEZMoJpIJABOfPcN9gDJRaiKF9MVbSygLg4NXu3/A=="], + "bun-types": ["bun-types@1.2.17", "", { "dependencies": { "@types/node": "*" } }, "sha512-ElC7ItwT3SCQwYZDYoAH+q6KT4Fxjl8DtZ6qDulUFBmXA8YB4xo+l54J9ZJN+k2pphfn9vk7kfubeSd5QfTVJQ=="], "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], @@ -203,9 +226,9 @@ "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], - "discord-api-types": ["discord-api-types@0.38.11", "", {}, "sha512-XN0qhcQpetkyb/49hcDHuoeUPsQqOkb17wbV/t48gUkoEDi4ajhsxqugGcxvcN17BBtI9FPPWEgzv6IhQmCwyw=="], + "discord-api-types": ["discord-api-types@0.38.12", "", {}, "sha512-vqkRM50N5Zc6OVckAqtSslbUEoXmpN4bd9xq2jkoK9fgO3KNRIOyMMQ7ipqjwjKuAgzWvU6G8bRIcYWaUe1sCA=="], - "discord.js": ["discord.js@14.19.3", "", { "dependencies": { "@discordjs/builders": "^1.11.2", "@discordjs/collection": "1.5.3", "@discordjs/formatters": "^0.6.1", "@discordjs/rest": "^2.5.0", "@discordjs/util": "^1.1.1", "@discordjs/ws": "^1.2.2", "@sapphire/snowflake": "3.5.3", "discord-api-types": "^0.38.1", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.1" } }, "sha512-lncTRk0k+8Q5D3nThnODBR8fR8x2fM798o8Vsr40Krx0DjPwpZCuxxTcFMrXMQVOqM1QB9wqWgaXPg3TbmlHqA=="], + "discord.js": ["discord.js@14.20.0", "", { "dependencies": { "@discordjs/builders": "^1.11.2", "@discordjs/collection": "1.5.3", "@discordjs/formatters": "^0.6.1", "@discordjs/rest": "^2.5.1", "@discordjs/util": "^1.1.1", "@discordjs/ws": "^1.2.3", "@sapphire/snowflake": "3.5.3", "discord-api-types": "^0.38.1", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.3" } }, "sha512-5fRTptK2vpuz+bTuAEUQLSo/3AgCSLHl6Mm9+/ofb+8cbbnjWllhtaqRBq7XcpzlBnfNEugKv8HvCwcOtIHpCg=="], "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], @@ -229,7 +252,7 @@ "express": ["express@5.1.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="], - "express-rate-limit": ["express-rate-limit@7.5.0", "", { "peerDependencies": { "express": "^4.11 || 5 || ^5.0.0-beta.1" } }, "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg=="], + "express-rate-limit": ["express-rate-limit@7.5.1", "", { "peerDependencies": { "express": ">= 4.11" } }, "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw=="], "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], @@ -253,7 +276,7 @@ "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], - "hono": ["hono@4.7.11", "", {}, "sha512-rv0JMwC0KALbbmwJDEnxvQCeJh+xbS3KEWW5PC9cMJ08Ur9xgatI0HmtgYZfOdOSOeYsp5LO2cOhdI8cLEbDEQ=="], + "hono": ["hono@4.8.2", "", {}, "sha512-hM+1RIn9PK1I6SiTNS6/y7O1mvg88awYLFEuEtoiMtRyT3SD2iu9pSFgbBXT3b1Ua4IwzvSTLvwO0SEhDxCi4w=="], "http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="], @@ -289,6 +312,8 @@ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "nanoid": ["nanoid@5.1.5", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw=="], + "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], @@ -299,7 +324,7 @@ "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], - "openai": ["openai@5.3.0", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.23.8" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-VIKmoF7y4oJCDOwP/oHXGzM69+x0dpGFmN9QmYO+uPbLFOmmnwO+x1GbsgUtI+6oraxomGZ566Y421oYVu191w=="], + "openai": ["openai@5.6.0", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.23.8" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-jNH5z+hYAdOMZXyEt0yZ7246s+UZjg2AwFQqkAhZIPPjxNtHHO5mykOefau6FkOqj16aC94MOdJl/rZBcKj/cQ=="], "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], @@ -349,8 +374,6 @@ "style-mod": ["style-mod@4.1.2", "", {}, "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw=="], - "text-do": ["text-do@workspace:packages/text-do"], - "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], "ts-mixer": ["ts-mixer@6.0.4", "", {}, "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA=="], @@ -361,7 +384,7 @@ "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], - "undici": ["undici@6.21.1", "", {}, "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ=="], + "undici": ["undici@6.21.3", "", {}, "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw=="], "undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="], @@ -379,7 +402,7 @@ "ws": ["ws@8.18.2", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ=="], - "zod": ["zod@3.25.64", "", {}, "sha512-hbP9FpSZf7pkS7hRVUrOjhwKJNyampPgtXKc3AN6DsWtoHsg2Sb4SQaS4Tcay380zSwd2VPo9G9180emBACp5g=="], + "zod": ["zod@3.25.67", "", {}, "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw=="], "zod-to-json-schema": ["zod-to-json-schema@3.24.5", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g=="], @@ -387,16 +410,6 @@ "@discordjs/ws/@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="], - "@workshop/spike/@types/bun": ["@types/bun@1.2.17", "", { "dependencies": { "bun-types": "1.2.17" } }, "sha512-l/BYs/JYt+cXA/0+wUhulYJB6a6p//GTPiJ7nV+QHa8iiId4HZmnu/3J/SowP5g0rTiERY2kfGKXEK5Ehltx4Q=="], - "http-errors/statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], - - "text-do/@types/bun": ["@types/bun@1.2.17", "", { "dependencies": { "bun-types": "1.2.17" } }, "sha512-l/BYs/JYt+cXA/0+wUhulYJB6a6p//GTPiJ7nV+QHa8iiId4HZmnu/3J/SowP5g0rTiERY2kfGKXEK5Ehltx4Q=="], - - "text-do/hono": ["hono@4.8.2", "", {}, "sha512-hM+1RIn9PK1I6SiTNS6/y7O1mvg88awYLFEuEtoiMtRyT3SD2iu9pSFgbBXT3b1Ua4IwzvSTLvwO0SEhDxCi4w=="], - - "@workshop/spike/@types/bun/bun-types": ["bun-types@1.2.17", "", { "dependencies": { "@types/node": "*" } }, "sha512-ElC7ItwT3SCQwYZDYoAH+q6KT4Fxjl8DtZ6qDulUFBmXA8YB4xo+l54J9ZJN+k2pphfn9vk7kfubeSd5QfTVJQ=="], - - "text-do/@types/bun/bun-types": ["bun-types@1.2.17", "", { "dependencies": { "@types/node": "*" } }, "sha512-ElC7ItwT3SCQwYZDYoAH+q6KT4Fxjl8DtZ6qDulUFBmXA8YB4xo+l54J9ZJN+k2pphfn9vk7kfubeSd5QfTVJQ=="], } } diff --git a/package.json b/package.json index 70ce10b..05019be 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,14 @@ { "name": "workshop", "private": true, - "workspaces": [ - "packages/*" - ], + "workspaces": { + "packages": [ + "packages/*" + ], + "catalog": { + "hono": "^4.8.0" + } + }, "prettier": { "printWidth": 110, "semi": false diff --git a/packages/http/package.json b/packages/http/package.json index 19861fe..c6a5a52 100644 --- a/packages/http/package.json +++ b/packages/http/package.json @@ -12,9 +12,10 @@ "semi": false }, "dependencies": { - "hono": "^4.7.11", + "hono": "catalog:", "@workshop/nano-remix": "workspace:*", - "@workshop/shared": "workspace:*" + "@workshop/shared": "workspace:*", + "@workshop/todo": "workspace:*" }, "devDependencies": { "@types/bun": "latest" diff --git a/packages/http/src/routes/todos.tsx b/packages/http/src/routes/todos.tsx deleted file mode 100644 index fa04354..0000000 --- a/packages/http/src/routes/todos.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import KV from "@workshop/shared/kv" -import { Form, type Head, type LoaderProps, useAction } from "@workshop/nano-remix" -import { addReminder, deleteReminder, type Reminder } from "@workshop/shared/reminders" -import { CreateReminder } from "@/components/createReminder" - -export const head: Head = { - title: "Todos", - scripts: [{ src: "https://cdn.tailwindcss.com" }], -} - -export const loader = async (req: Request) => { - const todos = await KV.get("todos", {}) - return { todos } -} - -export const action = async (req: Request) => { - const formData = await req.formData() - const action = formData.get("_action") as string - - // Handle delete action - if (action === "delete") { - const id = formData.get("id") as string - if (!id) { - return { error: "Reminder ID is required" } - } - - try { - await deleteReminder(id) - return { success: true, message: "Reminder deleted successfully" } - } catch (error) { - return { error: `Failed to delete reminder: ${error}` } - } - } else if (action === "create") { - // Handle create action (default) - const title = formData.get("title") as string - const dueDate = formData.get("dueDate") as string - const assignee = (formData.get("assignee") as string) || undefined - - if (!title) { - return { error: "Title is required" } - } - - if (!dueDate) { - return { error: "Due date is required" } - } - - try { - const newReminder = await addReminder(title, dueDate, assignee as any) - return { success: true, newReminder } - } catch (error) { - return { error: `Failed to create reminder: ${error}` } - } - } -} - -export default (props: LoaderProps) => { - const { data, loading, error } = useAction() - - return ( -
-

Reminders

-
-
-

Current Reminders

- -
-
- -
-
-
- ) -} - -const Reminders = ({ reminders }: { reminders: Reminder[] }) => { - return ( - <> - {reminders.length === 0 ? ( -

No reminders found.

- ) : ( - - )} - - ) -} diff --git a/packages/http/src/routes/todos/[id].tsx b/packages/http/src/routes/todos/[id].tsx new file mode 100644 index 0000000..e02ad35 --- /dev/null +++ b/packages/http/src/routes/todos/[id].tsx @@ -0,0 +1,47 @@ +import KV from "@workshop/shared/kv" +import { type Head, type LoaderProps } from "@workshop/nano-remix" +import { TodoEditor } from "@workshop/todo" + +export const head: Head = { + title: "Todos", + scripts: [{ src: "https://cdn.tailwindcss.com" }], +} + +export const loader = async (req: Request, params: { id: string }) => { + const todos = await KV.get("todos", {}) + return { todos: todos[params.id], todoName: params.id } +} + +export const action = async (req: Request, params: { id: string }) => { + console.log(`🌭 ACTION ${params}`) + const formData = await req.formData() + const todosString = formData.get("todos") as string + await KV.update("todos", {}, (todos) => { + todos[params.id] = todosString + return todos + }) + + return { success: true } +} + +export default ({ todos, todoName }: LoaderProps) => { + return ( +
+

Hey

+
+ { + const formData = new FormData() + console.log(`🌭`, todoName) + formData.append("todos", newTodos) + const response = await fetch(`/todos/${todoName}`, { + method: "POST", + body: formData, + headers: { Accept: "application/json" }, + }) + }} + /> +
+ ) +} diff --git a/packages/http/src/server.tsx b/packages/http/src/server.tsx index b584bda..5221f32 100644 --- a/packages/http/src/server.tsx +++ b/packages/http/src/server.tsx @@ -12,10 +12,7 @@ function startServer(opts: StartOptions) { "/*": (req) => nanoRemix(req, opts), }, - development: process.env.NODE_ENV !== "production" && { - hmr: true, - console: true, - }, + development: process.env.NODE_ENV !== "production" && { hmr: true, console: true }, }) console.log(`🤖 Server running at ${server.url}`) @@ -25,4 +22,4 @@ if (import.meta.main) { startServer({ routesDir: join(import.meta.dir, "routes") }) } -export { startServer } \ No newline at end of file +export { startServer } diff --git a/packages/nano-remix/package.json b/packages/nano-remix/package.json index 9d4ce43..9343f41 100644 --- a/packages/nano-remix/package.json +++ b/packages/nano-remix/package.json @@ -11,7 +11,7 @@ "typescript": "^5" }, "dependencies": { - "hono": "^4.7.11" + "hono": "catalog:" }, "devDependencies": { "@types/bun": "latest" diff --git a/packages/nano-remix/src/main.ts b/packages/nano-remix/src/main.ts index ea73686..ed931f8 100644 --- a/packages/nano-remix/src/main.ts +++ b/packages/nano-remix/src/main.ts @@ -4,9 +4,16 @@ import { Form, useAction, wrapComponentWithLoader } from "@/clientHelpers" export { Form, useAction, wrapComponentWithLoader } export { nanoRemix } -export type Loader = (req: Request) => Promise | Data +export type Loader = ( + req: Request, + params: Record +) => Promise | Data export type LoaderProps = T extends Loader ? Awaited : never -export type Action = (req: Request) => Promise + +export type Action = ( + req: Request, + params: Record +) => Promise export type Head = { title?: string diff --git a/packages/nano-remix/src/nanoRemix.ts b/packages/nano-remix/src/nanoRemix.ts index 171ff45..207ee1e 100644 --- a/packages/nano-remix/src/nanoRemix.ts +++ b/packages/nano-remix/src/nanoRemix.ts @@ -21,6 +21,8 @@ export const nanoRemix = async (req: Request, options: Options = {}) => { const basename = ext ? url.pathname.slice(0, -ext.length) : url.pathname const route = router.match(basename) + console.log(`🌭 requesting ${url} got ${route?.pathname}`) + if (!route) { return new Response("Route Not Found", { status: 404, @@ -29,7 +31,6 @@ export const nanoRemix = async (req: Request, options: Options = {}) => { } const routeName = route.name === "/" ? "/index" : route.name - if (!ext) { await buildDynamicRoute(distDir, routeName, route.filePath) // Eventually this should be running only on initial build and when a route changes return await renderServer(req, route) @@ -41,7 +42,6 @@ export const nanoRemix = async (req: Request, options: Options = {}) => { headers: { "Content-Type": "text/plain" }, }) } - return new Response(file) } } diff --git a/packages/nano-remix/src/renderServer.tsx b/packages/nano-remix/src/renderServer.tsx index 578919b..4fe0632 100644 --- a/packages/nano-remix/src/renderServer.tsx +++ b/packages/nano-remix/src/renderServer.tsx @@ -22,9 +22,9 @@ const handleAction = async (req: Request, route: Bun.MatchedRoute) => { ) } - const actionData = await action(req) + const actionData = await action(req, route.params) if (actionData instanceof Response) return actionData // This should only happen if the action wants to redirect - const loaderData = await loader?.(req) + const loaderData = await loader?.(req, route.params) const result = { actionData, loaderData } return new Response(JSON.stringify(result), { @@ -35,8 +35,8 @@ const handleAction = async (req: Request, route: Bun.MatchedRoute) => { const renderHtml = async (req: Request, route: Bun.MatchedRoute) => { const component = await import(route.filePath) - const loader = component.loader - const loaderData = loader ? await loader(req) : {} + const loader = component.loader as Loader + const loaderData = loader ? await loader(req, route.params) : {} const routeName = route.name === "/" ? "/index" : route.name diff --git a/packages/shared/src/kv.ts b/packages/shared/src/kv.ts index 07af1cc..2c7356d 100644 --- a/packages/shared/src/kv.ts +++ b/packages/shared/src/kv.ts @@ -1,6 +1,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs" import { dirname, join } from "node:path" import type { Reminder } from "@/reminders" +import type { Todo } from "@workshop/todo" export type Conversation = { message: string; role: "user" | "assistant" } export interface Keys { diff --git a/packages/spike2/src/cli/index.ts b/packages/spike2/src/cli/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/todo/index.ts b/packages/todo/index.ts deleted file mode 100644 index f67b2c6..0000000 --- a/packages/todo/index.ts +++ /dev/null @@ -1 +0,0 @@ -console.log("Hello via Bun!"); \ No newline at end of file diff --git a/packages/todo/package.json b/packages/todo/package.json index e5bdd7d..cbe1d00 100644 --- a/packages/todo/package.json +++ b/packages/todo/package.json @@ -1,7 +1,8 @@ { - "name": "todo", - "module": "index.ts", + "name": "@workshop/todo", + "module": "src/main.ts", "type": "module", + "types": "src/main.ts", "private": true, "dependencies": { "@codemirror/commands": "^6.8.1", @@ -11,8 +12,8 @@ "@lezer/generator": "^1.7.3", "@lezer/highlight": "^1.2.1", "@lezer/lr": "^1.4.2", - "hono": "^4.8.0", - "luxon": "^3.6.1", + "hono": "catalog:", + "luxon": "^3.6.1" }, "prettier": { "semi": false, @@ -22,6 +23,7 @@ "@types/bun": "latest" }, "peerDependencies": { - "typescript": "^5" + "typescript": "^5", + "hono": "^4" } } \ No newline at end of file diff --git a/packages/todo/src/index.css b/packages/todo/src/index.css new file mode 100644 index 0000000..e9316fe --- /dev/null +++ b/packages/todo/src/index.css @@ -0,0 +1,32 @@ +/* Todo styling */ +.todo-completed { + text-decoration: line-through; + color: #6B7280; +} + +.todo-tag { + color: #3B82F6; +} + +.todo-completed .todo-tag, .todo-completed .todo-date { + color: #6B7280; +} + +.todo-date { + color: #10B981; +} + +.todo-invalid { + text-decoration-line: underline; + text-decoration-color: #EF4444; + text-decoration-style: wavy; +} + +.todo-header { + font-size: 1.25rem; + font-weight: 700; +} + +.todo-filtered { + background-color: greenyellow; +} \ No newline at end of file diff --git a/packages/todo/src/main.ts b/packages/todo/src/main.ts index 7ed3a77..3599457 100644 --- a/packages/todo/src/main.ts +++ b/packages/todo/src/main.ts @@ -1,3 +1,3 @@ -export {TodoEditor } from "@/todoEditor" -export {parseTodoLine, parseTodoList} from "@/todo" -export type {Todo} from "@/todo" \ No newline at end of file +export { TodoEditor } from "@/todoEditor" +export { parseTodoLine, parseTodoList } from "@/todo" +export type { Todo } from "@/todo" diff --git a/packages/todo/src/todoDecorations.ts b/packages/todo/src/todoDecorations.ts index ff7014d..1c4a1a2 100644 --- a/packages/todo/src/todoDecorations.ts +++ b/packages/todo/src/todoDecorations.ts @@ -1,7 +1,7 @@ -import { parseTodoLine, Todo } from "@/todo/todo" +import { parseTodoLine, type Todo } from "@/todo" import { RangeSetBuilder, StateEffect } from "@codemirror/state" import { EditorView, Decoration, ViewPlugin, ViewUpdate } from "@codemirror/view" -import { RefObject } from "hono/jsx" +import { type RefObject } from "hono/jsx" // Effect to trigger a decoration refresh on filter change export const refreshFilterEffect = StateEffect.define() diff --git a/packages/todo/src/todoEditor.tsx b/packages/todo/src/todoEditor.tsx index 8b72541..bd32d9e 100644 --- a/packages/todo/src/todoEditor.tsx +++ b/packages/todo/src/todoEditor.tsx @@ -1,17 +1,22 @@ -import { useCallback, useEffect, useRef, useState } from "hono/jsx" +import { useCallback, useEffect, useRef, useState, type KeyboardEvent } from "hono/jsx" import { defaultKeymap, history, historyKeymap } from "@codemirror/commands" import { EditorState } from "@codemirror/state" import { EditorView, lineNumbers, keymap } from "@codemirror/view" import { keymap as viewKeymap } from "@codemirror/view" // ensure keymap is imported from view if not already import { foldGutter, foldKeymap } from "@codemirror/language" -import { refreshFilterEffect, todoDecorations } from "@/todo/todoDecorations" -import { autoTodoOnNewline } from "@/todo/autoTodoOnNewline" -import { todoKeymap } from "@/todo/todoKeymap" +import { refreshFilterEffect, todoDecorations } from "@/todoDecorations" +import { autoTodoOnNewline } from "@/autoTodoOnNewline" +import { todoKeymap } from "@/todoKeymap" import { DateTime } from "luxon" import "./index.css" -export const TodoEditor = () => { +type TodoEditorProps = { + todos?: string + onChange: (todos: string) => void +} + +export const TodoEditor = ({ todos, onChange }: TodoEditorProps) => { const editorContainer = useRef(null) const editorRef = useRef(null) const [filter, setFilter] = useState("") @@ -28,13 +33,14 @@ export const TodoEditor = () => { if (!editorContainer.current) return const changeListener = EditorView.updateListener.of((update) => { - localStorage.setItem("todo-editor-content", update.state.doc.toString()) + if (update.docChanged) { + onChange(update.state.doc.toString()) + } }) - const savedDoc = localStorage.getItem("todo-editor-content") - const docText = savedDoc ?? defaultDoc + // Use todos prop for content const state = EditorState.create({ - doc: docText, + doc: todos || defaultDoc, extensions: [ foldGutter(), lineNumbers(), @@ -54,7 +60,7 @@ export const TodoEditor = () => { editorRef.current = view return () => view.destroy() - }, []) + }, [todos, onChange]) const filterInput = useCallback((e: KeyboardEvent) => { if (e.key === "Enter" || e.key === "Escape") { @@ -67,7 +73,7 @@ export const TodoEditor = () => { }, []) return ( -
+
{ ) } -export default App - const defaultDoc = ` # Today (Group tasks by when they are due) -- [ ] Sample task with a due date @${DateTime.local().toFormat("yyyy/MM/dd")} +- [ ] Sample task with a due date @${DateTime.local().toFormat("MM/dd/yyyy")} - [ ] You can use a #tag to filter tasks - [ ] A sub task! Create nested tasks by indenting with - [x] Complete a task by pressing # This week -- [ ] Another task with a due date @${DateTime.local().plus({ days: 3 }).toFormat("yyyy/MM/dd")} +- [ ] Another task with a due date @${DateTime.local().plus({ days: 3 }).toFormat("MM/dd/yyyy")} # Later - [ ] I use later as a junk drawer for tasks I don't want to forget diff --git a/packages/todo/src/todoKeymap.ts b/packages/todo/src/todoKeymap.ts index 8c8db04..fe6e7b8 100644 --- a/packages/todo/src/todoKeymap.ts +++ b/packages/todo/src/todoKeymap.ts @@ -1,7 +1,7 @@ import { indentMore, indentLess } from "@codemirror/commands" import { EditorView, keymap } from "@codemirror/view" import { parseTodoLine, todoToText } from "./todo" -import { RefObject } from "hono/jsx" +import { type RefObject } from "hono/jsx" export const todoKeymap = (filterElRef: RefObject) => { return keymap.of([ diff --git a/packages/todo/tsconfig.json b/packages/todo/tsconfig.json index dd311f4..4410a1d 100644 --- a/packages/todo/tsconfig.json +++ b/packages/todo/tsconfig.json @@ -1,11 +1,12 @@ { "compilerOptions": { // Environment setup & latest features - "lib": ["ESNext"], + "lib": ["ESNext", "DOM"], "target": "ESNext", "module": "Preserve", "moduleDetection": "force", "jsx": "react-jsx", + "jsxImportSource": "hono/jsx", "allowJs": true, // Bundler mode