+6
-1
.gitignore
+6
-1
.gitignore
+48
dev.compose.yml
+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
+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
-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
+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
+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
+1
-1
drizzle.config.ts
+9
-3
package.json
+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
+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
+3
-27
src/hooks.server.ts
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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] : []