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
@@ -8,10 +8,13 @@ export function walkAst(node, enter, leave = undefined, context = undefined, par
8
8
  const entered = enter(node, parent, parentKey, context)
9
9
  if (entered === SKIP_AST_CHILDREN) return
10
10
  const childContext = entered === undefined ? context : entered
11
- for (const [key, value] of Object.entries(node)) {
11
+ const keys = Object.keys(node)
12
+ for (let index = 0; index < keys.length; index++) {
13
+ const key = keys[index]
12
14
  if (AST_METADATA_KEYS.has(key)) continue
15
+ const value = node[key]
13
16
  if (Array.isArray(value)) {
14
- for (const child of value) walkAst(child, enter, leave, childContext, node, key)
17
+ for (let child = 0; child < value.length; child++) walkAst(value[child], enter, leave, childContext, node, key)
15
18
  } else {
16
19
  walkAst(value, enter, leave, childContext, node, key)
17
20
  }
@@ -86,6 +86,7 @@ export function normalizeBindingDescriptors(bindings) {
86
86
  workerDescriptors: Object.freeze(namespaces.map(namespace => Object.freeze({
87
87
  global: namespace.global,
88
88
  members: namespace.members,
89
+ ...(reservedNames.has(namespace.global) ? {} : { shadowable: true }),
89
90
  ...(namespace.emptyObjectMembers === undefined
90
91
  ? {}
91
92
  : { emptyObjectMembers: namespace.emptyObjectMembers }),
@@ -0,0 +1,25 @@
1
+ /** Read binding names from Acorn or Babel patterns without compiler dependencies. */
2
+ export function walkBindingPattern(pattern, visit) {
3
+ if (pattern === null || typeof pattern !== 'object') return
4
+ if (pattern.type === 'Identifier') {
5
+ visit(pattern)
6
+ return
7
+ }
8
+ if (pattern.type === 'RestElement') return walkBindingPattern(pattern.argument, visit)
9
+ if (pattern.type === 'AssignmentPattern') return walkBindingPattern(pattern.left, visit)
10
+ if (pattern.type === 'ArrayPattern') {
11
+ for (const element of pattern.elements) walkBindingPattern(element, visit)
12
+ return
13
+ }
14
+ if (pattern.type === 'ObjectPattern') {
15
+ for (const property of pattern.properties) {
16
+ walkBindingPattern(property.type === 'RestElement' ? property.argument : property.value, visit)
17
+ }
18
+ }
19
+ }
20
+
21
+ export function bindingNodes(pattern) {
22
+ const nodes = []
23
+ walkBindingPattern(pattern, node => nodes.push(node))
24
+ return nodes
25
+ }
@@ -1,51 +1,48 @@
1
1
  /** Shared binding-pattern traversal used by cell analysis and REPL policy code. */
2
+ import { types as t } from '@babel/core'
2
3
 
3
- export function walkBindingPattern(pattern, visit) {
4
- if (pattern === null || typeof pattern !== 'object') return
5
- if (pattern.type === 'Identifier') {
6
- visit(pattern)
7
- return
8
- }
9
- if (pattern.type === 'RestElement') return walkBindingPattern(pattern.argument, visit)
10
- if (pattern.type === 'AssignmentPattern') return walkBindingPattern(pattern.left, visit)
11
- if (pattern.type === 'ArrayPattern') {
12
- for (const element of pattern.elements) walkBindingPattern(element, visit)
13
- return
14
- }
15
- if (pattern.type === 'ObjectPattern') {
16
- for (const property of pattern.properties) {
17
- walkBindingPattern(property.type === 'RestElement' ? property.argument : property.value, visit)
18
- }
19
- }
20
- }
4
+ export { walkBindingPattern, bindingNodes } from './binding-pattern-traversal.js'
21
5
 
22
- export function bindingNodes(pattern) {
23
- const nodes = []
24
- walkBindingPattern(pattern, node => nodes.push(node))
25
- return nodes
6
+ /** Recognize an actual assignment target through erased expression syntax. */
7
+ export function isWriteIdentifier(path) {
8
+ let selected = path
9
+ while (selected.parentPath?.isObjectProperty() && selected.key === 'value'
10
+ || selected.parentPath?.isObjectPattern() || selected.parentPath?.isArrayPattern()
11
+ || selected.parentPath?.isRestElement() && selected.key === 'argument'
12
+ || selected.parentPath?.isAssignmentPattern() && selected.key === 'left'
13
+ || selected.parent?.type.startsWith('TS') && selected.key === 'expression'
14
+ || selected.parentPath?.isParenthesizedExpression() && selected.key === 'expression') selected = selected.parentPath
15
+ const parent = selected.parentPath
16
+ return parent?.isAssignmentExpression() && selected.key === 'left'
17
+ || parent?.isUpdateExpression() && selected.key === 'argument'
18
+ || (parent?.isForOfStatement() || parent?.isForInStatement()) && selected.key === 'left'
26
19
  }
27
20
 
28
21
  /** Allocate private identifiers outside both source and caller-owned name sets. */
29
- export function createGeneratedNameAllocator(root, unavailableNames = []) {
22
+ export function createGeneratedNameAllocator(root, unavailableNames = [], { compact = false } = {}) {
30
23
  const used = new Set(unavailableNames)
31
- const seen = new Set()
32
24
  const pending = [root]
33
25
  while (pending.length > 0) {
34
26
  const value = pending.pop()
35
- if (value === null || typeof value !== 'object' || seen.has(value)) continue
36
- seen.add(value)
27
+ if (value === null || typeof value !== 'object') continue
28
+ if (Array.isArray(value)) {
29
+ for (const item of value) pending.push(item)
30
+ continue
31
+ }
37
32
  if (value.type === 'Identifier') used.add(value.name)
38
- for (const child of Object.values(value)) {
39
- if (Array.isArray(child)) pending.push(...child)
33
+ for (const key of t.VISITOR_KEYS[value.type] ?? Object.keys(value)) {
34
+ const child = value[key]
35
+ if (Array.isArray(child)) for (const item of child) pending.push(item)
40
36
  else if (child !== null && typeof child === 'object') pending.push(child)
41
37
  }
42
38
  }
43
39
  const sequences = new Map()
40
+ let nextCompact = 0
44
41
  return (purpose) => {
45
42
  let sequence = sequences.get(purpose) ?? 0
46
43
  let name
47
44
  do {
48
- name = `__dsh_ptc_${purpose}_${sequence++}__`
45
+ name = compact ? `__ptc$${(nextCompact++).toString(36)}` : `__dsh_ptc_${purpose}_${sequence++}__`
49
46
  } while (used.has(name))
50
47
  sequences.set(purpose, sequence)
51
48
  used.add(name)
@@ -0,0 +1,391 @@
1
+ import { parse } from '@babel/parser'
2
+ import { AMBIENT_GLOBALS, renderDurabilityReasons } from './module-policy.js'
3
+ import { CELL_PARSER_PLUGINS, normalizeTypeScriptValues } from './repl-scope-normalizer.js'
4
+ import { identitySourceMap } from './source-position-map.js'
5
+ import { LEGACY_USER_BINDING_TRANSFORM, transformTypeScriptSource } from './typescript-transform.js'
6
+ import { compileStatefulModule } from './module-compilation.js'
7
+ import { prepareProgram, classifyDurability } from './cell-analysis.js'
8
+ import { createCompilerSourceCache } from './compiler-source-cache.js'
9
+
10
+ const MAX_NAME_LENGTH = 128
11
+ const GLOBAL_TYPE_NAMES = new Set([
12
+ 'Array', 'ArrayBuffer', 'ArrayBufferView', 'AsyncIterable', 'AsyncIterableIterator',
13
+ 'AsyncIterator', 'Awaited', 'BigInt', 'BigInt64Array', 'BigUint64Array', 'Boolean',
14
+ 'Buffer', 'ConstructorParameters', 'DataView', 'Date', 'Error', 'EvalError', 'Exclude',
15
+ 'Extract', 'Float32Array', 'Float64Array', 'Function', 'Generator', 'GeneratorFunction',
16
+ 'InstanceType', 'Int8Array', 'Int16Array', 'Int32Array', 'Iterable', 'IterableIterator',
17
+ 'Iterator', 'Map', 'NonNullable', 'Number', 'Object', 'Omit', 'OmitThisParameter',
18
+ 'Parameters', 'Partial', 'Pick', 'Promise', 'PromiseLike', 'RangeError', 'Readonly',
19
+ 'ReadonlyArray', 'ReadonlyMap', 'ReadonlySet', 'Record', 'ReferenceError', 'RegExp',
20
+ 'Required', 'ReturnType', 'Set', 'SharedArrayBuffer', 'String', 'Symbol', 'SyntaxError',
21
+ 'ThisParameterType', 'ThisType', 'TypeError', 'URIError', 'Uint8Array', 'Uint8ClampedArray',
22
+ 'Uint16Array', 'Uint32Array', 'Uppercase', 'Lowercase', 'Capitalize', 'Uncapitalize',
23
+ 'WeakMap', 'WeakSet',
24
+ ])
25
+ const RESERVED_BINDINGS = new Set([
26
+ ...AMBIENT_GLOBALS,
27
+ 'capabilities',
28
+ 'code',
29
+ 'repl',
30
+ 'tools',
31
+ 'CapabilityExplorationError',
32
+ 'CodeExecutionError',
33
+ 'ToolCallError',
34
+ 'Buffer',
35
+ 'console',
36
+ 'global',
37
+ 'globalThis',
38
+ 'module',
39
+ 'process',
40
+ ])
41
+
42
+ export function identifier(value, subject, reserved = false) {
43
+ if (typeof value !== 'string' || value.length === 0 || value.length > MAX_NAME_LENGTH) {
44
+ throw new TypeError(`${subject} must be a non-empty identifier of at most ${MAX_NAME_LENGTH} characters`)
45
+ }
46
+ try {
47
+ const { program } = parse(`const ${value} = 0`, { sourceType: 'module' })
48
+ const declaration = program.body[0]
49
+ const binding = declaration.declarations[0].id
50
+ if (program.body.length !== 1 || declaration.declarations.length !== 1
51
+ || binding.type !== 'Identifier' || binding.name !== value) {
52
+ throw new TypeError('identifier spelling must match the complete binding name')
53
+ }
54
+ } catch {
55
+ throw new TypeError(`${subject} must be a valid JavaScript identifier`)
56
+ }
57
+ if (reserved && RESERVED_BINDINGS.has(value)) {
58
+ throw new TypeError(`${subject} conflicts with reserved REPL binding ${JSON.stringify(value)}`)
59
+ }
60
+ return value
61
+ }
62
+
63
+ function sourceSlice(source, node) {
64
+ return Number.isSafeInteger(node?.start) && Number.isSafeInteger(node?.end)
65
+ ? source.slice(node.start, node.end)
66
+ : ''
67
+ }
68
+
69
+ function bindingNames(pattern, names = []) {
70
+ if (pattern?.type === 'Identifier') names.push(pattern.name)
71
+ else if (pattern?.type === 'RestElement') bindingNames(pattern.argument, names)
72
+ else if (pattern?.type === 'AssignmentPattern') bindingNames(pattern.left, names)
73
+ else if (pattern?.type === 'ArrayPattern') {
74
+ for (const element of pattern.elements) bindingNames(element, names)
75
+ } else if (pattern?.type === 'ObjectPattern') {
76
+ for (const property of pattern.properties) {
77
+ bindingNames(property.type === 'RestElement' ? property.argument : property.value, names)
78
+ }
79
+ }
80
+ return names
81
+ }
82
+
83
+ function typeParameterNames(node) {
84
+ return (node?.params ?? [])
85
+ .map(parameter => parameter?.name)
86
+ .filter(name => typeof name === 'string')
87
+ }
88
+
89
+ function typeReferenceRoot(node) {
90
+ let current = node
91
+ while (current?.type === 'TSQualifiedName') current = current.left
92
+ return current?.type === 'Identifier' ? current.name : undefined
93
+ }
94
+
95
+ function projectedType(source, node, outerTypeNames = new Set(), sourceTypeNames = new Set()) {
96
+ const replacements = []
97
+ const commentRanges = new Set()
98
+ const visit = (current, inheritedNames) => {
99
+ if (current === null || typeof current !== 'object') return
100
+ if (current.type === 'CommentBlock' || current.type === 'CommentLine') {
101
+ if (Number.isSafeInteger(current.start) && Number.isSafeInteger(current.end)
102
+ && current.start >= node.start && current.end <= node.end) {
103
+ const key = `${current.start}:${current.end}`
104
+ if (!commentRanges.has(key)) {
105
+ commentRanges.add(key)
106
+ replacements.push({ start: current.start, end: current.end, text: ' ' })
107
+ }
108
+ }
109
+ return
110
+ }
111
+ let typeNames = inheritedNames
112
+ if (current.typeParameters?.type === 'TSTypeParameterDeclaration') {
113
+ typeNames = new Set([...inheritedNames, ...typeParameterNames(current.typeParameters)])
114
+ }
115
+ if (current.type === 'TSMappedType' && typeof current.typeParameter?.name === 'string') {
116
+ typeNames = new Set([...typeNames, current.typeParameter.name])
117
+ }
118
+ if (current.type === 'TSImportType' || current.type === 'TSTypeQuery') {
119
+ replacements.push({ start: current.start, end: current.end, text: 'unknown' })
120
+ return
121
+ }
122
+ if (current.type === 'TSTypeReference' || current.type === 'TSExpressionWithTypeArguments') {
123
+ const name = typeReferenceRoot(current.typeName ?? current.expression)
124
+ if (name === undefined || (!typeNames.has(name)
125
+ && (sourceTypeNames.has(name) || !GLOBAL_TYPE_NAMES.has(name)))) {
126
+ replacements.push({ start: current.start, end: current.end, text: 'unknown' })
127
+ return
128
+ }
129
+ }
130
+ for (const [key, value] of Object.entries(current)) {
131
+ if (key === 'loc' || key === 'start' || key === 'end'
132
+ || key === 'typeParameters' || key === 'typeName' || key === 'expression') continue
133
+ if (Array.isArray(value)) value.forEach(item => visit(item, typeNames))
134
+ else visit(value, typeNames)
135
+ }
136
+ if (current.typeParameters !== undefined) visit(current.typeParameters, typeNames)
137
+ }
138
+ visit(node, outerTypeNames)
139
+ let text = sourceSlice(source, node) || 'unknown'
140
+ const start = node.start ?? 0
141
+ for (const replacement of replacements.sort((left, right) => right.start - left.start)) {
142
+ text = text.slice(0, replacement.start - start)
143
+ + replacement.text
144
+ + text.slice(replacement.end - start)
145
+ }
146
+ return text
147
+ }
148
+
149
+ function typeParameters(source, node, outerTypeNames = new Set(), sourceTypeNames = new Set()) {
150
+ const declaration = node?.typeParameters
151
+ if (declaration?.type !== 'TSTypeParameterDeclaration') {
152
+ return { text: '', names: outerTypeNames }
153
+ }
154
+ const names = new Set([...outerTypeNames, ...typeParameterNames(declaration)])
155
+ return { text: projectedType(source, declaration, names, sourceTypeNames), names }
156
+ }
157
+
158
+ function annotation(source, node, typeNames = new Set(), sourceTypeNames = new Set()) {
159
+ const type = node?.typeAnnotation?.typeAnnotation
160
+ return type === undefined ? 'unknown' : projectedType(source, type, typeNames, sourceTypeNames)
161
+ }
162
+
163
+ function parameter(source, node, index, typeNames = new Set(), sourceTypeNames = new Set()) {
164
+ if (node?.type === 'TSParameterProperty') {
165
+ return parameter(source, node.parameter, index, typeNames, sourceTypeNames)
166
+ }
167
+ if (node?.type === 'RestElement') {
168
+ const argument = node.argument
169
+ return argument?.type === 'Identifier'
170
+ ? `...${argument.name}: ${node.typeAnnotation === undefined ? 'unknown[]' : annotation(source, node, typeNames, sourceTypeNames)}`
171
+ : `...args${index}: unknown[]`
172
+ }
173
+ const optional = node?.type === 'AssignmentPattern'
174
+ const target = optional ? node.left : node
175
+ if (target?.type !== 'Identifier') return `arg${index}: unknown`
176
+ return `${target.name}${optional || target.optional === true ? '?' : ''}: ${annotation(source, target, typeNames, sourceTypeNames)}`
177
+ }
178
+
179
+ function returnType(source, node, typeNames = new Set(), sourceTypeNames = new Set()) {
180
+ const explicit = node?.returnType?.typeAnnotation
181
+ if (explicit !== undefined) return projectedType(source, explicit, typeNames, sourceTypeNames)
182
+ return node?.async === true ? 'Promise<unknown>' : 'unknown'
183
+ }
184
+
185
+ function functionType(source, node, outerTypeNames = new Set(), sourceTypeNames = new Set()) {
186
+ const generics = typeParameters(source, node, outerTypeNames, sourceTypeNames)
187
+ const parameters = (node.params ?? [])
188
+ .map((item, index) => parameter(source, item, index, generics.names, sourceTypeNames)).join(', ')
189
+ return `${generics.text}(${parameters}) => ${returnType(source, node, generics.names, sourceTypeNames)}`
190
+ }
191
+
192
+ function inferredType(source, node, depth = 0, sourceTypeNames = new Set()) {
193
+ if (depth > 2 || node === null || node === undefined) return 'unknown'
194
+ if (node.type === 'StringLiteral' || node.type === 'TemplateLiteral') return 'string'
195
+ if (node.type === 'NumericLiteral') return 'number'
196
+ if (node.type === 'BooleanLiteral') return 'boolean'
197
+ if (node.type === 'BigIntLiteral') return 'bigint'
198
+ if (node.type === 'NullLiteral') return 'null'
199
+ if (node.type === 'ArrowFunctionExpression' || node.type === 'FunctionExpression') {
200
+ return functionType(source, node, new Set(), sourceTypeNames)
201
+ }
202
+ if (node.type === 'ArrayExpression') return 'unknown[]'
203
+ if (node.type === 'ObjectExpression') {
204
+ const members = []
205
+ for (const property of node.properties) {
206
+ if (property.type !== 'ObjectProperty' || property.computed || property.method) return 'Record<string, unknown>'
207
+ const name = property.key.type === 'Identifier'
208
+ ? property.key.name
209
+ : property.key.type === 'StringLiteral' ? JSON.stringify(property.key.value) : undefined
210
+ if (name === undefined) return 'Record<string, unknown>'
211
+ members.push(`${name}: ${inferredType(source, property.value, depth + 1, sourceTypeNames)}`)
212
+ }
213
+ return `{ ${members.join('; ')} }`
214
+ }
215
+ return 'unknown'
216
+ }
217
+
218
+ function classMembers(source, node, classTypeNames, sourceTypeNames) {
219
+ const members = []
220
+ let constructorParameters = '...args: unknown[]'
221
+ for (const member of node.body?.body ?? []) {
222
+ if (member.type !== 'ClassMethod' || member.static || member.computed
223
+ || member.accessibility === 'private' || member.key?.type === 'PrivateName') continue
224
+ const name = member.key?.type === 'Identifier'
225
+ ? member.key.name
226
+ : member.key?.type === 'StringLiteral' ? JSON.stringify(member.key.value) : undefined
227
+ if (name === undefined) continue
228
+ const generics = typeParameters(source, member, classTypeNames, sourceTypeNames)
229
+ const parameters = (member.params ?? [])
230
+ .map((item, index) => parameter(source, item, index, generics.names, sourceTypeNames)).join(', ')
231
+ if (member.kind === 'constructor') constructorParameters = parameters
232
+ else if (member.kind === 'method') {
233
+ members.push(`${name}${generics.text}(${parameters}): ${returnType(source, member, generics.names, sourceTypeNames)}`)
234
+ }
235
+ }
236
+ return { constructorParameters, members, instance: `{ ${members.join('; ')} }` }
237
+ }
238
+
239
+ function jsdocPurpose(...nodes) {
240
+ const comment = nodes.flatMap(node => node?.leadingComments ?? [])
241
+ .find(item => item.type === 'CommentBlock' && item.value.startsWith('*'))
242
+ if (comment === undefined) return ''
243
+ return comment.value.slice(1).split(/\r?\n/)
244
+ .map(line => line.replace(/^\s*\*?\s?/, '').trim())
245
+ .find(line => line !== '' && !line.startsWith('@')) ?? ''
246
+ }
247
+
248
+ function symbolDescriptor(source, name, node, exportNode = node, sourceTypeNames = new Set()) {
249
+ if (node?.type === 'FunctionDeclaration') {
250
+ const generics = typeParameters(source, node, new Set(), sourceTypeNames)
251
+ const parameters = (node.params ?? [])
252
+ .map((item, index) => parameter(source, item, index, generics.names, sourceTypeNames)).join(', ')
253
+ return {
254
+ name,
255
+ kind: 'function',
256
+ purpose: jsdocPurpose(exportNode, node),
257
+ declaration: `function ${name}${generics.text}(${parameters}): ${returnType(source, node, generics.names, sourceTypeNames)}`,
258
+ member: `${name}${generics.text}(${parameters}): ${returnType(source, node, generics.names, sourceTypeNames)}`,
259
+ }
260
+ }
261
+ if (node?.type === 'ClassDeclaration') {
262
+ const generics = typeParameters(source, node, new Set(), sourceTypeNames)
263
+ const shape = classMembers(source, node, generics.names, sourceTypeNames)
264
+ return {
265
+ name,
266
+ kind: 'class',
267
+ purpose: jsdocPurpose(exportNode, node),
268
+ declaration: `class ${name}${generics.text} { constructor(${shape.constructorParameters});${shape.members.length === 0 ? '' : ` ${shape.members.join('; ')};`} }`,
269
+ member: `${name}: { new${generics.text}(${shape.constructorParameters}): ${shape.instance} }`,
270
+ }
271
+ }
272
+ const declarator = node?.type === 'VariableDeclarator' ? node : undefined
273
+ const type = declarator?.id?.type !== 'Identifier'
274
+ ? 'unknown'
275
+ : declarator.id.typeAnnotation !== undefined
276
+ ? annotation(source, declarator.id, new Set(), sourceTypeNames)
277
+ : inferredType(source, declarator.init, 0, sourceTypeNames)
278
+ return {
279
+ name,
280
+ kind: 'variable',
281
+ purpose: jsdocPurpose(exportNode, node),
282
+ declaration: `const ${name}: ${type}`,
283
+ member: `${name}: ${type}`,
284
+ }
285
+ }
286
+
287
+ export const exportedSymbols = createCompilerSourceCache(analyzeExportedSymbols)
288
+
289
+ function analyzeExportedSymbols(source, transform) {
290
+ let ast
291
+ try {
292
+ ast = parse(source, {
293
+ sourceType: 'module',
294
+ plugins: transform === LEGACY_USER_BINDING_TRANSFORM
295
+ ? ['typescript', 'topLevelAwait', 'importAttributes'] : CELL_PARSER_PLUGINS,
296
+ errorRecovery: transform !== LEGACY_USER_BINDING_TRANSFORM,
297
+ })
298
+ } catch (error) {
299
+ throw new SyntaxError(`binding source could not be parsed: ${error.message}`)
300
+ }
301
+ const sourceTypeNames = new Set()
302
+ for (const statement of ast.program.body) {
303
+ const declaration = statement.type === 'ExportNamedDeclaration' ? statement.declaration : statement
304
+ if (statement.type === 'ImportDeclaration') {
305
+ for (const specifier of statement.specifiers) sourceTypeNames.add(specifier.local.name)
306
+ }
307
+ if (declaration?.type === 'TSTypeAliasDeclaration'
308
+ || declaration?.type === 'TSInterfaceDeclaration'
309
+ || declaration?.type === 'TSEnumDeclaration'
310
+ || declaration?.type === 'TSModuleDeclaration'
311
+ || declaration?.type === 'ClassDeclaration') {
312
+ if (declaration.id?.type === 'Identifier') sourceTypeNames.add(declaration.id.name)
313
+ }
314
+ }
315
+ if (transform !== LEGACY_USER_BINDING_TRANSFORM) {
316
+ source = normalizeTypeScriptValues(source, identitySourceMap(source.length), 'module').code
317
+ ast = parse(source, { sourceType: 'module', plugins: CELL_PARSER_PLUGINS, errorRecovery: true })
318
+ }
319
+ const locals = new Map()
320
+ for (const statement of ast.program.body) {
321
+ const declaration = statement.type === 'ExportNamedDeclaration' ? statement.declaration : statement
322
+ if (declaration?.type === 'FunctionDeclaration' || declaration?.type === 'ClassDeclaration') {
323
+ if (declaration.id !== null) locals.set(declaration.id.name, declaration)
324
+ } else if (declaration?.type === 'VariableDeclaration') {
325
+ for (const item of declaration.declarations) {
326
+ for (const name of bindingNames(item.id)) locals.set(name, item)
327
+ }
328
+ }
329
+ }
330
+ const exports = new Map()
331
+ for (const statement of ast.program.body) {
332
+ if (statement.type === 'ExportDefaultDeclaration' || statement.type === 'ExportAllDeclaration') {
333
+ throw new TypeError('binding source supports named exports only')
334
+ }
335
+ if (statement.type !== 'ExportNamedDeclaration' || statement.exportKind === 'type') continue
336
+ if (statement.source !== null && statement.source !== undefined) {
337
+ throw new TypeError('binding source cannot re-export from another module')
338
+ }
339
+ const declaration = statement.declaration
340
+ if (declaration?.type === 'FunctionDeclaration' || declaration?.type === 'ClassDeclaration') {
341
+ if (declaration.id === null) throw new TypeError('binding source exports must be named')
342
+ exports.set(declaration.id.name, symbolDescriptor(
343
+ source, declaration.id.name, declaration, statement, sourceTypeNames,
344
+ ))
345
+ continue
346
+ }
347
+ if (declaration?.type === 'VariableDeclaration') {
348
+ for (const item of declaration.declarations) {
349
+ for (const name of bindingNames(item.id)) {
350
+ exports.set(name, symbolDescriptor(source, name, item, statement, sourceTypeNames))
351
+ }
352
+ }
353
+ continue
354
+ }
355
+ for (const specifier of statement.specifiers) {
356
+ if (specifier.exportKind === 'type' || specifier.type !== 'ExportSpecifier') continue
357
+ const exported = specifier.exported.type === 'Identifier' ? specifier.exported.name : specifier.exported.value
358
+ const local = specifier.local.type === 'Identifier' ? specifier.local.name : specifier.local.value
359
+ identifier(exported, 'exported binding name')
360
+ const target = locals.get(local)
361
+ if (target === undefined) throw new TypeError(`export ${JSON.stringify(exported)} has no local value declaration`)
362
+ exports.set(exported, symbolDescriptor(source, exported, target, target, sourceTypeNames))
363
+ }
364
+ }
365
+ if (exports.size === 0) throw new TypeError('binding source must contain at least one named value export')
366
+ return exports
367
+ }
368
+
369
+ export const sourceDurability = createCompilerSourceCache(analyzeSourceDurability)
370
+
371
+ function analyzeSourceDurability(source, transform) {
372
+ const compiled = compileStatefulModule(source, { transform })
373
+ // Keep source imports visible to durability analysis after runtime adaptation.
374
+ const javascript = transformTypeScriptSource(compiled.classificationCode,
375
+ transform === LEGACY_USER_BINDING_TRANSFORM ? {} : { module: true, transform: { tsEnumIsMutable: true } }).code
376
+ const prepared = transform !== LEGACY_USER_BINDING_TRANSFORM
377
+ ? classifyDurability(javascript, new Set(), { sourceType: 'module',
378
+ internalModules: new Set([new URL('./stateful-module-runtime.js', import.meta.url).href]) })
379
+ : prepareProgram(javascript, {
380
+ languageSemantics: 'legacy-v1',
381
+ knownBindings: new Set(),
382
+ bindingPolicy: { variableRedeclarations: true, functionClassRedeclarations: true },
383
+ reservedBindings: new Set(),
384
+ rewritesEnabled: { autoRewriteImports: true, autoStripExports: true, autoSplitRedeclarations: true },
385
+ })
386
+ const reason = prepared.reason ?? renderDurabilityReasons(prepared.reasons)
387
+ return {
388
+ durability: prepared.durability,
389
+ ...(reason === '' ? {} : { volatileReason: reason }),
390
+ }
391
+ }
@@ -0,0 +1,66 @@
1
+ import { LIVE_MODULE_SEMANTICS } from './repl-rewrite-contract.js'
2
+ import { LEGACY_LANGUAGE_SEMANTICS, STATEFUL_LANGUAGE_SEMANTICS,
3
+ PROTECTED_LANGUAGE_SEMANTICS, normalizeLanguageSemantics } from './language-semantics.js'
4
+
5
+ /**
6
+ * Translate the public compatibility settings into the execution policy used
7
+ * by the compiler and journal. Keeping this mapping in one owner prevents
8
+ * individual lowering paths from combining legacy switches differently.
9
+ */
10
+ export function executionPolicies(config, replayRecord = undefined) {
11
+ if (config.bindingUpdates !== undefined && !['stateful', 'protected'].includes(config.bindingUpdates)) {
12
+ throw new TypeError('ptc-plus: bindingUpdates must be stateful or protected')
13
+ }
14
+ if (replayRecord !== undefined) {
15
+ const bindingPolicy = Object.freeze({ ...replayRecord.bindingPolicy })
16
+ const rewritesEnabled = Object.freeze({ ...replayRecord.rewritePolicy })
17
+ const moduleSemantics = Object.freeze({ ...replayRecord.moduleSemantics })
18
+ return Object.freeze({
19
+ languageSemantics: normalizeLanguageSemantics(replayRecord.languageSemantics ?? LEGACY_LANGUAGE_SEMANTICS),
20
+ bindingPolicy,
21
+ rewritesEnabled,
22
+ moduleSemantics,
23
+ })
24
+ }
25
+ if (config.bindingUpdates === 'stateful' && config.legacyBindingSettings !== true) {
26
+ return Object.freeze({
27
+ languageSemantics: STATEFUL_LANGUAGE_SEMANTICS,
28
+ bindingPolicy: Object.freeze({ variableRedeclarations: true, functionClassRedeclarations: true }),
29
+ rewritesEnabled: Object.freeze({ autoRewriteImports: true, autoStripExports: true, autoSplitRedeclarations: true }),
30
+ moduleSemantics: LIVE_MODULE_SEMANTICS,
31
+ })
32
+ }
33
+ if (config.bindingUpdates === 'protected' && config.legacyBindingSettings !== true) {
34
+ return Object.freeze({
35
+ languageSemantics: PROTECTED_LANGUAGE_SEMANTICS,
36
+ bindingPolicy: Object.freeze({ variableRedeclarations: false, functionClassRedeclarations: false }),
37
+ // Module syntax is part of the language in protected mode too. The
38
+ // policy protects existing names; it does not remove valid syntax.
39
+ rewritesEnabled: Object.freeze({ autoRewriteImports: true, autoStripExports: true, autoSplitRedeclarations: true }),
40
+ moduleSemantics: LIVE_MODULE_SEMANTICS,
41
+ })
42
+ }
43
+ return Object.freeze({
44
+ languageSemantics: LEGACY_LANGUAGE_SEMANTICS,
45
+ bindingPolicy: Object.freeze({
46
+ variableRedeclarations: config.looseTopLevelRedeclarations === true,
47
+ functionClassRedeclarations: config.looseTopLevelFunctionClassRedeclarations === true,
48
+ }),
49
+ rewritesEnabled: Object.freeze({
50
+ autoRewriteImports: config.autoRewriteImports === true,
51
+ autoStripExports: config.autoStripExports === true,
52
+ autoSplitRedeclarations: config.autoSplitRedeclarations === true,
53
+ }),
54
+ moduleSemantics: LIVE_MODULE_SEMANTICS,
55
+ })
56
+ }
57
+
58
+ /** Stable prose inputs for the model-visible REPL guidance. */
59
+ export function guidancePolicies(config) {
60
+ const policies = executionPolicies(config)
61
+ return Object.freeze({
62
+ ...policies,
63
+ durableReplay: config.durableReplay === true,
64
+ cordisToolsEnabled: config.cordisToolsEnabled === true,
65
+ })
66
+ }
@@ -0,0 +1,56 @@
1
+ /** How a bounded worker round ended without a result. */
2
+ export const ROUND_TIMEOUT = 'timeout'
3
+ export const ROUND_CANCELLED = 'cancelled'
4
+ export const ROUND_OUTPUT_LIMIT = 'output-limit'
5
+
6
+ /**
7
+ * Own one bounded worker round: captured output bytes, the wall-clock deadline,
8
+ * cancellation and a single settlement. Worker creation, retention and
9
+ * termination stay with the caller, which is told exactly once how the round
10
+ * ended through `settle({ ok: true, result })` or `settle({ ok: false, reason })`.
11
+ */
12
+ export function createBoundedWorkerRound({ maxOutputBytes, maxWallMs, signal, settle }) {
13
+ const logs = []
14
+ let capturedBytes = 0
15
+ let ended = false
16
+ const end = (outcome) => {
17
+ if (ended) return false
18
+ ended = true
19
+ clearTimeout(timer)
20
+ signal?.removeEventListener('abort', abort)
21
+ settle(outcome)
22
+ return true
23
+ }
24
+ const fail = reason => end({ ok: false, reason })
25
+ const abort = () => { fail(ROUND_CANCELLED) }
26
+ const timer = setTimeout(() => fail(ROUND_TIMEOUT), maxWallMs)
27
+ signal?.addEventListener('abort', abort, { once: true })
28
+ if (signal?.aborted === true) fail(ROUND_CANCELLED)
29
+ return Object.freeze({
30
+ logs,
31
+ /** Record one stream chunk; ends the round when the byte budget is exceeded. */
32
+ capture(channel, text) {
33
+ if (ended) return
34
+ capturedBytes += Buffer.byteLength(text, 'utf8')
35
+ if (capturedBytes > maxOutputBytes) { fail(ROUND_OUTPUT_LIMIT); return }
36
+ logs.push({ channel, text })
37
+ },
38
+ /**
39
+ * True when the byte budget is spent.
40
+ *
41
+ * `envelope` measures the object the caller returns with the captured `logs`
42
+ * included as a member, so log wrappers and JSON escaping count against the
43
+ * same budget; that is the candidate run's returned-envelope budget. Without
44
+ * it the raw captured bytes plus the serialized payload are measured, which is
45
+ * the workbench console's output budget.
46
+ */
47
+ exceeds(payload, { envelope = false } = {}) {
48
+ if (envelope) {
49
+ return Buffer.byteLength(JSON.stringify({ ...payload, logs }), 'utf8') > maxOutputBytes
50
+ }
51
+ return capturedBytes + Buffer.byteLength(JSON.stringify(payload), 'utf8') > maxOutputBytes
52
+ },
53
+ succeed(result) { return end({ ok: true, result }) },
54
+ fail,
55
+ })
56
+ }