prototypey.org - atproto lexicon typescript toolkit - mirror https://github.com/tylersayshi/prototypey

Compare changes

Choose any two refs to compare.

+3533 -895
+44
.github/workflows/sync-tangled.yml
···
··· 1 + name: sync-tangled 2 + 3 + on: 4 + push: 5 + branches: 6 + - main 7 + 8 + jobs: 9 + sync: 10 + runs-on: ubuntu-latest 11 + environment: tangled-sync 12 + steps: 13 + - uses: actions/checkout@v4.1.7 14 + with: 15 + fetch-depth: 0 16 + ref: ${{ github.event.pull_request.head.sha }} 17 + - name: sync tangled 18 + env: 19 + TANGLED_SSH_PRIVATE_KEY: ${{ secrets.TANGLED_SSH_PRIVATE_KEY }} 20 + run: | 21 + set -euo pipefail 22 + # Turn off strict SSH key checking 23 + mkdir -p ~/.ssh 24 + echo "Host * 25 + StrictHostKeyChecking no 26 + UserKnownHostsFile=/dev/null" > ~/.ssh/config 27 + 28 + # Write SSH key to disk 29 + echo "$TANGLED_SSH_PRIVATE_KEY" > ~/.ssh/tangled_key 30 + chmod 600 ~/.ssh/tangled_key 31 + 32 + # Configure SSH to use the key for tangled.sh 33 + echo "Host tangled.sh 34 + IdentityFile ~/.ssh/tangled_key" >> ~/.ssh/config 35 + 36 + chmod 600 ~/.ssh/config 37 + 38 + # Set git user 39 + git config --global user.name "Prototypey Bot" 40 + git config --global user.email "bot@prototypey.org" 41 + 42 + git remote add tangled git@tangled.sh:tylur.dev/prototypey 43 + git push -f --all tangled 44 + git push -f --tags tangled
+28
.github/workflows/update-dependencies.yml
···
··· 1 + name: Update Dependencies 2 + 3 + on: 4 + schedule: 5 + # Run every Monday at 9:00 AM UTC 6 + - cron: "0 9 * * 1" 7 + workflow_dispatch: # Allow manual trigger 8 + 9 + jobs: 10 + update_dependencies: 11 + name: Update Dependencies 12 + runs-on: ubuntu-latest 13 + environment: update 14 + permissions: 15 + contents: write 16 + pull-requests: write 17 + steps: 18 + - uses: actions/checkout@v4 19 + 20 + - uses: ./.github/actions/prepare 21 + 22 + - uses: tylersayshi/taze-update-action@v1 23 + with: 24 + taze-input: -rw --concurrency 1 25 + branch-prefix: update-deps 26 + pr-title: "update dependencies" 27 + pr-labels: dependencies 28 + github-token: ${{ secrets.PAT_TOKEN }}
+1 -1
.node-version
··· 1 - 24.10.0
··· 1 + 25
+1 -1
knip.json
··· 15 } 16 }, 17 "ignore": ["**/*.d.ts", "**/dist/**", "**/lib/**", "**/node_modules/**"], 18 - "ignoreDependencies": [], 19 "ignoreExportsUsedInFile": true 20 }
··· 15 } 16 }, 17 "ignore": ["**/*.d.ts", "**/dist/**", "**/lib/**", "**/node_modules/**"], 18 + "ignoreDependencies": ["tailwindcss"], 19 "ignoreExportsUsedInFile": true 20 }
+3 -3
package.json
··· 19 "tsc": "pnpm -r tsc" 20 }, 21 "devDependencies": { 22 - "@changesets/cli": "^2.29.7", 23 "@eslint/js": "9.29.0", 24 "eslint": "9.29.0", 25 - "knip": "^5.66.2", 26 "prettier": "3.6.1", 27 "typescript-eslint": "8.35.0" 28 }, 29 - "packageManager": "pnpm@10.19.0", 30 "engines": { 31 "node": ">=20.19.0" 32 },
··· 19 "tsc": "pnpm -r tsc" 20 }, 21 "devDependencies": { 22 + "@changesets/cli": "^2.29.8", 23 "@eslint/js": "9.29.0", 24 "eslint": "9.29.0", 25 + "knip": "^5.83.1", 26 "prettier": "3.6.1", 27 "typescript-eslint": "8.35.0" 28 }, 29 + "packageManager": "pnpm@10.29.2", 30 "engines": { 31 "node": ">=20.19.0" 32 },
+60
packages/prototypey/CHANGELOG.md
··· 1 # prototypey 2 3 ## 0.2.5 4 5 ### Patch Changes
··· 1 # prototypey 2 3 + ## 0.3.8 4 + 5 + ### Patch Changes 6 + 7 + - 7a19f90: releast changes from dep updates and #66 8 + 9 + ## 0.3.7 10 + 11 + ### Patch Changes 12 + 13 + - e75de54: update docs 14 + 15 + ## 0.3.6 16 + 17 + ### Patch Changes 18 + 19 + - 2b55317: fix exported type bug 20 + 21 + ## 0.3.5 22 + 23 + ### Patch Changes 24 + 25 + - abb4b31: updated docs 26 + 27 + ## 0.3.4 28 + 29 + ### Patch Changes 30 + 31 + - 3329654: fix for type of record key and description hint 32 + 33 + ## 0.3.3 34 + 35 + ### Patch Changes 36 + 37 + - e7a7497: documentation update 38 + 39 + ## 0.3.2 40 + 41 + ### Patch Changes 42 + 43 + - 6a6cae5: update deps 44 + 45 + ## 0.3.1 46 + 47 + ### Patch Changes 48 + 49 + - d5d3143: update docs - we're featured! 50 + 51 + ## 0.3.0 52 + 53 + ### Minor Changes 54 + 55 + - 91a8c84: generate prototypey lexicon utils from json definitions 56 + 57 + ## 0.2.6 58 + 59 + ### Patch Changes 60 + 61 + - 6c5569b: only export intended items 62 + 63 ## 0.2.5 64 65 ### Patch Changes
+61 -17
packages/prototypey/README.md
··· 1 # prototypey 2 3 - A (soon-to-be) fully-featured sdk for developing lexicons with typescript. 4 5 ## Installation 6 ··· 138 prototypey gen-emit ./lexicons ./src/lexicons/**/*.ts 139 ``` 140 141 - ### Typical Workflow 142 143 1. Author lexicons in TypeScript using the library 144 2. Emit JSON schemas with `gen-emit` for runtime validation ··· 159 npm run lexicon:emit 160 ``` 161 162 - ## State of the Project 163 164 - **Done:** 165 166 - - Full atproto spec lexicon authoring with in IDE docs & hints for each attribute (ts => json) 167 - - CLI generates json from ts definitions 168 - - CLI generates ts from json definitions 169 - - Inferrance of valid type from full lexicon definition 170 - - the really cool part of this is that it fills in the refs from the defs all at the type level 171 - - `lx.lexicon(...).validate(data)` for validating data using `@atproto/lexicon` and your lexicon definitions 172 173 - **TODO/In Progress:** 174 175 - - Library art! Please reach out if you'd be willing to contribute some drawings or anything! 176 - - Add CLI support for inferring and validating from json as the starting point 177 178 - Please give any and all feedback. I've not really written many lexicons much myself yet, so this project is at a point of "well I think this makes sense" ๐Ÿ˜‚. Both the [issues page](https://github.com/tylersayshi/prototypey/issues) and [discussions](https://github.com/tylersayshi/prototypey/discussions) are open and ready for y'all ๐Ÿ™‚. 179 180 --- 181 182 - > ๐Ÿ’ This package was templated with 183 - > [`create-typescript-app`](https://github.com/JoshuaKGoldberg/create-typescript-app) 184 - > using the [Bingo framework](https://create.bingo).
··· 1 # prototypey 2 3 + A fully-featured sdk for developing lexicons with typescript. 4 + 5 + Below this is the docs and features of the library. If you'd like the story for why prototypey exists and what it's good for: [that's published here](https://notes.tylur.dev/3m5a3do4eus2w) 6 + 7 + ## Features 8 + 9 + - atproto spec lexicon authoring with in IDE docs & hints for each attribute (ts => json) 10 + - CLI to generate json from ts definitions 11 + - CLI to generate ts from json definitions 12 + - inference of usage type from full lexicon definition 13 + - the really cool part of this is that it fills in the refs from the defs all at the type level 14 + - `lx.lexicon(...).validate(data)` for validating data using `@atproto/lexicon` 15 + - `fromJSON()` helper for creating lexicons directly from JSON objects with full type inference 16 17 ## Installation 18 ··· 150 prototypey gen-emit ./lexicons ./src/lexicons/**/*.ts 151 ``` 152 153 + #### `gen-from-json` - Generate TypeScript from JSON schemas 154 + 155 + ```bash 156 + prototypey gen-from-json <outdir> <sources...> 157 + ``` 158 + 159 + Generates TypeScript files from JSON lexicon schemas using the `fromJSON` helper. This is useful when you have existing lexicon JSON files and want to work with them in TypeScript with full type inference. 160 + 161 + **Example:** 162 + 163 + ```bash 164 + prototypey gen-from-json ./src/lexicons ./lexicons/**/*.json 165 + ``` 166 + 167 + This will create TypeScript files that export typed lexicon objects: 168 + 169 + ```ts 170 + // Generated file: src/lexicons/app.bsky.feed.post.ts 171 + import { fromJSON } from "prototypey"; 172 + 173 + export const appBskyFeedPost = fromJSON({ 174 + // ... lexicon JSON 175 + }); 176 + ``` 177 + 178 + ### Typical Workflows 179 + 180 + #### TypeScript-first workflow 181 182 1. Author lexicons in TypeScript using the library 183 2. Emit JSON schemas with `gen-emit` for runtime validation ··· 198 npm run lexicon:emit 199 ``` 200 201 + #### JSON-first workflow 202 203 + 1. Start with JSON lexicon schemas (e.g., from atproto) 204 + 2. Generate TypeScript with `gen-from-json` for type-safe access 205 206 + **Recommended:** Add as a script to your `package.json`: 207 208 + ```json 209 + { 210 + "scripts": { 211 + "lexicon:import": "prototypey gen-from-json ./src/lexicons ./lexicons/**/*.json" 212 + } 213 + } 214 + ``` 215 216 + Then run: 217 218 + ```bash 219 + npm run lexicon:import 220 + ``` 221 222 --- 223 224 + Please give any and all feedback. I've not really written many lexicons much myself yet, so this project is at a point of "well I think this makes sense". Both the [issues page](https://github.com/tylersayshi/prototypey/issues) and [discussions](https://github.com/tylersayshi/prototypey/discussions) are open and ready for y'all ๐Ÿ™‚. 225 + 226 + **Call For Contribution:** 227 + 228 + We need library art! Please reach out if you'd be willing to contribute some drawings or anything :)
+111
packages/prototypey/cli/gen-from-json.ts
···
··· 1 + import { glob } from "tinyglobby"; 2 + import { mkdir, writeFile, readFile } from "node:fs/promises"; 3 + import { join, dirname, basename } from "node:path"; 4 + 5 + interface LexiconJSON { 6 + lexicon: number; 7 + id: string; 8 + defs: Record<string, unknown>; 9 + } 10 + 11 + /** 12 + * Converts a lexicon ID to a valid TypeScript export name 13 + * e.g., "app.bsky.feed.post" -> "appBskyFeedPost" 14 + * "com.atproto.repo.createRecord" -> "comAtprotoRepoCreateRecord" 15 + */ 16 + function lexiconIdToExportName(id: string): string { 17 + // Split by dots and handle camelCase conversion 18 + const parts = id.split("."); 19 + 20 + // For the first part (e.g., "app", "com"), keep it lowercase 21 + // For subsequent parts, capitalize the first letter of each word 22 + // But preserve any existing camelCase within parts 23 + return parts 24 + .map((part, index) => { 25 + if (index === 0) return part; 26 + // Capitalize first letter of the part 27 + return part.charAt(0).toUpperCase() + part.slice(1); 28 + }) 29 + .join(""); 30 + } 31 + 32 + export async function genFromJSON( 33 + outdir: string, 34 + sources: string | string[], 35 + ): Promise<void> { 36 + try { 37 + const sourcePatterns = Array.isArray(sources) ? sources : [sources]; 38 + 39 + // Find all JSON files matching the patterns 40 + const jsonFiles = await glob(sourcePatterns, { 41 + absolute: true, 42 + onlyFiles: true, 43 + }); 44 + 45 + if (jsonFiles.length === 0) { 46 + console.log("No JSON files found matching patterns:", sourcePatterns); 47 + return; 48 + } 49 + 50 + console.log(`Found ${String(jsonFiles.length)} JSON file(s)`); 51 + 52 + // Ensure output directory exists 53 + await mkdir(outdir, { recursive: true }); 54 + 55 + // Process each JSON file 56 + for (const jsonPath of jsonFiles) { 57 + await processJSONFile(jsonPath, outdir); 58 + } 59 + 60 + console.log(`\nGenerated TypeScript files in ${outdir}`); 61 + } catch (error) { 62 + console.error("Error generating TypeScript from JSON:", error); 63 + process.exit(1); 64 + } 65 + } 66 + 67 + async function processJSONFile( 68 + jsonPath: string, 69 + outdir: string, 70 + ): Promise<void> { 71 + try { 72 + // Read and parse the JSON file 73 + const content = await readFile(jsonPath, "utf-8"); 74 + const lexiconJSON = JSON.parse(content); 75 + 76 + // Validate it's a lexicon 77 + if ( 78 + !lexiconJSON.lexicon || 79 + !lexiconJSON.id || 80 + !lexiconJSON.defs || 81 + typeof lexiconJSON.defs !== "object" 82 + ) { 83 + console.warn(` โš  ${jsonPath}: Not a valid lexicon JSON`); 84 + return; 85 + } 86 + 87 + const { id } = lexiconJSON as LexiconJSON; 88 + const exportName = lexiconIdToExportName(id); 89 + 90 + // Generate TypeScript content 91 + const tsContent = `import { fromJSON } from "prototypey"; 92 + 93 + export const ${exportName} = fromJSON(${JSON.stringify(lexiconJSON, null, "\t")}); 94 + `; 95 + 96 + // Determine output path - use same structure but in outdir 97 + const outputFileName = `${basename(jsonPath, ".json")}.ts`; 98 + const outputPath = join(outdir, outputFileName); 99 + 100 + // Ensure output directory exists 101 + await mkdir(dirname(outputPath), { recursive: true }); 102 + 103 + // Write the TypeScript file 104 + await writeFile(outputPath, tsContent, "utf-8"); 105 + 106 + console.log(` โœ“ ${id} -> ${outputFileName}`); 107 + } catch (error) { 108 + console.error(` โœ— Error processing ${jsonPath}:`, error); 109 + throw error; 110 + } 111 + }
+7
packages/prototypey/cli/main.ts
··· 2 3 import sade from "sade"; 4 import { genEmit } from "./gen-emit.ts"; 5 import pkg from "../package.json" with { type: "json" }; 6 7 const prog = sade("prototypey"); ··· 13 .describe("Emit JSON lexicon schemas from authored TypeScript") 14 .example("gen-emit ./lexicons ./src/lexicons/**/*.ts") 15 .action(genEmit); 16 17 prog.parse(process.argv);
··· 2 3 import sade from "sade"; 4 import { genEmit } from "./gen-emit.ts"; 5 + import { genFromJSON } from "./gen-from-json.ts"; 6 import pkg from "../package.json" with { type: "json" }; 7 8 const prog = sade("prototypey"); ··· 14 .describe("Emit JSON lexicon schemas from authored TypeScript") 15 .example("gen-emit ./lexicons ./src/lexicons/**/*.ts") 16 .action(genEmit); 17 + 18 + prog 19 + .command("gen-from-json <outdir> <sources...>") 20 + .describe("Generate TypeScript files from JSON lexicon schemas") 21 + .example("gen-from-json ./src/lexicons ./lexicons/**/*.json") 22 + .action(genFromJSON); 23 24 prog.parse(process.argv);
+434
packages/prototypey/cli/tests/gen-from-json.test.ts
···
··· 1 + import { expect, test, describe, beforeEach, afterEach } from "vitest"; 2 + import { mkdir, writeFile, rm, readFile } from "node:fs/promises"; 3 + import { join } from "node:path"; 4 + import { tmpdir } from "node:os"; 5 + import { genFromJSON } from "../gen-from-json.ts"; 6 + 7 + describe("genFromJSON", () => { 8 + let testDir: string; 9 + let outDir: string; 10 + 11 + beforeEach(async () => { 12 + // Create a temporary directory for test files 13 + testDir = join(tmpdir(), `prototypey-test-import-${String(Date.now())}`); 14 + outDir = join(testDir, "output"); 15 + await mkdir(testDir, { recursive: true }); 16 + await mkdir(outDir, { recursive: true }); 17 + }); 18 + 19 + afterEach(async () => { 20 + // Clean up test directory 21 + await rm(testDir, { recursive: true, force: true }); 22 + }); 23 + 24 + test("generates TypeScript from a simple JSON lexicon", async () => { 25 + // Create a test JSON lexicon file 26 + const jsonFile = join(testDir, "app.bsky.actor.profile.json"); 27 + const lexiconJSON = { 28 + lexicon: 1, 29 + id: "app.bsky.actor.profile", 30 + defs: { 31 + main: { 32 + type: "record", 33 + key: "self", 34 + record: { 35 + type: "object", 36 + properties: { 37 + displayName: { 38 + type: "string", 39 + maxLength: 64, 40 + maxGraphemes: 64, 41 + }, 42 + description: { 43 + type: "string", 44 + maxLength: 256, 45 + maxGraphemes: 256, 46 + }, 47 + }, 48 + }, 49 + }, 50 + }, 51 + }; 52 + 53 + await writeFile(jsonFile, JSON.stringify(lexiconJSON, null, 2)); 54 + 55 + // Run the from-json command 56 + await genFromJSON(outDir, jsonFile); 57 + 58 + // Read the generated TypeScript file 59 + const outputFile = join(outDir, "app.bsky.actor.profile.ts"); 60 + const content = await readFile(outputFile, "utf-8"); 61 + 62 + // Verify the structure 63 + expect(content).toContain('import { fromJSON } from "prototypey"'); 64 + expect(content).toContain("export const appBskyActorProfile = fromJSON("); 65 + expect(content).toContain('"id": "app.bsky.actor.profile"'); 66 + expect(content).toContain('"lexicon": 1'); 67 + 68 + // Verify it can be parsed as JSON within the call 69 + const jsonMatch = content.match(/fromJSON\(([\s\S]+)\);/); 70 + expect(jsonMatch).toBeTruthy(); 71 + if (jsonMatch) { 72 + const parsedJSON = JSON.parse(jsonMatch[1]); 73 + expect(parsedJSON).toEqual(lexiconJSON); 74 + } 75 + }); 76 + 77 + test("handles multiple JSON files with glob pattern", async () => { 78 + // Create multiple test JSON files 79 + const lexicons = join(testDir, "lexicons"); 80 + await mkdir(lexicons, { recursive: true }); 81 + 82 + const profileJSON = { 83 + lexicon: 1, 84 + id: "app.bsky.actor.profile", 85 + defs: { 86 + main: { 87 + type: "record", 88 + key: "self", 89 + record: { type: "object", properties: {} }, 90 + }, 91 + }, 92 + }; 93 + 94 + const postJSON = { 95 + lexicon: 1, 96 + id: "app.bsky.feed.post", 97 + defs: { 98 + main: { 99 + type: "record", 100 + key: "tid", 101 + record: { type: "object", properties: {} }, 102 + }, 103 + }, 104 + }; 105 + 106 + await writeFile( 107 + join(lexicons, "app.bsky.actor.profile.json"), 108 + JSON.stringify(profileJSON, null, 2), 109 + ); 110 + await writeFile( 111 + join(lexicons, "app.bsky.feed.post.json"), 112 + JSON.stringify(postJSON, null, 2), 113 + ); 114 + 115 + // Run with glob pattern 116 + await genFromJSON(outDir, `${lexicons}/*.json`); 117 + 118 + // Verify both files were created 119 + const profileTS = await readFile( 120 + join(outDir, "app.bsky.actor.profile.ts"), 121 + "utf-8", 122 + ); 123 + const postTS = await readFile( 124 + join(outDir, "app.bsky.feed.post.ts"), 125 + "utf-8", 126 + ); 127 + 128 + expect(profileTS).toContain("appBskyActorProfile"); 129 + expect(postTS).toContain("appBskyFeedPost"); 130 + }); 131 + 132 + test("generates correct export names from lexicon IDs", async () => { 133 + const testCases = [ 134 + { id: "app.bsky.feed.post", expectedName: "appBskyFeedPost" }, 135 + { 136 + id: "com.atproto.repo.createRecord", 137 + expectedName: "comAtprotoRepoCreateRecord", 138 + }, 139 + { id: "app.bsky.actor.profile", expectedName: "appBskyActorProfile" }, 140 + { id: "simple", expectedName: "simple" }, 141 + ]; 142 + 143 + for (const { id, expectedName } of testCases) { 144 + const jsonFile = join(testDir, `${id}.json`); 145 + const lexiconJSON = { 146 + lexicon: 1, 147 + id, 148 + defs: { main: { type: "object", properties: {} } }, 149 + }; 150 + 151 + await writeFile(jsonFile, JSON.stringify(lexiconJSON, null, 2)); 152 + await genFromJSON(outDir, jsonFile); 153 + 154 + const outputFile = join(outDir, `${id}.ts`); 155 + const content = await readFile(outputFile, "utf-8"); 156 + 157 + expect(content).toContain(`export const ${expectedName} = fromJSON(`); 158 + } 159 + }); 160 + 161 + test("generates TypeScript from query endpoint JSON", async () => { 162 + const jsonFile = join(testDir, "app.bsky.feed.searchPosts.json"); 163 + const lexiconJSON = { 164 + lexicon: 1, 165 + id: "app.bsky.feed.searchPosts", 166 + defs: { 167 + main: { 168 + type: "query", 169 + description: "Find posts matching search criteria", 170 + parameters: { 171 + type: "params", 172 + properties: { 173 + q: { type: "string", required: true }, 174 + limit: { type: "integer", minimum: 1, maximum: 100, default: 25 }, 175 + cursor: { type: "string" }, 176 + }, 177 + required: ["q"], 178 + }, 179 + output: { 180 + encoding: "application/json", 181 + schema: { 182 + type: "object", 183 + properties: { 184 + cursor: { type: "string" }, 185 + posts: { 186 + type: "array", 187 + items: { type: "ref", ref: "app.bsky.feed.defs#postView" }, 188 + required: true, 189 + }, 190 + }, 191 + required: ["posts"], 192 + }, 193 + }, 194 + }, 195 + }, 196 + }; 197 + 198 + await writeFile(jsonFile, JSON.stringify(lexiconJSON, null, 2)); 199 + await genFromJSON(outDir, jsonFile); 200 + 201 + const outputFile = join(outDir, "app.bsky.feed.searchPosts.ts"); 202 + const content = await readFile(outputFile, "utf-8"); 203 + 204 + expect(content).toContain("appBskyFeedSearchPosts"); 205 + expect(content).toContain('"type": "query"'); 206 + expect(content).toContain("Find posts matching search criteria"); 207 + }); 208 + 209 + test("generates TypeScript from procedure endpoint JSON", async () => { 210 + const jsonFile = join(testDir, "com.atproto.repo.createRecord.json"); 211 + const lexiconJSON = { 212 + lexicon: 1, 213 + id: "com.atproto.repo.createRecord", 214 + defs: { 215 + main: { 216 + type: "procedure", 217 + description: "Create a record", 218 + input: { 219 + encoding: "application/json", 220 + schema: { 221 + type: "object", 222 + properties: { 223 + repo: { type: "string", required: true }, 224 + collection: { type: "string", required: true }, 225 + record: { type: "unknown", required: true }, 226 + }, 227 + required: ["repo", "collection", "record"], 228 + }, 229 + }, 230 + output: { 231 + encoding: "application/json", 232 + schema: { 233 + type: "object", 234 + properties: { 235 + uri: { type: "string", required: true }, 236 + cid: { type: "string", required: true }, 237 + }, 238 + required: ["uri", "cid"], 239 + }, 240 + }, 241 + }, 242 + }, 243 + }; 244 + 245 + await writeFile(jsonFile, JSON.stringify(lexiconJSON, null, 2)); 246 + await genFromJSON(outDir, jsonFile); 247 + 248 + const outputFile = join(outDir, "com.atproto.repo.createRecord.ts"); 249 + const content = await readFile(outputFile, "utf-8"); 250 + 251 + expect(content).toContain("comAtprotoRepoCreateRecord"); 252 + expect(content).toContain('"type": "procedure"'); 253 + }); 254 + 255 + test("generates TypeScript from subscription endpoint JSON", async () => { 256 + const jsonFile = join(testDir, "com.atproto.sync.subscribeRepos.json"); 257 + const lexiconJSON = { 258 + lexicon: 1, 259 + id: "com.atproto.sync.subscribeRepos", 260 + defs: { 261 + main: { 262 + type: "subscription", 263 + description: "Repository event stream", 264 + parameters: { 265 + type: "params", 266 + properties: { 267 + cursor: { type: "integer" }, 268 + }, 269 + }, 270 + message: { 271 + schema: { 272 + type: "union", 273 + refs: ["#commit", "#identity", "#account"], 274 + }, 275 + }, 276 + }, 277 + commit: { 278 + type: "object", 279 + properties: { 280 + seq: { type: "integer", required: true }, 281 + rebase: { type: "boolean", required: true }, 282 + }, 283 + required: ["seq", "rebase"], 284 + }, 285 + identity: { 286 + type: "object", 287 + properties: { 288 + seq: { type: "integer", required: true }, 289 + did: { type: "string", format: "did", required: true }, 290 + }, 291 + required: ["seq", "did"], 292 + }, 293 + account: { 294 + type: "object", 295 + properties: { 296 + seq: { type: "integer", required: true }, 297 + active: { type: "boolean", required: true }, 298 + }, 299 + required: ["seq", "active"], 300 + }, 301 + }, 302 + }; 303 + 304 + await writeFile(jsonFile, JSON.stringify(lexiconJSON, null, 2)); 305 + await genFromJSON(outDir, jsonFile); 306 + 307 + const outputFile = join(outDir, "com.atproto.sync.subscribeRepos.ts"); 308 + const content = await readFile(outputFile, "utf-8"); 309 + 310 + expect(content).toContain("comAtprotoSyncSubscribeRepos"); 311 + expect(content).toContain('"type": "subscription"'); 312 + expect(content).toContain("commit"); 313 + expect(content).toContain("identity"); 314 + expect(content).toContain("account"); 315 + }); 316 + 317 + test("generates TypeScript from complex namespace with refs and unions", async () => { 318 + const jsonFile = join(testDir, "app.bsky.feed.defs.json"); 319 + const lexiconJSON = { 320 + lexicon: 1, 321 + id: "app.bsky.feed.defs", 322 + defs: { 323 + postView: { 324 + type: "object", 325 + properties: { 326 + uri: { type: "string", format: "at-uri", required: true }, 327 + cid: { type: "string", format: "cid", required: true }, 328 + author: { 329 + type: "ref", 330 + ref: "app.bsky.actor.defs#profileViewBasic", 331 + required: true, 332 + }, 333 + embed: { 334 + type: "union", 335 + refs: ["app.bsky.embed.images#view", "app.bsky.embed.video#view"], 336 + }, 337 + likeCount: { type: "integer", minimum: 0 }, 338 + }, 339 + required: ["uri", "cid", "author"], 340 + }, 341 + requestLess: { 342 + type: "token", 343 + description: "Request less content like this", 344 + }, 345 + requestMore: { 346 + type: "token", 347 + description: "Request more content like this", 348 + }, 349 + }, 350 + }; 351 + 352 + await writeFile(jsonFile, JSON.stringify(lexiconJSON, null, 2)); 353 + await genFromJSON(outDir, jsonFile); 354 + 355 + const outputFile = join(outDir, "app.bsky.feed.defs.ts"); 356 + const content = await readFile(outputFile, "utf-8"); 357 + 358 + expect(content).toContain("appBskyFeedDefs"); 359 + expect(content).toContain("postView"); 360 + expect(content).toContain("requestLess"); 361 + expect(content).toContain("requestMore"); 362 + }); 363 + 364 + test("handles invalid JSON gracefully", async () => { 365 + const jsonFile = join(testDir, "invalid.json"); 366 + await writeFile(jsonFile, "{ this is not valid json }"); 367 + 368 + await expect(genFromJSON(outDir, jsonFile)).rejects.toThrow(); 369 + }); 370 + 371 + test("skips non-lexicon JSON files", async () => { 372 + const jsonFile = join(testDir, "not-a-lexicon.json"); 373 + await writeFile( 374 + jsonFile, 375 + JSON.stringify({ someKey: "someValue" }, null, 2), 376 + ); 377 + 378 + // Should not throw, just warn and skip 379 + await genFromJSON(outDir, jsonFile); 380 + 381 + // Verify no output file was created 382 + const outputFiles = await readdir(outDir).catch(() => []); 383 + expect(outputFiles.length).toBe(0); 384 + }); 385 + 386 + test("round-trip: gen-emit then gen-from-json produces equivalent types", async () => { 387 + // This is an integration test that verifies the round-trip works 388 + const intermediateDir = join(testDir, "json"); 389 + await mkdir(intermediateDir, { recursive: true }); 390 + 391 + // First, create a simple TypeScript lexicon 392 + const tsFile = join(testDir, "original.ts"); 393 + await writeFile( 394 + tsFile, 395 + ` 396 + import { lx } from "prototypey"; 397 + 398 + export const postSchema = lx.lexicon("app.bsky.feed.post", { 399 + main: lx.record({ 400 + key: "tid", 401 + record: lx.object({ 402 + text: lx.string({ maxLength: 300, required: true }), 403 + createdAt: lx.string({ format: "datetime", required: true }), 404 + }), 405 + }), 406 + }); 407 + `, 408 + ); 409 + 410 + // Import gen-emit dynamically to use it 411 + const { genEmit } = await import("../gen-emit.ts"); 412 + 413 + // Step 1: gen-emit to create JSON 414 + await genEmit(intermediateDir, tsFile); 415 + 416 + // Step 2: gen-from-json to create TypeScript from JSON 417 + const jsonFile = join(intermediateDir, "app.bsky.feed.post.json"); 418 + await genFromJSON(outDir, jsonFile); 419 + 420 + // Verify the output exists 421 + const outputFile = join(outDir, "app.bsky.feed.post.ts"); 422 + const content = await readFile(outputFile, "utf-8"); 423 + 424 + expect(content).toContain("appBskyFeedPost"); 425 + expect(content).toContain('"id": "app.bsky.feed.post"'); 426 + expect(content).toContain('"type": "record"'); 427 + }); 428 + }); 429 + 430 + // Helper function that was missing from imports 431 + async function readdir(path: string): Promise<string[]> { 432 + const { readdir: fsReaddir } = await import("node:fs/promises"); 433 + return fsReaddir(path); 434 + }
+73 -49
packages/prototypey/core/lib.ts
··· 33 * Common options available for lexicon items. 34 * @see https://atproto.com/specs/lexicon#string-formats 35 */ 36 - interface LexiconItemCommonOptions { 37 /** Indicates this field must be provided */ 38 required?: boolean; 39 /** Indicates this field can be explicitly set to null */ 40 nullable?: boolean; 41 - } 42 43 /** 44 * Base interface for all lexicon items. 45 * @see https://atproto.com/specs/lexicon#overview-of-types 46 */ 47 - interface LexiconItem extends LexiconItemCommonOptions { 48 type: LexiconType; 49 - } 50 51 /** 52 * Definition in a lexicon namespace. 53 * @see https://atproto.com/specs/lexicon#lexicon-document 54 */ 55 - interface Def { 56 type: LexiconType; 57 - } 58 59 /** 60 * Lexicon namespace document structure. 61 * @see https://atproto.com/specs/lexicon#lexicon-document 62 */ 63 - interface LexiconNamespace { 64 /** Namespaced identifier (NSID) for this lexicon */ 65 id: string; 66 /** Named definitions within this namespace */ 67 defs: Record<string, Def>; 68 - } 69 70 /** 71 * String type options. 72 * @see https://atproto.com/specs/lexicon#string 73 */ 74 - interface StringOptions extends LexiconItemCommonOptions { 75 /** 76 * Semantic string format constraint. 77 * @see https://atproto.com/specs/lexicon#string-formats ··· 104 default?: string; 105 /** Fixed, unchangeable value */ 106 const?: string; 107 - } 108 109 /** 110 * Boolean type options. 111 * @see https://atproto.com/specs/lexicon#boolean 112 */ 113 - interface BooleanOptions extends LexiconItemCommonOptions { 114 /** Default value if not provided */ 115 default?: boolean; 116 /** Fixed, unchangeable value */ 117 const?: boolean; 118 - } 119 120 /** 121 * Integer type options. 122 * @see https://atproto.com/specs/lexicon#integer 123 */ 124 - interface IntegerOptions extends LexiconItemCommonOptions { 125 /** Minimum allowed value (inclusive) */ 126 minimum?: number; 127 /** Maximum allowed value (inclusive) */ ··· 132 default?: number; 133 /** Fixed, unchangeable value */ 134 const?: number; 135 - } 136 137 /** 138 * Bytes type options for arbitrary byte arrays. 139 * @see https://atproto.com/specs/lexicon#bytes 140 */ 141 - interface BytesOptions extends LexiconItemCommonOptions { 142 /** Minimum byte array length */ 143 minLength?: number; 144 /** Maximum byte array length */ 145 maxLength?: number; 146 - } 147 148 /** 149 * Blob type options for binary data with MIME types. 150 * @see https://atproto.com/specs/lexicon#blob 151 */ 152 - interface BlobOptions extends LexiconItemCommonOptions { 153 /** Allowed MIME types (e.g., ["image/png", "image/jpeg"]) */ 154 accept?: string[]; 155 /** Maximum blob size in bytes */ 156 maxSize?: number; 157 - } 158 159 /** 160 * Array type options. 161 * @see https://atproto.com/specs/lexicon#array 162 */ 163 - interface ArrayOptions extends LexiconItemCommonOptions { 164 /** Minimum array length */ 165 minLength?: number; 166 /** Maximum array length */ 167 maxLength?: number; 168 - } 169 170 /** 171 * Record type options for repository records. 172 * @see https://atproto.com/specs/lexicon#record 173 */ 174 - interface RecordOptions { 175 - /** Record key strategy: "self" for self-describing or "tid" for timestamp IDs */ 176 - key: "self" | "tid"; 177 /** Object schema defining the record structure */ 178 record: { type: "object" }; 179 /** Human-readable description */ 180 description?: string; 181 - } 182 183 /** 184 * Union type options for multiple possible types. 185 * @see https://atproto.com/specs/lexicon#union 186 */ 187 - interface UnionOptions extends LexiconItemCommonOptions { 188 /** If true, only listed refs are allowed; if false, additional types may be added */ 189 closed?: boolean; 190 - } 191 192 /** 193 * Map of property names to their lexicon item definitions. ··· 200 } 201 >; 202 203 type RequiredKeys<T> = { 204 [K in keyof T]: T[K] extends { required: true } ? K : never; 205 }[keyof T]; ··· 212 * Resulting object schema with required and nullable fields extracted. 213 * @see https://atproto.com/specs/lexicon#object 214 */ 215 - type ObjectResult<T extends ObjectProperties> = { 216 type: "object"; 217 /** Property definitions */ 218 properties: { ··· 225 : { required: UnionToTuple<RequiredKeys<T>> }) & 226 ([NullableKeys<T>] extends [never] 227 ? {} 228 - : { nullable: UnionToTuple<NullableKeys<T>> }); 229 230 /** 231 * Map of parameter names to their lexicon item definitions. ··· 251 * HTTP request or response body schema. 252 * @see https://atproto.com/specs/lexicon#http-endpoints 253 */ 254 - interface BodySchema { 255 /** MIME type encoding (typically "application/json") */ 256 encoding: "application/json" | (string & {}); 257 /** Human-readable description */ 258 description?: string; 259 /** Object schema defining the body structure */ 260 schema?: ObjectResult<ObjectProperties>; 261 - } 262 263 /** 264 * Error definition for HTTP endpoints. 265 * @see https://atproto.com/specs/lexicon#http-endpoints 266 */ 267 - interface ErrorDef { 268 /** Error name/code */ 269 name: string; 270 /** Human-readable error description */ 271 description?: string; 272 - } 273 274 /** 275 * Query endpoint options (HTTP GET). 276 * @see https://atproto.com/specs/lexicon#query 277 */ 278 - interface QueryOptions { 279 /** Human-readable description */ 280 description?: string; 281 /** Query string parameters */ ··· 284 output?: BodySchema; 285 /** Possible error responses */ 286 errors?: ErrorDef[]; 287 - } 288 289 /** 290 * Procedure endpoint options (HTTP POST). 291 * @see https://atproto.com/specs/lexicon#procedure 292 */ 293 - interface ProcedureOptions { 294 /** Human-readable description */ 295 description?: string; 296 /** Query string parameters */ ··· 301 output?: BodySchema; 302 /** Possible error responses */ 303 errors?: ErrorDef[]; 304 - } 305 306 /** 307 * WebSocket message schema for subscriptions. 308 * @see https://atproto.com/specs/lexicon#subscription 309 */ 310 - interface MessageSchema { 311 /** Human-readable description */ 312 description?: string; 313 /** Union of possible message types */ 314 schema: { type: "union"; refs: readonly string[] }; 315 - } 316 317 /** 318 * Subscription endpoint options (WebSocket). 319 * @see https://atproto.com/specs/lexicon#subscription 320 */ 321 - interface SubscriptionOptions { 322 /** Human-readable description */ 323 description?: string; 324 /** Query string parameters */ ··· 327 message?: MessageSchema; 328 /** Possible error responses */ 329 errors?: ErrorDef[]; 330 - } 331 332 /** 333 * Public interface for Lexicon to avoid exposing private implementation details 334 */ 335 - export interface LexiconSchema<T extends LexiconNamespace> { 336 json: T; 337 "~infer": Infer<{ json: T }>; 338 validate( 339 data: unknown, 340 def?: keyof T["defs"], 341 ): ValidationResult<Infer<{ json: T }>>; 342 - } 343 344 class Lexicon<T extends LexiconNamespace> implements LexiconSchema<T> { 345 public json: T; ··· 524 * Creates an object type with defined properties. 525 * @see https://atproto.com/specs/lexicon#object 526 */ 527 - object<T extends ObjectProperties>(options: T): ObjectResult<T> { 528 - const required = Object.keys(options).filter( 529 - (key) => "required" in options[key] && options[key].required, 530 ); 531 - const nullable = Object.keys(options).filter( 532 - (key) => "nullable" in options[key] && options[key].nullable, 533 ); 534 const result: Record<string, unknown> = { 535 type: "object", 536 - properties: options, 537 }; 538 if (required.length > 0) { 539 result.required = required; ··· 541 if (nullable.length > 0) { 542 result.nullable = nullable; 543 } 544 - return result as ObjectResult<T>; 545 }, 546 /** 547 * Creates a params type for query string parameters. ··· 611 }); 612 }, 613 };
··· 33 * Common options available for lexicon items. 34 * @see https://atproto.com/specs/lexicon#string-formats 35 */ 36 + type LexiconItemCommonOptions = { 37 /** Indicates this field must be provided */ 38 required?: boolean; 39 /** Indicates this field can be explicitly set to null */ 40 nullable?: boolean; 41 + /** Human-readable description */ 42 + description?: string; 43 + }; 44 45 /** 46 * Base interface for all lexicon items. 47 * @see https://atproto.com/specs/lexicon#overview-of-types 48 */ 49 + type LexiconItem = LexiconItemCommonOptions & { 50 type: LexiconType; 51 + }; 52 53 /** 54 * Definition in a lexicon namespace. 55 * @see https://atproto.com/specs/lexicon#lexicon-document 56 */ 57 + type Def = { 58 type: LexiconType; 59 + }; 60 61 /** 62 * Lexicon namespace document structure. 63 * @see https://atproto.com/specs/lexicon#lexicon-document 64 */ 65 + type LexiconNamespace = { 66 /** Namespaced identifier (NSID) for this lexicon */ 67 id: string; 68 /** Named definitions within this namespace */ 69 defs: Record<string, Def>; 70 + }; 71 72 /** 73 * String type options. 74 * @see https://atproto.com/specs/lexicon#string 75 */ 76 + type StringOptions = LexiconItemCommonOptions & { 77 /** 78 * Semantic string format constraint. 79 * @see https://atproto.com/specs/lexicon#string-formats ··· 106 default?: string; 107 /** Fixed, unchangeable value */ 108 const?: string; 109 + }; 110 111 /** 112 * Boolean type options. 113 * @see https://atproto.com/specs/lexicon#boolean 114 */ 115 + type BooleanOptions = LexiconItemCommonOptions & { 116 /** Default value if not provided */ 117 default?: boolean; 118 /** Fixed, unchangeable value */ 119 const?: boolean; 120 + }; 121 122 /** 123 * Integer type options. 124 * @see https://atproto.com/specs/lexicon#integer 125 */ 126 + type IntegerOptions = LexiconItemCommonOptions & { 127 /** Minimum allowed value (inclusive) */ 128 minimum?: number; 129 /** Maximum allowed value (inclusive) */ ··· 134 default?: number; 135 /** Fixed, unchangeable value */ 136 const?: number; 137 + }; 138 139 /** 140 * Bytes type options for arbitrary byte arrays. 141 * @see https://atproto.com/specs/lexicon#bytes 142 */ 143 + type BytesOptions = LexiconItemCommonOptions & { 144 /** Minimum byte array length */ 145 minLength?: number; 146 /** Maximum byte array length */ 147 maxLength?: number; 148 + }; 149 150 /** 151 * Blob type options for binary data with MIME types. 152 * @see https://atproto.com/specs/lexicon#blob 153 */ 154 + type BlobOptions = LexiconItemCommonOptions & { 155 /** Allowed MIME types (e.g., ["image/png", "image/jpeg"]) */ 156 accept?: string[]; 157 /** Maximum blob size in bytes */ 158 maxSize?: number; 159 + }; 160 161 /** 162 * Array type options. 163 * @see https://atproto.com/specs/lexicon#array 164 */ 165 + type ArrayOptions = LexiconItemCommonOptions & { 166 /** Minimum array length */ 167 minLength?: number; 168 /** Maximum array length */ 169 maxLength?: number; 170 + }; 171 172 /** 173 * Record type options for repository records. 174 * @see https://atproto.com/specs/lexicon#record 175 */ 176 + type RecordOptions = { 177 + /** 178 + * Record key strategy: "self" for self-describing or "tid" for timestamp IDs 179 + * @see https://atproto.com/specs/record-key 180 + */ 181 + key: string; 182 /** Object schema defining the record structure */ 183 record: { type: "object" }; 184 /** Human-readable description */ 185 description?: string; 186 + }; 187 188 /** 189 * Union type options for multiple possible types. 190 * @see https://atproto.com/specs/lexicon#union 191 */ 192 + type UnionOptions = LexiconItemCommonOptions & { 193 /** If true, only listed refs are allowed; if false, additional types may be added */ 194 closed?: boolean; 195 + }; 196 197 /** 198 * Map of property names to their lexicon item definitions. ··· 205 } 206 >; 207 208 + /** 209 + * Object-level options (not property-level). 210 + * @see https://atproto.com/specs/lexicon#object 211 + */ 212 + type ObjectOptions = { 213 + /** Human-readable description of the object */ 214 + description?: string; 215 + }; 216 + 217 type RequiredKeys<T> = { 218 [K in keyof T]: T[K] extends { required: true } ? K : never; 219 }[keyof T]; ··· 226 * Resulting object schema with required and nullable fields extracted. 227 * @see https://atproto.com/specs/lexicon#object 228 */ 229 + type ObjectResult<T extends ObjectProperties, O extends ObjectOptions = {}> = { 230 type: "object"; 231 /** Property definitions */ 232 properties: { ··· 239 : { required: UnionToTuple<RequiredKeys<T>> }) & 240 ([NullableKeys<T>] extends [never] 241 ? {} 242 + : { nullable: UnionToTuple<NullableKeys<T>> }) & 243 + O; 244 245 /** 246 * Map of parameter names to their lexicon item definitions. ··· 266 * HTTP request or response body schema. 267 * @see https://atproto.com/specs/lexicon#http-endpoints 268 */ 269 + type BodySchema = { 270 /** MIME type encoding (typically "application/json") */ 271 encoding: "application/json" | (string & {}); 272 /** Human-readable description */ 273 description?: string; 274 /** Object schema defining the body structure */ 275 schema?: ObjectResult<ObjectProperties>; 276 + }; 277 278 /** 279 * Error definition for HTTP endpoints. 280 * @see https://atproto.com/specs/lexicon#http-endpoints 281 */ 282 + type ErrorDef = { 283 /** Error name/code */ 284 name: string; 285 /** Human-readable error description */ 286 description?: string; 287 + }; 288 289 /** 290 * Query endpoint options (HTTP GET). 291 * @see https://atproto.com/specs/lexicon#query 292 */ 293 + type QueryOptions = { 294 /** Human-readable description */ 295 description?: string; 296 /** Query string parameters */ ··· 299 output?: BodySchema; 300 /** Possible error responses */ 301 errors?: ErrorDef[]; 302 + }; 303 304 /** 305 * Procedure endpoint options (HTTP POST). 306 * @see https://atproto.com/specs/lexicon#procedure 307 */ 308 + type ProcedureOptions = { 309 /** Human-readable description */ 310 description?: string; 311 /** Query string parameters */ ··· 316 output?: BodySchema; 317 /** Possible error responses */ 318 errors?: ErrorDef[]; 319 + }; 320 321 /** 322 * WebSocket message schema for subscriptions. 323 * @see https://atproto.com/specs/lexicon#subscription 324 */ 325 + type MessageSchema = { 326 /** Human-readable description */ 327 description?: string; 328 /** Union of possible message types */ 329 schema: { type: "union"; refs: readonly string[] }; 330 + }; 331 332 /** 333 * Subscription endpoint options (WebSocket). 334 * @see https://atproto.com/specs/lexicon#subscription 335 */ 336 + type SubscriptionOptions = { 337 /** Human-readable description */ 338 description?: string; 339 /** Query string parameters */ ··· 342 message?: MessageSchema; 343 /** Possible error responses */ 344 errors?: ErrorDef[]; 345 + }; 346 347 /** 348 * Public interface for Lexicon to avoid exposing private implementation details 349 */ 350 + export type LexiconSchema<T extends LexiconNamespace> = { 351 json: T; 352 "~infer": Infer<{ json: T }>; 353 validate( 354 data: unknown, 355 def?: keyof T["defs"], 356 ): ValidationResult<Infer<{ json: T }>>; 357 + }; 358 359 class Lexicon<T extends LexiconNamespace> implements LexiconSchema<T> { 360 public json: T; ··· 539 * Creates an object type with defined properties. 540 * @see https://atproto.com/specs/lexicon#object 541 */ 542 + object<T extends ObjectProperties, O extends ObjectOptions>( 543 + properties: T, 544 + options?: O, 545 + ): ObjectResult<T, O> { 546 + const required = Object.keys(properties).filter( 547 + (key) => "required" in properties[key] && properties[key].required, 548 ); 549 + const nullable = Object.keys(properties).filter( 550 + (key) => "nullable" in properties[key] && properties[key].nullable, 551 ); 552 const result: Record<string, unknown> = { 553 type: "object", 554 + properties, 555 + ...options, 556 }; 557 if (required.length > 0) { 558 result.required = required; ··· 560 if (nullable.length > 0) { 561 result.nullable = nullable; 562 } 563 + return result as ObjectResult<T, O>; 564 }, 565 /** 566 * Creates a params type for query string parameters. ··· 630 }); 631 }, 632 }; 633 + 634 + /** helper to pull lexicon from json directly */ 635 + export function fromJSON<const Lex extends LexiconNamespace>(json: Lex) { 636 + return lx.lexicon<Lex["id"], Lex["defs"]>(json.id, json.defs); 637 + }
+3 -2
packages/prototypey/core/main.ts
··· 1 - export * from "./lib.ts"; 2 - export * from "./infer.ts";
··· 1 + export { lx, fromJSON } from "./lib.ts"; 2 + export { type Infer } from "./infer.ts"; 3 + export type * from "@atproto/lexicon";
+1226
packages/prototypey/core/tests/from-json-infer.test.ts
···
··· 1 + import { test } from "vitest"; 2 + import { attest } from "@ark/attest"; 3 + import { fromJSON } from "../lib.ts"; 4 + import { Infer } from "../infer.ts"; 5 + 6 + test("fromJSON InferNS produces expected type shape", () => { 7 + const exampleLexicon = fromJSON({ 8 + id: "com.example.post", 9 + defs: { 10 + main: { 11 + type: "record", 12 + key: "tid", 13 + record: { 14 + type: "object", 15 + properties: { 16 + text: { type: "string", required: true }, 17 + createdAt: { type: "string", required: true, format: "datetime" }, 18 + likes: { type: "integer" }, 19 + tags: { type: "array", items: { type: "string" }, maxLength: 5 }, 20 + }, 21 + required: ["text", "createdAt"], 22 + }, 23 + }, 24 + }, 25 + }); 26 + 27 + // Type snapshot - this captures how types appear on hover 28 + attest(exampleLexicon["~infer"]).type.toString.snap(`{ 29 + $type: "com.example.post" 30 + tags?: string[] | undefined 31 + likes?: number | undefined 32 + createdAt: string 33 + text: string 34 + }`); 35 + }); 36 + 37 + test("fromJSON InferObject handles required fields", () => { 38 + const schema = fromJSON({ 39 + id: "test", 40 + defs: { 41 + main: { 42 + type: "object", 43 + properties: { 44 + required: { type: "string", required: true }, 45 + optional: { type: "string" }, 46 + }, 47 + required: ["required"], 48 + }, 49 + }, 50 + }); 51 + 52 + attest(schema["~infer"]).type.toString.snap(`{ 53 + $type: "test" 54 + optional?: string | undefined 55 + required: string 56 + }`); 57 + }); 58 + 59 + test("fromJSON InferObject handles nullable fields", () => { 60 + const schema = fromJSON({ 61 + id: "test", 62 + defs: { 63 + main: { 64 + type: "object", 65 + properties: { 66 + nullable: { type: "string", nullable: true, required: true }, 67 + }, 68 + required: ["nullable"], 69 + nullable: ["nullable"], 70 + }, 71 + }, 72 + }); 73 + 74 + attest(schema["~infer"]).type.toString.snap( 75 + '{ $type: "test"; nullable: string | null }', 76 + ); 77 + }); 78 + 79 + // ============================================================================ 80 + // PRIMITIVE TYPES TESTS 81 + // ============================================================================ 82 + 83 + test("fromJSON InferType handles string primitive", () => { 84 + const lexicon = fromJSON({ 85 + id: "test.string", 86 + defs: { 87 + main: { 88 + type: "object", 89 + properties: { 90 + simpleString: { type: "string" }, 91 + }, 92 + }, 93 + }, 94 + }); 95 + 96 + attest(lexicon["~infer"]).type.toString.snap(`{ 97 + $type: "test.string" 98 + simpleString?: string | undefined 99 + }`); 100 + }); 101 + 102 + test("fromJSON InferType handles integer primitive", () => { 103 + const lexicon = fromJSON({ 104 + id: "test.integer", 105 + defs: { 106 + main: { 107 + type: "object", 108 + properties: { 109 + count: { type: "integer" }, 110 + age: { type: "integer", minimum: 0, maximum: 120 }, 111 + }, 112 + }, 113 + }, 114 + }); 115 + 116 + attest(lexicon["~infer"]).type.toString.snap(`{ 117 + $type: "test.integer" 118 + count?: number | undefined 119 + age?: number | undefined 120 + }`); 121 + }); 122 + 123 + test("fromJSON InferType handles boolean primitive", () => { 124 + const lexicon = fromJSON({ 125 + id: "test.boolean", 126 + defs: { 127 + main: { 128 + type: "object", 129 + properties: { 130 + isActive: { type: "boolean" }, 131 + hasAccess: { type: "boolean", required: true }, 132 + }, 133 + required: ["hasAccess"], 134 + }, 135 + }, 136 + }); 137 + 138 + attest(lexicon["~infer"]).type.toString.snap(`{ 139 + $type: "test.boolean" 140 + isActive?: boolean | undefined 141 + hasAccess: boolean 142 + }`); 143 + }); 144 + 145 + test("fromJSON InferType handles null primitive", () => { 146 + const lexicon = fromJSON({ 147 + id: "test.null", 148 + defs: { 149 + main: { 150 + type: "object", 151 + properties: { 152 + nullValue: { type: "null" }, 153 + }, 154 + }, 155 + }, 156 + }); 157 + 158 + attest(lexicon["~infer"]).type.toString.snap(`{ 159 + $type: "test.null" 160 + nullValue?: null | undefined 161 + }`); 162 + }); 163 + 164 + test("fromJSON InferType handles unknown primitive", () => { 165 + const lexicon = fromJSON({ 166 + id: "test.unknown", 167 + defs: { 168 + main: { 169 + type: "object", 170 + properties: { 171 + metadata: { type: "unknown" }, 172 + }, 173 + }, 174 + }, 175 + }); 176 + 177 + attest(lexicon["~infer"]).type.toString.snap( 178 + '{ $type: "test.unknown"; metadata?: unknown }', 179 + ); 180 + }); 181 + 182 + test("fromJSON InferType handles bytes primitive", () => { 183 + const lexicon = fromJSON({ 184 + id: "test.bytes", 185 + defs: { 186 + main: { 187 + type: "object", 188 + properties: { 189 + data: { type: "bytes" }, 190 + }, 191 + }, 192 + }, 193 + }); 194 + 195 + attest(lexicon["~infer"]).type.toString.snap(`{ 196 + $type: "test.bytes" 197 + data?: Uint8Array<ArrayBufferLike> | undefined 198 + }`); 199 + }); 200 + 201 + test("fromJSON InferType handles blob primitive", () => { 202 + const lexicon = fromJSON({ 203 + id: "test.blob", 204 + defs: { 205 + main: { 206 + type: "object", 207 + properties: { 208 + image: { 209 + type: "blob", 210 + accept: ["image/png", "image/jpeg"], 211 + }, 212 + }, 213 + }, 214 + }, 215 + }); 216 + 217 + attest(lexicon["~infer"]).type.toString.snap( 218 + '{ $type: "test.blob"; image?: Blob | undefined }', 219 + ); 220 + }); 221 + 222 + // ============================================================================ 223 + // TOKEN TYPE TESTS 224 + // ============================================================================ 225 + 226 + test("fromJSON InferToken handles basic token without enum", () => { 227 + const lexicon = fromJSON({ 228 + id: "test.token", 229 + defs: { 230 + main: { 231 + type: "object", 232 + properties: { 233 + symbol: { type: "token", description: "A symbolic value" }, 234 + }, 235 + }, 236 + }, 237 + }); 238 + 239 + attest(lexicon["~infer"]).type.toString.snap(`{ 240 + $type: "test.token" 241 + symbol?: string | undefined 242 + }`); 243 + }); 244 + 245 + // ============================================================================ 246 + // ARRAY TYPE TESTS 247 + // ============================================================================ 248 + 249 + test("fromJSON InferArray handles string arrays", () => { 250 + const lexicon = fromJSON({ 251 + id: "test.array.string", 252 + defs: { 253 + main: { 254 + type: "object", 255 + properties: { 256 + tags: { type: "array", items: { type: "string" } }, 257 + }, 258 + }, 259 + }, 260 + }); 261 + 262 + attest(lexicon["~infer"]).type.toString.snap(`{ 263 + $type: "test.array.string" 264 + tags?: string[] | undefined 265 + }`); 266 + }); 267 + 268 + test("fromJSON InferArray handles integer arrays", () => { 269 + const lexicon = fromJSON({ 270 + id: "test.array.integer", 271 + defs: { 272 + main: { 273 + type: "object", 274 + properties: { 275 + scores: { 276 + type: "array", 277 + items: { type: "integer" }, 278 + minLength: 1, 279 + maxLength: 10, 280 + }, 281 + }, 282 + }, 283 + }, 284 + }); 285 + 286 + attest(lexicon["~infer"]).type.toString.snap(`{ 287 + $type: "test.array.integer" 288 + scores?: number[] | undefined 289 + }`); 290 + }); 291 + 292 + test("fromJSON InferArray handles boolean arrays", () => { 293 + const lexicon = fromJSON({ 294 + id: "test.array.boolean", 295 + defs: { 296 + main: { 297 + type: "object", 298 + properties: { 299 + flags: { type: "array", items: { type: "boolean" } }, 300 + }, 301 + }, 302 + }, 303 + }); 304 + 305 + attest(lexicon["~infer"]).type.toString.snap(`{ 306 + $type: "test.array.boolean" 307 + flags?: boolean[] | undefined 308 + }`); 309 + }); 310 + 311 + test("fromJSON InferArray handles unknown arrays", () => { 312 + const lexicon = fromJSON({ 313 + id: "test.array.unknown", 314 + defs: { 315 + main: { 316 + type: "object", 317 + properties: { 318 + items: { type: "array", items: { type: "unknown" } }, 319 + }, 320 + }, 321 + }, 322 + }); 323 + 324 + attest(lexicon["~infer"]).type.toString.snap(`{ 325 + $type: "test.array.unknown" 326 + items?: unknown[] | undefined 327 + }`); 328 + }); 329 + 330 + // ============================================================================ 331 + // OBJECT PROPERTY COMBINATIONS 332 + // ============================================================================ 333 + 334 + test("fromJSON InferObject handles mixed optional and required fields", () => { 335 + const lexicon = fromJSON({ 336 + id: "test.mixed", 337 + defs: { 338 + main: { 339 + type: "object", 340 + properties: { 341 + id: { type: "string", required: true }, 342 + name: { type: "string", required: true }, 343 + email: { type: "string" }, 344 + age: { type: "integer" }, 345 + }, 346 + required: ["id", "name"], 347 + }, 348 + }, 349 + }); 350 + 351 + attest(lexicon["~infer"]).type.toString.snap(`{ 352 + $type: "test.mixed" 353 + age?: number | undefined 354 + email?: string | undefined 355 + id: string 356 + name: string 357 + }`); 358 + }); 359 + 360 + test("fromJSON InferObject handles all optional fields", () => { 361 + const lexicon = fromJSON({ 362 + id: "test.allOptional", 363 + defs: { 364 + main: { 365 + type: "object", 366 + properties: { 367 + field1: { type: "string" }, 368 + field2: { type: "integer" }, 369 + field3: { type: "boolean" }, 370 + }, 371 + }, 372 + }, 373 + }); 374 + 375 + attest(lexicon["~infer"]).type.toString.snap(`{ 376 + $type: "test.allOptional" 377 + field1?: string | undefined 378 + field2?: number | undefined 379 + field3?: boolean | undefined 380 + }`); 381 + }); 382 + 383 + test("fromJSON InferObject handles all required fields", () => { 384 + const lexicon = fromJSON({ 385 + id: "test.allRequired", 386 + defs: { 387 + main: { 388 + type: "object", 389 + properties: { 390 + field1: { type: "string", required: true }, 391 + field2: { type: "integer", required: true }, 392 + field3: { type: "boolean", required: true }, 393 + }, 394 + required: ["field1", "field2", "field3"], 395 + }, 396 + }, 397 + }); 398 + 399 + attest(lexicon["~infer"]).type.toString.snap(`{ 400 + $type: "test.allRequired" 401 + field1: string 402 + field2: number 403 + field3: boolean 404 + }`); 405 + }); 406 + 407 + // ============================================================================ 408 + // NULLABLE FIELDS TESTS 409 + // ============================================================================ 410 + 411 + test("fromJSON InferObject handles nullable optional field", () => { 412 + const lexicon = fromJSON({ 413 + id: "test.nullableOptional", 414 + defs: { 415 + main: { 416 + type: "object", 417 + properties: { 418 + description: { type: "string", nullable: true }, 419 + }, 420 + nullable: ["description"], 421 + }, 422 + }, 423 + }); 424 + 425 + attest(lexicon["~infer"]).type.toString.snap(`{ 426 + $type: "test.nullableOptional" 427 + description?: string | null | undefined 428 + }`); 429 + }); 430 + 431 + test("fromJSON InferObject handles multiple nullable fields", () => { 432 + const lexicon = fromJSON({ 433 + id: "test.multipleNullable", 434 + defs: { 435 + main: { 436 + type: "object", 437 + properties: { 438 + field1: { type: "string", nullable: true }, 439 + field2: { type: "integer", nullable: true }, 440 + field3: { type: "boolean", nullable: true }, 441 + }, 442 + nullable: ["field1", "field2", "field3"], 443 + }, 444 + }, 445 + }); 446 + 447 + attest(lexicon["~infer"]).type.toString.snap(`{ 448 + $type: "test.multipleNullable" 449 + field1?: string | null | undefined 450 + field2?: number | null | undefined 451 + field3?: boolean | null | undefined 452 + }`); 453 + }); 454 + 455 + test("fromJSON InferObject handles nullable and required field", () => { 456 + const lexicon = fromJSON({ 457 + id: "test.nullableRequired", 458 + defs: { 459 + main: { 460 + type: "object", 461 + properties: { 462 + value: { type: "string", nullable: true, required: true }, 463 + }, 464 + required: ["value"], 465 + nullable: ["value"], 466 + }, 467 + }, 468 + }); 469 + 470 + attest(lexicon["~infer"]).type.toString.snap(`{ 471 + $type: "test.nullableRequired" 472 + value: string | null 473 + }`); 474 + }); 475 + 476 + test("fromJSON InferObject handles mixed nullable, required, and optional", () => { 477 + const lexicon = fromJSON({ 478 + id: "test.mixedNullable", 479 + defs: { 480 + main: { 481 + type: "object", 482 + properties: { 483 + requiredNullable: { type: "string", required: true, nullable: true }, 484 + optionalNullable: { type: "string", nullable: true }, 485 + required: { type: "string", required: true }, 486 + optional: { type: "string" }, 487 + }, 488 + required: ["requiredNullable", "required"], 489 + nullable: ["requiredNullable", "optionalNullable"], 490 + }, 491 + }, 492 + }); 493 + 494 + attest(lexicon["~infer"]).type.toString.snap(`{ 495 + $type: "test.mixedNullable" 496 + optional?: string | undefined 497 + required: string 498 + optionalNullable?: string | null | undefined 499 + requiredNullable: string | null 500 + }`); 501 + }); 502 + 503 + // ============================================================================ 504 + // REF TYPE TESTS 505 + // ============================================================================ 506 + 507 + test("fromJSON InferRef handles external reference (unknown)", () => { 508 + const lexicon = fromJSON({ 509 + id: "test.ref", 510 + defs: { 511 + main: { 512 + type: "object", 513 + properties: { 514 + post: { type: "ref", ref: "com.example.post" }, 515 + }, 516 + }, 517 + }, 518 + }); 519 + 520 + attest(lexicon["~infer"]).type.toString.snap(`{ 521 + $type: "test.ref" 522 + post?: 523 + | { [x: string]: unknown; $type: "com.example.post" } 524 + | undefined 525 + }`); 526 + }); 527 + 528 + // ============================================================================ 529 + // UNION TYPE TESTS 530 + // ============================================================================ 531 + 532 + test("fromJSON InferUnion handles external union (unknown)", () => { 533 + const lexicon = fromJSON({ 534 + id: "test.union", 535 + defs: { 536 + main: { 537 + type: "object", 538 + properties: { 539 + content: { 540 + type: "union", 541 + refs: ["com.example.text", "com.example.image"], 542 + }, 543 + }, 544 + }, 545 + }, 546 + }); 547 + 548 + attest(lexicon["~infer"]).type.toString.snap(`{ 549 + $type: "test.union" 550 + content?: 551 + | { [x: string]: unknown; $type: "com.example.text" } 552 + | { [x: string]: unknown; $type: "com.example.image" } 553 + | undefined 554 + }`); 555 + }); 556 + 557 + // ============================================================================ 558 + // PARAMS TYPE TESTS 559 + // ============================================================================ 560 + 561 + test("fromJSON InferParams handles basic params", () => { 562 + const lexicon = fromJSON({ 563 + id: "test.params", 564 + defs: { 565 + main: { 566 + type: "params", 567 + properties: { 568 + limit: { type: "integer" }, 569 + offset: { type: "integer" }, 570 + }, 571 + }, 572 + }, 573 + }); 574 + 575 + attest(lexicon["~infer"]).type.toString.snap(`{ 576 + $type: "test.params" 577 + limit?: number | undefined 578 + offset?: number | undefined 579 + }`); 580 + }); 581 + 582 + test("fromJSON InferParams handles required params", () => { 583 + const lexicon = fromJSON({ 584 + id: "test.paramsRequired", 585 + defs: { 586 + main: { 587 + type: "params", 588 + properties: { 589 + query: { type: "string", required: true }, 590 + limit: { type: "integer" }, 591 + }, 592 + required: ["query"], 593 + }, 594 + }, 595 + }); 596 + 597 + attest(lexicon["~infer"]).type.toString.snap(`{ 598 + $type: "test.paramsRequired" 599 + limit?: number | undefined 600 + query: string 601 + }`); 602 + }); 603 + 604 + // ============================================================================ 605 + // RECORD TYPE TESTS 606 + // ============================================================================ 607 + 608 + test("fromJSON InferRecord handles record with object schema", () => { 609 + const lexicon = fromJSON({ 610 + id: "test.record", 611 + defs: { 612 + main: { 613 + type: "record", 614 + key: "tid", 615 + record: { 616 + type: "object", 617 + properties: { 618 + title: { type: "string", required: true }, 619 + content: { type: "string", required: true }, 620 + published: { type: "boolean" }, 621 + }, 622 + required: ["title", "content"], 623 + }, 624 + }, 625 + }, 626 + }); 627 + 628 + attest(lexicon["~infer"]).type.toString.snap(`{ 629 + $type: "test.record" 630 + published?: boolean | undefined 631 + content: string 632 + title: string 633 + }`); 634 + }); 635 + 636 + // ============================================================================ 637 + // NESTED OBJECTS TESTS 638 + // ============================================================================ 639 + 640 + test("fromJSON InferObject handles nested objects", () => { 641 + const lexicon = fromJSON({ 642 + id: "test.nested", 643 + defs: { 644 + main: { 645 + type: "object", 646 + properties: { 647 + user: { 648 + type: "object", 649 + properties: { 650 + name: { type: "string", required: true }, 651 + email: { type: "string", required: true }, 652 + }, 653 + required: ["name", "email"], 654 + }, 655 + }, 656 + }, 657 + }, 658 + }); 659 + 660 + attest(lexicon["~infer"]).type.toString.snap(`{ 661 + $type: "test.nested" 662 + user?: { name: string; email: string } | undefined 663 + }`); 664 + }); 665 + 666 + test("fromJSON InferObject handles deeply nested objects", () => { 667 + const lexicon = fromJSON({ 668 + id: "test.deepNested", 669 + defs: { 670 + main: { 671 + type: "object", 672 + properties: { 673 + data: { 674 + type: "object", 675 + properties: { 676 + user: { 677 + type: "object", 678 + properties: { 679 + profile: { 680 + type: "object", 681 + properties: { 682 + name: { type: "string", required: true }, 683 + }, 684 + required: ["name"], 685 + }, 686 + }, 687 + }, 688 + }, 689 + }, 690 + }, 691 + }, 692 + }, 693 + }); 694 + 695 + attest(lexicon["~infer"]).type.toString.snap(`{ 696 + $type: "test.deepNested" 697 + data?: 698 + | { 699 + user?: 700 + | { profile?: { name: string } | undefined } 701 + | undefined 702 + } 703 + | undefined 704 + }`); 705 + }); 706 + 707 + // ============================================================================ 708 + // NESTED ARRAYS TESTS 709 + // ============================================================================ 710 + 711 + test("fromJSON InferArray handles arrays of objects", () => { 712 + const lexicon = fromJSON({ 713 + id: "test.arrayOfObjects", 714 + defs: { 715 + main: { 716 + type: "object", 717 + properties: { 718 + users: { 719 + type: "array", 720 + items: { 721 + type: "object", 722 + properties: { 723 + id: { type: "string", required: true }, 724 + name: { type: "string", required: true }, 725 + }, 726 + required: ["id", "name"], 727 + }, 728 + }, 729 + }, 730 + }, 731 + }, 732 + }); 733 + 734 + attest(lexicon["~infer"]).type.toString.snap(`{ 735 + $type: "test.arrayOfObjects" 736 + users?: { id: string; name: string }[] | undefined 737 + }`); 738 + }); 739 + 740 + test("fromJSON InferArray handles arrays of arrays", () => { 741 + const lexicon = fromJSON({ 742 + id: "test.nestedArrays", 743 + defs: { 744 + main: { 745 + type: "object", 746 + properties: { 747 + matrix: { 748 + type: "array", 749 + items: { type: "array", items: { type: "integer" } }, 750 + }, 751 + }, 752 + }, 753 + }, 754 + }); 755 + 756 + attest(lexicon["~infer"]).type.toString.snap(`{ 757 + $type: "test.nestedArrays" 758 + matrix?: number[][] | undefined 759 + }`); 760 + }); 761 + 762 + test("fromJSON InferArray handles arrays of refs", () => { 763 + const lexicon = fromJSON({ 764 + id: "test.arrayOfRefs", 765 + defs: { 766 + user: { 767 + type: "object", 768 + properties: { 769 + name: { type: "string", required: true }, 770 + handle: { type: "string", required: true }, 771 + }, 772 + required: ["name", "handle"], 773 + }, 774 + main: { 775 + type: "object", 776 + properties: { 777 + followers: { 778 + type: "array", 779 + items: { type: "ref", ref: "#user" }, 780 + }, 781 + }, 782 + }, 783 + }, 784 + }); 785 + 786 + attest(lexicon["~infer"]).type.toString.snap(`{ 787 + $type: "test.arrayOfRefs" 788 + followers?: 789 + | { handle: string; name: string; $type: "#user" }[] 790 + | undefined 791 + }`); 792 + }); 793 + 794 + // ============================================================================ 795 + // COMPLEX NESTED STRUCTURES 796 + // ============================================================================ 797 + 798 + test("fromJSON InferObject handles complex nested structure", () => { 799 + const lexicon = fromJSON({ 800 + id: "test.complex", 801 + defs: { 802 + text: { 803 + type: "object", 804 + properties: { 805 + content: { type: "string", required: true }, 806 + }, 807 + required: ["content"], 808 + }, 809 + image: { 810 + type: "object", 811 + properties: { 812 + url: { type: "string", required: true }, 813 + alt: { type: "string" }, 814 + }, 815 + required: ["url"], 816 + }, 817 + main: { 818 + type: "object", 819 + properties: { 820 + id: { type: "string", required: true }, 821 + author: { 822 + type: "object", 823 + properties: { 824 + did: { type: "string", required: true, format: "did" }, 825 + handle: { type: "string", required: true, format: "handle" }, 826 + avatar: { type: "string" }, 827 + }, 828 + required: ["did", "handle"], 829 + }, 830 + content: { 831 + type: "union", 832 + refs: ["#text", "#image"], 833 + }, 834 + tags: { type: "array", items: { type: "string" }, maxLength: 10 }, 835 + metadata: { 836 + type: "object", 837 + properties: { 838 + views: { type: "integer" }, 839 + likes: { type: "integer" }, 840 + shares: { type: "integer" }, 841 + }, 842 + }, 843 + }, 844 + required: ["id"], 845 + }, 846 + }, 847 + }); 848 + 849 + attest(lexicon["~infer"]).type.toString.snap(`{ 850 + $type: "test.complex" 851 + tags?: string[] | undefined 852 + content?: 853 + | { content: string; $type: "#text" } 854 + | { 855 + alt?: string | undefined 856 + url: string 857 + $type: "#image" 858 + } 859 + | undefined 860 + author?: 861 + | { 862 + avatar?: string | undefined 863 + did: string 864 + handle: string 865 + } 866 + | undefined 867 + metadata?: 868 + | { 869 + likes?: number | undefined 870 + views?: number | undefined 871 + shares?: number | undefined 872 + } 873 + | undefined 874 + id: string 875 + }`); 876 + }); 877 + 878 + // ============================================================================ 879 + // MULTIPLE DEFS IN NAMESPACE 880 + // ============================================================================ 881 + 882 + test("fromJSON InferNS handles multiple defs in namespace", () => { 883 + const lexicon = fromJSON({ 884 + id: "com.example.app", 885 + defs: { 886 + user: { 887 + type: "object", 888 + properties: { 889 + name: { type: "string", required: true }, 890 + email: { type: "string", required: true }, 891 + }, 892 + required: ["name", "email"], 893 + }, 894 + post: { 895 + type: "object", 896 + properties: { 897 + title: { type: "string", required: true }, 898 + content: { type: "string", required: true }, 899 + }, 900 + required: ["title", "content"], 901 + }, 902 + comment: { 903 + type: "object", 904 + properties: { 905 + text: { type: "string", required: true }, 906 + author: { type: "ref", ref: "#user" }, 907 + }, 908 + required: ["text"], 909 + }, 910 + }, 911 + }); 912 + 913 + attest(lexicon["~infer"]).type.toString.snap("never"); 914 + }); 915 + 916 + test("fromJSON InferNS handles namespace with record and object defs", () => { 917 + const lexicon = fromJSON({ 918 + id: "com.example.blog", 919 + defs: { 920 + main: { 921 + type: "record", 922 + key: "tid", 923 + record: { 924 + type: "object", 925 + properties: { 926 + title: { type: "string", required: true }, 927 + body: { type: "string", required: true }, 928 + }, 929 + required: ["title", "body"], 930 + }, 931 + }, 932 + metadata: { 933 + type: "object", 934 + properties: { 935 + category: { type: "string" }, 936 + tags: { type: "array", items: { type: "string" } }, 937 + }, 938 + }, 939 + }, 940 + }); 941 + 942 + attest(lexicon["~infer"]).type.toString.snap(`{ 943 + $type: "com.example.blog" 944 + title: string 945 + body: string 946 + }`); 947 + }); 948 + 949 + // ============================================================================ 950 + // LOCAL REF RESOLUTION TESTS 951 + // ============================================================================ 952 + 953 + test("fromJSON Local ref resolution: resolves refs to actual types", () => { 954 + const ns = fromJSON({ 955 + id: "test", 956 + defs: { 957 + user: { 958 + type: "object", 959 + properties: { 960 + name: { type: "string", required: true }, 961 + email: { type: "string", required: true }, 962 + }, 963 + required: ["name", "email"], 964 + }, 965 + main: { 966 + type: "object", 967 + properties: { 968 + author: { type: "ref", ref: "#user", required: true }, 969 + content: { type: "string", required: true }, 970 + }, 971 + required: ["author", "content"], 972 + }, 973 + }, 974 + }); 975 + 976 + attest(ns["~infer"]).type.toString.snap(`{ 977 + $type: "test" 978 + content: string 979 + author: { name: string; email: string; $type: "#user" } 980 + }`); 981 + }); 982 + 983 + test("fromJSON Local ref resolution: refs in arrays", () => { 984 + const ns = fromJSON({ 985 + id: "test", 986 + defs: { 987 + user: { 988 + type: "object", 989 + properties: { 990 + name: { type: "string", required: true }, 991 + }, 992 + required: ["name"], 993 + }, 994 + main: { 995 + type: "object", 996 + properties: { 997 + users: { type: "array", items: { type: "ref", ref: "#user" } }, 998 + }, 999 + }, 1000 + }, 1001 + }); 1002 + 1003 + attest(ns["~infer"]).type.toString.snap(`{ 1004 + $type: "test" 1005 + users?: { name: string; $type: "#user" }[] | undefined 1006 + }`); 1007 + }); 1008 + 1009 + test("fromJSON Local ref resolution: refs in unions", () => { 1010 + const ns = fromJSON({ 1011 + id: "test", 1012 + defs: { 1013 + text: { 1014 + type: "object", 1015 + properties: { content: { type: "string", required: true } }, 1016 + required: ["content"], 1017 + }, 1018 + image: { 1019 + type: "object", 1020 + properties: { url: { type: "string", required: true } }, 1021 + required: ["url"], 1022 + }, 1023 + main: { 1024 + type: "object", 1025 + properties: { 1026 + embed: { type: "union", refs: ["#text", "#image"] }, 1027 + }, 1028 + }, 1029 + }, 1030 + }); 1031 + 1032 + attest(ns["~infer"]).type.toString.snap(`{ 1033 + $type: "test" 1034 + embed?: 1035 + | { content: string; $type: "#text" } 1036 + | { url: string; $type: "#image" } 1037 + | undefined 1038 + }`); 1039 + }); 1040 + 1041 + test("fromJSON Local ref resolution: nested refs", () => { 1042 + const ns = fromJSON({ 1043 + id: "test", 1044 + defs: { 1045 + profile: { 1046 + type: "object", 1047 + properties: { 1048 + bio: { type: "string", required: true }, 1049 + }, 1050 + required: ["bio"], 1051 + }, 1052 + user: { 1053 + type: "object", 1054 + properties: { 1055 + name: { type: "string", required: true }, 1056 + profile: { type: "ref", ref: "#profile", required: true }, 1057 + }, 1058 + required: ["name", "profile"], 1059 + }, 1060 + main: { 1061 + type: "object", 1062 + properties: { 1063 + author: { type: "ref", ref: "#user", required: true }, 1064 + }, 1065 + required: ["author"], 1066 + }, 1067 + }, 1068 + }); 1069 + 1070 + attest(ns["~infer"]).type.toString.snap(`{ 1071 + $type: "test" 1072 + author: { 1073 + name: string 1074 + profile: { bio: string; $type: "#profile" } 1075 + $type: "#user" 1076 + } 1077 + }`); 1078 + }); 1079 + 1080 + // ============================================================================ 1081 + // EDGE CASE TESTS 1082 + // ============================================================================ 1083 + 1084 + test("fromJSON Edge case: circular reference detection", () => { 1085 + const ns = fromJSON({ 1086 + id: "test", 1087 + defs: { 1088 + main: { 1089 + type: "object", 1090 + properties: { 1091 + value: { type: "string", required: true }, 1092 + parent: { type: "ref", ref: "#main" }, 1093 + }, 1094 + required: ["value"], 1095 + }, 1096 + }, 1097 + }); 1098 + 1099 + attest(ns["~infer"]).type.toString.snap(`{ 1100 + $type: "test" 1101 + parent?: 1102 + | { 1103 + parent?: 1104 + | "[Circular reference detected: #main]" 1105 + | undefined 1106 + value: string 1107 + $type: "#main" 1108 + } 1109 + | undefined 1110 + value: string 1111 + }`); 1112 + }); 1113 + 1114 + test("fromJSON Edge case: circular reference between multiple types", () => { 1115 + const ns = fromJSON({ 1116 + id: "test", 1117 + defs: { 1118 + user: { 1119 + type: "object", 1120 + properties: { 1121 + name: { type: "string", required: true }, 1122 + posts: { type: "array", items: { type: "ref", ref: "#post" } }, 1123 + }, 1124 + required: ["name"], 1125 + }, 1126 + post: { 1127 + type: "object", 1128 + properties: { 1129 + title: { type: "string", required: true }, 1130 + author: { type: "ref", ref: "#user", required: true }, 1131 + }, 1132 + required: ["title", "author"], 1133 + }, 1134 + main: { 1135 + type: "object", 1136 + properties: { 1137 + users: { type: "array", items: { type: "ref", ref: "#user" } }, 1138 + }, 1139 + }, 1140 + }, 1141 + }); 1142 + 1143 + attest(ns["~infer"]).type.toString.snap(`{ 1144 + $type: "test" 1145 + users?: 1146 + | { 1147 + posts?: 1148 + | { 1149 + author: "[Circular reference detected: #user]" 1150 + title: string 1151 + $type: "#post" 1152 + }[] 1153 + | undefined 1154 + name: string 1155 + $type: "#user" 1156 + }[] 1157 + | undefined 1158 + }`); 1159 + }); 1160 + 1161 + test("fromJSON Edge case: missing reference detection", () => { 1162 + const ns = fromJSON({ 1163 + id: "test", 1164 + defs: { 1165 + main: { 1166 + type: "object", 1167 + properties: { 1168 + author: { type: "ref", ref: "#user", required: true }, 1169 + }, 1170 + required: ["author"], 1171 + }, 1172 + }, 1173 + }); 1174 + 1175 + attest(ns["~infer"]).type.toString.snap(`{ 1176 + $type: "test" 1177 + author: "[Reference not found: #user]" 1178 + }`); 1179 + }); 1180 + 1181 + // ============================================================================ 1182 + // REAL-WORLD EXAMPLE: BLUESKY PROFILE 1183 + // ============================================================================ 1184 + 1185 + test("fromJSON Real-world example: app.bsky.actor.profile", () => { 1186 + const lexicon = fromJSON({ 1187 + id: "app.bsky.actor.profile", 1188 + defs: { 1189 + main: { 1190 + type: "record", 1191 + key: "self", 1192 + record: { 1193 + type: "object", 1194 + properties: { 1195 + displayName: { 1196 + type: "string", 1197 + maxLength: 64, 1198 + maxGraphemes: 64, 1199 + }, 1200 + description: { 1201 + type: "string", 1202 + maxLength: 256, 1203 + maxGraphemes: 256, 1204 + }, 1205 + }, 1206 + }, 1207 + }, 1208 + }, 1209 + }); 1210 + 1211 + type Profile = Infer<typeof lexicon>; 1212 + 1213 + const george: Profile = { 1214 + $type: "app.bsky.actor.profile", 1215 + description: "George", 1216 + }; 1217 + 1218 + lexicon.validate({ foo: "bar" }); // will fail 1219 + lexicon.validate(george); // will pass ๐ŸŽ‰ 1220 + 1221 + attest(lexicon["~infer"]).type.toString.snap(`{ 1222 + $type: "app.bsky.actor.profile" 1223 + description?: string | undefined 1224 + displayName?: string | undefined 1225 + }`); 1226 + });
+136
packages/prototypey/core/tests/from-json.test.ts
···
··· 1 + import { expect, test } from "vitest"; 2 + import { fromJSON, lx } from "../lib.ts"; 3 + 4 + test("fromJSON creates lexicon from JSON", () => { 5 + const lexicon = fromJSON({ 6 + id: "app.bsky.actor.profile", 7 + defs: { 8 + main: { 9 + type: "record", 10 + key: "self", 11 + record: { 12 + type: "object", 13 + properties: { 14 + displayName: { 15 + type: "string", 16 + maxLength: 64, 17 + maxGraphemes: 64, 18 + }, 19 + description: { 20 + type: "string", 21 + maxLength: 256, 22 + maxGraphemes: 256, 23 + }, 24 + }, 25 + }, 26 + }, 27 + }, 28 + }); 29 + 30 + expect(lexicon.json).toEqual({ 31 + lexicon: 1, 32 + id: "app.bsky.actor.profile", 33 + defs: { 34 + main: { 35 + type: "record", 36 + key: "self", 37 + record: { 38 + type: "object", 39 + properties: { 40 + displayName: { 41 + type: "string", 42 + maxLength: 64, 43 + maxGraphemes: 64, 44 + }, 45 + description: { 46 + type: "string", 47 + maxLength: 256, 48 + maxGraphemes: 256, 49 + }, 50 + }, 51 + }, 52 + }, 53 + }, 54 + }); 55 + }); 56 + 57 + test("fromJSON and lx.lexicon produce equivalent results", () => { 58 + // Create using lx.lexicon 59 + const viaLx = lx.lexicon("app.bsky.feed.post", { 60 + main: lx.record({ 61 + key: "tid", 62 + record: lx.object({ 63 + text: lx.string({ maxLength: 300, required: true }), 64 + createdAt: lx.string({ format: "datetime", required: true }), 65 + }), 66 + }), 67 + }); 68 + 69 + // Create using fromJSON 70 + const viaJSON = fromJSON({ 71 + id: "app.bsky.feed.post", 72 + defs: { 73 + main: { 74 + type: "record", 75 + key: "tid", 76 + record: { 77 + type: "object", 78 + properties: { 79 + text: { 80 + type: "string", 81 + maxLength: 300, 82 + required: true, 83 + }, 84 + createdAt: { 85 + type: "string", 86 + format: "datetime", 87 + required: true, 88 + }, 89 + }, 90 + required: ["text", "createdAt"], 91 + }, 92 + }, 93 + }, 94 + }); 95 + 96 + expect(viaLx.json).toEqual(viaJSON.json); 97 + }); 98 + 99 + test("fromJSON supports validation", () => { 100 + const lexicon = fromJSON({ 101 + id: "com.example.post", 102 + defs: { 103 + main: { 104 + type: "record", 105 + key: "tid", 106 + record: { 107 + type: "object", 108 + properties: { 109 + text: { 110 + type: "string", 111 + maxLength: 100, 112 + required: true, 113 + }, 114 + }, 115 + required: ["text"], 116 + }, 117 + }, 118 + }, 119 + }); 120 + 121 + // Valid data 122 + const validResult = lexicon.validate({ 123 + text: "Hello world", 124 + }); 125 + expect(validResult.success).toBe(true); 126 + 127 + // Invalid data - missing required field 128 + const invalidResult = lexicon.validate({}); 129 + expect(invalidResult.success).toBe(false); 130 + 131 + // Invalid data - text too long 132 + const tooLongResult = lexicon.validate({ 133 + text: "a".repeat(101), 134 + }); 135 + expect(tooLongResult.success).toBe(false); 136 + });
+213 -4
packages/prototypey/core/tests/infer.bench.ts
··· 1 import { bench } from "@ark/attest"; 2 import { lx } from "../lib.ts"; 3 4 bench("infer with simple object", () => { 5 const schema = lx.lexicon("test.simple", { ··· 9 }), 10 }); 11 return schema["~infer"]; 12 - }).types([741, "instantiations"]); 13 14 bench("infer with complex nested structure", () => { 15 const schema = lx.lexicon("test.complex", { ··· 32 }), 33 }); 34 return schema["~infer"]; 35 - }).types([1040, "instantiations"]); 36 37 bench("infer with circular reference", () => { 38 const ns = lx.lexicon("test", { ··· 49 }), 50 }); 51 return ns["~infer"]; 52 - }).types([692, "instantiations"]); 53 54 bench("infer with app.bsky.feed.defs lexicon", () => { 55 const schema = lx.lexicon("app.bsky.feed.defs", { ··· 116 interactionShare: lx.token("User shared the feed item"), 117 }); 118 return schema["~infer"]; 119 - }).types([1285, "instantiations"]);
··· 1 import { bench } from "@ark/attest"; 2 import { lx } from "../lib.ts"; 3 + import { fromJSON } from "../lib.ts"; 4 5 bench("infer with simple object", () => { 6 const schema = lx.lexicon("test.simple", { ··· 10 }), 11 }); 12 return schema["~infer"]; 13 + }).types([685, "instantiations"]); 14 15 bench("infer with complex nested structure", () => { 16 const schema = lx.lexicon("test.complex", { ··· 33 }), 34 }); 35 return schema["~infer"]; 36 + }).types([956, "instantiations"]); 37 38 bench("infer with circular reference", () => { 39 const ns = lx.lexicon("test", { ··· 50 }), 51 }); 52 return ns["~infer"]; 53 + }).types([634, "instantiations"]); 54 55 bench("infer with app.bsky.feed.defs lexicon", () => { 56 const schema = lx.lexicon("app.bsky.feed.defs", { ··· 117 interactionShare: lx.token("User shared the feed item"), 118 }); 119 return schema["~infer"]; 120 + }).types([1237, "instantiations"]); 121 + 122 + bench("fromJSON infer with simple object", () => { 123 + const schema = fromJSON({ 124 + id: "test.simple", 125 + defs: { 126 + main: { 127 + type: "object", 128 + properties: { 129 + id: { type: "string", required: true }, 130 + name: { type: "string", required: true }, 131 + }, 132 + required: ["id", "name"], 133 + }, 134 + }, 135 + }); 136 + return schema["~infer"]; 137 + }).types([438, "instantiations"]); 138 + 139 + bench("fromJSON infer with complex nested structure", () => { 140 + const schema = fromJSON({ 141 + id: "test.complex", 142 + defs: { 143 + user: { 144 + type: "object", 145 + properties: { 146 + handle: { type: "string", required: true }, 147 + displayName: { type: "string" }, 148 + }, 149 + required: ["handle"], 150 + }, 151 + reply: { 152 + type: "object", 153 + properties: { 154 + text: { type: "string", required: true }, 155 + author: { type: "ref", ref: "#user", required: true }, 156 + }, 157 + required: ["text", "author"], 158 + }, 159 + main: { 160 + type: "record", 161 + key: "tid", 162 + record: { 163 + type: "object", 164 + properties: { 165 + author: { type: "ref", ref: "#user", required: true }, 166 + replies: { type: "array", items: { type: "ref", ref: "#reply" } }, 167 + content: { type: "string", required: true }, 168 + createdAt: { 169 + type: "string", 170 + required: true, 171 + format: "datetime", 172 + }, 173 + }, 174 + required: ["author", "content", "createdAt"], 175 + }, 176 + }, 177 + }, 178 + }); 179 + return schema["~infer"]; 180 + }).types([499, "instantiations"]); 181 + 182 + bench("fromJSON infer with circular reference", () => { 183 + const ns = fromJSON({ 184 + id: "test", 185 + defs: { 186 + user: { 187 + type: "object", 188 + properties: { 189 + name: { type: "string", required: true }, 190 + posts: { type: "array", items: { type: "ref", ref: "#post" } }, 191 + }, 192 + required: ["name"], 193 + }, 194 + post: { 195 + type: "object", 196 + properties: { 197 + title: { type: "string", required: true }, 198 + author: { type: "ref", ref: "#user", required: true }, 199 + }, 200 + required: ["title", "author"], 201 + }, 202 + main: { 203 + type: "object", 204 + properties: { 205 + users: { type: "array", items: { type: "ref", ref: "#user" } }, 206 + }, 207 + }, 208 + }, 209 + }); 210 + return ns["~infer"]; 211 + }).types([411, "instantiations"]); 212 + 213 + bench("fromJSON infer with app.bsky.feed.defs lexicon", () => { 214 + const schema = fromJSON({ 215 + id: "app.bsky.feed.defs", 216 + defs: { 217 + viewerState: { 218 + type: "object", 219 + properties: { 220 + repost: { type: "string", format: "at-uri" }, 221 + like: { type: "string", format: "at-uri" }, 222 + bookmarked: { type: "boolean" }, 223 + threadMuted: { type: "boolean" }, 224 + replyDisabled: { type: "boolean" }, 225 + embeddingDisabled: { type: "boolean" }, 226 + pinned: { type: "boolean" }, 227 + }, 228 + }, 229 + main: { 230 + type: "object", 231 + properties: { 232 + uri: { type: "string", required: true, format: "at-uri" }, 233 + cid: { type: "string", required: true, format: "cid" }, 234 + author: { 235 + type: "ref", 236 + ref: "app.bsky.actor.defs#profileViewBasic", 237 + required: true, 238 + }, 239 + record: { type: "unknown", required: true }, 240 + embed: { 241 + type: "union", 242 + refs: [ 243 + "app.bsky.embed.images#view", 244 + "app.bsky.embed.video#view", 245 + "app.bsky.embed.external#view", 246 + "app.bsky.embed.record#view", 247 + "app.bsky.embed.recordWithMedia#view", 248 + ], 249 + }, 250 + bookmarkCount: { type: "integer" }, 251 + replyCount: { type: "integer" }, 252 + repostCount: { type: "integer" }, 253 + likeCount: { type: "integer" }, 254 + quoteCount: { type: "integer" }, 255 + indexedAt: { type: "string", required: true, format: "datetime" }, 256 + viewer: { type: "ref", ref: "#viewerState" }, 257 + labels: { 258 + type: "array", 259 + items: { type: "ref", ref: "com.atproto.label.defs#label" }, 260 + }, 261 + threadgate: { type: "ref", ref: "#threadgateView" }, 262 + }, 263 + required: ["uri", "cid", "author", "record", "indexedAt"], 264 + }, 265 + requestLess: { 266 + type: "token", 267 + description: 268 + "Request that less content like the given feed item be shown in the feed", 269 + }, 270 + requestMore: { 271 + type: "token", 272 + description: 273 + "Request that more content like the given feed item be shown in the feed", 274 + }, 275 + clickthroughItem: { 276 + type: "token", 277 + description: "User clicked through to the feed item", 278 + }, 279 + clickthroughAuthor: { 280 + type: "token", 281 + description: "User clicked through to the author of the feed item", 282 + }, 283 + clickthroughReposter: { 284 + type: "token", 285 + description: "User clicked through to the reposter of the feed item", 286 + }, 287 + clickthroughEmbed: { 288 + type: "token", 289 + description: 290 + "User clicked through to the embedded content of the feed item", 291 + }, 292 + contentModeUnspecified: { 293 + type: "token", 294 + description: "Declares the feed generator returns any types of posts.", 295 + }, 296 + contentModeVideo: { 297 + type: "token", 298 + description: 299 + "Declares the feed generator returns posts containing app.bsky.embed.video embeds.", 300 + }, 301 + interactionSeen: { 302 + type: "token", 303 + description: "Feed item was seen by user", 304 + }, 305 + interactionLike: { 306 + type: "token", 307 + description: "User liked the feed item", 308 + }, 309 + interactionRepost: { 310 + type: "token", 311 + description: "User reposted the feed item", 312 + }, 313 + interactionReply: { 314 + type: "token", 315 + description: "User replied to the feed item", 316 + }, 317 + interactionQuote: { 318 + type: "token", 319 + description: "User quoted the feed item", 320 + }, 321 + interactionShare: { 322 + type: "token", 323 + description: "User shared the feed item", 324 + }, 325 + }, 326 + }); 327 + return schema["~infer"]; 328 + }).types([513, "instantiations"]);
+73
packages/prototypey/core/tests/primitives.test.ts
··· 183 }); 184 }); 185 186 test("lx.token() with interaction event", () => { 187 const result = lx.token( 188 "Request that less content like the given feed item be shown in the feed",
··· 183 }); 184 }); 185 186 + test("lx.object() basic", () => { 187 + const result = lx.object({ 188 + name: lx.string(), 189 + }); 190 + expect(result).toEqual({ 191 + type: "object", 192 + properties: { 193 + name: { type: "string" }, 194 + }, 195 + }); 196 + }); 197 + 198 + test("lx.object() with description", () => { 199 + const result = lx.object( 200 + { 201 + enabled: lx.boolean({ 202 + default: true, 203 + description: "Whether this feature is enabled.", 204 + }), 205 + }, 206 + { 207 + description: "Configuration options for the feature.", 208 + }, 209 + ); 210 + expect(result).toEqual({ 211 + type: "object", 212 + description: "Configuration options for the feature.", 213 + properties: { 214 + enabled: { 215 + type: "boolean", 216 + default: true, 217 + description: "Whether this feature is enabled.", 218 + }, 219 + }, 220 + }); 221 + }); 222 + 223 + test("lx.object() with required and description", () => { 224 + const result = lx.object( 225 + { 226 + id: lx.string({ required: true }), 227 + name: lx.string(), 228 + }, 229 + { description: "User profile object" }, 230 + ); 231 + expect(result).toEqual({ 232 + type: "object", 233 + description: "User profile object", 234 + properties: { 235 + id: { type: "string", required: true }, 236 + name: { type: "string" }, 237 + }, 238 + required: ["id"], 239 + }); 240 + }); 241 + 242 + test("lx.object() with nullable and description", () => { 243 + const result = lx.object( 244 + { 245 + bio: lx.string({ nullable: true }), 246 + }, 247 + { description: "Optional profile fields" }, 248 + ); 249 + expect(result).toEqual({ 250 + type: "object", 251 + description: "Optional profile fields", 252 + properties: { 253 + bio: { type: "string", nullable: true }, 254 + }, 255 + nullable: ["bio"], 256 + }); 257 + }); 258 + 259 test("lx.token() with interaction event", () => { 260 const result = lx.token( 261 "Request that less content like the given feed item be shown in the feed",
+4 -4
packages/prototypey/package.json
··· 1 { 2 "name": "prototypey", 3 - "version": "0.2.5", 4 "description": "atproto lexicon typescript toolkit", 5 "repository": { 6 "type": "git", ··· 36 "tsc": "tsc" 37 }, 38 "dependencies": { 39 - "@atproto/lexicon": "^0.5.1", 40 "sade": "^1.8.1", 41 "tinyglobby": "^0.2.15" 42 }, 43 "devDependencies": { 44 "@ark/attest": "^0.49.0", 45 "@types/node": "24.0.4", 46 - "tsdown": "0.12.7", 47 - "typescript": "5.8.3", 48 "vitest": "^3.2.4" 49 }, 50 "engines": {
··· 1 { 2 "name": "prototypey", 3 + "version": "0.3.8", 4 "description": "atproto lexicon typescript toolkit", 5 "repository": { 6 "type": "git", ··· 36 "tsc": "tsc" 37 }, 38 "dependencies": { 39 + "@atproto/lexicon": "^0.5.2", 40 "sade": "^1.8.1", 41 "tinyglobby": "^0.2.15" 42 }, 43 "devDependencies": { 44 "@ark/attest": "^0.49.0", 45 "@types/node": "24.0.4", 46 + "tsdown": "^0.15.12", 47 + "typescript": "5.9.3", 48 "vitest": "^3.2.4" 49 }, 50 "engines": {
+13 -11
packages/site/package.json
··· 11 "test": "vitest" 12 }, 13 "dependencies": { 14 - "@monaco-editor/react": "^4.6.0", 15 "lz-string": "^1.5.0", 16 "monaco-editor": "0.52.0", 17 - "nuqs": "^2.7.2", 18 "prototypey": "workspace:*", 19 - "react": "^19.2.0", 20 - "react-dom": "^19.2.0" 21 }, 22 "devDependencies": { 23 "@testing-library/jest-dom": "^6.9.1", 24 - "@testing-library/react": "^16.1.0", 25 - "@testing-library/user-event": "^14.5.2", 26 - "@types/react": "^19.2.2", 27 - "@types/react-dom": "^19.2.2", 28 - "@vitejs/plugin-react": "^5.0.4", 29 "babel-plugin-react-compiler": "^1.0.0", 30 "eslint-plugin-react-compiler": "19.1.0-rc.2", 31 "jsdom": "^25.0.1", 32 - "typescript": "5.8.3", 33 - "vite": "^6.0.5", 34 "vitest": "^3.2.4" 35 } 36 }
··· 11 "test": "vitest" 12 }, 13 "dependencies": { 14 + "@monaco-editor/react": "^4.7.0", 15 "lz-string": "^1.5.0", 16 "monaco-editor": "0.52.0", 17 + "nuqs": "^2.8.8", 18 "prototypey": "workspace:*", 19 + "react": "^19.2.4", 20 + "react-dom": "^19.2.4" 21 }, 22 "devDependencies": { 23 + "@tailwindcss/vite": "^4.1.18", 24 "@testing-library/jest-dom": "^6.9.1", 25 + "@testing-library/react": "^16.3.2", 26 + "@testing-library/user-event": "^14.6.1", 27 + "@types/react": "^19.2.13", 28 + "@types/react-dom": "^19.2.3", 29 + "@vitejs/plugin-react": "^5.1.3", 30 "babel-plugin-react-compiler": "^1.0.0", 31 "eslint-plugin-react-compiler": "19.1.0-rc.2", 32 "jsdom": "^25.0.1", 33 + "tailwindcss": "^4.1.18", 34 + "typescript": "5.9.3", 35 + "vite": "^6.4.1", 36 "vitest": "^3.2.4" 37 } 38 }
+2 -2
packages/site/src/App.tsx
··· 3 4 export function App() { 5 return ( 6 - <> 7 <Header /> 8 <Playground /> 9 - </> 10 ); 11 }
··· 3 4 export function App() { 5 return ( 6 + <div className="flex flex-col min-h-screen w-full bg-white dark:bg-gray-900 text-gray-700 dark:text-gray-200"> 7 <Header /> 8 <Playground /> 9 + </div> 10 ); 11 }
+6 -31
packages/site/src/components/Editor.tsx
··· 83 84 if (!isReady) { 85 return ( 86 - <div style={{ flex: 1, display: "flex", flexDirection: "column" }}> 87 - <div 88 - style={{ 89 - padding: "0.75rem 1rem", 90 - backgroundColor: "var(--color-bg-secondary)", 91 - borderBottom: "1px solid var(--color-border)", 92 - fontSize: "0.875rem", 93 - fontWeight: "600", 94 - color: "var(--color-text-secondary)", 95 - }} 96 - > 97 Input 98 </div> 99 - <div 100 - style={{ 101 - flex: 1, 102 - display: "flex", 103 - alignItems: "center", 104 - justifyContent: "center", 105 - }} 106 - > 107 Loading... 108 </div> 109 </div> ··· 111 } 112 113 return ( 114 - <div style={{ flex: 1, display: "flex", flexDirection: "column" }}> 115 - <div 116 - style={{ 117 - padding: "0.75rem 1rem", 118 - backgroundColor: "var(--color-bg-secondary)", 119 - borderBottom: "1px solid var(--color-border)", 120 - fontSize: "0.875rem", 121 - fontWeight: "600", 122 - color: "var(--color-text-secondary)", 123 - }} 124 - > 125 Input 126 </div> 127 - <div style={{ flex: 1 }}> 128 <MonacoEditor 129 height="100%" 130 defaultLanguage="typescript"
··· 83 84 if (!isReady) { 85 return ( 86 + <div className="flex-1 flex flex-col"> 87 + <div className="py-3 px-4 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 text-sm font-semibold text-gray-500 dark:text-gray-400"> 88 Input 89 </div> 90 + <div className="flex-1 flex items-center justify-center"> 91 Loading... 92 </div> 93 </div> ··· 95 } 96 97 return ( 98 + <div className="flex-1 flex flex-col"> 99 + <div className="py-3 px-4 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 text-sm font-semibold text-gray-500 dark:text-gray-400"> 100 Input 101 </div> 102 + <div className="flex-1"> 103 <MonacoEditor 104 height="100%" 105 defaultLanguage="typescript"
+89 -137
packages/site/src/components/Header.tsx
··· 1 import { useState } from "react"; 2 3 export function Header() { 4 const [copied, setCopied] = useState(false); 5 ··· 16 }; 17 18 return ( 19 - <header 20 - style={{ 21 - padding: "2rem 2rem 1rem 2rem", 22 - borderBottom: "1px solid var(--color-border)", 23 - }} 24 - > 25 - <div style={{ maxWidth: "1400px", margin: "0 auto" }}> 26 - <div className="header-content"> 27 <div> 28 - <h1 29 - style={{ 30 - fontSize: "2.5rem", 31 - fontWeight: "700", 32 - marginBottom: "0.5rem", 33 - }} 34 - > 35 - <span style={{ color: "var(--color-text-secondary)" }}> 36 - at:// 37 - </span> 38 prototypey 39 </h1> 40 - <p 41 - style={{ 42 - fontSize: "1.125rem", 43 - color: "var(--color-text-secondary)", 44 - marginTop: "0.5rem", 45 - }} 46 - > 47 ATProto lexicon typescript toolkit 48 </p> 49 </div> 50 - <div 51 - className="link-container" 52 - style={{ 53 - display: "flex", 54 - flexDirection: "column", 55 - alignItems: "flex-end", 56 - gap: "1rem", 57 - }} 58 - > 59 - <div className="header-links"> 60 - <a 61 - href="https://github.com/tylersayshi/prototypey" 62 - target="_blank" 63 - rel="noopener noreferrer" 64 - style={{ 65 - color: "var(--color-text-heading)", 66 - textDecoration: "none", 67 - fontSize: "1rem", 68 - fontWeight: "600", 69 - display: "flex", 70 - alignItems: "center", 71 - gap: "0.5rem", 72 - transition: "opacity 0.2s", 73 - willChange: "opacity", 74 - }} 75 - onMouseEnter={(e) => (e.currentTarget.style.opacity = "0.6")} 76 - onMouseLeave={(e) => (e.currentTarget.style.opacity = "1")} 77 - > 78 - <svg 79 - width="20" 80 - height="20" 81 - viewBox="0 0 24 24" 82 - fill="currentColor" 83 - aria-hidden="true" 84 - style={{ flexShrink: 0 }} 85 - > 86 - <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" /> 87 - </svg> 88 - github 89 - </a> 90 - <a 91 href="https://www.npmjs.com/package/prototypey" 92 - target="_blank" 93 - rel="noopener noreferrer" 94 - style={{ 95 - color: "var(--color-text-heading)", 96 - textDecoration: "none", 97 - fontSize: "1rem", 98 - fontWeight: "600", 99 - display: "flex", 100 - alignItems: "center", 101 - gap: "0.5rem", 102 - transition: "opacity 0.2s", 103 - willChange: "opacity", 104 - }} 105 - onMouseEnter={(e) => (e.currentTarget.style.opacity = "0.6")} 106 - onMouseLeave={(e) => (e.currentTarget.style.opacity = "1")} 107 - > 108 - <svg 109 - width="20" 110 - height="20" 111 - viewBox="0 0 24 24" 112 - fill="none" 113 - stroke="currentColor" 114 - strokeWidth="2" 115 - strokeLinecap="round" 116 - strokeLinejoin="round" 117 - aria-hidden="true" 118 - style={{ flexShrink: 0 }} 119 - > 120 - <path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" /> 121 - <polyline points="3.27 6.96 12 12.01 20.73 6.96" /> 122 - <line x1="12" y1="22.08" x2="12" y2="12" /> 123 - </svg> 124 - npm 125 - </a> 126 </div> 127 <button 128 onClick={handleShare} 129 - style={{ 130 - color: "var(--color-text-heading)", 131 - fontSize: "0.875rem", 132 - fontWeight: "600", 133 - display: "flex", 134 - alignItems: "center", 135 - gap: "0.5rem", 136 - transition: "background-color 0.2s, border-color 0.2s", 137 - backgroundColor: "transparent", 138 - border: "1px solid var(--color-border)", 139 - borderRadius: "0.5rem", 140 - cursor: "pointer", 141 - padding: "0.5rem 1rem", 142 - boxShadow: "none", 143 - outline: "none", 144 - flexShrink: 0, 145 - whiteSpace: "nowrap", 146 - }} 147 - onMouseEnter={(e) => { 148 - const bgColor = getComputedStyle(document.documentElement) 149 - .getPropertyValue("--color-bg-secondary") 150 - .trim(); 151 - const borderColor = getComputedStyle(document.documentElement) 152 - .getPropertyValue("--color-text-heading") 153 - .trim(); 154 - e.currentTarget.style.backgroundColor = bgColor; 155 - e.currentTarget.style.borderColor = borderColor; 156 - }} 157 - onMouseLeave={(e) => { 158 - e.currentTarget.style.backgroundColor = "transparent"; 159 - const borderColor = getComputedStyle(document.documentElement) 160 - .getPropertyValue("--color-border") 161 - .trim(); 162 - e.currentTarget.style.borderColor = borderColor; 163 - }} 164 aria-label="share playground" 165 > 166 {copied ? ( ··· 175 strokeLinecap="round" 176 strokeLinejoin="round" 177 aria-hidden="true" 178 - style={{ flexShrink: 0 }} 179 > 180 <polyline points="20 6 9 17 4 12" /> 181 </svg> ··· 193 strokeLinecap="round" 194 strokeLinejoin="round" 195 aria-hidden="true" 196 - style={{ flexShrink: 0 }} 197 > 198 <circle cx="18" cy="5" r="3" /> 199 <circle cx="6" cy="12" r="3" />
··· 1 import { useState } from "react"; 2 3 + interface HeaderLinkProps { 4 + href: string; 5 + icon: React.ReactNode; 6 + label: string; 7 + } 8 + 9 + function HeaderLink({ href, icon, label }: HeaderLinkProps) { 10 + return ( 11 + <a 12 + href={href} 13 + target="_blank" 14 + rel="noopener noreferrer" 15 + className="text-gray-900 dark:text-gray-100 no-underline text-base font-semibold flex items-center gap-2 transition-opacity duration-200 hover:opacity-60" 16 + > 17 + {icon} 18 + {label} 19 + </a> 20 + ); 21 + } 22 + 23 export function Header() { 24 const [copied, setCopied] = useState(false); 25 ··· 36 }; 37 38 return ( 39 + <header className="py-8 px-8 pb-4 border-b border-gray-200 dark:border-gray-700"> 40 + <div className="max-w-[1400px] mx-auto"> 41 + <div className="flex justify-between items-start flex-col md:flex-row gap-4 md:gap-0"> 42 <div> 43 + <h1 className="text-4xl font-bold mb-2"> 44 + <span className="text-gray-500 dark:text-gray-400">at://</span> 45 prototypey 46 </h1> 47 + <p className="text-lg text-gray-500 dark:text-gray-400 mt-2"> 48 ATProto lexicon typescript toolkit 49 </p> 50 </div> 51 + <div className="flex flex-col items-end gap-4 w-full md:w-auto"> 52 + <div className="flex gap-5 pt-2 md:pt-0 w-full md:w-auto justify-start"> 53 + <HeaderLink 54 + href="https://github.com/tylersayshi/prototypey?tab=readme-ov-file#prototypey" 55 + icon={ 56 + <svg 57 + width="20" 58 + height="20" 59 + viewBox="0 0 24 24" 60 + fill="currentColor" 61 + aria-hidden="true" 62 + className="shrink-0" 63 + > 64 + <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" /> 65 + </svg> 66 + } 67 + label="docs" 68 + /> 69 + <HeaderLink 70 href="https://www.npmjs.com/package/prototypey" 71 + icon={ 72 + <svg 73 + width="20" 74 + height="20" 75 + viewBox="0 0 24 24" 76 + fill="none" 77 + stroke="currentColor" 78 + strokeWidth="2" 79 + strokeLinecap="round" 80 + strokeLinejoin="round" 81 + aria-hidden="true" 82 + className="shrink-0" 83 + > 84 + <path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" /> 85 + <polyline points="3.27 6.96 12 12.01 20.73 6.96" /> 86 + <line x1="12" y1="22.08" x2="12" y2="12" /> 87 + </svg> 88 + } 89 + label="npm" 90 + /> 91 + <HeaderLink 92 + href="https://notes.tylur.dev/3m5a3do4eus2w" 93 + icon={ 94 + <svg 95 + width="20" 96 + height="20" 97 + viewBox="0 0 24 24" 98 + fill="none" 99 + stroke="currentColor" 100 + strokeWidth="2" 101 + strokeLinecap="round" 102 + strokeLinejoin="round" 103 + aria-hidden="true" 104 + className="shrink-0" 105 + > 106 + <path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20" /> 107 + <path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z" /> 108 + </svg> 109 + } 110 + label="story" 111 + /> 112 </div> 113 <button 114 onClick={handleShare} 115 + className="text-gray-900 dark:text-gray-100 text-sm font-semibold flex items-center gap-2 transition-all duration-200 bg-transparent border border-gray-200 dark:border-gray-700 rounded-lg cursor-pointer py-2 px-4 shadow-none outline-none shrink-0 whitespace-nowrap hover:bg-gray-50 dark:hover:bg-gray-800 hover:border-gray-900 dark:hover:border-gray-100" 116 aria-label="share playground" 117 > 118 {copied ? ( ··· 127 strokeLinecap="round" 128 strokeLinejoin="round" 129 aria-hidden="true" 130 + className="shrink-0" 131 > 132 <polyline points="20 6 9 17 4 12" /> 133 </svg> ··· 145 strokeLinecap="round" 146 strokeLinejoin="round" 147 aria-hidden="true" 148 + className="shrink-0" 149 > 150 <circle cx="18" cy="5" r="3" /> 151 <circle cx="6" cy="12" r="3" />
+4 -21
packages/site/src/components/OutputPanel.tsx
··· 27 }, []); 28 29 return ( 30 - <div style={{ flex: 1, display: "flex", flexDirection: "column" }}> 31 - <div 32 - style={{ 33 - padding: "0.75rem 1rem", 34 - backgroundColor: "var(--color-bg-secondary)", 35 - borderBottom: "1px solid var(--color-border)", 36 - fontSize: "0.875rem", 37 - fontWeight: "600", 38 - color: "var(--color-text-secondary)", 39 - }} 40 - > 41 Output 42 </div> 43 - <div style={{ flex: 1 }}> 44 {output.error ? ( 45 - <div 46 - style={{ 47 - padding: "1rem", 48 - color: "var(--color-error)", 49 - backgroundColor: "var(--color-error-bg)", 50 - height: "100%", 51 - overflow: "auto", 52 - }} 53 - > 54 <strong>Error:</strong> {output.error} 55 </div> 56 ) : (
··· 27 }, []); 28 29 return ( 30 + <div className="flex-1 flex flex-col"> 31 + <div className="py-3 px-4 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 text-sm font-semibold text-gray-500 dark:text-gray-400"> 32 Output 33 </div> 34 + <div className="flex-1"> 35 {output.error ? ( 36 + <div className="p-4 text-red-600 dark:text-red-400 bg-red-50 dark:bg-red-950 h-full overflow-auto"> 37 <strong>Error:</strong> {output.error} 38 </div> 39 ) : (
+12 -80
packages/site/src/components/Playground.tsx
··· 112 return ( 113 <> 114 {/* Desktop playground */} 115 - <div 116 - className="desktop-only" 117 - style={{ 118 - flex: 1, 119 - overflow: "hidden", 120 - }} 121 - > 122 - <div 123 - style={{ 124 - flex: 1, 125 - display: "flex", 126 - borderRight: "1px solid var(--color-border)", 127 - }} 128 - > 129 <Editor 130 value={code} 131 onChange={handleCodeChange} 132 onReady={handleEditorReady} 133 /> 134 </div> 135 - <div style={{ flex: 1, display: "flex" }}> 136 <OutputPanel output={output} /> 137 </div> 138 </div> 139 140 {/* Mobile static demo */} 141 - <div 142 - className="mobile-only" 143 - style={{ 144 - flex: 1, 145 - flexDirection: "column", 146 - overflow: "auto", 147 - padding: "1rem", 148 - }} 149 - > 150 - <div 151 - style={{ 152 - backgroundColor: "var(--color-bg-secondary)", 153 - padding: "1rem", 154 - borderRadius: "0.5rem", 155 - marginBottom: "1rem", 156 - textAlign: "center", 157 - color: "var(--color-text-secondary)", 158 - fontSize: "0.875rem", 159 - }} 160 - > 161 Playground available on desktop 162 </div> 163 ··· 190 const jsonWrappedLines = estimateWrappedLines(json, 50); 191 192 return ( 193 - <div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}> 194 {/* You write section */} 195 - <div style={{ display: "flex", flexDirection: "column" }}> 196 - <div 197 - style={{ 198 - padding: "0.75rem 1rem", 199 - backgroundColor: "var(--color-bg-secondary)", 200 - borderBottom: "1px solid var(--color-border)", 201 - fontSize: "0.875rem", 202 - fontWeight: "600", 203 - color: "var(--color-text-secondary)", 204 - borderTopLeftRadius: "0.5rem", 205 - borderTopRightRadius: "0.5rem", 206 - }} 207 - > 208 You write 209 </div> 210 - <div 211 - style={{ 212 - border: "1px solid var(--color-border)", 213 - borderTop: "none", 214 - borderBottomLeftRadius: "0.5rem", 215 - borderBottomRightRadius: "0.5rem", 216 - overflow: "hidden", 217 - }} 218 - > 219 <MonacoEditor 220 height={`${String(codeWrappedLines * 18 + 32)}px`} 221 defaultLanguage="typescript" ··· 244 </div> 245 246 {/* JSON generated section */} 247 - <div style={{ display: "flex", flexDirection: "column" }}> 248 - <div 249 - style={{ 250 - padding: "0.75rem 1rem", 251 - backgroundColor: "var(--color-bg-secondary)", 252 - borderBottom: "1px solid var(--color-border)", 253 - fontSize: "0.875rem", 254 - fontWeight: "600", 255 - color: "var(--color-text-secondary)", 256 - borderTopLeftRadius: "0.5rem", 257 - borderTopRightRadius: "0.5rem", 258 - }} 259 - > 260 JSON generated 261 </div> 262 - <div 263 - style={{ 264 - border: "1px solid var(--color-border)", 265 - borderTop: "none", 266 - borderBottomLeftRadius: "0.5rem", 267 - borderBottomRightRadius: "0.5rem", 268 - overflow: "hidden", 269 - }} 270 - > 271 <MonacoEditor 272 height={`${String(jsonWrappedLines * 18 + 32)}px`} 273 defaultLanguage="json"
··· 112 return ( 113 <> 114 {/* Desktop playground */} 115 + <div className="hidden md:flex flex-1 overflow-hidden"> 116 + <div className="flex-1 flex border-r border-gray-200 dark:border-gray-700"> 117 <Editor 118 value={code} 119 onChange={handleCodeChange} 120 onReady={handleEditorReady} 121 /> 122 </div> 123 + <div className="flex-1 flex"> 124 <OutputPanel output={output} /> 125 </div> 126 </div> 127 128 {/* Mobile static demo */} 129 + <div className="flex md:hidden flex-1 flex-col overflow-auto p-4"> 130 + <div className="bg-gray-50 dark:bg-gray-800 p-4 rounded-lg mb-4 text-center text-gray-500 dark:text-gray-400 text-sm"> 131 Playground available on desktop 132 </div> 133 ··· 160 const jsonWrappedLines = estimateWrappedLines(json, 50); 161 162 return ( 163 + <div className="flex flex-col gap-4"> 164 {/* You write section */} 165 + <div className="flex flex-col"> 166 + <div className="py-3 px-4 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 text-sm font-semibold text-gray-500 dark:text-gray-400 rounded-t-lg"> 167 You write 168 </div> 169 + <div className="border border-gray-200 dark:border-gray-700 border-t-0 rounded-b-lg overflow-hidden"> 170 <MonacoEditor 171 height={`${String(codeWrappedLines * 18 + 32)}px`} 172 defaultLanguage="typescript" ··· 195 </div> 196 197 {/* JSON generated section */} 198 + <div className="flex flex-col"> 199 + <div className="py-3 px-4 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 text-sm font-semibold text-gray-500 dark:text-gray-400 rounded-t-lg"> 200 JSON generated 201 </div> 202 + <div className="border border-gray-200 dark:border-gray-700 border-t-0 rounded-b-lg overflow-hidden"> 203 <MonacoEditor 204 height={`${String(jsonWrappedLines * 18 + 32)}px`} 205 defaultLanguage="json"
+2 -129
packages/site/src/index.css
··· 1 - /* 2 - Josh's Custom CSS Reset 3 - https://www.joshwcomeau.com/css/custom-css-reset/ 4 - */ 5 - *, 6 - *::before, 7 - *::after { 8 - box-sizing: border-box; 9 - } 10 - 11 - * { 12 - margin: 0; 13 - } 14 - 15 - body { 16 - line-height: 1.5; 17 - -webkit-font-smoothing: antialiased; 18 - } 19 - 20 - img, 21 - picture, 22 - video, 23 - canvas, 24 - svg { 25 - display: block; 26 - max-width: 100%; 27 - } 28 - 29 - input, 30 - button, 31 - textarea, 32 - select { 33 - font: inherit; 34 - } 35 - 36 - p, 37 - h1, 38 - h2, 39 - h3, 40 - h4, 41 - h5, 42 - h6 { 43 - overflow-wrap: break-word; 44 - } 45 - 46 - #root { 47 - isolation: isolate; 48 - } 49 50 :root { 51 font-family: 52 -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", 53 "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; 54 - line-height: 1.5; 55 - font-weight: 400; 56 font-synthesis: none; 57 text-rendering: optimizeLegibility; 58 -webkit-font-smoothing: antialiased; 59 -moz-osx-font-smoothing: grayscale; 60 - 61 - --color-text: #213547; 62 - --color-text-secondary: #6b7280; 63 - --color-text-heading: #111827; 64 - --color-bg: #ffffff; 65 - --color-bg-secondary: #f9fafb; 66 - --color-border: #e5e7eb; 67 - --color-error: #dc2626; 68 - --color-error-bg: #fef2f2; 69 - 70 - color: var(--color-text); 71 - background-color: var(--color-bg); 72 - } 73 - 74 - @media (prefers-color-scheme: dark) { 75 - :root { 76 - --color-text: #e5e7eb; 77 - --color-text-secondary: #9ca3af; 78 - --color-text-heading: #f9fafb; 79 - --color-bg: #111827; 80 - --color-bg-secondary: #1f2937; 81 - --color-border: #374151; 82 - --color-error: #f87171; 83 - --color-error-bg: #3f1f1f; 84 - } 85 } 86 87 body { 88 - display: flex; 89 min-width: 320px; 90 min-height: 100vh; 91 } 92 93 #root { 94 - width: 100%; 95 - display: flex; 96 - flex-direction: column; 97 - } 98 - 99 - /* Desktop layout - default */ 100 - .header-content { 101 - display: flex; 102 - justify-content: space-between; 103 - align-items: flex-start; 104 - } 105 - 106 - .header-links { 107 - display: flex; 108 - gap: 1.25rem; 109 - padding-top: 0.5rem; 110 - } 111 - 112 - .mobile-only { 113 - display: none; 114 - } 115 - 116 - .desktop-only { 117 - display: flex; 118 - } 119 - 120 - /* Mobile layout */ 121 - @media (max-width: 768px) { 122 - .header-content { 123 - flex-direction: column; 124 - gap: 1rem; 125 - } 126 - 127 - .header-links { 128 - padding-top: 0; 129 - width: 100%; 130 - justify-content: flex-start; 131 - } 132 - 133 - .mobile-only { 134 - display: flex !important; 135 - flex-direction: column; 136 - } 137 - 138 - .desktop-only { 139 - display: none !important; 140 - } 141 - 142 - .link-container { 143 - width: 100%; 144 - flex-direction: row !important; 145 - justify-content: space-between; 146 - } 147 }
··· 1 + @import "tailwindcss"; 2 3 :root { 4 font-family: 5 -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", 6 "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; 7 font-synthesis: none; 8 text-rendering: optimizeLegibility; 9 -webkit-font-smoothing: antialiased; 10 -moz-osx-font-smoothing: grayscale; 11 } 12 13 body { 14 min-width: 320px; 15 min-height: 100vh; 16 } 17 18 #root { 19 + isolation: isolate; 20 }
+2
packages/site/vite.config.ts
··· 1 import { defineConfig } from "vite"; 2 import react from "@vitejs/plugin-react"; 3 4 export default defineConfig({ 5 plugins: [ 6 react({ 7 babel: { 8 plugins: [["babel-plugin-react-compiler", {}]],
··· 1 import { defineConfig } from "vite"; 2 import react from "@vitejs/plugin-react"; 3 + import tailwindcss from "@tailwindcss/vite"; 4 5 export default defineConfig({ 6 plugins: [ 7 + tailwindcss(), 8 react({ 9 babel: { 10 plugins: [["babel-plugin-react-compiler", {}]],
+925 -403
pnpm-lock.yaml
··· 12 .: 13 devDependencies: 14 '@changesets/cli': 15 - specifier: ^2.29.7 16 - version: 2.29.7(@types/node@24.0.4) 17 '@eslint/js': 18 specifier: 9.29.0 19 version: 9.29.0 ··· 21 specifier: 9.29.0 22 version: 9.29.0(jiti@2.6.1) 23 knip: 24 - specifier: ^5.66.2 25 - version: 5.66.2(@types/node@24.0.4)(typescript@5.8.3) 26 prettier: 27 specifier: 3.6.1 28 version: 3.6.1 29 typescript-eslint: 30 specifier: 8.35.0 31 - version: 8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.8.3) 32 33 packages/prototypey: 34 dependencies: 35 '@atproto/lexicon': 36 - specifier: ^0.5.1 37 - version: 0.5.1 38 sade: 39 specifier: ^1.8.1 40 version: 1.8.1 ··· 44 devDependencies: 45 '@ark/attest': 46 specifier: ^0.49.0 47 - version: 0.49.0(typescript@5.8.3) 48 '@types/node': 49 specifier: 24.0.4 50 version: 24.0.4 51 tsdown: 52 - specifier: 0.12.7 53 - version: 0.12.7(oxc-resolver@11.11.1)(typescript@5.8.3) 54 typescript: 55 - specifier: 5.8.3 56 - version: 5.8.3 57 vitest: 58 specifier: ^3.2.4 59 version: 3.2.4(@types/node@24.0.4)(esbuild@0.25.10)(jiti@2.6.1)(jsdom@25.0.1) ··· 61 packages/site: 62 dependencies: 63 '@monaco-editor/react': 64 - specifier: ^4.6.0 65 - version: 4.7.0(monaco-editor@0.52.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) 66 lz-string: 67 specifier: ^1.5.0 68 version: 1.5.0 ··· 70 specifier: 0.52.0 71 version: 0.52.0 72 nuqs: 73 - specifier: ^2.7.2 74 - version: 2.7.2(react@19.2.0) 75 prototypey: 76 specifier: workspace:* 77 version: link:../prototypey 78 react: 79 - specifier: ^19.2.0 80 - version: 19.2.0 81 react-dom: 82 - specifier: ^19.2.0 83 - version: 19.2.0(react@19.2.0) 84 devDependencies: 85 '@testing-library/jest-dom': 86 specifier: ^6.9.1 87 version: 6.9.1 88 '@testing-library/react': 89 - specifier: ^16.1.0 90 - version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) 91 '@testing-library/user-event': 92 - specifier: ^14.5.2 93 version: 14.6.1(@testing-library/dom@10.4.1) 94 '@types/react': 95 - specifier: ^19.2.2 96 - version: 19.2.2 97 '@types/react-dom': 98 - specifier: ^19.2.2 99 - version: 19.2.2(@types/react@19.2.2) 100 '@vitejs/plugin-react': 101 - specifier: ^5.0.4 102 - version: 5.0.4(rolldown-vite@7.0.6(@types/node@24.0.4)(esbuild@0.25.10)(jiti@2.6.1)) 103 babel-plugin-react-compiler: 104 specifier: ^1.0.0 105 version: 1.0.0 ··· 109 jsdom: 110 specifier: ^25.0.1 111 version: 25.0.1 112 typescript: 113 - specifier: 5.8.3 114 - version: 5.8.3 115 vite: 116 specifier: npm:rolldown-vite@7.0.6 117 version: rolldown-vite@7.0.6(@types/node@24.0.4)(esbuild@0.25.10)(jiti@2.6.1) ··· 142 '@asamuzakjp/css-color@3.2.0': 143 resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} 144 145 - '@atproto/common-web@0.4.3': 146 - resolution: {integrity: sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg==} 147 148 - '@atproto/lexicon@0.5.1': 149 - resolution: {integrity: sha512-y8AEtYmfgVl4fqFxqXAeGvhesiGkxiy3CWoJIfsFDDdTlZUC8DFnZrYhcqkIop3OlCkkljvpSJi1hbeC1tbi8A==} 150 151 '@atproto/syntax@0.4.1': 152 resolution: {integrity: sha512-CJdImtLAiFO+0z3BWTtxwk6aY5w4t8orHTMVJgkf++QRJWTxPbIFko/0hrkADB7n2EruDxDSeAgfUGehpH6ngw==} ··· 155 resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} 156 engines: {node: '>=6.9.0'} 157 158 '@babel/compat-data@7.28.4': 159 resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==} 160 engines: {node: '>=6.9.0'} 161 162 '@babel/core@7.28.4': 163 resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} 164 engines: {node: '>=6.9.0'} 165 166 '@babel/generator@7.28.3': 167 resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} 168 engines: {node: '>=6.9.0'} 169 170 '@babel/helper-annotate-as-pure@7.27.3': 171 resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} 172 engines: {node: '>=6.9.0'} ··· 175 resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} 176 engines: {node: '>=6.9.0'} 177 178 '@babel/helper-create-class-features-plugin@7.28.3': 179 resolution: {integrity: sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==} 180 engines: {node: '>=6.9.0'} ··· 193 resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} 194 engines: {node: '>=6.9.0'} 195 196 '@babel/helper-module-transforms@7.28.3': 197 resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} 198 engines: {node: '>=6.9.0'} 199 peerDependencies: 200 '@babel/core': ^7.0.0 201 202 '@babel/helper-optimise-call-expression@7.27.1': 203 resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} 204 engines: {node: '>=6.9.0'} ··· 225 resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} 226 engines: {node: '>=6.9.0'} 227 228 '@babel/helper-validator-option@7.27.1': 229 resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} 230 engines: {node: '>=6.9.0'} ··· 233 resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} 234 engines: {node: '>=6.9.0'} 235 236 '@babel/parser@7.28.4': 237 resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} 238 engines: {node: '>=6.0.0'} 239 hasBin: true 240 241 '@babel/plugin-proposal-private-methods@7.18.6': 242 resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==} 243 engines: {node: '>=6.9.0'} ··· 265 resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} 266 engines: {node: '>=6.9.0'} 267 268 '@babel/traverse@7.28.4': 269 resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} 270 engines: {node: '>=6.9.0'} 271 272 '@babel/types@7.28.4': 273 resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} 274 engines: {node: '>=6.9.0'} 275 276 - '@changesets/apply-release-plan@7.0.13': 277 - resolution: {integrity: sha512-BIW7bofD2yAWoE8H4V40FikC+1nNFEKBisMECccS16W1rt6qqhNTBDmIw5HaqmMgtLNz9e7oiALiEUuKrQ4oHg==} 278 279 '@changesets/assemble-release-plan@6.0.9': 280 resolution: {integrity: sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ==} ··· 282 '@changesets/changelog-git@0.2.1': 283 resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} 284 285 - '@changesets/cli@2.29.7': 286 - resolution: {integrity: sha512-R7RqWoaksyyKXbKXBTbT4REdy22yH81mcFK6sWtqSanxUCbUi9Uf+6aqxZtDQouIqPdem2W56CdxXgsxdq7FLQ==} 287 hasBin: true 288 289 - '@changesets/config@3.1.1': 290 - resolution: {integrity: sha512-bd+3Ap2TKXxljCggI0mKPfzCQKeV/TU4yO2h2C6vAihIo8tzseAn2e7klSuiyYYXvgu53zMN1OeYMIQkaQoWnA==} 291 292 '@changesets/errors@0.2.0': 293 resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} ··· 295 '@changesets/get-dependents-graph@2.1.3': 296 resolution: {integrity: sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==} 297 298 - '@changesets/get-release-plan@4.0.13': 299 - resolution: {integrity: sha512-DWG1pus72FcNeXkM12tx+xtExyH/c9I1z+2aXlObH3i9YA7+WZEVaiHzHl03thpvAgWTRaH64MpfHxozfF7Dvg==} 300 301 '@changesets/get-version-range-type@0.4.0': 302 resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} ··· 307 '@changesets/logger@0.1.1': 308 resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} 309 310 - '@changesets/parse@0.4.1': 311 - resolution: {integrity: sha512-iwksMs5Bf/wUItfcg+OXrEpravm5rEd9Bf4oyIPL4kVTmJQ7PNDSd6MDYkpSJR1pn7tz/k8Zf2DhTCqX08Ou+Q==} 312 313 '@changesets/pre@2.0.2': 314 resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==} 315 316 - '@changesets/read@0.6.5': 317 - resolution: {integrity: sha512-UPzNGhsSjHD3Veb0xO/MwvasGe8eMyNrR/sT9gR8Q3DhOQZirgKhhXv/8hVsI0QpPjR004Z9iFxoJU6in3uGMg==} 318 319 '@changesets/should-skip-package@0.1.2': 320 resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==} ··· 359 '@emnapi/core@1.5.0': 360 resolution: {integrity: sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==} 361 362 '@emnapi/runtime@1.5.0': 363 resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} 364 365 '@emnapi/wasi-threads@1.1.0': 366 resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} ··· 626 '@napi-rs/wasm-runtime@1.0.7': 627 resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} 628 629 '@nodelib/fs.scandir@2.1.5': 630 resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 631 engines: {node: '>= 8'} ··· 638 resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 639 engines: {node: '>= 8'} 640 641 - '@oxc-project/runtime@0.72.2': 642 - resolution: {integrity: sha512-J2lsPDen2mFs3cOA1gIBd0wsHEhum2vTnuKIRwmj3HJJcIz/XgeNdzvgSOioIXOJgURIpcDaK05jwaDG1rhDwg==} 643 - engines: {node: '>=6.9.0'} 644 - 645 '@oxc-project/runtime@0.75.0': 646 resolution: {integrity: sha512-gzRmVI/vorsPmbDXt7GD4Uh2lD3rCOku/1xWPB4Yx48k0EP4TZmzQudWapjN4+7Vv+rgXr0RqCHQadeaMvdBuw==} 647 engines: {node: '>=6.9.0'} ··· 650 resolution: {integrity: sha512-UH07DRi7xXqAsJ/sFbJJg0liIXnapB6P5uADXIiF1s6WQjZzcTIkKHca0s522QVxmijPxVX5ijCYxSr7eSq5CQ==} 651 engines: {node: '>=6.9.0'} 652 653 - '@oxc-project/types@0.72.2': 654 - resolution: {integrity: sha512-il5RF8AP85XC0CMjHF4cnVT9nT/v/ocm6qlZQpSiAR9qBbQMGkFKloBZwm7PcnOdiUX97yHgsKM7uDCCWCu3tg==} 655 - 656 '@oxc-project/types@0.75.1': 657 resolution: {integrity: sha512-7ZJy+51qWpZRvynaQUezeYfjCtaSdiXIWFUZIlOuTSfDXpXqnSl/m1IUPLx6XrOy6s0SFv3CLE14vcZy63bz7g==} 658 659 - '@oxc-resolver/binding-android-arm-eabi@11.11.1': 660 - resolution: {integrity: sha512-v5rtczLD5d8lasBdP6GXoM7VQ1Av9pZyWGXF5afQawRZcWTVvncrIzu9nhKpvIhhmC4C6MYdXA3CNZc60LvUig==} 661 cpu: [arm] 662 os: [android] 663 664 - '@oxc-resolver/binding-android-arm64@11.11.1': 665 - resolution: {integrity: sha512-0QqKsM8/XRNDGZy1/rxys53U/aCLVBKV2jgGFr3msypTZtNH/d4qao7QULM++H4hcaXghfXaB4xVCPDg3tHCvQ==} 666 cpu: [arm64] 667 os: [android] 668 669 - '@oxc-resolver/binding-darwin-arm64@11.11.1': 670 - resolution: {integrity: sha512-xdHj8Mn3WB+dTGWcMzC07pZiSvQfKxcCEIlmDGrwwLmS4MgyJcraDDykFg4NXwd8dJNKOLqEfV3RMLRYgE2f8w==} 671 cpu: [arm64] 672 os: [darwin] 673 674 - '@oxc-resolver/binding-darwin-x64@11.11.1': 675 - resolution: {integrity: sha512-t9ImHoJXhFimPx3u0UMbQzADUBq/xnQY1eGc3bfOu5l4h0k/3rlsO16Fe8dheG8Iuvc3j2lh8H8dFg/Los4WeQ==} 676 cpu: [x64] 677 os: [darwin] 678 679 - '@oxc-resolver/binding-freebsd-x64@11.11.1': 680 - resolution: {integrity: sha512-aK7b1Yr2VkC2efK0w63v7gZkCqYmhR4XTCCYgA5KbtpJVg1OwFXVRjO1vfWNn5osk9dNpaIdeo3C1IfWPhW68w==} 681 cpu: [x64] 682 os: [freebsd] 683 684 - '@oxc-resolver/binding-linux-arm-gnueabihf@11.11.1': 685 - resolution: {integrity: sha512-9wHEYo+1VLoCjX4iI29ZR2ExdcGbG8JlmSR0aRW/A/NuzKxFB+bfiPkwUrvdSv7syXS8CrixvLdqAkBoXgk/rQ==} 686 cpu: [arm] 687 os: [linux] 688 689 - '@oxc-resolver/binding-linux-arm-musleabihf@11.11.1': 690 - resolution: {integrity: sha512-Mf8wZJEeGAQ1WAwp6nvtxucYAQDDtj9Qhv1BaQS8SbeR3na203RUFdEm6F5ptWzF8cuY+ye+FsGKr8lKG3pvWg==} 691 cpu: [arm] 692 os: [linux] 693 694 - '@oxc-resolver/binding-linux-arm64-gnu@11.11.1': 695 - resolution: {integrity: sha512-Fk8BrFBfKzUveCEAXZ6kDhyc5RLWIWOI0+UZGp1G3WQIFo9HDEqnYtsOtUbzLtjifbyMhtaTteElRSGNKTJ3nA==} 696 cpu: [arm64] 697 os: [linux] 698 699 - '@oxc-resolver/binding-linux-arm64-musl@11.11.1': 700 - resolution: {integrity: sha512-jbsO1/VTDRb5FAvWnxEIFOnFHA7dALBn5HPdxdoAbnuvjgjIPYMVvTFEBPNLz3BSFxWdounZasZDYYFhBhFzmQ==} 701 cpu: [arm64] 702 os: [linux] 703 704 - '@oxc-resolver/binding-linux-ppc64-gnu@11.11.1': 705 - resolution: {integrity: sha512-+aY2AjUQkByiOtKUU0RyqB7VV7HIh3SMBh54/9nzUbHN5RiF0As5DApV/IwbQjB2oKc0VywQZzE+/Wj/Ijvd/Q==} 706 cpu: [ppc64] 707 os: [linux] 708 709 - '@oxc-resolver/binding-linux-riscv64-gnu@11.11.1': 710 - resolution: {integrity: sha512-HqBogCmIl344en3EAhC9vSm/h52fb5BA0eFxsgsH9HgwYY6qH4th4msBqBAiMRCKcC6hVwjh0fmzHgST2rx4Cw==} 711 cpu: [riscv64] 712 os: [linux] 713 714 - '@oxc-resolver/binding-linux-riscv64-musl@11.11.1': 715 - resolution: {integrity: sha512-9dXyIMQMrh76WyMtNDJhsRYqc6KDsQe3/ja9fAPBk28p7kltEvZvHpivq1Xa8Ca/JCa8QgTROgLInChNEF27bQ==} 716 cpu: [riscv64] 717 os: [linux] 718 719 - '@oxc-resolver/binding-linux-s390x-gnu@11.11.1': 720 - resolution: {integrity: sha512-Ybp/bSJmnl0sr8zh+nIz0cpU077tDZDYRYDhZiWN+f7rcWF7D8Z/pKD9zPxRocvJieZGfzrIwmHiHf9eY47P9w==} 721 cpu: [s390x] 722 os: [linux] 723 724 - '@oxc-resolver/binding-linux-x64-gnu@11.11.1': 725 - resolution: {integrity: sha512-uVWj/UI6+l5/CeV2d4XpjycJNDkk/JfxNzQLAFCsVl5ZbrIfWQ9TzEzAi7xsDgVZYLBuL7iSowQ7YYRp1LQZlA==} 726 cpu: [x64] 727 os: [linux] 728 729 - '@oxc-resolver/binding-linux-x64-musl@11.11.1': 730 - resolution: {integrity: sha512-Q9kQmiZn4bNnCOqPHvdF4bHdKXBa7Ow6yfeKTWPNOHyoZXdyxIu5C+3jSjo+SJiFNhmnh0hEAN8om6GEuJEYCA==} 731 cpu: [x64] 732 os: [linux] 733 734 - '@oxc-resolver/binding-wasm32-wasi@11.11.1': 735 - resolution: {integrity: sha512-skGIwjoRwEh2qFIaG/wwa74i5KcoWNTEy1ycB6qdRV+OOSlkosVFIzXPYzjcuwtIL1M6pC7+M+56TgQQEzOUmw==} 736 engines: {node: '>=14.0.0'} 737 cpu: [wasm32] 738 739 - '@oxc-resolver/binding-win32-arm64-msvc@11.11.1': 740 - resolution: {integrity: sha512-5R2GVH44JXGoI+gVlR4+O3ql6KZICQlCmIB0ZbpiYbEHNxaB47v3aSMVxcCuwhYKndJUJZwRXnYzoCfMEu4o0g==} 741 cpu: [arm64] 742 os: [win32] 743 744 - '@oxc-resolver/binding-win32-ia32-msvc@11.11.1': 745 - resolution: {integrity: sha512-iB/ljDyPJCMIO7WPx2bj8fRCB1TxmHSv/t3oyUwOiz79Q0A33QbwZWhdx+ZXdazGPer71mYZfr3eb0hAnmlgrg==} 746 cpu: [ia32] 747 os: [win32] 748 749 - '@oxc-resolver/binding-win32-x64-msvc@11.11.1': 750 - resolution: {integrity: sha512-OtUpzpStS5bgVGXV7eaBr7Spot9lXu/wyd0yWEyoG2tyzm/bwdRKCwJQzxWIhlecRxMDGA+qlLRRicTNOejkSQ==} 751 cpu: [x64] 752 os: [win32] 753 ··· 759 '@quansync/fs@0.1.5': 760 resolution: {integrity: sha512-lNS9hL2aS2NZgNW7BBj+6EBl4rOf8l+tQ0eRY6JWCI8jI2kc53gSoqbjojU0OnAWhzoXiOjFyGsHcDGePB3lhA==} 761 762 - '@rolldown/binding-darwin-arm64@1.0.0-beta.11-commit.f051675': 763 - resolution: {integrity: sha512-Hlt/h+lOJ+ksC2wED2M9Hku/9CA2Hr17ENK82gNMmi3OqwcZLdZFqJDpASTli65wIOeT4p9rIUMdkfshCoJpYA==} 764 cpu: [arm64] 765 - os: [darwin] 766 767 '@rolldown/binding-darwin-arm64@1.0.0-beta.24': 768 resolution: {integrity: sha512-gE4HGjIioZaMGZupq2zQQdqhlRV2b2qnjFHHkJEW50zVDmiVNWwdHjwvZDPx9JfW5y4GuHgp/zKDLZZbJlQ1/Q==} 769 cpu: [arm64] 770 os: [darwin] 771 772 - '@rolldown/binding-darwin-x64@1.0.0-beta.11-commit.f051675': 773 - resolution: {integrity: sha512-Bnst+HBwhW2YrNybEiNf9TJkI1myDgXmiPBVIOS0apzrLCmByzei6PilTClOpTpNFYB+UviL3Ox2gKUmcgUjGw==} 774 - cpu: [x64] 775 os: [darwin] 776 777 '@rolldown/binding-darwin-x64@1.0.0-beta.24': ··· 779 cpu: [x64] 780 os: [darwin] 781 782 - '@rolldown/binding-freebsd-x64@1.0.0-beta.11-commit.f051675': 783 - resolution: {integrity: sha512-3jAxVmYDPc8vMZZOfZI1aokGB9cP6VNeU9XNCx0UJ6ShlSPK3qkAa0sWgueMhaQkgBVf8MOfGpjo47ohGd7QrA==} 784 cpu: [x64] 785 - os: [freebsd] 786 787 '@rolldown/binding-freebsd-x64@1.0.0-beta.24': 788 resolution: {integrity: sha512-lx3Q2TU2bbY4yDCZ6e+Wiom3VMLFlZmQswx/1CyjFd+Vv3Q+99SZm6CSfNAIZBaWD246yQRRr1Vx+iIoWCdYzQ==} 789 cpu: [x64] 790 os: [freebsd] 791 792 - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.11-commit.f051675': 793 - resolution: {integrity: sha512-TpUltUdvcsAf2WvXXD8AVc3BozvhgazJ2gJLXp4DVV2V82m26QelI373Bzx8d/4hB167EEIg4wWW/7GXB/ltoQ==} 794 - cpu: [arm] 795 - os: [linux] 796 797 '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.24': 798 resolution: {integrity: sha512-PLtsV6uf3uS1/cNF8Wu/kitTpXT2YpOZbN6VJm7oMi5A8o5oO0vh8STCB71O5k2kwQMVycsmxHWFk2ZyEa6aMw==} 799 cpu: [arm] 800 os: [linux] 801 802 - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.11-commit.f051675': 803 - resolution: {integrity: sha512-eGvHnYQSdbdhsTdjdp/+83LrN81/7X9HD6y3jg7mEmdsicxEMEIt6CsP7tvYS/jn4489jgO/6mLxW/7Vg+B8pw==} 804 - cpu: [arm64] 805 os: [linux] 806 807 '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.24': 808 resolution: {integrity: sha512-UxGukDkWnv7uS5R+BPVeJ4FSuwA+lgC62LRsyPPSJhJhKMNGZ2W9sQPIpEtBRlww8t0qR6QBsiD5TGLW98ktGw==} 809 cpu: [arm64] 810 os: [linux] 811 812 - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.11-commit.f051675': 813 - resolution: {integrity: sha512-0NJZWXJls83FpBRzkTbGBsXXstaQLsfodnyeOghxbnNdsjn+B4dcNPpMK5V3QDsjC0pNjDLaDdzB2jWKlZbP/Q==} 814 cpu: [arm64] 815 os: [linux] 816 817 '@rolldown/binding-linux-arm64-musl@1.0.0-beta.24': 818 resolution: {integrity: sha512-vB99yGYW9FOQe4lk3MNKa13+vRj+7waZFlRE3Ba/IpEy7RFxZ78ASkPLXkz4+kYYbUvMnRaOfk9RKX2fqYZRUg==} 819 cpu: [arm64] 820 os: [linux] 821 822 - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.11-commit.f051675': 823 - resolution: {integrity: sha512-9vXnu27r4zgS/BHP6RCLBOrJoV2xxtLYHT68IVpSOdCkBHGpf1oOJt6blv1y5NRRJBEfAFCvj5NmwSMhETF96w==} 824 - cpu: [x64] 825 os: [linux] 826 827 '@rolldown/binding-linux-x64-gnu@1.0.0-beta.24': 828 resolution: {integrity: sha512-fAMZBWutuKWHsyvHVsKjFYRXVgTbzBfNmomzPPpog8UtdkHk5Vnb0qVEeZP4hR4TsXnKfzD2EQ98NRqFej5QYA==} 829 cpu: [x64] 830 os: [linux] 831 832 - '@rolldown/binding-linux-x64-musl@1.0.0-beta.11-commit.f051675': 833 - resolution: {integrity: sha512-e6tvsZbtHt4kzl82oCajOUxwIN8uMfjhuQ0qxIVRzPekRRjKEzyH9agYPW6toN0cnHpkhPsu51tyZKJOdUl7jg==} 834 cpu: [x64] 835 os: [linux] 836 837 '@rolldown/binding-linux-x64-musl@1.0.0-beta.24': 838 resolution: {integrity: sha512-0UY/Qo8fAlpolcWOg2ZU7SCUrsCJWifdRMliV9GXlZaBKbMoVNFw0pHGDm9cj/3TWhJu/iB0peZK00dm22LlNw==} 839 cpu: [x64] 840 os: [linux] 841 842 - '@rolldown/binding-wasm32-wasi@1.0.0-beta.11-commit.f051675': 843 - resolution: {integrity: sha512-nBQVizPoUQiViANhWrOyihXNf2booP2iq3S396bI1tmHftdgUXWKa6yAoleJBgP0oF0idXpTPU82ciaROUcjpg==} 844 - engines: {node: '>=14.21.3'} 845 - cpu: [wasm32] 846 847 '@rolldown/binding-wasm32-wasi@1.0.0-beta.24': 848 resolution: {integrity: sha512-7ubbtKCo6FBuAM4q6LoT5dOea7f/zj9OYXgumbwSmA0fw18mN5h8SrFTUjl7h9MpPkOyhi2uY6ss4pb39KXkcw==} 849 engines: {node: '>=14.21.3'} 850 cpu: [wasm32] 851 852 - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.11-commit.f051675': 853 - resolution: {integrity: sha512-Rey/ECXKI/UEykrKfJX3oVAPXDH2k1p2BKzYGza0z3S2X5I3sTDOeBn2I0IQgyyf7U3+DCBhYjkDFnmSePrU/A==} 854 - cpu: [arm64] 855 - os: [win32] 856 857 '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.24': 858 resolution: {integrity: sha512-S5WKIabtRBJyzu31KnJRlbZRR6FMrTMzYRrNTnIY2hWWXfpcB1PNuHqbo+98ODLpH8knul4Vyf5sCL61okLTjA==} 859 cpu: [arm64] 860 os: [win32] 861 862 - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.11-commit.f051675': 863 - resolution: {integrity: sha512-LtuMKJe6iFH4iV55dy+gDwZ9v23Tfxx5cd7ZAxvhYFGoVNSvarxAgl844BvFGReERCnLTGRvo85FUR6fDHQX+A==} 864 - cpu: [ia32] 865 os: [win32] 866 867 '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.24': ··· 869 cpu: [ia32] 870 os: [win32] 871 872 - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.11-commit.f051675': 873 - resolution: {integrity: sha512-YY8UYfBm4dbWa4psgEPPD9T9X0nAvlYu0BOsQC5vDfCwzzU7IHT4jAfetvlQq+4+M6qWHSTr6v+/WX5EmlM1WA==} 874 - cpu: [x64] 875 os: [win32] 876 877 '@rolldown/binding-win32-x64-msvc@1.0.0-beta.24': ··· 879 cpu: [x64] 880 os: [win32] 881 882 - '@rolldown/pluginutils@1.0.0-beta.11-commit.f051675': 883 - resolution: {integrity: sha512-TAqMYehvpauLKz7v4TZOTUQNjxa5bUQWw2+51/+Zk3ItclBxgoSWhnZ31sXjdoX6le6OXdK2vZfV3KoyW/O/GA==} 884 885 '@rolldown/pluginutils@1.0.0-beta.24': 886 resolution: {integrity: sha512-NMiim/enJlffMP16IanVj1ajFNEg8SaMEYyxyYfJoEyt5EiFT3HUH/T2GRdeStNWp+/kg5U8DiJqnQBgLQ8uCw==} 887 888 - '@rolldown/pluginutils@1.0.0-beta.38': 889 - resolution: {integrity: sha512-N/ICGKleNhA5nc9XXQG/kkKHJ7S55u0x0XUJbbkmdCnFuoRkM1Il12q9q0eX19+M7KKUEPw/daUPIRnxhcxAIw==} 890 891 '@standard-schema/spec@1.0.0': 892 resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} 893 894 '@testing-library/dom@10.4.1': 895 resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} 896 engines: {node: '>=18'} ··· 899 resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} 900 engines: {node: '>=14', npm: '>=6', yarn: '>=1'} 901 902 - '@testing-library/react@16.3.0': 903 - resolution: {integrity: sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==} 904 engines: {node: '>=18'} 905 peerDependencies: 906 '@testing-library/dom': ^10.0.0 ··· 956 '@types/node@24.0.4': 957 resolution: {integrity: sha512-ulyqAkrhnuNq9pB76DRBTkcS6YsmDALy6Ua63V8OhrOBgbcYt6IOdzpw5P1+dyRIyMerzLkeYWBeOXPpA9GMAA==} 958 959 - '@types/react-dom@19.2.2': 960 - resolution: {integrity: sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==} 961 peerDependencies: 962 '@types/react': ^19.2.0 963 964 - '@types/react@19.2.2': 965 - resolution: {integrity: sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==} 966 967 '@typescript-eslint/eslint-plugin@8.35.0': 968 resolution: {integrity: sha512-ijItUYaiWuce0N1SoSMrEd0b6b6lYkYt99pqCPfybd+HKVXtEvYhICfLdwp42MhiI5mp0oq7PKEL+g1cNiz/Eg==} ··· 1032 peerDependencies: 1033 typescript: '*' 1034 1035 - '@vitejs/plugin-react@5.0.4': 1036 - resolution: {integrity: sha512-La0KD0vGkVkSk6K+piWDKRUyg8Rl5iAIKRMH0vMJI0Eg47bq1eOxmoObAaQG37WMW9MSyk7Cs8EIWwJC1PtzKA==} 1037 engines: {node: ^20.19.0 || >=22.12.0} 1038 peerDependencies: 1039 vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 ··· 1124 resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} 1125 engines: {node: '>=12'} 1126 1127 - ast-kit@2.1.3: 1128 - resolution: {integrity: sha512-TH+b3Lv6pUjy/Nu0m6A2JULtdzLpmqF9x1Dhj00ZoEiML8qvVA9j1flkzTKNYgdEhWrjDwtWNpyyCUbfQe514g==} 1129 engines: {node: '>=20.19.0'} 1130 1131 asynckit@0.4.0: ··· 1145 resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} 1146 engines: {node: '>=4'} 1147 1148 - birpc@2.6.1: 1149 - resolution: {integrity: sha512-LPnFhlDpdSH6FJhJyn4M0kFO7vtQ5iPw24FnG0y21q09xC7e8+1LeR31S1MAIrDAHp4m7aas4bEkTDTvMAtebQ==} 1150 1151 brace-expansion@1.1.12: 1152 resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} ··· 1232 resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} 1233 engines: {node: '>=18'} 1234 1235 - csstype@3.1.3: 1236 - resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} 1237 1238 data-urls@5.0.0: 1239 resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} ··· 1291 dom-accessibility-api@0.6.3: 1292 resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} 1293 1294 - dts-resolver@2.1.2: 1295 - resolution: {integrity: sha512-xeXHBQkn2ISSXxbJWD828PFjtyg+/UrMDo7W4Ffcs7+YWCquxU8YjV1KoxuiL+eJ5pg3ll+bC6flVv61L3LKZg==} 1296 - engines: {node: '>=20.18.0'} 1297 peerDependencies: 1298 oxc-resolver: '>=11.0.0' 1299 peerDependenciesMeta: ··· 1310 emoji-regex@8.0.0: 1311 resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 1312 1313 - empathic@1.1.0: 1314 - resolution: {integrity: sha512-rsPft6CK3eHtrlp9Y5ALBb+hfK+DWnA4WFebbazxjWyx8vSm3rZeoM3z9irsjcqO3PYRzlfv27XIB4tz2DV7RA==} 1315 engines: {node: '>=14'} 1316 1317 enquirer@2.4.1: 1318 resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} 1319 engines: {node: '>=8.6'} ··· 1513 resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} 1514 engines: {node: '>= 0.4'} 1515 1516 - get-tsconfig@4.12.0: 1517 - resolution: {integrity: sha512-LScr2aNr2FbjAjZh2C6X6BxRx1/x+aTDExct/xyq2XKbYOiG5c0aK7pMsSuyc0brz3ibr/lbQiHD9jzt4lccJw==} 1518 1519 glob-parent@5.1.2: 1520 resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} ··· 1665 resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} 1666 hasBin: true 1667 1668 jsdom@25.0.1: 1669 resolution: {integrity: sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==} 1670 engines: {node: '>=18'} ··· 1708 keyv@4.5.4: 1709 resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} 1710 1711 - knip@5.66.2: 1712 - resolution: {integrity: sha512-5wvsdc17C5bMxjuGfN9KVS/tW5KIvzP1RClfpTMdLYm8IXIsfWsiHlFkTvZIca9skwoVDyTyXmbRq4w1Poim+A==} 1713 engines: {node: '>=18.18.0'} 1714 hasBin: true 1715 peerDependencies: ··· 1755 engines: {node: '>= 12.0.0'} 1756 cpu: [arm64] 1757 os: [linux] 1758 1759 lightningcss-linux-arm64-musl@1.30.2: 1760 resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} 1761 engines: {node: '>= 12.0.0'} 1762 cpu: [arm64] 1763 os: [linux] 1764 1765 lightningcss-linux-x64-gnu@1.30.2: 1766 resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} 1767 engines: {node: '>= 12.0.0'} 1768 cpu: [x64] 1769 os: [linux] 1770 1771 lightningcss-linux-x64-musl@1.30.2: 1772 resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} 1773 engines: {node: '>= 12.0.0'} 1774 cpu: [x64] 1775 os: [linux] 1776 1777 lightningcss-win32-arm64-msvc@1.30.2: 1778 resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} ··· 1819 1820 magic-string@0.30.19: 1821 resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} 1822 1823 make-synchronized@0.4.2: 1824 resolution: {integrity: sha512-EwEJSg8gSGLicKXp/VzNi1tvzhdmNBxOzslkkJSoNUCQFZKH/NIUIp7xlfN+noaHrz4BJDN73gne8IHnjl/F/A==} ··· 1881 node-releases@2.0.25: 1882 resolution: {integrity: sha512-4auku8B/vw5psvTiiN9j1dAOsXvMoGqJuKJcR+dTdqiXEK20mMTk1UEo3HS16LeGQsVG6+qKTPM9u/qQ2LqATA==} 1883 1884 - nuqs@2.7.2: 1885 - resolution: {integrity: sha512-wOPJoz5om7jMJQick9zU1S/Q+joL+B2DZTZxfCleHEcUzjUnPoujGod4+nAmUWb+G9TwZnyv+mfNqlyfEi8Zag==} 1886 peerDependencies: 1887 '@remix-run/react': '>=2' 1888 '@tanstack/react-router': ^1 1889 next: '>=14.2.0' 1890 react: '>=18.2.0 || ^19.0.0-0' 1891 - react-router: ^6 || ^7 1892 - react-router-dom: ^6 || ^7 1893 peerDependenciesMeta: 1894 '@remix-run/react': 1895 optional: true ··· 1905 nwsapi@2.2.22: 1906 resolution: {integrity: sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==} 1907 1908 optionator@0.9.4: 1909 resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} 1910 engines: {node: '>= 0.8.0'} ··· 1912 outdent@0.5.0: 1913 resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} 1914 1915 - oxc-resolver@11.11.1: 1916 - resolution: {integrity: sha512-4Z86u4xQAxl2IC1OAAdHjk/S9GNbE2ewALQVOpBk9F8NkfqXlglY6R2ts+HEgY/Q3T9m/H8W0G4id71muw/Nng==} 1917 1918 p-filter@2.1.0: 1919 resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} ··· 2024 queue-microtask@1.2.3: 2025 resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 2026 2027 - react-dom@19.2.0: 2028 - resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==} 2029 peerDependencies: 2030 - react: ^19.2.0 2031 2032 react-is@17.0.2: 2033 resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} 2034 2035 - react-refresh@0.17.0: 2036 - resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} 2037 engines: {node: '>=0.10.0'} 2038 2039 - react@19.2.0: 2040 - resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} 2041 engines: {node: '>=0.10.0'} 2042 2043 read-yaml-file@1.1.0: ··· 2075 resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} 2076 engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 2077 2078 - rolldown-plugin-dts@0.13.14: 2079 - resolution: {integrity: sha512-wjNhHZz9dlN6PTIXyizB6u/mAg1wEFMW9yw7imEVe3CxHSRnNHVyycIX0yDEOVJfDNISLPbkCIPEpFpizy5+PQ==} 2080 engines: {node: '>=20.18.0'} 2081 peerDependencies: 2082 '@typescript/native-preview': '>=7.0.0-dev.20250601.1' 2083 - rolldown: ^1.0.0-beta.9 2084 typescript: ^5.0.0 2085 - vue-tsc: ^2.2.0 || ^3.0.0 2086 peerDependenciesMeta: 2087 '@typescript/native-preview': 2088 optional: true 2089 typescript: ··· 2131 yaml: 2132 optional: true 2133 2134 - rolldown@1.0.0-beta.11-commit.f051675: 2135 - resolution: {integrity: sha512-g8MCVkvg2GnrrG+j+WplOTx1nAmjSwYOMSOQI0qfxf8D4NmYZqJuG3f85yWK64XXQv6pKcXZsfMkOPs9B6B52A==} 2136 - hasBin: true 2137 - 2138 rolldown@1.0.0-beta.24: 2139 resolution: {integrity: sha512-eDyipoOnoHQ5p6INkJ8g31eKGlqPSCAN9PapyOTw5HET4FYIWALZnSgpMZ67mdn+xT3jAsqGidNnBcIM6EAUhA==} 2140 hasBin: true 2141 2142 rrweb-cssom@0.7.1: 2143 resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==} 2144 ··· 2193 resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} 2194 engines: {node: '>=8'} 2195 2196 - smol-toml@1.4.2: 2197 - resolution: {integrity: sha512-rInDH6lCNiEyn3+hH8KVGFdbjc099j47+OSgbMrfDYX1CmXLfdKd7qi6IfcWj2wFxvSVkuI46M+wPGYfEOEj6g==} 2198 engines: {node: '>= 18'} 2199 2200 source-map-js@1.2.1: ··· 2242 resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} 2243 engines: {node: '>=8'} 2244 2245 - strip-json-comments@5.0.2: 2246 - resolution: {integrity: sha512-4X2FR3UwhNUE9G49aIsJW5hRRR3GXGTBTZRMfv568O60ojM8HcWjV/VxAxCDW3SUND33O6ZY66ZuRcdkj73q2g==} 2247 engines: {node: '>=14.16'} 2248 2249 strip-literal@3.1.0: ··· 2256 symbol-tree@3.2.4: 2257 resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} 2258 2259 term-size@2.2.1: 2260 resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} 2261 engines: {node: '>=8'} ··· 2269 tinyexec@0.3.2: 2270 resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} 2271 2272 - tinyexec@1.0.1: 2273 - resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} 2274 2275 tinyglobby@0.2.15: 2276 resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} ··· 2307 resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} 2308 engines: {node: '>=18'} 2309 2310 treeify@1.1.0: 2311 resolution: {integrity: sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==} 2312 engines: {node: '>=0.6'} ··· 2317 peerDependencies: 2318 typescript: '>=4.8.4' 2319 2320 - tsdown@0.12.7: 2321 - resolution: {integrity: sha512-VJjVaqJfIQuQwtOoeuEJMOJUf3MPDrfX0X7OUNx3nq5pQeuIl3h58tmdbM1IZcu8Dn2j8NQjLh+5TXa0yPb9zg==} 2322 - engines: {node: '>=18.0.0'} 2323 hasBin: true 2324 peerDependencies: 2325 '@arethetypeswrong/core': ^0.18.1 ··· 2327 typescript: ^5.0.0 2328 unplugin-lightningcss: ^0.4.0 2329 unplugin-unused: ^0.5.0 2330 peerDependenciesMeta: 2331 '@arethetypeswrong/core': 2332 optional: true ··· 2337 unplugin-lightningcss: 2338 optional: true 2339 unplugin-unused: 2340 optional: true 2341 2342 tslib@2.8.1: ··· 2353 eslint: ^8.57.0 || ^9.0.0 2354 typescript: '>=4.8.4 <5.9.0' 2355 2356 - typescript@5.8.3: 2357 - resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} 2358 engines: {node: '>=14.17'} 2359 hasBin: true 2360 2361 uint8arrays@3.0.0: 2362 resolution: {integrity: sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==} 2363 2364 - unconfig@7.3.3: 2365 - resolution: {integrity: sha512-QCkQoOnJF8L107gxfHL0uavn7WD9b3dpBcFX6HtfQYmjw2YzWxGuFQ0N0J6tE9oguCBJn9KOvfqYDCMPHIZrBA==} 2366 2367 undici-types@7.8.0: 2368 resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} 2369 2370 universalify@0.1.2: 2371 resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} ··· 2512 2513 '@adobe/css-tools@4.4.4': {} 2514 2515 - '@ark/attest@0.49.0(typescript@5.8.3)': 2516 dependencies: 2517 '@ark/fs': 0.49.0 2518 '@ark/util': 0.49.0 2519 '@prettier/sync': 0.5.5(prettier@3.5.3) 2520 '@typescript/analyze-trace': 0.10.1 2521 - '@typescript/vfs': 1.6.1(typescript@5.8.3) 2522 arktype: 2.1.22 2523 prettier: 3.5.3 2524 - typescript: 5.8.3 2525 transitivePeerDependencies: 2526 - supports-color 2527 ··· 2541 '@csstools/css-tokenizer': 3.0.4 2542 lru-cache: 10.4.3 2543 2544 - '@atproto/common-web@0.4.3': 2545 dependencies: 2546 - graphemer: 1.4.0 2547 multiformats: 9.9.0 2548 uint8arrays: 3.0.0 2549 - zod: 3.25.76 2550 2551 - '@atproto/lexicon@0.5.1': 2552 dependencies: 2553 - '@atproto/common-web': 0.4.3 2554 '@atproto/syntax': 0.4.1 2555 iso-datestring-validator: 2.2.2 2556 multiformats: 9.9.0 ··· 2561 '@babel/code-frame@7.27.1': 2562 dependencies: 2563 '@babel/helper-validator-identifier': 7.27.1 2564 js-tokens: 4.0.0 2565 picocolors: 1.1.1 2566 2567 '@babel/compat-data@7.28.4': {} 2568 2569 '@babel/core@7.28.4': 2570 dependencies: 2571 '@babel/code-frame': 7.27.1 ··· 2586 transitivePeerDependencies: 2587 - supports-color 2588 2589 '@babel/generator@7.28.3': 2590 dependencies: 2591 '@babel/parser': 7.28.4 ··· 2594 '@jridgewell/trace-mapping': 0.3.31 2595 jsesc: 3.1.0 2596 2597 '@babel/helper-annotate-as-pure@7.27.3': 2598 dependencies: 2599 '@babel/types': 7.28.4 ··· 2606 lru-cache: 5.1.1 2607 semver: 6.3.1 2608 2609 '@babel/helper-create-class-features-plugin@7.28.3(@babel/core@7.28.4)': 2610 dependencies: 2611 '@babel/core': 7.28.4 ··· 2635 transitivePeerDependencies: 2636 - supports-color 2637 2638 '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)': 2639 dependencies: 2640 '@babel/core': 7.28.4 2641 '@babel/helper-module-imports': 7.27.1 2642 '@babel/helper-validator-identifier': 7.27.1 2643 '@babel/traverse': 7.28.4 2644 transitivePeerDependencies: 2645 - supports-color 2646 ··· 2670 2671 '@babel/helper-validator-identifier@7.27.1': {} 2672 2673 '@babel/helper-validator-option@7.27.1': {} 2674 2675 '@babel/helpers@7.28.4': ··· 2677 '@babel/template': 7.27.2 2678 '@babel/types': 7.28.4 2679 2680 '@babel/parser@7.28.4': 2681 dependencies: 2682 '@babel/types': 7.28.4 2683 2684 '@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.28.4)': 2685 dependencies: 2686 '@babel/core': 7.28.4 ··· 2689 transitivePeerDependencies: 2690 - supports-color 2691 2692 - '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.4)': 2693 dependencies: 2694 - '@babel/core': 7.28.4 2695 '@babel/helper-plugin-utils': 7.27.1 2696 2697 - '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.4)': 2698 dependencies: 2699 - '@babel/core': 7.28.4 2700 '@babel/helper-plugin-utils': 7.27.1 2701 2702 '@babel/runtime@7.28.4': {} ··· 2707 '@babel/parser': 7.28.4 2708 '@babel/types': 7.28.4 2709 2710 '@babel/traverse@7.28.4': 2711 dependencies: 2712 '@babel/code-frame': 7.27.1 ··· 2719 transitivePeerDependencies: 2720 - supports-color 2721 2722 '@babel/types@7.28.4': 2723 dependencies: 2724 '@babel/helper-string-parser': 7.27.1 2725 '@babel/helper-validator-identifier': 7.27.1 2726 2727 - '@changesets/apply-release-plan@7.0.13': 2728 dependencies: 2729 - '@changesets/config': 3.1.1 2730 '@changesets/get-version-range-type': 0.4.0 2731 '@changesets/git': 3.0.4 2732 '@changesets/should-skip-package': 0.1.2 ··· 2753 dependencies: 2754 '@changesets/types': 6.1.0 2755 2756 - '@changesets/cli@2.29.7(@types/node@24.0.4)': 2757 dependencies: 2758 - '@changesets/apply-release-plan': 7.0.13 2759 '@changesets/assemble-release-plan': 6.0.9 2760 '@changesets/changelog-git': 0.2.1 2761 - '@changesets/config': 3.1.1 2762 '@changesets/errors': 0.2.0 2763 '@changesets/get-dependents-graph': 2.1.3 2764 - '@changesets/get-release-plan': 4.0.13 2765 '@changesets/git': 3.0.4 2766 '@changesets/logger': 0.1.1 2767 '@changesets/pre': 2.0.2 2768 - '@changesets/read': 0.6.5 2769 '@changesets/should-skip-package': 0.1.2 2770 '@changesets/types': 6.1.0 2771 '@changesets/write': 0.4.0 ··· 2786 transitivePeerDependencies: 2787 - '@types/node' 2788 2789 - '@changesets/config@3.1.1': 2790 dependencies: 2791 '@changesets/errors': 0.2.0 2792 '@changesets/get-dependents-graph': 2.1.3 ··· 2807 picocolors: 1.1.1 2808 semver: 7.7.3 2809 2810 - '@changesets/get-release-plan@4.0.13': 2811 dependencies: 2812 '@changesets/assemble-release-plan': 6.0.9 2813 - '@changesets/config': 3.1.1 2814 '@changesets/pre': 2.0.2 2815 - '@changesets/read': 0.6.5 2816 '@changesets/types': 6.1.0 2817 '@manypkg/get-packages': 1.1.3 2818 ··· 2830 dependencies: 2831 picocolors: 1.1.1 2832 2833 - '@changesets/parse@0.4.1': 2834 dependencies: 2835 '@changesets/types': 6.1.0 2836 - js-yaml: 3.14.1 2837 2838 '@changesets/pre@2.0.2': 2839 dependencies: ··· 2842 '@manypkg/get-packages': 1.1.3 2843 fs-extra: 7.0.1 2844 2845 - '@changesets/read@0.6.5': 2846 dependencies: 2847 '@changesets/git': 3.0.4 2848 '@changesets/logger': 0.1.1 2849 - '@changesets/parse': 0.4.1 2850 '@changesets/types': 6.1.0 2851 fs-extra: 7.0.1 2852 p-filter: 2.1.0 ··· 2894 tslib: 2.8.1 2895 optional: true 2896 2897 '@emnapi/runtime@1.5.0': 2898 dependencies: 2899 tslib: 2.8.1 2900 optional: true ··· 3087 dependencies: 3088 state-local: 1.0.7 3089 3090 - '@monaco-editor/react@4.7.0(monaco-editor@0.52.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': 3091 dependencies: 3092 '@monaco-editor/loader': 1.6.1 3093 monaco-editor: 0.52.0 3094 - react: 19.2.0 3095 - react-dom: 19.2.0(react@19.2.0) 3096 3097 '@napi-rs/wasm-runtime@0.2.12': 3098 dependencies: ··· 3108 '@tybys/wasm-util': 0.10.1 3109 optional: true 3110 3111 '@nodelib/fs.scandir@2.1.5': 3112 dependencies: 3113 '@nodelib/fs.stat': 2.0.5 ··· 3120 '@nodelib/fs.scandir': 2.1.5 3121 fastq: 1.19.1 3122 3123 - '@oxc-project/runtime@0.72.2': {} 3124 - 3125 '@oxc-project/runtime@0.75.0': {} 3126 3127 '@oxc-project/runtime@0.75.1': {} 3128 3129 - '@oxc-project/types@0.72.2': {} 3130 3131 - '@oxc-project/types@0.75.1': {} 3132 3133 - '@oxc-resolver/binding-android-arm-eabi@11.11.1': 3134 optional: true 3135 3136 - '@oxc-resolver/binding-android-arm64@11.11.1': 3137 optional: true 3138 3139 - '@oxc-resolver/binding-darwin-arm64@11.11.1': 3140 optional: true 3141 3142 - '@oxc-resolver/binding-darwin-x64@11.11.1': 3143 optional: true 3144 3145 - '@oxc-resolver/binding-freebsd-x64@11.11.1': 3146 optional: true 3147 3148 - '@oxc-resolver/binding-linux-arm-gnueabihf@11.11.1': 3149 optional: true 3150 3151 - '@oxc-resolver/binding-linux-arm-musleabihf@11.11.1': 3152 optional: true 3153 3154 - '@oxc-resolver/binding-linux-arm64-gnu@11.11.1': 3155 optional: true 3156 3157 - '@oxc-resolver/binding-linux-arm64-musl@11.11.1': 3158 optional: true 3159 3160 - '@oxc-resolver/binding-linux-ppc64-gnu@11.11.1': 3161 optional: true 3162 3163 - '@oxc-resolver/binding-linux-riscv64-gnu@11.11.1': 3164 optional: true 3165 3166 - '@oxc-resolver/binding-linux-riscv64-musl@11.11.1': 3167 optional: true 3168 3169 - '@oxc-resolver/binding-linux-s390x-gnu@11.11.1': 3170 optional: true 3171 3172 - '@oxc-resolver/binding-linux-x64-gnu@11.11.1': 3173 optional: true 3174 3175 - '@oxc-resolver/binding-linux-x64-musl@11.11.1': 3176 optional: true 3177 3178 - '@oxc-resolver/binding-wasm32-wasi@11.11.1': 3179 dependencies: 3180 - '@napi-rs/wasm-runtime': 1.0.7 3181 optional: true 3182 3183 - '@oxc-resolver/binding-win32-arm64-msvc@11.11.1': 3184 optional: true 3185 3186 - '@oxc-resolver/binding-win32-ia32-msvc@11.11.1': 3187 optional: true 3188 3189 - '@oxc-resolver/binding-win32-x64-msvc@11.11.1': 3190 optional: true 3191 3192 '@prettier/sync@0.5.5(prettier@3.5.3)': ··· 3198 dependencies: 3199 quansync: 0.2.11 3200 3201 - '@rolldown/binding-darwin-arm64@1.0.0-beta.11-commit.f051675': 3202 optional: true 3203 3204 '@rolldown/binding-darwin-arm64@1.0.0-beta.24': 3205 optional: true 3206 3207 - '@rolldown/binding-darwin-x64@1.0.0-beta.11-commit.f051675': 3208 optional: true 3209 3210 '@rolldown/binding-darwin-x64@1.0.0-beta.24': 3211 optional: true 3212 3213 - '@rolldown/binding-freebsd-x64@1.0.0-beta.11-commit.f051675': 3214 optional: true 3215 3216 '@rolldown/binding-freebsd-x64@1.0.0-beta.24': 3217 optional: true 3218 3219 - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.11-commit.f051675': 3220 optional: true 3221 3222 '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.24': 3223 optional: true 3224 3225 - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.11-commit.f051675': 3226 optional: true 3227 3228 '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.24': 3229 optional: true 3230 3231 - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.11-commit.f051675': 3232 optional: true 3233 3234 '@rolldown/binding-linux-arm64-musl@1.0.0-beta.24': 3235 optional: true 3236 3237 - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.11-commit.f051675': 3238 optional: true 3239 3240 '@rolldown/binding-linux-x64-gnu@1.0.0-beta.24': 3241 optional: true 3242 3243 - '@rolldown/binding-linux-x64-musl@1.0.0-beta.11-commit.f051675': 3244 optional: true 3245 3246 '@rolldown/binding-linux-x64-musl@1.0.0-beta.24': 3247 optional: true 3248 3249 - '@rolldown/binding-wasm32-wasi@1.0.0-beta.11-commit.f051675': 3250 - dependencies: 3251 - '@napi-rs/wasm-runtime': 0.2.12 3252 optional: true 3253 3254 '@rolldown/binding-wasm32-wasi@1.0.0-beta.24': ··· 3256 '@napi-rs/wasm-runtime': 0.2.12 3257 optional: true 3258 3259 - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.11-commit.f051675': 3260 optional: true 3261 3262 '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.24': 3263 optional: true 3264 3265 - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.11-commit.f051675': 3266 optional: true 3267 3268 '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.24': 3269 optional: true 3270 3271 - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.11-commit.f051675': 3272 optional: true 3273 3274 '@rolldown/binding-win32-x64-msvc@1.0.0-beta.24': 3275 optional: true 3276 3277 - '@rolldown/pluginutils@1.0.0-beta.11-commit.f051675': {} 3278 3279 '@rolldown/pluginutils@1.0.0-beta.24': {} 3280 3281 - '@rolldown/pluginutils@1.0.0-beta.38': {} 3282 3283 '@standard-schema/spec@1.0.0': {} 3284 3285 '@testing-library/dom@10.4.1': 3286 dependencies: 3287 '@babel/code-frame': 7.27.1 ··· 3302 picocolors: 1.1.1 3303 redent: 3.0.0 3304 3305 - '@testing-library/react@16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': 3306 dependencies: 3307 '@babel/runtime': 7.28.4 3308 '@testing-library/dom': 10.4.1 3309 - react: 19.2.0 3310 - react-dom: 19.2.0(react@19.2.0) 3311 optionalDependencies: 3312 - '@types/react': 19.2.2 3313 - '@types/react-dom': 19.2.2(@types/react@19.2.2) 3314 3315 '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)': 3316 dependencies: ··· 3325 3326 '@types/babel__core@7.20.5': 3327 dependencies: 3328 - '@babel/parser': 7.28.4 3329 - '@babel/types': 7.28.4 3330 '@types/babel__generator': 7.27.0 3331 '@types/babel__template': 7.4.4 3332 '@types/babel__traverse': 7.28.0 3333 3334 '@types/babel__generator@7.27.0': 3335 dependencies: 3336 - '@babel/types': 7.28.4 3337 3338 '@types/babel__template@7.4.4': 3339 dependencies: 3340 - '@babel/parser': 7.28.4 3341 - '@babel/types': 7.28.4 3342 3343 '@types/babel__traverse@7.28.0': 3344 dependencies: 3345 - '@babel/types': 7.28.4 3346 3347 '@types/chai@5.2.2': 3348 dependencies: ··· 3360 dependencies: 3361 undici-types: 7.8.0 3362 3363 - '@types/react-dom@19.2.2(@types/react@19.2.2)': 3364 dependencies: 3365 - '@types/react': 19.2.2 3366 3367 - '@types/react@19.2.2': 3368 dependencies: 3369 - csstype: 3.1.3 3370 3371 - '@typescript-eslint/eslint-plugin@8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.29.0(jiti@2.6.1))(typescript@5.8.3)': 3372 dependencies: 3373 '@eslint-community/regexpp': 4.12.1 3374 - '@typescript-eslint/parser': 8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.8.3) 3375 '@typescript-eslint/scope-manager': 8.35.0 3376 - '@typescript-eslint/type-utils': 8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.8.3) 3377 - '@typescript-eslint/utils': 8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.8.3) 3378 '@typescript-eslint/visitor-keys': 8.35.0 3379 eslint: 9.29.0(jiti@2.6.1) 3380 graphemer: 1.4.0 3381 ignore: 7.0.5 3382 natural-compare: 1.4.0 3383 - ts-api-utils: 2.1.0(typescript@5.8.3) 3384 - typescript: 5.8.3 3385 transitivePeerDependencies: 3386 - supports-color 3387 3388 - '@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.8.3)': 3389 dependencies: 3390 '@typescript-eslint/scope-manager': 8.35.0 3391 '@typescript-eslint/types': 8.35.0 3392 - '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) 3393 '@typescript-eslint/visitor-keys': 8.35.0 3394 debug: 4.4.3 3395 eslint: 9.29.0(jiti@2.6.1) 3396 - typescript: 5.8.3 3397 transitivePeerDependencies: 3398 - supports-color 3399 3400 - '@typescript-eslint/project-service@8.35.0(typescript@5.8.3)': 3401 dependencies: 3402 - '@typescript-eslint/tsconfig-utils': 8.35.0(typescript@5.8.3) 3403 '@typescript-eslint/types': 8.35.0 3404 debug: 4.4.3 3405 - typescript: 5.8.3 3406 transitivePeerDependencies: 3407 - supports-color 3408 ··· 3411 '@typescript-eslint/types': 8.35.0 3412 '@typescript-eslint/visitor-keys': 8.35.0 3413 3414 - '@typescript-eslint/tsconfig-utils@8.35.0(typescript@5.8.3)': 3415 dependencies: 3416 - typescript: 5.8.3 3417 3418 - '@typescript-eslint/type-utils@8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.8.3)': 3419 dependencies: 3420 - '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) 3421 - '@typescript-eslint/utils': 8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.8.3) 3422 debug: 4.4.3 3423 eslint: 9.29.0(jiti@2.6.1) 3424 - ts-api-utils: 2.1.0(typescript@5.8.3) 3425 - typescript: 5.8.3 3426 transitivePeerDependencies: 3427 - supports-color 3428 3429 '@typescript-eslint/types@8.35.0': {} 3430 3431 - '@typescript-eslint/typescript-estree@8.35.0(typescript@5.8.3)': 3432 dependencies: 3433 - '@typescript-eslint/project-service': 8.35.0(typescript@5.8.3) 3434 - '@typescript-eslint/tsconfig-utils': 8.35.0(typescript@5.8.3) 3435 '@typescript-eslint/types': 8.35.0 3436 '@typescript-eslint/visitor-keys': 8.35.0 3437 debug: 4.4.3 ··· 3439 is-glob: 4.0.3 3440 minimatch: 9.0.5 3441 semver: 7.7.3 3442 - ts-api-utils: 2.1.0(typescript@5.8.3) 3443 - typescript: 5.8.3 3444 transitivePeerDependencies: 3445 - supports-color 3446 3447 - '@typescript-eslint/utils@8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.8.3)': 3448 dependencies: 3449 '@eslint-community/eslint-utils': 4.9.0(eslint@9.29.0(jiti@2.6.1)) 3450 '@typescript-eslint/scope-manager': 8.35.0 3451 '@typescript-eslint/types': 8.35.0 3452 - '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) 3453 eslint: 9.29.0(jiti@2.6.1) 3454 - typescript: 5.8.3 3455 transitivePeerDependencies: 3456 - supports-color 3457 ··· 3471 treeify: 1.1.0 3472 yargs: 16.2.0 3473 3474 - '@typescript/vfs@1.6.1(typescript@5.8.3)': 3475 dependencies: 3476 debug: 4.4.3 3477 - typescript: 5.8.3 3478 transitivePeerDependencies: 3479 - supports-color 3480 3481 - '@vitejs/plugin-react@5.0.4(rolldown-vite@7.0.6(@types/node@24.0.4)(esbuild@0.25.10)(jiti@2.6.1))': 3482 dependencies: 3483 - '@babel/core': 7.28.4 3484 - '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4) 3485 - '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.4) 3486 - '@rolldown/pluginutils': 1.0.0-beta.38 3487 '@types/babel__core': 7.20.5 3488 - react-refresh: 0.17.0 3489 vite: rolldown-vite@7.0.6(@types/node@24.0.4)(esbuild@0.25.10)(jiti@2.6.1) 3490 transitivePeerDependencies: 3491 - supports-color ··· 3578 3579 assertion-error@2.0.1: {} 3580 3581 - ast-kit@2.1.3: 3582 dependencies: 3583 - '@babel/parser': 7.28.4 3584 pathe: 2.0.3 3585 3586 asynckit@0.4.0: {} ··· 3597 dependencies: 3598 is-windows: 1.0.2 3599 3600 - birpc@2.6.1: {} 3601 3602 brace-expansion@1.1.12: 3603 dependencies: ··· 3687 '@asamuzakjp/css-color': 3.2.0 3688 rrweb-cssom: 0.8.0 3689 3690 - csstype@3.1.3: {} 3691 3692 data-urls@5.0.0: 3693 dependencies: ··· 3724 3725 dom-accessibility-api@0.6.3: {} 3726 3727 - dts-resolver@2.1.2(oxc-resolver@11.11.1): 3728 optionalDependencies: 3729 - oxc-resolver: 11.11.1 3730 3731 dunder-proto@1.0.1: 3732 dependencies: ··· 3738 3739 emoji-regex@8.0.0: {} 3740 3741 - empathic@1.1.0: {} 3742 3743 enquirer@2.4.1: 3744 dependencies: ··· 3993 dunder-proto: 1.0.1 3994 es-object-atoms: 1.1.1 3995 3996 - get-tsconfig@4.12.0: 3997 dependencies: 3998 resolve-pkg-maps: 1.0.0 3999 ··· 4122 dependencies: 4123 argparse: 2.0.1 4124 4125 jsdom@25.0.1: 4126 dependencies: 4127 cssstyle: 4.6.0 ··· 4175 dependencies: 4176 json-buffer: 3.0.1 4177 4178 - knip@5.66.2(@types/node@24.0.4)(typescript@5.8.3): 4179 dependencies: 4180 '@nodelib/fs.walk': 1.2.8 4181 '@types/node': 24.0.4 4182 fast-glob: 3.3.3 4183 formatly: 0.3.0 4184 jiti: 2.6.1 4185 - js-yaml: 4.1.0 4186 minimist: 1.2.8 4187 - oxc-resolver: 11.11.1 4188 picocolors: 1.1.1 4189 picomatch: 4.0.3 4190 - smol-toml: 1.4.2 4191 - strip-json-comments: 5.0.2 4192 - typescript: 5.8.3 4193 zod: 4.1.12 4194 4195 levn@0.4.1: ··· 4272 dependencies: 4273 '@jridgewell/sourcemap-codec': 1.5.5 4274 4275 make-synchronized@0.4.2: {} 4276 4277 math-intrinsics@1.1.0: {} ··· 4315 4316 node-releases@2.0.25: {} 4317 4318 - nuqs@2.7.2(react@19.2.0): 4319 dependencies: 4320 '@standard-schema/spec': 1.0.0 4321 - react: 19.2.0 4322 4323 nwsapi@2.2.22: {} 4324 4325 optionator@0.9.4: 4326 dependencies: ··· 4333 4334 outdent@0.5.0: {} 4335 4336 - oxc-resolver@11.11.1: 4337 optionalDependencies: 4338 - '@oxc-resolver/binding-android-arm-eabi': 11.11.1 4339 - '@oxc-resolver/binding-android-arm64': 11.11.1 4340 - '@oxc-resolver/binding-darwin-arm64': 11.11.1 4341 - '@oxc-resolver/binding-darwin-x64': 11.11.1 4342 - '@oxc-resolver/binding-freebsd-x64': 11.11.1 4343 - '@oxc-resolver/binding-linux-arm-gnueabihf': 11.11.1 4344 - '@oxc-resolver/binding-linux-arm-musleabihf': 11.11.1 4345 - '@oxc-resolver/binding-linux-arm64-gnu': 11.11.1 4346 - '@oxc-resolver/binding-linux-arm64-musl': 11.11.1 4347 - '@oxc-resolver/binding-linux-ppc64-gnu': 11.11.1 4348 - '@oxc-resolver/binding-linux-riscv64-gnu': 11.11.1 4349 - '@oxc-resolver/binding-linux-riscv64-musl': 11.11.1 4350 - '@oxc-resolver/binding-linux-s390x-gnu': 11.11.1 4351 - '@oxc-resolver/binding-linux-x64-gnu': 11.11.1 4352 - '@oxc-resolver/binding-linux-x64-musl': 11.11.1 4353 - '@oxc-resolver/binding-wasm32-wasi': 11.11.1 4354 - '@oxc-resolver/binding-win32-arm64-msvc': 11.11.1 4355 - '@oxc-resolver/binding-win32-ia32-msvc': 11.11.1 4356 - '@oxc-resolver/binding-win32-x64-msvc': 11.11.1 4357 4358 p-filter@2.1.0: 4359 dependencies: ··· 4435 4436 queue-microtask@1.2.3: {} 4437 4438 - react-dom@19.2.0(react@19.2.0): 4439 dependencies: 4440 - react: 19.2.0 4441 scheduler: 0.27.0 4442 4443 react-is@17.0.2: {} 4444 4445 - react-refresh@0.17.0: {} 4446 4447 - react@19.2.0: {} 4448 4449 read-yaml-file@1.1.0: 4450 dependencies: ··· 4476 4477 reusify@1.1.0: {} 4478 4479 - rolldown-plugin-dts@0.13.14(oxc-resolver@11.11.1)(rolldown@1.0.0-beta.11-commit.f051675)(typescript@5.8.3): 4480 dependencies: 4481 - '@babel/generator': 7.28.3 4482 - '@babel/parser': 7.28.4 4483 - '@babel/types': 7.28.4 4484 - ast-kit: 2.1.3 4485 - birpc: 2.6.1 4486 - debug: 4.4.3 4487 - dts-resolver: 2.1.2(oxc-resolver@11.11.1) 4488 - get-tsconfig: 4.12.0 4489 - rolldown: 1.0.0-beta.11-commit.f051675 4490 optionalDependencies: 4491 - typescript: 5.8.3 4492 transitivePeerDependencies: 4493 - oxc-resolver 4494 - - supports-color 4495 4496 rolldown-vite@7.0.6(@types/node@24.0.4)(esbuild@0.25.10)(jiti@2.6.1): 4497 dependencies: ··· 4508 fsevents: 2.3.3 4509 jiti: 2.6.1 4510 4511 - rolldown@1.0.0-beta.11-commit.f051675: 4512 - dependencies: 4513 - '@oxc-project/runtime': 0.72.2 4514 - '@oxc-project/types': 0.72.2 4515 - '@rolldown/pluginutils': 1.0.0-beta.11-commit.f051675 4516 - ansis: 4.2.0 4517 - optionalDependencies: 4518 - '@rolldown/binding-darwin-arm64': 1.0.0-beta.11-commit.f051675 4519 - '@rolldown/binding-darwin-x64': 1.0.0-beta.11-commit.f051675 4520 - '@rolldown/binding-freebsd-x64': 1.0.0-beta.11-commit.f051675 4521 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.11-commit.f051675 4522 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.11-commit.f051675 4523 - '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.11-commit.f051675 4524 - '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.11-commit.f051675 4525 - '@rolldown/binding-linux-x64-musl': 1.0.0-beta.11-commit.f051675 4526 - '@rolldown/binding-wasm32-wasi': 1.0.0-beta.11-commit.f051675 4527 - '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.11-commit.f051675 4528 - '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.11-commit.f051675 4529 - '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.11-commit.f051675 4530 - 4531 rolldown@1.0.0-beta.24: 4532 dependencies: 4533 '@oxc-project/runtime': 0.75.1 ··· 4548 '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.24 4549 '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.24 4550 4551 rrweb-cssom@0.7.1: {} 4552 4553 rrweb-cssom@0.8.0: {} ··· 4586 4587 slash@3.0.0: {} 4588 4589 - smol-toml@1.4.2: {} 4590 4591 source-map-js@1.2.1: {} 4592 ··· 4629 4630 strip-json-comments@3.1.1: {} 4631 4632 - strip-json-comments@5.0.2: {} 4633 4634 strip-literal@3.1.0: 4635 dependencies: ··· 4641 4642 symbol-tree@3.2.4: {} 4643 4644 term-size@2.2.1: {} 4645 4646 through2@4.0.2: ··· 4651 4652 tinyexec@0.3.2: {} 4653 4654 - tinyexec@1.0.1: {} 4655 4656 tinyglobby@0.2.15: 4657 dependencies: ··· 4682 dependencies: 4683 punycode: 2.3.1 4684 4685 treeify@1.1.0: {} 4686 4687 - ts-api-utils@2.1.0(typescript@5.8.3): 4688 dependencies: 4689 - typescript: 5.8.3 4690 4691 - tsdown@0.12.7(oxc-resolver@11.11.1)(typescript@5.8.3): 4692 dependencies: 4693 ansis: 4.2.0 4694 cac: 6.7.14 4695 chokidar: 4.0.3 4696 debug: 4.4.3 4697 diff: 8.0.2 4698 - empathic: 1.1.0 4699 hookable: 5.5.3 4700 - rolldown: 1.0.0-beta.11-commit.f051675 4701 - rolldown-plugin-dts: 0.13.14(oxc-resolver@11.11.1)(rolldown@1.0.0-beta.11-commit.f051675)(typescript@5.8.3) 4702 semver: 7.7.3 4703 - tinyexec: 1.0.1 4704 tinyglobby: 0.2.15 4705 - unconfig: 7.3.3 4706 optionalDependencies: 4707 - typescript: 5.8.3 4708 transitivePeerDependencies: 4709 - '@typescript/native-preview' 4710 - oxc-resolver 4711 - supports-color 4712 - vue-tsc 4713 4714 - tslib@2.8.1: 4715 - optional: true 4716 4717 type-check@0.4.0: 4718 dependencies: 4719 prelude-ls: 1.2.1 4720 4721 - typescript-eslint@8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.8.3): 4722 dependencies: 4723 - '@typescript-eslint/eslint-plugin': 8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.29.0(jiti@2.6.1))(typescript@5.8.3) 4724 - '@typescript-eslint/parser': 8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.8.3) 4725 - '@typescript-eslint/utils': 8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.8.3) 4726 eslint: 9.29.0(jiti@2.6.1) 4727 - typescript: 5.8.3 4728 transitivePeerDependencies: 4729 - supports-color 4730 4731 - typescript@5.8.3: {} 4732 4733 uint8arrays@3.0.0: 4734 dependencies: 4735 multiformats: 9.9.0 4736 4737 - unconfig@7.3.3: 4738 dependencies: 4739 '@quansync/fs': 0.1.5 4740 defu: 6.1.4 4741 jiti: 2.6.1 4742 quansync: 0.2.11 4743 4744 undici-types@7.8.0: {} 4745 4746 universalify@0.1.2: {} 4747
··· 12 .: 13 devDependencies: 14 '@changesets/cli': 15 + specifier: ^2.29.8 16 + version: 2.29.8(@types/node@24.0.4) 17 '@eslint/js': 18 specifier: 9.29.0 19 version: 9.29.0 ··· 21 specifier: 9.29.0 22 version: 9.29.0(jiti@2.6.1) 23 knip: 24 + specifier: ^5.83.1 25 + version: 5.83.1(@types/node@24.0.4)(typescript@5.9.3) 26 prettier: 27 specifier: 3.6.1 28 version: 3.6.1 29 typescript-eslint: 30 specifier: 8.35.0 31 + version: 8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3) 32 33 packages/prototypey: 34 dependencies: 35 '@atproto/lexicon': 36 + specifier: ^0.5.2 37 + version: 0.5.2 38 sade: 39 specifier: ^1.8.1 40 version: 1.8.1 ··· 44 devDependencies: 45 '@ark/attest': 46 specifier: ^0.49.0 47 + version: 0.49.0(typescript@5.9.3) 48 '@types/node': 49 specifier: 24.0.4 50 version: 24.0.4 51 tsdown: 52 + specifier: ^0.15.12 53 + version: 0.15.12(ms@2.1.3)(oxc-resolver@11.15.0)(typescript@5.9.3) 54 typescript: 55 + specifier: 5.9.3 56 + version: 5.9.3 57 vitest: 58 specifier: ^3.2.4 59 version: 3.2.4(@types/node@24.0.4)(esbuild@0.25.10)(jiti@2.6.1)(jsdom@25.0.1) ··· 61 packages/site: 62 dependencies: 63 '@monaco-editor/react': 64 + specifier: ^4.7.0 65 + version: 4.7.0(monaco-editor@0.52.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) 66 lz-string: 67 specifier: ^1.5.0 68 version: 1.5.0 ··· 70 specifier: 0.52.0 71 version: 0.52.0 72 nuqs: 73 + specifier: ^2.8.8 74 + version: 2.8.8(react@19.2.4) 75 prototypey: 76 specifier: workspace:* 77 version: link:../prototypey 78 react: 79 + specifier: ^19.2.4 80 + version: 19.2.4 81 react-dom: 82 + specifier: ^19.2.4 83 + version: 19.2.4(react@19.2.4) 84 devDependencies: 85 + '@tailwindcss/vite': 86 + specifier: ^4.1.18 87 + version: 4.1.18(rolldown-vite@7.0.6(@types/node@24.0.4)(esbuild@0.25.10)(jiti@2.6.1)) 88 '@testing-library/jest-dom': 89 specifier: ^6.9.1 90 version: 6.9.1 91 '@testing-library/react': 92 + specifier: ^16.3.2 93 + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) 94 '@testing-library/user-event': 95 + specifier: ^14.6.1 96 version: 14.6.1(@testing-library/dom@10.4.1) 97 '@types/react': 98 + specifier: ^19.2.13 99 + version: 19.2.13 100 '@types/react-dom': 101 + specifier: ^19.2.3 102 + version: 19.2.3(@types/react@19.2.13) 103 '@vitejs/plugin-react': 104 + specifier: ^5.1.3 105 + version: 5.1.3(rolldown-vite@7.0.6(@types/node@24.0.4)(esbuild@0.25.10)(jiti@2.6.1)) 106 babel-plugin-react-compiler: 107 specifier: ^1.0.0 108 version: 1.0.0 ··· 112 jsdom: 113 specifier: ^25.0.1 114 version: 25.0.1 115 + tailwindcss: 116 + specifier: ^4.1.18 117 + version: 4.1.18 118 typescript: 119 + specifier: 5.9.3 120 + version: 5.9.3 121 vite: 122 specifier: npm:rolldown-vite@7.0.6 123 version: rolldown-vite@7.0.6(@types/node@24.0.4)(esbuild@0.25.10)(jiti@2.6.1) ··· 148 '@asamuzakjp/css-color@3.2.0': 149 resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} 150 151 + '@atproto/common-web@0.4.5': 152 + resolution: {integrity: sha512-Tx0xUafLm3vRvOQpbBl5eb9V8xlC7TaRXs6dAulHRkDG3Kb+P9qn3pkDteq+aeMshbVXbVa1rm3Ok4vFyuoyYA==} 153 + 154 + '@atproto/lex-data@0.0.1': 155 + resolution: {integrity: sha512-DrS/8cQcQs3s5t9ELAFNtyDZ8/PdiCx47ALtFEP2GnX2uCBHZRkqWG7xmu6ehjc787nsFzZBvlnz3T/gov5fGA==} 156 + 157 + '@atproto/lex-json@0.0.1': 158 + resolution: {integrity: sha512-ivcF7+pDRuD/P97IEKQ/9TruunXj0w58Khvwk3M6psaI5eZT6LRsRZ4cWcKaXiFX4SHnjy+x43g0f7pPtIsERg==} 159 160 + '@atproto/lexicon@0.5.2': 161 + resolution: {integrity: sha512-lRmJgMA8f5j7VB5Iu5cp188ald5FuI4FlmZ7nn6EBrk1dgOstWVrI5Ft6K3z2vjyLZRG6nzknlsw+tDP63p7bQ==} 162 163 '@atproto/syntax@0.4.1': 164 resolution: {integrity: sha512-CJdImtLAiFO+0z3BWTtxwk6aY5w4t8orHTMVJgkf++QRJWTxPbIFko/0hrkADB7n2EruDxDSeAgfUGehpH6ngw==} ··· 167 resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} 168 engines: {node: '>=6.9.0'} 169 170 + '@babel/code-frame@7.29.0': 171 + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} 172 + engines: {node: '>=6.9.0'} 173 + 174 '@babel/compat-data@7.28.4': 175 resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==} 176 engines: {node: '>=6.9.0'} 177 178 + '@babel/compat-data@7.29.0': 179 + resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} 180 + engines: {node: '>=6.9.0'} 181 + 182 '@babel/core@7.28.4': 183 resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} 184 + engines: {node: '>=6.9.0'} 185 + 186 + '@babel/core@7.29.0': 187 + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} 188 engines: {node: '>=6.9.0'} 189 190 '@babel/generator@7.28.3': 191 resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} 192 engines: {node: '>=6.9.0'} 193 194 + '@babel/generator@7.28.5': 195 + resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} 196 + engines: {node: '>=6.9.0'} 197 + 198 + '@babel/generator@7.29.1': 199 + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} 200 + engines: {node: '>=6.9.0'} 201 + 202 '@babel/helper-annotate-as-pure@7.27.3': 203 resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} 204 engines: {node: '>=6.9.0'} ··· 207 resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} 208 engines: {node: '>=6.9.0'} 209 210 + '@babel/helper-compilation-targets@7.28.6': 211 + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} 212 + engines: {node: '>=6.9.0'} 213 + 214 '@babel/helper-create-class-features-plugin@7.28.3': 215 resolution: {integrity: sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==} 216 engines: {node: '>=6.9.0'} ··· 229 resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} 230 engines: {node: '>=6.9.0'} 231 232 + '@babel/helper-module-imports@7.28.6': 233 + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} 234 + engines: {node: '>=6.9.0'} 235 + 236 '@babel/helper-module-transforms@7.28.3': 237 resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} 238 engines: {node: '>=6.9.0'} 239 peerDependencies: 240 '@babel/core': ^7.0.0 241 242 + '@babel/helper-module-transforms@7.28.6': 243 + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} 244 + engines: {node: '>=6.9.0'} 245 + peerDependencies: 246 + '@babel/core': ^7.0.0 247 + 248 '@babel/helper-optimise-call-expression@7.27.1': 249 resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} 250 engines: {node: '>=6.9.0'} ··· 271 resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} 272 engines: {node: '>=6.9.0'} 273 274 + '@babel/helper-validator-identifier@7.28.5': 275 + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} 276 + engines: {node: '>=6.9.0'} 277 + 278 '@babel/helper-validator-option@7.27.1': 279 resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} 280 engines: {node: '>=6.9.0'} ··· 283 resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} 284 engines: {node: '>=6.9.0'} 285 286 + '@babel/helpers@7.28.6': 287 + resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} 288 + engines: {node: '>=6.9.0'} 289 + 290 '@babel/parser@7.28.4': 291 resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} 292 engines: {node: '>=6.0.0'} 293 hasBin: true 294 295 + '@babel/parser@7.28.5': 296 + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} 297 + engines: {node: '>=6.0.0'} 298 + hasBin: true 299 + 300 + '@babel/parser@7.29.0': 301 + resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} 302 + engines: {node: '>=6.0.0'} 303 + hasBin: true 304 + 305 '@babel/plugin-proposal-private-methods@7.18.6': 306 resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==} 307 engines: {node: '>=6.9.0'} ··· 329 resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} 330 engines: {node: '>=6.9.0'} 331 332 + '@babel/template@7.28.6': 333 + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} 334 + engines: {node: '>=6.9.0'} 335 + 336 '@babel/traverse@7.28.4': 337 resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} 338 engines: {node: '>=6.9.0'} 339 340 + '@babel/traverse@7.29.0': 341 + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} 342 + engines: {node: '>=6.9.0'} 343 + 344 '@babel/types@7.28.4': 345 resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} 346 engines: {node: '>=6.9.0'} 347 348 + '@babel/types@7.28.5': 349 + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} 350 + engines: {node: '>=6.9.0'} 351 + 352 + '@babel/types@7.29.0': 353 + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} 354 + engines: {node: '>=6.9.0'} 355 + 356 + '@changesets/apply-release-plan@7.0.14': 357 + resolution: {integrity: sha512-ddBvf9PHdy2YY0OUiEl3TV78mH9sckndJR14QAt87KLEbIov81XO0q0QAmvooBxXlqRRP8I9B7XOzZwQG7JkWA==} 358 359 '@changesets/assemble-release-plan@6.0.9': 360 resolution: {integrity: sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ==} ··· 362 '@changesets/changelog-git@0.2.1': 363 resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} 364 365 + '@changesets/cli@2.29.8': 366 + resolution: {integrity: sha512-1weuGZpP63YWUYjay/E84qqwcnt5yJMM0tep10Up7Q5cS/DGe2IZ0Uj3HNMxGhCINZuR7aO9WBMdKnPit5ZDPA==} 367 hasBin: true 368 369 + '@changesets/config@3.1.2': 370 + resolution: {integrity: sha512-CYiRhA4bWKemdYi/uwImjPxqWNpqGPNbEBdX1BdONALFIDK7MCUj6FPkzD+z9gJcvDFUQJn9aDVf4UG7OT6Kog==} 371 372 '@changesets/errors@0.2.0': 373 resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} ··· 375 '@changesets/get-dependents-graph@2.1.3': 376 resolution: {integrity: sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==} 377 378 + '@changesets/get-release-plan@4.0.14': 379 + resolution: {integrity: sha512-yjZMHpUHgl4Xl5gRlolVuxDkm4HgSJqT93Ri1Uz8kGrQb+5iJ8dkXJ20M2j/Y4iV5QzS2c5SeTxVSKX+2eMI0g==} 380 381 '@changesets/get-version-range-type@0.4.0': 382 resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} ··· 387 '@changesets/logger@0.1.1': 388 resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} 389 390 + '@changesets/parse@0.4.2': 391 + resolution: {integrity: sha512-Uo5MC5mfg4OM0jU3up66fmSn6/NE9INK+8/Vn/7sMVcdWg46zfbvvUSjD9EMonVqPi9fbrJH9SXHn48Tr1f2yA==} 392 393 '@changesets/pre@2.0.2': 394 resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==} 395 396 + '@changesets/read@0.6.6': 397 + resolution: {integrity: sha512-P5QaN9hJSQQKJShzzpBT13FzOSPyHbqdoIBUd2DJdgvnECCyO6LmAOWSV+O8se2TaZJVwSXjL+v9yhb+a9JeJg==} 398 399 '@changesets/should-skip-package@0.1.2': 400 resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==} ··· 439 '@emnapi/core@1.5.0': 440 resolution: {integrity: sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==} 441 442 + '@emnapi/core@1.7.1': 443 + resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} 444 + 445 '@emnapi/runtime@1.5.0': 446 resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} 447 + 448 + '@emnapi/runtime@1.7.1': 449 + resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} 450 451 '@emnapi/wasi-threads@1.1.0': 452 resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} ··· 712 '@napi-rs/wasm-runtime@1.0.7': 713 resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} 714 715 + '@napi-rs/wasm-runtime@1.1.0': 716 + resolution: {integrity: sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==} 717 + 718 '@nodelib/fs.scandir@2.1.5': 719 resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 720 engines: {node: '>= 8'} ··· 727 resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 728 engines: {node: '>= 8'} 729 730 '@oxc-project/runtime@0.75.0': 731 resolution: {integrity: sha512-gzRmVI/vorsPmbDXt7GD4Uh2lD3rCOku/1xWPB4Yx48k0EP4TZmzQudWapjN4+7Vv+rgXr0RqCHQadeaMvdBuw==} 732 engines: {node: '>=6.9.0'} ··· 735 resolution: {integrity: sha512-UH07DRi7xXqAsJ/sFbJJg0liIXnapB6P5uADXIiF1s6WQjZzcTIkKHca0s522QVxmijPxVX5ijCYxSr7eSq5CQ==} 736 engines: {node: '>=6.9.0'} 737 738 '@oxc-project/types@0.75.1': 739 resolution: {integrity: sha512-7ZJy+51qWpZRvynaQUezeYfjCtaSdiXIWFUZIlOuTSfDXpXqnSl/m1IUPLx6XrOy6s0SFv3CLE14vcZy63bz7g==} 740 741 + '@oxc-project/types@0.95.0': 742 + resolution: {integrity: sha512-vACy7vhpMPhjEJhULNxrdR0D943TkA/MigMpJCHmBHvMXxRStRi/dPtTlfQ3uDwWSzRpT8z+7ImjZVf8JWBocQ==} 743 + 744 + '@oxc-resolver/binding-android-arm-eabi@11.15.0': 745 + resolution: {integrity: sha512-Q+lWuFfq7whNelNJIP1dhXaVz4zO9Tu77GcQHyxDWh3MaCoO2Bisphgzmsh4ZoUe2zIchQh6OvQL99GlWHg9Tw==} 746 cpu: [arm] 747 os: [android] 748 749 + '@oxc-resolver/binding-android-arm64@11.15.0': 750 + resolution: {integrity: sha512-vbdBttesHR0W1oJaxgWVTboyMUuu+VnPsHXJ6jrXf4czELzB6GIg5DrmlyhAmFBhjwov+yJH/DfTnHS+2sDgOw==} 751 cpu: [arm64] 752 os: [android] 753 754 + '@oxc-resolver/binding-darwin-arm64@11.15.0': 755 + resolution: {integrity: sha512-R67lsOe1UzNjqVBCwCZX1rlItTsj/cVtBw4Uy19CvTicqEWvwaTn8t34zLD75LQwDDPCY3C8n7NbD+LIdw+ZoA==} 756 cpu: [arm64] 757 os: [darwin] 758 759 + '@oxc-resolver/binding-darwin-x64@11.15.0': 760 + resolution: {integrity: sha512-77mya5F8WV0EtCxI0MlVZcqkYlaQpfNwl/tZlfg4jRsoLpFbaTeWv75hFm6TE84WULVlJtSgvf7DhoWBxp9+ZQ==} 761 cpu: [x64] 762 os: [darwin] 763 764 + '@oxc-resolver/binding-freebsd-x64@11.15.0': 765 + resolution: {integrity: sha512-X1Sz7m5PC+6D3KWIDXMUtux+0Imj6HfHGdBStSvgdI60OravzI1t83eyn6eN0LPTrynuPrUgjk7tOnOsBzSWHw==} 766 cpu: [x64] 767 os: [freebsd] 768 769 + '@oxc-resolver/binding-linux-arm-gnueabihf@11.15.0': 770 + resolution: {integrity: sha512-L1x/wCaIRre+18I4cH/lTqSAymlV0k4HqfSYNNuI9oeL28Ks86lI6O5VfYL6sxxWYgjuWB98gNGo7tq7d4GarQ==} 771 cpu: [arm] 772 os: [linux] 773 774 + '@oxc-resolver/binding-linux-arm-musleabihf@11.15.0': 775 + resolution: {integrity: sha512-abGXd/zMGa0tH8nKlAXdOnRy4G7jZmkU0J85kMKWns161bxIgGn/j7zxqh3DKEW98wAzzU9GofZMJ0P5YCVPVw==} 776 cpu: [arm] 777 os: [linux] 778 779 + '@oxc-resolver/binding-linux-arm64-gnu@11.15.0': 780 + resolution: {integrity: sha512-SVjjjtMW66Mza76PBGJLqB0KKyFTBnxmtDXLJPbL6ZPGSctcXVmujz7/WAc0rb9m2oV0cHQTtVjnq6orQnI/jg==} 781 cpu: [arm64] 782 os: [linux] 783 + libc: [glibc] 784 785 + '@oxc-resolver/binding-linux-arm64-musl@11.15.0': 786 + resolution: {integrity: sha512-JDv2/AycPF2qgzEiDeMJCcSzKNDm3KxNg0KKWipoKEMDFqfM7LxNwwSVyAOGmrYlE4l3dg290hOMsr9xG7jv9g==} 787 cpu: [arm64] 788 os: [linux] 789 + libc: [musl] 790 791 + '@oxc-resolver/binding-linux-ppc64-gnu@11.15.0': 792 + resolution: {integrity: sha512-zbu9FhvBLW4KJxo7ElFvZWbSt4vP685Qc/Gyk/Ns3g2gR9qh2qWXouH8PWySy+Ko/qJ42+HJCLg+ZNcxikERfg==} 793 cpu: [ppc64] 794 os: [linux] 795 + libc: [glibc] 796 797 + '@oxc-resolver/binding-linux-riscv64-gnu@11.15.0': 798 + resolution: {integrity: sha512-Kfleehe6B09C2qCnyIU01xLFqFXCHI4ylzkicfX/89j+gNHh9xyNdpEvit88Kq6i5tTGdavVnM6DQfOE2qNtlg==} 799 cpu: [riscv64] 800 os: [linux] 801 + libc: [glibc] 802 803 + '@oxc-resolver/binding-linux-riscv64-musl@11.15.0': 804 + resolution: {integrity: sha512-J7LPiEt27Tpm8P+qURDwNc8q45+n+mWgyys4/V6r5A8v5gDentHRGUx3iVk5NxdKhgoGulrzQocPTZVosq25Eg==} 805 cpu: [riscv64] 806 os: [linux] 807 + libc: [musl] 808 809 + '@oxc-resolver/binding-linux-s390x-gnu@11.15.0': 810 + resolution: {integrity: sha512-+8/d2tAScPjVJNyqa7GPGnqleTB/XW9dZJQ2D/oIM3wpH3TG+DaFEXBbk4QFJ9K9AUGBhvQvWU2mQyhK/yYn3Q==} 811 cpu: [s390x] 812 os: [linux] 813 + libc: [glibc] 814 815 + '@oxc-resolver/binding-linux-x64-gnu@11.15.0': 816 + resolution: {integrity: sha512-xtvSzH7Nr5MCZI2FKImmOdTl9kzuQ51RPyLh451tvD2qnkg3BaqI9Ox78bTk57YJhlXPuxWSOL5aZhKAc9J6qg==} 817 cpu: [x64] 818 os: [linux] 819 + libc: [glibc] 820 821 + '@oxc-resolver/binding-linux-x64-musl@11.15.0': 822 + resolution: {integrity: sha512-14YL1zuXj06+/tqsuUZuzL0T425WA/I4nSVN1kBXeC5WHxem6lQ+2HGvG+crjeJEqHgZUT62YIgj88W+8E7eyg==} 823 cpu: [x64] 824 os: [linux] 825 + libc: [musl] 826 827 + '@oxc-resolver/binding-openharmony-arm64@11.15.0': 828 + resolution: {integrity: sha512-/7Qli+1Wk93coxnrQaU8ySlICYN8HsgyIrzqjgIkQEpI//9eUeaeIHZptNl2fMvBGeXa7k2QgLbRNaBRgpnvMw==} 829 + cpu: [arm64] 830 + os: [openharmony] 831 + 832 + '@oxc-resolver/binding-wasm32-wasi@11.15.0': 833 + resolution: {integrity: sha512-q5rn2eIMQLuc/AVGR2rQKb2EVlgreATGG8xXg8f4XbbYCVgpxaq+dgMbiPStyNywW1MH8VU2T09UEm30UtOQvg==} 834 engines: {node: '>=14.0.0'} 835 cpu: [wasm32] 836 837 + '@oxc-resolver/binding-win32-arm64-msvc@11.15.0': 838 + resolution: {integrity: sha512-yCAh2RWjU/8wWTxQDgGPgzV9QBv0/Ojb5ej1c/58iOjyTuy/J1ZQtYi2SpULjKmwIxLJdTiCHpMilauWimE31w==} 839 cpu: [arm64] 840 os: [win32] 841 842 + '@oxc-resolver/binding-win32-ia32-msvc@11.15.0': 843 + resolution: {integrity: sha512-lmXKb6lvA6M6QIbtYfgjd+AryJqExZVSY2bfECC18OPu7Lv1mHFF171Mai5l9hG3r4IhHPPIwT10EHoilSCYeA==} 844 cpu: [ia32] 845 os: [win32] 846 847 + '@oxc-resolver/binding-win32-x64-msvc@11.15.0': 848 + resolution: {integrity: sha512-HZsfne0s/tGOcJK9ZdTGxsNU2P/dH0Shf0jqrPvsC6wX0Wk+6AyhSpHFLQCnLOuFQiHHU0ePfM8iYsoJb5hHpQ==} 849 cpu: [x64] 850 os: [win32] 851 ··· 857 '@quansync/fs@0.1.5': 858 resolution: {integrity: sha512-lNS9hL2aS2NZgNW7BBj+6EBl4rOf8l+tQ0eRY6JWCI8jI2kc53gSoqbjojU0OnAWhzoXiOjFyGsHcDGePB3lhA==} 859 860 + '@rolldown/binding-android-arm64@1.0.0-beta.45': 861 + resolution: {integrity: sha512-bfgKYhFiXJALeA/riil908+2vlyWGdwa7Ju5S+JgWZYdR4jtiPOGdM6WLfso1dojCh+4ZWeiTwPeV9IKQEX+4g==} 862 + engines: {node: ^20.19.0 || >=22.12.0} 863 cpu: [arm64] 864 + os: [android] 865 866 '@rolldown/binding-darwin-arm64@1.0.0-beta.24': 867 resolution: {integrity: sha512-gE4HGjIioZaMGZupq2zQQdqhlRV2b2qnjFHHkJEW50zVDmiVNWwdHjwvZDPx9JfW5y4GuHgp/zKDLZZbJlQ1/Q==} 868 cpu: [arm64] 869 os: [darwin] 870 871 + '@rolldown/binding-darwin-arm64@1.0.0-beta.45': 872 + resolution: {integrity: sha512-xjCv4CRVsSnnIxTuyH1RDJl5OEQ1c9JYOwfDAHddjJDxCw46ZX9q80+xq7Eok7KC4bRSZudMJllkvOKv0T9SeA==} 873 + engines: {node: ^20.19.0 || >=22.12.0} 874 + cpu: [arm64] 875 os: [darwin] 876 877 '@rolldown/binding-darwin-x64@1.0.0-beta.24': ··· 879 cpu: [x64] 880 os: [darwin] 881 882 + '@rolldown/binding-darwin-x64@1.0.0-beta.45': 883 + resolution: {integrity: sha512-ddcO9TD3D/CLUa/l8GO8LHzBOaZqWg5ClMy3jICoxwCuoz47h9dtqPsIeTiB6yR501LQTeDsjA4lIFd7u3Ljfw==} 884 + engines: {node: ^20.19.0 || >=22.12.0} 885 cpu: [x64] 886 + os: [darwin] 887 888 '@rolldown/binding-freebsd-x64@1.0.0-beta.24': 889 resolution: {integrity: sha512-lx3Q2TU2bbY4yDCZ6e+Wiom3VMLFlZmQswx/1CyjFd+Vv3Q+99SZm6CSfNAIZBaWD246yQRRr1Vx+iIoWCdYzQ==} 890 cpu: [x64] 891 os: [freebsd] 892 893 + '@rolldown/binding-freebsd-x64@1.0.0-beta.45': 894 + resolution: {integrity: sha512-MBTWdrzW9w+UMYDUvnEuh0pQvLENkl2Sis15fHTfHVW7ClbGuez+RWopZudIDEGkpZXdeI4CkRXk+vdIIebrmg==} 895 + engines: {node: ^20.19.0 || >=22.12.0} 896 + cpu: [x64] 897 + os: [freebsd] 898 899 '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.24': 900 resolution: {integrity: sha512-PLtsV6uf3uS1/cNF8Wu/kitTpXT2YpOZbN6VJm7oMi5A8o5oO0vh8STCB71O5k2kwQMVycsmxHWFk2ZyEa6aMw==} 901 cpu: [arm] 902 os: [linux] 903 904 + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.45': 905 + resolution: {integrity: sha512-4YgoCFiki1HR6oSg+GxxfzfnVCesQxLF1LEnw9uXS/MpBmuog0EOO2rYfy69rWP4tFZL9IWp6KEfGZLrZ7aUog==} 906 + engines: {node: ^20.19.0 || >=22.12.0} 907 + cpu: [arm] 908 os: [linux] 909 910 '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.24': 911 resolution: {integrity: sha512-UxGukDkWnv7uS5R+BPVeJ4FSuwA+lgC62LRsyPPSJhJhKMNGZ2W9sQPIpEtBRlww8t0qR6QBsiD5TGLW98ktGw==} 912 cpu: [arm64] 913 os: [linux] 914 + libc: [glibc] 915 916 + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.45': 917 + resolution: {integrity: sha512-LE1gjAwQRrbCOorJJ7LFr10s5vqYf5a00V5Ea9wXcT2+56n5YosJkcp8eQ12FxRBv2YX8dsdQJb+ZTtYJwb6XQ==} 918 + engines: {node: ^20.19.0 || >=22.12.0} 919 cpu: [arm64] 920 os: [linux] 921 + libc: [glibc] 922 923 '@rolldown/binding-linux-arm64-musl@1.0.0-beta.24': 924 resolution: {integrity: sha512-vB99yGYW9FOQe4lk3MNKa13+vRj+7waZFlRE3Ba/IpEy7RFxZ78ASkPLXkz4+kYYbUvMnRaOfk9RKX2fqYZRUg==} 925 cpu: [arm64] 926 os: [linux] 927 + libc: [musl] 928 929 + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.45': 930 + resolution: {integrity: sha512-tdy8ThO/fPp40B81v0YK3QC+KODOmzJzSUOO37DinQxzlTJ026gqUSOM8tzlVixRbQJltgVDCTYF8HNPRErQTA==} 931 + engines: {node: ^20.19.0 || >=22.12.0} 932 + cpu: [arm64] 933 os: [linux] 934 + libc: [musl] 935 936 '@rolldown/binding-linux-x64-gnu@1.0.0-beta.24': 937 resolution: {integrity: sha512-fAMZBWutuKWHsyvHVsKjFYRXVgTbzBfNmomzPPpog8UtdkHk5Vnb0qVEeZP4hR4TsXnKfzD2EQ98NRqFej5QYA==} 938 cpu: [x64] 939 os: [linux] 940 + libc: [glibc] 941 942 + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.45': 943 + resolution: {integrity: sha512-lS082ROBWdmOyVY/0YB3JmsiClaWoxvC+dA8/rbhyB9VLkvVEaihLEOr4CYmrMse151C4+S6hCw6oa1iewox7g==} 944 + engines: {node: ^20.19.0 || >=22.12.0} 945 cpu: [x64] 946 os: [linux] 947 + libc: [glibc] 948 949 '@rolldown/binding-linux-x64-musl@1.0.0-beta.24': 950 resolution: {integrity: sha512-0UY/Qo8fAlpolcWOg2ZU7SCUrsCJWifdRMliV9GXlZaBKbMoVNFw0pHGDm9cj/3TWhJu/iB0peZK00dm22LlNw==} 951 cpu: [x64] 952 os: [linux] 953 + libc: [musl] 954 955 + '@rolldown/binding-linux-x64-musl@1.0.0-beta.45': 956 + resolution: {integrity: sha512-Hi73aYY0cBkr1/SvNQqH8Cd+rSV6S9RB5izCv0ySBcRnd/Wfn5plguUoGYwBnhHgFbh6cPw9m2dUVBR6BG1gxA==} 957 + engines: {node: ^20.19.0 || >=22.12.0} 958 + cpu: [x64] 959 + os: [linux] 960 + libc: [musl] 961 + 962 + '@rolldown/binding-openharmony-arm64@1.0.0-beta.45': 963 + resolution: {integrity: sha512-fljEqbO7RHHogNDxYtTzr+GNjlfOx21RUyGmF+NrkebZ8emYYiIqzPxsaMZuRx0rgZmVmliOzEp86/CQFDKhJQ==} 964 + engines: {node: ^20.19.0 || >=22.12.0} 965 + cpu: [arm64] 966 + os: [openharmony] 967 968 '@rolldown/binding-wasm32-wasi@1.0.0-beta.24': 969 resolution: {integrity: sha512-7ubbtKCo6FBuAM4q6LoT5dOea7f/zj9OYXgumbwSmA0fw18mN5h8SrFTUjl7h9MpPkOyhi2uY6ss4pb39KXkcw==} 970 engines: {node: '>=14.21.3'} 971 cpu: [wasm32] 972 973 + '@rolldown/binding-wasm32-wasi@1.0.0-beta.45': 974 + resolution: {integrity: sha512-ZJDB7lkuZE9XUnWQSYrBObZxczut+8FZ5pdanm8nNS1DAo8zsrPuvGwn+U3fwU98WaiFsNrA4XHngesCGr8tEQ==} 975 + engines: {node: '>=14.0.0'} 976 + cpu: [wasm32] 977 978 '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.24': 979 resolution: {integrity: sha512-S5WKIabtRBJyzu31KnJRlbZRR6FMrTMzYRrNTnIY2hWWXfpcB1PNuHqbo+98ODLpH8knul4Vyf5sCL61okLTjA==} 980 cpu: [arm64] 981 os: [win32] 982 983 + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.45': 984 + resolution: {integrity: sha512-zyzAjItHPUmxg6Z8SyRhLdXlJn3/D9KL5b9mObUrBHhWS/GwRH4665xCiFqeuktAhhWutqfc+rOV2LjK4VYQGQ==} 985 + engines: {node: ^20.19.0 || >=22.12.0} 986 + cpu: [arm64] 987 os: [win32] 988 989 '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.24': ··· 991 cpu: [ia32] 992 os: [win32] 993 994 + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.45': 995 + resolution: {integrity: sha512-wODcGzlfxqS6D7BR0srkJk3drPwXYLu7jPHN27ce2c4PUnVVmJnp9mJzUQGT4LpmHmmVdMZ+P6hKvyTGBzc1CA==} 996 + engines: {node: ^20.19.0 || >=22.12.0} 997 + cpu: [ia32] 998 os: [win32] 999 1000 '@rolldown/binding-win32-x64-msvc@1.0.0-beta.24': ··· 1002 cpu: [x64] 1003 os: [win32] 1004 1005 + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.45': 1006 + resolution: {integrity: sha512-wiU40G1nQo9rtfvF9jLbl79lUgjfaD/LTyUEw2Wg/gdF5OhjzpKMVugZQngO+RNdwYaNj+Fs+kWBWfp4VXPMHA==} 1007 + engines: {node: ^20.19.0 || >=22.12.0} 1008 + cpu: [x64] 1009 + os: [win32] 1010 1011 '@rolldown/pluginutils@1.0.0-beta.24': 1012 resolution: {integrity: sha512-NMiim/enJlffMP16IanVj1ajFNEg8SaMEYyxyYfJoEyt5EiFT3HUH/T2GRdeStNWp+/kg5U8DiJqnQBgLQ8uCw==} 1013 1014 + '@rolldown/pluginutils@1.0.0-beta.45': 1015 + resolution: {integrity: sha512-Le9ulGCrD8ggInzWw/k2J8QcbPz7eGIOWqfJ2L+1R0Opm7n6J37s2hiDWlh6LJN0Lk9L5sUzMvRHKW7UxBZsQA==} 1016 + 1017 + '@rolldown/pluginutils@1.0.0-rc.2': 1018 + resolution: {integrity: sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==} 1019 1020 '@standard-schema/spec@1.0.0': 1021 resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} 1022 1023 + '@tailwindcss/node@4.1.18': 1024 + resolution: {integrity: sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==} 1025 + 1026 + '@tailwindcss/oxide-android-arm64@4.1.18': 1027 + resolution: {integrity: sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==} 1028 + engines: {node: '>= 10'} 1029 + cpu: [arm64] 1030 + os: [android] 1031 + 1032 + '@tailwindcss/oxide-darwin-arm64@4.1.18': 1033 + resolution: {integrity: sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==} 1034 + engines: {node: '>= 10'} 1035 + cpu: [arm64] 1036 + os: [darwin] 1037 + 1038 + '@tailwindcss/oxide-darwin-x64@4.1.18': 1039 + resolution: {integrity: sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==} 1040 + engines: {node: '>= 10'} 1041 + cpu: [x64] 1042 + os: [darwin] 1043 + 1044 + '@tailwindcss/oxide-freebsd-x64@4.1.18': 1045 + resolution: {integrity: sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==} 1046 + engines: {node: '>= 10'} 1047 + cpu: [x64] 1048 + os: [freebsd] 1049 + 1050 + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': 1051 + resolution: {integrity: sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==} 1052 + engines: {node: '>= 10'} 1053 + cpu: [arm] 1054 + os: [linux] 1055 + 1056 + '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': 1057 + resolution: {integrity: sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==} 1058 + engines: {node: '>= 10'} 1059 + cpu: [arm64] 1060 + os: [linux] 1061 + libc: [glibc] 1062 + 1063 + '@tailwindcss/oxide-linux-arm64-musl@4.1.18': 1064 + resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==} 1065 + engines: {node: '>= 10'} 1066 + cpu: [arm64] 1067 + os: [linux] 1068 + libc: [musl] 1069 + 1070 + '@tailwindcss/oxide-linux-x64-gnu@4.1.18': 1071 + resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==} 1072 + engines: {node: '>= 10'} 1073 + cpu: [x64] 1074 + os: [linux] 1075 + libc: [glibc] 1076 + 1077 + '@tailwindcss/oxide-linux-x64-musl@4.1.18': 1078 + resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==} 1079 + engines: {node: '>= 10'} 1080 + cpu: [x64] 1081 + os: [linux] 1082 + libc: [musl] 1083 + 1084 + '@tailwindcss/oxide-wasm32-wasi@4.1.18': 1085 + resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==} 1086 + engines: {node: '>=14.0.0'} 1087 + cpu: [wasm32] 1088 + bundledDependencies: 1089 + - '@napi-rs/wasm-runtime' 1090 + - '@emnapi/core' 1091 + - '@emnapi/runtime' 1092 + - '@tybys/wasm-util' 1093 + - '@emnapi/wasi-threads' 1094 + - tslib 1095 + 1096 + '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': 1097 + resolution: {integrity: sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==} 1098 + engines: {node: '>= 10'} 1099 + cpu: [arm64] 1100 + os: [win32] 1101 + 1102 + '@tailwindcss/oxide-win32-x64-msvc@4.1.18': 1103 + resolution: {integrity: sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==} 1104 + engines: {node: '>= 10'} 1105 + cpu: [x64] 1106 + os: [win32] 1107 + 1108 + '@tailwindcss/oxide@4.1.18': 1109 + resolution: {integrity: sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==} 1110 + engines: {node: '>= 10'} 1111 + 1112 + '@tailwindcss/vite@4.1.18': 1113 + resolution: {integrity: sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==} 1114 + peerDependencies: 1115 + vite: ^5.2.0 || ^6 || ^7 1116 + 1117 '@testing-library/dom@10.4.1': 1118 resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} 1119 engines: {node: '>=18'} ··· 1122 resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} 1123 engines: {node: '>=14', npm: '>=6', yarn: '>=1'} 1124 1125 + '@testing-library/react@16.3.2': 1126 + resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==} 1127 engines: {node: '>=18'} 1128 peerDependencies: 1129 '@testing-library/dom': ^10.0.0 ··· 1179 '@types/node@24.0.4': 1180 resolution: {integrity: sha512-ulyqAkrhnuNq9pB76DRBTkcS6YsmDALy6Ua63V8OhrOBgbcYt6IOdzpw5P1+dyRIyMerzLkeYWBeOXPpA9GMAA==} 1181 1182 + '@types/react-dom@19.2.3': 1183 + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} 1184 peerDependencies: 1185 '@types/react': ^19.2.0 1186 1187 + '@types/react@19.2.13': 1188 + resolution: {integrity: sha512-KkiJeU6VbYbUOp5ITMIc7kBfqlYkKA5KhEHVrGMmUUMt7NeaZg65ojdPk+FtNrBAOXNVM5QM72jnADjM+XVRAQ==} 1189 1190 '@typescript-eslint/eslint-plugin@8.35.0': 1191 resolution: {integrity: sha512-ijItUYaiWuce0N1SoSMrEd0b6b6lYkYt99pqCPfybd+HKVXtEvYhICfLdwp42MhiI5mp0oq7PKEL+g1cNiz/Eg==} ··· 1255 peerDependencies: 1256 typescript: '*' 1257 1258 + '@vitejs/plugin-react@5.1.3': 1259 + resolution: {integrity: sha512-NVUnA6gQCl8jfoYqKqQU5Clv0aPw14KkZYCsX6T9Lfu9slI0LOU10OTwFHS/WmptsMMpshNd/1tuWsHQ2Uk+cg==} 1260 engines: {node: ^20.19.0 || >=22.12.0} 1261 peerDependencies: 1262 vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 ··· 1347 resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} 1348 engines: {node: '>=12'} 1349 1350 + ast-kit@2.2.0: 1351 + resolution: {integrity: sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw==} 1352 engines: {node: '>=20.19.0'} 1353 1354 asynckit@0.4.0: ··· 1368 resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} 1369 engines: {node: '>=4'} 1370 1371 + birpc@2.8.0: 1372 + resolution: {integrity: sha512-Bz2a4qD/5GRhiHSwj30c/8kC8QGj12nNDwz3D4ErQ4Xhy35dsSDvF+RA/tWpjyU0pdGtSDiEk6B5fBGE1qNVhw==} 1373 1374 brace-expansion@1.1.12: 1375 resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} ··· 1455 resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} 1456 engines: {node: '>=18'} 1457 1458 + csstype@3.2.3: 1459 + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} 1460 1461 data-urls@5.0.0: 1462 resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} ··· 1514 dom-accessibility-api@0.6.3: 1515 resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} 1516 1517 + dts-resolver@2.1.3: 1518 + resolution: {integrity: sha512-bihc7jPC90VrosXNzK0LTE2cuLP6jr0Ro8jk+kMugHReJVLIpHz/xadeq3MhuwyO4TD4OA3L1Q8pBBFRc08Tsw==} 1519 + engines: {node: '>=20.19.0'} 1520 peerDependencies: 1521 oxc-resolver: '>=11.0.0' 1522 peerDependenciesMeta: ··· 1533 emoji-regex@8.0.0: 1534 resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 1535 1536 + empathic@2.0.0: 1537 + resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} 1538 engines: {node: '>=14'} 1539 1540 + enhanced-resolve@5.18.3: 1541 + resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} 1542 + engines: {node: '>=10.13.0'} 1543 + 1544 enquirer@2.4.1: 1545 resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} 1546 engines: {node: '>=8.6'} ··· 1740 resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} 1741 engines: {node: '>= 0.4'} 1742 1743 + get-tsconfig@4.13.0: 1744 + resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} 1745 1746 glob-parent@5.1.2: 1747 resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} ··· 1892 resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} 1893 hasBin: true 1894 1895 + js-yaml@4.1.1: 1896 + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} 1897 + hasBin: true 1898 + 1899 jsdom@25.0.1: 1900 resolution: {integrity: sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==} 1901 engines: {node: '>=18'} ··· 1939 keyv@4.5.4: 1940 resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} 1941 1942 + knip@5.83.1: 1943 + resolution: {integrity: sha512-av3ZG/Nui6S/BNL8Tmj12yGxYfTnwWnslouW97m40him7o8MwiMjZBY9TPvlEWUci45aVId0/HbgTwSKIDGpMw==} 1944 engines: {node: '>=18.18.0'} 1945 hasBin: true 1946 peerDependencies: ··· 1986 engines: {node: '>= 12.0.0'} 1987 cpu: [arm64] 1988 os: [linux] 1989 + libc: [glibc] 1990 1991 lightningcss-linux-arm64-musl@1.30.2: 1992 resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} 1993 engines: {node: '>= 12.0.0'} 1994 cpu: [arm64] 1995 os: [linux] 1996 + libc: [musl] 1997 1998 lightningcss-linux-x64-gnu@1.30.2: 1999 resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} 2000 engines: {node: '>= 12.0.0'} 2001 cpu: [x64] 2002 os: [linux] 2003 + libc: [glibc] 2004 2005 lightningcss-linux-x64-musl@1.30.2: 2006 resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} 2007 engines: {node: '>= 12.0.0'} 2008 cpu: [x64] 2009 os: [linux] 2010 + libc: [musl] 2011 2012 lightningcss-win32-arm64-msvc@1.30.2: 2013 resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} ··· 2054 2055 magic-string@0.30.19: 2056 resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} 2057 + 2058 + magic-string@0.30.21: 2059 + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} 2060 2061 make-synchronized@0.4.2: 2062 resolution: {integrity: sha512-EwEJSg8gSGLicKXp/VzNi1tvzhdmNBxOzslkkJSoNUCQFZKH/NIUIp7xlfN+noaHrz4BJDN73gne8IHnjl/F/A==} ··· 2119 node-releases@2.0.25: 2120 resolution: {integrity: sha512-4auku8B/vw5psvTiiN9j1dAOsXvMoGqJuKJcR+dTdqiXEK20mMTk1UEo3HS16LeGQsVG6+qKTPM9u/qQ2LqATA==} 2121 2122 + nuqs@2.8.8: 2123 + resolution: {integrity: sha512-LF5sw9nWpHyPWzMMu9oho3r9C5DvkpmBIg4LQN78sexIzGaeRx8DWr0uy3YiFx5i2QGZN1Qqcb+OAtEVRa2bnA==} 2124 peerDependencies: 2125 '@remix-run/react': '>=2' 2126 '@tanstack/react-router': ^1 2127 next: '>=14.2.0' 2128 react: '>=18.2.0 || ^19.0.0-0' 2129 + react-router: ^5 || ^6 || ^7 2130 + react-router-dom: ^5 || ^6 || ^7 2131 peerDependenciesMeta: 2132 '@remix-run/react': 2133 optional: true ··· 2143 nwsapi@2.2.22: 2144 resolution: {integrity: sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==} 2145 2146 + obug@1.0.0: 2147 + resolution: {integrity: sha512-WKcS43Yl6YPJekid7KiRdT6CHMSmYWVfJiSFbTaGxWQlC+cEBPxHa9jR1uS2cMiQmXd8Hsa2ipAKErQ/GLhSpg==} 2148 + peerDependencies: 2149 + ms: ^2.0.0 2150 + peerDependenciesMeta: 2151 + ms: 2152 + optional: true 2153 + 2154 optionator@0.9.4: 2155 resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} 2156 engines: {node: '>= 0.8.0'} ··· 2158 outdent@0.5.0: 2159 resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} 2160 2161 + oxc-resolver@11.15.0: 2162 + resolution: {integrity: sha512-Hk2J8QMYwmIO9XTCUiOH00+Xk2/+aBxRUnhrSlANDyCnLYc32R1WSIq1sU2yEdlqd53FfMpPEpnBYIKQMzliJw==} 2163 2164 p-filter@2.1.0: 2165 resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} ··· 2270 queue-microtask@1.2.3: 2271 resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 2272 2273 + react-dom@19.2.4: 2274 + resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} 2275 peerDependencies: 2276 + react: ^19.2.4 2277 2278 react-is@17.0.2: 2279 resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} 2280 2281 + react-refresh@0.18.0: 2282 + resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} 2283 engines: {node: '>=0.10.0'} 2284 2285 + react@19.2.4: 2286 + resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} 2287 engines: {node: '>=0.10.0'} 2288 2289 read-yaml-file@1.1.0: ··· 2321 resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} 2322 engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 2323 2324 + rolldown-plugin-dts@0.17.7: 2325 + resolution: {integrity: sha512-ZGgXMhzCItmznNzbJlTcC/KdM6bIwcZoYUykJ2q14HOGvnMhnl2RXU+XrIrdjA2Hyzq3nWqDH7qWaM5a4uCVnw==} 2326 engines: {node: '>=20.18.0'} 2327 peerDependencies: 2328 + '@ts-macro/tsc': ^0.3.6 2329 '@typescript/native-preview': '>=7.0.0-dev.20250601.1' 2330 + rolldown: ^1.0.0-beta.44 2331 typescript: ^5.0.0 2332 + vue-tsc: ~3.1.0 2333 peerDependenciesMeta: 2334 + '@ts-macro/tsc': 2335 + optional: true 2336 '@typescript/native-preview': 2337 optional: true 2338 typescript: ··· 2380 yaml: 2381 optional: true 2382 2383 rolldown@1.0.0-beta.24: 2384 resolution: {integrity: sha512-eDyipoOnoHQ5p6INkJ8g31eKGlqPSCAN9PapyOTw5HET4FYIWALZnSgpMZ67mdn+xT3jAsqGidNnBcIM6EAUhA==} 2385 hasBin: true 2386 2387 + rolldown@1.0.0-beta.45: 2388 + resolution: {integrity: sha512-iMmuD72XXLf26Tqrv1cryNYLX6NNPLhZ3AmNkSf8+xda0H+yijjGJ+wVT9UdBUHOpKzq9RjKtQKRCWoEKQQBZQ==} 2389 + engines: {node: ^20.19.0 || >=22.12.0} 2390 + hasBin: true 2391 + 2392 rrweb-cssom@0.7.1: 2393 resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==} 2394 ··· 2443 resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} 2444 engines: {node: '>=8'} 2445 2446 + smol-toml@1.5.2: 2447 + resolution: {integrity: sha512-QlaZEqcAH3/RtNyet1IPIYPsEWAaYyXXv1Krsi+1L/QHppjX4Ifm8MQsBISz9vE8cHicIq3clogsheili5vhaQ==} 2448 engines: {node: '>= 18'} 2449 2450 source-map-js@1.2.1: ··· 2492 resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} 2493 engines: {node: '>=8'} 2494 2495 + strip-json-comments@5.0.3: 2496 + resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} 2497 engines: {node: '>=14.16'} 2498 2499 strip-literal@3.1.0: ··· 2506 symbol-tree@3.2.4: 2507 resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} 2508 2509 + tailwindcss@4.1.18: 2510 + resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==} 2511 + 2512 + tapable@2.3.0: 2513 + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} 2514 + engines: {node: '>=6'} 2515 + 2516 term-size@2.2.1: 2517 resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} 2518 engines: {node: '>=8'} ··· 2526 tinyexec@0.3.2: 2527 resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} 2528 2529 + tinyexec@1.0.2: 2530 + resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} 2531 + engines: {node: '>=18'} 2532 2533 tinyglobby@0.2.15: 2534 resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} ··· 2565 resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} 2566 engines: {node: '>=18'} 2567 2568 + tree-kill@1.2.2: 2569 + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} 2570 + hasBin: true 2571 + 2572 treeify@1.1.0: 2573 resolution: {integrity: sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==} 2574 engines: {node: '>=0.6'} ··· 2579 peerDependencies: 2580 typescript: '>=4.8.4' 2581 2582 + tsdown@0.15.12: 2583 + resolution: {integrity: sha512-c8VLlQm8/lFrOAg5VMVeN4NAbejZyVQkzd+ErjuaQgJFI/9MhR9ivr0H/CM7UlOF1+ELlF6YaI7sU/4itgGQ8w==} 2584 + engines: {node: '>=20.19.0'} 2585 hasBin: true 2586 peerDependencies: 2587 '@arethetypeswrong/core': ^0.18.1 ··· 2589 typescript: ^5.0.0 2590 unplugin-lightningcss: ^0.4.0 2591 unplugin-unused: ^0.5.0 2592 + unrun: ^0.2.1 2593 peerDependenciesMeta: 2594 '@arethetypeswrong/core': 2595 optional: true ··· 2600 unplugin-lightningcss: 2601 optional: true 2602 unplugin-unused: 2603 + optional: true 2604 + unrun: 2605 optional: true 2606 2607 tslib@2.8.1: ··· 2618 eslint: ^8.57.0 || ^9.0.0 2619 typescript: '>=4.8.4 <5.9.0' 2620 2621 + typescript@5.9.3: 2622 + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} 2623 engines: {node: '>=14.17'} 2624 hasBin: true 2625 2626 uint8arrays@3.0.0: 2627 resolution: {integrity: sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==} 2628 2629 + unconfig-core@7.4.1: 2630 + resolution: {integrity: sha512-Bp/bPZjV2Vl/fofoA2OYLSnw1Z0MOhCX7zHnVCYrazpfZvseBbGhwcNQMxsg185Mqh7VZQqK3C8hFG/Dyng+yA==} 2631 + 2632 + unconfig@7.4.1: 2633 + resolution: {integrity: sha512-uyQ7LElcGizrOGZyIq9KU+xkuEjcRf9IpmDTkCSYv5mEeZzrXSj6rb51C0L+WTedsmAoVxW9WKrLWhSwebIM9Q==} 2634 2635 undici-types@7.8.0: 2636 resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} 2637 + 2638 + unicode-segmenter@0.14.0: 2639 + resolution: {integrity: sha512-AH4lhPCJANUnSLEKnM4byboctePJzltF4xj8b+NbNiYeAkAXGh7px2K/4NANFp7dnr6+zB3e6HLu8Jj8SKyvYg==} 2640 2641 universalify@0.1.2: 2642 resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} ··· 2783 2784 '@adobe/css-tools@4.4.4': {} 2785 2786 + '@ark/attest@0.49.0(typescript@5.9.3)': 2787 dependencies: 2788 '@ark/fs': 0.49.0 2789 '@ark/util': 0.49.0 2790 '@prettier/sync': 0.5.5(prettier@3.5.3) 2791 '@typescript/analyze-trace': 0.10.1 2792 + '@typescript/vfs': 1.6.1(typescript@5.9.3) 2793 arktype: 2.1.22 2794 prettier: 3.5.3 2795 + typescript: 5.9.3 2796 transitivePeerDependencies: 2797 - supports-color 2798 ··· 2812 '@csstools/css-tokenizer': 3.0.4 2813 lru-cache: 10.4.3 2814 2815 + '@atproto/common-web@0.4.5': 2816 dependencies: 2817 + '@atproto/lex-data': 0.0.1 2818 + '@atproto/lex-json': 0.0.1 2819 + zod: 3.25.76 2820 + 2821 + '@atproto/lex-data@0.0.1': 2822 + dependencies: 2823 + '@atproto/syntax': 0.4.1 2824 multiformats: 9.9.0 2825 + tslib: 2.8.1 2826 uint8arrays: 3.0.0 2827 + unicode-segmenter: 0.14.0 2828 + 2829 + '@atproto/lex-json@0.0.1': 2830 + dependencies: 2831 + '@atproto/lex-data': 0.0.1 2832 + tslib: 2.8.1 2833 2834 + '@atproto/lexicon@0.5.2': 2835 dependencies: 2836 + '@atproto/common-web': 0.4.5 2837 '@atproto/syntax': 0.4.1 2838 iso-datestring-validator: 2.2.2 2839 multiformats: 9.9.0 ··· 2844 '@babel/code-frame@7.27.1': 2845 dependencies: 2846 '@babel/helper-validator-identifier': 7.27.1 2847 + js-tokens: 4.0.0 2848 + picocolors: 1.1.1 2849 + 2850 + '@babel/code-frame@7.29.0': 2851 + dependencies: 2852 + '@babel/helper-validator-identifier': 7.28.5 2853 js-tokens: 4.0.0 2854 picocolors: 1.1.1 2855 2856 '@babel/compat-data@7.28.4': {} 2857 2858 + '@babel/compat-data@7.29.0': {} 2859 + 2860 '@babel/core@7.28.4': 2861 dependencies: 2862 '@babel/code-frame': 7.27.1 ··· 2877 transitivePeerDependencies: 2878 - supports-color 2879 2880 + '@babel/core@7.29.0': 2881 + dependencies: 2882 + '@babel/code-frame': 7.29.0 2883 + '@babel/generator': 7.29.1 2884 + '@babel/helper-compilation-targets': 7.28.6 2885 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) 2886 + '@babel/helpers': 7.28.6 2887 + '@babel/parser': 7.29.0 2888 + '@babel/template': 7.28.6 2889 + '@babel/traverse': 7.29.0 2890 + '@babel/types': 7.29.0 2891 + '@jridgewell/remapping': 2.3.5 2892 + convert-source-map: 2.0.0 2893 + debug: 4.4.3 2894 + gensync: 1.0.0-beta.2 2895 + json5: 2.2.3 2896 + semver: 6.3.1 2897 + transitivePeerDependencies: 2898 + - supports-color 2899 + 2900 '@babel/generator@7.28.3': 2901 dependencies: 2902 '@babel/parser': 7.28.4 ··· 2905 '@jridgewell/trace-mapping': 0.3.31 2906 jsesc: 3.1.0 2907 2908 + '@babel/generator@7.28.5': 2909 + dependencies: 2910 + '@babel/parser': 7.28.5 2911 + '@babel/types': 7.28.5 2912 + '@jridgewell/gen-mapping': 0.3.13 2913 + '@jridgewell/trace-mapping': 0.3.31 2914 + jsesc: 3.1.0 2915 + 2916 + '@babel/generator@7.29.1': 2917 + dependencies: 2918 + '@babel/parser': 7.29.0 2919 + '@babel/types': 7.29.0 2920 + '@jridgewell/gen-mapping': 0.3.13 2921 + '@jridgewell/trace-mapping': 0.3.31 2922 + jsesc: 3.1.0 2923 + 2924 '@babel/helper-annotate-as-pure@7.27.3': 2925 dependencies: 2926 '@babel/types': 7.28.4 ··· 2933 lru-cache: 5.1.1 2934 semver: 6.3.1 2935 2936 + '@babel/helper-compilation-targets@7.28.6': 2937 + dependencies: 2938 + '@babel/compat-data': 7.29.0 2939 + '@babel/helper-validator-option': 7.27.1 2940 + browserslist: 4.26.3 2941 + lru-cache: 5.1.1 2942 + semver: 6.3.1 2943 + 2944 '@babel/helper-create-class-features-plugin@7.28.3(@babel/core@7.28.4)': 2945 dependencies: 2946 '@babel/core': 7.28.4 ··· 2970 transitivePeerDependencies: 2971 - supports-color 2972 2973 + '@babel/helper-module-imports@7.28.6': 2974 + dependencies: 2975 + '@babel/traverse': 7.29.0 2976 + '@babel/types': 7.29.0 2977 + transitivePeerDependencies: 2978 + - supports-color 2979 + 2980 '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)': 2981 dependencies: 2982 '@babel/core': 7.28.4 2983 '@babel/helper-module-imports': 7.27.1 2984 '@babel/helper-validator-identifier': 7.27.1 2985 '@babel/traverse': 7.28.4 2986 + transitivePeerDependencies: 2987 + - supports-color 2988 + 2989 + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': 2990 + dependencies: 2991 + '@babel/core': 7.29.0 2992 + '@babel/helper-module-imports': 7.28.6 2993 + '@babel/helper-validator-identifier': 7.28.5 2994 + '@babel/traverse': 7.29.0 2995 transitivePeerDependencies: 2996 - supports-color 2997 ··· 3021 3022 '@babel/helper-validator-identifier@7.27.1': {} 3023 3024 + '@babel/helper-validator-identifier@7.28.5': {} 3025 + 3026 '@babel/helper-validator-option@7.27.1': {} 3027 3028 '@babel/helpers@7.28.4': ··· 3030 '@babel/template': 7.27.2 3031 '@babel/types': 7.28.4 3032 3033 + '@babel/helpers@7.28.6': 3034 + dependencies: 3035 + '@babel/template': 7.28.6 3036 + '@babel/types': 7.29.0 3037 + 3038 '@babel/parser@7.28.4': 3039 dependencies: 3040 '@babel/types': 7.28.4 3041 3042 + '@babel/parser@7.28.5': 3043 + dependencies: 3044 + '@babel/types': 7.28.5 3045 + 3046 + '@babel/parser@7.29.0': 3047 + dependencies: 3048 + '@babel/types': 7.29.0 3049 + 3050 '@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.28.4)': 3051 dependencies: 3052 '@babel/core': 7.28.4 ··· 3055 transitivePeerDependencies: 3056 - supports-color 3057 3058 + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)': 3059 dependencies: 3060 + '@babel/core': 7.29.0 3061 '@babel/helper-plugin-utils': 7.27.1 3062 3063 + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)': 3064 dependencies: 3065 + '@babel/core': 7.29.0 3066 '@babel/helper-plugin-utils': 7.27.1 3067 3068 '@babel/runtime@7.28.4': {} ··· 3073 '@babel/parser': 7.28.4 3074 '@babel/types': 7.28.4 3075 3076 + '@babel/template@7.28.6': 3077 + dependencies: 3078 + '@babel/code-frame': 7.29.0 3079 + '@babel/parser': 7.29.0 3080 + '@babel/types': 7.29.0 3081 + 3082 '@babel/traverse@7.28.4': 3083 dependencies: 3084 '@babel/code-frame': 7.27.1 ··· 3091 transitivePeerDependencies: 3092 - supports-color 3093 3094 + '@babel/traverse@7.29.0': 3095 + dependencies: 3096 + '@babel/code-frame': 7.29.0 3097 + '@babel/generator': 7.29.1 3098 + '@babel/helper-globals': 7.28.0 3099 + '@babel/parser': 7.29.0 3100 + '@babel/template': 7.28.6 3101 + '@babel/types': 7.29.0 3102 + debug: 4.4.3 3103 + transitivePeerDependencies: 3104 + - supports-color 3105 + 3106 '@babel/types@7.28.4': 3107 dependencies: 3108 '@babel/helper-string-parser': 7.27.1 3109 '@babel/helper-validator-identifier': 7.27.1 3110 3111 + '@babel/types@7.28.5': 3112 + dependencies: 3113 + '@babel/helper-string-parser': 7.27.1 3114 + '@babel/helper-validator-identifier': 7.28.5 3115 + 3116 + '@babel/types@7.29.0': 3117 + dependencies: 3118 + '@babel/helper-string-parser': 7.27.1 3119 + '@babel/helper-validator-identifier': 7.28.5 3120 + 3121 + '@changesets/apply-release-plan@7.0.14': 3122 dependencies: 3123 + '@changesets/config': 3.1.2 3124 '@changesets/get-version-range-type': 0.4.0 3125 '@changesets/git': 3.0.4 3126 '@changesets/should-skip-package': 0.1.2 ··· 3147 dependencies: 3148 '@changesets/types': 6.1.0 3149 3150 + '@changesets/cli@2.29.8(@types/node@24.0.4)': 3151 dependencies: 3152 + '@changesets/apply-release-plan': 7.0.14 3153 '@changesets/assemble-release-plan': 6.0.9 3154 '@changesets/changelog-git': 0.2.1 3155 + '@changesets/config': 3.1.2 3156 '@changesets/errors': 0.2.0 3157 '@changesets/get-dependents-graph': 2.1.3 3158 + '@changesets/get-release-plan': 4.0.14 3159 '@changesets/git': 3.0.4 3160 '@changesets/logger': 0.1.1 3161 '@changesets/pre': 2.0.2 3162 + '@changesets/read': 0.6.6 3163 '@changesets/should-skip-package': 0.1.2 3164 '@changesets/types': 6.1.0 3165 '@changesets/write': 0.4.0 ··· 3180 transitivePeerDependencies: 3181 - '@types/node' 3182 3183 + '@changesets/config@3.1.2': 3184 dependencies: 3185 '@changesets/errors': 0.2.0 3186 '@changesets/get-dependents-graph': 2.1.3 ··· 3201 picocolors: 1.1.1 3202 semver: 7.7.3 3203 3204 + '@changesets/get-release-plan@4.0.14': 3205 dependencies: 3206 '@changesets/assemble-release-plan': 6.0.9 3207 + '@changesets/config': 3.1.2 3208 '@changesets/pre': 2.0.2 3209 + '@changesets/read': 0.6.6 3210 '@changesets/types': 6.1.0 3211 '@manypkg/get-packages': 1.1.3 3212 ··· 3224 dependencies: 3225 picocolors: 1.1.1 3226 3227 + '@changesets/parse@0.4.2': 3228 dependencies: 3229 '@changesets/types': 6.1.0 3230 + js-yaml: 4.1.1 3231 3232 '@changesets/pre@2.0.2': 3233 dependencies: ··· 3236 '@manypkg/get-packages': 1.1.3 3237 fs-extra: 7.0.1 3238 3239 + '@changesets/read@0.6.6': 3240 dependencies: 3241 '@changesets/git': 3.0.4 3242 '@changesets/logger': 0.1.1 3243 + '@changesets/parse': 0.4.2 3244 '@changesets/types': 6.1.0 3245 fs-extra: 7.0.1 3246 p-filter: 2.1.0 ··· 3288 tslib: 2.8.1 3289 optional: true 3290 3291 + '@emnapi/core@1.7.1': 3292 + dependencies: 3293 + '@emnapi/wasi-threads': 1.1.0 3294 + tslib: 2.8.1 3295 + optional: true 3296 + 3297 '@emnapi/runtime@1.5.0': 3298 + dependencies: 3299 + tslib: 2.8.1 3300 + optional: true 3301 + 3302 + '@emnapi/runtime@1.7.1': 3303 dependencies: 3304 tslib: 2.8.1 3305 optional: true ··· 3492 dependencies: 3493 state-local: 1.0.7 3494 3495 + '@monaco-editor/react@4.7.0(monaco-editor@0.52.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': 3496 dependencies: 3497 '@monaco-editor/loader': 1.6.1 3498 monaco-editor: 0.52.0 3499 + react: 19.2.4 3500 + react-dom: 19.2.4(react@19.2.4) 3501 3502 '@napi-rs/wasm-runtime@0.2.12': 3503 dependencies: ··· 3513 '@tybys/wasm-util': 0.10.1 3514 optional: true 3515 3516 + '@napi-rs/wasm-runtime@1.1.0': 3517 + dependencies: 3518 + '@emnapi/core': 1.7.1 3519 + '@emnapi/runtime': 1.7.1 3520 + '@tybys/wasm-util': 0.10.1 3521 + optional: true 3522 + 3523 '@nodelib/fs.scandir@2.1.5': 3524 dependencies: 3525 '@nodelib/fs.stat': 2.0.5 ··· 3532 '@nodelib/fs.scandir': 2.1.5 3533 fastq: 1.19.1 3534 3535 '@oxc-project/runtime@0.75.0': {} 3536 3537 '@oxc-project/runtime@0.75.1': {} 3538 3539 + '@oxc-project/types@0.75.1': {} 3540 3541 + '@oxc-project/types@0.95.0': {} 3542 3543 + '@oxc-resolver/binding-android-arm-eabi@11.15.0': 3544 optional: true 3545 3546 + '@oxc-resolver/binding-android-arm64@11.15.0': 3547 optional: true 3548 3549 + '@oxc-resolver/binding-darwin-arm64@11.15.0': 3550 optional: true 3551 3552 + '@oxc-resolver/binding-darwin-x64@11.15.0': 3553 optional: true 3554 3555 + '@oxc-resolver/binding-freebsd-x64@11.15.0': 3556 optional: true 3557 3558 + '@oxc-resolver/binding-linux-arm-gnueabihf@11.15.0': 3559 optional: true 3560 3561 + '@oxc-resolver/binding-linux-arm-musleabihf@11.15.0': 3562 optional: true 3563 3564 + '@oxc-resolver/binding-linux-arm64-gnu@11.15.0': 3565 optional: true 3566 3567 + '@oxc-resolver/binding-linux-arm64-musl@11.15.0': 3568 optional: true 3569 3570 + '@oxc-resolver/binding-linux-ppc64-gnu@11.15.0': 3571 optional: true 3572 3573 + '@oxc-resolver/binding-linux-riscv64-gnu@11.15.0': 3574 optional: true 3575 3576 + '@oxc-resolver/binding-linux-riscv64-musl@11.15.0': 3577 optional: true 3578 3579 + '@oxc-resolver/binding-linux-s390x-gnu@11.15.0': 3580 + optional: true 3581 + 3582 + '@oxc-resolver/binding-linux-x64-gnu@11.15.0': 3583 optional: true 3584 3585 + '@oxc-resolver/binding-linux-x64-musl@11.15.0': 3586 optional: true 3587 3588 + '@oxc-resolver/binding-openharmony-arm64@11.15.0': 3589 optional: true 3590 3591 + '@oxc-resolver/binding-wasm32-wasi@11.15.0': 3592 dependencies: 3593 + '@napi-rs/wasm-runtime': 1.1.0 3594 optional: true 3595 3596 + '@oxc-resolver/binding-win32-arm64-msvc@11.15.0': 3597 optional: true 3598 3599 + '@oxc-resolver/binding-win32-ia32-msvc@11.15.0': 3600 optional: true 3601 3602 + '@oxc-resolver/binding-win32-x64-msvc@11.15.0': 3603 optional: true 3604 3605 '@prettier/sync@0.5.5(prettier@3.5.3)': ··· 3611 dependencies: 3612 quansync: 0.2.11 3613 3614 + '@rolldown/binding-android-arm64@1.0.0-beta.45': 3615 optional: true 3616 3617 '@rolldown/binding-darwin-arm64@1.0.0-beta.24': 3618 optional: true 3619 3620 + '@rolldown/binding-darwin-arm64@1.0.0-beta.45': 3621 optional: true 3622 3623 '@rolldown/binding-darwin-x64@1.0.0-beta.24': 3624 optional: true 3625 3626 + '@rolldown/binding-darwin-x64@1.0.0-beta.45': 3627 optional: true 3628 3629 '@rolldown/binding-freebsd-x64@1.0.0-beta.24': 3630 optional: true 3631 3632 + '@rolldown/binding-freebsd-x64@1.0.0-beta.45': 3633 optional: true 3634 3635 '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.24': 3636 optional: true 3637 3638 + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.45': 3639 optional: true 3640 3641 '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.24': 3642 optional: true 3643 3644 + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.45': 3645 optional: true 3646 3647 '@rolldown/binding-linux-arm64-musl@1.0.0-beta.24': 3648 optional: true 3649 3650 + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.45': 3651 optional: true 3652 3653 '@rolldown/binding-linux-x64-gnu@1.0.0-beta.24': 3654 optional: true 3655 3656 + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.45': 3657 optional: true 3658 3659 '@rolldown/binding-linux-x64-musl@1.0.0-beta.24': 3660 optional: true 3661 3662 + '@rolldown/binding-linux-x64-musl@1.0.0-beta.45': 3663 + optional: true 3664 + 3665 + '@rolldown/binding-openharmony-arm64@1.0.0-beta.45': 3666 optional: true 3667 3668 '@rolldown/binding-wasm32-wasi@1.0.0-beta.24': ··· 3670 '@napi-rs/wasm-runtime': 0.2.12 3671 optional: true 3672 3673 + '@rolldown/binding-wasm32-wasi@1.0.0-beta.45': 3674 + dependencies: 3675 + '@napi-rs/wasm-runtime': 1.0.7 3676 optional: true 3677 3678 '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.24': 3679 optional: true 3680 3681 + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.45': 3682 optional: true 3683 3684 '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.24': 3685 optional: true 3686 3687 + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.45': 3688 optional: true 3689 3690 '@rolldown/binding-win32-x64-msvc@1.0.0-beta.24': 3691 optional: true 3692 3693 + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.45': 3694 + optional: true 3695 3696 '@rolldown/pluginutils@1.0.0-beta.24': {} 3697 3698 + '@rolldown/pluginutils@1.0.0-beta.45': {} 3699 + 3700 + '@rolldown/pluginutils@1.0.0-rc.2': {} 3701 3702 '@standard-schema/spec@1.0.0': {} 3703 3704 + '@tailwindcss/node@4.1.18': 3705 + dependencies: 3706 + '@jridgewell/remapping': 2.3.5 3707 + enhanced-resolve: 5.18.3 3708 + jiti: 2.6.1 3709 + lightningcss: 1.30.2 3710 + magic-string: 0.30.21 3711 + source-map-js: 1.2.1 3712 + tailwindcss: 4.1.18 3713 + 3714 + '@tailwindcss/oxide-android-arm64@4.1.18': 3715 + optional: true 3716 + 3717 + '@tailwindcss/oxide-darwin-arm64@4.1.18': 3718 + optional: true 3719 + 3720 + '@tailwindcss/oxide-darwin-x64@4.1.18': 3721 + optional: true 3722 + 3723 + '@tailwindcss/oxide-freebsd-x64@4.1.18': 3724 + optional: true 3725 + 3726 + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': 3727 + optional: true 3728 + 3729 + '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': 3730 + optional: true 3731 + 3732 + '@tailwindcss/oxide-linux-arm64-musl@4.1.18': 3733 + optional: true 3734 + 3735 + '@tailwindcss/oxide-linux-x64-gnu@4.1.18': 3736 + optional: true 3737 + 3738 + '@tailwindcss/oxide-linux-x64-musl@4.1.18': 3739 + optional: true 3740 + 3741 + '@tailwindcss/oxide-wasm32-wasi@4.1.18': 3742 + optional: true 3743 + 3744 + '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': 3745 + optional: true 3746 + 3747 + '@tailwindcss/oxide-win32-x64-msvc@4.1.18': 3748 + optional: true 3749 + 3750 + '@tailwindcss/oxide@4.1.18': 3751 + optionalDependencies: 3752 + '@tailwindcss/oxide-android-arm64': 4.1.18 3753 + '@tailwindcss/oxide-darwin-arm64': 4.1.18 3754 + '@tailwindcss/oxide-darwin-x64': 4.1.18 3755 + '@tailwindcss/oxide-freebsd-x64': 4.1.18 3756 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.18 3757 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.18 3758 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.18 3759 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.18 3760 + '@tailwindcss/oxide-linux-x64-musl': 4.1.18 3761 + '@tailwindcss/oxide-wasm32-wasi': 4.1.18 3762 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.18 3763 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.18 3764 + 3765 + '@tailwindcss/vite@4.1.18(rolldown-vite@7.0.6(@types/node@24.0.4)(esbuild@0.25.10)(jiti@2.6.1))': 3766 + dependencies: 3767 + '@tailwindcss/node': 4.1.18 3768 + '@tailwindcss/oxide': 4.1.18 3769 + tailwindcss: 4.1.18 3770 + vite: rolldown-vite@7.0.6(@types/node@24.0.4)(esbuild@0.25.10)(jiti@2.6.1) 3771 + 3772 '@testing-library/dom@10.4.1': 3773 dependencies: 3774 '@babel/code-frame': 7.27.1 ··· 3789 picocolors: 1.1.1 3790 redent: 3.0.0 3791 3792 + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.13))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': 3793 dependencies: 3794 '@babel/runtime': 7.28.4 3795 '@testing-library/dom': 10.4.1 3796 + react: 19.2.4 3797 + react-dom: 19.2.4(react@19.2.4) 3798 optionalDependencies: 3799 + '@types/react': 19.2.13 3800 + '@types/react-dom': 19.2.3(@types/react@19.2.13) 3801 3802 '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)': 3803 dependencies: ··· 3812 3813 '@types/babel__core@7.20.5': 3814 dependencies: 3815 + '@babel/parser': 7.28.5 3816 + '@babel/types': 7.28.5 3817 '@types/babel__generator': 7.27.0 3818 '@types/babel__template': 7.4.4 3819 '@types/babel__traverse': 7.28.0 3820 3821 '@types/babel__generator@7.27.0': 3822 dependencies: 3823 + '@babel/types': 7.28.5 3824 3825 '@types/babel__template@7.4.4': 3826 dependencies: 3827 + '@babel/parser': 7.28.5 3828 + '@babel/types': 7.28.5 3829 3830 '@types/babel__traverse@7.28.0': 3831 dependencies: 3832 + '@babel/types': 7.28.5 3833 3834 '@types/chai@5.2.2': 3835 dependencies: ··· 3847 dependencies: 3848 undici-types: 7.8.0 3849 3850 + '@types/react-dom@19.2.3(@types/react@19.2.13)': 3851 dependencies: 3852 + '@types/react': 19.2.13 3853 3854 + '@types/react@19.2.13': 3855 dependencies: 3856 + csstype: 3.2.3 3857 3858 + '@typescript-eslint/eslint-plugin@8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3)': 3859 dependencies: 3860 '@eslint-community/regexpp': 4.12.1 3861 + '@typescript-eslint/parser': 8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3) 3862 '@typescript-eslint/scope-manager': 8.35.0 3863 + '@typescript-eslint/type-utils': 8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3) 3864 + '@typescript-eslint/utils': 8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3) 3865 '@typescript-eslint/visitor-keys': 8.35.0 3866 eslint: 9.29.0(jiti@2.6.1) 3867 graphemer: 1.4.0 3868 ignore: 7.0.5 3869 natural-compare: 1.4.0 3870 + ts-api-utils: 2.1.0(typescript@5.9.3) 3871 + typescript: 5.9.3 3872 transitivePeerDependencies: 3873 - supports-color 3874 3875 + '@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3)': 3876 dependencies: 3877 '@typescript-eslint/scope-manager': 8.35.0 3878 '@typescript-eslint/types': 8.35.0 3879 + '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.9.3) 3880 '@typescript-eslint/visitor-keys': 8.35.0 3881 debug: 4.4.3 3882 eslint: 9.29.0(jiti@2.6.1) 3883 + typescript: 5.9.3 3884 transitivePeerDependencies: 3885 - supports-color 3886 3887 + '@typescript-eslint/project-service@8.35.0(typescript@5.9.3)': 3888 dependencies: 3889 + '@typescript-eslint/tsconfig-utils': 8.35.0(typescript@5.9.3) 3890 '@typescript-eslint/types': 8.35.0 3891 debug: 4.4.3 3892 + typescript: 5.9.3 3893 transitivePeerDependencies: 3894 - supports-color 3895 ··· 3898 '@typescript-eslint/types': 8.35.0 3899 '@typescript-eslint/visitor-keys': 8.35.0 3900 3901 + '@typescript-eslint/tsconfig-utils@8.35.0(typescript@5.9.3)': 3902 dependencies: 3903 + typescript: 5.9.3 3904 3905 + '@typescript-eslint/type-utils@8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3)': 3906 dependencies: 3907 + '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.9.3) 3908 + '@typescript-eslint/utils': 8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3) 3909 debug: 4.4.3 3910 eslint: 9.29.0(jiti@2.6.1) 3911 + ts-api-utils: 2.1.0(typescript@5.9.3) 3912 + typescript: 5.9.3 3913 transitivePeerDependencies: 3914 - supports-color 3915 3916 '@typescript-eslint/types@8.35.0': {} 3917 3918 + '@typescript-eslint/typescript-estree@8.35.0(typescript@5.9.3)': 3919 dependencies: 3920 + '@typescript-eslint/project-service': 8.35.0(typescript@5.9.3) 3921 + '@typescript-eslint/tsconfig-utils': 8.35.0(typescript@5.9.3) 3922 '@typescript-eslint/types': 8.35.0 3923 '@typescript-eslint/visitor-keys': 8.35.0 3924 debug: 4.4.3 ··· 3926 is-glob: 4.0.3 3927 minimatch: 9.0.5 3928 semver: 7.7.3 3929 + ts-api-utils: 2.1.0(typescript@5.9.3) 3930 + typescript: 5.9.3 3931 transitivePeerDependencies: 3932 - supports-color 3933 3934 + '@typescript-eslint/utils@8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3)': 3935 dependencies: 3936 '@eslint-community/eslint-utils': 4.9.0(eslint@9.29.0(jiti@2.6.1)) 3937 '@typescript-eslint/scope-manager': 8.35.0 3938 '@typescript-eslint/types': 8.35.0 3939 + '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.9.3) 3940 eslint: 9.29.0(jiti@2.6.1) 3941 + typescript: 5.9.3 3942 transitivePeerDependencies: 3943 - supports-color 3944 ··· 3958 treeify: 1.1.0 3959 yargs: 16.2.0 3960 3961 + '@typescript/vfs@1.6.1(typescript@5.9.3)': 3962 dependencies: 3963 debug: 4.4.3 3964 + typescript: 5.9.3 3965 transitivePeerDependencies: 3966 - supports-color 3967 3968 + '@vitejs/plugin-react@5.1.3(rolldown-vite@7.0.6(@types/node@24.0.4)(esbuild@0.25.10)(jiti@2.6.1))': 3969 dependencies: 3970 + '@babel/core': 7.29.0 3971 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) 3972 + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) 3973 + '@rolldown/pluginutils': 1.0.0-rc.2 3974 '@types/babel__core': 7.20.5 3975 + react-refresh: 0.18.0 3976 vite: rolldown-vite@7.0.6(@types/node@24.0.4)(esbuild@0.25.10)(jiti@2.6.1) 3977 transitivePeerDependencies: 3978 - supports-color ··· 4065 4066 assertion-error@2.0.1: {} 4067 4068 + ast-kit@2.2.0: 4069 dependencies: 4070 + '@babel/parser': 7.28.5 4071 pathe: 2.0.3 4072 4073 asynckit@0.4.0: {} ··· 4084 dependencies: 4085 is-windows: 1.0.2 4086 4087 + birpc@2.8.0: {} 4088 4089 brace-expansion@1.1.12: 4090 dependencies: ··· 4174 '@asamuzakjp/css-color': 3.2.0 4175 rrweb-cssom: 0.8.0 4176 4177 + csstype@3.2.3: {} 4178 4179 data-urls@5.0.0: 4180 dependencies: ··· 4211 4212 dom-accessibility-api@0.6.3: {} 4213 4214 + dts-resolver@2.1.3(oxc-resolver@11.15.0): 4215 optionalDependencies: 4216 + oxc-resolver: 11.15.0 4217 4218 dunder-proto@1.0.1: 4219 dependencies: ··· 4225 4226 emoji-regex@8.0.0: {} 4227 4228 + empathic@2.0.0: {} 4229 + 4230 + enhanced-resolve@5.18.3: 4231 + dependencies: 4232 + graceful-fs: 4.2.11 4233 + tapable: 2.3.0 4234 4235 enquirer@2.4.1: 4236 dependencies: ··· 4485 dunder-proto: 1.0.1 4486 es-object-atoms: 1.1.1 4487 4488 + get-tsconfig@4.13.0: 4489 dependencies: 4490 resolve-pkg-maps: 1.0.0 4491 ··· 4614 dependencies: 4615 argparse: 2.0.1 4616 4617 + js-yaml@4.1.1: 4618 + dependencies: 4619 + argparse: 2.0.1 4620 + 4621 jsdom@25.0.1: 4622 dependencies: 4623 cssstyle: 4.6.0 ··· 4671 dependencies: 4672 json-buffer: 3.0.1 4673 4674 + knip@5.83.1(@types/node@24.0.4)(typescript@5.9.3): 4675 dependencies: 4676 '@nodelib/fs.walk': 1.2.8 4677 '@types/node': 24.0.4 4678 fast-glob: 3.3.3 4679 formatly: 0.3.0 4680 jiti: 2.6.1 4681 + js-yaml: 4.1.1 4682 minimist: 1.2.8 4683 + oxc-resolver: 11.15.0 4684 picocolors: 1.1.1 4685 picomatch: 4.0.3 4686 + smol-toml: 1.5.2 4687 + strip-json-comments: 5.0.3 4688 + typescript: 5.9.3 4689 zod: 4.1.12 4690 4691 levn@0.4.1: ··· 4768 dependencies: 4769 '@jridgewell/sourcemap-codec': 1.5.5 4770 4771 + magic-string@0.30.21: 4772 + dependencies: 4773 + '@jridgewell/sourcemap-codec': 1.5.5 4774 + 4775 make-synchronized@0.4.2: {} 4776 4777 math-intrinsics@1.1.0: {} ··· 4815 4816 node-releases@2.0.25: {} 4817 4818 + nuqs@2.8.8(react@19.2.4): 4819 dependencies: 4820 '@standard-schema/spec': 1.0.0 4821 + react: 19.2.4 4822 4823 nwsapi@2.2.22: {} 4824 + 4825 + obug@1.0.0(ms@2.1.3): 4826 + optionalDependencies: 4827 + ms: 2.1.3 4828 4829 optionator@0.9.4: 4830 dependencies: ··· 4837 4838 outdent@0.5.0: {} 4839 4840 + oxc-resolver@11.15.0: 4841 optionalDependencies: 4842 + '@oxc-resolver/binding-android-arm-eabi': 11.15.0 4843 + '@oxc-resolver/binding-android-arm64': 11.15.0 4844 + '@oxc-resolver/binding-darwin-arm64': 11.15.0 4845 + '@oxc-resolver/binding-darwin-x64': 11.15.0 4846 + '@oxc-resolver/binding-freebsd-x64': 11.15.0 4847 + '@oxc-resolver/binding-linux-arm-gnueabihf': 11.15.0 4848 + '@oxc-resolver/binding-linux-arm-musleabihf': 11.15.0 4849 + '@oxc-resolver/binding-linux-arm64-gnu': 11.15.0 4850 + '@oxc-resolver/binding-linux-arm64-musl': 11.15.0 4851 + '@oxc-resolver/binding-linux-ppc64-gnu': 11.15.0 4852 + '@oxc-resolver/binding-linux-riscv64-gnu': 11.15.0 4853 + '@oxc-resolver/binding-linux-riscv64-musl': 11.15.0 4854 + '@oxc-resolver/binding-linux-s390x-gnu': 11.15.0 4855 + '@oxc-resolver/binding-linux-x64-gnu': 11.15.0 4856 + '@oxc-resolver/binding-linux-x64-musl': 11.15.0 4857 + '@oxc-resolver/binding-openharmony-arm64': 11.15.0 4858 + '@oxc-resolver/binding-wasm32-wasi': 11.15.0 4859 + '@oxc-resolver/binding-win32-arm64-msvc': 11.15.0 4860 + '@oxc-resolver/binding-win32-ia32-msvc': 11.15.0 4861 + '@oxc-resolver/binding-win32-x64-msvc': 11.15.0 4862 4863 p-filter@2.1.0: 4864 dependencies: ··· 4940 4941 queue-microtask@1.2.3: {} 4942 4943 + react-dom@19.2.4(react@19.2.4): 4944 dependencies: 4945 + react: 19.2.4 4946 scheduler: 0.27.0 4947 4948 react-is@17.0.2: {} 4949 4950 + react-refresh@0.18.0: {} 4951 4952 + react@19.2.4: {} 4953 4954 read-yaml-file@1.1.0: 4955 dependencies: ··· 4981 4982 reusify@1.1.0: {} 4983 4984 + rolldown-plugin-dts@0.17.7(ms@2.1.3)(oxc-resolver@11.15.0)(rolldown@1.0.0-beta.45)(typescript@5.9.3): 4985 dependencies: 4986 + '@babel/generator': 7.28.5 4987 + '@babel/parser': 7.28.5 4988 + '@babel/types': 7.28.5 4989 + ast-kit: 2.2.0 4990 + birpc: 2.8.0 4991 + dts-resolver: 2.1.3(oxc-resolver@11.15.0) 4992 + get-tsconfig: 4.13.0 4993 + magic-string: 0.30.21 4994 + obug: 1.0.0(ms@2.1.3) 4995 + rolldown: 1.0.0-beta.45 4996 optionalDependencies: 4997 + typescript: 5.9.3 4998 transitivePeerDependencies: 4999 + - ms 5000 - oxc-resolver 5001 5002 rolldown-vite@7.0.6(@types/node@24.0.4)(esbuild@0.25.10)(jiti@2.6.1): 5003 dependencies: ··· 5014 fsevents: 2.3.3 5015 jiti: 2.6.1 5016 5017 rolldown@1.0.0-beta.24: 5018 dependencies: 5019 '@oxc-project/runtime': 0.75.1 ··· 5034 '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.24 5035 '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.24 5036 5037 + rolldown@1.0.0-beta.45: 5038 + dependencies: 5039 + '@oxc-project/types': 0.95.0 5040 + '@rolldown/pluginutils': 1.0.0-beta.45 5041 + optionalDependencies: 5042 + '@rolldown/binding-android-arm64': 1.0.0-beta.45 5043 + '@rolldown/binding-darwin-arm64': 1.0.0-beta.45 5044 + '@rolldown/binding-darwin-x64': 1.0.0-beta.45 5045 + '@rolldown/binding-freebsd-x64': 1.0.0-beta.45 5046 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.45 5047 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.45 5048 + '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.45 5049 + '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.45 5050 + '@rolldown/binding-linux-x64-musl': 1.0.0-beta.45 5051 + '@rolldown/binding-openharmony-arm64': 1.0.0-beta.45 5052 + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.45 5053 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.45 5054 + '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.45 5055 + '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.45 5056 + 5057 rrweb-cssom@0.7.1: {} 5058 5059 rrweb-cssom@0.8.0: {} ··· 5092 5093 slash@3.0.0: {} 5094 5095 + smol-toml@1.5.2: {} 5096 5097 source-map-js@1.2.1: {} 5098 ··· 5135 5136 strip-json-comments@3.1.1: {} 5137 5138 + strip-json-comments@5.0.3: {} 5139 5140 strip-literal@3.1.0: 5141 dependencies: ··· 5147 5148 symbol-tree@3.2.4: {} 5149 5150 + tailwindcss@4.1.18: {} 5151 + 5152 + tapable@2.3.0: {} 5153 + 5154 term-size@2.2.1: {} 5155 5156 through2@4.0.2: ··· 5161 5162 tinyexec@0.3.2: {} 5163 5164 + tinyexec@1.0.2: {} 5165 5166 tinyglobby@0.2.15: 5167 dependencies: ··· 5192 dependencies: 5193 punycode: 2.3.1 5194 5195 + tree-kill@1.2.2: {} 5196 + 5197 treeify@1.1.0: {} 5198 5199 + ts-api-utils@2.1.0(typescript@5.9.3): 5200 dependencies: 5201 + typescript: 5.9.3 5202 5203 + tsdown@0.15.12(ms@2.1.3)(oxc-resolver@11.15.0)(typescript@5.9.3): 5204 dependencies: 5205 ansis: 4.2.0 5206 cac: 6.7.14 5207 chokidar: 4.0.3 5208 debug: 4.4.3 5209 diff: 8.0.2 5210 + empathic: 2.0.0 5211 hookable: 5.5.3 5212 + rolldown: 1.0.0-beta.45 5213 + rolldown-plugin-dts: 0.17.7(ms@2.1.3)(oxc-resolver@11.15.0)(rolldown@1.0.0-beta.45)(typescript@5.9.3) 5214 semver: 7.7.3 5215 + tinyexec: 1.0.2 5216 tinyglobby: 0.2.15 5217 + tree-kill: 1.2.2 5218 + unconfig: 7.4.1 5219 optionalDependencies: 5220 + typescript: 5.9.3 5221 transitivePeerDependencies: 5222 + - '@ts-macro/tsc' 5223 - '@typescript/native-preview' 5224 + - ms 5225 - oxc-resolver 5226 - supports-color 5227 - vue-tsc 5228 5229 + tslib@2.8.1: {} 5230 5231 type-check@0.4.0: 5232 dependencies: 5233 prelude-ls: 1.2.1 5234 5235 + typescript-eslint@8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3): 5236 dependencies: 5237 + '@typescript-eslint/eslint-plugin': 8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3) 5238 + '@typescript-eslint/parser': 8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3) 5239 + '@typescript-eslint/utils': 8.35.0(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3) 5240 eslint: 9.29.0(jiti@2.6.1) 5241 + typescript: 5.9.3 5242 transitivePeerDependencies: 5243 - supports-color 5244 5245 + typescript@5.9.3: {} 5246 5247 uint8arrays@3.0.0: 5248 dependencies: 5249 multiformats: 9.9.0 5250 5251 + unconfig-core@7.4.1: 5252 + dependencies: 5253 + '@quansync/fs': 0.1.5 5254 + quansync: 0.2.11 5255 + 5256 + unconfig@7.4.1: 5257 dependencies: 5258 '@quansync/fs': 0.1.5 5259 defu: 6.1.4 5260 jiti: 2.6.1 5261 quansync: 0.2.11 5262 + unconfig-core: 7.4.1 5263 5264 undici-types@7.8.0: {} 5265 + 5266 + unicode-segmenter@0.14.0: {} 5267 5268 universalify@0.1.2: {} 5269