feat(v1.1.9): dashboard Queues tab + queue:receive trigger form + 0.15.0

API client (src/lib/api.ts):
- TriggerKind gains 'queue'
- TriggerDetails gains { kind: 'queue', queue_name, visibility_timeout_secs, last_fired_at }
- CreateQueueTriggerInput, QueueSummary, QueueConsumer, QueueDetail
- api.triggers.createQueue(idOrSlug, input) -> POST /admin/.../triggers/queue
- api.queues.list(idOrSlug) -> GET /admin/.../queues
- api.queues.get(idOrSlug, name) -> GET /admin/.../queues/{name}

New routes:
- /apps/[slug]/queues — read-only list view (queue_name, total, pending,
  claimed, drilldown link); empty state explains how queues are created
  (first enqueue) and that consumers register via the Triggers tab
- /apps/[slug]/queues/[name] — drilldown showing depth + registered
  consumer (script name + visibility timeout + last_fired_at + trigger
  id); empty consumer state surfaces clearly

App detail page (src/routes/apps/[slug]/+page.svelte):
- New "Queues" link in the app-level nav between Files and Dead letters
- Triggers tab gains a "Queue:receive trigger (v1.1.9)" form alongside
  cron/pubsub/email — target script select, queue_name, visibility
  timeout (5–3600s, default 30), max_attempts (1–20, default 3)
- Trigger list renders queue triggers with queue_name + visibility
  timeout + last_fired_at

dashboard/package.json: 0.14.0 -> 0.15.0

npm run check — all queue/invoke-related code typechecks clean; the
existing Playwright test errors (149 unrelated) carry forward unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-06 22:41:59 +02:00
parent 714f6dae18
commit 529d34af83
5 changed files with 412 additions and 3 deletions

View File

@@ -218,7 +218,8 @@ export type TriggerKind =
| 'cron'
| 'files'
| 'pubsub'
| 'email';
| 'email'
| 'queue';
export type TriggerDispatchMode = 'sync' | 'async';
/// Per-kind detail, tagged by `kind` to match the Rust serde shape.
@@ -229,7 +230,8 @@ export type TriggerDetails =
| { kind: 'cron'; schedule: string; timezone: string; last_fired_at?: string | null }
| { kind: 'files'; collection_glob: string; ops: string[] }
| { kind: 'pubsub'; topic_pattern: string }
| { kind: 'email'; has_inbound_secret: boolean };
| { kind: 'email'; has_inbound_secret: boolean }
| { kind: 'queue'; queue_name: string; visibility_timeout_secs: number; last_fired_at?: string | null };
export interface CreateEmailTriggerInput {
script_id: string;
@@ -347,6 +349,37 @@ export interface CreatePubsubTriggerInput {
retry_base_ms?: number;
}
// v1.1.9 — queue:receive trigger.
export interface CreateQueueTriggerInput {
script_id: string;
queue_name: string;
visibility_timeout_secs?: number;
dispatch_mode?: TriggerDispatchMode;
retry_max_attempts?: number;
retry_backoff?: 'exponential' | 'linear' | 'constant';
retry_base_ms?: number;
}
// v1.1.9 — read-only queue inspection types (admin queues API).
export interface QueueSummary {
queue_name: string;
total: number;
pending: number;
claimed: number;
}
export interface QueueConsumer {
trigger_id: string;
script_id: string;
script_name: string;
visibility_timeout_secs: number;
last_fired_at: string | null;
}
export interface QueueDetail extends QueueSummary {
consumer: QueueConsumer | null;
}
// v1.1.6 — externally-subscribable realtime topics.
export type TopicAuthMode = 'public' | 'token' | 'session';
@@ -755,6 +788,11 @@ export const api = {
`/api/v1/admin/apps/${encodeURIComponent(idOrSlug)}/triggers/email`,
{ method: 'POST', body: JSON.stringify(input) }
),
createQueue: (idOrSlug: string, input: CreateQueueTriggerInput) =>
adminRequest<Trigger>(
`/api/v1/admin/apps/${encodeURIComponent(idOrSlug)}/triggers/queue`,
{ method: 'POST', body: JSON.stringify(input) }
),
remove: (idOrSlug: string, triggerId: string) =>
adminRequest<null>(
`/api/v1/admin/apps/${encodeURIComponent(idOrSlug)}/triggers/${triggerId}`,
@@ -762,6 +800,17 @@ export const api = {
)
},
queues: {
list: (idOrSlug: string) =>
adminRequest<QueueSummary[]>(
`/api/v1/admin/apps/${encodeURIComponent(idOrSlug)}/queues`
),
get: (idOrSlug: string, name: string) =>
adminRequest<QueueDetail>(
`/api/v1/admin/apps/${encodeURIComponent(idOrSlug)}/queues/${encodeURIComponent(name)}`
)
},
topics: {
list: (idOrSlug: string) =>
adminRequest<{ topics: Topic[] }>(