dsh-ptc-plus 0.4.1 → 0.4.3

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 (103) hide show
  1. package/README.en.md +164 -78
  2. package/README.md +165 -79
  3. package/assets/dsh-ptc-plus-banner-en.webp +0 -0
  4. package/assets/dsh-ptc-plus-banner-zh.webp +0 -0
  5. package/assets/ptc-plus-icon.svg +5 -0
  6. package/client.js +854 -336
  7. package/compiler-core.cjs +161 -157
  8. package/compiler-core.cjs.map +3 -3
  9. package/docs/README.md +4 -1
  10. package/docs/adr/0005-temporary-rejected-cell-edit-transport.md +8 -7
  11. package/docs/adr/0008-define-agent-operating-contract.md +2 -2
  12. package/docs/adr/0010-session-log-derived-recovery-tips.md +7 -0
  13. package/docs/adr/0016-archive-resolved-review-findings.md +3 -3
  14. package/docs/adr/0019-plugin-settings-and-kill-switch.md +3 -3
  15. package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -0
  16. package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +14 -3
  17. package/docs/adr/0023-global-user-bindings.md +7 -7
  18. package/docs/adr/0025-use-versioned-logical-binding-identities.md +3 -3
  19. package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +23 -7
  20. package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +9 -5
  21. package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +26 -6
  22. package/docs/adr/0031-close-semantic-obligations-by-owner.md +29 -0
  23. package/docs/adr/README.md +4 -4
  24. package/docs/architecture.md +15 -10
  25. package/docs/binding-review-placement-design.md +1 -1
  26. package/docs/client-ui.md +9 -5
  27. package/docs/durability-design.md +12 -9
  28. package/docs/installation.md +27 -9
  29. package/docs/runtime-reference.md +23 -12
  30. package/docs/semantic-validation.md +7 -3
  31. package/docs/stateful-binding-updates-proposal.md +8 -4
  32. package/docs/user-bindings.en.md +10 -7
  33. package/docs/user-bindings.md +9 -6
  34. package/docs/value-wire.md +1 -1
  35. package/docs/verification-optimization-work.md +41 -0
  36. package/docs/verification.md +53 -2
  37. package/index.js +39 -34
  38. package/internal/async-local-storage-intrinsics.js +43 -0
  39. package/internal/binding-declaration-contract.js +8 -0
  40. package/internal/binding-source-analysis.js +381 -64
  41. package/internal/binding-update-policy.js +7 -1
  42. package/internal/callable-source-facts.js +37 -1
  43. package/internal/cell-analysis.js +85 -17
  44. package/internal/compiler-intrinsics.js +9 -43
  45. package/internal/compiler-module-links.js +7 -4
  46. package/internal/config-spec.js +1 -1
  47. package/internal/cordis-tools-owner.js +20 -10
  48. package/internal/dynamic-environment-integration.js +8 -4
  49. package/internal/dynamic-environment-runtime.js +24 -3
  50. package/internal/dynamic-native-calls.js +4 -1
  51. package/internal/dynamic-native-runtime.js +15 -1
  52. package/internal/edit-transport-owner.js +80 -20
  53. package/internal/execution-seam-compat.js +192 -30
  54. package/internal/failure-reporting.js +38 -30
  55. package/internal/isolated-worker.js +17 -10
  56. package/internal/kernel-worker.js +645 -393
  57. package/internal/message-sources.js +14 -0
  58. package/internal/module-compilation.js +7 -5
  59. package/internal/module-transform-contract.js +32 -2
  60. package/internal/record-utils.js +24 -15
  61. package/internal/repl-memory-projection.js +10 -0
  62. package/internal/repl-scope-normalizer.js +72 -35
  63. package/internal/repl-value-observer.js +145 -46
  64. package/internal/runtime-config.js +62 -2
  65. package/internal/runtime-intrinsics.js +104 -0
  66. package/internal/runtime-messages.js +6 -4
  67. package/internal/session-cell-executor.js +26 -2
  68. package/internal/session-journal-recovery.js +267 -67
  69. package/internal/session-journal-schema.js +30 -13
  70. package/internal/session-journal.js +485 -44
  71. package/internal/session-log-view.js +5 -4
  72. package/internal/session-runtime.js +46 -21
  73. package/internal/session-state.js +7 -29
  74. package/internal/settings-compat.js +66 -1
  75. package/internal/source-position-map.js +38 -0
  76. package/internal/stateful-root-compiler.js +3 -1
  77. package/internal/typescript-transform.js +4 -2
  78. package/internal/user-binding-console-worker.js +115 -41
  79. package/internal/user-binding-draft-projection.js +3 -2
  80. package/internal/user-bindings-owner.js +2 -1
  81. package/internal/user-bindings.js +16 -3
  82. package/internal/validated-parse-repair.js +6 -2
  83. package/internal/value-wire.js +189 -94
  84. package/internal/worker-client.js +99 -8
  85. package/internal/worker-cwd-virtualization.js +82 -54
  86. package/internal/worker-environment.js +20 -0
  87. package/internal/worker-output-capture.js +187 -0
  88. package/internal/worker-output-fence.js +26 -0
  89. package/internal/worker-realm-surfaces.js +197 -0
  90. package/internal/worker-repl-realm.js +140 -0
  91. package/package.json +37 -4
  92. package/src/client-activity.js +12 -2
  93. package/src/client-authoring-view.js +121 -62
  94. package/src/client-console.js +4 -2
  95. package/src/client-copy.js +4 -2
  96. package/src/client-feature-gates.js +7 -9
  97. package/src/client-host-compat.js +104 -27
  98. package/src/client-repl-view.js +3 -1
  99. package/src/client-styles.js +12 -1
  100. package/src/client-tool-view.js +3 -1
  101. package/src/client-transport.js +2 -2
  102. package/src/client-workbench.js +4 -3
  103. package/src/client.js +56 -17
@@ -0,0 +1,14 @@
1
+ /** Producer identity shared by Host messages and Client history projections. */
2
+ export const PTC_MESSAGE_SOURCE_KIND = 'plugin:ptc-plus'
3
+
4
+ /** Read the current producer identity and its historical pre-migration wrapper. */
5
+ export function isPtcMessageSource(source) {
6
+ return source?.kind === PTC_MESSAGE_SOURCE_KIND
7
+ || (source?.kind === 'plugin' && source.plugin === 'ptc-plus')
8
+ }
9
+
10
+ /** DSH owns aggregate runtime-context messages independently of PTC messages. */
11
+ export function isHostRuntimeContextSource(source) {
12
+ return source?.kind === 'runtime-context'
13
+ || (source?.kind === 'plugin' && source.plugin === '@deepseek-ai/dsh-system-prompt')
14
+ }
@@ -14,7 +14,7 @@ export { commonJsExportEvidence, linkCommonJsEvidenceSource } from './commonjs-s
14
14
  import { COMMONJS_PARAMETERS } from './compiler-scope-facts.js'
15
15
  import {
16
16
  LEGACY_USER_BINDING_TRANSFORM, USER_BINDING_TRANSFORM, PROTECTED_MODULE_TRANSFORM,
17
- supportedUserBindingTransform, transformTypeScriptSource,
17
+ isStatefulUserBindingTransform, supportedUserBindingTransform, transformTypeScriptSource,
18
18
  } from './typescript-transform.js'
19
19
 
20
20
  const topLevelRegion = region => region.kind === 'program' || region.top === true
@@ -162,7 +162,7 @@ function commonJsBodyRange(source) {
162
162
 
163
163
  /** Module activations share lexical updates while retaining the native linker. */
164
164
  export function compileStatefulModule(source, {
165
- transform = USER_BINDING_TRANSFORM, target = 'module', url, nativeUsing,
165
+ transform = USER_BINDING_TRANSFORM, target = 'module', url, nativeUsing, programBindings = false,
166
166
  } = {}) {
167
167
  if (transform !== PROTECTED_MODULE_TRANSFORM && !supportedUserBindingTransform(transform)) {
168
168
  throw new TypeError('user binding snapshot cannot prove its historical TypeScript transform')
@@ -187,18 +187,19 @@ export function compileStatefulModule(source, {
187
187
  callableSources = marked.callableSources
188
188
  nativeJavaScript = marked.nativeJavaScript
189
189
  mapped = marked
190
- if (!nativeJavaScript) mapped = normalizeTypeScriptValues(mapped.code, mapped.sourceMap, target)
191
- if (target !== 'commonjs' && (transform === USER_BINDING_TRANSFORM
190
+ if (!nativeJavaScript) mapped = normalizeTypeScriptValues(mapped.code, mapped.sourceMap, target, callableSources)
191
+ if (target !== 'commonjs' && (isStatefulUserBindingTransform(transform)
192
192
  || transform === PROTECTED_MODULE_TRANSFORM && hasModuleResources(mapped.code))) {
193
193
  mapped = normalizeModuleExports(mapped.code, mapped.sourceMap)
194
194
  }
195
195
  }
196
196
  const commonJsSource = target === 'commonjs'
197
197
  ? commonJsExportEvidence(mapped.code, { legacy: transform === LEGACY_USER_BINDING_TRANSFORM }) : undefined
198
- const links = transform === USER_BINDING_TRANSFORM && target === 'module'
198
+ const links = isStatefulUserBindingTransform(transform) && target === 'module'
199
199
  ? moduleLinkPlan(mapped.code) : undefined
200
200
  let normalized = transform === LEGACY_USER_BINDING_TRANSFORM ? mapped
201
201
  : normalizeStatefulScopes(mapped.code, mapped.sourceMap, { target, moduleImport: links?.reader, nativeJavaScript, nativeUsing,
202
+ callableSources,
202
203
  mode: transform === PROTECTED_MODULE_TRANSFORM ? 'protected-v1' : 'stateful-v1' })
203
204
  // Protected entries retain native declaration policy; adapters still need
204
205
  // bounded source regions after TypeScript and decorator lowering.
@@ -222,6 +223,7 @@ export function compileStatefulModule(source, {
222
223
  sourceRegions: normalized.sourceRegions,
223
224
  callableCollector: createRegionCallableCollector(callableSources),
224
225
  module: true, compactOutput: true, importOperation: normalized.importOperation, sourceType, originalSource: source,
226
+ logicalRoots: programBindings,
225
227
  bindings: normalized.dynamicBindings, privateBindings: normalized.privateBindings,
226
228
  internalBindings: normalized.internalBindings, parserPlugins: CELL_PARSER_PLUGINS,
227
229
  }) }
@@ -1,12 +1,42 @@
1
1
  export const LEGACY_USER_BINDING_TRANSFORM = 'amaro@1.1.11'
2
- export const USER_BINDING_TRANSFORM = 'stateful-module-v1+amaro@1.1.11'
2
+ export const PREVIOUS_USER_BINDING_TRANSFORM = 'stateful-module-v1+amaro@1.1.11'
3
+ export const USER_BINDING_TRANSFORM = 'stateful-module-v2+amaro@1.1.11'
3
4
  export const PROTECTED_MODULE_TRANSFORM = 'protected-module-v1+amaro@1.1.11'
5
+ export const MODULE_TRANSFORMS = new Set([
6
+ LEGACY_USER_BINDING_TRANSFORM,
7
+ PREVIOUS_USER_BINDING_TRANSFORM,
8
+ USER_BINDING_TRANSFORM,
9
+ PROTECTED_MODULE_TRANSFORM,
10
+ ])
11
+
12
+ export function isStatefulUserBindingTransform(transform) {
13
+ return transform === USER_BINDING_TRANSFORM || transform === PREVIOUS_USER_BINDING_TRANSFORM
14
+ }
4
15
 
5
16
  export function moduleTransformForLanguage(languageSemantics) {
6
17
  return languageSemantics === 'stateful-v1' ? USER_BINDING_TRANSFORM
7
18
  : languageSemantics === 'protected-v1' ? PROTECTED_MODULE_TRANSFORM : LEGACY_USER_BINDING_TRANSFORM
8
19
  }
9
20
 
21
+ export function historicalModuleTransformForLanguage(languageSemantics) {
22
+ return languageSemantics === 'stateful-v1' ? PREVIOUS_USER_BINDING_TRANSFORM
23
+ : languageSemantics === 'protected-v1' ? PROTECTED_MODULE_TRANSFORM : LEGACY_USER_BINDING_TRANSFORM
24
+ }
25
+
26
+ export function moduleTransformMatchesLanguage(transform, languageSemantics) {
27
+ return languageSemantics === 'stateful-v1' ? isStatefulUserBindingTransform(transform)
28
+ : languageSemantics === 'protected-v1' ? transform === PROTECTED_MODULE_TRANSFORM
29
+ : languageSemantics === 'legacy-v1' ? transform === LEGACY_USER_BINDING_TRANSFORM : false
30
+ }
31
+
32
+ export function normalizeModuleTransform(transform, languageSemantics) {
33
+ if (!MODULE_TRANSFORMS.has(transform)) throw new TypeError('invalid dsh-ptc-plus journal module transform')
34
+ if (!moduleTransformMatchesLanguage(transform, languageSemantics)) {
35
+ throw new TypeError('dsh-ptc-plus journal module transform does not match its language semantics')
36
+ }
37
+ return transform
38
+ }
39
+
10
40
  export function supportedUserBindingTransform(transform) {
11
- return transform === USER_BINDING_TRANSFORM || transform === LEGACY_USER_BINDING_TRANSFORM
41
+ return isStatefulUserBindingTransform(transform) || transform === LEGACY_USER_BINDING_TRANSFORM
12
42
  }
@@ -1,14 +1,22 @@
1
1
  /** Shared structural guards for closed PTC metadata boundaries. */
2
2
 
3
+ import { runtimeIntrinsics as internal } from './runtime-intrinsics.js'
4
+
5
+ const { TypeError, isArray, reflectOwnKeys, objectGetOwnPropertyDescriptor,
6
+ objectHasOwn, objectPropertyIsEnumerable, objectFreeze, trimString, toString,
7
+ Set, setHas, setAdd, setSize, appendArray, popArray } = internal
8
+
3
9
  export function isRecord(value) {
4
- return value !== null && typeof value === 'object' && !Array.isArray(value)
10
+ return value !== null && typeof value === 'object' && !isArray(value)
5
11
  }
6
12
 
7
13
  export function assertOwnFields(value, allowed, label) {
8
- for (const key of Reflect.ownKeys(value)) {
9
- if (typeof key !== 'string' || !allowed.has(key)
10
- || !Object.prototype.propertyIsEnumerable.call(value, key)) {
11
- throw new TypeError(`invalid ${label} field ${String(key)}`)
14
+ const keys = reflectOwnKeys(value)
15
+ for (let index = 0; index < keys.length; index += 1) {
16
+ const key = keys[index]
17
+ if (typeof key !== 'string' || !setHas(allowed, key)
18
+ || !objectPropertyIsEnumerable(value, key)) {
19
+ throw new TypeError(`invalid ${label} field ${toString(key)}`)
12
20
  }
13
21
  }
14
22
  }
@@ -16,7 +24,7 @@ export function assertOwnFields(value, allowed, label) {
16
24
  export function assertFields(value, allowed, label) {
17
25
  if (!isRecord(value)) throw new TypeError(`invalid ${label}`)
18
26
  assertOwnFields(value, allowed, label)
19
- if (Reflect.ownKeys(value).length !== allowed.size) throw new TypeError(`invalid ${label} fields`)
27
+ if (reflectOwnKeys(value).length !== setSize(allowed)) throw new TypeError(`invalid ${label} fields`)
20
28
  }
21
29
 
22
30
  export function record(value, label) {
@@ -25,7 +33,7 @@ export function record(value, label) {
25
33
  }
26
34
 
27
35
  export function text(value, label) {
28
- if (typeof value !== 'string' || value.trim().length === 0) {
36
+ if (typeof value !== 'string' || trimString(value).length === 0) {
29
37
  throw new TypeError(`ptc-plus: ${label} must be a non-empty string`)
30
38
  }
31
39
  return value
@@ -37,17 +45,18 @@ export function deepFreeze(value) {
37
45
  const pending = [value]
38
46
  const seen = new Set()
39
47
  while (pending.length > 0) {
40
- const current = pending.pop()
41
- if (seen.has(current)) continue
42
- seen.add(current)
43
- for (const key of Reflect.ownKeys(current)) {
44
- const descriptor = Object.getOwnPropertyDescriptor(current, key)
45
- if (descriptor !== undefined && Object.hasOwn(descriptor, 'value')) {
48
+ const current = popArray(pending)
49
+ if (setHas(seen, current)) continue
50
+ setAdd(seen, current)
51
+ const keys = reflectOwnKeys(current)
52
+ for (let index = 0; index < keys.length; index += 1) {
53
+ const descriptor = objectGetOwnPropertyDescriptor(current, keys[index])
54
+ if (descriptor !== undefined && objectHasOwn(descriptor, 'value')) {
46
55
  const child = descriptor.value
47
- if (child !== null && typeof child === 'object') pending.push(child)
56
+ if (child !== null && typeof child === 'object') appendArray(pending, child)
48
57
  }
49
58
  }
50
- Object.freeze(current)
59
+ objectFreeze(current)
51
60
  }
52
61
  return value
53
62
  }
@@ -26,6 +26,16 @@ const PROJECTION_STATE_FIELDS = new Set(['generation', 'memory', 'pendingReplCal
26
26
  const PENDING_CALL_FIELDS = new Set(['callId', 'seq'])
27
27
  const REPL_MEMORY_META_VERSION = 5
28
28
 
29
+ /**
30
+ * Bound one definition source to the presentation budget. Producers apply this
31
+ * before a catalog entry can carry more than the validated metadata shape.
32
+ */
33
+ export function boundedDefinitionSource(source) {
34
+ return source.length <= MAX_DEFINITION_SOURCE_LENGTH
35
+ ? source
36
+ : `${source.slice(0, MAX_DEFINITION_SOURCE_LENGTH - 3)}...`
37
+ }
38
+
29
39
  function isReuseCount(value) {
30
40
  return Number.isSafeInteger(value) && value >= 0
31
41
  }
@@ -23,7 +23,7 @@ import { supportsNativeUsing, transformTypeScriptSource } from './typescript-tra
23
23
  import { createDynamicScopeAnalysis, varInitializerTarget } from './dynamic-scope-analysis.js'
24
24
  import { visitSource } from './compiler-source-regions.js'
25
25
  import { analyzeSourceDeclarations, resolveSourceBinding, COMMONJS_PARAMETERS } from './compiler-scope-facts.js'
26
- import { callableMarkerPosition } from './callable-source-facts.js'
26
+ import { callableMarkerPosition, restoreMappedCallableSourceMarks } from './callable-source-facts.js'
27
27
  import { createSourcePieces } from './compiler-source-pieces.js'
28
28
  import { indexSourceRegions, validateRegionSource, sourceFeatures } from './compiler-region-output.js'
29
29
 
@@ -424,7 +424,7 @@ export function lowerStatefulDecorators(result, target, intrinsicContext) {
424
424
 
425
425
  /** Erase type-only declarations and lower runtime TypeScript values before
426
426
  * logical ownership and module export/link plans consume the source. */
427
- export function normalizeTypeScriptValues(code, sourceMap, target) {
427
+ export function normalizeTypeScriptValues(code, sourceMap, target, callableSources) {
428
428
  const tree=parse(code,{...parserOptionsForTarget(target),errorRecovery:true})
429
429
  let parameterProperties = false
430
430
  let tokens
@@ -521,20 +521,15 @@ export function normalizeTypeScriptValues(code, sourceMap, target) {
521
521
  if(enumEdits.length>0) source=applySourceEdits(source,identitySourceMap(source.length),enumEdits).code
522
522
  const transformed=transformTypeScriptSource(source,{module:target!=='commonjs',sourceMap:true,
523
523
  transform:{tsEnumIsMutable:true,noEmptyExport:true}})
524
- const nativeMap=new SourceMap(JSON.parse(transformed.map))
525
- const starts=[0]
526
- for(let index=0;index<source.length;index++) if(source[index]==='\n') starts.push(index+1)
527
- const mappings=[]
528
- let line=1,column=1
529
- for(let index=0;index<transformed.code.length;index++) {
530
- const origin=nativeMap.findOrigin(line,column)
531
- const start=Math.min(source.length,Math.max(0,(starts[origin.lineNumber-1]??0)+(origin.columnNumber??1)-1))
532
- mappings.push({generatedStart:index,generatedEnd:index+1,originalStart:path.node.start+start,
533
- originalEnd:Math.min(owner.end,path.node.start+start+1)})
534
- if(transformed.code[index]==='\n'){line++;column=1}else column++
535
- }
536
- const exported=owner!==path.node&&transformed.code.trim()!==''?`\nexport {${path.node.id.name}};`:''
537
- edits.push({start:owner.start,end:owner.end,text:transformed.code+exported,mappings})
524
+ let mapped=mappedSourceTransform(source,identitySourceMap(source.length),transformed)
525
+ mapped=restoreMappedCallableSourceMarks(mapped,source,transformed,callableSources,parserOptionsForTarget(target))
526
+ const mappings=[...mapped.sourceMap].map(mapping=>({
527
+ ...mapping,
528
+ originalStart:path.node.start+mapping.originalStart,
529
+ originalEnd:Math.min(owner.end,path.node.start+mapping.originalEnd),
530
+ }))
531
+ const exported=owner!==path.node&&mapped.code.trim()!==''?`\nexport {${path.node.id.name}};`:''
532
+ edits.push({start:owner.start,end:owner.end,text:mapped.code+exported,mappings})
538
533
  path.skip()
539
534
  },
540
535
  })
@@ -1130,23 +1125,46 @@ export function preserveStatementBoundaries(code, sourceMap = identitySourceMap(
1130
1125
  * The root compiler owns Program bindings and calls this pass after lowering
1131
1126
  * those declarations. protected-v1 keeps native local binding policy.
1132
1127
  */
1128
+ const normalizationFailureInputs = new WeakMap()
1129
+
1130
+ function retainNormalizationFailureInput(error, input) {
1131
+ if ((typeof error === 'object' && error !== null) || typeof error === 'function') {
1132
+ if (!normalizationFailureInputs.has(error)) normalizationFailureInputs.set(error, input)
1133
+ }
1134
+ }
1135
+
1136
+ /** Latest mapped normalization stage established before an error was thrown. */
1137
+ export function normalizationFailureInput(error) {
1138
+ return (typeof error === 'object' && error !== null) || typeof error === 'function'
1139
+ ? normalizationFailureInputs.get(error) : undefined
1140
+ }
1141
+
1133
1142
  export function normalizeStatefulScopes(code, sourceMap = identitySourceMap(code.length), options = {}) {
1134
- const { outputPlan, ...metadata } = normalizeStatefulScopeInput(code, sourceMap, options)
1135
- let result = metadata
1136
- if (outputPlan) {
1137
- // Release declaration ASTs and scope ancestry before materializing the
1138
- // expanded output. This phase retains only numeric pieces and source facts.
1139
- const { pieces, root, code, sourceMap, regionFacts } = outputPlan
1140
- options.onPhase?.('mapping')
1141
- const output = pieces.emit(root)
1142
- result = { ...metadata, ...applySourceEdits(code, sourceMap,
1143
- [{ start: 0, end: code.length, ...output }]),
1144
- sourceRegions: { ...regionFacts, regions: output.regions } }
1143
+ let diagnosticInput = { code, sourceMap }
1144
+ try {
1145
+ const { outputPlan, ...metadata } = normalizeStatefulScopeInput(code, sourceMap, options)
1146
+ let result = metadata
1147
+ if (outputPlan) {
1148
+ // Release declaration ASTs and scope ancestry before materializing the
1149
+ // expanded output. This phase retains only numeric pieces and source facts.
1150
+ const { pieces, root, code, sourceMap, regionFacts } = outputPlan
1151
+ diagnosticInput = { code, sourceMap }
1152
+ options.onPhase?.('mapping')
1153
+ const output = pieces.emit(root)
1154
+ result = { ...metadata, ...applySourceEdits(code, sourceMap,
1155
+ [{ start: 0, end: code.length, ...output }]),
1156
+ sourceRegions: { ...regionFacts, regions: output.regions } }
1157
+ }
1158
+ diagnosticInput = result
1159
+ options.onPhase?.('validation')
1160
+ if (options.target === 'module' || options.target === 'commonjs') validateRegionSource(result, parserOptionsForTarget(options.target))
1161
+ result = options.deferDecorators ? result : lowerStatefulDecorators(result, options.target, options.intrinsicContext)
1162
+ diagnosticInput = result
1163
+ return options.deferResources ? result : lowerStatefulResources(result, options)
1164
+ } catch (error) {
1165
+ retainNormalizationFailureInput(error, diagnosticInput)
1166
+ throw error
1145
1167
  }
1146
- options.onPhase?.('validation')
1147
- if (options.target === 'module' || options.target === 'commonjs') validateRegionSource(result, parserOptionsForTarget(options.target))
1148
- result = options.deferDecorators ? result : lowerStatefulDecorators(result, options.target, options.intrinsicContext)
1149
- return options.deferResources ? result : lowerStatefulResources(result, options)
1150
1168
  }
1151
1169
 
1152
1170
  /** Resource lifetime is lowered only after declaration owners have consumed
@@ -1227,15 +1245,33 @@ export function hasModuleResources(code) {
1227
1245
  node.type === 'VariableDeclaration' && (node.kind === 'using' || node.kind === 'await using'))
1228
1246
  }
1229
1247
 
1230
- function normalizeStatefulScopeInput(code, sourceMap, { mode = 'stateful-v1', target, moduleImport, intrinsicContext, onPhase,
1231
- nativeJavaScript = false, nativeUsing = supportsNativeUsing() } = {}) {
1248
+ function normalizeStatefulScopeInput(code, sourceMap, options) {
1249
+ let diagnosticInput = { code, sourceMap }
1250
+ try {
1251
+ return normalizeStatefulScopeInputUnchecked(code, sourceMap, options, input => { diagnosticInput = input })
1252
+ } catch (error) {
1253
+ retainNormalizationFailureInput(error, diagnosticInput)
1254
+ throw error
1255
+ }
1256
+ }
1257
+
1258
+ function normalizeStatefulScopeInputUnchecked(code, sourceMap, { mode = 'stateful-v1', target, moduleImport, intrinsicContext, onPhase,
1259
+ callableSources, nativeJavaScript = false, nativeUsing = supportsNativeUsing() } = {}, updateDiagnosticInput) {
1232
1260
  if (mode !== 'stateful-v1' && mode !== 'protected-v1') throw new TypeError('ptc-plus: unsupported scope semantics')
1233
1261
  onPhase?.('typescript')
1234
- const typescript = nativeJavaScript ? { code, sourceMap }
1235
- : lowerLegacyParameterDecorators(normalizeTypeScriptValues(code,sourceMap,target),target,intrinsicContext)
1262
+ let typescript
1263
+ if (nativeJavaScript) {
1264
+ typescript = { code, sourceMap }
1265
+ } else {
1266
+ const normalizedTypescript = normalizeTypeScriptValues(code,sourceMap,target,callableSources)
1267
+ updateDiagnosticInput(normalizedTypescript)
1268
+ typescript = lowerLegacyParameterDecorators(normalizedTypescript,target,intrinsicContext)
1269
+ }
1270
+ updateDiagnosticInput(typescript)
1236
1271
  const separated = preserveStatementBoundaries(typescript.code, typescript.sourceMap, parserOptionsForTarget(target))
1237
1272
  code=separated.code
1238
1273
  sourceMap=separated.sourceMap
1274
+ updateDiagnosticInput(separated)
1239
1275
  const finish = result => {
1240
1276
  result = { ...result, deferredHelpers: typescript.deferredHelpers,
1241
1277
  internalBindings: new Set([...typescript.internalBindings ?? [], ...result.internalBindings ?? []]) }
@@ -1269,6 +1305,7 @@ function normalizeStatefulScopeInput(code, sourceMap, { mode = 'stateful-v1', ta
1269
1305
  const annex = introduceAnnexBVars(code, sourceMap, target, onPhase)
1270
1306
  code = annex.code
1271
1307
  sourceMap = annex.sourceMap
1308
+ updateDiagnosticInput(annex)
1272
1309
  onPhase?.('facts')
1273
1310
  const facts = analyzeLogicalScopes(code, { target, compact: true, compactNames: target === 'module' || target === 'commonjs', onPhase })
1274
1311
  onPhase?.('planning')
@@ -1,113 +1,212 @@
1
1
  import { types } from 'node:util'
2
- import { runInContext } from 'node:vm'
3
- import { parse } from 'acorn'
2
+ import { readFileSync } from 'node:fs'
3
+ import { createRequire } from 'node:module'
4
+ import { createContext, isContext, runInContext, runInThisContext } from 'node:vm'
4
5
  import { randomUUID } from 'node:crypto'
5
- import { walkAst, SKIP_AST_CHILDREN } from './ast-traversal.js'
6
- import { bindingNodes } from './binding-pattern-traversal.js'
6
+ import { runtimeIntrinsics as internal } from './runtime-intrinsics.js'
7
+ import { deleteWorkerRealmProperty, WORKER_REALM_MUTATION } from './worker-realm-surfaces.js'
7
8
 
8
9
  const MAX_ENTRIES = 128
9
10
  const MAX_TEXT = 512
10
11
  const MAX_PROPERTIES = 5
11
- const ownDescriptor = Object.getOwnPropertyDescriptor
12
- const isArray = Array.isArray
13
12
  const isProxy = types.isProxy
14
- const stringify = JSON.stringify
15
13
  const now = Date.now
16
- const bigintText = BigInt.prototype.toString
17
14
  const BIGINT_LIMIT = 10n ** 128n
15
+ const IDENTIFIER = /^[$_\p{ID_Start}][$\u200c\u200d\p{ID_Continue}]*$/u
16
+ const AST_METADATA_KEYS = new internal.Set(['start', 'end', 'loc', 'range'])
17
+ const {
18
+ Set: PrivateSet,
19
+ appendArray,
20
+ bigintToString,
21
+ isArray,
22
+ jsonStringify,
23
+ min,
24
+ objectCreate,
25
+ objectGetOwnPropertyDescriptor: ownDescriptor,
26
+ objectHasOwn,
27
+ objectIs,
28
+ objectKeys,
29
+ regexpTest,
30
+ replaceAllString,
31
+ setAdd,
32
+ setHas,
33
+ sliceString,
34
+ toString,
35
+ } = internal
18
36
  const unreadable = () => ({ status: 'unreadable', text: '', truncated: false })
37
+ const runInRealm = (source, context, options) => isContext(context)
38
+ ? runInContext(source, context, options)
39
+ : runInThisContext(source, options)
40
+
41
+ function createPrivateParser() {
42
+ const require = createRequire(import.meta.url)
43
+ const exports = objectCreate(null)
44
+ const module = objectCreate(null)
45
+ module.exports = exports
46
+ const context = createContext({ exports, module })
47
+ runInContext(readFileSync(require.resolve('acorn'), 'utf8'), context,
48
+ { filename: require.resolve('acorn') })
49
+ return module.exports.parse
50
+ }
51
+
52
+ const parse = createPrivateParser()
53
+
54
+ function parseProgram(program) {
55
+ const options = objectCreate(null)
56
+ options.ecmaVersion = 'latest'
57
+ options.allowAwaitOutsideFunction = true
58
+ return parse(program, options)
59
+ }
60
+
61
+ function containsAwait(node) {
62
+ if (node === null || typeof node !== 'object') return false
63
+ if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression'
64
+ || node.type === 'ArrowFunctionExpression') return false
65
+ if (node.type === 'AwaitExpression' || node.await === true) return true
66
+ const keys = objectKeys(node)
67
+ for (let index = 0; index < keys.length; index += 1) {
68
+ const key = keys[index]
69
+ if (setHas(AST_METADATA_KEYS, key)) continue
70
+ const value = node[key]
71
+ if (isArray(value)) {
72
+ for (let child = 0; child < value.length; child += 1) {
73
+ if (containsAwait(value[child])) return true
74
+ }
75
+ } else if (containsAwait(value)) return true
76
+ }
77
+ return false
78
+ }
79
+
80
+ function recordBindingNames(pattern, names) {
81
+ if (pattern === null || typeof pattern !== 'object') return
82
+ if (pattern.type === 'Identifier') {
83
+ setAdd(names, pattern.name)
84
+ return
85
+ }
86
+ if (pattern.type === 'RestElement') return recordBindingNames(pattern.argument, names)
87
+ if (pattern.type === 'AssignmentPattern') return recordBindingNames(pattern.left, names)
88
+ if (pattern.type === 'ArrayPattern') {
89
+ for (let index = 0; index < pattern.elements.length; index += 1) {
90
+ recordBindingNames(pattern.elements[index], names)
91
+ }
92
+ return
93
+ }
94
+ if (pattern.type === 'ObjectPattern') {
95
+ for (let index = 0; index < pattern.properties.length; index += 1) {
96
+ const property = pattern.properties[index]
97
+ recordBindingNames(property.type === 'RestElement' ? property.argument : property.value, names)
98
+ }
99
+ }
100
+ }
19
101
 
20
102
  function primitiveText(value) {
21
- if (typeof value === 'string') return stringify(value.slice(0, MAX_TEXT))
103
+ if (typeof value === 'string') return jsonStringify(sliceString(value, 0, MAX_TEXT))
22
104
  if (typeof value === 'bigint') return value > -BIGINT_LIMIT && value < BIGINT_LIMIT
23
- ? `${bigintText.call(value)}n` : '[bigint: more than 128 digits]'
105
+ ? `${bigintToString(value)}n` : '[bigint: more than 128 digits]'
24
106
  if (typeof value === 'symbol' || typeof value === 'function') return `[${typeof value}: unreadable]`
25
107
  if (value === null) return 'null'
26
108
  if (typeof value === 'object') return isProxy(value) ? '[proxy: unreadable]' : '[object]'
27
- return Object.is(value, -0) ? '-0' : String(value)
109
+ return objectIs(value, -0) ? '-0' : toString(value)
28
110
  }
29
111
 
30
112
  /** Inspect own descriptors only, rejecting proxies before any reflective operation. */
31
113
  export function previewBindingValue(value) {
32
- if (['function', 'symbol'].includes(typeof value)) return unreadable()
114
+ if (typeof value === 'function' || typeof value === 'symbol') return unreadable()
33
115
  if (value === null || typeof value !== 'object') {
34
116
  const text = primitiveText(value)
35
- return { status: 'readable', text: text.slice(0, MAX_TEXT), truncated: text.length > MAX_TEXT
117
+ return { status: 'readable', text: sliceString(text, 0, MAX_TEXT), truncated: text.length > MAX_TEXT
36
118
  || (typeof value === 'bigint' && (value <= -BIGINT_LIMIT || value >= BIGINT_LIMIT)) }
37
119
  }
38
120
  // Only arrays provide a finite slot range without enumerating the whole object.
39
121
  if (isProxy(value) || !isArray(value)) return unreadable()
40
- const keys = Array.from({ length: Math.min(ownDescriptor(value, 'length').value, MAX_PROPERTIES) }, (_value, index) => String(index))
41
- const properties = keys.map(key => {
122
+ const keys = []
123
+ const length = min(ownDescriptor(value, 'length').value, MAX_PROPERTIES)
124
+ for (let index = 0; index < length; index += 1) appendArray(keys, toString(index))
125
+ const properties = []
126
+ for (let index = 0; index < keys.length; index += 1) {
127
+ const key = keys[index]
42
128
  const descriptor = ownDescriptor(value, key)
43
129
  const text = descriptor === undefined ? '[empty]'
44
- : Object.hasOwn(descriptor, 'value') ? primitiveText(descriptor.value) : '[accessor: unreadable]'
45
- return `${stringify(key)}: ${text}`
46
- })
47
- const text = `array { ${properties.join(', ')} }`
130
+ : objectHasOwn(descriptor, 'value') ? primitiveText(descriptor.value) : '[accessor: unreadable]'
131
+ appendArray(properties, `${jsonStringify(key)}: ${text}`)
132
+ }
133
+ let text = 'array { '
134
+ for (let index = 0; index < properties.length; index += 1) {
135
+ if (index > 0) text += ', '
136
+ text += properties[index]
137
+ }
138
+ text += ' }'
48
139
  // Uninspected own properties remain unknown even when all indexed slots fit.
49
- return { status: 'readable', text: text.slice(0, MAX_TEXT), truncated: true }
140
+ return { status: 'readable', text: sliceString(text, 0, MAX_TEXT), truncated: true }
50
141
  }
51
142
 
52
143
  /** Probe the actual evaluator without consulting a Node version or any session value. */
53
144
  export async function supportsAwaitLexicals(context, evaluate) {
54
- const prefix = `__ptc_observation_${randomUUID().replaceAll('-', '')}`
145
+ const prefix = `__ptc_observation_${replaceAllString(randomUUID(), '-', '')}`
55
146
  const names = [`${prefix}_let`, `${prefix}_const`]
56
147
  try {
57
148
  await evaluate(`let ${names[0]} = await Promise.resolve(41); const ${names[1]} = 42;`)
58
- return names.every((name, index) => ownDescriptor(context, name) === undefined
59
- && runInContext(name, context, { timeout: 25 }) === index + 41)
149
+ for (let index = 0; index < names.length; index += 1) {
150
+ if (ownDescriptor(context, names[index]) !== undefined
151
+ || runInRealm(names[index], context, { timeout: 25 }) !== index + 41) return false
152
+ }
153
+ return true
60
154
  } catch {
61
155
  return false
62
156
  } finally {
63
157
  // Lexical probe names are private and never enter the binding inventory.
64
- for (const name of names) delete context[name]
158
+ for (let index = 0; index < names.length; index += 1) {
159
+ deleteWorkerRealmProperty(
160
+ WORKER_REALM_MUTATION.RESTORE,
161
+ 'await lexical probe',
162
+ context,
163
+ names[index],
164
+ )
165
+ }
65
166
  }
66
167
  }
67
168
 
68
169
  /** Successful programs prove storage; await lexical reads additionally require a runtime probe. */
69
170
  export function createReplValueObserver(context, { awaitLexicals = false } = {}) {
70
- const lexicals = new Set()
71
- const globals = new Set()
171
+ const lexicals = new PrivateSet()
172
+ const globals = new PrivateSet()
72
173
  return {
73
174
  record(program) {
74
175
  try {
75
- const tree = parse(program, { ecmaVersion: 'latest', allowAwaitOutsideFunction: true })
76
- let awaits = false
77
- walkAst(tree, node => {
78
- if (['FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression'].includes(node.type)) return SKIP_AST_CHILDREN
79
- if (node.type === 'AwaitExpression' || node.await === true) awaits = true
80
- })
81
- if (awaits && !awaitLexicals) return
82
- for (const statement of tree.body) {
176
+ const tree = parseProgram(program)
177
+ if (containsAwait(tree) && !awaitLexicals) return
178
+ for (let statementIndex = 0; statementIndex < tree.body.length; statementIndex += 1) {
179
+ const statement = tree.body[statementIndex]
83
180
  if (statement.type !== 'VariableDeclaration') continue
84
- for (const declaration of statement.declarations) {
85
- for (const binding of bindingNodes(declaration.id)) {
86
- (statement.kind === 'var' ? globals : lexicals).add(binding.name)
87
- }
181
+ const names = statement.kind === 'var' ? globals : lexicals
182
+ for (let index = 0; index < statement.declarations.length; index += 1) {
183
+ recordBindingNames(statement.declarations[index].id, names)
88
184
  }
89
185
  }
90
186
  } catch {}
91
187
  },
92
188
  observe(names) {
93
189
  const at = now()
94
- const entries = names.slice(0, MAX_ENTRIES).map(name => {
190
+ const entries = []
191
+ const length = min(names.length, MAX_ENTRIES)
192
+ for (let index = 0; index < length; index += 1) {
193
+ const name = names[index]
95
194
  let preview = unreadable()
96
195
  try {
97
- if (lexicals.has(name)) {
196
+ if (setHas(lexicals, name)) {
98
197
  // Lexical storage shadows global properties, even while uninitialized.
99
- if (/^[$_\p{ID_Start}][$\u200c\u200d\p{ID_Continue}]*$/u.test(name) && now() - at < 100) {
100
- preview = previewBindingValue(runInContext(name, context, { timeout: 25, displayErrors: false }))
198
+ if (regexpTest(IDENTIFIER, name) && now() - at < 100) {
199
+ preview = previewBindingValue(runInRealm(name, context, { timeout: 25, displayErrors: false }))
101
200
  }
102
- } else if (globals.has(name)) {
201
+ } else if (setHas(globals, name)) {
103
202
  const descriptor = ownDescriptor(context, name)
104
- if (descriptor !== undefined && Object.hasOwn(descriptor, 'value')) {
203
+ if (descriptor !== undefined && objectHasOwn(descriptor, 'value')) {
105
204
  preview = previewBindingValue(descriptor.value)
106
205
  }
107
206
  }
108
207
  } catch {}
109
- return { name, ...preview }
110
- })
208
+ appendArray(entries, { name, ...preview })
209
+ }
111
210
  return { at, entries }
112
211
  },
113
212
  }