+6
-1
.gitignore
+6
-1
.gitignore
+14
dev.compose.yml
+14
dev.compose.yml
···
26
26
timeout: 5s
27
27
networks:
28
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
+
29
43
volumes:
30
44
valkey_data:
31
45
postgres_data:
+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
+8
-3
package.json
+8
-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",
55
+
"@tailwindcss/vite": "^4.1.18",
53
56
"@valkey/valkey-glide": "^2.2.1",
54
-
"better-sqlite3": "12.4.1",
57
+
"daisyui": "^5.5.14",
55
58
"node-schedule": "^2.1.1",
56
-
"pino": "^10.1.0"
59
+
"pg": "^8.16.3",
60
+
"pino": "^10.1.0",
61
+
"tailwindcss": "^4.1.18"
57
62
},
58
63
"optionalDependencies": {
59
64
"@rollup/rollup-linux-x64-musl": "^4.52.5"
+874
-76
pnpm-lock.yaml
+874
-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)))
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))
44
50
'@valkey/valkey-glide':
45
51
specifier: ^2.2.1
46
52
version: 2.2.1
47
-
better-sqlite3:
48
-
specifier: 12.4.1
49
-
version: 12.4.1
53
+
daisyui:
54
+
specifier: ^5.5.14
55
+
version: 5.5.14
50
56
node-schedule:
51
57
specifier: ^2.1.1
52
58
version: 2.1.1
59
+
pg:
60
+
specifier: ^8.16.3
61
+
version: 8.16.3
53
62
pino:
54
63
specifier: ^10.1.0
55
64
version: 10.1.0
65
+
tailwindcss:
66
+
specifier: ^4.1.18
67
+
version: 4.1.18
56
68
devDependencies:
57
69
'@eslint/compat':
58
70
specifier: ^1.4.0
59
-
version: 1.4.1(eslint@9.39.1)
71
+
version: 1.4.1(eslint@9.39.1(jiti@2.6.1))
60
72
'@eslint/js':
61
73
specifier: ^9.39.1
62
74
version: 9.39.1
63
75
'@sveltejs/adapter-auto':
64
76
specifier: ^7.0.0
65
-
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)))
66
78
'@sveltejs/kit':
67
79
specifier: ^2.48.5
68
-
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))
69
81
'@sveltejs/vite-plugin-svelte':
70
82
specifier: ^6.2.1
71
-
version: 6.2.1(svelte@5.45.8)(vite@7.2.7(@types/node@24.10.2))
72
-
'@types/better-sqlite3':
73
-
specifier: ^7.6.13
74
-
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))
75
84
'@types/node':
76
85
specifier: ^24
77
86
version: 24.10.2
87
+
'@types/pg':
88
+
specifier: ^8.16.0
89
+
version: 8.16.0
78
90
dotenv:
79
91
specifier: ^17.2.3
80
92
version: 17.2.3
···
83
95
version: 0.31.8
84
96
drizzle-orm:
85
97
specifier: ^0.44.7
86
-
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)
87
99
eslint:
88
100
specifier: ^9.39.1
89
-
version: 9.39.1
101
+
version: 9.39.1(jiti@2.6.1)
90
102
eslint-plugin-svelte:
91
103
specifier: ^3.13.0
92
-
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)
93
105
globals:
94
106
specifier: ^16.5.0
95
107
version: 16.5.0
···
99
111
svelte-check:
100
112
specifier: ^4.3.4
101
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
102
117
typescript:
103
118
specifier: ^5.9.3
104
119
version: 5.9.3
105
120
typescript-eslint:
106
121
specifier: ^8.47.0
107
-
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)
108
123
vite:
109
124
specifier: ^7.2.2
110
-
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)
111
126
optionalDependencies:
112
127
'@rollup/rollup-linux-x64-musl':
113
128
specifier: ^4.52.5
···
207
222
'@atproto/syntax@0.4.2':
208
223
resolution: {integrity: sha512-X9XSRPinBy/0VQ677j8VXlBsYSsUXaiqxWVpGGxJYsAhugdQRb0jqaVKJFtm6RskeNkV6y9xclSUi9UYG/COrA==}
209
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
+
210
233
'@atproto/xrpc@0.7.7':
211
234
resolution: {integrity: sha512-K1ZyO/BU8JNtXX5dmPp7b5UrkLMMqpsIa/Lrj5D3Su+j1Xwq1m6QJ2XJ1AgjEjkI1v4Muzm7klianLE6XGxtmA==}
212
235
···
231
254
cpu: [ppc64]
232
255
os: [aix]
233
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
+
234
263
'@esbuild/android-arm64@0.18.20':
235
264
resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
236
265
engines: {node: '>=12'}
···
239
268
240
269
'@esbuild/android-arm64@0.25.12':
241
270
resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==}
271
+
engines: {node: '>=18'}
272
+
cpu: [arm64]
273
+
os: [android]
274
+
275
+
'@esbuild/android-arm64@0.27.1':
276
+
resolution: {integrity: sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==}
242
277
engines: {node: '>=18'}
243
278
cpu: [arm64]
244
279
os: [android]
···
255
290
cpu: [arm]
256
291
os: [android]
257
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
+
258
299
'@esbuild/android-x64@0.18.20':
259
300
resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==}
260
301
engines: {node: '>=12'}
···
267
308
cpu: [x64]
268
309
os: [android]
269
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
+
270
317
'@esbuild/darwin-arm64@0.18.20':
271
318
resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==}
272
319
engines: {node: '>=12'}
···
279
326
cpu: [arm64]
280
327
os: [darwin]
281
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
+
282
335
'@esbuild/darwin-x64@0.18.20':
283
336
resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==}
284
337
engines: {node: '>=12'}
···
291
344
cpu: [x64]
292
345
os: [darwin]
293
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
+
294
353
'@esbuild/freebsd-arm64@0.18.20':
295
354
resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==}
296
355
engines: {node: '>=12'}
···
299
358
300
359
'@esbuild/freebsd-arm64@0.25.12':
301
360
resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==}
361
+
engines: {node: '>=18'}
362
+
cpu: [arm64]
363
+
os: [freebsd]
364
+
365
+
'@esbuild/freebsd-arm64@0.27.1':
366
+
resolution: {integrity: sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==}
302
367
engines: {node: '>=18'}
303
368
cpu: [arm64]
304
369
os: [freebsd]
···
315
380
cpu: [x64]
316
381
os: [freebsd]
317
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
+
318
389
'@esbuild/linux-arm64@0.18.20':
319
390
resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==}
320
391
engines: {node: '>=12'}
···
327
398
cpu: [arm64]
328
399
os: [linux]
329
400
401
+
'@esbuild/linux-arm64@0.27.1':
402
+
resolution: {integrity: sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==}
403
+
engines: {node: '>=18'}
404
+
cpu: [arm64]
405
+
os: [linux]
406
+
330
407
'@esbuild/linux-arm@0.18.20':
331
408
resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==}
332
409
engines: {node: '>=12'}
···
339
416
cpu: [arm]
340
417
os: [linux]
341
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
+
342
425
'@esbuild/linux-ia32@0.18.20':
343
426
resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==}
344
427
engines: {node: '>=12'}
···
351
434
cpu: [ia32]
352
435
os: [linux]
353
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
+
354
443
'@esbuild/linux-loong64@0.18.20':
355
444
resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==}
356
445
engines: {node: '>=12'}
···
363
452
cpu: [loong64]
364
453
os: [linux]
365
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
+
366
461
'@esbuild/linux-mips64el@0.18.20':
367
462
resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==}
368
463
engines: {node: '>=12'}
···
375
470
cpu: [mips64el]
376
471
os: [linux]
377
472
473
+
'@esbuild/linux-mips64el@0.27.1':
474
+
resolution: {integrity: sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==}
475
+
engines: {node: '>=18'}
476
+
cpu: [mips64el]
477
+
os: [linux]
478
+
378
479
'@esbuild/linux-ppc64@0.18.20':
379
480
resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==}
380
481
engines: {node: '>=12'}
···
387
488
cpu: [ppc64]
388
489
os: [linux]
389
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
+
390
497
'@esbuild/linux-riscv64@0.18.20':
391
498
resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==}
392
499
engines: {node: '>=12'}
···
399
506
cpu: [riscv64]
400
507
os: [linux]
401
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
+
402
515
'@esbuild/linux-s390x@0.18.20':
403
516
resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==}
404
517
engines: {node: '>=12'}
···
411
524
cpu: [s390x]
412
525
os: [linux]
413
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
+
414
533
'@esbuild/linux-x64@0.18.20':
415
534
resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==}
416
535
engines: {node: '>=12'}
···
423
542
cpu: [x64]
424
543
os: [linux]
425
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
+
426
551
'@esbuild/netbsd-arm64@0.25.12':
427
552
resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==}
553
+
engines: {node: '>=18'}
554
+
cpu: [arm64]
555
+
os: [netbsd]
556
+
557
+
'@esbuild/netbsd-arm64@0.27.1':
558
+
resolution: {integrity: sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==}
428
559
engines: {node: '>=18'}
429
560
cpu: [arm64]
430
561
os: [netbsd]
···
441
572
cpu: [x64]
442
573
os: [netbsd]
443
574
575
+
'@esbuild/netbsd-x64@0.27.1':
576
+
resolution: {integrity: sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==}
577
+
engines: {node: '>=18'}
578
+
cpu: [x64]
579
+
os: [netbsd]
580
+
444
581
'@esbuild/openbsd-arm64@0.25.12':
445
582
resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==}
583
+
engines: {node: '>=18'}
584
+
cpu: [arm64]
585
+
os: [openbsd]
586
+
587
+
'@esbuild/openbsd-arm64@0.27.1':
588
+
resolution: {integrity: sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==}
446
589
engines: {node: '>=18'}
447
590
cpu: [arm64]
448
591
os: [openbsd]
···
459
602
cpu: [x64]
460
603
os: [openbsd]
461
604
605
+
'@esbuild/openbsd-x64@0.27.1':
606
+
resolution: {integrity: sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==}
607
+
engines: {node: '>=18'}
608
+
cpu: [x64]
609
+
os: [openbsd]
610
+
462
611
'@esbuild/openharmony-arm64@0.25.12':
463
612
resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==}
613
+
engines: {node: '>=18'}
614
+
cpu: [arm64]
615
+
os: [openharmony]
616
+
617
+
'@esbuild/openharmony-arm64@0.27.1':
618
+
resolution: {integrity: sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==}
464
619
engines: {node: '>=18'}
465
620
cpu: [arm64]
466
621
os: [openharmony]
···
477
632
cpu: [x64]
478
633
os: [sunos]
479
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
+
480
641
'@esbuild/win32-arm64@0.18.20':
481
642
resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==}
482
643
engines: {node: '>=12'}
···
489
650
cpu: [arm64]
490
651
os: [win32]
491
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
+
492
659
'@esbuild/win32-ia32@0.18.20':
493
660
resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==}
494
661
engines: {node: '>=12'}
···
501
668
cpu: [ia32]
502
669
os: [win32]
503
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
+
504
677
'@esbuild/win32-x64@0.18.20':
505
678
resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==}
506
679
engines: {node: '>=12'}
···
509
682
510
683
'@esbuild/win32-x64@0.25.12':
511
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==}
512
691
engines: {node: '>=18'}
513
692
cpu: [x64]
514
693
os: [win32]
···
840
1019
svelte: ^5.0.0
841
1020
vite: ^6.3.0 || ^7.0.0
842
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
+
843
1112
'@types/better-sqlite3@7.6.13':
844
1113
resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==}
845
1114
···
854
1123
855
1124
'@types/node@24.10.2':
856
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==}
857
1129
858
1130
'@types/resolve@1.20.2':
859
1131
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
···
1073
1345
engines: {node: '>=4'}
1074
1346
hasBin: true
1075
1347
1348
+
daisyui@5.5.14:
1349
+
resolution: {integrity: sha512-L47rvw7I7hK68TA97VB8Ee0woHew+/ohR6Lx6Ah/krfISOqcG4My7poNpX5Mo5/ytMxiR40fEaz6njzDi7cuSg==}
1350
+
1076
1351
debug@4.4.3:
1077
1352
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
1078
1353
engines: {node: '>=6.0'}
···
1207
1482
end-of-stream@1.4.5:
1208
1483
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
1209
1484
1485
+
enhanced-resolve@5.18.4:
1486
+
resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==}
1487
+
engines: {node: '>=10.13.0'}
1488
+
1210
1489
esbuild-register@3.6.0:
1211
1490
resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==}
1212
1491
peerDependencies:
···
1222
1501
engines: {node: '>=18'}
1223
1502
hasBin: true
1224
1503
1504
+
esbuild@0.27.1:
1505
+
resolution: {integrity: sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==}
1506
+
engines: {node: '>=18'}
1507
+
hasBin: true
1508
+
1225
1509
escape-string-regexp@4.0.0:
1226
1510
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
1227
1511
engines: {node: '>=10'}
···
1368
1652
resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==}
1369
1653
engines: {node: '>=18'}
1370
1654
1655
+
graceful-fs@4.2.11:
1656
+
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
1657
+
1371
1658
has-flag@4.0.0:
1372
1659
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
1373
1660
engines: {node: '>=8'}
···
1432
1719
iso-datestring-validator@2.2.2:
1433
1720
resolution: {integrity: sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==}
1434
1721
1722
+
jiti@2.6.1:
1723
+
resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
1724
+
hasBin: true
1725
+
1435
1726
jose@5.10.0:
1436
1727
resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==}
1437
1728
···
1462
1753
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
1463
1754
engines: {node: '>= 0.8.0'}
1464
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
+
1465
1826
lilconfig@2.1.0:
1466
1827
resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
1467
1828
engines: {node: '>=10'}
···
1576
1937
path-parse@1.0.7:
1577
1938
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
1578
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
+
1579
1974
picocolors@1.1.1:
1580
1975
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
1581
1976
···
1634
2029
postcss@8.5.6:
1635
2030
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
1636
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'}
1637
2048
1638
2049
prebuild-install@7.1.3:
1639
2050
resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
···
1806
2217
resolution: {integrity: sha512-1Jh7FwVh/2Uxg0T7SeE1qFKMhwYH45b2v53bcZpW7qHa6O8iU1ByEj56PF0IQ6dU4HE5gRkic6h+vx+tclHeiw==}
1807
2218
engines: {node: '>=18'}
1808
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
+
1809
2227
tar-fs@2.1.4:
1810
2228
resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==}
1811
2229
···
1839
2257
1840
2258
tslib@2.8.1:
1841
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
1842
2265
1843
2266
tunnel-agent@0.6.0:
1844
2267
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
···
1937
2360
1938
2361
wrappy@1.0.2:
1939
2362
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
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'}
1940
2379
1941
2380
yaml@1.10.2:
1942
2381
resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
···
2135
2574
2136
2575
'@atproto/syntax@0.4.2': {}
2137
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
+
2138
2596
'@atproto/xrpc@0.7.7':
2139
2597
dependencies:
2140
2598
'@atproto/lexicon': 0.6.0
···
2157
2615
'@esbuild/aix-ppc64@0.25.12':
2158
2616
optional: true
2159
2617
2618
+
'@esbuild/aix-ppc64@0.27.1':
2619
+
optional: true
2620
+
2160
2621
'@esbuild/android-arm64@0.18.20':
2161
2622
optional: true
2162
2623
2163
2624
'@esbuild/android-arm64@0.25.12':
2164
2625
optional: true
2165
2626
2627
+
'@esbuild/android-arm64@0.27.1':
2628
+
optional: true
2629
+
2166
2630
'@esbuild/android-arm@0.18.20':
2167
2631
optional: true
2168
2632
2169
2633
'@esbuild/android-arm@0.25.12':
2170
2634
optional: true
2171
2635
2636
+
'@esbuild/android-arm@0.27.1':
2637
+
optional: true
2638
+
2172
2639
'@esbuild/android-x64@0.18.20':
2173
2640
optional: true
2174
2641
2175
2642
'@esbuild/android-x64@0.25.12':
2643
+
optional: true
2644
+
2645
+
'@esbuild/android-x64@0.27.1':
2176
2646
optional: true
2177
2647
2178
2648
'@esbuild/darwin-arm64@0.18.20':
2179
2649
optional: true
2180
2650
2181
2651
'@esbuild/darwin-arm64@0.25.12':
2652
+
optional: true
2653
+
2654
+
'@esbuild/darwin-arm64@0.27.1':
2182
2655
optional: true
2183
2656
2184
2657
'@esbuild/darwin-x64@0.18.20':
···
2187
2660
'@esbuild/darwin-x64@0.25.12':
2188
2661
optional: true
2189
2662
2663
+
'@esbuild/darwin-x64@0.27.1':
2664
+
optional: true
2665
+
2190
2666
'@esbuild/freebsd-arm64@0.18.20':
2191
2667
optional: true
2192
2668
2193
2669
'@esbuild/freebsd-arm64@0.25.12':
2194
2670
optional: true
2195
2671
2672
+
'@esbuild/freebsd-arm64@0.27.1':
2673
+
optional: true
2674
+
2196
2675
'@esbuild/freebsd-x64@0.18.20':
2197
2676
optional: true
2198
2677
2199
2678
'@esbuild/freebsd-x64@0.25.12':
2200
2679
optional: true
2201
2680
2681
+
'@esbuild/freebsd-x64@0.27.1':
2682
+
optional: true
2683
+
2202
2684
'@esbuild/linux-arm64@0.18.20':
2203
2685
optional: true
2204
2686
2205
2687
'@esbuild/linux-arm64@0.25.12':
2688
+
optional: true
2689
+
2690
+
'@esbuild/linux-arm64@0.27.1':
2206
2691
optional: true
2207
2692
2208
2693
'@esbuild/linux-arm@0.18.20':
2209
2694
optional: true
2210
2695
2211
2696
'@esbuild/linux-arm@0.25.12':
2697
+
optional: true
2698
+
2699
+
'@esbuild/linux-arm@0.27.1':
2212
2700
optional: true
2213
2701
2214
2702
'@esbuild/linux-ia32@0.18.20':
···
2217
2705
'@esbuild/linux-ia32@0.25.12':
2218
2706
optional: true
2219
2707
2708
+
'@esbuild/linux-ia32@0.27.1':
2709
+
optional: true
2710
+
2220
2711
'@esbuild/linux-loong64@0.18.20':
2221
2712
optional: true
2222
2713
2223
2714
'@esbuild/linux-loong64@0.25.12':
2224
2715
optional: true
2225
2716
2717
+
'@esbuild/linux-loong64@0.27.1':
2718
+
optional: true
2719
+
2226
2720
'@esbuild/linux-mips64el@0.18.20':
2227
2721
optional: true
2228
2722
2229
2723
'@esbuild/linux-mips64el@0.25.12':
2230
2724
optional: true
2231
2725
2726
+
'@esbuild/linux-mips64el@0.27.1':
2727
+
optional: true
2728
+
2232
2729
'@esbuild/linux-ppc64@0.18.20':
2233
2730
optional: true
2234
2731
2235
2732
'@esbuild/linux-ppc64@0.25.12':
2733
+
optional: true
2734
+
2735
+
'@esbuild/linux-ppc64@0.27.1':
2236
2736
optional: true
2237
2737
2238
2738
'@esbuild/linux-riscv64@0.18.20':
2239
2739
optional: true
2240
2740
2241
2741
'@esbuild/linux-riscv64@0.25.12':
2742
+
optional: true
2743
+
2744
+
'@esbuild/linux-riscv64@0.27.1':
2242
2745
optional: true
2243
2746
2244
2747
'@esbuild/linux-s390x@0.18.20':
···
2247
2750
'@esbuild/linux-s390x@0.25.12':
2248
2751
optional: true
2249
2752
2753
+
'@esbuild/linux-s390x@0.27.1':
2754
+
optional: true
2755
+
2250
2756
'@esbuild/linux-x64@0.18.20':
2251
2757
optional: true
2252
2758
2253
2759
'@esbuild/linux-x64@0.25.12':
2760
+
optional: true
2761
+
2762
+
'@esbuild/linux-x64@0.27.1':
2254
2763
optional: true
2255
2764
2256
2765
'@esbuild/netbsd-arm64@0.25.12':
2257
2766
optional: true
2258
2767
2768
+
'@esbuild/netbsd-arm64@0.27.1':
2769
+
optional: true
2770
+
2259
2771
'@esbuild/netbsd-x64@0.18.20':
2260
2772
optional: true
2261
2773
2262
2774
'@esbuild/netbsd-x64@0.25.12':
2263
2775
optional: true
2264
2776
2777
+
'@esbuild/netbsd-x64@0.27.1':
2778
+
optional: true
2779
+
2265
2780
'@esbuild/openbsd-arm64@0.25.12':
2781
+
optional: true
2782
+
2783
+
'@esbuild/openbsd-arm64@0.27.1':
2266
2784
optional: true
2267
2785
2268
2786
'@esbuild/openbsd-x64@0.18.20':
···
2271
2789
'@esbuild/openbsd-x64@0.25.12':
2272
2790
optional: true
2273
2791
2792
+
'@esbuild/openbsd-x64@0.27.1':
2793
+
optional: true
2794
+
2274
2795
'@esbuild/openharmony-arm64@0.25.12':
2796
+
optional: true
2797
+
2798
+
'@esbuild/openharmony-arm64@0.27.1':
2275
2799
optional: true
2276
2800
2277
2801
'@esbuild/sunos-x64@0.18.20':
···
2280
2804
'@esbuild/sunos-x64@0.25.12':
2281
2805
optional: true
2282
2806
2807
+
'@esbuild/sunos-x64@0.27.1':
2808
+
optional: true
2809
+
2283
2810
'@esbuild/win32-arm64@0.18.20':
2284
2811
optional: true
2285
2812
2286
2813
'@esbuild/win32-arm64@0.25.12':
2287
2814
optional: true
2288
2815
2816
+
'@esbuild/win32-arm64@0.27.1':
2817
+
optional: true
2818
+
2289
2819
'@esbuild/win32-ia32@0.18.20':
2290
2820
optional: true
2291
2821
2292
2822
'@esbuild/win32-ia32@0.25.12':
2823
+
optional: true
2824
+
2825
+
'@esbuild/win32-ia32@0.27.1':
2293
2826
optional: true
2294
2827
2295
2828
'@esbuild/win32-x64@0.18.20':
···
2298
2831
'@esbuild/win32-x64@0.25.12':
2299
2832
optional: true
2300
2833
2301
-
'@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))':
2302
2838
dependencies:
2303
-
eslint: 9.39.1
2839
+
eslint: 9.39.1(jiti@2.6.1)
2304
2840
eslint-visitor-keys: 3.4.3
2305
2841
2306
2842
'@eslint-community/regexpp@4.12.2': {}
2307
2843
2308
-
'@eslint/compat@1.4.1(eslint@9.39.1)':
2844
+
'@eslint/compat@1.4.1(eslint@9.39.1(jiti@2.6.1))':
2309
2845
dependencies:
2310
2846
'@eslint/core': 0.17.0
2311
2847
optionalDependencies:
2312
-
eslint: 9.39.1
2848
+
eslint: 9.39.1(jiti@2.6.1)
2313
2849
2314
2850
'@eslint/config-array@0.21.1':
2315
2851
dependencies:
···
2534
3070
dependencies:
2535
3071
acorn: 8.15.0
2536
3072
2537
-
'@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)))':
2538
3074
dependencies:
2539
-
'@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))
2540
3076
2541
-
'@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)))':
2542
3078
dependencies:
2543
3079
'@rollup/plugin-commonjs': 28.0.9(rollup@4.53.3)
2544
3080
'@rollup/plugin-json': 6.1.0(rollup@4.53.3)
2545
3081
'@rollup/plugin-node-resolve': 16.0.3(rollup@4.53.3)
2546
-
'@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))
2547
3083
rollup: 4.53.3
2548
3084
2549
-
'@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))':
2550
3086
dependencies:
2551
3087
'@standard-schema/spec': 1.0.0
2552
3088
'@sveltejs/acorn-typescript': 1.0.8(acorn@8.15.0)
2553
-
'@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))
2554
3090
'@types/cookie': 0.6.0
2555
3091
acorn: 8.15.0
2556
3092
cookie: 0.6.0
···
2563
3099
set-cookie-parser: 2.7.2
2564
3100
sirv: 3.0.2
2565
3101
svelte: 5.45.8
2566
-
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)
2567
3103
2568
-
'@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))':
2569
3105
dependencies:
2570
-
'@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))
2571
3107
debug: 4.4.3
2572
3108
svelte: 5.45.8
2573
-
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)
2574
3110
transitivePeerDependencies:
2575
3111
- supports-color
2576
3112
2577
-
'@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))':
2578
3114
dependencies:
2579
-
'@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))
2580
3116
debug: 4.4.3
2581
3117
deepmerge: 4.3.1
2582
3118
magic-string: 0.30.21
2583
3119
svelte: 5.45.8
2584
-
vite: 7.2.7(@types/node@24.10.2)
2585
-
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))
2586
3122
transitivePeerDependencies:
2587
3123
- supports-color
2588
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
+
2589
3193
'@types/better-sqlite3@7.6.13':
2590
3194
dependencies:
2591
3195
'@types/node': 24.10.2
3196
+
optional: true
2592
3197
2593
3198
'@types/cookie@0.6.0': {}
2594
3199
···
2600
3205
dependencies:
2601
3206
undici-types: 7.16.0
2602
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
+
2603
3214
'@types/resolve@1.20.2': {}
2604
3215
2605
-
'@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)':
2606
3217
dependencies:
2607
3218
'@eslint-community/regexpp': 4.12.2
2608
-
'@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)
2609
3220
'@typescript-eslint/scope-manager': 8.49.0
2610
-
'@typescript-eslint/type-utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3)
2611
-
'@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)
2612
3223
'@typescript-eslint/visitor-keys': 8.49.0
2613
-
eslint: 9.39.1
3224
+
eslint: 9.39.1(jiti@2.6.1)
2614
3225
ignore: 7.0.5
2615
3226
natural-compare: 1.4.0
2616
3227
ts-api-utils: 2.1.0(typescript@5.9.3)
···
2618
3229
transitivePeerDependencies:
2619
3230
- supports-color
2620
3231
2621
-
'@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)':
2622
3233
dependencies:
2623
3234
'@typescript-eslint/scope-manager': 8.49.0
2624
3235
'@typescript-eslint/types': 8.49.0
2625
3236
'@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3)
2626
3237
'@typescript-eslint/visitor-keys': 8.49.0
2627
3238
debug: 4.4.3
2628
-
eslint: 9.39.1
3239
+
eslint: 9.39.1(jiti@2.6.1)
2629
3240
typescript: 5.9.3
2630
3241
transitivePeerDependencies:
2631
3242
- supports-color
···
2648
3259
dependencies:
2649
3260
typescript: 5.9.3
2650
3261
2651
-
'@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)':
2652
3263
dependencies:
2653
3264
'@typescript-eslint/types': 8.49.0
2654
3265
'@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3)
2655
-
'@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)
2656
3267
debug: 4.4.3
2657
-
eslint: 9.39.1
3268
+
eslint: 9.39.1(jiti@2.6.1)
2658
3269
ts-api-utils: 2.1.0(typescript@5.9.3)
2659
3270
typescript: 5.9.3
2660
3271
transitivePeerDependencies:
···
2677
3288
transitivePeerDependencies:
2678
3289
- supports-color
2679
3290
2680
-
'@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)':
2681
3292
dependencies:
2682
-
'@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))
2683
3294
'@typescript-eslint/scope-manager': 8.49.0
2684
3295
'@typescript-eslint/types': 8.49.0
2685
3296
'@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3)
2686
-
eslint: 9.39.1
3297
+
eslint: 9.39.1(jiti@2.6.1)
2687
3298
typescript: 5.9.3
2688
3299
transitivePeerDependencies:
2689
3300
- supports-color
···
2762
3373
dependencies:
2763
3374
bindings: 1.5.0
2764
3375
prebuild-install: 7.1.3
3376
+
optional: true
2765
3377
2766
3378
bindings@1.5.0:
2767
3379
dependencies:
2768
3380
file-uri-to-path: 1.0.0
3381
+
optional: true
2769
3382
2770
3383
bl@4.1.0:
2771
3384
dependencies:
2772
3385
buffer: 5.7.1
2773
3386
inherits: 2.0.4
2774
3387
readable-stream: 3.6.2
3388
+
optional: true
2775
3389
2776
3390
brace-expansion@1.1.12:
2777
3391
dependencies:
···
2788
3402
dependencies:
2789
3403
base64-js: 1.5.1
2790
3404
ieee754: 1.2.1
3405
+
optional: true
2791
3406
2792
3407
buffer@6.0.3:
2793
3408
dependencies:
···
2805
3420
dependencies:
2806
3421
readdirp: 4.1.2
2807
3422
2808
-
chownr@1.1.4: {}
3423
+
chownr@1.1.4:
3424
+
optional: true
2809
3425
2810
3426
clsx@2.1.1: {}
2811
3427
···
2835
3451
2836
3452
cssesc@3.0.0: {}
2837
3453
3454
+
daisyui@5.5.14: {}
3455
+
2838
3456
debug@4.4.3:
2839
3457
dependencies:
2840
3458
ms: 2.1.3
···
2842
3460
decompress-response@6.0.0:
2843
3461
dependencies:
2844
3462
mimic-response: 3.1.0
3463
+
optional: true
2845
3464
2846
-
deep-extend@0.6.0: {}
3465
+
deep-extend@0.6.0:
3466
+
optional: true
2847
3467
2848
3468
deep-is@0.1.4: {}
2849
3469
···
2864
3484
transitivePeerDependencies:
2865
3485
- supports-color
2866
3486
2867
-
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):
2868
3488
optionalDependencies:
2869
3489
'@types/better-sqlite3': 7.6.13
3490
+
'@types/pg': 8.16.0
2870
3491
better-sqlite3: 12.4.1
3492
+
pg: 8.16.3
2871
3493
2872
3494
end-of-stream@1.4.5:
2873
3495
dependencies:
2874
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
2875
3503
2876
3504
esbuild-register@3.6.0(esbuild@0.25.12):
2877
3505
dependencies:
···
2934
3562
'@esbuild/win32-ia32': 0.25.12
2935
3563
'@esbuild/win32-x64': 0.25.12
2936
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
+
2937
3594
escape-string-regexp@4.0.0: {}
2938
3595
2939
-
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):
2940
3597
dependencies:
2941
-
'@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))
2942
3599
'@jridgewell/sourcemap-codec': 1.5.5
2943
-
eslint: 9.39.1
3600
+
eslint: 9.39.1(jiti@2.6.1)
2944
3601
esutils: 2.0.3
2945
3602
globals: 16.5.0
2946
3603
known-css-properties: 0.37.0
···
2963
3620
2964
3621
eslint-visitor-keys@4.2.1: {}
2965
3622
2966
-
eslint@9.39.1:
3623
+
eslint@9.39.1(jiti@2.6.1):
2967
3624
dependencies:
2968
-
'@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))
2969
3626
'@eslint-community/regexpp': 4.12.2
2970
3627
'@eslint/config-array': 0.21.1
2971
3628
'@eslint/config-helpers': 0.4.2
···
2999
3656
minimatch: 3.1.2
3000
3657
natural-compare: 1.4.0
3001
3658
optionator: 0.9.4
3659
+
optionalDependencies:
3660
+
jiti: 2.6.1
3002
3661
transitivePeerDependencies:
3003
3662
- supports-color
3004
3663
···
3032
3691
3033
3692
events@3.3.0: {}
3034
3693
3035
-
expand-template@2.0.3: {}
3694
+
expand-template@2.0.3:
3695
+
optional: true
3036
3696
3037
3697
fast-deep-equal@3.1.3: {}
3038
3698
···
3050
3710
dependencies:
3051
3711
flat-cache: 4.0.1
3052
3712
3053
-
file-uri-to-path@1.0.0: {}
3713
+
file-uri-to-path@1.0.0:
3714
+
optional: true
3054
3715
3055
3716
find-up@5.0.0:
3056
3717
dependencies:
···
3064
3725
3065
3726
flatted@3.3.3: {}
3066
3727
3067
-
fs-constants@1.0.0: {}
3728
+
fs-constants@1.0.0:
3729
+
optional: true
3068
3730
3069
3731
fsevents@2.3.3:
3070
3732
optional: true
···
3075
3737
dependencies:
3076
3738
resolve-pkg-maps: 1.0.0
3077
3739
3078
-
github-from-package@0.0.0: {}
3740
+
github-from-package@0.0.0:
3741
+
optional: true
3079
3742
3080
3743
glob-parent@6.0.2:
3081
3744
dependencies:
···
3085
3748
3086
3749
globals@16.5.0: {}
3087
3750
3751
+
graceful-fs@4.2.11: {}
3752
+
3088
3753
has-flag@4.0.0: {}
3089
3754
3090
3755
hasown@2.0.2:
···
3104
3769
3105
3770
imurmurhash@0.1.4: {}
3106
3771
3107
-
inherits@2.0.4: {}
3772
+
inherits@2.0.4:
3773
+
optional: true
3108
3774
3109
-
ini@1.3.8: {}
3775
+
ini@1.3.8:
3776
+
optional: true
3110
3777
3111
3778
ipaddr.js@2.3.0: {}
3112
3779
···
3134
3801
3135
3802
iso-datestring-validator@2.2.2: {}
3136
3803
3804
+
jiti@2.6.1: {}
3805
+
3137
3806
jose@5.10.0: {}
3138
3807
3139
3808
js-yaml@4.1.1:
···
3159
3828
prelude-ls: 1.2.1
3160
3829
type-check: 0.4.0
3161
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
+
3162
3880
lilconfig@2.1.0: {}
3163
3881
3164
3882
locate-character@3.0.0: {}
···
3181
3899
dependencies:
3182
3900
'@jridgewell/sourcemap-codec': 1.5.5
3183
3901
3184
-
mimic-response@3.1.0: {}
3902
+
mimic-response@3.1.0:
3903
+
optional: true
3185
3904
3186
3905
minimatch@3.1.2:
3187
3906
dependencies:
···
3191
3910
dependencies:
3192
3911
brace-expansion: 2.0.2
3193
3912
3194
-
minimist@1.2.8: {}
3913
+
minimist@1.2.8:
3914
+
optional: true
3195
3915
3196
-
mkdirp-classic@0.5.3: {}
3916
+
mkdirp-classic@0.5.3:
3917
+
optional: true
3197
3918
3198
3919
mri@1.2.0: {}
3199
3920
···
3205
3926
3206
3927
nanoid@3.3.11: {}
3207
3928
3208
-
napi-build-utils@2.0.0: {}
3929
+
napi-build-utils@2.0.0:
3930
+
optional: true
3209
3931
3210
3932
natural-compare@1.4.0: {}
3211
3933
3212
3934
node-abi@3.85.0:
3213
3935
dependencies:
3214
3936
semver: 7.7.3
3937
+
optional: true
3215
3938
3216
3939
node-schedule@2.1.1:
3217
3940
dependencies:
···
3224
3947
once@1.4.0:
3225
3948
dependencies:
3226
3949
wrappy: 1.0.2
3950
+
optional: true
3227
3951
3228
3952
optionator@0.9.4:
3229
3953
dependencies:
···
3252
3976
3253
3977
path-parse@1.0.7: {}
3254
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
+
3255
4014
picocolors@1.1.1: {}
3256
4015
3257
4016
picomatch@4.0.3: {}
···
3323
4082
picocolors: 1.1.1
3324
4083
source-map-js: 1.2.1
3325
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
+
3326
4095
prebuild-install@7.1.3:
3327
4096
dependencies:
3328
4097
detect-libc: 2.1.2
···
3337
4106
simple-get: 4.0.1
3338
4107
tar-fs: 2.1.4
3339
4108
tunnel-agent: 0.6.0
4109
+
optional: true
3340
4110
3341
4111
prelude-ls@1.2.1: {}
3342
4112
···
3365
4135
dependencies:
3366
4136
end-of-stream: 1.4.5
3367
4137
once: 1.4.0
4138
+
optional: true
3368
4139
3369
4140
punycode@2.3.1: {}
3370
4141
···
3376
4147
ini: 1.3.8
3377
4148
minimist: 1.2.8
3378
4149
strip-json-comments: 2.0.1
4150
+
optional: true
3379
4151
3380
4152
readable-stream@3.6.2:
3381
4153
dependencies:
3382
4154
inherits: 2.0.4
3383
4155
string_decoder: 1.3.0
3384
4156
util-deprecate: 1.0.2
4157
+
optional: true
3385
4158
3386
4159
readable-stream@4.7.0:
3387
4160
dependencies:
···
3451
4224
3452
4225
shebang-regex@3.0.0: {}
3453
4226
3454
-
simple-concat@1.0.1: {}
4227
+
simple-concat@1.0.1:
4228
+
optional: true
3455
4229
3456
4230
simple-get@4.0.1:
3457
4231
dependencies:
3458
4232
decompress-response: 6.0.0
3459
4233
once: 1.4.0
3460
4234
simple-concat: 1.0.1
4235
+
optional: true
3461
4236
3462
4237
sirv@3.0.2:
3463
4238
dependencies:
···
3490
4265
dependencies:
3491
4266
safe-buffer: 5.2.1
3492
4267
3493
-
strip-json-comments@2.0.1: {}
4268
+
strip-json-comments@2.0.1:
4269
+
optional: true
3494
4270
3495
4271
strip-json-comments@3.1.1: {}
3496
4272
···
3540
4316
locate-character: 3.0.0
3541
4317
magic-string: 0.30.21
3542
4318
zimmerframe: 1.1.4
4319
+
4320
+
tailwindcss@4.1.18: {}
4321
+
4322
+
tapable@2.3.0: {}
3543
4323
3544
4324
tar-fs@2.1.4:
3545
4325
dependencies:
···
3547
4327
mkdirp-classic: 0.5.3
3548
4328
pump: 3.0.3
3549
4329
tar-stream: 2.2.0
4330
+
optional: true
3550
4331
3551
4332
tar-stream@2.2.0:
3552
4333
dependencies:
···
3555
4336
fs-constants: 1.0.0
3556
4337
inherits: 2.0.4
3557
4338
readable-stream: 3.6.2
4339
+
optional: true
3558
4340
3559
4341
thread-stream@2.7.0:
3560
4342
dependencies:
···
3579
4361
3580
4362
tslib@2.8.1: {}
3581
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
+
3582
4371
tunnel-agent@0.6.0:
3583
4372
dependencies:
3584
4373
safe-buffer: 5.2.1
4374
+
optional: true
3585
4375
3586
4376
type-check@0.4.0:
3587
4377
dependencies:
3588
4378
prelude-ls: 1.2.1
3589
4379
3590
-
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):
3591
4381
dependencies:
3592
-
'@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)
3593
-
'@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)
3594
4384
'@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3)
3595
-
'@typescript-eslint/utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3)
3596
-
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)
3597
4387
typescript: 5.9.3
3598
4388
transitivePeerDependencies:
3599
4389
- supports-color
···
3616
4406
3617
4407
util-deprecate@1.0.2: {}
3618
4408
3619
-
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):
3620
4410
dependencies:
3621
4411
esbuild: 0.25.12
3622
4412
fdir: 6.5.0(picomatch@4.0.3)
···
3627
4417
optionalDependencies:
3628
4418
'@types/node': 24.10.2
3629
4419
fsevents: 2.3.3
4420
+
jiti: 2.6.1
4421
+
lightningcss: 1.30.2
4422
+
tsx: 4.21.0
3630
4423
3631
-
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)):
3632
4425
optionalDependencies:
3633
-
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)
3634
4427
3635
4428
which@2.0.2:
3636
4429
dependencies:
···
3638
4431
3639
4432
word-wrap@1.2.5: {}
3640
4433
3641
-
wrappy@1.0.2: {}
4434
+
wrappy@1.0.2:
4435
+
optional: true
4436
+
4437
+
ws@8.18.3: {}
4438
+
4439
+
xtend@4.0.2: {}
3642
4440
3643
4441
yaml@1.10.2: {}
3644
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>
+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
-
});
+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>
+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] : []