semantifold 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -3
- package/bin/semantifold.js +6 -0
- package/build/bin/semantifold.d.ts +3 -0
- package/build/bin/semantifold.d.ts.map +1 -0
- package/build/bin/semantifold.js +5 -0
- package/build/index.d.ts +8 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +9 -1
- package/build/src/artifact-path.d.ts +16 -0
- package/build/src/artifact-path.d.ts.map +1 -1
- package/build/src/artifact-path.js +29 -3
- package/build/src/backends/c-check.d.ts +7 -0
- package/build/src/backends/c-check.d.ts.map +1 -0
- package/build/src/backends/c-check.js +65 -0
- package/build/src/backends/check-plan.d.ts +96 -0
- package/build/src/backends/check-plan.d.ts.map +1 -0
- package/build/src/backends/check-plan.js +135 -0
- package/build/src/backends/cpp-check.d.ts +7 -0
- package/build/src/backends/cpp-check.d.ts.map +1 -0
- package/build/src/backends/cpp-check.js +59 -0
- package/build/src/backends/csharp-check.d.ts +7 -0
- package/build/src/backends/csharp-check.d.ts.map +1 -0
- package/build/src/backends/csharp-check.js +125 -0
- package/build/src/backends/csharp.d.ts +1 -0
- package/build/src/backends/csharp.d.ts.map +1 -1
- package/build/src/backends/csharp.js +5 -4
- package/build/src/backends/dart-check.d.ts +7 -0
- package/build/src/backends/dart-check.d.ts.map +1 -0
- package/build/src/backends/dart-check.js +103 -0
- package/build/src/backends/dart.d.ts +2 -0
- package/build/src/backends/dart.d.ts.map +1 -1
- package/build/src/backends/dart.js +5 -5
- package/build/src/backends/go-check.d.ts +7 -0
- package/build/src/backends/go-check.d.ts.map +1 -0
- package/build/src/backends/go-check.js +77 -0
- package/build/src/backends/go.d.ts +1 -0
- package/build/src/backends/go.d.ts.map +1 -1
- package/build/src/backends/go.js +3 -3
- package/build/src/backends/java-check.d.ts +21 -0
- package/build/src/backends/java-check.d.ts.map +1 -0
- package/build/src/backends/java-check.js +73 -0
- package/build/src/backends/javascript-check.d.ts +7 -0
- package/build/src/backends/javascript-check.d.ts.map +1 -0
- package/build/src/backends/javascript-check.js +29 -0
- package/build/src/backends/kotlin-check.d.ts +7 -0
- package/build/src/backends/kotlin-check.d.ts.map +1 -0
- package/build/src/backends/kotlin-check.js +31 -0
- package/build/src/backends/php-check.d.ts +7 -0
- package/build/src/backends/php-check.d.ts.map +1 -0
- package/build/src/backends/php-check.js +21 -0
- package/build/src/backends/program.d.ts +7 -0
- package/build/src/backends/program.d.ts.map +1 -1
- package/build/src/backends/program.js +16 -1
- package/build/src/backends/python-check.d.ts +7 -0
- package/build/src/backends/python-check.d.ts.map +1 -0
- package/build/src/backends/python-check.js +28 -0
- package/build/src/backends/ruby-check.d.ts +7 -0
- package/build/src/backends/ruby-check.d.ts.map +1 -0
- package/build/src/backends/ruby-check.js +21 -0
- package/build/src/backends/rust-check.d.ts +7 -0
- package/build/src/backends/rust-check.d.ts.map +1 -0
- package/build/src/backends/rust-check.js +65 -0
- package/build/src/backends/swift-check.d.ts +7 -0
- package/build/src/backends/swift-check.d.ts.map +1 -0
- package/build/src/backends/swift-check.js +77 -0
- package/build/src/backends/typescript-check.d.ts +7 -0
- package/build/src/backends/typescript-check.d.ts.map +1 -0
- package/build/src/backends/typescript-check.js +33 -0
- package/build/src/backends/zig-check.d.ts +7 -0
- package/build/src/backends/zig-check.d.ts.map +1 -0
- package/build/src/backends/zig-check.js +70 -0
- package/build/src/diagnostic.d.ts +13 -1
- package/build/src/diagnostic.d.ts.map +1 -1
- package/build/src/diagnostic.js +14 -2
- package/build/src/frontends/program.d.ts +6 -0
- package/build/src/frontends/program.d.ts.map +1 -1
- package/build/src/frontends/program.js +9 -1
- package/build/src/language-registry.d.ts +54 -0
- package/build/src/language-registry.d.ts.map +1 -1
- package/build/src/language-registry.js +107 -5
- package/build/src/project-build.d.ts +172 -0
- package/build/src/project-build.d.ts.map +1 -0
- package/build/src/project-build.js +320 -0
- package/build/src/project-cli.d.ts +40 -0
- package/build/src/project-cli.d.ts.map +1 -0
- package/build/src/project-cli.js +145 -0
- package/build/src/project-manifest.d.ts +148 -0
- package/build/src/project-manifest.d.ts.map +1 -0
- package/build/src/project-manifest.js +432 -0
- package/build/src/project-reporter.d.ts +81 -0
- package/build/src/project-reporter.d.ts.map +1 -0
- package/build/src/project-reporter.js +293 -0
- package/build/src/project-snapshot.d.ts +129 -0
- package/build/src/project-snapshot.d.ts.map +1 -0
- package/build/src/project-snapshot.js +362 -0
- package/build/src/project-watch-reporter.d.ts +125 -0
- package/build/src/project-watch-reporter.d.ts.map +1 -0
- package/build/src/project-watch-reporter.js +280 -0
- package/build/src/project-watch.d.ts +133 -0
- package/build/src/project-watch.d.ts.map +1 -0
- package/build/src/project-watch.js +839 -0
- package/build/src/publication.d.ts +69 -5
- package/build/src/publication.d.ts.map +1 -1
- package/build/src/publication.js +246 -57
- package/build/src/semantic/types.d.ts +303 -2
- package/build/src/semantic/types.d.ts.map +1 -1
- package/build/src/semantic/types.js +79 -2
- package/build/src/subprocess.d.ts +8 -0
- package/build/src/subprocess.d.ts.map +1 -1
- package/build/src/subprocess.js +50 -9
- package/build/src/target-check.d.ts +52 -0
- package/build/src/target-check.d.ts.map +1 -0
- package/build/src/target-check.js +796 -0
- package/build/src/toolchains.d.ts.map +1 -1
- package/build/src/toolchains.js +3 -3
- package/docs/architecture.md +3 -1
- package/docs/c.md +4 -0
- package/docs/cpp.md +4 -0
- package/docs/dart.md +4 -0
- package/docs/generated-artifact-publication.md +11 -8
- package/docs/kotlin.md +2 -2
- package/docs/language-support.md +29 -3
- package/docs/project-build-cli.md +147 -0
- package/docs/project-watch.md +60 -0
- package/docs/rust.md +4 -0
- package/docs/swift.md +2 -0
- package/docs/testing.md +70 -3
- package/docs/watch-build-pipeline.md +39 -23
- package/docs/zig.md +2 -0
- package/examples/jsdoc-java-watch/expected-output.txt +3 -0
- package/examples/jsdoc-java-watch/semantifold.json +23 -0
- package/examples/jsdoc-java-watch/src/main.js +21 -0
- package/index.js +8 -0
- package/package.json +6 -1
- package/src/artifact-path.js +31 -2
- package/src/backends/c-check.js +80 -0
- package/src/backends/check-plan.js +147 -0
- package/src/backends/cpp-check.js +73 -0
- package/src/backends/csharp-check.js +139 -0
- package/src/backends/csharp.js +4 -3
- package/src/backends/dart-check.js +117 -0
- package/src/backends/dart.js +4 -4
- package/src/backends/go-check.js +89 -0
- package/src/backends/go.js +2 -2
- package/src/backends/java-check.js +77 -0
- package/src/backends/javascript-check.js +33 -0
- package/src/backends/kotlin-check.js +41 -0
- package/src/backends/php-check.js +23 -0
- package/src/backends/program.js +17 -0
- package/src/backends/python-check.js +30 -0
- package/src/backends/ruby-check.js +23 -0
- package/src/backends/rust-check.js +78 -0
- package/src/backends/swift-check.js +89 -0
- package/src/backends/typescript-check.js +37 -0
- package/src/backends/zig-check.js +81 -0
- package/src/diagnostic.js +9 -1
- package/src/frontends/program.js +9 -0
- package/src/language-registry.js +113 -4
- package/src/project-build.js +353 -0
- package/src/project-cli.js +155 -0
- package/src/project-manifest.js +479 -0
- package/src/project-reporter.js +320 -0
- package/src/project-snapshot.js +403 -0
- package/src/project-watch-reporter.js +303 -0
- package/src/project-watch.js +870 -0
- package/src/publication.js +275 -58
- package/src/semantic/types.js +87 -1
- package/src/subprocess.js +49 -8
- package/src/target-check.js +844 -0
- package/src/toolchains.js +3 -2
- package/tsconfig.json +1 -1
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import {SemantifoldDiagnostic} from "./diagnostic.js"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Emits concise human output or the deterministic Task-039 NDJSON state protocol.
|
|
7
|
+
*/
|
|
8
|
+
export class ProjectBuildReporter {
|
|
9
|
+
/** @type {"human" | "ndjson"} */
|
|
10
|
+
#format
|
|
11
|
+
/** @type {WritableOutput} */
|
|
12
|
+
#stdout
|
|
13
|
+
/** @type {WritableOutput} */
|
|
14
|
+
#stderr
|
|
15
|
+
/** @type {string | null} */
|
|
16
|
+
#projectId = null
|
|
17
|
+
/** @type {string | null} */
|
|
18
|
+
#snapshotHash = null
|
|
19
|
+
#terminal = false
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Creates a one-cycle reporter.
|
|
23
|
+
* @param {{format?: "human" | "ndjson", stderr?: WritableOutput, stdout?: WritableOutput}} [options] - Output mode and sinks.
|
|
24
|
+
*/
|
|
25
|
+
constructor(options = {}) {
|
|
26
|
+
const format = options.format ?? "human"
|
|
27
|
+
|
|
28
|
+
if (format != "human" && format != "ndjson") throw new TypeError("Invalid project build reporter format.")
|
|
29
|
+
this.#format = format
|
|
30
|
+
this.#stdout = options.stdout ?? process.stdout
|
|
31
|
+
this.#stderr = options.stderr ?? process.stderr
|
|
32
|
+
if (typeof this.#stdout?.write != "function" || typeof this.#stderr?.write != "function") {
|
|
33
|
+
throw new TypeError("Project build reporter requires writable output sinks.")
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Reports validated project configuration.
|
|
39
|
+
* @param {string} projectId - Stable project identity.
|
|
40
|
+
* @returns {void}
|
|
41
|
+
*/
|
|
42
|
+
projectLoaded(projectId) {
|
|
43
|
+
this.#projectId = projectId
|
|
44
|
+
this.#emitState({state: "project-loaded"})
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Reports one stable complete source snapshot.
|
|
49
|
+
* @param {string} snapshotHash - Complete ordered snapshot hash.
|
|
50
|
+
* @returns {void}
|
|
51
|
+
*/
|
|
52
|
+
snapshotLoaded(snapshotHash) {
|
|
53
|
+
this.#snapshotHash = snapshotHash
|
|
54
|
+
this.#emitState({snapshotHash, state: "snapshot-loaded"})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Reports one ordered generated target.
|
|
59
|
+
* @param {import("./project-build.js").ProjectBuildTargetResult} target - Completed target result.
|
|
60
|
+
* @returns {void}
|
|
61
|
+
*/
|
|
62
|
+
targetGenerated(target) {
|
|
63
|
+
this.#emitState({
|
|
64
|
+
artifactCount: target.artifactCount,
|
|
65
|
+
language: target.language,
|
|
66
|
+
role: target.role,
|
|
67
|
+
state: "target-generated",
|
|
68
|
+
target: target.id
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Reports one successful target-check stage with exact process evidence.
|
|
74
|
+
* @param {{id: string, language: string, stage: import("./semantic/types.js").TargetCheckStageResult}} checked - Target and stage result.
|
|
75
|
+
* @returns {void}
|
|
76
|
+
*/
|
|
77
|
+
targetChecked(checked) {
|
|
78
|
+
this.#emitState({
|
|
79
|
+
argv: checked.stage.argv,
|
|
80
|
+
durationMs: checked.stage.durationMs,
|
|
81
|
+
exitCode: checked.stage.exitCode,
|
|
82
|
+
language: checked.language,
|
|
83
|
+
signal: checked.stage.signal,
|
|
84
|
+
stage: checked.stage.stage,
|
|
85
|
+
state: "target-checked",
|
|
86
|
+
stderr: checked.stage.stderr,
|
|
87
|
+
stdout: checked.stage.stdout,
|
|
88
|
+
target: checked.id,
|
|
89
|
+
tool: checked.stage.tool
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Emits the sole successful terminal record.
|
|
95
|
+
* @param {import("./project-build.js").ProjectBuildSuccess} result - Committed build result.
|
|
96
|
+
* @returns {void}
|
|
97
|
+
*/
|
|
98
|
+
succeeded(result) {
|
|
99
|
+
this.#requireOpenTerminal()
|
|
100
|
+
this.#terminal = true
|
|
101
|
+
this.#projectId = result.projectId
|
|
102
|
+
this.#snapshotHash = result.snapshotHash
|
|
103
|
+
if (this.#format == "human") {
|
|
104
|
+
const targetWord = result.targets.length == 1 ? "target" : "targets"
|
|
105
|
+
|
|
106
|
+
const action = result.checked ? "Built and checked" : "Built"
|
|
107
|
+
|
|
108
|
+
this.#stdout.write(`${action} project '${result.projectId}' as generation '${result.generationId}' with ${result.targets.length} ${targetWord}.\n`)
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
this.#writeNdjson({
|
|
112
|
+
exitCode: 0,
|
|
113
|
+
checked: result.checked,
|
|
114
|
+
generationId: result.generationId,
|
|
115
|
+
snapshotHash: result.snapshotHash,
|
|
116
|
+
state: "succeeded",
|
|
117
|
+
targets: result.targets.map(target => ({
|
|
118
|
+
artifactCount: target.artifactCount,
|
|
119
|
+
id: target.id,
|
|
120
|
+
language: target.language,
|
|
121
|
+
role: target.role
|
|
122
|
+
})),
|
|
123
|
+
terminal: true
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Emits the sole failed terminal record while preserving the first diagnostic.
|
|
129
|
+
* @param {unknown} failure - Build or CLI failure.
|
|
130
|
+
* @returns {void}
|
|
131
|
+
*/
|
|
132
|
+
failed(failure) {
|
|
133
|
+
this.#requireOpenTerminal()
|
|
134
|
+
this.#terminal = true
|
|
135
|
+
const diagnostic = normalizeDiagnostic(failure)
|
|
136
|
+
|
|
137
|
+
if (this.#format == "human") {
|
|
138
|
+
this.#stderr.write(formatHumanProjectFailure(diagnostic, "Build failed"))
|
|
139
|
+
return
|
|
140
|
+
}
|
|
141
|
+
this.#writeNdjson({
|
|
142
|
+
diagnostic: diagnosticRecord(diagnostic),
|
|
143
|
+
exitCode: 1,
|
|
144
|
+
...(this.#snapshotHash === null ? {} : {snapshotHash: this.#snapshotHash}),
|
|
145
|
+
state: "failed",
|
|
146
|
+
terminal: true
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Emits a non-terminal machine state when selected.
|
|
152
|
+
* @param {Record<string, unknown>} fields - State-specific fields.
|
|
153
|
+
* @returns {void}
|
|
154
|
+
*/
|
|
155
|
+
#emitState(fields) {
|
|
156
|
+
if (this.#format == "ndjson") this.#writeNdjson(fields)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Writes one complete NDJSON protocol record.
|
|
161
|
+
* @param {Record<string, unknown>} fields - State-specific fields.
|
|
162
|
+
* @returns {void}
|
|
163
|
+
*/
|
|
164
|
+
#writeNdjson(fields) {
|
|
165
|
+
this.#stdout.write(`${JSON.stringify({
|
|
166
|
+
cycle: 1,
|
|
167
|
+
project: this.#projectId,
|
|
168
|
+
schema: "SemantifoldBuildEvent",
|
|
169
|
+
version: 1,
|
|
170
|
+
...fields
|
|
171
|
+
})}\n`)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Rejects an attempt to emit more than one terminal record.
|
|
176
|
+
* @returns {void}
|
|
177
|
+
*/
|
|
178
|
+
#requireOpenTerminal() {
|
|
179
|
+
if (this.#terminal) throw new TypeError("Project build reporter already emitted its terminal record.")
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Normalizes an unexpected failure without replacing a Semantifold diagnostic.
|
|
185
|
+
* @param {unknown} failure - Opaque failure.
|
|
186
|
+
* @returns {SemantifoldDiagnostic} Stable diagnostic.
|
|
187
|
+
*/
|
|
188
|
+
export function normalizeDiagnostic(failure) {
|
|
189
|
+
if (failure instanceof SemantifoldDiagnostic) return failure
|
|
190
|
+
const cause = failure instanceof Error ? failure : new Error(String(failure))
|
|
191
|
+
|
|
192
|
+
return new SemantifoldDiagnostic({
|
|
193
|
+
cause,
|
|
194
|
+
code: "PROJECT_BUILD_FAILED",
|
|
195
|
+
language: "project",
|
|
196
|
+
message: "Project build failed unexpectedly."
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Serializes stable public diagnostic fields without causes or host paths.
|
|
202
|
+
* @param {SemantifoldDiagnostic} diagnostic - Normalized diagnostic.
|
|
203
|
+
* @returns {Record<string, unknown>} Protocol diagnostic.
|
|
204
|
+
*/
|
|
205
|
+
export function diagnosticRecord(diagnostic) {
|
|
206
|
+
return {
|
|
207
|
+
code: diagnostic.code,
|
|
208
|
+
detail: diagnostic.detail,
|
|
209
|
+
language: diagnostic.language,
|
|
210
|
+
...(diagnostic.location === undefined ? {} : {location: diagnostic.location}),
|
|
211
|
+
...optionalDiagnosticFields(diagnostic),
|
|
212
|
+
...(diagnostic.cause instanceof SemantifoldDiagnostic ? {cause: diagnosticRecord(diagnostic.cause)} : {})
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Retains stable structured process and target context.
|
|
218
|
+
* @param {SemantifoldDiagnostic} diagnostic - Diagnostic to serialize.
|
|
219
|
+
* @returns {Record<string, unknown>} Present optional fields.
|
|
220
|
+
*/
|
|
221
|
+
function optionalDiagnosticFields(diagnostic) {
|
|
222
|
+
const fields = [
|
|
223
|
+
"command", "durationMs", "executable", "exitCode", "projectId", "signal", "stage", "stderr", "stdout", "targetId",
|
|
224
|
+
"toolId", "version"
|
|
225
|
+
]
|
|
226
|
+
/** @type {Record<string, unknown>} */
|
|
227
|
+
const result = {}
|
|
228
|
+
|
|
229
|
+
for (const field of fields) {
|
|
230
|
+
const value = Reflect.get(diagnostic, field)
|
|
231
|
+
|
|
232
|
+
if (value !== undefined) result[field] = value
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return result
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Finds the most specific Semantifold diagnostic in a preserved cause chain.
|
|
240
|
+
* @param {SemantifoldDiagnostic} diagnostic - Root diagnostic.
|
|
241
|
+
* @returns {SemantifoldDiagnostic} Deepest structured diagnostic.
|
|
242
|
+
*/
|
|
243
|
+
function deepestDiagnostic(diagnostic) {
|
|
244
|
+
let current = diagnostic
|
|
245
|
+
|
|
246
|
+
while (current.cause instanceof SemantifoldDiagnostic) current = current.cause
|
|
247
|
+
|
|
248
|
+
return current
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Formats nested structured diagnostics without duplicating detailed checker-process evidence.
|
|
253
|
+
* @param {SemantifoldDiagnostic} diagnostic - Root diagnostic.
|
|
254
|
+
* @param {SemantifoldDiagnostic} deepest - Deepest structured diagnostic.
|
|
255
|
+
* @returns {string} Ordered human-readable cause lines.
|
|
256
|
+
*/
|
|
257
|
+
function humanDiagnosticCauses(diagnostic, deepest) {
|
|
258
|
+
let current = diagnostic.cause
|
|
259
|
+
let result = ""
|
|
260
|
+
|
|
261
|
+
while (current instanceof SemantifoldDiagnostic) {
|
|
262
|
+
if (current !== deepest || !current.code.startsWith("TARGET_CHECK_")) {
|
|
263
|
+
result += `Caused by: ${current.message}\n`
|
|
264
|
+
}
|
|
265
|
+
current = current.cause
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return result
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Formats complete bounded check-process evidence without flattening its diagnostic.
|
|
273
|
+
* @param {SemantifoldDiagnostic} diagnostic - Deepest structured diagnostic.
|
|
274
|
+
* @returns {string} Human-readable check context and streams, when applicable.
|
|
275
|
+
*/
|
|
276
|
+
function humanProcessEvidence(diagnostic) {
|
|
277
|
+
if (!diagnostic.code.startsWith("TARGET_CHECK_")) return ""
|
|
278
|
+
/**
|
|
279
|
+
* Reads one optional diagnostic field for compact human display.
|
|
280
|
+
* @param {string} field - Field identity.
|
|
281
|
+
* @returns {unknown} Present value or an explicit absence marker.
|
|
282
|
+
*/
|
|
283
|
+
const value = field => Reflect.get(diagnostic, field) ?? "none"
|
|
284
|
+
const heading = `Check failure: project='${value("projectId")}' target='${value("targetId")}' ` +
|
|
285
|
+
`language='${diagnostic.language}' tool='${value("toolId")}' executable='${value("executable")}' ` +
|
|
286
|
+
`version='${value("version")}' stage='${value("stage")}' exitCode=${value("exitCode")} ` +
|
|
287
|
+
`signal=${value("signal")} durationMs=${value("durationMs")}\n`
|
|
288
|
+
|
|
289
|
+
return `${heading}${humanStream("stdout", diagnostic.stdout)}${humanStream("stderr", diagnostic.stderr)}`
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Formats one normalized failure with preserved nested process evidence.
|
|
294
|
+
* This helper is shared by the one-shot and watch reporters but is not a package-root API.
|
|
295
|
+
* @param {unknown} failure - Opaque or normalized failure.
|
|
296
|
+
* @param {string} heading - Human output heading without punctuation.
|
|
297
|
+
* @returns {string} Complete newline-terminated failure text.
|
|
298
|
+
*/
|
|
299
|
+
export function formatHumanProjectFailure(failure, heading) {
|
|
300
|
+
const diagnostic = normalizeDiagnostic(failure)
|
|
301
|
+
const processFailure = deepestDiagnostic(diagnostic)
|
|
302
|
+
const causes = humanDiagnosticCauses(diagnostic, processFailure)
|
|
303
|
+
const evidence = humanProcessEvidence(processFailure)
|
|
304
|
+
|
|
305
|
+
return `${heading}: ${diagnostic.message}\n${causes}${evidence}`
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Labels one bounded process stream and preserves its exact content.
|
|
310
|
+
* @param {string} name - Stream identity.
|
|
311
|
+
* @param {unknown} content - Captured stream value.
|
|
312
|
+
* @returns {string} Labeled newline-terminated stream evidence.
|
|
313
|
+
*/
|
|
314
|
+
function humanStream(name, content) {
|
|
315
|
+
if (typeof content != "string" || content.length == 0) return `${name}:\n<empty>\n`
|
|
316
|
+
|
|
317
|
+
return `${name}:\n${content}${content.endsWith("\n") ? "" : "\n"}`
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** @typedef {{write: (chunk: string) => unknown}} WritableOutput */
|