feat(v1.1.6): realtime channels + v1.1.5 follow-ups + version bumps
Server-side realtime SSE on per-app pub/sub topics, plus the three
v1.1.5 follow-ups and the version bumps.
Realtime:
- topics registry (0021) + admin endpoints + Capability::AppTopicManage
(-> app:admin; no new scope).
- GET /realtime/topics/{topic} SSE endpoint (orchestrator-core data
plane): Host -> app, RealtimeAuthority gate (404 missing/internal,
401 bad/absent token), broadcast::Receiver stream + heartbeat.
- RealtimeBroadcaster / RealtimeEvent / RealtimeAuthority traits
(picloud-shared); InProcessBroadcaster + GC (orchestrator-core);
DB-backed RealtimeAuthorityImpl (manager-core). Publish path fans out
to in-process subscribers after the durable outbox commit (best-effort,
panic-isolated).
- HMAC subscriber tokens (subscriber_token.rs) + app_secrets table (0022)
+ pubsub::subscriber_token SDK (schema 1.6 -> 1.7). TTL clamp + env
overrides.
- Dashboard Topics tab (register/list/edit/delete, prominent external
badge, flip confirmation).
v1.1.5 follow-ups:
- Empty blobs accepted (NewFile/FileUpdate::validate) + round-trip test.
- Orphan *.tmp.* sweeper (spawn_files_orphan_sweep).
- Dispatcher e2e tests, one per trigger kind (DATABASE_URL-gated).
Versions: workspace 1.1.6, SDK 1.7, dashboard 0.12.0. Schema-snapshot
golden re-blessed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -270,6 +270,28 @@ export interface CreatePubsubTriggerInput {
|
||||
retry_base_ms?: number;
|
||||
}
|
||||
|
||||
// v1.1.6 — externally-subscribable realtime topics.
|
||||
export type TopicAuthMode = 'public' | 'token';
|
||||
|
||||
export interface Topic {
|
||||
name: string;
|
||||
external_subscribable: boolean;
|
||||
auth_mode: TopicAuthMode;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface CreateTopicInput {
|
||||
name: string;
|
||||
external_subscribable: boolean;
|
||||
auth_mode: TopicAuthMode;
|
||||
}
|
||||
|
||||
export interface UpdateTopicInput {
|
||||
external_subscribable?: boolean;
|
||||
auth_mode?: TopicAuthMode;
|
||||
}
|
||||
|
||||
export interface ExecutionResult {
|
||||
status: number;
|
||||
headers: Record<string, string>;
|
||||
@@ -653,6 +675,28 @@ export const api = {
|
||||
)
|
||||
},
|
||||
|
||||
topics: {
|
||||
list: (idOrSlug: string) =>
|
||||
adminRequest<{ topics: Topic[] }>(
|
||||
`/api/v1/admin/apps/${encodeURIComponent(idOrSlug)}/topics`
|
||||
),
|
||||
create: (idOrSlug: string, input: CreateTopicInput) =>
|
||||
adminRequest<Topic>(`/api/v1/admin/apps/${encodeURIComponent(idOrSlug)}/topics`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(input)
|
||||
}),
|
||||
update: (idOrSlug: string, name: string, input: UpdateTopicInput) =>
|
||||
adminRequest<Topic>(
|
||||
`/api/v1/admin/apps/${encodeURIComponent(idOrSlug)}/topics/${encodeURIComponent(name)}`,
|
||||
{ method: 'PATCH', body: JSON.stringify(input) }
|
||||
),
|
||||
remove: (idOrSlug: string, name: string) =>
|
||||
adminRequest<null>(
|
||||
`/api/v1/admin/apps/${encodeURIComponent(idOrSlug)}/topics/${encodeURIComponent(name)}`,
|
||||
{ method: 'DELETE' }
|
||||
)
|
||||
},
|
||||
|
||||
files: {
|
||||
list: (idOrSlug: string, collection: string, opts: { cursor?: string; limit?: number } = {}) => {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
Reference in New Issue
Block a user