dsh-ptc-plus 0.3.2 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/README.en.md +65 -78
  2. package/README.md +65 -78
  3. package/assets/ptc-plus-bindings-en.png +0 -0
  4. package/assets/ptc-plus-bindings-zh.png +0 -0
  5. package/assets/ptc-plus-repair-en.png +0 -0
  6. package/assets/ptc-plus-repair-zh.png +0 -0
  7. package/assets/ptc-plus-repl-workspace-en.png +0 -0
  8. package/assets/ptc-plus-repl-workspace-zh.png +0 -0
  9. package/assets/ptc-plus-settings-en.png +0 -0
  10. package/assets/ptc-plus-settings-zh.png +0 -0
  11. package/client.js +30596 -742
  12. package/compiler-amaro.cjs +5 -0
  13. package/compiler-amaro.cjs.map +6 -0
  14. package/compiler-asset-124f80c1bba92ff5fe79cfec6e8ad518f476de4a42514cae3919538887f510e8.txt +1 -0
  15. package/compiler-assets.json +3 -0
  16. package/compiler-core.cjs +554 -0
  17. package/compiler-core.cjs.map +6 -0
  18. package/compiler-platform.cjs +2 -0
  19. package/compiler-platform.cjs.map +6 -0
  20. package/compiler-typescript.cjs +388 -0
  21. package/compiler-typescript.cjs.map +6 -0
  22. package/docs/README.md +6 -0
  23. package/docs/adr/0004-declare-program-bindings-at-the-host.md +1 -1
  24. package/docs/adr/0006-rewrite-module-syntax-with-an-ast.md +5 -3
  25. package/docs/adr/0007-separate-worker-transport-from-session-semantics.md +1 -1
  26. package/docs/adr/0009-bound-expensive-acceptance-default.md +2 -0
  27. package/docs/adr/0010-session-log-derived-recovery-tips.md +59 -5
  28. package/docs/adr/0011-isolate-the-local-recovery-tip-provider.md +1 -1
  29. package/docs/adr/0012-preserve-mixed-destructuring-initialization.md +3 -1
  30. package/docs/adr/0013-isolate-repl-redeclaration-convenience.md +3 -1
  31. package/docs/adr/0014-persist-cell-rewrite-policy.md +6 -2
  32. package/docs/adr/0015-preserve-source-positions-through-rewrites.md +1 -1
  33. package/docs/adr/0016-archive-resolved-review-findings.md +1 -1
  34. package/docs/adr/0017-track-the-latest-dsh-public-surface.md +49 -0
  35. package/docs/adr/0018-use-versioned-fixture-for-ab-benchmark.md +3 -3
  36. package/docs/adr/0019-plugin-settings-and-kill-switch.md +5 -3
  37. package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
  38. package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +12 -0
  39. package/docs/adr/0023-global-user-bindings.md +104 -0
  40. package/docs/adr/0024-repl-console-observation.md +33 -0
  41. package/docs/adr/0025-use-versioned-logical-binding-identities.md +119 -0
  42. package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +21 -0
  43. package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +37 -0
  44. package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +103 -0
  45. package/docs/adr/0029-keep-plugin-unavailability-out-of-the-host-agent-lifecycle.md +27 -0
  46. package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +27 -0
  47. package/docs/architecture.md +73 -21
  48. package/docs/binding-review-placement-design.md +200 -0
  49. package/docs/capability-projection.md +1 -1
  50. package/docs/client-ui.md +95 -19
  51. package/docs/durability-design.md +25 -10
  52. package/docs/evaluation.md +78 -6
  53. package/docs/installation.md +41 -6
  54. package/docs/publishing.md +5 -3
  55. package/docs/runtime-reference.md +72 -25
  56. package/docs/semantic-validation.md +29 -0
  57. package/docs/stateful-binding-updates-proposal.md +755 -0
  58. package/docs/user-bindings.en.md +64 -0
  59. package/docs/user-bindings.md +64 -0
  60. package/docs/value-wire.md +3 -3
  61. package/docs/verification.md +57 -0
  62. package/index.js +369 -92
  63. package/internal/ast-traversal.js +5 -2
  64. package/internal/binding-descriptors.js +1 -0
  65. package/internal/binding-pattern-traversal.js +25 -0
  66. package/internal/binding-pattern.js +26 -29
  67. package/internal/binding-source-analysis.js +391 -0
  68. package/internal/binding-update-policy.js +66 -0
  69. package/internal/bounded-worker-round.js +56 -0
  70. package/internal/callable-definition-environment.js +42 -0
  71. package/internal/callable-source-catalog.js +129 -0
  72. package/internal/callable-source-encoding.js +47 -0
  73. package/internal/callable-source-facts.js +459 -0
  74. package/internal/callable-source-format.js +2 -0
  75. package/internal/callable-source-lowering.js +123 -0
  76. package/internal/cell-analysis-contract.js +25 -0
  77. package/internal/cell-analysis.js +240 -290
  78. package/internal/cell-completion.js +15 -0
  79. package/internal/cell-error.js +7 -0
  80. package/internal/cell-parser.js +66 -0
  81. package/internal/cell-rewriter.js +3 -677
  82. package/internal/commonjs-export-evidence.js +63 -0
  83. package/internal/commonjs-source-evidence.js +72 -0
  84. package/internal/compiler-amaro.cjs +2 -0
  85. package/internal/compiler-compression.js +12 -0
  86. package/internal/compiler-data.js +58 -0
  87. package/internal/compiler-descriptors.js +54 -0
  88. package/internal/compiler-dynamic-cache.js +48 -0
  89. package/internal/compiler-entry.js +35 -0
  90. package/internal/compiler-generated-names.js +17 -0
  91. package/internal/compiler-intrinsics.js +84 -0
  92. package/internal/compiler-module-links.js +34 -0
  93. package/internal/compiler-operations.js +61 -0
  94. package/internal/compiler-platform-factory.js +84 -0
  95. package/internal/compiler-platform.js +136 -0
  96. package/internal/compiler-record-roles.js +53 -0
  97. package/internal/compiler-region-output.js +259 -0
  98. package/internal/compiler-resource-lowering.js +103 -0
  99. package/internal/compiler-scope-facts.js +115 -0
  100. package/internal/compiler-service.js +109 -0
  101. package/internal/compiler-source-cache.js +32 -0
  102. package/internal/compiler-source-pieces.js +93 -0
  103. package/internal/compiler-source-regions.js +132 -0
  104. package/internal/compiler-storage-source.js +54 -0
  105. package/internal/compiler-text.js +15 -0
  106. package/internal/compiler-typescript.cjs +2 -0
  107. package/internal/config-spec.js +83 -27
  108. package/internal/cordis-tools-owner.js +130 -21
  109. package/internal/diagnostic.js +27 -1
  110. package/internal/direct-surface-owner.js +76 -23
  111. package/internal/dynamic-binding-evidence.js +16 -0
  112. package/internal/dynamic-environment-compiler.js +779 -0
  113. package/internal/dynamic-environment-integration.js +87 -0
  114. package/internal/dynamic-environment-runtime.js +362 -0
  115. package/internal/dynamic-native-calls.js +150 -0
  116. package/internal/dynamic-native-runtime.js +248 -0
  117. package/internal/dynamic-scope-analysis.js +90 -0
  118. package/internal/edit-transport-owner.js +27 -2
  119. package/internal/execution-seam-compat.js +193 -0
  120. package/internal/failure-reporting.js +118 -18
  121. package/internal/host-rpc.js +66 -0
  122. package/internal/isolated-worker.js +548 -0
  123. package/internal/kernel-child.js +139 -0
  124. package/internal/kernel-worker.js +904 -120
  125. package/internal/language-semantics.js +14 -0
  126. package/internal/legacy-binding-catalog.js +48 -0
  127. package/internal/legacy-cell-analysis.js +600 -0
  128. package/internal/legacy-cell-parser.js +50 -0
  129. package/internal/legacy-cell-rewriter.js +631 -0
  130. package/internal/legacy-repl-convenience.js +291 -0
  131. package/internal/managed-module-operations.js +170 -0
  132. package/internal/module-compilation.js +321 -0
  133. package/internal/module-runtime-platform.js +19 -0
  134. package/internal/module-transform-contract.js +12 -0
  135. package/internal/native-await-declarations.js +68 -0
  136. package/internal/native-root-compilation.js +54 -0
  137. package/internal/native-root-dynamic.js +67 -0
  138. package/internal/recovery-tips.js +27 -15
  139. package/internal/rejected-cell-editor.js +13 -8
  140. package/internal/repl-convenience.js +2 -289
  141. package/internal/repl-memory-projection.js +81 -12
  142. package/internal/repl-observation-interest.js +70 -0
  143. package/internal/repl-rewrite-contract.js +6 -0
  144. package/internal/repl-scope-normalizer.js +2091 -0
  145. package/internal/repl-value-observer.js +114 -0
  146. package/internal/rpc-contract.js +52 -0
  147. package/internal/runtime-bridge-owner.js +172 -28
  148. package/internal/runtime-config.js +48 -52
  149. package/internal/runtime-contexts.js +82 -10
  150. package/internal/runtime-messages.js +111 -0
  151. package/internal/session-cell-executor.js +401 -87
  152. package/internal/session-journal-recovery.js +684 -0
  153. package/internal/session-journal-schema.js +106 -0
  154. package/internal/session-journal.js +184 -654
  155. package/internal/session-log-view.js +42 -9
  156. package/internal/session-runtime.js +256 -95
  157. package/internal/session-state.js +247 -71
  158. package/internal/source-position-map.js +211 -71
  159. package/internal/stateful-module-compiler.js +154 -0
  160. package/internal/stateful-module-runtime.js +303 -0
  161. package/internal/stateful-root-compiler.js +598 -0
  162. package/internal/stateful-root-runtime.js +340 -0
  163. package/internal/typert-codec-compat.js +28 -0
  164. package/internal/typescript-transform.js +29 -0
  165. package/internal/user-binding-authoring.js +70 -0
  166. package/internal/user-binding-console-worker.js +184 -0
  167. package/internal/user-binding-console.js +148 -0
  168. package/internal/user-binding-draft-projection.js +335 -0
  169. package/internal/user-binding-model-context.js +31 -0
  170. package/internal/user-binding-runner.js +56 -0
  171. package/internal/user-bindings-owner.js +1065 -0
  172. package/internal/user-bindings-store.js +293 -0
  173. package/internal/user-bindings.js +353 -0
  174. package/internal/value-wire-schema.js +28 -0
  175. package/internal/value-wire.js +29 -20
  176. package/internal/worker-client.js +57 -67
  177. package/internal/worker-environment.js +45 -0
  178. package/internal/worker-output-relay.js +29 -0
  179. package/internal/worker-shutdown.js +14 -0
  180. package/package.json +75 -7
  181. package/src/client-activity.js +88 -53
  182. package/src/client-authoring-view.js +580 -0
  183. package/src/client-binding-review.js +230 -0
  184. package/src/client-bindings-data.js +29 -0
  185. package/src/client-catalog.js +127 -0
  186. package/src/client-code-editor.js +84 -0
  187. package/src/client-console.js +112 -0
  188. package/src/client-copy.js +370 -0
  189. package/src/client-feature-gates.js +62 -0
  190. package/src/client-host-compat.js +150 -0
  191. package/src/client-indicator-view.js +267 -0
  192. package/src/client-repl-view.js +292 -0
  193. package/src/client-rpc.js +20 -0
  194. package/src/client-settings-view.js +221 -0
  195. package/src/client-styles.js +106 -0
  196. package/src/client-tool-view.js +108 -0
  197. package/src/client-transport.js +159 -0
  198. package/src/client-workbench.js +521 -0
  199. package/src/client.js +233 -615
@@ -1,15 +1,10 @@
1
- import { stripTypeScriptTypes } from 'node:module'
1
+ import { ModuleRewriteError } from './cell-rewriter.js'
2
+ import { declarationSpan, PreflightError } from './cell-analysis-contract.js'
3
+ export { declarationSpan, PreflightError } from './cell-analysis-contract.js'
2
4
  import { parse } from 'acorn'
3
- import {
4
- STRIP_PREFIX,
5
- STRIP_SUFFIX,
6
- rewriteModuleImportsExports,
7
- } from './cell-rewriter.js'
8
- import {
9
- LEGACY_DEFAULT_EXPORT_BINDING,
10
- LIVE_DEFAULT_EXPORT_BINDING,
11
- redeclarationCommitTarget,
12
- } from './repl-rewrite-contract.js'
5
+ import { parse as parseSource } from '@babel/parser'
6
+ import { parseExecutableCell } from './cell-parser.js'
7
+ import { redeclarationCommitTarget } from './repl-rewrite-contract.js'
13
8
  import {
14
9
  bindingNodes,
15
10
  createGeneratedNameAllocator,
@@ -22,9 +17,15 @@ import {
22
17
  renderDurabilityReason,
23
18
  renderDurabilityReasons,
24
19
  } from './module-policy.js'
25
- import { rewriteReplRedeclarations } from './repl-convenience.js'
26
- import { applySourceEdits, mapSourceSpan } from './source-position-map.js'
20
+ import { applySourceEdits, mapSourcePosition, mapSourceSpan } from './source-position-map.js'
27
21
  import { SKIP_AST_CHILDREN, walkAst } from './ast-traversal.js'
22
+ import { compileStatefulRoot } from './stateful-root-compiler.js'
23
+ import { CELL_PARSER_PLUGINS, bindCompilerIntrinsics, lowerStatefulDecorators, lowerStatefulResources, lowerNativeLanguageSource, normalizeStatefulScopes } from './repl-scope-normalizer.js'
24
+ import { markCallableSources, collectCallableSources } from './callable-source-facts.js'
25
+ import { LegacyPreflightError, prepareLegacyProgram } from './legacy-cell-analysis.js'
26
+ import { adaptLegacyModuleImports } from './managed-module-operations.js'
27
+ import { adaptNativeRootDynamic } from './native-root-compilation.js'
28
+ import { sourceRegionData } from './compiler-region-output.js'
28
29
 
29
30
  /**
30
31
  * Pure AST analysis for PTC cells: binding inventory, durability classification,
@@ -32,24 +33,6 @@ import { SKIP_AST_CHILDREN, walkAst } from './ast-traversal.js'
32
33
  * or session state so its behavior is fully testable in isolation.
33
34
  */
34
35
 
35
- export function declarationSpan(node) {
36
- const start = node.loc?.start
37
- /* c8 ignore next */
38
- const end = node.loc?.end ?? start
39
- /* c8 ignore next */
40
- if (start === undefined) return undefined
41
- return {
42
- line: Math.max(1, start.line - 1),
43
- column: start.column + 1,
44
- /* c8 ignore next */
45
- ...(end === undefined ? {} : {
46
- end: {
47
- line: Math.max(1, end.line - 1),
48
- column: end.column + 1,
49
- },
50
- }),
51
- }
52
- }
53
36
 
54
37
  function addPatternBindings(pattern, names) {
55
38
  walkPattern(pattern, node => names.add(node.name))
@@ -69,10 +52,26 @@ function topLevelBindings(body) {
69
52
  return new Set(topLevelDeclarations(body).map(declaration => declaration.name))
70
53
  }
71
54
 
55
+ function rootVarDeclarations(body, declarations, seen) {
56
+ walkAst({ type: 'Program', body }, node => {
57
+ if (node.type === 'VariableDeclaration' && node.kind === 'var' && !seen.has(node)) {
58
+ seen.add(node)
59
+ const definitionSpan = declarationSpan(node)
60
+ for (const declaration of node.declarations) {
61
+ addPatternDeclarations(declaration.id, declarations, 'variable', definitionSpan, true)
62
+ }
63
+ }
64
+ if (isFunction(node) || node.type === 'ClassDeclaration' || node.type === 'ClassExpression'
65
+ || node.type === 'StaticBlock') return SKIP_AST_CHILDREN
66
+ })
67
+ }
68
+
72
69
  function topLevelDeclarations(body, variableRedeclarations = false) {
73
70
  const declarations = []
71
+ const seen = new Set()
74
72
  for (const statement of body) {
75
73
  if (statement.type === 'VariableDeclaration') {
74
+ seen.add(statement)
76
75
  const definitionSpan = declarationSpan(statement)
77
76
  for (const declaration of statement.declarations) {
78
77
  addPatternDeclarations(
@@ -95,6 +94,7 @@ function topLevelDeclarations(body, variableRedeclarations = false) {
95
94
  })
96
95
  }
97
96
  }
97
+ rootVarDeclarations(body, declarations, seen)
98
98
  return declarations
99
99
  }
100
100
 
@@ -111,21 +111,56 @@ function directBlockBindings(body) {
111
111
  return names
112
112
  }
113
113
 
114
- function functionBindings(node) {
114
+ function functionParameterBindings(node) {
115
115
  const names = new Set()
116
116
  if (node.id !== null && node.id !== undefined) names.add(node.id.name)
117
117
  /* c8 ignore next */
118
118
  for (const param of node.params ?? []) addPatternBindings(param, names)
119
- walkAst(node.body, (current) => {
120
- if (current !== node && isFunction(current)) {
121
- if (current.type === 'FunctionDeclaration' && current.id !== null) names.add(current.id.name)
119
+ return names
120
+ }
121
+
122
+ function hasStrictDirective(body) {
123
+ if (body.type !== 'BlockStatement') return false
124
+ for (const statement of body.body) {
125
+ if (statement.directive === undefined) break
126
+ if (statement.directive === 'use strict') return true
127
+ }
128
+ return false
129
+ }
130
+
131
+ function addFunctionVariables(body, names, strict) {
132
+ const addBlockFunction = (node, blocked) => {
133
+ if (!strict && node.type === 'FunctionDeclaration' && !node.async && !node.generator
134
+ && !blocked.has(node.id.name)) names.add(node.id.name)
135
+ }
136
+ walkAst(body, (current, parent, key, blocked) => {
137
+ if (isFunction(current)) {
138
+ addBlockFunction(current, blocked)
122
139
  return SKIP_AST_CHILDREN
123
140
  }
141
+ if (current.type === 'StaticBlock') return SKIP_AST_CHILDREN
124
142
  if (current.type === 'VariableDeclaration' && current.kind === 'var') {
125
143
  for (const declaration of current.declarations) addPatternBindings(declaration.id, names)
126
144
  }
127
- })
128
- return names
145
+ const statements = current.type === 'BlockStatement' ? current.body
146
+ : current.type === 'SwitchCase' ? parent.cases.flatMap(branch => branch.consequent) : undefined
147
+ if (statements !== undefined) {
148
+ // Annex B creates a function-scope var only when intervening lexical
149
+ // declarations permit it. The declaration's own block binding is excluded.
150
+ for (const statement of statements) addBlockFunction(statement, blocked)
151
+ return new Set([...blocked, ...directBlockBindings(statements)])
152
+ }
153
+ if (['ForStatement', 'ForInStatement', 'ForOfStatement'].includes(current.type)) {
154
+ return new Set([...blocked, ...loopBindings(current)])
155
+ }
156
+ if (current.type === 'CatchClause' && current.param !== null && current.param.type !== 'Identifier') {
157
+ // The Annex B catch exception admits var names through a simple parameter,
158
+ // but a destructured catch binding still blocks function-scope promotion.
159
+ const nested = new Set(blocked)
160
+ addPatternBindings(current.param, nested)
161
+ return nested
162
+ }
163
+ }, undefined, new Set())
129
164
  }
130
165
 
131
166
  function loopBindings(node) {
@@ -171,10 +206,10 @@ function staticMemberName(node) {
171
206
  return undefined
172
207
  }
173
208
 
174
- function globalThisMember(node) {
209
+ function globalObjectMember(node) {
175
210
  return node?.type === 'MemberExpression'
176
211
  && node.object?.type === 'Identifier'
177
- && node.object.name === 'globalThis'
212
+ && ['global', 'globalThis'].includes(node.object.name)
178
213
  ? staticMemberName(node)
179
214
  : undefined
180
215
  }
@@ -185,12 +220,6 @@ function isFunction(node) {
185
220
  || node.type === 'ArrowFunctionExpression'
186
221
  }
187
222
 
188
- export class PreflightError extends Error {
189
- constructor(message, node, span = undefined) {
190
- super(message)
191
- this.span = span ?? declarationSpan(node)
192
- }
193
- }
194
223
 
195
224
  function staticModuleClassification(moduleLoads) {
196
225
  const reasons = new Map()
@@ -211,15 +240,46 @@ function staticModuleClassification(moduleLoads) {
211
240
  }
212
241
 
213
242
  /** Conservatively classify a cell before giving it non-journalable capability. */
214
- export function classifyDurability(code, knownBindings = new Set()) {
215
- const tree = parse(`${STRIP_PREFIX}${code}${STRIP_SUFFIX}`, { ecmaVersion: 'latest', sourceType: 'script', locations: true })
216
- const outer = tree.body[0]
217
- /* c8 ignore next */
218
- if (outer?.type !== 'FunctionDeclaration') throw new Error('ptc-plus: failed to parse cell wrapper')
219
- const declared = topLevelBindings(outer.body.body)
243
+ export function classifyDurability(code, knownBindings = new Set(), { sourceType = 'script', internalModules = new Set() } = {}) {
244
+ let body
245
+ let moduleLoads = []
246
+ if (sourceType === 'module') {
247
+ const program = parse(code, { ecmaVersion: 'latest', sourceType: 'module', locations: true })
248
+ knownBindings = new Set(knownBindings)
249
+ moduleLoads = program.body.filter(statement => statement.source != null
250
+ && ['ImportDeclaration', 'ExportNamedDeclaration', 'ExportAllDeclaration'].includes(statement.type)
251
+ && !internalModules.has(statement.source.value)).map(statement => ({
252
+ source: statement.source.value, position: declarationSpan(statement.source),
253
+ }))
254
+ body = { type: 'BlockStatement', body: program.body.flatMap(statement => {
255
+ if (statement.type === 'ImportDeclaration') {
256
+ for (const specifier of statement.specifiers) knownBindings.add(specifier.local.name)
257
+ return []
258
+ }
259
+ if (statement.type === 'ExportAllDeclaration') return []
260
+ if (statement.type === 'ExportNamedDeclaration') return statement.declaration === null ? [] : [statement.declaration]
261
+ if (statement.type === 'ExportDefaultDeclaration') return [statement.declaration]
262
+ return [statement]
263
+ }) }
264
+ } else ({ body } = parseExecutableCell(code))
265
+ const declared = topLevelBindings(body.body)
220
266
  const rootBindings = new Set([...knownBindings, ...declared])
267
+ const rootStrict = sourceType === 'module' || hasStrictDirective(body)
268
+ addFunctionVariables(body, rootBindings, rootStrict)
269
+ // A `var` is instantiated with the cell even on the path that never runs its
270
+ // initializer, so it occupies the shared environment but does not by itself
271
+ // replace a name an earlier cell bound.
272
+ const hoistedVars = new Set()
273
+ walkAst(body, (node) => {
274
+ if (isFunction(node)) return SKIP_AST_CHILDREN
275
+ if (node.type === 'StaticBlock') return SKIP_AST_CHILDREN
276
+ if (node.type === 'VariableDeclaration' && node.kind === 'var') {
277
+ for (const declaration of node.declarations) addPatternBindings(declaration.id, hoistedVars)
278
+ }
279
+ })
221
280
  const reasons = new Map()
222
281
  const addReason = reason => reasons.set(renderDurabilityReason(reason), reason)
282
+ for (const reason of staticModuleClassification(moduleLoads)) addReason(reason)
223
283
  const classifyModule = (source) => {
224
284
  if (source?.type !== 'Literal' || typeof source.value !== 'string') {
225
285
  addReason(DYNAMIC_MODULE_REASON)
@@ -237,12 +297,25 @@ export function classifyDurability(code, knownBindings = new Set()) {
237
297
  }
238
298
  return false
239
299
  }
240
- walkAst(outer.body, (node, parent, parentKey, scopes) => {
300
+ walkAst(body, (node, parent, parentKey, { scopes, strict }) => {
241
301
  let nestedScopes = scopes
242
302
  if (isFunction(node)) {
243
- nestedScopes = [...scopes, functionBindings(node)]
244
- } else if (node.type === 'BlockStatement' && node !== outer.body) {
245
- nestedScopes = [...scopes, directBlockBindings(node.body)]
303
+ strict ||= hasStrictDirective(node.body)
304
+ nestedScopes = [...scopes, functionParameterBindings(node)]
305
+ } else if (node.type === 'BlockStatement' && node !== body) {
306
+ const names = directBlockBindings(node.body)
307
+ if (isFunction(parent)) addFunctionVariables(node, names, strict)
308
+ nestedScopes = [...scopes, names]
309
+ } else if (node.type === 'StaticBlock') {
310
+ const names = directBlockBindings(node.body)
311
+ for (const statement of node.body) addFunctionVariables(statement, names, true)
312
+ strict = true
313
+ nestedScopes = [...scopes, names]
314
+ } else if (node.type === 'ClassDeclaration' || node.type === 'ClassExpression') {
315
+ strict = true
316
+ nestedScopes = [...scopes, new Set(node.id === null ? [] : [node.id.name])]
317
+ } else if (node.type === 'SwitchCase') {
318
+ nestedScopes = [...scopes, directBlockBindings(parent.cases.flatMap(branch => branch.consequent))]
246
319
  } else if (node.type === 'CatchClause') {
247
320
  const names = new Set()
248
321
  addPatternBindings(node.param, names)
@@ -266,279 +339,156 @@ export function classifyDurability(code, knownBindings = new Set()) {
266
339
  && !isBound('process', nestedScopes) && node.name === 'process' && !isStableProcessMember(node, parent)) {
267
340
  addReason(Object.freeze({ kind: 'ambient', name: 'process' }))
268
341
  }
342
+ if (node.type === 'Identifier' && ['global', 'globalThis'].includes(node.name)
343
+ && isReferenceIdentifier(node, parent, parentKey) && !isBound(node.name, nestedScopes)
344
+ && !(parent?.type === 'MemberExpression' && parent.object === node)) {
345
+ addReason(Object.freeze({ kind: 'ambient', name: node.name }))
346
+ }
347
+ if (node.type === 'Identifier' && node.name === 'Math'
348
+ && isReferenceIdentifier(node, parent, parentKey) && !isBound('Math', nestedScopes)
349
+ && !(parent?.type === 'MemberExpression' && parent.object === node && staticMemberName(parent) !== undefined)) {
350
+ addReason(Object.freeze({ kind: 'ambient', name: 'Math' }))
351
+ }
269
352
  if (node.type === 'MemberExpression'
270
353
  && ((node.object?.type === 'Identifier' && node.object.name === 'Math' && !isBound('Math', nestedScopes))
271
- || (globalThisMember(node.object) === 'Math' && !isBound('globalThis', nestedScopes)))
354
+ || (globalObjectMember(node.object) === 'Math' && !isBound(node.object.object.name, nestedScopes)))
272
355
  && staticMemberName(node) === 'random') {
273
356
  addReason(Object.freeze({ kind: 'math-random' }))
274
357
  }
275
358
  if (node.type === 'MemberExpression' && node.object?.type === 'Identifier'
276
- && node.object.name === 'globalThis' && !isBound('globalThis', nestedScopes)) {
359
+ && ['global', 'globalThis'].includes(node.object.name) && !isBound(node.object.name, nestedScopes)) {
277
360
  const member = staticMemberName(node)
278
361
  if (member === undefined) addReason(Object.freeze({ kind: 'computed-global-access' }))
279
362
  else if (member === 'process') addReason(Object.freeze({ kind: 'ambient', name: 'process' }))
280
363
  else if (AMBIENT_GLOBALS.has(member)) addReason(Object.freeze({ kind: 'ambient', name: member }))
364
+ else if (member === 'global' || member === 'globalThis') addReason(Object.freeze({ kind: 'ambient', name: member }))
365
+ else if (member === 'Math' && !(parent?.type === 'MemberExpression' && parent.object === node
366
+ && staticMemberName(parent) !== undefined)) {
367
+ addReason(Object.freeze({ kind: 'ambient', name: 'Math' }))
368
+ }
281
369
  }
282
- return nestedScopes
283
- }, undefined, [rootBindings], outer, 'body')
370
+ return { scopes: nestedScopes, strict }
371
+ }, undefined, { scopes: [rootBindings], strict: rootStrict })
284
372
  return {
285
373
  durability: reasons.size === 0 ? 'durable' : 'volatile',
286
374
  reasons: Object.freeze([...reasons.values()]),
287
375
  declared,
376
+ hoistedVars,
288
377
  }
289
378
  }
290
379
 
291
380
  function rewriteCellReturns(code, sourceMap, unavailableNames) {
292
- const wrapped = STRIP_PREFIX + code + STRIP_SUFFIX
293
- const tree = parse(wrapped, { ecmaVersion: 'latest', sourceType: 'script' })
294
- const outer = tree.body[0]
295
- /* c8 ignore next */
296
- if (outer?.type !== 'FunctionDeclaration') throw new Error('ptc-plus: failed to parse cell wrapper')
297
- const offset = STRIP_PREFIX.length
381
+ const { body } = parseExecutableCell(code)
298
382
  const edits = []
299
- const allocateName = createGeneratedNameAllocator(tree, unavailableNames)
383
+ const allocateName = createGeneratedNameAllocator(body, unavailableNames)
300
384
  const returnSignal = allocateName('return_signal')
301
385
  const signalReference = `this[${JSON.stringify(returnSignal)}]`
386
+ const completion = allocateName('cell_completion')
387
+ const exit = allocateName('cell_exit')
388
+ const ignored = allocateName('completion_effect')
389
+ let asyncCompletion = false
302
390
 
303
- walkAst(outer.body, (node) => {
304
- if (node !== outer.body && isFunction(node)) return SKIP_AST_CHILDREN
391
+ walkAst(body, (node) => {
392
+ if (node !== body && isFunction(node)) return SKIP_AST_CHILDREN
393
+ if (node.type === 'AwaitExpression' || node.await === true
394
+ || node.type === 'VariableDeclaration' && node.kind === 'await using') asyncCompletion = true
305
395
  if (node.type === 'ReturnStatement') {
306
- const start = node.start - offset
307
- const end = node.end - offset
396
+ const start = node.start
397
+ const end = node.end
308
398
  const argument = node.argument === null
309
399
  ? ''
310
- : code.slice(node.argument.start - offset, node.argument.end - offset)
311
- // A ReturnStatement may be the last clause body in a switch case. The
312
- // generated throw must terminate explicitly because the original return
313
- // statement's semicolon is part of the replaced AST range.
314
- edits.push({ start, end, text: `throw new ${signalReference}(${argument});` })
315
- return SKIP_AST_CHILDREN
316
- }
317
- if (node.type === 'CatchClause') {
318
- const bodyStart = node.body.start - offset + 1
319
- const temporary = allocateName('caught')
320
- if (node.param === null) {
321
- edits.push({ start: node.start - offset + 5, end: node.start - offset + 5, text: ` (${temporary})` })
322
- edits.push({ start: bodyStart, end: bodyStart, text: `\nif (${temporary} instanceof ${signalReference}) throw ${temporary};` })
323
- } else if (node.param.type === 'Identifier') {
324
- edits.push({
325
- start: bodyStart,
326
- end: bodyStart,
327
- text: `\nif (${node.param.name} instanceof ${signalReference}) throw ${node.param.name};`,
328
- })
329
- } else {
330
- const pattern = code.slice(node.param.start - offset, node.param.end - offset)
331
- edits.push({ start: node.param.start - offset, end: node.param.end - offset, text: temporary })
332
- edits.push({
333
- start: bodyStart,
334
- end: bodyStart,
335
- text: `\nif (${temporary} instanceof ${signalReference}) throw ${temporary};\nconst ${pattern} = ${temporary};`,
336
- })
337
- }
400
+ : code.slice(node.argument.start, node.argument.end)
401
+ // Break leaves resource and finally scopes without making disposal
402
+ // suppress a private error for an otherwise successful return.
403
+ edits.push({ start, end, text: `{let ${ignored}=(${completion}=new ${signalReference}(${argument}));break ${exit};}` })
338
404
  }
339
405
  })
340
406
 
341
- /* c8 ignore next */
342
- edits.sort((left, right) => right.start - left.start || right.end - left.end)
343
- return { ...applySourceEdits(code, sourceMap, edits), returnSignal }
407
+ const directiveEnd = body.body.findLast(node => node.directive !== undefined)?.end ?? 0
408
+ // Fallthrough cancels a return overridden by finally break/continue. Empty
409
+ // declaration completions preserve the source's ordinary expression value.
410
+ edits.push({ start: directiveEnd, end: directiveEnd,
411
+ text: `\n{let ${completion};${exit}:{\n` })
412
+ edits.push({ start: code.length, end: code.length,
413
+ text: `\n;{let ${ignored}=(${completion}=void 0);}}{let ${ignored}=${completion}&&(()=>{throw ${completion}})();}}\n` })
414
+ let lowered = applySourceEdits(code, sourceMap, edits)
415
+ if (asyncCompletion) {
416
+ // The frame result is compiler-owned transport. Letting Node create it
417
+ // would hide the promise from our observer and re-enter source protocols
418
+ // when the host REPL awaits this realm's completion.
419
+ lowered = applySourceEdits(lowered.code, lowered.sourceMap, [
420
+ { start: 0, end: 0, text: '(async()=>{' },
421
+ { start: lowered.code.length, end: lowered.code.length, text: '\n})()' },
422
+ ])
423
+ }
424
+ return { ...lowered, returnSignal, asyncCompletion }
425
+ }
426
+
427
+ /** Workbench completion uses the same source grammar and compilation boundary. */
428
+ export function prepareConsoleProgram(source, options) {
429
+ const program = parseSource(source, { sourceType: 'script', errorRecovery: true,
430
+ allowImportExportEverywhere: true, allowUndeclaredExports: true,
431
+ allowAwaitOutsideFunction: true, allowReturnOutsideFunction: true, plugins: CELL_PARSER_PLUGINS }).program
432
+ const last = program.body.at(-1)
433
+ const input = last?.type !== 'ExpressionStatement' ? source
434
+ : `${source.slice(0, last.start)}return (${source.slice(last.expression.start, last.expression.end)});${source.slice(last.end)}`
435
+ return prepareProgram(input, options)
344
436
  }
345
437
 
346
- export function prepareProgram(program, knownBindings, bindingPolicy, reservedBindings = new Set(), rewritesEnabled, importBindings = new Map(), importNamespaces = new Set(), writableBindings = undefined, moduleSemantics = { defaultExportBinding: LIVE_DEFAULT_EXPORT_BINDING }) {
438
+ export function prepareProgram(program, options = {}) {
347
439
  if (typeof program !== 'string') throw new TypeError('ptc-plus: program must be a string')
348
- if (typeof bindingPolicy === 'boolean') {
349
- bindingPolicy = {
350
- variableRedeclarations: bindingPolicy,
351
- functionClassRedeclarations: false,
352
- }
353
- }
354
- if (bindingPolicy === null || typeof bindingPolicy !== 'object' || Array.isArray(bindingPolicy)
355
- || typeof bindingPolicy.variableRedeclarations !== 'boolean'
356
- || typeof bindingPolicy.functionClassRedeclarations !== 'boolean') {
357
- throw new TypeError('ptc-plus: binding policy must define variableRedeclarations and functionClassRedeclarations booleans')
358
- }
359
- writableBindings ??= bindingPolicy.variableRedeclarations ? new Set(knownBindings) : new Set()
360
- if (rewritesEnabled === null || typeof rewritesEnabled !== 'object' || Array.isArray(rewritesEnabled)
361
- || typeof rewritesEnabled.autoRewriteImports !== 'boolean'
362
- || typeof rewritesEnabled.autoStripExports !== 'boolean'
363
- || typeof rewritesEnabled.autoSplitRedeclarations !== 'boolean') {
364
- throw new TypeError('ptc-plus: rewrite policy must define autoRewriteImports, autoStripExports, and autoSplitRedeclarations booleans')
365
- }
366
- if (moduleSemantics === null || typeof moduleSemantics !== 'object' || Array.isArray(moduleSemantics)
367
- || ![LEGACY_DEFAULT_EXPORT_BINDING, LIVE_DEFAULT_EXPORT_BINDING]
368
- .includes(moduleSemantics.defaultExportBinding)) {
369
- throw new TypeError('ptc-plus: module semantics must define a supported defaultExportBinding')
370
- }
371
- const unavailableGeneratedNames = new Set([
372
- ...knownBindings,
373
- ...reservedBindings,
374
- ...importBindings.keys(),
375
- ...importNamespaces,
376
- ])
377
- const moduleRewrite = rewriteModuleImportsExports(
378
- program,
379
- rewritesEnabled,
380
- importBindings,
381
- importNamespaces,
382
- unavailableGeneratedNames,
383
- moduleSemantics,
384
- )
385
- const staticModuleReasons = staticModuleClassification(moduleRewrite.moduleLoads)
386
- const wrapped = STRIP_PREFIX + moduleRewrite.code + STRIP_SUFFIX
387
- let stripped = wrapped
388
- let tree
389
- try {
390
- tree = parse(wrapped, { ecmaVersion: 'latest', sourceType: 'script', locations: true })
391
- } catch (javascriptError) {
440
+ if (options.languageSemantics === undefined || options.languageSemantics === 'legacy-v1') {
392
441
  try {
393
- stripped = stripTypeScriptTypes(wrapped)
394
- tree = parse(stripped, { ecmaVersion: 'latest', sourceType: 'script', locations: true })
395
- } catch {
396
- throw javascriptError
397
- }
398
- }
399
- const code = stripped.slice(STRIP_PREFIX.length, stripped.length - STRIP_SUFFIX.length)
400
- const sourceMap = moduleRewrite.sourceMap
401
- const outer = tree.body[0]
402
- const commitSignal = moduleRewrite.commitSignal
403
- /* c8 ignore next */
404
- const generatedDeclarations = outer?.type === 'FunctionDeclaration'
405
- ? topLevelDeclarations(outer.body.body, bindingPolicy.variableRedeclarations)
406
- : []
407
- const collisionFor = (declaration) => {
408
- /* c8 ignore next */
409
- const mapped = declaration.original === true ? declaration.span : declaration.span === undefined
410
- ? { line: 1, column: 1 }
411
- : mapSourceSpan(declaration.span, code, program, sourceMap)
412
- return {
413
- name: declaration.name,
414
- kind: declaration.kind,
415
- replaceableByVariableDeclaration: declaration.replaceableByVariableDeclaration === true,
416
- ...(declaration.reason === undefined ? {} : { reason: declaration.reason }),
417
- ...(declaration.commitDependency === undefined
418
- ? {}
419
- : { commitDependency: declaration.commitDependency }),
420
- start: { line: mapped.line, column: mapped.column },
421
- /* c8 ignore next */
422
- ...(mapped.end === undefined ? {} : { end: mapped.end }),
423
- }
424
- }
425
- const classifyPrepared = (preparedCode, preparedSourceMap) => {
426
- try {
427
- const classification = classifyDurability(preparedCode, knownBindings)
428
- const reasons = new Map()
429
- for (const reason of [...staticModuleReasons, ...classification.reasons]) {
430
- reasons.set(renderDurabilityReason(reason), reason)
431
- }
432
- return {
433
- ...classification,
434
- durability: reasons.size === 0 ? 'durable' : 'volatile',
435
- reasons: Object.freeze([...reasons.values()]),
436
- reason: renderDurabilityReasons([...reasons.values()]),
437
- }
442
+ // Logical imports have no persistent native namespace slot. Their proved
443
+ // identity enters the native execution environment with the other roots.
444
+ const nativeOptions = { ...options, importBindings: new Map([...options.importBindings ?? []]
445
+ .filter(([name]) => !options.establishedRoots?.has(name) || options.nativeBindings?.has(name))) }
446
+ const prepared = adaptNativeRootDynamic(adaptLegacyModuleImports(prepareLegacyProgram(program, nativeOptions), nativeOptions), options, program)
447
+ return { ...prepared, sourceRegions: sourceRegionData(prepared.sourceRegions), languageSemantics: 'legacy-v1' }
438
448
  } catch (error) {
439
- if (error instanceof PreflightError && error.span !== undefined) {
440
- error.span = mapSourceSpan(error.span, preparedCode, program, preparedSourceMap)
441
- }
449
+ if (error instanceof LegacyPreflightError) throw new PreflightError(error.message, undefined, error.span)
442
450
  throw error
443
451
  }
444
452
  }
445
- const importedNames = new Set(moduleRewrite.imports.keys())
446
- const originalDeclarationNames = new Set(moduleRewrite.exportDeclarations.map(declaration => declaration.name))
447
- const declarations = [
448
- ...moduleRewrite.importDeclarations.map(declaration => ({ ...declaration, writable: false })),
449
- ...moduleRewrite.exportDeclarations.map(declaration => ({
450
- ...declaration,
451
- writable: bindingPolicy.variableRedeclarations,
452
- })),
453
- ...generatedDeclarations
454
- .filter(declaration => !moduleRewrite.generatedNamespaces.has(declaration.name)
455
- && !originalDeclarationNames.has(declaration.name))
456
- .map(declaration => ({
457
- ...declaration,
458
- ...(declaration.definitionSpan === undefined ? {} : {
459
- definitionSpan: mapSourceSpan(declaration.definitionSpan, code, program, sourceMap),
460
- }),
461
- })),
462
- ]
463
- const normalizeClassification = classification => {
464
- const imports = new Map(moduleRewrite.imports)
465
- for (const name of classification.declared) {
466
- if (!moduleRewrite.generatedNamespaces.has(name)) imports.delete(name)
467
- }
468
- return {
469
- ...classification,
470
- imports,
471
- declared: new Set([...classification.declared, ...importedNames]),
472
- }
473
- }
474
- const reserved = declarations.filter(declaration => (
475
- reservedBindings.has(declaration.name)
476
- || moduleRewrite.importNamespaces.has(declaration.name)
477
- || (declaration.kind === 'import' && knownBindings.has(declaration.name))
478
- ))
479
- if (reserved.length > 0) {
480
- const classification = normalizeClassification(classifyPrepared(code, sourceMap))
481
- return {
482
- code,
483
- ...classification,
484
- declarations,
485
- imports: classification.imports,
486
- importNamespaces: moduleRewrite.importNamespaces,
487
- collisions: reserved.map(collisionFor),
488
- redeclared: [],
489
- rewrites: moduleRewrite.rewrites,
490
- moduleLoads: moduleRewrite.moduleLoads,
453
+ if (options.languageSemantics === 'stateful-v1' || options.languageSemantics === 'protected-v1') {
454
+ let normalized
455
+ let prepared
456
+ let callableSources
457
+ const intrinsicContext = { bindings: new Set() }
458
+ try {
459
+ const marked = markCallableSources(program, undefined, { plugins: CELL_PARSER_PLUGINS, allowImportExportEverywhere: true, allowUndeclaredExports: true },
460
+ { lowerNativeSource: lowerNativeLanguageSource, nativeUsing: options.nativeUsing })
461
+ callableSources = marked.callableSources
462
+ normalized = normalizeStatefulScopes(marked.code, marked.sourceMap, { mode: options.languageSemantics,
463
+ nativeJavaScript: marked.nativeJavaScript, deferDecorators: true, deferResources: true, intrinsicContext })
464
+ prepared = compileStatefulRoot(normalized.code, { ...options, sourceMap: normalized.sourceMap,
465
+ internalBindings: normalized.internalBindings, dynamicBindings: normalized.dynamicBindings,
466
+ privateBindings: normalized.privateBindings, originalSource: program })
467
+ } catch (error) {
468
+ if (error instanceof ModuleRewriteError) throw error
469
+ const position = error.loc === undefined ? undefined : { line: error.loc.line, column: error.loc.column + 1 }
470
+ throw new ModuleRewriteError(error.message, normalized === undefined ? position
471
+ : mapSourcePosition(position, normalized.code, program, normalized.sourceMap))
491
472
  }
492
- }
493
- const convenience = rewriteReplRedeclarations({
494
- code,
495
- sourceMap,
496
- body: outer?.type === 'FunctionDeclaration' ? outer.body.body : undefined,
497
- offset: STRIP_PREFIX.length,
498
- knownBindings,
499
- variableRedeclarationBindings: bindingPolicy.variableRedeclarations
500
- ? new Set([...knownBindings].filter(name => !importBindings.has(name)))
501
- : new Set(knownBindings),
502
- writableBindings: new Set([...writableBindings].filter(name => !importBindings.has(name))),
503
- declarations,
504
- declarationSpan,
505
- collisionFor,
506
- bindingPolicy,
507
- autoSplitRedeclarations: rewritesEnabled.autoSplitRedeclarations,
508
- commitSignal,
509
- })
510
- const { executableCode, executableSourceMap, collisions, redeclared, rewrites } = convenience
511
- const commitTargets = new Set(moduleRewrite.commitTargets)
512
- for (const declaration of redeclared) {
513
- if (declaration.kind === 'function' || declaration.kind === 'class') {
514
- commitTargets.add(declaration.commitDependency)
473
+ prepared = { ...prepared, deferredHelpers: normalized.deferredHelpers }
474
+ intrinsicContext.expression = `this[${JSON.stringify(prepared.rootRuntimeName)}].intrinsics`
475
+ const classificationSource = bindCompilerIntrinsics(lowerStatefulDecorators(
476
+ { ...prepared, code: prepared.classificationCode }, undefined, intrinsicContext), intrinsicContext).code
477
+ prepared = bindCompilerIntrinsics(lowerStatefulDecorators(prepared, undefined, intrinsicContext), intrinsicContext)
478
+ prepared = lowerStatefulResources(prepared, { intrinsicContext, nativeUsing: options.nativeUsing })
479
+ const parsed = parseExecutableCell(prepared.code, { eraseTypes: true })
480
+ const classification = classifyDurability(parseExecutableCell(classificationSource, { eraseTypes: true }).code,
481
+ new Set([...(options.knownBindings ?? []), ...prepared.declared]))
482
+ const reasons = [...staticModuleClassification(prepared.moduleLoads), ...classification.reasons]
483
+ const lowered = rewriteCellReturns(parsed.code, prepared.sourceMap,
484
+ new Set([...prepared.declared, ...(options.knownBindings ?? [])]))
485
+ prepared.rootBindings.callableSources = collectCallableSources(lowered.code, callableSources)
486
+ return { ...prepared, ...lowered,
487
+ sourceRegions: sourceRegionData(prepared.sourceRegions),
488
+ returnSignal: lowered.returnSignal,
489
+ durability: reasons.length === 0 ? 'durable' : 'volatile', reasons: Object.freeze(reasons),
490
+ reason: renderDurabilityReasons(reasons),
515
491
  }
516
492
  }
517
- // Classification sees the rewritten program so declared bindings reflect the
518
- // split form; it must never see the return rewrite, whose computed
519
- // globalThis access would mark every returning cell volatile.
520
- const classification = normalizeClassification(classifyPrepared(executableCode, executableSourceMap))
521
- let lowered = { code, sourceMap }
522
- if (collisions.length === 0) {
523
- lowered = rewriteCellReturns(
524
- executableCode,
525
- executableSourceMap,
526
- unavailableGeneratedNames,
527
- )
528
- }
529
- return {
530
- code: lowered.code,
531
- sourceMap: lowered.sourceMap,
532
- ...(lowered.returnSignal === undefined ? {} : { returnSignal: lowered.returnSignal }),
533
- ...classification,
534
- declarations,
535
- imports: classification.imports,
536
- importNamespaces: moduleRewrite.importNamespaces,
537
- collisions,
538
- redeclared,
539
- rewrites: [...moduleRewrite.rewrites, ...rewrites],
540
- moduleLoads: moduleRewrite.moduleLoads,
541
- commitSignal,
542
- commitTargets,
543
- }
493
+ throw new TypeError('ptc-plus: unsupported language semantics')
544
494
  }