dsh-ptc-plus 0.3.2 → 0.4.1

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 (199) hide show
  1. package/README.en.md +65 -78
  2. package/README.md +65 -78
  3. package/assets/ptc-plus-bindings-en.png +0 -0
  4. package/assets/ptc-plus-bindings-zh.png +0 -0
  5. package/assets/ptc-plus-repair-en.png +0 -0
  6. package/assets/ptc-plus-repair-zh.png +0 -0
  7. package/assets/ptc-plus-repl-workspace-en.png +0 -0
  8. package/assets/ptc-plus-repl-workspace-zh.png +0 -0
  9. package/assets/ptc-plus-settings-en.png +0 -0
  10. package/assets/ptc-plus-settings-zh.png +0 -0
  11. package/client.js +30596 -742
  12. package/compiler-amaro.cjs +5 -0
  13. package/compiler-amaro.cjs.map +6 -0
  14. package/compiler-asset-124f80c1bba92ff5fe79cfec6e8ad518f476de4a42514cae3919538887f510e8.txt +1 -0
  15. package/compiler-assets.json +3 -0
  16. package/compiler-core.cjs +554 -0
  17. package/compiler-core.cjs.map +6 -0
  18. package/compiler-platform.cjs +2 -0
  19. package/compiler-platform.cjs.map +6 -0
  20. package/compiler-typescript.cjs +388 -0
  21. package/compiler-typescript.cjs.map +6 -0
  22. package/docs/README.md +6 -0
  23. package/docs/adr/0004-declare-program-bindings-at-the-host.md +1 -1
  24. package/docs/adr/0006-rewrite-module-syntax-with-an-ast.md +5 -3
  25. package/docs/adr/0007-separate-worker-transport-from-session-semantics.md +1 -1
  26. package/docs/adr/0009-bound-expensive-acceptance-default.md +2 -0
  27. package/docs/adr/0010-session-log-derived-recovery-tips.md +59 -5
  28. package/docs/adr/0011-isolate-the-local-recovery-tip-provider.md +1 -1
  29. package/docs/adr/0012-preserve-mixed-destructuring-initialization.md +3 -1
  30. package/docs/adr/0013-isolate-repl-redeclaration-convenience.md +3 -1
  31. package/docs/adr/0014-persist-cell-rewrite-policy.md +6 -2
  32. package/docs/adr/0015-preserve-source-positions-through-rewrites.md +1 -1
  33. package/docs/adr/0016-archive-resolved-review-findings.md +1 -1
  34. package/docs/adr/0017-track-the-latest-dsh-public-surface.md +49 -0
  35. package/docs/adr/0018-use-versioned-fixture-for-ab-benchmark.md +3 -3
  36. package/docs/adr/0019-plugin-settings-and-kill-switch.md +5 -3
  37. package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
  38. package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +12 -0
  39. package/docs/adr/0023-global-user-bindings.md +104 -0
  40. package/docs/adr/0024-repl-console-observation.md +33 -0
  41. package/docs/adr/0025-use-versioned-logical-binding-identities.md +119 -0
  42. package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +21 -0
  43. package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +37 -0
  44. package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +103 -0
  45. package/docs/adr/0029-keep-plugin-unavailability-out-of-the-host-agent-lifecycle.md +27 -0
  46. package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +27 -0
  47. package/docs/architecture.md +73 -21
  48. package/docs/binding-review-placement-design.md +200 -0
  49. package/docs/capability-projection.md +1 -1
  50. package/docs/client-ui.md +95 -19
  51. package/docs/durability-design.md +25 -10
  52. package/docs/evaluation.md +78 -6
  53. package/docs/installation.md +41 -6
  54. package/docs/publishing.md +5 -3
  55. package/docs/runtime-reference.md +72 -25
  56. package/docs/semantic-validation.md +29 -0
  57. package/docs/stateful-binding-updates-proposal.md +755 -0
  58. package/docs/user-bindings.en.md +64 -0
  59. package/docs/user-bindings.md +64 -0
  60. package/docs/value-wire.md +3 -3
  61. package/docs/verification.md +57 -0
  62. package/index.js +369 -92
  63. package/internal/ast-traversal.js +5 -2
  64. package/internal/binding-descriptors.js +1 -0
  65. package/internal/binding-pattern-traversal.js +25 -0
  66. package/internal/binding-pattern.js +26 -29
  67. package/internal/binding-source-analysis.js +391 -0
  68. package/internal/binding-update-policy.js +66 -0
  69. package/internal/bounded-worker-round.js +56 -0
  70. package/internal/callable-definition-environment.js +42 -0
  71. package/internal/callable-source-catalog.js +129 -0
  72. package/internal/callable-source-encoding.js +47 -0
  73. package/internal/callable-source-facts.js +459 -0
  74. package/internal/callable-source-format.js +2 -0
  75. package/internal/callable-source-lowering.js +123 -0
  76. package/internal/cell-analysis-contract.js +25 -0
  77. package/internal/cell-analysis.js +240 -290
  78. package/internal/cell-completion.js +15 -0
  79. package/internal/cell-error.js +7 -0
  80. package/internal/cell-parser.js +66 -0
  81. package/internal/cell-rewriter.js +3 -677
  82. package/internal/commonjs-export-evidence.js +63 -0
  83. package/internal/commonjs-source-evidence.js +72 -0
  84. package/internal/compiler-amaro.cjs +2 -0
  85. package/internal/compiler-compression.js +12 -0
  86. package/internal/compiler-data.js +58 -0
  87. package/internal/compiler-descriptors.js +54 -0
  88. package/internal/compiler-dynamic-cache.js +48 -0
  89. package/internal/compiler-entry.js +35 -0
  90. package/internal/compiler-generated-names.js +17 -0
  91. package/internal/compiler-intrinsics.js +84 -0
  92. package/internal/compiler-module-links.js +34 -0
  93. package/internal/compiler-operations.js +61 -0
  94. package/internal/compiler-platform-factory.js +84 -0
  95. package/internal/compiler-platform.js +136 -0
  96. package/internal/compiler-record-roles.js +53 -0
  97. package/internal/compiler-region-output.js +259 -0
  98. package/internal/compiler-resource-lowering.js +103 -0
  99. package/internal/compiler-scope-facts.js +115 -0
  100. package/internal/compiler-service.js +109 -0
  101. package/internal/compiler-source-cache.js +32 -0
  102. package/internal/compiler-source-pieces.js +93 -0
  103. package/internal/compiler-source-regions.js +132 -0
  104. package/internal/compiler-storage-source.js +54 -0
  105. package/internal/compiler-text.js +15 -0
  106. package/internal/compiler-typescript.cjs +2 -0
  107. package/internal/config-spec.js +83 -27
  108. package/internal/cordis-tools-owner.js +130 -21
  109. package/internal/diagnostic.js +27 -1
  110. package/internal/direct-surface-owner.js +76 -23
  111. package/internal/dynamic-binding-evidence.js +16 -0
  112. package/internal/dynamic-environment-compiler.js +779 -0
  113. package/internal/dynamic-environment-integration.js +87 -0
  114. package/internal/dynamic-environment-runtime.js +362 -0
  115. package/internal/dynamic-native-calls.js +150 -0
  116. package/internal/dynamic-native-runtime.js +248 -0
  117. package/internal/dynamic-scope-analysis.js +90 -0
  118. package/internal/edit-transport-owner.js +27 -2
  119. package/internal/execution-seam-compat.js +193 -0
  120. package/internal/failure-reporting.js +118 -18
  121. package/internal/host-rpc.js +66 -0
  122. package/internal/isolated-worker.js +548 -0
  123. package/internal/kernel-child.js +139 -0
  124. package/internal/kernel-worker.js +904 -120
  125. package/internal/language-semantics.js +14 -0
  126. package/internal/legacy-binding-catalog.js +48 -0
  127. package/internal/legacy-cell-analysis.js +600 -0
  128. package/internal/legacy-cell-parser.js +50 -0
  129. package/internal/legacy-cell-rewriter.js +631 -0
  130. package/internal/legacy-repl-convenience.js +291 -0
  131. package/internal/managed-module-operations.js +170 -0
  132. package/internal/module-compilation.js +321 -0
  133. package/internal/module-runtime-platform.js +19 -0
  134. package/internal/module-transform-contract.js +12 -0
  135. package/internal/native-await-declarations.js +68 -0
  136. package/internal/native-root-compilation.js +54 -0
  137. package/internal/native-root-dynamic.js +67 -0
  138. package/internal/recovery-tips.js +27 -15
  139. package/internal/rejected-cell-editor.js +13 -8
  140. package/internal/repl-convenience.js +2 -289
  141. package/internal/repl-memory-projection.js +81 -12
  142. package/internal/repl-observation-interest.js +70 -0
  143. package/internal/repl-rewrite-contract.js +6 -0
  144. package/internal/repl-scope-normalizer.js +2091 -0
  145. package/internal/repl-value-observer.js +114 -0
  146. package/internal/rpc-contract.js +52 -0
  147. package/internal/runtime-bridge-owner.js +172 -28
  148. package/internal/runtime-config.js +48 -52
  149. package/internal/runtime-contexts.js +82 -10
  150. package/internal/runtime-messages.js +111 -0
  151. package/internal/session-cell-executor.js +401 -87
  152. package/internal/session-journal-recovery.js +684 -0
  153. package/internal/session-journal-schema.js +106 -0
  154. package/internal/session-journal.js +184 -654
  155. package/internal/session-log-view.js +42 -9
  156. package/internal/session-runtime.js +256 -95
  157. package/internal/session-state.js +247 -71
  158. package/internal/source-position-map.js +211 -71
  159. package/internal/stateful-module-compiler.js +154 -0
  160. package/internal/stateful-module-runtime.js +303 -0
  161. package/internal/stateful-root-compiler.js +598 -0
  162. package/internal/stateful-root-runtime.js +340 -0
  163. package/internal/typert-codec-compat.js +28 -0
  164. package/internal/typescript-transform.js +29 -0
  165. package/internal/user-binding-authoring.js +70 -0
  166. package/internal/user-binding-console-worker.js +184 -0
  167. package/internal/user-binding-console.js +148 -0
  168. package/internal/user-binding-draft-projection.js +335 -0
  169. package/internal/user-binding-model-context.js +31 -0
  170. package/internal/user-binding-runner.js +56 -0
  171. package/internal/user-bindings-owner.js +1065 -0
  172. package/internal/user-bindings-store.js +293 -0
  173. package/internal/user-bindings.js +353 -0
  174. package/internal/value-wire-schema.js +28 -0
  175. package/internal/value-wire.js +29 -20
  176. package/internal/worker-client.js +57 -67
  177. package/internal/worker-environment.js +45 -0
  178. package/internal/worker-output-relay.js +29 -0
  179. package/internal/worker-shutdown.js +14 -0
  180. package/package.json +75 -7
  181. package/src/client-activity.js +88 -53
  182. package/src/client-authoring-view.js +580 -0
  183. package/src/client-binding-review.js +230 -0
  184. package/src/client-bindings-data.js +29 -0
  185. package/src/client-catalog.js +127 -0
  186. package/src/client-code-editor.js +84 -0
  187. package/src/client-console.js +112 -0
  188. package/src/client-copy.js +370 -0
  189. package/src/client-feature-gates.js +62 -0
  190. package/src/client-host-compat.js +150 -0
  191. package/src/client-indicator-view.js +267 -0
  192. package/src/client-repl-view.js +292 -0
  193. package/src/client-rpc.js +20 -0
  194. package/src/client-settings-view.js +221 -0
  195. package/src/client-styles.js +106 -0
  196. package/src/client-tool-view.js +108 -0
  197. package/src/client-transport.js +159 -0
  198. package/src/client-workbench.js +521 -0
  199. package/src/client.js +233 -615
@@ -1,3 +1,59 @@
1
+ import { TraceMap, decodedMappings } from '@jridgewell/trace-mapping'
2
+
3
+ export const SOURCE_MAP_RUNS = Symbol.for('ptc.compiler.source-map-runs')
4
+
5
+ export class SourceMapRuns {
6
+ constructor(data) { this.data = data; this.length = data.length / 4; this[SOURCE_MAP_RUNS] = true }
7
+ at(index) {
8
+ if (index < 0) index += this.length
9
+ if (index < 0 || index >= this.length) return undefined
10
+ const offset = index * 4
11
+ return { generatedStart: this.data[offset], generatedEnd: this.data[offset + 1],
12
+ originalStart: this.data[offset + 2], originalEnd: this.data[offset + 3] }
13
+ }
14
+ *[Symbol.iterator]() { for (let index = 0; index < this.length; index++) yield this.at(index) }
15
+ }
16
+
17
+ const isSourceMap = value => Array.isArray(value) || value instanceof SourceMapRuns
18
+
19
+ const validMappingOffset = value => Number.isSafeInteger(value) && value >= 0 && value <= 0xffffffff
20
+
21
+ /** Numeric mapping runs avoid retaining an object for every copied token. */
22
+ export function createSourceMapBuilder() {
23
+ const blocks = []
24
+ const blockLength = 4096
25
+ let size = 0
26
+ // Positional writes keep the hot mapping paths from allocating a segment
27
+ // object that is immediately taken apart again.
28
+ const pushValues = (generatedStart, generatedEnd, originalStart, originalEnd) => {
29
+ if (!validMappingOffset(generatedStart) || !validMappingOffset(generatedEnd)
30
+ || !validMappingOffset(originalStart) || !validMappingOffset(originalEnd)) {
31
+ throw new RangeError('source mapping offsets must fit a source buffer')
32
+ }
33
+ if (size % blockLength === 0) blocks.push(new Uint32Array(blockLength))
34
+ const block = blocks.at(-1), offset = size % blockLength
35
+ block[offset] = generatedStart
36
+ block[offset + 1] = generatedEnd
37
+ block[offset + 2] = originalStart
38
+ block[offset + 3] = originalEnd
39
+ size += 4
40
+ }
41
+ return {
42
+ push({ generatedStart, generatedEnd, originalStart, originalEnd }) {
43
+ pushValues(generatedStart, generatedEnd, originalStart, originalEnd)
44
+ },
45
+ pushValues,
46
+ finish(compact = false) {
47
+ const data = new Uint32Array(size)
48
+ for (let index = 0; index < blocks.length; index++) {
49
+ data.set(blocks[index].subarray(0, Math.min(blockLength, size - index * blockLength)), index * blockLength)
50
+ }
51
+ const runs = new SourceMapRuns(data)
52
+ return !compact && runs.length < 4096 ? [...runs] : runs
53
+ },
54
+ }
55
+ }
56
+
1
57
  export function identitySourceMap(length) {
2
58
  return [{ generatedStart: 0, generatedEnd: length, originalStart: 0, originalEnd: length }]
3
59
  }
@@ -42,16 +98,26 @@ export function createMappedTextBuilder(source, sourceOffset = 0) {
42
98
  }
43
99
  }
44
100
 
45
- function originalOffsetAt(sourceMap, generatedOffset) {
101
+ function segmentIndexAt(sourceMap, generatedOffset) {
46
102
  let low = 0
47
103
  let high = sourceMap.length
104
+ const packed = sourceMap instanceof SourceMapRuns ? sourceMap.data : undefined
48
105
  while (low < high) {
49
106
  const middle = (low + high) >>> 1
50
- if (generatedOffset < sourceMap[middle].generatedEnd) high = middle
107
+ const end = packed === undefined ? sourceMap.at(middle).generatedEnd : packed[middle * 4 + 1]
108
+ if (generatedOffset < end) high = middle
51
109
  else low = middle + 1
52
110
  }
53
- const segment = sourceMap[low] ?? sourceMap.at(-1)
111
+ return low
112
+ }
113
+
114
+ export function sourceOffsetAt(sourceMap, generatedOffset) {
115
+ const segment = sourceMap.at(segmentIndexAt(sourceMap, generatedOffset)) ?? sourceMap.at(-1)
54
116
  if (segment === undefined) return generatedOffset
117
+ return segmentOffsetAt(segment, generatedOffset)
118
+ }
119
+
120
+ function segmentOffsetAt(segment, generatedOffset) {
55
121
  const generatedLength = segment.generatedEnd - segment.generatedStart
56
122
  const originalLength = segment.originalEnd - segment.originalStart
57
123
  const localOffset = Math.max(0, generatedOffset - segment.generatedStart)
@@ -62,51 +128,64 @@ function originalOffsetAt(sourceMap, generatedOffset) {
62
128
  + Math.floor((localOffset * (originalLength - 1)) / (generatedLength - 1))
63
129
  }
64
130
 
131
+ /** A copied interval is already bounded by this segment. Its endpoints need
132
+ * no additional search through the complete source map. */
133
+ function copiedSegmentOriginal(segment, start, end) {
134
+ return [segmentOffsetAt(segment, start),
135
+ segmentOffsetAt(segment, end - 1) + (segment.originalEnd > segment.originalStart ? 1 : 0)]
136
+ }
137
+
65
138
  function mappedOffsetRange(sourceMap, start, end) {
139
+ const lastOffset = Math.max(start, end - 1)
140
+ const last = sourceMap.at(segmentIndexAt(sourceMap, lastOffset)) ?? sourceMap.at(-1)
141
+ const hasSourceCharacter = last === undefined || lastOffset < last.generatedEnd && last.originalEnd > last.originalStart
142
+ const originalLast = last === undefined ? lastOffset : segmentOffsetAt(last, lastOffset)
66
143
  return {
67
- originalStart: originalOffsetAt(sourceMap, start),
68
- originalEnd: originalOffsetAt(sourceMap, Math.max(start, end - 1)) + (end > start ? 1 : 0),
144
+ originalStart: start === lastOffset ? originalLast : sourceOffsetAt(sourceMap, start),
145
+ originalEnd: originalLast + (end > start && hasSourceCharacter ? 1 : 0),
69
146
  }
70
147
  }
71
148
 
72
- function replacementSegments(sourceMap, start, end, replacementLength, mappings) {
149
+ /** Replacement runs reach the mapping builder as positional writes. The
150
+ * generator this replaced allocated a segment object for every emitted run. */
151
+ function emitReplacementSegments(sourceMap, start, end, replacementLength, mappings, emit) {
73
152
  const { originalStart: startOriginal, originalEnd: endOriginal } = mappedOffsetRange(sourceMap, start, end)
74
- if (!Array.isArray(mappings) || mappings.length === 0) {
75
- return replacementLength === 0 ? [] : [{
76
- generatedStart: start,
77
- generatedEnd: start + replacementLength,
78
- originalStart: startOriginal,
79
- originalEnd: endOriginal,
80
- }]
81
- }
82
- const segments = []
153
+ if (!isSourceMap(mappings) || mappings.length === 0) {
154
+ if (replacementLength !== 0) emit(start, start + replacementLength, startOriginal, endOriginal)
155
+ return
156
+ }
83
157
  let cursor = 0
158
+ let anchorOffset = start
84
159
  for (const mapping of mappings) {
85
160
  if (mapping.generatedStart > cursor) {
86
- segments.push({
87
- generatedStart: start + cursor,
88
- generatedEnd: start + mapping.generatedStart,
89
- originalStart: startOriginal,
90
- originalEnd: endOriginal,
91
- })
161
+ const nextAnchor = sourceOffsetAt(sourceMap, mapping.originalStart)
162
+ emit(start + cursor, start + mapping.generatedStart, nextAnchor, nextAnchor)
163
+ }
164
+ if (mapping.generatedEnd - mapping.generatedStart === mapping.originalEnd - mapping.originalStart
165
+ && mapping.originalEnd > mapping.originalStart) {
166
+ // A verbatim copy retains every previous boundary, including inserted
167
+ // compiler text. Collapsing its endpoints would interpolate that text
168
+ // into source positions and corrupt later call/write provenance.
169
+ for (let index = segmentIndexAt(sourceMap, mapping.originalStart); index < sourceMap.length; index++) {
170
+ const segment = sourceMap.at(index)
171
+ if (segment.generatedStart >= mapping.originalEnd) break
172
+ const from = Math.max(mapping.originalStart, segment.generatedStart)
173
+ const to = Math.min(mapping.originalEnd, segment.generatedEnd)
174
+ const [originalStart, originalEnd] = copiedSegmentOriginal(segment, from, to)
175
+ emit(start + mapping.generatedStart + from - mapping.originalStart,
176
+ start + mapping.generatedStart + to - mapping.originalStart, originalStart, originalEnd)
177
+ }
178
+ } else {
179
+ const { originalStart, originalEnd } = mappedOffsetRange(sourceMap, mapping.originalStart, mapping.originalEnd)
180
+ emit(start + mapping.generatedStart, start + mapping.generatedEnd, originalStart, originalEnd)
92
181
  }
93
- const original = mappedOffsetRange(sourceMap, mapping.originalStart, mapping.originalEnd)
94
- segments.push({
95
- generatedStart: start + mapping.generatedStart,
96
- generatedEnd: start + mapping.generatedEnd,
97
- ...original,
98
- })
99
182
  cursor = mapping.generatedEnd
183
+ anchorOffset = mapping.originalEnd
100
184
  }
101
185
  if (cursor < replacementLength) {
102
- segments.push({
103
- generatedStart: start + cursor,
104
- generatedEnd: start + replacementLength,
105
- originalStart: startOriginal,
106
- originalEnd: endOriginal,
107
- })
186
+ const anchor = sourceOffsetAt(sourceMap, anchorOffset)
187
+ emit(start + cursor, start + replacementLength, anchor, anchor)
108
188
  }
109
- return segments
110
189
  }
111
190
 
112
191
  export function applySourceEdits(code, sourceMap, edits) {
@@ -120,12 +199,12 @@ export function applySourceEdits(code, sourceMap, edits) {
120
199
  if (!Number.isSafeInteger(item.start) || !Number.isSafeInteger(item.end)
121
200
  || item.start < 0 || item.end < item.start || item.end > code.length
122
201
  || item.start < previousEnd || typeof text !== 'string'
123
- || (item.mappings !== undefined && !Array.isArray(item.mappings))
202
+ || (item.mappings !== undefined && !isSourceMap(item.mappings))
124
203
  || (item.start === item.end && previous?.start === item.start && previous.end === item.end)) {
125
204
  throw new RangeError('source edits must be bounded, ordered, and non-overlapping')
126
205
  }
127
- const itemMappings = [...(item.mappings ?? [])]
128
- .sort((left, right) => left.generatedStart - right.generatedStart)
206
+ const itemMappings = item.mappings instanceof SourceMapRuns ? item.mappings
207
+ : [...(item.mappings ?? [])].sort((left, right) => left.generatedStart - right.generatedStart)
129
208
  let mappingEnd = 0
130
209
  for (const mapping of itemMappings) {
131
210
  if (!Number.isSafeInteger(mapping.generatedStart) || !Number.isSafeInteger(mapping.generatedEnd)
@@ -143,27 +222,23 @@ export function applySourceEdits(code, sourceMap, edits) {
143
222
  }
144
223
 
145
224
  const chunks = []
146
- const mappings = []
225
+ const mappings = createSourceMapBuilder()
147
226
  let generatedOffset = 0
148
227
  let sourceOffset = 0
149
228
  let segmentIndex = 0
150
229
  const appendUnchanged = end => {
151
230
  if (end <= sourceOffset) return
152
231
  chunks.push(code.slice(sourceOffset, end))
153
- while (segmentIndex < sourceMap.length && sourceMap[segmentIndex].generatedEnd <= sourceOffset) segmentIndex += 1
232
+ while (segmentIndex < sourceMap.length && sourceMap.at(segmentIndex).generatedEnd <= sourceOffset) segmentIndex += 1
154
233
  for (let index = segmentIndex; index < sourceMap.length; index += 1) {
155
- const segment = sourceMap[index]
234
+ const segment = sourceMap.at(index)
156
235
  if (segment.generatedStart >= end) break
157
236
  const start = Math.max(sourceOffset, segment.generatedStart)
158
237
  const stop = Math.min(end, segment.generatedEnd)
159
238
  if (stop <= start) continue
160
- const { originalStart, originalEnd } = mappedOffsetRange(sourceMap, start, stop)
161
- mappings.push({
162
- generatedStart: generatedOffset + start - sourceOffset,
163
- generatedEnd: generatedOffset + stop - sourceOffset,
164
- originalStart,
165
- originalEnd,
166
- })
239
+ const [originalStart, originalEnd] = copiedSegmentOriginal(segment, start, stop)
240
+ mappings.pushValues(generatedOffset + start - sourceOffset, generatedOffset + stop - sourceOffset,
241
+ originalStart, originalEnd)
167
242
  }
168
243
  generatedOffset += end - sourceOffset
169
244
  sourceOffset = end
@@ -173,52 +248,117 @@ export function applySourceEdits(code, sourceMap, edits) {
173
248
  appendUnchanged(item.start)
174
249
  const { text } = item
175
250
  chunks.push(text)
176
- mappings.push(...replacementSegments(sourceMap, item.start, item.end, text.length, item.mappings)
177
- .map(segment => ({ ...segment, generatedStart: segment.generatedStart - item.start + generatedOffset,
178
- generatedEnd: segment.generatedEnd - item.start + generatedOffset })))
251
+ emitReplacementSegments(sourceMap, item.start, item.end, text.length, item.mappings,
252
+ (generatedStart, generatedEnd, originalStart, originalEnd) => mappings.pushValues(
253
+ generatedStart - item.start + generatedOffset, generatedEnd - item.start + generatedOffset,
254
+ originalStart, originalEnd))
179
255
  generatedOffset += text.length
180
256
  sourceOffset = item.end
181
257
  }
182
258
  appendUnchanged(code.length)
183
- return { code: chunks.join(''), sourceMap: mappings }
259
+ return { code: chunks.join(''), sourceMap: mappings.finish() }
260
+ }
261
+
262
+ const LINE_TERMINATORS = /\r\n|[\n\r\u2028\u2029]/gu
263
+
264
+ /** JavaScript source coordinates count CRLF as one line boundary. */
265
+ export function sourceLineStarts(source) {
266
+ const starts = [0]
267
+ for (const match of source.matchAll(LINE_TERMINATORS)) starts.push(match.index + match[0].length)
268
+ return starts
269
+ }
270
+
271
+ /** Visit maintained emitter coordinates without re-encoding available raw maps.
272
+ * All line numbers at this boundary are zero-based; absent origins stay absent. */
273
+ export function visitSourceMappings(transformed, visit) {
274
+ const raw = transformed.rawMappings
275
+ if (raw !== undefined) {
276
+ for (const item of raw) visit(item.generated.line - 1, item.generated.column,
277
+ item.original === undefined ? undefined : item.original.line - 1, item.original?.column)
278
+ return
279
+ }
280
+ const nativeMap = new TraceMap(typeof transformed.map === 'string' ? JSON.parse(transformed.map) : transformed.map)
281
+ for (const [line, entries] of decodedMappings(nativeMap).entries()) {
282
+ for (const entry of entries) visit(line, entry[0], entry[2], entry[3])
283
+ }
284
+ }
285
+
286
+ /** Compose a parser's line/column map with the existing source-offset map. */
287
+ export function mappedSourceTransform(code, sourceMap, transformed) {
288
+ const starts = sourceLineStarts(code)
289
+ const generatedStarts = sourceLineStarts(transformed.code)
290
+ const mappings = createSourceMapBuilder()
291
+ let previous
292
+ visitSourceMappings(transformed, (line, column, originalLine, originalColumn) => {
293
+ const generatedStart = Math.min(transformed.code.length, generatedStarts[line] + column)
294
+ if (previous !== undefined) {
295
+ previous.generatedEnd = generatedStart
296
+ mappings.push(previous)
297
+ }
298
+ if (starts[originalLine] === undefined) {
299
+ previous = undefined
300
+ return
301
+ }
302
+ const originalStart = Math.min(code.length, starts[originalLine] + originalColumn)
303
+ previous = { generatedStart, generatedEnd: transformed.code.length,
304
+ originalStart, originalEnd: Math.min(code.length, originalStart + 1) }
305
+ })
306
+ if (previous !== undefined) mappings.push(previous)
307
+ return applySourceEdits(code, sourceMap, [{ start: 0, end: code.length, text: transformed.code, mappings: mappings.finish(true) }])
308
+ }
309
+
310
+ function lineStartOffset(source, line, starts) {
311
+ return starts[line - 1] ?? source.length
312
+ }
313
+
314
+ function positionAtOffset(source, offset, starts) {
315
+ let low = 0, high = starts.length
316
+ while (low < high) {
317
+ const middle = (low + high) >>> 1
318
+ if (starts[middle] <= offset) low = middle + 1
319
+ else high = middle
320
+ }
321
+ const line = low - 1
322
+ return { line: line + 1, column: offset - starts[line] + 1 }
184
323
  }
185
324
 
186
- function lineStartOffset(source, line) {
187
- let offset = 0
188
- for (let current = 1; current < line; current += 1) {
189
- const newline = source.indexOf('\n', offset)
190
- if (newline < 0) return source.length
191
- offset = newline + 1
325
+ const coordinateIndexes = new WeakMap()
326
+ function coordinateIndex(sourceMap, generatedSource, originalSource) {
327
+ let index = coordinateIndexes.get(sourceMap)
328
+ if (index?.generatedSource !== generatedSource || index.originalSource !== originalSource) {
329
+ index = { generatedSource, originalSource, generatedStarts: sourceLineStarts(generatedSource),
330
+ originalStarts: sourceLineStarts(originalSource) }
331
+ coordinateIndexes.set(sourceMap, index)
192
332
  }
193
- return offset
333
+ return index
194
334
  }
195
335
 
196
- function positionAtOffset(source, offset) {
197
- const prefix = source.slice(0, offset)
198
- const line = prefix.split(/\r?\n/).length
199
- const lastBreak = prefix.lastIndexOf('\n')
200
- return { line, column: offset - lastBreak }
336
+ export function sourceTextAtSpan(source, span, starts = sourceLineStarts(source)) {
337
+ return source.slice(lineStartOffset(source, span.line, starts) + span.column - 1,
338
+ lineStartOffset(source, span.end.line, starts) + span.end.column - 1)
201
339
  }
202
340
 
203
- export function mapSourcePosition(position, generatedSource, originalSource, sourceMap) {
341
+ export function mapSourcePosition(position, generatedSource, originalSource, sourceMap, coordinates) {
204
342
  if (position === undefined || sourceMap === undefined) return position
205
343
  if (!Number.isSafeInteger(position.line) || position.line < 1
206
344
  || !Number.isSafeInteger(position.column) || position.column < 1) return position
207
- const lineStart = lineStartOffset(generatedSource, position.line)
208
- const mappedOffset = originalOffsetAt(sourceMap, lineStart + position.column - 1)
209
- return positionAtOffset(originalSource, mappedOffset)
345
+ const index = coordinates ?? coordinateIndex(sourceMap, generatedSource, originalSource)
346
+ const lineStart = lineStartOffset(generatedSource, position.line, index.generatedStarts)
347
+ const mappedOffset = sourceOffsetAt(sourceMap, lineStart + position.column - 1)
348
+ return positionAtOffset(originalSource, mappedOffset, index.originalStarts)
210
349
  }
211
350
 
212
351
  /** Map a complete generated-source span into original cell coordinates. */
213
- export function mapSourceSpan(span, generatedSource, originalSource, sourceMap) {
352
+ export function mapSourceSpan(span, generatedSource, originalSource, sourceMap, coordinates) {
214
353
  if (span === undefined) return undefined
215
- const start = mapSourcePosition(span, generatedSource, originalSource, sourceMap)
354
+ const start = mapSourcePosition(span, generatedSource, originalSource, sourceMap, coordinates)
216
355
  if (span.end === undefined) return start === span ? span : start
217
356
  if (!Number.isSafeInteger(span.end.line) || span.end.line < 1
218
357
  || !Number.isSafeInteger(span.end.column) || span.end.column < 1) {
219
358
  return { ...start, end: span.end }
220
359
  }
221
- const generatedEnd = lineStartOffset(generatedSource, span.end.line) + span.end.column - 1
360
+ const index = coordinates ?? coordinateIndex(sourceMap, generatedSource, originalSource)
361
+ const generatedEnd = lineStartOffset(generatedSource, span.end.line, index.generatedStarts) + span.end.column - 1
222
362
  const original = mappedOffsetRange(sourceMap, Math.max(0, generatedEnd - 1), generatedEnd)
223
- return { ...start, end: positionAtOffset(originalSource, original.originalEnd) }
363
+ return { ...start, end: positionAtOffset(originalSource, original.originalEnd, index.originalStarts) }
224
364
  }
@@ -0,0 +1,154 @@
1
+ import { compilerModuleReference } from './compiler-module-links.js'
2
+ import { readFileSync } from 'node:fs'
3
+ import { createRequire, findPackageJSON } from 'node:module'
4
+ import { basename, extname } from 'node:path'
5
+ import { compileModuleSource, detectModuleSourceFormat, attachNamespaceSource,
6
+ linkCommonJsEvidenceSource } from './compiler-service.js'
7
+ import { managedModuleAdapter, loadManagedModuleAdapter, recordStatefulModuleResolution, reserveStatefulModule,
8
+ statefulModuleLink, resolveStatefulModuleLink } from './stateful-module-runtime.js'
9
+ import { createCommonJsEvidence } from './commonjs-export-evidence.js'
10
+ import { USER_BINDING_TRANSFORM } from './module-transform-contract.js'
11
+ import { createDynamicEnvironmentRuntime } from './dynamic-environment-runtime.js'
12
+ import { moduleRuntimeIntrinsics } from './compiler-intrinsics.js'
13
+ import { compilerPlatformBridge } from './compiler-platform.js'
14
+
15
+ const { Map, Set, Object, Reflect, mapGet, mapHas, mapSet, setHas, setAdd, includes, startsWith,
16
+ replaceAllString, includesString } = moduleRuntimeIntrinsics
17
+ const readSourceFile = readFileSync, nativeCreateRequire = createRequire, packageJsonPath = findPackageJSON
18
+ const pathBasename = basename, pathExtension = extname, parseJson = JSON.parse
19
+ const bufferFrom = Buffer.from, bufferToString = Buffer.prototype.toString
20
+ const sourceText = source => typeof source === 'string' ? source
21
+ : Reflect.apply(bufferToString, bufferFrom(source), ['utf8'])
22
+ const sourcePath = compilerPlatformBridge.fileURLToPath
23
+
24
+ // Dependencies installed under a package's node_modules belong to Node's
25
+ // module owner. Compiling them as if they were session source needlessly
26
+ // parses and retains large third-party programs (for example TypeScript),
27
+ // while providing no PTC binding continuity: only session-owned modules can
28
+ // publish logical bindings. Namespace adaptation still wraps their native
29
+ // result at the managed module boundary.
30
+ function isExternalDependency(url) {
31
+ if (!startsWith(url, 'file:')) return false
32
+ const path = replaceAllString(sourcePath(url), '\\', '/')
33
+ return includesString(path, '/node_modules/')
34
+ }
35
+
36
+ /** Source generation returns link facts; the runtime owns registration. */
37
+ export function compileStatefulModule(source, options) {
38
+ const prepared = compileModuleSource(source, options)
39
+ for (let index = 0; index < prepared.staticLinks.length; index++) {
40
+ const link = prepared.staticLinks[index]
41
+ statefulModuleLink(link.source, link.reference)
42
+ }
43
+ // Install reflection facts before Node can expose hoisted exports to a
44
+ // cyclic importer. Catalog data never enters the user's executable source.
45
+ if (prepared.callableSources?.length > 0) {
46
+ createDynamicEnvironmentRuntime().installIntrinsics().registerSources(prepared.callableSources)
47
+ }
48
+ return prepared
49
+ }
50
+
51
+ function commonJsLoadFormat(url, source) {
52
+ if (!startsWith(url, 'file:')) return undefined
53
+ const extension = pathExtension(sourcePath(url))
54
+ if (extension === '.mjs' || extension === '.mts') return 'module'
55
+ if (extension === '.cjs' || extension === '.cts') return 'commonjs'
56
+ if (extension !== '.js' && extension !== '.ts' && extension !== '') return undefined
57
+ const packagePath = packageJsonPath(url)
58
+ if (packagePath !== undefined && pathBasename(packagePath) === 'package.json') {
59
+ const type = parseJson(readSourceFile(packagePath, 'utf8')).type
60
+ if (type === 'module' || type === 'commonjs') return type
61
+ }
62
+ return detectModuleSourceFormat(source, { extension })
63
+ }
64
+
65
+ let nextNamespaceReference = 0
66
+ function attachModuleNamespace(url, source, attributes, references, sourceRegions) {
67
+ const reference = compilerModuleReference('self', ++nextNamespaceReference)
68
+ mapSet(references, reference, url)
69
+ const text = sourceText(source)
70
+ return attachNamespaceSource(text, attributes, reference, sourceRegions)
71
+ }
72
+
73
+ /** Track execution-owned module graphs without changing Node's URL identity. */
74
+ export function createUserModuleCompilationHooks({ transformForParent = () => undefined } = {}) {
75
+ const modules = new Map()
76
+ const compiled = new Set()
77
+ const loaded = new Set()
78
+ const interfaces = new Map()
79
+ const namespaceReferences = new Map()
80
+ const sourceRegionPlans = new Map()
81
+ const commonJsSources = new Map()
82
+ const commonJsEvidence = createCommonJsEvidence({ linkSource: linkCommonJsEvidenceSource })
83
+ const bootstrapFiles = new Set()
84
+ const bootstrapNames = Object.keys(nativeCreateRequire(import.meta.url).cache)
85
+ for (let index = 0; index < bootstrapNames.length; index++) setAdd(bootstrapFiles, bootstrapNames[index])
86
+ const internalRoot = compilerPlatformBridge.url('./', import.meta.url)
87
+ const mark = (url, { transform = USER_BINDING_TRANSFORM, compiled: prepared = false, moduleInterface, commonJsSource, sourceRegions } = {}) => {
88
+ if (!setHas(compiled, url) && !setHas(loaded, url)) mapSet(modules, url, transform)
89
+ if (prepared) {
90
+ setAdd(compiled, url)
91
+ mapSet(sourceRegionPlans, url, sourceRegions)
92
+ if (commonJsSource !== undefined) mapSet(commonJsSources, url, commonJsSource)
93
+ if (moduleInterface !== undefined) {
94
+ mapSet(interfaces, url, moduleInterface)
95
+ reserveStatefulModule(url, moduleInterface)
96
+ }
97
+ }
98
+ }
99
+ const recordResolution = (parentUrl, specifier, importAttributes, result) => {
100
+ recordStatefulModuleResolution(parentUrl, specifier, importAttributes, result.url)
101
+ const transform = mapGet(modules, parentUrl) ?? transformForParent(parentUrl)
102
+ // Runtime links are outside the user compilation graph. Skipping only
103
+ // their load would still propagate user transforms to their dependencies.
104
+ if (transform !== undefined && !startsWith(result.url, 'node:')
105
+ && !startsWith(result.url, internalRoot) && !isExternalDependency(result.url)) mark(result.url, { transform })
106
+ }
107
+ return {
108
+ mark,
109
+ resolve(specifier, context, nextResolve) {
110
+ const evidence = commonJsEvidence.resolve(specifier, context, recordResolution)
111
+ if (evidence !== undefined) return evidence
112
+ const link = resolveStatefulModuleLink(specifier, context)
113
+ if (link !== undefined) return link
114
+ const namespaceUrl = mapGet(namespaceReferences, specifier)
115
+ if (namespaceUrl !== undefined) return { url: namespaceUrl, shortCircuit: true }
116
+ const parentUrl = context.parentURL
117
+ const parentAdapter = managedModuleAdapter(parentUrl)
118
+ if (parentAdapter?.kind === 'module' && specifier === parentAdapter.source) {
119
+ return { ...parentAdapter.resolution, shortCircuit: true }
120
+ }
121
+ const importAttributes = { ...context.importAttributes }
122
+ const result = nextResolve(specifier, context)
123
+ recordResolution(parentUrl, specifier, importAttributes, result)
124
+ return result
125
+ },
126
+ load(url, context, nextLoad) {
127
+ const evidence = commonJsEvidence.load(url)
128
+ if (evidence !== undefined) return evidence
129
+ const attributes = { ...context.importAttributes }
130
+ const adapter = loadManagedModuleAdapter(url, attributes)
131
+ if (adapter !== undefined) return adapter
132
+ const result = nextLoad(url, context)
133
+ const transform = mapGet(modules, url)
134
+ if (transform === undefined
135
+ || startsWith(url, internalRoot)
136
+ || startsWith(url, 'file:') && setHas(bootstrapFiles, sourcePath(url))) return result
137
+ if (setHas(compiled, url)) return mapHas(interfaces, url)
138
+ ? { ...result, source: attachModuleNamespace(url, result.source, attributes, namespaceReferences, mapGet(sourceRegionPlans, url)) }
139
+ : mapHas(commonJsSources, url) ? { ...result, source: commonJsEvidence.attach(url, result.source, mapGet(commonJsSources, url)) } : result
140
+ const formats = ['module', 'commonjs', 'module-typescript', 'commonjs-typescript']
141
+ if (result.format !== undefined && result.format !== 'typescript' && !includes(formats, result.format)) return result
142
+ const source = result.source ?? readSourceFile(sourcePath(url))
143
+ const text = sourceText(source)
144
+ const format = result.format === undefined || result.format === 'typescript' ? commonJsLoadFormat(url, text) : result.format
145
+ if (!includes(formats, format)) return result
146
+ const target = startsWith(format, 'commonjs') ? 'commonjs' : 'module'
147
+ const prepared = compileStatefulModule(text, { transform, target, url })
148
+ setAdd(loaded, url)
149
+ if (target === 'module') reserveStatefulModule(url, prepared.moduleInterface)
150
+ return { ...result, format: target, source: target === 'module' ? attachModuleNamespace(url, prepared.code, attributes, namespaceReferences, prepared.sourceRegions)
151
+ : commonJsEvidence.attach(url, prepared.code, prepared.commonJsSource) }
152
+ },
153
+ }
154
+ }