ttsc 0.34.1 → 0.34.2

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.
@@ -69,13 +69,16 @@ type apiTransformResult struct {
69
69
  // The actual preparation supplies ForceNoEmit and resolved argv/cwd. This
70
70
  // wrapper owns its one LoadProgram call and closes a nonnil acquired Program
71
71
  // after the borrowed source-response operation returns. That response owner
72
- // constructs the graph before linked source hooks and never emits files.
72
+ // constructs the graph before linked source hooks and never emits files. Failed
73
+ // config/argument parsing retains its actual recovery graph without a Program.
73
74
  func runAPITransform(args []string) int {
74
75
  request, code := prepareAPITransformInvocation(args)
75
76
  if code != 0 {
76
77
  return code
77
78
  }
78
79
  cwd := request.cwd
80
+ var recovery *driver.TransformGraph
81
+ request.options.OnFailureInputs = func(graph *driver.TransformGraph) { recovery = graph }
79
82
  prog, diags, err := driver.LoadProgram(cwd, request.tsconfigPath, request.options)
80
83
  if err != nil {
81
84
  fmt.Fprintf(stderr, "ttsc api-transform: %v\n", err)
@@ -84,7 +87,7 @@ func runAPITransform(args []string) int {
84
87
  if prog != nil {
85
88
  defer prog.Close()
86
89
  }
87
- return writeTransformedProgramResponse(prog, diags, cwd)
90
+ return writeTransformedProgramResponse(prog, diags, cwd, recovery)
88
91
  }
89
92
 
90
93
  // writeTransformedProgramResponse borrows the loaded generation for the actual
@@ -102,14 +105,21 @@ func runAPITransform(args []string) int {
102
105
  // Performance: Efficient algorithms: The original graph, resident-file enumeration and one JSON encoding remain; work scales with actual graph/source/envelope bytes.
103
106
  // Performance: Reuse equivalent work: An immutable no-plugin generation already emitted to memory can supply its original source text and graph without a second load. This is operation-level reuse, not a claim of equal compile/transform loader policies.
104
107
  // Performance: Bound retention and release resources: The caller retains the checker lease across borrowing and closes it once after the final consumer; this operation creates invocation-local envelope collections and does not own the lease or a retained cache.
105
- func writeTransformedProgramResponse(prog *driver.Program, diags []driver.Diagnostic, cwd string) int {
108
+ func writeTransformedProgramResponse(prog *driver.Program, diags []driver.Diagnostic, cwd string, recovery ...*driver.TransformGraph) int {
106
109
  typescript := map[string]string{}
107
110
  var dependencies driver.TransformDependencies
108
111
  var graph *driver.TransformGraph
112
+ if len(recovery) != 0 {
113
+ graph = recovery[0]
114
+ }
109
115
  var hostInputs []string
110
116
  var hostInputHashes map[string]*string
111
117
  var hostInputRealpaths map[string]*string
112
118
  var observationsComplete *bool
119
+ if prog == nil {
120
+ unavailable := false
121
+ observationsComplete = &unavailable
122
+ }
113
123
  if prog != nil {
114
124
  // Compute the reference graph before SourceFiles() runs linked plugin
115
125
  // hooks: those mutate parsed ASTs in place, and the graph must describe
@@ -0,0 +1,44 @@
1
+ package main
2
+
3
+ import (
4
+ "encoding/json"
5
+ "slices"
6
+ "testing"
7
+ )
8
+
9
+ // TestAPITransformConfigFailureRetainsRecoveryInputs verifies the actual
10
+ // built-in transform command transports failed-load observations.
11
+ //
12
+ // Source output cannot carry repair inputs when config parsing creates no Program.
13
+ //
14
+ // 1. Run the actual API command on a missing extended config and response file.
15
+ // 2. Decode its native stdout envelope and retain the failure status.
16
+ // 3. Require missing config predicates and response reads without source output.
17
+ //
18
+ // @evidence contracts/testing.md#behavioral-verification Real runAPITransform consumes its ordinary argument preparation and loader, then serializes failed-load recovery metadata rather than dropping it with the nil Program.
19
+ // @evidence contracts/testing.md#independent-expectations Status 2, empty TypeScript, literal missing.json/main.rsp inputs and a false existence predicate define expected failure transport independently of command output.
20
+ // @evidence contracts/testing.md#distinguishing-cases A pre-Program config failure contrasts the existing semantic-error Program case; actual argv response expansion remains observable in the failure graph on both targets.
21
+ // @evidence contracts/testing.md#execution-ownership The command Go runner owns one temporary project and invocation-local captured streams; the actual command executes in process without a separate build or compiler subprocess.
22
+ func TestAPITransformConfigFailureRetainsRecoveryInputs(t *testing.T) {
23
+ root := t.TempDir()
24
+ writeCommandProjectFile(t, root, "tsconfig.json", `{"extends":"./missing.json","files":["main.ts"]}`)
25
+ writeCommandProjectFile(t, root, "main.ts", "export const value = 1;\n")
26
+ writeCommandProjectFile(t, root, "main.rsp", "--strict\n")
27
+ code, output, stderr := captureCommand(t, func() int {
28
+ return runAPITransform([]string{"--cwd", root, "--tsconfig", "tsconfig.json", "--tsgo-args", `["@main.rsp"]`})
29
+ })
30
+ var result apiTransformResult
31
+ if err := json.Unmarshal([]byte(output), &result); err != nil {
32
+ t.Fatalf("response decode: %v stdout=%s stderr=%s", err, output, stderr)
33
+ }
34
+ if code != 2 || len(result.TypeScript) != 0 || result.Graph == nil || !slices.Contains(result.Graph.ResolutionInputs, "missing.json") || !slices.Contains(result.Graph.ResolutionInputs, "main.rsp") {
35
+ t.Fatalf("lost config recovery metadata: code=%d output=%s stderr=%s", code, output, stderr)
36
+ }
37
+ if result.ObservationsComplete == nil || *result.ObservationsComplete {
38
+ t.Fatalf("failed load claimed complete observations: %s", output)
39
+ }
40
+ absent := result.Graph.InputObservations["missing.json"].FileExists
41
+ if absent == nil || *absent {
42
+ t.Fatalf("lost config absence predicate: %s", output)
43
+ }
44
+ }
@@ -11,8 +11,10 @@ import (
11
11
  // caller must keep the Program open until this snapshot is written. Command
12
12
  // status and diagnostics remain independent of these observation facts.
13
13
  //
14
- // A nil Program publishes empty observation collections and an explicit incomplete
15
- // marker, because no generation supplied input authority. For a loaded Program,
14
+ // A nil Program publishes empty hook observation collections and an explicit
15
+ // incomplete marker. WriteCheckRecoveryObservationsJSON additionally accepts
16
+ // actual failed-load CLI/config predicates without completed Program authority.
17
+ // For a loaded Program,
16
18
  // only an explicit hook limitation adds that marker. Its absence does not prove
17
19
  // the population complete or repair missing or conflicting individual proofs.
18
20
  // The caller owns a fresh result path, its parent directory and eventual artifact
@@ -27,6 +29,23 @@ import (
27
29
  // @evidence contracts/performance.md#reuse-equivalent-work The same check generation's recorded observations supply the result without reparsing sources or reexecuting configuration hooks.
28
30
  // @evidence contracts/performance.md#bound-retention-and-release-resources Scope snapshots and encoding buffers are call-local without a payload cap. The shared publisher closes its temporary descriptor before rename and attempts removal on failure, returning cleanup errors; the caller owns the Program, fresh destination, parent directory and final artifact removal.
29
31
  func WriteCheckObservationsJSON(fileName string, program *Program) error {
32
+ return WriteCheckRecoveryObservationsJSON(fileName, program, nil)
33
+ }
34
+
35
+ // WriteCheckRecoveryObservationsJSON publishes check observations with an
36
+ // optional failed-load recovery graph. A loaded Program always supplies its own
37
+ // graph instead; nil Program remains explicitly incomplete. The recovery graph
38
+ // comes from that invocation's OnFailureInputs sink, not a later filesystem read.
39
+ //
40
+ // @evidence contracts/common.md#principled-implementation Loaded Program observations retain their generation; only a nil Program admits supplied failed-load predicates, with explicit incomplete hook authority.
41
+ // @evidence contracts/common.md#clear-and-simple-design One publisher serves the original two-argument API and native callers retaining failed-load recovery inputs.
42
+ // @evidence contracts/common.md#prohibited-implementation-shortcuts No fake Program, reconstructed resolver or later read supplies recovery metadata; status remains separate.
43
+ // @evidence contracts/common.md#meaningful-documentation Native prose states recovery provenance, Program precedence and incomplete authority.
44
+ // @evidence contracts/portability.md#os-neutral-implementation Native absolute path validation and the existing same-directory private publisher retain observation spellings unchanged.
45
+ // @evidence contracts/performance.md#efficient-algorithms Program accessors snapshot current ledgers and graph; JSON encoding and publication visit retained predicate/path bytes and allocate the serialized payload without a population cap.
46
+ // @evidence contracts/performance.md#reuse-equivalent-work The invocation's existing observations supply metadata without another compiler load or filesystem proof read.
47
+ // @evidence contracts/performance.md#bound-retention-and-release-resources The caller owns Program/recovery lifetimes and the final artifact; call-local snapshots and encoding buffers add no historical registry, and the shared publisher closes temporary handles.
48
+ func WriteCheckRecoveryObservationsJSON(fileName string, program *Program, recovery *TransformGraph) error {
30
49
  if !filepath.IsAbs(fileName) {
31
50
  return fmt.Errorf("driver: check observations path must be absolute: %q", fileName)
32
51
  }
@@ -35,6 +54,9 @@ func WriteCheckObservationsJSON(fileName string, program *Program) error {
35
54
  realpaths := map[string]*string{}
36
55
  var complete *bool
37
56
  var graph *TransformGraph
57
+ if program == nil {
58
+ graph = recovery
59
+ }
38
60
  if program == nil || program.PluginObservationsIncomplete() {
39
61
  unavailable := false
40
62
  complete = &unavailable
package/driver/graph.go CHANGED
@@ -37,7 +37,7 @@ const bundledScheme = "bundled:///"
37
37
  // results. They are a separate class from resolved edges: a predicate or
38
38
  // package manifest changing can change an unchanged reference's meaning.
39
39
  // - ResolutionInputs lists automatic type discovery and resolution inputs
40
- // whose state can affect every source file, including type-root directory
40
+ // whose state can affect every source file, including compiler response files and type-root directory
41
41
  // membership.
42
42
  // - InputObservations preserves the independent filesystem predicates the
43
43
  // compiler actually asked. InputHashes and InputRealpaths retain the legacy
@@ -48,6 +48,14 @@ const bundledScheme = "bundled:///"
48
48
  // project's root specs and compared paths with, so a host deciding the
49
49
  // same membership uses the compiler's policy rather than a guess from the
50
50
  // platform.
51
+ // - ContentMapperInputs lists mapper manifests and upstream-declared dynamic
52
+ // dependencies for every output. They are declarations, not observed reads,
53
+ // and require independent before/after dependency witnesses.
54
+ //
55
+ // A failed-load recovery graph contains only the CLI/config predicates observed
56
+ // before Program construction failed. Empty source/config lists and absent
57
+ // membership settings supply no completed Program authority. Failure status and
58
+ // explicit observation limitations remain in the containing response.
51
59
  //
52
60
  // Keys and values use the same convention as the envelope's `typescript`
53
61
  // map: project-relative slash paths, falling back to slash-normalized
@@ -62,6 +70,11 @@ const bundledScheme = "bundled:///"
62
70
  // @evidenceExclude contracts/performance.md#reuse-equivalent-work This schema carries proof without coordinating artifact reuse.
63
71
  // @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The caller-owned value owns no resident cache or resource.
64
72
  type TransformGraph struct {
73
+ // Membership is the effective native root-admission and output-exclusion policy.
74
+ Membership *TransformMembership `json:"membership,omitempty"`
75
+ // ContentMapperInputs are declared project dependencies, not observed reads.
76
+ // Consumers witness them before compilation independently of graph proof.
77
+ ContentMapperInputs []string `json:"contentMapperInputs,omitempty"`
65
78
  Edges map[string][]string `json:"edges"`
66
79
  Globals []string `json:"globals"`
67
80
  Configs []string `json:"configs"`
@@ -94,6 +107,7 @@ func NewTransformGraph(prog *Program, cwd string) *TransformGraph {
94
107
  }
95
108
  resolution := ObserveProgramResolutions(prog, cwd)
96
109
  graph := &TransformGraph{
110
+ Membership: programRootMembership(prog),
97
111
  Edges: map[string][]string{},
98
112
  Globals: []string{},
99
113
  Configs: []string{},
@@ -101,6 +115,22 @@ func NewTransformGraph(prog *Program, cwd string) *TransformGraph {
101
115
  ResolutionInputs: resolution.Universal,
102
116
  UseCaseSensitiveFileNames: prog.TSProgram.UseCaseSensitiveFileNames(),
103
117
  }
118
+ for _, input := range prog.mapperDependencies {
119
+ graph.ContentMapperInputs = append(graph.ContentMapperInputs, TransformOutputKey(cwd, input))
120
+ }
121
+ sort.Strings(graph.ContentMapperInputs)
122
+ argumentInputs := map[string]struct{}{}
123
+ for _, input := range graph.ResolutionInputs {
124
+ argumentInputs[input] = struct{}{}
125
+ }
126
+ for _, input := range prog.compilerArgumentInputs {
127
+ key := TransformOutputKey(cwd, input)
128
+ if _, found := argumentInputs[key]; !found {
129
+ graph.ResolutionInputs = append(graph.ResolutionInputs, key)
130
+ argumentInputs[key] = struct{}{}
131
+ }
132
+ }
133
+ sort.Strings(graph.ResolutionInputs)
104
134
  for _, file := range prog.TSProgram.SourceFiles() {
105
135
  fileName := file.FileName()
106
136
  if strings.HasPrefix(string(fileName), bundledScheme) {
@@ -135,6 +165,13 @@ func (graph *TransformGraph) attachInputProof(prog *Program, cwd string) {
135
165
  if prog == nil || prog.inputObserver == nil {
136
166
  return
137
167
  }
168
+ graph.attachObservedInputProof(prog.inputObserver, cwd)
169
+ }
170
+
171
+ // attachObservedInputProof projects only this observer's existing predicates.
172
+ // Failed CLI/config loads use it without constructing a compiler Program;
173
+ // successful graph construction retains the same realized-source requirements.
174
+ func (graph *TransformGraph) attachObservedInputProof(observer *inputObservationFS, cwd string) {
138
175
  inputs := map[string]struct{}{}
139
176
  realized := map[string]struct{}{}
140
177
  addRealized := func(input string) {
@@ -174,14 +211,14 @@ func (graph *TransformGraph) attachInputProof(prog *Program, cwd string) {
174
211
  if !filepath.IsAbs(file) {
175
212
  file = filepath.Join(cwd, file)
176
213
  }
177
- observation, predicateFailure := prog.inputObserver.predicateProof(file)
214
+ observation, predicateFailure := observer.predicateProof(file)
178
215
  if predicateFailure == "" {
179
216
  observations[input] = observation
180
217
  }
181
218
  var hash, realpath *string
182
219
  legacyFailure := predicateFailure
183
220
  if predicateFailure == "" {
184
- hash, realpath, legacyFailure = prog.inputObserver.proof(file)
221
+ hash, realpath, legacyFailure = observer.proof(file)
185
222
  }
186
223
  if legacyFailure != "" {
187
224
  // A resolver input can have a complete predicate proof that the legacy
@@ -271,6 +271,22 @@ func boolPointer(value bool) *bool {
271
271
  return &value
272
272
  }
273
273
 
274
+ // readPaths snapshots the lexical reads already made through this observer.
275
+ // LoadProgram calls it immediately after upstream argument parsing, before
276
+ // configuration or source reads can enter the population. It performs no new
277
+ // filesystem query and keeps failed/conflicting observations for graph proof.
278
+ func (fs *inputObservationFS) readPaths() []string {
279
+ fs.mu.Lock()
280
+ defer fs.mu.Unlock()
281
+ paths := make([]string, 0, len(fs.observationOrder))
282
+ for _, key := range fs.observationOrder {
283
+ if fs.observations[key].proof.ReadFile != nil {
284
+ paths = append(paths, fs.observationSpellings[key])
285
+ }
286
+ }
287
+ return paths
288
+ }
289
+
274
290
  func (fs *inputObservationFS) observationKey(path string) string {
275
291
  if !filepath.IsAbs(path) {
276
292
  return ""
package/driver/program.go CHANGED
@@ -323,7 +323,10 @@ type Program struct {
323
323
  Host shimcompiler.CompilerHost
324
324
  // hosts builds every generation's Host and owns the content-mapper processes
325
325
  // those hosts share; Close releases it.
326
- hosts *shimcompiler.ProjectHosts
326
+ hosts *shimcompiler.ProjectHosts
327
+ mapperDependencies []string
328
+ mapperDependencyError error
329
+ compilerArgumentInputs []string
327
330
 
328
331
  // FS is the input-observing filesystem, including any source preamble layer.
329
332
  FS vfs.FS
@@ -340,10 +343,10 @@ type Program struct {
340
343
  // the program. `ForceEmit` is used by `ttsc --emit` and runtime compilation
341
344
  // so execution still works when the project defaults to `noEmit`.
342
345
  //
343
- // @evidence contracts/common.md#principled-implementation Explicit overrides and an optional FS represent caller-owned load choices separately from resolved tsconfig values.
346
+ // @evidence contracts/common.md#principled-implementation Explicit overrides, an optional FS and a failure-observation sink represent caller-owned load choices separately from resolved tsconfig values.
344
347
  // @evidence contracts/common.md#clear-and-simple-design One options value carries load-time choices; compiler option merging stays with LoadProgram and the native parser.
345
348
  // @evidence contracts/common.md#prohibited-implementation-shortcuts Dedicated fields express supported overrides without deriving policy from a fixture or consumer name.
346
- // @evidence contracts/common.md#meaningful-documentation Native member prose explains force emit, semantic config ownership, threading and optional FS under documentation-skill guidance.
349
+ // @evidence contracts/common.md#meaningful-documentation Native member prose explains force emit, semantic config ownership, threading, optional FS and failed-load observation ownership under documentation-skill guidance.
347
350
  // @evidence contracts/portability.md#os-neutral-implementation FS supplies native capabilities and path fields retain caller spellings for the loader's supported normalization boundary.
348
351
  // @evidenceExclude contracts/performance.md#efficient-algorithms Options encode choices but perform no compilation algorithm.
349
352
  // @evidenceExclude contracts/performance.md#reuse-equivalent-work This input value does not establish whether loaded work can be shared.
@@ -394,6 +397,12 @@ type LoadProgramOptions struct {
394
397
  // is used. A resident Session passes an overlay FS so in-memory edits stay
395
398
  // visible to the program and to incremental UpdateProgram calls.
396
399
  FS vfs.FS
400
+
401
+ // OnFailureInputs receives the actual CLI/config input observations if this
402
+ // load exits after observation starts but before a Program is acquired. It
403
+ // runs synchronously once; it does not report a completed source population.
404
+ // Successful loads expose their graph through NewTransformGraph instead.
405
+ OnFailureInputs func(*TransformGraph)
397
406
  }
398
407
 
399
408
  // Close releases the checker pool lease acquired by LoadProgram and, on the
@@ -582,13 +591,13 @@ func forceSingleChecker(parsed *tsoptions.ParsedCommandLine) {
582
591
  // callers should provide an absolute anchor. tsconfigPath may be relative to it.
583
592
  //
584
593
  // @evidence contracts/common.md#principled-implementation Plugin preambles enter before parsing, native CLI/config merging resolves options before Program creation, and the facade associates the observing VFS and borrowed checker with that generation; this is not a certificate of every filesystem access by plugins or their host.
585
- // @evidence contracts/common.md#clear-and-simple-design One loader orders plugin config, filesystem layers, config overrides and checker acquisition; private helpers each own one policy.
594
+ // @evidence contracts/common.md#clear-and-simple-design One loader orders plugin config, filesystem layers, config overrides and checker acquisition; an optional failure sink transfers only the rejected load's observed recovery inputs.
586
595
  // @evidence contracts/common.md#prohibited-implementation-shortcuts Invalid configuration or CLI input returns before construction; an explicitly empty argument list does not fall through to an ancestor's flags.
587
596
  // @evidence contracts/common.md#meaningful-documentation Native prose and options document one-shot load, path anchoring, force overrides and checker ownership under documentation-skill guidance.
588
597
  // @evidence contracts/portability.md#os-neutral-implementation Native cwd resolution uses filepath/tspath and the chosen VFS provides actual case and path capabilities; semantic wrapper ownership uses an explicit config path.
589
- // @evidence contracts/performance.md#efficient-algorithms One load composes plugin/preamble work, config/CLI parsing, native Program processing and checker acquisition with VFS observation. Read-text hashing runs on each observed read, including repeated reads; config/source bytes, native resolution and directory work, plugin callbacks and retained observation maps govern cost rather than only facade construction.
598
+ // @evidence contracts/performance.md#efficient-algorithms One load composes plugin/preamble work, config/CLI parsing, native Program processing and checker acquisition with VFS observation. A requested failed-load graph additionally snapshots, projects and sorts the observed predicate population and invokes the caller's sink. Read-text hashing runs on each observed read, including repeated reads; config/source bytes, native resolution and directory work, plugin callbacks and retained observation maps govern cost rather than only facade construction.
590
599
  // @evidenceExclude contracts/performance.md#reuse-equivalent-work This one-shot factory does not coordinate callers; Session and resident hosts own cross-request compiler reuse.
591
- // @evidence contracts/performance.md#bound-retention-and-release-resources Errors before checker acquisition return no lease. Success transfers the lease, generation trees and input/plugin ledgers to Program without a byte cap; Close releases only the checker callback, while caller reachability controls facade state and separately retained plugin callbacks.
600
+ // @evidence contracts/performance.md#bound-retention-and-release-resources Errors before checker acquisition return no lease; an optional synchronous sink receives call-owned recovery metadata once after observation starts. Successful loading transfers checker and mapper ownership plus input/plugin ledgers to Program; Close releases owned native resources, while caller reachability controls remaining facade state.
592
601
  func LoadProgram(cwd, tsconfigPath string, options LoadProgramOptions) (*Program, []Diagnostic, error) {
593
602
  if !filepath.IsAbs(cwd) {
594
603
  if abs, err := filepath.Abs(cwd); err == nil {
@@ -616,6 +625,14 @@ func LoadProgram(cwd, tsconfigPath string, options LoadProgramOptions) (*Program
616
625
  fs = DefaultFS()
617
626
  }
618
627
  inputObserver := newInputObservationFS(fs)
628
+ loaded := false
629
+ if options.OnFailureInputs != nil {
630
+ defer func() {
631
+ if !loaded {
632
+ options.OnFailureInputs(inputObserver.recoveryGraph(cwd))
633
+ }
634
+ }()
635
+ }
619
636
  fs = inputObserver
620
637
  if options.SourcePreamble != "" {
621
638
  fs = sourcePreambleFS{
@@ -646,6 +663,7 @@ func LoadProgram(cwd, tsconfigPath string, options LoadProgramOptions) (*Program
646
663
  if len(cliDiags) > 0 {
647
664
  return nil, cliDiags, nil
648
665
  }
666
+ compilerArgumentInputs := inputObserver.readPaths()
649
667
 
650
668
  // Config identity is explicit; argument conversion uses the original argv
651
669
  // base, while the Program and linked plugin state retain their selected cwd.
@@ -684,21 +702,26 @@ func LoadProgram(cwd, tsconfigPath string, options LoadProgramOptions) (*Program
684
702
  hosts := shimcompiler.NewProjectHosts(parsed)
685
703
  host = projectHost(hosts, cwd, fs)
686
704
  tsProgram, _, _ := CreateProgramFromConfig(parsed, host)
705
+ mapperDependencies, mapperDependencyError := hosts.Dependencies()
687
706
 
688
707
  checker, done := tsProgram.GetTypeChecker(context.Background())
689
708
  prog := &Program{
690
- TSProgram: tsProgram,
691
- ParsedConfig: parsed,
692
- Checker: checker,
693
- checkerRelease: done,
694
- Host: host,
695
- hosts: hosts,
696
- FS: fs,
697
- inputObserver: inputObserver,
698
- SourcePreamble: options.SourcePreamble,
709
+ TSProgram: tsProgram,
710
+ ParsedConfig: parsed,
711
+ Checker: checker,
712
+ checkerRelease: done,
713
+ Host: host,
714
+ hosts: hosts,
715
+ mapperDependencies: mapperDependencies,
716
+ mapperDependencyError: mapperDependencyError,
717
+ compilerArgumentInputs: compilerArgumentInputs,
718
+ FS: fs,
719
+ inputObserver: inputObserver,
720
+ SourcePreamble: options.SourcePreamble,
699
721
  }
700
722
  prog.plugins = pluginState
701
723
  e2etrace.Program("program-load-outcome", "driver-create", "facade-installed", false, tsProgram)
724
+ loaded = true
702
725
  return prog, nil, nil
703
726
  }
704
727
 
@@ -1101,6 +1124,9 @@ func (p *Program) diagnostics(files []*ast.SourceFile) []Diagnostic {
1101
1124
  // `driver: nil program` above is the precedent for a driver-level entry with
1102
1125
  // no file or code.
1103
1126
  var out []Diagnostic
1127
+ if p.mapperDependencyError != nil {
1128
+ out = append(out, Diagnostic{Severity: SeverityError, Message: "driver: content mapper dependencies: " + p.mapperDependencyError.Error()})
1129
+ }
1104
1130
  if p.pluginsApplied && p.pluginsApplyErr != nil {
1105
1131
  out = append(out, Diagnostic{
1106
1132
  Severity: SeverityError,
@@ -0,0 +1,25 @@
1
+ package driver
2
+
3
+ import (
4
+ "sort"
5
+
6
+ shimtspath "github.com/microsoft/typescript-go/shim/tspath"
7
+ )
8
+
9
+ // recoveryGraph retains the predicates already observed before a failed load.
10
+ // Empty source/config populations make no Program-membership claim; every
11
+ // observed path is universal recovery input, including missing candidates.
12
+ // Snapshotting and proof projection perform no new filesystem operation. Work
13
+ // and returned storage follow observed paths/predicates plus path sorting.
14
+ func (fs *inputObservationFS) recoveryGraph(cwd string) *TransformGraph {
15
+ graph := &TransformGraph{
16
+ Edges: map[string][]string{}, Globals: []string{}, Configs: []string{},
17
+ UseCaseSensitiveFileNames: fs.caseSensitivity == shimtspath.CaseSensitive,
18
+ }
19
+ for _, input := range fs.observedPaths() {
20
+ graph.ResolutionInputs = append(graph.ResolutionInputs, TransformOutputKey(cwd, input))
21
+ }
22
+ sort.Strings(graph.ResolutionInputs)
23
+ graph.attachObservedInputProof(fs, cwd)
24
+ return graph
25
+ }
@@ -9,9 +9,21 @@ import (
9
9
  ast "github.com/microsoft/typescript-go/shim/ast"
10
10
  shimcompiler "github.com/microsoft/typescript-go/shim/compiler"
11
11
  shimprinter "github.com/microsoft/typescript-go/shim/printer"
12
+ shimtsoptions "github.com/microsoft/typescript-go/shim/tsoptions"
12
13
  shimtspath "github.com/microsoft/typescript-go/shim/tspath"
13
14
  )
14
15
 
16
+ // programRootMembership uses the 7.0 supported-extension APIs and effective
17
+ // parsed options. A facade constructed without parsed config has no report.
18
+ func programRootMembership(prog *Program) *TransformMembership {
19
+ if prog.ParsedConfig == nil || prog.ParsedConfig.CompilerOptions() == nil {
20
+ return nil
21
+ }
22
+ options := prog.ParsedConfig.CompilerOptions()
23
+ groups := shimtsoptions.GetSupportedExtensions(options, nil)
24
+ return transformMembership(prog, shimtsoptions.GetSupportedExtensionsWithJsonIfResolveJsonModule(options, groups))
25
+ }
26
+
15
27
  // TypeScript 7.0 compiler target.
16
28
  //
17
29
  // The driver's emit and diagnostic glue calls compiler APIs whose signatures
@@ -9,9 +9,21 @@ import (
9
9
  ast "github.com/microsoft/typescript-go/shim/ast"
10
10
  shimcompiler "github.com/microsoft/typescript-go/shim/compiler"
11
11
  shimprinter "github.com/microsoft/typescript-go/shim/printer"
12
+ shimtsoptions "github.com/microsoft/typescript-go/shim/tsoptions"
12
13
  shimtspath "github.com/microsoft/typescript-go/shim/tspath"
13
14
  )
14
15
 
16
+ // programRootMembership uses the 7.1 supported-extension APIs with the Program's
17
+ // actual mapper extensions. No root selection is reimplemented here.
18
+ func programRootMembership(prog *Program) *TransformMembership {
19
+ if prog.ParsedConfig == nil || prog.ParsedConfig.CompilerOptions() == nil {
20
+ return nil
21
+ }
22
+ options := prog.ParsedConfig.CompilerOptions()
23
+ groups := shimtsoptions.GetSupportedExtensions(options, prog.TSProgram.ContentMapperExtensions())
24
+ return transformMembership(prog, shimtsoptions.GetSupportedExtensionsWithJsonIfResolveJsonModule(options, groups))
25
+ }
26
+
15
27
  // TypeScript 7.1 compiler target.
16
28
  //
17
29
  // The driver's emit and diagnostic glue calls compiler APIs whose signatures
@@ -15,6 +15,9 @@ import (
15
15
  // no contributor declarations to require, so every transformed file is listed.
16
16
  // This side channel does not independently certify resolver, host or plugin
17
17
  // source inputs reported through other envelope fields.
18
+ // Content mapper manifests and upstream-declared dynamic project inputs are
19
+ // added for every file independently of plugin completeness. These declarations
20
+ // carry no compiler-read proof; consumers witness them before a compilation.
18
21
  //
19
22
  // @evidence contracts/common.md#principled-implementation Reported dependencies and explicit completeness are distinct; an empty contributor set is complete without pretending silent contributors proved anything.
20
23
  // @evidence contracts/common.md#clear-and-simple-design One per-file adjacency map and one complete-file list carry separate facts.
@@ -95,7 +98,25 @@ func (p *Program) TransformDependenciesFor(cwd string) TransformDependencies {
95
98
  for _, file := range files {
96
99
  keys = append(keys, TransformOutputKey(cwd, string(file.FileName())))
97
100
  }
98
- return p.plugins.transformDependencies(keys)
101
+ out := p.plugins.transformDependencies(keys)
102
+ if len(p.mapperDependencies) != 0 {
103
+ for _, key := range keys {
104
+ inputs := map[string]struct{}{}
105
+ for _, input := range out.Dependencies[key] {
106
+ inputs[input] = struct{}{}
107
+ }
108
+ for _, input := range p.mapperDependencies {
109
+ name := TransformOutputKey(cwd, input)
110
+ if name != key {
111
+ inputs[name] = struct{}{}
112
+ }
113
+ }
114
+ if len(inputs) != 0 {
115
+ out.Dependencies = appendDependencyEntry(out.Dependencies, key, inputs)
116
+ }
117
+ }
118
+ }
119
+ return out
99
120
  }
100
121
 
101
122
  // transformDependencies aggregates every contributing plugin's declarations
@@ -0,0 +1,61 @@
1
+ package driver
2
+
3
+ import (
4
+ "sort"
5
+ "strings"
6
+ )
7
+
8
+ // TransformMembership reports the native settings needed to discover possible
9
+ // roots. Output directories are absolute compiler paths; nil clears an inherited
10
+ // output option. Explicit excludes and files/include specifications remain in
11
+ // the authored configuration. This metadata does not certify a completed walk.
12
+ //
13
+ // @evidence contracts/common.md#principled-implementation Supported extension groups and parsed output paths retain effective native settings independently of JSON-only reconstruction.
14
+ // @evidence contracts/common.md#clear-and-simple-design Three fields express the root-admission dimensions that invocation options can change; config specification ownership stays separate.
15
+ // @evidence contracts/common.md#prohibited-implementation-shortcuts No source population or completed walk is inferred from this settings report.
16
+ // @evidence contracts/common.md#meaningful-documentation Native prose and fields distinguish possible suffixes, absolute output paths and null clearing.
17
+ // @evidence contracts/portability.md#os-neutral-implementation Native compiler paths retain their spelling; consumers compare them under observed project anchors and compiler case policy.
18
+ // @evidenceExclude contracts/performance.md#efficient-algorithms This declaration chooses no algorithm.
19
+ // @evidenceExclude contracts/performance.md#reuse-equivalent-work This metadata owns no cache or reuse verdict.
20
+ // @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The caller-owned report acquires no handle or historical state.
21
+ type TransformMembership struct {
22
+ // InputExtensions preserve possible-root suffix spellings from upstream APIs.
23
+ InputExtensions []string `json:"inputExtensions"`
24
+ // OutDir is the parsed absolute output directory, or nil when unset.
25
+ OutDir *string `json:"outDir"`
26
+ // DeclarationDir is the parsed absolute declaration directory, or nil when unset.
27
+ DeclarationDir *string `json:"declarationDir"`
28
+ }
29
+
30
+ // transformMembership projects upstream-supported suffixes and parsed paths.
31
+ // Declaration suffixes already covered by their underlying source suffix are
32
+ // redundant for possible-root admission and are omitted. Work follows extension
33
+ // text plus sorting; the returned report owns its slices and path pointers.
34
+ func transformMembership(prog *Program, groups [][]string) *TransformMembership {
35
+ extensions := map[string]struct{}{}
36
+ for _, group := range groups {
37
+ for _, extension := range group {
38
+ extensions[extension] = struct{}{}
39
+ }
40
+ }
41
+ for extension := range extensions {
42
+ if strings.HasPrefix(extension, ".d.") {
43
+ if _, covered := extensions[extension[2:]]; covered {
44
+ delete(extensions, extension)
45
+ }
46
+ }
47
+ }
48
+ out := &TransformMembership{InputExtensions: make([]string, 0, len(extensions))}
49
+ for extension := range extensions {
50
+ out.InputExtensions = append(out.InputExtensions, extension)
51
+ }
52
+ sort.Strings(out.InputExtensions)
53
+ options := prog.ParsedConfig.CompilerOptions()
54
+ if value := string(options.OutDir); value != "" {
55
+ out.OutDir = &value
56
+ }
57
+ if value := string(options.DeclarationDir); value != "" {
58
+ out.DeclarationDir = &value
59
+ }
60
+ return out
61
+ }
@@ -29,7 +29,7 @@ export declare function appendBuildOutput(left: TtscBuildResult, right: TtscBuil
29
29
  /**
30
30
  * Preserve each phase's source closure and refuse contradictory witnesses.
31
31
  *
32
- * @evidence contracts/common.md#principled-implementation Unioned memberships retain every producer's source authority; conflicting content, physical and predicate observations leave an explicit proof failure instead of selecting a later witness.
32
+ * @evidence contracts/common.md#principled-implementation Own dictionary entries union every producer's source authority without admitting inherited properties; conflicting content, physical and predicate observations leave an explicit proof failure instead of selecting a later witness. A private null-prototype failure accumulator preserves reserved keys and projects to the established ordinary public object.
33
33
  * @evidence contracts/common.md#clear-and-simple-design One graph combiner serves check composition and the later transform phase without reading inputs or creating a Program.
34
34
  * @evidence contracts/common.md#prohibited-implementation-shortcuts Reported omissions remain omissions; incompatible witnesses cannot be replaced by current filesystem observations.
35
35
  * @evidence contracts/common.md#meaningful-documentation The native description states phase composition and the unresolved-witness behavior without claiming execution acceptance.