Merge finalized Web transcript parent

# Conflicts:
#	apps/cli/README.i18n.yaml
#	packages/client/runtime/README.i18n.yaml
#	packages/client/ui-trajectory/README.i18n.yaml
#	packages/client/ui-trajectory/README.zh.md
This commit is contained in:
Tianyi Cui
2026-07-31 13:42:47 +08:00
324 changed files with 10708 additions and 1388 deletions

View File

@@ -1,6 +1,17 @@
#!/usr/bin/env node
import { randomUUID } from 'node:crypto'
import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'
import {
closeSync,
existsSync,
fstatSync,
lstatSync,
mkdirSync,
openSync,
readdirSync,
readFileSync,
unlinkSync,
writeFileSync,
} from 'node:fs'
import { spawnSync } from 'node:child_process'
import { dirname, isAbsolute, join, resolve } from 'node:path'
@@ -11,6 +22,7 @@ const OWNERSHIP_MARKER_VERSION = 1
const OWNERSHIP_MARKER_OWNER = 'deepseek-harness worktree-local lefthook hooks'
const INSTALL_LOCK = 'dsh-lefthook-install.lock'
const INSTALL_LOCK_TIMEOUT_MS = 30_000
const INSTALL_LOCK_INITIALIZATION_TIMEOUT_MS = 1_000
const INSTALL_LOCK_POLL_MS = 50
const ALLOW_HOOKS_PATH_OVERRIDE = 'DSH_LEFTHOOK_ALLOW_HOOKS_PATH_OVERRIDE'
const REPOSITORY_EXTENSION_PATTERN = '^extensions\\.'
@@ -303,6 +315,11 @@ function parseInstallLock(record) {
return Number.isSafeInteger(owner) ? owner : undefined
}
function installLockRecordMayBeIncomplete(record) {
// Exclusive creation exposes the inode before its owner record is fully written.
return record === '' || (!record.endsWith('\n') && /^[1-9]\d*(?: [0-9a-f-]*)?$/i.test(record))
}
function lockOwnerIsAlive(owner) {
try {
process.kill(owner, 0)
@@ -351,11 +368,29 @@ async function acquireInstallLock(commonDirectory) {
const lockPath = join(commonDirectory, INSTALL_LOCK)
const deadline = Date.now() + INSTALL_LOCK_TIMEOUT_MS
const ownedRecord = `${String(process.pid)} ${randomUUID()}\n`
let initializingLock
while (true) {
try {
writeFileSync(lockPath, ownedRecord, { flag: 'wx', mode: 0o600 })
const ownedStat = installLockStat(lockPath)
if (ownedStat === undefined || !ownedStat.isFile() || ownedStat.isSymbolicLink()) {
const lockHandle = openSync(lockPath, 'wx', 0o600)
let ownedStat
try {
ownedStat = fstatSync(lockHandle)
const writeDelay = Number(process.env.DSH_TEST_LEFTHOOK_LOCK_WRITE_DELAY_MS ?? 0)
if (writeDelay > 0) {
await new Promise(resolveWait => setTimeout(resolveWait, writeDelay))
}
writeFileSync(lockHandle, ownedRecord)
} finally {
closeSync(lockHandle)
}
const publishedStat = installLockStat(lockPath)
if (
publishedStat === undefined
|| !publishedStat.isFile()
|| publishedStat.isSymbolicLink()
|| publishedStat.dev !== ownedStat.dev
|| publishedStat.ino !== ownedStat.ino
) {
throw lockOwnershipChangedError(lockPath)
}
return () => releaseInstallLock(lockPath, ownedRecord, ownedStat)
@@ -368,8 +403,36 @@ async function acquireInstallLock(commonDirectory) {
}
const existingRecord = readInstallLock(lockPath)
if (existingRecord === undefined) continue
const verifiedStat = installLockStat(lockPath)
if (verifiedStat === undefined) continue
if (!verifiedStat.isFile() || verifiedStat.isSymbolicLink()) {
throw manualLockRecoveryError(lockPath, 'invalid')
}
if (verifiedStat.dev !== existingStat.dev || verifiedStat.ino !== existingStat.ino) continue
const owner = parseInstallLock(existingRecord)
if (owner === undefined) throw manualLockRecoveryError(lockPath, 'invalid')
if (owner === undefined) {
if (!installLockRecordMayBeIncomplete(existingRecord)) {
throw manualLockRecoveryError(lockPath, 'invalid')
}
const now = Date.now()
if (
initializingLock === undefined
|| initializingLock.dev !== existingStat.dev
|| initializingLock.ino !== existingStat.ino
) {
initializingLock = {
deadline: now + INSTALL_LOCK_INITIALIZATION_TIMEOUT_MS,
dev: existingStat.dev,
ino: existingStat.ino,
}
}
if (now >= initializingLock.deadline) {
throw manualLockRecoveryError(lockPath, 'invalid')
}
await new Promise(resolveWait => setTimeout(resolveWait, INSTALL_LOCK_POLL_MS))
continue
}
initializingLock = undefined
if (!lockOwnerIsAlive(owner)) throw manualLockRecoveryError(lockPath, 'stale')
if (Date.now() >= deadline) {
throw new Error(`timed out waiting for Lefthook installer lock ${lockPath}`)

View File

@@ -196,7 +196,7 @@ function runInstaller(
})
}
describe('worktree-local Lefthook installer', () => {
describe('worktree-local Lefthook installer', { timeout: 15_000 }, () => {
for (const [label, extraEnv] of [
['CI', { CI: 'true' }],
['GitHub Actions', { GITHUB_ACTIONS: 'true' }],
@@ -307,6 +307,22 @@ describe('worktree-local Lefthook installer', () => {
expect(existsSync(join(hooksPath(fixture, fixture.main), '.fake-lefthook-running'))).toBe(false)
})
it('waits for a concurrent installer to finish publishing its lock record', async () => {
const fixture = createFixture()
const lockPath = installLockPath(fixture)
const publishing = runInstaller(fixture, fixture.main, {
DSH_TEST_LEFTHOOK_LOCK_WRITE_DELAY_MS: '200',
})
await waitForPath(lockPath)
expect(readFileSync(lockPath, 'utf8')).toBe('')
const waiting = runInstaller(fixture, fixture.linked)
const results = await Promise.all([publishing, waiting])
for (const result of results) expect(result.status, result.stderr).toBe(0)
expect(existsSync(lockPath)).toBe(false)
})
it('repairs its owned absolute hook path after the checkout moves', async () => {
const fixture = createFixture()
const oldRoot = fixture.main

View File

@@ -135,6 +135,23 @@ describe('Oxlint gate', () => {
})
})
describe('Node compatibility graph', () => {
it('runs the jsdom environment smoke on every advertised Node line', () => {
const subject = withPnpmEntrypoint(() => gatesForMode('node-compat'))
expect(subject.find(item => item.id === 'vitest-jsdom-smoke')).toMatchObject({
label: 'Vitest jsdom smoke',
args: [
'/private/pnpm.cjs',
'exec',
'vitest',
'run',
'scripts/vitest-environment.compat.spec.ts',
],
})
})
})
describe('Node 24 lane ownership', () => {
it('keeps the static lane source-only', () => {
const subject = withPnpmEntrypoint(() => gatesForMode('ci-static'))

View File

@@ -270,14 +270,27 @@ function ciPrimaryGates(): Gate[] {
}
function nodeCompatGates(): Gate[] {
const typecheck = flagEnabled('DSH_NODE_COMPAT_SKIP_TYPECHECK')
? []
: [pnpmScript('typecheck', 'typecheck')]
if (runningNodeMajor() !== 22) {
return [...typecheck, ...nodeCompatSmokeGates()]
}
return [
...flagEnabled('DSH_NODE_COMPAT_SKIP_TYPECHECK') ? [] : [pnpmScript('typecheck', 'typecheck')],
...nodeCompatSmokeGates(),
...typecheck,
pnpmScript('build', 'build', {
...typecheck.length === 0 ? {} : { needs: ['typecheck'] },
}),
pnpmScript('build:web', 'build:web', {
label: 'Web frontend build',
needs: ['build'],
}),
...nodeCompatSmokeGates({ cliSmoke: true }),
]
}
function nodeCompatSmokeGates(): Gate[] {
return [
function nodeCompatSmokeGates(options: { cliSmoke?: boolean } = {}): Gate[] {
const gates: Gate[] = [
pnpmExec('source-worker-smoke', [
'vitest',
'run',
@@ -293,7 +306,35 @@ function nodeCompatSmokeGates(): Gate[] {
'run',
'apps/cli/tests/source-launch.compat.spec.ts',
], { label: 'dsh source-launch smoke' }),
pnpmExec('vitest-jsdom-smoke', [
'vitest',
'run',
'scripts/vitest-environment.compat.spec.ts',
], { label: 'Vitest jsdom smoke' }),
]
if (options.cliSmoke) {
gates.push(
pnpmExec('cli-lazy-search-startup-smoke', [
'vitest',
'run',
'apps/cli/tests/lazy-search-startup.compat.spec.ts',
], {
label: 'CLI lazy-search startup smoke',
env: { DSH_REQUIRE_BUILT_CLI_SMOKE: '1' },
needs: ['build:web'],
}),
)
}
return gates
}
/** Active Node major used to scope version-specific compatibility contracts. */
function runningNodeMajor(): number {
const major = Number.parseInt(process.versions.node.split('.')[0] ?? '', 10)
if (!Number.isSafeInteger(major)) {
throw new Error(`run-gates: cannot parse Node version ${JSON.stringify(process.versions.node)}.`)
}
return major
}
function ciStaticGates(options: { ownsBuild: boolean }): Gate[] {

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,14 @@
// @vitest-environment jsdom
import { describe, expect, it } from 'vitest'
describe('Vitest jsdom compatibility', () => {
it('provides isolated browser storage instead of Node process storage', () => {
if (process.allowedNodeEnvironmentFlags.has('--webstorage')) {
expect(process.execArgv.filter(argument => argument === '--no-webstorage')).toHaveLength(1)
}
localStorage.setItem('dsh-vitest-storage-probe', 'available')
expect(localStorage.getItem('dsh-vitest-storage-probe')).toBe('available')
localStorage.removeItem('dsh-vitest-storage-probe')
})
})