refactor(packages): dissolve ui/ and rename sdk/ to scaffold/

git mv per the regrouping RFC: the five human-collaboration seams and
tui join packages/interaction/, app-boot becomes packages/boot/, and
jsonrpc joins the renamed scaffold/ (formerly sdk/) as its server half
beside client/protocol/create-sdk/helper/scripts/telemetry, whose
folders drop the legacy sdk- prefix. Three new group README triplets
replace the ui/ and sdk/ ones; tsconfig references/paths/globs,
knip keys, vitest globs, gate scripts, catalogs, docs, and the
lockfile follow. Adds the four settled FIXME rename markers
(dsh-sdk-server, dsh-sdk-telemetry, dsh-sdk-helper, dsh-sdk-scripts).

The scaffold folders diverge from their npm names until those renames
land, so tsconfig.base.json maps the three affected names explicitly
beside the group wildcard. Also repairs two pre-existing stale-path
classes the strengthened sweep surfaced: docs/web-styling.md's retired
web-ui host package and type-model spec fixture-literal joins.

app-boot's three Loader-composition specs time out at the default 5s
under full-suite parallel load on this filesystem (pre-existing;
pass isolated with --testTimeout=30000); interaction/scaffold/boot
suites otherwise green (687 passed).
This commit is contained in:
Tianyi Cui
2026-07-30 03:13:49 +08:00
parent 7e445c3a67
commit 3fc35c91ff
351 changed files with 368 additions and 311 deletions

View File

@@ -0,0 +1,111 @@
/**
* Ownership-aware, line-preserving dotenv document.
*
* @module @deepseek-ai/dsh-helper/documents/env-file
*/
import { ProjectFile, withTrailingNewline } from './project-file.ts'
interface ParsedVariable {
index: number
value: string
}
const VARIABLE = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)=(.*)$/
const VARIABLE_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/
/** `.env` appends missing variables; `.env.example` supports managed replacement and removal. */
export class EnvFile extends ProjectFile {
private readonly lines: string[]
private constructor(relativePath: '.env' | '.env.example', lines: string[], originalText?: string) {
super(relativePath, originalText, relativePath === '.env' ? 0o600 : undefined)
this.lines = [...lines]
}
/** Create an empty environment file. */
static create(relativePath: '.env' | '.env.example'): EnvFile {
return new EnvFile(relativePath, [])
}
/** Parse an existing environment file without rewriting unknown lines. */
static parse(relativePath: '.env' | '.env.example', text: string): EnvFile {
const normalized = text.replace(/\n$/, '')
return new EnvFile(relativePath, normalized.length === 0 ? [] : normalized.split('\n'), text)
}
/** Clone the current line model. */
override clone(): EnvFile {
return new EnvFile(this.relativePath as '.env' | '.env.example', this.lines, this.originalText)
}
private variables(): Map<string, ParsedVariable[]> {
const values = new Map<string, ParsedVariable[]>()
this.lines.forEach((line, index) => {
const match = VARIABLE.exec(line)
if (!match) return
const name = match[1]
const value = match[2]
/* v8 ignore next -- both captures are mandatory in VARIABLE */
if (name === undefined || value === undefined) return
const occurrences = values.get(name) ?? []
occurrences.push({ index, value })
values.set(name, occurrences)
})
return values
}
/** Read the effective value; append-only `.env` accepts duplicates and uses the last declaration. */
get(name: string): string | undefined {
const occurrences = this.variables().get(name) ?? []
if (this.relativePath === '.env.example' && occurrences.length > 1) {
throw new Error(`${this.relativePath} contains duplicate variable ${name}`)
}
return occurrences.at(-1)?.value
}
/** Add or replace one SDK-managed `.env.example` variable while preserving unrelated lines. */
set(name: string, value: string): void {
if (this.relativePath !== '.env.example') throw new Error('.env is append-only')
if (!VARIABLE_NAME.test(name)) throw new Error(`invalid environment variable name: ${name}`)
const occurrences = this.variables().get(name) ?? []
if (occurrences.length > 1) throw new Error(`${this.relativePath} contains duplicate variable ${name}`)
const line = `${name}=${value}`
if (occurrences[0]) this.lines[occurrences[0].index] = line
else this.lines.push(line)
}
/** Append a missing `.env` variable and optional comment without changing any existing declaration. */
append(name: string, value: string, comment?: string): boolean {
if (this.relativePath !== '.env') throw new Error('.env.example is SDK-managed')
if (!VARIABLE_NAME.test(name)) throw new Error(`invalid environment variable name: ${name}`)
if (comment !== undefined && (!comment || comment.includes('\n'))) {
throw new Error('environment comment must be one non-empty line')
}
if (this.variables().has(name)) return false
if (comment) this.lines.push(`# ${comment}`)
this.lines.push(`${name}=${value}`)
return true
}
/** Remove one SDK-managed `.env.example` variable while retaining every other line. */
remove(name: string): void {
if (this.relativePath !== '.env.example') throw new Error('.env is append-only')
const occurrences = this.variables().get(name) ?? []
if (occurrences.length > 1) throw new Error(`${this.relativePath} contains duplicate variable ${name}`)
if (occurrences[0]) this.lines.splice(occurrences[0].index, 1)
}
/** Validate the managed placeholder file; append-only `.env` accepts duplicate declarations. */
override validate(): void {
if (this.relativePath === '.env') return
for (const [name, occurrences] of this.variables()) {
if (occurrences.length > 1) throw new Error(`${this.relativePath} contains duplicate variable ${name}`)
}
}
/** Serialize all retained lines with one trailing newline. */
override serialize(): string {
return withTrailingNewline(this.lines.join('\n'))
}
}