feat: init
a3e32071
28 file(s) · +1813 −0
| 1 | + | # Dependencies |
|
| 2 | + | node_modules/ |
|
| 3 | + | bun.lockb |
|
| 4 | + | ||
| 5 | + | # Build outputs |
|
| 6 | + | dist/ |
|
| 7 | + | *.tsbuildinfo |
|
| 8 | + | ||
| 9 | + | # Environment files |
|
| 10 | + | .env |
|
| 11 | + | .env.local |
|
| 12 | + | .dev.vars |
|
| 13 | + | ||
| 14 | + | # Wrangler |
|
| 15 | + | .wrangler/ |
|
| 16 | + | ||
| 17 | + | # IDE |
|
| 18 | + | .vscode/ |
|
| 19 | + | .idea/ |
|
| 20 | + | *.swp |
|
| 21 | + | *.swo |
|
| 22 | + | *~ |
|
| 23 | + | ||
| 24 | + | # OS |
|
| 25 | + | .DS_Store |
|
| 26 | + | Thumbs.db |
|
| 27 | + | ||
| 28 | + | # Logs |
|
| 29 | + | *.log |
|
| 30 | + | npm-debug.log* |
| 1 | + | # AT Feeds |
|
| 2 | + | ||
| 3 | + | A monorepo for indexing and displaying [Standard.site](https://standard.site) documents from the AT Protocol, powered by Cloudflare Workers, D1, and Queues. |
|
| 4 | + | ||
| 5 | + | ## Architecture |
|
| 6 | + | ||
| 7 | + | ``` |
|
| 8 | + | ┌─────────────────────────────────────────────────────────────┐ |
|
| 9 | + | │ Cloudflare │ |
|
| 10 | + | ├─────────────────────────────────────────────────────────────┤ |
|
| 11 | + | │ │ |
|
| 12 | + | │ ┌──────────────┐ ┌──────────────┐ ┌─────────────┐ │ |
|
| 13 | + | │ │ Pages │────▶│ Worker │────▶│ D1 │ │ |
|
| 14 | + | │ │ (Client) │ │ (API) │ │ (Database) │ │ |
|
| 15 | + | │ └──────────────┘ └──────────────┘ └─────────────┘ │ |
|
| 16 | + | │ ▲ ▲ │ |
|
| 17 | + | │ │ │ │ |
|
| 18 | + | │ ┌──────┴───────┐ ┌──────┴───────┐ │ |
|
| 19 | + | │ │ Queue │ │ Cron │ │ |
|
| 20 | + | │ │ (Resolver) │ │ (Refresh) │ │ |
|
| 21 | + | │ └──────┬───────┘ └──────────────┘ │ |
|
| 22 | + | │ │ │ |
|
| 23 | + | └──────────────────────────────┼──────────────────────────────┘ |
|
| 24 | + | │ POST /webhook/tap |
|
| 25 | + | ┌──────────┴───────────┐ |
|
| 26 | + | │ Tap Instance │ |
|
| 27 | + | │ (External VPS) │ |
|
| 28 | + | └──────────────────────┘ |
|
| 29 | + | ``` |
|
| 30 | + | ||
| 31 | + | **Components:** |
|
| 32 | + | ||
| 33 | + | 1. **Tap Indexer** (External) - Subscribes to the AT Protocol firehose and sends webhook events |
|
| 34 | + | 2. **Server** (`packages/server`) - Cloudflare Worker with Hono API, D1 database, and Queue consumer |
|
| 35 | + | 3. **Client** (`packages/client`) - Vite + React app deployed to Cloudflare Pages |
|
| 36 | + | ||
| 37 | + | ## Quick Start |
|
| 38 | + | ||
| 39 | + | ### Prerequisites |
|
| 40 | + | ||
| 41 | + | - [Bun](https://bun.sh) installed |
|
| 42 | + | - [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/) installed and authenticated |
|
| 43 | + | - A tap instance running somewhere (VPS, Fly.io, etc.) |
|
| 44 | + | ||
| 45 | + | ### Setup |
|
| 46 | + | ||
| 47 | + | 1. Install dependencies: |
|
| 48 | + | ||
| 49 | + | ```bash |
|
| 50 | + | bun install |
|
| 51 | + | ``` |
|
| 52 | + | ||
| 53 | + | 2. Create the D1 database: |
|
| 54 | + | ||
| 55 | + | ```bash |
|
| 56 | + | bun run db:create |
|
| 57 | + | ``` |
|
| 58 | + | ||
| 59 | + | Copy the database ID and update `packages/server/wrangler.toml`. |
|
| 60 | + | ||
| 61 | + | 3. Create the queue: |
|
| 62 | + | ||
| 63 | + | ```bash |
|
| 64 | + | wrangler queues create document-resolution |
|
| 65 | + | ``` |
|
| 66 | + | ||
| 67 | + | 4. Run database migrations: |
|
| 68 | + | ||
| 69 | + | ```bash |
|
| 70 | + | # Local development |
|
| 71 | + | bun run db:migrate |
|
| 72 | + | ||
| 73 | + | # Production |
|
| 74 | + | bun run db:migrate:prod |
|
| 75 | + | ``` |
|
| 76 | + | ||
| 77 | + | 5. (Optional) Set webhook secret: |
|
| 78 | + | ||
| 79 | + | ```bash |
|
| 80 | + | bun run secret:set |
|
| 81 | + | ``` |
|
| 82 | + | ||
| 83 | + | 6. Deploy the worker: |
|
| 84 | + | ||
| 85 | + | ```bash |
|
| 86 | + | bun run deploy |
|
| 87 | + | ``` |
|
| 88 | + | ||
| 89 | + | 7. Configure your tap instance: |
|
| 90 | + | ||
| 91 | + | ```bash |
|
| 92 | + | TAP_WEBHOOK_URL=https://your-worker.workers.dev/webhook/tap |
|
| 93 | + | TAP_SIGNAL_COLLECTION=site.standard.document |
|
| 94 | + | TAP_COLLECTION_FILTERS=site.standard.document |
|
| 95 | + | ``` |
|
| 96 | + | ||
| 97 | + | 8. Trigger initial resolution of existing records: |
|
| 98 | + | ||
| 99 | + | ```bash |
|
| 100 | + | curl -X POST https://your-worker.workers.dev/admin/resolve-all |
|
| 101 | + | ``` |
|
| 102 | + | ||
| 103 | + | ## Local Development |
|
| 104 | + | ||
| 105 | + | 1. Start the worker locally: |
|
| 106 | + | ||
| 107 | + | ```bash |
|
| 108 | + | bun run dev:server |
|
| 109 | + | ``` |
|
| 110 | + | ||
| 111 | + | The API will run on `http://localhost:8787`. |
|
| 112 | + | ||
| 113 | + | 2. Start the client (in a separate terminal): |
|
| 114 | + | ||
| 115 | + | ```bash |
|
| 116 | + | bun run dev:client |
|
| 117 | + | ``` |
|
| 118 | + | ||
| 119 | + | The client will run on `http://localhost:5173`. |
|
| 120 | + | ||
| 121 | + | ## API Endpoints |
|
| 122 | + | ||
| 123 | + | ### Health & Stats |
|
| 124 | + | ||
| 125 | + | | Endpoint | Method | Description | |
|
| 126 | + | |----------|--------|-------------| |
|
| 127 | + | | `/health` | GET | Health check | |
|
| 128 | + | | `/stats` | GET | Database statistics | |
|
| 129 | + | ||
| 130 | + | ### Feed Endpoints |
|
| 131 | + | ||
| 132 | + | | Endpoint | Method | Description | |
|
| 133 | + | |----------|--------|-------------| |
|
| 134 | + | | `/feed` | GET | Pre-resolved documents (fast) | |
|
| 135 | + | | `/feed-raw` | GET | Raw record references (for client-side resolution) | |
|
| 136 | + | | `/records/:did` | GET | Records by DID | |
|
| 137 | + | ||
| 138 | + | ### Webhook |
|
| 139 | + | ||
| 140 | + | | Endpoint | Method | Description | |
|
| 141 | + | |----------|--------|-------------| |
|
| 142 | + | | `/webhook/tap` | POST | Receives events from tap | |
|
| 143 | + | | `/webhook/tap/debug` | POST | Debug endpoint (echoes payload) | |
|
| 144 | + | ||
| 145 | + | ### Admin |
|
| 146 | + | ||
| 147 | + | | Endpoint | Method | Description | |
|
| 148 | + | |----------|--------|-------------| |
|
| 149 | + | | `/admin/resolve-all` | POST | Queue unresolved records for processing | |
|
| 150 | + | ||
| 151 | + | ## How It Works |
|
| 152 | + | ||
| 153 | + | 1. **Tap** subscribes to the AT Protocol firehose and filters for `site.standard.document` records |
|
| 154 | + | 2. **Webhook** receives events and stores record references in D1, then pushes to the resolution queue |
|
| 155 | + | 3. **Queue consumer** resolves each document (PDS lookup → record fetch → publication URL) and stores in `resolved_documents` |
|
| 156 | + | 4. **Cron job** (every 15 min) refreshes stale documents and processes any missed records |
|
| 157 | + | 5. **`/feed` endpoint** reads directly from `resolved_documents` for instant responses |
|
| 158 | + | ||
| 159 | + | ## Project Structure |
|
| 160 | + | ||
| 161 | + | ``` |
|
| 162 | + | . |
|
| 163 | + | ├── package.json # Root workspace config |
|
| 164 | + | └── packages/ |
|
| 165 | + | ├── server/ # Cloudflare Worker |
|
| 166 | + | │ ├── wrangler.toml # Worker configuration |
|
| 167 | + | │ ├── schema.sql # D1 database schema |
|
| 168 | + | │ ├── package.json |
|
| 169 | + | │ └── src/ |
|
| 170 | + | │ └── index.ts # API + Queue consumer + Cron handler |
|
| 171 | + | └── client/ # Vite + React app |
|
| 172 | + | ├── package.json |
|
| 173 | + | ├── vite.config.ts |
|
| 174 | + | └── src/ |
|
| 175 | + | ├── main.tsx |
|
| 176 | + | └── App.tsx |
|
| 177 | + | ``` |
|
| 178 | + | ||
| 179 | + | ## Scripts |
|
| 180 | + | ||
| 181 | + | ```bash |
|
| 182 | + | # Development |
|
| 183 | + | bun run dev # Run all packages in dev mode |
|
| 184 | + | bun run dev:server # Run worker locally |
|
| 185 | + | bun run dev:client # Run client locally |
|
| 186 | + | ||
| 187 | + | # Deployment |
|
| 188 | + | bun run deploy # Deploy worker to Cloudflare |
|
| 189 | + | bun run deploy:client # Deploy client to Cloudflare Pages |
|
| 190 | + | ||
| 191 | + | # Database |
|
| 192 | + | bun run db:create # Create D1 database |
|
| 193 | + | bun run db:migrate # Run migrations (local) |
|
| 194 | + | bun run db:migrate:prod # Run migrations (production) |
|
| 195 | + | ||
| 196 | + | # Secrets |
|
| 197 | + | bun run secret:set # Set TAP_WEBHOOK_SECRET |
|
| 198 | + | ``` |
|
| 199 | + | ||
| 200 | + | ## Resources |
|
| 201 | + | ||
| 202 | + | - [tap Documentation](https://github.com/bluesky-social/indigo/tree/main/cmd/tap) |
|
| 203 | + | - [AT Protocol Specs](https://atproto.com/) |
|
| 204 | + | - [Cloudflare Workers](https://developers.cloudflare.com/workers/) |
|
| 205 | + | - [Cloudflare D1](https://developers.cloudflare.com/d1/) |
|
| 206 | + | - [Cloudflare Queues](https://developers.cloudflare.com/queues/) |
|
| 207 | + | - [Hono Documentation](https://hono.dev/) |
|
| 208 | + | ||
| 209 | + | ## License |
|
| 210 | + | ||
| 211 | + | MIT |
| 1 | + | { |
|
| 2 | + | "lockfileVersion": 1, |
|
| 3 | + | "configVersion": 1, |
|
| 4 | + | "workspaces": { |
|
| 5 | + | "": { |
|
| 6 | + | "name": "atfeeds", |
|
| 7 | + | }, |
|
| 8 | + | "packages/client": { |
|
| 9 | + | "name": "@atfeeds/client", |
|
| 10 | + | "version": "1.0.0", |
|
| 11 | + | "dependencies": { |
|
| 12 | + | "react": "^18.2.0", |
|
| 13 | + | "react-dom": "^18.2.0", |
|
| 14 | + | }, |
|
| 15 | + | "devDependencies": { |
|
| 16 | + | "@types/react": "^18.2.0", |
|
| 17 | + | "@types/react-dom": "^18.2.0", |
|
| 18 | + | "@vitejs/plugin-react": "^4.0.0", |
|
| 19 | + | "typescript": "^5.0.0", |
|
| 20 | + | "vite": "^5.0.0", |
|
| 21 | + | }, |
|
| 22 | + | }, |
|
| 23 | + | "packages/cloudflare": { |
|
| 24 | + | "name": "atfeeds-cloudflare", |
|
| 25 | + | "version": "1.0.0", |
|
| 26 | + | "dependencies": { |
|
| 27 | + | "hono": "^4.0.0", |
|
| 28 | + | }, |
|
| 29 | + | "devDependencies": { |
|
| 30 | + | "@cloudflare/workers-types": "^4.20240117.0", |
|
| 31 | + | "wrangler": "^3.0.0", |
|
| 32 | + | }, |
|
| 33 | + | }, |
|
| 34 | + | "packages/server": { |
|
| 35 | + | "name": "@atfeeds/server", |
|
| 36 | + | "version": "1.0.0", |
|
| 37 | + | "dependencies": { |
|
| 38 | + | "hono": "^4.0.0", |
|
| 39 | + | }, |
|
| 40 | + | "devDependencies": { |
|
| 41 | + | "@types/bun": "^1.0.0", |
|
| 42 | + | }, |
|
| 43 | + | }, |
|
| 44 | + | }, |
|
| 45 | + | "packages": { |
|
| 46 | + | "@atfeeds/client": ["@atfeeds/client@workspace:packages/client"], |
|
| 47 | + | ||
| 48 | + | "@atfeeds/server": ["@atfeeds/server@workspace:packages/server"], |
|
| 49 | + | ||
| 50 | + | "@babel/code-frame": ["@babel/code-frame@7.28.6", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q=="], |
|
| 51 | + | ||
| 52 | + | "@babel/compat-data": ["@babel/compat-data@7.28.6", "", {}, "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg=="], |
|
| 53 | + | ||
| 54 | + | "@babel/core": ["@babel/core@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/template": "^7.28.6", "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw=="], |
|
| 55 | + | ||
| 56 | + | "@babel/generator": ["@babel/generator@7.28.6", "", { "dependencies": { "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw=="], |
|
| 57 | + | ||
| 58 | + | "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="], |
|
| 59 | + | ||
| 60 | + | "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], |
|
| 61 | + | ||
| 62 | + | "@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="], |
|
| 63 | + | ||
| 64 | + | "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="], |
|
| 65 | + | ||
| 66 | + | "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.28.6", "", {}, "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug=="], |
|
| 67 | + | ||
| 68 | + | "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], |
|
| 69 | + | ||
| 70 | + | "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], |
|
| 71 | + | ||
| 72 | + | "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], |
|
| 73 | + | ||
| 74 | + | "@babel/helpers": ["@babel/helpers@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="], |
|
| 75 | + | ||
| 76 | + | "@babel/parser": ["@babel/parser@7.28.6", "", { "dependencies": { "@babel/types": "^7.28.6" }, "bin": "./bin/babel-parser.js" }, "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ=="], |
|
| 77 | + | ||
| 78 | + | "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="], |
|
| 79 | + | ||
| 80 | + | "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="], |
|
| 81 | + | ||
| 82 | + | "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="], |
|
| 83 | + | ||
| 84 | + | "@babel/traverse": ["@babel/traverse@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.6", "@babel/template": "^7.28.6", "@babel/types": "^7.28.6", "debug": "^4.3.1" } }, "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg=="], |
|
| 85 | + | ||
| 86 | + | "@babel/types": ["@babel/types@7.28.6", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg=="], |
|
| 87 | + | ||
| 88 | + | "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.3.4", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-YLPHc8yASwjNkmcDMQMY35yiWjoKAKnhUbPRszBRS0YgH+IXtsMp61j+yTcnCE3oO2DgP0U3iejLC8FTtKDC8Q=="], |
|
| 89 | + | ||
| 90 | + | "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.0.2", "", { "peerDependencies": { "unenv": "2.0.0-rc.14", "workerd": "^1.20250124.0" }, "optionalPeers": ["workerd"] }, "sha512-nyzYnlZjjV5xT3LizahG1Iu6mnrCaxglJ04rZLpDwlDVDZ7v46lNsfxhV3A/xtfgQuSHmLnc6SVI+KwBpc3Lwg=="], |
|
| 91 | + | ||
| 92 | + | "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250718.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-FHf4t7zbVN8yyXgQ/r/GqLPaYZSGUVzeR7RnL28Mwj2djyw2ZergvytVc7fdGcczl6PQh+VKGfZCfUqpJlbi9g=="], |
|
| 93 | + | ||
| 94 | + | "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250718.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-fUiyUJYyqqp4NqJ0YgGtp4WJh/II/YZsUnEb6vVy5Oeas8lUOxnN+ZOJ8N/6/5LQCVAtYCChRiIrBbfhTn5Z8Q=="], |
|
| 95 | + | ||
| 96 | + | "@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250718.0", "", { "os": "linux", "cpu": "x64" }, "sha512-5+eb3rtJMiEwp08Kryqzzu8d1rUcK+gdE442auo5eniMpT170Dz0QxBrqkg2Z48SFUPYbj+6uknuA5tzdRSUSg=="], |
|
| 97 | + | ||
| 98 | + | "@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250718.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Aa2M/DVBEBQDdATMbn217zCSFKE+ud/teS+fFS+OQqKABLn0azO2qq6ANAHYOIE6Q3Sq4CxDIQr8lGdaJHwUog=="], |
|
| 99 | + | ||
| 100 | + | "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250718.0", "", { "os": "win32", "cpu": "x64" }, "sha512-dY16RXKffmugnc67LTbyjdDHZn5NoTF1yHEf2fN4+OaOnoGSp3N1x77QubTDwqZ9zECWxgQfDLjddcH8dWeFhg=="], |
|
| 101 | + | ||
| 102 | + | "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20260111.0", "", {}, "sha512-NFA2U+AqEWHkAmw6oRzNWJyc14rIvBlF/OlK3lixokunRKwyziuON07nWUZ0w0kKWlW4fJ/muA09tEUaQY07tA=="], |
|
| 103 | + | ||
| 104 | + | "@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="], |
|
| 105 | + | ||
| 106 | + | "@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], |
|
| 107 | + | ||
| 108 | + | "@esbuild-plugins/node-globals-polyfill": ["@esbuild-plugins/node-globals-polyfill@0.2.3", "", { "peerDependencies": { "esbuild": "*" } }, "sha512-r3MIryXDeXDOZh7ih1l/yE9ZLORCd5e8vWg02azWRGj5SPTuoh69A2AIyn0Z31V/kHBfZ4HgWJ+OK3GTTwLmnw=="], |
|
| 109 | + | ||
| 110 | + | "@esbuild-plugins/node-modules-polyfill": ["@esbuild-plugins/node-modules-polyfill@0.2.2", "", { "dependencies": { "escape-string-regexp": "^4.0.0", "rollup-plugin-node-polyfills": "^0.2.1" }, "peerDependencies": { "esbuild": "*" } }, "sha512-LXV7QsWJxRuMYvKbiznh+U1ilIop3g2TeKRzUxOG5X3YITc8JyyTa90BmLwqqv0YnX4v32CSlG+vsziZp9dMvA=="], |
|
| 111 | + | ||
| 112 | + | "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], |
|
| 113 | + | ||
| 114 | + | "@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], |
|
| 115 | + | ||
| 116 | + | "@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="], |
|
| 117 | + | ||
| 118 | + | "@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="], |
|
| 119 | + | ||
| 120 | + | "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="], |
|
| 121 | + | ||
| 122 | + | "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="], |
|
| 123 | + | ||
| 124 | + | "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="], |
|
| 125 | + | ||
| 126 | + | "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="], |
|
| 127 | + | ||
| 128 | + | "@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="], |
|
| 129 | + | ||
| 130 | + | "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="], |
|
| 131 | + | ||
| 132 | + | "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="], |
|
| 133 | + | ||
| 134 | + | "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="], |
|
| 135 | + | ||
| 136 | + | "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="], |
|
| 137 | + | ||
| 138 | + | "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="], |
|
| 139 | + | ||
| 140 | + | "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="], |
|
| 141 | + | ||
| 142 | + | "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="], |
|
| 143 | + | ||
| 144 | + | "@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="], |
|
| 145 | + | ||
| 146 | + | "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="], |
|
| 147 | + | ||
| 148 | + | "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="], |
|
| 149 | + | ||
| 150 | + | "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="], |
|
| 151 | + | ||
| 152 | + | "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="], |
|
| 153 | + | ||
| 154 | + | "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="], |
|
| 155 | + | ||
| 156 | + | "@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], |
|
| 157 | + | ||
| 158 | + | "@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="], |
|
| 159 | + | ||
| 160 | + | "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], |
|
| 161 | + | ||
| 162 | + | "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], |
|
| 163 | + | ||
| 164 | + | "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], |
|
| 165 | + | ||
| 166 | + | "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], |
|
| 167 | + | ||
| 168 | + | "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], |
|
| 169 | + | ||
| 170 | + | "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], |
|
| 171 | + | ||
| 172 | + | "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], |
|
| 173 | + | ||
| 174 | + | "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], |
|
| 175 | + | ||
| 176 | + | "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], |
|
| 177 | + | ||
| 178 | + | "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], |
|
| 179 | + | ||
| 180 | + | "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], |
|
| 181 | + | ||
| 182 | + | "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], |
|
| 183 | + | ||
| 184 | + | "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], |
|
| 185 | + | ||
| 186 | + | "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], |
|
| 187 | + | ||
| 188 | + | "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], |
|
| 189 | + | ||
| 190 | + | "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], |
|
| 191 | + | ||
| 192 | + | "@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="], |
|
| 193 | + | ||
| 194 | + | "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], |
|
| 195 | + | ||
| 196 | + | "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], |
|
| 197 | + | ||
| 198 | + | "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], |
|
| 199 | + | ||
| 200 | + | "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], |
|
| 201 | + | ||
| 202 | + | "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], |
|
| 203 | + | ||
| 204 | + | "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], |
|
| 205 | + | ||
| 206 | + | "@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=="], |
|
| 207 | + | ||
| 208 | + | "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="], |
|
| 209 | + | ||
| 210 | + | "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.55.1", "", { "os": "android", "cpu": "arm" }, "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg=="], |
|
| 211 | + | ||
| 212 | + | "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.55.1", "", { "os": "android", "cpu": "arm64" }, "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg=="], |
|
| 213 | + | ||
| 214 | + | "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.55.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg=="], |
|
| 215 | + | ||
| 216 | + | "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.55.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ=="], |
|
| 217 | + | ||
| 218 | + | "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.55.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg=="], |
|
| 219 | + | ||
| 220 | + | "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.55.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw=="], |
|
| 221 | + | ||
| 222 | + | "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.55.1", "", { "os": "linux", "cpu": "arm" }, "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ=="], |
|
| 223 | + | ||
| 224 | + | "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.55.1", "", { "os": "linux", "cpu": "arm" }, "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg=="], |
|
| 225 | + | ||
| 226 | + | "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.55.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ=="], |
|
| 227 | + | ||
| 228 | + | "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.55.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA=="], |
|
| 229 | + | ||
| 230 | + | "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g=="], |
|
| 231 | + | ||
| 232 | + | "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw=="], |
|
| 233 | + | ||
| 234 | + | "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.55.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw=="], |
|
| 235 | + | ||
| 236 | + | "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.55.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw=="], |
|
| 237 | + | ||
| 238 | + | "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw=="], |
|
| 239 | + | ||
| 240 | + | "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg=="], |
|
| 241 | + | ||
| 242 | + | "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.55.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg=="], |
|
| 243 | + | ||
| 244 | + | "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.55.1", "", { "os": "linux", "cpu": "x64" }, "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg=="], |
|
| 245 | + | ||
| 246 | + | "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.55.1", "", { "os": "linux", "cpu": "x64" }, "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w=="], |
|
| 247 | + | ||
| 248 | + | "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.55.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg=="], |
|
| 249 | + | ||
| 250 | + | "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.55.1", "", { "os": "none", "cpu": "arm64" }, "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw=="], |
|
| 251 | + | ||
| 252 | + | "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.55.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g=="], |
|
| 253 | + | ||
| 254 | + | "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.55.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA=="], |
|
| 255 | + | ||
| 256 | + | "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.55.1", "", { "os": "win32", "cpu": "x64" }, "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg=="], |
|
| 257 | + | ||
| 258 | + | "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.55.1", "", { "os": "win32", "cpu": "x64" }, "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw=="], |
|
| 259 | + | ||
| 260 | + | "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], |
|
| 261 | + | ||
| 262 | + | "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="], |
|
| 263 | + | ||
| 264 | + | "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="], |
|
| 265 | + | ||
| 266 | + | "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], |
|
| 267 | + | ||
| 268 | + | "@types/bun": ["@types/bun@1.3.5", "", { "dependencies": { "bun-types": "1.3.5" } }, "sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w=="], |
|
| 269 | + | ||
| 270 | + | "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], |
|
| 271 | + | ||
| 272 | + | "@types/node": ["@types/node@25.0.6", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-NNu0sjyNxpoiW3YuVFfNz7mxSQ+S4X2G28uqg2s+CzoqoQjLPsWSbsFFyztIAqt2vb8kfEAsJNepMGPTxFDx3Q=="], |
|
| 273 | + | ||
| 274 | + | "@types/prop-types": ["@types/prop-types@15.7.15", "", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="], |
|
| 275 | + | ||
| 276 | + | "@types/react": ["@types/react@18.3.27", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" } }, "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w=="], |
|
| 277 | + | ||
| 278 | + | "@types/react-dom": ["@types/react-dom@18.3.7", "", { "peerDependencies": { "@types/react": "^18.0.0" } }, "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ=="], |
|
| 279 | + | ||
| 280 | + | "@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="], |
|
| 281 | + | ||
| 282 | + | "acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="], |
|
| 283 | + | ||
| 284 | + | "acorn-walk": ["acorn-walk@8.3.2", "", {}, "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A=="], |
|
| 285 | + | ||
| 286 | + | "as-table": ["as-table@1.0.55", "", { "dependencies": { "printable-characters": "^1.0.42" } }, "sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ=="], |
|
| 287 | + | ||
| 288 | + | "atfeeds-cloudflare": ["atfeeds-cloudflare@workspace:packages/cloudflare"], |
|
| 289 | + | ||
| 290 | + | "baseline-browser-mapping": ["baseline-browser-mapping@2.9.14", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg=="], |
|
| 291 | + | ||
| 292 | + | "blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="], |
|
| 293 | + | ||
| 294 | + | "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], |
|
| 295 | + | ||
| 296 | + | "bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="], |
|
| 297 | + | ||
| 298 | + | "caniuse-lite": ["caniuse-lite@1.0.30001764", "", {}, "sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g=="], |
|
| 299 | + | ||
| 300 | + | "color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="], |
|
| 301 | + | ||
| 302 | + | "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], |
|
| 303 | + | ||
| 304 | + | "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], |
|
| 305 | + | ||
| 306 | + | "color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="], |
|
| 307 | + | ||
| 308 | + | "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], |
|
| 309 | + | ||
| 310 | + | "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], |
|
| 311 | + | ||
| 312 | + | "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], |
|
| 313 | + | ||
| 314 | + | "data-uri-to-buffer": ["data-uri-to-buffer@2.0.2", "", {}, "sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA=="], |
|
| 315 | + | ||
| 316 | + | "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], |
|
| 317 | + | ||
| 318 | + | "defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="], |
|
| 319 | + | ||
| 320 | + | "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], |
|
| 321 | + | ||
| 322 | + | "electron-to-chromium": ["electron-to-chromium@1.5.267", "", {}, "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw=="], |
|
| 323 | + | ||
| 324 | + | "esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], |
|
| 325 | + | ||
| 326 | + | "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], |
|
| 327 | + | ||
| 328 | + | "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], |
|
| 329 | + | ||
| 330 | + | "estree-walker": ["estree-walker@0.6.1", "", {}, "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w=="], |
|
| 331 | + | ||
| 332 | + | "exit-hook": ["exit-hook@2.2.1", "", {}, "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw=="], |
|
| 333 | + | ||
| 334 | + | "exsolve": ["exsolve@1.0.8", "", {}, "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA=="], |
|
| 335 | + | ||
| 336 | + | "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], |
|
| 337 | + | ||
| 338 | + | "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], |
|
| 339 | + | ||
| 340 | + | "get-source": ["get-source@2.0.12", "", { "dependencies": { "data-uri-to-buffer": "^2.0.0", "source-map": "^0.6.1" } }, "sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w=="], |
|
| 341 | + | ||
| 342 | + | "glob-to-regexp": ["glob-to-regexp@0.4.1", "", {}, "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="], |
|
| 343 | + | ||
| 344 | + | "hono": ["hono@4.11.3", "", {}, "sha512-PmQi306+M/ct/m5s66Hrg+adPnkD5jiO6IjA7WhWw0gSBSo1EcRegwuI1deZ+wd5pzCGynCcn2DprnE4/yEV4w=="], |
|
| 345 | + | ||
| 346 | + | "is-arrayish": ["is-arrayish@0.3.4", "", {}, "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA=="], |
|
| 347 | + | ||
| 348 | + | "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], |
|
| 349 | + | ||
| 350 | + | "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], |
|
| 351 | + | ||
| 352 | + | "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], |
|
| 353 | + | ||
| 354 | + | "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], |
|
| 355 | + | ||
| 356 | + | "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], |
|
| 357 | + | ||
| 358 | + | "magic-string": ["magic-string@0.25.9", "", { "dependencies": { "sourcemap-codec": "^1.4.8" } }, "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ=="], |
|
| 359 | + | ||
| 360 | + | "mime": ["mime@3.0.0", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="], |
|
| 361 | + | ||
| 362 | + | "miniflare": ["miniflare@3.20250718.3", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "stoppable": "1.1.0", "undici": "^5.28.5", "workerd": "1.20250718.0", "ws": "8.18.0", "youch": "3.3.4", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-JuPrDJhwLrNLEJiNLWO7ZzJrv/Vv9kZuwMYCfv0LskQDM6Eonw4OvywO3CH/wCGjgHzha/qyjUh8JQ068TjDgQ=="], |
|
| 363 | + | ||
| 364 | + | "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], |
|
| 365 | + | ||
| 366 | + | "mustache": ["mustache@4.2.0", "", { "bin": { "mustache": "bin/mustache" } }, "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ=="], |
|
| 367 | + | ||
| 368 | + | "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], |
|
| 369 | + | ||
| 370 | + | "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], |
|
| 371 | + | ||
| 372 | + | "ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="], |
|
| 373 | + | ||
| 374 | + | "path-to-regexp": ["path-to-regexp@6.3.0", "", {}, "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="], |
|
| 375 | + | ||
| 376 | + | "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], |
|
| 377 | + | ||
| 378 | + | "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], |
|
| 379 | + | ||
| 380 | + | "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], |
|
| 381 | + | ||
| 382 | + | "printable-characters": ["printable-characters@1.0.42", "", {}, "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ=="], |
|
| 383 | + | ||
| 384 | + | "react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="], |
|
| 385 | + | ||
| 386 | + | "react-dom": ["react-dom@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" }, "peerDependencies": { "react": "^18.3.1" } }, "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw=="], |
|
| 387 | + | ||
| 388 | + | "react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="], |
|
| 389 | + | ||
| 390 | + | "rollup": ["rollup@4.55.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.55.1", "@rollup/rollup-android-arm64": "4.55.1", "@rollup/rollup-darwin-arm64": "4.55.1", "@rollup/rollup-darwin-x64": "4.55.1", "@rollup/rollup-freebsd-arm64": "4.55.1", "@rollup/rollup-freebsd-x64": "4.55.1", "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", "@rollup/rollup-linux-arm-musleabihf": "4.55.1", "@rollup/rollup-linux-arm64-gnu": "4.55.1", "@rollup/rollup-linux-arm64-musl": "4.55.1", "@rollup/rollup-linux-loong64-gnu": "4.55.1", "@rollup/rollup-linux-loong64-musl": "4.55.1", "@rollup/rollup-linux-ppc64-gnu": "4.55.1", "@rollup/rollup-linux-ppc64-musl": "4.55.1", "@rollup/rollup-linux-riscv64-gnu": "4.55.1", "@rollup/rollup-linux-riscv64-musl": "4.55.1", "@rollup/rollup-linux-s390x-gnu": "4.55.1", "@rollup/rollup-linux-x64-gnu": "4.55.1", "@rollup/rollup-linux-x64-musl": "4.55.1", "@rollup/rollup-openbsd-x64": "4.55.1", "@rollup/rollup-openharmony-arm64": "4.55.1", "@rollup/rollup-win32-arm64-msvc": "4.55.1", "@rollup/rollup-win32-ia32-msvc": "4.55.1", "@rollup/rollup-win32-x64-gnu": "4.55.1", "@rollup/rollup-win32-x64-msvc": "4.55.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A=="], |
|
| 391 | + | ||
| 392 | + | "rollup-plugin-inject": ["rollup-plugin-inject@3.0.2", "", { "dependencies": { "estree-walker": "^0.6.1", "magic-string": "^0.25.3", "rollup-pluginutils": "^2.8.1" } }, "sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w=="], |
|
| 393 | + | ||
| 394 | + | "rollup-plugin-node-polyfills": ["rollup-plugin-node-polyfills@0.2.1", "", { "dependencies": { "rollup-plugin-inject": "^3.0.0" } }, "sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA=="], |
|
| 395 | + | ||
| 396 | + | "rollup-pluginutils": ["rollup-pluginutils@2.8.2", "", { "dependencies": { "estree-walker": "^0.6.1" } }, "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ=="], |
|
| 397 | + | ||
| 398 | + | "scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], |
|
| 399 | + | ||
| 400 | + | "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], |
|
| 401 | + | ||
| 402 | + | "sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], |
|
| 403 | + | ||
| 404 | + | "simple-swizzle": ["simple-swizzle@0.2.4", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw=="], |
|
| 405 | + | ||
| 406 | + | "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], |
|
| 407 | + | ||
| 408 | + | "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], |
|
| 409 | + | ||
| 410 | + | "sourcemap-codec": ["sourcemap-codec@1.4.8", "", {}, "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="], |
|
| 411 | + | ||
| 412 | + | "stacktracey": ["stacktracey@2.1.8", "", { "dependencies": { "as-table": "^1.0.36", "get-source": "^2.0.12" } }, "sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw=="], |
|
| 413 | + | ||
| 414 | + | "stoppable": ["stoppable@1.1.0", "", {}, "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw=="], |
|
| 415 | + | ||
| 416 | + | "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], |
|
| 417 | + | ||
| 418 | + | "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], |
|
| 419 | + | ||
| 420 | + | "ufo": ["ufo@1.6.2", "", {}, "sha512-heMioaxBcG9+Znsda5Q8sQbWnLJSl98AFDXTO80wELWEzX3hordXsTdxrIfMQoO9IY1MEnoGoPjpoKpMj+Yx0Q=="], |
|
| 421 | + | ||
| 422 | + | "undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="], |
|
| 423 | + | ||
| 424 | + | "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], |
|
| 425 | + | ||
| 426 | + | "unenv": ["unenv@2.0.0-rc.14", "", { "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.1", "ohash": "^2.0.10", "pathe": "^2.0.3", "ufo": "^1.5.4" } }, "sha512-od496pShMen7nOy5VmVJCnq8rptd45vh6Nx/r2iPbrba6pa6p+tS2ywuIHRZ/OBvSbQZB0kWvpO9XBNVFXHD3Q=="], |
|
| 427 | + | ||
| 428 | + | "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], |
|
| 429 | + | ||
| 430 | + | "vite": ["vite@5.4.21", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="], |
|
| 431 | + | ||
| 432 | + | "workerd": ["workerd@1.20250718.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250718.0", "@cloudflare/workerd-darwin-arm64": "1.20250718.0", "@cloudflare/workerd-linux-64": "1.20250718.0", "@cloudflare/workerd-linux-arm64": "1.20250718.0", "@cloudflare/workerd-windows-64": "1.20250718.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-kqkIJP/eOfDlUyBzU7joBg+tl8aB25gEAGqDap+nFWb+WHhnooxjGHgxPBy3ipw2hnShPFNOQt5lFRxbwALirg=="], |
|
| 433 | + | ||
| 434 | + | "wrangler": ["wrangler@3.114.16", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.3.4", "@cloudflare/unenv-preset": "2.0.2", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@esbuild-plugins/node-modules-polyfill": "0.2.2", "blake3-wasm": "2.1.5", "esbuild": "0.17.19", "miniflare": "3.20250718.3", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.14", "workerd": "1.20250718.0" }, "optionalDependencies": { "fsevents": "~2.3.2", "sharp": "^0.33.5" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250408.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-ve/ULRjrquu5BHNJ+1T0ipJJlJ6pD7qLmhwRkk0BsUIxatNe4HP4odX/R4Mq/RHG6LOnVAFs7SMeSHlz/1mNlQ=="], |
|
| 435 | + | ||
| 436 | + | "ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], |
|
| 437 | + | ||
| 438 | + | "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], |
|
| 439 | + | ||
| 440 | + | "youch": ["youch@3.3.4", "", { "dependencies": { "cookie": "^0.7.1", "mustache": "^4.2.0", "stacktracey": "^2.1.8" } }, "sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg=="], |
|
| 441 | + | ||
| 442 | + | "zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="], |
|
| 443 | + | ||
| 444 | + | "@cspotcode/source-map-support/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="], |
|
| 445 | + | ||
| 446 | + | "sharp/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], |
|
| 447 | + | ||
| 448 | + | "wrangler/esbuild": ["esbuild@0.17.19", "", { "optionalDependencies": { "@esbuild/android-arm": "0.17.19", "@esbuild/android-arm64": "0.17.19", "@esbuild/android-x64": "0.17.19", "@esbuild/darwin-arm64": "0.17.19", "@esbuild/darwin-x64": "0.17.19", "@esbuild/freebsd-arm64": "0.17.19", "@esbuild/freebsd-x64": "0.17.19", "@esbuild/linux-arm": "0.17.19", "@esbuild/linux-arm64": "0.17.19", "@esbuild/linux-ia32": "0.17.19", "@esbuild/linux-loong64": "0.17.19", "@esbuild/linux-mips64el": "0.17.19", "@esbuild/linux-ppc64": "0.17.19", "@esbuild/linux-riscv64": "0.17.19", "@esbuild/linux-s390x": "0.17.19", "@esbuild/linux-x64": "0.17.19", "@esbuild/netbsd-x64": "0.17.19", "@esbuild/openbsd-x64": "0.17.19", "@esbuild/sunos-x64": "0.17.19", "@esbuild/win32-arm64": "0.17.19", "@esbuild/win32-ia32": "0.17.19", "@esbuild/win32-x64": "0.17.19" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw=="], |
|
| 449 | + | ||
| 450 | + | "wrangler/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.17.19", "", { "os": "android", "cpu": "arm" }, "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A=="], |
|
| 451 | + | ||
| 452 | + | "wrangler/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.17.19", "", { "os": "android", "cpu": "arm64" }, "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA=="], |
|
| 453 | + | ||
| 454 | + | "wrangler/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.17.19", "", { "os": "android", "cpu": "x64" }, "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww=="], |
|
| 455 | + | ||
| 456 | + | "wrangler/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.17.19", "", { "os": "darwin", "cpu": "arm64" }, "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg=="], |
|
| 457 | + | ||
| 458 | + | "wrangler/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.17.19", "", { "os": "darwin", "cpu": "x64" }, "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw=="], |
|
| 459 | + | ||
| 460 | + | "wrangler/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.17.19", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ=="], |
|
| 461 | + | ||
| 462 | + | "wrangler/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.17.19", "", { "os": "freebsd", "cpu": "x64" }, "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ=="], |
|
| 463 | + | ||
| 464 | + | "wrangler/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.17.19", "", { "os": "linux", "cpu": "arm" }, "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA=="], |
|
| 465 | + | ||
| 466 | + | "wrangler/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.17.19", "", { "os": "linux", "cpu": "arm64" }, "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg=="], |
|
| 467 | + | ||
| 468 | + | "wrangler/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.17.19", "", { "os": "linux", "cpu": "ia32" }, "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ=="], |
|
| 469 | + | ||
| 470 | + | "wrangler/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ=="], |
|
| 471 | + | ||
| 472 | + | "wrangler/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A=="], |
|
| 473 | + | ||
| 474 | + | "wrangler/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.17.19", "", { "os": "linux", "cpu": "ppc64" }, "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg=="], |
|
| 475 | + | ||
| 476 | + | "wrangler/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA=="], |
|
| 477 | + | ||
| 478 | + | "wrangler/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.17.19", "", { "os": "linux", "cpu": "s390x" }, "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q=="], |
|
| 479 | + | ||
| 480 | + | "wrangler/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.17.19", "", { "os": "linux", "cpu": "x64" }, "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw=="], |
|
| 481 | + | ||
| 482 | + | "wrangler/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.17.19", "", { "os": "none", "cpu": "x64" }, "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q=="], |
|
| 483 | + | ||
| 484 | + | "wrangler/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.17.19", "", { "os": "openbsd", "cpu": "x64" }, "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g=="], |
|
| 485 | + | ||
| 486 | + | "wrangler/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.17.19", "", { "os": "sunos", "cpu": "x64" }, "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg=="], |
|
| 487 | + | ||
| 488 | + | "wrangler/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.17.19", "", { "os": "win32", "cpu": "arm64" }, "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag=="], |
|
| 489 | + | ||
| 490 | + | "wrangler/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.17.19", "", { "os": "win32", "cpu": "ia32" }, "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw=="], |
|
| 491 | + | ||
| 492 | + | "wrangler/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.17.19", "", { "os": "win32", "cpu": "x64" }, "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA=="], |
|
| 493 | + | } |
|
| 494 | + | } |
| 1 | + | { |
|
| 2 | + | "name": "atfeeds", |
|
| 3 | + | "version": "1.0.0", |
|
| 4 | + | "private": true, |
|
| 5 | + | "workspaces": [ |
|
| 6 | + | "packages/*" |
|
| 7 | + | ], |
|
| 8 | + | "scripts": { |
|
| 9 | + | "dev": "bun run --filter '*' dev", |
|
| 10 | + | "dev:server": "cd packages/server && npm run dev", |
|
| 11 | + | "dev:client": "cd packages/client && npm run dev", |
|
| 12 | + | "deploy": "cd packages/server && npm run deploy", |
|
| 13 | + | "deploy:client": "cd packages/client && npm run pages:deploy", |
|
| 14 | + | "db:create": "cd packages/server && npm run db:create", |
|
| 15 | + | "db:migrate": "cd packages/server && npm run db:migrate", |
|
| 16 | + | "db:migrate:prod": "cd packages/server && npm run db:migrate:prod", |
|
| 17 | + | "secret:set": "cd packages/server && npm run secret:set" |
|
| 18 | + | } |
|
| 19 | + | } |
| 1 | + | <!DOCTYPE html> |
|
| 2 | + | <html lang="en"> |
|
| 3 | + | <head> |
|
| 4 | + | <meta charset="UTF-8" /> |
|
| 5 | + | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
|
| 6 | + | <title>AT Feeds - Standard.site Documents</title> |
|
| 7 | + | </head> |
|
| 8 | + | <body> |
|
| 9 | + | <div id="root"></div> |
|
| 10 | + | <script type="module" src="/src/main.tsx"></script> |
|
| 11 | + | </body> |
|
| 12 | + | </html> |
| 1 | + | { |
|
| 2 | + | "name": "@atfeeds/client", |
|
| 3 | + | "version": "1.0.0", |
|
| 4 | + | "type": "module", |
|
| 5 | + | "scripts": { |
|
| 6 | + | "dev": "vite", |
|
| 7 | + | "build": "tsc && vite build", |
|
| 8 | + | "preview": "vite preview", |
|
| 9 | + | "pages:deploy": "vite build && wrangler pages deploy dist --project-name=atfeeds" |
|
| 10 | + | }, |
|
| 11 | + | "dependencies": { |
|
| 12 | + | "react": "^18.2.0", |
|
| 13 | + | "react-dom": "^18.2.0" |
|
| 14 | + | }, |
|
| 15 | + | "devDependencies": { |
|
| 16 | + | "@types/react": "^18.2.0", |
|
| 17 | + | "@types/react-dom": "^18.2.0", |
|
| 18 | + | "@vitejs/plugin-react": "^4.0.0", |
|
| 19 | + | "typescript": "^5.0.0", |
|
| 20 | + | "vite": "^5.0.0" |
|
| 21 | + | } |
|
| 22 | + | } |
| 1 | + | import { useEffect, useState } from "react"; |
|
| 2 | + | ||
| 3 | + | // API base URL - empty for same-origin (local dev), or set via env var for production |
|
| 4 | + | const API_URL = "https://atfeeds-api.stevedsimkins.workers.dev"; |
|
| 5 | + | ||
| 6 | + | interface Document { |
|
| 7 | + | uri: string; |
|
| 8 | + | did: string; |
|
| 9 | + | rkey: string; |
|
| 10 | + | title: string; |
|
| 11 | + | path: string | null; |
|
| 12 | + | site: string | null; |
|
| 13 | + | content: { |
|
| 14 | + | $type: string; |
|
| 15 | + | markdown?: string; |
|
| 16 | + | } | null; |
|
| 17 | + | textContent: string | null; |
|
| 18 | + | publishedAt: string | null; |
|
| 19 | + | viewUrl: string | null; |
|
| 20 | + | } |
|
| 21 | + | ||
| 22 | + | interface FeedResponse { |
|
| 23 | + | count: number; |
|
| 24 | + | limit: number; |
|
| 25 | + | offset: number; |
|
| 26 | + | documents: Document[]; |
|
| 27 | + | } |
|
| 28 | + | ||
| 29 | + | function App() { |
|
| 30 | + | const [documents, setDocuments] = useState<Document[]>([]); |
|
| 31 | + | const [loading, setLoading] = useState(true); |
|
| 32 | + | const [error, setError] = useState<string | null>(null); |
|
| 33 | + | ||
| 34 | + | useEffect(() => { |
|
| 35 | + | async function fetchFeed() { |
|
| 36 | + | try { |
|
| 37 | + | const response = await fetch(`${API_URL}/feed`); |
|
| 38 | + | if (!response.ok) { |
|
| 39 | + | throw new Error("Failed to fetch feed"); |
|
| 40 | + | } |
|
| 41 | + | const data: FeedResponse = await response.json(); |
|
| 42 | + | setDocuments(data.documents); |
|
| 43 | + | } catch (err) { |
|
| 44 | + | setError(err instanceof Error ? err.message : "Unknown error"); |
|
| 45 | + | } finally { |
|
| 46 | + | setLoading(false); |
|
| 47 | + | } |
|
| 48 | + | } |
|
| 49 | + | ||
| 50 | + | fetchFeed(); |
|
| 51 | + | }, []); |
|
| 52 | + | ||
| 53 | + | const formatDate = (dateString: string | null) => { |
|
| 54 | + | if (!dateString) return "Unknown date"; |
|
| 55 | + | return new Date(dateString).toLocaleDateString("en-US", { |
|
| 56 | + | year: "numeric", |
|
| 57 | + | month: "long", |
|
| 58 | + | day: "numeric", |
|
| 59 | + | }); |
|
| 60 | + | }; |
|
| 61 | + | ||
| 62 | + | const truncateText = (text: string | null, maxLength: number = 200) => { |
|
| 63 | + | if (!text) return ""; |
|
| 64 | + | if (text.length <= maxLength) return text; |
|
| 65 | + | return text.slice(0, maxLength) + "..."; |
|
| 66 | + | }; |
|
| 67 | + | ||
| 68 | + | if (loading) { |
|
| 69 | + | return ( |
|
| 70 | + | <div className="container"> |
|
| 71 | + | <h1>Standard.site Documents</h1> |
|
| 72 | + | <p className="loading">Loading documents...</p> |
|
| 73 | + | </div> |
|
| 74 | + | ); |
|
| 75 | + | } |
|
| 76 | + | ||
| 77 | + | if (error) { |
|
| 78 | + | return ( |
|
| 79 | + | <div className="container"> |
|
| 80 | + | <h1>Standard.site Documents</h1> |
|
| 81 | + | <p className="error">Error: {error}</p> |
|
| 82 | + | </div> |
|
| 83 | + | ); |
|
| 84 | + | } |
|
| 85 | + | ||
| 86 | + | return ( |
|
| 87 | + | <div className="container"> |
|
| 88 | + | <h1>Standard.site Documents</h1> |
|
| 89 | + | <p className="subtitle">{documents.length} documents found</p> |
|
| 90 | + | ||
| 91 | + | <div className="feed"> |
|
| 92 | + | {documents.map((doc) => ( |
|
| 93 | + | <article key={doc.uri} className="document-card"> |
|
| 94 | + | <h2> |
|
| 95 | + | {doc.viewUrl ? ( |
|
| 96 | + | <a href={doc.viewUrl} target="_blank" rel="noopener noreferrer"> |
|
| 97 | + | {doc.title} |
|
| 98 | + | </a> |
|
| 99 | + | ) : ( |
|
| 100 | + | doc.title |
|
| 101 | + | )} |
|
| 102 | + | </h2> |
|
| 103 | + | <time dateTime={doc.publishedAt || undefined}> |
|
| 104 | + | {formatDate(doc.publishedAt)} |
|
| 105 | + | </time> |
|
| 106 | + | {doc.textContent && ( |
|
| 107 | + | <p className="excerpt">{truncateText(doc.textContent)}</p> |
|
| 108 | + | )} |
|
| 109 | + | {doc.viewUrl && ( |
|
| 110 | + | <a |
|
| 111 | + | href={doc.viewUrl} |
|
| 112 | + | target="_blank" |
|
| 113 | + | rel="noopener noreferrer" |
|
| 114 | + | className="read-more" |
|
| 115 | + | > |
|
| 116 | + | Read on author's site |
|
| 117 | + | </a> |
|
| 118 | + | )} |
|
| 119 | + | </article> |
|
| 120 | + | ))} |
|
| 121 | + | </div> |
|
| 122 | + | ||
| 123 | + | {documents.length === 0 && <p className="empty">No documents found.</p>} |
|
| 124 | + | </div> |
|
| 125 | + | ); |
|
| 126 | + | } |
|
| 127 | + | ||
| 128 | + | export default App; |
| 1 | + | * { |
|
| 2 | + | box-sizing: border-box; |
|
| 3 | + | margin: 0; |
|
| 4 | + | padding: 0; |
|
| 5 | + | } |
|
| 6 | + | ||
| 7 | + | body { |
|
| 8 | + | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, |
|
| 9 | + | Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; |
|
| 10 | + | line-height: 1.6; |
|
| 11 | + | color: #333; |
|
| 12 | + | background: #f5f5f5; |
|
| 13 | + | } |
|
| 14 | + | ||
| 15 | + | .container { |
|
| 16 | + | max-width: 800px; |
|
| 17 | + | margin: 0 auto; |
|
| 18 | + | padding: 2rem 1rem; |
|
| 19 | + | } |
|
| 20 | + | ||
| 21 | + | h1 { |
|
| 22 | + | font-size: 2rem; |
|
| 23 | + | margin-bottom: 0.5rem; |
|
| 24 | + | } |
|
| 25 | + | ||
| 26 | + | .subtitle { |
|
| 27 | + | color: #666; |
|
| 28 | + | margin-bottom: 2rem; |
|
| 29 | + | } |
|
| 30 | + | ||
| 31 | + | .loading, |
|
| 32 | + | .error, |
|
| 33 | + | .empty { |
|
| 34 | + | text-align: center; |
|
| 35 | + | padding: 2rem; |
|
| 36 | + | color: #666; |
|
| 37 | + | } |
|
| 38 | + | ||
| 39 | + | .error { |
|
| 40 | + | color: #c00; |
|
| 41 | + | } |
|
| 42 | + | ||
| 43 | + | .feed { |
|
| 44 | + | display: flex; |
|
| 45 | + | flex-direction: column; |
|
| 46 | + | gap: 1.5rem; |
|
| 47 | + | } |
|
| 48 | + | ||
| 49 | + | .document-card { |
|
| 50 | + | background: white; |
|
| 51 | + | border-radius: 8px; |
|
| 52 | + | padding: 1.5rem; |
|
| 53 | + | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); |
|
| 54 | + | } |
|
| 55 | + | ||
| 56 | + | .document-card h2 { |
|
| 57 | + | font-size: 1.25rem; |
|
| 58 | + | margin-bottom: 0.5rem; |
|
| 59 | + | } |
|
| 60 | + | ||
| 61 | + | .document-card h2 a { |
|
| 62 | + | color: #1a1a1a; |
|
| 63 | + | text-decoration: none; |
|
| 64 | + | } |
|
| 65 | + | ||
| 66 | + | .document-card h2 a:hover { |
|
| 67 | + | color: #0066cc; |
|
| 68 | + | text-decoration: underline; |
|
| 69 | + | } |
|
| 70 | + | ||
| 71 | + | .document-card time { |
|
| 72 | + | display: block; |
|
| 73 | + | font-size: 0.875rem; |
|
| 74 | + | color: #666; |
|
| 75 | + | margin-bottom: 0.75rem; |
|
| 76 | + | } |
|
| 77 | + | ||
| 78 | + | .document-card .excerpt { |
|
| 79 | + | color: #444; |
|
| 80 | + | margin-bottom: 1rem; |
|
| 81 | + | } |
|
| 82 | + | ||
| 83 | + | .document-card .read-more { |
|
| 84 | + | display: inline-block; |
|
| 85 | + | color: #0066cc; |
|
| 86 | + | text-decoration: none; |
|
| 87 | + | font-size: 0.875rem; |
|
| 88 | + | } |
|
| 89 | + | ||
| 90 | + | .document-card .read-more:hover { |
|
| 91 | + | text-decoration: underline; |
|
| 92 | + | } |
| 1 | + | import React from "react"; |
|
| 2 | + | import ReactDOM from "react-dom/client"; |
|
| 3 | + | import App from "./App"; |
|
| 4 | + | import "./index.css"; |
|
| 5 | + | ||
| 6 | + | ReactDOM.createRoot(document.getElementById("root")!).render( |
|
| 7 | + | <React.StrictMode> |
|
| 8 | + | <App /> |
|
| 9 | + | </React.StrictMode>, |
|
| 10 | + | ); |
| 1 | + | /// <reference types="vite/client" /> |
|
| 2 | + | ||
| 3 | + | interface ImportMetaEnv { |
|
| 4 | + | readonly VITE_API_URL: string |
|
| 5 | + | } |
|
| 6 | + | ||
| 7 | + | interface ImportMeta { |
|
| 8 | + | readonly env: ImportMetaEnv |
|
| 9 | + | } |
| 1 | + | { |
|
| 2 | + | "compilerOptions": { |
|
| 3 | + | "target": "ES2020", |
|
| 4 | + | "useDefineForClassFields": true, |
|
| 5 | + | "lib": ["ES2020", "DOM", "DOM.Iterable"], |
|
| 6 | + | "module": "ESNext", |
|
| 7 | + | "skipLibCheck": true, |
|
| 8 | + | "moduleResolution": "bundler", |
|
| 9 | + | "allowImportingTsExtensions": true, |
|
| 10 | + | "resolveJsonModule": true, |
|
| 11 | + | "isolatedModules": true, |
|
| 12 | + | "noEmit": true, |
|
| 13 | + | "jsx": "react-jsx", |
|
| 14 | + | "strict": true, |
|
| 15 | + | "noUnusedLocals": true, |
|
| 16 | + | "noUnusedParameters": true, |
|
| 17 | + | "noFallthroughCasesInSwitch": true |
|
| 18 | + | }, |
|
| 19 | + | "include": ["src"], |
|
| 20 | + | "references": [{ "path": "./tsconfig.node.json" }] |
|
| 21 | + | } |
| 1 | + | { |
|
| 2 | + | "compilerOptions": { |
|
| 3 | + | "composite": true, |
|
| 4 | + | "skipLibCheck": true, |
|
| 5 | + | "module": "ESNext", |
|
| 6 | + | "moduleResolution": "bundler", |
|
| 7 | + | "allowSyntheticDefaultImports": true, |
|
| 8 | + | "strict": true |
|
| 9 | + | }, |
|
| 10 | + | "include": ["vite.config.ts"] |
|
| 11 | + | } |
| 1 | + | import { defineConfig } from 'vite' |
|
| 2 | + | import react from '@vitejs/plugin-react' |
|
| 3 | + | ||
| 4 | + | export default defineConfig({ |
|
| 5 | + | plugins: [react()], |
|
| 6 | + | // Define env variables for the client |
|
| 7 | + | define: { |
|
| 8 | + | 'import.meta.env.VITE_API_URL': JSON.stringify(process.env.VITE_API_URL || ''), |
|
| 9 | + | }, |
|
| 10 | + | server: { |
|
| 11 | + | port: 5173, |
|
| 12 | + | proxy: { |
|
| 13 | + | '/feed-raw': 'http://localhost:3000', |
|
| 14 | + | '/feed': 'http://localhost:3000', |
|
| 15 | + | '/health': 'http://localhost:3000', |
|
| 16 | + | }, |
|
| 17 | + | }, |
|
| 18 | + | build: { |
|
| 19 | + | outDir: 'dist', |
|
| 20 | + | sourcemap: true, |
|
| 21 | + | }, |
|
| 22 | + | }) |
| 1 | + | { |
|
| 2 | + | "name": "@atfeeds/server", |
|
| 3 | + | "version": "1.0.0", |
|
| 4 | + | "private": true, |
|
| 5 | + | "scripts": { |
|
| 6 | + | "dev": "wrangler dev", |
|
| 7 | + | "deploy": "wrangler deploy", |
|
| 8 | + | "db:create": "wrangler d1 create atfeeds-db", |
|
| 9 | + | "db:migrate": "wrangler d1 execute atfeeds-db --local --file=./schema.sql", |
|
| 10 | + | "db:migrate:prod": "wrangler d1 execute atfeeds-db --remote --file=./schema.sql", |
|
| 11 | + | "secret:set": "wrangler secret put TAP_WEBHOOK_SECRET" |
|
| 12 | + | }, |
|
| 13 | + | "dependencies": { |
|
| 14 | + | "hono": "^4.0.0" |
|
| 15 | + | }, |
|
| 16 | + | "devDependencies": { |
|
| 17 | + | "@cloudflare/workers-types": "^4.20240117.0", |
|
| 18 | + | "wrangler": "^3.0.0" |
|
| 19 | + | } |
|
| 20 | + | } |
| 1 | + | -- Records synced from external tap instance |
|
| 2 | + | CREATE TABLE IF NOT EXISTS repo_records ( |
|
| 3 | + | id INTEGER PRIMARY KEY AUTOINCREMENT, |
|
| 4 | + | did TEXT NOT NULL, |
|
| 5 | + | rkey TEXT NOT NULL, |
|
| 6 | + | collection TEXT NOT NULL, |
|
| 7 | + | cid TEXT, |
|
| 8 | + | synced_at TEXT DEFAULT (datetime('now')), |
|
| 9 | + | UNIQUE(did, collection, rkey) |
|
| 10 | + | ); |
|
| 11 | + | ||
| 12 | + | CREATE INDEX IF NOT EXISTS idx_repo_records_collection ON repo_records(collection); |
|
| 13 | + | CREATE INDEX IF NOT EXISTS idx_repo_records_did ON repo_records(did); |
|
| 14 | + | CREATE INDEX IF NOT EXISTS idx_repo_records_rkey ON repo_records(rkey DESC); |
|
| 15 | + | ||
| 16 | + | -- Cache for resolved PDS endpoints |
|
| 17 | + | CREATE TABLE IF NOT EXISTS pds_cache ( |
|
| 18 | + | did TEXT PRIMARY KEY, |
|
| 19 | + | pds_endpoint TEXT NOT NULL, |
|
| 20 | + | cached_at TEXT DEFAULT (datetime('now')) |
|
| 21 | + | ); |
|
| 22 | + | ||
| 23 | + | -- Sync metadata to track last sync |
|
| 24 | + | CREATE TABLE IF NOT EXISTS sync_metadata ( |
|
| 25 | + | key TEXT PRIMARY KEY, |
|
| 26 | + | value TEXT NOT NULL, |
|
| 27 | + | updated_at TEXT DEFAULT (datetime('now')) |
|
| 28 | + | ); |
|
| 29 | + | ||
| 30 | + | -- Pre-resolved documents for fast feed serving |
|
| 31 | + | CREATE TABLE IF NOT EXISTS resolved_documents ( |
|
| 32 | + | uri TEXT PRIMARY KEY, |
|
| 33 | + | did TEXT NOT NULL, |
|
| 34 | + | rkey TEXT NOT NULL, |
|
| 35 | + | title TEXT, |
|
| 36 | + | path TEXT, |
|
| 37 | + | site TEXT, |
|
| 38 | + | content TEXT, -- JSON blob |
|
| 39 | + | text_content TEXT, |
|
| 40 | + | published_at TEXT, |
|
| 41 | + | view_url TEXT, |
|
| 42 | + | resolved_at TEXT DEFAULT (datetime('now')), |
|
| 43 | + | stale_at TEXT -- When this record should be re-resolved |
|
| 44 | + | ); |
|
| 45 | + | ||
| 46 | + | CREATE INDEX IF NOT EXISTS idx_resolved_documents_rkey ON resolved_documents(rkey DESC); |
|
| 47 | + | CREATE INDEX IF NOT EXISTS idx_resolved_documents_stale ON resolved_documents(stale_at); |
| 1 | + | import { Hono } from "hono"; |
|
| 2 | + | import { cors } from "hono/cors"; |
|
| 3 | + | import type { Bindings } from "./types"; |
|
| 4 | + | import { health, webhook, feed, stats, records } from "./routes"; |
|
| 5 | + | ||
| 6 | + | const app = new Hono<{ Bindings: Bindings }>(); |
|
| 7 | + | ||
| 8 | + | // Middleware |
|
| 9 | + | app.use("*", cors()); |
|
| 10 | + | ||
| 11 | + | // Mount routes |
|
| 12 | + | app.route("/health", health); |
|
| 13 | + | app.route("/webhook", webhook); |
|
| 14 | + | app.route("/feed", feed); |
|
| 15 | + | app.route("/stats", stats); |
|
| 16 | + | app.route("/records", records); |
|
| 17 | + | ||
| 18 | + | // Legacy alias: /feed-raw -> /feed/raw |
|
| 19 | + | app.get("/feed-raw", async (c) => { |
|
| 20 | + | const db = c.env.DB; |
|
| 21 | + | const limit = Math.min(Number(c.req.query("limit")) || 15, 15); |
|
| 22 | + | const offset = Number(c.req.query("offset")) || 0; |
|
| 23 | + | ||
| 24 | + | const { results } = await db |
|
| 25 | + | .prepare( |
|
| 26 | + | `SELECT did, rkey FROM repo_records |
|
| 27 | + | WHERE collection = 'site.standard.document' |
|
| 28 | + | ORDER BY rkey DESC |
|
| 29 | + | LIMIT ? OFFSET ?`, |
|
| 30 | + | ) |
|
| 31 | + | .bind(limit, offset) |
|
| 32 | + | .all<{ did: string; rkey: string }>(); |
|
| 33 | + | ||
| 34 | + | return c.json({ |
|
| 35 | + | count: results?.length || 0, |
|
| 36 | + | limit, |
|
| 37 | + | offset, |
|
| 38 | + | records: results || [], |
|
| 39 | + | }); |
|
| 40 | + | }); |
|
| 41 | + | ||
| 42 | + | // 404 handler |
|
| 43 | + | app.notFound((c) => { |
|
| 44 | + | return c.json({ error: "Not found" }, 404); |
|
| 45 | + | }); |
|
| 46 | + | ||
| 47 | + | // Export for Cloudflare Workers |
|
| 48 | + | export default { |
|
| 49 | + | fetch: app.fetch, |
|
| 50 | + | }; |
| 1 | + | import { Hono } from "hono"; |
|
| 2 | + | import type { Bindings } from "../types"; |
|
| 3 | + | ||
| 4 | + | const feed = new Hono<{ Bindings: Bindings }>(); |
|
| 5 | + | ||
| 6 | + | // Get raw feed data (for client-side fetching) |
|
| 7 | + | // Accessible at both /feed/raw and /feed-raw (via alias in index.ts) |
|
| 8 | + | feed.get("/raw", async (c) => { |
|
| 9 | + | try { |
|
| 10 | + | const db = c.env.DB; |
|
| 11 | + | const limit = Math.min(Number(c.req.query("limit")) || 15, 15); |
|
| 12 | + | const offset = Number(c.req.query("offset")) || 0; |
|
| 13 | + | ||
| 14 | + | const { results } = await db |
|
| 15 | + | .prepare( |
|
| 16 | + | `SELECT did, rkey FROM repo_records |
|
| 17 | + | WHERE collection = 'site.standard.document' |
|
| 18 | + | ORDER BY rkey DESC |
|
| 19 | + | LIMIT ? OFFSET ?` |
|
| 20 | + | ) |
|
| 21 | + | .bind(limit, offset) |
|
| 22 | + | .all<{ did: string; rkey: string }>(); |
|
| 23 | + | ||
| 24 | + | return c.json({ |
|
| 25 | + | count: results?.length || 0, |
|
| 26 | + | limit, |
|
| 27 | + | offset, |
|
| 28 | + | records: results || [], |
|
| 29 | + | }); |
|
| 30 | + | } catch (error) { |
|
| 31 | + | return c.json( |
|
| 32 | + | { error: "Failed to fetch feed", details: String(error) }, |
|
| 33 | + | 500 |
|
| 34 | + | ); |
|
| 35 | + | } |
|
| 36 | + | }); |
|
| 37 | + | ||
| 38 | + | // Get feed of documents with resolved URLs (server-side resolution) |
|
| 39 | + | feed.get("/", async (c) => { |
|
| 40 | + | try { |
|
| 41 | + | const db = c.env.DB; |
|
| 42 | + | const limit = Number(c.req.query("limit")) || 50; |
|
| 43 | + | const offset = Number(c.req.query("offset")) || 0; |
|
| 44 | + | ||
| 45 | + | const { results } = await db |
|
| 46 | + | .prepare( |
|
| 47 | + | `SELECT uri, did, rkey, title, path, site, content, text_content, published_at, view_url |
|
| 48 | + | FROM resolved_documents |
|
| 49 | + | ORDER BY rkey DESC |
|
| 50 | + | LIMIT ? OFFSET ?` |
|
| 51 | + | ) |
|
| 52 | + | .bind(limit, offset) |
|
| 53 | + | .all<{ |
|
| 54 | + | uri: string; |
|
| 55 | + | did: string; |
|
| 56 | + | rkey: string; |
|
| 57 | + | title: string | null; |
|
| 58 | + | path: string | null; |
|
| 59 | + | site: string | null; |
|
| 60 | + | content: string | null; |
|
| 61 | + | text_content: string | null; |
|
| 62 | + | published_at: string | null; |
|
| 63 | + | view_url: string | null; |
|
| 64 | + | }>(); |
|
| 65 | + | ||
| 66 | + | const documents = (results || []).map((doc) => ({ |
|
| 67 | + | uri: doc.uri, |
|
| 68 | + | did: doc.did, |
|
| 69 | + | rkey: doc.rkey, |
|
| 70 | + | title: doc.title || "Untitled", |
|
| 71 | + | path: doc.path, |
|
| 72 | + | site: doc.site, |
|
| 73 | + | content: doc.content ? JSON.parse(doc.content) : null, |
|
| 74 | + | textContent: doc.text_content, |
|
| 75 | + | publishedAt: doc.published_at, |
|
| 76 | + | viewUrl: doc.view_url, |
|
| 77 | + | })); |
|
| 78 | + | ||
| 79 | + | return c.json({ |
|
| 80 | + | count: documents.length, |
|
| 81 | + | limit, |
|
| 82 | + | offset, |
|
| 83 | + | documents, |
|
| 84 | + | }); |
|
| 85 | + | } catch (error) { |
|
| 86 | + | return c.json( |
|
| 87 | + | { error: "Failed to fetch feed", details: String(error) }, |
|
| 88 | + | 500 |
|
| 89 | + | ); |
|
| 90 | + | } |
|
| 91 | + | }); |
|
| 92 | + | ||
| 93 | + | export default feed; |
| 1 | + | import { Hono } from "hono"; |
|
| 2 | + | import type { Bindings } from "../types"; |
|
| 3 | + | ||
| 4 | + | const health = new Hono<{ Bindings: Bindings }>(); |
|
| 5 | + | ||
| 6 | + | health.get("/", (c) => { |
|
| 7 | + | return c.json({ status: "ok", timestamp: new Date().toISOString() }); |
|
| 8 | + | }); |
|
| 9 | + | ||
| 10 | + | export default health; |
| 1 | + | export { default as health } from "./health"; |
|
| 2 | + | export { default as webhook } from "./webhook"; |
|
| 3 | + | export { default as feed } from "./feed"; |
|
| 4 | + | export { default as stats } from "./stats"; |
|
| 5 | + | export { default as records } from "./records"; |
| 1 | + | import { Hono } from "hono"; |
|
| 2 | + | import type { Bindings } from "../types"; |
|
| 3 | + | ||
| 4 | + | const records = new Hono<{ Bindings: Bindings }>(); |
|
| 5 | + | ||
| 6 | + | records.get("/:did", async (c) => { |
|
| 7 | + | try { |
|
| 8 | + | const db = c.env.DB; |
|
| 9 | + | const did = c.req.param("did"); |
|
| 10 | + | const limit = Number(c.req.query("limit")) || 20; |
|
| 11 | + | const offset = Number(c.req.query("offset")) || 0; |
|
| 12 | + | ||
| 13 | + | const { results } = await db |
|
| 14 | + | .prepare( |
|
| 15 | + | `SELECT * FROM repo_records |
|
| 16 | + | WHERE did = ? AND collection = 'site.standard.document' |
|
| 17 | + | ORDER BY rkey DESC |
|
| 18 | + | LIMIT ? OFFSET ?` |
|
| 19 | + | ) |
|
| 20 | + | .bind(did, limit, offset) |
|
| 21 | + | .all(); |
|
| 22 | + | ||
| 23 | + | return c.json({ |
|
| 24 | + | did, |
|
| 25 | + | count: results?.length || 0, |
|
| 26 | + | limit, |
|
| 27 | + | offset, |
|
| 28 | + | records: results || [], |
|
| 29 | + | }); |
|
| 30 | + | } catch (error) { |
|
| 31 | + | return c.json( |
|
| 32 | + | { error: "Failed to fetch records", details: String(error) }, |
|
| 33 | + | 500 |
|
| 34 | + | ); |
|
| 35 | + | } |
|
| 36 | + | }); |
|
| 37 | + | ||
| 38 | + | export default records; |
| 1 | + | import { Hono } from "hono"; |
|
| 2 | + | import type { Bindings } from "../types"; |
|
| 3 | + | ||
| 4 | + | const stats = new Hono<{ Bindings: Bindings }>(); |
|
| 5 | + | ||
| 6 | + | stats.get("/", async (c) => { |
|
| 7 | + | try { |
|
| 8 | + | const db = c.env.DB; |
|
| 9 | + | const [records, pdsCache, recordCache, pubCache] = await Promise.all([ |
|
| 10 | + | db |
|
| 11 | + | .prepare("SELECT COUNT(*) as count FROM repo_records") |
|
| 12 | + | .first<{ count: number }>(), |
|
| 13 | + | db |
|
| 14 | + | .prepare("SELECT COUNT(*) as count FROM pds_cache") |
|
| 15 | + | .first<{ count: number }>(), |
|
| 16 | + | db |
|
| 17 | + | .prepare("SELECT COUNT(*) as count FROM record_cache") |
|
| 18 | + | .first<{ count: number }>(), |
|
| 19 | + | db |
|
| 20 | + | .prepare("SELECT COUNT(*) as count FROM publication_cache") |
|
| 21 | + | .first<{ count: number }>(), |
|
| 22 | + | ]); |
|
| 23 | + | ||
| 24 | + | return c.json({ |
|
| 25 | + | repo_records: records?.count || 0, |
|
| 26 | + | pds_cache: pdsCache?.count || 0, |
|
| 27 | + | record_cache: recordCache?.count || 0, |
|
| 28 | + | publication_cache: pubCache?.count || 0, |
|
| 29 | + | }); |
|
| 30 | + | } catch (error) { |
|
| 31 | + | return c.json( |
|
| 32 | + | { error: "Failed to fetch stats", details: String(error) }, |
|
| 33 | + | 500 |
|
| 34 | + | ); |
|
| 35 | + | } |
|
| 36 | + | }); |
|
| 37 | + | ||
| 38 | + | export default stats; |
| 1 | + | import { Hono } from "hono"; |
|
| 2 | + | import type { Bindings, TapEvent } from "../types"; |
|
| 3 | + | import { resolvePds, parseAtUri } from "../utils"; |
|
| 4 | + | ||
| 5 | + | const webhook = new Hono<{ Bindings: Bindings }>(); |
|
| 6 | + | ||
| 7 | + | async function resolveViewUrl( |
|
| 8 | + | db: D1Database, |
|
| 9 | + | siteUri: string, |
|
| 10 | + | path: string |
|
| 11 | + | ): Promise<string | null> { |
|
| 12 | + | const parsed = parseAtUri(siteUri); |
|
| 13 | + | if (!parsed) return null; |
|
| 14 | + | ||
| 15 | + | try { |
|
| 16 | + | const pds = await resolvePds(db, parsed.did); |
|
| 17 | + | if (!pds) return null; |
|
| 18 | + | ||
| 19 | + | const url = `${pds}/xrpc/com.atproto.repo.getRecord?repo=${encodeURIComponent(parsed.did)}&collection=${encodeURIComponent(parsed.collection)}&rkey=${encodeURIComponent(parsed.rkey)}`; |
|
| 20 | + | const response = await fetch(url); |
|
| 21 | + | if (!response.ok) return null; |
|
| 22 | + | ||
| 23 | + | const data = (await response.json()) as { |
|
| 24 | + | value?: { url?: string; domain?: string }; |
|
| 25 | + | }; |
|
| 26 | + | const siteUrl = data.value?.url || data.value?.domain; |
|
| 27 | + | if (!siteUrl) return null; |
|
| 28 | + | ||
| 29 | + | const baseUrl = siteUrl.startsWith("http") ? siteUrl : `https://${siteUrl}`; |
|
| 30 | + | return `${baseUrl}${path}`; |
|
| 31 | + | } catch { |
|
| 32 | + | return null; |
|
| 33 | + | } |
|
| 34 | + | } |
|
| 35 | + | ||
| 36 | + | webhook.post("/tap", async (c) => { |
|
| 37 | + | try { |
|
| 38 | + | const db = c.env.DB; |
|
| 39 | + | ||
| 40 | + | const secret = c.env.TAP_WEBHOOK_SECRET; |
|
| 41 | + | if (secret) { |
|
| 42 | + | const auth = c.req.header("Authorization"); |
|
| 43 | + | if (auth !== `Bearer ${secret}`) { |
|
| 44 | + | return c.json({ error: "Unauthorized" }, 401); |
|
| 45 | + | } |
|
| 46 | + | } |
|
| 47 | + | ||
| 48 | + | const event = (await c.req.json()) as TapEvent; |
|
| 49 | + | ||
| 50 | + | if (event.type === "record") { |
|
| 51 | + | const { record } = event; |
|
| 52 | + | ||
| 53 | + | if (record.collection === "site.standard.document") { |
|
| 54 | + | if (record.action === "create" || record.action === "update") { |
|
| 55 | + | await db |
|
| 56 | + | .prepare( |
|
| 57 | + | `INSERT INTO repo_records (did, rkey, collection, cid, synced_at) |
|
| 58 | + | VALUES (?, ?, ?, ?, datetime('now')) |
|
| 59 | + | ON CONFLICT(did, collection, rkey) DO UPDATE SET |
|
| 60 | + | cid = ?, |
|
| 61 | + | synced_at = datetime('now')` |
|
| 62 | + | ) |
|
| 63 | + | .bind( |
|
| 64 | + | record.did, |
|
| 65 | + | record.rkey, |
|
| 66 | + | record.collection, |
|
| 67 | + | record.cid || null, |
|
| 68 | + | record.cid || null |
|
| 69 | + | ) |
|
| 70 | + | .run(); |
|
| 71 | + | ||
| 72 | + | if (record.record) { |
|
| 73 | + | const uri = `at://${record.did}/${record.collection}/${record.rkey}`; |
|
| 74 | + | const doc = record.record as { |
|
| 75 | + | title?: string; |
|
| 76 | + | path?: string; |
|
| 77 | + | site?: string; |
|
| 78 | + | content?: unknown; |
|
| 79 | + | textContent?: string; |
|
| 80 | + | publishedAt?: string; |
|
| 81 | + | }; |
|
| 82 | + | ||
| 83 | + | let viewUrl: string | null = null; |
|
| 84 | + | if (doc.site && doc.path) { |
|
| 85 | + | viewUrl = await resolveViewUrl(db, doc.site, doc.path); |
|
| 86 | + | } |
|
| 87 | + | ||
| 88 | + | await db |
|
| 89 | + | .prepare( |
|
| 90 | + | `INSERT INTO resolved_documents (uri, did, rkey, title, path, site, content, text_content, published_at, view_url, resolved_at) |
|
| 91 | + | VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now')) |
|
| 92 | + | ON CONFLICT(uri) DO UPDATE SET |
|
| 93 | + | title = ?, path = ?, site = ?, content = ?, text_content = ?, published_at = ?, view_url = ?, resolved_at = datetime('now')` |
|
| 94 | + | ) |
|
| 95 | + | .bind( |
|
| 96 | + | uri, |
|
| 97 | + | record.did, |
|
| 98 | + | record.rkey, |
|
| 99 | + | doc.title || null, |
|
| 100 | + | doc.path || null, |
|
| 101 | + | doc.site || null, |
|
| 102 | + | doc.content ? JSON.stringify(doc.content) : null, |
|
| 103 | + | doc.textContent || null, |
|
| 104 | + | doc.publishedAt || null, |
|
| 105 | + | viewUrl, |
|
| 106 | + | doc.title || null, |
|
| 107 | + | doc.path || null, |
|
| 108 | + | doc.site || null, |
|
| 109 | + | doc.content ? JSON.stringify(doc.content) : null, |
|
| 110 | + | doc.textContent || null, |
|
| 111 | + | doc.publishedAt || null, |
|
| 112 | + | viewUrl |
|
| 113 | + | ) |
|
| 114 | + | .run(); |
|
| 115 | + | } |
|
| 116 | + | } else if (record.action === "delete") { |
|
| 117 | + | await db |
|
| 118 | + | .prepare( |
|
| 119 | + | "DELETE FROM repo_records WHERE did = ? AND collection = ? AND rkey = ?" |
|
| 120 | + | ) |
|
| 121 | + | .bind(record.did, record.collection, record.rkey) |
|
| 122 | + | .run(); |
|
| 123 | + | ||
| 124 | + | const uri = `at://${record.did}/${record.collection}/${record.rkey}`; |
|
| 125 | + | await db |
|
| 126 | + | .prepare("DELETE FROM resolved_documents WHERE uri = ?") |
|
| 127 | + | .bind(uri) |
|
| 128 | + | .run(); |
|
| 129 | + | } |
|
| 130 | + | } |
|
| 131 | + | } |
|
| 132 | + | ||
| 133 | + | return c.json({ ok: true }); |
|
| 134 | + | } catch (error) { |
|
| 135 | + | console.error("Webhook error:", error); |
|
| 136 | + | return c.json( |
|
| 137 | + | { error: "Failed to process webhook", details: String(error) }, |
|
| 138 | + | 500 |
|
| 139 | + | ); |
|
| 140 | + | } |
|
| 141 | + | }); |
|
| 142 | + | ||
| 143 | + | webhook.post("/tap/batch", async (c) => { |
|
| 144 | + | try { |
|
| 145 | + | const db = c.env.DB; |
|
| 146 | + | ||
| 147 | + | const secret = c.env.TAP_WEBHOOK_SECRET; |
|
| 148 | + | if (secret) { |
|
| 149 | + | const auth = c.req.header("Authorization"); |
|
| 150 | + | if (auth !== `Bearer ${secret}`) { |
|
| 151 | + | return c.json({ error: "Unauthorized" }, 401); |
|
| 152 | + | } |
|
| 153 | + | } |
|
| 154 | + | ||
| 155 | + | const events = (await c.req.json()) as Array<{ |
|
| 156 | + | type: string; |
|
| 157 | + | did: string; |
|
| 158 | + | collection?: string; |
|
| 159 | + | rkey?: string; |
|
| 160 | + | cid?: string; |
|
| 161 | + | record?: Record<string, unknown>; |
|
| 162 | + | }>; |
|
| 163 | + | ||
| 164 | + | let processed = 0; |
|
| 165 | + | let errors = 0; |
|
| 166 | + | ||
| 167 | + | for (const event of events) { |
|
| 168 | + | try { |
|
| 169 | + | if ( |
|
| 170 | + | (event.type === "commit" || |
|
| 171 | + | event.type === "create" || |
|
| 172 | + | event.type === "update") && |
|
| 173 | + | event.collection === "site.standard.document" && |
|
| 174 | + | event.did && |
|
| 175 | + | event.rkey |
|
| 176 | + | ) { |
|
| 177 | + | await db |
|
| 178 | + | .prepare( |
|
| 179 | + | `INSERT INTO repo_records (did, rkey, collection, cid, synced_at) |
|
| 180 | + | VALUES (?, ?, ?, ?, datetime('now')) |
|
| 181 | + | ON CONFLICT(did, collection, rkey) DO UPDATE SET cid = ?, synced_at = datetime('now')` |
|
| 182 | + | ) |
|
| 183 | + | .bind( |
|
| 184 | + | event.did, |
|
| 185 | + | event.rkey, |
|
| 186 | + | event.collection, |
|
| 187 | + | event.cid || null, |
|
| 188 | + | event.cid || null |
|
| 189 | + | ) |
|
| 190 | + | .run(); |
|
| 191 | + | ||
| 192 | + | if (event.record) { |
|
| 193 | + | const uri = `at://${event.did}/${event.collection}/${event.rkey}`; |
|
| 194 | + | const doc = event.record as { |
|
| 195 | + | title?: string; |
|
| 196 | + | path?: string; |
|
| 197 | + | site?: string; |
|
| 198 | + | content?: unknown; |
|
| 199 | + | textContent?: string; |
|
| 200 | + | publishedAt?: string; |
|
| 201 | + | }; |
|
| 202 | + | ||
| 203 | + | let viewUrl: string | null = null; |
|
| 204 | + | if (doc.site && doc.path) { |
|
| 205 | + | viewUrl = await resolveViewUrl(db, doc.site, doc.path); |
|
| 206 | + | } |
|
| 207 | + | ||
| 208 | + | await db |
|
| 209 | + | .prepare( |
|
| 210 | + | `INSERT INTO resolved_documents (uri, did, rkey, title, path, site, content, text_content, published_at, view_url, resolved_at) |
|
| 211 | + | VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now')) |
|
| 212 | + | ON CONFLICT(uri) DO UPDATE SET |
|
| 213 | + | title = ?, path = ?, site = ?, content = ?, text_content = ?, published_at = ?, view_url = ?, resolved_at = datetime('now')` |
|
| 214 | + | ) |
|
| 215 | + | .bind( |
|
| 216 | + | uri, |
|
| 217 | + | event.did, |
|
| 218 | + | event.rkey, |
|
| 219 | + | doc.title || null, |
|
| 220 | + | doc.path || null, |
|
| 221 | + | doc.site || null, |
|
| 222 | + | doc.content ? JSON.stringify(doc.content) : null, |
|
| 223 | + | doc.textContent || null, |
|
| 224 | + | doc.publishedAt || null, |
|
| 225 | + | viewUrl, |
|
| 226 | + | doc.title || null, |
|
| 227 | + | doc.path || null, |
|
| 228 | + | doc.site || null, |
|
| 229 | + | doc.content ? JSON.stringify(doc.content) : null, |
|
| 230 | + | doc.textContent || null, |
|
| 231 | + | doc.publishedAt || null, |
|
| 232 | + | viewUrl |
|
| 233 | + | ) |
|
| 234 | + | .run(); |
|
| 235 | + | } |
|
| 236 | + | processed++; |
|
| 237 | + | } else if ( |
|
| 238 | + | event.type === "delete" && |
|
| 239 | + | event.collection === "site.standard.document" && |
|
| 240 | + | event.did && |
|
| 241 | + | event.rkey |
|
| 242 | + | ) { |
|
| 243 | + | await db |
|
| 244 | + | .prepare( |
|
| 245 | + | "DELETE FROM repo_records WHERE did = ? AND collection = ? AND rkey = ?" |
|
| 246 | + | ) |
|
| 247 | + | .bind(event.did, event.collection, event.rkey) |
|
| 248 | + | .run(); |
|
| 249 | + | ||
| 250 | + | const uri = `at://${event.did}/${event.collection}/${event.rkey}`; |
|
| 251 | + | await db |
|
| 252 | + | .prepare("DELETE FROM resolved_documents WHERE uri = ?") |
|
| 253 | + | .bind(uri) |
|
| 254 | + | .run(); |
|
| 255 | + | processed++; |
|
| 256 | + | } |
|
| 257 | + | } catch { |
|
| 258 | + | errors++; |
|
| 259 | + | } |
|
| 260 | + | } |
|
| 261 | + | ||
| 262 | + | return c.json({ ok: true, processed, errors }); |
|
| 263 | + | } catch (error) { |
|
| 264 | + | console.error("Batch webhook error:", error); |
|
| 265 | + | return c.json( |
|
| 266 | + | { error: "Failed to process batch webhook", details: String(error) }, |
|
| 267 | + | 500 |
|
| 268 | + | ); |
|
| 269 | + | } |
|
| 270 | + | }); |
|
| 271 | + | ||
| 272 | + | export default webhook; |
| 1 | + | export type Bindings = { |
|
| 2 | + | DB: D1Database; |
|
| 3 | + | TAP_WEBHOOK_SECRET?: string; |
|
| 4 | + | }; |
|
| 5 | + | ||
| 6 | + | export interface TapRecordEvent { |
|
| 7 | + | id: number; |
|
| 8 | + | type: "record"; |
|
| 9 | + | record: { |
|
| 10 | + | live: boolean; |
|
| 11 | + | rev: string; |
|
| 12 | + | did: string; |
|
| 13 | + | collection: string; |
|
| 14 | + | rkey: string; |
|
| 15 | + | action: "create" | "update" | "delete"; |
|
| 16 | + | cid?: string; |
|
| 17 | + | record?: Record<string, unknown>; |
|
| 18 | + | }; |
|
| 19 | + | } |
|
| 20 | + | ||
| 21 | + | export interface TapIdentityEvent { |
|
| 22 | + | id: number; |
|
| 23 | + | type: "identity"; |
|
| 24 | + | identity: { |
|
| 25 | + | did: string; |
|
| 26 | + | handle: string; |
|
| 27 | + | isActive: boolean; |
|
| 28 | + | status: string; |
|
| 29 | + | }; |
|
| 30 | + | } |
|
| 31 | + | ||
| 32 | + | export type TapEvent = TapRecordEvent | TapIdentityEvent; |
|
| 33 | + | ||
| 34 | + | export interface Document { |
|
| 35 | + | uri: string; |
|
| 36 | + | did: string; |
|
| 37 | + | rkey: string; |
|
| 38 | + | title: string; |
|
| 39 | + | path: string | null; |
|
| 40 | + | site: string | null; |
|
| 41 | + | content: unknown; |
|
| 42 | + | textContent: string | null; |
|
| 43 | + | publishedAt: string | null; |
|
| 44 | + | viewUrl: string | null; |
|
| 45 | + | } |
| 1 | + | export interface AtUriComponents { |
|
| 2 | + | did: string; |
|
| 3 | + | collection: string; |
|
| 4 | + | rkey: string; |
|
| 5 | + | } |
|
| 6 | + | ||
| 7 | + | export function parseAtUri(uri: string): AtUriComponents | null { |
|
| 8 | + | const match = uri.match(/^at:\/\/([^/]+)\/([^/]+)\/([^/]+)$/); |
|
| 9 | + | if (!match) return null; |
|
| 10 | + | return { did: match[1], collection: match[2], rkey: match[3] }; |
|
| 11 | + | } |
|
| 12 | + | ||
| 13 | + | export function buildAtUri(did: string, collection: string, rkey: string): string { |
|
| 14 | + | return `at://${did}/${collection}/${rkey}`; |
|
| 15 | + | } |
| 1 | + | export { parseAtUri, buildAtUri, type AtUriComponents } from "./at-uri"; |
|
| 2 | + | export { resolvePds } from "./resolver"; |
| 1 | + | // PDS cache TTL: 1 hour (PDS endpoints rarely change) |
|
| 2 | + | const PDS_CACHE_TTL_MS = 60 * 60 * 1000; |
|
| 3 | + | ||
| 4 | + | function isPdsCacheValid(cachedAt: string | null): boolean { |
|
| 5 | + | if (!cachedAt) return false; |
|
| 6 | + | const cacheTime = new Date(cachedAt).getTime(); |
|
| 7 | + | return Date.now() - cacheTime < PDS_CACHE_TTL_MS; |
|
| 8 | + | } |
|
| 9 | + | ||
| 10 | + | export async function resolvePds( |
|
| 11 | + | db: D1Database, |
|
| 12 | + | did: string |
|
| 13 | + | ): Promise<string | null> { |
|
| 14 | + | const cached = await db |
|
| 15 | + | .prepare("SELECT pds_endpoint, cached_at FROM pds_cache WHERE did = ?") |
|
| 16 | + | .bind(did) |
|
| 17 | + | .first<{ pds_endpoint: string; cached_at: string }>(); |
|
| 18 | + | ||
| 19 | + | if (cached && isPdsCacheValid(cached.cached_at)) { |
|
| 20 | + | return cached.pds_endpoint; |
|
| 21 | + | } |
|
| 22 | + | ||
| 23 | + | try { |
|
| 24 | + | const response = await fetch(`https://plc.directory/${did}`); |
|
| 25 | + | if (!response.ok) return null; |
|
| 26 | + | ||
| 27 | + | const doc = (await response.json()) as { |
|
| 28 | + | service?: Array<{ id: string; type: string; serviceEndpoint: string }>; |
|
| 29 | + | }; |
|
| 30 | + | ||
| 31 | + | const pds = doc.service?.find((s) => s.id === "#atproto_pds"); |
|
| 32 | + | if (pds?.serviceEndpoint) { |
|
| 33 | + | await db |
|
| 34 | + | .prepare( |
|
| 35 | + | `INSERT INTO pds_cache (did, pds_endpoint, cached_at) |
|
| 36 | + | VALUES (?, ?, datetime('now')) |
|
| 37 | + | ON CONFLICT(did) DO UPDATE SET pds_endpoint = ?, cached_at = datetime('now')` |
|
| 38 | + | ) |
|
| 39 | + | .bind(did, pds.serviceEndpoint, pds.serviceEndpoint) |
|
| 40 | + | .run(); |
|
| 41 | + | ||
| 42 | + | return pds.serviceEndpoint; |
|
| 43 | + | } |
|
| 44 | + | ||
| 45 | + | return null; |
|
| 46 | + | } catch { |
|
| 47 | + | return null; |
|
| 48 | + | } |
|
| 49 | + | } |
| 1 | + | { |
|
| 2 | + | "compilerOptions": { |
|
| 3 | + | "target": "ES2022", |
|
| 4 | + | "module": "ESNext", |
|
| 5 | + | "moduleResolution": "bundler", |
|
| 6 | + | "strict": true, |
|
| 7 | + | "skipLibCheck": true, |
|
| 8 | + | "lib": ["ES2022"], |
|
| 9 | + | "types": ["@cloudflare/workers-types"], |
|
| 10 | + | "noEmit": true, |
|
| 11 | + | "isolatedModules": true, |
|
| 12 | + | "esModuleInterop": true, |
|
| 13 | + | "allowSyntheticDefaultImports": true |
|
| 14 | + | }, |
|
| 15 | + | "include": ["src/**/*"], |
|
| 16 | + | "exclude": ["node_modules"] |
|
| 17 | + | } |
| 1 | + | name = "atfeeds-api" |
|
| 2 | + | main = "src/index.ts" |
|
| 3 | + | compatibility_date = "2024-01-01" |
|
| 4 | + | ||
| 5 | + | # D1 Database binding |
|
| 6 | + | [[d1_databases]] |
|
| 7 | + | binding = "DB" |
|
| 8 | + | database_name = "atfeeds-db" |
|
| 9 | + | database_id = "bfbb9955-1496-47e9-9602-e32c9b1fa7b2" |
|
| 10 | + | ||
| 11 | + | # Queue for processing document resolution |
|
| 12 | + | # [[queues.producers]] |
|
| 13 | + | # queue = "document-resolution" |
|
| 14 | + | # binding = "RESOLUTION_QUEUE" |
|
| 15 | + | ||
| 16 | + | # [[queues.consumers]] |
|
| 17 | + | # queue = "document-resolution" |
|
| 18 | + | # max_batch_size = 10 |
|
| 19 | + | # max_batch_timeout = 30 |
|
| 20 | + | ||
| 21 | + | # Cron trigger to refresh stale documents |
|
| 22 | + | [triggers] |
|
| 23 | + | crons = ["*/15 * * * *"] # Every 15 minutes |
|
| 24 | + | ||
| 25 | + | # Environment variables (secrets should be set via wrangler secret) |
|
| 26 | + | # TAP_WEBHOOK_SECRET - Optional secret for webhook authentication |
|
| 27 | + | # Set via: wrangler secret put TAP_WEBHOOK_SECRET |
|
| 28 | + | ||
| 29 | + | # Development settings |
|
| 30 | + | [dev] |
|
| 31 | + | port = 8787 |