Skip to content

Instantly share code, notes, and snippets.

@stevekinney
Created May 15, 2025 20:51
Show Gist options
  • Save stevekinney/1c17f31556902d67ef11f451313c3524 to your computer and use it in GitHub Desktop.
Save stevekinney/1c17f31556902d67ef11f451313c3524 to your computer and use it in GitHub Desktop.
import chalk from 'chalk';
import getPort, { portNumbers } from 'get-port';
export type TemporalServerOptions = {
/** The port for the Temporal server to listen on.*/
port: number;
/** The port for the Temporal UI to listen on. */
uiPort: number;
/** The port for the Temporal HTTP API to listen on. */
httpPort: number;
};
export class Temporal {
readonly #port: number;
readonly #uiPort: number;
readonly #httpPort: number;
#server: import('bun').Subprocess | null = null;
static async create(): Promise<Temporal> {
const defaultPort = 7233;
const port = await getPort({
port: portNumbers(defaultPort, defaultPort + 1000),
});
const uiPort = await getPort({ port: port + 1000 });
const httpPort = await getPort({ port: uiPort + 1000 });
const temporal = new Temporal({
port,
uiPort,
httpPort,
});
return temporal;
}
private constructor(options: TemporalServerOptions) {
this.#port = options.port;
this.#uiPort = options.uiPort;
this.#httpPort = options.httpPort;
this.shutdown = this.shutdown.bind(this);
process.on('SIGINT', this.shutdown);
process.on('SIGTERM', this.shutdown);
process.on('exit', this.shutdown);
}
get port(): string {
return String(this.#port);
}
get uiPort(): string {
return String(this.#uiPort);
}
get httpPort(): string {
return String(this.#httpPort);
}
start() {
if (this.#server) return;
this.#server = Bun.spawn([
this.cmd,
'server',
'start-dev',
'--log-level',
'error',
'--port',
this.port,
'--ui-port',
this.uiPort,
'--http-port',
this.httpPort,
]);
}
async shutdown(exitCode: number = 0) {
if (this.#server && this.#server.pid && !this.#server.killed) {
console.log();
this.log(chalk.blue('Terminating Temporal process…'));
this.#server.kill(exitCode);
await this.#server.exited;
this.log(chalk.blue('Temporal process terminated.'));
}
process.off('SIGINT', this.shutdown);
process.off('SIGTERM', this.shutdown);
process.off('exit', this.shutdown);
}
get cmd(): string {
const cmd = Bun.which('temporal');
if (!cmd) {
throw new Error(
'Temporal CLI not found. Please install it by running `brew install temporal` or refer to the Temporal documentation for installation instructions.',
);
}
return cmd;
}
log(...message: unknown[]) {
console.log(chalk.bgMagenta(' Temporal '), ...message);
}
}
if (import.meta.main) {
const temporal = await Temporal.create();
temporal.start();
temporal.log(
chalk.green('Started'),
chalk.cyan(`http://localhost:${temporal.port}`),
);
temporal.log(
chalk.yellow(`UI`),
chalk.cyan(`http://localhost:${temporal.uiPort}`),
);
temporal.log(
chalk.yellow(`HTTP API`),
chalk.cyan(`http://localhost:${temporal.uiPort}`),
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment