Files
deepseek-harness/packages/scaffold/helper/src/documents/project-file.ts
Tianyi Cui 3fc35c91ff 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).
2026-08-09 01:21:12 +08:00

65 lines
2.0 KiB
TypeScript

/**
* Base abstraction for one file in an SDK project snapshot.
*
* @module @deepseek-ai/dsh-helper/documents/project-file
*/
/** Return text with exactly one trailing newline. */
export function withTrailingNewline(text: string): string {
return text.replace(/\n*$/, '') + '\n'
}
/** One cloneable, validatable project file. */
export abstract class ProjectFile {
/** Project-relative POSIX path. */
readonly relativePath: string
/** Text observed when the document entered the snapshot; absent for a new file. */
readonly originalText: string | undefined
/** Permission bits used only when the file is first created. */
readonly createMode: number | undefined
protected constructor(relativePath: string, originalText?: string, createMode?: number) {
if (relativePath.startsWith('/') || relativePath.split('/').includes('..')) {
throw new Error(`project document path must stay inside the project: ${relativePath}`)
}
this.relativePath = relativePath
this.originalText = originalText
this.createMode = createMode
}
/** Clone the document for an isolated edit session. */
abstract clone(): ProjectFile
/** Validate the document's complete current state. */
abstract validate(): void
/** Serialize the complete current file. */
abstract serialize(): string
}
/** Immutable complete-text file used by one-shot artifacts. */
export class TextProjectFile extends ProjectFile {
private readonly text: string
/** Create a complete-text project document. */
constructor(relativePath: string, text: string, originalText?: string) {
super(relativePath, originalText)
this.text = withTrailingNewline(text)
}
/** Clone this immutable document. */
override clone(): TextProjectFile {
return new TextProjectFile(this.relativePath, this.text, this.originalText)
}
/** Complete text artifacts have no extra structural validation. */
override validate(): void {}
/** Return the complete artifact text. */
override serialize(): string {
return this.text
}
}