semantifold 0.9.0 → 0.11.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 (198) 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/kotlin.d.ts +1 -1
  49. package/build/src/backends/kotlin.js +2 -2
  50. package/build/src/backends/php-check.d.ts +7 -0
  51. package/build/src/backends/php-check.d.ts.map +1 -0
  52. package/build/src/backends/php-check.js +21 -0
  53. package/build/src/backends/program.d.ts +7 -0
  54. package/build/src/backends/program.d.ts.map +1 -1
  55. package/build/src/backends/program.js +16 -1
  56. package/build/src/backends/python-check.d.ts +7 -0
  57. package/build/src/backends/python-check.d.ts.map +1 -0
  58. package/build/src/backends/python-check.js +28 -0
  59. package/build/src/backends/ruby-check.d.ts +7 -0
  60. package/build/src/backends/ruby-check.d.ts.map +1 -0
  61. package/build/src/backends/ruby-check.js +21 -0
  62. package/build/src/backends/rust-check.d.ts +7 -0
  63. package/build/src/backends/rust-check.d.ts.map +1 -0
  64. package/build/src/backends/rust-check.js +65 -0
  65. package/build/src/backends/shared.d.ts.map +1 -1
  66. package/build/src/backends/shared.js +29 -1
  67. package/build/src/backends/swift-check.d.ts +7 -0
  68. package/build/src/backends/swift-check.d.ts.map +1 -0
  69. package/build/src/backends/swift-check.js +77 -0
  70. package/build/src/backends/typescript-check.d.ts +7 -0
  71. package/build/src/backends/typescript-check.d.ts.map +1 -0
  72. package/build/src/backends/typescript-check.js +33 -0
  73. package/build/src/backends/zig-check.d.ts +7 -0
  74. package/build/src/backends/zig-check.d.ts.map +1 -0
  75. package/build/src/backends/zig-check.js +70 -0
  76. package/build/src/diagnostic.d.ts +13 -1
  77. package/build/src/diagnostic.d.ts.map +1 -1
  78. package/build/src/diagnostic.js +14 -2
  79. package/build/src/frontends/java.d.ts.map +1 -1
  80. package/build/src/frontends/java.js +23 -1
  81. package/build/src/frontends/javascript-typescript.d.ts.map +1 -1
  82. package/build/src/frontends/javascript-typescript.js +45 -4
  83. package/build/src/frontends/php.d.ts.map +1 -1
  84. package/build/src/frontends/php.js +54 -3
  85. package/build/src/frontends/program.d.ts +6 -0
  86. package/build/src/frontends/program.d.ts.map +1 -1
  87. package/build/src/frontends/program.js +9 -1
  88. package/build/src/frontends/ruby.d.ts.map +1 -1
  89. package/build/src/frontends/ruby.js +55 -5
  90. package/build/src/language-registry.d.ts +54 -0
  91. package/build/src/language-registry.d.ts.map +1 -1
  92. package/build/src/language-registry.js +124 -10
  93. package/build/src/project-build.d.ts +172 -0
  94. package/build/src/project-build.d.ts.map +1 -0
  95. package/build/src/project-build.js +320 -0
  96. package/build/src/project-cli.d.ts +40 -0
  97. package/build/src/project-cli.d.ts.map +1 -0
  98. package/build/src/project-cli.js +145 -0
  99. package/build/src/project-manifest.d.ts +148 -0
  100. package/build/src/project-manifest.d.ts.map +1 -0
  101. package/build/src/project-manifest.js +432 -0
  102. package/build/src/project-reporter.d.ts +81 -0
  103. package/build/src/project-reporter.d.ts.map +1 -0
  104. package/build/src/project-reporter.js +293 -0
  105. package/build/src/project-snapshot.d.ts +129 -0
  106. package/build/src/project-snapshot.d.ts.map +1 -0
  107. package/build/src/project-snapshot.js +362 -0
  108. package/build/src/project-watch-reporter.d.ts +125 -0
  109. package/build/src/project-watch-reporter.d.ts.map +1 -0
  110. package/build/src/project-watch-reporter.js +280 -0
  111. package/build/src/project-watch.d.ts +133 -0
  112. package/build/src/project-watch.d.ts.map +1 -0
  113. package/build/src/project-watch.js +839 -0
  114. package/build/src/publication.d.ts +69 -5
  115. package/build/src/publication.d.ts.map +1 -1
  116. package/build/src/publication.js +246 -57
  117. package/build/src/semantic/effects.js +3 -1
  118. package/build/src/semantic/provenance.d.ts.map +1 -1
  119. package/build/src/semantic/provenance.js +11 -1
  120. package/build/src/semantic/types.d.ts +355 -4
  121. package/build/src/semantic/types.d.ts.map +1 -1
  122. package/build/src/semantic/types.js +92 -3
  123. package/build/src/semantic/validate.js +18 -1
  124. package/build/src/subprocess.d.ts +8 -0
  125. package/build/src/subprocess.d.ts.map +1 -1
  126. package/build/src/subprocess.js +50 -9
  127. package/build/src/target-check.d.ts +52 -0
  128. package/build/src/target-check.d.ts.map +1 -0
  129. package/build/src/target-check.js +796 -0
  130. package/build/src/toolchains.d.ts.map +1 -1
  131. package/build/src/toolchains.js +3 -3
  132. package/docs/adr-expression-breadth.md +186 -0
  133. package/docs/architecture.md +3 -1
  134. package/docs/c.md +4 -0
  135. package/docs/cpp.md +4 -0
  136. package/docs/dart.md +4 -0
  137. package/docs/generated-artifact-publication.md +11 -8
  138. package/docs/kotlin.md +2 -2
  139. package/docs/language-support.md +40 -6
  140. package/docs/parser-qualification.md +1 -1
  141. package/docs/project-build-cli.md +147 -0
  142. package/docs/project-watch.md +60 -0
  143. package/docs/rust.md +4 -0
  144. package/docs/swift.md +2 -0
  145. package/docs/testing.md +70 -3
  146. package/docs/watch-build-pipeline.md +39 -23
  147. package/docs/zig.md +2 -0
  148. package/examples/jsdoc-java-watch/expected-output.txt +3 -0
  149. package/examples/jsdoc-java-watch/semantifold.json +23 -0
  150. package/examples/jsdoc-java-watch/src/main.js +21 -0
  151. package/index.js +8 -0
  152. package/package.json +6 -1
  153. package/src/artifact-path.js +31 -2
  154. package/src/backends/c-check.js +80 -0
  155. package/src/backends/check-plan.js +147 -0
  156. package/src/backends/cpp-check.js +73 -0
  157. package/src/backends/csharp-check.js +139 -0
  158. package/src/backends/csharp.js +4 -3
  159. package/src/backends/dart-check.js +117 -0
  160. package/src/backends/dart.js +4 -4
  161. package/src/backends/go-check.js +89 -0
  162. package/src/backends/go.js +2 -2
  163. package/src/backends/java-check.js +77 -0
  164. package/src/backends/javascript-check.js +33 -0
  165. package/src/backends/kotlin-check.js +41 -0
  166. package/src/backends/kotlin.js +1 -1
  167. package/src/backends/php-check.js +23 -0
  168. package/src/backends/program.js +17 -0
  169. package/src/backends/python-check.js +30 -0
  170. package/src/backends/ruby-check.js +23 -0
  171. package/src/backends/rust-check.js +78 -0
  172. package/src/backends/shared.js +30 -0
  173. package/src/backends/swift-check.js +89 -0
  174. package/src/backends/typescript-check.js +37 -0
  175. package/src/backends/zig-check.js +81 -0
  176. package/src/diagnostic.js +9 -1
  177. package/src/frontends/java.js +24 -0
  178. package/src/frontends/javascript-typescript.js +49 -2
  179. package/src/frontends/php.js +61 -2
  180. package/src/frontends/program.js +9 -0
  181. package/src/frontends/ruby.js +62 -2
  182. package/src/language-registry.js +130 -9
  183. package/src/project-build.js +353 -0
  184. package/src/project-cli.js +155 -0
  185. package/src/project-manifest.js +479 -0
  186. package/src/project-reporter.js +320 -0
  187. package/src/project-snapshot.js +403 -0
  188. package/src/project-watch-reporter.js +303 -0
  189. package/src/project-watch.js +870 -0
  190. package/src/publication.js +275 -58
  191. package/src/semantic/effects.js +1 -0
  192. package/src/semantic/provenance.js +8 -0
  193. package/src/semantic/types.js +101 -2
  194. package/src/semantic/validate.js +26 -0
  195. package/src/subprocess.js +49 -8
  196. package/src/target-check.js +844 -0
  197. package/src/toolchains.js +3 -2
  198. package/tsconfig.json +1 -1
@@ -165,6 +165,13 @@
165
165
  * @property {readonly string[]} toolchains - Required canonical toolchain IDs.
166
166
  */
167
167
 
168
+ /**
169
+ * @typedef LanguageCheckCapabilities
170
+ * @property {boolean} supported - Whether the target owns a developer check plan.
171
+ * @property {readonly AcceptanceStage[]} stages - Ordered non-executing developer-check stages.
172
+ * @property {readonly string[]} toolchains - Exact canonical tools required to construct the plan.
173
+ */
174
+
168
175
  /**
169
176
  * @typedef LanguageFeatureCapabilities
170
177
  * @property {boolean} generalFunctionsAndCalls - Task 005 arbitrary required signatures, resolved direct calls, and void functions.
@@ -173,6 +180,8 @@
173
180
  * @property {boolean} orderedListIteration - Task 008 ordered immutable-list iteration and nearest-loop control.
174
181
  * @property {boolean} orderedMapIteration - Task 014 insertion-ordered immutable-map pair iteration.
175
182
  * @property {boolean} conditionControlledLoops - Task 032 strict-Boolean pre-condition loops and resolved control.
183
+ * @property {boolean} conditionalExpressions - Task 048 strict-Boolean conditional expressions with joined arm types.
184
+ * @property {boolean} stringInterpolation - Task 048 parser-proved string interpolation lowered to checked string concatenation.
176
185
  * @property {boolean} closedRecords - Task 009 nominal closed immutable records, construction, and member reads.
177
186
  * @property {boolean} typedErrors - Task 011 nominal unchecked errors, raises, and exact typed catches.
178
187
  * @property {boolean} typeParametersAndGenerics - Task 012 invariant unbounded type parameters and closed generic applications.
@@ -189,6 +198,7 @@
189
198
  * @property {Readonly<LanguageFeatureCapabilities>} features - Semantic feature capabilities.
190
199
  * @property {Readonly<LanguageMappingCapabilities>} mapping - Mapping forms.
191
200
  * @property {Readonly<LanguageAcceptanceCapabilities>} acceptance - Stages and toolchains.
201
+ * @property {Readonly<LanguageCheckCapabilities>} check - Non-executing developer-check capability.
192
202
  */
193
203
 
194
204
  /**
@@ -425,10 +435,15 @@
425
435
 
426
436
  /**
427
437
  * @typedef PublicationRequest
428
- * @property {string} generationId - Unique explicit generation/cycle identity.
438
+ * @property {string} generationId - Explicit generation/cycle identity, fresh or an exact retained validator-free candidate.
429
439
  * @property {readonly PublicationTargetInput[]} targets - Complete ordered project targets.
430
440
  */
431
441
 
442
+ /**
443
+ * @typedef PublicationOptions
444
+ * @property {AbortSignal} [signal] - Cancellation authority through the active-pointer commit boundary.
445
+ */
446
+
432
447
  /**
433
448
  * @typedef GenerationContentHash
434
449
  * @property {"sha256"} algorithm - Hash algorithm.
@@ -504,6 +519,79 @@
504
519
  * @property {readonly string[]} versionArguments - Exact version argument array.
505
520
  */
506
521
 
522
+ /**
523
+ * @typedef TargetCheckPathArgument
524
+ * @property {number} index - Exact argv index containing the path.
525
+ * @property {"source" | "build"} ownership - Candidate subtree that owns the path.
526
+ * @property {string} [prefix] - Exact non-path argv prefix preceding an owned absolute path.
527
+ */
528
+
529
+ /**
530
+ * @typedef TargetCheckEnvironmentPath
531
+ * @property {string} name - Exact environment variable containing an owned absolute path.
532
+ * @property {"source" | "build" | "tool"} ownership - Candidate subtree or discovered tool that owns the path.
533
+ * @property {DiscoveredToolchain} [tool] - Exact tool executable bound by a tool-owned environment value.
534
+ */
535
+
536
+ /**
537
+ * @typedef TargetCheckOutputOwnership
538
+ * @property {"build"} ownership - Compiler outputs belong only to the candidate build subtree.
539
+ * @property {string} path - Exact generation-scoped build root.
540
+ * @property {string} mediaType - Media type assigned to every produced regular file.
541
+ * @property {string} role - Language-neutral compiler-output role.
542
+ */
543
+
544
+ /**
545
+ * @typedef TargetCheckStageRequest
546
+ * @property {AcceptanceStage} stage - Declared non-executing acceptance stage.
547
+ * @property {DiscoveredToolchain} tool - Exact immutable discovered tool record.
548
+ * @property {string} executable - Exact executable copied from the tool record.
549
+ * @property {readonly string[]} argv - Exact shell-free argument vector.
550
+ * @property {readonly TargetCheckPathArgument[]} pathArguments - Complete ownership declaration for argv paths.
551
+ * @property {string} cwd - Exact candidate-scoped working directory.
552
+ * @property {Readonly<Record<string, string>>} environment - Exact deterministic environment.
553
+ * @property {readonly TargetCheckEnvironmentPath[]} environmentPaths - Owned absolute environment paths.
554
+ * @property {string | null} inputHash - SHA-256 of declared restore inputs, or null for unconditional stages.
555
+ * @property {readonly string[]} inputs - Complete staged artifact inputs read by the stage.
556
+ * @property {Readonly<TargetCheckOutputOwnership> | null} output - Build output ownership, or null for non-producing checks.
557
+ * @property {readonly string[]} transientPaths - Candidate source/build cache or intermediate directories removed after checking.
558
+ */
559
+
560
+ /**
561
+ * @typedef TargetCheckPlan
562
+ * @property {"SemantifoldTargetCheckPlan"} schema - Plan schema discriminator.
563
+ * @property {1} version - Plan schema version.
564
+ * @property {"developer-check"} mode - Non-executing developer-check mode.
565
+ * @property {string} projectId - Exact project identity.
566
+ * @property {string} targetId - Exact project target identity.
567
+ * @property {string} target - Registered backend target identity.
568
+ * @property {string} sourcePath - Exact staged generated-source root.
569
+ * @property {string} buildPath - Exact staged compiler-output root.
570
+ * @property {readonly string[]} artifactPaths - Exact staged artifact paths.
571
+ * @property {readonly TargetCheckStageRequest[]} stages - Ordered target-owned requests.
572
+ */
573
+
574
+ /**
575
+ * @typedef TargetCheckStageResult
576
+ * @property {AcceptanceStage} stage - Completed stage identity.
577
+ * @property {Readonly<{command: string, executable: string, id: string, source: "override" | "canonical", version: string}>} tool - Exact tool identity.
578
+ * @property {readonly string[]} argv - Exact executed argument vector.
579
+ * @property {number} exitCode - Successful process exit code.
580
+ * @property {null} signal - Successful process signal state.
581
+ * @property {string} stdout - Bounded captured standard output.
582
+ * @property {string} stderr - Bounded captured standard error.
583
+ * @property {number} durationMs - Non-negative stage duration.
584
+ */
585
+
586
+ /**
587
+ * @typedef TargetCheckResult
588
+ * @property {string} projectId - Exact project identity.
589
+ * @property {string} targetId - Exact project target identity.
590
+ * @property {string} target - Registered backend target identity.
591
+ * @property {readonly TargetCheckStageResult[]} stages - Ordered successful stage evidence.
592
+ * @property {readonly PublicationBuildArtifactInput[]} outputs - Complete compiler-owned build files.
593
+ */
594
+
507
595
  /**
508
596
  * @typedef AcceptanceStageResult
509
597
  * @property {AcceptanceStage} stage - Completed stage.
@@ -897,6 +985,17 @@
897
985
  * @property {SemanticNodeSourceProvenance} [sourceProvenance] - Node-associated provenance.
898
986
  */
899
987
 
988
+ /**
989
+ * @typedef ConditionalExpression
990
+ * @property {"ConditionalExpression"} kind - Node discriminator.
991
+ * @property {Expression} condition - Strict boolean test evaluated exactly once.
992
+ * @property {Expression} consequent - Arm evaluated when the test is true.
993
+ * @property {Expression} alternate - Arm evaluated when the test is false.
994
+ * @property {SemanticTypeName} type - Joined arm result type assigned by validation.
995
+ * @property {SourceLocation} location - Source location.
996
+ * @property {SemanticNodeSourceProvenance} [sourceProvenance] - Node-associated provenance.
997
+ */
998
+
900
999
  /**
901
1000
  * @typedef CallExpression
902
1001
  * @property {"CallExpression"} kind - Node discriminator.
@@ -996,7 +1095,7 @@
996
1095
  * @property {SemanticNodeSourceProvenance} [sourceProvenance] - Parser-owned message-member range.
997
1096
  */
998
1097
 
999
- /** @typedef {IdentifierExpression | ReceiverExpression | IntegerLiteral | BooleanLiteral | StringLiteral | OptionalNone | OptionalSome | OptionalIsPresent | OptionalUnwrap | ListLiteral | MapLiteral | OrderedMapLiteral | ListIndexExpression | MapLookupExpression | CollectionSizeExpression | UnaryExpression | BinaryExpression | CallExpression | EffectCallExpression | OwnedMoveExpression | OwnedBorrowExpression | MethodCallExpression | ReferenceConstruction | RecordConstruction | MemberRead | PrivateFieldRead | ErrorMessageRead} Expression */
1098
+ /** @typedef {IdentifierExpression | ReceiverExpression | IntegerLiteral | BooleanLiteral | StringLiteral | OptionalNone | OptionalSome | OptionalIsPresent | OptionalUnwrap | ListLiteral | MapLiteral | OrderedMapLiteral | ListIndexExpression | MapLookupExpression | CollectionSizeExpression | UnaryExpression | BinaryExpression | ConditionalExpression | CallExpression | EffectCallExpression | OwnedMoveExpression | OwnedBorrowExpression | MethodCallExpression | ReferenceConstruction | RecordConstruction | MemberRead | PrivateFieldRead | ErrorMessageRead} Expression */
1000
1099
 
1001
1100
  /**
1002
1101
  * @typedef LocalDeclaration
@@ -2284,6 +2284,32 @@ function inferExpressionType(expression, scope, functions, records, fail, normal
2284
2284
  }
2285
2285
  return scalarType(signature.result)
2286
2286
  }
2287
+ if (expression.kind == "ConditionalExpression") {
2288
+ const conditionType = inferValueExpressionType(expression.condition, scope, functions, records, fail,
2289
+ normalizeOperations, "a condition")
2290
+
2291
+ if (!isScalarType(conditionType, "boolean")) {
2292
+ fail("NON_BOOLEAN_CONDITION", `Conditional expression condition type ${typeDescription(conditionType)}; expected boolean.`,
2293
+ expression.condition.location)
2294
+ }
2295
+ const consequentScope = createScope(scope, [])
2296
+ const alternateScope = createScope(scope, [])
2297
+
2298
+ applyPresenceNarrowing(expression.condition, scope, consequentScope, true)
2299
+ applyPresenceNarrowing(expression.condition, scope, alternateScope, false)
2300
+ const consequentType = inferValueExpressionType(expression.consequent, consequentScope, functions, records, fail,
2301
+ normalizeOperations, "a conditional expression consequent")
2302
+ const alternateType = inferValueExpressionType(expression.alternate, alternateScope, functions, records, fail,
2303
+ normalizeOperations, "a conditional expression alternate")
2304
+
2305
+ if (!sameType(consequentType, alternateType)) {
2306
+ fail("TYPE_MISMATCH", `Conditional expression arms ${typeDescription(consequentType)} and ${typeDescription(alternateType)}; expected one joined type.`,
2307
+ expression.alternate.location)
2308
+ }
2309
+ Reflect.set(expression, "type", consequentType)
2310
+
2311
+ return consequentType
2312
+ }
2287
2313
  if (expression.kind == "BinaryExpression") {
2288
2314
  const left = inferValueExpressionType(expression.left, scope, functions, records, fail, normalizeOperations, "a binary operand")
2289
2315
  const right = inferValueExpressionType(expression.right, scope, functions, records, fail, normalizeOperations, "a binary operand")
package/src/subprocess.js CHANGED
@@ -6,6 +6,7 @@ import {StringDecoder} from "node:string_decoder"
6
6
  const forcedTerminationGraceMs = 250
7
7
  const retainedPipeGraceMs = 250
8
8
  const deadlineFailures = new WeakSet()
9
+ const cancellationFailures = new WeakSet()
9
10
 
10
11
  /**
11
12
  * Executes one exact file while owning its complete bounded lifecycle.
@@ -15,6 +16,7 @@ const deadlineFailures = new WeakSet()
15
16
  * @param {Readonly<Record<string, string>>} input.environment - Exact environment.
16
17
  * @param {string} input.executable - Exact executable path.
17
18
  * @param {number} input.maxBuffer - Maximum bytes captured per output stream.
19
+ * @param {AbortSignal} [input.signal] - Optional owned cancellation signal.
18
20
  * @param {number} input.timeoutMs - Deadline before termination begins.
19
21
  * @returns {Promise<{stderr: string, stdout: string}>} Output after the owned process group closes.
20
22
  */
@@ -38,7 +40,7 @@ export function createExecuteFileWithDeadline({registerSpawnListener}) {
38
40
  * @param {(child: import("node:child_process").ChildProcess, listener: () => void) => void} registerSpawnListener - Registers deadline initialization.
39
41
  * @returns {Promise<{stderr: string, stdout: string}>} Output after the owned process group closes.
40
42
  */
41
- function executeFileWithDeadlineUsing({arguments: processArguments, cwd, environment, executable, maxBuffer, timeoutMs}, registerSpawnListener) {
43
+ function executeFileWithDeadlineUsing({arguments: processArguments, cwd, environment, executable, maxBuffer, signal, timeoutMs}, registerSpawnListener) {
42
44
  if (process.platform == "win32") {
43
45
  return Promise.reject(Object.assign(new Error("Owned process-tree termination is unavailable on this platform."), {
44
46
  code: "ENOTSUP",
@@ -46,6 +48,12 @@ function executeFileWithDeadlineUsing({arguments: processArguments, cwd, environ
46
48
  stdout: ""
47
49
  }))
48
50
  }
51
+ if (signal?.aborted) {
52
+ const cancellationError = Object.assign(new Error("Subprocess was cancelled before launch."), {stderr: "", stdout: ""})
53
+
54
+ cancellationFailures.add(cancellationError)
55
+ return Promise.reject(cancellationError)
56
+ }
49
57
 
50
58
  return new Promise((resolve, reject) => {
51
59
  /** @type {ReturnType<typeof setTimeout> | undefined} */
@@ -56,8 +64,10 @@ function executeFileWithDeadlineUsing({arguments: processArguments, cwd, environ
56
64
  let pipeTimer
57
65
  /** @type {Error | undefined} */
58
66
  let launchError
67
+ let cancellationRequested = false
59
68
  let deadlineExceeded = false
60
69
  let outputExceeded = false
70
+ let processGroupOwned = false
61
71
  let settled = false
62
72
  const stderr = outputCapture(maxBuffer, () => terminateForOutputLimit())
63
73
  const stdout = outputCapture(maxBuffer, () => terminateForOutputLimit())
@@ -68,9 +78,15 @@ function executeFileWithDeadlineUsing({arguments: processArguments, cwd, environ
68
78
  stdio: ["ignore", "pipe", "pipe"]
69
79
  })
70
80
  const processGroupId = child.pid
81
+ const cancel = () => {
82
+ if (settled || cancellationRequested || deadlineExceeded || outputExceeded) return
83
+ cancellationRequested = true
84
+ if (processGroupOwned && processGroupId != undefined) beginTermination(processGroupId)
85
+ }
71
86
 
72
87
  child.stderr.on("data", stderr.receive)
73
88
  child.stdout.on("data", stdout.receive)
89
+ signal?.addEventListener("abort", cancel, {once: true})
74
90
  child.once("error", (error) => {
75
91
  launchError = error
76
92
  })
@@ -80,18 +96,24 @@ function executeFileWithDeadlineUsing({arguments: processArguments, cwd, environ
80
96
  child.kill("SIGKILL")
81
97
  return
82
98
  }
99
+ processGroupOwned = true
100
+ if (cancellationRequested) {
101
+ beginTermination(processGroupId)
102
+ return
103
+ }
83
104
 
84
105
  deadlineTimer = setTimeout(() => {
85
- if (settled) return
106
+ if (settled || cancellationRequested || deadlineExceeded || outputExceeded) return
86
107
  deadlineExceeded = true
87
108
  beginTermination(processGroupId)
88
109
  }, timeoutMs)
89
110
  })
90
- child.once("close", (code, signal) => {
111
+ child.once("close", (code, closeSignal) => {
91
112
  settled = true
92
113
  if (deadlineTimer != undefined) clearTimeout(deadlineTimer)
93
114
  if (forceTimer != undefined) clearTimeout(forceTimer)
94
115
  if (pipeTimer != undefined) clearTimeout(pipeTimer)
116
+ signal?.removeEventListener("abort", cancel)
95
117
  const capturedStderr = stderr.finish()
96
118
  const capturedStdout = stdout.finish()
97
119
 
@@ -100,24 +122,34 @@ function executeFileWithDeadlineUsing({arguments: processArguments, cwd, environ
100
122
  } else if (outputExceeded) {
101
123
  reject(Object.assign(new Error(`Subprocess exceeded its ${maxBuffer}-byte output capture limit.`), {
102
124
  code: "ERR_CHILD_PROCESS_STDIO_MAXBUFFER",
103
- signal,
125
+ signal: closeSignal,
104
126
  stderr: capturedStderr,
105
127
  stdout: capturedStdout
106
128
  }))
129
+ } else if (cancellationRequested) {
130
+ const cancellationError = Object.assign(new Error("Subprocess was cancelled."), {
131
+ code,
132
+ signal: closeSignal,
133
+ stderr: capturedStderr,
134
+ stdout: capturedStdout
135
+ })
136
+
137
+ cancellationFailures.add(cancellationError)
138
+ reject(cancellationError)
107
139
  } else if (deadlineExceeded) {
108
140
  const timeoutError = Object.assign(new Error("Subprocess exceeded its owned deadline."), {
109
141
  code,
110
- signal,
142
+ signal: closeSignal,
111
143
  stderr: capturedStderr,
112
144
  stdout: capturedStdout
113
145
  })
114
146
 
115
147
  deadlineFailures.add(timeoutError)
116
148
  reject(timeoutError)
117
- } else if (code != 0 || signal != null) {
149
+ } else if (code != 0 || closeSignal != null) {
118
150
  reject(Object.assign(new Error("Subprocess failed after launch."), {
119
151
  code,
120
- signal,
152
+ signal: closeSignal,
121
153
  stderr: capturedStderr,
122
154
  stdout: capturedStdout
123
155
  }))
@@ -126,7 +158,7 @@ function executeFileWithDeadlineUsing({arguments: processArguments, cwd, environ
126
158
 
127
159
  /** Begins bounded termination after output capture crosses its limit. */
128
160
  function terminateForOutputLimit() {
129
- if (outputExceeded || settled) return
161
+ if (settled || cancellationRequested || deadlineExceeded || outputExceeded) return
130
162
  outputExceeded = true
131
163
  if (processGroupId != undefined) beginTermination(processGroupId)
132
164
  }
@@ -230,3 +262,12 @@ function signalOwnedProcessGroup(processGroupId, signal) {
230
262
  export function exceededOwnedDeadline(error) {
231
263
  return typeof error == "object" && error != null && deadlineFailures.has(error)
232
264
  }
265
+
266
+ /**
267
+ * Reports whether this module's cancellation signal initiated termination.
268
+ * @param {unknown} error - Subprocess failure.
269
+ * @returns {boolean} Whether the failure belongs to owned cancellation.
270
+ */
271
+ export function cancelledOwnedProcess(error) {
272
+ return typeof error == "object" && error != null && cancellationFailures.has(error)
273
+ }