refactor(tsconnect): move the fork's own TS onto the init callback
build-pkg runs tsc and dts-bundle-generator over src/, so the demo app and the package entry point have to follow the runtime off the global factory or the wasm build stops working. Both now start the runtime through a shared startIPN helper, which installs the callback, passes its name in through go.env, and races readiness against the runtime exiting so a startup crash rejects instead of hanging. The panic handler is wired to that exit rather than being attached to a floating go.run() promise. Co-Authored-By: claude-opus-5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) Tailscale Inc & contributors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
/**
|
||||
* Starts a Go runtime and returns the single IPN it owns.
|
||||
*
|
||||
* The runtime does not publish its bridge on a global. It reads the name of a
|
||||
* callback from its environment and invokes it once the bridge is ready, so
|
||||
* this resolves on an explicit signal rather than on the Go scheduler having
|
||||
* run far enough. The name is generated per runtime, so several runtimes can
|
||||
* start in one page without racing each other.
|
||||
*/
|
||||
export async function startIPN(
|
||||
go: Go,
|
||||
instance: WebAssembly.Instance,
|
||||
config: IPNConfig,
|
||||
onExit: (reason: string) => void
|
||||
): Promise<IPN> {
|
||||
const name = `__tsconnectInit_${Math.random().toString(36).slice(2)}`
|
||||
const globals = globalThis as Record<string, unknown>
|
||||
|
||||
const ready = new Promise<(config: IPNConfig) => Promise<IPN>>(
|
||||
(resolve) => {
|
||||
globals[name] = (newIPN: (config: IPNConfig) => Promise<IPN>) => {
|
||||
delete globals[name]
|
||||
resolve(newIPN)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
go.env[INIT_CALLBACK_ENV] = name
|
||||
const exited = go.run(instance).then(() => {
|
||||
delete globals[name]
|
||||
onExit("Unexpected shutdown")
|
||||
throw new Error("Go runtime exited before the IPN was ready")
|
||||
})
|
||||
|
||||
const newIPN = await Promise.race([ready, exited])
|
||||
return newIPN(config)
|
||||
}
|
||||
|
||||
/** Must match initCallbackEnv in wasm_js.go. */
|
||||
const INIT_CALLBACK_ENV = "TSCONNECT_INIT_CALLBACK"
|
||||
Reference in New Issue
Block a user