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.
Files changed (171) hide show
  1. package/README.md +6 -3
  2. package/bin/semantifold.js +6 -0
  3. package/build/bin/semantifold.d.ts +3 -0
  4. package/build/bin/semantifold.d.ts.map +1 -0
  5. package/build/bin/semantifold.js +5 -0
  6. package/build/index.d.ts +8 -0
  7. package/build/index.d.ts.map +1 -1
  8. package/build/index.js +9 -1
  9. package/build/src/artifact-path.d.ts +16 -0
  10. package/build/src/artifact-path.d.ts.map +1 -1
  11. package/build/src/artifact-path.js +29 -3
  12. package/build/src/backends/c-check.d.ts +7 -0
  13. package/build/src/backends/c-check.d.ts.map +1 -0
  14. package/build/src/backends/c-check.js +65 -0
  15. package/build/src/backends/check-plan.d.ts +96 -0
  16. package/build/src/backends/check-plan.d.ts.map +1 -0
  17. package/build/src/backends/check-plan.js +135 -0
  18. package/build/src/backends/cpp-check.d.ts +7 -0
  19. package/build/src/backends/cpp-check.d.ts.map +1 -0
  20. package/build/src/backends/cpp-check.js +59 -0
  21. package/build/src/backends/csharp-check.d.ts +7 -0
  22. package/build/src/backends/csharp-check.d.ts.map +1 -0
  23. package/build/src/backends/csharp-check.js +125 -0
  24. package/build/src/backends/csharp.d.ts +1 -0
  25. package/build/src/backends/csharp.d.ts.map +1 -1
  26. package/build/src/backends/csharp.js +5 -4
  27. package/build/src/backends/dart-check.d.ts +7 -0
  28. package/build/src/backends/dart-check.d.ts.map +1 -0
  29. package/build/src/backends/dart-check.js +103 -0
  30. package/build/src/backends/dart.d.ts +2 -0
  31. package/build/src/backends/dart.d.ts.map +1 -1
  32. package/build/src/backends/dart.js +5 -5
  33. package/build/src/backends/go-check.d.ts +7 -0
  34. package/build/src/backends/go-check.d.ts.map +1 -0
  35. package/build/src/backends/go-check.js +77 -0
  36. package/build/src/backends/go.d.ts +1 -0
  37. package/build/src/backends/go.d.ts.map +1 -1
  38. package/build/src/backends/go.js +3 -3
  39. package/build/src/backends/java-check.d.ts +21 -0
  40. package/build/src/backends/java-check.d.ts.map +1 -0
  41. package/build/src/backends/java-check.js +73 -0
  42. package/build/src/backends/javascript-check.d.ts +7 -0
  43. package/build/src/backends/javascript-check.d.ts.map +1 -0
  44. package/build/src/backends/javascript-check.js +29 -0
  45. package/build/src/backends/kotlin-check.d.ts +7 -0
  46. package/build/src/backends/kotlin-check.d.ts.map +1 -0
  47. package/build/src/backends/kotlin-check.js +31 -0
  48. package/build/src/backends/php-check.d.ts +7 -0
  49. package/build/src/backends/php-check.d.ts.map +1 -0
  50. package/build/src/backends/php-check.js +21 -0
  51. package/build/src/backends/program.d.ts +7 -0
  52. package/build/src/backends/program.d.ts.map +1 -1
  53. package/build/src/backends/program.js +16 -1
  54. package/build/src/backends/python-check.d.ts +7 -0
  55. package/build/src/backends/python-check.d.ts.map +1 -0
  56. package/build/src/backends/python-check.js +28 -0
  57. package/build/src/backends/ruby-check.d.ts +7 -0
  58. package/build/src/backends/ruby-check.d.ts.map +1 -0
  59. package/build/src/backends/ruby-check.js +21 -0
  60. package/build/src/backends/rust-check.d.ts +7 -0
  61. package/build/src/backends/rust-check.d.ts.map +1 -0
  62. package/build/src/backends/rust-check.js +65 -0
  63. package/build/src/backends/swift-check.d.ts +7 -0
  64. package/build/src/backends/swift-check.d.ts.map +1 -0
  65. package/build/src/backends/swift-check.js +77 -0
  66. package/build/src/backends/typescript-check.d.ts +7 -0
  67. package/build/src/backends/typescript-check.d.ts.map +1 -0
  68. package/build/src/backends/typescript-check.js +33 -0
  69. package/build/src/backends/zig-check.d.ts +7 -0
  70. package/build/src/backends/zig-check.d.ts.map +1 -0
  71. package/build/src/backends/zig-check.js +70 -0
  72. package/build/src/diagnostic.d.ts +13 -1
  73. package/build/src/diagnostic.d.ts.map +1 -1
  74. package/build/src/diagnostic.js +14 -2
  75. package/build/src/frontends/program.d.ts +6 -0
  76. package/build/src/frontends/program.d.ts.map +1 -1
  77. package/build/src/frontends/program.js +9 -1
  78. package/build/src/language-registry.d.ts +54 -0
  79. package/build/src/language-registry.d.ts.map +1 -1
  80. package/build/src/language-registry.js +107 -5
  81. package/build/src/project-build.d.ts +172 -0
  82. package/build/src/project-build.d.ts.map +1 -0
  83. package/build/src/project-build.js +320 -0
  84. package/build/src/project-cli.d.ts +40 -0
  85. package/build/src/project-cli.d.ts.map +1 -0
  86. package/build/src/project-cli.js +145 -0
  87. package/build/src/project-manifest.d.ts +148 -0
  88. package/build/src/project-manifest.d.ts.map +1 -0
  89. package/build/src/project-manifest.js +432 -0
  90. package/build/src/project-reporter.d.ts +81 -0
  91. package/build/src/project-reporter.d.ts.map +1 -0
  92. package/build/src/project-reporter.js +293 -0
  93. package/build/src/project-snapshot.d.ts +129 -0
  94. package/build/src/project-snapshot.d.ts.map +1 -0
  95. package/build/src/project-snapshot.js +362 -0
  96. package/build/src/project-watch-reporter.d.ts +125 -0
  97. package/build/src/project-watch-reporter.d.ts.map +1 -0
  98. package/build/src/project-watch-reporter.js +280 -0
  99. package/build/src/project-watch.d.ts +133 -0
  100. package/build/src/project-watch.d.ts.map +1 -0
  101. package/build/src/project-watch.js +839 -0
  102. package/build/src/publication.d.ts +69 -5
  103. package/build/src/publication.d.ts.map +1 -1
  104. package/build/src/publication.js +246 -57
  105. package/build/src/semantic/types.d.ts +303 -2
  106. package/build/src/semantic/types.d.ts.map +1 -1
  107. package/build/src/semantic/types.js +79 -2
  108. package/build/src/subprocess.d.ts +8 -0
  109. package/build/src/subprocess.d.ts.map +1 -1
  110. package/build/src/subprocess.js +50 -9
  111. package/build/src/target-check.d.ts +52 -0
  112. package/build/src/target-check.d.ts.map +1 -0
  113. package/build/src/target-check.js +796 -0
  114. package/build/src/toolchains.d.ts.map +1 -1
  115. package/build/src/toolchains.js +3 -3
  116. package/docs/architecture.md +3 -1
  117. package/docs/c.md +4 -0
  118. package/docs/cpp.md +4 -0
  119. package/docs/dart.md +4 -0
  120. package/docs/generated-artifact-publication.md +11 -8
  121. package/docs/kotlin.md +2 -2
  122. package/docs/language-support.md +29 -3
  123. package/docs/project-build-cli.md +147 -0
  124. package/docs/project-watch.md +60 -0
  125. package/docs/rust.md +4 -0
  126. package/docs/swift.md +2 -0
  127. package/docs/testing.md +70 -3
  128. package/docs/watch-build-pipeline.md +39 -23
  129. package/docs/zig.md +2 -0
  130. package/examples/jsdoc-java-watch/expected-output.txt +3 -0
  131. package/examples/jsdoc-java-watch/semantifold.json +23 -0
  132. package/examples/jsdoc-java-watch/src/main.js +21 -0
  133. package/index.js +8 -0
  134. package/package.json +6 -1
  135. package/src/artifact-path.js +31 -2
  136. package/src/backends/c-check.js +80 -0
  137. package/src/backends/check-plan.js +147 -0
  138. package/src/backends/cpp-check.js +73 -0
  139. package/src/backends/csharp-check.js +139 -0
  140. package/src/backends/csharp.js +4 -3
  141. package/src/backends/dart-check.js +117 -0
  142. package/src/backends/dart.js +4 -4
  143. package/src/backends/go-check.js +89 -0
  144. package/src/backends/go.js +2 -2
  145. package/src/backends/java-check.js +77 -0
  146. package/src/backends/javascript-check.js +33 -0
  147. package/src/backends/kotlin-check.js +41 -0
  148. package/src/backends/php-check.js +23 -0
  149. package/src/backends/program.js +17 -0
  150. package/src/backends/python-check.js +30 -0
  151. package/src/backends/ruby-check.js +23 -0
  152. package/src/backends/rust-check.js +78 -0
  153. package/src/backends/swift-check.js +89 -0
  154. package/src/backends/typescript-check.js +37 -0
  155. package/src/backends/zig-check.js +81 -0
  156. package/src/diagnostic.js +9 -1
  157. package/src/frontends/program.js +9 -0
  158. package/src/language-registry.js +113 -4
  159. package/src/project-build.js +353 -0
  160. package/src/project-cli.js +155 -0
  161. package/src/project-manifest.js +479 -0
  162. package/src/project-reporter.js +320 -0
  163. package/src/project-snapshot.js +403 -0
  164. package/src/project-watch-reporter.js +303 -0
  165. package/src/project-watch.js +870 -0
  166. package/src/publication.js +275 -58
  167. package/src/semantic/types.js +87 -1
  168. package/src/subprocess.js +49 -8
  169. package/src/target-check.js +844 -0
  170. package/src/toolchains.js +3 -2
  171. 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 */