Compare changes

Choose any two refs to compare.

+8
.changeset/README.md
··· 1 + # Changesets 2 + 3 + Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 + with multi-package repos, or single-package repos to help you version and publish your code. You can 5 + find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 + 7 + We have a quick list of common questions to get you started engaging with this project in 8 + [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
+11
.changeset/config.json
··· 1 + { 2 + "$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json", 3 + "changelog": "@changesets/cli/changelog", 4 + "commit": false, 5 + "fixed": [], 6 + "linked": [], 7 + "access": "restricted", 8 + "baseBranch": "main", 9 + "updateInternalDependencies": "patch", 10 + "ignore": [] 11 + }
+64
.github/workflows/ci.yml
··· 1 + name: CI 2 + 3 + on: 4 + push: 5 + branches: 6 + - main 7 + pull_request: 8 + branches: ["main"] 9 + 10 + 11 + concurrency: 12 + group: ${{ github.workflow }}-${{ github.ref }} 13 + cancel-in-progress: true 14 + 15 + jobs: 16 + lint: 17 + runs-on: ubuntu-latest 18 + steps: 19 + - uses: actions/checkout@v4 20 + - name: Install PNPM 21 + uses: pnpm/action-setup@v4 22 + with: 23 + version: 10 24 + - name: Setup Node 25 + uses: actions/setup-node@v4 26 + with: 27 + cache: "pnpm" 28 + - name: Install dependencies 29 + run: pnpm install 30 + - name: Lint 31 + run: pnpm lint 32 + test: 33 + runs-on: ubuntu-latest 34 + steps: 35 + - uses: actions/checkout@v4 36 + - name: Install PNPM 37 + uses: pnpm/action-setup@v4 38 + with: 39 + version: 10 40 + - name: Setup Node 41 + uses: actions/setup-node@v4 42 + with: 43 + cache: "pnpm" 44 + - name: Install dependencies 45 + run: pnpm install 46 + - name: Test 47 + run: pnpm test 48 + format: 49 + needs: [lint, test] 50 + runs-on: ubuntu-latest 51 + steps: 52 + - uses: actions/checkout@v4 53 + - name: Install PNPM 54 + uses: pnpm/action-setup@v4 55 + with: 56 + version: 10 57 + - name: Setup Node 58 + uses: actions/setup-node@v4 59 + with: 60 + cache: "pnpm" 61 + - name: Install dependencies 62 + run: pnpm install 63 + - name: Lint 64 + run: pnpm lint
+34
.github/workflows/publish.yml
··· 1 + name: Publish 2 + on: 3 + workflow_run: 4 + workflows: [CI] 5 + branches: [main] 6 + types: [completed] 7 + 8 + concurrency: ${{ github.workflow }}-${{ github.ref }} 9 + 10 + permissions: 11 + contents: write 12 + pull-requests: write 13 + 14 + jobs: 15 + publish: 16 + if: ${{ github.event.workflow_run.conclusion == 'success' }} 17 + runs-on: ubuntu-latest 18 + steps: 19 + - uses: actions/checkout@v4 20 + - uses: pnpm/action-setup@v2 21 + with: 22 + version: 10 23 + - uses: actions/setup-node@v4 24 + with: 25 + cache: "pnpm" 26 + - run: pnpm install --frozen-lockfile 27 + - name: Create Release Pull Request or Publish 28 + id: changesets 29 + uses: changesets/action@v1 30 + with: 31 + publish: pnpm run release 32 + env: 33 + GITHUB_TOKEN: ${{ secrets.GHA_TOKEN }} 34 + NPM_TOKEN: ${{ secrets.LEAFLET_PUBLISHING_TOKEN }}
+3 -1
.gitignore
··· 1 - node_modules 1 + node_modules 2 + dist 3 + *.tgz
+50
CHANGELOG.md
··· 1 + # leaflet-loader-astro 2 + 3 + ## 1.3.0 4 + 5 + ### Minor Changes 6 + 7 + - eb3bc4b: Add iframe block 8 + 9 + ## 1.2.0 10 + 11 + ### Minor Changes 12 + 13 + - f920153: Add support for blockquotes 14 + 15 + ### Patch Changes 16 + 17 + - 8922bb1: Add JSDoc comments for available loader options for leafletStaticLoader and leafletLiveLoader 18 + 19 + ## 1.1.0 20 + 21 + ### Minor Changes 22 + 23 + - 6d70cc6: Added support for these leaflet blocks: 24 + 25 + - ul/li 26 + - math 27 + - code 28 + - img 29 + - hr 30 + 31 + 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 32 + 33 + - 5524ce5: Added the ability to use a handle or did when specifying a repo for leafletStaticLoader and leafletLiveLoader 34 + 35 + ```ts 36 + import { defineLiveCollection } from "astro:content"; 37 + import { leafletLiveLoader } from "leaflet-loader-astro"; 38 + 39 + const documents = defineLiveCollection({ 40 + loader: leafletLiveLoader({ repo: "dane.computer" }), // or repo: did:plc:qttsv4e7pu2jl3ilanfgc3zn, both work! 41 + }); 42 + 43 + export const collections = { documents }; 44 + ``` 45 + 46 + ## 1.0.0 47 + 48 + ### Major Changes 49 + 50 + - b4309c0: This is the initial release for `leaflet-loader-astro`
+218 -2
README.md
··· 1 - # An astro loader for leaflet.pub 1 + # Leaflet Astro Loader 2 + 3 + This loader is for [leaflet.pub](https://leaflet.pub/). It fetches leaflet document records from your personal data server to then be used on your astro site. 4 + 5 + There are two different types of loaders you can use from this package: 6 + 7 + - **Static Loader:** Fetches data at build time and is served statically on your website 8 + - **Live Loader (experimental astro feature):** Fetches data on each request. **Note**: This package does not provide any caching mechanisms for the live loader. So to avoid slamming your PDS (or someone elses PDS) with requests it's probably a good idea to set up some sort of cache either using cache headers or some other means. 9 + 10 + ## Installation 11 + 12 + ```bash 13 + npm install @nulfrost/leaflet-loader-astro 14 + ``` 15 + 16 + ## Usage 17 + 18 + <details> 19 + <summary>Build-time loader: leafletStaticLoader **(recommended)**</summary> 20 + 21 + ```ts 22 + // src/content.config.ts 23 + import { defineCollection, z } from "astro:content"; 24 + import { leafletStaticLoader } from "@nulfrost/leaflet-loader-astro"; 25 + 26 + const documents = defineCollection({ 27 + loader: leafletStaticLoader({ repo: "did:plc:qttsv4e7pu2jl3ilanfgc3zn" }), // or repo: dane.is.extraordinarily.cool 28 + }); 29 + 30 + export const collections = { documents }; 31 + ``` 2 32 3 - TODO 33 + ```ts 34 + // src/pages/index.astro 35 + --- 36 + import { getCollection } from "astro:content"; 37 + 38 + const documents = await getCollection("documents"); 39 + --- 40 + 41 + <html lang="en"> 42 + <head> 43 + <meta charset="utf-8" /> 44 + <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> 45 + <meta name="viewport" content="width=device-width" /> 46 + <meta name="generator" content={Astro.generator} /> 47 + <title>Astro</title> 48 + </head> 49 + <body> 50 + <h1>Astro + Leaflet.pub</h1> 51 + <ul> 52 + {documents.map(document => <li> 53 + <a href={`/blogs/${document.id}`}>{document.data.title}</a> 54 + </li>)} 55 + </ul> 56 + </body> 57 + </html> 58 + ``` 59 + 60 + ```ts 61 + // src/pages/blog/[blog].astro 62 + --- 63 + import { getCollection, getEntry } from "astro:content"; 64 + import { render } from "astro:content"; 65 + 66 + export async function getStaticPaths() { 67 + const documents = await getCollection("documents"); 68 + return documents.map((document) => ({ 69 + params: { blog: document.id }, 70 + props: document, 71 + })); 72 + } 73 + 74 + const document = await getEntry("documents", Astro.params.blog); 75 + 76 + if (!document) { 77 + throw new Error(`Document with id "${Astro.params.blog}" not found`); 78 + } 79 + 80 + const { Content } = await render(document); 81 + --- 82 + 83 + <Content /> 84 + ``` 85 + </details> 86 + 87 + <details> 88 + <summary>Live loader: leafletLiveLoader</summary> 89 + 90 + ```ts 91 + // astro.config.mjs 92 + 93 + // @ts-check 94 + import { defineConfig } from "astro/config"; 95 + 96 + // https://astro.build/config 97 + export default defineConfig({ 98 + experimental: { 99 + liveContentCollections: true, // make sure to enable this 100 + }, 101 + }); 102 + ``` 103 + 104 + ```ts 105 + // src/live.config.ts 106 + import { defineLiveCollection, z } from "astro:content"; 107 + import { leafletLiveLoader } from "@nulfrost/leaflet-loader-astro"; 108 + 109 + const documents = defineLiveCollection({ 110 + loader: leafletLiveLoader({ repo: "did:plc:qttsv4e7pu2jl3ilanfgc3zn" }), // or repo: dane.is.extraordinarily.cool 111 + }); 112 + 113 + export const collections = { documents }; 114 + ``` 115 + 116 + ```ts 117 + // src/pages/index.astro 118 + --- 119 + import { getLiveCollection } from "astro:content"; 120 + 121 + export const prerender = false; 122 + 123 + const documents = await getLiveCollection("documents"); 124 + --- 125 + 126 + <html lang="en"> 127 + <head> 128 + <meta charset="utf-8" /> 129 + <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> 130 + <meta name="viewport" content="width=device-width" /> 131 + <meta name="generator" content={Astro.generator} /> 132 + <title>Astro</title> 133 + </head> 134 + <body> 135 + <h1>Astro + Leaflet.pub</h1> 136 + <ul> 137 + {documents.map(document => <li> 138 + <a href={`/blogs/${document.id}`}>{document.data.title}</a> 139 + </li>)} 140 + </ul> 141 + </body> 142 + </html> 143 + ``` 144 + 145 + ```ts 146 + // src/pages/blog/[blog].astro 147 + --- 148 + import { getLiveEntry } from "astro:content"; 149 + import { render } from "astro:content"; 150 + 151 + export const prerender = false; 152 + 153 + const document = await getLiveEntry("documents", Astro.params.blog); 154 + 155 + if (!document) { 156 + throw new Error(`Document with id "${Astro.params.blog}" not found`); 157 + } 158 + 159 + const { Content } = await render(document?.entry); 160 + --- 161 + 162 + <Content /> 163 + ``` 164 + 165 + </details> 166 + 167 + ## Loader Options 168 + 169 + ### Static Loader 170 + 171 + ```ts 172 + leafletStaticLoader() 173 + ``` 174 + 175 + `repo`: This can be either your DID (did:plc:qttsv4e7pu2jl3ilanfgc3zn) or your handle (dane.is.extraordinarily.cool) 176 + 177 + `limit`: How many leaflet documents to return when calling `getCollection`. The default is 50 and the range is from 1 to 100. 178 + 179 + `reverse`: Whether or not to return the leaflet documents in reverse order. By default this is false. 180 + 181 + ### Live Loader 182 + 183 + ```ts 184 + leafletLiveLoader() 185 + ``` 186 + 187 + `repo`: This can be either your DID (did:plc:qttsv4e7pu2jl3ilanfgc3zn) or your handle (dane.is.extraordinarily.cool) 188 + 189 + > [!NOTE] 190 + > `getLiveCollection` supports a second argument where you can add additional filters, similar to the options you have access to for `leafletStaticLoader` 191 + 192 + ```ts 193 + getLiveCollection() 194 + ``` 195 + 196 + `limit`: How many leaflet documents to return when calling `getCollection`. The default is 50 and the range is from 1 to 100. 197 + 198 + `reverse`: Whether or not to return the leaflet documents in reverse order. By default this is false. 199 + 200 + ## Supported Leaflet Blocks 201 + 202 + - [ ] Bluesky post 203 + - [x] Iframe 204 + - [x] Horizontal Rule 205 + - [x] Unordered List 206 + - [x] Math 207 + - [x] Code 208 + - [ ] Website 209 + - [x] Image 210 + - [x] Blockquote 211 + - [x] Text 212 + - [x] Header 213 + - [x] List Item 214 + 215 + ## License 216 + 217 + MIT 218 + 219 + For questions, contributions, and support, please open an issue on GitHub.
+35
biome.json
··· 1 + { 2 + "$schema": "https://biomejs.dev/schemas/2.1.3/schema.json", 3 + "vcs": { 4 + "enabled": false, 5 + "clientKind": "git", 6 + "useIgnoreFile": false 7 + }, 8 + "files": { 9 + "ignoreUnknown": false, 10 + "includes": ["**", "!src/lexicons/**/*.ts"] 11 + }, 12 + "formatter": { 13 + "enabled": true, 14 + "indentStyle": "tab" 15 + }, 16 + "linter": { 17 + "enabled": true, 18 + "rules": { 19 + "recommended": true 20 + } 21 + }, 22 + "javascript": { 23 + "formatter": { 24 + "quoteStyle": "double" 25 + } 26 + }, 27 + "assist": { 28 + "enabled": true, 29 + "actions": { 30 + "source": { 31 + "organizeImports": "on" 32 + } 33 + } 34 + } 35 + }
+6
lex.config.js
··· 1 + import { defineLexiconConfig } from "@atcute/lex-cli"; 2 + 3 + export default defineLexiconConfig({ 4 + files: ["lexicons/**/*.json"], 5 + outdir: "lib/lexicons/", 6 + });
+15
lexicons/com/atproto/repo/strongRef.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "com.atproto.repo.strongRef", 4 + "description": "A URI with a content-hash fingerprint.", 5 + "defs": { 6 + "main": { 7 + "type": "object", 8 + "required": ["uri", "cid"], 9 + "properties": { 10 + "uri": { "type": "string", "format": "at-uri" }, 11 + "cid": { "type": "string", "format": "cid" } 12 + } 13 + } 14 + } 15 + }
+22
lexicons/pub/leaflet/blocks/blockquote.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "pub.leaflet.blocks.blockquote", 4 + "defs": { 5 + "main": { 6 + "type": "object", 7 + "required": ["plaintext"], 8 + "properties": { 9 + "plaintext": { 10 + "type": "string" 11 + }, 12 + "facets": { 13 + "type": "array", 14 + "items": { 15 + "type": "ref", 16 + "ref": "pub.leaflet.richtext.facet" 17 + } 18 + } 19 + } 20 + } 21 + } 22 + }
+21
lexicons/pub/leaflet/blocks/code.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "pub.leaflet.blocks.code", 4 + "defs": { 5 + "main": { 6 + "type": "object", 7 + "required": ["plaintext"], 8 + "properties": { 9 + "plaintext": { 10 + "type": "string" 11 + }, 12 + "language": { 13 + "type": "string" 14 + }, 15 + "syntaxHighlightingTheme": { 16 + "type": "string" 17 + } 18 + } 19 + } 20 + } 21 + }
+27
lexicons/pub/leaflet/blocks/header.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "pub.leaflet.blocks.header", 4 + "defs": { 5 + "main": { 6 + "type": "object", 7 + "required": ["plaintext"], 8 + "properties": { 9 + "level": { 10 + "type": "integer", 11 + "minimum": 1, 12 + "maximum": 6 13 + }, 14 + "plaintext": { 15 + "type": "string" 16 + }, 17 + "facets": { 18 + "type": "array", 19 + "items": { 20 + "type": "ref", 21 + "ref": "pub.leaflet.richtext.facet" 22 + } 23 + } 24 + } 25 + } 26 + } 27 + }
+11
lexicons/pub/leaflet/blocks/horizontalRule.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "pub.leaflet.blocks.horizontalRule", 4 + "defs": { 5 + "main": { 6 + "type": "object", 7 + "required": [], 8 + "properties": {} 9 + } 10 + } 11 + }
+21
lexicons/pub/leaflet/blocks/iframe.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "pub.leaflet.blocks.iframe", 4 + "defs": { 5 + "main": { 6 + "type": "object", 7 + "required": ["url"], 8 + "properties": { 9 + "url": { 10 + "type": "string", 11 + "format": "uri" 12 + }, 13 + "height": { 14 + "type": "integer", 15 + "minimum": 16, 16 + "maximum": 1600 17 + } 18 + } 19 + } 20 + } 21 + }
+37
lexicons/pub/leaflet/blocks/image.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "pub.leaflet.blocks.image", 4 + "defs": { 5 + "main": { 6 + "type": "object", 7 + "required": ["image", "aspectRatio"], 8 + "properties": { 9 + "image": { 10 + "type": "blob", 11 + "accept": ["image/*"], 12 + "maxSize": 1000000 13 + }, 14 + "alt": { 15 + "type": "string", 16 + "description": "Alt text description of the image, for accessibility." 17 + }, 18 + "aspectRatio": { 19 + "type": "ref", 20 + "ref": "#aspectRatio" 21 + } 22 + } 23 + }, 24 + "aspectRatio": { 25 + "type": "object", 26 + "required": ["width", "height"], 27 + "properties": { 28 + "width": { 29 + "type": "integer" 30 + }, 31 + "height": { 32 + "type": "integer" 33 + } 34 + } 35 + } 36 + } 37 + }
+15
lexicons/pub/leaflet/blocks/math.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "pub.leaflet.blocks.math", 4 + "defs": { 5 + "main": { 6 + "type": "object", 7 + "required": ["tex"], 8 + "properties": { 9 + "tex": { 10 + "type": "string" 11 + } 12 + } 13 + } 14 + } 15 + }
+22
lexicons/pub/leaflet/blocks/text.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "pub.leaflet.blocks.text", 4 + "defs": { 5 + "main": { 6 + "type": "object", 7 + "required": ["plaintext"], 8 + "properties": { 9 + "plaintext": { 10 + "type": "string" 11 + }, 12 + "facets": { 13 + "type": "array", 14 + "items": { 15 + "type": "ref", 16 + "ref": "pub.leaflet.richtext.facet" 17 + } 18 + } 19 + } 20 + } 21 + } 22 + }
+40
lexicons/pub/leaflet/blocks/unorderedList.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "pub.leaflet.blocks.unorderedList", 4 + "defs": { 5 + "main": { 6 + "type": "object", 7 + "required": ["children"], 8 + "properties": { 9 + "children": { 10 + "type": "array", 11 + "items": { 12 + "type": "ref", 13 + "ref": "#listItem" 14 + } 15 + } 16 + } 17 + }, 18 + "listItem": { 19 + "type": "object", 20 + "required": ["content"], 21 + "properties": { 22 + "content": { 23 + "type": "union", 24 + "refs": [ 25 + "pub.leaflet.blocks.text", 26 + "pub.leaflet.blocks.header", 27 + "pub.leaflet.blocks.image" 28 + ] 29 + }, 30 + "children": { 31 + "type": "array", 32 + "items": { 33 + "type": "ref", 34 + "ref": "#listItem" 35 + } 36 + } 37 + } 38 + } 39 + } 40 + }
+27
lexicons/pub/leaflet/blocks/website.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "pub.leaflet.blocks.website", 4 + "defs": { 5 + "main": { 6 + "type": "object", 7 + "required": ["src"], 8 + "properties": { 9 + "previewImage": { 10 + "type": "blob", 11 + "accept": ["image/*"], 12 + "maxSize": 1000000 13 + }, 14 + "title": { 15 + "type": "string" 16 + }, 17 + "description": { 18 + "type": "string" 19 + }, 20 + "src": { 21 + "type": "string", 22 + "format": "uri" 23 + } 24 + } 25 + } 26 + } 27 + }
+52
lexicons/pub/leaflet/document.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "pub.leaflet.document", 4 + "revision": 1, 5 + "description": "A lexicon for long form rich media documents", 6 + "defs": { 7 + "main": { 8 + "type": "record", 9 + "key": "tid", 10 + "description": "Record containing a document", 11 + "record": { 12 + "type": "object", 13 + "required": ["pages", "author", "title", "publication"], 14 + "properties": { 15 + "title": { 16 + "type": "string", 17 + "maxLength": 1280, 18 + "maxGraphemes": 128 19 + }, 20 + "postRef": { 21 + "type": "ref", 22 + "ref": "com.atproto.repo.strongRef" 23 + }, 24 + "description": { 25 + "type": "string", 26 + "maxLength": 3000, 27 + "maxGraphemes": 300 28 + }, 29 + "publishedAt": { 30 + "type": "string", 31 + "format": "datetime" 32 + }, 33 + "publication": { 34 + "type": "string", 35 + "format": "at-uri" 36 + }, 37 + "author": { 38 + "type": "string", 39 + "format": "at-identifier" 40 + }, 41 + "pages": { 42 + "type": "array", 43 + "items": { 44 + "type": "union", 45 + "refs": ["pub.leaflet.pages.linearDocument"] 46 + } 47 + } 48 + } 49 + } 50 + } 51 + } 52 + }
+53
lexicons/pub/leaflet/pages/linearDocument.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "pub.leaflet.pages.linearDocument", 4 + "defs": { 5 + "main": { 6 + "type": "object", 7 + "properties": { 8 + "blocks": { 9 + "type": "array", 10 + "items": { 11 + "type": "ref", 12 + "ref": "#block" 13 + } 14 + } 15 + } 16 + }, 17 + "block": { 18 + "type": "object", 19 + "required": ["block"], 20 + "properties": { 21 + "block": { 22 + "type": "union", 23 + "refs": [ 24 + "pub.leaflet.blocks.text", 25 + "pub.leaflet.blocks.header", 26 + "pub.leaflet.blocks.image", 27 + "pub.leaflet.blocks.unorderedList", 28 + "pub.leaflet.blocks.website", 29 + "pub.leaflet.blocks.math", 30 + "pub.leaflet.blocks.code" 31 + ] 32 + }, 33 + "alignment": { 34 + "type": "string", 35 + "knownValues": [ 36 + "#textAlignLeft", 37 + "#textAlignCenter", 38 + "#textAlignRight" 39 + ] 40 + } 41 + } 42 + }, 43 + "textAlignLeft": { 44 + "type": "token" 45 + }, 46 + "textAlignCenter": { 47 + "type": "token" 48 + }, 49 + "textAlignRight": { 50 + "type": "token" 51 + } 52 + } 53 + }
+105
lexicons/pub/leaflet/richtext/facet.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "pub.leaflet.richtext.facet", 4 + "defs": { 5 + "main": { 6 + "type": "object", 7 + "description": "Annotation of a sub-string within rich text.", 8 + "required": ["index", "features"], 9 + "properties": { 10 + "index": { 11 + "type": "ref", 12 + "ref": "#byteSlice" 13 + }, 14 + "features": { 15 + "type": "array", 16 + "items": { 17 + "type": "union", 18 + "refs": [ 19 + "#link", 20 + "#code", 21 + "#highlight", 22 + "#underline", 23 + "#strikethrough", 24 + "#id", 25 + "#bold", 26 + "#italic" 27 + ] 28 + } 29 + } 30 + } 31 + }, 32 + "byteSlice": { 33 + "type": "object", 34 + "description": "Specifies the sub-string range a facet feature applies to. Start index is inclusive, end index is exclusive. Indices are zero-indexed, counting bytes of the UTF-8 encoded text. NOTE: some languages, like Javascript, use UTF-16 or Unicode codepoints for string slice indexing; in these languages, convert to byte arrays before working with facets.", 35 + "required": ["byteStart", "byteEnd"], 36 + "properties": { 37 + "byteStart": { 38 + "type": "integer", 39 + "minimum": 0 40 + }, 41 + "byteEnd": { 42 + "type": "integer", 43 + "minimum": 0 44 + } 45 + } 46 + }, 47 + "link": { 48 + "type": "object", 49 + "description": "Facet feature for a URL. The text URL may have been simplified or truncated, but the facet reference should be a complete URL.", 50 + "required": ["uri"], 51 + "properties": { 52 + "uri": { 53 + "type": "string", 54 + "format": "uri" 55 + } 56 + } 57 + }, 58 + "code": { 59 + "type": "object", 60 + "description": "Facet feature for inline code.", 61 + "required": [], 62 + "properties": {} 63 + }, 64 + "highlight": { 65 + "type": "object", 66 + "description": "Facet feature for highlighted text.", 67 + "required": [], 68 + "properties": {} 69 + }, 70 + "underline": { 71 + "type": "object", 72 + "description": "Facet feature for underline markup", 73 + "required": [], 74 + "properties": {} 75 + }, 76 + "strikethrough": { 77 + "type": "object", 78 + "description": "Facet feature for strikethrough markup", 79 + "required": [], 80 + "properties": {} 81 + }, 82 + "id": { 83 + "type": "object", 84 + "description": "Facet feature for an identifier. Used for linking to a segment", 85 + "required": [], 86 + "properties": { 87 + "id": { 88 + "type": "string" 89 + } 90 + } 91 + }, 92 + "bold": { 93 + "type": "object", 94 + "description": "Facet feature for bold text", 95 + "required": [], 96 + "properties": {} 97 + }, 98 + "italic": { 99 + "type": "object", 100 + "description": "Facet feature for italic text", 101 + "required": [], 102 + "properties": {} 103 + } 104 + } 105 + }
+2
lib/index.ts
··· 1 + export { leafletLiveLoader } from "./leaflet-live-loader.js"; 2 + export { leafletStaticLoader } from "./leaftlet-static-loader.js";
+139
lib/leaflet-live-loader.ts
··· 1 + import { Client, simpleFetchHandler } from "@atcute/client"; 2 + import { isHandle } from "@atcute/lexicons/syntax"; 3 + import type { LiveLoader } from "astro/loaders"; 4 + import type { 5 + CollectionFilter, 6 + EntryFilter, 7 + LeafletDocumentRecord, 8 + LeafletDocumentView, 9 + LiveLeafletLoaderOptions, 10 + } from "./types.js"; 11 + import { 12 + getLeafletDocuments, 13 + getSingleLeafletDocument, 14 + isPlcDid, 15 + leafletBlocksToHTML, 16 + leafletDocumentRecordToView, 17 + LiveLoaderError, 18 + resolveMiniDoc, 19 + uriToRkey, 20 + } from "./utils.js"; 21 + 22 + export function leafletLiveLoader( 23 + options: LiveLeafletLoaderOptions, 24 + ): LiveLoader< 25 + LeafletDocumentView, 26 + EntryFilter, 27 + CollectionFilter, 28 + LiveLoaderError 29 + > { 30 + const { repo } = options; 31 + 32 + if (!repo || typeof repo !== "string") { 33 + throw new LiveLoaderError( 34 + "missing or invalid did", 35 + "MISSING_OR_INVALID_DID", 36 + ); 37 + } 38 + 39 + // not a valid handle, check if valid did 40 + if (!isHandle(repo)) { 41 + // not a valid handle or did, throw 42 + if (!isPlcDid(repo)) { 43 + throw new LiveLoaderError( 44 + "invalid handle or did", 45 + "INVALID_HANDLE_OR_DID", 46 + ); 47 + } 48 + } 49 + 50 + return { 51 + name: "leaflet-loader-astro", 52 + loadCollection: async ({ filter }) => { 53 + try { 54 + const { pds, did } = await resolveMiniDoc(repo); 55 + const handler = simpleFetchHandler({ service: pds }); 56 + const rpc = new Client({ handler }); 57 + 58 + const { documents } = await getLeafletDocuments({ 59 + rpc, 60 + repo, 61 + reverse: filter?.reverse, 62 + cursor: filter?.cursor, 63 + limit: filter?.limit, 64 + }); 65 + 66 + return { 67 + entries: documents.map((document) => { 68 + const id = uriToRkey(document.uri); 69 + return { 70 + id, 71 + data: leafletDocumentRecordToView({ 72 + uri: document.uri, 73 + cid: document.cid, 74 + value: document.value as unknown as LeafletDocumentRecord, 75 + }), 76 + rendered: { 77 + html: leafletBlocksToHTML({ 78 + record: document.value as unknown as LeafletDocumentRecord, 79 + did, 80 + }), 81 + }, 82 + }; 83 + }), 84 + }; 85 + } catch { 86 + return { 87 + error: new LiveLoaderError( 88 + "could not recover from error, please report on github", 89 + "UNRECOVERABLE_ERROR", 90 + ), 91 + }; 92 + } 93 + }, 94 + loadEntry: async ({ filter }) => { 95 + if (!filter.id) { 96 + return { 97 + error: new LiveLoaderError( 98 + "must provide an id for specific document", 99 + "MISSING_DOCUMENT_ID", 100 + ), 101 + }; 102 + } 103 + try { 104 + const { pds, did } = await resolveMiniDoc(repo); 105 + const handler = simpleFetchHandler({ service: pds }); 106 + const rpc = new Client({ handler }); 107 + const document = await getSingleLeafletDocument({ 108 + rpc, 109 + id: filter.id, 110 + repo, 111 + }); 112 + 113 + const cid = document?.cid?.toString() ?? ""; 114 + 115 + return { 116 + id: filter.id, 117 + data: leafletDocumentRecordToView({ 118 + uri: document.uri, 119 + cid, 120 + value: document.value as unknown as LeafletDocumentRecord, 121 + }), 122 + rendered: { 123 + html: leafletBlocksToHTML({ 124 + record: document.value as unknown as LeafletDocumentRecord, 125 + did, 126 + }), 127 + }, 128 + }; 129 + } catch { 130 + return { 131 + error: new LiveLoaderError( 132 + "could not recover from error, please report on github", 133 + "UNRECOVERABLE_ERROR", 134 + ), 135 + }; 136 + } 137 + }, 138 + }; 139 + }
+111
lib/leaftlet-static-loader.ts
··· 1 + import { Client, simpleFetchHandler } from "@atcute/client"; 2 + import { isHandle } from "@atcute/lexicons/syntax"; 3 + import type { Loader, LoaderContext } from "astro/loaders"; 4 + import { LeafletDocumentSchema } from "schema.js"; 5 + import type { 6 + LeafletDocumentRecord, 7 + StaticLeafletLoaderOptions, 8 + } from "types.js"; 9 + import { 10 + getLeafletDocuments, 11 + isPlcDid, 12 + leafletBlocksToHTML, 13 + leafletDocumentRecordToView, 14 + LiveLoaderError, 15 + resolveMiniDoc, 16 + uriToRkey, 17 + } from "utils.js"; 18 + 19 + export function leafletStaticLoader( 20 + options: StaticLeafletLoaderOptions, 21 + ): Loader { 22 + const { repo, limit, reverse } = options; 23 + 24 + if (!repo || typeof repo !== "string") { 25 + throw new LiveLoaderError( 26 + "missing or invalid did", 27 + "MISSING_OR_INVALID_DID", 28 + ); 29 + } 30 + 31 + // not a valid handle, check if valid did 32 + if (!isHandle(repo)) { 33 + // not a valid handle or did, throw 34 + if (!isPlcDid(repo)) { 35 + throw new LiveLoaderError( 36 + "invalid handle or did", 37 + "INVALID_HANDLE_OR_DID", 38 + ); 39 + } 40 + } 41 + 42 + return { 43 + name: "static-leaflet-loader-astro", 44 + schema: LeafletDocumentSchema, 45 + load: async ({ 46 + store, 47 + logger, 48 + generateDigest, 49 + parseData, 50 + }: LoaderContext) => { 51 + try { 52 + logger.info("fetching latest leaflet documents"); 53 + const { pds, did } = await resolveMiniDoc(repo); 54 + const handler = simpleFetchHandler({ service: pds }); 55 + const rpc = new Client({ handler }); 56 + 57 + let cursor: string | undefined; 58 + let count = 0; 59 + 60 + fetching: do { 61 + const { documents, cursor: documentsCursor } = 62 + await getLeafletDocuments({ 63 + rpc, 64 + repo, 65 + cursor, 66 + reverse, 67 + limit: 50, 68 + }); 69 + for (const document of documents) { 70 + if (limit && count >= limit) { 71 + count++; 72 + break fetching; 73 + } 74 + count++; 75 + 76 + const id = uriToRkey(document.uri); 77 + const digest = generateDigest(document.value); 78 + store.set({ 79 + id, 80 + data: await parseData({ 81 + id, 82 + data: JSON.parse( 83 + JSON.stringify( 84 + leafletDocumentRecordToView({ 85 + uri: document.uri, 86 + cid: document.cid, 87 + value: document.value as unknown as LeafletDocumentRecord, 88 + }), 89 + ), 90 + ), 91 + }), 92 + digest, 93 + rendered: { 94 + html: leafletBlocksToHTML({ 95 + record: document.value as unknown as LeafletDocumentRecord, 96 + did, 97 + }), 98 + }, 99 + }); 100 + } 101 + cursor = documentsCursor; 102 + logger.info(`Fetched ${count} posts`); 103 + } while (cursor); 104 + } catch (error) { 105 + logger.error( 106 + `There was an error fetching the leaflet documents: ${(error as Error).message}`, 107 + ); 108 + } 109 + }, 110 + }; 111 + }
+14
lib/lexicons/index.ts
··· 1 + export * as ComAtprotoRepoStrongRef from "./types/com/atproto/repo/strongRef.js"; 2 + export * as PubLeafletBlocksBlockquote from "./types/pub/leaflet/blocks/blockquote.js"; 3 + export * as PubLeafletBlocksCode from "./types/pub/leaflet/blocks/code.js"; 4 + export * as PubLeafletBlocksHeader from "./types/pub/leaflet/blocks/header.js"; 5 + export * as PubLeafletBlocksHorizontalRule from "./types/pub/leaflet/blocks/horizontalRule.js"; 6 + export * as PubLeafletBlocksIframe from "./types/pub/leaflet/blocks/iframe.js"; 7 + export * as PubLeafletBlocksImage from "./types/pub/leaflet/blocks/image.js"; 8 + export * as PubLeafletBlocksMath from "./types/pub/leaflet/blocks/math.js"; 9 + export * as PubLeafletBlocksText from "./types/pub/leaflet/blocks/text.js"; 10 + export * as PubLeafletBlocksUnorderedList from "./types/pub/leaflet/blocks/unorderedList.js"; 11 + export * as PubLeafletBlocksWebsite from "./types/pub/leaflet/blocks/website.js"; 12 + export * as PubLeafletDocument from "./types/pub/leaflet/document.js"; 13 + export * as PubLeafletPagesLinearDocument from "./types/pub/leaflet/pages/linearDocument.js"; 14 + export * as PubLeafletRichtextFacet from "./types/pub/leaflet/richtext/facet.js";
+18
lib/lexicons/types/com/atproto/repo/strongRef.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("com.atproto.repo.strongRef"), 7 + ), 8 + cid: /*#__PURE__*/ v.cidString(), 9 + uri: /*#__PURE__*/ v.resourceUriString(), 10 + }); 11 + 12 + type main$schematype = typeof _mainSchema; 13 + 14 + export interface mainSchema extends main$schematype {} 15 + 16 + export const mainSchema = _mainSchema as mainSchema; 17 + 18 + export interface Main extends v.InferInput<typeof mainSchema> {}
+23
lib/lexicons/types/pub/leaflet/blocks/blockquote.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import * as PubLeafletRichtextFacet from "../richtext/facet.js"; 4 + 5 + const _mainSchema = /*#__PURE__*/ v.object({ 6 + $type: /*#__PURE__*/ v.optional( 7 + /*#__PURE__*/ v.literal("pub.leaflet.blocks.blockquote"), 8 + ), 9 + get facets() { 10 + return /*#__PURE__*/ v.optional( 11 + /*#__PURE__*/ v.array(PubLeafletRichtextFacet.mainSchema), 12 + ); 13 + }, 14 + plaintext: /*#__PURE__*/ v.string(), 15 + }); 16 + 17 + type main$schematype = typeof _mainSchema; 18 + 19 + export interface mainSchema extends main$schematype {} 20 + 21 + export const mainSchema = _mainSchema as mainSchema; 22 + 23 + export interface Main extends v.InferInput<typeof mainSchema> {}
+19
lib/lexicons/types/pub/leaflet/blocks/code.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.code"), 7 + ), 8 + language: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 9 + plaintext: /*#__PURE__*/ v.string(), 10 + syntaxHighlightingTheme: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 11 + }); 12 + 13 + type main$schematype = typeof _mainSchema; 14 + 15 + export interface mainSchema extends main$schematype {} 16 + 17 + export const mainSchema = _mainSchema as mainSchema; 18 + 19 + export interface Main extends v.InferInput<typeof mainSchema> {}
+28
lib/lexicons/types/pub/leaflet/blocks/header.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import * as PubLeafletRichtextFacet from "../richtext/facet.js"; 4 + 5 + const _mainSchema = /*#__PURE__*/ v.object({ 6 + $type: /*#__PURE__*/ v.optional( 7 + /*#__PURE__*/ v.literal("pub.leaflet.blocks.header"), 8 + ), 9 + get facets() { 10 + return /*#__PURE__*/ v.optional( 11 + /*#__PURE__*/ v.array(PubLeafletRichtextFacet.mainSchema), 12 + ); 13 + }, 14 + level: /*#__PURE__*/ v.optional( 15 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.integer(), [ 16 + /*#__PURE__*/ v.integerRange(1, 6), 17 + ]), 18 + ), 19 + plaintext: /*#__PURE__*/ v.string(), 20 + }); 21 + 22 + type main$schematype = typeof _mainSchema; 23 + 24 + export interface mainSchema extends main$schematype {} 25 + 26 + export const mainSchema = _mainSchema as mainSchema; 27 + 28 + export interface Main extends v.InferInput<typeof mainSchema> {}
+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> {}
+22
lib/lexicons/types/pub/leaflet/blocks/iframe.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.iframe"), 7 + ), 8 + height: /*#__PURE__*/ v.optional( 9 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.integer(), [ 10 + /*#__PURE__*/ v.integerRange(16, 1600), 11 + ]), 12 + ), 13 + url: /*#__PURE__*/ v.genericUriString(), 14 + }); 15 + 16 + type main$schematype = typeof _mainSchema; 17 + 18 + export interface mainSchema extends main$schematype {} 19 + 20 + export const mainSchema = _mainSchema as mainSchema; 21 + 22 + export interface Main extends v.InferInput<typeof mainSchema> {}
+32
lib/lexicons/types/pub/leaflet/blocks/image.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + 4 + const _aspectRatioSchema = /*#__PURE__*/ v.object({ 5 + $type: /*#__PURE__*/ v.optional( 6 + /*#__PURE__*/ v.literal("pub.leaflet.blocks.image#aspectRatio"), 7 + ), 8 + height: /*#__PURE__*/ v.integer(), 9 + width: /*#__PURE__*/ v.integer(), 10 + }); 11 + const _mainSchema = /*#__PURE__*/ v.object({ 12 + $type: /*#__PURE__*/ v.optional( 13 + /*#__PURE__*/ v.literal("pub.leaflet.blocks.image"), 14 + ), 15 + alt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 16 + get aspectRatio() { 17 + return aspectRatioSchema; 18 + }, 19 + image: /*#__PURE__*/ v.blob(), 20 + }); 21 + 22 + type aspectRatio$schematype = typeof _aspectRatioSchema; 23 + type main$schematype = typeof _mainSchema; 24 + 25 + export interface aspectRatioSchema extends aspectRatio$schematype {} 26 + export interface mainSchema extends main$schematype {} 27 + 28 + export const aspectRatioSchema = _aspectRatioSchema as aspectRatioSchema; 29 + export const mainSchema = _mainSchema as mainSchema; 30 + 31 + export interface AspectRatio extends v.InferInput<typeof aspectRatioSchema> {} 32 + export interface Main extends v.InferInput<typeof mainSchema> {}
+17
lib/lexicons/types/pub/leaflet/blocks/math.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.math"), 7 + ), 8 + tex: /*#__PURE__*/ v.string(), 9 + }); 10 + 11 + type main$schematype = typeof _mainSchema; 12 + 13 + export interface mainSchema extends main$schematype {} 14 + 15 + export const mainSchema = _mainSchema as mainSchema; 16 + 17 + export interface Main extends v.InferInput<typeof mainSchema> {}
+23
lib/lexicons/types/pub/leaflet/blocks/text.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import * as PubLeafletRichtextFacet from "../richtext/facet.js"; 4 + 5 + const _mainSchema = /*#__PURE__*/ v.object({ 6 + $type: /*#__PURE__*/ v.optional( 7 + /*#__PURE__*/ v.literal("pub.leaflet.blocks.text"), 8 + ), 9 + get facets() { 10 + return /*#__PURE__*/ v.optional( 11 + /*#__PURE__*/ v.array(PubLeafletRichtextFacet.mainSchema), 12 + ); 13 + }, 14 + plaintext: /*#__PURE__*/ v.string(), 15 + }); 16 + 17 + type main$schematype = typeof _mainSchema; 18 + 19 + export interface mainSchema extends main$schematype {} 20 + 21 + export const mainSchema = _mainSchema as mainSchema; 22 + 23 + export interface Main extends v.InferInput<typeof mainSchema> {}
+41
lib/lexicons/types/pub/leaflet/blocks/unorderedList.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import * as PubLeafletBlocksHeader from "./header.js"; 4 + import * as PubLeafletBlocksImage from "./image.js"; 5 + import * as PubLeafletBlocksText from "./text.js"; 6 + 7 + const _listItemSchema = /*#__PURE__*/ v.object({ 8 + $type: /*#__PURE__*/ v.optional( 9 + /*#__PURE__*/ v.literal("pub.leaflet.blocks.unorderedList#listItem"), 10 + ), 11 + get children() { 12 + return /*#__PURE__*/ v.optional(/*#__PURE__*/ v.array(listItemSchema)); 13 + }, 14 + get content() { 15 + return /*#__PURE__*/ v.variant([ 16 + PubLeafletBlocksHeader.mainSchema, 17 + PubLeafletBlocksImage.mainSchema, 18 + PubLeafletBlocksText.mainSchema, 19 + ]); 20 + }, 21 + }); 22 + const _mainSchema = /*#__PURE__*/ v.object({ 23 + $type: /*#__PURE__*/ v.optional( 24 + /*#__PURE__*/ v.literal("pub.leaflet.blocks.unorderedList"), 25 + ), 26 + get children() { 27 + return /*#__PURE__*/ v.array(listItemSchema); 28 + }, 29 + }); 30 + 31 + type listItem$schematype = typeof _listItemSchema; 32 + type main$schematype = typeof _mainSchema; 33 + 34 + export interface listItemSchema extends listItem$schematype {} 35 + export interface mainSchema extends main$schematype {} 36 + 37 + export const listItemSchema = _listItemSchema as listItemSchema; 38 + export const mainSchema = _mainSchema as mainSchema; 39 + 40 + export interface ListItem extends v.InferInput<typeof listItemSchema> {} 41 + export interface Main extends v.InferInput<typeof mainSchema> {}
+20
lib/lexicons/types/pub/leaflet/blocks/website.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.website"), 7 + ), 8 + description: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 9 + previewImage: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.blob()), 10 + src: /*#__PURE__*/ v.genericUriString(), 11 + title: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 12 + }); 13 + 14 + type main$schematype = typeof _mainSchema; 15 + 16 + export interface mainSchema extends main$schematype {} 17 + 18 + export const mainSchema = _mainSchema as mainSchema; 19 + 20 + export interface Main extends v.InferInput<typeof mainSchema> {}
+47
lib/lexicons/types/pub/leaflet/document.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + import * as ComAtprotoRepoStrongRef from "../../com/atproto/repo/strongRef.js"; 5 + import * as PubLeafletPagesLinearDocument from "./pages/linearDocument.js"; 6 + 7 + const _mainSchema = /*#__PURE__*/ v.record( 8 + /*#__PURE__*/ v.tidString(), 9 + /*#__PURE__*/ v.object({ 10 + $type: /*#__PURE__*/ v.literal("pub.leaflet.document"), 11 + author: /*#__PURE__*/ v.actorIdentifierString(), 12 + description: /*#__PURE__*/ v.optional( 13 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 14 + /*#__PURE__*/ v.stringLength(0, 3000), 15 + /*#__PURE__*/ v.stringGraphemes(0, 300), 16 + ]), 17 + ), 18 + get pages() { 19 + return /*#__PURE__*/ v.array( 20 + /*#__PURE__*/ v.variant([PubLeafletPagesLinearDocument.mainSchema]), 21 + ); 22 + }, 23 + get postRef() { 24 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 25 + }, 26 + publication: /*#__PURE__*/ v.resourceUriString(), 27 + publishedAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 28 + title: /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 29 + /*#__PURE__*/ v.stringLength(0, 1280), 30 + /*#__PURE__*/ v.stringGraphemes(0, 128), 31 + ]), 32 + }), 33 + ); 34 + 35 + type main$schematype = typeof _mainSchema; 36 + 37 + export interface mainSchema extends main$schematype {} 38 + 39 + export const mainSchema = _mainSchema as mainSchema; 40 + 41 + export interface Main extends v.InferInput<typeof mainSchema> {} 42 + 43 + declare module "@atcute/lexicons/ambient" { 44 + interface Records { 45 + "pub.leaflet.document": mainSchema; 46 + } 47 + }
+74
lib/lexicons/types/pub/leaflet/pages/linearDocument.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import * as PubLeafletBlocksCode from "../blocks/code.js"; 4 + import * as PubLeafletBlocksHeader from "../blocks/header.js"; 5 + import * as PubLeafletBlocksImage from "../blocks/image.js"; 6 + import * as PubLeafletBlocksMath from "../blocks/math.js"; 7 + import * as PubLeafletBlocksText from "../blocks/text.js"; 8 + import * as PubLeafletBlocksUnorderedList from "../blocks/unorderedList.js"; 9 + import * as PubLeafletBlocksWebsite from "../blocks/website.js"; 10 + 11 + const _blockSchema = /*#__PURE__*/ v.object({ 12 + $type: /*#__PURE__*/ v.optional( 13 + /*#__PURE__*/ v.literal("pub.leaflet.pages.linearDocument#block"), 14 + ), 15 + alignment: /*#__PURE__*/ v.optional( 16 + /*#__PURE__*/ v.string< 17 + "#textAlignCenter" | "#textAlignLeft" | "#textAlignRight" | (string & {}) 18 + >(), 19 + ), 20 + get block() { 21 + return /*#__PURE__*/ v.variant([ 22 + PubLeafletBlocksCode.mainSchema, 23 + PubLeafletBlocksHeader.mainSchema, 24 + PubLeafletBlocksImage.mainSchema, 25 + PubLeafletBlocksMath.mainSchema, 26 + PubLeafletBlocksText.mainSchema, 27 + PubLeafletBlocksUnorderedList.mainSchema, 28 + PubLeafletBlocksWebsite.mainSchema, 29 + ]); 30 + }, 31 + }); 32 + const _mainSchema = /*#__PURE__*/ v.object({ 33 + $type: /*#__PURE__*/ v.optional( 34 + /*#__PURE__*/ v.literal("pub.leaflet.pages.linearDocument"), 35 + ), 36 + get blocks() { 37 + return /*#__PURE__*/ v.optional(/*#__PURE__*/ v.array(blockSchema)); 38 + }, 39 + }); 40 + const _textAlignCenterSchema = /*#__PURE__*/ v.literal( 41 + "pub.leaflet.pages.linearDocument#textAlignCenter", 42 + ); 43 + const _textAlignLeftSchema = /*#__PURE__*/ v.literal( 44 + "pub.leaflet.pages.linearDocument#textAlignLeft", 45 + ); 46 + const _textAlignRightSchema = /*#__PURE__*/ v.literal( 47 + "pub.leaflet.pages.linearDocument#textAlignRight", 48 + ); 49 + 50 + type block$schematype = typeof _blockSchema; 51 + type main$schematype = typeof _mainSchema; 52 + type textAlignCenter$schematype = typeof _textAlignCenterSchema; 53 + type textAlignLeft$schematype = typeof _textAlignLeftSchema; 54 + type textAlignRight$schematype = typeof _textAlignRightSchema; 55 + 56 + export interface blockSchema extends block$schematype {} 57 + export interface mainSchema extends main$schematype {} 58 + export interface textAlignCenterSchema extends textAlignCenter$schematype {} 59 + export interface textAlignLeftSchema extends textAlignLeft$schematype {} 60 + export interface textAlignRightSchema extends textAlignRight$schematype {} 61 + 62 + export const blockSchema = _blockSchema as blockSchema; 63 + export const mainSchema = _mainSchema as mainSchema; 64 + export const textAlignCenterSchema = 65 + _textAlignCenterSchema as textAlignCenterSchema; 66 + export const textAlignLeftSchema = _textAlignLeftSchema as textAlignLeftSchema; 67 + export const textAlignRightSchema = 68 + _textAlignRightSchema as textAlignRightSchema; 69 + 70 + export interface Block extends v.InferInput<typeof blockSchema> {} 71 + export interface Main extends v.InferInput<typeof mainSchema> {} 72 + export type TextAlignCenter = v.InferInput<typeof textAlignCenterSchema>; 73 + export type TextAlignLeft = v.InferInput<typeof textAlignLeftSchema>; 74 + export type TextAlignRight = v.InferInput<typeof textAlignRightSchema>;
+119
lib/lexicons/types/pub/leaflet/richtext/facet.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + 4 + const _boldSchema = /*#__PURE__*/ v.object({ 5 + $type: /*#__PURE__*/ v.optional( 6 + /*#__PURE__*/ v.literal("pub.leaflet.richtext.facet#bold"), 7 + ), 8 + }); 9 + const _byteSliceSchema = /*#__PURE__*/ v.object({ 10 + $type: /*#__PURE__*/ v.optional( 11 + /*#__PURE__*/ v.literal("pub.leaflet.richtext.facet#byteSlice"), 12 + ), 13 + byteEnd: /*#__PURE__*/ v.integer(), 14 + byteStart: /*#__PURE__*/ v.integer(), 15 + }); 16 + const _codeSchema = /*#__PURE__*/ v.object({ 17 + $type: /*#__PURE__*/ v.optional( 18 + /*#__PURE__*/ v.literal("pub.leaflet.richtext.facet#code"), 19 + ), 20 + }); 21 + const _highlightSchema = /*#__PURE__*/ v.object({ 22 + $type: /*#__PURE__*/ v.optional( 23 + /*#__PURE__*/ v.literal("pub.leaflet.richtext.facet#highlight"), 24 + ), 25 + }); 26 + const _idSchema = /*#__PURE__*/ v.object({ 27 + $type: /*#__PURE__*/ v.optional( 28 + /*#__PURE__*/ v.literal("pub.leaflet.richtext.facet#id"), 29 + ), 30 + id: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 31 + }); 32 + const _italicSchema = /*#__PURE__*/ v.object({ 33 + $type: /*#__PURE__*/ v.optional( 34 + /*#__PURE__*/ v.literal("pub.leaflet.richtext.facet#italic"), 35 + ), 36 + }); 37 + const _linkSchema = /*#__PURE__*/ v.object({ 38 + $type: /*#__PURE__*/ v.optional( 39 + /*#__PURE__*/ v.literal("pub.leaflet.richtext.facet#link"), 40 + ), 41 + uri: /*#__PURE__*/ v.genericUriString(), 42 + }); 43 + const _mainSchema = /*#__PURE__*/ v.object({ 44 + $type: /*#__PURE__*/ v.optional( 45 + /*#__PURE__*/ v.literal("pub.leaflet.richtext.facet"), 46 + ), 47 + get features() { 48 + return /*#__PURE__*/ v.array( 49 + /*#__PURE__*/ v.variant([ 50 + boldSchema, 51 + codeSchema, 52 + highlightSchema, 53 + idSchema, 54 + italicSchema, 55 + linkSchema, 56 + strikethroughSchema, 57 + underlineSchema, 58 + ]), 59 + ); 60 + }, 61 + get index() { 62 + return byteSliceSchema; 63 + }, 64 + }); 65 + const _strikethroughSchema = /*#__PURE__*/ v.object({ 66 + $type: /*#__PURE__*/ v.optional( 67 + /*#__PURE__*/ v.literal("pub.leaflet.richtext.facet#strikethrough"), 68 + ), 69 + }); 70 + const _underlineSchema = /*#__PURE__*/ v.object({ 71 + $type: /*#__PURE__*/ v.optional( 72 + /*#__PURE__*/ v.literal("pub.leaflet.richtext.facet#underline"), 73 + ), 74 + }); 75 + 76 + type bold$schematype = typeof _boldSchema; 77 + type byteSlice$schematype = typeof _byteSliceSchema; 78 + type code$schematype = typeof _codeSchema; 79 + type highlight$schematype = typeof _highlightSchema; 80 + type id$schematype = typeof _idSchema; 81 + type italic$schematype = typeof _italicSchema; 82 + type link$schematype = typeof _linkSchema; 83 + type main$schematype = typeof _mainSchema; 84 + type strikethrough$schematype = typeof _strikethroughSchema; 85 + type underline$schematype = typeof _underlineSchema; 86 + 87 + export interface boldSchema extends bold$schematype {} 88 + export interface byteSliceSchema extends byteSlice$schematype {} 89 + export interface codeSchema extends code$schematype {} 90 + export interface highlightSchema extends highlight$schematype {} 91 + export interface idSchema extends id$schematype {} 92 + export interface italicSchema extends italic$schematype {} 93 + export interface linkSchema extends link$schematype {} 94 + export interface mainSchema extends main$schematype {} 95 + export interface strikethroughSchema extends strikethrough$schematype {} 96 + export interface underlineSchema extends underline$schematype {} 97 + 98 + export const boldSchema = _boldSchema as boldSchema; 99 + export const byteSliceSchema = _byteSliceSchema as byteSliceSchema; 100 + export const codeSchema = _codeSchema as codeSchema; 101 + export const highlightSchema = _highlightSchema as highlightSchema; 102 + export const idSchema = _idSchema as idSchema; 103 + export const italicSchema = _italicSchema as italicSchema; 104 + export const linkSchema = _linkSchema as linkSchema; 105 + export const mainSchema = _mainSchema as mainSchema; 106 + export const strikethroughSchema = _strikethroughSchema as strikethroughSchema; 107 + export const underlineSchema = _underlineSchema as underlineSchema; 108 + 109 + export interface Bold extends v.InferInput<typeof boldSchema> {} 110 + export interface ByteSlice extends v.InferInput<typeof byteSliceSchema> {} 111 + export interface Code extends v.InferInput<typeof codeSchema> {} 112 + export interface Highlight extends v.InferInput<typeof highlightSchema> {} 113 + export interface Id extends v.InferInput<typeof idSchema> {} 114 + export interface Italic extends v.InferInput<typeof italicSchema> {} 115 + export interface Link extends v.InferInput<typeof linkSchema> {} 116 + export interface Main extends v.InferInput<typeof mainSchema> {} 117 + export interface Strikethrough 118 + extends v.InferInput<typeof strikethroughSchema> {} 119 + export interface Underline extends v.InferInput<typeof underlineSchema> {}
+11
lib/schema.ts
··· 1 + import { z } from "astro/zod"; 2 + 3 + export const LeafletDocumentSchema = z.object({ 4 + rkey: z.string(), 5 + cid: z.string(), 6 + title: z.string(), 7 + description: z.string(), 8 + author: z.string(), 9 + publication: z.string(), 10 + publishedAt: z.string(), 11 + });
+91
lib/types.ts
··· 1 + import type { Client } from "@atcute/client"; 2 + import type { ActorIdentifier } from "@atcute/lexicons"; 3 + import type { XRPCProcedures, XRPCQueries } from "@atcute/lexicons/ambient"; 4 + import type { PubLeafletRichtextFacet } from "./lexicons/index.js"; 5 + 6 + export interface LiveLeafletLoaderOptions { 7 + /** 8 + * @description Your repo is your DID (did:plc... or did:web...) or handle (username.bsky.social). You can find this information using: https://pdsls.dev 9 + */ 10 + repo: string; 11 + } 12 + 13 + export interface StaticLeafletLoaderOptions { 14 + /** 15 + * @description Your repo is your DID (did:plc... or did:web...) or handle (username.bsky.social). You can find this information using: https://pdsls.dev 16 + */ 17 + repo: string; 18 + /** 19 + * @description The number of records leaflet records to return for getCollection, the default being 50. The range can be from 1 to 100. 20 + * @default 50 21 + */ 22 + limit?: number; 23 + /** 24 + * @description Whether or not the records should be returned in reverse order. 25 + * @default undefined 26 + */ 27 + reverse?: boolean; 28 + } 29 + 30 + export interface LeafletDocumentRecord { 31 + $type: "pub.leaflet.document"; 32 + pages: { [x: string]: unknown }; 33 + title: string; 34 + author: string; 35 + description: string; 36 + publication: string; 37 + publishedAt: string; 38 + } 39 + 40 + export interface LeafletDocumentView { 41 + rkey: string; 42 + cid: string; 43 + title: string; 44 + description: string; 45 + author: string; 46 + publication: string; 47 + publishedAt: string; 48 + } 49 + 50 + export interface MiniDoc { 51 + did: string; 52 + handle: string; 53 + pds: string; 54 + signing_key: string; 55 + } 56 + 57 + export interface CollectionFilter { 58 + limit?: number; 59 + reverse?: boolean; 60 + cursor?: string; 61 + } 62 + 63 + export interface EntryFilter { 64 + id?: string; 65 + } 66 + 67 + export interface GetLeafletDocumentsParams { 68 + repo: ActorIdentifier; 69 + rpc: Client<XRPCQueries, XRPCProcedures>; 70 + cursor?: string; 71 + limit?: number; 72 + reverse?: boolean; 73 + } 74 + 75 + export interface GetSingleLeafletDocumentParams { 76 + repo: ActorIdentifier; 77 + rpc: Client<XRPCQueries, XRPCProcedures>; 78 + id: string; 79 + } 80 + 81 + export interface Facet extends PubLeafletRichtextFacet.Main {} 82 + export interface RichTextSegment { 83 + text: string; 84 + facet?: Exclude<Facet["features"], { $type: string }>; 85 + } 86 + 87 + // yoinked from: https://github.com/mary-ext/atcute/blob/trunk/packages/lexicons/lexicons/lib/syntax/handle.ts 88 + /** 89 + * represents a decentralized identifier (DID). 90 + */ 91 + export type Did<Method extends string = string> = `did:${Method}:${string}`;
+388
lib/utils.ts
··· 1 + import type {} from "@atcute/atproto"; 2 + import { is } from "@atcute/lexicons"; 3 + import { AtUri, UnicodeString } from "@atproto/api"; 4 + import katex from "katex"; 5 + import sanitizeHTML from "sanitize-html"; 6 + import { 7 + PubLeafletBlocksBlockquote, 8 + PubLeafletBlocksCode, 9 + PubLeafletBlocksHeader, 10 + PubLeafletBlocksHorizontalRule, 11 + PubLeafletBlocksIframe, 12 + PubLeafletBlocksImage, 13 + PubLeafletBlocksMath, 14 + PubLeafletBlocksText, 15 + PubLeafletBlocksUnorderedList, 16 + PubLeafletPagesLinearDocument, 17 + } from "./lexicons/index.js"; 18 + import type { 19 + Did, 20 + Facet, 21 + GetLeafletDocumentsParams, 22 + GetSingleLeafletDocumentParams, 23 + LeafletDocumentRecord, 24 + LeafletDocumentView, 25 + MiniDoc, 26 + RichTextSegment, 27 + } from "./types.js"; 28 + 29 + export class LiveLoaderError extends Error { 30 + constructor( 31 + message: string, 32 + public code?: string, 33 + ) { 34 + super(message); 35 + this.name = "LiveLoaderError"; 36 + } 37 + } 38 + 39 + export function uriToRkey(uri: string): string { 40 + const u = AtUri.make(uri); 41 + if (!u.rkey) { 42 + throw new Error("failed to get rkey"); 43 + } 44 + return u.rkey; 45 + } 46 + 47 + export async function resolveMiniDoc(handleOrDid: string) { 48 + try { 49 + const response = await fetch( 50 + `https://slingshot.microcosm.blue/xrpc/com.bad-example.identity.resolveMiniDoc?identifier=${handleOrDid}`, 51 + ); 52 + 53 + if (!response.ok || response.status >= 300) { 54 + throw new Error( 55 + `could not resolve did doc due to invalid handle or did ${handleOrDid}`, 56 + ); 57 + } 58 + const data = (await response.json()) as MiniDoc; 59 + 60 + return { 61 + pds: data.pds, 62 + did: data.did, 63 + }; 64 + } catch { 65 + throw new Error(`failed to resolve handle: ${handleOrDid}`); 66 + } 67 + } 68 + 69 + export async function getLeafletDocuments({ 70 + repo, 71 + reverse, 72 + cursor, 73 + rpc, 74 + limit, 75 + }: GetLeafletDocumentsParams) { 76 + const { ok, data } = await rpc.get("com.atproto.repo.listRecords", { 77 + params: { 78 + collection: "pub.leaflet.document", 79 + cursor, 80 + reverse, 81 + limit, 82 + repo, 83 + }, 84 + }); 85 + 86 + if (!ok) { 87 + throw new LiveLoaderError( 88 + "error fetching leaflet documents", 89 + "DOCUMENT_FETCH_ERROR", 90 + ); 91 + } 92 + 93 + return { 94 + documents: data?.records, 95 + cursor: data?.cursor, 96 + }; 97 + } 98 + 99 + export async function getSingleLeafletDocument({ 100 + rpc, 101 + repo, 102 + id, 103 + }: GetSingleLeafletDocumentParams) { 104 + const { ok, data } = await rpc.get("com.atproto.repo.getRecord", { 105 + params: { 106 + collection: "pub.leaflet.document", 107 + repo, 108 + rkey: id, 109 + }, 110 + }); 111 + 112 + if (!ok) { 113 + throw new LiveLoaderError( 114 + "error fetching single document", 115 + "DOCUMENT_FETCH_ERROR", 116 + ); 117 + } 118 + 119 + return data; 120 + } 121 + 122 + export function leafletDocumentRecordToView({ 123 + uri, 124 + cid, 125 + value, 126 + }: { 127 + uri: string; 128 + cid: string; 129 + value: LeafletDocumentRecord; 130 + }): LeafletDocumentView { 131 + return { 132 + rkey: uriToRkey(uri), 133 + cid, 134 + title: value.title, 135 + description: value.description, 136 + author: value.author, 137 + publication: value.publication, 138 + publishedAt: value.publishedAt, 139 + }; 140 + } 141 + 142 + export function leafletBlocksToHTML({ 143 + record, 144 + did, 145 + }: { 146 + record: LeafletDocumentRecord; 147 + did: string; 148 + }) { 149 + let html = ""; 150 + const firstPage = record.pages[0]; 151 + let blocks: PubLeafletPagesLinearDocument.Block[] = []; 152 + 153 + if (is(PubLeafletPagesLinearDocument.mainSchema, firstPage)) { 154 + blocks = firstPage.blocks || []; 155 + } 156 + 157 + for (const block of blocks) { 158 + html += parseBlocks({ block, did }); 159 + } 160 + 161 + return sanitizeHTML(html, { 162 + allowedAttributes: { 163 + "*": ["class", "style"], 164 + img: ["src", "height", "width", "alt"], 165 + a: ["href", "target", "rel"], 166 + iframe: ["height", "allow", "loading", "src"], 167 + }, 168 + allowedTags: [ 169 + "img", 170 + "pre", 171 + "code", 172 + "p", 173 + "a", 174 + "b", 175 + "s", 176 + "ul", 177 + "li", 178 + "i", 179 + "h1", 180 + "h2", 181 + "h3", 182 + "h4", 183 + "h5", 184 + "h6", 185 + "hr", 186 + "div", 187 + "span", 188 + "blockquote", 189 + "iframe", 190 + ], 191 + selfClosing: ["img"], 192 + }); 193 + } 194 + 195 + export class RichText { 196 + unicodeText: UnicodeString; 197 + facets?: Facet[]; 198 + constructor(props: { text: string; facets: Facet[] }) { 199 + this.unicodeText = new UnicodeString(props.text); 200 + this.facets = props.facets; 201 + if (this.facets) { 202 + this.facets = this.facets 203 + .filter((facet) => facet.index.byteStart <= facet.index.byteEnd) 204 + .sort((a, b) => a.index.byteStart - b.index.byteStart); 205 + } 206 + } 207 + 208 + *segments(): Generator<RichTextSegment, void, void> { 209 + const facets = this.facets || []; 210 + if (!facets.length) { 211 + yield { text: this.unicodeText.utf16 }; 212 + return; 213 + } 214 + 215 + let textCursor = 0; 216 + let facetCursor = 0; 217 + do { 218 + const currFacet = facets[facetCursor]; 219 + if (currFacet) { 220 + if (textCursor < currFacet.index.byteStart) { 221 + yield { 222 + text: this.unicodeText.slice(textCursor, currFacet.index.byteStart), 223 + }; 224 + } else if (textCursor > currFacet.index.byteStart) { 225 + facetCursor++; 226 + continue; 227 + } 228 + if (currFacet.index.byteStart < currFacet.index.byteEnd) { 229 + const subtext = this.unicodeText.slice( 230 + currFacet.index.byteStart, 231 + currFacet.index.byteEnd, 232 + ); 233 + if (!subtext.trim()) { 234 + // dont empty string entities 235 + yield { text: subtext }; 236 + } else { 237 + yield { text: subtext, facet: currFacet.features }; 238 + } 239 + } 240 + textCursor = currFacet.index.byteEnd; 241 + facetCursor++; 242 + } 243 + } while (facetCursor < facets.length); 244 + if (textCursor < this.unicodeText.length) { 245 + yield { 246 + text: this.unicodeText.slice(textCursor, this.unicodeText.length), 247 + }; 248 + } 249 + } 250 + } 251 + 252 + export function parseTextBlock(block: PubLeafletBlocksText.Main) { 253 + let html = ""; 254 + const rt = new RichText({ 255 + text: block.plaintext, 256 + facets: block.facets || [], 257 + }); 258 + const children = []; 259 + for (const segment of rt.segments()) { 260 + const link = segment.facet?.find( 261 + (segment) => segment.$type === "pub.leaflet.richtext.facet#link", 262 + ); 263 + const isBold = segment.facet?.find( 264 + (segment) => segment.$type === "pub.leaflet.richtext.facet#bold", 265 + ); 266 + const isCode = segment.facet?.find( 267 + (segment) => segment.$type === "pub.leaflet.richtext.facet#code", 268 + ); 269 + const isStrikethrough = segment.facet?.find( 270 + (segment) => segment.$type === "pub.leaflet.richtext.facet#strikethrough", 271 + ); 272 + const isUnderline = segment.facet?.find( 273 + (segment) => segment.$type === "pub.leaflet.richtext.facet#underline", 274 + ); 275 + const isItalic = segment.facet?.find( 276 + (segment) => segment.$type === "pub.leaflet.richtext.facet#italic", 277 + ); 278 + if (isCode) { 279 + children.push(`<pre><code>${segment.text}</code></pre>`); 280 + } else if (link) { 281 + children.push( 282 + `<a href="${link.uri}" target="_blank" rel="noopener noreferrer">${segment.text}</a>`, 283 + ); 284 + } else if (isBold) { 285 + children.push(`<b>${segment.text}</b>`); 286 + } else if (isStrikethrough) { 287 + children.push(`<s>${segment.text}</s>`); 288 + } else if (isUnderline) { 289 + children.push( 290 + `<span style="text-decoration:underline;">${segment.text}</span>`, 291 + ); 292 + } else if (isItalic) { 293 + children.push(`<i>${segment.text}</i>`); 294 + } else { 295 + children.push(`${segment.text}`); 296 + } 297 + } 298 + html += `<p>${children.join("")}</p>`; 299 + 300 + return html.trim(); 301 + } 302 + 303 + export function parseBlocks({ 304 + block, 305 + did, 306 + }: { 307 + block: PubLeafletPagesLinearDocument.Block; 308 + did: string; 309 + }): string { 310 + let html = ""; 311 + 312 + if (is(PubLeafletBlocksText.mainSchema, block.block)) { 313 + html += parseTextBlock(block.block); 314 + } 315 + 316 + if (is(PubLeafletBlocksHeader.mainSchema, block.block)) { 317 + if (block.block.level === 1) { 318 + html += `<h2>${block.block.plaintext}</h2>`; 319 + } 320 + } 321 + if (is(PubLeafletBlocksHeader.mainSchema, block.block)) { 322 + if (block.block.level === 2) { 323 + html += `<h3>${block.block.plaintext}</h3>`; 324 + } 325 + } 326 + if (is(PubLeafletBlocksHeader.mainSchema, block.block)) { 327 + if (block.block.level === 3) { 328 + html += `<h4>${block.block.plaintext}</h4>`; 329 + } 330 + } 331 + if (is(PubLeafletBlocksHeader.mainSchema, block.block)) { 332 + if (!block.block.level) { 333 + html += `<h6>${block.block.plaintext}</h6>`; 334 + } 335 + } 336 + 337 + if (is(PubLeafletBlocksHorizontalRule.mainSchema, block.block)) { 338 + html += `<hr />`; 339 + } 340 + if (is(PubLeafletBlocksUnorderedList.mainSchema, block.block)) { 341 + html += `<ul>${block.block.children.map((child) => renderListItem({ item: child, did })).join("")}</ul>`; 342 + } 343 + 344 + if (is(PubLeafletBlocksMath.mainSchema, block.block)) { 345 + html += `<div>${katex.renderToString(block.block.tex, { displayMode: true, output: "html", throwOnError: false })}</div>`; 346 + } 347 + 348 + if (is(PubLeafletBlocksCode.mainSchema, block.block)) { 349 + html += `<pre><code data-language=${block.block.language}>${block.block.plaintext}</code></pre>`; 350 + } 351 + 352 + if (is(PubLeafletBlocksImage.mainSchema, block.block)) { 353 + // @ts-ignore 354 + 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>`; 355 + } 356 + 357 + if (is(PubLeafletBlocksBlockquote.mainSchema, block.block)) { 358 + html += `<blockquote>${parseTextBlock(block.block)}</blockquote>`; 359 + } 360 + 361 + if (is(PubLeafletBlocksIframe.mainSchema, block.block)) { 362 + // @ts-ignore 363 + html += `<iframe height="${block.block.height}" allow="fullscreen" loading="lazy" src="${block.block.url}"></iframe>`; 364 + } 365 + 366 + return html.trim(); 367 + } 368 + 369 + export function renderListItem({ 370 + item, 371 + did, 372 + }: { 373 + item: PubLeafletBlocksUnorderedList.ListItem; 374 + did: string; 375 + }): string { 376 + const children: string | null = item.children?.length 377 + ? `<ul>${item.children.map((child) => renderListItem({ item: child, did }))}</ul>` 378 + : ""; 379 + 380 + return `<li>${parseBlocks({ block: { block: item.content }, did })}${children}</li>`; 381 + } 382 + 383 + // yoinked from: https://github.com/mary-ext/atcute/blob/trunk/packages/lexicons/lexicons/lib/syntax/handle.ts 384 + const PLC_DID_RE = /^did:plc:([a-z2-7]{24})$/; 385 + 386 + export const isPlcDid = (input: string): input is Did<"plc"> => { 387 + return input.length === 32 && PLC_DID_RE.test(input); 388 + };
+33 -7
package.json
··· 1 1 { 2 - "name": "leaflet-loader-astro", 3 - "version": "0.0.1", 2 + "name": "@nulfrost/leaflet-loader-astro", 3 + "version": "1.3.0", 4 4 "description": "A leaflet.pub astro collection loader", 5 5 "keywords": [ 6 6 "astro", ··· 12 12 "bugs": { 13 13 "url": "https://github.com/nulfrost/leaflet-loader-astro/issues" 14 14 }, 15 - "author": "Dane Miller", 15 + "author": "Dane Miller <me@dane.computer>", 16 16 "repository": { 17 17 "type": "git", 18 18 "url": "git+https://github.com/nulfrost/leaflet-loader-astro.git" 19 19 }, 20 20 "scripts": { 21 - "build": "tsc" 21 + "lint": "biome lint", 22 + "format": "biome format --write ./lib", 23 + "lex": "lex-cli generate -c ./lex.config.js", 24 + "test": "vitest --run", 25 + "typecheck": "tsc", 26 + "release": "pnpm run build && changeset publish", 27 + "build": "rm -rf dist && tsup --format esm --dts", 28 + "pack": "rm -rf *.tgz && pnpm build && pnpm pack" 22 29 }, 23 30 "license": "MIT", 24 31 "files": [ 25 32 "dist" 26 33 ], 34 + "publishConfig": { 35 + "access": "public" 36 + }, 27 37 "type": "module", 28 38 "main": "dist/index.js", 39 + "module": "dist/index.js", 40 + "types": "dist/index.d.ts", 41 + "exports": { 42 + ".": { 43 + "import": "./dist/index.js", 44 + "require": "./dist/index.cjs" 45 + } 46 + }, 29 47 "devDependencies": { 48 + "@atcute/atproto": "^3.1.1", 49 + "@atcute/lex-cli": "^2.1.1", 50 + "@biomejs/biome": "2.1.3", 51 + "@changesets/cli": "^2.29.5", 52 + "@types/bun": "^1.2.19", 53 + "@types/sanitize-html": "^2.16.0", 30 54 "astro": "^5.12.8", 31 55 "tsup": "^8.5.0", 32 56 "typescript": "^5.9.2", 33 57 "vitest": "^3.2.4" 34 58 }, 35 59 "dependencies": { 36 - "@atproto/api": "^0.16.1", 37 - "@atproto/did": "^0.1.5", 38 - "@atproto/syntax": "^0.4.0" 60 + "@atcute/client": "^4.0.3", 61 + "@atcute/lexicons": "^1.1.0", 62 + "@atproto/api": "^0.16.2", 63 + "katex": "^0.16.22", 64 + "sanitize-html": "^2.17.0" 39 65 } 40 66 }
+1068 -51
pnpm-lock.yaml
··· 8 8 9 9 .: 10 10 dependencies: 11 + '@atcute/client': 12 + specifier: ^4.0.3 13 + version: 4.0.3 14 + '@atcute/lexicons': 15 + specifier: ^1.1.0 16 + version: 1.1.0 11 17 '@atproto/api': 12 - specifier: ^0.16.1 13 - version: 0.16.1 14 - '@atproto/did': 15 - specifier: ^0.1.5 16 - version: 0.1.5 17 - '@atproto/syntax': 18 - specifier: ^0.4.0 19 - version: 0.4.0 18 + specifier: ^0.16.2 19 + version: 0.16.2 20 + katex: 21 + specifier: ^0.16.22 22 + version: 0.16.22 23 + sanitize-html: 24 + specifier: ^2.17.0 25 + version: 2.17.0 20 26 devDependencies: 27 + '@atcute/atproto': 28 + specifier: ^3.1.1 29 + version: 3.1.1 30 + '@atcute/lex-cli': 31 + specifier: ^2.1.1 32 + version: 2.1.1 33 + '@biomejs/biome': 34 + specifier: 2.1.3 35 + version: 2.1.3 36 + '@changesets/cli': 37 + specifier: ^2.29.5 38 + version: 2.29.5 39 + '@types/bun': 40 + specifier: ^1.2.19 41 + version: 1.2.19(@types/react@19.1.9) 42 + '@types/sanitize-html': 43 + specifier: ^2.16.0 44 + version: 2.16.0 21 45 astro: 22 46 specifier: ^5.12.8 23 - version: 5.12.8(@types/node@24.2.0)(rollup@4.46.2)(typescript@5.9.2) 47 + version: 5.12.9(@types/node@24.2.1)(rollup@4.46.2)(typescript@5.9.2) 24 48 tsup: 25 49 specifier: ^8.5.0 26 50 version: 8.5.0(postcss@8.5.6)(typescript@5.9.2) ··· 29 53 version: 5.9.2 30 54 vitest: 31 55 specifier: ^3.2.4 32 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.2.0) 56 + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.2.1) 33 57 34 58 packages: 35 59 ··· 50 74 resolution: {integrity: sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==} 51 75 engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} 52 76 53 - '@atproto/api@0.16.1': 54 - resolution: {integrity: sha512-w48BlTmzKym7nZETWxgiuUX/wwRXU3xsLLKORWo/xtGnwlvpchUFnHKI3k4ttYJ2/JQE59+/4C16BaLzDyiU2w==} 77 + '@atcute/atproto@3.1.1': 78 + resolution: {integrity: sha512-D+RLTIPF0xLu7BPZY8KSewAPemJFh+3n3zeQ3ROsLxbTtCHbrTDMAmAFexaVRAPGcPYrwXaBUlv7yZjScJolMg==} 79 + 80 + '@atcute/client@4.0.3': 81 + resolution: {integrity: sha512-RIOZWFVLca/HiPAAUDqQPOdOreCxTbL5cb+WUf5yqQOKIu5yEAP3eksinmlLmgIrlr5qVOE7brazUUzaskFCfw==} 82 + 83 + '@atcute/identity@1.0.3': 84 + resolution: {integrity: sha512-mNMxbKHFGys03A8JXKk0KfMBzdd0vrYMzZZWjpw1nYTs0+ea6bo5S1hwqVUZxHdo1gFHSe/t63jxQIF4yL9aKw==} 85 + 86 + '@atcute/lex-cli@2.1.1': 87 + resolution: {integrity: sha512-QaR0sOP8Z24opGHKsSfleDbP/ahUb6HECkVaOqSwG7ORZzbLK1w0265o1BRjCVr2dT6FxlsMUa2Ge85JMA9bxg==} 88 + hasBin: true 89 + 90 + '@atcute/lexicon-doc@1.0.3': 91 + resolution: {integrity: sha512-U7rinsTOwXGGcrF6/s7GzTXargcQpDr4BTrj5ci/XTK+POEK5jpcI+Ag1fF932pBX3k97em6y4TWwTSO8M/McQ==} 92 + 93 + '@atcute/lexicons@1.1.0': 94 + resolution: {integrity: sha512-LFqwnria78xLYb62Ri/+WwQpUTgZp2DuyolNGIIOV1dpiKhFFFh//nscHMA6IExFLQRqWDs3tTjy7zv0h3sf1Q==} 95 + 96 + '@atproto/api@0.16.2': 97 + resolution: {integrity: sha512-sSTg31J8ws8DNaoiizp+/uJideRxRaJsq+Nyl8rnSxGw0w3oCvoeRU19iRWh2t0jZEmiRJAGkveGu23NKmPYEQ==} 55 98 56 99 '@atproto/common-web@0.4.2': 57 100 resolution: {integrity: sha512-vrXwGNoFGogodjQvJDxAeP3QbGtawgZute2ed1XdRO0wMixLk3qewtikZm06H259QDJVu6voKC5mubml+WgQUw==} 58 - 59 - '@atproto/did@0.1.5': 60 - resolution: {integrity: sha512-8+1D08QdGE5TF0bB0vV8HLVrVZJeLNITpRTUVEoABNMRaUS7CoYSVb0+JNQDeJIVmqMjOL8dOjvCUDkp3gEaGQ==} 61 101 62 102 '@atproto/lexicon@0.4.12': 63 103 resolution: {integrity: sha512-fcEvEQ1GpQYF5igZ4IZjPWEoWVpsEF22L9RexxLS3ptfySXLflEyH384e7HITzO/73McDeaJx3lqHIuqn9ulnw==} ··· 81 121 engines: {node: '>=6.0.0'} 82 122 hasBin: true 83 123 124 + '@babel/runtime@7.28.2': 125 + resolution: {integrity: sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==} 126 + engines: {node: '>=6.9.0'} 127 + 84 128 '@babel/types@7.28.2': 85 129 resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} 86 130 engines: {node: '>=6.9.0'} 87 131 132 + '@badrap/valita@0.4.6': 133 + resolution: {integrity: sha512-4kdqcjyxo/8RQ8ayjms47HCWZIF5981oE5nIenbfThKDxWXtEHKipAOWlflpPJzZx9y/JWYQkp18Awr7VuepFg==} 134 + engines: {node: '>= 18'} 135 + 136 + '@biomejs/biome@2.1.3': 137 + resolution: {integrity: sha512-KE/tegvJIxTkl7gJbGWSgun7G6X/n2M6C35COT6ctYrAy7SiPyNvi6JtoQERVK/VRbttZfgGq96j2bFmhmnH4w==} 138 + engines: {node: '>=14.21.3'} 139 + hasBin: true 140 + 141 + '@biomejs/cli-darwin-arm64@2.1.3': 142 + resolution: {integrity: sha512-LFLkSWRoSGS1wVUD/BE6Nlt2dSn0ulH3XImzg2O/36BoToJHKXjSxzPEMAqT9QvwVtk7/9AQhZpTneERU9qaXA==} 143 + engines: {node: '>=14.21.3'} 144 + cpu: [arm64] 145 + os: [darwin] 146 + 147 + '@biomejs/cli-darwin-x64@2.1.3': 148 + resolution: {integrity: sha512-Q/4OTw8P9No9QeowyxswcWdm0n2MsdCwWcc5NcKQQvzwPjwuPdf8dpPPf4r+x0RWKBtl1FLiAUtJvBlri6DnYw==} 149 + engines: {node: '>=14.21.3'} 150 + cpu: [x64] 151 + os: [darwin] 152 + 153 + '@biomejs/cli-linux-arm64-musl@2.1.3': 154 + resolution: {integrity: sha512-KXouFSBnoxAWZYDQrnNRzZBbt5s9UJkIm40hdvSL9mBxSSoxRFQJbtg1hP3aa8A2SnXyQHxQfpiVeJlczZt76w==} 155 + engines: {node: '>=14.21.3'} 156 + cpu: [arm64] 157 + os: [linux] 158 + 159 + '@biomejs/cli-linux-arm64@2.1.3': 160 + resolution: {integrity: sha512-2hS6LgylRqMFmAZCOFwYrf77QMdUwJp49oe8PX/O8+P2yKZMSpyQTf3Eo5ewnsMFUEmYbPOskafdV1ds1MZMJA==} 161 + engines: {node: '>=14.21.3'} 162 + cpu: [arm64] 163 + os: [linux] 164 + 165 + '@biomejs/cli-linux-x64-musl@2.1.3': 166 + resolution: {integrity: sha512-KaLAxnROouzIWtl6a0Y88r/4hW5oDUJTIqQorOTVQITaKQsKjZX4XCUmHIhdEk8zMnaiLZzRTAwk1yIAl+mIew==} 167 + engines: {node: '>=14.21.3'} 168 + cpu: [x64] 169 + os: [linux] 170 + 171 + '@biomejs/cli-linux-x64@2.1.3': 172 + resolution: {integrity: sha512-NxlSCBhLvQtWGagEztfAZ4WcE1AkMTntZV65ZvR+J9jp06+EtOYEBPQndA70ZGhHbEDG57bR6uNvqkd1WrEYVA==} 173 + engines: {node: '>=14.21.3'} 174 + cpu: [x64] 175 + os: [linux] 176 + 177 + '@biomejs/cli-win32-arm64@2.1.3': 178 + resolution: {integrity: sha512-V9CUZCtWH4u0YwyCYbQ3W5F4ZGPWp2C2TYcsiWFNNyRfmOW1j/TY/jAurl33SaRjgZPO5UUhGyr9m6BN9t84NQ==} 179 + engines: {node: '>=14.21.3'} 180 + cpu: [arm64] 181 + os: [win32] 182 + 183 + '@biomejs/cli-win32-x64@2.1.3': 184 + resolution: {integrity: sha512-dxy599q6lgp8ANPpR8sDMscwdp9oOumEsVXuVCVT9N2vAho8uYXlCz53JhxX6LtJOXaE73qzgkGQ7QqvFlMC0g==} 185 + engines: {node: '>=14.21.3'} 186 + cpu: [x64] 187 + os: [win32] 188 + 88 189 '@capsizecss/unpack@2.4.0': 89 190 resolution: {integrity: sha512-GrSU71meACqcmIUxPYOJvGKF0yryjN/L1aCuE9DViCTJI7bfkjgYDPD1zbNDcINJwSSP6UaBZY9GAbYDO7re0Q==} 90 191 192 + '@changesets/apply-release-plan@7.0.12': 193 + resolution: {integrity: sha512-EaET7As5CeuhTzvXTQCRZeBUcisoYPDDcXvgTE/2jmmypKp0RC7LxKj/yzqeh/1qFTZI7oDGFcL1PHRuQuketQ==} 194 + 195 + '@changesets/assemble-release-plan@6.0.9': 196 + resolution: {integrity: sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ==} 197 + 198 + '@changesets/changelog-git@0.2.1': 199 + resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} 200 + 201 + '@changesets/cli@2.29.5': 202 + resolution: {integrity: sha512-0j0cPq3fgxt2dPdFsg4XvO+6L66RC0pZybT9F4dG5TBrLA3jA/1pNkdTXH9IBBVHkgsKrNKenI3n1mPyPlIydg==} 203 + hasBin: true 204 + 205 + '@changesets/config@3.1.1': 206 + resolution: {integrity: sha512-bd+3Ap2TKXxljCggI0mKPfzCQKeV/TU4yO2h2C6vAihIo8tzseAn2e7klSuiyYYXvgu53zMN1OeYMIQkaQoWnA==} 207 + 208 + '@changesets/errors@0.2.0': 209 + resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} 210 + 211 + '@changesets/get-dependents-graph@2.1.3': 212 + resolution: {integrity: sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==} 213 + 214 + '@changesets/get-release-plan@4.0.13': 215 + resolution: {integrity: sha512-DWG1pus72FcNeXkM12tx+xtExyH/c9I1z+2aXlObH3i9YA7+WZEVaiHzHl03thpvAgWTRaH64MpfHxozfF7Dvg==} 216 + 217 + '@changesets/get-version-range-type@0.4.0': 218 + resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} 219 + 220 + '@changesets/git@3.0.4': 221 + resolution: {integrity: sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==} 222 + 223 + '@changesets/logger@0.1.1': 224 + resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} 225 + 226 + '@changesets/parse@0.4.1': 227 + resolution: {integrity: sha512-iwksMs5Bf/wUItfcg+OXrEpravm5rEd9Bf4oyIPL4kVTmJQ7PNDSd6MDYkpSJR1pn7tz/k8Zf2DhTCqX08Ou+Q==} 228 + 229 + '@changesets/pre@2.0.2': 230 + resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==} 231 + 232 + '@changesets/read@0.6.5': 233 + resolution: {integrity: sha512-UPzNGhsSjHD3Veb0xO/MwvasGe8eMyNrR/sT9gR8Q3DhOQZirgKhhXv/8hVsI0QpPjR004Z9iFxoJU6in3uGMg==} 234 + 235 + '@changesets/should-skip-package@0.1.2': 236 + resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==} 237 + 238 + '@changesets/types@4.1.0': 239 + resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} 240 + 241 + '@changesets/types@6.1.0': 242 + resolution: {integrity: sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==} 243 + 244 + '@changesets/write@0.4.0': 245 + resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} 246 + 91 247 '@emnapi/runtime@1.4.5': 92 248 resolution: {integrity: sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==} 93 249 ··· 247 403 cpu: [x64] 248 404 os: [win32] 249 405 406 + '@externdefs/collider@0.3.0': 407 + resolution: {integrity: sha512-x5CpeZ4c8n+1wMFthUMWSQKqCGcQo52/Qbda5ES+JFRRg/D8Ep6/JOvUUq5HExFuv/wW+6UYG2U/mXzw0IAd8Q==} 408 + peerDependencies: 409 + '@badrap/valita': ^0.4.4 410 + 250 411 '@img/sharp-darwin-arm64@0.33.5': 251 412 resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} 252 413 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} ··· 369 530 '@jridgewell/trace-mapping@0.3.29': 370 531 resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} 371 532 533 + '@manypkg/find-root@1.1.0': 534 + resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} 535 + 536 + '@manypkg/get-packages@1.1.3': 537 + resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} 538 + 539 + '@nodelib/fs.scandir@2.1.5': 540 + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 541 + engines: {node: '>= 8'} 542 + 543 + '@nodelib/fs.stat@2.0.5': 544 + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} 545 + engines: {node: '>= 8'} 546 + 547 + '@nodelib/fs.walk@1.2.8': 548 + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 549 + engines: {node: '>= 8'} 550 + 372 551 '@oslojs/encoding@1.1.0': 373 552 resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} 374 553 ··· 509 688 '@swc/helpers@0.5.17': 510 689 resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} 511 690 691 + '@types/bun@1.2.19': 692 + resolution: {integrity: sha512-d9ZCmrH3CJ2uYKXQIUuZ/pUnTqIvLDS0SK7pFmbx8ma+ziH/FRMoAq5bYpRG7y+w1gl+HgyNZbtqgMq4W4e2Lg==} 693 + 512 694 '@types/chai@5.2.2': 513 695 resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} 514 696 ··· 536 718 '@types/nlcst@2.0.3': 537 719 resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==} 538 720 539 - '@types/node@24.2.0': 540 - resolution: {integrity: sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==} 721 + '@types/node@12.20.55': 722 + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} 723 + 724 + '@types/node@24.2.1': 725 + resolution: {integrity: sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==} 726 + 727 + '@types/react@19.1.9': 728 + resolution: {integrity: sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA==} 729 + 730 + '@types/sanitize-html@2.16.0': 731 + resolution: {integrity: sha512-l6rX1MUXje5ztPT0cAFtUayXF06DqPhRyfVXareEN5gGCFaP/iwsxIyKODr9XDhfxPpN6vXUFNfo5kZMXCxBtw==} 541 732 542 733 '@types/unist@3.0.3': 543 734 resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} ··· 582 773 ansi-align@3.0.1: 583 774 resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} 584 775 776 + ansi-colors@4.1.3: 777 + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} 778 + engines: {node: '>=6'} 779 + 585 780 ansi-regex@5.0.1: 586 781 resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 587 782 engines: {node: '>=8'} ··· 605 800 resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} 606 801 engines: {node: '>= 8'} 607 802 803 + argparse@1.0.10: 804 + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} 805 + 608 806 argparse@2.0.1: 609 807 resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 610 808 ··· 615 813 array-iterate@2.0.1: 616 814 resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==} 617 815 816 + array-union@2.1.0: 817 + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} 818 + engines: {node: '>=8'} 819 + 618 820 assertion-error@2.0.1: 619 821 resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} 620 822 engines: {node: '>=12'} 621 823 622 - astro@5.12.8: 623 - resolution: {integrity: sha512-KkJ7FR+c2SyZYlpakm48XBiuQcRsrVtdjG5LN5an0givI/tLik+ePJ4/g3qrAVhYMjJOxBA2YgFQxANPiWB+Mw==} 824 + astro@5.12.9: 825 + resolution: {integrity: sha512-cZ7kZ61jyE5nwSrFKSRyf5Gds+uJELqQxJFqMkcgiWQvhWZJUSShn8Uz3yc9WLyLw5Kim5P5un9SkJSGogfEZQ==} 624 826 engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0'} 625 827 hasBin: true 626 828 ··· 643 845 base64-js@1.5.1: 644 846 resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} 645 847 848 + better-path-resolve@1.0.0: 849 + resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} 850 + engines: {node: '>=4'} 851 + 646 852 blob-to-buffer@1.2.9: 647 853 resolution: {integrity: sha512-BF033y5fN6OCofD3vgHmNtwZWRcq9NLyyxyILx9hfMy1sXYy4ojFl765hJ2lP0YaN2fuxPaLO2Vzzoxy0FLFFA==} 648 854 ··· 653 859 brace-expansion@2.0.2: 654 860 resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} 655 861 862 + braces@3.0.3: 863 + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} 864 + engines: {node: '>=8'} 865 + 656 866 brotli@1.3.3: 657 867 resolution: {integrity: sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==} 868 + 869 + bun-types@1.2.19: 870 + resolution: {integrity: sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ==} 871 + peerDependencies: 872 + '@types/react': ^19 658 873 659 874 bundle-require@5.1.0: 660 875 resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} ··· 690 905 character-entities@2.0.2: 691 906 resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} 692 907 908 + chardet@0.7.0: 909 + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} 910 + 693 911 check-error@2.1.1: 694 912 resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} 695 913 engines: {node: '>= 16'} ··· 697 915 chokidar@4.0.3: 698 916 resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} 699 917 engines: {node: '>= 14.16.0'} 918 + 919 + ci-info@3.9.0: 920 + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} 921 + engines: {node: '>=8'} 700 922 701 923 ci-info@4.3.0: 702 924 resolution: {integrity: sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==} ··· 735 957 resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} 736 958 engines: {node: '>= 6'} 737 959 960 + commander@8.3.0: 961 + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} 962 + engines: {node: '>= 12'} 963 + 738 964 common-ancestor-path@1.0.1: 739 965 resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==} 740 966 ··· 771 997 engines: {node: '>=4'} 772 998 hasBin: true 773 999 1000 + csstype@3.1.3: 1001 + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} 1002 + 774 1003 debug@4.4.1: 775 1004 resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} 776 1005 engines: {node: '>=6.0'} ··· 787 1016 resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} 788 1017 engines: {node: '>=6'} 789 1018 1019 + deepmerge@4.3.1: 1020 + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} 1021 + engines: {node: '>=0.10.0'} 1022 + 790 1023 defu@6.1.4: 791 1024 resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} 792 1025 ··· 797 1030 destr@2.0.5: 798 1031 resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} 799 1032 1033 + detect-indent@6.1.0: 1034 + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} 1035 + engines: {node: '>=8'} 1036 + 800 1037 detect-libc@2.0.4: 801 1038 resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} 802 1039 engines: {node: '>=8'} ··· 818 1055 resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} 819 1056 engines: {node: '>=0.3.1'} 820 1057 1058 + dir-glob@3.0.1: 1059 + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} 1060 + engines: {node: '>=8'} 1061 + 821 1062 dlv@1.1.3: 822 1063 resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} 823 1064 1065 + dom-serializer@2.0.0: 1066 + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} 1067 + 1068 + domelementtype@2.3.0: 1069 + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} 1070 + 1071 + domhandler@5.0.3: 1072 + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} 1073 + engines: {node: '>= 4'} 1074 + 1075 + domutils@3.2.2: 1076 + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} 1077 + 824 1078 dset@3.1.4: 825 1079 resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} 826 1080 engines: {node: '>=4'} ··· 837 1091 emoji-regex@9.2.2: 838 1092 resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} 839 1093 1094 + enquirer@2.4.1: 1095 + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} 1096 + engines: {node: '>=8.6'} 1097 + 1098 + entities@4.5.0: 1099 + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} 1100 + engines: {node: '>=0.12'} 1101 + 840 1102 entities@6.0.1: 841 1103 resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} 842 1104 engines: {node: '>=0.12'} ··· 849 1111 engines: {node: '>=18'} 850 1112 hasBin: true 851 1113 1114 + escape-string-regexp@4.0.0: 1115 + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} 1116 + engines: {node: '>=10'} 1117 + 852 1118 escape-string-regexp@5.0.0: 853 1119 resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} 854 1120 engines: {node: '>=12'} 855 1121 1122 + esm-env@1.2.2: 1123 + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} 1124 + 1125 + esprima@4.0.1: 1126 + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} 1127 + engines: {node: '>=4'} 1128 + hasBin: true 1129 + 856 1130 estree-walker@2.0.2: 857 1131 resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} 858 1132 ··· 869 1143 extend@3.0.2: 870 1144 resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} 871 1145 1146 + extendable-error@0.1.7: 1147 + resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} 1148 + 1149 + external-editor@3.1.0: 1150 + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} 1151 + engines: {node: '>=4'} 1152 + 872 1153 fast-deep-equal@3.1.3: 873 1154 resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} 874 1155 1156 + fast-glob@3.3.3: 1157 + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} 1158 + engines: {node: '>=8.6.0'} 1159 + 1160 + fastq@1.19.1: 1161 + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} 1162 + 875 1163 fdir@6.4.6: 876 1164 resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} 877 1165 peerDependencies: ··· 880 1168 picomatch: 881 1169 optional: true 882 1170 1171 + fill-range@7.1.1: 1172 + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} 1173 + engines: {node: '>=8'} 1174 + 1175 + find-up@4.1.0: 1176 + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} 1177 + engines: {node: '>=8'} 1178 + 883 1179 fix-dts-default-cjs-exports@1.0.1: 884 1180 resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} 885 1181 ··· 896 1192 foreground-child@3.3.1: 897 1193 resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} 898 1194 engines: {node: '>=14'} 1195 + 1196 + fs-extra@7.0.1: 1197 + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} 1198 + engines: {node: '>=6 <7 || >=8'} 1199 + 1200 + fs-extra@8.1.0: 1201 + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} 1202 + engines: {node: '>=6 <7 || >=8'} 899 1203 900 1204 fsevents@2.3.3: 901 1205 resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} ··· 909 1213 github-slugger@2.0.0: 910 1214 resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} 911 1215 1216 + glob-parent@5.1.2: 1217 + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 1218 + engines: {node: '>= 6'} 1219 + 912 1220 glob@10.4.5: 913 1221 resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} 914 1222 hasBin: true 915 1223 1224 + globby@11.1.0: 1225 + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} 1226 + engines: {node: '>=10'} 1227 + 1228 + graceful-fs@4.2.11: 1229 + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} 1230 + 916 1231 graphemer@1.4.0: 917 1232 resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} 918 1233 ··· 955 1270 html-void-elements@3.0.0: 956 1271 resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} 957 1272 1273 + htmlparser2@8.0.2: 1274 + resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} 1275 + 958 1276 http-cache-semantics@4.2.0: 959 1277 resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} 1278 + 1279 + human-id@4.1.1: 1280 + resolution: {integrity: sha512-3gKm/gCSUipeLsRYZbbdA1BD83lBoWUkZ7G9VFrhWPAU76KwYo5KR8V28bpoPm/ygy0x5/GCbpRQdY7VLYCoIg==} 1281 + hasBin: true 1282 + 1283 + iconv-lite@0.4.24: 1284 + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} 1285 + engines: {node: '>=0.10.0'} 1286 + 1287 + ignore@5.3.2: 1288 + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} 1289 + engines: {node: '>= 4'} 960 1290 961 1291 import-meta-resolve@4.1.0: 962 1292 resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} ··· 972 1302 engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 973 1303 hasBin: true 974 1304 1305 + is-extglob@2.1.1: 1306 + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 1307 + engines: {node: '>=0.10.0'} 1308 + 975 1309 is-fullwidth-code-point@3.0.0: 976 1310 resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 977 1311 engines: {node: '>=8'} 978 1312 1313 + is-glob@4.0.3: 1314 + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 1315 + engines: {node: '>=0.10.0'} 1316 + 979 1317 is-inside-container@1.0.0: 980 1318 resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} 981 1319 engines: {node: '>=14.16'} 982 1320 hasBin: true 983 1321 1322 + is-number@7.0.0: 1323 + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 1324 + engines: {node: '>=0.12.0'} 1325 + 984 1326 is-plain-obj@4.1.0: 985 1327 resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} 986 1328 engines: {node: '>=12'} 987 1329 1330 + is-plain-object@5.0.0: 1331 + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} 1332 + engines: {node: '>=0.10.0'} 1333 + 1334 + is-subdir@1.2.0: 1335 + resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} 1336 + engines: {node: '>=4'} 1337 + 1338 + is-windows@1.0.2: 1339 + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} 1340 + engines: {node: '>=0.10.0'} 1341 + 988 1342 is-wsl@3.1.0: 989 1343 resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} 990 1344 engines: {node: '>=16'} ··· 1005 1359 js-tokens@9.0.1: 1006 1360 resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} 1007 1361 1362 + js-yaml@3.14.1: 1363 + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} 1364 + hasBin: true 1365 + 1008 1366 js-yaml@4.1.0: 1009 1367 resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} 1368 + hasBin: true 1369 + 1370 + jsonfile@4.0.0: 1371 + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} 1372 + 1373 + katex@0.16.22: 1374 + resolution: {integrity: sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==} 1010 1375 hasBin: true 1011 1376 1012 1377 kleur@3.0.3: ··· 1028 1393 resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} 1029 1394 engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 1030 1395 1396 + locate-path@5.0.0: 1397 + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} 1398 + engines: {node: '>=8'} 1399 + 1031 1400 lodash.sortby@4.7.0: 1032 1401 resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} 1402 + 1403 + lodash.startcase@4.4.0: 1404 + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} 1033 1405 1034 1406 longest-streak@3.1.0: 1035 1407 resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} ··· 1091 1463 mdn-data@2.12.2: 1092 1464 resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} 1093 1465 1466 + merge2@1.4.1: 1467 + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} 1468 + engines: {node: '>= 8'} 1469 + 1094 1470 micromark-core-commonmark@2.0.3: 1095 1471 resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} 1096 1472 ··· 1175 1551 micromark@4.0.2: 1176 1552 resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} 1177 1553 1554 + micromatch@4.0.8: 1555 + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} 1556 + engines: {node: '>=8.6'} 1557 + 1178 1558 minimatch@9.0.5: 1179 1559 resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 1180 1560 engines: {node: '>=16 || 14 >=14.17'} ··· 1186 1566 mlly@1.7.4: 1187 1567 resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} 1188 1568 1569 + mri@1.2.0: 1570 + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} 1571 + engines: {node: '>=4'} 1572 + 1189 1573 mrmime@2.0.1: 1190 1574 resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} 1191 1575 engines: {node: '>=10'} ··· 1246 1630 oniguruma-to-es@4.3.3: 1247 1631 resolution: {integrity: sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==} 1248 1632 1633 + os-tmpdir@1.0.2: 1634 + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} 1635 + engines: {node: '>=0.10.0'} 1636 + 1637 + outdent@0.5.0: 1638 + resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} 1639 + 1640 + p-filter@2.1.0: 1641 + resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} 1642 + engines: {node: '>=8'} 1643 + 1644 + p-limit@2.3.0: 1645 + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} 1646 + engines: {node: '>=6'} 1647 + 1249 1648 p-limit@6.2.0: 1250 1649 resolution: {integrity: sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==} 1251 1650 engines: {node: '>=18'} 1252 1651 1652 + p-locate@4.1.0: 1653 + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} 1654 + engines: {node: '>=8'} 1655 + 1656 + p-map@2.1.0: 1657 + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} 1658 + engines: {node: '>=6'} 1659 + 1253 1660 p-queue@8.1.0: 1254 1661 resolution: {integrity: sha512-mxLDbbGIBEXTJL0zEx8JIylaj3xQ7Z/7eEVjcF9fJX4DBiH9oqe+oahYnlKKxm0Ci9TlWTyhSHgygxMxjIB2jw==} 1255 1662 engines: {node: '>=18'} ··· 1258 1665 resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} 1259 1666 engines: {node: '>=14.16'} 1260 1667 1668 + p-try@2.2.0: 1669 + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} 1670 + engines: {node: '>=6'} 1671 + 1261 1672 package-json-from-dist@1.0.1: 1262 1673 resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} 1263 1674 1675 + package-manager-detector@0.2.11: 1676 + resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} 1677 + 1264 1678 package-manager-detector@1.3.0: 1265 1679 resolution: {integrity: sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==} 1266 1680 ··· 1269 1683 1270 1684 parse-latin@7.0.0: 1271 1685 resolution: {integrity: sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==} 1686 + 1687 + parse-srcset@1.0.2: 1688 + resolution: {integrity: sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==} 1272 1689 1273 1690 parse5@7.3.0: 1274 1691 resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} 1275 1692 1693 + path-exists@4.0.0: 1694 + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} 1695 + engines: {node: '>=8'} 1696 + 1276 1697 path-key@3.1.1: 1277 1698 resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 1278 1699 engines: {node: '>=8'} ··· 1280 1701 path-scurry@1.11.1: 1281 1702 resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} 1282 1703 engines: {node: '>=16 || 14 >=14.18'} 1704 + 1705 + path-type@4.0.0: 1706 + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} 1707 + engines: {node: '>=8'} 1283 1708 1284 1709 pathe@2.0.3: 1285 1710 resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} ··· 1299 1724 resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} 1300 1725 engines: {node: '>=12'} 1301 1726 1727 + pify@4.0.1: 1728 + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} 1729 + engines: {node: '>=6'} 1730 + 1302 1731 pirates@4.0.7: 1303 1732 resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} 1304 1733 engines: {node: '>= 6'} ··· 1328 1757 resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} 1329 1758 engines: {node: ^10 || ^12 || >=14} 1330 1759 1760 + prettier@2.8.8: 1761 + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} 1762 + engines: {node: '>=10.13.0'} 1763 + hasBin: true 1764 + 1765 + prettier@3.6.2: 1766 + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} 1767 + engines: {node: '>=14'} 1768 + hasBin: true 1769 + 1331 1770 prismjs@1.30.0: 1332 1771 resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} 1333 1772 engines: {node: '>=6'} ··· 1346 1785 resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} 1347 1786 engines: {node: '>=6'} 1348 1787 1788 + quansync@0.2.10: 1789 + resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==} 1790 + 1791 + queue-microtask@1.2.3: 1792 + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 1793 + 1349 1794 radix3@1.1.2: 1350 1795 resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} 1796 + 1797 + read-yaml-file@1.1.0: 1798 + resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} 1799 + engines: {node: '>=6'} 1351 1800 1352 1801 readdirp@4.1.2: 1353 1802 resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} ··· 1409 1858 retext@9.0.0: 1410 1859 resolution: {integrity: sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==} 1411 1860 1861 + reusify@1.1.0: 1862 + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} 1863 + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 1864 + 1412 1865 rollup@4.46.2: 1413 1866 resolution: {integrity: sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==} 1414 1867 engines: {node: '>=18.0.0', npm: '>=8.0.0'} 1415 1868 hasBin: true 1869 + 1870 + run-parallel@1.2.0: 1871 + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 1872 + 1873 + safer-buffer@2.1.2: 1874 + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} 1875 + 1876 + sanitize-html@2.17.0: 1877 + resolution: {integrity: sha512-dLAADUSS8rBwhaevT12yCezvioCA+bmUTPH/u57xKPT8d++voeYE6HeluA/bPbQ15TwDBG2ii+QZIEmYx8VdxA==} 1416 1878 1417 1879 semver@7.7.2: 1418 1880 resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} ··· 1447 1909 sisteransi@1.0.5: 1448 1910 resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} 1449 1911 1450 - smol-toml@1.4.1: 1451 - resolution: {integrity: sha512-CxdwHXyYTONGHThDbq5XdwbFsuY4wlClRGejfE2NtwUtiHYsP1QtNsHb/hnj31jKYSchztJsaA8pSQoVzkfCFg==} 1912 + slash@3.0.0: 1913 + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} 1914 + engines: {node: '>=8'} 1915 + 1916 + smol-toml@1.4.2: 1917 + resolution: {integrity: sha512-rInDH6lCNiEyn3+hH8KVGFdbjc099j47+OSgbMrfDYX1CmXLfdKd7qi6IfcWj2wFxvSVkuI46M+wPGYfEOEj6g==} 1452 1918 engines: {node: '>= 18'} 1453 1919 1454 1920 source-map-js@1.2.1: ··· 1463 1929 space-separated-tokens@2.0.2: 1464 1930 resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} 1465 1931 1932 + spawndamnit@3.0.1: 1933 + resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} 1934 + 1935 + sprintf-js@1.0.3: 1936 + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} 1937 + 1466 1938 stackback@0.0.2: 1467 1939 resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} 1468 1940 ··· 1492 1964 resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} 1493 1965 engines: {node: '>=12'} 1494 1966 1967 + strip-bom@3.0.0: 1968 + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} 1969 + engines: {node: '>=4'} 1970 + 1495 1971 strip-literal@3.0.0: 1496 1972 resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} 1497 1973 ··· 1499 1975 resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} 1500 1976 engines: {node: '>=16 || 14 >=14.17'} 1501 1977 hasBin: true 1978 + 1979 + term-size@2.2.1: 1980 + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} 1981 + engines: {node: '>=8'} 1502 1982 1503 1983 thenify-all@1.6.0: 1504 1984 resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} ··· 1536 2016 resolution: {integrity: sha512-AldGGlDP0PNgwppe2quAvuBl18UcjuNtOnDuUkqhd6ipPqrYYBt3aTxK1QTsBVknk97lS2JcafWMghjGWFtunw==} 1537 2017 hasBin: true 1538 2018 2019 + tmp@0.0.33: 2020 + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} 2021 + engines: {node: '>=0.6.0'} 2022 + 2023 + to-regex-range@5.0.1: 2024 + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 2025 + engines: {node: '>=8.0'} 2026 + 1539 2027 tr46@0.0.3: 1540 2028 resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} 1541 2029 ··· 1649 2137 1650 2138 unist-util-visit@5.0.0: 1651 2139 resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} 2140 + 2141 + universalify@0.1.2: 2142 + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} 2143 + engines: {node: '>= 4.0.0'} 1652 2144 1653 2145 unstorage@1.16.1: 1654 2146 resolution: {integrity: sha512-gdpZ3guLDhz+zWIlYP1UwQ259tG5T5vYRzDaHMkQ1bBY1SQPutvZnrRjTFaWUUpseErJIgAZS51h6NOcZVZiqQ==} ··· 1763 2255 yaml: 1764 2256 optional: true 1765 2257 1766 - vite@7.0.6: 1767 - resolution: {integrity: sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==} 2258 + vite@7.1.1: 2259 + resolution: {integrity: sha512-yJ+Mp7OyV+4S+afWo+QyoL9jFWD11QFH0i5i7JypnfTcA1rmgxCbiA8WwAICDEtZ1Z1hzrVhN8R8rGTqkTY8ZQ==} 1768 2260 engines: {node: ^20.19.0 || >=22.12.0} 1769 2261 hasBin: true 1770 2262 peerDependencies: ··· 1943 2435 remark-rehype: 11.1.2 1944 2436 remark-smartypants: 3.0.2 1945 2437 shiki: 3.9.2 1946 - smol-toml: 1.4.1 2438 + smol-toml: 1.4.2 1947 2439 unified: 11.0.5 1948 2440 unist-util-remove-position: 5.0.0 1949 2441 unist-util-visit: 5.0.0 ··· 1968 2460 transitivePeerDependencies: 1969 2461 - supports-color 1970 2462 1971 - '@atproto/api@0.16.1': 2463 + '@atcute/atproto@3.1.1': 2464 + dependencies: 2465 + '@atcute/lexicons': 1.1.0 2466 + 2467 + '@atcute/client@4.0.3': 2468 + dependencies: 2469 + '@atcute/identity': 1.0.3 2470 + '@atcute/lexicons': 1.1.0 2471 + 2472 + '@atcute/identity@1.0.3': 2473 + dependencies: 2474 + '@atcute/lexicons': 1.1.0 2475 + '@badrap/valita': 0.4.6 2476 + 2477 + '@atcute/lex-cli@2.1.1': 2478 + dependencies: 2479 + '@atcute/lexicon-doc': 1.0.3 2480 + '@badrap/valita': 0.4.6 2481 + '@externdefs/collider': 0.3.0(@badrap/valita@0.4.6) 2482 + picocolors: 1.1.1 2483 + prettier: 3.6.2 2484 + 2485 + '@atcute/lexicon-doc@1.0.3': 2486 + dependencies: 2487 + '@badrap/valita': 0.4.6 2488 + 2489 + '@atcute/lexicons@1.1.0': 2490 + dependencies: 2491 + esm-env: 1.2.2 2492 + 2493 + '@atproto/api@0.16.2': 1972 2494 dependencies: 1973 2495 '@atproto/common-web': 0.4.2 1974 2496 '@atproto/lexicon': 0.4.12 ··· 1984 2506 graphemer: 1.4.0 1985 2507 multiformats: 9.9.0 1986 2508 uint8arrays: 3.0.0 1987 - zod: 3.25.76 1988 - 1989 - '@atproto/did@0.1.5': 1990 - dependencies: 1991 2509 zod: 3.25.76 1992 2510 1993 2511 '@atproto/lexicon@0.4.12': ··· 2012 2530 '@babel/parser@7.28.0': 2013 2531 dependencies: 2014 2532 '@babel/types': 7.28.2 2533 + 2534 + '@babel/runtime@7.28.2': {} 2015 2535 2016 2536 '@babel/types@7.28.2': 2017 2537 dependencies: 2018 2538 '@babel/helper-string-parser': 7.27.1 2019 2539 '@babel/helper-validator-identifier': 7.27.1 2020 2540 2541 + '@badrap/valita@0.4.6': {} 2542 + 2543 + '@biomejs/biome@2.1.3': 2544 + optionalDependencies: 2545 + '@biomejs/cli-darwin-arm64': 2.1.3 2546 + '@biomejs/cli-darwin-x64': 2.1.3 2547 + '@biomejs/cli-linux-arm64': 2.1.3 2548 + '@biomejs/cli-linux-arm64-musl': 2.1.3 2549 + '@biomejs/cli-linux-x64': 2.1.3 2550 + '@biomejs/cli-linux-x64-musl': 2.1.3 2551 + '@biomejs/cli-win32-arm64': 2.1.3 2552 + '@biomejs/cli-win32-x64': 2.1.3 2553 + 2554 + '@biomejs/cli-darwin-arm64@2.1.3': 2555 + optional: true 2556 + 2557 + '@biomejs/cli-darwin-x64@2.1.3': 2558 + optional: true 2559 + 2560 + '@biomejs/cli-linux-arm64-musl@2.1.3': 2561 + optional: true 2562 + 2563 + '@biomejs/cli-linux-arm64@2.1.3': 2564 + optional: true 2565 + 2566 + '@biomejs/cli-linux-x64-musl@2.1.3': 2567 + optional: true 2568 + 2569 + '@biomejs/cli-linux-x64@2.1.3': 2570 + optional: true 2571 + 2572 + '@biomejs/cli-win32-arm64@2.1.3': 2573 + optional: true 2574 + 2575 + '@biomejs/cli-win32-x64@2.1.3': 2576 + optional: true 2577 + 2021 2578 '@capsizecss/unpack@2.4.0': 2022 2579 dependencies: 2023 2580 blob-to-buffer: 1.2.9 ··· 2026 2583 transitivePeerDependencies: 2027 2584 - encoding 2028 2585 2586 + '@changesets/apply-release-plan@7.0.12': 2587 + dependencies: 2588 + '@changesets/config': 3.1.1 2589 + '@changesets/get-version-range-type': 0.4.0 2590 + '@changesets/git': 3.0.4 2591 + '@changesets/should-skip-package': 0.1.2 2592 + '@changesets/types': 6.1.0 2593 + '@manypkg/get-packages': 1.1.3 2594 + detect-indent: 6.1.0 2595 + fs-extra: 7.0.1 2596 + lodash.startcase: 4.4.0 2597 + outdent: 0.5.0 2598 + prettier: 2.8.8 2599 + resolve-from: 5.0.0 2600 + semver: 7.7.2 2601 + 2602 + '@changesets/assemble-release-plan@6.0.9': 2603 + dependencies: 2604 + '@changesets/errors': 0.2.0 2605 + '@changesets/get-dependents-graph': 2.1.3 2606 + '@changesets/should-skip-package': 0.1.2 2607 + '@changesets/types': 6.1.0 2608 + '@manypkg/get-packages': 1.1.3 2609 + semver: 7.7.2 2610 + 2611 + '@changesets/changelog-git@0.2.1': 2612 + dependencies: 2613 + '@changesets/types': 6.1.0 2614 + 2615 + '@changesets/cli@2.29.5': 2616 + dependencies: 2617 + '@changesets/apply-release-plan': 7.0.12 2618 + '@changesets/assemble-release-plan': 6.0.9 2619 + '@changesets/changelog-git': 0.2.1 2620 + '@changesets/config': 3.1.1 2621 + '@changesets/errors': 0.2.0 2622 + '@changesets/get-dependents-graph': 2.1.3 2623 + '@changesets/get-release-plan': 4.0.13 2624 + '@changesets/git': 3.0.4 2625 + '@changesets/logger': 0.1.1 2626 + '@changesets/pre': 2.0.2 2627 + '@changesets/read': 0.6.5 2628 + '@changesets/should-skip-package': 0.1.2 2629 + '@changesets/types': 6.1.0 2630 + '@changesets/write': 0.4.0 2631 + '@manypkg/get-packages': 1.1.3 2632 + ansi-colors: 4.1.3 2633 + ci-info: 3.9.0 2634 + enquirer: 2.4.1 2635 + external-editor: 3.1.0 2636 + fs-extra: 7.0.1 2637 + mri: 1.2.0 2638 + p-limit: 2.3.0 2639 + package-manager-detector: 0.2.11 2640 + picocolors: 1.1.1 2641 + resolve-from: 5.0.0 2642 + semver: 7.7.2 2643 + spawndamnit: 3.0.1 2644 + term-size: 2.2.1 2645 + 2646 + '@changesets/config@3.1.1': 2647 + dependencies: 2648 + '@changesets/errors': 0.2.0 2649 + '@changesets/get-dependents-graph': 2.1.3 2650 + '@changesets/logger': 0.1.1 2651 + '@changesets/types': 6.1.0 2652 + '@manypkg/get-packages': 1.1.3 2653 + fs-extra: 7.0.1 2654 + micromatch: 4.0.8 2655 + 2656 + '@changesets/errors@0.2.0': 2657 + dependencies: 2658 + extendable-error: 0.1.7 2659 + 2660 + '@changesets/get-dependents-graph@2.1.3': 2661 + dependencies: 2662 + '@changesets/types': 6.1.0 2663 + '@manypkg/get-packages': 1.1.3 2664 + picocolors: 1.1.1 2665 + semver: 7.7.2 2666 + 2667 + '@changesets/get-release-plan@4.0.13': 2668 + dependencies: 2669 + '@changesets/assemble-release-plan': 6.0.9 2670 + '@changesets/config': 3.1.1 2671 + '@changesets/pre': 2.0.2 2672 + '@changesets/read': 0.6.5 2673 + '@changesets/types': 6.1.0 2674 + '@manypkg/get-packages': 1.1.3 2675 + 2676 + '@changesets/get-version-range-type@0.4.0': {} 2677 + 2678 + '@changesets/git@3.0.4': 2679 + dependencies: 2680 + '@changesets/errors': 0.2.0 2681 + '@manypkg/get-packages': 1.1.3 2682 + is-subdir: 1.2.0 2683 + micromatch: 4.0.8 2684 + spawndamnit: 3.0.1 2685 + 2686 + '@changesets/logger@0.1.1': 2687 + dependencies: 2688 + picocolors: 1.1.1 2689 + 2690 + '@changesets/parse@0.4.1': 2691 + dependencies: 2692 + '@changesets/types': 6.1.0 2693 + js-yaml: 3.14.1 2694 + 2695 + '@changesets/pre@2.0.2': 2696 + dependencies: 2697 + '@changesets/errors': 0.2.0 2698 + '@changesets/types': 6.1.0 2699 + '@manypkg/get-packages': 1.1.3 2700 + fs-extra: 7.0.1 2701 + 2702 + '@changesets/read@0.6.5': 2703 + dependencies: 2704 + '@changesets/git': 3.0.4 2705 + '@changesets/logger': 0.1.1 2706 + '@changesets/parse': 0.4.1 2707 + '@changesets/types': 6.1.0 2708 + fs-extra: 7.0.1 2709 + p-filter: 2.1.0 2710 + picocolors: 1.1.1 2711 + 2712 + '@changesets/should-skip-package@0.1.2': 2713 + dependencies: 2714 + '@changesets/types': 6.1.0 2715 + '@manypkg/get-packages': 1.1.3 2716 + 2717 + '@changesets/types@4.1.0': {} 2718 + 2719 + '@changesets/types@6.1.0': {} 2720 + 2721 + '@changesets/write@0.4.0': 2722 + dependencies: 2723 + '@changesets/types': 6.1.0 2724 + fs-extra: 7.0.1 2725 + human-id: 4.1.1 2726 + prettier: 2.8.8 2727 + 2029 2728 '@emnapi/runtime@1.4.5': 2030 2729 dependencies: 2031 2730 tslib: 2.8.1 ··· 2108 2807 2109 2808 '@esbuild/win32-x64@0.25.8': 2110 2809 optional: true 2810 + 2811 + '@externdefs/collider@0.3.0(@badrap/valita@0.4.6)': 2812 + dependencies: 2813 + '@badrap/valita': 0.4.6 2111 2814 2112 2815 '@img/sharp-darwin-arm64@0.33.5': 2113 2816 optionalDependencies: ··· 2207 2910 '@jridgewell/resolve-uri': 3.1.2 2208 2911 '@jridgewell/sourcemap-codec': 1.5.4 2209 2912 2913 + '@manypkg/find-root@1.1.0': 2914 + dependencies: 2915 + '@babel/runtime': 7.28.2 2916 + '@types/node': 12.20.55 2917 + find-up: 4.1.0 2918 + fs-extra: 8.1.0 2919 + 2920 + '@manypkg/get-packages@1.1.3': 2921 + dependencies: 2922 + '@babel/runtime': 7.28.2 2923 + '@changesets/types': 4.1.0 2924 + '@manypkg/find-root': 1.1.0 2925 + fs-extra: 8.1.0 2926 + globby: 11.1.0 2927 + read-yaml-file: 1.1.0 2928 + 2929 + '@nodelib/fs.scandir@2.1.5': 2930 + dependencies: 2931 + '@nodelib/fs.stat': 2.0.5 2932 + run-parallel: 1.2.0 2933 + 2934 + '@nodelib/fs.stat@2.0.5': {} 2935 + 2936 + '@nodelib/fs.walk@1.2.8': 2937 + dependencies: 2938 + '@nodelib/fs.scandir': 2.1.5 2939 + fastq: 1.19.1 2940 + 2210 2941 '@oslojs/encoding@1.1.0': {} 2211 2942 2212 2943 '@pkgjs/parseargs@0.11.0': ··· 2317 3048 dependencies: 2318 3049 tslib: 2.8.1 2319 3050 3051 + '@types/bun@1.2.19(@types/react@19.1.9)': 3052 + dependencies: 3053 + bun-types: 1.2.19(@types/react@19.1.9) 3054 + transitivePeerDependencies: 3055 + - '@types/react' 3056 + 2320 3057 '@types/chai@5.2.2': 2321 3058 dependencies: 2322 3059 '@types/deep-eql': 4.0.2 ··· 2331 3068 2332 3069 '@types/fontkit@2.0.8': 2333 3070 dependencies: 2334 - '@types/node': 24.2.0 3071 + '@types/node': 24.2.1 2335 3072 2336 3073 '@types/hast@3.0.4': 2337 3074 dependencies: ··· 2347 3084 dependencies: 2348 3085 '@types/unist': 3.0.3 2349 3086 2350 - '@types/node@24.2.0': 3087 + '@types/node@12.20.55': {} 3088 + 3089 + '@types/node@24.2.1': 2351 3090 dependencies: 2352 3091 undici-types: 7.10.0 2353 3092 3093 + '@types/react@19.1.9': 3094 + dependencies: 3095 + csstype: 3.1.3 3096 + 3097 + '@types/sanitize-html@2.16.0': 3098 + dependencies: 3099 + htmlparser2: 8.0.2 3100 + 2354 3101 '@types/unist@3.0.3': {} 2355 3102 2356 3103 '@ungap/structured-clone@1.3.0': {} ··· 2363 3110 chai: 5.2.1 2364 3111 tinyrainbow: 2.0.0 2365 3112 2366 - '@vitest/mocker@3.2.4(vite@7.0.6(@types/node@24.2.0))': 3113 + '@vitest/mocker@3.2.4(vite@7.1.1(@types/node@24.2.1))': 2367 3114 dependencies: 2368 3115 '@vitest/spy': 3.2.4 2369 3116 estree-walker: 3.0.3 2370 3117 magic-string: 0.30.17 2371 3118 optionalDependencies: 2372 - vite: 7.0.6(@types/node@24.2.0) 3119 + vite: 7.1.1(@types/node@24.2.1) 2373 3120 2374 3121 '@vitest/pretty-format@3.2.4': 2375 3122 dependencies: ··· 2403 3150 dependencies: 2404 3151 string-width: 4.2.3 2405 3152 3153 + ansi-colors@4.1.3: {} 3154 + 2406 3155 ansi-regex@5.0.1: {} 2407 3156 2408 3157 ansi-regex@6.1.0: {} ··· 2420 3169 normalize-path: 3.0.0 2421 3170 picomatch: 2.3.1 2422 3171 3172 + argparse@1.0.10: 3173 + dependencies: 3174 + sprintf-js: 1.0.3 3175 + 2423 3176 argparse@2.0.1: {} 2424 3177 2425 3178 aria-query@5.3.2: {} 2426 3179 2427 3180 array-iterate@2.0.1: {} 2428 3181 3182 + array-union@2.1.0: {} 3183 + 2429 3184 assertion-error@2.0.1: {} 2430 3185 2431 - astro@5.12.8(@types/node@24.2.0)(rollup@4.46.2)(typescript@5.9.2): 3186 + astro@5.12.9(@types/node@24.2.1)(rollup@4.46.2)(typescript@5.9.2): 2432 3187 dependencies: 2433 3188 '@astrojs/compiler': 2.12.2 2434 3189 '@astrojs/internal-helpers': 0.7.1 ··· 2475 3230 rehype: 13.0.2 2476 3231 semver: 7.7.2 2477 3232 shiki: 3.9.2 2478 - smol-toml: 1.4.1 3233 + smol-toml: 1.4.2 2479 3234 tinyexec: 0.3.2 2480 3235 tinyglobby: 0.2.14 2481 3236 tsconfck: 3.1.6(typescript@5.9.2) ··· 2484 3239 unist-util-visit: 5.0.0 2485 3240 unstorage: 1.16.1 2486 3241 vfile: 6.0.3 2487 - vite: 6.3.5(@types/node@24.2.0) 2488 - vitefu: 1.1.1(vite@6.3.5(@types/node@24.2.0)) 3242 + vite: 6.3.5(@types/node@24.2.1) 3243 + vitefu: 1.1.1(vite@6.3.5(@types/node@24.2.1)) 2489 3244 xxhash-wasm: 1.1.0 2490 3245 yargs-parser: 21.1.1 2491 3246 yocto-spinner: 0.2.3 ··· 2541 3296 2542 3297 base64-js@1.5.1: {} 2543 3298 3299 + better-path-resolve@1.0.0: 3300 + dependencies: 3301 + is-windows: 1.0.2 3302 + 2544 3303 blob-to-buffer@1.2.9: {} 2545 3304 2546 3305 boxen@8.0.1: ··· 2558 3317 dependencies: 2559 3318 balanced-match: 1.0.2 2560 3319 3320 + braces@3.0.3: 3321 + dependencies: 3322 + fill-range: 7.1.1 3323 + 2561 3324 brotli@1.3.3: 2562 3325 dependencies: 2563 3326 base64-js: 1.5.1 2564 3327 3328 + bun-types@1.2.19(@types/react@19.1.9): 3329 + dependencies: 3330 + '@types/node': 24.2.1 3331 + '@types/react': 19.1.9 3332 + 2565 3333 bundle-require@5.1.0(esbuild@0.25.8): 2566 3334 dependencies: 2567 3335 esbuild: 0.25.8 ··· 2589 3357 2590 3358 character-entities@2.0.2: {} 2591 3359 3360 + chardet@0.7.0: {} 3361 + 2592 3362 check-error@2.1.1: {} 2593 3363 2594 3364 chokidar@4.0.3: 2595 3365 dependencies: 2596 3366 readdirp: 4.1.2 2597 3367 3368 + ci-info@3.9.0: {} 3369 + 2598 3370 ci-info@4.3.0: {} 2599 3371 2600 3372 cli-boxes@3.0.0: {} ··· 2624 3396 comma-separated-tokens@2.0.3: {} 2625 3397 2626 3398 commander@4.1.1: {} 3399 + 3400 + commander@8.3.0: {} 2627 3401 2628 3402 common-ancestor-path@1.0.1: {} 2629 3403 ··· 2658 3432 2659 3433 cssesc@3.0.0: {} 2660 3434 3435 + csstype@3.1.3: {} 3436 + 2661 3437 debug@4.4.1: 2662 3438 dependencies: 2663 3439 ms: 2.1.3 ··· 2668 3444 2669 3445 deep-eql@5.0.2: {} 2670 3446 3447 + deepmerge@4.3.1: {} 3448 + 2671 3449 defu@6.1.4: {} 2672 3450 2673 3451 dequal@2.0.3: {} 2674 3452 2675 3453 destr@2.0.5: {} 3454 + 3455 + detect-indent@6.1.0: {} 2676 3456 2677 3457 detect-libc@2.0.4: 2678 3458 optional: true ··· 2690 3470 dfa@1.2.0: {} 2691 3471 2692 3472 diff@5.2.0: {} 3473 + 3474 + dir-glob@3.0.1: 3475 + dependencies: 3476 + path-type: 4.0.0 2693 3477 2694 3478 dlv@1.1.3: {} 2695 3479 3480 + dom-serializer@2.0.0: 3481 + dependencies: 3482 + domelementtype: 2.3.0 3483 + domhandler: 5.0.3 3484 + entities: 4.5.0 3485 + 3486 + domelementtype@2.3.0: {} 3487 + 3488 + domhandler@5.0.3: 3489 + dependencies: 3490 + domelementtype: 2.3.0 3491 + 3492 + domutils@3.2.2: 3493 + dependencies: 3494 + dom-serializer: 2.0.0 3495 + domelementtype: 2.3.0 3496 + domhandler: 5.0.3 3497 + 2696 3498 dset@3.1.4: {} 2697 3499 2698 3500 eastasianwidth@0.2.0: {} ··· 2702 3504 emoji-regex@8.0.0: {} 2703 3505 2704 3506 emoji-regex@9.2.2: {} 3507 + 3508 + enquirer@2.4.1: 3509 + dependencies: 3510 + ansi-colors: 4.1.3 3511 + strip-ansi: 6.0.1 3512 + 3513 + entities@4.5.0: {} 2705 3514 2706 3515 entities@6.0.1: {} 2707 3516 ··· 2736 3545 '@esbuild/win32-ia32': 0.25.8 2737 3546 '@esbuild/win32-x64': 0.25.8 2738 3547 3548 + escape-string-regexp@4.0.0: {} 3549 + 2739 3550 escape-string-regexp@5.0.0: {} 3551 + 3552 + esm-env@1.2.2: {} 3553 + 3554 + esprima@4.0.1: {} 2740 3555 2741 3556 estree-walker@2.0.2: {} 2742 3557 ··· 2750 3565 2751 3566 extend@3.0.2: {} 2752 3567 3568 + extendable-error@0.1.7: {} 3569 + 3570 + external-editor@3.1.0: 3571 + dependencies: 3572 + chardet: 0.7.0 3573 + iconv-lite: 0.4.24 3574 + tmp: 0.0.33 3575 + 2753 3576 fast-deep-equal@3.1.3: {} 2754 3577 3578 + fast-glob@3.3.3: 3579 + dependencies: 3580 + '@nodelib/fs.stat': 2.0.5 3581 + '@nodelib/fs.walk': 1.2.8 3582 + glob-parent: 5.1.2 3583 + merge2: 1.4.1 3584 + micromatch: 4.0.8 3585 + 3586 + fastq@1.19.1: 3587 + dependencies: 3588 + reusify: 1.1.0 3589 + 2755 3590 fdir@6.4.6(picomatch@4.0.3): 2756 3591 optionalDependencies: 2757 3592 picomatch: 4.0.3 2758 3593 3594 + fill-range@7.1.1: 3595 + dependencies: 3596 + to-regex-range: 5.0.1 3597 + 3598 + find-up@4.1.0: 3599 + dependencies: 3600 + locate-path: 5.0.0 3601 + path-exists: 4.0.0 3602 + 2759 3603 fix-dts-default-cjs-exports@1.0.1: 2760 3604 dependencies: 2761 3605 magic-string: 0.30.17 ··· 2786 3630 cross-spawn: 7.0.6 2787 3631 signal-exit: 4.1.0 2788 3632 3633 + fs-extra@7.0.1: 3634 + dependencies: 3635 + graceful-fs: 4.2.11 3636 + jsonfile: 4.0.0 3637 + universalify: 0.1.2 3638 + 3639 + fs-extra@8.1.0: 3640 + dependencies: 3641 + graceful-fs: 4.2.11 3642 + jsonfile: 4.0.0 3643 + universalify: 0.1.2 3644 + 2789 3645 fsevents@2.3.3: 2790 3646 optional: true 2791 3647 ··· 2793 3649 2794 3650 github-slugger@2.0.0: {} 2795 3651 3652 + glob-parent@5.1.2: 3653 + dependencies: 3654 + is-glob: 4.0.3 3655 + 2796 3656 glob@10.4.5: 2797 3657 dependencies: 2798 3658 foreground-child: 3.3.1 ··· 2801 3661 minipass: 7.1.2 2802 3662 package-json-from-dist: 1.0.1 2803 3663 path-scurry: 1.11.1 3664 + 3665 + globby@11.1.0: 3666 + dependencies: 3667 + array-union: 2.1.0 3668 + dir-glob: 3.0.1 3669 + fast-glob: 3.3.3 3670 + ignore: 5.3.2 3671 + merge2: 1.4.1 3672 + slash: 3.0.0 3673 + 3674 + graceful-fs@4.2.11: {} 2804 3675 2805 3676 graphemer@1.4.0: {} 2806 3677 ··· 2907 3778 2908 3779 html-void-elements@3.0.0: {} 2909 3780 3781 + htmlparser2@8.0.2: 3782 + dependencies: 3783 + domelementtype: 2.3.0 3784 + domhandler: 5.0.3 3785 + domutils: 3.2.2 3786 + entities: 4.5.0 3787 + 2910 3788 http-cache-semantics@4.2.0: {} 2911 3789 3790 + human-id@4.1.1: {} 3791 + 3792 + iconv-lite@0.4.24: 3793 + dependencies: 3794 + safer-buffer: 2.1.2 3795 + 3796 + ignore@5.3.2: {} 3797 + 2912 3798 import-meta-resolve@4.1.0: {} 2913 3799 2914 3800 iron-webcrypto@1.2.1: {} ··· 2918 3804 2919 3805 is-docker@3.0.0: {} 2920 3806 3807 + is-extglob@2.1.1: {} 3808 + 2921 3809 is-fullwidth-code-point@3.0.0: {} 2922 3810 3811 + is-glob@4.0.3: 3812 + dependencies: 3813 + is-extglob: 2.1.1 3814 + 2923 3815 is-inside-container@1.0.0: 2924 3816 dependencies: 2925 3817 is-docker: 3.0.0 3818 + 3819 + is-number@7.0.0: {} 2926 3820 2927 3821 is-plain-obj@4.1.0: {} 2928 3822 3823 + is-plain-object@5.0.0: {} 3824 + 3825 + is-subdir@1.2.0: 3826 + dependencies: 3827 + better-path-resolve: 1.0.0 3828 + 3829 + is-windows@1.0.2: {} 3830 + 2929 3831 is-wsl@3.1.0: 2930 3832 dependencies: 2931 3833 is-inside-container: 1.0.0 ··· 2944 3846 2945 3847 js-tokens@9.0.1: {} 2946 3848 3849 + js-yaml@3.14.1: 3850 + dependencies: 3851 + argparse: 1.0.10 3852 + esprima: 4.0.1 3853 + 2947 3854 js-yaml@4.1.0: 2948 3855 dependencies: 2949 3856 argparse: 2.0.1 2950 3857 3858 + jsonfile@4.0.0: 3859 + optionalDependencies: 3860 + graceful-fs: 4.2.11 3861 + 3862 + katex@0.16.22: 3863 + dependencies: 3864 + commander: 8.3.0 3865 + 2951 3866 kleur@3.0.3: {} 2952 3867 2953 3868 kleur@4.1.5: {} ··· 2958 3873 2959 3874 load-tsconfig@0.2.5: {} 2960 3875 3876 + locate-path@5.0.0: 3877 + dependencies: 3878 + p-locate: 4.1.0 3879 + 2961 3880 lodash.sortby@4.7.0: {} 3881 + 3882 + lodash.startcase@4.4.0: {} 2962 3883 2963 3884 longest-streak@3.1.0: {} 2964 3885 ··· 3099 4020 '@types/mdast': 4.0.4 3100 4021 3101 4022 mdn-data@2.12.2: {} 4023 + 4024 + merge2@1.4.1: {} 3102 4025 3103 4026 micromark-core-commonmark@2.0.3: 3104 4027 dependencies: ··· 3291 4214 transitivePeerDependencies: 3292 4215 - supports-color 3293 4216 4217 + micromatch@4.0.8: 4218 + dependencies: 4219 + braces: 3.0.3 4220 + picomatch: 2.3.1 4221 + 3294 4222 minimatch@9.0.5: 3295 4223 dependencies: 3296 4224 brace-expansion: 2.0.2 ··· 3303 4231 pathe: 2.0.3 3304 4232 pkg-types: 1.3.1 3305 4233 ufo: 1.6.1 4234 + 4235 + mri@1.2.0: {} 3306 4236 3307 4237 mrmime@2.0.1: {} 3308 4238 ··· 3352 4282 regex: 6.0.1 3353 4283 regex-recursion: 6.0.2 3354 4284 4285 + os-tmpdir@1.0.2: {} 4286 + 4287 + outdent@0.5.0: {} 4288 + 4289 + p-filter@2.1.0: 4290 + dependencies: 4291 + p-map: 2.1.0 4292 + 4293 + p-limit@2.3.0: 4294 + dependencies: 4295 + p-try: 2.2.0 4296 + 3355 4297 p-limit@6.2.0: 3356 4298 dependencies: 3357 4299 yocto-queue: 1.2.1 3358 4300 4301 + p-locate@4.1.0: 4302 + dependencies: 4303 + p-limit: 2.3.0 4304 + 4305 + p-map@2.1.0: {} 4306 + 3359 4307 p-queue@8.1.0: 3360 4308 dependencies: 3361 4309 eventemitter3: 5.0.1 3362 4310 p-timeout: 6.1.4 3363 4311 3364 4312 p-timeout@6.1.4: {} 4313 + 4314 + p-try@2.2.0: {} 3365 4315 3366 4316 package-json-from-dist@1.0.1: {} 3367 4317 4318 + package-manager-detector@0.2.11: 4319 + dependencies: 4320 + quansync: 0.2.10 4321 + 3368 4322 package-manager-detector@1.3.0: {} 3369 4323 3370 4324 pako@0.2.9: {} ··· 3378 4332 unist-util-visit-children: 3.0.0 3379 4333 vfile: 6.0.3 3380 4334 4335 + parse-srcset@1.0.2: {} 4336 + 3381 4337 parse5@7.3.0: 3382 4338 dependencies: 3383 4339 entities: 6.0.1 4340 + 4341 + path-exists@4.0.0: {} 3384 4342 3385 4343 path-key@3.1.1: {} 3386 4344 ··· 3389 4347 lru-cache: 10.4.3 3390 4348 minipass: 7.1.2 3391 4349 4350 + path-type@4.0.0: {} 4351 + 3392 4352 pathe@2.0.3: {} 3393 4353 3394 4354 pathval@2.0.1: {} ··· 3398 4358 picomatch@2.3.1: {} 3399 4359 3400 4360 picomatch@4.0.3: {} 4361 + 4362 + pify@4.0.1: {} 3401 4363 3402 4364 pirates@4.0.7: {} 3403 4365 ··· 3419 4381 picocolors: 1.1.1 3420 4382 source-map-js: 1.2.1 3421 4383 4384 + prettier@2.8.8: {} 4385 + 4386 + prettier@3.6.2: {} 4387 + 3422 4388 prismjs@1.30.0: {} 3423 4389 3424 4390 prompts@2.4.2: ··· 3432 4398 3433 4399 punycode@2.3.1: {} 3434 4400 4401 + quansync@0.2.10: {} 4402 + 4403 + queue-microtask@1.2.3: {} 4404 + 3435 4405 radix3@1.1.2: {} 3436 4406 4407 + read-yaml-file@1.1.0: 4408 + dependencies: 4409 + graceful-fs: 4.2.11 4410 + js-yaml: 3.14.1 4411 + pify: 4.0.1 4412 + strip-bom: 3.0.0 4413 + 3437 4414 readdirp@4.1.2: {} 3438 4415 3439 4416 regex-recursion@6.0.2: ··· 3541 4518 retext-stringify: 4.0.0 3542 4519 unified: 11.0.5 3543 4520 4521 + reusify@1.1.0: {} 4522 + 3544 4523 rollup@4.46.2: 3545 4524 dependencies: 3546 4525 '@types/estree': 1.0.8 ··· 3566 4545 '@rollup/rollup-win32-ia32-msvc': 4.46.2 3567 4546 '@rollup/rollup-win32-x64-msvc': 4.46.2 3568 4547 fsevents: 2.3.3 4548 + 4549 + run-parallel@1.2.0: 4550 + dependencies: 4551 + queue-microtask: 1.2.3 4552 + 4553 + safer-buffer@2.1.2: {} 4554 + 4555 + sanitize-html@2.17.0: 4556 + dependencies: 4557 + deepmerge: 4.3.1 4558 + escape-string-regexp: 4.0.0 4559 + htmlparser2: 8.0.2 4560 + is-plain-object: 5.0.0 4561 + parse-srcset: 1.0.2 4562 + postcss: 8.5.6 3569 4563 3570 4564 semver@7.7.2: {} 3571 4565 ··· 3624 4618 3625 4619 sisteransi@1.0.5: {} 3626 4620 3627 - smol-toml@1.4.1: {} 4621 + slash@3.0.0: {} 4622 + 4623 + smol-toml@1.4.2: {} 3628 4624 3629 4625 source-map-js@1.2.1: {} 3630 4626 ··· 3634 4630 3635 4631 space-separated-tokens@2.0.2: {} 3636 4632 4633 + spawndamnit@3.0.1: 4634 + dependencies: 4635 + cross-spawn: 7.0.6 4636 + signal-exit: 4.1.0 4637 + 4638 + sprintf-js@1.0.3: {} 4639 + 3637 4640 stackback@0.0.2: {} 3638 4641 3639 4642 std-env@3.9.0: {} ··· 3668 4671 strip-ansi@7.1.0: 3669 4672 dependencies: 3670 4673 ansi-regex: 6.1.0 4674 + 4675 + strip-bom@3.0.0: {} 3671 4676 3672 4677 strip-literal@3.0.0: 3673 4678 dependencies: ··· 3683 4688 pirates: 4.0.7 3684 4689 ts-interface-checker: 0.1.13 3685 4690 4691 + term-size@2.2.1: {} 4692 + 3686 4693 thenify-all@1.6.0: 3687 4694 dependencies: 3688 4695 thenify: 3.3.1 ··· 3710 4717 3711 4718 tlds@1.259.0: {} 3712 4719 4720 + tmp@0.0.33: 4721 + dependencies: 4722 + os-tmpdir: 1.0.2 4723 + 4724 + to-regex-range@5.0.1: 4725 + dependencies: 4726 + is-number: 7.0.0 4727 + 3713 4728 tr46@0.0.3: {} 3714 4729 3715 4730 tr46@1.0.1: ··· 3842 4857 unist-util-is: 6.0.0 3843 4858 unist-util-visit-parents: 6.0.1 3844 4859 4860 + universalify@0.1.2: {} 4861 + 3845 4862 unstorage@1.16.1: 3846 4863 dependencies: 3847 4864 anymatch: 3.1.3 ··· 3868 4885 '@types/unist': 3.0.3 3869 4886 vfile-message: 4.0.3 3870 4887 3871 - vite-node@3.2.4(@types/node@24.2.0): 4888 + vite-node@3.2.4(@types/node@24.2.1): 3872 4889 dependencies: 3873 4890 cac: 6.7.14 3874 4891 debug: 4.4.1 3875 4892 es-module-lexer: 1.7.0 3876 4893 pathe: 2.0.3 3877 - vite: 7.0.6(@types/node@24.2.0) 4894 + vite: 7.1.1(@types/node@24.2.1) 3878 4895 transitivePeerDependencies: 3879 4896 - '@types/node' 3880 4897 - jiti ··· 3889 4906 - tsx 3890 4907 - yaml 3891 4908 3892 - vite@6.3.5(@types/node@24.2.0): 4909 + vite@6.3.5(@types/node@24.2.1): 3893 4910 dependencies: 3894 4911 esbuild: 0.25.8 3895 4912 fdir: 6.4.6(picomatch@4.0.3) ··· 3898 4915 rollup: 4.46.2 3899 4916 tinyglobby: 0.2.14 3900 4917 optionalDependencies: 3901 - '@types/node': 24.2.0 4918 + '@types/node': 24.2.1 3902 4919 fsevents: 2.3.3 3903 4920 3904 - vite@7.0.6(@types/node@24.2.0): 4921 + vite@7.1.1(@types/node@24.2.1): 3905 4922 dependencies: 3906 4923 esbuild: 0.25.8 3907 4924 fdir: 6.4.6(picomatch@4.0.3) ··· 3910 4927 rollup: 4.46.2 3911 4928 tinyglobby: 0.2.14 3912 4929 optionalDependencies: 3913 - '@types/node': 24.2.0 4930 + '@types/node': 24.2.1 3914 4931 fsevents: 2.3.3 3915 4932 3916 - vitefu@1.1.1(vite@6.3.5(@types/node@24.2.0)): 4933 + vitefu@1.1.1(vite@6.3.5(@types/node@24.2.1)): 3917 4934 optionalDependencies: 3918 - vite: 6.3.5(@types/node@24.2.0) 4935 + vite: 6.3.5(@types/node@24.2.1) 3919 4936 3920 - vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.2.0): 4937 + vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.2.1): 3921 4938 dependencies: 3922 4939 '@types/chai': 5.2.2 3923 4940 '@vitest/expect': 3.2.4 3924 - '@vitest/mocker': 3.2.4(vite@7.0.6(@types/node@24.2.0)) 4941 + '@vitest/mocker': 3.2.4(vite@7.1.1(@types/node@24.2.1)) 3925 4942 '@vitest/pretty-format': 3.2.4 3926 4943 '@vitest/runner': 3.2.4 3927 4944 '@vitest/snapshot': 3.2.4 ··· 3939 4956 tinyglobby: 0.2.14 3940 4957 tinypool: 1.1.1 3941 4958 tinyrainbow: 2.0.0 3942 - vite: 7.0.6(@types/node@24.2.0) 3943 - vite-node: 3.2.4(@types/node@24.2.0) 4959 + vite: 7.1.1(@types/node@24.2.1) 4960 + vite-node: 3.2.4(@types/node@24.2.1) 3944 4961 why-is-node-running: 2.3.0 3945 4962 optionalDependencies: 3946 4963 '@types/debug': 4.1.12 3947 - '@types/node': 24.2.0 4964 + '@types/node': 24.2.1 3948 4965 transitivePeerDependencies: 3949 4966 - jiti 3950 4967 - less
-5
pnpm-workspace.yaml
··· 1 - ignoredBuiltDependencies: 2 - - sharp 3 - 4 - onlyBuiltDependencies: 5 - - esbuild
-129
src/leaflet-live-loader.ts
··· 1 - import type { LiveLoader } from "astro/loaders"; 2 - import { Agent } from "@atproto/api"; 3 - import { isDid } from "@atproto/did"; 4 - import { isValidHandle } from "@atproto/syntax"; 5 - import { 6 - getLeafletDocuments, 7 - getSingleLeafletDocument, 8 - resolveMiniDoc, 9 - uriToRkey, 10 - } from "./utils.js"; 11 - import type { 12 - CollectionFilter, 13 - EntryFilter, 14 - LeafletRecord, 15 - LeafletLoaderOptions, 16 - } from "./types.js"; 17 - 18 - export class LiveLoaderError extends Error { 19 - constructor( 20 - message: string, 21 - public code?: string, 22 - ) { 23 - super(message); 24 - this.name = "LiveLoaderError"; 25 - } 26 - } 27 - 28 - /** 29 - * Flow: 30 - * - Check for valid handle or did [done] 31 - * - Resolve PDS url from handle or did [done, thanks Phil!] 32 - * - Fetch leaflet documents [done] 33 - * - Find out how to use leaflet types here 34 - */ 35 - 36 - export function leafletLiveLoader( 37 - options: LeafletLoaderOptions, 38 - ): LiveLoader<LeafletRecord, EntryFilter, CollectionFilter, LiveLoaderError> { 39 - const { repo } = options; 40 - 41 - if (!repo || typeof repo !== "string") { 42 - throw new LiveLoaderError( 43 - "missing or invalid handle or did", 44 - "MISSING_OR_INVALID_HANDLE_OR_DID", 45 - ); 46 - } 47 - 48 - if (!isValidHandle(repo)) { 49 - // not a valid handle, let's check if it's a valid did 50 - if (!isDid(repo)) { 51 - throw new LiveLoaderError( 52 - "invalid handle or did", 53 - "INVALID_HANDLE_OR_DID", 54 - ); 55 - } 56 - } 57 - 58 - return { 59 - name: "leaflet-loader-astro", 60 - loadCollection: async ({ filter }) => { 61 - try { 62 - const pds_url = await resolveMiniDoc(repo); 63 - const agent = new Agent({ service: pds_url }); 64 - 65 - const documents = await getLeafletDocuments({ 66 - agent, 67 - repo, 68 - reverse: filter?.reverse, 69 - cursor: filter?.cursor, 70 - limit: filter?.limit, 71 - }); 72 - 73 - return { 74 - entries: documents.map((document) => { 75 - const id = uriToRkey(document.uri); 76 - return { 77 - id, 78 - data: { 79 - id, 80 - ...document, 81 - }, 82 - }; 83 - }), 84 - }; 85 - } catch (error) { 86 - return { 87 - error: new LiveLoaderError( 88 - "could not recover from error, please report on github", 89 - "UNRECOVERABLE_ERROR", 90 - ), 91 - }; 92 - } 93 - }, 94 - loadEntry: async ({ filter }) => { 95 - try { 96 - if (!filter.id) { 97 - return { 98 - error: new LiveLoaderError( 99 - "must provide an id for specific document", 100 - "MISSING_DOCUMENT_ID", 101 - ), 102 - }; 103 - } 104 - const pds_url = await resolveMiniDoc(repo); 105 - const agent = new Agent({ service: pds_url }); 106 - const document = await getSingleLeafletDocument({ 107 - agent, 108 - id: filter.id, 109 - repo, 110 - }); 111 - 112 - return { 113 - id: filter?.id, 114 - data: { 115 - id: filter?.id, 116 - ...document, 117 - }, 118 - }; 119 - } catch { 120 - return { 121 - error: new LiveLoaderError( 122 - "could not recover from error, please report on github", 123 - "UNRECOVERABLE_ERROR", 124 - ), 125 - }; 126 - } 127 - }, 128 - }; 129 - }
-181
src/test.ts
··· 1 - import { 2 - AtpAgent, 3 - type AppBskyFeedGetAuthorFeed, 4 - type AppBskyFeedDefs, 5 - } from "@atproto/api"; 6 - import type { LiveLoader } from "astro/loaders"; 7 - 8 - export interface LiveBlueskyLoaderOptions { 9 - identifier?: string; 10 - service?: string; 11 - } 12 - 13 - export interface CollectionFilter { 14 - limit?: number; 15 - since?: Date; 16 - until?: Date; 17 - type?: AppBskyFeedGetAuthorFeed.QueryParams["filter"]; 18 - identifier?: string; 19 - } 20 - 21 - export interface EntryFilter { 22 - id?: string; 23 - } 24 - 25 - export class BlueskyError extends Error { 26 - constructor( 27 - message: string, 28 - public code?: string, 29 - public identifier?: string, 30 - ) { 31 - super(message); 32 - this.name = "BlueskyError"; 33 - } 34 - } 35 - 36 - export function liveBlueskyLoader( 37 - options: LiveBlueskyLoaderOptions = {}, 38 - ): LiveLoader< 39 - AppBskyFeedDefs.PostView, 40 - EntryFilter, 41 - CollectionFilter, 42 - BlueskyError 43 - > { 44 - const { 45 - identifier: defaultIdentifier, 46 - service = "https://public.api.bsky.app", 47 - } = options; 48 - 49 - return { 50 - name: "live-bluesky-loader", 51 - 52 - loadCollection: async ({ filter }) => { 53 - try { 54 - const identifier = filter?.identifier || defaultIdentifier; 55 - 56 - if (!identifier) { 57 - return { 58 - error: new BlueskyError( 59 - "Identifier must be provided either in loader options or collection filter", 60 - "MISSING_IDENTIFIER", 61 - ), 62 - }; 63 - } 64 - 65 - const agent = new AtpAgent({ service }); 66 - 67 - let cursor = undefined; 68 - const allPosts: AppBskyFeedDefs.PostView[] = []; 69 - let count = 0; 70 - 71 - do { 72 - const { data } = await agent.getAuthorFeed({ 73 - actor: identifier, 74 - filter: filter?.type, 75 - cursor, 76 - limit: 100, 77 - }); 78 - 79 - for (const { post } of data.feed) { 80 - // Apply collection filters 81 - if (filter?.limit && count >= filter.limit) { 82 - break; 83 - } 84 - 85 - if (filter?.since) { 86 - const postDate = new Date(post.indexedAt); 87 - if (postDate < filter.since) { 88 - continue; 89 - } 90 - } 91 - 92 - if (filter?.until) { 93 - const postDate = new Date(post.indexedAt); 94 - if (postDate > filter.until) { 95 - continue; 96 - } 97 - } 98 - 99 - allPosts.push(post); 100 - count++; 101 - } 102 - 103 - cursor = data.cursor; 104 - } while (cursor && (!filter?.limit || count < filter.limit)); 105 - 106 - return { 107 - entries: allPosts.map((post) => ({ 108 - id: post.uri, 109 - data: post, 110 - // rendered: { 111 - // html: renderPostAsHtml(post), 112 - // }, 113 - })), 114 - }; 115 - } catch (error) { 116 - const identifier = filter?.identifier || defaultIdentifier; 117 - return { 118 - error: new BlueskyError( 119 - `Failed to load Bluesky posts for ${identifier || "unknown"}`, 120 - "COLLECTION_LOAD_ERROR", 121 - identifier, 122 - ), 123 - }; 124 - } 125 - }, 126 - 127 - loadEntry: async ({ filter }) => { 128 - try { 129 - const agent = new AtpAgent({ service }); 130 - 131 - if (!filter.id) { 132 - return { 133 - error: new BlueskyError( 134 - "'id' must be provided in the filter", 135 - "INVALID_FILTER", 136 - ), 137 - }; 138 - } 139 - 140 - // Validate that the ID is a full AT URI 141 - if (!filter.id.startsWith("at://")) { 142 - return { 143 - error: new BlueskyError( 144 - `Invalid ID format: '${filter.id}'. Must be a full AT URI (e.g., 'at://did:plc:user/app.bsky.feed.post/id')`, 145 - "INVALID_ID_FORMAT", 146 - ), 147 - }; 148 - } 149 - 150 - const postUri = filter.id; 151 - 152 - // Fetch the post directly using getPosts 153 - const { data } = await agent.getPosts({ uris: [postUri] }); 154 - 155 - const [post] = data.posts; 156 - 157 - if (!post) { 158 - return; 159 - } 160 - 161 - return { 162 - id: post.uri, 163 - data: post, 164 - // rendered: { 165 - // html: renderPostAsHtml(post), 166 - // }, 167 - }; 168 - } catch (error) { 169 - const errorMessage = 170 - error instanceof Error ? error.message : "Unknown error"; 171 - const requestedUri = filter.id || "unknown"; 172 - return { 173 - error: new BlueskyError( 174 - `Failed to load Bluesky post '${requestedUri}': ${errorMessage}`, 175 - "ENTRY_LOAD_ERROR", 176 - ), 177 - }; 178 - } 179 - }, 180 - }; 181 - }
-46
src/types.ts
··· 1 - import type { Agent } from "@atproto/api"; 2 - 3 - export interface LeafletLoaderOptions { 4 - /** 5 - * @description Your repo is either your handle (@you.some.url) or your DID (did:plc... or did:web...). You can find this information using: https://pdsls.dev 6 - */ 7 - repo: string; 8 - } 9 - 10 - export interface LeafletRecord { 11 - id: string; 12 - uri: string; 13 - cid?: string; 14 - value: unknown; 15 - } 16 - 17 - export interface MiniDoc { 18 - did: string; 19 - handle: string; 20 - pds: string; 21 - signing_key: string; 22 - } 23 - 24 - export interface CollectionFilter { 25 - limit?: number; 26 - reverse?: boolean; 27 - cursor?: string; 28 - } 29 - 30 - export interface EntryFilter { 31 - id?: string; 32 - } 33 - 34 - export interface GetLeafletDocumentsParams { 35 - repo: string; 36 - agent: Agent; 37 - cursor?: string; 38 - limit?: number; 39 - reverse?: boolean; 40 - } 41 - 42 - export interface GetSingleLeafletDocumentParams { 43 - repo: string; 44 - agent: Agent; 45 - id: string; 46 - }
-80
src/utils.ts
··· 1 - import type { Agent } from "@atproto/api"; 2 - import type { 3 - GetLeafletDocumentsParams, 4 - GetSingleLeafletDocumentParams, 5 - MiniDoc, 6 - } from "./types.js"; 7 - import { LiveLoaderError } from "./leaflet-live-loader.js"; 8 - 9 - export function uriToRkey(uri: string): string { 10 - const rkey = uri.split("/").pop(); 11 - if (!rkey) { 12 - throw new Error("Failed to get rkey from uri."); 13 - } 14 - return rkey; 15 - } 16 - 17 - export async function resolveMiniDoc(handleOrDid: string) { 18 - try { 19 - const response = await fetch( 20 - `https://slingshot.microcosm.blue/xrpc/com.bad-example.identity.resolveMiniDoc?identifier=${handleOrDid}`, 21 - ); 22 - 23 - if (!response.ok || response.status >= 300) { 24 - throw new Error( 25 - `could not resolve did doc due to invalid handle or did ${handleOrDid}`, 26 - ); 27 - } 28 - const data = (await response.json()) as MiniDoc; 29 - 30 - return data.pds; 31 - } catch (error) { 32 - throw new Error(`failed to resolve handle: ${handleOrDid}`); 33 - } 34 - } 35 - 36 - export async function getLeafletDocuments({ 37 - repo, 38 - reverse, 39 - cursor, 40 - agent, 41 - limit, 42 - }: GetLeafletDocumentsParams) { 43 - const response = await agent.com.atproto.repo.listRecords({ 44 - repo, 45 - collection: "pub.leaflet.document", 46 - cursor, 47 - reverse, 48 - limit, 49 - }); 50 - 51 - if (response.success === false) { 52 - throw new LiveLoaderError( 53 - "Could not fetch leaflet documents", 54 - "FETCH_FAILED", 55 - ); 56 - } 57 - 58 - return response?.data?.records; 59 - } 60 - 61 - export async function getSingleLeafletDocument({ 62 - agent, 63 - repo, 64 - id, 65 - }: GetSingleLeafletDocumentParams) { 66 - const response = await agent.com.atproto.repo.getRecord({ 67 - repo, 68 - collection: "pub.leaflet.document", 69 - rkey: id, 70 - }); 71 - 72 - if (response.success === false) { 73 - throw new LiveLoaderError( 74 - "error fetching document", 75 - "DOCUMENT_FETCH_ERROR", 76 - ); 77 - } 78 - 79 - return response?.data; 80 - }
+189
tests/parse-blocks.test.ts
··· 1 + import { expect, test } from "vitest"; 2 + import { parseBlocks } from "../lib/utils"; 3 + 4 + test("should correctly parse an h1 block to an h2 tag", () => { 5 + const html = parseBlocks({ 6 + block: { 7 + $type: "pub.leaflet.pages.linearDocument#block", 8 + block: { 9 + $type: "pub.leaflet.blocks.header", 10 + level: 1, 11 + facets: [], 12 + plaintext: "heading 1", 13 + }, 14 + }, 15 + did: "did:plc:qttsv4e7pu2jl3ilanfgc3zn", 16 + }); 17 + 18 + expect(html).toMatchInlineSnapshot(`"<h2>heading 1</h2>"`); 19 + }); 20 + 21 + test("should correctly parse an h2 block to an h3 tag", () => { 22 + const html = parseBlocks({ 23 + block: { 24 + $type: "pub.leaflet.pages.linearDocument#block", 25 + block: { 26 + $type: "pub.leaflet.blocks.header", 27 + level: 2, 28 + facets: [], 29 + plaintext: "heading 2", 30 + }, 31 + }, 32 + did: "did:plc:qttsv4e7pu2jl3ilanfgc3zn", 33 + }); 34 + 35 + expect(html).toMatchInlineSnapshot(`"<h3>heading 2</h3>"`); 36 + }); 37 + 38 + test("should correctly parse an h3 block to an h4 tag", () => { 39 + const html = parseBlocks({ 40 + block: { 41 + $type: "pub.leaflet.pages.linearDocument#block", 42 + block: { 43 + $type: "pub.leaflet.blocks.header", 44 + level: 3, 45 + facets: [], 46 + plaintext: "heading 3", 47 + }, 48 + }, 49 + did: "did:plc:qttsv4e7pu2jl3ilanfgc3zn", 50 + }); 51 + 52 + expect(html).toMatchInlineSnapshot(`"<h4>heading 3</h4>"`); 53 + }); 54 + 55 + test("should correctly parse a block with no level to an h6 tag", () => { 56 + const html = parseBlocks({ 57 + block: { 58 + $type: "pub.leaflet.pages.linearDocument#block", 59 + block: { 60 + $type: "pub.leaflet.blocks.header", 61 + facets: [], 62 + plaintext: "heading 6", 63 + }, 64 + }, 65 + did: "did:plc:qttsv4e7pu2jl3ilanfgc3zn", 66 + }); 67 + 68 + expect(html).toMatchInlineSnapshot(`"<h6>heading 6</h6>"`); 69 + }); 70 + 71 + test("should correctly parse an unordered list block", () => { 72 + const html = parseBlocks({ 73 + block: { 74 + $type: "pub.leaflet.pages.linearDocument#block", 75 + block: { 76 + $type: "pub.leaflet.blocks.unorderedList", 77 + children: [ 78 + { 79 + $type: "pub.leaflet.blocks.unorderedList#listItem", 80 + content: { 81 + $type: "pub.leaflet.blocks.text", 82 + facets: [ 83 + { 84 + index: { 85 + byteEnd: 18, 86 + byteStart: 0, 87 + }, 88 + features: [ 89 + { 90 + uri: "https://pdsls.dev/", 91 + $type: "pub.leaflet.richtext.facet#link", 92 + }, 93 + ], 94 + }, 95 + { 96 + index: { 97 + byteEnd: 28, 98 + byteStart: 22, 99 + }, 100 + features: [ 101 + { 102 + uri: "https://bsky.app/profile/juli.ee", 103 + $type: "pub.leaflet.richtext.facet#link", 104 + }, 105 + ], 106 + }, 107 + ], 108 + plaintext: "https://pdsls.dev/ by Juliet", 109 + }, 110 + children: [], 111 + }, 112 + { 113 + $type: "pub.leaflet.blocks.unorderedList#listItem", 114 + content: { 115 + $type: "pub.leaflet.blocks.text", 116 + facets: [ 117 + { 118 + index: { 119 + byteEnd: 34, 120 + byteStart: 0, 121 + }, 122 + features: [ 123 + { 124 + uri: "https://github.com/mary-ext/atcute", 125 + $type: "pub.leaflet.richtext.facet#link", 126 + }, 127 + ], 128 + }, 129 + { 130 + index: { 131 + byteEnd: 42, 132 + byteStart: 38, 133 + }, 134 + features: [ 135 + { 136 + uri: "https://bsky.app/profile/mary.my.id", 137 + $type: "pub.leaflet.richtext.facet#link", 138 + }, 139 + ], 140 + }, 141 + ], 142 + plaintext: "https://github.com/mary-ext/atcute by mary", 143 + }, 144 + children: [], 145 + }, 146 + { 147 + $type: "pub.leaflet.blocks.unorderedList#listItem", 148 + content: { 149 + $type: "pub.leaflet.blocks.text", 150 + facets: [ 151 + { 152 + index: { 153 + byteEnd: 27, 154 + byteStart: 0, 155 + }, 156 + features: [ 157 + { 158 + uri: "https://www.microcosm.blue/", 159 + $type: "pub.leaflet.richtext.facet#link", 160 + }, 161 + ], 162 + }, 163 + { 164 + index: { 165 + byteEnd: 35, 166 + byteStart: 31, 167 + }, 168 + features: [ 169 + { 170 + uri: "https://bsky.app/profile/bad-example.com", 171 + $type: "pub.leaflet.richtext.facet#link", 172 + }, 173 + ], 174 + }, 175 + ], 176 + plaintext: "https://www.microcosm.blue/ by phil", 177 + }, 178 + children: [], 179 + }, 180 + ], 181 + }, 182 + }, 183 + did: "did:plc:qttsv4e7pu2jl3ilanfgc3zn", 184 + }); 185 + 186 + expect(html).toMatchInlineSnapshot( 187 + `"<ul><li><p><a href="https://pdsls.dev/" target="_blank" rel="noopener noreferrer">https://pdsls.dev/</a> by <a href="https://bsky.app/profile/juli.ee" target="_blank" rel="noopener noreferrer">Juliet</a></p></li><li><p><a href="https://github.com/mary-ext/atcute" target="_blank" rel="noopener noreferrer">https://github.com/mary-ext/atcute</a> by <a href="https://bsky.app/profile/mary.my.id" target="_blank" rel="noopener noreferrer">mary</a></p></li><li><p><a href="https://www.microcosm.blue/" target="_blank" rel="noopener noreferrer">https://www.microcosm.blue/</a> by <a href="https://bsky.app/profile/bad-example.com" target="_blank" rel="noopener noreferrer">phil</a></p></li></ul>"`, 188 + ); 189 + });
+133
tests/parse-text-blocks.test.ts
··· 1 + import { expect, test } from "vitest"; 2 + import { parseTextBlock } from "../lib/utils"; 3 + 4 + test("should correctly parse a text block without facets", () => { 5 + const html = parseTextBlock({ 6 + $type: "pub.leaflet.blocks.text", 7 + facets: [], 8 + plaintext: "just plaintext no facets", 9 + }); 10 + 11 + expect(html).toMatchInlineSnapshot(`"<p>just plaintext no facets</p>"`); 12 + }); 13 + 14 + test("should correctly parse a text block with bolded text", () => { 15 + const html = parseTextBlock({ 16 + $type: "pub.leaflet.blocks.text", 17 + facets: [ 18 + { 19 + index: { 20 + byteEnd: 11, 21 + byteStart: 0, 22 + }, 23 + features: [ 24 + { 25 + $type: "pub.leaflet.richtext.facet#bold", 26 + }, 27 + ], 28 + }, 29 + ], 30 + plaintext: "bolded text with some plaintext", 31 + }); 32 + 33 + expect(html).toMatchInlineSnapshot( 34 + `"<p><b>bolded text</b> with some plaintext</p>"`, 35 + ); 36 + }); 37 + 38 + test("should correctly parse a text block with an inline link", () => { 39 + const html = parseTextBlock({ 40 + $type: "pub.leaflet.blocks.text", 41 + facets: [ 42 + { 43 + index: { 44 + byteEnd: 27, 45 + byteStart: 0, 46 + }, 47 + features: [ 48 + { 49 + uri: "https://blacksky.community/", 50 + $type: "pub.leaflet.richtext.facet#link", 51 + }, 52 + ], 53 + }, 54 + ], 55 + plaintext: "https://blacksky.community/", 56 + }); 57 + 58 + expect(html).toMatchInlineSnapshot( 59 + `"<p><a href="https://blacksky.community/" target="_blank" rel="noopener noreferrer">https://blacksky.community/</a></p>"`, 60 + ); 61 + }); 62 + 63 + test("should correctly parse a text block with strikethrough text", () => { 64 + const html = parseTextBlock({ 65 + $type: "pub.leaflet.blocks.text", 66 + facets: [ 67 + { 68 + index: { 69 + byteEnd: 13, 70 + byteStart: 0, 71 + }, 72 + features: [ 73 + { 74 + $type: "pub.leaflet.richtext.facet#strikethrough", 75 + }, 76 + ], 77 + }, 78 + ], 79 + plaintext: "strikethrough text with some plaintext", 80 + }); 81 + 82 + expect(html).toMatchInlineSnapshot( 83 + `"<p><s>strikethrough</s> text with some plaintext</p>"`, 84 + ); 85 + }); 86 + 87 + test("should correctly parse a text block with underlined text", () => { 88 + const html = parseTextBlock({ 89 + $type: "pub.leaflet.blocks.text", 90 + facets: [ 91 + { 92 + index: { 93 + byteEnd: 10, 94 + byteStart: 0, 95 + }, 96 + features: [ 97 + { 98 + $type: "pub.leaflet.richtext.facet#underline", 99 + }, 100 + ], 101 + }, 102 + ], 103 + plaintext: "underlined text with some plaintext", 104 + }); 105 + 106 + expect(html).toMatchInlineSnapshot( 107 + `"<p><span style="text-decoration:underline;">underlined</span> text with some plaintext</p>"`, 108 + ); 109 + }); 110 + 111 + test("should correctly parse a text block with italicized text", () => { 112 + const html = parseTextBlock({ 113 + $type: "pub.leaflet.blocks.text", 114 + facets: [ 115 + { 116 + index: { 117 + byteEnd: 10, 118 + byteStart: 0, 119 + }, 120 + features: [ 121 + { 122 + $type: "pub.leaflet.richtext.facet#italic", 123 + }, 124 + ], 125 + }, 126 + ], 127 + plaintext: "italicized text with some plaintext", 128 + }); 129 + 130 + expect(html).toMatchInlineSnapshot( 131 + `"<p><i>italicized</i> text with some plaintext</p>"`, 132 + ); 133 + });
+18
tests/uri-to-rkey.test.ts
··· 1 + import { expect, test } from "vitest"; 2 + import { uriToRkey } from "../lib/utils"; 3 + 4 + test("should throw if invalid at uri is passed in", () => { 5 + expect(() => uriToRkey("test")).toThrowError("get rkey"); 6 + }); 7 + 8 + test("should pass if valid at uri is passed in", () => { 9 + expect( 10 + uriToRkey( 11 + "at://did:plc:qttsv4e7pu2jl3ilanfgc3zn/pub.leaflet.document/3lvl7m6jd4s2e", 12 + ), 13 + ).toBe("3lvl7m6jd4s2e"); 14 + }); 15 + 16 + test("should not pass if invalid uri is passed in", () => { 17 + expect(() => uriToRkey("invalid")).toThrowError(/failed to get rkey/i); 18 + });
+8 -5
tsconfig.json
··· 6 6 "lib": ["es2022"], 7 7 "target": "es2022", 8 8 "allowJs": true, 9 - "resolveJsonModule": true, 10 9 "moduleDetection": "force", 11 10 "isolatedModules": true, 12 - "verbatimModuleSyntax": true, 13 11 "strict": true, 14 12 "noUncheckedIndexedAccess": true, 15 13 "noImplicitOverride": true, 16 - "module": "NodeNext", 14 + "module": "nodenext", 15 + "baseUrl": "lib", 17 16 "outDir": "dist", 18 - "rootDir": "src", 17 + "moduleResolution": "nodenext", 18 + "rootDir": "lib", 19 19 "sourceMap": true, 20 + "noEmit": true, 20 21 "declaration": true 21 - } 22 + }, 23 + "include": ["lib"], 24 + "exclude": ["node_modules", "dist", "**/*.test.ts"] 22 25 }
+9
tsup.config.ts
··· 1 + import { defineConfig } from "tsup"; 2 + 3 + export default defineConfig({ 4 + entryPoints: ["lib/index.ts"], 5 + format: ["cjs", "esm"], 6 + dts: true, 7 + outDir: "dist", 8 + clean: true, 9 + });
+8
vitest.config.ts
··· 1 + import { defineConfig } from "vitest/config"; 2 + 3 + export default defineConfig({ 4 + test: { 5 + globals: true, 6 + environment: "node", 7 + }, 8 + });