[WIP] book tracker on the atmosphere~
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: init

* stuff doesn't work * boilerplate generated by AI regrettably * lexicon
structure is nowhere near finalised

willow 744a6e50 09d72826

+1691 -303
+374
bun.lock
··· 4 4 "": { 5 5 "name": "oxybrary", 6 6 "dependencies": { 7 + "@atproto/api": "^0.17.0", 8 + "@atproto/common": "^0.4.12", 9 + "@atproto/identity": "^0.4.9", 10 + "@atproto/oauth-client-node": "^0.3.8", 11 + "@atproto/sync": "^0.1.35", 12 + "@atproto/syntax": "^0.4.1", 13 + "@atproto/xrpc-server": "^0.9.5", 14 + "@catppuccin/tailwindcss": "^1.0.0", 7 15 "@node-rs/argon2": "^2.0.2", 16 + "openbook.js": "github:42willow/openbook.js", 8 17 }, 9 18 "devDependencies": { 19 + "@atproto/lex-cli": "^0.9.5", 10 20 "@libsql/client": "^0.14.0", 11 21 "@oslojs/crypto": "^1.0.1", 12 22 "@oslojs/encoding": "^1.1.0", ··· 29 39 }, 30 40 }, 31 41 "packages": { 42 + "@atproto-labs/did-resolver": ["@atproto-labs/did-resolver@0.2.1", "", { "dependencies": { "@atproto-labs/fetch": "0.2.3", "@atproto-labs/pipe": "0.1.1", "@atproto-labs/simple-store": "0.3.0", "@atproto-labs/simple-store-memory": "0.1.4", "@atproto/did": "0.2.0", "zod": "^3.23.8" } }, "sha512-zSoHyqwwRYUtMNLW+RrWsImt1U5S47nJv5FfmAXTmon6wVKjxKD/PFrD1pg/4G6THqJmQHTs1Hj+54XVupYnvQ=="], 43 + 44 + "@atproto-labs/fetch": ["@atproto-labs/fetch@0.2.3", "", { "dependencies": { "@atproto-labs/pipe": "0.1.1" } }, "sha512-NZtbJOCbxKUFRFKMpamT38PUQMY0hX0p7TG5AEYOPhZKZEP7dHZ1K2s1aB8MdVH0qxmqX7nQleNrrvLf09Zfdw=="], 45 + 46 + "@atproto-labs/fetch-node": ["@atproto-labs/fetch-node@0.1.10", "", { "dependencies": { "@atproto-labs/fetch": "0.2.3", "@atproto-labs/pipe": "0.1.1", "ipaddr.js": "^2.1.0", "undici": "^6.14.1" } }, "sha512-o7hGaonA71A6p7O107VhM6UBUN/g9tTyYohMp1q0Kf6xQ4npnuZYRSHSf2g6reSfGQJ1GoFNjBObETTT1ge/jQ=="], 47 + 48 + "@atproto-labs/handle-resolver": ["@atproto-labs/handle-resolver@0.3.1", "", { "dependencies": { "@atproto-labs/simple-store": "0.3.0", "@atproto-labs/simple-store-memory": "0.1.4", "@atproto/did": "0.2.0", "zod": "^3.23.8" } }, "sha512-mLZdMNvwomgnn9sffKO1/xr02ctgeiT0FUVw7JekbchTckub2RM7qMu8Rw1mC4bpCpW+i7DXDiOxpoajkppwYQ=="], 49 + 50 + "@atproto-labs/handle-resolver-node": ["@atproto-labs/handle-resolver-node@0.1.19", "", { "dependencies": { "@atproto-labs/fetch-node": "0.1.10", "@atproto-labs/handle-resolver": "0.3.1", "@atproto/did": "0.2.0" } }, "sha512-nNVCfiKudvMYfDcWCa9koOMOpCYaC0wG4Uys5dZev99s/Nka7tRlIZIV+u+GWivnG9lqCupKATkoyCd6Per8Gw=="], 51 + 52 + "@atproto-labs/identity-resolver": ["@atproto-labs/identity-resolver@0.3.1", "", { "dependencies": { "@atproto-labs/did-resolver": "0.2.1", "@atproto-labs/handle-resolver": "0.3.1" } }, "sha512-jCgotRRqPykPwh4gh0FBLOqeofv1G8OH/DZ5s88HWm7biUZeksZwDrEvL5TnqEFUpXT3O9Hcyp/XEpfCAplRoQ=="], 53 + 54 + "@atproto-labs/pipe": ["@atproto-labs/pipe@0.1.1", "", {}, "sha512-hdNw2oUs2B6BN1lp+32pF7cp8EMKuIN5Qok2Vvv/aOpG/3tNSJ9YkvfI0k6Zd188LeDDYRUpYpxcoFIcGH/FNg=="], 55 + 56 + "@atproto-labs/simple-store": ["@atproto-labs/simple-store@0.3.0", "", {}, "sha512-nOb6ONKBRJHRlukW1sVawUkBqReLlLx6hT35VS3imaNPwiXDxLnTK7lxw3Lrl9k5yugSBDQAkZAq3MPTEFSUBQ=="], 57 + 58 + "@atproto-labs/simple-store-memory": ["@atproto-labs/simple-store-memory@0.1.4", "", { "dependencies": { "@atproto-labs/simple-store": "0.3.0", "lru-cache": "^10.2.0" } }, "sha512-3mKY4dP8I7yKPFj9VKpYyCRzGJOi5CEpOLPlRhoJyLmgs3J4RzDrjn323Oakjz2Aj2JzRU/AIvWRAZVhpYNJHw=="], 59 + 60 + "@atproto/api": ["@atproto/api@0.17.0", "", { "dependencies": { "@atproto/common-web": "^0.4.3", "@atproto/lexicon": "^0.5.1", "@atproto/syntax": "^0.4.1", "@atproto/xrpc": "^0.7.5", "await-lock": "^2.2.2", "multiformats": "^9.9.0", "tlds": "^1.234.0", "zod": "^3.23.8" } }, "sha512-FNS9SW7/3kslAnJH7F4fO9/jPjXzC0NMD6u9NjJ/h4EnaIEpWHZQPkmD9Q2hvAwD6+Uo2boYZEPKkOa55Lr5Dg=="], 61 + 62 + "@atproto/common": ["@atproto/common@0.4.12", "", { "dependencies": { "@atproto/common-web": "^0.4.3", "@ipld/dag-cbor": "^7.0.3", "cbor-x": "^1.5.1", "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "pino": "^8.21.0" } }, "sha512-NC+TULLQiqs6MvNymhQS5WDms3SlbIKGLf4n33tpftRJcalh507rI+snbcUb7TLIkKw7VO17qMqxEXtIdd5auQ=="], 63 + 64 + "@atproto/common-web": ["@atproto/common-web@0.4.3", "", { "dependencies": { "graphemer": "^1.4.0", "multiformats": "^9.9.0", "uint8arrays": "3.0.0", "zod": "^3.23.8" } }, "sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg=="], 65 + 66 + "@atproto/crypto": ["@atproto/crypto@0.4.4", "", { "dependencies": { "@noble/curves": "^1.7.0", "@noble/hashes": "^1.6.1", "uint8arrays": "3.0.0" } }, "sha512-Yq9+crJ7WQl7sxStVpHgie5Z51R05etaK9DLWYG/7bR5T4bhdcIgF6IfklLShtZwLYdVVj+K15s0BqW9a8PSDA=="], 67 + 68 + "@atproto/did": ["@atproto/did@0.2.0", "", { "dependencies": { "zod": "^3.23.8" } }, "sha512-BskT39KYbwY1DUsWekkHh47xS+wvJpFq5F9acsicNfYniinyAMnNTzGKQEhnjQuG7K0qQItg/SnmC+y0tJXV7Q=="], 69 + 70 + "@atproto/identity": ["@atproto/identity@0.4.9", "", { "dependencies": { "@atproto/common-web": "^0.4.3", "@atproto/crypto": "^0.4.4" } }, "sha512-pRYCaeaEJMZ4vQlRQYYTrF3cMiRp21n/k/pUT1o7dgKby56zuLErDmFXkbKfKWPf7SgWRgamSaNmsGLqAOD7lQ=="], 71 + 72 + "@atproto/jwk": ["@atproto/jwk@0.5.0", "", { "dependencies": { "multiformats": "^9.9.0", "zod": "^3.23.8" } }, "sha512-Qi2NtEqhkG+uz3CKia4+H05WMV/z//dz3ESo5+cyBKrOnxVTJ5ZubMyltWjoYvy6v/jLhorXdDWcjn07yky7MQ=="], 73 + 74 + "@atproto/jwk-jose": ["@atproto/jwk-jose@0.1.10", "", { "dependencies": { "@atproto/jwk": "0.5.0", "jose": "^5.2.0" } }, "sha512-Eiu/u4tZHz3IIhHZt0zneYEffSAO3Oqk/ToKwlu1TqKte6sjtPs/4uquSiAAGFYozqgo92JC/AQclWzzkHI5QQ=="], 75 + 76 + "@atproto/jwk-webcrypto": ["@atproto/jwk-webcrypto@0.1.10", "", { "dependencies": { "@atproto/jwk": "0.5.0", "@atproto/jwk-jose": "0.1.10", "zod": "^3.23.8" } }, "sha512-JZsavs6JiSmw5rgcjkGDwzr1aCJGdybZOjVfYH+m9sXRU1BrUCA30uwNfZY7eFyWXyRAnCFiYiGVZgypXyKotw=="], 77 + 78 + "@atproto/lex-cli": ["@atproto/lex-cli@0.9.5", "", { "dependencies": { "@atproto/lexicon": "^0.5.1", "@atproto/syntax": "^0.4.1", "chalk": "^4.1.2", "commander": "^9.4.0", "prettier": "^3.2.5", "ts-morph": "^24.0.0", "yesno": "^0.4.0", "zod": "^3.23.8" }, "bin": { "lex": "dist/index.js" } }, "sha512-zun4jhD1dbjD7IHtLIjh/1UsMx+6E8+OyOT2GXYAKIj9N6wmLKM/v2OeQBKxcyqUmtRi57lxWnGikWjjU7pplQ=="], 79 + 80 + "@atproto/lexicon": ["@atproto/lexicon@0.5.1", "", { "dependencies": { "@atproto/common-web": "^0.4.3", "@atproto/syntax": "^0.4.1", "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "zod": "^3.23.8" } }, "sha512-y8AEtYmfgVl4fqFxqXAeGvhesiGkxiy3CWoJIfsFDDdTlZUC8DFnZrYhcqkIop3OlCkkljvpSJi1hbeC1tbi8A=="], 81 + 82 + "@atproto/oauth-client": ["@atproto/oauth-client@0.5.6", "", { "dependencies": { "@atproto-labs/did-resolver": "0.2.1", "@atproto-labs/fetch": "0.2.3", "@atproto-labs/handle-resolver": "0.3.1", "@atproto-labs/identity-resolver": "0.3.1", "@atproto-labs/simple-store": "0.3.0", "@atproto-labs/simple-store-memory": "0.1.4", "@atproto/did": "0.2.0", "@atproto/jwk": "0.5.0", "@atproto/oauth-types": "0.4.1", "@atproto/xrpc": "0.7.5", "multiformats": "^9.9.0", "zod": "^3.23.8" } }, "sha512-O1S9lPptJxWPcNd2kODaLgWntz+A7PzskU2hP4IFa7hVLs4aEnEt9dKq5wJE97tDli8mgyh/ndPQhxUaCVQ5iQ=="], 83 + 84 + "@atproto/oauth-client-node": ["@atproto/oauth-client-node@0.3.8", "", { "dependencies": { "@atproto-labs/did-resolver": "0.2.1", "@atproto-labs/handle-resolver-node": "0.1.19", "@atproto-labs/simple-store": "0.3.0", "@atproto/did": "0.2.0", "@atproto/jwk": "0.5.0", "@atproto/jwk-jose": "0.1.10", "@atproto/jwk-webcrypto": "0.1.10", "@atproto/oauth-client": "0.5.6", "@atproto/oauth-types": "0.4.1" } }, "sha512-HIBiYQERj04Xa0l8cJkqcZC0BbHH5uqDEvhqHWnJ5umSq/ms0+HZi3JKJXGv1XfYOvxUxx6NKgXJ8VhhYoQa5A=="], 85 + 86 + "@atproto/oauth-types": ["@atproto/oauth-types@0.4.1", "", { "dependencies": { "@atproto/jwk": "0.5.0", "zod": "^3.23.8" } }, "sha512-c5ixf2ZOzcltOu1fDBnO/tok6Wj7JDDK66+Z0q/+bAr8LXgOnxP7zQfJ+DD4gTkB+saTqsqWtVv8qvx/IEtm1g=="], 87 + 88 + "@atproto/repo": ["@atproto/repo@0.8.10", "", { "dependencies": { "@atproto/common": "^0.4.12", "@atproto/common-web": "^0.4.3", "@atproto/crypto": "^0.4.4", "@atproto/lexicon": "^0.5.1", "@ipld/dag-cbor": "^7.0.0", "multiformats": "^9.9.0", "uint8arrays": "3.0.0", "varint": "^6.0.0", "zod": "^3.23.8" } }, "sha512-REs6TZGyxNaYsjqLf447u+gSdyzhvMkVbxMBiKt1ouEVRkiho1CY32+omn62UkpCuGK2y6SCf6x3sVMctgmX4g=="], 89 + 90 + "@atproto/sync": ["@atproto/sync@0.1.35", "", { "dependencies": { "@atproto/common": "^0.4.12", "@atproto/identity": "^0.4.9", "@atproto/lexicon": "^0.5.1", "@atproto/repo": "^0.8.10", "@atproto/syntax": "^0.4.1", "@atproto/xrpc-server": "^0.9.5", "multiformats": "^9.9.0", "p-queue": "^6.6.2", "ws": "^8.12.0" } }, "sha512-MPvmTjJYCilZEQF1ds7itzF9tNEZtw4Ez0HeMO5E5GaPtTAccBU3AsTxwWST87EX5qsVxMlBTq2go6G6+Swd7Q=="], 91 + 92 + "@atproto/syntax": ["@atproto/syntax@0.4.1", "", {}, "sha512-CJdImtLAiFO+0z3BWTtxwk6aY5w4t8orHTMVJgkf++QRJWTxPbIFko/0hrkADB7n2EruDxDSeAgfUGehpH6ngw=="], 93 + 94 + "@atproto/xrpc": ["@atproto/xrpc@0.7.5", "", { "dependencies": { "@atproto/lexicon": "^0.5.1", "zod": "^3.23.8" } }, "sha512-MUYNn5d2hv8yVegRL0ccHvTHAVj5JSnW07bkbiaz96UH45lvYNRVwt44z+yYVnb0/mvBzyD3/ZQ55TRGt7fHkA=="], 95 + 96 + "@atproto/xrpc-server": ["@atproto/xrpc-server@0.9.5", "", { "dependencies": { "@atproto/common": "^0.4.12", "@atproto/crypto": "^0.4.4", "@atproto/lexicon": "^0.5.1", "@atproto/xrpc": "^0.7.5", "cbor-x": "^1.5.1", "express": "^4.17.2", "http-errors": "^2.0.0", "mime-types": "^2.1.35", "rate-limiter-flexible": "^2.4.1", "uint8arrays": "3.0.0", "ws": "^8.12.0", "zod": "^3.23.8" } }, "sha512-V0srjUgy6mQ5yf9+MSNBLs457m4qclEaWZsnqIE7RfYywvntexTAbMoo7J7ONfTNwdmA9Gw4oLak2z2cDAET4w=="], 97 + 98 + "@catppuccin/tailwindcss": ["@catppuccin/tailwindcss@1.0.0", "", {}, "sha512-l8pOlcYe2ncGd8a1gUmL5AHmKlxR2+CHuG5kt4Me6IZwzntW1DoLmj89BH+DcsPHBsdDGLrTSv35emlYyU3FeQ=="], 99 + 100 + "@cbor-extract/cbor-extract-darwin-arm64": ["@cbor-extract/cbor-extract-darwin-arm64@2.2.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-P7swiOAdF7aSi0H+tHtHtr6zrpF3aAq/W9FXx5HektRvLTM2O89xCyXF3pk7pLc7QpaY7AoaE8UowVf9QBdh3w=="], 101 + 102 + "@cbor-extract/cbor-extract-darwin-x64": ["@cbor-extract/cbor-extract-darwin-x64@2.2.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-1liF6fgowph0JxBbYnAS7ZlqNYLf000Qnj4KjqPNW4GViKrEql2MgZnAsExhY9LSy8dnvA4C0qHEBgPrll0z0w=="], 103 + 104 + "@cbor-extract/cbor-extract-linux-arm": ["@cbor-extract/cbor-extract-linux-arm@2.2.0", "", { "os": "linux", "cpu": "arm" }, "sha512-QeBcBXk964zOytiedMPQNZr7sg0TNavZeuUCD6ON4vEOU/25+pLhNN6EDIKJ9VLTKaZ7K7EaAriyYQ1NQ05s/Q=="], 105 + 106 + "@cbor-extract/cbor-extract-linux-arm64": ["@cbor-extract/cbor-extract-linux-arm64@2.2.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-rQvhNmDuhjTVXSPFLolmQ47/ydGOFXtbR7+wgkSY0bdOxCFept1hvg59uiLPT2fVDuJFuEy16EImo5tE2x3RsQ=="], 107 + 108 + "@cbor-extract/cbor-extract-linux-x64": ["@cbor-extract/cbor-extract-linux-x64@2.2.0", "", { "os": "linux", "cpu": "x64" }, "sha512-cWLAWtT3kNLHSvP4RKDzSTX9o0wvQEEAj4SKvhWuOVZxiDAeQazr9A+PSiRILK1VYMLeDml89ohxCnUNQNQNCw=="], 109 + 110 + "@cbor-extract/cbor-extract-win32-x64": ["@cbor-extract/cbor-extract-win32-x64@2.2.0", "", { "os": "win32", "cpu": "x64" }, "sha512-l2M+Z8DO2vbvADOBNLbbh9y5ST1RY5sqkWOg/58GkUPBYou/cuNZ68SGQ644f1CvZ8kcOxyZtw06+dxWHIoN/w=="], 111 + 32 112 "@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="], 33 113 34 114 "@emnapi/core": ["@emnapi/core@1.5.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="], ··· 93 173 94 174 "@esbuild/win32-x64": ["@esbuild/win32-x64@0.19.12", "", { "os": "win32", "cpu": "x64" }, "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA=="], 95 175 176 + "@ipld/dag-cbor": ["@ipld/dag-cbor@7.0.3", "", { "dependencies": { "cborg": "^1.6.0", "multiformats": "^9.5.4" } }, "sha512-1VVh2huHsuohdXC1bGJNE8WR72slZ9XE2T3wbBBq31dm7ZBatmKLLxrB+XAqafxfRFjv08RZmj/W/ZqaM13AuA=="], 177 + 96 178 "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], 97 179 98 180 "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], ··· 133 215 134 216 "@neon-rs/load": ["@neon-rs/load@0.0.4", "", {}, "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw=="], 135 217 218 + "@noble/curves": ["@noble/curves@1.9.7", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw=="], 219 + 220 + "@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], 221 + 136 222 "@node-rs/argon2": ["@node-rs/argon2@2.0.2", "", { "optionalDependencies": { "@node-rs/argon2-android-arm-eabi": "2.0.2", "@node-rs/argon2-android-arm64": "2.0.2", "@node-rs/argon2-darwin-arm64": "2.0.2", "@node-rs/argon2-darwin-x64": "2.0.2", "@node-rs/argon2-freebsd-x64": "2.0.2", "@node-rs/argon2-linux-arm-gnueabihf": "2.0.2", "@node-rs/argon2-linux-arm64-gnu": "2.0.2", "@node-rs/argon2-linux-arm64-musl": "2.0.2", "@node-rs/argon2-linux-x64-gnu": "2.0.2", "@node-rs/argon2-linux-x64-musl": "2.0.2", "@node-rs/argon2-wasm32-wasi": "2.0.2", "@node-rs/argon2-win32-arm64-msvc": "2.0.2", "@node-rs/argon2-win32-ia32-msvc": "2.0.2", "@node-rs/argon2-win32-x64-msvc": "2.0.2" } }, "sha512-t64wIsPEtNd4aUPuTAyeL2ubxATCBGmeluaKXEMAFk/8w6AJIVVkeLKMBpgLW6LU2t5cQxT+env/c6jxbtTQBg=="], 137 223 138 224 "@node-rs/argon2-android-arm-eabi": ["@node-rs/argon2-android-arm-eabi@2.0.2", "", { "os": "android", "cpu": "arm" }, "sha512-DV/H8p/jt40lrao5z5g6nM9dPNPGEHL+aK6Iy/og+dbL503Uj0AHLqj1Hk9aVUSCNnsDdUEKp4TVMi0YakDYKw=="], ··· 261 347 262 348 "@tailwindcss/vite": ["@tailwindcss/vite@4.1.14", "", { "dependencies": { "@tailwindcss/node": "4.1.14", "@tailwindcss/oxide": "4.1.14", "tailwindcss": "4.1.14" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-BoFUoU0XqgCUS1UXWhmDJroKKhNXeDzD7/XwabjkDIAbMnc4ULn5e2FuEuBbhZ6ENZoSYzKlzvZ44Yr6EUDUSA=="], 263 349 350 + "@ts-morph/common": ["@ts-morph/common@0.25.0", "", { "dependencies": { "minimatch": "^9.0.4", "path-browserify": "^1.0.1", "tinyglobby": "^0.2.9" } }, "sha512-kMnZz+vGGHi4GoHnLmMhGNjm44kGtKUXGnOvrKmMwAuvNjM/PgKVGfUnL7IDvK7Jb2QQ82jq3Zmp04Gy+r3Dkg=="], 351 + 264 352 "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], 265 353 266 354 "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="], ··· 271 359 272 360 "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], 273 361 362 + "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], 363 + 364 + "accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="], 365 + 274 366 "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], 367 + 368 + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], 275 369 276 370 "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], 277 371 372 + "array-flatten": ["array-flatten@1.1.1", "", {}, "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="], 373 + 374 + "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], 375 + 376 + "atomic-sleep": ["atomic-sleep@1.0.0", "", {}, "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="], 377 + 378 + "await-lock": ["await-lock@2.2.2", "", {}, "sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw=="], 379 + 380 + "axios": ["axios@1.12.2", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw=="], 381 + 278 382 "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], 279 383 384 + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], 385 + 386 + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], 387 + 388 + "body-parser": ["body-parser@1.20.3", "", { "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" } }, "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g=="], 389 + 390 + "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], 391 + 392 + "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], 393 + 280 394 "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], 281 395 396 + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], 397 + 398 + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], 399 + 400 + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], 401 + 402 + "cbor-extract": ["cbor-extract@2.2.0", "", { "dependencies": { "node-gyp-build-optional-packages": "5.1.1" }, "optionalDependencies": { "@cbor-extract/cbor-extract-darwin-arm64": "2.2.0", "@cbor-extract/cbor-extract-darwin-x64": "2.2.0", "@cbor-extract/cbor-extract-linux-arm": "2.2.0", "@cbor-extract/cbor-extract-linux-arm64": "2.2.0", "@cbor-extract/cbor-extract-linux-x64": "2.2.0", "@cbor-extract/cbor-extract-win32-x64": "2.2.0" }, "bin": { "download-cbor-prebuilds": "bin/download-prebuilds.js" } }, "sha512-Ig1zM66BjLfTXpNgKpvBePq271BPOvu8MR0Jl080yG7Jsl+wAZunfrwiwA+9ruzm/WEdIV5QF/bjDZTqyAIVHA=="], 403 + 404 + "cbor-x": ["cbor-x@1.6.0", "", { "optionalDependencies": { "cbor-extract": "^2.2.0" } }, "sha512-0kareyRwHSkL6ws5VXHEf8uY1liitysCVJjlmhaLG+IXLqhSaOO+t63coaso7yjwEzWZzLy8fJo06gZDVQM9Qg=="], 405 + 406 + "cborg": ["cborg@1.10.2", "", { "bin": { "cborg": "cli.js" } }, "sha512-b3tFPA9pUr2zCUiCfRd2+wok2/LBSNUMKOuRRok+WlvvAgEt/PlbgPTsZUcwCOs53IJvLgTp0eotwtosE6njug=="], 407 + 408 + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], 409 + 282 410 "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], 283 411 284 412 "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], 285 413 286 414 "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], 287 415 416 + "code-block-writer": ["code-block-writer@13.0.3", "", {}, "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg=="], 417 + 418 + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], 419 + 420 + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], 421 + 422 + "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], 423 + 424 + "commander": ["commander@9.5.0", "", {}, "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ=="], 425 + 426 + "content-disposition": ["content-disposition@0.5.4", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ=="], 427 + 428 + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], 429 + 288 430 "cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="], 431 + 432 + "cookie-signature": ["cookie-signature@1.0.6", "", {}, "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="], 289 433 290 434 "data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="], 291 435 ··· 293 437 294 438 "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], 295 439 440 + "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], 441 + 442 + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], 443 + 444 + "destroy": ["destroy@1.2.0", "", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="], 445 + 296 446 "detect-libc": ["detect-libc@2.0.2", "", {}, "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw=="], 297 447 298 448 "devalue": ["devalue@5.3.2", "", {}, "sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw=="], ··· 301 451 302 452 "drizzle-orm": ["drizzle-orm@0.40.1", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-aPNhtiJiPfm3qxz1czrnIDkfvkSdKGXYeZkpG55NPTVI186LmK2fBLMi4dsHpPHlJrZeQ92D322YFPHADBALew=="], 303 453 454 + "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=="], 455 + 456 + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], 457 + 458 + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], 459 + 304 460 "enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="], 305 461 306 462 "env-paths": ["env-paths@3.0.0", "", {}, "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A=="], 463 + 464 + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], 465 + 466 + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], 467 + 468 + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], 469 + 470 + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], 307 471 308 472 "esbuild": ["esbuild@0.19.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.19.12", "@esbuild/android-arm": "0.19.12", "@esbuild/android-arm64": "0.19.12", "@esbuild/android-x64": "0.19.12", "@esbuild/darwin-arm64": "0.19.12", "@esbuild/darwin-x64": "0.19.12", "@esbuild/freebsd-arm64": "0.19.12", "@esbuild/freebsd-x64": "0.19.12", "@esbuild/linux-arm": "0.19.12", "@esbuild/linux-arm64": "0.19.12", "@esbuild/linux-ia32": "0.19.12", "@esbuild/linux-loong64": "0.19.12", "@esbuild/linux-mips64el": "0.19.12", "@esbuild/linux-ppc64": "0.19.12", "@esbuild/linux-riscv64": "0.19.12", "@esbuild/linux-s390x": "0.19.12", "@esbuild/linux-x64": "0.19.12", "@esbuild/netbsd-x64": "0.19.12", "@esbuild/openbsd-x64": "0.19.12", "@esbuild/sunos-x64": "0.19.12", "@esbuild/win32-arm64": "0.19.12", "@esbuild/win32-ia32": "0.19.12", "@esbuild/win32-x64": "0.19.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg=="], 309 473 310 474 "esbuild-register": ["esbuild-register@3.6.0", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "esbuild": ">=0.12 <1" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="], 311 475 476 + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], 477 + 312 478 "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="], 313 479 314 480 "esrap": ["esrap@2.1.0", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA=="], 315 481 482 + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], 483 + 484 + "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="], 485 + 486 + "eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="], 487 + 488 + "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], 489 + 490 + "express": ["express@4.21.2", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA=="], 491 + 492 + "fast-redact": ["fast-redact@3.5.0", "", {}, "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A=="], 493 + 316 494 "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], 317 495 318 496 "fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="], 319 497 498 + "finalhandler": ["finalhandler@1.3.1", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", "statuses": "2.0.1", "unpipe": "~1.0.0" } }, "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ=="], 499 + 500 + "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], 501 + 502 + "form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="], 503 + 320 504 "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="], 321 505 506 + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], 507 + 508 + "fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="], 509 + 322 510 "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 511 + 512 + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], 323 513 324 514 "gel": ["gel@2.1.1", "", { "dependencies": { "@petamoriken/float16": "^3.8.7", "debug": "^4.3.4", "env-paths": "^3.0.0", "semver": "^7.6.2", "shell-quote": "^1.8.1", "which": "^4.0.0" }, "bin": { "gel": "dist/cli.mjs" } }, "sha512-Newg9X7mRYskoBjSw70l1YnJ/ZGbq64VPyR821H5WVkTGpHG2O0mQILxCeUhxdYERLFY9B4tUyKLyf3uMTjtKw=="], 325 515 516 + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], 517 + 518 + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], 519 + 326 520 "get-tsconfig": ["get-tsconfig@4.10.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="], 521 + 522 + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], 327 523 328 524 "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], 329 525 526 + "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], 527 + 528 + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], 529 + 530 + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], 531 + 532 + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], 533 + 534 + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], 535 + 536 + "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=="], 537 + 538 + "iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], 539 + 540 + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], 541 + 542 + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], 543 + 544 + "ipaddr.js": ["ipaddr.js@2.2.0", "", {}, "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA=="], 545 + 330 546 "is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="], 331 547 332 548 "isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="], 333 549 550 + "iso-datestring-validator": ["iso-datestring-validator@2.2.2", "", {}, "sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA=="], 551 + 334 552 "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], 553 + 554 + "jose": ["jose@5.10.0", "", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="], 335 555 336 556 "js-base64": ["js-base64@3.7.8", "", {}, "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow=="], 337 557 ··· 363 583 364 584 "locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="], 365 585 586 + "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], 587 + 366 588 "magic-string": ["magic-string@0.30.19", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw=="], 367 589 590 + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], 591 + 592 + "media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="], 593 + 594 + "merge-descriptors": ["merge-descriptors@1.0.3", "", {}, "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ=="], 595 + 596 + "methods": ["methods@1.1.2", "", {}, "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="], 597 + 598 + "mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="], 599 + 600 + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], 601 + 602 + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], 603 + 604 + "minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], 605 + 368 606 "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], 369 607 370 608 "minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="], ··· 374 612 "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], 375 613 376 614 "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], 615 + 616 + "multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 377 617 378 618 "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], 379 619 620 + "negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], 621 + 380 622 "node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="], 381 623 382 624 "node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="], 383 625 626 + "node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.1.1", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-test": "build-test.js", "node-gyp-build-optional-packages-optional": "optional.js" } }, "sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw=="], 627 + 628 + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], 629 + 630 + "on-exit-leak-free": ["on-exit-leak-free@2.1.2", "", {}, "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA=="], 631 + 632 + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], 633 + 634 + "openbook.js": ["openbook.js@github:42willow/openbook.js#50d222f", { "dependencies": { "axios": "^1.11.0", "zod": "^4.1.5" } }, "42willow-openbook.js-50d222f"], 635 + 636 + "p-finally": ["p-finally@1.0.0", "", {}, "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow=="], 637 + 638 + "p-queue": ["p-queue@6.6.2", "", { "dependencies": { "eventemitter3": "^4.0.4", "p-timeout": "^3.2.0" } }, "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ=="], 639 + 640 + "p-timeout": ["p-timeout@3.2.0", "", { "dependencies": { "p-finally": "^1.0.0" } }, "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg=="], 641 + 642 + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], 643 + 644 + "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="], 645 + 646 + "path-to-regexp": ["path-to-regexp@0.1.12", "", {}, "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="], 647 + 384 648 "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], 385 649 386 650 "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], 651 + 652 + "pino": ["pino@8.21.0", "", { "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^1.2.0", "pino-std-serializers": "^6.0.0", "process-warning": "^3.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^3.7.0", "thread-stream": "^2.6.0" }, "bin": { "pino": "bin.js" } }, "sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q=="], 653 + 654 + "pino-abstract-transport": ["pino-abstract-transport@1.2.0", "", { "dependencies": { "readable-stream": "^4.0.0", "split2": "^4.0.0" } }, "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q=="], 655 + 656 + "pino-std-serializers": ["pino-std-serializers@6.2.2", "", {}, "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA=="], 387 657 388 658 "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], 389 659 ··· 393 663 394 664 "prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.6.14", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-hermes": "*", "@prettier/plugin-oxc": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-import-sort": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-style-order": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-hermes", "@prettier/plugin-oxc", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-import-sort", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-style-order", "prettier-plugin-svelte"] }, "sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg=="], 395 665 666 + "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="], 667 + 668 + "process-warning": ["process-warning@3.0.0", "", {}, "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ=="], 669 + 396 670 "promise-limit": ["promise-limit@2.7.0", "", {}, "sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw=="], 397 671 672 + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], 673 + 674 + "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], 675 + 676 + "qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="], 677 + 678 + "quick-format-unescaped": ["quick-format-unescaped@4.0.4", "", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="], 679 + 680 + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], 681 + 682 + "rate-limiter-flexible": ["rate-limiter-flexible@2.4.2", "", {}, "sha512-rMATGGOdO1suFyf/mI5LYhts71g1sbdhmd6YvdiXO2gJnd42Tt6QS4JUKJKSWVVkMtBacm6l40FR7Trjo6Iruw=="], 683 + 684 + "raw-body": ["raw-body@2.5.2", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } }, "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA=="], 685 + 686 + "readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="], 687 + 398 688 "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], 689 + 690 + "real-require": ["real-require@0.2.0", "", {}, "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="], 399 691 400 692 "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], 401 693 ··· 403 695 404 696 "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], 405 697 698 + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], 699 + 700 + "safe-stable-stringify": ["safe-stable-stringify@2.5.0", "", {}, "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA=="], 701 + 702 + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], 703 + 406 704 "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], 407 705 706 + "send": ["send@0.19.0", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" } }, "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw=="], 707 + 708 + "serve-static": ["serve-static@1.16.2", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "0.19.0" } }, "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw=="], 709 + 408 710 "set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="], 409 711 712 + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], 713 + 410 714 "shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="], 411 715 716 + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], 717 + 718 + "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], 719 + 720 + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], 721 + 722 + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], 723 + 412 724 "sirv": ["sirv@3.0.2", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g=="], 725 + 726 + "sonic-boom": ["sonic-boom@3.8.1", "", { "dependencies": { "atomic-sleep": "^1.0.0" } }, "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg=="], 413 727 414 728 "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], 415 729 ··· 417 731 418 732 "source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="], 419 733 734 + "split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="], 735 + 736 + "statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], 737 + 738 + "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], 739 + 740 + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], 741 + 420 742 "svelte": ["svelte@5.39.8", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^2.1.0", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-KfZ3hCITdxIXTOvrea4nFZX2o+47HPTChKeocgj9BwJQYqWrviVCcPj4boXHF5yf8+eBKqhHY8xii//XaakKXA=="], 421 743 422 744 "svelte-check": ["svelte-check@4.3.2", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-71udP5w2kaSTcX8iV0hn3o2FWlabQHhJTJLIQrCqMsrcOeDUO2VhCQKKCA8AMVHSPwdxLEWkUWh9OKxns5PD9w=="], ··· 427 749 428 750 "tar": ["tar@7.5.1", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g=="], 429 751 752 + "thread-stream": ["thread-stream@2.7.0", "", { "dependencies": { "real-require": "^0.2.0" } }, "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw=="], 753 + 430 754 "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], 755 + 756 + "tlds": ["tlds@1.260.0", "", { "bin": { "tlds": "bin.js" } }, "sha512-78+28EWBhCEE7qlyaHA9OR3IPvbCLiDh3Ckla593TksfFc9vfTsgvH7eS+dr3o9qr31gwGbogcI16yN91PoRjQ=="], 757 + 758 + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], 431 759 432 760 "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], 433 761 762 + "ts-morph": ["ts-morph@24.0.0", "", { "dependencies": { "@ts-morph/common": "~0.25.0", "code-block-writer": "^13.0.3" } }, "sha512-2OAOg/Ob5yx9Et7ZX4CvTCc0UFoZHwLEJ+dpDPSUi5TgwwlTlX47w+iFRrEwzUZwYACjq83cgjS/Da50Ga37uw=="], 763 + 434 764 "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 435 765 766 + "type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="], 767 + 436 768 "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], 437 769 770 + "uint8arrays": ["uint8arrays@3.0.0", "", { "dependencies": { "multiformats": "^9.4.2" } }, "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA=="], 771 + 772 + "undici": ["undici@6.21.3", "", {}, "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw=="], 773 + 438 774 "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], 439 775 776 + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], 777 + 778 + "utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="], 779 + 780 + "varint": ["varint@6.0.0", "", {}, "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg=="], 781 + 782 + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], 783 + 440 784 "vite": ["vite@7.1.7", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA=="], 441 785 442 786 "vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="], ··· 448 792 "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], 449 793 450 794 "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], 795 + 796 + "yesno": ["yesno@0.4.0", "", {}, "sha512-tdBxmHvbXPBKYIg81bMCB7bVeDmHkRzk5rVJyYYXurwKkHq/MCd8rz4HSJUP7hW0H2NlXiq8IFiWvYKEHhlotA=="], 451 797 452 798 "zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="], 453 799 800 + "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], 801 + 454 802 "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], 455 803 456 804 "@tailwindcss/oxide/detect-libc": ["detect-libc@2.1.1", "", {}, "sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw=="], ··· 467 815 468 816 "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 469 817 818 + "body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], 819 + 820 + "express/cookie": ["cookie@0.7.1", "", {}, "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w=="], 821 + 822 + "express/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], 823 + 824 + "finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], 825 + 470 826 "lightningcss/detect-libc": ["detect-libc@2.1.1", "", {}, "sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw=="], 827 + 828 + "node-gyp-build-optional-packages/detect-libc": ["detect-libc@2.1.1", "", {}, "sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw=="], 829 + 830 + "openbook.js/zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="], 831 + 832 + "proxy-addr/ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], 833 + 834 + "send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], 835 + 836 + "send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="], 471 837 472 838 "vite/esbuild": ["esbuild@0.25.10", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.10", "@esbuild/android-arm": "0.25.10", "@esbuild/android-arm64": "0.25.10", "@esbuild/android-x64": "0.25.10", "@esbuild/darwin-arm64": "0.25.10", "@esbuild/darwin-x64": "0.25.10", "@esbuild/freebsd-arm64": "0.25.10", "@esbuild/freebsd-x64": "0.25.10", "@esbuild/linux-arm": "0.25.10", "@esbuild/linux-arm64": "0.25.10", "@esbuild/linux-ia32": "0.25.10", "@esbuild/linux-loong64": "0.25.10", "@esbuild/linux-mips64el": "0.25.10", "@esbuild/linux-ppc64": "0.25.10", "@esbuild/linux-riscv64": "0.25.10", "@esbuild/linux-s390x": "0.25.10", "@esbuild/linux-x64": "0.25.10", "@esbuild/netbsd-arm64": "0.25.10", "@esbuild/netbsd-x64": "0.25.10", "@esbuild/openbsd-arm64": "0.25.10", "@esbuild/openbsd-x64": "0.25.10", "@esbuild/openharmony-arm64": "0.25.10", "@esbuild/sunos-x64": "0.25.10", "@esbuild/win32-arm64": "0.25.10", "@esbuild/win32-ia32": "0.25.10", "@esbuild/win32-x64": "0.25.10" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ=="], 473 839 ··· 514 880 "@esbuild-kit/core-utils/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.18.20", "", { "os": "win32", "cpu": "ia32" }, "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g=="], 515 881 516 882 "@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="], 883 + 884 + "body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], 885 + 886 + "express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], 887 + 888 + "finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], 889 + 890 + "send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], 517 891 518 892 "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.10", "", { "os": "aix", "cpu": "ppc64" }, "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw=="], 519 893
+30
lexicons/book.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "app.oxiary.book", 4 + "description": "A record representing a book in a library.", 5 + "defs": { 6 + "main": { 7 + "type": "record", 8 + "description": "A book record.", 9 + "key": "tid", 10 + "record": { 11 + "type": "object", 12 + "properties": { 13 + "OLID": { 14 + "type": "string", 15 + "description": "Open Library Work ID (e.g. OL27448W)." 16 + }, 17 + "title": { "type": "string" }, 18 + "authors": { 19 + "type": "array", 20 + "items": { "type": "ref", "ref": "app.oxiary.defs#author" }, 21 + "description": "List of authors" 22 + }, 23 + "description": { "type": "string" }, 24 + "firstPublishedAt": { "type": "string" } 25 + }, 26 + "required": ["OLID", "title", "authors", "description", "firstPublishedAt"] 27 + } 28 + } 29 + } 30 + }
+30
lexicons/cover.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "app.oxiary.cover", 4 + "description": "A record representing a cover image for a book or edition.", 5 + "defs": { 6 + "main": { 7 + "type": "record", 8 + "description": "A cover record.", 9 + "key": "tid", 10 + "record": { 11 + "type": "object", 12 + "properties": { 13 + "editionRef": { 14 + "type": "string", 15 + "format": "at-uri", 16 + "description": "AT-URI of the edition this cover is for." 17 + }, 18 + "image": { "type": "blob", "accept": ["image/*"], "description": "Cover image file." }, 19 + "submittedBy": { 20 + "type": "string", 21 + "format": "at-identifier", 22 + "description": "DID or handle of the user who submitted the cover." 23 + }, 24 + "createdAt": { "type": "string", "format": "datetime" } 25 + }, 26 + "required": ["editionRef", "image", "submittedBy", "createdAt"] 27 + } 28 + } 29 + } 30 + }
+22
lexicons/defs.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "app.oxiary.defs", 4 + "defs": { 5 + "genre": { 6 + "type": "string", 7 + "enum": ["fiction", "non-fiction", "fantasy", "biography", "science-fiction", "romance"], 8 + "description": "Book genre." 9 + }, 10 + "author": { 11 + "type": "object", 12 + "properties": { 13 + "OLID": { 14 + "type": "string", 15 + "description": "Open Library Author ID (e.g. OL26320A)." 16 + }, 17 + "name": { "type": "string" } 18 + }, 19 + "required": ["name", "OLID"] 20 + } 21 + } 22 + }
+14 -3
package.json
··· 1 1 { 2 - "name": "oxybrary", 2 + "name": "oxiary", 3 3 "private": true, 4 4 "version": "0.0.1", 5 5 "type": "module", ··· 15 15 "db:push": "drizzle-kit push", 16 16 "db:generate": "drizzle-kit generate", 17 17 "db:migrate": "drizzle-kit migrate", 18 - "db:studio": "drizzle-kit studio" 18 + "db:studio": "drizzle-kit studio", 19 + "lexgen": "lex gen-server ./src/lexicon ./lexicons/*" 19 20 }, 20 21 "devDependencies": { 22 + "@atproto/lex-cli": "^0.9.5", 21 23 "@libsql/client": "^0.14.0", 22 24 "@oslojs/crypto": "^1.0.1", 23 25 "@oslojs/encoding": "^1.1.0", ··· 38 40 "vite": "^7.0.4" 39 41 }, 40 42 "dependencies": { 41 - "@node-rs/argon2": "^2.0.2" 43 + "@atproto/api": "^0.17.0", 44 + "@atproto/common": "^0.4.12", 45 + "@atproto/identity": "^0.4.9", 46 + "@atproto/oauth-client-node": "^0.3.8", 47 + "@atproto/sync": "^0.1.35", 48 + "@atproto/syntax": "^0.4.1", 49 + "@atproto/xrpc-server": "^0.9.5", 50 + "@catppuccin/tailwindcss": "^1.0.0", 51 + "@node-rs/argon2": "^2.0.2", 52 + "openbook.js": "github:42willow/openbook.js" 42 53 } 43 54 }
+1
src/app.css
··· 1 1 @import 'tailwindcss'; 2 + @import '@catppuccin/tailwindcss/mocha.css';
+10 -7
src/app.d.ts
··· 1 1 // See https://svelte.dev/docs/kit/types#app.d.ts 2 2 // for information about these interfaces 3 + import type { Agent } from '@atproto/api'; 4 + 3 5 declare global { 4 6 namespace App { 5 7 interface Locals { 6 - user: import('$lib/server/auth').SessionValidationResult['user']; 7 - session: import('$lib/server/auth').SessionValidationResult['session']; 8 + agent: Agent | null; 9 + user: { did: string } | null; 8 10 } 9 - } // interface Error {} 10 - // interface Locals {} 11 - } // interface PageData {} 12 - // interface PageState {} 11 + // interface Error {} 12 + // interface PageData {} 13 + // interface PageState {} 14 + // interface Platform {} 15 + } 16 + } 13 17 14 - // interface Platform {} 15 18 export {};
+12 -19
src/hooks.server.ts
··· 1 1 import type { Handle } from '@sveltejs/kit'; 2 - import * as auth from '$lib/server/auth'; 2 + import { getSessionAgent } from '$lib/server/auth'; 3 + import { startIngester } from '$lib/server/atproto/ingester'; 3 4 4 - const handleAuth: Handle = async ({ event, resolve }) => { 5 - const sessionToken = event.cookies.get(auth.sessionCookieName); 5 + // Start the AT Protocol ingester when the server starts 6 + startIngester(); 6 7 7 - if (!sessionToken) { 8 - event.locals.user = null; 9 - event.locals.session = null; 10 - return resolve(event); 11 - } 12 - 13 - const { session, user } = await auth.validateSessionToken(sessionToken); 14 - 15 - if (session) { 16 - auth.setSessionTokenCookie(event, sessionToken, session.expiresAt); 17 - } else { 18 - auth.deleteSessionTokenCookie(event); 19 - } 20 - 21 - event.locals.user = user; 22 - event.locals.session = session; 8 + const handleAuth: Handle = async ({ event, resolve }) => { 9 + // Get the AT Protocol agent for the current session 10 + const agent = await getSessionAgent(event); 11 + 12 + // Store the agent in locals for use in routes 13 + event.locals.agent = agent; 14 + event.locals.user = agent ? { did: agent.assertDid } : null; 15 + 23 16 return resolve(event); 24 17 }; 25 18
+44
src/lexicon/index.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { 5 + type Auth, 6 + type Options as XrpcOptions, 7 + Server as XrpcServer, 8 + type StreamConfigOrHandler, 9 + type MethodConfigOrHandler, 10 + createServer as createXrpcServer, 11 + } from '@atproto/xrpc-server' 12 + import { schemas } from './lexicons.js' 13 + 14 + export function createServer(options?: XrpcOptions): Server { 15 + return new Server(options) 16 + } 17 + 18 + export class Server { 19 + xrpc: XrpcServer 20 + app: AppNS 21 + 22 + constructor(options?: XrpcOptions) { 23 + this.xrpc = createXrpcServer(schemas, options) 24 + this.app = new AppNS(this) 25 + } 26 + } 27 + 28 + export class AppNS { 29 + _server: Server 30 + oxiary: AppOxiaryNS 31 + 32 + constructor(server: Server) { 33 + this._server = server 34 + this.oxiary = new AppOxiaryNS(server) 35 + } 36 + } 37 + 38 + export class AppOxiaryNS { 39 + _server: Server 40 + 41 + constructor(server: Server) { 42 + this._server = server 43 + } 44 + }
+184
src/lexicon/lexicons.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { 5 + type LexiconDoc, 6 + Lexicons, 7 + ValidationError, 8 + type ValidationResult, 9 + } from '@atproto/lexicon' 10 + import { type $Typed, is$typed, maybe$typed } from './util.js' 11 + 12 + export const schemaDict = { 13 + AppOxiaryBook: { 14 + lexicon: 1, 15 + id: 'app.oxiary.book', 16 + description: 'A record representing a book in a library.', 17 + defs: { 18 + main: { 19 + type: 'record', 20 + description: 'A book record.', 21 + key: 'tid', 22 + record: { 23 + type: 'object', 24 + properties: { 25 + OLID: { 26 + type: 'string', 27 + description: 'Open Library Work ID (e.g. OL27448W).', 28 + }, 29 + title: { 30 + type: 'string', 31 + }, 32 + authors: { 33 + type: 'array', 34 + items: { 35 + type: 'ref', 36 + ref: 'lex:app.oxiary.defs#author', 37 + }, 38 + description: 'List of authors', 39 + }, 40 + description: { 41 + type: 'string', 42 + }, 43 + firstPublishedAt: { 44 + type: 'string', 45 + }, 46 + }, 47 + required: [ 48 + 'OLID', 49 + 'title', 50 + 'authors', 51 + 'description', 52 + 'firstPublishedAt', 53 + ], 54 + }, 55 + }, 56 + }, 57 + }, 58 + AppOxiaryCover: { 59 + lexicon: 1, 60 + id: 'app.oxiary.cover', 61 + description: 'A record representing a cover image for a book or edition.', 62 + defs: { 63 + main: { 64 + type: 'record', 65 + description: 'A cover record.', 66 + key: 'tid', 67 + record: { 68 + type: 'object', 69 + properties: { 70 + editionRef: { 71 + type: 'string', 72 + format: 'at-uri', 73 + description: 'AT-URI of the edition this cover is for.', 74 + }, 75 + image: { 76 + type: 'blob', 77 + accept: ['image/*'], 78 + description: 'Cover image file.', 79 + }, 80 + submittedBy: { 81 + type: 'string', 82 + format: 'at-identifier', 83 + description: 'DID or handle of the user who submitted the cover.', 84 + }, 85 + createdAt: { 86 + type: 'string', 87 + format: 'datetime', 88 + }, 89 + }, 90 + required: ['editionRef', 'image', 'submittedBy', 'createdAt'], 91 + }, 92 + }, 93 + }, 94 + }, 95 + AppOxiaryDefs: { 96 + lexicon: 1, 97 + id: 'app.oxiary.defs', 98 + defs: { 99 + genre: { 100 + type: 'string', 101 + enum: [ 102 + 'fiction', 103 + 'non-fiction', 104 + 'fantasy', 105 + 'biography', 106 + 'science-fiction', 107 + 'romance', 108 + ], 109 + description: 'Book genre.', 110 + }, 111 + author: { 112 + type: 'object', 113 + properties: { 114 + OLID: { 115 + type: 'string', 116 + description: 'Open Library Author ID (e.g. OL26320A).', 117 + }, 118 + name: { 119 + type: 'string', 120 + }, 121 + }, 122 + required: ['name', 'OLID'], 123 + }, 124 + book: { 125 + type: 'object', 126 + properties: { 127 + title: { 128 + type: 'string', 129 + }, 130 + authors: { 131 + type: 'array', 132 + items: { 133 + type: 'ref', 134 + ref: 'lex:app.oxiary.defs#author', 135 + }, 136 + }, 137 + description: { 138 + type: 'string', 139 + }, 140 + firstPublishedAt: { 141 + type: 'string', 142 + }, 143 + }, 144 + required: ['title', 'authors', 'description', 'firstPublishedAt'], 145 + }, 146 + }, 147 + }, 148 + } as const satisfies Record<string, LexiconDoc> 149 + export const schemas = Object.values(schemaDict) satisfies LexiconDoc[] 150 + export const lexicons: Lexicons = new Lexicons(schemas) 151 + 152 + export function validate<T extends { $type: string }>( 153 + v: unknown, 154 + id: string, 155 + hash: string, 156 + requiredType: true, 157 + ): ValidationResult<T> 158 + export function validate<T extends { $type?: string }>( 159 + v: unknown, 160 + id: string, 161 + hash: string, 162 + requiredType?: false, 163 + ): ValidationResult<T> 164 + export function validate( 165 + v: unknown, 166 + id: string, 167 + hash: string, 168 + requiredType?: boolean, 169 + ): ValidationResult { 170 + return (requiredType ? is$typed : maybe$typed)(v, id, hash) 171 + ? lexicons.validate(`${id}#${hash}`, v) 172 + : { 173 + success: false, 174 + error: new ValidationError( 175 + `Must be an object with "${hash === 'main' ? id : `${id}#${hash}`}" $type property`, 176 + ), 177 + } 178 + } 179 + 180 + export const ids = { 181 + AppOxiaryBook: 'app.oxiary.book', 182 + AppOxiaryCover: 'app.oxiary.cover', 183 + AppOxiaryDefs: 'app.oxiary.defs', 184 + } as const
+34
src/lexicon/types/app/oxiary/book.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 + import { CID } from 'multiformats/cid' 6 + import { validate as _validate } from '../../../lexicons' 7 + import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 8 + import type * as AppOxiaryDefs from './defs.js' 9 + 10 + const is$typed = _is$typed, 11 + validate = _validate 12 + const id = 'app.oxiary.book' 13 + 14 + export interface Record { 15 + $type: 'app.oxiary.book' 16 + /** Open Library Work ID (e.g. OL27448W). */ 17 + OLID: string 18 + title: string 19 + /** List of authors */ 20 + authors: AppOxiaryDefs.Author[] 21 + description: string 22 + firstPublishedAt: string 23 + [k: string]: unknown 24 + } 25 + 26 + const hashRecord = 'main' 27 + 28 + export function isRecord<V>(v: V) { 29 + return is$typed(v, id, hashRecord) 30 + } 31 + 32 + export function validateRecord<V>(v: V) { 33 + return validate<Record & V>(v, id, hashRecord, true) 34 + }
+33
src/lexicon/types/app/oxiary/cover.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 + import { CID } from 'multiformats/cid' 6 + import { validate as _validate } from '../../../lexicons' 7 + import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 8 + 9 + const is$typed = _is$typed, 10 + validate = _validate 11 + const id = 'app.oxiary.cover' 12 + 13 + export interface Record { 14 + $type: 'app.oxiary.cover' 15 + /** AT-URI of the edition this cover is for. */ 16 + editionRef: string 17 + /** Cover image file. */ 18 + image: BlobRef 19 + /** DID or handle of the user who submitted the cover. */ 20 + submittedBy: string 21 + createdAt: string 22 + [k: string]: unknown 23 + } 24 + 25 + const hashRecord = 'main' 26 + 27 + export function isRecord<V>(v: V) { 28 + return is$typed(v, id, hashRecord) 29 + } 30 + 31 + export function validateRecord<V>(v: V) { 32 + return validate<Record & V>(v, id, hashRecord, true) 33 + }
+55
src/lexicon/types/app/oxiary/defs.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 + import { CID } from 'multiformats/cid' 6 + import { validate as _validate } from '../../../lexicons' 7 + import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 8 + 9 + const is$typed = _is$typed, 10 + validate = _validate 11 + const id = 'app.oxiary.defs' 12 + 13 + /** Book genre. */ 14 + export type Genre = 15 + | 'fiction' 16 + | 'non-fiction' 17 + | 'fantasy' 18 + | 'biography' 19 + | 'science-fiction' 20 + | 'romance' 21 + 22 + export interface Author { 23 + $type?: 'app.oxiary.defs#author' 24 + /** Open Library Author ID (e.g. OL26320A). */ 25 + OLID: string 26 + name: string 27 + } 28 + 29 + const hashAuthor = 'author' 30 + 31 + export function isAuthor<V>(v: V) { 32 + return is$typed(v, id, hashAuthor) 33 + } 34 + 35 + export function validateAuthor<V>(v: V) { 36 + return validate<Author & V>(v, id, hashAuthor) 37 + } 38 + 39 + export interface Book { 40 + $type?: 'app.oxiary.defs#book' 41 + title: string 42 + authors: Author[] 43 + description: string 44 + firstPublishedAt: string 45 + } 46 + 47 + const hashBook = 'book' 48 + 49 + export function isBook<V>(v: V) { 50 + return is$typed(v, id, hashBook) 51 + } 52 + 53 + export function validateBook<V>(v: V) { 54 + return validate<Book & V>(v, id, hashBook) 55 + }
+82
src/lexicon/util.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + 5 + import { type ValidationResult } from '@atproto/lexicon' 6 + 7 + export type OmitKey<T, K extends keyof T> = { 8 + [K2 in keyof T as K2 extends K ? never : K2]: T[K2] 9 + } 10 + 11 + export type $Typed<V, T extends string = string> = V & { $type: T } 12 + export type Un$Typed<V extends { $type?: string }> = OmitKey<V, '$type'> 13 + 14 + export type $Type<Id extends string, Hash extends string> = Hash extends 'main' 15 + ? Id 16 + : `${Id}#${Hash}` 17 + 18 + function isObject<V>(v: V): v is V & object { 19 + return v != null && typeof v === 'object' 20 + } 21 + 22 + function is$type<Id extends string, Hash extends string>( 23 + $type: unknown, 24 + id: Id, 25 + hash: Hash, 26 + ): $type is $Type<Id, Hash> { 27 + return hash === 'main' 28 + ? $type === id 29 + : // $type === `${id}#${hash}` 30 + typeof $type === 'string' && 31 + $type.length === id.length + 1 + hash.length && 32 + $type.charCodeAt(id.length) === 35 /* '#' */ && 33 + $type.startsWith(id) && 34 + $type.endsWith(hash) 35 + } 36 + 37 + export type $TypedObject< 38 + V, 39 + Id extends string, 40 + Hash extends string, 41 + > = V extends { 42 + $type: $Type<Id, Hash> 43 + } 44 + ? V 45 + : V extends { $type?: string } 46 + ? V extends { $type?: infer T extends $Type<Id, Hash> } 47 + ? V & { $type: T } 48 + : never 49 + : V & { $type: $Type<Id, Hash> } 50 + 51 + export function is$typed<V, Id extends string, Hash extends string>( 52 + v: V, 53 + id: Id, 54 + hash: Hash, 55 + ): v is $TypedObject<V, Id, Hash> { 56 + return isObject(v) && '$type' in v && is$type(v.$type, id, hash) 57 + } 58 + 59 + export function maybe$typed<V, Id extends string, Hash extends string>( 60 + v: V, 61 + id: Id, 62 + hash: Hash, 63 + ): v is V & object & { $type?: $Type<Id, Hash> } { 64 + return ( 65 + isObject(v) && 66 + ('$type' in v ? v.$type === undefined || is$type(v.$type, id, hash) : true) 67 + ) 68 + } 69 + 70 + export type Validator<R = unknown> = (v: unknown) => ValidationResult<R> 71 + export type ValidatorParam<V extends Validator> = 72 + V extends Validator<infer R> ? R : never 73 + 74 + /** 75 + * Utility function that allows to convert a "validate*" utility function into a 76 + * type predicate. 77 + */ 78 + export function asPredicate<V extends Validator>(validate: V) { 79 + return function <T>(v: T): v is T & ValidatorParam<V> { 80 + return validate(v).success 81 + } 82 + }
+30
src/lib/components/BookCard.svelte
··· 1 + <script lang="ts"> 2 + import type { Book } from '$lib/server/db/schema'; 3 + 4 + export let book: Book; 5 + export let authorHandle: string | undefined = undefined; 6 + 7 + function formatDate(dateString: string) { 8 + return new Date(dateString).toLocaleDateString(); 9 + } 10 + </script> 11 + 12 + <div 13 + class="rounded-lg border border-ctp-surface2 bg-ctp-surface1 p-4 transition-colors hover:bg-ctp-surface2" 14 + > 15 + <h4 class="mb-1 font-semibold text-ctp-text">{book.title}</h4> 16 + <p class="mb-2 text-sm text-ctp-subtext1">by {book.author}</p> 17 + 18 + {#if book.publishedYear} 19 + <p class="mb-1 text-xs text-ctp-subtext0">Published: {book.publishedYear}</p> 20 + {/if} 21 + 22 + <div class="space-y-1"> 23 + <p class="text-xs text-ctp-overlay1">Added: {formatDate(book.createdAt)}</p> 24 + {#if authorHandle !== undefined} 25 + <p class="truncate text-xs text-ctp-overlay0"> 26 + By: {authorHandle || book.authorDid} 27 + </p> 28 + {/if} 29 + </div> 30 + </div>
+14
src/lib/components/Card.svelte
··· 1 + <script lang="ts"> 2 + let { title, children, props = {} } = $props(); 3 + </script> 4 + 5 + <div {...props}> 6 + <div class="rounded-lg border border-ctp-surface1 bg-ctp-surface0 shadow-lg"> 7 + <div class="border-b border-ctp-surface1 px-6 py-4"> 8 + <h3 class="text-lg font-semibold text-ctp-text">{title}</h3> 9 + </div> 10 + <div class="p-6"> 11 + {@render children()} 12 + </div> 13 + </div> 14 + </div>
+88
src/lib/server/atproto/client.ts
··· 1 + import { 2 + NodeOAuthClient, 3 + type NodeSavedSession, 4 + type NodeSavedState 5 + } from '@atproto/oauth-client-node'; 6 + import { db } from '$lib/server/db'; 7 + import { authSession, authState } from '$lib/server/db/schema'; 8 + import { eq } from 'drizzle-orm'; 9 + import { env } from '$env/dynamic/private'; 10 + 11 + // storage implementations for OAuth client 12 + class SessionStore { 13 + async get(key: string) { 14 + const result = await db.select().from(authSession).where(eq(authSession.key, key)).limit(1); 15 + return (result[0]?.session as NodeSavedSession) || undefined; 16 + } 17 + 18 + async set(key: string, session: unknown) { 19 + await db 20 + .insert(authSession) 21 + .values({ key, session: session as any }) 22 + .onConflictDoUpdate({ 23 + target: authSession.key, 24 + set: { session: session as any } 25 + }); 26 + } 27 + 28 + async del(key: string) { 29 + await db.delete(authSession).where(eq(authSession.key, key)); 30 + } 31 + } 32 + 33 + class StateStore { 34 + async get(key: string) { 35 + const result = await db.select().from(authState).where(eq(authState.key, key)).limit(1); 36 + return (result[0]?.state as NodeSavedState) || undefined; 37 + } 38 + 39 + async set(key: string, state: unknown) { 40 + await db 41 + .insert(authState) 42 + .values({ key, state: state as any }) 43 + .onConflictDoUpdate({ 44 + target: authState.key, 45 + set: { state: state as any } 46 + }); 47 + } 48 + 49 + async del(key: string) { 50 + await db.delete(authState).where(eq(authState.key, key)); 51 + } 52 + } 53 + 54 + export async function createOAuthClient() { 55 + const publicUrl = env.PUBLIC_URL; 56 + const port = env.PORT || '5173'; 57 + const url = publicUrl || `http://127.0.0.1:${port}`; 58 + const enc = encodeURIComponent; 59 + 60 + return new NodeOAuthClient({ 61 + clientMetadata: { 62 + client_name: 'Oxybrary - ATProto Book Tracker', 63 + client_id: publicUrl 64 + ? `${url}/client-metadata.json` 65 + : `http://localhost?redirect_uri=${enc(`${url}/oauth/callback`)}&scope=${enc('atproto transition:generic')}`, 66 + client_uri: url, 67 + redirect_uris: [`${url}/oauth/callback`], 68 + scope: 'atproto transition:generic', 69 + grant_types: ['authorization_code', 'refresh_token'], 70 + response_types: ['code'], 71 + application_type: 'web', 72 + token_endpoint_auth_method: 'none', 73 + dpop_bound_access_tokens: true 74 + }, 75 + stateStore: new StateStore(), 76 + sessionStore: new SessionStore() 77 + }); 78 + } 79 + 80 + // global OAuth client instance 81 + let oauthClient: NodeOAuthClient | null = null; 82 + 83 + export async function getOAuthClient() { 84 + if (!oauthClient) { 85 + oauthClient = await createOAuthClient(); 86 + } 87 + return oauthClient; 88 + }
+80
src/lib/server/atproto/ingester.ts
··· 1 + import { IdResolver } from '@atproto/identity'; 2 + import { Firehose } from '@atproto/sync'; 3 + import { eq } from 'drizzle-orm'; 4 + import { db } from '$lib/server/db'; 5 + import { book } from '$lib/server/db/schema'; 6 + import * as Book from '$lexicon/book'; 7 + 8 + // listen from events from the firehose and synchronise them with the database 9 + export function createIngester() { 10 + const idResolver = new IdResolver(); 11 + 12 + return new Firehose({ 13 + idResolver, 14 + handleEvent: async (evt) => { 15 + // watch for write events 16 + if (evt.event === 'create' || evt.event === 'update') { 17 + const now = new Date(); 18 + const record = evt.record; 19 + 20 + // if the write is a valid book record 21 + if ( 22 + evt.collection === 'app.oxiary.book' && 23 + Book.isRecord(record) && 24 + Book.validateRecord(record).success 25 + ) { 26 + // store the book in our SQLite 27 + await db 28 + .insert(book) 29 + .values({ 30 + uri: evt.uri.toString(), 31 + authorDid: evt.did, 32 + createdAt: record.createdAt, 33 + indexedAt: now.toISOString(), 34 + 35 + OLID: record.OLID, 36 + title: record.title, 37 + authors: record.authors, 38 + description: record.description, 39 + firstPublishedAt: record.firstPublishedAt 40 + }) 41 + .onConflictDoUpdate({ 42 + target: book.uri, 43 + set: { 44 + indexedAt: now.toISOString(), 45 + 46 + title: record.title, 47 + authors: record.authors, 48 + firstPublishedAt: record.firstPublishedAt 49 + } 50 + }); 51 + } 52 + } else if (evt.event === 'delete' && evt.collection === 'app.oxiary.book') { 53 + // remove the book from our SQLite 54 + await db.delete(book).where(eq(book.uri, evt.uri.toString())); 55 + } 56 + }, 57 + onError: (err) => { 58 + console.error('Error on firehose ingestion:', err); 59 + }, 60 + filterCollections: ['app.oxiary.book'], 61 + excludeIdentity: true, 62 + excludeAccount: true 63 + }); 64 + } 65 + 66 + // global ingester instance 67 + let ingester: Firehose | null = null; 68 + 69 + export function getIngester() { 70 + if (!ingester) { 71 + ingester = createIngester(); 72 + } 73 + return ingester; 74 + } 75 + 76 + export function startIngester() { 77 + const ing = getIngester(); 78 + ing.start(); 79 + return ing; 80 + }
+106
src/lib/server/atproto/resolver.ts
··· 1 + import { IdResolver, MemoryCache } from '@atproto/identity'; 2 + 3 + export class BidirectionalResolver { 4 + private baseResolver: IdResolver; 5 + private didToHandleCache = new Map<string, string>(); 6 + private handleToDidCache = new Map<string, string>(); 7 + 8 + constructor(baseResolver: IdResolver) { 9 + this.baseResolver = baseResolver; 10 + } 11 + 12 + async resolveDid(did: string): Promise<string | undefined> { 13 + // check cache first 14 + if (this.didToHandleCache.has(did)) { 15 + return this.didToHandleCache.get(did); 16 + } 17 + 18 + try { 19 + const doc = await this.baseResolver.did.resolve(did); 20 + if (doc?.alsoKnownAs?.[0]) { 21 + const handle = doc.alsoKnownAs[0].replace('at://', ''); 22 + // cache the result 23 + this.didToHandleCache.set(did, handle); 24 + this.handleToDidCache.set(handle, did); 25 + return handle; 26 + } 27 + } catch (err) { 28 + console.warn('failed to resolve DID to handle:', did, err); 29 + } 30 + 31 + return undefined; 32 + } 33 + 34 + async resolveHandle(handle: string): Promise<string | undefined> { 35 + // check cache first 36 + if (this.handleToDidCache.has(handle)) { 37 + return this.handleToDidCache.get(handle); 38 + } 39 + 40 + try { 41 + const did = await this.baseResolver.handle.resolve(handle); 42 + if (did) { 43 + // cache the result 44 + this.handleToDidCache.set(handle, did); 45 + this.didToHandleCache.set(did, handle); 46 + return did; 47 + } 48 + } catch (err) { 49 + console.warn('failed to resolve handle to DID:', handle, err); 50 + } 51 + 52 + return undefined; 53 + } 54 + 55 + async resolveDidsToHandles(dids: string[]): Promise<Record<string, string>> { 56 + const result: Record<string, string> = {}; 57 + 58 + // resolve all DIDs in parallel 59 + const promises = dids.map(async (did) => { 60 + const handle = await this.resolveDid(did); 61 + if (handle) { 62 + result[did] = handle; 63 + } 64 + }); 65 + 66 + await Promise.all(promises); 67 + return result; 68 + } 69 + 70 + async resolveHandlesToDids(handles: string[]): Promise<Record<string, string>> { 71 + const result: Record<string, string> = {}; 72 + 73 + // resolve all handles in parallel 74 + const promises = handles.map(async (handle) => { 75 + const did = await this.resolveHandle(handle); 76 + if (did) { 77 + result[handle] = did; 78 + } 79 + }); 80 + 81 + await Promise.all(promises); 82 + return result; 83 + } 84 + } 85 + 86 + // create and export resolver instances 87 + export function createIdResolver() { 88 + return new IdResolver({ 89 + didCache: new MemoryCache() 90 + }); 91 + } 92 + 93 + export function createBidirectionalResolver(baseResolver: IdResolver) { 94 + return new BidirectionalResolver(baseResolver); 95 + } 96 + 97 + // global resolver instance 98 + let resolver: BidirectionalResolver | null = null; 99 + 100 + export function getResolver() { 101 + if (!resolver) { 102 + const baseResolver = createIdResolver(); 103 + resolver = createBidirectionalResolver(baseResolver); 104 + } 105 + return resolver; 106 + }
+27 -72
src/lib/server/auth.ts
··· 1 1 import type { RequestEvent } from '@sveltejs/kit'; 2 - import { eq } from 'drizzle-orm'; 3 - import { sha256 } from '@oslojs/crypto/sha2'; 4 - import { encodeBase64url, encodeHexLowerCase } from '@oslojs/encoding'; 5 - import { db } from '$lib/server/db'; 6 - import * as table from '$lib/server/db/schema'; 2 + import { Agent } from '@atproto/api'; 3 + import { getOAuthClient } from './atproto/client'; 7 4 8 - const DAY_IN_MS = 1000 * 60 * 60 * 24; 5 + export const sessionCookieName = 'atproto-session'; 9 6 10 - export const sessionCookieName = 'auth-session'; 7 + export async function getSessionAgent(event: RequestEvent) { 8 + const sessionDid = event.cookies.get(sessionCookieName); 9 + if (!sessionDid) return null; 11 10 12 - export function generateSessionToken() { 13 - const bytes = crypto.getRandomValues(new Uint8Array(18)); 14 - const token = encodeBase64url(bytes); 15 - return token; 11 + try { 12 + const oauthClient = await getOAuthClient(); 13 + const oauthSession = await oauthClient.restore(sessionDid); 14 + return oauthSession ? new Agent(oauthSession) : null; 15 + } catch (err) { 16 + console.warn('OAuth restore failed:', err); 17 + event.cookies.delete(sessionCookieName, { path: '/' }); 18 + return null; 19 + } 16 20 } 17 21 18 - export async function createSession(token: string, userId: string) { 19 - const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token))); 20 - const session: table.Session = { 21 - id: sessionId, 22 - userId, 23 - expiresAt: new Date(Date.now() + DAY_IN_MS * 30) 24 - }; 25 - await db.insert(table.session).values(session); 26 - return session; 22 + export function setSessionCookie(event: RequestEvent, did: string) { 23 + event.cookies.set(sessionCookieName, did, { 24 + path: '/', 25 + httpOnly: true, 26 + secure: process.env.NODE_ENV === 'production', 27 + sameSite: 'lax', 28 + maxAge: 60 * 60 * 24 * 30 // 30 days 29 + }); 27 30 } 28 31 29 - export async function validateSessionToken(token: string) { 30 - const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token))); 31 - const [result] = await db 32 - .select({ 33 - // Adjust user table here to tweak returned data 34 - user: { id: table.user.id, username: table.user.username }, 35 - session: table.session 36 - }) 37 - .from(table.session) 38 - .innerJoin(table.user, eq(table.session.userId, table.user.id)) 39 - .where(eq(table.session.id, sessionId)); 40 - 41 - if (!result) { 42 - return { session: null, user: null }; 43 - } 44 - const { session, user } = result; 45 - 46 - const sessionExpired = Date.now() >= session.expiresAt.getTime(); 47 - if (sessionExpired) { 48 - await db.delete(table.session).where(eq(table.session.id, session.id)); 49 - return { session: null, user: null }; 50 - } 51 - 52 - const renewSession = Date.now() >= session.expiresAt.getTime() - DAY_IN_MS * 15; 53 - if (renewSession) { 54 - session.expiresAt = new Date(Date.now() + DAY_IN_MS * 30); 55 - await db 56 - .update(table.session) 57 - .set({ expiresAt: session.expiresAt }) 58 - .where(eq(table.session.id, session.id)); 59 - } 60 - 61 - return { session, user }; 62 - } 63 - 64 - export type SessionValidationResult = Awaited<ReturnType<typeof validateSessionToken>>; 65 - 66 - export async function invalidateSession(sessionId: string) { 67 - await db.delete(table.session).where(eq(table.session.id, sessionId)); 68 - } 69 - 70 - export function setSessionTokenCookie(event: RequestEvent, token: string, expiresAt: Date) { 71 - event.cookies.set(sessionCookieName, token, { 72 - expires: expiresAt, 73 - path: '/' 74 - }); 75 - } 76 - 77 - export function deleteSessionTokenCookie(event: RequestEvent) { 78 - event.cookies.delete(sessionCookieName, { 79 - path: '/' 80 - }); 32 + export function deleteSessionCookie(event: RequestEvent) { 33 + event.cookies.delete(sessionCookieName, { 34 + path: '/' 35 + }); 81 36 }
+32 -13
src/lib/server/db/schema.ts
··· 1 - import { sqliteTable, integer, text } from 'drizzle-orm/sqlite-core'; 1 + import { sqliteTable, text } from 'drizzle-orm/sqlite-core'; 2 + import type { Record as BookRecord } from '$lexicon/book'; 3 + import { sql } from 'drizzle-orm'; 4 + import type { Author } from '$lexicon/defs'; 2 5 3 - export const user = sqliteTable('user', { 4 - id: text('id').primaryKey(), 5 - age: integer('age'), 6 - username: text('username').notNull().unique(), 7 - passwordHash: text('password_hash').notNull() 6 + export const authSession = sqliteTable('session', { 7 + key: text('key').primaryKey(), 8 + session: text('session', { mode: 'json' }).notNull() 9 + }); 10 + 11 + export const authState = sqliteTable('state', { 12 + key: text('key').primaryKey(), 13 + state: text('state', { mode: 'json' }).notNull() 8 14 }); 9 15 10 - export const session = sqliteTable('session', { 11 - id: text('id').primaryKey(), 12 - userId: text('user_id') 16 + export const book = sqliteTable('book', { 17 + uri: text('uri').primaryKey(), 18 + authorDid: text('authorDid').notNull(), 19 + createdAt: text('createdAt').notNull(), 20 + indexedAt: text('indexedAt').notNull(), 21 + 22 + OLID: text('OLID').notNull(), 23 + title: text('title').notNull(), 24 + authors: text('authors', { mode: 'json' }) 13 25 .notNull() 14 - .references(() => user.id), 15 - expiresAt: integer('expires_at', { mode: 'timestamp' }).notNull() 26 + .$type<Author[]>() 27 + .default(sql`'[]'`), 28 + description: text('description').notNull(), 29 + firstPublishedAt: text('firstPublishedAt').notNull() 16 30 }); 17 31 18 - export type Session = typeof session.$inferSelect; 32 + export type Session = typeof authSession.$inferSelect; 33 + export type State = typeof authState.$inferSelect; 34 + export type Book = typeof book.$inferSelect; 19 35 20 - export type User = typeof user.$inferSelect; 36 + type LexiconShape<T> = { 37 + [K in keyof T as string extends K ? never : K extends '$type' ? never : K]: T[K]; 38 + }; 39 + const _typeCheckBook: LexiconShape<BookRecord> = {} as Book;
+126
src/routes/+page.server.ts
··· 1 + import { fail } from '@sveltejs/kit'; 2 + import { TID } from '@atproto/common'; 3 + import { eq, desc } from 'drizzle-orm'; 4 + import { db } from '$lib/server/db'; 5 + import { book } from '$lib/server/db/schema'; 6 + import * as Book from '../lexicon/types/app/oxiary/book'; 7 + import { getResolver } from '$lib/server/atproto/resolver'; 8 + import type { Actions, PageServerLoad } from './$types'; 9 + import { OpenLibraryClient } from 'openbook.js'; 10 + import { version } from '$app/environment'; 11 + 12 + export const load: PageServerLoad = async ({ locals, url }) => { 13 + // fetch recent books from the database 14 + const books = await db.select().from(book).orderBy(desc(book.indexedAt)).limit(20); 15 + 16 + // get user's books if logged in 17 + const myBooks = locals.user 18 + ? await db 19 + .select() 20 + .from(book) 21 + .where(eq(book.authorDid, locals.user.did)) 22 + .orderBy(desc(book.indexedAt)) 23 + .limit(10) 24 + : []; 25 + 26 + // resolve DIDs to handles 27 + const resolver = getResolver(); 28 + const allDids = [...books.map((b) => b.authorDid), ...(locals.user ? [locals.user.did] : [])]; 29 + const uniqueDids = [...new Set(allDids)]; 30 + const didHandleMap = await resolver.resolveDidsToHandles(uniqueDids); 31 + 32 + // get current user's handle 33 + const userHandle = locals.user ? didHandleMap[locals.user.did] : undefined; 34 + 35 + const error = url.searchParams.get('error'); 36 + 37 + return { 38 + books, 39 + myBooks, 40 + user: locals.user ? { ...locals.user, handle: userHandle } : null, 41 + didHandleMap, 42 + error 43 + }; 44 + }; 45 + 46 + export const actions: Actions = { 47 + addBook: async ({ request, locals }) => { 48 + if (!locals.agent || !locals.user) { 49 + return fail(401, { error: 'authentication required' }); 50 + } 51 + 52 + const data = await request.formData(); 53 + const OLID = data.get('OLID')?.toString(); 54 + 55 + if (!OLID) { 56 + return fail(400, { error: 'OLID is required' }); 57 + } 58 + 59 + // pull the data from the API 60 + const client = new OpenLibraryClient(`Oxiary/${version} (42willow@pm.me)`); 61 + const work = await client.getWork(OLID); 62 + 63 + if (!work.title || !work.authors || !work.description) { 64 + return fail(400, { error: 'work data is incomplete' }); 65 + } 66 + 67 + const editions = await client.getWorkEditions(OLID); 68 + const years: string[] = editions.entries 69 + .flatMap((edition) => 70 + edition.publish_date ? [edition.publish_date.replace(/\b\d{4}\b/g, '').trim()] : [] 71 + ) 72 + .sort(); 73 + 74 + // construct the book record 75 + const rkey = TID.nextStr(); 76 + const record: Book.Record = { 77 + $type: 'app.oxiary.book', 78 + OLID, 79 + title: work.title.trim(), 80 + authors: work.authors.map((author) => author.name), 81 + firstPublishedAt: firstPublishedAt, 82 + description: work.description.toString(), 83 + createdAt: new Date().toISOString() 84 + }; 85 + 86 + // validate the record 87 + const validation = Book.validateRecord(record); 88 + if (!validation.success) { 89 + return fail(400, { error: validation.error }); 90 + } 91 + 92 + try { 93 + // write the book record to the user's repository 94 + const response = await locals.agent.com.atproto.repo.putRecord({ 95 + repo: locals.agent.assertDid, 96 + collection: 'app.oxiary.book', 97 + rkey, 98 + record, 99 + validate: false 100 + }); 101 + 102 + const uri = response.data.uri; 103 + 104 + // optimistically update the database 105 + try { 106 + await db.insert(book).values({ 107 + uri, 108 + authorDid: locals.agent.assertDid, 109 + createdAt: record.createdAt, 110 + indexedAt: new Date().toISOString(), 111 + 112 + title: record.title, 113 + author: record.author, 114 + firstPublishedAt: record.firstPublishedAt 115 + }); 116 + } catch (dbErr) { 117 + console.warn('failed to update local database, will be caught by firehose:', dbErr); 118 + } 119 + 120 + return { success: true }; 121 + } catch (err) { 122 + console.error('failed to add book:', err); 123 + return fail(500, { error: 'failed to add book' }); 124 + } 125 + } 126 + };
+138 -2
src/routes/+page.svelte
··· 1 - <h1>Welcome to SvelteKit</h1> 2 - <p>Visit <a href="https://svelte.dev/docs/kit">svelte.dev/docs/kit</a> to read the documentation</p> 1 + <script lang="ts"> 2 + import { enhance } from '$app/forms'; 3 + import type { PageData, ActionData } from './$types'; 4 + import Card from '$lib/components/Card.svelte'; 5 + import BookCard from '$lib/components/BookCard.svelte'; 6 + 7 + export let data: PageData; 8 + export let form: ActionData; 9 + </script> 10 + 11 + <svelte:head> 12 + <title>Oxiary - ATProto Book Tracker</title> 13 + </svelte:head> 14 + 15 + <div class="min-h-screen bg-ctp-base"> 16 + <header class="border-b border-ctp-surface0 bg-ctp-mantle shadow-lg"> 17 + <div class="mx-auto max-w-7xl px-4"> 18 + <div class="flex items-center justify-between py-6"> 19 + <div class="flex items-center gap-4"> 20 + <h1 class="text-3xl font-bold text-ctp-text">📚 Oxiary</h1> 21 + <p class="text-ctp-subtext1">ATProto Book Tracker</p> 22 + </div> 23 + 24 + <div class="flex items-center gap-4"> 25 + {#if data.user} 26 + <span class="text-sm text-ctp-subtext0" 27 + >Welcome, {data.user.handle || data.user.did}</span 28 + > 29 + <form method="POST" action="/logout"> 30 + <button 31 + type="submit" 32 + class="rounded bg-ctp-red px-4 py-2 font-medium text-ctp-base transition-colors hover:bg-ctp-maroon" 33 + > 34 + Logout 35 + </button> 36 + </form> 37 + {:else} 38 + <a 39 + href="/login" 40 + class="rounded bg-ctp-blue px-4 py-2 font-medium text-ctp-base transition-colors hover:bg-ctp-sapphire" 41 + > 42 + Login with AT Protocol 43 + </a> 44 + {/if} 45 + </div> 46 + </div> 47 + </div> 48 + </header> 49 + 50 + <main class="mx-auto flex max-w-7xl flex-col gap-8 px-4 py-6"> 51 + {#if data.error} 52 + <div class="mb-4 rounded border border-ctp-red/30 bg-ctp-red/10 px-4 py-3 text-ctp-red"> 53 + {data.error === 'oauth_failed' 54 + ? 'OAuth authentication failed. Please try again.' 55 + : data.error} 56 + </div> 57 + {/if} 58 + 59 + {#if data.user} 60 + <Card title="Search for books"> 61 + <input 62 + type="text" 63 + placeholder="Enter search term" 64 + /> 65 + </Card> 66 + <Card title="Add a Book"> 67 + <form method="POST" action="?/addBook" use:enhance class="space-y-4"> 68 + {#if form?.error} 69 + <div class="rounded border border-ctp-red/30 bg-ctp-red/10 px-4 py-3 text-ctp-red"> 70 + {form.error} 71 + </div> 72 + {/if} 73 + 74 + {#if form?.success} 75 + <div 76 + class="rounded border border-ctp-green/30 bg-ctp-green/10 px-4 py-3 text-ctp-green" 77 + > 78 + Book added successfully! 79 + </div> 80 + {/if} 81 + 82 + <div class="grid grid-cols-1 gap-4 sm:grid-cols-3"> 83 + <div class="space-y-2"> 84 + <label for="OLID" class="block text-sm font-medium text-ctp-subtext1">OLID</label> 85 + <input 86 + type="text" 87 + name="OLID" 88 + id="OLID" 89 + required 90 + class="w-full rounded border border-ctp-surface2 bg-ctp-surface1 px-3 py-2 text-ctp-text placeholder-ctp-overlay0 focus:border-ctp-blue focus:ring-2 focus:ring-ctp-blue focus:outline-none" 91 + placeholder="Enter book OLID" 92 + /> 93 + </div> 94 + </div> 95 + 96 + <button 97 + type="submit" 98 + class="w-full rounded bg-ctp-blue px-4 py-2 font-medium text-ctp-base transition-colors hover:bg-ctp-sapphire" 99 + > 100 + Add Book to Library 101 + </button> 102 + </form> 103 + </Card> 104 + 105 + {#if data.myBooks.length > 0} 106 + <Card title="My Books"> 107 + <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3"> 108 + {#each data.myBooks as book} 109 + <BookCard {book} /> 110 + {/each} 111 + </div> 112 + </Card> 113 + {/if} 114 + {/if} 115 + 116 + <Card title={data.user ? 'Community Library' : 'Recent Books'}> 117 + {#if data.books.length === 0} 118 + <div class="py-8 text-center"> 119 + <p class="text-ctp-subtext0"> 120 + No books in the library yet. 121 + {#if !data.user} 122 + <a href="/login" class="text-ctp-blue underline hover:text-ctp-sapphire">Login</a> to add 123 + the first book! 124 + {:else} 125 + Add the first book above! 126 + {/if} 127 + </p> 128 + </div> 129 + {:else} 130 + <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"> 131 + {#each data.books as book} 132 + <BookCard {book} authorHandle={data.didHandleMap[book.authorDid]} /> 133 + {/each} 134 + </div> 135 + {/if} 136 + </Card> 137 + </main> 138 + </div>
+8
src/routes/client-metadata.json/+server.ts
··· 1 + import { json } from '@sveltejs/kit'; 2 + import { getOAuthClient } from '$lib/server/atproto/client'; 3 + import type { RequestHandler } from './$types'; 4 + 5 + export const GET: RequestHandler = async () => { 6 + const oauthClient = await getOAuthClient(); 7 + return json(oauthClient.clientMetadata); 8 + };
-1
src/routes/demo/+page.svelte
··· 1 - <a href="/demo/lucia">lucia</a>
-31
src/routes/demo/lucia/+page.server.ts
··· 1 - import * as auth from '$lib/server/auth'; 2 - import { fail, redirect } from '@sveltejs/kit'; 3 - import { getRequestEvent } from '$app/server'; 4 - import type { Actions, PageServerLoad } from './$types'; 5 - 6 - export const load: PageServerLoad = async () => { 7 - const user = requireLogin(); 8 - return { user }; 9 - }; 10 - 11 - export const actions: Actions = { 12 - logout: async (event) => { 13 - if (!event.locals.session) { 14 - return fail(401); 15 - } 16 - await auth.invalidateSession(event.locals.session.id); 17 - auth.deleteSessionTokenCookie(event); 18 - 19 - return redirect(302, '/demo/lucia/login'); 20 - } 21 - }; 22 - 23 - function requireLogin() { 24 - const { locals } = getRequestEvent(); 25 - 26 - if (!locals.user) { 27 - return redirect(302, '/demo/lucia/login'); 28 - } 29 - 30 - return locals.user; 31 - }
-12
src/routes/demo/lucia/+page.svelte
··· 1 - <script lang="ts"> 2 - import { enhance } from '$app/forms'; 3 - import type { PageServerData } from './$types'; 4 - 5 - let { data }: { data: PageServerData } = $props(); 6 - </script> 7 - 8 - <h1>Hi, {data.user.username}!</h1> 9 - <p>Your user ID is {data.user.id}.</p> 10 - <form method="post" action="?/logout" use:enhance> 11 - <button>Sign out</button> 12 - </form>
-107
src/routes/demo/lucia/login/+page.server.ts
··· 1 - import { hash, verify } from '@node-rs/argon2'; 2 - import { encodeBase32LowerCase } from '@oslojs/encoding'; 3 - import { fail, redirect } from '@sveltejs/kit'; 4 - import { eq } from 'drizzle-orm'; 5 - import * as auth from '$lib/server/auth'; 6 - import { db } from '$lib/server/db'; 7 - import * as table from '$lib/server/db/schema'; 8 - import type { Actions, PageServerLoad } from './$types'; 9 - 10 - export const load: PageServerLoad = async (event) => { 11 - if (event.locals.user) { 12 - return redirect(302, '/demo/lucia'); 13 - } 14 - return {}; 15 - }; 16 - 17 - export const actions: Actions = { 18 - login: async (event) => { 19 - const formData = await event.request.formData(); 20 - const username = formData.get('username'); 21 - const password = formData.get('password'); 22 - 23 - if (!validateUsername(username)) { 24 - return fail(400, { 25 - message: 'Invalid username (min 3, max 31 characters, alphanumeric only)' 26 - }); 27 - } 28 - if (!validatePassword(password)) { 29 - return fail(400, { message: 'Invalid password (min 6, max 255 characters)' }); 30 - } 31 - 32 - const results = await db.select().from(table.user).where(eq(table.user.username, username)); 33 - 34 - const existingUser = results.at(0); 35 - if (!existingUser) { 36 - return fail(400, { message: 'Incorrect username or password' }); 37 - } 38 - 39 - const validPassword = await verify(existingUser.passwordHash, password, { 40 - memoryCost: 19456, 41 - timeCost: 2, 42 - outputLen: 32, 43 - parallelism: 1 44 - }); 45 - if (!validPassword) { 46 - return fail(400, { message: 'Incorrect username or password' }); 47 - } 48 - 49 - const sessionToken = auth.generateSessionToken(); 50 - const session = await auth.createSession(sessionToken, existingUser.id); 51 - auth.setSessionTokenCookie(event, sessionToken, session.expiresAt); 52 - 53 - return redirect(302, '/demo/lucia'); 54 - }, 55 - register: async (event) => { 56 - const formData = await event.request.formData(); 57 - const username = formData.get('username'); 58 - const password = formData.get('password'); 59 - 60 - if (!validateUsername(username)) { 61 - return fail(400, { message: 'Invalid username' }); 62 - } 63 - if (!validatePassword(password)) { 64 - return fail(400, { message: 'Invalid password' }); 65 - } 66 - 67 - const userId = generateUserId(); 68 - const passwordHash = await hash(password, { 69 - // recommended minimum parameters 70 - memoryCost: 19456, 71 - timeCost: 2, 72 - outputLen: 32, 73 - parallelism: 1 74 - }); 75 - 76 - try { 77 - await db.insert(table.user).values({ id: userId, username, passwordHash }); 78 - 79 - const sessionToken = auth.generateSessionToken(); 80 - const session = await auth.createSession(sessionToken, userId); 81 - auth.setSessionTokenCookie(event, sessionToken, session.expiresAt); 82 - } catch { 83 - return fail(500, { message: 'An error has occurred' }); 84 - } 85 - return redirect(302, '/demo/lucia'); 86 - } 87 - }; 88 - 89 - function generateUserId() { 90 - // ID with 120 bits of entropy, or about the same as UUID v4. 91 - const bytes = crypto.getRandomValues(new Uint8Array(15)); 92 - const id = encodeBase32LowerCase(bytes); 93 - return id; 94 - } 95 - 96 - function validateUsername(username: unknown): username is string { 97 - return ( 98 - typeof username === 'string' && 99 - username.length >= 3 && 100 - username.length <= 31 && 101 - /^[a-z0-9_-]+$/.test(username) 102 - ); 103 - } 104 - 105 - function validatePassword(password: unknown): password is string { 106 - return typeof password === 'string' && password.length >= 6 && password.length <= 255; 107 - }
-34
src/routes/demo/lucia/login/+page.svelte
··· 1 - <script lang="ts"> 2 - import { enhance } from '$app/forms'; 3 - import type { ActionData } from './$types'; 4 - 5 - let { form }: { form: ActionData } = $props(); 6 - </script> 7 - 8 - <h1>Login/Register</h1> 9 - <form method="post" action="?/login" use:enhance> 10 - <label> 11 - Username 12 - <input 13 - name="username" 14 - class="mt-1 rounded-md border border-gray-300 bg-white px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-500 focus:outline-none" 15 - /> 16 - </label> 17 - <label> 18 - Password 19 - <input 20 - type="password" 21 - name="password" 22 - class="mt-1 rounded-md border border-gray-300 bg-white px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-500 focus:outline-none" 23 - /> 24 - </label> 25 - <button class="rounded-md bg-blue-600 px-4 py-2 text-white transition hover:bg-blue-700" 26 - >Login</button 27 - > 28 - <button 29 - formaction="?/register" 30 - class="rounded-md bg-blue-600 px-4 py-2 text-white transition hover:bg-blue-700" 31 - >Register</button 32 - > 33 - </form> 34 - <p style="color: red">{form?.message ?? ''}</p>
+34
src/routes/login/+page.server.ts
··· 1 + import { redirect, fail } from '@sveltejs/kit'; 2 + import { isValidHandle } from '@atproto/syntax'; 3 + import { OAuthResolverError } from '@atproto/oauth-client-node'; 4 + import { getOAuthClient } from '$lib/server/atproto/client'; 5 + import type { Actions, PageServerLoad } from './$types'; 6 + 7 + export const load: PageServerLoad = async ({ locals }) => { 8 + if (locals.user) { 9 + redirect(302, '/'); 10 + } 11 + 12 + return {}; 13 + }; 14 + 15 + export const actions: Actions = { 16 + default: async ({ request, url }) => { 17 + const data = await request.formData(); 18 + const handle = data.get('handle')?.toString(); 19 + 20 + if (!handle || !isValidHandle(handle)) { 21 + return fail(400, { error: 'Invalid handle' }); 22 + } 23 + 24 + const oauthClient = await getOAuthClient(); 25 + const authUrl = await oauthClient.authorize(handle, { 26 + scope: 'atproto transition:generic' 27 + }); 28 + 29 + // The authUrl should be a URL object, convert to string 30 + const urlString = authUrl instanceof URL ? authUrl.toString() : String(authUrl); 31 + console.log(urlString); 32 + redirect(302, urlString); 33 + } 34 + };
+48
src/routes/login/+page.svelte
··· 1 + <script lang="ts"> 2 + import { enhance } from '$app/forms'; 3 + import type { PageData, ActionData } from './$types'; 4 + 5 + export let form: ActionData; 6 + </script> 7 + 8 + <svelte:head> 9 + <title>Login - Oxiary</title> 10 + </svelte:head> 11 + 12 + <div class="flex min-h-screen items-center justify-center bg-ctp-base p-4"> 13 + <div class="w-full max-w-md rounded-lg border border-ctp-surface1 bg-ctp-surface0 p-8 shadow-xl"> 14 + <div class="mb-8 text-center"> 15 + <h2 class="mb-2 text-2xl font-bold text-ctp-text">Sign in to Oxiary</h2> 16 + <p class="text-ctp-subtext1">Track your books on the Atmosphere</p> 17 + </div> 18 + 19 + <form method="POST" use:enhance class="space-y-6"> 20 + {#if form?.error} 21 + <div class="rounded border border-ctp-red/30 bg-ctp-red/10 px-4 py-3 text-ctp-red"> 22 + {form.error} 23 + </div> 24 + {/if} 25 + 26 + <div class="space-y-2"> 27 + <label for="handle" class="block text-sm font-medium text-ctp-subtext1" 28 + >AT Protocol Handle</label 29 + > 30 + <input 31 + id="handle" 32 + name="handle" 33 + type="text" 34 + required 35 + class="w-full rounded border border-ctp-surface2 bg-ctp-surface1 px-3 py-3 text-ctp-text placeholder-ctp-overlay0 focus:border-ctp-blue focus:ring-2 focus:ring-ctp-blue focus:outline-none" 36 + placeholder="your-handle.bsky.social" 37 + /> 38 + </div> 39 + 40 + <button 41 + type="submit" 42 + class="w-full rounded bg-ctp-blue px-4 py-3 font-medium text-ctp-base transition-colors hover:bg-ctp-sapphire" 43 + > 44 + Sign in with AT Protocol 45 + </button> 46 + </form> 47 + </div> 48 + </div>
+8
src/routes/logout/+server.ts
··· 1 + import { redirect } from '@sveltejs/kit'; 2 + import { deleteSessionCookie } from '$lib/server/auth'; 3 + import type { RequestHandler } from './$types'; 4 + 5 + export const POST: RequestHandler = async ({ cookies }) => { 6 + deleteSessionCookie({ cookies } as any); 7 + redirect(302, '/'); 8 + };
+17
src/routes/oauth/callback/+server.ts
··· 1 + import { redirect } from '@sveltejs/kit'; 2 + import { getOAuthClient } from '$lib/server/atproto/client'; 3 + import { setSessionCookie } from '$lib/server/auth'; 4 + import type { RequestHandler } from './$types'; 5 + 6 + export const GET: RequestHandler = async (event) => { 7 + const { url } = event; 8 + const params = new URLSearchParams(url.search); 9 + 10 + const oauthClient = await getOAuthClient(); 11 + const { session } = await oauthClient.callback(params); 12 + 13 + // set the session cookie 14 + setSessionCookie(event, session.did); 15 + 16 + redirect(302, '/'); 17 + };
+5 -1
svelte.config.js
··· 11 11 // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. 12 12 // If your environment is not supported, or you settled on a specific environment, switch out the adapter. 13 13 // See https://svelte.dev/docs/kit/adapters for more information about adapters. 14 - adapter: adapter() 14 + adapter: adapter(), 15 + alias: { 16 + $lexicon: 'src/lexicon/types/app/oxiary', 17 + $: 'src' 18 + } 15 19 } 16 20 }; 17 21
+5 -1
vite.config.ts
··· 3 3 import { defineConfig } from 'vite'; 4 4 5 5 export default defineConfig({ 6 - plugins: [tailwindcss(), sveltekit()] 6 + plugins: [tailwindcss(), sveltekit()], 7 + server: { 8 + host: '0.0.0.0', 9 + port: 5173 10 + } 7 11 });