Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/content/1.guide/21.events.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Each subsystem host emits on `ctx.<subsystem>.events`, consumed **inside the sam
| `docks:entry:updated` | `DocksHost.register` / `update` | context → `devframe:docks` shared state | `DevframeDockUserEntry` |
| `docks:activate` | `DocksHost.activate()` | context → broadcast + `devframe:docks:active` | `DevframeDockActivation` |
| `terminals:session:updated` | `TerminalsHost` register / update / remove / status change | context → `devframe:terminals:updated`; terminals plugin | `DevframeTerminalSession` |
| `terminals:session:output` | `TerminalsHost` stream consumption | opt-in node listeners | session id, live output chunk |
| `messages:added` / `messages:updated` / `messages:removed` / `messages:cleared` | `MessagesHost` mutations | context → `devframe:messages:updated`; messages plugin | entry / entry / id / — |
| `commands:registered` / `commands:unregistered` | `CommandsHost` register / update / unregister | context → `devframe:commands` shared state | entry / id |

Expand Down
1 change: 1 addition & 0 deletions packages/hub/src/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const HUB_EVENTS = {
docksEntryUpdated: 'docks:entry:updated',
docksActivate: 'docks:activate',
terminalsSessionUpdated: 'terminals:session:updated',
terminalsSessionOutput: 'terminals:session:output',
messagesAdded: 'messages:added',
messagesUpdated: 'messages:updated',
messagesRemoved: 'messages:removed',
Expand Down
43 changes: 43 additions & 0 deletions packages/hub/src/node/__tests__/host-terminals.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,49 @@ async function waitUntil(assertion: () => void): Promise<void> {
}

describe('devframeTerminalHost stream lifecycle', () => {
it('emits each consumed output chunk with its session id', async () => {
const { host } = createTerminalHost()
const output: Array<[sessionId: string, chunk: string]> = []
let controller: ReadableStreamDefaultController<string>
const stream = new ReadableStream<string>({
start(_controller) {
controller = _controller
},
})

host.events.on('terminals:session:output', (sessionId, chunk) => output.push([sessionId, chunk]))
host.register({ id: 'terminal', title: 'Terminal', status: 'running', stream })
controller!.enqueue('first')
controller!.enqueue('second')

await waitUntil(() => {
expect(output).toEqual([
['terminal', 'first'],
['terminal', 'second'],
])
})
})

it('emits output from child-process sessions', async () => {
const { host } = createTerminalHost()
const output: Array<[sessionId: string, chunk: string]> = []
host.events.on('terminals:session:output', (sessionId, chunk) => output.push([sessionId, chunk]))

const session = await host.startChildProcess({
command: process.execPath,
args: ['-e', 'process.stdout.write("child output")'],
}, {
id: 'child',
title: 'Child',
})

await session.getResult()
await waitUntil(() => {
expect(output.map(([, chunk]) => chunk).join('')).toContain('child output')
})
expect(output.every(([sessionId]) => sessionId === 'child')).toBe(true)
})

it('cancels a bound stream when a session is removed', async () => {
const { host, sinks } = createTerminalHost()
let controller: ReadableStreamDefaultController<string>
Expand Down
1 change: 1 addition & 0 deletions packages/hub/src/node/host-terminals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ export class DevframeTerminalsHost implements DevframeTerminalsHostType {
break
if (result.done)
break
this.events.emit(HUB_EVENTS.bus.terminalsSessionOutput, session.id, result.value)
// Mirror to the legacy session.buffer used by `terminals:read` —
// bounded tail kept for the snapshot endpoint.
sessionBuffer.push(result.value)
Expand Down
2 changes: 2 additions & 0 deletions packages/hub/src/types/terminals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export interface DevframeTerminalsHost {
readonly sessions: Map<string, DevframeTerminalSession>
readonly events: EventEmitter<{
'terminals:session:updated': (session: DevframeTerminalSession) => void
/** Live output chunks consumed from a session stream. Chunks are not replayed. */
'terminals:session:output': (sessionId: string, chunk: string) => void
}>

register: (session: DevframeTerminalSession) => DevframeTerminalSession
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export declare const HUB_EVENTS: {
readonly docksEntryUpdated: "docks:entry:updated";
readonly docksActivate: "docks:activate";
readonly terminalsSessionUpdated: "terminals:session:updated";
readonly terminalsSessionOutput: "terminals:session:output";
readonly messagesAdded: "messages:added";
readonly messagesUpdated: "messages:updated";
readonly messagesRemoved: "messages:removed";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ export interface DevframeTerminalsHost {
readonly sessions: Map<string, DevframeTerminalSession>;
readonly events: EventEmitter<{
'terminals:session:updated': (session: DevframeTerminalSession) => void;
'terminals:session:output': (sessionId: string, chunk: string) => void;
}>;
register: (_: DevframeTerminalSession) => DevframeTerminalSession;
update: (_: DevframeTerminalSession) => void;
Expand Down
Loading