An entry for the streamplace vod showcase
1
fork

Configure Feed

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

docs(at-run): add Response objects, CORS, and performance sections

- Document returning Response objects for binary data
- Explain automatic CORS headers on all responses
- Add batch endpoints and cacheable GET best practices
- Document manual job execution and multiple entry points

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+162
+162
packages/at-run/README.md
··· 318 318 |---------|-------------| 319 319 | `at-run jobs list [bundle]` | List all jobs (optionally filter by bundle) | 320 320 | `at-run jobs create <bundle> <job> --runner <did>` | Create a job | 321 + | `at-run jobs run <bundle> <job>` | Manually trigger a job and wait for result | 321 322 | `at-run jobs enable <bundle> <job>` | Enable a job | 322 323 | `at-run jobs disable <bundle> <job>` | Disable a job | 323 324 | `at-run jobs delete <bundle> <job>` | Delete a job | 325 + 326 + ### Manual Job Execution 327 + 328 + Trigger a job immediately and see the result: 329 + 330 + ```bash 331 + at-run jobs run my-tasks updateStats --job-version=0.1.5 332 + ``` 333 + 334 + The command waits for the job to complete and prints the return value, useful for debugging. 324 335 325 336 ### Job Version Flags 326 337 ··· 356 367 | `--job-version=x.y.z` | Set explicit job version | 357 368 | `--patch/--minor/--major` | Auto-bump job version | 358 369 370 + ### Under Review 371 + 372 + > **Note:** The following features are under review and may change. 373 + 374 + #### Job Version vs Bundle Version 375 + 376 + Jobs have their own versioning independent from bundle versions. When working with jobs: 377 + - `--job-version` refers to the job record's version (e.g., `0.1.10`) 378 + - `--bundle-version` refers to which bundle version the job executes (e.g., `0.2.7`) 379 + 380 + Use `at-run jobs list` to see both versions for each job. 381 + 382 + #### Job Update Workflow 383 + 384 + When updating job code, the typical workflow is: 385 + 386 + ```bash 387 + # 1. Deploy new bundle version 388 + at-run deploy dist/bundle.js 389 + 390 + # 2. Disable old job version 391 + at-run jobs disable my-bundle myJob --job-version=0.1.5 392 + 393 + # 3. Create new job pointing to new bundle 394 + at-run jobs create my-bundle myJob --runner did:plc:xxx \ 395 + --bundle-version=0.2.0 --cron "*/10 * * * *" 396 + ``` 397 + 359 398 ### Cron Expressions 360 399 361 400 Standard 5-field cron format: ··· 394 433 395 434 The effective permissions are the **intersection** of manifest and endpoint permissions. 396 435 436 + ## Returning Response Objects 437 + 438 + Endpoints can return raw `Response` objects for binary data, custom headers, or caching: 439 + 440 + ```typescript 441 + export const getImage = endpoint({ 442 + input: v.object({ id: v.string() }), 443 + async handler({ id }) { 444 + const bytes = await Deno.readFile(`/tmp/images/${id}.jpg`) 445 + return new Response(bytes, { 446 + headers: { 447 + "Content-Type": "image/jpeg", 448 + "Cache-Control": "public, max-age=86400", 449 + }, 450 + }) 451 + }, 452 + }) 453 + 454 + export const getDataWithCache = endpoint({ 455 + input: v.object({ key: v.string() }), 456 + async handler({ key }) { 457 + const data = await fetchData(key) 458 + return new Response(JSON.stringify(data), { 459 + headers: { 460 + "Content-Type": "application/json", 461 + "Cache-Control": "public, max-age=3600", 462 + }, 463 + }) 464 + }, 465 + }) 466 + ``` 467 + 468 + The runner automatically handles binary data by encoding it through the sandbox boundary. 469 + 470 + ### CORS Headers 471 + 472 + The runner automatically adds CORS headers to all responses: 473 + 474 + - `Access-Control-Allow-Origin: *` 475 + - `Access-Control-Allow-Methods: GET, POST, OPTIONS` 476 + - `Access-Control-Allow-Headers: Content-Type` 477 + 478 + If your endpoint's Response already includes these headers, they are preserved. 479 + 480 + ## Performance Best Practices 481 + 482 + ### Batch Endpoints 483 + 484 + When clients need to fetch data for many items, create batch endpoints to reduce request count: 485 + 486 + ```typescript 487 + // Instead of: GET /profile?did=xxx (called 50 times) 488 + // Use: POST /batchProfiles with { dids: [...] } (called once) 489 + 490 + const BatchProfilesSchema = v.object({ 491 + dids: v.array(v.string()), 492 + }) 493 + 494 + export const batchProfiles = endpoint({ 495 + input: BatchProfilesSchema, 496 + async handler({ dids }) { 497 + const results: Record<string, Profile | null> = {} 498 + await Promise.all( 499 + dids.map(async (did) => { 500 + try { 501 + results[did] = await fetchProfile(did) 502 + } catch { 503 + results[did] = null 504 + } 505 + }) 506 + ) 507 + return results 508 + }, 509 + }) 510 + ``` 511 + 512 + This reduces hundreds of concurrent requests to a single batch call, improving both client and server performance. 513 + 514 + ### Cacheable GET Endpoints 515 + 516 + For data that can be cached by browsers, use GET requests with query parameters: 517 + 518 + ```typescript 519 + // Client can use: GET /thumbnail?uri=at://... 520 + // Browser caches based on full URL 521 + 522 + export const getThumbnail = endpoint({ 523 + input: v.object({ uri: v.string() }), 524 + async handler({ uri }) { 525 + const bytes = await loadThumbnail(uri) 526 + return new Response(bytes, { 527 + headers: { 528 + "Content-Type": "image/jpeg", 529 + "Cache-Control": "public, max-age=86400, immutable", 530 + }, 531 + }) 532 + }, 533 + }) 534 + ``` 535 + 397 536 ## Lexicons 398 537 399 538 ### Bundle Record ··· 442 581 ``` 443 582 444 583 ## Development 584 + 585 + ### Multiple Entry Points 586 + 587 + A project can have separate bundles for endpoints and jobs: 588 + 589 + ``` 590 + my-app/ 591 + src/ 592 + index.ts # Endpoints bundle (e.g., "my-api") 593 + jobs.ts # Jobs bundle (e.g., "my-api-jobs") 594 + ``` 595 + 596 + Build and deploy them separately: 597 + 598 + ```bash 599 + # Build both bundles 600 + bun build src/index.ts --outfile=dist/api.js --target=browser 601 + bun build src/jobs.ts --outfile=dist/jobs.js --target=browser 602 + 603 + # Deploy both 604 + at-run deploy dist/api.js 605 + at-run deploy dist/jobs.js 606 + ``` 445 607 446 608 ### Local Testing 447 609