dsh-ptc-plus 0.3.2 → 0.4.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.
Files changed (206) hide show
  1. package/README.en.md +63 -80
  2. package/README.md +63 -80
  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 +30827 -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 +556 -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 +7 -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 +77 -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 +28 -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 +76 -25
  56. package/docs/semantic-validation.md +29 -0
  57. package/docs/stateful-binding-updates-proposal.md +759 -0
  58. package/docs/user-bindings.en.md +66 -0
  59. package/docs/user-bindings.md +66 -0
  60. package/docs/value-wire.md +3 -3
  61. package/docs/verification-optimization-work.md +41 -0
  62. package/docs/verification.md +71 -0
  63. package/index.js +369 -92
  64. package/internal/ast-traversal.js +5 -2
  65. package/internal/async-local-storage-intrinsics.js +38 -0
  66. package/internal/binding-declaration-contract.js +8 -0
  67. package/internal/binding-descriptors.js +1 -0
  68. package/internal/binding-pattern-traversal.js +25 -0
  69. package/internal/binding-pattern.js +26 -29
  70. package/internal/binding-source-analysis.js +708 -0
  71. package/internal/binding-update-policy.js +72 -0
  72. package/internal/bounded-worker-round.js +56 -0
  73. package/internal/callable-definition-environment.js +42 -0
  74. package/internal/callable-source-catalog.js +129 -0
  75. package/internal/callable-source-encoding.js +47 -0
  76. package/internal/callable-source-facts.js +459 -0
  77. package/internal/callable-source-format.js +2 -0
  78. package/internal/callable-source-lowering.js +123 -0
  79. package/internal/cell-analysis-contract.js +25 -0
  80. package/internal/cell-analysis.js +253 -290
  81. package/internal/cell-completion.js +15 -0
  82. package/internal/cell-error.js +7 -0
  83. package/internal/cell-parser.js +66 -0
  84. package/internal/cell-rewriter.js +3 -677
  85. package/internal/commonjs-export-evidence.js +63 -0
  86. package/internal/commonjs-source-evidence.js +72 -0
  87. package/internal/compiler-amaro.cjs +2 -0
  88. package/internal/compiler-compression.js +12 -0
  89. package/internal/compiler-data.js +58 -0
  90. package/internal/compiler-descriptors.js +54 -0
  91. package/internal/compiler-dynamic-cache.js +48 -0
  92. package/internal/compiler-entry.js +35 -0
  93. package/internal/compiler-generated-names.js +17 -0
  94. package/internal/compiler-intrinsics.js +50 -0
  95. package/internal/compiler-module-links.js +37 -0
  96. package/internal/compiler-operations.js +61 -0
  97. package/internal/compiler-platform-factory.js +84 -0
  98. package/internal/compiler-platform.js +136 -0
  99. package/internal/compiler-record-roles.js +53 -0
  100. package/internal/compiler-region-output.js +259 -0
  101. package/internal/compiler-resource-lowering.js +103 -0
  102. package/internal/compiler-scope-facts.js +115 -0
  103. package/internal/compiler-service.js +109 -0
  104. package/internal/compiler-source-cache.js +32 -0
  105. package/internal/compiler-source-pieces.js +93 -0
  106. package/internal/compiler-source-regions.js +132 -0
  107. package/internal/compiler-storage-source.js +54 -0
  108. package/internal/compiler-text.js +15 -0
  109. package/internal/compiler-typescript.cjs +2 -0
  110. package/internal/config-spec.js +83 -27
  111. package/internal/cordis-tools-owner.js +130 -21
  112. package/internal/diagnostic.js +27 -1
  113. package/internal/direct-surface-owner.js +76 -23
  114. package/internal/dynamic-binding-evidence.js +16 -0
  115. package/internal/dynamic-environment-compiler.js +779 -0
  116. package/internal/dynamic-environment-integration.js +91 -0
  117. package/internal/dynamic-environment-runtime.js +383 -0
  118. package/internal/dynamic-native-calls.js +153 -0
  119. package/internal/dynamic-native-runtime.js +262 -0
  120. package/internal/dynamic-scope-analysis.js +90 -0
  121. package/internal/edit-transport-owner.js +27 -2
  122. package/internal/execution-seam-compat.js +193 -0
  123. package/internal/failure-reporting.js +136 -28
  124. package/internal/host-rpc.js +66 -0
  125. package/internal/isolated-worker.js +555 -0
  126. package/internal/kernel-child.js +139 -0
  127. package/internal/kernel-worker.js +1179 -233
  128. package/internal/language-semantics.js +14 -0
  129. package/internal/legacy-binding-catalog.js +48 -0
  130. package/internal/legacy-cell-analysis.js +600 -0
  131. package/internal/legacy-cell-parser.js +50 -0
  132. package/internal/legacy-cell-rewriter.js +631 -0
  133. package/internal/legacy-repl-convenience.js +291 -0
  134. package/internal/managed-module-operations.js +170 -0
  135. package/internal/module-compilation.js +322 -0
  136. package/internal/module-runtime-platform.js +19 -0
  137. package/internal/module-transform-contract.js +42 -0
  138. package/internal/native-await-declarations.js +68 -0
  139. package/internal/native-root-compilation.js +54 -0
  140. package/internal/native-root-dynamic.js +67 -0
  141. package/internal/record-utils.js +24 -15
  142. package/internal/recovery-tips.js +27 -15
  143. package/internal/rejected-cell-editor.js +13 -8
  144. package/internal/repl-convenience.js +2 -289
  145. package/internal/repl-memory-projection.js +81 -12
  146. package/internal/repl-observation-interest.js +70 -0
  147. package/internal/repl-rewrite-contract.js +6 -0
  148. package/internal/repl-scope-normalizer.js +2091 -0
  149. package/internal/repl-value-observer.js +117 -0
  150. package/internal/rpc-contract.js +52 -0
  151. package/internal/runtime-bridge-owner.js +172 -28
  152. package/internal/runtime-config.js +48 -52
  153. package/internal/runtime-contexts.js +82 -10
  154. package/internal/runtime-intrinsics.js +101 -0
  155. package/internal/runtime-messages.js +111 -0
  156. package/internal/session-cell-executor.js +402 -87
  157. package/internal/session-journal-recovery.js +684 -0
  158. package/internal/session-journal-schema.js +123 -0
  159. package/internal/session-journal.js +199 -654
  160. package/internal/session-log-view.js +42 -9
  161. package/internal/session-runtime.js +257 -95
  162. package/internal/session-state.js +247 -71
  163. package/internal/source-position-map.js +230 -71
  164. package/internal/stateful-module-compiler.js +154 -0
  165. package/internal/stateful-module-runtime.js +303 -0
  166. package/internal/stateful-root-compiler.js +600 -0
  167. package/internal/stateful-root-runtime.js +340 -0
  168. package/internal/typert-codec-compat.js +28 -0
  169. package/internal/typescript-transform.js +31 -0
  170. package/internal/user-binding-authoring.js +70 -0
  171. package/internal/user-binding-console-worker.js +244 -0
  172. package/internal/user-binding-console.js +148 -0
  173. package/internal/user-binding-draft-projection.js +335 -0
  174. package/internal/user-binding-model-context.js +31 -0
  175. package/internal/user-binding-runner.js +56 -0
  176. package/internal/user-bindings-owner.js +1065 -0
  177. package/internal/user-bindings-store.js +293 -0
  178. package/internal/user-bindings.js +361 -0
  179. package/internal/value-wire-schema.js +28 -0
  180. package/internal/value-wire.js +130 -91
  181. package/internal/worker-client.js +57 -67
  182. package/internal/worker-cwd-virtualization.js +80 -53
  183. package/internal/worker-environment.js +65 -0
  184. package/internal/worker-output-relay.js +29 -0
  185. package/internal/worker-repl-realm.js +134 -0
  186. package/internal/worker-shutdown.js +14 -0
  187. package/package.json +76 -7
  188. package/src/client-activity.js +95 -53
  189. package/src/client-authoring-view.js +580 -0
  190. package/src/client-binding-review.js +230 -0
  191. package/src/client-bindings-data.js +29 -0
  192. package/src/client-catalog.js +127 -0
  193. package/src/client-code-editor.js +84 -0
  194. package/src/client-console.js +112 -0
  195. package/src/client-copy.js +370 -0
  196. package/src/client-feature-gates.js +62 -0
  197. package/src/client-host-compat.js +150 -0
  198. package/src/client-indicator-view.js +267 -0
  199. package/src/client-repl-view.js +292 -0
  200. package/src/client-rpc.js +20 -0
  201. package/src/client-settings-view.js +221 -0
  202. package/src/client-styles.js +106 -0
  203. package/src/client-tool-view.js +108 -0
  204. package/src/client-transport.js +159 -0
  205. package/src/client-workbench.js +521 -0
  206. 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,45 @@ 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
+ /** Whether a complete generated interval still contains its exact original text. */
121
+ export function sourceRangeHasOriginalText(sourceMap, generatedSource, originalSource, generatedStart, generatedEnd) {
122
+ if (typeof generatedSource !== 'string' || typeof originalSource !== 'string'
123
+ || generatedEnd <= generatedStart || generatedStart < 0 || generatedEnd > generatedSource.length) return false
124
+ let cursor = generatedStart
125
+ for (let index = segmentIndexAt(sourceMap, generatedStart); index < sourceMap.length && cursor < generatedEnd; index += 1) {
126
+ const segment = sourceMap.at(index)
127
+ if (segment.generatedStart > cursor || segment.generatedEnd <= cursor) break
128
+ const end = Math.min(generatedEnd, segment.generatedEnd)
129
+ const length = end - cursor
130
+ const originalStart = segmentOffsetAt(segment, cursor)
131
+ if (segment.originalEnd <= segment.originalStart
132
+ || originalStart + length > originalSource.length
133
+ || generatedSource.slice(cursor, end) !== originalSource.slice(originalStart, originalStart + length)) return false
134
+ cursor = end
135
+ }
136
+ return cursor === generatedEnd
137
+ }
138
+
139
+ function segmentOffsetAt(segment, generatedOffset) {
55
140
  const generatedLength = segment.generatedEnd - segment.generatedStart
56
141
  const originalLength = segment.originalEnd - segment.originalStart
57
142
  const localOffset = Math.max(0, generatedOffset - segment.generatedStart)
@@ -62,51 +147,64 @@ function originalOffsetAt(sourceMap, generatedOffset) {
62
147
  + Math.floor((localOffset * (originalLength - 1)) / (generatedLength - 1))
63
148
  }
64
149
 
150
+ /** A copied interval is already bounded by this segment. Its endpoints need
151
+ * no additional search through the complete source map. */
152
+ function copiedSegmentOriginal(segment, start, end) {
153
+ return [segmentOffsetAt(segment, start),
154
+ segmentOffsetAt(segment, end - 1) + (segment.originalEnd > segment.originalStart ? 1 : 0)]
155
+ }
156
+
65
157
  function mappedOffsetRange(sourceMap, start, end) {
158
+ const lastOffset = Math.max(start, end - 1)
159
+ const last = sourceMap.at(segmentIndexAt(sourceMap, lastOffset)) ?? sourceMap.at(-1)
160
+ const hasSourceCharacter = last === undefined || lastOffset < last.generatedEnd && last.originalEnd > last.originalStart
161
+ const originalLast = last === undefined ? lastOffset : segmentOffsetAt(last, lastOffset)
66
162
  return {
67
- originalStart: originalOffsetAt(sourceMap, start),
68
- originalEnd: originalOffsetAt(sourceMap, Math.max(start, end - 1)) + (end > start ? 1 : 0),
163
+ originalStart: start === lastOffset ? originalLast : sourceOffsetAt(sourceMap, start),
164
+ originalEnd: originalLast + (end > start && hasSourceCharacter ? 1 : 0),
69
165
  }
70
166
  }
71
167
 
72
- function replacementSegments(sourceMap, start, end, replacementLength, mappings) {
168
+ /** Replacement runs reach the mapping builder as positional writes. The
169
+ * generator this replaced allocated a segment object for every emitted run. */
170
+ function emitReplacementSegments(sourceMap, start, end, replacementLength, mappings, emit) {
73
171
  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 = []
172
+ if (!isSourceMap(mappings) || mappings.length === 0) {
173
+ if (replacementLength !== 0) emit(start, start + replacementLength, startOriginal, endOriginal)
174
+ return
175
+ }
83
176
  let cursor = 0
177
+ let anchorOffset = start
84
178
  for (const mapping of mappings) {
85
179
  if (mapping.generatedStart > cursor) {
86
- segments.push({
87
- generatedStart: start + cursor,
88
- generatedEnd: start + mapping.generatedStart,
89
- originalStart: startOriginal,
90
- originalEnd: endOriginal,
91
- })
180
+ const nextAnchor = sourceOffsetAt(sourceMap, mapping.originalStart)
181
+ emit(start + cursor, start + mapping.generatedStart, nextAnchor, nextAnchor)
182
+ }
183
+ if (mapping.generatedEnd - mapping.generatedStart === mapping.originalEnd - mapping.originalStart
184
+ && mapping.originalEnd > mapping.originalStart) {
185
+ // A verbatim copy retains every previous boundary, including inserted
186
+ // compiler text. Collapsing its endpoints would interpolate that text
187
+ // into source positions and corrupt later call/write provenance.
188
+ for (let index = segmentIndexAt(sourceMap, mapping.originalStart); index < sourceMap.length; index++) {
189
+ const segment = sourceMap.at(index)
190
+ if (segment.generatedStart >= mapping.originalEnd) break
191
+ const from = Math.max(mapping.originalStart, segment.generatedStart)
192
+ const to = Math.min(mapping.originalEnd, segment.generatedEnd)
193
+ const [originalStart, originalEnd] = copiedSegmentOriginal(segment, from, to)
194
+ emit(start + mapping.generatedStart + from - mapping.originalStart,
195
+ start + mapping.generatedStart + to - mapping.originalStart, originalStart, originalEnd)
196
+ }
197
+ } else {
198
+ const { originalStart, originalEnd } = mappedOffsetRange(sourceMap, mapping.originalStart, mapping.originalEnd)
199
+ emit(start + mapping.generatedStart, start + mapping.generatedEnd, originalStart, originalEnd)
92
200
  }
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
201
  cursor = mapping.generatedEnd
202
+ anchorOffset = mapping.originalEnd
100
203
  }
101
204
  if (cursor < replacementLength) {
102
- segments.push({
103
- generatedStart: start + cursor,
104
- generatedEnd: start + replacementLength,
105
- originalStart: startOriginal,
106
- originalEnd: endOriginal,
107
- })
205
+ const anchor = sourceOffsetAt(sourceMap, anchorOffset)
206
+ emit(start + cursor, start + replacementLength, anchor, anchor)
108
207
  }
109
- return segments
110
208
  }
111
209
 
112
210
  export function applySourceEdits(code, sourceMap, edits) {
@@ -120,12 +218,12 @@ export function applySourceEdits(code, sourceMap, edits) {
120
218
  if (!Number.isSafeInteger(item.start) || !Number.isSafeInteger(item.end)
121
219
  || item.start < 0 || item.end < item.start || item.end > code.length
122
220
  || item.start < previousEnd || typeof text !== 'string'
123
- || (item.mappings !== undefined && !Array.isArray(item.mappings))
221
+ || (item.mappings !== undefined && !isSourceMap(item.mappings))
124
222
  || (item.start === item.end && previous?.start === item.start && previous.end === item.end)) {
125
223
  throw new RangeError('source edits must be bounded, ordered, and non-overlapping')
126
224
  }
127
- const itemMappings = [...(item.mappings ?? [])]
128
- .sort((left, right) => left.generatedStart - right.generatedStart)
225
+ const itemMappings = item.mappings instanceof SourceMapRuns ? item.mappings
226
+ : [...(item.mappings ?? [])].sort((left, right) => left.generatedStart - right.generatedStart)
129
227
  let mappingEnd = 0
130
228
  for (const mapping of itemMappings) {
131
229
  if (!Number.isSafeInteger(mapping.generatedStart) || !Number.isSafeInteger(mapping.generatedEnd)
@@ -143,27 +241,23 @@ export function applySourceEdits(code, sourceMap, edits) {
143
241
  }
144
242
 
145
243
  const chunks = []
146
- const mappings = []
244
+ const mappings = createSourceMapBuilder()
147
245
  let generatedOffset = 0
148
246
  let sourceOffset = 0
149
247
  let segmentIndex = 0
150
248
  const appendUnchanged = end => {
151
249
  if (end <= sourceOffset) return
152
250
  chunks.push(code.slice(sourceOffset, end))
153
- while (segmentIndex < sourceMap.length && sourceMap[segmentIndex].generatedEnd <= sourceOffset) segmentIndex += 1
251
+ while (segmentIndex < sourceMap.length && sourceMap.at(segmentIndex).generatedEnd <= sourceOffset) segmentIndex += 1
154
252
  for (let index = segmentIndex; index < sourceMap.length; index += 1) {
155
- const segment = sourceMap[index]
253
+ const segment = sourceMap.at(index)
156
254
  if (segment.generatedStart >= end) break
157
255
  const start = Math.max(sourceOffset, segment.generatedStart)
158
256
  const stop = Math.min(end, segment.generatedEnd)
159
257
  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
- })
258
+ const [originalStart, originalEnd] = copiedSegmentOriginal(segment, start, stop)
259
+ mappings.pushValues(generatedOffset + start - sourceOffset, generatedOffset + stop - sourceOffset,
260
+ originalStart, originalEnd)
167
261
  }
168
262
  generatedOffset += end - sourceOffset
169
263
  sourceOffset = end
@@ -173,52 +267,117 @@ export function applySourceEdits(code, sourceMap, edits) {
173
267
  appendUnchanged(item.start)
174
268
  const { text } = item
175
269
  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 })))
270
+ emitReplacementSegments(sourceMap, item.start, item.end, text.length, item.mappings,
271
+ (generatedStart, generatedEnd, originalStart, originalEnd) => mappings.pushValues(
272
+ generatedStart - item.start + generatedOffset, generatedEnd - item.start + generatedOffset,
273
+ originalStart, originalEnd))
179
274
  generatedOffset += text.length
180
275
  sourceOffset = item.end
181
276
  }
182
277
  appendUnchanged(code.length)
183
- return { code: chunks.join(''), sourceMap: mappings }
278
+ return { code: chunks.join(''), sourceMap: mappings.finish() }
279
+ }
280
+
281
+ const LINE_TERMINATORS = /\r\n|[\n\r\u2028\u2029]/gu
282
+
283
+ /** JavaScript source coordinates count CRLF as one line boundary. */
284
+ export function sourceLineStarts(source) {
285
+ const starts = [0]
286
+ for (const match of source.matchAll(LINE_TERMINATORS)) starts.push(match.index + match[0].length)
287
+ return starts
288
+ }
289
+
290
+ /** Visit maintained emitter coordinates without re-encoding available raw maps.
291
+ * All line numbers at this boundary are zero-based; absent origins stay absent. */
292
+ export function visitSourceMappings(transformed, visit) {
293
+ const raw = transformed.rawMappings
294
+ if (raw !== undefined) {
295
+ for (const item of raw) visit(item.generated.line - 1, item.generated.column,
296
+ item.original === undefined ? undefined : item.original.line - 1, item.original?.column)
297
+ return
298
+ }
299
+ const nativeMap = new TraceMap(typeof transformed.map === 'string' ? JSON.parse(transformed.map) : transformed.map)
300
+ for (const [line, entries] of decodedMappings(nativeMap).entries()) {
301
+ for (const entry of entries) visit(line, entry[0], entry[2], entry[3])
302
+ }
303
+ }
304
+
305
+ /** Compose a parser's line/column map with the existing source-offset map. */
306
+ export function mappedSourceTransform(code, sourceMap, transformed) {
307
+ const starts = sourceLineStarts(code)
308
+ const generatedStarts = sourceLineStarts(transformed.code)
309
+ const mappings = createSourceMapBuilder()
310
+ let previous
311
+ visitSourceMappings(transformed, (line, column, originalLine, originalColumn) => {
312
+ const generatedStart = Math.min(transformed.code.length, generatedStarts[line] + column)
313
+ if (previous !== undefined) {
314
+ previous.generatedEnd = generatedStart
315
+ mappings.push(previous)
316
+ }
317
+ if (starts[originalLine] === undefined) {
318
+ previous = undefined
319
+ return
320
+ }
321
+ const originalStart = Math.min(code.length, starts[originalLine] + originalColumn)
322
+ previous = { generatedStart, generatedEnd: transformed.code.length,
323
+ originalStart, originalEnd: Math.min(code.length, originalStart + 1) }
324
+ })
325
+ if (previous !== undefined) mappings.push(previous)
326
+ return applySourceEdits(code, sourceMap, [{ start: 0, end: code.length, text: transformed.code, mappings: mappings.finish(true) }])
327
+ }
328
+
329
+ function lineStartOffset(source, line, starts) {
330
+ return starts[line - 1] ?? source.length
331
+ }
332
+
333
+ function positionAtOffset(source, offset, starts) {
334
+ let low = 0, high = starts.length
335
+ while (low < high) {
336
+ const middle = (low + high) >>> 1
337
+ if (starts[middle] <= offset) low = middle + 1
338
+ else high = middle
339
+ }
340
+ const line = low - 1
341
+ return { line: line + 1, column: offset - starts[line] + 1 }
184
342
  }
185
343
 
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
344
+ const coordinateIndexes = new WeakMap()
345
+ function coordinateIndex(sourceMap, generatedSource, originalSource) {
346
+ let index = coordinateIndexes.get(sourceMap)
347
+ if (index?.generatedSource !== generatedSource || index.originalSource !== originalSource) {
348
+ index = { generatedSource, originalSource, generatedStarts: sourceLineStarts(generatedSource),
349
+ originalStarts: sourceLineStarts(originalSource) }
350
+ coordinateIndexes.set(sourceMap, index)
192
351
  }
193
- return offset
352
+ return index
194
353
  }
195
354
 
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 }
355
+ export function sourceTextAtSpan(source, span, starts = sourceLineStarts(source)) {
356
+ return source.slice(lineStartOffset(source, span.line, starts) + span.column - 1,
357
+ lineStartOffset(source, span.end.line, starts) + span.end.column - 1)
201
358
  }
202
359
 
203
- export function mapSourcePosition(position, generatedSource, originalSource, sourceMap) {
360
+ export function mapSourcePosition(position, generatedSource, originalSource, sourceMap, coordinates) {
204
361
  if (position === undefined || sourceMap === undefined) return position
205
362
  if (!Number.isSafeInteger(position.line) || position.line < 1
206
363
  || !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)
364
+ const index = coordinates ?? coordinateIndex(sourceMap, generatedSource, originalSource)
365
+ const lineStart = lineStartOffset(generatedSource, position.line, index.generatedStarts)
366
+ const mappedOffset = sourceOffsetAt(sourceMap, lineStart + position.column - 1)
367
+ return positionAtOffset(originalSource, mappedOffset, index.originalStarts)
210
368
  }
211
369
 
212
370
  /** Map a complete generated-source span into original cell coordinates. */
213
- export function mapSourceSpan(span, generatedSource, originalSource, sourceMap) {
371
+ export function mapSourceSpan(span, generatedSource, originalSource, sourceMap, coordinates) {
214
372
  if (span === undefined) return undefined
215
- const start = mapSourcePosition(span, generatedSource, originalSource, sourceMap)
373
+ const start = mapSourcePosition(span, generatedSource, originalSource, sourceMap, coordinates)
216
374
  if (span.end === undefined) return start === span ? span : start
217
375
  if (!Number.isSafeInteger(span.end.line) || span.end.line < 1
218
376
  || !Number.isSafeInteger(span.end.column) || span.end.column < 1) {
219
377
  return { ...start, end: span.end }
220
378
  }
221
- const generatedEnd = lineStartOffset(generatedSource, span.end.line) + span.end.column - 1
379
+ const index = coordinates ?? coordinateIndex(sourceMap, generatedSource, originalSource)
380
+ const generatedEnd = lineStartOffset(generatedSource, span.end.line, index.generatedStarts) + span.end.column - 1
222
381
  const original = mappedOffsetRange(sourceMap, Math.max(0, generatedEnd - 1), generatedEnd)
223
- return { ...start, end: positionAtOffset(originalSource, original.originalEnd) }
382
+ return { ...start, end: positionAtOffset(originalSource, original.originalEnd, index.originalStarts) }
224
383
  }
@@ -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
+ }