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
@@ -0,0 +1,14 @@
1
+ /** Recorded compilation contracts. Live configuration cannot reinterpret history. */
2
+ export const LEGACY_LANGUAGE_SEMANTICS = 'legacy-v1'
3
+ export const STATEFUL_LANGUAGE_SEMANTICS = 'stateful-v1'
4
+ export const PROTECTED_LANGUAGE_SEMANTICS = 'protected-v1'
5
+ export const LANGUAGE_SEMANTICS = new Set([
6
+ LEGACY_LANGUAGE_SEMANTICS,
7
+ STATEFUL_LANGUAGE_SEMANTICS,
8
+ PROTECTED_LANGUAGE_SEMANTICS,
9
+ ])
10
+
11
+ export function normalizeLanguageSemantics(value) {
12
+ if (!LANGUAGE_SEMANTICS.has(value)) throw new TypeError('invalid dsh-ptc-plus language semantics')
13
+ return value
14
+ }
@@ -0,0 +1,48 @@
1
+ /** Historical legacy-v1 catalog transition, retained from c51616c8d6c8:session-state.js. */
2
+ export function advanceLegacyBindings(previousEntries, prepared, extractDefinition, committedRedeclarations) {
3
+ const entries = new Map(previousEntries)
4
+ const touched = new Set()
5
+ const redeclared = new Set((prepared.redeclared ?? []).map(declaration => declaration.name))
6
+ const commitGated = prepared.commitTargets
7
+ const committed = committedRedeclarations instanceof Set
8
+ ? committedRedeclarations
9
+ : new Set([...redeclared, ...commitGated])
10
+ const uncommitted = new Set()
11
+ for (const declaration of prepared.declarations ?? []) {
12
+ if (typeof declaration?.name !== 'string') continue
13
+ const dependency = typeof declaration.commitDependency === 'string'
14
+ && commitGated.has(declaration.commitDependency)
15
+ ? declaration.commitDependency
16
+ : commitGated.has(declaration.name) ? declaration.name : undefined
17
+ if (dependency !== undefined && !committed.has(dependency)) {
18
+ uncommitted.add(declaration.name)
19
+ continue
20
+ }
21
+ touched.add(declaration.name)
22
+ const previous = entries.get(declaration.name)
23
+ const definition = extractDefinition(declaration.definitionSpan)
24
+ entries.set(declaration.name, {
25
+ kind: declaration.kind ?? 'variable',
26
+ definition: definition ?? previous?.definition,
27
+ writable: redeclared.has(declaration.name) ? previous?.writable === true : declaration.writable === true,
28
+ })
29
+ }
30
+ for (const name of prepared.declared) {
31
+ if (!uncommitted.has(name)) touched.add(name)
32
+ }
33
+ for (const name of touched) {
34
+ const entry = entries.get(name) ?? { kind: 'variable', writable: false }
35
+ entries.delete(name)
36
+ entries.set(name, entry)
37
+ }
38
+ const imports = new Map(prepared.imports)
39
+ for (const [name, binding] of imports) {
40
+ if (typeof binding?.commitDependency !== 'string'
41
+ || !commitGated.has(binding.commitDependency)
42
+ || committed.has(binding.commitDependency)) continue
43
+ if (previousEntries.get(name)?.import !== undefined) imports.set(name, previousEntries.get(name).import)
44
+ else imports.delete(name)
45
+ }
46
+ for (const [name, entry] of entries) entries.set(name, { ...entry, import: imports.get(name) })
47
+ return { entries, namespaces: prepared.importNamespaces }
48
+ }
@@ -0,0 +1,600 @@
1
+ /** Historical legacy-v1 lowering, retained from c51616c8d6c8:cell-analysis.js. */
2
+ import { ModuleRewriteError, rewriteModuleImportsExports } from './legacy-cell-rewriter.js'
3
+ import { parseExecutableCell } from './legacy-cell-parser.js'
4
+ import {
5
+ LEGACY_DEFAULT_EXPORT_BINDING,
6
+ LIVE_DEFAULT_EXPORT_BINDING,
7
+ LIVE_MODULE_SEMANTICS,
8
+ LEGACY_IMPORT_EXPRESSION_BOUNDARY,
9
+ LIVE_IMPORT_EXPRESSION_BOUNDARY,
10
+ redeclarationCommitTarget,
11
+ } from './repl-rewrite-contract.js'
12
+ import {
13
+ bindingNodes,
14
+ createGeneratedNameAllocator,
15
+ walkBindingPattern as walkPattern,
16
+ } from './binding-pattern.js'
17
+ import {
18
+ AMBIENT_GLOBALS,
19
+ DYNAMIC_MODULE_REASON,
20
+ classifyModuleSource,
21
+ renderDurabilityReason,
22
+ renderDurabilityReasons,
23
+ } from './module-policy.js'
24
+ import { rewriteReplRedeclarations } from './legacy-repl-convenience.js'
25
+ import { applySourceEdits, mapSourcePosition, mapSourceSpan } from './source-position-map.js'
26
+ import { SKIP_AST_CHILDREN, walkAst } from './ast-traversal.js'
27
+
28
+ /**
29
+ * Pure AST analysis for PTC cells: binding inventory, durability classification,
30
+ * return rewriting, and program preparation. This module owns no worker, journal,
31
+ * or session state so its behavior is fully testable in isolation.
32
+ */
33
+
34
+ export function declarationSpan(node) {
35
+ const start = node.loc?.start
36
+ /* c8 ignore next */
37
+ const end = node.loc?.end ?? start
38
+ /* c8 ignore next */
39
+ if (start === undefined) return undefined
40
+ return {
41
+ line: start.line,
42
+ column: start.column + 1,
43
+ /* c8 ignore next */
44
+ ...(end === undefined ? {} : {
45
+ end: {
46
+ line: end.line,
47
+ column: end.column + 1,
48
+ },
49
+ }),
50
+ }
51
+ }
52
+
53
+ function addPatternBindings(pattern, names) {
54
+ walkPattern(pattern, node => names.add(node.name))
55
+ }
56
+
57
+ function addPatternDeclarations(pattern, declarations, kind = 'variable', definitionSpan = undefined, writable = false) {
58
+ bindingNodes(pattern).forEach(node => declarations.push({
59
+ name: node.name,
60
+ kind,
61
+ span: declarationSpan(node),
62
+ definitionSpan,
63
+ writable,
64
+ }))
65
+ }
66
+
67
+ function topLevelBindings(body) {
68
+ return new Set(topLevelDeclarations(body).map(declaration => declaration.name))
69
+ }
70
+
71
+ function topLevelDeclarations(body, variableRedeclarations = false) {
72
+ const declarations = []
73
+ for (const statement of body) {
74
+ if (statement.type === 'VariableDeclaration') {
75
+ const definitionSpan = declarationSpan(statement)
76
+ for (const declaration of statement.declarations) {
77
+ addPatternDeclarations(
78
+ declaration.id,
79
+ declarations,
80
+ 'variable',
81
+ definitionSpan,
82
+ statement.kind !== 'const' || variableRedeclarations,
83
+ )
84
+ }
85
+ } else if ((statement.type === 'FunctionDeclaration' || statement.type === 'ClassDeclaration')
86
+ && statement.id !== null) {
87
+ declarations.push({
88
+ name: statement.id.name,
89
+ kind: statement.type === 'ClassDeclaration' ? 'class' : 'function',
90
+ span: declarationSpan(statement.id),
91
+ definitionSpan: declarationSpan(statement),
92
+ commitDependency: redeclarationCommitTarget(statement.id.name, statement.start),
93
+ writable: true,
94
+ })
95
+ }
96
+ }
97
+ return declarations
98
+ }
99
+
100
+ function directBlockBindings(body) {
101
+ const names = new Set()
102
+ for (const statement of body) {
103
+ if (statement.type === 'VariableDeclaration' && statement.kind !== 'var') {
104
+ for (const declaration of statement.declarations) addPatternBindings(declaration.id, names)
105
+ } else if ((statement.type === 'FunctionDeclaration' || statement.type === 'ClassDeclaration')
106
+ && statement.id !== null) {
107
+ names.add(statement.id.name)
108
+ }
109
+ }
110
+ return names
111
+ }
112
+
113
+ function functionParameterBindings(node) {
114
+ const names = new Set()
115
+ if (node.id !== null && node.id !== undefined) names.add(node.id.name)
116
+ /* c8 ignore next */
117
+ for (const param of node.params ?? []) addPatternBindings(param, names)
118
+ return names
119
+ }
120
+
121
+ function hasStrictDirective(body) {
122
+ if (body.type !== 'BlockStatement') return false
123
+ for (const statement of body.body) {
124
+ if (statement.directive === undefined) break
125
+ if (statement.directive === 'use strict') return true
126
+ }
127
+ return false
128
+ }
129
+
130
+ function addFunctionVariables(body, names, strict) {
131
+ const addBlockFunction = (node, blocked) => {
132
+ if (!strict && node.type === 'FunctionDeclaration' && !node.async && !node.generator
133
+ && !blocked.has(node.id.name)) names.add(node.id.name)
134
+ }
135
+ walkAst(body, (current, parent, key, blocked) => {
136
+ if (isFunction(current)) {
137
+ addBlockFunction(current, blocked)
138
+ return SKIP_AST_CHILDREN
139
+ }
140
+ if (current.type === 'StaticBlock') return SKIP_AST_CHILDREN
141
+ if (current.type === 'VariableDeclaration' && current.kind === 'var') {
142
+ for (const declaration of current.declarations) addPatternBindings(declaration.id, names)
143
+ }
144
+ const statements = current.type === 'BlockStatement' ? current.body
145
+ : current.type === 'SwitchCase' ? parent.cases.flatMap(branch => branch.consequent) : undefined
146
+ if (statements !== undefined) {
147
+ // Annex B creates a function-scope var only when intervening lexical
148
+ // declarations permit it. The declaration's own block binding is excluded.
149
+ for (const statement of statements) addBlockFunction(statement, blocked)
150
+ return new Set([...blocked, ...directBlockBindings(statements)])
151
+ }
152
+ if (['ForStatement', 'ForInStatement', 'ForOfStatement'].includes(current.type)) {
153
+ return new Set([...blocked, ...loopBindings(current)])
154
+ }
155
+ if (current.type === 'CatchClause' && current.param !== null && current.param.type !== 'Identifier') {
156
+ // The Annex B catch exception admits var names through a simple parameter,
157
+ // but a destructured catch binding still blocks function-scope promotion.
158
+ const nested = new Set(blocked)
159
+ addPatternBindings(current.param, nested)
160
+ return nested
161
+ }
162
+ }, undefined, new Set())
163
+ }
164
+
165
+ function loopBindings(node) {
166
+ const declaration = node.type === 'ForStatement' ? node.init : node.left
167
+ if (declaration?.type !== 'VariableDeclaration' || declaration.kind === 'var') return new Set()
168
+ const names = new Set()
169
+ for (const entry of declaration.declarations) addPatternBindings(entry.id, names)
170
+ return names
171
+ }
172
+
173
+ function isReferenceIdentifier(node, parent, key) {
174
+ /* c8 ignore next */
175
+ if (parent === undefined) return false
176
+ if ((parent.type === 'MemberExpression' || parent.type === 'OptionalMemberExpression')
177
+ && key === 'property' && !parent.computed) return false
178
+ if ((parent.type === 'Property' || parent.type === 'MethodDefinition' || parent.type === 'PropertyDefinition')
179
+ && key === 'key' && !parent.computed && !parent.shorthand) return false
180
+ if (['VariableDeclarator', 'FunctionDeclaration', 'FunctionExpression', 'ClassDeclaration', 'ClassExpression']
181
+ .includes(parent.type) && key === 'id') return false
182
+ if ((parent.type === 'FunctionDeclaration' || parent.type === 'FunctionExpression' || parent.type === 'ArrowFunctionExpression')
183
+ && key === 'params') return false
184
+ if (parent.type === 'CatchClause' && key === 'param') return false
185
+ if (['LabeledStatement', 'BreakStatement', 'ContinueStatement'].includes(parent.type) && key === 'label') return false
186
+ return true
187
+ }
188
+
189
+ function isStableProcessMember(node, parent) {
190
+ /* c8 ignore next */
191
+ if (node.name !== 'process' || parent?.type !== 'MemberExpression' || parent.object !== node) return false
192
+ const member = parent.computed
193
+ ? parent.property?.type === 'Literal' ? parent.property.value : undefined
194
+ /* c8 ignore next */
195
+ : parent.property?.type === 'Identifier' ? parent.property.name : undefined
196
+ return ['stdout', 'stderr', 'cwd'].includes(member)
197
+ }
198
+
199
+ function staticMemberName(node) {
200
+ if (node?.type !== 'MemberExpression') return undefined
201
+ if (!node.computed && node.property?.type === 'Identifier') return node.property.name
202
+ if (node.computed && node.property?.type === 'Literal' && typeof node.property.value === 'string') {
203
+ return node.property.value
204
+ }
205
+ return undefined
206
+ }
207
+
208
+ function globalObjectMember(node) {
209
+ return node?.type === 'MemberExpression'
210
+ && node.object?.type === 'Identifier'
211
+ && ['global', 'globalThis'].includes(node.object.name)
212
+ ? staticMemberName(node)
213
+ : undefined
214
+ }
215
+
216
+ function isFunction(node) {
217
+ return node.type === 'FunctionDeclaration'
218
+ || node.type === 'FunctionExpression'
219
+ || node.type === 'ArrowFunctionExpression'
220
+ }
221
+
222
+ export class LegacyPreflightError extends Error {
223
+ constructor(message, node, span = undefined) {
224
+ super(message)
225
+ this.span = span ?? declarationSpan(node)
226
+ }
227
+ }
228
+
229
+ function staticModuleClassification(moduleLoads) {
230
+ const reasons = new Map()
231
+ for (const load of moduleLoads) {
232
+ const classification = classifyModuleSource(load.source)
233
+ if (classification.status === 'forbidden') {
234
+ throw new LegacyPreflightError(
235
+ `cell import of ${load.source} is forbidden because it exposes kernel control`,
236
+ undefined,
237
+ load.position,
238
+ )
239
+ }
240
+ if (classification.reason !== undefined) {
241
+ reasons.set(renderDurabilityReason(classification.reason), classification.reason)
242
+ }
243
+ }
244
+ return [...reasons.values()]
245
+ }
246
+
247
+ /** Conservatively classify a cell before giving it non-journalable capability. */
248
+ export function classifyDurability(code, knownBindings = new Set()) {
249
+ const { body } = parseExecutableCell(code)
250
+ const declared = topLevelBindings(body.body)
251
+ const rootBindings = new Set([...knownBindings, ...declared])
252
+ const rootStrict = hasStrictDirective(body)
253
+ addFunctionVariables(body, rootBindings, rootStrict)
254
+ const reasons = new Map()
255
+ const addReason = reason => reasons.set(renderDurabilityReason(reason), reason)
256
+ const classifyModule = (source) => {
257
+ if (source?.type !== 'Literal' || typeof source.value !== 'string') {
258
+ addReason(DYNAMIC_MODULE_REASON)
259
+ return
260
+ }
261
+ const classification = classifyModuleSource(source.value)
262
+ if (classification.status === 'forbidden') {
263
+ throw new LegacyPreflightError(`cell import of ${source.value} is forbidden because it exposes kernel control`, source)
264
+ }
265
+ if (classification.reason !== undefined) addReason(classification.reason)
266
+ }
267
+ const isBound = (name, scopes) => {
268
+ for (let index = scopes.length - 1; index >= 0; index -= 1) {
269
+ if (scopes[index].has(name)) return true
270
+ }
271
+ return false
272
+ }
273
+ walkAst(body, (node, parent, parentKey, { scopes, strict }) => {
274
+ let nestedScopes = scopes
275
+ if (isFunction(node)) {
276
+ strict ||= hasStrictDirective(node.body)
277
+ nestedScopes = [...scopes, functionParameterBindings(node)]
278
+ } else if (node.type === 'BlockStatement' && node !== body) {
279
+ const names = directBlockBindings(node.body)
280
+ if (isFunction(parent)) addFunctionVariables(node, names, strict)
281
+ nestedScopes = [...scopes, names]
282
+ } else if (node.type === 'StaticBlock') {
283
+ const names = directBlockBindings(node.body)
284
+ for (const statement of node.body) addFunctionVariables(statement, names, true)
285
+ strict = true
286
+ nestedScopes = [...scopes, names]
287
+ } else if (node.type === 'ClassDeclaration' || node.type === 'ClassExpression') {
288
+ strict = true
289
+ nestedScopes = [...scopes, new Set(node.id === null ? [] : [node.id.name])]
290
+ } else if (node.type === 'SwitchCase') {
291
+ nestedScopes = [...scopes, directBlockBindings(parent.cases.flatMap(branch => branch.consequent))]
292
+ } else if (node.type === 'CatchClause') {
293
+ const names = new Set()
294
+ addPatternBindings(node.param, names)
295
+ nestedScopes = [...scopes, names]
296
+ } else if (['ForStatement', 'ForInStatement', 'ForOfStatement'].includes(node.type)) {
297
+ nestedScopes = [...scopes, loopBindings(node)]
298
+ }
299
+ if (node.type === 'ImportExpression') {
300
+ classifyModule(node.source)
301
+ }
302
+ if (node.type === 'CallExpression' && node.callee?.type === 'Identifier' && node.callee.name === 'require'
303
+ && !isBound('require', nestedScopes)) {
304
+ classifyModule(node.arguments[0])
305
+ }
306
+ if (node.type === 'Identifier' && isReferenceIdentifier(node, parent, parentKey)
307
+ && !isBound(node.name, nestedScopes) && AMBIENT_GLOBALS.has(node.name)
308
+ && !(node.name === 'require' && parent?.type === 'CallExpression' && parent.callee === node)) {
309
+ addReason(Object.freeze({ kind: 'ambient', name: node.name }))
310
+ }
311
+ if (node.type === 'Identifier' && isReferenceIdentifier(node, parent, parentKey)
312
+ && !isBound('process', nestedScopes) && node.name === 'process' && !isStableProcessMember(node, parent)) {
313
+ addReason(Object.freeze({ kind: 'ambient', name: 'process' }))
314
+ }
315
+ if (node.type === 'Identifier' && ['global', 'globalThis'].includes(node.name)
316
+ && isReferenceIdentifier(node, parent, parentKey) && !isBound(node.name, nestedScopes)
317
+ && !(parent?.type === 'MemberExpression' && parent.object === node)) {
318
+ addReason(Object.freeze({ kind: 'ambient', name: node.name }))
319
+ }
320
+ if (node.type === 'Identifier' && node.name === 'Math'
321
+ && isReferenceIdentifier(node, parent, parentKey) && !isBound('Math', nestedScopes)
322
+ && !(parent?.type === 'MemberExpression' && parent.object === node && staticMemberName(parent) !== undefined)) {
323
+ addReason(Object.freeze({ kind: 'ambient', name: 'Math' }))
324
+ }
325
+ if (node.type === 'MemberExpression'
326
+ && ((node.object?.type === 'Identifier' && node.object.name === 'Math' && !isBound('Math', nestedScopes))
327
+ || (globalObjectMember(node.object) === 'Math' && !isBound(node.object.object.name, nestedScopes)))
328
+ && staticMemberName(node) === 'random') {
329
+ addReason(Object.freeze({ kind: 'math-random' }))
330
+ }
331
+ if (node.type === 'MemberExpression' && node.object?.type === 'Identifier'
332
+ && ['global', 'globalThis'].includes(node.object.name) && !isBound(node.object.name, nestedScopes)) {
333
+ const member = staticMemberName(node)
334
+ if (member === undefined) addReason(Object.freeze({ kind: 'computed-global-access' }))
335
+ else if (member === 'process') addReason(Object.freeze({ kind: 'ambient', name: 'process' }))
336
+ else if (AMBIENT_GLOBALS.has(member)) addReason(Object.freeze({ kind: 'ambient', name: member }))
337
+ else if (member === 'global' || member === 'globalThis') addReason(Object.freeze({ kind: 'ambient', name: member }))
338
+ else if (member === 'Math' && !(parent?.type === 'MemberExpression' && parent.object === node
339
+ && staticMemberName(parent) !== undefined)) {
340
+ addReason(Object.freeze({ kind: 'ambient', name: 'Math' }))
341
+ }
342
+ }
343
+ return { scopes: nestedScopes, strict }
344
+ }, undefined, { scopes: [rootBindings], strict: rootStrict })
345
+ return {
346
+ durability: reasons.size === 0 ? 'durable' : 'volatile',
347
+ reasons: Object.freeze([...reasons.values()]),
348
+ declared,
349
+ }
350
+ }
351
+
352
+ function rewriteCellReturns(code, sourceMap, unavailableNames) {
353
+ const { body } = parseExecutableCell(code)
354
+ const edits = []
355
+ const allocateName = createGeneratedNameAllocator(body, unavailableNames)
356
+ const returnSignal = allocateName('return_signal')
357
+ const signalReference = `this[${JSON.stringify(returnSignal)}]`
358
+
359
+ walkAst(body, (node) => {
360
+ if (node !== body && isFunction(node)) return SKIP_AST_CHILDREN
361
+ if (node.type === 'ReturnStatement') {
362
+ const start = node.start
363
+ const end = node.end
364
+ const argument = node.argument === null
365
+ ? ''
366
+ : code.slice(node.argument.start, node.argument.end)
367
+ // A ReturnStatement may be the last clause body in a switch case. The
368
+ // generated throw must terminate explicitly because the original return
369
+ // statement's semicolon is part of the replaced AST range.
370
+ edits.push({ start, end, text: `throw new ${signalReference}(${argument});` })
371
+ return SKIP_AST_CHILDREN
372
+ }
373
+ if (node.type === 'CatchClause') {
374
+ const bodyStart = node.body.start + 1
375
+ const temporary = allocateName('caught')
376
+ if (node.param === null) {
377
+ edits.push({ start: node.start + 5, end: node.start + 5, text: ` (${temporary})` })
378
+ edits.push({ start: bodyStart, end: bodyStart, text: `\nif (${temporary} instanceof ${signalReference}) throw ${temporary};` })
379
+ } else if (node.param.type === 'Identifier') {
380
+ edits.push({
381
+ start: bodyStart,
382
+ end: bodyStart,
383
+ text: `\nif (${node.param.name} instanceof ${signalReference}) throw ${node.param.name};`,
384
+ })
385
+ } else {
386
+ const pattern = code.slice(node.param.start, node.param.end)
387
+ edits.push({ start: node.param.start, end: node.param.end, text: temporary })
388
+ edits.push({
389
+ start: bodyStart,
390
+ end: bodyStart,
391
+ text: `\nif (${temporary} instanceof ${signalReference}) throw ${temporary};\nconst ${pattern} = ${temporary};`,
392
+ })
393
+ }
394
+ }
395
+ })
396
+
397
+ /* c8 ignore next */
398
+ edits.sort((left, right) => right.start - left.start || right.end - left.end)
399
+ return { ...applySourceEdits(code, sourceMap, edits), returnSignal }
400
+ }
401
+
402
+ function normalizePreparationOptions({
403
+ knownBindings = new Set(),
404
+ bindingPolicy,
405
+ reservedBindings = new Set(),
406
+ rewritesEnabled,
407
+ importBindings = new Map(),
408
+ importNamespaces = new Set(),
409
+ writableBindings,
410
+ moduleSemantics = LIVE_MODULE_SEMANTICS,
411
+ }) {
412
+ if (typeof bindingPolicy === 'boolean') {
413
+ bindingPolicy = {
414
+ variableRedeclarations: bindingPolicy,
415
+ functionClassRedeclarations: false,
416
+ }
417
+ }
418
+ if (bindingPolicy === null || typeof bindingPolicy !== 'object' || Array.isArray(bindingPolicy)
419
+ || typeof bindingPolicy.variableRedeclarations !== 'boolean'
420
+ || typeof bindingPolicy.functionClassRedeclarations !== 'boolean') {
421
+ throw new TypeError('ptc-plus: binding policy must define variableRedeclarations and functionClassRedeclarations booleans')
422
+ }
423
+ writableBindings ??= bindingPolicy.variableRedeclarations ? new Set(knownBindings) : new Set()
424
+ if (rewritesEnabled === null || typeof rewritesEnabled !== 'object' || Array.isArray(rewritesEnabled)
425
+ || typeof rewritesEnabled.autoRewriteImports !== 'boolean'
426
+ || typeof rewritesEnabled.autoStripExports !== 'boolean'
427
+ || typeof rewritesEnabled.autoSplitRedeclarations !== 'boolean') {
428
+ throw new TypeError('ptc-plus: rewrite policy must define autoRewriteImports, autoStripExports, and autoSplitRedeclarations booleans')
429
+ }
430
+ if (moduleSemantics === null || typeof moduleSemantics !== 'object' || Array.isArray(moduleSemantics)
431
+ || ![LEGACY_DEFAULT_EXPORT_BINDING, LIVE_DEFAULT_EXPORT_BINDING]
432
+ .includes(moduleSemantics.defaultExportBinding)
433
+ || ![LEGACY_IMPORT_EXPRESSION_BOUNDARY, LIVE_IMPORT_EXPRESSION_BOUNDARY]
434
+ .includes(moduleSemantics.importExpressionBoundary)) {
435
+ throw new TypeError('ptc-plus: module semantics must define supported default-export and import-expression rules')
436
+ }
437
+ return { knownBindings, bindingPolicy, reservedBindings, rewritesEnabled,
438
+ importBindings, importNamespaces, writableBindings, moduleSemantics }
439
+ }
440
+
441
+ function parseMappedCell(moduleRewrite, program) {
442
+ try {
443
+ return parseExecutableCell(moduleRewrite.code, { eraseTypes: true })
444
+ } catch (error) {
445
+ if (error instanceof ModuleRewriteError) {
446
+ error.cellPosition = mapSourcePosition(error.cellPosition, moduleRewrite.code, program, moduleRewrite.sourceMap)
447
+ }
448
+ throw error
449
+ }
450
+ }
451
+
452
+ function collisionMapper(code, program, sourceMap) {
453
+ return declaration => {
454
+ /* c8 ignore next */
455
+ const mapped = declaration.original === true ? declaration.span : declaration.span === undefined
456
+ ? { line: 1, column: 1 }
457
+ : mapSourceSpan(declaration.span, code, program, sourceMap)
458
+ return {
459
+ name: declaration.name,
460
+ kind: declaration.kind,
461
+ replaceableByVariableDeclaration: declaration.replaceableByVariableDeclaration === true,
462
+ ...(declaration.reason === undefined ? {} : { reason: declaration.reason }),
463
+ ...(declaration.commitDependency === undefined
464
+ ? {}
465
+ : { commitDependency: declaration.commitDependency }),
466
+ start: { line: mapped.line, column: mapped.column },
467
+ /* c8 ignore next */
468
+ ...(mapped.end === undefined ? {} : { end: mapped.end }),
469
+ }
470
+ }
471
+ }
472
+
473
+ function preparedDeclarations(moduleRewrite, parsed, program, bindingPolicy) {
474
+ const generatedDeclarations = topLevelDeclarations(parsed.body.body, bindingPolicy.variableRedeclarations)
475
+ const originalDeclarationNames = new Set(moduleRewrite.exportDeclarations.map(declaration => declaration.name))
476
+ return [
477
+ ...moduleRewrite.importDeclarations.map(declaration => ({ ...declaration, writable: false })),
478
+ ...moduleRewrite.exportDeclarations.map(declaration => ({
479
+ ...declaration,
480
+ writable: bindingPolicy.variableRedeclarations,
481
+ })),
482
+ ...generatedDeclarations
483
+ .filter(declaration => !moduleRewrite.generatedNamespaces.has(declaration.name)
484
+ && !originalDeclarationNames.has(declaration.name))
485
+ .map(declaration => ({
486
+ ...declaration,
487
+ ...(declaration.definitionSpan === undefined ? {} : {
488
+ definitionSpan: mapSourceSpan(declaration.definitionSpan, parsed.code, program, moduleRewrite.sourceMap),
489
+ }),
490
+ })),
491
+ ]
492
+ }
493
+
494
+ function classifyPrepared({ code, sourceMap }, program, knownBindings, moduleRewrite, staticModuleReasons) {
495
+ let classification
496
+ try {
497
+ classification = classifyDurability(code, knownBindings)
498
+ } catch (error) {
499
+ if (error instanceof LegacyPreflightError && error.span !== undefined) {
500
+ error.span = mapSourceSpan(error.span, code, program, sourceMap)
501
+ }
502
+ throw error
503
+ }
504
+ const reasons = new Map()
505
+ for (const reason of [...staticModuleReasons, ...classification.reasons]) {
506
+ reasons.set(renderDurabilityReason(reason), reason)
507
+ }
508
+ const imports = new Map(moduleRewrite.imports)
509
+ for (const name of classification.declared) {
510
+ if (!moduleRewrite.generatedNamespaces.has(name)) imports.delete(name)
511
+ }
512
+ return {
513
+ ...classification,
514
+ durability: reasons.size === 0 ? 'durable' : 'volatile',
515
+ reasons: Object.freeze([...reasons.values()]),
516
+ reason: renderDurabilityReasons([...reasons.values()]),
517
+ imports,
518
+ declared: new Set([...classification.declared, ...moduleRewrite.imports.keys()]),
519
+ }
520
+ }
521
+
522
+ export function prepareLegacyProgram(program, options = {}) {
523
+ if (typeof program !== 'string') throw new TypeError('ptc-plus: program must be a string')
524
+ const { knownBindings, bindingPolicy, reservedBindings, rewritesEnabled,
525
+ importBindings, importNamespaces, writableBindings, moduleSemantics } = normalizePreparationOptions(options)
526
+ const unavailableGeneratedNames = new Set([
527
+ ...knownBindings, ...reservedBindings, ...importBindings.keys(), ...importNamespaces,
528
+ ])
529
+ const moduleRewrite = rewriteModuleImportsExports(
530
+ program, rewritesEnabled, importBindings, importNamespaces, unavailableGeneratedNames, moduleSemantics,
531
+ )
532
+ const staticModuleReasons = staticModuleClassification(moduleRewrite.moduleLoads)
533
+ const parsed = parseMappedCell(moduleRewrite, program)
534
+ const { code } = parsed
535
+ const { sourceMap, commitSignal } = moduleRewrite
536
+ const declarations = preparedDeclarations(moduleRewrite, parsed, program, bindingPolicy)
537
+ const collisionFor = collisionMapper(code, program, sourceMap)
538
+ const reserved = declarations.filter(declaration => (
539
+ reservedBindings.has(declaration.name)
540
+ || moduleRewrite.importNamespaces.has(declaration.name)
541
+ || (declaration.kind === 'import' && knownBindings.has(declaration.name))
542
+ ))
543
+ const convenience = reserved.length > 0 ? {
544
+ executableCode: code, executableSourceMap: sourceMap,
545
+ collisions: reserved.map(declaration => ({ ...collisionFor(declaration),
546
+ reason: reservedBindings.has(declaration.name)
547
+ ? 'reserved-program-binding-not-shadowable' : 'import-binding-redeclaration' })),
548
+ redeclared: [], rewrites: [],
549
+ } : rewriteReplRedeclarations({
550
+ code,
551
+ sourceMap,
552
+ body: parsed.body.body,
553
+ knownBindings,
554
+ variableRedeclarationBindings: bindingPolicy.variableRedeclarations
555
+ ? new Set([...knownBindings].filter(name => !importBindings.has(name)))
556
+ : new Set(knownBindings),
557
+ writableBindings: new Set([...writableBindings].filter(name => !importBindings.has(name))),
558
+ declarations,
559
+ declarationSpan,
560
+ collisionFor,
561
+ bindingPolicy,
562
+ autoSplitRedeclarations: rewritesEnabled.autoSplitRedeclarations,
563
+ commitSignal,
564
+ })
565
+ const { executableCode, executableSourceMap, collisions, redeclared, rewrites } = convenience
566
+ const commitTargets = new Set(moduleRewrite.commitTargets)
567
+ for (const declaration of redeclared) {
568
+ if (declaration.kind === 'function' || declaration.kind === 'class') {
569
+ commitTargets.add(declaration.commitDependency)
570
+ }
571
+ }
572
+ // Generated control helpers are not ambient inputs from the user's program.
573
+ const classification = classifyPrepared(
574
+ { code: executableCode, sourceMap: executableSourceMap },
575
+ program, knownBindings, moduleRewrite, staticModuleReasons,
576
+ )
577
+ let lowered = { code, sourceMap }
578
+ if (collisions.length === 0) {
579
+ lowered = rewriteCellReturns(
580
+ executableCode,
581
+ executableSourceMap,
582
+ unavailableGeneratedNames,
583
+ )
584
+ }
585
+ return {
586
+ code: lowered.code,
587
+ sourceMap: lowered.sourceMap,
588
+ returnSignal: lowered.returnSignal,
589
+ ...classification,
590
+ declarations,
591
+ imports: classification.imports,
592
+ importNamespaces: moduleRewrite.importNamespaces,
593
+ collisions,
594
+ redeclared,
595
+ rewrites: [...moduleRewrite.rewrites, ...rewrites],
596
+ moduleLoads: moduleRewrite.moduleLoads,
597
+ commitSignal,
598
+ commitTargets,
599
+ }
600
+ }