Skip to content

Commit

Permalink
fix: content api for frontend events from backend, ui updates and res…
Browse files Browse the repository at this point in the history
…et command (#101)

* fix: move context to it's own class

* fix: result stream moved to it's own model

* fix: set raw result string to a buffer output when provided

* fix: attach context to the event list to runtime query

* fix: add context event handling

* fix: use dynamic event name list based on events

* fix: prevent events after abort

* fix: safe hook runtime usage

* fix: reset command
  • Loading branch information
daretodave authored May 8, 2024
1 parent aac890d commit d8fbce5
Show file tree
Hide file tree
Showing 22 changed files with 386 additions and 201 deletions.
5 changes: 3 additions & 2 deletions src/main/framework/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import short from 'short-uuid'
import { runInNewContext } from 'node:vm'
import * as tryRequire from 'try-require'
import { compile } from '../vendor/webpack'
import { ExecuteContext } from './runtime'
import { Settings } from './settings'
import { ExecuteContext } from './execute-context'

export type FrontendHook = () => Promise<void> | void
export class Commands {
public lib: object = {}
public state: Map<string, object> = new Map<string, object>()

public handlers: Map<string, FrontendHook> = new Map<string, FrontendHook>()
constructor(
private workingDirectory: string,
private templateDirectory: string
Expand Down
154 changes: 154 additions & 0 deletions src/main/framework/execute-context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import { Workspace } from './workspace'
import { Command, ResultContentEvent, ResultStreamEvent, Runtime } from './runtime'
import { WebContents } from 'electron'
import { readFile } from 'fs-extra'
import short from 'short-uuid'
import { ResultStream } from './result-stream'

export interface RuntimeContentHandle {
id: string
update(html: string): void
on(event: string, handler: RuntimeContentEventCallback): void
}

export interface RuntimeContentEvent {
event: string
}

export type RuntimeContentEventCallback = (event: RuntimeContentEvent) => Promise<void> | void

export class ExecuteContext {
public readonly start: number = Date.now()
public readonly id: string = short.generate()

public readonly events: ResultContentEvent[] = []
private readonly eventHandlers = new Map<string, RuntimeContentEventCallback>()

constructor(
public readonly platform: string,
public readonly sender: WebContents,
public readonly workspace: Workspace,
public readonly runtime: Runtime,
public readonly command: Command,
public readonly profile: string,
public readonly history: {
enabled: boolean
results: boolean
max: number
}
) {}

out(text: string, error: boolean = false): ResultStream | null {
const isFinished = this.command.aborted || this.command.complete
if (isFinished) {
if (!this.command.result.edit) {
return null
}
}
const streamEntry = new ResultStream(text, error)

this.command.result.stream.push(streamEntry)

const streamEvent: ResultStreamEvent = {
entry: streamEntry,
runtime: this.runtime.id,
command: this.command.id
}

if (!this.sender.isDestroyed()) {
this.sender.send('runtime.commandEvent', streamEvent)
}

return streamEntry
}

content(html: string): RuntimeContentHandle {
const contextId = this.id
const runtimeId = this.runtime.id
const commandId = this.command.id

const command = this.command

const id = short.generate()
const events = this.events
const container = (html: string): string => `<span id="${id}">${html}</span>`

const R = this.out(container(html), false)
const sender = this.sender
const eventHandlers = this.eventHandlers
return {
id,
update(newHTML: string): void {
if (R !== null && !command.aborted && !command.complete) {
R.setText(container(newHTML))

sender.send('runtime.commandEvent')
}
},
on(event: string, callback: RuntimeContentEventCallback): void {
const eventHandlerId = short.generate()

eventHandlers.set(eventHandlerId, callback)

events.push({
event,
commandId,
contextId,
runtimeId,
contentId: id,
handlerId: eventHandlerId
})

sender.send('runtime.observe', {
contextId,
contentId: id,
eventHandlerId,
event
})

sender.send('runtime.commandEvent')
}
}
}

async fireEvent(eventName: string, handlerId: string): Promise<void> {
const event = this.eventHandlers.get(handlerId)
if (this.command.aborted || this.command.complete) {
return
}
if (!event) {
return
}

await event({
event: eventName
})
}

async edit(path: string, callback: (text: string) => void): Promise<void> {
const file = await readFile(path)

this.command.result.edit = {
path,
modified: false,
content: file.toString(),
callback
}

this.sender.send('runtime.commandEvent')
}

finish(code: number): void {
if (this.command.aborted || this.command.complete) {
return
}

this.command.result.code = code
this.command.complete = true
this.command.error = this.command.result.code !== 0

if (this.history.enabled) {
this.workspace.history.append(this.command, this.start, this.profile, this.history.results)
}
}
}
18 changes: 18 additions & 0 deletions src/main/framework/result-stream.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { sanitizeOutput } from '../util'

export class ResultStream {
public text: string
constructor(
public raw: string,
public error: boolean = false
) {
this.raw = this.raw.toString()
this.text = sanitizeOutput(raw)
}

setText(raw: string, error: boolean = this.error): void {
this.raw = raw
this.text = sanitizeOutput(raw)
this.error = error
}
}
Loading

0 comments on commit d8fbce5

Please sign in to comment.