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,340 @@
1
+ /** Session logical identities, independent of the native cell's lexical frame. */
2
+ import { createDynamicEnvironmentRuntime } from './dynamic-environment-runtime.js'
3
+ import { captureCompilerIntrinsics, moduleRuntimeIntrinsics as internal } from './compiler-intrinsics.js'
4
+
5
+ const { Map, Set, Object, Proxy, mapGet, mapSet, mapHas, mapClear, mapForEach,
6
+ setHas, setAdd, includes, appendArray, stringify } = internal
7
+
8
+ export function createStatefulRootRuntime({ readAmbient, typeofAmbient, writeAmbient, deleteAmbient,
9
+ canWriteAmbient = () => true,
10
+ errors,
11
+ dynamicIntrinsics, importModule = (source, options) => import(source, options),
12
+ hasOverlay = () => false, hasAmbient, publish = () => {}, changed = () => {}, refresh = () => {} }) {
13
+ const entries = new Map()
14
+ const intrinsics = captureCompilerIntrinsics(dynamicIntrinsics?.realmFunction)
15
+ errors ??= intrinsics
16
+ const dynamicRuntime = createDynamicEnvironmentRuntime({ ...dynamicIntrinsics, importModule })
17
+ const moduleRuntime = dynamicIntrinsics === undefined ? undefined : createDynamicEnvironmentRuntime()
18
+ let commit
19
+ const read = name => {
20
+ refresh(name)
21
+ const entry = mapGet(entries, name)
22
+ return hasOverlay(name) || entry === undefined || entry.object === true || entry.deleted === true
23
+ ? readAmbient(name) : entry.read === undefined ? entry.value : entry.read()
24
+ }
25
+ const storeValue = (name, value, { source = 'local', readOnly = false, declaration = false,
26
+ strict = false, target, nativeKind } = {}) => {
27
+ refresh(name)
28
+ const previous = mapGet(entries, name)
29
+ if (hasOverlay(name)) {
30
+ // A hard request overlay is not shadowable, so this store cannot publish a session identity.
31
+ // A declaration is the overlay's own pre-emption and stays absorbed; an actual write that the
32
+ // ambient property cannot accept must fail observably instead of being discarded.
33
+ if (!declaration && !canWriteAmbient(name)) {
34
+ throw new errors.TypeError(`${name} cannot be overwritten because reserved program bindings are not shadowable`)
35
+ }
36
+ writeAmbient(name, value, strict)
37
+ return undefined
38
+ }
39
+ if (!declaration && (previous === undefined || previous.object === true || previous.deleted === true) && writeAmbient !== undefined) {
40
+ writeAmbient(name, value, strict)
41
+ mapSet(entries, name, { source, object: true, ...(previous?.legacyWritable === true ? { legacyWritable: true } : {}) })
42
+ } else if (previous?.legacyWritable === true && writeAmbient !== undefined
43
+ && (previous.object !== true || canWriteAmbient(name))) {
44
+ writeAmbient(name, value)
45
+ mapSet(entries, name, { ...previous, source, readOnly })
46
+ } else mapSet(entries, name, { value, source, readOnly, ...(previous?.nativeKind === undefined ? {} : { nativeKind: previous.nativeKind }) })
47
+ const writeTarget = target ?? (declaration ? undefined : previous?.writeTarget)
48
+ const entry = mapGet(entries, name)
49
+ delete entry.writeTarget
50
+ if (writeTarget !== undefined) entry.writeTarget = writeTarget
51
+ if (nativeKind !== undefined) entry.nativeKind = nativeKind
52
+ return entry
53
+ }
54
+ const write = (name, value, options) => {
55
+ const entry = storeValue(name, value, options)
56
+ if (entry === undefined) return value
57
+ if (entry.object !== true) publish(name, () => read(name), value => write(name, value))
58
+ changed(name, entry.source)
59
+ return value
60
+ }
61
+ const begin = ({ declared = [], known = [], readOnly = [], legacyImports = [], legacyWritable = [], legacyLexicals = [], legacyObjects = [],
62
+ declarationKinds = [], declaredKinds = [], callableSources = [], languageSemantics = 'stateful-v1', committed }) => {
63
+ dynamicRuntime.registerSources(callableSources)
64
+ commit = committed
65
+ const kinds = new Map()
66
+ for (let index = 0; index < declarationKinds.length; index++) mapSet(kinds, declarationKinds[index][0], declarationKinds[index][1])
67
+ for (let index = 0; index < legacyImports.length; index++) {
68
+ const name = legacyImports[index][0], binding = legacyImports[index][1]
69
+ if (!mapHas(entries, name)) mapSet(entries, name, { source: 'import', read: () => {
70
+ const namespace = readAmbient(binding.namespace)
71
+ return binding.imported === undefined ? namespace : namespace[binding.imported]
72
+ } })
73
+ }
74
+ for (let index = 0; index < legacyWritable.length; index++) {
75
+ const name = legacyWritable[index]
76
+ if (!mapHas(entries, name)) mapSet(entries, name, { source: 'local', legacyWritable: true,
77
+ ...(includes(legacyObjects, name) ? { object: true } : {}), read: () => readAmbient(name) })
78
+ }
79
+ for (let index = 0; index < legacyLexicals.length; index++) {
80
+ const name = legacyLexicals[index]
81
+ if (!mapHas(entries, name)) mapSet(entries, name, { source: 'local', readOnly: true, read: () => readAmbient(name) })
82
+ }
83
+ const protectedNames = new Set()
84
+ if (languageSemantics === 'protected-v1') {
85
+ for (let index = 0; index < readOnly.length; index++) setAdd(protectedNames, readOnly[index])
86
+ mapForEach(entries, (entry, name) => { if (entry.readOnly) setAdd(protectedNames, name) })
87
+ }
88
+ const fresh = new Set()
89
+ for (let index = 0; index < declared.length; index++) {
90
+ const name = declared[index]
91
+ if (!mapHas(entries, name) && !includes(known, name)) setAdd(fresh, name)
92
+ }
93
+ const checkInitialized = name => {
94
+ if (!hasOverlay(name) && setHas(fresh, name) && !mapHas(entries, name)) throw new errors.ReferenceError(`Cannot access '${name}' before initialization`)
95
+ }
96
+ const get = name => {
97
+ checkInitialized(name)
98
+ return read(name)
99
+ }
100
+ const checkWritable = name => {
101
+ if (languageSemantics === 'protected-v1') checkInitialized(name)
102
+ if (languageSemantics === 'protected-v1' && (setHas(protectedNames, name) || mapGet(entries, name)?.readOnly === true)) {
103
+ throw new errors.TypeError('Assignment to constant variable.')
104
+ }
105
+ }
106
+ const needsDeclaration = name => {
107
+ const entry = mapGet(entries, name)
108
+ return entry === undefined && (setHas(fresh, name) || hasAmbient !== undefined && !hasAmbient(name))
109
+ || (entry?.object === true || entry?.deleted === true) && !hasAmbient(name)
110
+ }
111
+ const remove = name => {
112
+ const entry = mapGet(entries, name)
113
+ if (hasOverlay(name) || entry?.object || entry?.deleted || entry === undefined && !setHas(fresh, name)) return deleteAmbient(name)
114
+ if (entry?.nativeKind !== 'eval-var') return false
115
+ mapSet(entries, name, { ...entry, deleted: true })
116
+ return true
117
+ }
118
+ const values = new Proxy(Object.create(null), {
119
+ get: (_, name) => get(name),
120
+ set: (_, name, value) => {
121
+ checkWritable(name)
122
+ write(name, value)
123
+ return true
124
+ },
125
+ deleteProperty: (_, name) => remove(name),
126
+ })
127
+ const result = {
128
+ values,
129
+ intrinsics,
130
+ importModule,
131
+ dynamic({ realm = false } = {}) {
132
+ const pendingKinds = new Map()
133
+ for (let index = 0; index < declaredKinds.length; index++) mapSet(pendingKinds, declaredKinds[index][0], declaredKinds[index][1])
134
+ const kindFor = name => {
135
+ const entry = mapGet(entries, name)
136
+ if (hasOverlay(name) || entry?.deleted) return undefined
137
+ if (entry?.object || entry === undefined && realm && !setHas(fresh, name)) return realm && hasAmbient(name) ? 'var' : undefined
138
+ return entry === undefined ? setHas(fresh, name) ? mapGet(pendingKinds, name) ?? 'let' : undefined : entry.nativeKind ?? 'let'
139
+ }
140
+ const variableKind = kind => includes(['var', 'eval-var', 'hoisted'], kind)
141
+ const descriptor = (name, kind) => ({
142
+ kind,
143
+ get: () => get(name),
144
+ set(value, strict, origin, writeTarget) {
145
+ result.reference(name, strict, writeTarget ?? (origin === undefined ? undefined : `${origin}:${stringify(name)}`)).value = value
146
+ },
147
+ delete() {
148
+ const entry = mapGet(entries, name)
149
+ if (realm && (entry === undefined || entry.object)) return delete values[name]
150
+ return remove(name)
151
+ },
152
+ })
153
+ const lexicalFrame = { has: name => kindFor(name) !== undefined && !variableKind(kindFor(name)),
154
+ get: name => lexicalFrame.has(name) ? descriptor(name, kindFor(name)) : undefined }
155
+ const varFrame = { has: name => variableKind(kindFor(name)), get: name => varFrame.has(name) ? descriptor(name, 'var') : undefined,
156
+ set() {} }
157
+ return dynamicRuntime.environment({
158
+ varFrame, frames: [{ kind: 'lexical', bindings: lexicalFrame }],
159
+ declareVars: realm ? (names, functionNames) => {
160
+ const globals = []
161
+ for (let index = 0; index < names.length; index++) {
162
+ const name = names[index], entry = mapGet(entries, name)
163
+ if (hasOverlay(name) || entry === undefined || entry.object === true || entry.deleted === true) appendArray(globals, name)
164
+ }
165
+ dynamicRuntime.declareGlobals(globals, functionNames)
166
+ } : undefined,
167
+ ambient(name, strict, origin, writeTarget) {
168
+ return dynamicRuntime.reference(name, {
169
+ get: () => get(name),
170
+ set: value => { result.reference(name, strict, writeTarget ?? (origin === undefined ? undefined : `${origin}:${stringify(name)}`)).value = value },
171
+ typeof: () => result.typeof(name),
172
+ delete: () => delete values[name],
173
+ }, undefined, strict)
174
+ },
175
+ createVar(name, origin) {
176
+ if (!mapHas(entries, name) || mapGet(entries, name).object || mapGet(entries, name).deleted) {
177
+ // A hard overlay pre-empts a realm-level var declaration, so that declaration is
178
+ // absorbed instead of being written through to a property the ambient cannot accept.
179
+ write(name, undefined, { declaration: !realm || hasOverlay(name), nativeKind: 'eval-var',
180
+ target: origin === undefined ? undefined : `${origin}:${stringify(name)}` })
181
+ }
182
+ return descriptor(name, 'var')
183
+ },
184
+ })
185
+ },
186
+ typeof(name) {
187
+ // Native typeof distinguishes an unresolvable reference from any error
188
+ // thrown by resolving a getter, including errors from another realm.
189
+ if (hasOverlay(name) || !mapHas(entries, name) && !setHas(fresh, name)
190
+ || mapGet(entries, name)?.object === true || mapGet(entries, name)?.deleted === true) return typeofAmbient(name)
191
+ return typeof get(name)
192
+ },
193
+ declare(name, target) {
194
+ if (needsDeclaration(name)) {
195
+ write(name, undefined, { readOnly: setHas(protectedNames, name), declaration: true,
196
+ nativeKind: mapGet(kinds, target) ?? 'var' })
197
+ commit(target)
198
+ }
199
+ },
200
+ assign(name, value, target) {
201
+ write(name, value, { readOnly: setHas(protectedNames, name), declaration: true,
202
+ nativeKind: mapGet(kinds, target) ?? 'let' })
203
+ commit(target)
204
+ return value
205
+ },
206
+ reference(name, strict, target) {
207
+ return new Proxy(Object.create(null), {
208
+ get: () => get(name),
209
+ set: (_, key, value) => {
210
+ checkWritable(name)
211
+ write(name, value, { strict, target })
212
+ return true
213
+ },
214
+ })
215
+ },
216
+ import(name, namespace, imported, target) {
217
+ mapSet(entries, name, { source: 'import', nativeKind: 'const', readOnly: setHas(protectedNames, name), read: () => imported === null ? namespace : namespace[imported] })
218
+ publish(name, () => read(name), value => write(name, value))
219
+ changed(name, 'import')
220
+ commit(target)
221
+ },
222
+ link(name, linked, target) {
223
+ if (name !== linked) {
224
+ mapSet(entries, name, { source: 'local', read: () => read(linked) })
225
+ publish(name, () => read(name), value => write(name, value))
226
+ changed(name, 'local')
227
+ }
228
+ commit(target)
229
+ },
230
+ candidate(names) {
231
+ const pending = new Map()
232
+ let published = false
233
+ // Pattern targets initialize values; source references enforce writes.
234
+ const values = new Proxy(Object.create(null), {
235
+ deleteProperty: (_, name) => published ? remove(name) : false,
236
+ get: (_, name) => {
237
+ if (published) return read(name)
238
+ return mapHas(pending, name) ? mapGet(pending, name) : get(name)
239
+ },
240
+ set: (_, name, value) => {
241
+ if (published) {
242
+ checkWritable(name)
243
+ write(name, value)
244
+ }
245
+ else mapSet(pending, name, value)
246
+ return true
247
+ },
248
+ })
249
+ return {
250
+ values,
251
+ reference(name, strict = false, target) {
252
+ return dynamicRuntime.reference(name, {
253
+ get: () => values[name],
254
+ set(value) {
255
+ if (!published && mapHas(pending, name)) {
256
+ if (setHas(protectedNames, name)) throw new errors.TypeError('Assignment to constant variable.')
257
+ } else checkWritable(name)
258
+ if (published) write(name, value, { strict, target })
259
+ else mapSet(pending, name, value)
260
+ },
261
+ delete: () => published ? remove(name) : false,
262
+ }, undefined, strict)
263
+ },
264
+ commit(target) {
265
+ const stored = []
266
+ for (let index = 0; index < names.length; index++) {
267
+ const name = names[index]
268
+ // A with/catch target can receive a var initializer while the
269
+ // declaration still establishes its own previously absent name.
270
+ if (!mapHas(pending, name) && (mapGet(kinds, target) !== 'var' || !needsDeclaration(name))) continue
271
+ if (storeValue(name, mapGet(pending, name), { readOnly: setHas(protectedNames, name), declaration: true,
272
+ nativeKind: mapGet(kinds, target) ?? 'let' }) === undefined) continue
273
+ appendArray(stored, name)
274
+ }
275
+ mapClear(pending)
276
+ published = true
277
+ for (let index = 0; index < stored.length; index++) {
278
+ const name = stored[index]
279
+ publish(name, () => read(name), value => write(name, value))
280
+ changed(name, 'local')
281
+ }
282
+ commit(target)
283
+ },
284
+ }
285
+ },
286
+ }
287
+ dynamicRuntime.setRootEnvironment(() => result.dynamic({ realm: true }))
288
+ // Only new-generation environments select intrinsic interfaces. Native
289
+ // global/prototype properties and saved legacy identities remain intact.
290
+ if (dynamicIntrinsics !== undefined) {
291
+ dynamicRuntime.installIntrinsics()
292
+ moduleRuntime.installIntrinsics()
293
+ }
294
+ return result
295
+ }
296
+ return {
297
+ begin,
298
+ read,
299
+ legacyReference(name, writable) {
300
+ const kind = mapGet(entries, name)?.nativeKind ?? 'let'
301
+ return { kind: includes(['var', 'eval-var', 'hoisted'], kind) ? 'var' : kind,
302
+ get: () => read(name),
303
+ typeof: () => mapGet(entries, name)?.deleted === true ? typeofAmbient(name) : typeof read(name),
304
+ set(value) {
305
+ if (!writable) throw new errors.TypeError('Assignment to constant variable.')
306
+ write(name, value)
307
+ },
308
+ declare() { write(name, undefined, { declaration: true }) },
309
+ delete() {
310
+ const entry = mapGet(entries, name)
311
+ if (entry.nativeKind !== 'eval-var') return false
312
+ mapSet(entries, name, { ...entry, deleted: true })
313
+ return true
314
+ },
315
+ }
316
+ },
317
+ publishLegacy(name, writable, binding, object = false) {
318
+ mapSet(entries, name, binding === undefined
319
+ ? { source: 'local', readOnly: !writable, legacyWritable: writable, object, read: () => readAmbient(name) }
320
+ : { source: 'import', read: () => {
321
+ const namespace = readAmbient(binding.namespace)
322
+ return binding.imported === undefined ? namespace : namespace[binding.imported]
323
+ } })
324
+ publish(name, () => read(name), value => write(name, value))
325
+ changed(name, binding === undefined ? 'local' : 'import')
326
+ },
327
+ localValue(name) {
328
+ const entry = mapGet(entries, name)
329
+ return entry?.source === 'local' && entry.object !== true && entry.read === undefined ? { value: entry.value } : undefined
330
+ },
331
+ has: name => mapHas(entries, name) && mapGet(entries, name).object !== true && mapGet(entries, name).deleted !== true,
332
+ facts() {
333
+ const facts = []
334
+ mapForEach(entries, (entry, name) => appendArray(facts, { name,
335
+ source: entry.deleted === true || entry.object === true && !hasAmbient(name) ? 'absent' : entry.source,
336
+ ...(entry.writeTarget === undefined ? {} : { write: entry.writeTarget }) }))
337
+ return facts
338
+ },
339
+ }
340
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Own the strict codec shape PTC Plus publishes into DSH's TYPERT registry.
3
+ *
4
+ * A strict codec decodes one wire member. DSH changed where the host reads that
5
+ * decoder: the preceding generation keeps it on `codec.schema` and evaluates
6
+ * `codec.schema.parse(value)`, while the current generation keeps a `create()`
7
+ * factory and evaluates `codec.create().parse(value)`. Each generation validates
8
+ * the member it owns and ignores the other, so every codec here publishes both
9
+ * and serves whichever shape the installed host implements — no version gate, no
10
+ * second registration path, and one decoder for both.
11
+ *
12
+ * The decoder is the maintained schema runtime, presented as the `parse` member
13
+ * both generations evaluate. Business payload validation stays with the owner of
14
+ * each RPC contract.
15
+ *
16
+ * @param {object} options - `typeSymbol` names the wire type; `schema` is the
17
+ * maintained schema the decoder evaluates.
18
+ * @returns {object} a strict codec accepted by either generation.
19
+ */
20
+ export function strictCodec({ typeSymbol, schema }) {
21
+ const decoder = { parse: schema }
22
+ return {
23
+ mode: 'strict',
24
+ typeSymbol,
25
+ schema: decoder,
26
+ create: () => decoder,
27
+ }
28
+ }
@@ -0,0 +1,29 @@
1
+ import loadAmaro from './compiler-amaro.cjs'
2
+
3
+ export { LEGACY_USER_BINDING_TRANSFORM, USER_BINDING_TRANSFORM, PROTECTED_MODULE_TRANSFORM,
4
+ moduleTransformForLanguage, supportedUserBindingTransform } from './module-transform-contract.js'
5
+
6
+ /** Keep binding-module lowering independent of the Host's experimental transform API. */
7
+ export function transformTypeScriptModule(source) {
8
+ return transformTypeScriptSource(source).code
9
+ }
10
+
11
+ export function transformTypeScriptSource(source, options = {}) {
12
+ try {
13
+ return loadAmaro().transformSync(source, { mode: 'transform', sourceMap: false, ...options })
14
+ } catch (error) {
15
+ if (error instanceof Error) throw error
16
+ throw new SyntaxError(error.message, { cause: error })
17
+ }
18
+ }
19
+
20
+ let nativeUsingSupport
21
+
22
+ /** Node 22.19 does not parse explicit resource declarations by default. Keep
23
+ * one compiler-owned fallback so every source entry gets the same disposal
24
+ * semantics until the host parser proves native support. */
25
+ export function supportsNativeUsing() {
26
+ if (nativeUsingSupport !== undefined) return nativeUsingSupport
27
+ try { Function('using __ptc_resource = null')(); nativeUsingSupport = true } catch { nativeUsingSupport = false }
28
+ return nativeUsingSupport
29
+ }
@@ -0,0 +1,70 @@
1
+ import { deepFreeze } from './record-utils.js'
2
+
3
+ export const BINDING_SUBMISSION = deepFreeze({
4
+ name: 'submitBindingDraft',
5
+ description: 'Submit a Global User Binding draft for user review during /binding. Call inside run_code with the supplied requestId. Validates source and accepts one draft per request; does not save, enable or run it.',
6
+ parameters: {
7
+ type: 'object', additionalProperties: false, required: ['requestId', 'entry'],
8
+ properties: {
9
+ requestId: { type: 'string', description: 'Copy from the active /binding request.' },
10
+ entry: {
11
+ type: 'object', additionalProperties: false,
12
+ required: ['id', 'name', 'scope', 'purpose', 'source'],
13
+ properties: {
14
+ id: { type: 'string', description: 'Stable storage ID: 1-64 ASCII letters, digits, dots, underscores or hyphens; starts with a letter or digit.' },
15
+ name: { type: 'string', description: 'namespace: JavaScript identifier used directly as the call name; top-level: display label.' },
16
+ scope: { type: 'string', enum: ['namespace', 'top-level'] },
17
+ symbols: { type: 'array', items: { type: 'string' }, description: 'Named value exports to expose; omit to select all. Names must exist in source.' },
18
+ purpose: { type: 'string', description: 'One factual line explaining the API.' },
19
+ modelContext: {
20
+ type: 'object', additionalProperties: false,
21
+ description: 'Model documentation when enabled: includeDeclaration selects the source-derived interface (default true); instructions supplies a separate usage prompt (default empty, at most 4096 characters).',
22
+ properties: {
23
+ includeDeclaration: { type: 'boolean', description: 'Include the source-derived interface (default true), independently of instructions.' },
24
+ instructions: { type: 'string', maxLength: 4096, description: 'Usage prompt for the model: when to call this API and any constraints absent from its types. Empty omits the prompt.' },
25
+ },
26
+ },
27
+ source: { type: 'string', description: 'Complete TypeScript module with named value exports, explicit useful types, no default exports or re-exports.' },
28
+ },
29
+ },
30
+ },
31
+ },
32
+ returns: {
33
+ type: 'object', additionalProperties: false, required: ['accepted', 'id', 'requestId'],
34
+ properties: { accepted: { const: true }, id: { type: 'string' }, requestId: { type: 'string' } },
35
+ },
36
+ effect: 'ptc-state', authority: 'ptc-plus-program-binding', completeness: 'complete', replay: 'recorded-value',
37
+ })
38
+
39
+ export const BINDING_AUTHORING_SDK = `
40
+ During /binding, submit a draft for user review from run_code using the supplied requestId. Submission does not save, enable or execute it.
41
+ \`\`\`ts
42
+ declare namespace code {
43
+ function submitBindingDraft(args: {
44
+ requestId: string;
45
+ entry: { id: string; name: string; scope: "namespace" | "top-level"; symbols?: string[]; purpose: string; source: string; modelContext?: { includeDeclaration?: boolean; instructions?: string } };
46
+ }): Promise<{ accepted: true; id: string; requestId: string }>;
47
+ }
48
+ \`\`\``
49
+
50
+ export function bindingAuthoringInstructions(requestId, cwd) {
51
+ return `Create exactly one Global User Binding candidate for the user's requirement.
52
+
53
+ Develop and check the helper in small run_code cells before submission. Start with a small executable check of the core behavior, then extend it using in-memory inputs and assertions for normal cases, edge cases and failures. Use node:assert/strict for comparisons. Keep the candidate and reusable test inputs in REPL variables; correct them from observed results and recheck changed behavior. For bulk tests, print totals and a few representative failures, keeping successful cases out of the output.
54
+
55
+ Tests must not write or delete files, change external services, or call effectful tools. For filesystem or network helpers, use in-memory substitutes and check argument forwarding and error propagation. Check imports and initializers before executing them; review unknown effects without running them. A substitute verifies only the behavior its assertions cover; real integration remains untested.
56
+
57
+ Review the final source, selected exports, types and usage prompt against the requirement and tested implementation. Keep test scaffolding out of the submitted module. In the answer, briefly report verified behavior and remaining limits; distinguish test-harness errors from helper errors and cite only behavior supported by the checks.
58
+
59
+ Submit from run_code with code.submitBindingDraft({ requestId: ${JSON.stringify(requestId)}, entry }). One valid submission completes this request; correct validation errors and retry only rejected submissions. The receipt confirms a disabled draft awaiting the user's save decision. Saving and enabling belong to the user.
60
+
61
+ Candidate contract:
62
+ ${Object.entries(BINDING_SUBMISSION.parameters.properties.entry.properties).map(([name, field]) => `- ${name}: ${field.description ?? field.enum.join(' | ')}`).join('\n')}
63
+
64
+ Use namespace scope for a compact API: name "textHelpers" with export "trim" is called as textHelpers.trim(...). Top-level scope exposes selected exports directly. Supply complete TypeScript module source with useful types and API comments; the interface is generated from it. Do not supply entry.enabled or entry.declaration. Include modelContext.includeDeclaration and modelContext.instructions; leave instructions empty when the interface is sufficient, otherwise add only usage information missing from it. On edit, preserve the entry ID and existing prompt preferences unless the requirement changes them.
65
+
66
+ Relative imports in saved modules resolve from ${JSON.stringify(cwd)}, the binding storage directory, rather than the session cwd used by ordinary REPL cells. Prefer Node built-ins when sufficient; inspect dependencies only as needed. The selected entry is supplied for edits. Do not read or modify binding storage or unrelated entries, or scan the host installation to discover this API.
67
+
68
+ Submission example:
69
+ await code.submitBindingDraft({ requestId: ${JSON.stringify(requestId)}, entry: { id: "text-helpers", name: "textHelpers", scope: "namespace", purpose: "Trim surrounding whitespace.", source: "export function trim(value: string): string { return value.trim() }", modelContext: { includeDeclaration: true, instructions: "" } } })`
70
+ }
@@ -0,0 +1,184 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks'
2
+ import { randomUUID } from 'node:crypto'
3
+ import { create as createDomain } from 'node:domain'
4
+ import { createRequire, registerHooks } from 'node:module'
5
+ import { resolve } from 'node:path'
6
+ import repl from 'node:repl'
7
+ import { PassThrough } from 'node:stream'
8
+ import { inspect } from 'node:util'
9
+ import { pathToFileURL } from 'node:url'
10
+ import { parentPort, workerData } from 'node:worker_threads'
11
+ import { runInContext } from 'node:vm'
12
+ import { prepareConsoleProgram } from './compiler-service.js'
13
+ import { createStatefulRootRuntime } from './stateful-root-runtime.js'
14
+ import { managedModuleImport, managedRequire, readModuleImport } from './stateful-module-runtime.js'
15
+ import { compileStatefulModule, createUserModuleCompilationHooks } from './stateful-module-compiler.js'
16
+ import { USER_BINDING_TRANSFORM } from './module-transform-contract.js'
17
+ import { moduleRuntimeIntrinsics } from './compiler-intrinsics.js'
18
+ import { stringifyCompilerData } from './compiler-data.js'
19
+ import { createCellCompletionObserver } from './cell-completion.js'
20
+ import { WORKER_SHUTDOWN_ACKNOWLEDGEMENT, WORKER_SHUTDOWN_REQUEST } from './worker-shutdown.js'
21
+
22
+ const { Object, Set, setHas, setAdd, setDelete, appendArray } = moduleRuntimeIntrinsics
23
+
24
+ const input = new PassThrough()
25
+ const output = new PassThrough()
26
+ output.resume()
27
+ const evaluations = new AsyncLocalStorage()
28
+ const server = repl.start({ input, output, terminal: false, prompt: '', useGlobal: false })
29
+ const completionObserver = createCellCompletionObserver(runInContext('Function', server.context))
30
+ server.context.console = console
31
+ const errorDomain = server.eval.domain ?? createDomain()
32
+ errorDomain.removeAllListeners('error')
33
+ errorDomain.on('error', error => evaluations.getStore()?.finish(true, error))
34
+ const completionKey = `__ptc_console_${randomUUID().replaceAll('-', '')}`
35
+ Object.defineProperty(server.context, completionKey, { value: value => {
36
+ evaluations.getStore().completed = true
37
+ return value
38
+ } })
39
+ const parentUrl = pathToFileURL(resolve(workerData.cwd, 'ptc-plus-console')).href
40
+ let moduleUrl
41
+ let replParent
42
+ let replModuleRealm
43
+ const adapterParents = new Set()
44
+ const knownBindings = new Set()
45
+ const roots = createStatefulRootRuntime({
46
+ importModule: (source, options) => managedModuleImport(replParent, source, options, replModuleRealm),
47
+ errors: runInContext('({ ReferenceError, TypeError })', server.context),
48
+ dynamicIntrinsics: runInContext('({intrinsicEval:eval,realmFunction:Function,globalObject:globalThis,errors:{ReferenceError,TypeError,SyntaxError},reflect:Reflect,object:Object})', server.context),
49
+ readAmbient: name => runInContext(name, server.context),
50
+ typeofAmbient: name => runInContext(`typeof ${name}`, server.context),
51
+ writeAmbient(name, value, strict) {
52
+ const argument = name === '__ptc_value' ? '__ptc_other_value' : '__ptc_value'
53
+ runInContext(`${strict ? '"use strict";' : ''}(${argument}) => (${name} = ${argument})`, server.context)(value)
54
+ },
55
+ deleteAmbient: name => runInContext(`delete ${name}`, server.context),
56
+ hasAmbient: name => Reflect.has(server.context, name),
57
+ publish: name => setAdd(knownBindings, name),
58
+ })
59
+ class ConsoleReturn {
60
+ constructor(value) { this.value = value }
61
+ }
62
+ const moduleCompilation = createUserModuleCompilationHooks({
63
+ transformForParent: parent => parent === replParent || parent === parentUrl ? USER_BINDING_TRANSFORM : undefined,
64
+ })
65
+ server.context.require = managedRequire(parentUrl, createRequire(parentUrl))
66
+ const importCanary = 'data:text/javascript,export default 1'
67
+ registerHooks({ resolve(specifier, context, nextResolve) {
68
+ if (specifier === importCanary) replParent = context.parentURL
69
+ return moduleCompilation.resolve(specifier, context, (selected, original) =>
70
+ nextResolve(selected, original.parentURL === moduleUrl || original.parentURL === replParent || setHas(adapterParents, original.parentURL)
71
+ ? { ...original, parentURL: parentUrl } : original))
72
+ }, load: moduleCompilation.load })
73
+ const describe = value => inspect(value, {
74
+ colors: false, customInspect: false, getters: false, depth: 4,
75
+ maxArrayLength: 40, maxStringLength: Math.min(workerData.maxOutputBytes, 8192), breakLength: 100,
76
+ })
77
+
78
+ async function evaluate(source) {
79
+ const prepared = prepareConsoleProgram(source, { languageSemantics: 'stateful-v1', knownBindings })
80
+ const helpers = [prepared.rootRuntimeName, prepared.returnSignal]
81
+ Object.defineProperty(server.context, prepared.rootRuntimeName, {
82
+ configurable: true, value: roots.begin({ ...prepared.rootBindings, committed: () => {} }),
83
+ })
84
+ Object.defineProperty(server.context, prepared.returnSignal, { configurable: true, value: ConsoleReturn })
85
+ try {
86
+ for (let index = 0; index < prepared.moduleLoads.length; index++) {
87
+ const load = prepared.moduleLoads[index]
88
+ let attributes = ''
89
+ const entries = Object.entries(load.options?.with ?? {})
90
+ for (let entry = 0; entry < entries.length; entry++) {
91
+ attributes += `${entry === 0 ? ' with {' : ','}${stringifyCompilerData(entries[entry][0])}:${stringifyCompilerData(entries[entry][1])}`
92
+ }
93
+ if (entries.length > 0) attributes += '}'
94
+ const source = stringifyCompilerData(load.source)
95
+ let required
96
+ if (load.requiredExports !== undefined) {
97
+ required = ''
98
+ for (let entry = 0; entry < load.requiredExports.length; entry++) {
99
+ required += `${entry === 0 ? '' : ','}${stringifyCompilerData(load.requiredExports[entry])} as __required_${entry}`
100
+ }
101
+ }
102
+ const adapter = `data:text/javascript,${encodeURIComponent(`import * as namespace from ${source}${attributes};\n${required === undefined ? '' : `export { ${required} } from ${source}${attributes};`}\nexport { namespace };`)}#console-${randomUUID()}`
103
+ setAdd(adapterParents, adapter)
104
+ moduleCompilation.mark(adapter, { compiled: true })
105
+ let namespace
106
+ try {
107
+ const native = await import(adapter)
108
+ namespace = readModuleImport(adapter, load.source, null, () => native.namespace, load.options?.with)
109
+ } finally { setDelete(adapterParents, adapter) }
110
+ if (load.global !== undefined) {
111
+ appendArray(helpers, load.global)
112
+ Object.defineProperty(server.context, load.global, { configurable: true, value: namespace })
113
+ }
114
+ }
115
+ return await evaluatePrepared(prepared.code, prepared.asyncCompletion)
116
+ } finally {
117
+ for (let index = 0; index < helpers.length; index++) delete server.context[helpers[index]]
118
+ }
119
+ }
120
+
121
+ function evaluatePrepared(javascript, asyncCompletion = false) {
122
+ return new Promise((resolve, reject) => {
123
+ const complete = `this[${stringifyCompilerData(completionKey)}]`
124
+ // Preserve expression results while distinguishing rejection with null/undefined.
125
+ const code = `${javascript}\n;{let completed = ${complete}();}\n;`
126
+ let settled = false
127
+ const state = { completed: false, finish(failed, value) {
128
+ if (settled) return
129
+ settled = true
130
+ if (failed && value instanceof ConsoleReturn) completionObserver.settle(value.value, resolve, reject)
131
+ else if (failed) reject(value)
132
+ else completionObserver.settle(value, resolve, reject)
133
+ } }
134
+ evaluations.run(state, () => errorDomain.run(() => {
135
+ server.eval(`${code}\n`, server.context, 'ptc-plus-console', (error, value) => {
136
+ if (asyncCompletion && (error === null || error === undefined)) {
137
+ completionObserver.observe(value, value => state.finish(false, value), error => state.finish(true, error))
138
+ return
139
+ }
140
+ const failed = !state.completed || error !== null && error !== undefined
141
+ state.finish(failed, failed ? error : value)
142
+ })
143
+ }))
144
+ })
145
+ }
146
+ try {
147
+ replModuleRealm = await evaluatePrepared('({Promise,stringify:value=>`${value}`,importModule:(source,options)=>import(source,options)})')
148
+ const prepared = compileStatefulModule(workerData.source)
149
+ moduleUrl = `data:text/javascript,${encodeURIComponent(prepared.code)}#binding`
150
+ moduleCompilation.mark(moduleUrl, { compiled: true, moduleInterface: prepared.moduleInterface, sourceRegions: prepared.sourceRegions })
151
+ const namespace = await managedModuleImport(moduleUrl, moduleUrl)
152
+ await evaluatePrepared(`import(${stringifyCompilerData(importCanary)})`)
153
+ const names = Object.keys(namespace)
154
+ for (let index = 0; index < names.length; index++) {
155
+ const name = names[index]
156
+ setAdd(knownBindings, name)
157
+ Object.defineProperty(server.context, name, { configurable: true, enumerable: true, get: () => namespace[name] })
158
+ }
159
+ parentPort.on('message', async (message) => {
160
+ if (message?.type === WORKER_SHUTDOWN_REQUEST) {
161
+ // Release the realm, REPL and domain this console owns, then report the
162
+ // release before the host gives up on the thread; see worker-shutdown.js.
163
+ server.close()
164
+ input.destroy()
165
+ output.destroy()
166
+ errorDomain.removeAllListeners('error')
167
+ parentPort.postMessage({ type: WORKER_SHUTDOWN_ACKNOWLEDGEMENT })
168
+ parentPort.close()
169
+ return
170
+ }
171
+ const { id, code } = message
172
+ let result
173
+ try {
174
+ const value = await evaluate(code)
175
+ result = { id, output: describe(value) }
176
+ } catch (error) {
177
+ result = { id, error: describe(error) }
178
+ }
179
+ await new Promise(resolve => process.stdout.write('', () => process.stderr.write('', resolve)))
180
+ parentPort.postMessage(result)
181
+ })
182
+ } catch (error) {
183
+ parentPort.postMessage({ fatal: true, error: describe(error) })
184
+ }