mirror of
https://github.com/stablyai/orca.git
synced 2026-11-01 17:47:40 +00:00
* [comment audit] Align legacy failure-read counts with cached display * [comment audit] Condense shared provider lifecycle and browser recovery comments [skip ci] * [comment audit] Condense shared generation, path and preview comments [skip ci] * [comment audit] Condense host, file and Git contract rationale [skip ci] * [comment audit] Tighten Git lifecycle and provider contract comments [skip ci] * [comment audit] Clarify shared host authority and launch isolation [skip ci] * [comment audit] simplify local access mobile and native chat contracts [skip ci] * [comment audit] simplify native chat presentation and state contracts [skip ci] * [comment audit] simplify native runtime and provider contracts [skip ci] * [comment audit] simplify migration and orchestration contracts [skip ci] * [comment audit] simplify plugin trust and process capture rationale [skip ci] * [comment audit] simplify terminal delivery and remote compatibility rationale [skip ci] * [comment audit] clarify remote recovery and launch wire rationale [skip ci] * [comment audit] shorten shared RPC compatibility and runtime evidence comments [skip ci] * [comment audit] simplify secure storage shell startup and sidebar rationale [skip ci] * [comment audit] clarify sidebar, skill and SSH invariants [skip ci] * [comment audit] condense structured chat delivery and timing rationale [skip ci] * [comment audit] condense terminal replay and telemetry invariants [skip ci] * [comment audit] clarify launch permission and terminal delivery rationale [skip ci] * [comment audit] clarify workspace ownership and layout contracts [skip ci] * [comment audit] wait for fitted and painted SSH terminal links * [comment audit] clarify layout persistence and session ownership [skip ci] * [comment audit] clarify host routing and workspace lifecycle contracts [skip ci] * [comment audit] clarify startup persistence and app lifetime comments [skip ci] * [comment audit] clarify recovery and activity boundaries [skip ci] * [comment audit] condense activity and artifact invariants [skip ci] * [comment audit] condense automation authority contracts [skip ci] * [comment audit] simplify automation scheduling and history contracts [skip ci] * [comment audit] clarify browser chrome and annotation lifecycles [skip ci] * [comment audit] clarify browser routing and retained guest ownership [skip ci] * [comment audit] clarify browser recovery and document ownership comments [skip ci] * [comment audit] restore hosted runners required by native IME fixtures * [comment audit] shorten preview, palette and dashboard invariants [skip ci] * [comment audit] clarify terminal preview, dashboard and retention comments [skip ci] * [comment audit] clarify diff-note recovery and editor lifecycle comments [skip ci] * [comment audit] shorten rich-text and combined-diff ownership comments [skip ci] * [comment audit] shorten autosave, draft recovery and model ownership comments [skip ci] * [comment audit] shorten save-queue, notebook and Markdown constraints [skip ci] * [comment audit] shorten Monaco, PDF and rich Markdown safeguards [skip ci] * [comment audit] clarify rich Markdown source and selection safeguards [skip ci] * [comment audit] clarify editor loading and emulator stream lifetimes [skip ci] * [comment audit] trim emulator and feature tour narration [skip ci] * [comment audit] clarify floating workspace and GitHub cache ownership [skip ci] * [comment audit] trim provider controls and Linear filter explanations [skip ci] * [comment audit] clarify pairing and chat resume lifetimes [skip ci] * [comment audit] clarify chat delivery and transcript lifetimes [skip ci] * [comment audit] clarify session options and host-owned status [skip ci] * [comment audit] clarify draft durability and live-status precedence [skip ci] * [comment audit] Trim chat send and model ownership explanations [skip ci] * [comment audit] Shorten transcript ownership and send recovery comments [skip ci] * [comment audit] Simplify native chat composer and transcript hook comments [skip ci] * [comment audit] Shorten native chat scroll and structured session explanations [skip ci] * [comment audit] Trim workspace picker and onboarding commentary [skip ci] * [comment audit] Simplify pet rendering, review cache and quick open comments [skip ci] * [comment audit] Trim sidebar and session history explanations [skip ci] * [comment audit] Condense session resume and review checks comments [skip ci] * [comment audit] Shorten checks state and file explorer refresh comments [skip ci] * [comment audit] Trim plugin bridge and source-control action rationale [skip ci] * [comment audit] Trim source-control listing and review rationale [skip ci] * [comment audit] Shorten sidebar lifecycle and explorer safeguards [skip ci] * [comment audit] Condense Settings account and appearance rationale [skip ci] * [comment audit] Trim pairing and repository Settings explanations [skip ci] * [comment audit] Condense Settings search SSH and terminal safeguards [skip ci] * [comment audit] Shorten Settings voice and provider ownership comments [skip ci] * [comment audit] Condense Settings draft persistence and host-state rationale [skip ci] * [comment audit] Trim Settings hooks and sidebar setup rationale [skip ci] * [comment audit] Condense sidebar deletion and board-filter safeguards [skip ci] * [comment audit] Condense worktree cards, host ownership and Markdown contracts [skip ci] * [comment audit] Condense sidebar drag, folder and filtering contracts [skip ci] * [comment audit] Condense sidebar parity, sleep and host ownership contracts [skip ci] * [comment audit] Prune focus-read baseline after comment cleanup * [comment audit] Clarify sidebar visibility, board drops and agent evidence [skip ci] * [comment audit] Tighten sidebar listing, drag and reveal contracts [skip ci] * [comment audit] Clarify viewport geometry and skill update safeguards [skip ci] * [comment audit] Tighten skill inventory, usage and host status comments [skip ci] * [comment audit] Clarify usage ownership and resource kill safeguards [skip ci] * [comment audit] Tighten tab selection, rename and search contracts [skip ci] * [comment audit] Tighten tab-strip geometry and drag ownership comments [skip ci] * [comment audit] Tighten Tasks recovery, pagination and mutation comments [skip ci] * [comment audit] Tighten provider loading and terminal recovery comments [skip ci] * [comment audit] Tighten completion, cadence and PTY ownership comments [skip ci] * [comment audit] Clarify terminal restore and parked-pane boundaries [skip ci] * [comment audit] Tighten PTY startup and replay explanations [skip ci] * [comment audit] Clarify PTY identity and delivery ownership [skip ci] * [comment audit] Shorten remote reconnect and input replay rationale [skip ci] * [comment audit] Condense terminal parking clipboard and file link comments [skip ci] * [comment audit] Clarify IME ownership and trim trace history [skip ci] * [comment audit] Shorten pane recovery and parked watcher explanations [skip ci] * [comment audit] Condense snapshot replay and visibility recovery rationale [skip ci] * [comment audit] Shorten terminal parking and keyboard ownership rationale [skip ci] * [comment audit] Trim renderer lifecycle and menu rationale [skip ci] * [comment audit] Condense workspace cleanup ownership and settlement notes [skip ci] * [comment audit] Clarify renderer hook evidence and source ownership [skip ci] * [comment audit] Tighten IPC ownership and reconnect explanations [skip ci] * [comment audit] Trim renderer focus, paste and attention comments [skip ci] * [comment audit] Shorten scroll, locale and agent lifecycle comments [skip ci] * [comment audit] Trim agent launch, resume and ownership commentary [skip ci] * [comment audit] Shorten account, browser and editor comments [skip ci] * [comment audit] Clarify host-mirror waits, previews and input ownership [skip ci] * [comment audit] Condense launch recovery and editor tokenization comments [skip ci] * [comment audit] Condense workspace selection and terminal pane rationale [skip ci] * [comment audit] Condense terminal fitting and rendering recovery comments [skip ci] * [comment audit] Condense terminal input and host routing explanations [skip ci] * [comment audit] Condense ownership and sleeping-session recovery rationale [skip ci] * [comment audit] Condense structured launch and terminal link explanations [skip ci] * [comment audit] Condense terminal ownership and workspace file routing rationale [skip ci] * [comment audit] Condense session persistence and workspace activation rationale [skip ci] * [comment audit] Condense workspace routing and browser ownership rationale [skip ci] * [comment audit] Condense mirror recovery and layout authority rationale [skip ci] * [comment audit] Condense runtime transport and file safety rationale [skip ci] * [comment audit] Condense runtime graph and structured session rationale [skip ci] * [comment audit] Condense browser creation and remote tab recovery rationale [skip ci] * [comment audit] Condense snapshot authority and publisher recovery rationale [skip ci] * [comment audit] Condense frame, startup and provider cache rationale [skip ci] * [comment audit] Condense host twin and agent lifecycle rationale [skip ci] * [comment audit] Condense browser authority and editor cleanup rationale [skip ci] * [comment audit] Condense editor buffer identity and review cache rationale [skip ci] * [comment audit] Condense provider lifetime and repository ownership rationale [skip ci] * [comment audit] Condense reconnect, hydration and terminal retirement rationale [skip ci] * [comment audit] Condense recovery, reconciliation and persistence rationale [skip ci] * [comment audit] Condense cleanup ownership and listing admission rationale [skip ci] * [comment audit] Condense teardown ordering and terminal ownership rationale [skip ci] * [comment audit] Condense recovery ledger and web host-boundary rationale [skip ci] * [comment audit] Condense web reconnect and credential lifecycle rationale [skip ci] * [comment audit] Condense cloud rollout and assignment safety rationale [skip ci] * [comment audit] Condense relay session and database recovery rationale [skip ci] * [comment audit] Condense relay placement and protocol compatibility rationale [skip ci] * [comment audit] Condense build and packaging constraints [skip ci] * [comment audit] Condense CI gates and validation isolation rationale [skip ci] * [comment audit] Condense catalog identity and browser validation rationale [skip ci] * [comment audit] Condense mobile browser and frame-budget rationale [skip ci] * [comment audit] Shorten mobile route and browser test explanations [skip ci] * [comment audit] Condense editor and session seam test rationale [skip ci] * [comment audit] Trim session and terminal closure history [skip ci] * [comment audit] Shorten terminal browser and census explanations [skip ci] * [comment audit] Preserve root-only E2E diagnostic baseline * [comment audit] Condense mobile seam and HTML preview test rationale [skip ci] * [comment audit] Condense preview instrumentation and render harness rationale [skip ci] * [comment audit] Condense bundle contracts and native-build constraints [skip ci] * [comment audit] Shorten native packaging and CI routing rationale [skip ci] * [comment audit] Condense release tooling and benchmark constraints [skip ci] * [comment audit] Condense packaging and platform gate rationale [skip ci] * [comment audit] Shorten mobile route and history rationale [skip ci] * [comment audit] Condense mobile browser and component rationale [skip ci] * [comment audit] Shorten drawer and workspace-form rationale [skip ci] * [comment audit] Trim mobile review and editor-document explanations [skip ci] * [comment audit] Condense editor lifecycle and Markdown explanations [skip ci] * [comment audit] Trim mobile file, diagnostics and dictation commentary [skip ci] * [comment audit] Simplify mobile host-list and workspace-view explanations [skip ci] * [comment audit] Clarify mobile shell lifetime and bridge delivery constraints [skip ci] * [comment audit] Condense bridge stream, native request and audio rationale [skip ci] * [comment audit] Reduce bridge schema and page-client commentary [skip ci] * [comment audit] Tighten mobile page bridge contracts [skip ci] * [comment audit] Clarify mobile cache and session constraints [skip ci] * [comment audit] Condense mobile route and lifecycle rationale [skip ci] * [comment audit] Tighten mobile shell hook timing rationale [skip ci] * [comment audit] Clarify route handoff and notification compatibility [skip ci] * [comment audit] Clarify push registration and dictation cleanup constraints [skip ci] * [comment audit] Condense keyboard geometry and native media ownership rationale [skip ci] * [comment audit] Tighten mobile chat scroll and composer concurrency rationale [skip ci] * [comment audit] Condense mobile reply compatibility and upload ownership rationale [skip ci] * [comment audit] Condense optimistic echo and scroll ownership rationale [skip ci] * [comment audit] Tighten stream replay and queued session ownership rationale [skip ci] * [comment audit] Condense mobile composer and reply-schema constraints [skip ci] * [comment audit] Clarify mobile transcript recovery and session ownership [skip ci] * [comment audit] Clarify mobile queue and source-control compatibility [skip ci] * [comment audit] Condense storage and task launch constraints [skip ci] * [comment audit] Clarify task schemas and terminal document ownership [skip ci] * [comment audit] Tighten terminal input and viewport rationale [skip ci] * [comment audit] Shorten terminal and RPC recorder contracts [skip ci] * [comment audit] Condense recorder engine and connection contracts [skip ci] * [comment audit] Shorten host credential and relay recovery comments [skip ci] * [comment audit] Tighten pairing recovery and bundle transport rationale [skip ci] * [comment audit] Clarify RPC delivery and workspace catalog boundaries [skip ci] * [comment audit] Condense catalog and relay entry contracts [skip ci] * [comment audit] Shorten shutdown and SSH relay delivery contracts [skip ci] * [comment audit] Condense WSL relay and E2E regression rationale [skip ci] * [comment audit] Shorten cross-version harness and renderer fixture explanations [skip ci] * [comment audit] Condense paired-host and E2E lifecycle rationale [skip ci] * [comment audit] Shorten layout oracle and regression explanations [skip ci] * [comment audit] Condense paired recovery and ownership regression contracts [skip ci] * [comment audit] Shorten restart, sidebar and SSH lifecycle contracts [skip ci] * [comment audit] Condense terminal rendering and IME regression rationale [skip ci] * [comment audit] Shorten terminal recovery and benchmark contracts [skip ci] * [comment audit] Condense tooling and Windows lifecycle proof rationale [skip ci] * [comment audit] Condense workflow routing and release rationale [skip ci] * [comment audit] Condense mobile security and installer contracts [skip ci] * [comment audit] Shorten native rationale and stylesheet workarounds [skip ci] * [comment audit] Shorten Windows visibility probe rationale [skip ci] * [comment audit] Condense embedded release and signing rationale [skip ci] * [comment audit] Retain current main test behavior in final reconciliation [skip ci] * [comment audit] Complete final comment review across current source inventory * [comment audit] Handle consolidated audit reports before final CI * [comment audit] Preserve credential storage census detail [skip ci] * [comment audit] Scale broad changed E2E selections with verified timing shards * [comment audit] Align E2E assertions with browser close and settled session metadata * [comment audit] Match existing E2Es to current resource and windowing contracts * [comment audit] Observe the shared server outage notice in reconnect coverage * [comment audit] Make existing renderer probes measure settled tab and PTY work * [comment audit] Align palette E2E selector with task search * [comment audit] Refresh remaining palette search selectors * [comment audit] Keep selector correction within selected audit specs * [comment audit] Await emoji fixture settings inside renderer evaluation * [comment audit] Cancel fixture fit retries between terminal tests --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
349 lines
12 KiB
TypeScript
349 lines
12 KiB
TypeScript
import { spawn } from 'node:child_process'
|
|
import { isBuiltin } from 'node:module'
|
|
import { join } from 'node:path'
|
|
import type { Plugin, Rollup } from 'vite'
|
|
|
|
type NormalizedInputOptions = Rollup.NormalizedInputOptions
|
|
type NormalizedOutputOptions = Rollup.NormalizedOutputOptions
|
|
type OutputBundle = Rollup.OutputBundle
|
|
type OutputChunk = Rollup.OutputChunk
|
|
|
|
// A shared-chunk Electron import killed plain-Node daemons in v1.4.129-rc.1 (#7642).
|
|
// Scan every reachable fork/worker chunk for Electron requires; smoke-load daemon-entry to verify module resolution.
|
|
|
|
// The CLI loads these paths after electron-vite replaces out/main.
|
|
export const CLI_MAIN_ENTRY_NAMES = [
|
|
'agent-hooks/managed-agent-hook-controls',
|
|
'gitlab/project-ref-parser',
|
|
'orca-profiles/profile-index-store',
|
|
'claude-accounts/keychain',
|
|
'skills/bundled-skill-install/cli-entry',
|
|
'macos-tree-clone',
|
|
'orcad/orcad-stale-lock-reclaim',
|
|
...[
|
|
'access',
|
|
'active-location',
|
|
'storage-classification',
|
|
'offline-settings',
|
|
'backup-path',
|
|
'database-recovery',
|
|
'domain-reader',
|
|
'recovery-command'
|
|
].map((module) => `persistence/profile-state/profile-state-${module}`),
|
|
'persistence/profile-state/legacy-json/profile-state-export-path',
|
|
'persistence/profile-state/legacy-json/profile-state-recovery',
|
|
'startup/http1-compatibility-marker'
|
|
] as const
|
|
|
|
// Plain-Node processes and CLI modules cannot load Electron's API.
|
|
const PLAIN_NODE_ENTRY_NAMES = [
|
|
'daemon-entry',
|
|
'parcel-watcher-process-entry',
|
|
'session-scanner-service-entry',
|
|
'computer-sidecar',
|
|
'wsl-transcript-fs-process-entry',
|
|
'orcad/orcad-local-serve-selection-entry',
|
|
...CLI_MAIN_ENTRY_NAMES
|
|
] as const
|
|
|
|
// Electron is unavailable in main-process worker threads too (verified Electron 43); scan their graphs rather than trusting entry comments.
|
|
const WORKER_THREAD_ENTRY_NAMES = [
|
|
'stt-worker',
|
|
'warp-theme-parser-worker',
|
|
'foreign-sqlite-reader-entry',
|
|
'main-thread-hang-watchdog-entry',
|
|
'port-scan-command-worker-entry',
|
|
'worker-thread-child-process-entry',
|
|
'usage-scan-worker-entry',
|
|
'workspace-space-scan-worker-entry',
|
|
'claude-profile-setup-worker-entry',
|
|
'profile-state-backup-worker-entry',
|
|
'profile-state-writer-worker-entry'
|
|
] as const
|
|
|
|
export const GUARDED_ENTRY_NAMES = [
|
|
...PLAIN_NODE_ENTRY_NAMES,
|
|
...WORKER_THREAD_ENTRY_NAMES
|
|
] as const
|
|
|
|
type EntryRuntime = 'plain-Node process' | 'worker thread'
|
|
|
|
// Subpaths (electron/main) are as unloadable as the bare module under plain Node.
|
|
const ELECTRON_REQUIRE_RE = /require\(\s*["'`]electron(?:\/[^"'`]+)?["'`]\s*\)/
|
|
|
|
// Pin guarded names to Rollup inputs or renames silently remove entries from the guard.
|
|
function assertEntryNamesAreRollupInputs(input: NormalizedInputOptions['input']): void {
|
|
if (typeof input === 'string' || Array.isArray(input)) {
|
|
return
|
|
}
|
|
const inputNames = new Set(Object.keys(input))
|
|
const missing = GUARDED_ENTRY_NAMES.filter((name) => !inputNames.has(name))
|
|
if (missing.length > 0) {
|
|
throw new Error(
|
|
`[plain-node-entry-guard] guarded ${missing.map((name) => `"${name}"`).join(', ')} ` +
|
|
`${missing.length === 1 ? 'is not a rollup input' : 'are not rollup inputs'} anymore. ` +
|
|
`Update PLAIN_NODE_ENTRY_NAMES/WORKER_THREAD_ENTRY_NAMES in plain-node-entry-guard.ts to ` +
|
|
`the current entry names — a stale name silently stops guarding that entry.`
|
|
)
|
|
}
|
|
}
|
|
|
|
function collectReachableChunks(
|
|
entry: OutputChunk,
|
|
byFileName: Map<string, OutputChunk>
|
|
): OutputChunk[] {
|
|
const seen = new Set<string>()
|
|
const reachable: OutputChunk[] = []
|
|
const stack = [entry.fileName]
|
|
while (stack.length > 0) {
|
|
const fileName = stack.pop() as string
|
|
if (seen.has(fileName)) {
|
|
continue
|
|
}
|
|
seen.add(fileName)
|
|
const chunk = byFileName.get(fileName)
|
|
if (!chunk) {
|
|
continue
|
|
}
|
|
reachable.push(chunk)
|
|
for (const imported of [...chunk.imports, ...chunk.dynamicImports]) {
|
|
stack.push(imported)
|
|
}
|
|
}
|
|
return reachable
|
|
}
|
|
|
|
function assertNoElectronRequire(
|
|
entryName: string,
|
|
entry: OutputChunk,
|
|
byFileName: Map<string, OutputChunk>,
|
|
electronFreeChunkCode: Map<OutputChunk, string>,
|
|
runtime: EntryRuntime = 'plain-Node process'
|
|
): void {
|
|
for (const chunk of collectReachableChunks(entry, byFileName)) {
|
|
const code = chunk.code
|
|
if (electronFreeChunkCode.get(chunk) === code) {
|
|
continue
|
|
}
|
|
if (ELECTRON_REQUIRE_RE.test(code)) {
|
|
throw new Error(
|
|
`[plain-node-entry-guard] "${entryName}" reaches chunk "${chunk.fileName}" that ` +
|
|
`requires electron. "${entryName}" runs as a ${runtime}, where ` +
|
|
`require("electron") throws MODULE_NOT_FOUND and kills it at startup (the ` +
|
|
`v1.4.129-rc.1 daemon outage). Keep electron imports out of its module graph.`
|
|
)
|
|
}
|
|
electronFreeChunkCode.set(chunk, code)
|
|
}
|
|
}
|
|
|
|
// Relocated Windows daemon copies only these packages; other bare requires cause silent fallback to an unrelocated daemon that updates kill.
|
|
const DAEMON_HOST_MIRRORED_PACKAGES = [
|
|
'@orca/process-host',
|
|
'node-pty',
|
|
'@vscode/windows-process-tree'
|
|
]
|
|
const BARE_REQUIRE_RE = /require\(\s*["'`]([^"'`./][^"'`]*)["'`]\s*\)/g
|
|
|
|
function packageOfSpecifier(specifier: string): string {
|
|
const parts = specifier.split('/')
|
|
return specifier.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0]!
|
|
}
|
|
|
|
function assertDaemonExternalsAreMirrored(
|
|
entry: OutputChunk,
|
|
byFileName: Map<string, OutputChunk>
|
|
): void {
|
|
for (const chunk of collectReachableChunks(entry, byFileName)) {
|
|
for (const match of chunk.code.matchAll(BARE_REQUIRE_RE)) {
|
|
const specifier = match[1]!
|
|
if (specifier.startsWith('node:') || isBuiltin(specifier)) {
|
|
continue
|
|
}
|
|
if (!DAEMON_HOST_MIRRORED_PACKAGES.includes(packageOfSpecifier(specifier))) {
|
|
throw new Error(
|
|
`[plain-node-entry-guard] "daemon-entry" reaches chunk "${chunk.fileName}" that ` +
|
|
`requires "${specifier}", which the relocated Windows daemon-host does not mirror ` +
|
|
`(daemon-host-manifest.ts). Keep it out of the daemon graph, or mirror it there.`
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Owned by the argv parser in src/main/daemon/daemon-entry.ts — keep in sync.
|
|
const DAEMON_USAGE_PREFIX = 'Usage: daemon-entry'
|
|
|
|
export type SmokeTimings = {
|
|
timeoutMs: number
|
|
// daemon-entry traps SIGTERM and awaits a native shutdown, so the deadline
|
|
// needs an uncatchable follow-up to stay a deadline.
|
|
killGraceMs: number
|
|
}
|
|
|
|
const DEFAULT_SMOKE_TIMINGS: SmokeTimings = { timeoutMs: 15_000, killGraceMs: 2_000 }
|
|
|
|
// Bound the wait for stderr to flush after exit; a grandchild inheriting stdio
|
|
// can hold the pipes open long after the child is gone.
|
|
const SMOKE_STDERR_DRAIN_MS = 250
|
|
|
|
type SmokeResult = {
|
|
status: number | null
|
|
signal: NodeJS.Signals | null
|
|
stderr: string
|
|
error?: Error
|
|
timedOut: boolean
|
|
}
|
|
|
|
// spawnSync timeout waits after SIGTERM; trapped signals can hang builds, so escalate asynchronously to SIGKILL.
|
|
function runDaemonEntry(entryPath: string, timings: SmokeTimings): Promise<SmokeResult> {
|
|
return new Promise((resolve) => {
|
|
const child = spawn(process.execPath, [entryPath], { stdio: ['ignore', 'ignore', 'pipe'] })
|
|
let stderr = ''
|
|
let timedOut = false
|
|
let settled = false
|
|
let forceKillTimer: NodeJS.Timeout | undefined
|
|
let drainTimer: NodeJS.Timeout | undefined
|
|
|
|
child.stderr.setEncoding('utf8')
|
|
child.stderr.on('data', (chunk: string) => {
|
|
stderr += chunk
|
|
})
|
|
|
|
const deadlineTimer = setTimeout(() => {
|
|
timedOut = true
|
|
child.kill('SIGTERM')
|
|
forceKillTimer = setTimeout(() => child.kill('SIGKILL'), timings.killGraceMs)
|
|
}, timings.timeoutMs)
|
|
|
|
const finish = (status: number | null, signal: NodeJS.Signals | null, error?: Error): void => {
|
|
if (settled) {
|
|
return
|
|
}
|
|
settled = true
|
|
clearTimeout(deadlineTimer)
|
|
clearTimeout(forceKillTimer)
|
|
clearTimeout(drainTimer)
|
|
resolve({ status, signal, stderr, error, timedOut })
|
|
}
|
|
|
|
child.on('error', (error: Error) => finish(null, null, error))
|
|
// 'close' gives the full stderr; 'exit' is the fallback so a held-open pipe
|
|
// cannot outlast the process itself.
|
|
child.on('close', (status, signal) => finish(status, signal))
|
|
child.on('exit', (status, signal) => {
|
|
drainTimer = setTimeout(() => finish(status, signal), SMOKE_STDERR_DRAIN_MS)
|
|
})
|
|
})
|
|
}
|
|
|
|
// Smoke proves plain-Node graph resolution; static scan catches Electron leaks because dev node_modules can make require("electron") succeed.
|
|
async function smokeLoadDaemonEntry(outputDir: string, timings: SmokeTimings): Promise<void> {
|
|
const entryPath = join(outputDir, 'daemon-entry.js')
|
|
const result = await runDaemonEntry(entryPath, timings)
|
|
if (result.error) {
|
|
throw new Error(
|
|
`[plain-node-entry-guard] could not smoke-load daemon-entry.js under plain Node: ` +
|
|
`${result.error.message}`
|
|
)
|
|
}
|
|
// Almost always means the daemon stopped rejecting an empty argv and started
|
|
// listening instead.
|
|
if (result.timedOut) {
|
|
throw new Error(
|
|
`[plain-node-entry-guard] daemon-entry.js did not exit within ${timings.timeoutMs}ms on an ` +
|
|
`empty argv under plain Node, so the smoke killed it.`
|
|
)
|
|
}
|
|
if (result.signal) {
|
|
throw new Error(
|
|
`[plain-node-entry-guard] daemon-entry.js was killed by ${result.signal} under plain Node.`
|
|
)
|
|
}
|
|
const stderr = result.stderr
|
|
if (/Cannot find module|MODULE_NOT_FOUND/.test(stderr)) {
|
|
throw new Error(
|
|
`[plain-node-entry-guard] daemon-entry.js failed to load under plain Node:\n${stderr}`
|
|
)
|
|
}
|
|
if (result.status === 0 || !stderr.includes(DAEMON_USAGE_PREFIX)) {
|
|
throw new Error(
|
|
`[plain-node-entry-guard] daemon-entry.js did not reject an empty argv under plain Node ` +
|
|
`(expected a non-zero exit and the "${DAEMON_USAGE_PREFIX}" error, got exit ` +
|
|
`${result.status}). stderr:\n${stderr}`
|
|
)
|
|
}
|
|
}
|
|
|
|
export function createPlainNodeEntryGuardPlugin(
|
|
smokeTimings: SmokeTimings = DEFAULT_SMOKE_TIMINGS
|
|
): Plugin {
|
|
let daemonOutputDir: string | undefined
|
|
|
|
return {
|
|
name: 'orca-plain-node-entry-guard',
|
|
buildStart(options: NormalizedInputOptions) {
|
|
assertEntryNamesAreRollupInputs(options.input)
|
|
},
|
|
writeBundle(options: NormalizedOutputOptions, bundle: OutputBundle) {
|
|
// Skip dev-watch smoke or every rebuild respawns daemon-entry; gate produced builds only.
|
|
if (this.meta.watchMode) {
|
|
return
|
|
}
|
|
const chunks = Object.values(bundle).filter(
|
|
(item): item is OutputChunk => item.type === 'chunk'
|
|
)
|
|
const byFileName = new Map(chunks.map((chunk) => [chunk.fileName, chunk]))
|
|
const entryByName = new Map<string, OutputChunk>()
|
|
for (const chunk of chunks) {
|
|
if (chunk.isEntry && chunk.name) {
|
|
entryByName.set(chunk.name, chunk)
|
|
}
|
|
}
|
|
|
|
const electronFreeChunkCode = new Map<OutputChunk, string>()
|
|
for (const entryName of PLAIN_NODE_ENTRY_NAMES) {
|
|
const entry = entryByName.get(entryName)
|
|
if (entry) {
|
|
assertNoElectronRequire(
|
|
entryName,
|
|
entry,
|
|
byFileName,
|
|
electronFreeChunkCode,
|
|
'plain-Node process'
|
|
)
|
|
}
|
|
}
|
|
|
|
for (const entryName of WORKER_THREAD_ENTRY_NAMES) {
|
|
const entry = entryByName.get(entryName)
|
|
if (entry) {
|
|
assertNoElectronRequire(
|
|
entryName,
|
|
entry,
|
|
byFileName,
|
|
electronFreeChunkCode,
|
|
'worker thread'
|
|
)
|
|
}
|
|
}
|
|
|
|
const daemonEntry = entryByName.get('daemon-entry')
|
|
if (daemonEntry) {
|
|
assertDaemonExternalsAreMirrored(daemonEntry, byFileName)
|
|
}
|
|
|
|
if (entryByName.has('daemon-entry') && options.dir) {
|
|
daemonOutputDir = options.dir
|
|
}
|
|
},
|
|
async closeBundle() {
|
|
if (daemonOutputDir) {
|
|
const outputDir = daemonOutputDir
|
|
daemonOutputDir = undefined
|
|
await smokeLoadDaemonEntry(outputDir, smokeTimings)
|
|
}
|
|
}
|
|
}
|
|
}
|