this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

formatted and made progress in firehose

ansxor ac835d3c 2dc1bf9c

+1201 -943
+61 -55
apps/api/index.ts
··· 1 - import { XRPCRouter, json } from '@atcute/xrpc-server'; 1 + import type { Blob } from "@atcute/lexicons"; 2 + import type { InferOutput } from "@atcute/lexicons/validations"; 3 + import { json, XRPCRouter } from "@atcute/xrpc-server"; 4 + import { cors } from "@atcute/xrpc-server/middlewares/cors"; 5 + import type { BlobRef } from "db/schema"; 6 + import * as dbschema from "db/schema"; 7 + import { inArray } from "drizzle-orm"; 2 8 import { drizzle } from "drizzle-orm/postgres-js"; 3 - import * as dbschema from "db/schema" 4 - import type { BlobRef } from "db/schema" 5 - import { cors } from '@atcute/xrpc-server/middlewares/cors'; 6 - 7 - import { CaAnsxorCatnipGetTracks, CaAnsxorCatnipTrack } from 'lexicon/atcute-lexicon'; 8 - import type { InferOutput } from '@atcute/lexicons/validations'; 9 - import type { Blob } from '@atcute/lexicons'; 10 - import { inArray } from 'drizzle-orm'; 9 + import { 10 + CaAnsxorCatnipGetTracks, 11 + type CaAnsxorCatnipTrack, 12 + } from "lexicon/atcute-lexicon"; 11 13 12 14 type TrackOutput = InferOutput<typeof CaAnsxorCatnipTrack.mainSchema>; 13 15 14 16 type DbTrack = Awaited<ReturnType<typeof db.query.tracks.findMany>>[number] & { 15 - trackArtists: { 16 - artist: { did: string | null; name: string | null }; 17 - position: number; 18 - }[]; 17 + trackArtists: { 18 + artist: { did: string | null; name: string | null }; 19 + position: number; 20 + }[]; 19 21 }; 20 22 21 23 function blobRefToBlob(ref: BlobRef): Blob { 22 - return { 23 - $type: 'blob', 24 - mimeType: ref.mimeType, 25 - ref: { $link: ref.ref.$link }, 26 - size: ref.size, 27 - }; 24 + return { 25 + $type: "blob", 26 + mimeType: ref.mimeType, 27 + ref: { $link: ref.ref.$link }, 28 + size: ref.size, 29 + }; 28 30 } 29 31 30 32 function dbTrackToLexicon(track: DbTrack) { 31 - return { 32 - $type: 'ca.ansxor.catnip.track', 33 - title: track.title, 34 - description: track.description ?? undefined, 35 - createdAt: track.createdAt.toISOString(), 36 - releaseDate: track.releaseDate?.toISOString(), 37 - durationMs: track.durationMs ?? undefined, 38 - artists: track.trackArtists 39 - .sort((a, b) => a.position - b.position) 40 - .map(({ artist }) => ({ 41 - did: artist.did?.match(/^did:.+:.+$/) ? artist.did : undefined, 42 - name: artist.name ?? undefined, 43 - } as InferOutput<CaAnsxorCatnipTrack.artistCreditSchema>)), 44 - tags: track.tags ?? undefined, 45 - language: track.language ?? undefined, 46 - license: track.license ?? undefined, 47 - lyrics: track.lyrics ?? undefined, 48 - albumArt: track.albumArt ? blobRefToBlob(track.albumArt) : undefined, 49 - audio: blobRefToBlob(track.audio!), 50 - externalUrl: track.externalUrl as TrackOutput["externalUrl"], 51 - } satisfies TrackOutput; 33 + return { 34 + $type: "ca.ansxor.catnip.track", 35 + title: track.title, 36 + description: track.description ?? undefined, 37 + createdAt: track.createdAt.toISOString(), 38 + releaseDate: track.releaseDate?.toISOString(), 39 + durationMs: track.durationMs ?? undefined, 40 + artists: track.trackArtists 41 + .sort((a, b) => a.position - b.position) 42 + .map( 43 + ({ artist }) => 44 + ({ 45 + did: artist.did?.match(/^did:.+:.+$/) ? artist.did : undefined, 46 + name: artist.name ?? undefined, 47 + }) as InferOutput<CaAnsxorCatnipTrack.artistCreditSchema>, 48 + ), 49 + tags: track.tags ?? undefined, 50 + language: track.language ?? undefined, 51 + license: track.license ?? undefined, 52 + lyrics: track.lyrics ?? undefined, 53 + albumArt: track.albumArt ? blobRefToBlob(track.albumArt) : undefined, 54 + audio: blobRefToBlob(track.audio!), 55 + externalUrl: track.externalUrl as TrackOutput["externalUrl"], 56 + } satisfies TrackOutput; 52 57 } 53 58 54 - const db = drizzle("postgresql://postgres:postgres@0.0.0.0:5432/catnip", { schema: dbschema }); 59 + const db = drizzle("postgresql://postgres:postgres@0.0.0.0:5432/catnip", { 60 + schema: dbschema, 61 + }); 55 62 56 63 const router = new XRPCRouter({ middlewares: [cors()] }); 57 64 58 65 router.addQuery(CaAnsxorCatnipGetTracks, { 59 - async handler({ params }) { 60 - const tracks = await db.query.tracks.findMany({ 61 - where: inArray(dbschema.tracks.uri, params.uris), 62 - with: { 63 - trackArtists: { 64 - with: { 65 - artist: true, 66 - }, 67 - }, 68 - }, 69 - }); 66 + async handler({ params }) { 67 + const tracks = await db.query.tracks.findMany({ 68 + where: inArray(dbschema.tracks.uri, params.uris), 69 + with: { 70 + trackArtists: { 71 + with: { 72 + artist: true, 73 + }, 74 + }, 75 + }, 76 + }); 70 77 71 - return json({ tracks: tracks.map(t => dbTrackToLexicon(t as DbTrack)) }); 72 - }, 78 + return json({ tracks: tracks.map((t) => dbTrackToLexicon(t as DbTrack)) }); 79 + }, 73 80 }); 74 - 75 81 76 82 export default router;
+18 -18
apps/api/package.json
··· 1 1 { 2 - "name": "api", 3 - "module": "index.ts", 4 - "type": "module", 5 - "private": true, 6 - "dependencies": { 7 - "@atcute/lexicons": "^1.2.9", 8 - "@atcute/xrpc-server": "^0.1.11", 9 - "drizzle-orm": "^0.45.1", 10 - "lexicon": "workspace:*", 11 - "postgres": "^3.4.8", 12 - "db": "workspace:*" 13 - }, 14 - "devDependencies": { 15 - "@types/bun": "latest" 16 - }, 17 - "peerDependencies": { 18 - "typescript": "^5" 19 - } 2 + "name": "api", 3 + "module": "index.ts", 4 + "type": "module", 5 + "private": true, 6 + "dependencies": { 7 + "@atcute/lexicons": "^1.2.9", 8 + "@atcute/xrpc-server": "^0.1.11", 9 + "drizzle-orm": "^0.45.1", 10 + "lexicon": "workspace:*", 11 + "postgres": "^3.4.8", 12 + "db": "workspace:*" 13 + }, 14 + "devDependencies": { 15 + "@types/bun": "latest" 16 + }, 17 + "peerDependencies": { 18 + "typescript": "^5" 19 + } 20 20 }
+57 -51
apps/api/scripts/create-track.ts
··· 1 - import { drizzle } from "drizzle-orm/postgres-js"; 2 - import * as dbschema from "db/schema"; 3 1 import type { BlobRef } from "db/schema"; 2 + import * as dbschema from "db/schema"; 3 + import { drizzle } from "drizzle-orm/postgres-js"; 4 4 5 - const db = drizzle("postgresql://postgres:postgres@0.0.0.0:5432/catnip", { schema: dbschema }); 5 + const db = drizzle("postgresql://postgres:postgres@0.0.0.0:5432/catnip", { 6 + schema: dbschema, 7 + }); 6 8 7 9 const COLLECTION = "ca.ansxor.catnip.track"; 8 10 9 11 const SAMPLE_DIDS = [ 10 - "did:plc:ragtjsm2j2vknwkz3zp4oxrd", 11 - "did:plc:7iza6de2dwap2sbkpav7c6c6", 12 - "did:plc:ewvi7nxzyoun6zhxrhs64oiz", 13 - "did:plc:ia76kvnndjutgedggx2ibrem", 14 - "did:plc:z72i7hdynmk6r22z27h6tvur", 12 + "did:plc:ragtjsm2j2vknwkz3zp4oxrd", 13 + "did:plc:7iza6de2dwap2sbkpav7c6c6", 14 + "did:plc:ewvi7nxzyoun6zhxrhs64oiz", 15 + "did:plc:ia76kvnndjutgedggx2ibrem", 16 + "did:plc:z72i7hdynmk6r22z27h6tvur", 15 17 ]; 16 18 17 19 const SAMPLE_TITLES = [ 18 - "Midnight Echoes", 19 - "Solar Flare", 20 - "Velvet Haze", 21 - "Neon Dreams", 22 - "Crystal Waves", 23 - "Amber Glow", 24 - "Static Bloom", 25 - "Phantom Drift", 26 - "Cobalt Rain", 27 - "Hollow Sun", 20 + "Midnight Echoes", 21 + "Solar Flare", 22 + "Velvet Haze", 23 + "Neon Dreams", 24 + "Crystal Waves", 25 + "Amber Glow", 26 + "Static Bloom", 27 + "Phantom Drift", 28 + "Cobalt Rain", 29 + "Hollow Sun", 28 30 ]; 29 31 30 32 function generateTid(): string { 31 - const now = BigInt(Date.now()) * 1000n; 32 - const clockId = BigInt(Math.floor(Math.random() * 1024)); 33 - const tid = (now << 10n) | clockId; 34 - const chars = "234567abcdefghijklmnopqrstuvwxyz"; 35 - let result = ""; 36 - let remaining = tid; 37 - for (let i = 0; i < 13; i++) { 38 - result = chars[Number(remaining & 31n)] + result; 39 - remaining >>= 5n; 40 - } 41 - return result; 33 + const now = BigInt(Date.now()) * 1000n; 34 + const clockId = BigInt(Math.floor(Math.random() * 1024)); 35 + const tid = (now << 10n) | clockId; 36 + const chars = "234567abcdefghijklmnopqrstuvwxyz"; 37 + let result = ""; 38 + let remaining = tid; 39 + for (let i = 0; i < 13; i++) { 40 + result = chars[Number(remaining & 31n)] + result; 41 + remaining >>= 5n; 42 + } 43 + return result; 42 44 } 43 45 44 46 function randomCid(): string { 45 - const bytes = crypto.getRandomValues(new Uint8Array(32)); 46 - return "bafyrei" + Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join(""); 47 + const bytes = crypto.getRandomValues(new Uint8Array(32)); 48 + return ( 49 + "bafyrei" + 50 + Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("") 51 + ); 47 52 } 48 53 49 54 const did = SAMPLE_DIDS[Math.floor(Math.random() * SAMPLE_DIDS.length)]; ··· 53 58 const title = SAMPLE_TITLES[Math.floor(Math.random() * SAMPLE_TITLES.length)]; 54 59 55 60 const audioBlobRef: BlobRef = { 56 - $type: "blob", 57 - ref: { $link: randomCid() }, 58 - mimeType: "audio/ogg", 59 - size: Math.floor(Math.random() * 10_000_000) + 1_000_000, 61 + $type: "blob", 62 + ref: { $link: randomCid() }, 63 + mimeType: "audio/ogg", 64 + size: Math.floor(Math.random() * 10_000_000) + 1_000_000, 60 65 }; 61 66 62 67 const now = new Date(); 63 68 64 69 await db.insert(dbschema.tracks).values({ 65 - uri, 66 - did, 67 - rkey, 68 - cid, 69 - title, 70 - createdAt: now, 71 - audio: audioBlobRef, 70 + uri, 71 + did, 72 + rkey, 73 + cid, 74 + title, 75 + createdAt: now, 76 + audio: audioBlobRef, 72 77 }); 73 78 74 79 // Create an artist entry for the DID and link it 75 80 const [artist] = await db 76 - .insert(dbschema.artists) 77 - .values({ did }) 78 - .onConflictDoNothing() 79 - .returning(); 81 + .insert(dbschema.artists) 82 + .values({ did }) 83 + .onConflictDoNothing() 84 + .returning(); 80 85 81 86 const artistId = 82 - artist?.id ?? 83 - (await db.query.artists.findFirst({ where: (a, { eq }) => eq(a.did, did) }))!.id; 87 + artist?.id ?? 88 + (await db.query.artists.findFirst({ where: (a, { eq }) => eq(a.did, did) }))! 89 + .id; 84 90 85 91 await db.insert(dbschema.trackArtists).values({ 86 - trackUri: uri, 87 - artistId, 88 - position: 0, 92 + trackUri: uri, 93 + artistId, 94 + position: 0, 89 95 }); 90 96 91 97 console.log(uri);
+20
apps/firehose/bun.lock
··· 1 + { 2 + "lockfileVersion": 1, 3 + "configVersion": 1, 4 + "workspaces": { 5 + "": { 6 + "devDependencies": { 7 + "@types/bun": "latest", 8 + }, 9 + }, 10 + }, 11 + "packages": { 12 + "@types/bun": ["@types/bun@1.3.10", "", { "dependencies": { "bun-types": "1.3.10" } }, "sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ=="], 13 + 14 + "@types/node": ["@types/node@25.3.5", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-oX8xrhvpiyRCQkG1MFchB09f+cXftgIXb3a7UUa4Y3wpmZPw5tyZGTLWhlESOLq1Rq6oDlc8npVU2/9xiCuXMA=="], 15 + 16 + "bun-types": ["bun-types@1.3.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-tcpfCCl6XWo6nCVnpcVrxQ+9AYN1iqMIzgrSKYMB/fjLtV2eyAVEg7AxQJuCq/26R6HpKWykQXuSOq/21RYcbg=="], 17 + 18 + "undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="], 19 + } 20 + }
+21
apps/firehose/index.ts
··· 1 + import { JetstreamSubscription } from "@atcute/jetstream"; 2 + import { is } from '@atcute/lexicons'; 3 + 4 + const subscription = new JetstreamSubscription({ 5 + url: "wss://jetstream2.us-east.bsky.network", 6 + wantedCollections: ["ca.ansxor.catnip.track"] 7 + }); 8 + 9 + for await (const event of subscription) { 10 + if (event.kind !== "commit") { 11 + continue; 12 + } 13 + 14 + const commit = event.commit; 15 + if (commit.operation !== "create") { 16 + continue; 17 + } 18 + const { collection, operation, rkey, rev } = event.commit; 19 + 20 + if (!is) 21 + }
+16
apps/firehose/package.json
··· 1 + { 2 + "name": "firehose", 3 + "private": true, 4 + "version": "1.0.0", 5 + "type": "module", 6 + "devDependencies": { 7 + "@types/bun": "latest" 8 + }, 9 + "dependencies": { 10 + "@atcute/atproto": "^3.1.10", 11 + "@atcute/firehose": "^0.1.0", 12 + "@atcute/jetstream": "^1.1.2", 13 + "@atcute/lexicons": "^1.2.9", 14 + "lexicon": "workspace:*" 15 + } 16 + }
+29
apps/firehose/tsconfig.json
··· 1 + { 2 + "compilerOptions": { 3 + // Environment setup & latest features 4 + "lib": ["ESNext"], 5 + "target": "ESNext", 6 + "module": "Preserve", 7 + "moduleDetection": "force", 8 + "jsx": "react-jsx", 9 + "allowJs": true, 10 + 11 + // Bundler mode 12 + "moduleResolution": "bundler", 13 + "allowImportingTsExtensions": true, 14 + "verbatimModuleSyntax": true, 15 + "noEmit": true, 16 + 17 + // Best practices 18 + "strict": true, 19 + "skipLibCheck": true, 20 + "noFallthroughCasesInSwitch": true, 21 + "noUncheckedIndexedAccess": true, 22 + "noImplicitOverride": true, 23 + 24 + // Some stricter flags (disabled by default) 25 + "noUnusedLocals": false, 26 + "noUnusedParameters": false, 27 + "noPropertyAccessFromIndexSignature": false 28 + } 29 + }
+21 -21
apps/frontend/eslint.config.js
··· 1 - import js from '@eslint/js' 2 - import globals from 'globals' 3 - import reactHooks from 'eslint-plugin-react-hooks' 4 - import reactRefresh from 'eslint-plugin-react-refresh' 5 - import tseslint from 'typescript-eslint' 6 - import { defineConfig, globalIgnores } from 'eslint/config' 1 + import js from "@eslint/js"; 2 + import { defineConfig, globalIgnores } from "eslint/config"; 3 + import reactHooks from "eslint-plugin-react-hooks"; 4 + import reactRefresh from "eslint-plugin-react-refresh"; 5 + import globals from "globals"; 6 + import tseslint from "typescript-eslint"; 7 7 8 8 export default defineConfig([ 9 - globalIgnores(['dist']), 10 - { 11 - files: ['**/*.{ts,tsx}'], 12 - extends: [ 13 - js.configs.recommended, 14 - tseslint.configs.recommended, 15 - reactHooks.configs.flat.recommended, 16 - reactRefresh.configs.vite, 17 - ], 18 - languageOptions: { 19 - ecmaVersion: 2020, 20 - globals: globals.browser, 21 - }, 22 - }, 23 - ]) 9 + globalIgnores(["dist"]), 10 + { 11 + files: ["**/*.{ts,tsx}"], 12 + extends: [ 13 + js.configs.recommended, 14 + tseslint.configs.recommended, 15 + reactHooks.configs.flat.recommended, 16 + reactRefresh.configs.vite, 17 + ], 18 + languageOptions: { 19 + ecmaVersion: 2020, 20 + globals: globals.browser, 21 + }, 22 + }, 23 + ]);
+38 -37
apps/frontend/package.json
··· 1 1 { 2 - "name": "frontend", 3 - "private": true, 4 - "version": "0.0.0", 5 - "type": "module", 6 - "scripts": { 7 - "dev": "vite", 8 - "build": "tsc -b && vite build", 9 - "lint": "eslint .", 10 - "preview": "vite preview" 11 - }, 12 - "dependencies": { 13 - "@atcute/client": "^4.2.1", 14 - "@tanstack/react-router": "^1.166.3", 15 - "@tanstack/react-router-devtools": "^1.166.3", 16 - "@vitejs/plugin-react": "^5.1.4", 17 - "lexicon": "workspace:*", 18 - "react": "^19.2.0", 19 - "react-dom": "^19.2.0" 20 - }, 21 - "devDependencies": { 22 - "@eslint/js": "^9.39.1", 23 - "@tanstack/router-plugin": "^1.166.3", 24 - "@types/node": "^24.10.1", 25 - "@types/react": "^19.2.7", 26 - "@types/react-dom": "^19.2.3", 27 - "@vitejs/plugin-react-swc": "^4.2.2", 28 - "eslint": "^9.39.1", 29 - "eslint-plugin-react-hooks": "^7.0.1", 30 - "eslint-plugin-react-refresh": "^0.4.24", 31 - "globals": "^16.5.0", 32 - "typescript": "~5.9.3", 33 - "typescript-eslint": "^8.48.0", 34 - "vite": "^8.0.0-beta.13" 35 - }, 36 - "overrides": { 37 - "vite": "^8.0.0-beta.13" 38 - } 2 + "name": "frontend", 3 + "private": true, 4 + "version": "0.0.0", 5 + "type": "module", 6 + "scripts": { 7 + "dev": "vite", 8 + "build": "tsc -b && vite build", 9 + "lint": "eslint .", 10 + "preview": "vite preview" 11 + }, 12 + "dependencies": { 13 + "@atcute/client": "^4.2.1", 14 + "@tanstack/react-router": "^1.166.3", 15 + "@tanstack/react-router-devtools": "^1.166.3", 16 + "@vitejs/plugin-react": "^5.1.4", 17 + "lexicon": "workspace:*", 18 + "react": "^19.2.0", 19 + "react-dom": "^19.2.0" 20 + }, 21 + "devDependencies": { 22 + "@eslint/js": "^9.39.1", 23 + "@tanstack/router-plugin": "^1.166.3", 24 + "@types/node": "^24.10.1", 25 + "@types/react": "^19.2.7", 26 + "@types/react-dom": "^19.2.3", 27 + "@vitejs/plugin-react-swc": "^4.2.2", 28 + "babel-plugin-react-compiler": "^1.0.0", 29 + "eslint": "^9.39.1", 30 + "eslint-plugin-react-hooks": "^7.0.1", 31 + "eslint-plugin-react-refresh": "^0.4.24", 32 + "globals": "^16.5.0", 33 + "typescript": "~5.9.3", 34 + "typescript-eslint": "^8.48.0", 35 + "vite": "^8.0.0-beta.13" 36 + }, 37 + "overrides": { 38 + "vite": "^8.0.0-beta.13" 39 + } 39 40 }
+21 -21
apps/frontend/src/App.css
··· 1 1 #root { 2 - max-width: 1280px; 3 - margin: 0 auto; 4 - padding: 2rem; 5 - text-align: center; 2 + max-width: 1280px; 3 + margin: 0 auto; 4 + padding: 2rem; 5 + text-align: center; 6 6 } 7 7 8 8 .logo { 9 - height: 6em; 10 - padding: 1.5em; 11 - will-change: filter; 12 - transition: filter 300ms; 9 + height: 6em; 10 + padding: 1.5em; 11 + will-change: filter; 12 + transition: filter 300ms; 13 13 } 14 14 .logo:hover { 15 - filter: drop-shadow(0 0 2em #646cffaa); 15 + filter: drop-shadow(0 0 2em #646cffaa); 16 16 } 17 17 .logo.react:hover { 18 - filter: drop-shadow(0 0 2em #61dafbaa); 18 + filter: drop-shadow(0 0 2em #61dafbaa); 19 19 } 20 20 21 21 @keyframes logo-spin { 22 - from { 23 - transform: rotate(0deg); 24 - } 25 - to { 26 - transform: rotate(360deg); 27 - } 22 + from { 23 + transform: rotate(0deg); 24 + } 25 + to { 26 + transform: rotate(360deg); 27 + } 28 28 } 29 29 30 30 @media (prefers-reduced-motion: no-preference) { 31 - a:nth-of-type(2) .logo { 32 - animation: logo-spin infinite 20s linear; 33 - } 31 + a:nth-of-type(2) .logo { 32 + animation: logo-spin infinite 20s linear; 33 + } 34 34 } 35 35 36 36 .card { 37 - padding: 2em; 37 + padding: 2em; 38 38 } 39 39 40 40 .read-the-docs { 41 - color: #888; 41 + color: #888; 42 42 }
+30 -30
apps/frontend/src/App.tsx
··· 1 - import { useState } from 'react' 2 - import reactLogo from './assets/react.svg' 3 - import viteLogo from '/vite.svg' 4 - import './App.css' 1 + import { useState } from "react"; 2 + import viteLogo from "/vite.svg"; 3 + import reactLogo from "./assets/react.svg"; 4 + import "./App.css"; 5 5 6 6 function App() { 7 - const [count, setCount] = useState(0) 7 + const [count, setCount] = useState(0); 8 8 9 - return ( 10 - <> 11 - <div> 12 - <a href="https://vite.dev" target="_blank"> 13 - <img src={viteLogo} className="logo" alt="Vite logo" /> 14 - </a> 15 - <a href="https://react.dev" target="_blank"> 16 - <img src={reactLogo} className="logo react" alt="React logo" /> 17 - </a> 18 - </div> 19 - <h1>Vite + React</h1> 20 - <div className="card"> 21 - <button onClick={() => setCount((count) => count + 1)}> 22 - count is {count} 23 - </button> 24 - <p> 25 - Edit <code>src/App.tsx</code> and save to test HMR 26 - </p> 27 - </div> 28 - <p className="read-the-docs"> 29 - Click on the Vite and React logos to learn more 30 - </p> 31 - </> 32 - ) 9 + return ( 10 + <> 11 + <div> 12 + <a href="https://vite.dev" target="_blank" rel="noopener"> 13 + <img src={viteLogo} className="logo" alt="Vite logo" /> 14 + </a> 15 + <a href="https://react.dev" target="_blank" rel="noopener"> 16 + <img src={reactLogo} className="logo react" alt="React logo" /> 17 + </a> 18 + </div> 19 + <h1>Vite + React</h1> 20 + <div className="card"> 21 + <button onClick={() => setCount((count) => count + 1)}> 22 + count is {count} 23 + </button> 24 + <p> 25 + Edit <code>src/App.tsx</code> and save to test HMR 26 + </p> 27 + </div> 28 + <p className="read-the-docs"> 29 + Click on the Vite and React logos to learn more 30 + </p> 31 + </> 32 + ); 33 33 } 34 34 35 - export default App 35 + export default App;
+42 -42
apps/frontend/src/index.css
··· 1 1 :root { 2 - font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; 3 - line-height: 1.5; 4 - font-weight: 400; 2 + font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; 3 + line-height: 1.5; 4 + font-weight: 400; 5 5 6 - color-scheme: light dark; 7 - color: rgba(255, 255, 255, 0.87); 8 - background-color: #242424; 6 + color-scheme: light dark; 7 + color: rgba(255, 255, 255, 0.87); 8 + background-color: #242424; 9 9 10 - font-synthesis: none; 11 - text-rendering: optimizeLegibility; 12 - -webkit-font-smoothing: antialiased; 13 - -moz-osx-font-smoothing: grayscale; 10 + font-synthesis: none; 11 + text-rendering: optimizeLegibility; 12 + -webkit-font-smoothing: antialiased; 13 + -moz-osx-font-smoothing: grayscale; 14 14 } 15 15 16 16 a { 17 - font-weight: 500; 18 - color: #646cff; 19 - text-decoration: inherit; 17 + font-weight: 500; 18 + color: #646cff; 19 + text-decoration: inherit; 20 20 } 21 21 a:hover { 22 - color: #535bf2; 22 + color: #535bf2; 23 23 } 24 24 25 25 body { 26 - margin: 0; 27 - display: flex; 28 - place-items: center; 29 - min-width: 320px; 30 - min-height: 100vh; 26 + margin: 0; 27 + display: flex; 28 + place-items: center; 29 + min-width: 320px; 30 + min-height: 100vh; 31 31 } 32 32 33 33 h1 { 34 - font-size: 3.2em; 35 - line-height: 1.1; 34 + font-size: 3.2em; 35 + line-height: 1.1; 36 36 } 37 37 38 38 button { 39 - border-radius: 8px; 40 - border: 1px solid transparent; 41 - padding: 0.6em 1.2em; 42 - font-size: 1em; 43 - font-weight: 500; 44 - font-family: inherit; 45 - background-color: #1a1a1a; 46 - cursor: pointer; 47 - transition: border-color 0.25s; 39 + border-radius: 8px; 40 + border: 1px solid transparent; 41 + padding: 0.6em 1.2em; 42 + font-size: 1em; 43 + font-weight: 500; 44 + font-family: inherit; 45 + background-color: #1a1a1a; 46 + cursor: pointer; 47 + transition: border-color 0.25s; 48 48 } 49 49 button:hover { 50 - border-color: #646cff; 50 + border-color: #646cff; 51 51 } 52 52 button:focus, 53 53 button:focus-visible { 54 - outline: 4px auto -webkit-focus-ring-color; 54 + outline: 4px auto -webkit-focus-ring-color; 55 55 } 56 56 57 57 @media (prefers-color-scheme: light) { 58 - :root { 59 - color: #213547; 60 - background-color: #ffffff; 61 - } 62 - a:hover { 63 - color: #747bff; 64 - } 65 - button { 66 - background-color: #f9f9f9; 67 - } 58 + :root { 59 + color: #213547; 60 + background-color: #ffffff; 61 + } 62 + a:hover { 63 + color: #747bff; 64 + } 65 + button { 66 + background-color: #f9f9f9; 67 + } 68 68 }
+16 -16
apps/frontend/src/main.tsx
··· 1 - import { StrictMode } from 'react' 2 - import ReactDOM from 'react-dom/client' 3 - import { RouterProvider, createRouter } from '@tanstack/react-router' 1 + import { createRouter, RouterProvider } from "@tanstack/react-router"; 2 + import { StrictMode } from "react"; 3 + import ReactDOM from "react-dom/client"; 4 4 5 5 // Import the generated route tree 6 - import { routeTree } from './routeTree.gen' 6 + import { routeTree } from "./routeTree.gen"; 7 7 8 8 // Create a new router instance 9 - const router = createRouter({ routeTree }) 9 + const router = createRouter({ routeTree }); 10 10 11 11 // Register the router instance for type safety 12 - declare module '@tanstack/react-router' { 13 - interface Register { 14 - router: typeof router 15 - } 12 + declare module "@tanstack/react-router" { 13 + interface Register { 14 + router: typeof router; 15 + } 16 16 } 17 17 18 18 // Render the app 19 - const rootElement = document.getElementById('root')! 19 + const rootElement = document.getElementById("root")!; 20 20 if (!rootElement.innerHTML) { 21 - const root = ReactDOM.createRoot(rootElement) 22 - root.render( 23 - <StrictMode> 24 - <RouterProvider router={router} /> 25 - </StrictMode>, 26 - ) 21 + const root = ReactDOM.createRoot(rootElement); 22 + root.render( 23 + <StrictMode> 24 + <RouterProvider router={router} /> 25 + </StrictMode>, 26 + ); 27 27 }
+17 -17
apps/frontend/src/routes/__root.tsx
··· 1 - import { createRootRoute, Link, Outlet } from '@tanstack/react-router' 2 - import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' 1 + import { createRootRoute, Link, Outlet } from "@tanstack/react-router"; 2 + import { TanStackRouterDevtools } from "@tanstack/react-router-devtools"; 3 3 4 4 const RootLayout = () => ( 5 - <> 6 - <div className="p-2 flex gap-2"> 7 - <Link to="/" className="[&.active]:font-bold"> 8 - Home 9 - </Link>{' '} 10 - <Link to="/about" className="[&.active]:font-bold"> 11 - About 12 - </Link> 13 - </div> 14 - <hr /> 15 - <Outlet /> 16 - <TanStackRouterDevtools /> 17 - </> 18 - ) 5 + <> 6 + <div className="p-2 flex gap-2"> 7 + <Link to="/" className="[&.active]:font-bold"> 8 + Home 9 + </Link>{" "} 10 + <Link to="/about" className="[&.active]:font-bold"> 11 + About 12 + </Link> 13 + </div> 14 + <hr /> 15 + <Outlet /> 16 + <TanStackRouterDevtools /> 17 + </> 18 + ); 19 19 20 - export const Route = createRootRoute({ component: RootLayout }) 20 + export const Route = createRootRoute({ component: RootLayout });
+5 -5
apps/frontend/src/routes/about.tsx
··· 1 - import { createFileRoute } from '@tanstack/react-router' 1 + import { createFileRoute } from "@tanstack/react-router"; 2 2 3 - export const Route = createFileRoute('/about')({ 4 - component: About, 5 - }) 3 + export const Route = createFileRoute("/about")({ 4 + component: About, 5 + }); 6 6 7 7 function About() { 8 - return <div className="p-2">Hello from About!</div> 8 + return <div className="p-2">Hello from About!</div>; 9 9 }
+33 -29
apps/frontend/src/routes/index.tsx
··· 1 - import { Client, simpleFetchHandler } from '@atcute/client' 2 - import { createFileRoute } from '@tanstack/react-router' 3 - import type {} from "lexicon/atcute-lexicon" 1 + import { Client, simpleFetchHandler } from "@atcute/client"; 2 + import { createFileRoute } from "@tanstack/react-router"; 3 + import type {} from "lexicon/atcute-lexicon"; 4 4 5 - const rpc = new Client({ handler: simpleFetchHandler({ service: 'http://localhost:3000' })}) 5 + const rpc = new Client({ 6 + handler: simpleFetchHandler({ service: "http://localhost:3000" }), 7 + }); 6 8 7 - export const Route = createFileRoute('/')({ 8 - component: Index, 9 - loader: async () => { 10 - const result = await rpc.get("ca.ansxor.catnip.getTracks", { 11 - params: { 12 - uris: ["at://did:plc:z72i7hdynmk6r22z27h6tvur/ca.ansxor.catnip.track/3mglauwtrscyn"] 13 - } 14 - }) 15 - if (!result.ok) { 16 - throw new Error("noooo") 17 - } 18 - return result.data; 19 - } 20 - }) 9 + export const Route = createFileRoute("/")({ 10 + component: Index, 11 + loader: async () => { 12 + const result = await rpc.get("ca.ansxor.catnip.getTracks", { 13 + params: { 14 + uris: [ 15 + "at://did:plc:z72i7hdynmk6r22z27h6tvur/ca.ansxor.catnip.track/3mglauwtrscyn", 16 + ], 17 + }, 18 + }); 19 + if (!result.ok) { 20 + throw new Error("noooo"); 21 + } 22 + return result.data; 23 + }, 24 + }); 21 25 22 26 function Index() { 23 - const data = Route.useLoaderData(); 27 + const data = Route.useLoaderData(); 24 28 25 - return ( 26 - <div className="p-2"> 27 - <ul> 28 - {data.tracks.map(track => <li> 29 - {track.title} 30 - </li>)} 31 - </ul> 32 - <h3>Welcome Home!</h3> 33 - </div> 34 - ) 29 + return ( 30 + <div className="p-2"> 31 + <ul> 32 + {data.tracks.map((track) => ( 33 + <li>{track.title}</li> 34 + ))} 35 + </ul> 36 + <h3>Welcome Home!</h3> 37 + </div> 38 + ); 35 39 }
+24 -24
apps/frontend/tsconfig.app.json
··· 1 1 { 2 - "compilerOptions": { 3 - "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 4 - "target": "ES2022", 5 - "useDefineForClassFields": true, 6 - "lib": ["ES2022", "DOM", "DOM.Iterable"], 7 - "module": "ESNext", 8 - "types": ["vite/client"], 9 - "skipLibCheck": true, 2 + "compilerOptions": { 3 + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 4 + "target": "ES2022", 5 + "useDefineForClassFields": true, 6 + "lib": ["ES2022", "DOM", "DOM.Iterable"], 7 + "module": "ESNext", 8 + "types": ["vite/client"], 9 + "skipLibCheck": true, 10 10 11 - /* Bundler mode */ 12 - "moduleResolution": "bundler", 13 - "allowImportingTsExtensions": true, 14 - "verbatimModuleSyntax": true, 15 - "moduleDetection": "force", 16 - "noEmit": true, 17 - "jsx": "react-jsx", 11 + /* Bundler mode */ 12 + "moduleResolution": "bundler", 13 + "allowImportingTsExtensions": true, 14 + "verbatimModuleSyntax": true, 15 + "moduleDetection": "force", 16 + "noEmit": true, 17 + "jsx": "react-jsx", 18 18 19 - /* Linting */ 20 - "strict": true, 21 - "noUnusedLocals": true, 22 - "noUnusedParameters": true, 23 - "erasableSyntaxOnly": true, 24 - "noFallthroughCasesInSwitch": true, 25 - "noUncheckedSideEffectImports": true 26 - }, 27 - "include": ["src"] 19 + /* Linting */ 20 + "strict": true, 21 + "noUnusedLocals": true, 22 + "noUnusedParameters": true, 23 + "erasableSyntaxOnly": true, 24 + "noFallthroughCasesInSwitch": true, 25 + "noUncheckedSideEffectImports": true 26 + }, 27 + "include": ["src"] 28 28 }
+8 -8
apps/frontend/tsconfig.json
··· 1 1 { 2 - "files": [], 3 - "references": [ 4 - { "path": "./tsconfig.app.json" }, 5 - { "path": "./tsconfig.node.json" } 6 - ], 7 - "compilerOptions": { 8 - "types": ["lexicon/atcute-lexicon"] 9 - } 2 + "files": [], 3 + "references": [ 4 + { "path": "./tsconfig.app.json" }, 5 + { "path": "./tsconfig.node.json" } 6 + ], 7 + "compilerOptions": { 8 + "types": ["lexicon/atcute-lexicon"] 9 + } 10 10 }
+22 -22
apps/frontend/tsconfig.node.json
··· 1 1 { 2 - "compilerOptions": { 3 - "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 4 - "target": "ES2023", 5 - "lib": ["ES2023"], 6 - "module": "ESNext", 7 - "types": ["node"], 8 - "skipLibCheck": true, 2 + "compilerOptions": { 3 + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 4 + "target": "ES2023", 5 + "lib": ["ES2023"], 6 + "module": "ESNext", 7 + "types": ["node"], 8 + "skipLibCheck": true, 9 9 10 - /* Bundler mode */ 11 - "moduleResolution": "bundler", 12 - "allowImportingTsExtensions": true, 13 - "verbatimModuleSyntax": true, 14 - "moduleDetection": "force", 15 - "noEmit": true, 10 + /* Bundler mode */ 11 + "moduleResolution": "bundler", 12 + "allowImportingTsExtensions": true, 13 + "verbatimModuleSyntax": true, 14 + "moduleDetection": "force", 15 + "noEmit": true, 16 16 17 - /* Linting */ 18 - "strict": true, 19 - "noUnusedLocals": true, 20 - "noUnusedParameters": true, 21 - "erasableSyntaxOnly": true, 22 - "noFallthroughCasesInSwitch": true, 23 - "noUncheckedSideEffectImports": true 24 - }, 25 - "include": ["vite.config.ts"] 17 + /* Linting */ 18 + "strict": true, 19 + "noUnusedLocals": true, 20 + "noUnusedParameters": true, 21 + "erasableSyntaxOnly": true, 22 + "noFallthroughCasesInSwitch": true, 23 + "noUncheckedSideEffectImports": true 24 + }, 25 + "include": ["vite.config.ts"] 26 26 }
+16 -11
apps/frontend/vite.config.ts
··· 1 - import { defineConfig } from 'vite' 2 - import react from '@vitejs/plugin-react' 3 - 4 - import { tanstackRouter } from '@tanstack/router-plugin/vite' 1 + import { tanstackRouter } from "@tanstack/router-plugin/vite"; 2 + import react from "@vitejs/plugin-react"; 3 + import { defineConfig } from "vite"; 5 4 // https://vite.dev/config/ 6 5 export default defineConfig({ 7 - plugins: [ 8 - // Please make sure that '@tanstack/router-plugin' is passed before '@vitejs/plugin-react' 9 - tanstackRouter({ 10 - target: 'react', 11 - autoCodeSplitting: true, 12 - }),react()], 13 - }) 6 + plugins: [ 7 + // Please make sure that '@tanstack/router-plugin' is passed before '@vitejs/plugin-react' 8 + tanstackRouter({ 9 + target: "react", 10 + autoCodeSplitting: true, 11 + }), 12 + react({ 13 + babel: { 14 + plugins: ["babel-plugin-react-compiler"], 15 + }, 16 + }), 17 + ], 18 + });
+34
biome.json
··· 1 + { 2 + "$schema": "https://biomejs.dev/schemas/2.4.6/schema.json", 3 + "vcs": { 4 + "enabled": true, 5 + "clientKind": "git", 6 + "useIgnoreFile": true 7 + }, 8 + "files": { 9 + "ignoreUnknown": false 10 + }, 11 + "formatter": { 12 + "enabled": true, 13 + "indentStyle": "tab" 14 + }, 15 + "linter": { 16 + "enabled": true, 17 + "rules": { 18 + "recommended": true 19 + } 20 + }, 21 + "javascript": { 22 + "formatter": { 23 + "quoteStyle": "double" 24 + } 25 + }, 26 + "assist": { 27 + "enabled": true, 28 + "actions": { 29 + "source": { 30 + "organizeImports": "on" 31 + } 32 + } 33 + } 34 + }
+57 -1
bun.lock
··· 8 8 "@atcute/lex-cli": "^2.5.3", 9 9 }, 10 10 "devDependencies": { 11 + "@biomejs/biome": "2.4.6", 11 12 "@types/bun": "latest", 12 13 }, 13 14 "peerDependencies": { ··· 29 30 }, 30 31 "peerDependencies": { 31 32 "typescript": "^5", 33 + }, 34 + }, 35 + "apps/firehose": { 36 + "name": "firehose", 37 + "version": "1.0.0", 38 + "dependencies": { 39 + "@atcute/atproto": "^3.1.10", 40 + "@atcute/firehose": "^0.1.0", 41 + "@atcute/jetstream": "^1.1.2", 42 + "@atcute/lexicons": "^1.2.9", 43 + "lexicon": "workspace:*", 44 + }, 45 + "devDependencies": { 46 + "@types/bun": "latest", 32 47 }, 33 48 }, 34 49 "apps/frontend": { ··· 50 65 "@types/react": "^19.2.7", 51 66 "@types/react-dom": "^19.2.3", 52 67 "@vitejs/plugin-react-swc": "^4.2.2", 68 + "babel-plugin-react-compiler": "^1.0.0", 53 69 "eslint": "^9.39.1", 54 70 "eslint-plugin-react-hooks": "^7.0.1", 55 71 "eslint-plugin-react-refresh": "^0.4.24", ··· 84 100 }, 85 101 }, 86 102 "packages": { 103 + "@atcute/atproto": ["@atcute/atproto@3.1.10", "", { "dependencies": { "@atcute/lexicons": "^1.2.6" } }, "sha512-+GKZpOc0PJcdWMQEkTfg/rSNDAAHxmAUGBl60g2az15etqJn5WaUPNGFE2sB7hKpwi5Ue2h/L0OacINcE/JDDQ=="], 104 + 87 105 "@atcute/car": ["@atcute/car@5.1.1", "", { "dependencies": { "@atcute/cbor": "^2.3.2", "@atcute/cid": "^2.4.1", "@atcute/uint8array": "^1.1.1", "@atcute/varint": "^2.0.0" } }, "sha512-MeRUJNXYgAHrJZw7mMoZJb9xIqv3LZLQw90rRRAVAo8SGNdICwyqe6Bf2LGesX73QM04MBuYO6Kqhvold3TFfg=="], 88 106 89 107 "@atcute/cbor": ["@atcute/cbor@2.3.2", "", { "dependencies": { "@atcute/cid": "^2.4.1", "@atcute/multibase": "^1.1.8", "@atcute/uint8array": "^1.1.1" } }, "sha512-xP2SORSau/VVI00x2V4BjwIkHr6EQ7l/MXEOPaa4LGYtePFc4gnD4L1yN10dT5NEuUnvGEuCh6arLB7gz1smVQ=="], ··· 94 112 95 113 "@atcute/crypto": ["@atcute/crypto@2.4.0", "", { "dependencies": { "@atcute/multibase": "^1.1.8", "@atcute/uint8array": "^1.1.1", "@noble/secp256k1": "^3.0.0" } }, "sha512-XtEeDaSgfr92C7b1VDRvd3F9pI8tVUyy8PJAeu8IWQC7+e/GXZOSl58uWh5YP/9p1Lsa0I16uKHwogygxEwlMQ=="], 96 114 115 + "@atcute/firehose": ["@atcute/firehose@0.1.0", "", { "dependencies": { "@atcute/cbor": "^2.2.7", "@atcute/lexicons": "^1.2.2", "@atcute/uint8array": "^1.0.5", "@mary-ext/event-iterator": "^1.0.0", "@mary-ext/simple-event-emitter": "^1.0.0", "partysocket": "^1.1.6", "type-fest": "^4.41.0" } }, "sha512-xBEKdi6rkODpCIIRpXtXhhcuQ1vTbufDykAM2kA6bmWEpuwI4acoUCg9zbiUWqd21SMMOAthu9Eh72i2VYrD7A=="], 116 + 97 117 "@atcute/identity": ["@atcute/identity@1.1.3", "", { "dependencies": { "@atcute/lexicons": "^1.2.4", "@badrap/valita": "^0.4.6" } }, "sha512-oIqPoI8TwWeQxvcLmFEZLdN2XdWcaLVtlm8pNk0E72As9HNzzD9pwKPrLr3rmTLRIoULPPFmq9iFNsTeCIU9ng=="], 98 118 99 119 "@atcute/identity-resolver": ["@atcute/identity-resolver@1.2.2", "", { "dependencies": { "@atcute/lexicons": "^1.2.6", "@atcute/util-fetch": "^1.0.5", "@badrap/valita": "^0.4.6" }, "peerDependencies": { "@atcute/identity": "^1.0.0" } }, "sha512-eUh/UH4bFvuXS0X7epYCeJC/kj4rbBXfSRumLEH4smMVwNOgTo7cL/0Srty+P/qVPoZEyXdfEbS0PHJyzoXmHw=="], 120 + 121 + "@atcute/jetstream": ["@atcute/jetstream@1.1.2", "", { "dependencies": { "@atcute/lexicons": "^1.2.2", "@badrap/valita": "^0.4.6", "@mary-ext/event-iterator": "^1.0.0", "@mary-ext/simple-event-emitter": "^1.0.0", "partysocket": "^1.1.5", "type-fest": "^4.41.0", "yocto-queue": "^1.2.1" } }, "sha512-u6p/h2xppp7LE6W/9xErAJ6frfN60s8adZuCKtfAaaBBiiYbb1CfpzN8Uc+2qtJZNorqGvuuDb5572Jmh7yHBQ=="], 100 122 101 123 "@atcute/lex-cli": ["@atcute/lex-cli@2.5.3", "", { "dependencies": { "@atcute/identity": "^1.1.3", "@atcute/identity-resolver": "^1.2.2", "@atcute/lexicon-doc": "^2.1.0", "@atcute/lexicon-resolver": "^0.1.6", "@atcute/lexicons": "^1.2.7", "@badrap/valita": "^0.4.6", "@optique/core": "^0.6.10", "@optique/run": "^0.6.10", "picocolors": "^1.1.1", "prettier": "^3.7.4" }, "bin": { "lex-cli": "cli.mjs" } }, "sha512-829rvezMOfRkJQRKvupNT8TWT/YYffJ2QsB80D9aPjkXSogrETZA7xZcPaMZBXg+mJaVbLO9S4ThPQmlF0L4UQ=="], 102 124 ··· 166 188 167 189 "@badrap/valita": ["@badrap/valita@0.4.6", "", {}, "sha512-4kdqcjyxo/8RQ8ayjms47HCWZIF5981oE5nIenbfThKDxWXtEHKipAOWlflpPJzZx9y/JWYQkp18Awr7VuepFg=="], 168 190 191 + "@biomejs/biome": ["@biomejs/biome@2.4.6", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.6", "@biomejs/cli-darwin-x64": "2.4.6", "@biomejs/cli-linux-arm64": "2.4.6", "@biomejs/cli-linux-arm64-musl": "2.4.6", "@biomejs/cli-linux-x64": "2.4.6", "@biomejs/cli-linux-x64-musl": "2.4.6", "@biomejs/cli-win32-arm64": "2.4.6", "@biomejs/cli-win32-x64": "2.4.6" }, "bin": { "biome": "bin/biome" } }, "sha512-QnHe81PMslpy3mnpL8DnO2M4S4ZnYPkjlGCLWBZT/3R9M6b5daArWMMtEfP52/n174RKnwRIf3oT8+wc9ihSfQ=="], 192 + 193 + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-NW18GSyxr+8sJIqgoGwVp5Zqm4SALH4b4gftIA0n62PTuBs6G2tHlwNAOj0Vq0KKSs7Sf88VjjmHh0O36EnzrQ=="], 194 + 195 + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-4uiE/9tuI7cnjtY9b07RgS7gGyYOAfIAGeVJWEfeCnAarOAS7qVmuRyX6d7JTKw28/mt+rUzMasYeZ+0R/U1Mw=="], 196 + 197 + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-kMLaI7OF5GN1Q8Doymjro1P8rVEoy7BKQALNz6fiR8IC1WKduoNyteBtJlHT7ASIL0Cx2jR6VUOBIbcB1B8pew=="], 198 + 199 + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-F/JdB7eN22txiTqHM5KhIVt0jVkzZwVYrdTR1O3Y4auBOQcXxHK4dxULf4z43QyZI5tsnQJrRBHZy7wwtL+B3A=="], 200 + 201 + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.6", "", { "os": "linux", "cpu": "x64" }, "sha512-oHXmUFEoH8Lql1xfc3QkFLiC1hGR7qedv5eKNlC185or+o4/4HiaU7vYODAH3peRCfsuLr1g6v2fK9dFFOYdyw=="], 202 + 203 + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.6", "", { "os": "linux", "cpu": "x64" }, "sha512-C9s98IPDu7DYarjlZNuzJKTjVHN03RUnmHV5htvqsx6vEUXCDSJ59DNwjKVD5XYoSS4N+BYhq3RTBAL8X6svEg=="], 204 + 205 + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-xzThn87Pf3YrOGTEODFGONmqXpTwUNxovQb72iaUOdcw8sBSY3+3WD8Hm9IhMYLnPi0n32s3L3NWU6+eSjfqFg=="], 206 + 207 + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.6", "", { "os": "win32", "cpu": "x64" }, "sha512-7++XhnsPlr1HDbor5amovPjOH6vsrFOCdp93iKXhFn6bcMUI6soodj3WWKfgEO6JosKU1W5n3uky3WW9RlRjTg=="], 208 + 169 209 "@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="], 170 210 171 211 "@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="], ··· 265 305 "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], 266 306 267 307 "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], 308 + 309 + "@mary-ext/event-iterator": ["@mary-ext/event-iterator@1.0.0", "", { "dependencies": { "yocto-queue": "^1.2.1" } }, "sha512-l6gCPsWJ8aRCe/s7/oCmero70kDHgIK5m4uJvYgwEYTqVxoBOIXbKr5tnkLqUHEg6mNduB4IWvms3h70Hp9ADQ=="], 310 + 311 + "@mary-ext/simple-event-emitter": ["@mary-ext/simple-event-emitter@1.0.1", "", {}, "sha512-9+VvZisxZ/gSg+JJH7hmXaA8Qj42Qjz3O58RSB+INYc8iLA0icATZxHB9vKbj59ojDGZjO3hCKzMXocx3L0H8w=="], 268 312 269 313 "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" } }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="], 270 314 ··· 472 516 473 517 "babel-dead-code-elimination": ["babel-dead-code-elimination@1.0.12", "", { "dependencies": { "@babel/core": "^7.23.7", "@babel/parser": "^7.23.6", "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6" } }, "sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig=="], 474 518 519 + "babel-plugin-react-compiler": ["babel-plugin-react-compiler@1.0.0", "", { "dependencies": { "@babel/types": "^7.26.0" } }, "sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw=="], 520 + 475 521 "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], 476 522 477 523 "baseline-browser-mapping": ["baseline-browser-mapping@2.10.0", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA=="], ··· 562 608 563 609 "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], 564 610 611 + "event-target-polyfill": ["event-target-polyfill@0.0.4", "", {}, "sha512-Gs6RLjzlLRdT8X9ZipJdIZI/Y6/HhRLyq9RdDlCsnpxr/+Nn6bU2EFGuC94GjxqhM+Nmij2Vcq98yoHrU8uNFQ=="], 612 + 565 613 "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], 566 614 567 615 "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], ··· 575 623 "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], 576 624 577 625 "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], 626 + 627 + "firehose": ["firehose@workspace:apps/firehose"], 578 628 579 629 "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], 580 630 ··· 690 740 691 741 "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], 692 742 743 + "partysocket": ["partysocket@1.1.16", "", { "dependencies": { "event-target-polyfill": "^0.0.4" }, "peerDependencies": { "react": ">=17" }, "optionalPeers": ["react"] }, "sha512-d7xFv+ZC7x0p/DAHWJ5FhxQhimIx+ucyZY+kxL0cKddLBmK9c4p2tEA/L+dOOrWm6EYrRwrBjKQV0uSzOY9x1w=="], 744 + 693 745 "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], 694 746 695 747 "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], ··· 766 818 767 819 "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], 768 820 821 + "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], 822 + 769 823 "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], 770 824 771 825 "typescript-eslint": ["typescript-eslint@8.56.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.56.1", "@typescript-eslint/parser": "8.56.1", "@typescript-eslint/typescript-estree": "8.56.1", "@typescript-eslint/utils": "8.56.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-U4lM6pjmBX7J5wk4szltF7I1cGBHXZopnAXCMXb3+fZ3B/0Z3hq3wS/CCUB2NZBNAExK92mCU2tEohWuwVMsDQ=="], ··· 792 846 793 847 "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], 794 848 795 - "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], 849 + "yocto-queue": ["yocto-queue@1.2.2", "", {}, "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ=="], 796 850 797 851 "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], 798 852 ··· 825 879 "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], 826 880 827 881 "eslint-plugin-react-hooks/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], 882 + 883 + "p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], 828 884 829 885 "postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], 830 886
-1
index.ts
··· 1 - console.log("Hello via Bun!");
+18 -17
package.json
··· 1 1 { 2 - "name": "catnip", 3 - "module": "index.ts", 4 - "type": "module", 5 - "private": true, 6 - "workspaces": [ 7 - "apps/*", 8 - "packages/*" 9 - ], 10 - "devDependencies": { 11 - "@types/bun": "latest" 12 - }, 13 - "peerDependencies": { 14 - "typescript": "^5" 15 - }, 16 - "dependencies": { 17 - "@atcute/lex-cli": "^2.5.3" 18 - } 2 + "name": "catnip", 3 + "module": "index.ts", 4 + "type": "module", 5 + "private": true, 6 + "workspaces": [ 7 + "apps/*", 8 + "packages/*" 9 + ], 10 + "devDependencies": { 11 + "@biomejs/biome": "2.4.6", 12 + "@types/bun": "latest" 13 + }, 14 + "peerDependencies": { 15 + "typescript": "^5" 16 + }, 17 + "dependencies": { 18 + "@atcute/lex-cli": "^2.5.3" 19 + } 19 20 }
+8 -8
packages/db/drizzle.config.ts
··· 2 2 import { defineConfig } from "drizzle-kit"; 3 3 4 4 export default defineConfig({ 5 - out: "./drizzle", 6 - schema: "./src/schema.ts", 7 - dialect: "postgresql", 8 - dbCredentials: { 9 - url: 10 - process.env.DATABASE_URL ?? 11 - "postgresql://postgres:postgres@0.0.0.0:5432/catnip", 12 - }, 5 + out: "./drizzle", 6 + schema: "./src/schema.ts", 7 + dialect: "postgresql", 8 + dbCredentials: { 9 + url: 10 + process.env.DATABASE_URL ?? 11 + "postgresql://postgres:postgres@0.0.0.0:5432/catnip", 12 + }, 13 13 });
+232 -242
packages/db/drizzle/meta/0000_snapshot.json
··· 1 1 { 2 - "id": "fb0d3659-46d1-49a7-8559-2ca67ac8a282", 3 - "prevId": "00000000-0000-0000-0000-000000000000", 4 - "version": "7", 5 - "dialect": "postgresql", 6 - "tables": { 7 - "public.artists": { 8 - "name": "artists", 9 - "schema": "", 10 - "columns": { 11 - "id": { 12 - "name": "id", 13 - "type": "serial", 14 - "primaryKey": true, 15 - "notNull": true 16 - }, 17 - "did": { 18 - "name": "did", 19 - "type": "text", 20 - "primaryKey": false, 21 - "notNull": false 22 - }, 23 - "name": { 24 - "name": "name", 25 - "type": "text", 26 - "primaryKey": false, 27 - "notNull": false 28 - } 29 - }, 30 - "indexes": {}, 31 - "foreignKeys": {}, 32 - "compositePrimaryKeys": {}, 33 - "uniqueConstraints": { 34 - "artists_did_unique": { 35 - "name": "artists_did_unique", 36 - "nullsNotDistinct": false, 37 - "columns": [ 38 - "did" 39 - ] 40 - } 41 - }, 42 - "policies": {}, 43 - "checkConstraints": { 44 - "did_or_name": { 45 - "name": "did_or_name", 46 - "value": "\"artists\".\"did\" IS NOT NULL OR \"artists\".\"name\" IS NOT NULL" 47 - } 48 - }, 49 - "isRLSEnabled": false 50 - }, 51 - "public.track_artists": { 52 - "name": "track_artists", 53 - "schema": "", 54 - "columns": { 55 - "id": { 56 - "name": "id", 57 - "type": "serial", 58 - "primaryKey": true, 59 - "notNull": true 60 - }, 61 - "track_uri": { 62 - "name": "track_uri", 63 - "type": "text", 64 - "primaryKey": false, 65 - "notNull": true 66 - }, 67 - "artist_id": { 68 - "name": "artist_id", 69 - "type": "integer", 70 - "primaryKey": false, 71 - "notNull": true 72 - }, 73 - "position": { 74 - "name": "position", 75 - "type": "integer", 76 - "primaryKey": false, 77 - "notNull": true 78 - } 79 - }, 80 - "indexes": {}, 81 - "foreignKeys": { 82 - "track_artists_track_uri_tracks_uri_fk": { 83 - "name": "track_artists_track_uri_tracks_uri_fk", 84 - "tableFrom": "track_artists", 85 - "tableTo": "tracks", 86 - "columnsFrom": [ 87 - "track_uri" 88 - ], 89 - "columnsTo": [ 90 - "uri" 91 - ], 92 - "onDelete": "cascade", 93 - "onUpdate": "no action" 94 - }, 95 - "track_artists_artist_id_artists_id_fk": { 96 - "name": "track_artists_artist_id_artists_id_fk", 97 - "tableFrom": "track_artists", 98 - "tableTo": "artists", 99 - "columnsFrom": [ 100 - "artist_id" 101 - ], 102 - "columnsTo": [ 103 - "id" 104 - ], 105 - "onDelete": "cascade", 106 - "onUpdate": "no action" 107 - } 108 - }, 109 - "compositePrimaryKeys": {}, 110 - "uniqueConstraints": {}, 111 - "policies": {}, 112 - "checkConstraints": {}, 113 - "isRLSEnabled": false 114 - }, 115 - "public.tracks": { 116 - "name": "tracks", 117 - "schema": "", 118 - "columns": { 119 - "uri": { 120 - "name": "uri", 121 - "type": "text", 122 - "primaryKey": true, 123 - "notNull": true 124 - }, 125 - "did": { 126 - "name": "did", 127 - "type": "text", 128 - "primaryKey": false, 129 - "notNull": true 130 - }, 131 - "rkey": { 132 - "name": "rkey", 133 - "type": "text", 134 - "primaryKey": false, 135 - "notNull": true 136 - }, 137 - "cid": { 138 - "name": "cid", 139 - "type": "text", 140 - "primaryKey": false, 141 - "notNull": true 142 - }, 143 - "title": { 144 - "name": "title", 145 - "type": "text", 146 - "primaryKey": false, 147 - "notNull": true 148 - }, 149 - "description": { 150 - "name": "description", 151 - "type": "text", 152 - "primaryKey": false, 153 - "notNull": false 154 - }, 155 - "created_at": { 156 - "name": "created_at", 157 - "type": "timestamp with time zone", 158 - "primaryKey": false, 159 - "notNull": true 160 - }, 161 - "release_date": { 162 - "name": "release_date", 163 - "type": "timestamp with time zone", 164 - "primaryKey": false, 165 - "notNull": false 166 - }, 167 - "duration_ms": { 168 - "name": "duration_ms", 169 - "type": "integer", 170 - "primaryKey": false, 171 - "notNull": false 172 - }, 173 - "tags": { 174 - "name": "tags", 175 - "type": "text[]", 176 - "primaryKey": false, 177 - "notNull": false 178 - }, 179 - "language": { 180 - "name": "language", 181 - "type": "text", 182 - "primaryKey": false, 183 - "notNull": false 184 - }, 185 - "license": { 186 - "name": "license", 187 - "type": "text", 188 - "primaryKey": false, 189 - "notNull": false 190 - }, 191 - "lyrics": { 192 - "name": "lyrics", 193 - "type": "text", 194 - "primaryKey": false, 195 - "notNull": false 196 - }, 197 - "album_art": { 198 - "name": "album_art", 199 - "type": "jsonb", 200 - "primaryKey": false, 201 - "notNull": false 202 - }, 203 - "audio": { 204 - "name": "audio", 205 - "type": "jsonb", 206 - "primaryKey": false, 207 - "notNull": false 208 - }, 209 - "external_url": { 210 - "name": "external_url", 211 - "type": "text", 212 - "primaryKey": false, 213 - "notNull": false 214 - }, 215 - "indexed_at": { 216 - "name": "indexed_at", 217 - "type": "timestamp with time zone", 218 - "primaryKey": false, 219 - "notNull": true, 220 - "default": "now()" 221 - } 222 - }, 223 - "indexes": {}, 224 - "foreignKeys": {}, 225 - "compositePrimaryKeys": {}, 226 - "uniqueConstraints": {}, 227 - "policies": {}, 228 - "checkConstraints": {}, 229 - "isRLSEnabled": false 230 - } 231 - }, 232 - "enums": {}, 233 - "schemas": {}, 234 - "sequences": {}, 235 - "roles": {}, 236 - "policies": {}, 237 - "views": {}, 238 - "_meta": { 239 - "columns": {}, 240 - "schemas": {}, 241 - "tables": {} 242 - } 243 - } 2 + "id": "fb0d3659-46d1-49a7-8559-2ca67ac8a282", 3 + "prevId": "00000000-0000-0000-0000-000000000000", 4 + "version": "7", 5 + "dialect": "postgresql", 6 + "tables": { 7 + "public.artists": { 8 + "name": "artists", 9 + "schema": "", 10 + "columns": { 11 + "id": { 12 + "name": "id", 13 + "type": "serial", 14 + "primaryKey": true, 15 + "notNull": true 16 + }, 17 + "did": { 18 + "name": "did", 19 + "type": "text", 20 + "primaryKey": false, 21 + "notNull": false 22 + }, 23 + "name": { 24 + "name": "name", 25 + "type": "text", 26 + "primaryKey": false, 27 + "notNull": false 28 + } 29 + }, 30 + "indexes": {}, 31 + "foreignKeys": {}, 32 + "compositePrimaryKeys": {}, 33 + "uniqueConstraints": { 34 + "artists_did_unique": { 35 + "name": "artists_did_unique", 36 + "nullsNotDistinct": false, 37 + "columns": ["did"] 38 + } 39 + }, 40 + "policies": {}, 41 + "checkConstraints": { 42 + "did_or_name": { 43 + "name": "did_or_name", 44 + "value": "\"artists\".\"did\" IS NOT NULL OR \"artists\".\"name\" IS NOT NULL" 45 + } 46 + }, 47 + "isRLSEnabled": false 48 + }, 49 + "public.track_artists": { 50 + "name": "track_artists", 51 + "schema": "", 52 + "columns": { 53 + "id": { 54 + "name": "id", 55 + "type": "serial", 56 + "primaryKey": true, 57 + "notNull": true 58 + }, 59 + "track_uri": { 60 + "name": "track_uri", 61 + "type": "text", 62 + "primaryKey": false, 63 + "notNull": true 64 + }, 65 + "artist_id": { 66 + "name": "artist_id", 67 + "type": "integer", 68 + "primaryKey": false, 69 + "notNull": true 70 + }, 71 + "position": { 72 + "name": "position", 73 + "type": "integer", 74 + "primaryKey": false, 75 + "notNull": true 76 + } 77 + }, 78 + "indexes": {}, 79 + "foreignKeys": { 80 + "track_artists_track_uri_tracks_uri_fk": { 81 + "name": "track_artists_track_uri_tracks_uri_fk", 82 + "tableFrom": "track_artists", 83 + "tableTo": "tracks", 84 + "columnsFrom": ["track_uri"], 85 + "columnsTo": ["uri"], 86 + "onDelete": "cascade", 87 + "onUpdate": "no action" 88 + }, 89 + "track_artists_artist_id_artists_id_fk": { 90 + "name": "track_artists_artist_id_artists_id_fk", 91 + "tableFrom": "track_artists", 92 + "tableTo": "artists", 93 + "columnsFrom": ["artist_id"], 94 + "columnsTo": ["id"], 95 + "onDelete": "cascade", 96 + "onUpdate": "no action" 97 + } 98 + }, 99 + "compositePrimaryKeys": {}, 100 + "uniqueConstraints": {}, 101 + "policies": {}, 102 + "checkConstraints": {}, 103 + "isRLSEnabled": false 104 + }, 105 + "public.tracks": { 106 + "name": "tracks", 107 + "schema": "", 108 + "columns": { 109 + "uri": { 110 + "name": "uri", 111 + "type": "text", 112 + "primaryKey": true, 113 + "notNull": true 114 + }, 115 + "did": { 116 + "name": "did", 117 + "type": "text", 118 + "primaryKey": false, 119 + "notNull": true 120 + }, 121 + "rkey": { 122 + "name": "rkey", 123 + "type": "text", 124 + "primaryKey": false, 125 + "notNull": true 126 + }, 127 + "cid": { 128 + "name": "cid", 129 + "type": "text", 130 + "primaryKey": false, 131 + "notNull": true 132 + }, 133 + "title": { 134 + "name": "title", 135 + "type": "text", 136 + "primaryKey": false, 137 + "notNull": true 138 + }, 139 + "description": { 140 + "name": "description", 141 + "type": "text", 142 + "primaryKey": false, 143 + "notNull": false 144 + }, 145 + "created_at": { 146 + "name": "created_at", 147 + "type": "timestamp with time zone", 148 + "primaryKey": false, 149 + "notNull": true 150 + }, 151 + "release_date": { 152 + "name": "release_date", 153 + "type": "timestamp with time zone", 154 + "primaryKey": false, 155 + "notNull": false 156 + }, 157 + "duration_ms": { 158 + "name": "duration_ms", 159 + "type": "integer", 160 + "primaryKey": false, 161 + "notNull": false 162 + }, 163 + "tags": { 164 + "name": "tags", 165 + "type": "text[]", 166 + "primaryKey": false, 167 + "notNull": false 168 + }, 169 + "language": { 170 + "name": "language", 171 + "type": "text", 172 + "primaryKey": false, 173 + "notNull": false 174 + }, 175 + "license": { 176 + "name": "license", 177 + "type": "text", 178 + "primaryKey": false, 179 + "notNull": false 180 + }, 181 + "lyrics": { 182 + "name": "lyrics", 183 + "type": "text", 184 + "primaryKey": false, 185 + "notNull": false 186 + }, 187 + "album_art": { 188 + "name": "album_art", 189 + "type": "jsonb", 190 + "primaryKey": false, 191 + "notNull": false 192 + }, 193 + "audio": { 194 + "name": "audio", 195 + "type": "jsonb", 196 + "primaryKey": false, 197 + "notNull": false 198 + }, 199 + "external_url": { 200 + "name": "external_url", 201 + "type": "text", 202 + "primaryKey": false, 203 + "notNull": false 204 + }, 205 + "indexed_at": { 206 + "name": "indexed_at", 207 + "type": "timestamp with time zone", 208 + "primaryKey": false, 209 + "notNull": true, 210 + "default": "now()" 211 + } 212 + }, 213 + "indexes": {}, 214 + "foreignKeys": {}, 215 + "compositePrimaryKeys": {}, 216 + "uniqueConstraints": {}, 217 + "policies": {}, 218 + "checkConstraints": {}, 219 + "isRLSEnabled": false 220 + } 221 + }, 222 + "enums": {}, 223 + "schemas": {}, 224 + "sequences": {}, 225 + "roles": {}, 226 + "policies": {}, 227 + "views": {}, 228 + "_meta": { 229 + "columns": {}, 230 + "schemas": {}, 231 + "tables": {} 232 + } 233 + }
+12 -12
packages/db/drizzle/meta/_journal.json
··· 1 1 { 2 - "version": "7", 3 - "dialect": "postgresql", 4 - "entries": [ 5 - { 6 - "idx": 0, 7 - "version": "7", 8 - "when": 1772998797948, 9 - "tag": "0000_sleepy_star_brand", 10 - "breakpoints": true 11 - } 12 - ] 13 - } 2 + "version": "7", 3 + "dialect": "postgresql", 4 + "entries": [ 5 + { 6 + "idx": 0, 7 + "version": "7", 8 + "when": 1772998797948, 9 + "tag": "0000_sleepy_star_brand", 10 + "breakpoints": true 11 + } 12 + ] 13 + }
+15 -15
packages/db/package.json
··· 1 1 { 2 - "name": "db", 3 - "version": "1.0.0", 4 - "exports": { 5 - "./schema": "./src/schema.ts" 6 - }, 7 - "devDependencies": { 8 - "@types/bun": "latest", 9 - "drizzle-kit": "^0.31.9", 10 - "tsx": "^4.21.0" 11 - }, 12 - "dependencies": { 13 - "dotenv": "^17.3.1", 14 - "drizzle-orm": "^0.45.1", 15 - "postgres": "^3.4.8" 16 - } 2 + "name": "db", 3 + "version": "1.0.0", 4 + "exports": { 5 + "./schema": "./src/schema.ts" 6 + }, 7 + "devDependencies": { 8 + "@types/bun": "latest", 9 + "drizzle-kit": "^0.31.9", 10 + "tsx": "^4.21.0" 11 + }, 12 + "dependencies": { 13 + "dotenv": "^17.3.1", 14 + "drizzle-orm": "^0.45.1", 15 + "postgres": "^3.4.8" 16 + } 17 17 }
+80 -53
packages/db/src/schema.ts
··· 1 - import { pgTable, text, integer, jsonb, timestamp, check, serial } from 'drizzle-orm/pg-core'; 2 - import { relations, sql } from 'drizzle-orm'; 1 + import { relations, sql } from "drizzle-orm"; 2 + import { 3 + check, 4 + integer, 5 + jsonb, 6 + pgTable, 7 + serial, 8 + text, 9 + timestamp, 10 + } from "drizzle-orm/pg-core"; 3 11 4 - export const tracks = pgTable('tracks', { 5 - /** AT Protocol record URI (at://did/collection/rkey) */ 6 - uri: text('uri').primaryKey(), 7 - /** DID of the record owner */ 8 - did: text('did').notNull(), 9 - /** Record key (TID) */ 10 - rkey: text('rkey').notNull(), 11 - /** CID of the record */ 12 - cid: text('cid').notNull(), 12 + export const tracks = pgTable("tracks", { 13 + /** AT Protocol record URI (at://did/collection/rkey) */ 14 + uri: text("uri").primaryKey(), 15 + /** DID of the record owner */ 16 + did: text("did").notNull(), 17 + /** Record key (TID) */ 18 + rkey: text("rkey").notNull(), 19 + /** CID of the record */ 20 + cid: text("cid").notNull(), 13 21 14 - title: text('title').notNull(), 15 - description: text('description'), 16 - createdAt: timestamp('created_at', { withTimezone: true }).notNull(), 17 - releaseDate: timestamp('release_date', { withTimezone: true }), 18 - durationMs: integer('duration_ms'), 22 + title: text("title").notNull(), 23 + description: text("description"), 24 + createdAt: timestamp("created_at", { withTimezone: true }).notNull(), 25 + releaseDate: timestamp("release_date", { withTimezone: true }), 26 + durationMs: integer("duration_ms"), 19 27 20 - /** Text array of tags */ 21 - tags: text('tags').array(), 28 + /** Text array of tags */ 29 + tags: text("tags").array(), 22 30 23 - language: text('language'), 24 - license: text('license'), 25 - lyrics: text('lyrics'), 31 + language: text("language"), 32 + license: text("license"), 33 + lyrics: text("lyrics"), 26 34 27 - /** AT Protocol blob reference for album art */ 28 - albumArt: jsonb('album_art').$type<BlobRef>(), 29 - /** AT Protocol blob reference for audio */ 30 - audio: jsonb('audio').$type<BlobRef>(), 35 + /** AT Protocol blob reference for album art */ 36 + albumArt: jsonb("album_art").$type<BlobRef>(), 37 + /** AT Protocol blob reference for audio */ 38 + audio: jsonb("audio").$type<BlobRef>(), 31 39 32 - externalUrl: text('external_url'), 40 + externalUrl: text("external_url"), 33 41 34 - /** Indexer bookkeeping */ 35 - indexedAt: timestamp('indexed_at', { withTimezone: true }).notNull().defaultNow(), 42 + /** Indexer bookkeeping */ 43 + indexedAt: timestamp("indexed_at", { withTimezone: true }) 44 + .notNull() 45 + .defaultNow(), 36 46 }); 37 47 38 - export const artists = pgTable('artists', { 39 - id: serial('id').primaryKey(), 40 - /** atproto DID of the artist, if they are an atproto user */ 41 - did: text('did').unique(), 42 - /** Display name or stage name */ 43 - name: text('name'), 44 - }, (table) => [ 45 - check('did_or_name', sql`${table.did} IS NOT NULL OR ${table.name} IS NOT NULL`), 46 - ]); 48 + export const artists = pgTable( 49 + "artists", 50 + { 51 + id: serial("id").primaryKey(), 52 + /** atproto DID of the artist, if they are an atproto user */ 53 + did: text("did").unique(), 54 + /** Display name or stage name */ 55 + name: text("name"), 56 + }, 57 + (table) => [ 58 + check( 59 + "did_or_name", 60 + sql`${table.did} IS NOT NULL OR ${table.name} IS NOT NULL`, 61 + ), 62 + ], 63 + ); 47 64 48 - export const trackArtists = pgTable('track_artists', { 49 - id: serial('id').primaryKey(), 50 - /** References tracks.uri */ 51 - trackUri: text('track_uri').notNull().references(() => tracks.uri, { onDelete: 'cascade' }), 52 - /** References artists.id */ 53 - artistId: integer('artist_id').notNull().references(() => artists.id, { onDelete: 'cascade' }), 54 - /** Position in the artist list for ordering */ 55 - position: integer('position').notNull(), 65 + export const trackArtists = pgTable("track_artists", { 66 + id: serial("id").primaryKey(), 67 + /** References tracks.uri */ 68 + trackUri: text("track_uri") 69 + .notNull() 70 + .references(() => tracks.uri, { onDelete: "cascade" }), 71 + /** References artists.id */ 72 + artistId: integer("artist_id") 73 + .notNull() 74 + .references(() => artists.id, { onDelete: "cascade" }), 75 + /** Position in the artist list for ordering */ 76 + position: integer("position").notNull(), 56 77 }); 57 78 58 79 export const tracksRelations = relations(tracks, ({ many }) => ({ 59 - trackArtists: many(trackArtists), 80 + trackArtists: many(trackArtists), 60 81 })); 61 82 62 83 export const artistsRelations = relations(artists, ({ many }) => ({ 63 - trackArtists: many(trackArtists), 84 + trackArtists: many(trackArtists), 64 85 })); 65 86 66 87 export const trackArtistsRelations = relations(trackArtists, ({ one }) => ({ 67 - track: one(tracks, { fields: [trackArtists.trackUri], references: [tracks.uri] }), 68 - artist: one(artists, { fields: [trackArtists.artistId], references: [artists.id] }), 88 + track: one(tracks, { 89 + fields: [trackArtists.trackUri], 90 + references: [tracks.uri], 91 + }), 92 + artist: one(artists, { 93 + fields: [trackArtists.artistId], 94 + references: [artists.id], 95 + }), 69 96 })); 70 97 71 98 export type BlobRef = { 72 - $type: 'blob'; 73 - ref: { $link: string }; 74 - mimeType: string; 75 - size: number; 99 + $type: "blob"; 100 + ref: { $link: string }; 101 + mimeType: string; 102 + size: number; 76 103 };
+24 -24
packages/db/tsconfig.json
··· 1 1 { 2 - "compilerOptions": { 3 - // Environment setup & latest features 4 - "lib": ["ESNext"], 5 - "target": "ESNext", 6 - "module": "Preserve", 7 - "moduleDetection": "force", 8 - "jsx": "react-jsx", 9 - "allowJs": true, 2 + "compilerOptions": { 3 + // Environment setup & latest features 4 + "lib": ["ESNext"], 5 + "target": "ESNext", 6 + "module": "Preserve", 7 + "moduleDetection": "force", 8 + "jsx": "react-jsx", 9 + "allowJs": true, 10 10 11 - // Bundler mode 12 - "moduleResolution": "bundler", 13 - "allowImportingTsExtensions": true, 14 - "verbatimModuleSyntax": true, 15 - "noEmit": true, 11 + // Bundler mode 12 + "moduleResolution": "bundler", 13 + "allowImportingTsExtensions": true, 14 + "verbatimModuleSyntax": true, 15 + "noEmit": true, 16 16 17 - // Best practices 18 - "strict": true, 19 - "skipLibCheck": true, 20 - "noFallthroughCasesInSwitch": true, 21 - "noUncheckedIndexedAccess": true, 22 - "noImplicitOverride": true, 17 + // Best practices 18 + "strict": true, 19 + "skipLibCheck": true, 20 + "noFallthroughCasesInSwitch": true, 21 + "noUncheckedIndexedAccess": true, 22 + "noImplicitOverride": true, 23 23 24 - // Some stricter flags (disabled by default) 25 - "noUnusedLocals": false, 26 - "noUnusedParameters": false, 27 - "noPropertyAccessFromIndexSignature": false 28 - } 24 + // Some stricter flags (disabled by default) 25 + "noUnusedLocals": false, 26 + "noUnusedParameters": false, 27 + "noPropertyAccessFromIndexSignature": false 28 + } 29 29 }
+7 -7
packages/lexicon/lex.config.ts
··· 1 - import { defineLexiconConfig } from '@atcute/lex-cli'; 1 + import { defineLexiconConfig } from "@atcute/lex-cli"; 2 2 3 3 export default defineLexiconConfig({ 4 - files: ['lexicons-src/**/*.ts'], 5 - outdir: 'dist/src/', 6 - export: { 7 - outdir: "dist/lexicon", 8 - clean: true 9 - } 4 + files: ["lexicons-src/**/*.ts"], 5 + outdir: "dist/src/", 6 + export: { 7 + outdir: "dist/lexicon", 8 + clean: true, 9 + }, 10 10 });
+43 -28
packages/lexicon/lexicons-src/ca.ansxor.catnip.getTracks.ts
··· 1 - import { document, query, params, object, required, string, array, ref } from '@atcute/lexicon-doc/builder'; 1 + import { 2 + array, 3 + document, 4 + object, 5 + params, 6 + query, 7 + ref, 8 + required, 9 + string, 10 + } from "@atcute/lexicon-doc/builder"; 2 11 3 12 export default document({ 4 - id: 'ca.ansxor.catnip.getTracks', 5 - description: 'Get track views for a list of tracks (by AT-URI).', 6 - defs: { 7 - main: query({ 8 - description: 'Get track views for a specified list of tracks (by AT-URI).', 9 - parameters: params({ 10 - properties: { 11 - uris: required(array({ 12 - description: 'List of track AT-URIs to return hydrated views for.', 13 - items: string({ format: 'at-uri' }), 14 - maxLength: 25, 15 - })), 16 - }, 17 - }), 18 - output: { 19 - encoding: 'application/json', 20 - schema: object({ 21 - properties: { 22 - tracks: required(array({ 23 - description: 'List of track views.', 24 - items: ref({ ref: 'ca.ansxor.catnip.track' }), 25 - })), 26 - }, 27 - }), 28 - }, 29 - }), 30 - }, 13 + id: "ca.ansxor.catnip.getTracks", 14 + description: "Get track views for a list of tracks (by AT-URI).", 15 + defs: { 16 + main: query({ 17 + description: 18 + "Get track views for a specified list of tracks (by AT-URI).", 19 + parameters: params({ 20 + properties: { 21 + uris: required( 22 + array({ 23 + description: 24 + "List of track AT-URIs to return hydrated views for.", 25 + items: string({ format: "at-uri" }), 26 + maxLength: 25, 27 + }), 28 + ), 29 + }, 30 + }), 31 + output: { 32 + encoding: "application/json", 33 + schema: object({ 34 + properties: { 35 + tracks: required( 36 + array({ 37 + description: "List of track views.", 38 + items: ref({ ref: "ca.ansxor.catnip.track" }), 39 + }), 40 + ), 41 + }, 42 + }), 43 + }, 44 + }), 45 + }, 31 46 });
+121 -93
packages/lexicon/lexicons-src/ca.ansxor.catnip.track.ts
··· 1 - import { document, record, object, required, string, integer, array, blob, ref } from '@atcute/lexicon-doc/builder'; 1 + import { 2 + array, 3 + blob, 4 + document, 5 + integer, 6 + object, 7 + record, 8 + ref, 9 + required, 10 + string, 11 + } from "@atcute/lexicon-doc/builder"; 2 12 3 13 const artistCredit = object({ 4 - description: 'An artist credit. Either a DID (for atproto users) or a plain name (for non-atproto collaborators), or both.', 5 - properties: { 6 - did: string({ 7 - format: 'did', 8 - description: 'The atproto DID of the collaborator, if they are an atproto user.', 9 - }), 10 - name: string({ 11 - maxLength: 128, 12 - description: 'Display name or stage name of the collaborator. Required if DID is absent.', 13 - }), 14 - }, 14 + description: 15 + "An artist credit. Either a DID (for atproto users) or a plain name (for non-atproto collaborators), or both.", 16 + properties: { 17 + did: string({ 18 + format: "did", 19 + description: 20 + "The atproto DID of the collaborator, if they are an atproto user.", 21 + }), 22 + name: string({ 23 + maxLength: 128, 24 + description: 25 + "Display name or stage name of the collaborator. Required if DID is absent.", 26 + }), 27 + }, 15 28 }); 16 29 17 30 export default document({ 18 - id: 'ca.ansxor.catnip.track', 19 - description: 'A music track record for the Catnip music platform.', 20 - defs: { 21 - main: record({ 22 - key: 'tid', 23 - record: object({ 24 - properties: { 25 - title: required(string({ 26 - maxLength: 256, 27 - description: 'The title of the track.', 28 - })), 31 + id: "ca.ansxor.catnip.track", 32 + description: "A music track record for the Catnip music platform.", 33 + defs: { 34 + main: record({ 35 + key: "tid", 36 + record: object({ 37 + properties: { 38 + title: required( 39 + string({ 40 + maxLength: 256, 41 + description: "The title of the track.", 42 + }), 43 + ), 29 44 30 - description: string({ 31 - maxLength: 2000, 32 - description: 'An optional description or note about the track.', 33 - }), 45 + description: string({ 46 + maxLength: 2000, 47 + description: "An optional description or note about the track.", 48 + }), 34 49 35 - createdAt: required(string({ 36 - format: 'datetime', 37 - description: 'Timestamp of when the record was created on the PDS.', 38 - })), 50 + createdAt: required( 51 + string({ 52 + format: "datetime", 53 + description: 54 + "Timestamp of when the record was created on the PDS.", 55 + }), 56 + ), 39 57 40 - releaseDate: string({ 41 - format: 'datetime', 42 - description: 'Optional self-reported release date of the track. Used for display only.', 43 - }), 58 + releaseDate: string({ 59 + format: "datetime", 60 + description: 61 + "Optional self-reported release date of the track. Used for display only.", 62 + }), 44 63 45 - durationMs: integer({ 46 - minimum: 0, 47 - description: 'Optional track duration in milliseconds. Treat as a display hint only — not authoritative.', 48 - }), 64 + durationMs: integer({ 65 + minimum: 0, 66 + description: 67 + "Optional track duration in milliseconds. Treat as a display hint only — not authoritative.", 68 + }), 49 69 50 - artists: array({ 51 - maxLength: 32, 52 - description: 'List of artists or collaborators. Each entry must have at least a DID or a name.', 53 - items: ref({ ref: '#artistCredit' }), 54 - }), 70 + artists: array({ 71 + maxLength: 32, 72 + description: 73 + "List of artists or collaborators. Each entry must have at least a DID or a name.", 74 + items: ref({ ref: "#artistCredit" }), 75 + }), 55 76 56 - tags: array({ 57 - maxLength: 10, 58 - description: 'Optional tags for the track, e.g. genre, mood, instrumentation.', 59 - items: string({ 60 - maxLength: 64, 61 - }), 62 - }), 77 + tags: array({ 78 + maxLength: 10, 79 + description: 80 + "Optional tags for the track, e.g. genre, mood, instrumentation.", 81 + items: string({ 82 + maxLength: 64, 83 + }), 84 + }), 63 85 64 - language: string({ 65 - maxLength: 16, 66 - description: "BCP 47 language code for the track's lyrics or primary language (e.g. 'en', 'ja', 'fr-CA').", 67 - }), 86 + language: string({ 87 + maxLength: 16, 88 + description: 89 + "BCP 47 language code for the track's lyrics or primary language (e.g. 'en', 'ja', 'fr-CA').", 90 + }), 68 91 69 - license: string({ 70 - knownValues: [ 71 - 'CC0', 72 - 'CC BY', 73 - 'CC BY-SA', 74 - 'CC BY-NC', 75 - 'CC BY-NC-SA', 76 - 'CC BY-ND', 77 - 'CC BY-NC-ND', 78 - 'All Rights Reserved', 79 - 'Other', 80 - ], 81 - description: 'The license under which the track is released.', 82 - }), 92 + license: string({ 93 + knownValues: [ 94 + "CC0", 95 + "CC BY", 96 + "CC BY-SA", 97 + "CC BY-NC", 98 + "CC BY-NC-SA", 99 + "CC BY-ND", 100 + "CC BY-NC-ND", 101 + "All Rights Reserved", 102 + "Other", 103 + ], 104 + description: "The license under which the track is released.", 105 + }), 83 106 84 - lyrics: string({ 85 - maxLength: 50000, 86 - description: 'Optional lyrics for the track, as plain text.', 87 - }), 107 + lyrics: string({ 108 + maxLength: 50000, 109 + description: "Optional lyrics for the track, as plain text.", 110 + }), 88 111 89 - albumArt: blob({ 90 - accept: ['image/jpeg', 'image/png', 'image/webp'], 91 - maxSize: 1000000, 92 - description: 'Optional album art image. Recommended size: at least 500x500px.', 93 - }), 112 + albumArt: blob({ 113 + accept: ["image/jpeg", "image/png", "image/webp"], 114 + maxSize: 1000000, 115 + description: 116 + "Optional album art image. Recommended size: at least 500x500px.", 117 + }), 94 118 95 - audio: required(blob({ 96 - accept: ['audio/ogg', 'audio/opus'], 97 - maxSize: 52428800, 98 - description: 'Required self-hosted audio file in Opus format (~50MB limit). Mutually exclusive with externalUrl at the app level.', 99 - })), 119 + audio: required( 120 + blob({ 121 + accept: ["audio/ogg", "audio/opus"], 122 + maxSize: 52428800, 123 + description: 124 + "Required self-hosted audio file in Opus format (~50MB limit). Mutually exclusive with externalUrl at the app level.", 125 + }), 126 + ), 100 127 101 - externalUrl: string({ 102 - format: 'uri', 103 - maxLength: 512, 104 - description: 'Optional link to the track on an external platform (e.g. BandCamp, SoundCloud). Mutually exclusive with audio at the app level.', 105 - }), 106 - }, 107 - }), 108 - }), 128 + externalUrl: string({ 129 + format: "uri", 130 + maxLength: 512, 131 + description: 132 + "Optional link to the track on an external platform (e.g. BandCamp, SoundCloud). Mutually exclusive with audio at the app level.", 133 + }), 134 + }, 135 + }), 136 + }), 109 137 110 - artistCredit, 111 - }, 138 + artistCredit, 139 + }, 112 140 });
+11 -11
packages/lexicon/package.json
··· 1 1 { 2 - "name": "lexicon", 3 - "version": "1.0.0", 4 - "exports": { 5 - "./atcute-lexicon": "./dist/src/index.ts", 6 - "./lexicon": "./dist/lexicon" 7 - }, 8 - "dependencies": { 9 - "@atcute/lex-cli": "^2.5.3", 10 - "@atcute/lexicon-doc": "^2.1.1", 11 - "@atcute/lexicons": "^1.2.9" 12 - } 2 + "name": "lexicon", 3 + "version": "1.0.0", 4 + "exports": { 5 + "./atcute-lexicon": "./dist/src/index.ts", 6 + "./lexicon": "./dist/lexicon" 7 + }, 8 + "dependencies": { 9 + "@atcute/lex-cli": "^2.5.3", 10 + "@atcute/lexicon-doc": "^2.1.1", 11 + "@atcute/lexicons": "^1.2.9" 12 + } 13 13 }
+24 -24
tsconfig.json
··· 1 1 { 2 - "compilerOptions": { 3 - // Environment setup & latest features 4 - "lib": ["ESNext"], 5 - "target": "ESNext", 6 - "module": "Preserve", 7 - "moduleDetection": "force", 8 - "jsx": "react-jsx", 9 - "allowJs": true, 2 + "compilerOptions": { 3 + // Environment setup & latest features 4 + "lib": ["ESNext"], 5 + "target": "ESNext", 6 + "module": "Preserve", 7 + "moduleDetection": "force", 8 + "jsx": "react-jsx", 9 + "allowJs": true, 10 10 11 - // Bundler mode 12 - "moduleResolution": "bundler", 13 - "allowImportingTsExtensions": true, 14 - "verbatimModuleSyntax": true, 15 - "noEmit": true, 11 + // Bundler mode 12 + "moduleResolution": "bundler", 13 + "allowImportingTsExtensions": true, 14 + "verbatimModuleSyntax": true, 15 + "noEmit": true, 16 16 17 - // Best practices 18 - "strict": true, 19 - "skipLibCheck": true, 20 - "noFallthroughCasesInSwitch": true, 21 - "noUncheckedIndexedAccess": true, 22 - "noImplicitOverride": true, 17 + // Best practices 18 + "strict": true, 19 + "skipLibCheck": true, 20 + "noFallthroughCasesInSwitch": true, 21 + "noUncheckedIndexedAccess": true, 22 + "noImplicitOverride": true, 23 23 24 - // Some stricter flags (disabled by default) 25 - "noUnusedLocals": false, 26 - "noUnusedParameters": false, 27 - "noPropertyAccessFromIndexSignature": false 28 - } 24 + // Some stricter flags (disabled by default) 25 + "noUnusedLocals": false, 26 + "noUnusedParameters": false, 27 + "noPropertyAccessFromIndexSignature": false 28 + } 29 29 }