Compare changes

Choose any two refs to compare.

+6 -1
.gitignore
··· 23 23 vite.config.ts.timestamp-* 24 24 25 25 .idea 26 - local.db 26 + local.db 27 + 28 + #tap 29 + tap.db 30 + tap.db-shm 31 + tap.db-wal
+48
dev.compose.yml
··· 1 + services: 2 + postgres: 3 + image: postgres:18 4 + restart: unless-stopped 5 + environment: 6 + POSTGRES_USER: ${DB_USER} 7 + POSTGRES_PASSWORD: ${DB_PASSWORD} 8 + POSTGRES_DB: ${DB_NAME} 9 + ports: 10 + - "5432:5432" 11 + volumes: 12 + - postgres_data:/var/lib/postgresql 13 + extra_hosts: 14 + - "host.docker.internal:host-gateway" 15 + networks: 16 + - services-network 17 + valkey: 18 + image: valkey/valkey:9.0 19 + ports: 20 + - '${FORWARD_VAL_KEY_PORT:-6379}:6379' 21 + volumes: 22 + - 'valkey_data:/data' 23 + healthcheck: 24 + test: [ "CMD", "valkey-cli", "ping" ] 25 + retries: 3 26 + timeout: 5s 27 + networks: 28 + - services-network 29 + tap: 30 + image: ghcr.io/bluesky-social/indigo/tap:latest 31 + platform: linux/amd64 32 + depends_on: 33 + - postgres 34 + - valkey 35 + ports: 36 + - '2480:2480' 37 + env_file: 38 + - .env 39 + extra_hosts: 40 + - "host.docker.internal:host-gateway" 41 + network_mode: "host" 42 + 43 + volumes: 44 + valkey_data: 45 + postgres_data: 46 + networks: 47 + services-network: 48 + driver: bridge
+35
drizzle/0000_breezy_menace.sql
··· 1 + CREATE TABLE "record_pokes" ( 2 + "id" serial PRIMARY KEY NOT NULL, 3 + "recordId" integer, 4 + "pokersRepo" text NOT NULL, 5 + "atUri" text NOT NULL, 6 + "indexedAt" time DEFAULT now() NOT NULL 7 + ); 8 + --> statement-breakpoint 9 + CREATE TABLE "records" ( 10 + "id" serial PRIMARY KEY NOT NULL, 11 + "rkey" varchar NOT NULL, 12 + "collection" varchar NOT NULL, 13 + "repo" varchar NOT NULL, 14 + "atUri" text NOT NULL, 15 + "data" jsonb NOT NULL, 16 + "indexedAt" timestamp DEFAULT now() NOT NULL 17 + ); 18 + --> statement-breakpoint 19 + CREATE TABLE "user_pokes" ( 20 + "id" serial PRIMARY KEY NOT NULL, 21 + "subject" text NOT NULL, 22 + "poker" text NOT NULL, 23 + "at_uri" text NOT NULL, 24 + "indexedAt" time DEFAULT now() NOT NULL 25 + ); 26 + --> statement-breakpoint 27 + ALTER TABLE "record_pokes" ADD CONSTRAINT "record_pokes_recordId_records_id_fk" FOREIGN KEY ("recordId") REFERENCES "public"."records"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint 28 + CREATE INDEX "record_pokes_pokersRepo_index" ON "record_pokes" USING btree ("pokersRepo");--> statement-breakpoint 29 + CREATE INDEX "record_pokes_atUri_index" ON "record_pokes" USING btree ("atUri");--> statement-breakpoint 30 + CREATE INDEX "records_rkey_index" ON "records" USING btree ("rkey");--> statement-breakpoint 31 + CREATE INDEX "records_collection_index" ON "records" USING btree ("collection");--> statement-breakpoint 32 + CREATE INDEX "records_repo_index" ON "records" USING btree ("repo");--> statement-breakpoint 33 + CREATE UNIQUE INDEX "records_atUri_index" ON "records" USING btree ("atUri");--> statement-breakpoint 34 + CREATE INDEX "user_pokes_subject_index" ON "user_pokes" USING btree ("subject");--> statement-breakpoint 35 + CREATE INDEX "user_pokes_poker_index" ON "user_pokes" USING btree ("poker");
-14
drizzle/0000_cultured_thor_girl.sql
··· 1 - CREATE TABLE `key_value_store` ( 2 - `key` text PRIMARY KEY NOT NULL, 3 - `value` text, 4 - `storeName` text, 5 - `createdAt` integer 6 - ); 7 - --> statement-breakpoint 8 - CREATE TABLE `session_store` ( 9 - `id` text PRIMARY KEY NOT NULL, 10 - `did` text NOT NULL, 11 - `handle` text NOT NULL, 12 - `createdAt` integer NOT NULL, 13 - `expiresAt` integer NOT NULL 14 - );
+254 -55
drizzle/meta/0000_snapshot.json
··· 1 1 { 2 - "version": "6", 3 - "dialect": "sqlite", 4 - "id": "a1fafe21-58d6-4360-80a9-ab14cdefb1d6", 2 + "id": "b5b063c6-2451-42ca-8bef-0119b8202cb3", 5 3 "prevId": "00000000-0000-0000-0000-000000000000", 4 + "version": "7", 5 + "dialect": "postgresql", 6 6 "tables": { 7 - "key_value_store": { 8 - "name": "key_value_store", 7 + "public.record_pokes": { 8 + "name": "record_pokes", 9 + "schema": "", 9 10 "columns": { 10 - "key": { 11 - "name": "key", 11 + "id": { 12 + "name": "id", 13 + "type": "serial", 14 + "primaryKey": true, 15 + "notNull": true 16 + }, 17 + "recordId": { 18 + "name": "recordId", 19 + "type": "integer", 20 + "primaryKey": false, 21 + "notNull": false 22 + }, 23 + "pokersRepo": { 24 + "name": "pokersRepo", 25 + "type": "text", 26 + "primaryKey": false, 27 + "notNull": true 28 + }, 29 + "atUri": { 30 + "name": "atUri", 12 31 "type": "text", 32 + "primaryKey": false, 33 + "notNull": true 34 + }, 35 + "indexedAt": { 36 + "name": "indexedAt", 37 + "type": "time", 38 + "primaryKey": false, 39 + "notNull": true, 40 + "default": "now()" 41 + } 42 + }, 43 + "indexes": { 44 + "record_pokes_pokersRepo_index": { 45 + "name": "record_pokes_pokersRepo_index", 46 + "columns": [ 47 + { 48 + "expression": "pokersRepo", 49 + "isExpression": false, 50 + "asc": true, 51 + "nulls": "last" 52 + } 53 + ], 54 + "isUnique": false, 55 + "concurrently": false, 56 + "method": "btree", 57 + "with": {} 58 + }, 59 + "record_pokes_atUri_index": { 60 + "name": "record_pokes_atUri_index", 61 + "columns": [ 62 + { 63 + "expression": "atUri", 64 + "isExpression": false, 65 + "asc": true, 66 + "nulls": "last" 67 + } 68 + ], 69 + "isUnique": false, 70 + "concurrently": false, 71 + "method": "btree", 72 + "with": {} 73 + } 74 + }, 75 + "foreignKeys": { 76 + "record_pokes_recordId_records_id_fk": { 77 + "name": "record_pokes_recordId_records_id_fk", 78 + "tableFrom": "record_pokes", 79 + "tableTo": "records", 80 + "columnsFrom": [ 81 + "recordId" 82 + ], 83 + "columnsTo": [ 84 + "id" 85 + ], 86 + "onDelete": "no action", 87 + "onUpdate": "no action" 88 + } 89 + }, 90 + "compositePrimaryKeys": {}, 91 + "uniqueConstraints": {}, 92 + "policies": {}, 93 + "checkConstraints": {}, 94 + "isRLSEnabled": false 95 + }, 96 + "public.records": { 97 + "name": "records", 98 + "schema": "", 99 + "columns": { 100 + "id": { 101 + "name": "id", 102 + "type": "serial", 13 103 "primaryKey": true, 14 - "notNull": true, 15 - "autoincrement": false 104 + "notNull": true 105 + }, 106 + "rkey": { 107 + "name": "rkey", 108 + "type": "varchar", 109 + "primaryKey": false, 110 + "notNull": true 111 + }, 112 + "collection": { 113 + "name": "collection", 114 + "type": "varchar", 115 + "primaryKey": false, 116 + "notNull": true 16 117 }, 17 - "value": { 18 - "name": "value", 19 - "type": "text", 118 + "repo": { 119 + "name": "repo", 120 + "type": "varchar", 20 121 "primaryKey": false, 21 - "notNull": false, 22 - "autoincrement": false 122 + "notNull": true 23 123 }, 24 - "storeName": { 25 - "name": "storeName", 124 + "atUri": { 125 + "name": "atUri", 26 126 "type": "text", 27 127 "primaryKey": false, 28 - "notNull": false, 29 - "autoincrement": false 128 + "notNull": true 30 129 }, 31 - "createdAt": { 32 - "name": "createdAt", 33 - "type": "integer", 130 + "data": { 131 + "name": "data", 132 + "type": "jsonb", 133 + "primaryKey": false, 134 + "notNull": true 135 + }, 136 + "indexedAt": { 137 + "name": "indexedAt", 138 + "type": "timestamp", 34 139 "primaryKey": false, 35 - "notNull": false, 36 - "autoincrement": false 140 + "notNull": true, 141 + "default": "now()" 142 + } 143 + }, 144 + "indexes": { 145 + "records_rkey_index": { 146 + "name": "records_rkey_index", 147 + "columns": [ 148 + { 149 + "expression": "rkey", 150 + "isExpression": false, 151 + "asc": true, 152 + "nulls": "last" 153 + } 154 + ], 155 + "isUnique": false, 156 + "concurrently": false, 157 + "method": "btree", 158 + "with": {} 159 + }, 160 + "records_collection_index": { 161 + "name": "records_collection_index", 162 + "columns": [ 163 + { 164 + "expression": "collection", 165 + "isExpression": false, 166 + "asc": true, 167 + "nulls": "last" 168 + } 169 + ], 170 + "isUnique": false, 171 + "concurrently": false, 172 + "method": "btree", 173 + "with": {} 174 + }, 175 + "records_repo_index": { 176 + "name": "records_repo_index", 177 + "columns": [ 178 + { 179 + "expression": "repo", 180 + "isExpression": false, 181 + "asc": true, 182 + "nulls": "last" 183 + } 184 + ], 185 + "isUnique": false, 186 + "concurrently": false, 187 + "method": "btree", 188 + "with": {} 189 + }, 190 + "records_atUri_index": { 191 + "name": "records_atUri_index", 192 + "columns": [ 193 + { 194 + "expression": "atUri", 195 + "isExpression": false, 196 + "asc": true, 197 + "nulls": "last" 198 + } 199 + ], 200 + "isUnique": true, 201 + "concurrently": false, 202 + "method": "btree", 203 + "with": {} 37 204 } 38 205 }, 39 - "indexes": {}, 40 206 "foreignKeys": {}, 41 207 "compositePrimaryKeys": {}, 42 208 "uniqueConstraints": {}, 43 - "checkConstraints": {} 209 + "policies": {}, 210 + "checkConstraints": {}, 211 + "isRLSEnabled": false 44 212 }, 45 - "session_store": { 46 - "name": "session_store", 213 + "public.user_pokes": { 214 + "name": "user_pokes", 215 + "schema": "", 47 216 "columns": { 48 217 "id": { 49 218 "name": "id", 50 - "type": "text", 219 + "type": "serial", 51 220 "primaryKey": true, 52 - "notNull": true, 53 - "autoincrement": false 221 + "notNull": true 54 222 }, 55 - "did": { 56 - "name": "did", 223 + "subject": { 224 + "name": "subject", 57 225 "type": "text", 58 226 "primaryKey": false, 59 - "notNull": true, 60 - "autoincrement": false 227 + "notNull": true 61 228 }, 62 - "handle": { 63 - "name": "handle", 229 + "poker": { 230 + "name": "poker", 64 231 "type": "text", 65 232 "primaryKey": false, 66 - "notNull": true, 67 - "autoincrement": false 233 + "notNull": true 68 234 }, 69 - "createdAt": { 70 - "name": "createdAt", 71 - "type": "integer", 235 + "at_uri": { 236 + "name": "at_uri", 237 + "type": "text", 72 238 "primaryKey": false, 73 - "notNull": true, 74 - "autoincrement": false 239 + "notNull": true 75 240 }, 76 - "expiresAt": { 77 - "name": "expiresAt", 78 - "type": "integer", 241 + "indexedAt": { 242 + "name": "indexedAt", 243 + "type": "time", 79 244 "primaryKey": false, 80 245 "notNull": true, 81 - "autoincrement": false 246 + "default": "now()" 247 + } 248 + }, 249 + "indexes": { 250 + "user_pokes_subject_index": { 251 + "name": "user_pokes_subject_index", 252 + "columns": [ 253 + { 254 + "expression": "subject", 255 + "isExpression": false, 256 + "asc": true, 257 + "nulls": "last" 258 + } 259 + ], 260 + "isUnique": false, 261 + "concurrently": false, 262 + "method": "btree", 263 + "with": {} 264 + }, 265 + "user_pokes_poker_index": { 266 + "name": "user_pokes_poker_index", 267 + "columns": [ 268 + { 269 + "expression": "poker", 270 + "isExpression": false, 271 + "asc": true, 272 + "nulls": "last" 273 + } 274 + ], 275 + "isUnique": false, 276 + "concurrently": false, 277 + "method": "btree", 278 + "with": {} 82 279 } 83 280 }, 84 - "indexes": {}, 85 281 "foreignKeys": {}, 86 282 "compositePrimaryKeys": {}, 87 283 "uniqueConstraints": {}, 88 - "checkConstraints": {} 284 + "policies": {}, 285 + "checkConstraints": {}, 286 + "isRLSEnabled": false 89 287 } 90 288 }, 289 + "enums": {}, 290 + "schemas": {}, 291 + "sequences": {}, 292 + "roles": {}, 293 + "policies": {}, 91 294 "views": {}, 92 - "enums": {}, 93 295 "_meta": { 296 + "columns": {}, 94 297 "schemas": {}, 95 - "tables": {}, 96 - "columns": {} 97 - }, 98 - "internal": { 99 - "indexes": {} 298 + "tables": {} 100 299 } 101 300 }
+4 -4
drizzle/meta/_journal.json
··· 1 1 { 2 2 "version": "7", 3 - "dialect": "sqlite", 3 + "dialect": "postgresql", 4 4 "entries": [ 5 5 { 6 6 "idx": 0, 7 - "version": "6", 8 - "when": 1765476251050, 9 - "tag": "0000_cultured_thor_girl", 7 + "version": "7", 8 + "when": 1766121859810, 9 + "tag": "0000_breezy_menace", 10 10 "breakpoints": true 11 11 } 12 12 ]
+1 -1
drizzle.config.ts
··· 4 4 5 5 export default defineConfig({ 6 6 schema: './src/lib/server/db/schema.ts', 7 - dialect: 'sqlite', 7 + dialect: 'postgresql', 8 8 dbCredentials: { url: process.env.DATABASE_URL }, 9 9 verbose: true, 10 10 strict: true
+9 -3
package.json
··· 24 24 "@sveltejs/adapter-auto": "^7.0.0", 25 25 "@sveltejs/kit": "^2.48.5", 26 26 "@sveltejs/vite-plugin-svelte": "^6.2.1", 27 - "@types/better-sqlite3": "^7.6.13", 28 27 "@types/node": "^24", 28 + "@types/pg": "^8.16.0", 29 29 "dotenv": "^17.2.3", 30 30 "drizzle-kit": "^0.31.7", 31 31 "drizzle-orm": "^0.44.7", ··· 34 34 "globals": "^16.5.0", 35 35 "svelte": "^5.43.8", 36 36 "svelte-check": "^4.3.4", 37 + "tsx": "^4.21.0", 37 38 "typescript": "^5.9.3", 38 39 "typescript-eslint": "^8.47.0", 39 40 "vite": "^7.2.2" ··· 47 48 "@atproto/jwk-jose": "^0.1.11", 48 49 "@atproto/oauth-client-node": "^0.3.13", 49 50 "@atproto/oauth-types": "^0.5.2", 51 + "@atproto/tap": "^0.0.2", 50 52 "@oslojs/crypto": "^1.0.1", 51 53 "@oslojs/encoding": "^1.1.0", 52 54 "@sveltejs/adapter-node": "^5.4.0", 53 - "better-sqlite3": "12.4.1", 55 + "@tailwindcss/vite": "^4.1.18", 56 + "@valkey/valkey-glide": "^2.2.1", 57 + "daisyui": "^5.5.14", 54 58 "node-schedule": "^2.1.1", 55 - "pino": "^10.1.0" 59 + "pg": "^8.16.3", 60 + "pino": "^10.1.0", 61 + "tailwindcss": "^4.1.18" 56 62 }, 57 63 "optionalDependencies": { 58 64 "@rollup/rollup-linux-x64-musl": "^4.52.5"
+1018 -76
pnpm-lock.yaml
··· 32 32 '@atproto/oauth-types': 33 33 specifier: ^0.5.2 34 34 version: 0.5.2 35 + '@atproto/tap': 36 + specifier: ^0.0.2 37 + version: 0.0.2 35 38 '@oslojs/crypto': 36 39 specifier: ^1.0.1 37 40 version: 1.0.1 ··· 40 43 version: 1.1.0 41 44 '@sveltejs/adapter-node': 42 45 specifier: ^5.4.0 43 - version: 5.4.0(@sveltejs/kit@2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)))(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2))) 44 - better-sqlite3: 45 - specifier: 12.4.1 46 - version: 12.4.1 46 + version: 5.4.0(@sveltejs/kit@2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)))(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))) 47 + '@tailwindcss/vite': 48 + specifier: ^4.1.18 49 + version: 4.1.18(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) 50 + '@valkey/valkey-glide': 51 + specifier: ^2.2.1 52 + version: 2.2.1 53 + daisyui: 54 + specifier: ^5.5.14 55 + version: 5.5.14 47 56 node-schedule: 48 57 specifier: ^2.1.1 49 58 version: 2.1.1 59 + pg: 60 + specifier: ^8.16.3 61 + version: 8.16.3 50 62 pino: 51 63 specifier: ^10.1.0 52 64 version: 10.1.0 65 + tailwindcss: 66 + specifier: ^4.1.18 67 + version: 4.1.18 53 68 devDependencies: 54 69 '@eslint/compat': 55 70 specifier: ^1.4.0 56 - version: 1.4.1(eslint@9.39.1) 71 + version: 1.4.1(eslint@9.39.1(jiti@2.6.1)) 57 72 '@eslint/js': 58 73 specifier: ^9.39.1 59 74 version: 9.39.1 60 75 '@sveltejs/adapter-auto': 61 76 specifier: ^7.0.0 62 - version: 7.0.0(@sveltejs/kit@2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)))(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2))) 77 + version: 7.0.0(@sveltejs/kit@2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)))(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))) 63 78 '@sveltejs/kit': 64 79 specifier: ^2.48.5 65 - version: 2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)))(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)) 80 + version: 2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)))(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) 66 81 '@sveltejs/vite-plugin-svelte': 67 82 specifier: ^6.2.1 68 - version: 6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)) 69 - '@types/better-sqlite3': 70 - specifier: ^7.6.13 71 - version: 7.6.13 83 + version: 6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) 72 84 '@types/node': 73 85 specifier: ^24 74 86 version: 24.10.2 87 + '@types/pg': 88 + specifier: ^8.16.0 89 + version: 8.16.0 75 90 dotenv: 76 91 specifier: ^17.2.3 77 92 version: 17.2.3 ··· 80 95 version: 0.31.8 81 96 drizzle-orm: 82 97 specifier: ^0.44.7 83 - version: 0.44.7(@types/better-sqlite3@7.6.13)(better-sqlite3@12.4.1) 98 + version: 0.44.7(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(better-sqlite3@12.4.1)(pg@8.16.3) 84 99 eslint: 85 100 specifier: ^9.39.1 86 - version: 9.39.1 101 + version: 9.39.1(jiti@2.6.1) 87 102 eslint-plugin-svelte: 88 103 specifier: ^3.13.0 89 - version: 3.13.1(eslint@9.39.1)(svelte@5.45.8) 104 + version: 3.13.1(eslint@9.39.1(jiti@2.6.1))(svelte@5.45.8) 90 105 globals: 91 106 specifier: ^16.5.0 92 107 version: 16.5.0 ··· 96 111 svelte-check: 97 112 specifier: ^4.3.4 98 113 version: 4.3.4(picomatch@4.0.3)(svelte@5.45.8)(typescript@5.9.3) 114 + tsx: 115 + specifier: ^4.21.0 116 + version: 4.21.0 99 117 typescript: 100 118 specifier: ^5.9.3 101 119 version: 5.9.3 102 120 typescript-eslint: 103 121 specifier: ^8.47.0 104 - version: 8.49.0(eslint@9.39.1)(typescript@5.9.3) 122 + version: 8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) 105 123 vite: 106 124 specifier: ^7.2.2 107 - version: 7.2.7(@types/node@24.10.2) 125 + version: 7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) 108 126 optionalDependencies: 109 127 '@rollup/rollup-linux-x64-musl': 110 128 specifier: ^4.52.5 ··· 204 222 '@atproto/syntax@0.4.2': 205 223 resolution: {integrity: sha512-X9XSRPinBy/0VQ677j8VXlBsYSsUXaiqxWVpGGxJYsAhugdQRb0jqaVKJFtm6RskeNkV6y9xclSUi9UYG/COrA==} 206 224 225 + '@atproto/tap@0.0.2': 226 + resolution: {integrity: sha512-CrfJWrvozuSIokOQLMeSFcF5ZpstpxIZ9PnBpgIkbLQQKb3wO+0dn90xZN5jlLjczPHvT4PrF1z8uYgVlujTlg==} 227 + engines: {node: '>=18.7.0'} 228 + 229 + '@atproto/ws-client@0.0.4': 230 + resolution: {integrity: sha512-dox1XIymuC7/ZRhUqKezIGgooZS45C6vHCfu0PnWjfvsLCK2kAlnvX4IBkA/WpcoijDhQ9ejChnFbo/sLmgvAg==} 231 + engines: {node: '>=18.7.0'} 232 + 207 233 '@atproto/xrpc@0.7.7': 208 234 resolution: {integrity: sha512-K1ZyO/BU8JNtXX5dmPp7b5UrkLMMqpsIa/Lrj5D3Su+j1Xwq1m6QJ2XJ1AgjEjkI1v4Muzm7klianLE6XGxtmA==} 209 235 ··· 228 254 cpu: [ppc64] 229 255 os: [aix] 230 256 257 + '@esbuild/aix-ppc64@0.27.1': 258 + resolution: {integrity: sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==} 259 + engines: {node: '>=18'} 260 + cpu: [ppc64] 261 + os: [aix] 262 + 231 263 '@esbuild/android-arm64@0.18.20': 232 264 resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} 233 265 engines: {node: '>=12'} ··· 240 272 cpu: [arm64] 241 273 os: [android] 242 274 275 + '@esbuild/android-arm64@0.27.1': 276 + resolution: {integrity: sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==} 277 + engines: {node: '>=18'} 278 + cpu: [arm64] 279 + os: [android] 280 + 243 281 '@esbuild/android-arm@0.18.20': 244 282 resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} 245 283 engines: {node: '>=12'} ··· 252 290 cpu: [arm] 253 291 os: [android] 254 292 293 + '@esbuild/android-arm@0.27.1': 294 + resolution: {integrity: sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==} 295 + engines: {node: '>=18'} 296 + cpu: [arm] 297 + os: [android] 298 + 255 299 '@esbuild/android-x64@0.18.20': 256 300 resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} 257 301 engines: {node: '>=12'} ··· 264 308 cpu: [x64] 265 309 os: [android] 266 310 311 + '@esbuild/android-x64@0.27.1': 312 + resolution: {integrity: sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==} 313 + engines: {node: '>=18'} 314 + cpu: [x64] 315 + os: [android] 316 + 267 317 '@esbuild/darwin-arm64@0.18.20': 268 318 resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} 269 319 engines: {node: '>=12'} ··· 276 326 cpu: [arm64] 277 327 os: [darwin] 278 328 329 + '@esbuild/darwin-arm64@0.27.1': 330 + resolution: {integrity: sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==} 331 + engines: {node: '>=18'} 332 + cpu: [arm64] 333 + os: [darwin] 334 + 279 335 '@esbuild/darwin-x64@0.18.20': 280 336 resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} 281 337 engines: {node: '>=12'} ··· 288 344 cpu: [x64] 289 345 os: [darwin] 290 346 347 + '@esbuild/darwin-x64@0.27.1': 348 + resolution: {integrity: sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==} 349 + engines: {node: '>=18'} 350 + cpu: [x64] 351 + os: [darwin] 352 + 291 353 '@esbuild/freebsd-arm64@0.18.20': 292 354 resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} 293 355 engines: {node: '>=12'} ··· 300 362 cpu: [arm64] 301 363 os: [freebsd] 302 364 365 + '@esbuild/freebsd-arm64@0.27.1': 366 + resolution: {integrity: sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==} 367 + engines: {node: '>=18'} 368 + cpu: [arm64] 369 + os: [freebsd] 370 + 303 371 '@esbuild/freebsd-x64@0.18.20': 304 372 resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} 305 373 engines: {node: '>=12'} ··· 312 380 cpu: [x64] 313 381 os: [freebsd] 314 382 383 + '@esbuild/freebsd-x64@0.27.1': 384 + resolution: {integrity: sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==} 385 + engines: {node: '>=18'} 386 + cpu: [x64] 387 + os: [freebsd] 388 + 315 389 '@esbuild/linux-arm64@0.18.20': 316 390 resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} 317 391 engines: {node: '>=12'} ··· 320 394 321 395 '@esbuild/linux-arm64@0.25.12': 322 396 resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} 397 + engines: {node: '>=18'} 398 + cpu: [arm64] 399 + os: [linux] 400 + 401 + '@esbuild/linux-arm64@0.27.1': 402 + resolution: {integrity: sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==} 323 403 engines: {node: '>=18'} 324 404 cpu: [arm64] 325 405 os: [linux] ··· 336 416 cpu: [arm] 337 417 os: [linux] 338 418 419 + '@esbuild/linux-arm@0.27.1': 420 + resolution: {integrity: sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==} 421 + engines: {node: '>=18'} 422 + cpu: [arm] 423 + os: [linux] 424 + 339 425 '@esbuild/linux-ia32@0.18.20': 340 426 resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} 341 427 engines: {node: '>=12'} ··· 348 434 cpu: [ia32] 349 435 os: [linux] 350 436 437 + '@esbuild/linux-ia32@0.27.1': 438 + resolution: {integrity: sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==} 439 + engines: {node: '>=18'} 440 + cpu: [ia32] 441 + os: [linux] 442 + 351 443 '@esbuild/linux-loong64@0.18.20': 352 444 resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} 353 445 engines: {node: '>=12'} ··· 360 452 cpu: [loong64] 361 453 os: [linux] 362 454 455 + '@esbuild/linux-loong64@0.27.1': 456 + resolution: {integrity: sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==} 457 + engines: {node: '>=18'} 458 + cpu: [loong64] 459 + os: [linux] 460 + 363 461 '@esbuild/linux-mips64el@0.18.20': 364 462 resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} 365 463 engines: {node: '>=12'} ··· 368 466 369 467 '@esbuild/linux-mips64el@0.25.12': 370 468 resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} 469 + engines: {node: '>=18'} 470 + cpu: [mips64el] 471 + os: [linux] 472 + 473 + '@esbuild/linux-mips64el@0.27.1': 474 + resolution: {integrity: sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==} 371 475 engines: {node: '>=18'} 372 476 cpu: [mips64el] 373 477 os: [linux] ··· 384 488 cpu: [ppc64] 385 489 os: [linux] 386 490 491 + '@esbuild/linux-ppc64@0.27.1': 492 + resolution: {integrity: sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==} 493 + engines: {node: '>=18'} 494 + cpu: [ppc64] 495 + os: [linux] 496 + 387 497 '@esbuild/linux-riscv64@0.18.20': 388 498 resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} 389 499 engines: {node: '>=12'} ··· 396 506 cpu: [riscv64] 397 507 os: [linux] 398 508 509 + '@esbuild/linux-riscv64@0.27.1': 510 + resolution: {integrity: sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==} 511 + engines: {node: '>=18'} 512 + cpu: [riscv64] 513 + os: [linux] 514 + 399 515 '@esbuild/linux-s390x@0.18.20': 400 516 resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} 401 517 engines: {node: '>=12'} ··· 408 524 cpu: [s390x] 409 525 os: [linux] 410 526 527 + '@esbuild/linux-s390x@0.27.1': 528 + resolution: {integrity: sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==} 529 + engines: {node: '>=18'} 530 + cpu: [s390x] 531 + os: [linux] 532 + 411 533 '@esbuild/linux-x64@0.18.20': 412 534 resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} 413 535 engines: {node: '>=12'} ··· 420 542 cpu: [x64] 421 543 os: [linux] 422 544 545 + '@esbuild/linux-x64@0.27.1': 546 + resolution: {integrity: sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==} 547 + engines: {node: '>=18'} 548 + cpu: [x64] 549 + os: [linux] 550 + 423 551 '@esbuild/netbsd-arm64@0.25.12': 424 552 resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} 425 553 engines: {node: '>=18'} 426 554 cpu: [arm64] 427 555 os: [netbsd] 428 556 557 + '@esbuild/netbsd-arm64@0.27.1': 558 + resolution: {integrity: sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==} 559 + engines: {node: '>=18'} 560 + cpu: [arm64] 561 + os: [netbsd] 562 + 429 563 '@esbuild/netbsd-x64@0.18.20': 430 564 resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} 431 565 engines: {node: '>=12'} ··· 434 568 435 569 '@esbuild/netbsd-x64@0.25.12': 436 570 resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} 571 + engines: {node: '>=18'} 572 + cpu: [x64] 573 + os: [netbsd] 574 + 575 + '@esbuild/netbsd-x64@0.27.1': 576 + resolution: {integrity: sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==} 437 577 engines: {node: '>=18'} 438 578 cpu: [x64] 439 579 os: [netbsd] ··· 444 584 cpu: [arm64] 445 585 os: [openbsd] 446 586 587 + '@esbuild/openbsd-arm64@0.27.1': 588 + resolution: {integrity: sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==} 589 + engines: {node: '>=18'} 590 + cpu: [arm64] 591 + os: [openbsd] 592 + 447 593 '@esbuild/openbsd-x64@0.18.20': 448 594 resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} 449 595 engines: {node: '>=12'} ··· 456 602 cpu: [x64] 457 603 os: [openbsd] 458 604 605 + '@esbuild/openbsd-x64@0.27.1': 606 + resolution: {integrity: sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==} 607 + engines: {node: '>=18'} 608 + cpu: [x64] 609 + os: [openbsd] 610 + 459 611 '@esbuild/openharmony-arm64@0.25.12': 460 612 resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} 461 613 engines: {node: '>=18'} 462 614 cpu: [arm64] 463 615 os: [openharmony] 464 616 617 + '@esbuild/openharmony-arm64@0.27.1': 618 + resolution: {integrity: sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==} 619 + engines: {node: '>=18'} 620 + cpu: [arm64] 621 + os: [openharmony] 622 + 465 623 '@esbuild/sunos-x64@0.18.20': 466 624 resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} 467 625 engines: {node: '>=12'} ··· 474 632 cpu: [x64] 475 633 os: [sunos] 476 634 635 + '@esbuild/sunos-x64@0.27.1': 636 + resolution: {integrity: sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==} 637 + engines: {node: '>=18'} 638 + cpu: [x64] 639 + os: [sunos] 640 + 477 641 '@esbuild/win32-arm64@0.18.20': 478 642 resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} 479 643 engines: {node: '>=12'} ··· 486 650 cpu: [arm64] 487 651 os: [win32] 488 652 653 + '@esbuild/win32-arm64@0.27.1': 654 + resolution: {integrity: sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==} 655 + engines: {node: '>=18'} 656 + cpu: [arm64] 657 + os: [win32] 658 + 489 659 '@esbuild/win32-ia32@0.18.20': 490 660 resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} 491 661 engines: {node: '>=12'} ··· 498 668 cpu: [ia32] 499 669 os: [win32] 500 670 671 + '@esbuild/win32-ia32@0.27.1': 672 + resolution: {integrity: sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==} 673 + engines: {node: '>=18'} 674 + cpu: [ia32] 675 + os: [win32] 676 + 501 677 '@esbuild/win32-x64@0.18.20': 502 678 resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} 503 679 engines: {node: '>=12'} ··· 506 682 507 683 '@esbuild/win32-x64@0.25.12': 508 684 resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} 685 + engines: {node: '>=18'} 686 + cpu: [x64] 687 + os: [win32] 688 + 689 + '@esbuild/win32-x64@0.27.1': 690 + resolution: {integrity: sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==} 509 691 engines: {node: '>=18'} 510 692 cpu: [x64] 511 693 os: [win32] ··· 614 796 615 797 '@polka/url@1.0.0-next.29': 616 798 resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} 799 + 800 + '@protobufjs/aspromise@1.1.2': 801 + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} 802 + 803 + '@protobufjs/base64@1.1.2': 804 + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} 805 + 806 + '@protobufjs/codegen@2.0.4': 807 + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} 808 + 809 + '@protobufjs/eventemitter@1.1.0': 810 + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} 811 + 812 + '@protobufjs/fetch@1.1.0': 813 + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} 814 + 815 + '@protobufjs/float@1.0.2': 816 + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} 817 + 818 + '@protobufjs/inquire@1.1.0': 819 + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} 820 + 821 + '@protobufjs/path@1.1.2': 822 + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} 823 + 824 + '@protobufjs/pool@1.1.0': 825 + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} 826 + 827 + '@protobufjs/utf8@1.1.0': 828 + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} 617 829 618 830 '@rollup/plugin-commonjs@28.0.9': 619 831 resolution: {integrity: sha512-PIR4/OHZ79romx0BVVll/PkwWpJ7e5lsqFa3gFfcrFPWwLXLV39JVUzQV9RKjWerE7B845Hqjj9VYlQeieZ2dA==} ··· 807 1019 svelte: ^5.0.0 808 1020 vite: ^6.3.0 || ^7.0.0 809 1021 1022 + '@tailwindcss/node@4.1.18': 1023 + resolution: {integrity: sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==} 1024 + 1025 + '@tailwindcss/oxide-android-arm64@4.1.18': 1026 + resolution: {integrity: sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==} 1027 + engines: {node: '>= 10'} 1028 + cpu: [arm64] 1029 + os: [android] 1030 + 1031 + '@tailwindcss/oxide-darwin-arm64@4.1.18': 1032 + resolution: {integrity: sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==} 1033 + engines: {node: '>= 10'} 1034 + cpu: [arm64] 1035 + os: [darwin] 1036 + 1037 + '@tailwindcss/oxide-darwin-x64@4.1.18': 1038 + resolution: {integrity: sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==} 1039 + engines: {node: '>= 10'} 1040 + cpu: [x64] 1041 + os: [darwin] 1042 + 1043 + '@tailwindcss/oxide-freebsd-x64@4.1.18': 1044 + resolution: {integrity: sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==} 1045 + engines: {node: '>= 10'} 1046 + cpu: [x64] 1047 + os: [freebsd] 1048 + 1049 + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': 1050 + resolution: {integrity: sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==} 1051 + engines: {node: '>= 10'} 1052 + cpu: [arm] 1053 + os: [linux] 1054 + 1055 + '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': 1056 + resolution: {integrity: sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==} 1057 + engines: {node: '>= 10'} 1058 + cpu: [arm64] 1059 + os: [linux] 1060 + 1061 + '@tailwindcss/oxide-linux-arm64-musl@4.1.18': 1062 + resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==} 1063 + engines: {node: '>= 10'} 1064 + cpu: [arm64] 1065 + os: [linux] 1066 + 1067 + '@tailwindcss/oxide-linux-x64-gnu@4.1.18': 1068 + resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==} 1069 + engines: {node: '>= 10'} 1070 + cpu: [x64] 1071 + os: [linux] 1072 + 1073 + '@tailwindcss/oxide-linux-x64-musl@4.1.18': 1074 + resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==} 1075 + engines: {node: '>= 10'} 1076 + cpu: [x64] 1077 + os: [linux] 1078 + 1079 + '@tailwindcss/oxide-wasm32-wasi@4.1.18': 1080 + resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==} 1081 + engines: {node: '>=14.0.0'} 1082 + cpu: [wasm32] 1083 + bundledDependencies: 1084 + - '@napi-rs/wasm-runtime' 1085 + - '@emnapi/core' 1086 + - '@emnapi/runtime' 1087 + - '@tybys/wasm-util' 1088 + - '@emnapi/wasi-threads' 1089 + - tslib 1090 + 1091 + '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': 1092 + resolution: {integrity: sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==} 1093 + engines: {node: '>= 10'} 1094 + cpu: [arm64] 1095 + os: [win32] 1096 + 1097 + '@tailwindcss/oxide-win32-x64-msvc@4.1.18': 1098 + resolution: {integrity: sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==} 1099 + engines: {node: '>= 10'} 1100 + cpu: [x64] 1101 + os: [win32] 1102 + 1103 + '@tailwindcss/oxide@4.1.18': 1104 + resolution: {integrity: sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==} 1105 + engines: {node: '>= 10'} 1106 + 1107 + '@tailwindcss/vite@4.1.18': 1108 + resolution: {integrity: sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==} 1109 + peerDependencies: 1110 + vite: ^5.2.0 || ^6 || ^7 1111 + 810 1112 '@types/better-sqlite3@7.6.13': 811 1113 resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==} 812 1114 ··· 821 1123 822 1124 '@types/node@24.10.2': 823 1125 resolution: {integrity: sha512-WOhQTZ4G8xZ1tjJTvKOpyEVSGgOTvJAfDK3FNFgELyaTpzhdgHVHeqW8V+UJvzF5BT+/B54T/1S2K6gd9c7bbA==} 1126 + 1127 + '@types/pg@8.16.0': 1128 + resolution: {integrity: sha512-RmhMd/wD+CF8Dfo+cVIy3RR5cl8CyfXQ0tGgW6XBL8L4LM/UTEbNXYRbLwU6w+CgrKBNbrQWt4FUtTfaU5jSYQ==} 824 1129 825 1130 '@types/resolve@1.20.2': 826 1131 resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} ··· 884 1189 resolution: {integrity: sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==} 885 1190 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 886 1191 1192 + '@valkey/valkey-glide-darwin-arm64@2.2.1': 1193 + resolution: {integrity: sha512-4peyagRot9TOyYn1Iogt5lbQo8jd/aqAAn99iCbPCz8c4iUXeBT57KcDDql3lJinzw/YEJF35xJU2FO5XP7pyg==} 1194 + cpu: [arm64] 1195 + os: [darwin] 1196 + 1197 + '@valkey/valkey-glide-darwin-x64@2.2.1': 1198 + resolution: {integrity: sha512-+jxY9PnnlbIEuaf9guETibvoayErOMYnWAfVw7PirAAcliwwQ/xzZnrq9kEosU7jm3RClyPrSmVP3XdyRVzctg==} 1199 + cpu: [x64] 1200 + os: [darwin] 1201 + 1202 + '@valkey/valkey-glide-linux-arm64-gnu@2.2.1': 1203 + resolution: {integrity: sha512-B28NzpIk25bAP45nMnU9gt89TaCg/dTBgyBCTc3fNKtHRuRSdOF0pwrI03nhQ7b5EnEAOhjnHMEVnXJIB8aFtA==} 1204 + cpu: [arm64] 1205 + os: [linux] 1206 + 1207 + '@valkey/valkey-glide-linux-arm64-musl@2.2.1': 1208 + resolution: {integrity: sha512-TQDYDOG8GzBNybSx1789ik8UTo5DpCRMrlRHKA+ClhoYg1uGa48druRerqOHqkEOOBzK7IKjHOQiKW9QJNed0A==} 1209 + cpu: [arm64] 1210 + os: [linux] 1211 + 1212 + '@valkey/valkey-glide-linux-x64-gnu@2.2.1': 1213 + resolution: {integrity: sha512-W3pZgFJmV2DVzNeFV/uX/885Fizx9px//SDOBNu+F63aTwkm+FBZaqJ0qaAg81GaABdaqq8VfTmh5aidH77t7A==} 1214 + cpu: [x64] 1215 + os: [linux] 1216 + 1217 + '@valkey/valkey-glide-linux-x64-musl@2.2.1': 1218 + resolution: {integrity: sha512-Pyzv5535g8A92iKDzVGI8vXX0Hn7+G3Y6VclqHusZvz8xULWV46Kzsq4HUlhvnABm5zm2rvObIo9eoaMxhQooQ==} 1219 + cpu: [x64] 1220 + os: [linux] 1221 + 1222 + '@valkey/valkey-glide@2.2.1': 1223 + resolution: {integrity: sha512-VsF2GbtSE2olsFdKHEKffnVIkriH5k63d1ammcL/xALZMGg6OZTubuwRPB0yXdLo26QVTMcrs+d2xptE00S/qA==} 1224 + engines: {node: '>=16'} 1225 + 887 1226 abort-controller@3.0.0: 888 1227 resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} 889 1228 engines: {node: '>=6.5'} ··· 1005 1344 resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} 1006 1345 engines: {node: '>=4'} 1007 1346 hasBin: true 1347 + 1348 + daisyui@5.5.14: 1349 + resolution: {integrity: sha512-L47rvw7I7hK68TA97VB8Ee0woHew+/ohR6Lx6Ah/krfISOqcG4My7poNpX5Mo5/ytMxiR40fEaz6njzDi7cuSg==} 1008 1350 1009 1351 debug@4.4.3: 1010 1352 resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} ··· 1140 1482 end-of-stream@1.4.5: 1141 1483 resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} 1142 1484 1485 + enhanced-resolve@5.18.4: 1486 + resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==} 1487 + engines: {node: '>=10.13.0'} 1488 + 1143 1489 esbuild-register@3.6.0: 1144 1490 resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==} 1145 1491 peerDependencies: ··· 1152 1498 1153 1499 esbuild@0.25.12: 1154 1500 resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} 1501 + engines: {node: '>=18'} 1502 + hasBin: true 1503 + 1504 + esbuild@0.27.1: 1505 + resolution: {integrity: sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==} 1155 1506 engines: {node: '>=18'} 1156 1507 hasBin: true 1157 1508 ··· 1301 1652 resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} 1302 1653 engines: {node: '>=18'} 1303 1654 1655 + graceful-fs@4.2.11: 1656 + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} 1657 + 1304 1658 has-flag@4.0.0: 1305 1659 resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 1306 1660 engines: {node: '>=8'} ··· 1365 1719 iso-datestring-validator@2.2.2: 1366 1720 resolution: {integrity: sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==} 1367 1721 1722 + jiti@2.6.1: 1723 + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} 1724 + hasBin: true 1725 + 1368 1726 jose@5.10.0: 1369 1727 resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==} 1370 1728 ··· 1395 1753 resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} 1396 1754 engines: {node: '>= 0.8.0'} 1397 1755 1756 + lightningcss-android-arm64@1.30.2: 1757 + resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==} 1758 + engines: {node: '>= 12.0.0'} 1759 + cpu: [arm64] 1760 + os: [android] 1761 + 1762 + lightningcss-darwin-arm64@1.30.2: 1763 + resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==} 1764 + engines: {node: '>= 12.0.0'} 1765 + cpu: [arm64] 1766 + os: [darwin] 1767 + 1768 + lightningcss-darwin-x64@1.30.2: 1769 + resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==} 1770 + engines: {node: '>= 12.0.0'} 1771 + cpu: [x64] 1772 + os: [darwin] 1773 + 1774 + lightningcss-freebsd-x64@1.30.2: 1775 + resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==} 1776 + engines: {node: '>= 12.0.0'} 1777 + cpu: [x64] 1778 + os: [freebsd] 1779 + 1780 + lightningcss-linux-arm-gnueabihf@1.30.2: 1781 + resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==} 1782 + engines: {node: '>= 12.0.0'} 1783 + cpu: [arm] 1784 + os: [linux] 1785 + 1786 + lightningcss-linux-arm64-gnu@1.30.2: 1787 + resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==} 1788 + engines: {node: '>= 12.0.0'} 1789 + cpu: [arm64] 1790 + os: [linux] 1791 + 1792 + lightningcss-linux-arm64-musl@1.30.2: 1793 + resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} 1794 + engines: {node: '>= 12.0.0'} 1795 + cpu: [arm64] 1796 + os: [linux] 1797 + 1798 + lightningcss-linux-x64-gnu@1.30.2: 1799 + resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} 1800 + engines: {node: '>= 12.0.0'} 1801 + cpu: [x64] 1802 + os: [linux] 1803 + 1804 + lightningcss-linux-x64-musl@1.30.2: 1805 + resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} 1806 + engines: {node: '>= 12.0.0'} 1807 + cpu: [x64] 1808 + os: [linux] 1809 + 1810 + lightningcss-win32-arm64-msvc@1.30.2: 1811 + resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} 1812 + engines: {node: '>= 12.0.0'} 1813 + cpu: [arm64] 1814 + os: [win32] 1815 + 1816 + lightningcss-win32-x64-msvc@1.30.2: 1817 + resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==} 1818 + engines: {node: '>= 12.0.0'} 1819 + cpu: [x64] 1820 + os: [win32] 1821 + 1822 + lightningcss@1.30.2: 1823 + resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} 1824 + engines: {node: '>= 12.0.0'} 1825 + 1398 1826 lilconfig@2.1.0: 1399 1827 resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} 1400 1828 engines: {node: '>=10'} ··· 1411 1839 1412 1840 long-timeout@0.1.1: 1413 1841 resolution: {integrity: sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==} 1842 + 1843 + long@5.3.2: 1844 + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} 1414 1845 1415 1846 lru-cache@10.4.3: 1416 1847 resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} ··· 1506 1937 path-parse@1.0.7: 1507 1938 resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} 1508 1939 1940 + pg-cloudflare@1.2.7: 1941 + resolution: {integrity: sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==} 1942 + 1943 + pg-connection-string@2.9.1: 1944 + resolution: {integrity: sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==} 1945 + 1946 + pg-int8@1.0.1: 1947 + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} 1948 + engines: {node: '>=4.0.0'} 1949 + 1950 + pg-pool@3.10.1: 1951 + resolution: {integrity: sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==} 1952 + peerDependencies: 1953 + pg: '>=8.0' 1954 + 1955 + pg-protocol@1.10.3: 1956 + resolution: {integrity: sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==} 1957 + 1958 + pg-types@2.2.0: 1959 + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} 1960 + engines: {node: '>=4'} 1961 + 1962 + pg@8.16.3: 1963 + resolution: {integrity: sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==} 1964 + engines: {node: '>= 16.0.0'} 1965 + peerDependencies: 1966 + pg-native: '>=3.0.1' 1967 + peerDependenciesMeta: 1968 + pg-native: 1969 + optional: true 1970 + 1971 + pgpass@1.0.5: 1972 + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} 1973 + 1509 1974 picocolors@1.1.1: 1510 1975 resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 1511 1976 ··· 1564 2029 postcss@8.5.6: 1565 2030 resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} 1566 2031 engines: {node: ^10 || ^12 || >=14} 2032 + 2033 + postgres-array@2.0.0: 2034 + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} 2035 + engines: {node: '>=4'} 2036 + 2037 + postgres-bytea@1.0.0: 2038 + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} 2039 + engines: {node: '>=0.10.0'} 2040 + 2041 + postgres-date@1.0.7: 2042 + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} 2043 + engines: {node: '>=0.10.0'} 2044 + 2045 + postgres-interval@1.2.0: 2046 + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} 2047 + engines: {node: '>=0.10.0'} 1567 2048 1568 2049 prebuild-install@7.1.3: 1569 2050 resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} ··· 1584 2065 resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} 1585 2066 engines: {node: '>= 0.6.0'} 1586 2067 2068 + protobufjs@7.5.4: 2069 + resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} 2070 + engines: {node: '>=12.0.0'} 2071 + 1587 2072 pump@3.0.3: 1588 2073 resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} 1589 2074 ··· 1732 2217 resolution: {integrity: sha512-1Jh7FwVh/2Uxg0T7SeE1qFKMhwYH45b2v53bcZpW7qHa6O8iU1ByEj56PF0IQ6dU4HE5gRkic6h+vx+tclHeiw==} 1733 2218 engines: {node: '>=18'} 1734 2219 2220 + tailwindcss@4.1.18: 2221 + resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==} 2222 + 2223 + tapable@2.3.0: 2224 + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} 2225 + engines: {node: '>=6'} 2226 + 1735 2227 tar-fs@2.1.4: 1736 2228 resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==} 1737 2229 ··· 1765 2257 1766 2258 tslib@2.8.1: 1767 2259 resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 2260 + 2261 + tsx@4.21.0: 2262 + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} 2263 + engines: {node: '>=18.0.0'} 2264 + hasBin: true 1768 2265 1769 2266 tunnel-agent@0.6.0: 1770 2267 resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} ··· 1864 2361 wrappy@1.0.2: 1865 2362 resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} 1866 2363 2364 + ws@8.18.3: 2365 + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} 2366 + engines: {node: '>=10.0.0'} 2367 + peerDependencies: 2368 + bufferutil: ^4.0.1 2369 + utf-8-validate: '>=5.0.2' 2370 + peerDependenciesMeta: 2371 + bufferutil: 2372 + optional: true 2373 + utf-8-validate: 2374 + optional: true 2375 + 2376 + xtend@4.0.2: 2377 + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} 2378 + engines: {node: '>=0.4'} 2379 + 1867 2380 yaml@1.10.2: 1868 2381 resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} 1869 2382 engines: {node: '>= 6'} ··· 2061 2574 2062 2575 '@atproto/syntax@0.4.2': {} 2063 2576 2577 + '@atproto/tap@0.0.2': 2578 + dependencies: 2579 + '@atproto/common': 0.5.3 2580 + '@atproto/syntax': 0.4.2 2581 + '@atproto/ws-client': 0.0.4 2582 + ws: 8.18.3 2583 + zod: 3.25.76 2584 + transitivePeerDependencies: 2585 + - bufferutil 2586 + - utf-8-validate 2587 + 2588 + '@atproto/ws-client@0.0.4': 2589 + dependencies: 2590 + '@atproto/common': 0.5.3 2591 + ws: 8.18.3 2592 + transitivePeerDependencies: 2593 + - bufferutil 2594 + - utf-8-validate 2595 + 2064 2596 '@atproto/xrpc@0.7.7': 2065 2597 dependencies: 2066 2598 '@atproto/lexicon': 0.6.0 ··· 2083 2615 '@esbuild/aix-ppc64@0.25.12': 2084 2616 optional: true 2085 2617 2618 + '@esbuild/aix-ppc64@0.27.1': 2619 + optional: true 2620 + 2086 2621 '@esbuild/android-arm64@0.18.20': 2087 2622 optional: true 2088 2623 2089 2624 '@esbuild/android-arm64@0.25.12': 2625 + optional: true 2626 + 2627 + '@esbuild/android-arm64@0.27.1': 2090 2628 optional: true 2091 2629 2092 2630 '@esbuild/android-arm@0.18.20': ··· 2095 2633 '@esbuild/android-arm@0.25.12': 2096 2634 optional: true 2097 2635 2636 + '@esbuild/android-arm@0.27.1': 2637 + optional: true 2638 + 2098 2639 '@esbuild/android-x64@0.18.20': 2099 2640 optional: true 2100 2641 2101 2642 '@esbuild/android-x64@0.25.12': 2102 2643 optional: true 2103 2644 2645 + '@esbuild/android-x64@0.27.1': 2646 + optional: true 2647 + 2104 2648 '@esbuild/darwin-arm64@0.18.20': 2105 2649 optional: true 2106 2650 2107 2651 '@esbuild/darwin-arm64@0.25.12': 2108 2652 optional: true 2109 2653 2654 + '@esbuild/darwin-arm64@0.27.1': 2655 + optional: true 2656 + 2110 2657 '@esbuild/darwin-x64@0.18.20': 2111 2658 optional: true 2112 2659 2113 2660 '@esbuild/darwin-x64@0.25.12': 2661 + optional: true 2662 + 2663 + '@esbuild/darwin-x64@0.27.1': 2114 2664 optional: true 2115 2665 2116 2666 '@esbuild/freebsd-arm64@0.18.20': ··· 2119 2669 '@esbuild/freebsd-arm64@0.25.12': 2120 2670 optional: true 2121 2671 2672 + '@esbuild/freebsd-arm64@0.27.1': 2673 + optional: true 2674 + 2122 2675 '@esbuild/freebsd-x64@0.18.20': 2123 2676 optional: true 2124 2677 2125 2678 '@esbuild/freebsd-x64@0.25.12': 2126 2679 optional: true 2127 2680 2681 + '@esbuild/freebsd-x64@0.27.1': 2682 + optional: true 2683 + 2128 2684 '@esbuild/linux-arm64@0.18.20': 2129 2685 optional: true 2130 2686 2131 2687 '@esbuild/linux-arm64@0.25.12': 2688 + optional: true 2689 + 2690 + '@esbuild/linux-arm64@0.27.1': 2132 2691 optional: true 2133 2692 2134 2693 '@esbuild/linux-arm@0.18.20': ··· 2137 2696 '@esbuild/linux-arm@0.25.12': 2138 2697 optional: true 2139 2698 2699 + '@esbuild/linux-arm@0.27.1': 2700 + optional: true 2701 + 2140 2702 '@esbuild/linux-ia32@0.18.20': 2141 2703 optional: true 2142 2704 2143 2705 '@esbuild/linux-ia32@0.25.12': 2144 2706 optional: true 2145 2707 2708 + '@esbuild/linux-ia32@0.27.1': 2709 + optional: true 2710 + 2146 2711 '@esbuild/linux-loong64@0.18.20': 2147 2712 optional: true 2148 2713 2149 2714 '@esbuild/linux-loong64@0.25.12': 2150 2715 optional: true 2151 2716 2717 + '@esbuild/linux-loong64@0.27.1': 2718 + optional: true 2719 + 2152 2720 '@esbuild/linux-mips64el@0.18.20': 2153 2721 optional: true 2154 2722 2155 2723 '@esbuild/linux-mips64el@0.25.12': 2156 2724 optional: true 2157 2725 2726 + '@esbuild/linux-mips64el@0.27.1': 2727 + optional: true 2728 + 2158 2729 '@esbuild/linux-ppc64@0.18.20': 2159 2730 optional: true 2160 2731 2161 2732 '@esbuild/linux-ppc64@0.25.12': 2162 2733 optional: true 2163 2734 2735 + '@esbuild/linux-ppc64@0.27.1': 2736 + optional: true 2737 + 2164 2738 '@esbuild/linux-riscv64@0.18.20': 2165 2739 optional: true 2166 2740 2167 2741 '@esbuild/linux-riscv64@0.25.12': 2168 2742 optional: true 2169 2743 2744 + '@esbuild/linux-riscv64@0.27.1': 2745 + optional: true 2746 + 2170 2747 '@esbuild/linux-s390x@0.18.20': 2171 2748 optional: true 2172 2749 2173 2750 '@esbuild/linux-s390x@0.25.12': 2751 + optional: true 2752 + 2753 + '@esbuild/linux-s390x@0.27.1': 2174 2754 optional: true 2175 2755 2176 2756 '@esbuild/linux-x64@0.18.20': ··· 2179 2759 '@esbuild/linux-x64@0.25.12': 2180 2760 optional: true 2181 2761 2762 + '@esbuild/linux-x64@0.27.1': 2763 + optional: true 2764 + 2182 2765 '@esbuild/netbsd-arm64@0.25.12': 2183 2766 optional: true 2184 2767 2768 + '@esbuild/netbsd-arm64@0.27.1': 2769 + optional: true 2770 + 2185 2771 '@esbuild/netbsd-x64@0.18.20': 2186 2772 optional: true 2187 2773 2188 2774 '@esbuild/netbsd-x64@0.25.12': 2775 + optional: true 2776 + 2777 + '@esbuild/netbsd-x64@0.27.1': 2189 2778 optional: true 2190 2779 2191 2780 '@esbuild/openbsd-arm64@0.25.12': 2192 2781 optional: true 2193 2782 2783 + '@esbuild/openbsd-arm64@0.27.1': 2784 + optional: true 2785 + 2194 2786 '@esbuild/openbsd-x64@0.18.20': 2195 2787 optional: true 2196 2788 2197 2789 '@esbuild/openbsd-x64@0.25.12': 2198 2790 optional: true 2199 2791 2792 + '@esbuild/openbsd-x64@0.27.1': 2793 + optional: true 2794 + 2200 2795 '@esbuild/openharmony-arm64@0.25.12': 2201 2796 optional: true 2202 2797 2798 + '@esbuild/openharmony-arm64@0.27.1': 2799 + optional: true 2800 + 2203 2801 '@esbuild/sunos-x64@0.18.20': 2204 2802 optional: true 2205 2803 2206 2804 '@esbuild/sunos-x64@0.25.12': 2805 + optional: true 2806 + 2807 + '@esbuild/sunos-x64@0.27.1': 2207 2808 optional: true 2208 2809 2209 2810 '@esbuild/win32-arm64@0.18.20': ··· 2212 2813 '@esbuild/win32-arm64@0.25.12': 2213 2814 optional: true 2214 2815 2816 + '@esbuild/win32-arm64@0.27.1': 2817 + optional: true 2818 + 2215 2819 '@esbuild/win32-ia32@0.18.20': 2216 2820 optional: true 2217 2821 2218 2822 '@esbuild/win32-ia32@0.25.12': 2219 2823 optional: true 2220 2824 2825 + '@esbuild/win32-ia32@0.27.1': 2826 + optional: true 2827 + 2221 2828 '@esbuild/win32-x64@0.18.20': 2222 2829 optional: true 2223 2830 2224 2831 '@esbuild/win32-x64@0.25.12': 2225 2832 optional: true 2226 2833 2227 - '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1)': 2834 + '@esbuild/win32-x64@0.27.1': 2835 + optional: true 2836 + 2837 + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1(jiti@2.6.1))': 2228 2838 dependencies: 2229 - eslint: 9.39.1 2839 + eslint: 9.39.1(jiti@2.6.1) 2230 2840 eslint-visitor-keys: 3.4.3 2231 2841 2232 2842 '@eslint-community/regexpp@4.12.2': {} 2233 2843 2234 - '@eslint/compat@1.4.1(eslint@9.39.1)': 2844 + '@eslint/compat@1.4.1(eslint@9.39.1(jiti@2.6.1))': 2235 2845 dependencies: 2236 2846 '@eslint/core': 0.17.0 2237 2847 optionalDependencies: 2238 - eslint: 9.39.1 2848 + eslint: 9.39.1(jiti@2.6.1) 2239 2849 2240 2850 '@eslint/config-array@0.21.1': 2241 2851 dependencies: ··· 2329 2939 2330 2940 '@polka/url@1.0.0-next.29': {} 2331 2941 2942 + '@protobufjs/aspromise@1.1.2': {} 2943 + 2944 + '@protobufjs/base64@1.1.2': {} 2945 + 2946 + '@protobufjs/codegen@2.0.4': {} 2947 + 2948 + '@protobufjs/eventemitter@1.1.0': {} 2949 + 2950 + '@protobufjs/fetch@1.1.0': 2951 + dependencies: 2952 + '@protobufjs/aspromise': 1.1.2 2953 + '@protobufjs/inquire': 1.1.0 2954 + 2955 + '@protobufjs/float@1.0.2': {} 2956 + 2957 + '@protobufjs/inquire@1.1.0': {} 2958 + 2959 + '@protobufjs/path@1.1.2': {} 2960 + 2961 + '@protobufjs/pool@1.1.0': {} 2962 + 2963 + '@protobufjs/utf8@1.1.0': {} 2964 + 2332 2965 '@rollup/plugin-commonjs@28.0.9(rollup@4.53.3)': 2333 2966 dependencies: 2334 2967 '@rollup/pluginutils': 5.3.0(rollup@4.53.3) ··· 2437 3070 dependencies: 2438 3071 acorn: 8.15.0 2439 3072 2440 - '@sveltejs/adapter-auto@7.0.0(@sveltejs/kit@2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)))(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)))': 3073 + '@sveltejs/adapter-auto@7.0.0(@sveltejs/kit@2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)))(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)))': 2441 3074 dependencies: 2442 - '@sveltejs/kit': 2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)))(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)) 3075 + '@sveltejs/kit': 2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)))(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) 2443 3076 2444 - '@sveltejs/adapter-node@5.4.0(@sveltejs/kit@2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)))(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)))': 3077 + '@sveltejs/adapter-node@5.4.0(@sveltejs/kit@2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)))(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)))': 2445 3078 dependencies: 2446 3079 '@rollup/plugin-commonjs': 28.0.9(rollup@4.53.3) 2447 3080 '@rollup/plugin-json': 6.1.0(rollup@4.53.3) 2448 3081 '@rollup/plugin-node-resolve': 16.0.3(rollup@4.53.3) 2449 - '@sveltejs/kit': 2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)))(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)) 3082 + '@sveltejs/kit': 2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)))(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) 2450 3083 rollup: 4.53.3 2451 3084 2452 - '@sveltejs/kit@2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)))(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2))': 3085 + '@sveltejs/kit@2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)))(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': 2453 3086 dependencies: 2454 3087 '@standard-schema/spec': 1.0.0 2455 3088 '@sveltejs/acorn-typescript': 1.0.8(acorn@8.15.0) 2456 - '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)) 3089 + '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) 2457 3090 '@types/cookie': 0.6.0 2458 3091 acorn: 8.15.0 2459 3092 cookie: 0.6.0 ··· 2466 3099 set-cookie-parser: 2.7.2 2467 3100 sirv: 3.0.2 2468 3101 svelte: 5.45.8 2469 - vite: 7.2.7(@types/node@24.10.2) 3102 + vite: 7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) 2470 3103 2471 - '@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)))(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2))': 3104 + '@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)))(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': 2472 3105 dependencies: 2473 - '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)) 3106 + '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) 2474 3107 debug: 4.4.3 2475 3108 svelte: 5.45.8 2476 - vite: 7.2.7(@types/node@24.10.2) 3109 + vite: 7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) 2477 3110 transitivePeerDependencies: 2478 3111 - supports-color 2479 3112 2480 - '@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2))': 3113 + '@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': 2481 3114 dependencies: 2482 - '@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)))(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)) 3115 + '@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)))(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) 2483 3116 debug: 4.4.3 2484 3117 deepmerge: 4.3.1 2485 3118 magic-string: 0.30.21 2486 3119 svelte: 5.45.8 2487 - vite: 7.2.7(@types/node@24.10.2) 2488 - vitefu: 1.1.1(vite@7.2.7(@types/node@24.10.2)) 3120 + vite: 7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) 3121 + vitefu: 1.1.1(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) 2489 3122 transitivePeerDependencies: 2490 3123 - supports-color 2491 3124 3125 + '@tailwindcss/node@4.1.18': 3126 + dependencies: 3127 + '@jridgewell/remapping': 2.3.5 3128 + enhanced-resolve: 5.18.4 3129 + jiti: 2.6.1 3130 + lightningcss: 1.30.2 3131 + magic-string: 0.30.21 3132 + source-map-js: 1.2.1 3133 + tailwindcss: 4.1.18 3134 + 3135 + '@tailwindcss/oxide-android-arm64@4.1.18': 3136 + optional: true 3137 + 3138 + '@tailwindcss/oxide-darwin-arm64@4.1.18': 3139 + optional: true 3140 + 3141 + '@tailwindcss/oxide-darwin-x64@4.1.18': 3142 + optional: true 3143 + 3144 + '@tailwindcss/oxide-freebsd-x64@4.1.18': 3145 + optional: true 3146 + 3147 + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': 3148 + optional: true 3149 + 3150 + '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': 3151 + optional: true 3152 + 3153 + '@tailwindcss/oxide-linux-arm64-musl@4.1.18': 3154 + optional: true 3155 + 3156 + '@tailwindcss/oxide-linux-x64-gnu@4.1.18': 3157 + optional: true 3158 + 3159 + '@tailwindcss/oxide-linux-x64-musl@4.1.18': 3160 + optional: true 3161 + 3162 + '@tailwindcss/oxide-wasm32-wasi@4.1.18': 3163 + optional: true 3164 + 3165 + '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': 3166 + optional: true 3167 + 3168 + '@tailwindcss/oxide-win32-x64-msvc@4.1.18': 3169 + optional: true 3170 + 3171 + '@tailwindcss/oxide@4.1.18': 3172 + optionalDependencies: 3173 + '@tailwindcss/oxide-android-arm64': 4.1.18 3174 + '@tailwindcss/oxide-darwin-arm64': 4.1.18 3175 + '@tailwindcss/oxide-darwin-x64': 4.1.18 3176 + '@tailwindcss/oxide-freebsd-x64': 4.1.18 3177 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.18 3178 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.18 3179 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.18 3180 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.18 3181 + '@tailwindcss/oxide-linux-x64-musl': 4.1.18 3182 + '@tailwindcss/oxide-wasm32-wasi': 4.1.18 3183 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.18 3184 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.18 3185 + 3186 + '@tailwindcss/vite@4.1.18(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': 3187 + dependencies: 3188 + '@tailwindcss/node': 4.1.18 3189 + '@tailwindcss/oxide': 4.1.18 3190 + tailwindcss: 4.1.18 3191 + vite: 7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) 3192 + 2492 3193 '@types/better-sqlite3@7.6.13': 2493 3194 dependencies: 2494 3195 '@types/node': 24.10.2 3196 + optional: true 2495 3197 2496 3198 '@types/cookie@0.6.0': {} 2497 3199 ··· 2503 3205 dependencies: 2504 3206 undici-types: 7.16.0 2505 3207 3208 + '@types/pg@8.16.0': 3209 + dependencies: 3210 + '@types/node': 24.10.2 3211 + pg-protocol: 1.10.3 3212 + pg-types: 2.2.0 3213 + 2506 3214 '@types/resolve@1.20.2': {} 2507 3215 2508 - '@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)': 3216 + '@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': 2509 3217 dependencies: 2510 3218 '@eslint-community/regexpp': 4.12.2 2511 - '@typescript-eslint/parser': 8.49.0(eslint@9.39.1)(typescript@5.9.3) 3219 + '@typescript-eslint/parser': 8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) 2512 3220 '@typescript-eslint/scope-manager': 8.49.0 2513 - '@typescript-eslint/type-utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3) 2514 - '@typescript-eslint/utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3) 3221 + '@typescript-eslint/type-utils': 8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) 3222 + '@typescript-eslint/utils': 8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) 2515 3223 '@typescript-eslint/visitor-keys': 8.49.0 2516 - eslint: 9.39.1 3224 + eslint: 9.39.1(jiti@2.6.1) 2517 3225 ignore: 7.0.5 2518 3226 natural-compare: 1.4.0 2519 3227 ts-api-utils: 2.1.0(typescript@5.9.3) ··· 2521 3229 transitivePeerDependencies: 2522 3230 - supports-color 2523 3231 2524 - '@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3)': 3232 + '@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': 2525 3233 dependencies: 2526 3234 '@typescript-eslint/scope-manager': 8.49.0 2527 3235 '@typescript-eslint/types': 8.49.0 2528 3236 '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) 2529 3237 '@typescript-eslint/visitor-keys': 8.49.0 2530 3238 debug: 4.4.3 2531 - eslint: 9.39.1 3239 + eslint: 9.39.1(jiti@2.6.1) 2532 3240 typescript: 5.9.3 2533 3241 transitivePeerDependencies: 2534 3242 - supports-color ··· 2551 3259 dependencies: 2552 3260 typescript: 5.9.3 2553 3261 2554 - '@typescript-eslint/type-utils@8.49.0(eslint@9.39.1)(typescript@5.9.3)': 3262 + '@typescript-eslint/type-utils@8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': 2555 3263 dependencies: 2556 3264 '@typescript-eslint/types': 8.49.0 2557 3265 '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) 2558 - '@typescript-eslint/utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3) 3266 + '@typescript-eslint/utils': 8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) 2559 3267 debug: 4.4.3 2560 - eslint: 9.39.1 3268 + eslint: 9.39.1(jiti@2.6.1) 2561 3269 ts-api-utils: 2.1.0(typescript@5.9.3) 2562 3270 typescript: 5.9.3 2563 3271 transitivePeerDependencies: ··· 2580 3288 transitivePeerDependencies: 2581 3289 - supports-color 2582 3290 2583 - '@typescript-eslint/utils@8.49.0(eslint@9.39.1)(typescript@5.9.3)': 3291 + '@typescript-eslint/utils@8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': 2584 3292 dependencies: 2585 - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) 3293 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) 2586 3294 '@typescript-eslint/scope-manager': 8.49.0 2587 3295 '@typescript-eslint/types': 8.49.0 2588 3296 '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) 2589 - eslint: 9.39.1 3297 + eslint: 9.39.1(jiti@2.6.1) 2590 3298 typescript: 5.9.3 2591 3299 transitivePeerDependencies: 2592 3300 - supports-color ··· 2596 3304 '@typescript-eslint/types': 8.49.0 2597 3305 eslint-visitor-keys: 4.2.1 2598 3306 3307 + '@valkey/valkey-glide-darwin-arm64@2.2.1': 3308 + optional: true 3309 + 3310 + '@valkey/valkey-glide-darwin-x64@2.2.1': 3311 + optional: true 3312 + 3313 + '@valkey/valkey-glide-linux-arm64-gnu@2.2.1': 3314 + optional: true 3315 + 3316 + '@valkey/valkey-glide-linux-arm64-musl@2.2.1': 3317 + optional: true 3318 + 3319 + '@valkey/valkey-glide-linux-x64-gnu@2.2.1': 3320 + optional: true 3321 + 3322 + '@valkey/valkey-glide-linux-x64-musl@2.2.1': 3323 + optional: true 3324 + 3325 + '@valkey/valkey-glide@2.2.1': 3326 + dependencies: 3327 + long: 5.3.2 3328 + protobufjs: 7.5.4 3329 + optionalDependencies: 3330 + '@valkey/valkey-glide-darwin-arm64': 2.2.1 3331 + '@valkey/valkey-glide-darwin-x64': 2.2.1 3332 + '@valkey/valkey-glide-linux-arm64-gnu': 2.2.1 3333 + '@valkey/valkey-glide-linux-arm64-musl': 2.2.1 3334 + '@valkey/valkey-glide-linux-x64-gnu': 2.2.1 3335 + '@valkey/valkey-glide-linux-x64-musl': 2.2.1 3336 + 2599 3337 abort-controller@3.0.0: 2600 3338 dependencies: 2601 3339 event-target-shim: 5.0.1 ··· 2635 3373 dependencies: 2636 3374 bindings: 1.5.0 2637 3375 prebuild-install: 7.1.3 3376 + optional: true 2638 3377 2639 3378 bindings@1.5.0: 2640 3379 dependencies: 2641 3380 file-uri-to-path: 1.0.0 3381 + optional: true 2642 3382 2643 3383 bl@4.1.0: 2644 3384 dependencies: 2645 3385 buffer: 5.7.1 2646 3386 inherits: 2.0.4 2647 3387 readable-stream: 3.6.2 3388 + optional: true 2648 3389 2649 3390 brace-expansion@1.1.12: 2650 3391 dependencies: ··· 2661 3402 dependencies: 2662 3403 base64-js: 1.5.1 2663 3404 ieee754: 1.2.1 3405 + optional: true 2664 3406 2665 3407 buffer@6.0.3: 2666 3408 dependencies: ··· 2678 3420 dependencies: 2679 3421 readdirp: 4.1.2 2680 3422 2681 - chownr@1.1.4: {} 3423 + chownr@1.1.4: 3424 + optional: true 2682 3425 2683 3426 clsx@2.1.1: {} 2684 3427 ··· 2708 3451 2709 3452 cssesc@3.0.0: {} 2710 3453 3454 + daisyui@5.5.14: {} 3455 + 2711 3456 debug@4.4.3: 2712 3457 dependencies: 2713 3458 ms: 2.1.3 ··· 2715 3460 decompress-response@6.0.0: 2716 3461 dependencies: 2717 3462 mimic-response: 3.1.0 3463 + optional: true 2718 3464 2719 - deep-extend@0.6.0: {} 3465 + deep-extend@0.6.0: 3466 + optional: true 2720 3467 2721 3468 deep-is@0.1.4: {} 2722 3469 ··· 2737 3484 transitivePeerDependencies: 2738 3485 - supports-color 2739 3486 2740 - drizzle-orm@0.44.7(@types/better-sqlite3@7.6.13)(better-sqlite3@12.4.1): 3487 + drizzle-orm@0.44.7(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(better-sqlite3@12.4.1)(pg@8.16.3): 2741 3488 optionalDependencies: 2742 3489 '@types/better-sqlite3': 7.6.13 3490 + '@types/pg': 8.16.0 2743 3491 better-sqlite3: 12.4.1 3492 + pg: 8.16.3 2744 3493 2745 3494 end-of-stream@1.4.5: 2746 3495 dependencies: 2747 3496 once: 1.4.0 3497 + optional: true 3498 + 3499 + enhanced-resolve@5.18.4: 3500 + dependencies: 3501 + graceful-fs: 4.2.11 3502 + tapable: 2.3.0 2748 3503 2749 3504 esbuild-register@3.6.0(esbuild@0.25.12): 2750 3505 dependencies: ··· 2807 3562 '@esbuild/win32-ia32': 0.25.12 2808 3563 '@esbuild/win32-x64': 0.25.12 2809 3564 3565 + esbuild@0.27.1: 3566 + optionalDependencies: 3567 + '@esbuild/aix-ppc64': 0.27.1 3568 + '@esbuild/android-arm': 0.27.1 3569 + '@esbuild/android-arm64': 0.27.1 3570 + '@esbuild/android-x64': 0.27.1 3571 + '@esbuild/darwin-arm64': 0.27.1 3572 + '@esbuild/darwin-x64': 0.27.1 3573 + '@esbuild/freebsd-arm64': 0.27.1 3574 + '@esbuild/freebsd-x64': 0.27.1 3575 + '@esbuild/linux-arm': 0.27.1 3576 + '@esbuild/linux-arm64': 0.27.1 3577 + '@esbuild/linux-ia32': 0.27.1 3578 + '@esbuild/linux-loong64': 0.27.1 3579 + '@esbuild/linux-mips64el': 0.27.1 3580 + '@esbuild/linux-ppc64': 0.27.1 3581 + '@esbuild/linux-riscv64': 0.27.1 3582 + '@esbuild/linux-s390x': 0.27.1 3583 + '@esbuild/linux-x64': 0.27.1 3584 + '@esbuild/netbsd-arm64': 0.27.1 3585 + '@esbuild/netbsd-x64': 0.27.1 3586 + '@esbuild/openbsd-arm64': 0.27.1 3587 + '@esbuild/openbsd-x64': 0.27.1 3588 + '@esbuild/openharmony-arm64': 0.27.1 3589 + '@esbuild/sunos-x64': 0.27.1 3590 + '@esbuild/win32-arm64': 0.27.1 3591 + '@esbuild/win32-ia32': 0.27.1 3592 + '@esbuild/win32-x64': 0.27.1 3593 + 2810 3594 escape-string-regexp@4.0.0: {} 2811 3595 2812 - eslint-plugin-svelte@3.13.1(eslint@9.39.1)(svelte@5.45.8): 3596 + eslint-plugin-svelte@3.13.1(eslint@9.39.1(jiti@2.6.1))(svelte@5.45.8): 2813 3597 dependencies: 2814 - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) 3598 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) 2815 3599 '@jridgewell/sourcemap-codec': 1.5.5 2816 - eslint: 9.39.1 3600 + eslint: 9.39.1(jiti@2.6.1) 2817 3601 esutils: 2.0.3 2818 3602 globals: 16.5.0 2819 3603 known-css-properties: 0.37.0 ··· 2836 3620 2837 3621 eslint-visitor-keys@4.2.1: {} 2838 3622 2839 - eslint@9.39.1: 3623 + eslint@9.39.1(jiti@2.6.1): 2840 3624 dependencies: 2841 - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) 3625 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) 2842 3626 '@eslint-community/regexpp': 4.12.2 2843 3627 '@eslint/config-array': 0.21.1 2844 3628 '@eslint/config-helpers': 0.4.2 ··· 2872 3656 minimatch: 3.1.2 2873 3657 natural-compare: 1.4.0 2874 3658 optionator: 0.9.4 3659 + optionalDependencies: 3660 + jiti: 2.6.1 2875 3661 transitivePeerDependencies: 2876 3662 - supports-color 2877 3663 ··· 2905 3691 2906 3692 events@3.3.0: {} 2907 3693 2908 - expand-template@2.0.3: {} 3694 + expand-template@2.0.3: 3695 + optional: true 2909 3696 2910 3697 fast-deep-equal@3.1.3: {} 2911 3698 ··· 2923 3710 dependencies: 2924 3711 flat-cache: 4.0.1 2925 3712 2926 - file-uri-to-path@1.0.0: {} 3713 + file-uri-to-path@1.0.0: 3714 + optional: true 2927 3715 2928 3716 find-up@5.0.0: 2929 3717 dependencies: ··· 2937 3725 2938 3726 flatted@3.3.3: {} 2939 3727 2940 - fs-constants@1.0.0: {} 3728 + fs-constants@1.0.0: 3729 + optional: true 2941 3730 2942 3731 fsevents@2.3.3: 2943 3732 optional: true ··· 2948 3737 dependencies: 2949 3738 resolve-pkg-maps: 1.0.0 2950 3739 2951 - github-from-package@0.0.0: {} 3740 + github-from-package@0.0.0: 3741 + optional: true 2952 3742 2953 3743 glob-parent@6.0.2: 2954 3744 dependencies: ··· 2957 3747 globals@14.0.0: {} 2958 3748 2959 3749 globals@16.5.0: {} 3750 + 3751 + graceful-fs@4.2.11: {} 2960 3752 2961 3753 has-flag@4.0.0: {} 2962 3754 ··· 2977 3769 2978 3770 imurmurhash@0.1.4: {} 2979 3771 2980 - inherits@2.0.4: {} 3772 + inherits@2.0.4: 3773 + optional: true 2981 3774 2982 - ini@1.3.8: {} 3775 + ini@1.3.8: 3776 + optional: true 2983 3777 2984 3778 ipaddr.js@2.3.0: {} 2985 3779 ··· 3007 3801 3008 3802 iso-datestring-validator@2.2.2: {} 3009 3803 3804 + jiti@2.6.1: {} 3805 + 3010 3806 jose@5.10.0: {} 3011 3807 3012 3808 js-yaml@4.1.1: ··· 3032 3828 prelude-ls: 1.2.1 3033 3829 type-check: 0.4.0 3034 3830 3831 + lightningcss-android-arm64@1.30.2: 3832 + optional: true 3833 + 3834 + lightningcss-darwin-arm64@1.30.2: 3835 + optional: true 3836 + 3837 + lightningcss-darwin-x64@1.30.2: 3838 + optional: true 3839 + 3840 + lightningcss-freebsd-x64@1.30.2: 3841 + optional: true 3842 + 3843 + lightningcss-linux-arm-gnueabihf@1.30.2: 3844 + optional: true 3845 + 3846 + lightningcss-linux-arm64-gnu@1.30.2: 3847 + optional: true 3848 + 3849 + lightningcss-linux-arm64-musl@1.30.2: 3850 + optional: true 3851 + 3852 + lightningcss-linux-x64-gnu@1.30.2: 3853 + optional: true 3854 + 3855 + lightningcss-linux-x64-musl@1.30.2: 3856 + optional: true 3857 + 3858 + lightningcss-win32-arm64-msvc@1.30.2: 3859 + optional: true 3860 + 3861 + lightningcss-win32-x64-msvc@1.30.2: 3862 + optional: true 3863 + 3864 + lightningcss@1.30.2: 3865 + dependencies: 3866 + detect-libc: 2.1.2 3867 + optionalDependencies: 3868 + lightningcss-android-arm64: 1.30.2 3869 + lightningcss-darwin-arm64: 1.30.2 3870 + lightningcss-darwin-x64: 1.30.2 3871 + lightningcss-freebsd-x64: 1.30.2 3872 + lightningcss-linux-arm-gnueabihf: 1.30.2 3873 + lightningcss-linux-arm64-gnu: 1.30.2 3874 + lightningcss-linux-arm64-musl: 1.30.2 3875 + lightningcss-linux-x64-gnu: 1.30.2 3876 + lightningcss-linux-x64-musl: 1.30.2 3877 + lightningcss-win32-arm64-msvc: 1.30.2 3878 + lightningcss-win32-x64-msvc: 1.30.2 3879 + 3035 3880 lilconfig@2.1.0: {} 3036 3881 3037 3882 locate-character@3.0.0: {} ··· 3044 3889 3045 3890 long-timeout@0.1.1: {} 3046 3891 3892 + long@5.3.2: {} 3893 + 3047 3894 lru-cache@10.4.3: {} 3048 3895 3049 3896 luxon@3.7.2: {} ··· 3052 3899 dependencies: 3053 3900 '@jridgewell/sourcemap-codec': 1.5.5 3054 3901 3055 - mimic-response@3.1.0: {} 3902 + mimic-response@3.1.0: 3903 + optional: true 3056 3904 3057 3905 minimatch@3.1.2: 3058 3906 dependencies: ··· 3062 3910 dependencies: 3063 3911 brace-expansion: 2.0.2 3064 3912 3065 - minimist@1.2.8: {} 3913 + minimist@1.2.8: 3914 + optional: true 3066 3915 3067 - mkdirp-classic@0.5.3: {} 3916 + mkdirp-classic@0.5.3: 3917 + optional: true 3068 3918 3069 3919 mri@1.2.0: {} 3070 3920 ··· 3076 3926 3077 3927 nanoid@3.3.11: {} 3078 3928 3079 - napi-build-utils@2.0.0: {} 3929 + napi-build-utils@2.0.0: 3930 + optional: true 3080 3931 3081 3932 natural-compare@1.4.0: {} 3082 3933 3083 3934 node-abi@3.85.0: 3084 3935 dependencies: 3085 3936 semver: 7.7.3 3937 + optional: true 3086 3938 3087 3939 node-schedule@2.1.1: 3088 3940 dependencies: ··· 3095 3947 once@1.4.0: 3096 3948 dependencies: 3097 3949 wrappy: 1.0.2 3950 + optional: true 3098 3951 3099 3952 optionator@0.9.4: 3100 3953 dependencies: ··· 3123 3976 3124 3977 path-parse@1.0.7: {} 3125 3978 3979 + pg-cloudflare@1.2.7: 3980 + optional: true 3981 + 3982 + pg-connection-string@2.9.1: {} 3983 + 3984 + pg-int8@1.0.1: {} 3985 + 3986 + pg-pool@3.10.1(pg@8.16.3): 3987 + dependencies: 3988 + pg: 8.16.3 3989 + 3990 + pg-protocol@1.10.3: {} 3991 + 3992 + pg-types@2.2.0: 3993 + dependencies: 3994 + pg-int8: 1.0.1 3995 + postgres-array: 2.0.0 3996 + postgres-bytea: 1.0.0 3997 + postgres-date: 1.0.7 3998 + postgres-interval: 1.2.0 3999 + 4000 + pg@8.16.3: 4001 + dependencies: 4002 + pg-connection-string: 2.9.1 4003 + pg-pool: 3.10.1(pg@8.16.3) 4004 + pg-protocol: 1.10.3 4005 + pg-types: 2.2.0 4006 + pgpass: 1.0.5 4007 + optionalDependencies: 4008 + pg-cloudflare: 1.2.7 4009 + 4010 + pgpass@1.0.5: 4011 + dependencies: 4012 + split2: 4.2.0 4013 + 3126 4014 picocolors@1.1.1: {} 3127 4015 3128 4016 picomatch@4.0.3: {} ··· 3194 4082 picocolors: 1.1.1 3195 4083 source-map-js: 1.2.1 3196 4084 4085 + postgres-array@2.0.0: {} 4086 + 4087 + postgres-bytea@1.0.0: {} 4088 + 4089 + postgres-date@1.0.7: {} 4090 + 4091 + postgres-interval@1.2.0: 4092 + dependencies: 4093 + xtend: 4.0.2 4094 + 3197 4095 prebuild-install@7.1.3: 3198 4096 dependencies: 3199 4097 detect-libc: 2.1.2 ··· 3208 4106 simple-get: 4.0.1 3209 4107 tar-fs: 2.1.4 3210 4108 tunnel-agent: 0.6.0 4109 + optional: true 3211 4110 3212 4111 prelude-ls@1.2.1: {} 3213 4112 ··· 3217 4116 3218 4117 process@0.11.10: {} 3219 4118 4119 + protobufjs@7.5.4: 4120 + dependencies: 4121 + '@protobufjs/aspromise': 1.1.2 4122 + '@protobufjs/base64': 1.1.2 4123 + '@protobufjs/codegen': 2.0.4 4124 + '@protobufjs/eventemitter': 1.1.0 4125 + '@protobufjs/fetch': 1.1.0 4126 + '@protobufjs/float': 1.0.2 4127 + '@protobufjs/inquire': 1.1.0 4128 + '@protobufjs/path': 1.1.2 4129 + '@protobufjs/pool': 1.1.0 4130 + '@protobufjs/utf8': 1.1.0 4131 + '@types/node': 24.10.2 4132 + long: 5.3.2 4133 + 3220 4134 pump@3.0.3: 3221 4135 dependencies: 3222 4136 end-of-stream: 1.4.5 3223 4137 once: 1.4.0 4138 + optional: true 3224 4139 3225 4140 punycode@2.3.1: {} 3226 4141 ··· 3232 4147 ini: 1.3.8 3233 4148 minimist: 1.2.8 3234 4149 strip-json-comments: 2.0.1 4150 + optional: true 3235 4151 3236 4152 readable-stream@3.6.2: 3237 4153 dependencies: 3238 4154 inherits: 2.0.4 3239 4155 string_decoder: 1.3.0 3240 4156 util-deprecate: 1.0.2 4157 + optional: true 3241 4158 3242 4159 readable-stream@4.7.0: 3243 4160 dependencies: ··· 3307 4224 3308 4225 shebang-regex@3.0.0: {} 3309 4226 3310 - simple-concat@1.0.1: {} 4227 + simple-concat@1.0.1: 4228 + optional: true 3311 4229 3312 4230 simple-get@4.0.1: 3313 4231 dependencies: 3314 4232 decompress-response: 6.0.0 3315 4233 once: 1.4.0 3316 4234 simple-concat: 1.0.1 4235 + optional: true 3317 4236 3318 4237 sirv@3.0.2: 3319 4238 dependencies: ··· 3346 4265 dependencies: 3347 4266 safe-buffer: 5.2.1 3348 4267 3349 - strip-json-comments@2.0.1: {} 4268 + strip-json-comments@2.0.1: 4269 + optional: true 3350 4270 3351 4271 strip-json-comments@3.1.1: {} 3352 4272 ··· 3397 4317 magic-string: 0.30.21 3398 4318 zimmerframe: 1.1.4 3399 4319 4320 + tailwindcss@4.1.18: {} 4321 + 4322 + tapable@2.3.0: {} 4323 + 3400 4324 tar-fs@2.1.4: 3401 4325 dependencies: 3402 4326 chownr: 1.1.4 3403 4327 mkdirp-classic: 0.5.3 3404 4328 pump: 3.0.3 3405 4329 tar-stream: 2.2.0 4330 + optional: true 3406 4331 3407 4332 tar-stream@2.2.0: 3408 4333 dependencies: ··· 3411 4336 fs-constants: 1.0.0 3412 4337 inherits: 2.0.4 3413 4338 readable-stream: 3.6.2 4339 + optional: true 3414 4340 3415 4341 thread-stream@2.7.0: 3416 4342 dependencies: ··· 3435 4361 3436 4362 tslib@2.8.1: {} 3437 4363 4364 + tsx@4.21.0: 4365 + dependencies: 4366 + esbuild: 0.27.1 4367 + get-tsconfig: 4.13.0 4368 + optionalDependencies: 4369 + fsevents: 2.3.3 4370 + 3438 4371 tunnel-agent@0.6.0: 3439 4372 dependencies: 3440 4373 safe-buffer: 5.2.1 4374 + optional: true 3441 4375 3442 4376 type-check@0.4.0: 3443 4377 dependencies: 3444 4378 prelude-ls: 1.2.1 3445 4379 3446 - typescript-eslint@8.49.0(eslint@9.39.1)(typescript@5.9.3): 4380 + typescript-eslint@8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3): 3447 4381 dependencies: 3448 - '@typescript-eslint/eslint-plugin': 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3) 3449 - '@typescript-eslint/parser': 8.49.0(eslint@9.39.1)(typescript@5.9.3) 4382 + '@typescript-eslint/eslint-plugin': 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) 4383 + '@typescript-eslint/parser': 8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) 3450 4384 '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) 3451 - '@typescript-eslint/utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3) 3452 - eslint: 9.39.1 4385 + '@typescript-eslint/utils': 8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) 4386 + eslint: 9.39.1(jiti@2.6.1) 3453 4387 typescript: 5.9.3 3454 4388 transitivePeerDependencies: 3455 4389 - supports-color ··· 3472 4406 3473 4407 util-deprecate@1.0.2: {} 3474 4408 3475 - vite@7.2.7(@types/node@24.10.2): 4409 + vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0): 3476 4410 dependencies: 3477 4411 esbuild: 0.25.12 3478 4412 fdir: 6.5.0(picomatch@4.0.3) ··· 3483 4417 optionalDependencies: 3484 4418 '@types/node': 24.10.2 3485 4419 fsevents: 2.3.3 4420 + jiti: 2.6.1 4421 + lightningcss: 1.30.2 4422 + tsx: 4.21.0 3486 4423 3487 - vitefu@1.1.1(vite@7.2.7(@types/node@24.10.2)): 4424 + vitefu@1.1.1(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)): 3488 4425 optionalDependencies: 3489 - vite: 7.2.7(@types/node@24.10.2) 4426 + vite: 7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) 3490 4427 3491 4428 which@2.0.2: 3492 4429 dependencies: ··· 3494 4431 3495 4432 word-wrap@1.2.5: {} 3496 4433 3497 - wrappy@1.0.2: {} 4434 + wrappy@1.0.2: 4435 + optional: true 4436 + 4437 + ws@8.18.3: {} 4438 + 4439 + xtend@4.0.2: {} 3498 4440 3499 4441 yaml@1.10.2: {} 3500 4442
+1
pnpm-workspace.yaml
··· 2 2 - better-sqlite3 3 3 - core-js 4 4 - esbuild 5 + - protobufjs
+2
src/app.css
··· 1 + @import "tailwindcss"; 2 + @plugin "daisyui";
+3 -27
src/hooks.server.ts
··· 1 1 import { db } from '$lib/server/db'; 2 2 import type { Handle, ServerInit } from '@sveltejs/kit'; 3 - import { migrate } from 'drizzle-orm/better-sqlite3/migrator'; 3 + import { migrate } from 'drizzle-orm/node-postgres/migrator'; 4 4 import { env } from '$env/dynamic/private'; 5 - import { keyValueStore } from '$lib/server/db/schema'; 6 - import { and, eq, lt } from 'drizzle-orm'; 7 - import { STATE_STORE } from '$lib/server/cache'; 8 - import { logger } from '$lib/server/logger'; 5 + 9 6 import { HOUR } from '@atproto/common'; 10 7 import { getSessionManager, SessionRestorationError } from '$lib/server/session'; 11 8 12 - const clearExpiredStates = async () => { 13 - try { 14 - logger.info('Running cleanup of the state store'); 15 - const oneHourAgo = new Date(Date.now() - HOUR); 16 - const result = await db 17 - .delete(keyValueStore) 18 - .where( 19 - and( 20 - eq(keyValueStore.storeName, STATE_STORE), 21 - lt(keyValueStore.createdAt, oneHourAgo)) 22 - ); 23 - 24 - if (result.changes > 0) { 25 - logger.info(`Cleaned up ${result.changes} expired key(s) from keyValueStore`); 26 - } 27 - } catch (err) { 28 - logger.error(`${(err as Error).message}`); 29 - } 30 - }; 31 9 32 10 33 11 export const init: ServerInit = async () => { 34 12 // Run Drizzle migrations on server startup 35 - migrate(db, { migrationsFolder: env.MIGRATIONS_FOLDER ?? 'drizzle' }); 13 + await migrate(db, { migrationsFolder: env.MIGRATIONS_FOLDER ?? 'drizzle' }); 36 14 37 - await clearExpiredStates(); 38 15 39 16 // Start a background job to clean up state every hour, which is recommended in the oauth docs 40 17 setInterval(async () => { 41 - await clearExpiredStates(); 42 18 //TODO prob should do one for the session store as well for expired sessions 43 19 }, HOUR); // Run every hour 44 20 };
+91
src/lib/components/InteractionBar.svelte
··· 1 + <script lang="ts"> 2 + let { 3 + likeCount = 0, 4 + repostCount = 0, 5 + commentCount = 0, 6 + onlike, 7 + onrepost, 8 + oncomment 9 + }: { 10 + likeCount?: number; 11 + repostCount?: number; 12 + commentCount?: number; 13 + onlike?: () => void; 14 + onrepost?: () => void; 15 + oncomment?: () => void; 16 + } = $props(); 17 + </script> 18 + 19 + <div class="flex gap-4 mt-4"> 20 + <button 21 + class="btn btn-ghost btn-sm gap-2" 22 + onclick={onlike} 23 + aria-label="Like" 24 + > 25 + <svg 26 + xmlns="http://www.w3.org/2000/svg" 27 + class="h-5 w-5" 28 + fill="none" 29 + viewBox="0 0 24 24" 30 + stroke="currentColor" 31 + > 32 + <path 33 + stroke-linecap="round" 34 + stroke-linejoin="round" 35 + stroke-width="2" 36 + d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" 37 + /> 38 + </svg> 39 + {#if likeCount > 0} 40 + <span class="text-sm">{likeCount}</span> 41 + {/if} 42 + </button> 43 + 44 + <button 45 + class="btn btn-ghost btn-sm gap-2" 46 + onclick={onrepost} 47 + aria-label="Repost" 48 + > 49 + <svg 50 + xmlns="http://www.w3.org/2000/svg" 51 + class="h-5 w-5" 52 + fill="none" 53 + viewBox="0 0 24 24" 54 + stroke="currentColor" 55 + > 56 + <path 57 + stroke-linecap="round" 58 + stroke-linejoin="round" 59 + stroke-width="2" 60 + d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" 61 + /> 62 + </svg> 63 + {#if repostCount > 0} 64 + <span class="text-sm">{repostCount}</span> 65 + {/if} 66 + </button> 67 + 68 + <button 69 + class="btn btn-ghost btn-sm gap-2" 70 + onclick={oncomment} 71 + aria-label="Comment" 72 + > 73 + <svg 74 + xmlns="http://www.w3.org/2000/svg" 75 + class="h-5 w-5" 76 + fill="none" 77 + viewBox="0 0 24 24" 78 + stroke="currentColor" 79 + > 80 + <path 81 + stroke-linecap="round" 82 + stroke-linejoin="round" 83 + stroke-width="2" 84 + d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" 85 + /> 86 + </svg> 87 + {#if commentCount > 0} 88 + <span class="text-sm">{commentCount}</span> 89 + {/if} 90 + </button> 91 + </div>
+118
src/lib/components/LeafletDocumentCard.svelte
··· 1 + <script lang="ts"> 2 + import InteractionBar from './InteractionBar.svelte'; 3 + 4 + let { 5 + record, 6 + profile 7 + }: { 8 + record: any; 9 + profile?: { handle: string; avatar?: string; displayName?: string }; 10 + } = $props(); 11 + 12 + const data = $derived(record.data as { 13 + title?: string; 14 + description?: string; 15 + tags?: string[]; 16 + author?: string; 17 + publishedAt?: string; 18 + pages?: Array<{ 19 + blocks: Array<{ 20 + block: { 21 + $type: string; 22 + plaintext?: string; 23 + }; 24 + }>; 25 + }>; 26 + }); 27 + 28 + function formatDate(dateString: string): string { 29 + const date = new Date(dateString); 30 + return new Intl.DateTimeFormat('en-US', { 31 + month: 'short', 32 + day: 'numeric', 33 + year: 'numeric' 34 + }).format(date); 35 + } 36 + 37 + function getPreviewText(): string { 38 + if (!data.pages || data.pages.length === 0) return ''; 39 + 40 + for (const page of data.pages) { 41 + for (const blockItem of page.blocks) { 42 + if ( 43 + blockItem.block.$type === 'pub.leaflet.blocks.text' && 44 + blockItem.block.plaintext && 45 + blockItem.block.plaintext.trim().length > 0 46 + ) { 47 + const text = blockItem.block.plaintext; 48 + return text.length > 200 ? text.slice(0, 200) + '...' : text; 49 + } 50 + } 51 + } 52 + return ''; 53 + } 54 + 55 + const previewText = getPreviewText(); 56 + </script> 57 + 58 + <div class="card bg-base-100 border-b border-base-300"> 59 + <div class="card-body"> 60 + <div class="flex items-start gap-4"> 61 + <div class="flex-shrink-0"> 62 + <div class="avatar"> 63 + <div class="w-12 h-12 rounded-full"> 64 + {#if profile?.avatar} 65 + <img src={profile.avatar} alt={profile.handle} /> 66 + {:else} 67 + <div class="bg-accent text-accent-content rounded-full w-12 h-12 flex items-center justify-center"> 68 + <span class="text-xl">{profile?.handle?.[0]?.toUpperCase() || '?'}</span> 69 + </div> 70 + {/if} 71 + </div> 72 + </div> 73 + </div> 74 + 75 + <div class="flex-grow"> 76 + {#if profile} 77 + <div class="text-sm text-base-content/60 mb-2"> 78 + <a href="https://bsky.app/profile/{profile.handle}" target="_blank" rel="noopener noreferrer" class="link link-hover"> 79 + @{profile.handle} 80 + </a> 81 + {#if profile.displayName} 82 + <span class="ml-1">ยท {profile.displayName}</span> 83 + {/if} 84 + </div> 85 + {/if} 86 + <h3 class="card-title text-lg mb-2">{data.title || 'Untitled Document'}</h3> 87 + 88 + {#if data.description} 89 + <p class="text-base-content/80 mb-2">{data.description}</p> 90 + {/if} 91 + 92 + {#if previewText} 93 + <p class="text-sm text-base-content/70 mb-3 line-clamp-3">{previewText}</p> 94 + {/if} 95 + 96 + {#if data.tags && data.tags.length > 0} 97 + <div class="flex flex-wrap gap-2 mb-3"> 98 + {#each data.tags as tag} 99 + <div class="badge badge-primary badge-sm">#{tag}</div> 100 + {/each} 101 + </div> 102 + {/if} 103 + 104 + {#if data.publishedAt} 105 + <div class="flex items-center gap-2 text-sm text-base-content/60"> 106 + <span>Published {formatDate(data.publishedAt)}</span> 107 + </div> 108 + {/if} 109 + 110 + <InteractionBar 111 + onlike={() => console.log('Liked document')} 112 + onrepost={() => console.log('Reposted document')} 113 + oncomment={() => console.log('Commented on document')} 114 + /> 115 + </div> 116 + </div> 117 + </div> 118 + </div>
+110
src/lib/components/MusicPlayCard.svelte
··· 1 + <script lang="ts"> 2 + import InteractionBar from './InteractionBar.svelte'; 3 + 4 + let { 5 + record, 6 + profile 7 + }: { 8 + record: any; 9 + profile?: { handle: string; avatar?: string; displayName?: string }; 10 + } = $props(); 11 + 12 + const data = $derived(record.data as { 13 + trackName?: string; 14 + artists?: Array<{ artistName: string; artistMbId: string }>; 15 + releaseName?: string; 16 + playedTime?: string; 17 + duration?: number; 18 + originUrl?: string; 19 + musicServiceBaseDomain?: string; 20 + }); 21 + 22 + function formatDuration(seconds: number): string { 23 + const mins = Math.floor(seconds / 60); 24 + const secs = seconds % 60; 25 + return `${mins}:${secs.toString().padStart(2, '0')}`; 26 + } 27 + 28 + function formatDate(dateString: string): string { 29 + const date = new Date(dateString); 30 + return new Intl.DateTimeFormat('en-US', { 31 + month: 'short', 32 + day: 'numeric', 33 + hour: 'numeric', 34 + minute: '2-digit' 35 + }).format(date); 36 + } 37 + </script> 38 + 39 + <div class="card bg-base-100 border-b border-base-300"> 40 + <div class="card-body"> 41 + <div class="flex items-start gap-4"> 42 + <div class="flex-shrink-0"> 43 + <div class="avatar"> 44 + <div class="w-12 h-12 rounded-full"> 45 + {#if profile?.avatar} 46 + <img src={profile.avatar} alt={profile.handle} /> 47 + {:else} 48 + <div class="bg-primary text-primary-content rounded-full w-12 h-12 flex items-center justify-center"> 49 + <span class="text-xl">{profile?.handle?.[0]?.toUpperCase() || '?'}</span> 50 + </div> 51 + {/if} 52 + </div> 53 + </div> 54 + </div> 55 + 56 + <div class="flex-grow"> 57 + {#if profile} 58 + <div class="text-sm text-base-content/60 mb-2"> 59 + <a href="https://bsky.app/profile/{profile.handle}" target="_blank" rel="noopener noreferrer" class="link link-hover"> 60 + @{profile.handle} 61 + </a> 62 + {#if profile.displayName} 63 + <span class="ml-1">ยท {profile.displayName}</span> 64 + {/if} 65 + </div> 66 + {/if} 67 + <h3 class="card-title text-lg">{data.trackName || 'Unknown Track'}</h3> 68 + {#if data.artists && data.artists.length > 0} 69 + <p class="text-base-content/70"> 70 + {data.artists.map((a) => a.artistName).join(', ')} 71 + </p> 72 + {/if} 73 + {#if data.releaseName} 74 + <p class="text-sm text-base-content/60 mt-1">{data.releaseName}</p> 75 + {/if} 76 + 77 + <div class="flex items-center gap-4 mt-2 text-sm text-base-content/60"> 78 + {#if data.duration} 79 + <span>{formatDuration(data.duration)}</span> 80 + <span>โ€ข</span> 81 + {/if} 82 + {#if data.playedTime} 83 + <span>{formatDate(data.playedTime)}</span> 84 + {/if} 85 + {#if data.musicServiceBaseDomain} 86 + <span>โ€ข</span> 87 + <span class="capitalize">{data.musicServiceBaseDomain.split('.')[0]}</span> 88 + {/if} 89 + </div> 90 + 91 + {#if data.originUrl} 92 + <a 93 + href={data.originUrl} 94 + target="_blank" 95 + rel="noopener noreferrer" 96 + class="link link-primary text-sm mt-2 inline-block" 97 + > 98 + Listen on {data.musicServiceBaseDomain?.split('.')[0] || 'platform'} 99 + </a> 100 + {/if} 101 + 102 + <InteractionBar 103 + onlike={() => console.log('Liked music play')} 104 + onrepost={() => console.log('Reposted music play')} 105 + oncomment={() => console.log('Commented on music play')} 106 + /> 107 + </div> 108 + </div> 109 + </div> 110 + </div>
+110
src/lib/components/TangledRepoCard.svelte
··· 1 + <script lang="ts"> 2 + import InteractionBar from './InteractionBar.svelte'; 3 + 4 + let { 5 + record, 6 + profile 7 + }: { 8 + record: any; 9 + profile?: { handle: string; avatar?: string; displayName?: string }; 10 + } = $props(); 11 + 12 + const data = $derived(record.data as { 13 + name?: string; 14 + description?: string; 15 + knot?: string; 16 + labels?: string[]; 17 + source?: string; 18 + createdAt?: string; 19 + }); 20 + 21 + function formatDate(dateString: string): string { 22 + const date = new Date(dateString); 23 + return new Intl.DateTimeFormat('en-US', { 24 + month: 'short', 25 + day: 'numeric', 26 + year: 'numeric' 27 + }).format(date); 28 + } 29 + 30 + function extractLabelName(atUri: string): string { 31 + const parts = atUri.split('/'); 32 + return parts[parts.length - 1] || atUri; 33 + } 34 + </script> 35 + 36 + <div class="card bg-base-100 border-b border-base-300"> 37 + <div class="card-body"> 38 + <div class="flex items-start gap-4"> 39 + <div class="flex-shrink-0"> 40 + <div class="avatar"> 41 + <div class="w-12 h-12 rounded-full"> 42 + {#if profile?.avatar} 43 + <img src={profile.avatar} alt={profile.handle} /> 44 + {:else} 45 + <div class="bg-secondary text-secondary-content rounded-full w-12 h-12 flex items-center justify-center"> 46 + <span class="text-xl">{profile?.handle?.[0]?.toUpperCase() || '?'}</span> 47 + </div> 48 + {/if} 49 + </div> 50 + </div> 51 + </div> 52 + 53 + <div class="flex-grow"> 54 + {#if profile} 55 + <div class="text-sm text-base-content/60 mb-2"> 56 + <a href="https://bsky.app/profile/{profile.handle}" target="_blank" rel="noopener noreferrer" class="link link-hover"> 57 + @{profile.handle} 58 + </a> 59 + {#if profile.displayName} 60 + <span class="ml-1">ยท {profile.displayName}</span> 61 + {/if} 62 + </div> 63 + {/if} 64 + <div class="flex items-center gap-2"> 65 + <h3 class="card-title text-lg">{data.name || 'Unknown Repository'}</h3> 66 + {#if data.knot} 67 + <div class="badge badge-outline badge-sm">{data.knot}</div> 68 + {/if} 69 + </div> 70 + 71 + {#if data.description} 72 + <p class="text-base-content/80 mt-2">{data.description}</p> 73 + {/if} 74 + 75 + {#if data.labels && data.labels.length > 0} 76 + <div class="flex flex-wrap gap-2 mt-3"> 77 + {#each data.labels as label} 78 + <div class="badge badge-ghost badge-sm"> 79 + {extractLabelName(label)} 80 + </div> 81 + {/each} 82 + </div> 83 + {/if} 84 + 85 + {#if data.createdAt} 86 + <div class="flex items-center gap-2 mt-3 text-sm text-base-content/60"> 87 + <span>Created {formatDate(data.createdAt)}</span> 88 + </div> 89 + {/if} 90 + 91 + {#if data.source} 92 + <a 93 + href={data.source} 94 + target="_blank" 95 + rel="noopener noreferrer" 96 + class="link link-primary text-sm mt-2 inline-block" 97 + > 98 + View source 99 + </a> 100 + {/if} 101 + 102 + <InteractionBar 103 + onlike={() => console.log('Liked repo')} 104 + onrepost={() => console.log('Reposted repo')} 105 + oncomment={() => console.log('Commented on repo')} 106 + /> 107 + </div> 108 + </div> 109 + </div> 110 + </div>
+7 -3
src/lib/server/atproto/client.ts
··· 2 2 3 3 import { atprotoLoopbackClientMetadata, Keyset, NodeOAuthClient } from '@atproto/oauth-client-node'; 4 4 import { JoseKey } from '@atproto/jwk-jose'; 5 - import { db } from '$lib/server/db'; 6 5 import { SessionStore, StateStore } from '$lib/server/atproto/storage'; 7 6 import { env } from '$env/dynamic/private'; 8 7 import type { OAuthClientMetadataInput } from '@atproto/oauth-types'; 8 + import { getAValKeyClient } from '$lib/server/cache'; 9 + 9 10 10 11 //You will need to change these if you are using another collection, can also change by setting the env OAUTH_SCOPES 11 12 //For permission to all you can uncomment below ··· 28 29 export const atpOAuthClient = async () => { 29 30 if (!client) { 30 31 client = (async () => { 32 + 33 + const valKeyClient = await getAValKeyClient(); 34 + 31 35 const rootDomain = env.OAUTH_DOMAIN ?? '127.0.0.1:5173'; 32 36 const dev = env.DEV !== undefined; 33 37 const isConfidential = env.OAUTH_JWK !== undefined; ··· 67 71 }; 68 72 69 73 return new NodeOAuthClient({ 70 - stateStore: new StateStore(db), 71 - sessionStore: new SessionStore(db), 74 + stateStore: new StateStore(valKeyClient), 75 + sessionStore: new SessionStore(valKeyClient), 72 76 keyset, 73 77 clientMetadata, 74 78 // Not needed since this all runs locally to one machine I believe. But if you do run multiple instances and change out the DB from sqlite may need this
+6 -5
src/lib/server/atproto/storage.ts
··· 7 7 NodeSavedStateStore 8 8 } from '@atproto/oauth-client-node'; 9 9 import { Cache, SESSION_STORE, STATE_STORE } from '$lib/server/cache'; 10 - import { db } from '$lib/server/db'; 10 + import { GlideClient } from '@valkey/valkey-glide'; 11 + 11 12 12 13 export class StateStore implements NodeSavedStateStore{ 13 14 14 15 cache: Cache; 15 16 16 - constructor(database: typeof db) { 17 - this.cache = new Cache(database, STATE_STORE); 17 + constructor(valKeyClient: GlideClient) { 18 + this.cache = new Cache(valKeyClient, STATE_STORE, 1_800); 18 19 } 19 20 20 21 async del(key: string) { ··· 36 37 37 38 cache: Cache; 38 39 39 - constructor(database: typeof db) { 40 - this.cache = new Cache(database, SESSION_STORE); 40 + constructor(valKeyClient: GlideClient) { 41 + this.cache = new Cache(valKeyClient, SESSION_STORE); 41 42 } 42 43 43 44 async del(key: string) {
+67 -27
src/lib/server/cache.ts
··· 1 - // A key value key to the database that is mostly used for atproto session storage and state storage during the oauth session creation 2 - // The "stores" are divided up by a where on the store type so it can share the same interface just with that 1 + import { logger } from './logger'; 2 + import { env } from '$env/dynamic/private'; 3 + import { GlideClient, TimeUnit } from '@valkey/valkey-glide'; 4 + 5 + export const SESSION_STORE = 'atp_sessions:'; 6 + export const STATE_STORE = 'atp_states:'; 7 + 8 + 9 + let valKeyClient: Promise<GlideClient> | null = null; 10 + 11 + 12 + export const getAValKeyClient = async () => { 13 + if (!valKeyClient) { 14 + valKeyClient = (async () => { 15 + logger.info('Creating valkey client'); 16 + 17 + const addresses = [ 18 + { 19 + host: env.REDIS_HOST ?? 'localhost', 20 + // @ts-expect-error Going to leave it to the redis client to throw a run time error for this since 21 + // it is a run time error to not be able to have redis to connect 22 + port: env.REDIS_PORT as number ?? 6379 23 + }, 24 + ]; 3 25 4 - import { db } from './db'; 5 - import { keyValueStore } from './db/schema'; 6 - import { and, eq } from 'drizzle-orm'; 26 + return await GlideClient.createClient({ 27 + addresses, 28 + credentials: env.REDIS_PASSWORD ? { password: env.REDIS_PASSWORD } : undefined, 29 + useTLS: env.REDIS_TLS === 'true', 30 + //This may be a bit extreme, will see 31 + requestTimeout: 500, // 500ms timeout 32 + }); 33 + })(); 34 + } 35 + return valKeyClient; 36 + }; 7 37 8 - export const SESSION_STORE = 'sessions'; 9 - export const STATE_STORE = 'states'; 10 38 11 39 export class Cache { 12 40 13 - db: typeof db; 14 - cacheName: string; 41 + valKeyClient: GlideClient; 42 + //Set if the cache set should have an expiration 43 + expire: number | undefined; 44 + cachePrefix: string; 15 45 16 - constructor(database: typeof db, cacheName: string) { 17 - this.db = database; 18 - this.cacheName = cacheName; 46 + 47 + constructor(glideClient: GlideClient, cachePrefix: string, expire: number | undefined = undefined) { 48 + this.valKeyClient = glideClient; 49 + this.expire = expire; 50 + this.cachePrefix = cachePrefix; 51 + } 52 + 53 + $key(key: string) { 54 + return `${this.cachePrefix}${key}`; 19 55 } 20 56 21 57 async get(key: string) { 22 - const result = await this.db.select().from(keyValueStore).where(and( 23 - eq(keyValueStore.key, key), 24 - eq(keyValueStore.storeName, this.cacheName) 25 - )).limit(1); 26 - if(result.length > 0){ 27 - return result[0].value; 58 + const result = await this.valKeyClient.get(this.$key(key)); 59 + if(result){ 60 + return result.toString(); 28 61 } 29 - return null; 62 + return undefined; 30 63 } 31 64 32 - async set(key: string, value: string) { 33 - return this.db.insert(keyValueStore) 34 - .values({ key, value, storeName: this.cacheName, createdAt: new Date() }) 35 - .onConflictDoUpdate({ 36 - target: keyValueStore.key, 37 - set: { value, createdAt: new Date() } 38 - }); 65 + async set(key: string, value: string, customExpire?: number) { 66 + const expireSeconds = customExpire ?? this.expire; 67 + const expiryOptions = expireSeconds ? 68 + { 69 + expiry: { 70 + type: TimeUnit.Seconds, 71 + count: expireSeconds 72 + } 73 + } : undefined; 74 + return await this.valKeyClient.set(this.$key(key), value, expiryOptions); 39 75 } 40 76 41 77 async delete(key: string) { 42 - return this.db.delete(keyValueStore).where(eq(keyValueStore.key, key)); 78 + await this.valKeyClient.del([this.$key(key)]); 79 + } 80 + 81 + async refreshExpiry(key: string, seconds: number) { 82 + await this.valKeyClient.expire(this.$key(key), seconds); 43 83 } 44 84 45 85 }
+2 -4
src/lib/server/db/index.ts
··· 1 - import { drizzle } from 'drizzle-orm/better-sqlite3'; 2 - import Database from 'better-sqlite3'; 1 + import { drizzle } from 'drizzle-orm/node-postgres'; 3 2 import * as schema from './schema'; 4 3 import { env } from '$env/dynamic/private'; 5 4 import { logger } from '$lib/server/logger'; 6 5 7 6 if (!env.DATABASE_URL) throw new Error('DATABASE_URL is not set'); 8 7 9 - const client = new Database(env.DATABASE_URL); 10 8 logger.info('Connected to database'); 11 - export const db = drizzle(client, { schema }); 9 + export const db = drizzle(env.DATABASE_URL!,{ schema });
+52 -15
src/lib/server/db/schema.ts
··· 1 - import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'; 1 + import { index, text, integer, jsonb, pgTable, serial, time, varchar, uniqueIndex, timestamp } from 'drizzle-orm/pg-core'; 2 2 3 - export const keyValueStore = sqliteTable('key_value_store', { 4 - key: text('key').primaryKey(), 5 - value: text('value'), 6 - storeName: text('storeName'), 7 - createdAt: integer({ mode: 'timestamp' }) // Date 8 - }); 3 + /** 4 + * Holds collected records from firehose, backfilled, or manually entered 5 + */ 6 + export const recordsTable = pgTable('records', { 7 + id: serial().primaryKey(), 8 + rkey: varchar().notNull(), 9 + collection: varchar().notNull(), 10 + repo: varchar().notNull(), 11 + atUri: text().notNull(), 12 + data: jsonb().notNull(), 13 + indexedAt: timestamp({ mode: 'date' }).notNull().defaultNow() 14 + }, (table) => [ 15 + index().on(table.rkey), 16 + index().on(table.collection), 17 + index().on(table.repo), 18 + uniqueIndex().on(table.atUri), 19 + ]); 9 20 10 21 11 - export const sessionStore = sqliteTable('session_store', { 12 - id: text('id').primaryKey(), 13 - //Not leaving unique since it could be multiple logins from the same user across browsers 14 - did: text('did').notNull(), 15 - handle: text('handle').notNull(), 16 - createdAt: integer({ mode: 'timestamp' }).notNull(), // Date 17 - expiresAt: integer({ mode: 'timestamp' }).notNull() // Date 22 + /** 23 + * Holds pokes toward a record. Current thinking is when a user pokes a record it adds it to the "feeds" 24 + * Maybe have a "catch all feed" that shows all lexicons that the user wants to see 25 + * 26 + * these are the xyz.atpoke.feed.poke records 27 + */ 28 + export const recordPokesTable = pgTable('record_pokes',{ 29 + id: serial().primaryKey(), 30 + recordId: integer().references(() => recordsTable.id), 31 + pokersRepo: text().notNull(), 32 + atUri: text().notNull(), 33 + indexedAt: time().notNull().defaultNow() 34 + },(table) => [ 35 + index().on(table.pokersRepo), 36 + index().on(table.atUri) 37 + ]); 38 + 39 + /** 40 + * These are the traditional pokes. Like "I just poked user xyz". These will make it to the feed via recordsTable 41 + * 42 + * xyz.atpoke.graph.poke 43 + */ 44 + export const userPokes = pgTable('user_pokes', { 45 + id: serial().primaryKey(), 46 + //This is who you poked 47 + subject: text().notNull(), 48 + //Who is doing the poking 49 + poker: text().notNull(), 50 + at_uri: text().notNull(), 51 + indexedAt: time().notNull().defaultNow() 52 + }, (table) => [ 53 + index().on(table.subject), 54 + index().on(table.poker) 55 + ]); 18 56 19 - });
+42 -40
src/lib/server/session.ts
··· 1 1 // A cookie session store based on https://lucia-auth.com/ examples which is recommended from Svelte's docs 2 - // Creates a cookie that links to a session store inside the database allowing the atproto oauth session to be loaded 2 + // Creates a cookie that links to a session store inside Valkey allowing the atproto oauth session to be loaded 3 3 4 - import { db } from './db'; 5 4 import { atpOAuthClient } from './atproto/client'; 6 5 import { encodeBase32LowerCaseNoPadding, encodeHexLowerCase } from '@oslojs/encoding'; 7 6 import { sha256 } from '@oslojs/crypto/sha2'; 8 7 import { DAY } from '@atproto/common'; 9 - import { sessionStore } from '$lib/server/db/schema'; 10 8 import type { RequestEvent } from '@sveltejs/kit'; 11 - import { eq } from 'drizzle-orm'; 12 9 import { Agent } from '@atproto/api'; 13 10 import type { NodeOAuthClient } from '@atproto/oauth-client-node'; 14 11 import { logger } from '$lib/server/logger'; 12 + import { Cache, getAValKeyClient } from '$lib/server/cache'; 13 + 14 + const COOKIE_SESSION_STORE = 'cookie_sessions:'; 15 15 16 16 export class SessionRestorationError extends Error { 17 17 constructor(message: string) { ··· 23 23 // This is a sliding expiration for the cookie session. Can change it if you want it to be less or more. 24 24 // The actual atproto session goes for a while if it's a confidential client as long as it's refreshed 25 25 // https://atproto.com/specs/oauth#tokens-and-session-lifetime 26 - const DEFAULT_EXPIRY = 30 * DAY; 26 + const DEFAULT_EXPIRY_MS = 30 * DAY; 27 + const DEFAULT_EXPIRY_SECONDS = Math.floor(DEFAULT_EXPIRY_MS / 1000); 28 + 27 29 28 30 const NULL_SESSION_RESPONSE = { atpAgent: null, did: null, handle: null }; 29 31 32 + interface StoredSession { 33 + did: string; 34 + handle: string; 35 + createdAt: number; // timestamp in milliseconds 36 + } 30 37 31 38 export class Session { 32 - db: typeof db; 39 + cache: Cache; 33 40 atpOAuthClient: NodeOAuthClient; 34 41 35 - constructor(database: typeof db, oauthClient: NodeOAuthClient) { 36 - this.db = database; 42 + constructor(cache: Cache, oauthClient: NodeOAuthClient) { 43 + this.cache = cache; 37 44 this.atpOAuthClient = oauthClient; 38 45 } 39 46 40 47 41 48 async validateSessionToken(token: string): Promise<SessionValidationResult> { 42 49 const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token))); 43 - const result = await this.db.select().from(sessionStore).where(eq(sessionStore.id, sessionId)).limit(1); 44 - if(result.length > 1){ 45 - throw new Error('Multiple sessions found for token. Should not happen'); 46 - } 47 - if(result.length === 0){ 48 - return NULL_SESSION_RESPONSE; 49 - } 50 - const session = result[0]; 50 + const sessionData = await this.cache.get(sessionId); 51 51 52 - if (Date.now() >= session.expiresAt.getTime()) { 53 - await this.invalidateSession(session.id); 54 - logger.warn(`Session expired for the did: ${session.did}`); 52 + // If session doesn't exist or has expired, Valkey returns undefined 53 + if (!sessionData) { 55 54 return NULL_SESSION_RESPONSE; 56 55 } 57 - if (Date.now() >= session.expiresAt.getTime() - 1000 * 60 * 60 * 24 * 15) { 58 - session.expiresAt = new Date(Date.now() + DEFAULT_EXPIRY); 59 - await this.db.update(sessionStore).set(session).where(eq(sessionStore.id, sessionId)); 60 - } 61 - try{ 56 + 57 + const session: StoredSession = JSON.parse(sessionData); 58 + 59 + // Refresh TTL on each access (sliding window) 60 + await this.cache.refreshExpiry(sessionId, DEFAULT_EXPIRY_SECONDS); 61 + 62 + try { 62 63 const oAuthSession = await this.atpOAuthClient.restore(session.did); 63 - 64 64 const agent = new Agent(oAuthSession); 65 65 return { atpAgent: agent, did: session.did, handle: session.handle }; 66 - }catch (err){ 66 + } catch (err) { 67 67 const errorMessage = (err as Error).message; 68 68 logger.warn(`Error restoring session for did: ${session.did}, error: ${errorMessage}`); 69 - //Counting any error when restoring a session as a failed session resume and deleting the users web browser session 69 + //Counting any error when restoring a session as a failed session resume and deleting the user's web browser session 70 70 //You can go further and capture different types of errors 71 - await this.invalidateUserSessions(session.did); 71 + await this.invalidateSession(sessionId); 72 72 throw new SessionRestorationError(`Failed to restore your session: ${errorMessage}. Please log in again.`); 73 73 } 74 74 } 75 75 76 - private setSessionTokenCookie(event: RequestEvent, token: string, expiresAt: Date): void { 76 + private setSessionTokenCookie(event: RequestEvent, token: string): void { 77 77 event.cookies.set('session', token, { 78 78 httpOnly: true, 79 79 path: '/', 80 80 secure: import.meta.env.PROD, 81 81 sameSite: 'lax', 82 - expires: expiresAt 82 + maxAge: DEFAULT_EXPIRY_SECONDS 83 83 }); 84 84 } 85 85 ··· 94 94 } 95 95 96 96 async invalidateSession(sessionId: string) { 97 - await this.db.delete(sessionStore).where(eq(sessionStore.id, sessionId)); 97 + await this.cache.delete(sessionId); 98 98 } 99 99 100 100 async invalidateSessionByToken(token: string) { ··· 102 102 await this.invalidateSession(sessionId); 103 103 } 104 104 105 - async invalidateUserSessions(did: string) { 106 - await this.db.delete(sessionStore).where(eq(sessionStore.did, did)); 107 - } 108 - 109 105 private async createSession(token: string, did: string, handle: string) { 110 106 const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token))); 111 - const expiresAt = new Date(Date.now() + DEFAULT_EXPIRY); 112 - const session = { id: sessionId, did, handle, expiresAt, createdAt: new Date() }; 113 - await this.db.insert(sessionStore).values(session); 107 + const session: StoredSession = { 108 + did, 109 + handle, 110 + createdAt: Date.now() 111 + }; 112 + // Set with TTL - Valkey will automatically expire after DEFAULT_EXPIRY_SECONDS 113 + await this.cache.set(sessionId, JSON.stringify(session), DEFAULT_EXPIRY_SECONDS); 114 114 return session; 115 115 } 116 116 ··· 123 123 async createAndSetSession(event: RequestEvent, did: string, handle: string) { 124 124 const token = this.generateSessionToken(); 125 125 const session = await this.createSession(token, did, handle); 126 - this.setSessionTokenCookie(event, token, session.expiresAt); 126 + this.setSessionTokenCookie(event, token); 127 127 return session; 128 128 } 129 129 ··· 150 150 export const getSessionManager = async (): Promise<Session> => { 151 151 if (!sessionManager) { 152 152 sessionManager = (async () => { 153 + const valKeyClient = await getAValKeyClient(); 154 + const cache = new Cache(valKeyClient, COOKIE_SESSION_STORE); 153 155 const client = await atpOAuthClient(); 154 - return new Session(db, client); 156 + return new Session(cache, client); 155 157 })(); 156 158 } 157 159 return sessionManager;
+4 -1
src/routes/+layout.svelte
··· 1 1 <script lang="ts"> 2 2 import favicon from '$lib/assets/favicon.svg'; 3 + import '../app.css'; 3 4 let { children, data } = $props(); 4 5 import { enhance } from '$app/forms'; 5 6 </script> ··· 55 56 <li> 56 57 <a href="/">Home</a> 57 58 </li> 58 - 59 + <li> 60 + <a href="/feed">Feed</a> 61 + </li> 59 62 <li> 60 63 <a href="/demo">Demo</a> 61 64 </li>
+76
src/routes/feed/+page.server.ts
··· 1 + import type { PageServerLoad } from './$types'; 2 + import { redirect } from '@sveltejs/kit'; 3 + import { db } from '$lib/server/db'; 4 + import { recordsTable } from '$lib/server/db/schema'; 5 + import { desc, eq, ne } from 'drizzle-orm'; 6 + 7 + interface ProfileData { 8 + handle: string; 9 + avatar?: string; 10 + displayName?: string; 11 + } 12 + 13 + async function fetchProfile(repo: string): Promise<ProfileData | null> { 14 + try { 15 + const response = await fetch( 16 + `https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=${repo}` 17 + ); 18 + if (!response.ok) return null; 19 + const data = await response.json(); 20 + return { 21 + handle: data.handle, 22 + avatar: data.avatar, 23 + displayName: data.displayName 24 + }; 25 + } catch { 26 + return null; 27 + } 28 + } 29 + 30 + export const load: PageServerLoad = async (event) => { 31 + if (!event.locals.session) { 32 + return redirect(302, '/login'); 33 + } 34 + 35 + // Fetch one music play record 36 + const musicRecords = await db 37 + .select() 38 + .from(recordsTable) 39 + .where(eq(recordsTable.collection, 'fm.teal.alpha.feed.play')) 40 + .orderBy(desc(recordsTable.indexedAt)) 41 + .limit(1); 42 + 43 + // Fetch other records (non-music) 44 + const otherRecords = await db 45 + .select() 46 + .from(recordsTable) 47 + .where(ne(recordsTable.collection, 'fm.teal.alpha.feed.play')) 48 + .orderBy(desc(recordsTable.indexedAt)) 49 + .limit(49); 50 + 51 + // Combine and sort by indexedAt 52 + const records = [...musicRecords, ...otherRecords].sort( 53 + (a, b) => b.indexedAt.getTime() - a.indexedAt.getTime() 54 + ); 55 + 56 + // Fetch profile data for all unique repos 57 + const uniqueRepos = [...new Set(records.map((r) => r.repo))]; 58 + const profilePromises = uniqueRepos.map(async (repo) => { 59 + const profile = await fetchProfile(repo); 60 + return { repo, profile }; 61 + }); 62 + 63 + const profileResults = await Promise.all(profilePromises); 64 + const profiles: Record<string, ProfileData> = {}; 65 + for (const { repo, profile } of profileResults) { 66 + if (profile) { 67 + profiles[repo] = profile; 68 + } 69 + } 70 + 71 + return { 72 + records, 73 + profiles, 74 + usersDid: event.locals.session.did 75 + }; 76 + };
+58
src/routes/feed/+page.svelte
··· 1 + <script lang="ts"> 2 + import type { PageData } from './$types'; 3 + import MusicPlayCard from '$lib/components/MusicPlayCard.svelte'; 4 + import TangledRepoCard from '$lib/components/TangledRepoCard.svelte'; 5 + import LeafletDocumentCard from '$lib/components/LeafletDocumentCard.svelte'; 6 + 7 + let { data }: { data: PageData } = $props(); 8 + </script> 9 + 10 + <svelte:head> 11 + <title>Feed - atpoke.xyz</title> 12 + </svelte:head> 13 + 14 + <div class="container mx-auto px-4 py-8 max-w-3xl"> 15 + <div class="mb-8"> 16 + <h1 class="text-4xl font-bold mb-2">Feed</h1> 17 + <p class="text-base-content/70">Discover the latest from the ATProto ecosystem</p> 18 + </div> 19 + 20 + {#if data.records.length === 0} 21 + <div class="alert"> 22 + <svg 23 + xmlns="http://www.w3.org/2000/svg" 24 + fill="none" 25 + viewBox="0 0 24 24" 26 + class="stroke-info shrink-0 w-6 h-6" 27 + > 28 + <path 29 + stroke-linecap="round" 30 + stroke-linejoin="round" 31 + stroke-width="2" 32 + d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" 33 + ></path> 34 + </svg> 35 + <span>No records found. The feed is empty.</span> 36 + </div> 37 + {:else} 38 + <div> 39 + {#each data.records as record (record.id)} 40 + {#if record.collection === 'fm.teal.alpha.feed.play'} 41 + <MusicPlayCard {record} profile={data.profiles[record.repo]} /> 42 + {:else if record.collection === 'sh.tangled.repo'} 43 + <TangledRepoCard {record} profile={data.profiles[record.repo]} /> 44 + {:else if record.collection === 'pub.leaflet.document'} 45 + <LeafletDocumentCard {record} profile={data.profiles[record.repo]} /> 46 + {:else} 47 + <div class="card bg-base-100 border-b border-base-300"> 48 + <div class="card-body"> 49 + <h3 class="card-title text-sm opacity-60">Unknown Collection Type</h3> 50 + <p class="text-sm">{record.collection}</p> 51 + <pre class="text-xs overflow-auto">{JSON.stringify(record.data, null, 2)}</pre> 52 + </div> 53 + </div> 54 + {/if} 55 + {/each} 56 + </div> 57 + {/if} 58 + </div>
+2 -2
src/routes/logout/+page.server.ts
··· 13 13 sessionManager.deleteSessionTokenCookie(event); 14 14 15 15 const oauthClient = await atpOAuthClient(); 16 - if(event.locals.did) { 17 - await oauthClient.revoke(event.locals.did); 16 + if(event.locals.session?.did) { 17 + await oauthClient.revoke(event.locals.session.did); 18 18 } 19 19 } 20 20
+81
src/routes/webhooks/tap/+server.ts
··· 1 + import { json } from '@sveltejs/kit'; 2 + import { env } from '$env/dynamic/private'; 3 + import type { RequestHandler } from './$types'; 4 + import { logger } from '$lib/server/logger'; 5 + import { assureAdminAuth, type RecordEvent, type TapEvent } from '@atproto/tap'; 6 + import { db } from '$lib/server/db'; 7 + import { recordsTable } from '$lib/server/db/schema'; 8 + import { eq } from 'drizzle-orm'; 9 + 10 + export const POST: RequestHandler = async ({ request }) => { 11 + const auth = request.headers.get('Authorization'); 12 + 13 + if (auth === null) { 14 + logger.error('Missing Authorization header'); 15 + return json({ error: 'Missing Authorization header' }, { status: 401 }); 16 + } 17 + 18 + try{ 19 + if(env.TAP_ADMIN_PASSWORD === undefined) throw new Error('TAP_ADMIN_PASSWORD is not set'); 20 + assureAdminAuth(env.TAP_ADMIN_PASSWORD, auth); 21 + }catch (err){ 22 + const errorMessage = (err as Error).message; 23 + logger.error('Tap webhook auth error: ' + errorMessage + ''); 24 + return json({ error: 'Not authenticated' }, { status: 401 }); 25 + } 26 + 27 + try { 28 + const body = await request.json(); 29 + await parseAndProcessTapEvent(body); 30 + 31 + 32 + //This should just respond with 200 OK. comment out to not ack 33 + return json({ }); 34 + } catch (err) { 35 + console.error('Failed to process event:', err); 36 + return json({ error: 'Failed to process event' }, { status: 500 }); 37 + } 38 + 39 + }; 40 + 41 + const parseAndProcessTapEvent = async (event: TapEvent) => { 42 + switch (event.type) { 43 + case 'identity': 44 + logger.info(event); 45 + break; 46 + case 'record': 47 + await saveRecord(event.record as RecordEvent); 48 + break; 49 + default: 50 + throw new Error(`Unsupported event type: ${JSON.stringify(event)}`); 51 + } 52 + }; 53 + 54 + 55 + const saveRecord = async (event: RecordEvent) => { 56 + const atUri = `${event.did}/${event.collection}/${event.rkey}`; 57 + logger.info(`Processing record event: ${atUri}`); 58 + if (event.action === 'create' || event.action === 'update') { 59 + if (!event.record) { 60 + logger.warn(`Record event with action ${event.action} missing record data: ${event.rkey}`); 61 + return; 62 + } 63 + 64 + await db.insert(recordsTable).values({ 65 + rkey: event.rkey, 66 + collection: event.collection, 67 + repo: event.did, 68 + atUri: atUri, 69 + data: event.record, 70 + }).onConflictDoUpdate({ 71 + target: recordsTable.atUri, 72 + set: { 73 + data: event.record, 74 + 75 + } 76 + }); 77 + logger.info(`Saved record: ${event.did}/${event.collection}/${event.rkey}`); 78 + } else if (event.action === 'delete') { 79 + await db.delete(recordsTable).where(eq(recordsTable.atUri, atUri)); 80 + } 81 + };
+2 -1
vite.config.ts
··· 1 + import tailwindcss from '@tailwindcss/vite'; 1 2 import { sveltekit } from '@sveltejs/kit/vite'; 2 3 import { defineConfig } from 'vite'; 3 4 import 'dotenv/config'; 4 5 5 6 export default defineConfig({ 6 - plugins: [sveltekit()], 7 + plugins: [tailwindcss(), sveltekit()], 7 8 server: { 8 9 host: '0.0.0.0', 9 10 allowedHosts: process.env.OAUTH_DOMAIN ? [process.env.OAUTH_DOMAIN] : []