+13
.changeset/plain-beans-taste.md
+13
.changeset/plain-beans-taste.md
···
1
+
---
2
+
"@nulfrost/leaflet-loader-astro": minor
3
+
---
4
+
5
+
Added support for these leaflet blocks:
6
+
7
+
- ul/li
8
+
- math
9
+
- code
10
+
- img
11
+
- hr
12
+
13
+
the only remaining block to implement is "website", though I haven't thought of a good way to output that yet. stay tuned for a further release
+1
-1
lex.config.js
+1
-1
lex.config.js
+11
lexicons/pub/leaflet/blocks/horizontalRule.json
+11
lexicons/pub/leaflet/blocks/horizontalRule.json
+13
-15
lib/leaflet-live-loader.ts
+13
-15
lib/leaflet-live-loader.ts
···
1
-
import { Agent } from "@atproto/api";
2
1
import { isDid } from "@atproto/did";
3
2
import type { LiveLoader } from "astro/loaders";
4
3
import type {
···
17
16
resolveMiniDoc,
18
17
uriToRkey,
19
18
} from "./utils.js";
19
+
import { Client, simpleFetchHandler } from "@atcute/client";
20
20
21
21
export function leafletLiveLoader(
22
22
options: LiveLeafletLoaderOptions,
···
44
44
name: "leaflet-loader-astro",
45
45
loadCollection: async ({ filter }) => {
46
46
try {
47
-
const pds_url = await resolveMiniDoc(repo);
48
-
const agent = new Agent({ service: pds_url });
47
+
const { pds, did } = await resolveMiniDoc(repo);
48
+
const handler = simpleFetchHandler({ service: pds });
49
+
const rpc = new Client({ handler });
49
50
50
51
const { documents } = await getLeafletDocuments({
51
-
agent,
52
+
rpc,
52
53
repo,
53
54
reverse: filter?.reverse,
54
55
cursor: filter?.cursor,
···
67
68
}),
68
69
rendered: {
69
70
html: leafletBlocksToHTML({
70
-
id,
71
-
uri: document.uri,
72
-
cid: document.cid,
73
-
value: document.value as unknown as LeafletDocumentRecord,
71
+
record: document.value as unknown as LeafletDocumentRecord,
72
+
did,
74
73
}),
75
74
},
76
75
};
···
95
94
};
96
95
}
97
96
try {
98
-
const pds_url = await resolveMiniDoc(repo);
99
-
const agent = new Agent({ service: pds_url });
97
+
const { pds, did } = await resolveMiniDoc(repo);
98
+
const handler = simpleFetchHandler({ service: pds });
99
+
const rpc = new Client({ handler });
100
100
const document = await getSingleLeafletDocument({
101
-
agent,
101
+
rpc,
102
102
id: filter.id,
103
103
repo,
104
104
});
···
114
114
}),
115
115
rendered: {
116
116
html: leafletBlocksToHTML({
117
-
id: filter.id,
118
-
uri: document.uri,
119
-
cid,
120
-
value: document.value as unknown as LeafletDocumentRecord,
117
+
record: document.value as unknown as LeafletDocumentRecord,
118
+
did,
121
119
}),
122
120
},
123
121
};
+10
-11
lib/leaftlet-static-loader.ts
+10
-11
lib/leaftlet-static-loader.ts
···
1
-
import { Agent } from "@atproto/api";
1
+
import { Client, simpleFetchHandler } from "@atcute/client";
2
2
import { isDid } from "@atproto/did";
3
3
import type { Loader, LoaderContext } from "astro/loaders";
4
4
import { LeafletDocumentSchema } from "schema.js";
···
7
7
StaticLeafletLoaderOptions,
8
8
} from "types.js";
9
9
import {
10
+
getLeafletDocuments,
11
+
leafletBlocksToHTML,
12
+
leafletDocumentRecordToView,
10
13
LiveLoaderError,
11
14
resolveMiniDoc,
12
-
getLeafletDocuments,
13
15
uriToRkey,
14
-
leafletDocumentRecordToView,
15
-
leafletBlocksToHTML,
16
16
} from "utils.js";
17
17
18
18
export function leafletStaticLoader(
···
43
43
}: LoaderContext) => {
44
44
try {
45
45
logger.info("fetching latest leaflet documents");
46
-
const pds_url = await resolveMiniDoc(repo);
47
-
const agent = new Agent({ service: pds_url });
46
+
const { pds, did } = await resolveMiniDoc(repo);
47
+
const handler = simpleFetchHandler({ service: pds });
48
+
const rpc = new Client({ handler });
48
49
49
50
let cursor: string | undefined;
50
51
let count = 0;
···
52
53
fetching: do {
53
54
const { documents, cursor: documentsCursor } =
54
55
await getLeafletDocuments({
55
-
agent,
56
+
rpc,
56
57
repo,
57
58
cursor,
58
59
limit: 100,
···
83
84
digest,
84
85
rendered: {
85
86
html: leafletBlocksToHTML({
86
-
id,
87
-
uri: document.uri,
88
-
cid: document.cid,
89
-
value: document.value as unknown as LeafletDocumentRecord,
87
+
record: document.value as unknown as LeafletDocumentRecord,
88
+
did,
90
89
}),
91
90
},
92
91
});
+1
lib/lexicons/index.ts
+1
lib/lexicons/index.ts
···
1
1
export * as ComAtprotoRepoStrongRef from "./types/com/atproto/repo/strongRef.js";
2
2
export * as PubLeafletBlocksCode from "./types/pub/leaflet/blocks/code.js";
3
3
export * as PubLeafletBlocksHeader from "./types/pub/leaflet/blocks/header.js";
4
+
export * as PubLeafletBlocksHorizontalRule from "./types/pub/leaflet/blocks/horizontalRule.js";
4
5
export * as PubLeafletBlocksImage from "./types/pub/leaflet/blocks/image.js";
5
6
export * as PubLeafletBlocksMath from "./types/pub/leaflet/blocks/math.js";
6
7
export * as PubLeafletBlocksText from "./types/pub/leaflet/blocks/text.js";
+16
lib/lexicons/types/pub/leaflet/blocks/horizontalRule.ts
+16
lib/lexicons/types/pub/leaflet/blocks/horizontalRule.ts
···
1
+
import type {} from "@atcute/lexicons";
2
+
import * as v from "@atcute/lexicons/validations";
3
+
4
+
const _mainSchema = /*#__PURE__*/ v.object({
5
+
$type: /*#__PURE__*/ v.optional(
6
+
/*#__PURE__*/ v.literal("pub.leaflet.blocks.horizontalRule"),
7
+
),
8
+
});
9
+
10
+
type main$schematype = typeof _mainSchema;
11
+
12
+
export interface mainSchema extends main$schematype {}
13
+
14
+
export const mainSchema = _mainSchema as mainSchema;
15
+
16
+
export interface Main extends v.InferInput<typeof mainSchema> {}
+1
-1
lib/lexicons/types/pub/leaflet/blocks/image.ts
+1
-1
lib/lexicons/types/pub/leaflet/blocks/image.ts
+7
-5
lib/types.ts
+7
-5
lib/types.ts
···
1
-
import type { Agent } from "@atproto/api";
1
+
import type { Client } from "@atcute/client";
2
+
import type { ActorIdentifier } from "@atcute/lexicons";
3
+
import type { XRPCProcedures, XRPCQueries } from "@atcute/lexicons/ambient";
2
4
import type { PubLeafletRichtextFacet } from "./lexicons/index.js";
3
5
4
6
export interface LiveLeafletLoaderOptions {
···
58
60
}
59
61
60
62
export interface GetLeafletDocumentsParams {
61
-
repo: string;
62
-
agent: Agent;
63
+
repo: ActorIdentifier;
64
+
rpc: Client<XRPCQueries, XRPCProcedures>;
63
65
cursor?: string;
64
66
limit?: number;
65
67
reverse?: boolean;
66
68
}
67
69
68
70
export interface GetSingleLeafletDocumentParams {
69
-
repo: string;
70
-
agent: Agent;
71
+
repo: ActorIdentifier;
72
+
rpc: Client<XRPCQueries, XRPCProcedures>;
71
73
id: string;
72
74
}
73
75
+187
-107
lib/utils.ts
+187
-107
lib/utils.ts
···
1
+
import type {} from "@atcute/atproto";
1
2
import { is } from "@atcute/lexicons";
2
3
import { AtUri, UnicodeString } from "@atproto/api";
4
+
import katex from "katex";
3
5
import sanitizeHTML from "sanitize-html";
4
6
import {
7
+
PubLeafletBlocksCode,
5
8
PubLeafletBlocksHeader,
9
+
PubLeafletBlocksHorizontalRule,
10
+
PubLeafletBlocksImage,
11
+
PubLeafletBlocksMath,
6
12
PubLeafletBlocksText,
13
+
PubLeafletBlocksUnorderedList,
7
14
PubLeafletPagesLinearDocument,
8
15
} from "./lexicons/index.js";
9
16
import type {
···
29
36
export function uriToRkey(uri: string): string {
30
37
const u = AtUri.make(uri);
31
38
if (!u.rkey) {
32
-
throw new Error("Failed to get rkey from uri.");
39
+
throw new Error("failed to get rkey");
33
40
}
34
41
return u.rkey;
35
42
}
···
47
54
}
48
55
const data = (await response.json()) as MiniDoc;
49
56
50
-
return data.pds;
57
+
return {
58
+
pds: data.pds,
59
+
did: data.did,
60
+
};
51
61
} catch {
52
62
throw new Error(`failed to resolve handle: ${handleOrDid}`);
53
63
}
···
57
67
repo,
58
68
reverse,
59
69
cursor,
60
-
agent,
70
+
rpc,
61
71
limit,
62
72
}: GetLeafletDocumentsParams) {
63
-
const response = await agent.com.atproto.repo.listRecords({
64
-
repo,
65
-
collection: "pub.leaflet.document",
66
-
cursor,
67
-
reverse,
68
-
limit,
73
+
const { ok, data } = await rpc.get("com.atproto.repo.listRecords", {
74
+
params: {
75
+
collection: "pub.leaflet.document",
76
+
cursor,
77
+
reverse,
78
+
limit,
79
+
repo,
80
+
},
69
81
});
70
82
71
-
if (response.success === false) {
83
+
if (!ok) {
72
84
throw new LiveLoaderError(
73
85
"error fetching leaflet documents",
74
86
"DOCUMENT_FETCH_ERROR",
···
76
88
}
77
89
78
90
return {
79
-
documents: response?.data?.records,
80
-
cursor: response?.data?.cursor,
91
+
documents: data?.records,
92
+
cursor: data?.cursor,
81
93
};
82
94
}
83
95
84
96
export async function getSingleLeafletDocument({
85
-
agent,
97
+
rpc,
86
98
repo,
87
99
id,
88
100
}: GetSingleLeafletDocumentParams) {
89
-
const response = await agent.com.atproto.repo.getRecord({
90
-
repo,
91
-
collection: "pub.leaflet.document",
92
-
rkey: id,
101
+
const { ok, data } = await rpc.get("com.atproto.repo.getRecord", {
102
+
params: {
103
+
collection: "pub.leaflet.document",
104
+
repo,
105
+
rkey: id,
106
+
},
93
107
});
94
108
95
-
if (response.success === false) {
109
+
if (!ok) {
96
110
throw new LiveLoaderError(
97
111
"error fetching single document",
98
112
"DOCUMENT_FETCH_ERROR",
99
113
);
100
114
}
101
115
102
-
return response?.data;
116
+
return data;
103
117
}
104
118
105
119
export function leafletDocumentRecordToView({
···
122
136
};
123
137
}
124
138
125
-
export function leafletBlocksToHTML(record: {
126
-
id: string;
127
-
uri: string;
128
-
cid: string;
129
-
value: LeafletDocumentRecord;
139
+
export function leafletBlocksToHTML({
140
+
record,
141
+
did,
142
+
}: {
143
+
record: LeafletDocumentRecord;
144
+
did: string;
130
145
}) {
131
146
let html = "";
132
-
const firstPage = record.value.pages[0];
147
+
const firstPage = record.pages[0];
133
148
let blocks: PubLeafletPagesLinearDocument.Block[] = [];
149
+
134
150
if (is(PubLeafletPagesLinearDocument.mainSchema, firstPage)) {
135
151
blocks = firstPage.blocks || [];
136
152
}
137
153
138
154
for (const block of blocks) {
139
-
if (is(PubLeafletBlocksText.mainSchema, block.block)) {
140
-
const rt = new RichText({
141
-
text: block.block.plaintext,
142
-
facets: block.block.facets || [],
143
-
});
144
-
const children = [];
145
-
for (const segment of rt.segments()) {
146
-
const link = segment.facet?.find(
147
-
(segment) => segment.$type === "pub.leaflet.richtext.facet#link",
148
-
);
149
-
const isBold = segment.facet?.find(
150
-
(segment) => segment.$type === "pub.leaflet.richtext.facet#bold",
151
-
);
152
-
const isCode = segment.facet?.find(
153
-
(segment) => segment.$type === "pub.leaflet.richtext.facet#code",
154
-
);
155
-
const isStrikethrough = segment.facet?.find(
156
-
(segment) =>
157
-
segment.$type === "pub.leaflet.richtext.facet#strikethrough",
158
-
);
159
-
const isUnderline = segment.facet?.find(
160
-
(segment) => segment.$type === "pub.leaflet.richtext.facet#underline",
161
-
);
162
-
const isItalic = segment.facet?.find(
163
-
(segment) => segment.$type === "pub.leaflet.richtext.facet#italic",
164
-
);
165
-
if (isCode) {
166
-
children.push(` <code>
167
-
${segment.text}
168
-
</code>`);
169
-
} else if (link) {
170
-
children.push(
171
-
` <a
172
-
href="${link.uri}"
173
-
target="_blank"
174
-
>
175
-
${segment.text}
176
-
</a>`,
177
-
);
178
-
} else if (isBold) {
179
-
children.push(`<b>${segment.text}</b>`);
180
-
} else if (isStrikethrough) {
181
-
children.push(`<s>${segment.text}</s>`);
182
-
} else if (isUnderline) {
183
-
children.push(
184
-
`<span style="text-decoration:underline;">${segment.text}</span>`,
185
-
);
186
-
} else if (isItalic) {
187
-
children.push(`<i>${segment.text}</i>`);
188
-
} else {
189
-
children.push(
190
-
`
191
-
${segment.text}
192
-
`,
193
-
);
194
-
}
195
-
}
196
-
html += `<p>${children.join("\n")}</p>`;
197
-
}
198
-
199
-
if (is(PubLeafletBlocksHeader.mainSchema, block.block)) {
200
-
if (block.block.level === 1) {
201
-
html += `<h2>${block.block.plaintext}</h2>`;
202
-
}
203
-
}
204
-
if (is(PubLeafletBlocksHeader.mainSchema, block.block)) {
205
-
if (block.block.level === 2) {
206
-
html += `<h3>${block.block.plaintext}</h3>`;
207
-
}
208
-
}
209
-
if (is(PubLeafletBlocksHeader.mainSchema, block.block)) {
210
-
if (block.block.level === 3) {
211
-
html += `<h4>${block.block.plaintext}</h4>`;
212
-
}
213
-
}
214
-
if (is(PubLeafletBlocksHeader.mainSchema, block.block)) {
215
-
if (!block.block.level) {
216
-
html += `<h6>${block.block.plaintext}</h6>`;
217
-
}
218
-
}
155
+
html += parseBlocks({ block, did });
219
156
}
220
157
221
-
return sanitizeHTML(html);
158
+
return sanitizeHTML(html, {
159
+
allowedAttributes: {
160
+
"*": ["class", "style"],
161
+
img: ["src", "height", "width", "alt"],
162
+
a: ["href", "target", "rel"],
163
+
},
164
+
allowedTags: [
165
+
"img",
166
+
"pre",
167
+
"code",
168
+
"p",
169
+
"a",
170
+
"b",
171
+
"s",
172
+
"ul",
173
+
"li",
174
+
"i",
175
+
"h1",
176
+
"h2",
177
+
"h3",
178
+
"h4",
179
+
"h5",
180
+
"h6",
181
+
"hr",
182
+
"div",
183
+
"span",
184
+
],
185
+
selfClosing: ["img"],
186
+
});
222
187
}
223
188
224
189
export class RichText {
225
190
unicodeText: UnicodeString;
226
191
facets?: Facet[];
227
-
228
192
constructor(props: { text: string; facets: Facet[] }) {
229
193
this.unicodeText = new UnicodeString(props.text);
230
194
this.facets = props.facets;
···
278
242
}
279
243
}
280
244
}
245
+
246
+
function parseBlocks({
247
+
block,
248
+
did,
249
+
}: {
250
+
block: PubLeafletPagesLinearDocument.Block;
251
+
did: string;
252
+
}): string {
253
+
let html = "";
254
+
255
+
if (is(PubLeafletBlocksText.mainSchema, block.block)) {
256
+
const rt = new RichText({
257
+
text: block.block.plaintext,
258
+
facets: block.block.facets || [],
259
+
});
260
+
const children = [];
261
+
for (const segment of rt.segments()) {
262
+
const link = segment.facet?.find(
263
+
(segment) => segment.$type === "pub.leaflet.richtext.facet#link",
264
+
);
265
+
const isBold = segment.facet?.find(
266
+
(segment) => segment.$type === "pub.leaflet.richtext.facet#bold",
267
+
);
268
+
const isCode = segment.facet?.find(
269
+
(segment) => segment.$type === "pub.leaflet.richtext.facet#code",
270
+
);
271
+
const isStrikethrough = segment.facet?.find(
272
+
(segment) =>
273
+
segment.$type === "pub.leaflet.richtext.facet#strikethrough",
274
+
);
275
+
const isUnderline = segment.facet?.find(
276
+
(segment) => segment.$type === "pub.leaflet.richtext.facet#underline",
277
+
);
278
+
const isItalic = segment.facet?.find(
279
+
(segment) => segment.$type === "pub.leaflet.richtext.facet#italic",
280
+
);
281
+
if (isCode) {
282
+
children.push(`<pre><code>${segment.text}</code></pre>`);
283
+
} else if (link) {
284
+
children.push(
285
+
`<a href="${link.uri}" target="_blank" rel="noopener noreferrer">${segment.text}</a>`,
286
+
);
287
+
} else if (isBold) {
288
+
children.push(`<b>${segment.text}</b>`);
289
+
} else if (isStrikethrough) {
290
+
children.push(`<s>${segment.text}</s>`);
291
+
} else if (isUnderline) {
292
+
children.push(
293
+
`<span style="text-decoration:underline;">${segment.text}</span>`,
294
+
);
295
+
} else if (isItalic) {
296
+
children.push(`<i>${segment.text}</i>`);
297
+
} else {
298
+
children.push(`${segment.text}`);
299
+
}
300
+
}
301
+
html += `<p>${children.join("")}</p>`;
302
+
}
303
+
304
+
if (is(PubLeafletBlocksHeader.mainSchema, block.block)) {
305
+
if (block.block.level === 1) {
306
+
html += `<h2>${block.block.plaintext}</h2>`;
307
+
}
308
+
}
309
+
if (is(PubLeafletBlocksHeader.mainSchema, block.block)) {
310
+
if (block.block.level === 2) {
311
+
html += `<h3>${block.block.plaintext}</h3>`;
312
+
}
313
+
}
314
+
if (is(PubLeafletBlocksHeader.mainSchema, block.block)) {
315
+
if (block.block.level === 3) {
316
+
html += `<h4>${block.block.plaintext}</h4>`;
317
+
}
318
+
}
319
+
if (is(PubLeafletBlocksHeader.mainSchema, block.block)) {
320
+
if (!block.block.level) {
321
+
html += `<h6>${block.block.plaintext}</h6>`;
322
+
}
323
+
}
324
+
325
+
if (is(PubLeafletBlocksHorizontalRule.mainSchema, block.block)) {
326
+
html += `<hr />`;
327
+
}
328
+
if (is(PubLeafletBlocksUnorderedList.mainSchema, block.block)) {
329
+
html += `<ul>${block.block.children.map((child) => renderListItem({ item: child, did })).join("")}</ul>`;
330
+
}
331
+
332
+
if (is(PubLeafletBlocksMath.mainSchema, block.block)) {
333
+
html += `<div>${katex.renderToString(block.block.tex, { displayMode: true, output: "html", throwOnError: false })}</div>`;
334
+
}
335
+
336
+
if (is(PubLeafletBlocksCode.mainSchema, block.block)) {
337
+
html += `<pre><code data-language=${block.block.language}>${block.block.plaintext}</code></pre>`;
338
+
}
339
+
340
+
if (is(PubLeafletBlocksImage.mainSchema, block.block)) {
341
+
// @ts-ignore
342
+
html += `<div><img src="https://cdn.bsky.app/img/feed_fullsize/plain/${did}/${block.block.image.ref.$link}@jpeg" height="${block.block.aspectRatio.height}" width="${block.block.aspectRatio.width}" alt="${block.block.alt}" /></div>`;
343
+
}
344
+
345
+
return html.trim();
346
+
}
347
+
348
+
function renderListItem({
349
+
item,
350
+
did,
351
+
}: {
352
+
item: PubLeafletBlocksUnorderedList.ListItem;
353
+
did: string;
354
+
}): string {
355
+
const children: string | null = item.children?.length
356
+
? `<ul>${item.children.map((child) => renderListItem({ item: child, did }))}</ul>`
357
+
: "";
358
+
359
+
return `<li>${parseBlocks({ block: { block: item.content }, did })}${children}</li>`;
360
+
}
+6
-2
package.json
+6
-2
package.json
···
25
25
"typecheck": "tsc",
26
26
"release": "pnpm run build && changeset publish",
27
27
"build": "rm -rf dist && tsup --format esm --dts",
28
-
"pack": "pnpm build && pnpm pack"
28
+
"pack": "rm -rf *.tgz && pnpm build && pnpm pack"
29
29
},
30
30
"license": "MIT",
31
31
"files": [
···
45
45
}
46
46
},
47
47
"devDependencies": {
48
+
"@atcute/atproto": "^3.1.1",
48
49
"@atcute/lex-cli": "^2.1.1",
49
50
"@biomejs/biome": "2.1.3",
50
51
"@changesets/cli": "^2.29.5",
52
+
"@types/bun": "^1.2.19",
51
53
"@types/sanitize-html": "^2.16.0",
52
54
"astro": "^5.12.8",
53
55
"tsup": "^8.5.0",
···
55
57
"vitest": "^3.2.4"
56
58
},
57
59
"dependencies": {
60
+
"@atcute/client": "^4.0.3",
58
61
"@atcute/lexicons": "^1.1.0",
59
-
"@atproto/api": "^0.16.1",
62
+
"@atproto/api": "^0.16.2",
60
63
"@atproto/did": "^0.1.5",
64
+
"katex": "^0.16.22",
61
65
"sanitize-html": "^2.17.0"
62
66
}
63
67
}
+118
-38
pnpm-lock.yaml
+118
-38
pnpm-lock.yaml
···
8
8
9
9
.:
10
10
dependencies:
11
+
'@atcute/client':
12
+
specifier: ^4.0.3
13
+
version: 4.0.3
11
14
'@atcute/lexicons':
12
15
specifier: ^1.1.0
13
16
version: 1.1.0
14
17
'@atproto/api':
15
-
specifier: ^0.16.1
16
-
version: 0.16.1
18
+
specifier: ^0.16.2
19
+
version: 0.16.2
17
20
'@atproto/did':
18
21
specifier: ^0.1.5
19
22
version: 0.1.5
23
+
katex:
24
+
specifier: ^0.16.22
25
+
version: 0.16.22
20
26
sanitize-html:
21
27
specifier: ^2.17.0
22
28
version: 2.17.0
23
29
devDependencies:
30
+
'@atcute/atproto':
31
+
specifier: ^3.1.1
32
+
version: 3.1.1
24
33
'@atcute/lex-cli':
25
34
specifier: ^2.1.1
26
35
version: 2.1.1
···
30
39
'@changesets/cli':
31
40
specifier: ^2.29.5
32
41
version: 2.29.5
42
+
'@types/bun':
43
+
specifier: ^1.2.19
44
+
version: 1.2.19(@types/react@19.1.9)
33
45
'@types/sanitize-html':
34
46
specifier: ^2.16.0
35
47
version: 2.16.0
36
48
astro:
37
49
specifier: ^5.12.8
38
-
version: 5.12.8(@types/node@24.2.0)(rollup@4.46.2)(typescript@5.9.2)
50
+
version: 5.12.9(@types/node@24.2.1)(rollup@4.46.2)(typescript@5.9.2)
39
51
tsup:
40
52
specifier: ^8.5.0
41
53
version: 8.5.0(postcss@8.5.6)(typescript@5.9.2)
···
44
56
version: 5.9.2
45
57
vitest:
46
58
specifier: ^3.2.4
47
-
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.2.0)
59
+
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.2.1)
48
60
49
61
packages:
50
62
···
65
77
resolution: {integrity: sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==}
66
78
engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0}
67
79
80
+
'@atcute/atproto@3.1.1':
81
+
resolution: {integrity: sha512-D+RLTIPF0xLu7BPZY8KSewAPemJFh+3n3zeQ3ROsLxbTtCHbrTDMAmAFexaVRAPGcPYrwXaBUlv7yZjScJolMg==}
82
+
83
+
'@atcute/client@4.0.3':
84
+
resolution: {integrity: sha512-RIOZWFVLca/HiPAAUDqQPOdOreCxTbL5cb+WUf5yqQOKIu5yEAP3eksinmlLmgIrlr5qVOE7brazUUzaskFCfw==}
85
+
86
+
'@atcute/identity@1.0.3':
87
+
resolution: {integrity: sha512-mNMxbKHFGys03A8JXKk0KfMBzdd0vrYMzZZWjpw1nYTs0+ea6bo5S1hwqVUZxHdo1gFHSe/t63jxQIF4yL9aKw==}
88
+
68
89
'@atcute/lex-cli@2.1.1':
69
90
resolution: {integrity: sha512-QaR0sOP8Z24opGHKsSfleDbP/ahUb6HECkVaOqSwG7ORZzbLK1w0265o1BRjCVr2dT6FxlsMUa2Ge85JMA9bxg==}
70
91
hasBin: true
···
75
96
'@atcute/lexicons@1.1.0':
76
97
resolution: {integrity: sha512-LFqwnria78xLYb62Ri/+WwQpUTgZp2DuyolNGIIOV1dpiKhFFFh//nscHMA6IExFLQRqWDs3tTjy7zv0h3sf1Q==}
77
98
78
-
'@atproto/api@0.16.1':
79
-
resolution: {integrity: sha512-w48BlTmzKym7nZETWxgiuUX/wwRXU3xsLLKORWo/xtGnwlvpchUFnHKI3k4ttYJ2/JQE59+/4C16BaLzDyiU2w==}
99
+
'@atproto/api@0.16.2':
100
+
resolution: {integrity: sha512-sSTg31J8ws8DNaoiizp+/uJideRxRaJsq+Nyl8rnSxGw0w3oCvoeRU19iRWh2t0jZEmiRJAGkveGu23NKmPYEQ==}
80
101
81
102
'@atproto/common-web@0.4.2':
82
103
resolution: {integrity: sha512-vrXwGNoFGogodjQvJDxAeP3QbGtawgZute2ed1XdRO0wMixLk3qewtikZm06H259QDJVu6voKC5mubml+WgQUw==}
···
673
694
'@swc/helpers@0.5.17':
674
695
resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==}
675
696
697
+
'@types/bun@1.2.19':
698
+
resolution: {integrity: sha512-d9ZCmrH3CJ2uYKXQIUuZ/pUnTqIvLDS0SK7pFmbx8ma+ziH/FRMoAq5bYpRG7y+w1gl+HgyNZbtqgMq4W4e2Lg==}
699
+
676
700
'@types/chai@5.2.2':
677
701
resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==}
678
702
···
703
727
'@types/node@12.20.55':
704
728
resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==}
705
729
706
-
'@types/node@24.2.0':
707
-
resolution: {integrity: sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==}
730
+
'@types/node@24.2.1':
731
+
resolution: {integrity: sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==}
732
+
733
+
'@types/react@19.1.9':
734
+
resolution: {integrity: sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA==}
708
735
709
736
'@types/sanitize-html@2.16.0':
710
737
resolution: {integrity: sha512-l6rX1MUXje5ztPT0cAFtUayXF06DqPhRyfVXareEN5gGCFaP/iwsxIyKODr9XDhfxPpN6vXUFNfo5kZMXCxBtw==}
···
800
827
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
801
828
engines: {node: '>=12'}
802
829
803
-
astro@5.12.8:
804
-
resolution: {integrity: sha512-KkJ7FR+c2SyZYlpakm48XBiuQcRsrVtdjG5LN5an0givI/tLik+ePJ4/g3qrAVhYMjJOxBA2YgFQxANPiWB+Mw==}
830
+
astro@5.12.9:
831
+
resolution: {integrity: sha512-cZ7kZ61jyE5nwSrFKSRyf5Gds+uJELqQxJFqMkcgiWQvhWZJUSShn8Uz3yc9WLyLw5Kim5P5un9SkJSGogfEZQ==}
805
832
engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0'}
806
833
hasBin: true
807
834
···
845
872
brotli@1.3.3:
846
873
resolution: {integrity: sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==}
847
874
875
+
bun-types@1.2.19:
876
+
resolution: {integrity: sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ==}
877
+
peerDependencies:
878
+
'@types/react': ^19
879
+
848
880
bundle-require@5.1.0:
849
881
resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==}
850
882
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
···
931
963
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
932
964
engines: {node: '>= 6'}
933
965
966
+
commander@8.3.0:
967
+
resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
968
+
engines: {node: '>= 12'}
969
+
934
970
common-ancestor-path@1.0.1:
935
971
resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==}
936
972
···
966
1002
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
967
1003
engines: {node: '>=4'}
968
1004
hasBin: true
1005
+
1006
+
csstype@3.1.3:
1007
+
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
969
1008
970
1009
debug@4.4.1:
971
1010
resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
···
1337
1376
jsonfile@4.0.0:
1338
1377
resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
1339
1378
1379
+
katex@0.16.22:
1380
+
resolution: {integrity: sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==}
1381
+
hasBin: true
1382
+
1340
1383
kleur@3.0.3:
1341
1384
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
1342
1385
engines: {node: '>=6'}
···
1876
1919
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
1877
1920
engines: {node: '>=8'}
1878
1921
1879
-
smol-toml@1.4.1:
1880
-
resolution: {integrity: sha512-CxdwHXyYTONGHThDbq5XdwbFsuY4wlClRGejfE2NtwUtiHYsP1QtNsHb/hnj31jKYSchztJsaA8pSQoVzkfCFg==}
1922
+
smol-toml@1.4.2:
1923
+
resolution: {integrity: sha512-rInDH6lCNiEyn3+hH8KVGFdbjc099j47+OSgbMrfDYX1CmXLfdKd7qi6IfcWj2wFxvSVkuI46M+wPGYfEOEj6g==}
1881
1924
engines: {node: '>= 18'}
1882
1925
1883
1926
source-map-js@1.2.1:
···
2218
2261
yaml:
2219
2262
optional: true
2220
2263
2221
-
vite@7.0.6:
2222
-
resolution: {integrity: sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==}
2264
+
vite@7.1.1:
2265
+
resolution: {integrity: sha512-yJ+Mp7OyV+4S+afWo+QyoL9jFWD11QFH0i5i7JypnfTcA1rmgxCbiA8WwAICDEtZ1Z1hzrVhN8R8rGTqkTY8ZQ==}
2223
2266
engines: {node: ^20.19.0 || >=22.12.0}
2224
2267
hasBin: true
2225
2268
peerDependencies:
···
2398
2441
remark-rehype: 11.1.2
2399
2442
remark-smartypants: 3.0.2
2400
2443
shiki: 3.9.2
2401
-
smol-toml: 1.4.1
2444
+
smol-toml: 1.4.2
2402
2445
unified: 11.0.5
2403
2446
unist-util-remove-position: 5.0.0
2404
2447
unist-util-visit: 5.0.0
···
2423
2466
transitivePeerDependencies:
2424
2467
- supports-color
2425
2468
2469
+
'@atcute/atproto@3.1.1':
2470
+
dependencies:
2471
+
'@atcute/lexicons': 1.1.0
2472
+
2473
+
'@atcute/client@4.0.3':
2474
+
dependencies:
2475
+
'@atcute/identity': 1.0.3
2476
+
'@atcute/lexicons': 1.1.0
2477
+
2478
+
'@atcute/identity@1.0.3':
2479
+
dependencies:
2480
+
'@atcute/lexicons': 1.1.0
2481
+
'@badrap/valita': 0.4.6
2482
+
2426
2483
'@atcute/lex-cli@2.1.1':
2427
2484
dependencies:
2428
2485
'@atcute/lexicon-doc': 1.0.3
···
2439
2496
dependencies:
2440
2497
esm-env: 1.2.2
2441
2498
2442
-
'@atproto/api@0.16.1':
2499
+
'@atproto/api@0.16.2':
2443
2500
dependencies:
2444
2501
'@atproto/common-web': 0.4.2
2445
2502
'@atproto/lexicon': 0.4.12
···
3001
3058
dependencies:
3002
3059
tslib: 2.8.1
3003
3060
3061
+
'@types/bun@1.2.19(@types/react@19.1.9)':
3062
+
dependencies:
3063
+
bun-types: 1.2.19(@types/react@19.1.9)
3064
+
transitivePeerDependencies:
3065
+
- '@types/react'
3066
+
3004
3067
'@types/chai@5.2.2':
3005
3068
dependencies:
3006
3069
'@types/deep-eql': 4.0.2
···
3015
3078
3016
3079
'@types/fontkit@2.0.8':
3017
3080
dependencies:
3018
-
'@types/node': 24.2.0
3081
+
'@types/node': 24.2.1
3019
3082
3020
3083
'@types/hast@3.0.4':
3021
3084
dependencies:
···
3033
3096
3034
3097
'@types/node@12.20.55': {}
3035
3098
3036
-
'@types/node@24.2.0':
3099
+
'@types/node@24.2.1':
3037
3100
dependencies:
3038
3101
undici-types: 7.10.0
3102
+
3103
+
'@types/react@19.1.9':
3104
+
dependencies:
3105
+
csstype: 3.1.3
3039
3106
3040
3107
'@types/sanitize-html@2.16.0':
3041
3108
dependencies:
···
3053
3120
chai: 5.2.1
3054
3121
tinyrainbow: 2.0.0
3055
3122
3056
-
'@vitest/mocker@3.2.4(vite@7.0.6(@types/node@24.2.0))':
3123
+
'@vitest/mocker@3.2.4(vite@7.1.1(@types/node@24.2.1))':
3057
3124
dependencies:
3058
3125
'@vitest/spy': 3.2.4
3059
3126
estree-walker: 3.0.3
3060
3127
magic-string: 0.30.17
3061
3128
optionalDependencies:
3062
-
vite: 7.0.6(@types/node@24.2.0)
3129
+
vite: 7.1.1(@types/node@24.2.1)
3063
3130
3064
3131
'@vitest/pretty-format@3.2.4':
3065
3132
dependencies:
···
3126
3193
3127
3194
assertion-error@2.0.1: {}
3128
3195
3129
-
astro@5.12.8(@types/node@24.2.0)(rollup@4.46.2)(typescript@5.9.2):
3196
+
astro@5.12.9(@types/node@24.2.1)(rollup@4.46.2)(typescript@5.9.2):
3130
3197
dependencies:
3131
3198
'@astrojs/compiler': 2.12.2
3132
3199
'@astrojs/internal-helpers': 0.7.1
···
3173
3240
rehype: 13.0.2
3174
3241
semver: 7.7.2
3175
3242
shiki: 3.9.2
3176
-
smol-toml: 1.4.1
3243
+
smol-toml: 1.4.2
3177
3244
tinyexec: 0.3.2
3178
3245
tinyglobby: 0.2.14
3179
3246
tsconfck: 3.1.6(typescript@5.9.2)
···
3182
3249
unist-util-visit: 5.0.0
3183
3250
unstorage: 1.16.1
3184
3251
vfile: 6.0.3
3185
-
vite: 6.3.5(@types/node@24.2.0)
3186
-
vitefu: 1.1.1(vite@6.3.5(@types/node@24.2.0))
3252
+
vite: 6.3.5(@types/node@24.2.1)
3253
+
vitefu: 1.1.1(vite@6.3.5(@types/node@24.2.1))
3187
3254
xxhash-wasm: 1.1.0
3188
3255
yargs-parser: 21.1.1
3189
3256
yocto-spinner: 0.2.3
···
3268
3335
dependencies:
3269
3336
base64-js: 1.5.1
3270
3337
3338
+
bun-types@1.2.19(@types/react@19.1.9):
3339
+
dependencies:
3340
+
'@types/node': 24.2.1
3341
+
'@types/react': 19.1.9
3342
+
3271
3343
bundle-require@5.1.0(esbuild@0.25.8):
3272
3344
dependencies:
3273
3345
esbuild: 0.25.8
···
3335
3407
3336
3408
commander@4.1.1: {}
3337
3409
3410
+
commander@8.3.0: {}
3411
+
3338
3412
common-ancestor-path@1.0.1: {}
3339
3413
3340
3414
confbox@0.1.8: {}
···
3367
3441
source-map-js: 1.2.1
3368
3442
3369
3443
cssesc@3.0.0: {}
3444
+
3445
+
csstype@3.1.3: {}
3370
3446
3371
3447
debug@4.4.1:
3372
3448
dependencies:
···
3793
3869
optionalDependencies:
3794
3870
graceful-fs: 4.2.11
3795
3871
3872
+
katex@0.16.22:
3873
+
dependencies:
3874
+
commander: 8.3.0
3875
+
3796
3876
kleur@3.0.3: {}
3797
3877
3798
3878
kleur@4.1.5: {}
···
4550
4630
4551
4631
slash@3.0.0: {}
4552
4632
4553
-
smol-toml@1.4.1: {}
4633
+
smol-toml@1.4.2: {}
4554
4634
4555
4635
source-map-js@1.2.1: {}
4556
4636
···
4815
4895
'@types/unist': 3.0.3
4816
4896
vfile-message: 4.0.3
4817
4897
4818
-
vite-node@3.2.4(@types/node@24.2.0):
4898
+
vite-node@3.2.4(@types/node@24.2.1):
4819
4899
dependencies:
4820
4900
cac: 6.7.14
4821
4901
debug: 4.4.1
4822
4902
es-module-lexer: 1.7.0
4823
4903
pathe: 2.0.3
4824
-
vite: 7.0.6(@types/node@24.2.0)
4904
+
vite: 7.1.1(@types/node@24.2.1)
4825
4905
transitivePeerDependencies:
4826
4906
- '@types/node'
4827
4907
- jiti
···
4836
4916
- tsx
4837
4917
- yaml
4838
4918
4839
-
vite@6.3.5(@types/node@24.2.0):
4919
+
vite@6.3.5(@types/node@24.2.1):
4840
4920
dependencies:
4841
4921
esbuild: 0.25.8
4842
4922
fdir: 6.4.6(picomatch@4.0.3)
···
4845
4925
rollup: 4.46.2
4846
4926
tinyglobby: 0.2.14
4847
4927
optionalDependencies:
4848
-
'@types/node': 24.2.0
4928
+
'@types/node': 24.2.1
4849
4929
fsevents: 2.3.3
4850
4930
4851
-
vite@7.0.6(@types/node@24.2.0):
4931
+
vite@7.1.1(@types/node@24.2.1):
4852
4932
dependencies:
4853
4933
esbuild: 0.25.8
4854
4934
fdir: 6.4.6(picomatch@4.0.3)
···
4857
4937
rollup: 4.46.2
4858
4938
tinyglobby: 0.2.14
4859
4939
optionalDependencies:
4860
-
'@types/node': 24.2.0
4940
+
'@types/node': 24.2.1
4861
4941
fsevents: 2.3.3
4862
4942
4863
-
vitefu@1.1.1(vite@6.3.5(@types/node@24.2.0)):
4943
+
vitefu@1.1.1(vite@6.3.5(@types/node@24.2.1)):
4864
4944
optionalDependencies:
4865
-
vite: 6.3.5(@types/node@24.2.0)
4945
+
vite: 6.3.5(@types/node@24.2.1)
4866
4946
4867
-
vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.2.0):
4947
+
vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.2.1):
4868
4948
dependencies:
4869
4949
'@types/chai': 5.2.2
4870
4950
'@vitest/expect': 3.2.4
4871
-
'@vitest/mocker': 3.2.4(vite@7.0.6(@types/node@24.2.0))
4951
+
'@vitest/mocker': 3.2.4(vite@7.1.1(@types/node@24.2.1))
4872
4952
'@vitest/pretty-format': 3.2.4
4873
4953
'@vitest/runner': 3.2.4
4874
4954
'@vitest/snapshot': 3.2.4
···
4886
4966
tinyglobby: 0.2.14
4887
4967
tinypool: 1.1.1
4888
4968
tinyrainbow: 2.0.0
4889
-
vite: 7.0.6(@types/node@24.2.0)
4890
-
vite-node: 3.2.4(@types/node@24.2.0)
4969
+
vite: 7.1.1(@types/node@24.2.1)
4970
+
vite-node: 3.2.4(@types/node@24.2.1)
4891
4971
why-is-node-running: 2.3.0
4892
4972
optionalDependencies:
4893
4973
'@types/debug': 4.1.12
4894
-
'@types/node': 24.2.0
4974
+
'@types/node': 24.2.1
4895
4975
transitivePeerDependencies:
4896
4976
- jiti
4897
4977
- less