dsh-ptc-plus 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/README.en.md +164 -78
  2. package/README.md +165 -79
  3. package/assets/dsh-ptc-plus-banner-en.webp +0 -0
  4. package/assets/dsh-ptc-plus-banner-zh.webp +0 -0
  5. package/assets/ptc-plus-icon.svg +5 -0
  6. package/client.js +854 -336
  7. package/compiler-core.cjs +161 -157
  8. package/compiler-core.cjs.map +3 -3
  9. package/docs/README.md +4 -1
  10. package/docs/adr/0005-temporary-rejected-cell-edit-transport.md +8 -7
  11. package/docs/adr/0008-define-agent-operating-contract.md +2 -2
  12. package/docs/adr/0010-session-log-derived-recovery-tips.md +7 -0
  13. package/docs/adr/0016-archive-resolved-review-findings.md +3 -3
  14. package/docs/adr/0019-plugin-settings-and-kill-switch.md +3 -3
  15. package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -0
  16. package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +14 -3
  17. package/docs/adr/0023-global-user-bindings.md +7 -7
  18. package/docs/adr/0025-use-versioned-logical-binding-identities.md +3 -3
  19. package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +23 -7
  20. package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +9 -5
  21. package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +26 -6
  22. package/docs/adr/0031-close-semantic-obligations-by-owner.md +29 -0
  23. package/docs/adr/README.md +4 -4
  24. package/docs/architecture.md +15 -10
  25. package/docs/binding-review-placement-design.md +1 -1
  26. package/docs/client-ui.md +9 -5
  27. package/docs/durability-design.md +12 -9
  28. package/docs/installation.md +27 -9
  29. package/docs/runtime-reference.md +23 -12
  30. package/docs/semantic-validation.md +7 -3
  31. package/docs/stateful-binding-updates-proposal.md +8 -4
  32. package/docs/user-bindings.en.md +10 -7
  33. package/docs/user-bindings.md +9 -6
  34. package/docs/value-wire.md +1 -1
  35. package/docs/verification-optimization-work.md +41 -0
  36. package/docs/verification.md +53 -2
  37. package/index.js +39 -34
  38. package/internal/async-local-storage-intrinsics.js +43 -0
  39. package/internal/binding-declaration-contract.js +8 -0
  40. package/internal/binding-source-analysis.js +381 -64
  41. package/internal/binding-update-policy.js +7 -1
  42. package/internal/callable-source-facts.js +37 -1
  43. package/internal/cell-analysis.js +85 -17
  44. package/internal/compiler-intrinsics.js +9 -43
  45. package/internal/compiler-module-links.js +7 -4
  46. package/internal/config-spec.js +1 -1
  47. package/internal/cordis-tools-owner.js +20 -10
  48. package/internal/dynamic-environment-integration.js +8 -4
  49. package/internal/dynamic-environment-runtime.js +24 -3
  50. package/internal/dynamic-native-calls.js +4 -1
  51. package/internal/dynamic-native-runtime.js +15 -1
  52. package/internal/edit-transport-owner.js +80 -20
  53. package/internal/execution-seam-compat.js +192 -30
  54. package/internal/failure-reporting.js +38 -30
  55. package/internal/isolated-worker.js +17 -10
  56. package/internal/kernel-worker.js +645 -393
  57. package/internal/message-sources.js +14 -0
  58. package/internal/module-compilation.js +7 -5
  59. package/internal/module-transform-contract.js +32 -2
  60. package/internal/record-utils.js +24 -15
  61. package/internal/repl-memory-projection.js +10 -0
  62. package/internal/repl-scope-normalizer.js +72 -35
  63. package/internal/repl-value-observer.js +145 -46
  64. package/internal/runtime-config.js +62 -2
  65. package/internal/runtime-intrinsics.js +104 -0
  66. package/internal/runtime-messages.js +6 -4
  67. package/internal/session-cell-executor.js +26 -2
  68. package/internal/session-journal-recovery.js +267 -67
  69. package/internal/session-journal-schema.js +30 -13
  70. package/internal/session-journal.js +485 -44
  71. package/internal/session-log-view.js +5 -4
  72. package/internal/session-runtime.js +46 -21
  73. package/internal/session-state.js +7 -29
  74. package/internal/settings-compat.js +66 -1
  75. package/internal/source-position-map.js +38 -0
  76. package/internal/stateful-root-compiler.js +3 -1
  77. package/internal/typescript-transform.js +4 -2
  78. package/internal/user-binding-console-worker.js +115 -41
  79. package/internal/user-binding-draft-projection.js +3 -2
  80. package/internal/user-bindings-owner.js +2 -1
  81. package/internal/user-bindings.js +16 -3
  82. package/internal/validated-parse-repair.js +6 -2
  83. package/internal/value-wire.js +189 -94
  84. package/internal/worker-client.js +99 -8
  85. package/internal/worker-cwd-virtualization.js +82 -54
  86. package/internal/worker-environment.js +20 -0
  87. package/internal/worker-output-capture.js +187 -0
  88. package/internal/worker-output-fence.js +26 -0
  89. package/internal/worker-realm-surfaces.js +197 -0
  90. package/internal/worker-repl-realm.js +140 -0
  91. package/package.json +37 -4
  92. package/src/client-activity.js +12 -2
  93. package/src/client-authoring-view.js +121 -62
  94. package/src/client-console.js +4 -2
  95. package/src/client-copy.js +4 -2
  96. package/src/client-feature-gates.js +7 -9
  97. package/src/client-host-compat.js +104 -27
  98. package/src/client-repl-view.js +3 -1
  99. package/src/client-styles.js +12 -1
  100. package/src/client-tool-view.js +3 -1
  101. package/src/client-transport.js +2 -2
  102. package/src/client-workbench.js +4 -3
  103. package/src/client.js +56 -17
@@ -1,23 +1,29 @@
1
- import { AsyncLocalStorage } from 'node:async_hooks'
2
1
  import { create as createDomain } from 'node:domain'
2
+ import { Console } from 'node:console'
3
3
  import { createRequire, registerHooks } from 'node:module'
4
4
  import { managedModuleImport, managedRequire, readModuleImport, statefulModuleLink } from './stateful-module-runtime.js'
5
5
  import { staticModuleLinkReference } from './compiler-module-links.js'
6
6
  import { compileStatefulModule, createUserModuleCompilationHooks } from './stateful-module-compiler.js'
7
- import { USER_BINDING_TRANSFORM, LEGACY_USER_BINDING_TRANSFORM, moduleTransformForLanguage } from './module-transform-contract.js'
7
+ import { USER_BINDING_TRANSFORM, LEGACY_USER_BINDING_TRANSFORM } from './module-transform-contract.js'
8
8
  import { isAbsolute, resolve } from 'node:path'
9
9
  import repl from 'node:repl'
10
- import { PassThrough } from 'node:stream'
10
+ import { PassThrough, Writable } from 'node:stream'
11
11
  import { pathToFileURL } from 'node:url'
12
- import { formatWithOptions } from 'node:util'
13
12
  import { MessageChannel, parentPort, workerData } from 'node:worker_threads'
14
- import { runInContext } from 'node:vm'
15
13
  import { synchronizeBuiltinEsmExports } from './builtin-esm-sync.js'
14
+ import { createPrivateAsyncLocalStorage } from './async-local-storage-intrinsics.js'
16
15
  import { createExceptionOriginScope, errorDetails, messageOf, programBindingError } from './failure-reporting.js'
17
- import { AMBIENT_GLOBALS, DURABLE_IMPORTS, FORBIDDEN_IMPORTS } from './module-policy.js'
16
+ import { DURABLE_IMPORTS, FORBIDDEN_IMPORTS } from './module-policy.js'
18
17
  import { decodeValue, encodeValue } from './value-wire.js'
19
18
  import { LEGACY_USER_BINDINGS_REUSE_POLICY, LIVE_USER_BINDINGS_SHADOW_POLICY, normalizeUserBindingNames } from './session-journal-schema.js'
20
19
  import { installWorkerCwdVirtualization } from './worker-cwd-virtualization.js'
20
+ import {
21
+ WORKER_REALM_MUTATION,
22
+ assignWorkerRealmProperty,
23
+ defineWorkerRealmProperty,
24
+ deleteWorkerRealmProperty,
25
+ installProcessControlGuards,
26
+ } from './worker-realm-surfaces.js'
21
27
  import { createReplValueObserver, supportsAwaitLexicals, previewBindingValue } from './repl-value-observer.js'
22
28
  import { createStatefulRootRuntime } from './stateful-root-runtime.js'
23
29
  import { createNativeRootDynamic } from './native-root-dynamic.js'
@@ -25,9 +31,80 @@ import { moduleRuntimeIntrinsics } from './compiler-intrinsics.js'
25
31
  import { compilerDescriptors } from './compiler-descriptors.js'
26
32
  import { createCellCompletionObserver } from './cell-completion.js'
27
33
  import { WORKER_SHUTDOWN_ACKNOWLEDGEMENT, WORKER_SHUTDOWN_REQUEST } from './worker-shutdown.js'
34
+ import { outputFenceMarker } from './worker-output-fence.js'
35
+ import { WORKER_REPL_OPTIONS, createWorkerControlPromise, createWorkerReplErrorHandler,
36
+ createWorkerUncaughtExceptionHandler,
37
+ disableWorkerReplDomain,
38
+ captureWorkerReplGlobals, protectWorkerReplAsyncContext, restoreWorkerReplGlobals, runInWorkerReplRealm,
39
+ workerReplContext } from './worker-repl-realm.js'
40
+ import { installProgramAmbientResolver } from './dynamic-environment-runtime.js'
28
41
 
29
- const { Object, mapGet, mapSet, mapHas, mapDelete, setHas, setAdd, setDelete } = moduleRuntimeIntrinsics
30
- const hasProperty = Reflect.has
42
+ const { Object, Map, Set, Proxy, Error,
43
+ mapGet, mapSet, mapHas, mapDelete, mapClear, mapSize, mapForEach,
44
+ setHas, setAdd, setDelete, setClear, setForEach,
45
+ everyArray, someArray, appendArray, copyArray, join, sliceString, endsWith,
46
+ bufferConcat, bufferIsBuffer, bufferToString,
47
+ Reflect: privateReflect, promiseResolve, promiseReject } = moduleRuntimeIntrinsics
48
+ const PublicPromise = globalThis.Promise
49
+ const jsonStringify = globalThis.JSON.stringify
50
+ const bufferByteLength = globalThis.Buffer.byteLength
51
+ const String = globalThis.String
52
+ const process = globalThis.process
53
+ const ControlPromise = createWorkerControlPromise()
54
+ const hasProperty = privateReflect.has
55
+
56
+ function mapArray(values, visit) {
57
+ const result = []
58
+ for (let index = 0; index < values.length; index++) appendArray(result, visit(values[index], index))
59
+ return result
60
+ }
61
+
62
+ function filterArray(values, select) {
63
+ const result = []
64
+ for (let index = 0; index < values.length; index++) {
65
+ if (select(values[index], index)) appendArray(result, values[index])
66
+ }
67
+ return result
68
+ }
69
+
70
+ function mapFromArray(values, visit) {
71
+ const result = new Map()
72
+ for (let index = 0; index < values.length; index++) {
73
+ const entry = visit(values[index], index)
74
+ mapSet(result, entry[0], entry[1])
75
+ }
76
+ return result
77
+ }
78
+
79
+ function setFromArray(values) {
80
+ const result = new Set()
81
+ for (let index = 0; index < values.length; index++) setAdd(result, values[index])
82
+ return result
83
+ }
84
+
85
+ function mapKeysArray(value) {
86
+ const result = []
87
+ mapForEach(value, (_item, key) => appendArray(result, key))
88
+ return result
89
+ }
90
+
91
+ function mapEntriesArray(value) {
92
+ const result = []
93
+ mapForEach(value, (item, key) => appendArray(result, [key, item]))
94
+ return result
95
+ }
96
+
97
+ function mapValuesArray(value) {
98
+ const result = []
99
+ mapForEach(value, item => appendArray(result, item))
100
+ return result
101
+ }
102
+
103
+ function setValuesArray(value) {
104
+ const result = []
105
+ setForEach(value, item => appendArray(result, item))
106
+ return result
107
+ }
31
108
 
32
109
  if (parentPort === null) throw new Error('ptc-plus kernel worker started without a parent port')
33
110
  const { port1, port2: channel } = new MessageChannel()
@@ -35,42 +112,50 @@ const { port1, port2: channel } = new MessageChannel()
35
112
  const input = new PassThrough()
36
113
  const output = new PassThrough()
37
114
  output.resume()
38
- const evaluationScope = new AsyncLocalStorage()
115
+ const evaluationScope = createPrivateAsyncLocalStorage()
39
116
  const sessionCwd = typeof workerData?.cwd === 'string' ? workerData.cwd : undefined
40
117
  if (sessionCwd !== undefined && !isAbsolute(sessionCwd)) {
41
- throw new Error(`ptc-plus session cwd must be absolute, got ${JSON.stringify(sessionCwd)}`)
118
+ throw new Error(`ptc-plus session cwd must be absolute, got ${jsonStringify(sessionCwd)}`)
42
119
  }
120
+ const workerGlobalBaseline = captureWorkerReplGlobals()
43
121
  const server = repl.start({
44
122
  input,
45
123
  output,
46
124
  terminal: false,
47
125
  prompt: '',
48
- useGlobal: false,
126
+ ...WORKER_REPL_OPTIONS,
127
+ handleError: createWorkerReplErrorHandler(evaluationScope, (finish, error) => finish(true, error)),
49
128
  ignoreUndefined: true,
50
129
  })
130
+ protectWorkerReplAsyncContext(server)
131
+ const originalRequire = server.context.require
51
132
  // domain.bind exposes its owner on a bound evaluator in older Node REPLs.
52
133
  // Newer REPLs honor an explicitly entered domain. Capture before REPL error
53
134
  // formatting can evaluate user-defined stack/name getters.
54
- const errorDomain = server.eval.domain ?? createDomain()
135
+ const errorDomain = disableWorkerReplDomain(server.eval.domain ?? createDomain())
55
136
  errorDomain.removeAllListeners('error')
56
137
  errorDomain.on('error', error => evaluationScope.getStore()?.(true, error))
57
- const context = server.context
58
- const contextGlobal = runInContext('globalThis', context)
59
- const completionObserver = createCellCompletionObserver(runInContext('Function', context))
138
+ const handleUncaughtException = createWorkerUncaughtExceptionHandler(evaluationScope, process)
139
+ process.on('uncaughtException', handleUncaughtException)
140
+ const context = workerReplContext(server)
141
+ restoreWorkerReplGlobals(workerGlobalBaseline, context)
142
+ const workerBaselineGlobals = setFromArray(mapKeysArray(workerGlobalBaseline))
143
+ const contextGlobal = runInWorkerReplRealm('globalThis')
144
+ const completionObserver = createCellCompletionObserver(runInWorkerReplRealm('Function'))
60
145
  let replModuleRealm
61
146
  const statefulRoots = createStatefulRootRuntime({
62
147
  refresh: name => refreshLegacyPublication(name),
63
148
  importModule: (source, options) => managedModuleImport(replParent, source, options, replModuleRealm),
64
- errors: runInContext('({ ReferenceError, TypeError })', context),
65
- dynamicIntrinsics: runInContext('({intrinsicEval:eval,realmFunction:Function,globalObject:globalThis,errors:{ReferenceError,TypeError,SyntaxError},reflect:Reflect,object:Object})', context),
66
- readAmbient: name => runInContext(name, context),
67
- typeofAmbient: name => runInContext(`typeof ${name}`, context),
149
+ errors: runInWorkerReplRealm('({ ReferenceError, TypeError })'),
150
+ dynamicIntrinsics: runInWorkerReplRealm('({intrinsicEval:eval,realmFunction:Function,globalObject:globalThis,errors:{ReferenceError,TypeError,SyntaxError},reflect:Reflect,object:Object})'),
151
+ readAmbient: name => runInWorkerReplRealm(name),
152
+ typeofAmbient: name => runInWorkerReplRealm(`typeof ${name}`),
68
153
  canWriteAmbient: name => writableRootProperty(name),
69
154
  writeAmbient(name, value, strict) {
70
155
  const argument = name === '__ptc_value' ? '__ptc_other_value' : '__ptc_value'
71
- runInContext(`${strict ? '"use strict";' : ''}(${argument}) => (${name} = ${argument})`, context)(value)
156
+ runInWorkerReplRealm(`${strict ? '"use strict";' : ''}(${argument}) => (${name} = ${argument})`)(value)
72
157
  },
73
- deleteAmbient: name => runInContext(`delete ${name}`, context),
158
+ deleteAmbient: name => runInWorkerReplRealm(`delete ${name}`),
74
159
  hasAmbient: name => setHas(installedGlobals, name)
75
160
  ? mapGet(installedGlobalOriginals, name).descriptor !== undefined : hasProperty(context, name),
76
161
  // Request namespaces stay on globalThis; source declarations own separate
@@ -83,15 +168,15 @@ const statefulRoots = createStatefulRootRuntime({
83
168
  })
84
169
  const nativeRootDynamic = createNativeRootDynamic({
85
170
  logicalReference: (name, writable) => statefulRoots.legacyReference(name, writable),
86
- intrinsics: runInContext('({intrinsicEval:eval,realmFunction:Function,globalObject:globalThis,errors:{ReferenceError,TypeError,SyntaxError},reflect:Reflect,object:Object})', context),
171
+ intrinsics: runInWorkerReplRealm('({intrinsicEval:eval,realmFunction:Function,globalObject:globalThis,errors:{ReferenceError,TypeError,SyntaxError},reflect:Reflect,object:Object})'),
87
172
  importModule: (source, options) => managedModuleImport(replParent, source, options, replModuleRealm),
88
- read: name => runInContext(name, context),
89
- typeOf: name => runInContext(`typeof ${name}`, context),
173
+ read: name => runInWorkerReplRealm(name),
174
+ typeOf: name => runInWorkerReplRealm(`typeof ${name}`),
90
175
  write(name, value, strict) {
91
176
  const argument = name === '__ptc_value' ? '__ptc_other_value' : '__ptc_value'
92
- runInContext(`${strict ? '"use strict";' : ''}(${argument}) => (${name} = ${argument})`, context)(value)
177
+ runInWorkerReplRealm(`${strict ? '"use strict";' : ''}(${argument}) => (${name} = ${argument})`)(value)
93
178
  },
94
- remove: name => runInContext(`delete ${name}`, context),
179
+ remove: name => runInWorkerReplRealm(`delete ${name}`),
95
180
  })
96
181
  let valueObserver
97
182
  const REPL_IMPORT_CANARY = 'data:text/javascript,export default 1'
@@ -101,7 +186,7 @@ const staticAdapterParents = new Set()
101
186
  const userBindingModuleParents = new Map()
102
187
  const userModuleCompilation = createUserModuleCompilationHooks({
103
188
  transformForParent: parent => (parent === replParent || parent === sessionReplParent) && activeExecution !== undefined
104
- ? moduleTransformForLanguage(activeExecution.languageSemantics) : undefined,
189
+ ? activeExecution.moduleTransform : undefined,
105
190
  })
106
191
  registerHooks({
107
192
  resolve(specifier, context, nextResolve) {
@@ -117,12 +202,13 @@ registerHooks({
117
202
  },
118
203
  load: userModuleCompilation.load,
119
204
  })
120
- const logScope = new AsyncLocalStorage()
121
- const userBindingActivationScope = new AsyncLocalStorage()
205
+ const logScope = createPrivateAsyncLocalStorage()
206
+ const userBindingActivationScope = createPrivateAsyncLocalStorage()
122
207
  const pending = new Map()
208
+ const pendingOutputStarts = new Map()
123
209
  const installedGlobals = new Set()
124
210
  const installedGlobalOriginals = new Map()
125
- const PROCESS_CONTROLS = new Set(['exit', 'abort', 'kill', 'chdir'])
211
+ const PROCESS_CONTROLS = setFromArray(['exit', 'abort', 'kill', 'chdir'])
126
212
  const CELL_FRAME_SUFFIX = '\n;'
127
213
  let filenameSequence = 0
128
214
  let activeFilename = 'ptc-plus-repl'
@@ -131,12 +217,12 @@ const CONFORMANCE_CELL = `"use strict";
131
217
  if (this !== globalThis) throw new Error('invalid REPL global receiver semantics')
132
218
  const __ptc_canary = await Promise.resolve(1)
133
219
  if (__ptc_canary !== 1) throw new Error('invalid REPL await semantics')
134
- const __ptc_import_canary = await import(${JSON.stringify(REPL_IMPORT_CANARY)})
220
+ const __ptc_import_canary = await import(${jsonStringify(REPL_IMPORT_CANARY)})
135
221
  if (__ptc_import_canary.default !== 1) throw new Error('invalid REPL import semantics')
136
222
  }`
137
223
  let activeRun
138
224
  let activeExecution
139
- let pendingVolatileReason
225
+ let suppressReplCwdObservation = false
140
226
  let nextCallId = 0
141
227
  let nextStaticAdapterId = 0
142
228
  let nextUserBindingModuleId = 0
@@ -145,7 +231,20 @@ const userBindingNames = new Map()
145
231
  const userBindingSources = new Map()
146
232
  const dynamicNamespaces = new Map()
147
233
  const originalDynamicNamespaceGlobals = new Map()
148
- let retainedUserBindingRuntime = false
234
+ const dynamicNamespaceViews = new Map()
235
+ const dynamicNamespaceGlobalGetters = new Map()
236
+ let retainedLegacyUserBindingRuntime = false
237
+
238
+ installProgramAmbientResolver(name => {
239
+ if (!mapHas(dynamicNamespaces, name) && !mapHas(originalDynamicNamespaceGlobals, name)
240
+ && !mapHas(dynamicNamespaceViews, name)) return undefined
241
+ return {
242
+ get: () => dynamicNamespaceView(name),
243
+ set() { throw new TypeError(`${name} cannot be overwritten because reserved program bindings are not shadowable`) },
244
+ typeof: () => 'object',
245
+ delete: () => false,
246
+ }
247
+ })
149
248
 
150
249
  class StaticImportFailure {
151
250
  constructor(error, position) {
@@ -164,50 +263,79 @@ class CellReturn extends Error {
164
263
  evaluationScope.getStore().completed = true
165
264
  }
166
265
  }
167
- function appendLog(...values) {
168
- const current = logScope.getStore()
169
- if (current?.open !== true) return
170
- appendText(current, formatWithOptions({ colors: false, depth: 4, maxArrayLength: 100, maxStringLength: 10_000 }, ...values))
266
+ /**
267
+ * Convert one captured descriptor chunk with plugin-private operations.
268
+ *
269
+ * The private Writable decodes string writes into Buffers, and a cell may
270
+ * replace `Buffer.prototype.toString` or a typed-array join. Converting here
271
+ * with captured operations keeps the model-visible log record from running user
272
+ * code that could rewrite, forge, or fail the logs of the cell that printed
273
+ * them.
274
+ */
275
+ function outputText(chunk) {
276
+ if (typeof chunk === 'string') return chunk
277
+ // The private Writable hands over Buffers, and a direct write may pass any
278
+ // Uint8Array view. Any other chunk type fails in the captured conversion,
279
+ // exactly as the replaced stream method would reject it.
280
+ return bufferToString(bufferIsBuffer(chunk) ? chunk : bufferConcat([chunk]), 'utf8')
171
281
  }
172
282
 
173
283
  function appendText(current, text) {
174
- if (current.open !== true || current.outputLimited) return
175
- const bytes = Buffer.byteLength(JSON.stringify(text), 'utf8') + (current.logs.length === 0 ? 0 : 1)
284
+ if (current.open !== true) {
285
+ if (!current.lateOutputReported) {
286
+ current.lateOutputReported = true
287
+ channel.postMessage({ type: 'late-output', id: current.id })
288
+ }
289
+ return
290
+ }
291
+ if (current.outputLimited) return
292
+ const bytes = bufferByteLength(jsonStringify(text), 'utf8') + (current.logs.length === 0 ? 0 : 1)
176
293
  if (current.logBytes + bytes > current.maxOutputBytes) {
177
294
  current.outputLimited = true
178
295
  channel.postMessage({ type: 'output-limit', id: current.id, logs: current.logs })
179
296
  return
180
297
  }
181
298
  current.logBytes += bytes
182
- current.logs.push(text)
299
+ appendArray(current.logs, text)
183
300
  }
184
301
 
185
- const consoleView = Object.freeze({
186
- log: appendLog,
187
- info: appendLog,
188
- warn: appendLog,
189
- error: appendLog,
190
- debug: appendLog,
191
- dir: value => appendLog(value),
302
+ const consoleOutput = new Writable({
303
+ write(chunk, _encoding, callback) {
304
+ const current = logScope.getStore()
305
+ if (current !== undefined) {
306
+ const text = outputText(chunk)
307
+ appendText(current, endsWith(text, '\n') ? sliceString(text, 0, -1) : text)
308
+ }
309
+ callback()
310
+ },
192
311
  })
193
- Object.defineProperty(context, 'console', { configurable: true, value: consoleView })
312
+ const nativeStdoutWrite = process.stdout.write.bind(process.stdout)
313
+ const nativeStderrWrite = process.stderr.write.bind(process.stderr)
314
+ const consoleView = new Console({
315
+ stdout: consoleOutput,
316
+ stderr: consoleOutput,
317
+ colorMode: false,
318
+ inspectOptions: { depth: 4, maxArrayLength: 100, maxStringLength: 10_000 },
319
+ })
320
+ defineWorkerRealmProperty(WORKER_REALM_MUTATION.STABLE, 'console',
321
+ context, 'console', { configurable: true, value: consoleView })
194
322
 
195
323
  function captureWrite(chunk, ...rest) {
196
324
  const current = logScope.getStore()
197
- if (current?.open === true) appendText(current, typeof chunk === 'string' ? chunk : String(chunk))
198
- const callback = [rest[0], rest[1]].find(value => typeof value === 'function')
325
+ if (current !== undefined) appendText(current, outputText(chunk))
326
+ const callback = typeof rest[0] === 'function' ? rest[0]
327
+ : typeof rest[1] === 'function' ? rest[1] : undefined
199
328
  if (callback !== undefined) queueMicrotask(() => callback(null))
200
329
  return true
201
330
  }
202
- process.stdout.write = captureWrite
203
- process.stderr.write = captureWrite
331
+ assignWorkerRealmProperty(WORKER_REALM_MUTATION.STABLE, 'process.stdout.write/process.stderr.write',
332
+ process.stdout, 'write', captureWrite)
333
+ assignWorkerRealmProperty(WORKER_REALM_MUTATION.STABLE, 'process.stdout.write/process.stderr.write',
334
+ process.stderr, 'write', captureWrite)
204
335
 
205
336
  function markVolatile(reason) {
206
337
  const current = activeExecution
207
- if (current === undefined) {
208
- pendingVolatileReason ??= reason
209
- return
210
- }
338
+ if (current === undefined) return
211
339
  if (current.durability === 'volatile') return
212
340
  current.durability = 'volatile'
213
341
  current.volatileReason ??= reason
@@ -221,101 +349,32 @@ function completionDurability(execution) {
221
349
  }
222
350
  }
223
351
 
224
- const originalRequire = context.require
225
-
226
- function guardProcessControls() {
227
- for (const property of PROCESS_CONTROLS) {
228
- const descriptor = Object.getOwnPropertyDescriptor(process, property)
229
- Object.defineProperty(process, property, {
230
- configurable: false,
231
- enumerable: descriptor?.enumerable ?? true,
232
- writable: false,
233
- value: () => {
234
- throw new Error(`process.${property} is forbidden inside the REPL kernel`)
235
- },
236
- })
352
+ installProcessControlGuards(process, setValuesArray(PROCESS_CONTROLS))
353
+ installWorkerCwdVirtualization(sessionCwd, originalRequire, reason => {
354
+ if (suppressReplCwdObservation) {
355
+ suppressReplCwdObservation = false
356
+ return
237
357
  }
238
- }
239
-
240
- guardProcessControls()
241
- installWorkerCwdVirtualization(sessionCwd, originalRequire)
358
+ markVolatile(reason)
359
+ })
242
360
  synchronizeBuiltinEsmExports()
243
- const originalGlobals = Object.fromEntries(
244
- [...AMBIENT_GLOBALS].filter(name => name !== 'require')
245
- .map(name => [name, name === 'eval' || name === 'Function' ? runInContext(name, context) : globalThis[name]]),
246
- )
247
361
  const providedRequire = sessionReplParent === undefined ? originalRequire : createRequire(sessionReplParent)
248
362
  function selectProvidedRequire(args) {
249
363
  const specifier = args[0]
250
- if (FORBIDDEN_IMPORTS.has(specifier)) throw new Error(`module ${specifier} is forbidden because it exposes kernel control`)
251
- if (!DURABLE_IMPORTS.has(specifier)) markVolatile(`require(${JSON.stringify(specifier)})`)
364
+ if (setHas(FORBIDDEN_IMPORTS, specifier)) throw new Error(`module ${specifier} is forbidden because it exposes kernel control`)
365
+ if (!setHas(DURABLE_IMPORTS, specifier)) markVolatile(`require(${jsonStringify(specifier)})`)
252
366
  return managedRequire(activeExecution?.languageSemantics === 'legacy-v1' ? replParent : sessionReplParent ?? replParent,
253
367
  activeExecution?.languageSemantics === 'legacy-v1' ? originalRequire : providedRequire)
254
368
  }
255
- Object.defineProperty(context, 'require', {
369
+ defineWorkerRealmProperty(WORKER_REALM_MUTATION.STABLE, 'cell module metadata', context, 'require', {
256
370
  configurable: true,
257
371
  value: new Proxy(providedRequire, {
258
- apply: (_, receiver, args) => Reflect.apply(selectProvidedRequire(args), receiver, args),
259
- construct: (_, args, newTarget) => Reflect.construct(selectProvidedRequire(args), args, newTarget),
372
+ apply: (_, receiver, args) => privateReflect.apply(selectProvidedRequire(args), receiver, args),
373
+ construct: (_, args, newTarget) => privateReflect.construct(selectProvidedRequire(args), args, newTarget),
260
374
  }),
261
375
  })
262
-
263
- for (const [name, value] of Object.entries(originalGlobals)) {
264
- Object.defineProperty(context, name, {
265
- configurable: true,
266
- get() {
267
- markVolatile(`ambient ${name}`)
268
- return value
269
- },
270
- set(next) {
271
- markVolatile(`ambient ${name}`)
272
- Object.defineProperty(context, name, { configurable: true, writable: true, value: next })
273
- },
274
- })
275
- }
276
-
277
- const capturedOutput = Object.freeze({ write: captureWrite })
278
- const processView = new Proxy(process, {
279
- get(target, property) {
280
- if (property === 'stdout' || property === 'stderr') return capturedOutput
281
- if (property === 'cwd') {
282
- if (sessionCwd !== undefined) return () => sessionCwd
283
- markVolatile('process.cwd')
284
- return target.cwd.bind(target)
285
- }
286
- if (PROCESS_CONTROLS.has(property)) return Reflect.get(target, property, target)
287
- markVolatile(`process.${String(property)}`)
288
- const value = Reflect.get(target, property, target)
289
- return typeof value === 'function' ? value.bind(target) : value
290
- },
291
- set(target, property, value) {
292
- if (property === 'stdout' || property === 'stderr') return false
293
- markVolatile(`process.${String(property)}`)
294
- return Reflect.set(target, property, value, target)
295
- },
296
- ownKeys(target) {
297
- markVolatile('process reflection')
298
- return Reflect.ownKeys(target)
299
- },
300
- })
301
- Object.defineProperty(context, 'process', { configurable: true, value: processView })
302
-
303
- const mathDescriptors = Object.getOwnPropertyDescriptors(Math)
304
- mathDescriptors.random = {
305
- ...mathDescriptors.random,
306
- value: () => {
307
- markVolatile('Math.random')
308
- return Math.random()
309
- },
310
- }
311
- const mathView = Object.defineProperties(Object.create(Object.getPrototypeOf(Math)), mathDescriptors)
312
- Object.defineProperty(context, 'Math', {
313
- configurable: true,
314
- value: Object.freeze(mathView),
315
- })
316
-
317
376
  function evaluate(program, completionSignal, asyncCompletion = false) {
318
- return new Promise((resolve, reject) => {
377
+ return new ControlPromise((resolve, reject) => {
319
378
  let settled = false
320
379
  const finish = (failed, value) => {
321
380
  if (settled) return
@@ -334,6 +393,7 @@ function evaluate(program, completionSignal, asyncCompletion = false) {
334
393
  }
335
394
  }
336
395
  activeFilename = `ptc-plus-repl-${++filenameSequence}`
396
+ suppressReplCwdObservation = true
337
397
  evaluationScope.run(finish, () => errorDomain.run(() => {
338
398
  // Older REPL callbacks conflate null/undefined await rejection with a
339
399
  // successful empty result. A reached EOF distinguishes those outcomes.
@@ -341,7 +401,7 @@ function evaluate(program, completionSignal, asyncCompletion = false) {
341
401
  // preceding non-await expression value without adding a session binding.
342
402
  // Keep the final semicolon so REPL cannot guess a block is an object literal.
343
403
  const suffix = completionSignal === undefined || asyncCompletion ? CELL_FRAME_SUFFIX
344
- : `${CELL_FRAME_SUFFIX}{ let completed = this[${JSON.stringify(completionSignal)}].complete(); }${CELL_FRAME_SUFFIX}`
404
+ : `${CELL_FRAME_SUFFIX}{ let completed = this[${jsonStringify(completionSignal)}].complete(); }${CELL_FRAME_SUFFIX}`
345
405
  server.eval(program + suffix, context, activeFilename, (error, value) => {
346
406
  if (asyncCompletion && (error === null || error === undefined)) {
347
407
  completionObserver.observe(value, value => finish(false, value), error => finish(true, error))
@@ -351,6 +411,7 @@ function evaluate(program, completionSignal, asyncCompletion = false) {
351
411
  finish(failed, failed ? error : value)
352
412
  })
353
413
  }))
414
+ suppressReplCwdObservation = false
354
415
  })
355
416
  }
356
417
 
@@ -358,12 +419,19 @@ async function verifyEvaluation() {
358
419
  await evaluate(CONFORMANCE_CELL)
359
420
  const marker = new Error('PTC Plus REPL settlement probe')
360
421
  Object.defineProperty(marker, 'stack', { get() { throw new Error('REPL must not format an error before settlement') } })
361
- context.__ptc_settlement_probe__ = marker
362
- context.__ptc_completion_probe__ = CellReturn
422
+ assignWorkerRealmProperty(WORKER_REALM_MUTATION.TEMPORARY, undefined,
423
+ context, '__ptc_settlement_probe__', marker)
424
+ assignWorkerRealmProperty(WORKER_REALM_MUTATION.TEMPORARY, undefined,
425
+ context, '__ptc_completion_probe__', CellReturn)
363
426
  try {
364
- for (const value of [marker, null, undefined, false, 0]) {
365
- context.__ptc_settlement_probe__ = value
366
- for (const source of ['throw __ptc_settlement_probe__', 'await Promise.reject(__ptc_settlement_probe__)']) {
427
+ const values = [marker, null, undefined, false, 0]
428
+ for (let valueIndex = 0; valueIndex < values.length; valueIndex += 1) {
429
+ const value = values[valueIndex]
430
+ assignWorkerRealmProperty(WORKER_REALM_MUTATION.TEMPORARY, undefined,
431
+ context, '__ptc_settlement_probe__', value)
432
+ const sources = ['throw __ptc_settlement_probe__', 'await Promise.reject(__ptc_settlement_probe__)']
433
+ for (let sourceIndex = 0; sourceIndex < sources.length; sourceIndex += 1) {
434
+ const source = sources[sourceIndex]
367
435
  let caught = false
368
436
  try { await evaluate(source, '__ptc_completion_probe__') } catch (error) {
369
437
  if (error !== value) throw new Error('REPL did not preserve the original thrown value')
@@ -375,7 +443,8 @@ async function verifyEvaluation() {
375
443
  let syntaxRejected = false
376
444
  try { await evaluate('const =') } catch (error) { syntaxRejected = error?.name === 'SyntaxError' }
377
445
  if (!syntaxRejected) throw new Error('REPL did not preserve syntax failure')
378
- context.__ptc_settlement_probe__ = new CellReturn(Promise.resolve(marker))
446
+ assignWorkerRealmProperty(WORKER_REALM_MUTATION.TEMPORARY, undefined,
447
+ context, '__ptc_settlement_probe__', new CellReturn(promiseResolve(marker)))
379
448
  const returned = await evaluate('throw __ptc_settlement_probe__')
380
449
  if (!returned.hasValue || returned.value !== marker) throw new Error('REPL did not preserve cell return')
381
450
  await evaluate(CONFORMANCE_CELL)
@@ -383,54 +452,54 @@ async function verifyEvaluation() {
383
452
  const expression = await evaluate('42', '__ptc_completion_probe__')
384
453
  if (expression.value !== 42) throw new Error('REPL did not preserve expression completion')
385
454
  } finally {
386
- delete context.__ptc_settlement_probe__
387
- delete context.__ptc_completion_probe__
455
+ deleteWorkerRealmProperty(WORKER_REALM_MUTATION.RESTORE, undefined,
456
+ context, '__ptc_settlement_probe__')
457
+ deleteWorkerRealmProperty(WORKER_REALM_MUTATION.RESTORE, undefined,
458
+ context, '__ptc_completion_probe__')
388
459
  }
389
460
  }
390
461
 
391
462
  function staticImportClause(options) {
392
463
  if (options === undefined) return { attributes: undefined, clause: '' }
393
- const [keyword, attributes] = Object.entries(options)[0]
394
- const entries = Object.entries(attributes)
395
- .map(([key, value]) => `${JSON.stringify(key)}: ${JSON.stringify(value)}`)
396
- return { attributes, clause: ` ${keyword} { ${entries.join(', ')} }` }
464
+ const option = Object.entries(options)[0]
465
+ const keyword = option[0]
466
+ const attributes = option[1]
467
+ const entries = mapArray(Object.entries(attributes), entry => `${jsonStringify(entry[0])}: ${jsonStringify(entry[1])}`)
468
+ return { attributes, clause: ` ${keyword} { ${join(entries, ', ')} }` }
397
469
  }
398
470
 
399
471
  function staticAdapterSource(load) {
400
472
  const { attributes, clause } = staticImportClause(load.options)
401
- const source = JSON.stringify(statefulModuleLink(load.source, staticModuleLinkReference(load.source, attributes)))
473
+ const source = jsonStringify(statefulModuleLink(load.source, staticModuleLinkReference(load.source, attributes)))
402
474
  if (load.global === undefined) return `import ${source}${clause};`
403
- const requirements = load.requiredExports?.map((name, index) => {
404
- const imported = name === 'default' ? 'default' : JSON.stringify(name)
475
+ const requirements = load.requiredExports === undefined ? [] : mapArray(load.requiredExports, (name, index) => {
476
+ const imported = name === 'default' ? 'default' : jsonStringify(name)
405
477
  return `${imported} as __required_${index}__`
406
- }) ?? []
407
- return [
408
- `import * as namespace from ${source}${clause};`,
409
- ...(requirements.length === 0 ? [] : [
410
- `export { ${requirements.join(', ')} } from ${source}${clause};`,
411
- ]),
412
- 'export { namespace };',
413
- ].join('\n')
478
+ })
479
+ const lines = [`import * as namespace from ${source}${clause};`]
480
+ if (requirements.length > 0) appendArray(lines, `export { ${join(requirements, ', ')} } from ${source}${clause};`)
481
+ appendArray(lines, 'export { namespace };')
482
+ return join(lines, '\n')
414
483
  }
415
484
 
416
485
  async function loadStaticModule(load) {
417
486
  if (load.operation === 'native-dynamic') return { namespace: nativeRootDynamic.environment(load.callableSources,
418
- load.awaitRoot === true, load.logicalRoots.filter(name => statefulRoots.has(name)
487
+ load.awaitRoot === true, filterArray(load.logicalRoots, name => statefulRoots.has(name)
419
488
  && rootBindingStorage(name, moduleRuntimeIntrinsics.includes(load.nativeLexicals, name)) !== 'lexical'), load.writableRoots) }
420
489
  if (load.operation === 'import') {
421
490
  return { namespace: (source, options) => managedModuleImport(replParent, source, options, replModuleRealm) }
422
491
  }
423
492
  const adapter = `data:text/javascript,${encodeURIComponent(staticAdapterSource(load))}#${++nextStaticAdapterId}`
424
- staticAdapterParents.add(adapter)
493
+ setAdd(staticAdapterParents, adapter)
425
494
  userModuleCompilation.mark(adapter, { compiled: true,
426
- transform: moduleTransformForLanguage(activeExecution?.languageSemantics) })
495
+ transform: activeExecution?.moduleTransform })
427
496
  try {
428
- const completion = await evaluate(`import(${JSON.stringify(adapter)})`)
497
+ const completion = await evaluate(`import(${jsonStringify(adapter)})`)
429
498
  if (load.global === undefined) return { namespace: undefined }
430
499
  const namespace = readModuleImport(adapter, load.source, null, () => completion.value.namespace, load.options?.with)
431
500
  return { namespace }
432
501
  } finally {
433
- staticAdapterParents.delete(adapter)
502
+ setDelete(staticAdapterParents, adapter)
434
503
  }
435
504
  }
436
505
 
@@ -443,28 +512,30 @@ function hasExecutionLease(runId) {
443
512
  // call is pending, and the call must keep its submitted error identity.
444
513
  function callHost(runId, global, member, args, BoundError) {
445
514
  if (!hasExecutionLease(runId)) {
446
- return Promise.reject(programBindingError('lease', 'PTC execution lease expired'))
515
+ return promiseReject(programBindingError('lease', 'PTC execution lease expired'))
447
516
  }
448
517
  const activation = userBindingActivationScope.getStore()
449
518
  if (activation !== undefined) {
450
519
  activation.called = true
451
520
  if (activation.failed) {
452
- markVolatile(`failed user binding ${JSON.stringify(activation.id)} issued a host call`)
521
+ markVolatile(`failed user binding ${jsonStringify(activation.id)} issued a host call`)
453
522
  }
454
523
  }
455
524
  const id = ++nextCallId
456
525
  const valueLimits = activeExecution.valueLimits
457
526
  let settle
458
- const result = new Promise((resolve, reject) => { settle = { resolve, reject } })
459
- const settled = result.then(() => {}, () => {})
460
- pending.set(id, { ...settle, runId, valueLimits, settled, member, BoundError })
527
+ const result = new PublicPromise((resolve, reject) => { settle = { resolve, reject } })
528
+ const settled = new ControlPromise(resolve => {
529
+ moduleRuntimeIntrinsics.Reflect.apply(moduleRuntimeIntrinsics.promiseThen, result, [resolve, resolve])
530
+ })
531
+ mapSet(pending, id, { ...settle, runId, valueLimits, settled, member, BoundError })
461
532
  try {
462
533
  channel.postMessage({
463
534
  type: 'call', runId, id, global, member,
464
535
  args: encodeValue(args, valueLimits),
465
536
  })
466
537
  } catch (error) {
467
- pending.delete(id)
538
+ mapDelete(pending, id)
468
539
  settle.reject(error)
469
540
  }
470
541
  return result
@@ -480,36 +551,39 @@ function dynamicNamespace(name) {
480
551
  get(_target, property) {
481
552
  if (typeof property !== 'string') return undefined
482
553
  assertLease()
483
- const descriptor = dynamicNamespaces.get(name)
484
- if (descriptor === undefined || !descriptor.members.has(property)) return undefined
554
+ const descriptor = mapGet(dynamicNamespaces, name)
555
+ if (descriptor === undefined || !setHas(descriptor.members, property)) return undefined
485
556
  return (...args) => {
486
557
  if (!hasExecutionLease(logScope.getStore()?.id)) {
487
- return Promise.reject(programBindingError('lease', 'PTC execution lease expired'))
558
+ return promiseReject(programBindingError('lease', 'PTC execution lease expired'))
488
559
  }
489
- const current = dynamicNamespaces.get(name)
490
- if (current === undefined || !current.members.has(property)) {
491
- return Promise.reject(programBindingError('capability', `unknown binding ${name}.${property}`))
560
+ const current = mapGet(dynamicNamespaces, name)
561
+ if (current === undefined || !setHas(current.members, property)) {
562
+ return promiseReject(programBindingError('capability', `unknown binding ${name}.${property}`))
492
563
  }
493
564
  return callHost(
494
565
  logScope.getStore()?.id,
495
566
  name,
496
567
  property,
497
- args.length === 0 && current.emptyObjectMembers.has(property) ? {} : args[0],
568
+ args.length === 0 && setHas(current.emptyObjectMembers, property) ? {} : args[0],
498
569
  current.BoundError,
499
570
  )
500
571
  }
501
572
  },
502
573
  has(_target, property) {
503
574
  assertLease()
504
- return typeof property === 'string' && dynamicNamespaces.get(name)?.members.has(property) === true
575
+ const descriptor = mapGet(dynamicNamespaces, name)
576
+ return typeof property === 'string' && descriptor !== undefined && setHas(descriptor.members, property)
505
577
  },
506
578
  ownKeys() {
507
579
  assertLease()
508
- return [...dynamicNamespaces.get(name)?.members ?? []]
580
+ const descriptor = mapGet(dynamicNamespaces, name)
581
+ return descriptor === undefined ? [] : setValuesArray(descriptor.members)
509
582
  },
510
583
  getOwnPropertyDescriptor(_target, property) {
511
584
  assertLease()
512
- return typeof property === 'string' && dynamicNamespaces.get(name)?.members.has(property) === true
585
+ const descriptor = mapGet(dynamicNamespaces, name)
586
+ return typeof property === 'string' && descriptor !== undefined && setHas(descriptor.members, property)
513
587
  ? compilerDescriptors.descriptor({ configurable: true, enumerable: true })
514
588
  : undefined
515
589
  },
@@ -517,50 +591,98 @@ function dynamicNamespace(name) {
517
591
  })
518
592
  }
519
593
 
594
+ function dynamicNamespaceView(name) {
595
+ let view = mapGet(dynamicNamespaceViews, name)
596
+ if (view === undefined) {
597
+ view = dynamicNamespace(name)
598
+ mapSet(dynamicNamespaceViews, name, view)
599
+ }
600
+ return view
601
+ }
602
+
603
+ function namespaceGlobalGetter(name) {
604
+ let getter = mapGet(dynamicNamespaceGlobalGetters, name)
605
+ if (getter === undefined) {
606
+ getter = () => mapGet(dynamicNamespaces, name)?.cellView ?? dynamicNamespaceView(name)
607
+ mapSet(dynamicNamespaceGlobalGetters, name, getter)
608
+ }
609
+ return getter
610
+ }
611
+
612
+ function installNamespaceGlobal(name) {
613
+ defineWorkerRealmProperty(WORKER_REALM_MUTATION.PROGRAM, undefined, globalThis, name, {
614
+ configurable: true,
615
+ get: namespaceGlobalGetter(name),
616
+ })
617
+ }
618
+
520
619
  function installDynamicNamespaceGlobals() {
521
620
  const pendingGlobals = []
522
- for (const name of dynamicNamespaces.keys()) {
523
- if (originalDynamicNamespaceGlobals.has(name)) continue
524
- const original = Object.getOwnPropertyDescriptor(globalThis, name)
525
- if (original !== undefined) {
621
+ const names = mapKeysArray(dynamicNamespaces)
622
+ for (let index = 0; index < names.length; index += 1) {
623
+ const name = names[index]
624
+ if (mapHas(originalDynamicNamespaceGlobals, name)) {
625
+ installNamespaceGlobal(name)
626
+ continue
627
+ }
628
+ const original = setHas(installedGlobals, name)
629
+ ? mapGet(installedGlobalOriginals, name)
630
+ : capturedGlobalDescriptor(name)
631
+ if (setHas(workerBaselineGlobals, name)) {
526
632
  throw new Error(
527
- `program namespace ${JSON.stringify(name)} cannot be bridged into Global User Binding modules because the worker global already exists`,
633
+ `program namespace ${jsonStringify(name)} cannot be bridged into Global User Binding modules because the worker global already exists`,
528
634
  )
529
635
  }
530
- pendingGlobals.push(name)
636
+ appendArray(pendingGlobals, [name, original.descriptor])
531
637
  }
532
- for (const name of pendingGlobals) {
533
- originalDynamicNamespaceGlobals.set(name, undefined)
534
- Object.defineProperty(globalThis, name, {
535
- configurable: true,
536
- writable: false,
537
- value: dynamicNamespace(name),
538
- })
638
+ for (let index = 0; index < pendingGlobals.length; index += 1) {
639
+ const entry = pendingGlobals[index]
640
+ const name = entry[0]
641
+ const descriptor = entry[1]
642
+ mapSet(originalDynamicNamespaceGlobals, name, descriptor)
643
+ if (!setHas(installedGlobals, name)) installNamespaceGlobal(name)
539
644
  }
540
645
  }
541
646
 
542
647
  function restoreDynamicNamespaceGlobals() {
543
- // installDynamicNamespaceGlobals refuses an existing worker global, so every
544
- // recorded bridge install owns a previously absent name.
545
- for (const name of originalDynamicNamespaceGlobals.keys()) delete globalThis[name]
546
- originalDynamicNamespaceGlobals.clear()
648
+ const names = mapKeysArray(originalDynamicNamespaceGlobals)
649
+ for (let index = 0; index < names.length; index += 1) {
650
+ const name = names[index]
651
+ setHas(installedGlobals, name)
652
+ ? installNamespaceGlobal(name)
653
+ : deleteWorkerRealmProperty(WORKER_REALM_MUTATION.RESTORE, undefined, globalThis, name)
654
+ }
655
+ mapClear(originalDynamicNamespaceGlobals)
547
656
  }
548
657
 
549
658
  function installBindings(message) {
550
- for (const name of installedGlobals) {
551
- const original = installedGlobalOriginals.get(name)
552
- if (original.userGlobalEntryId !== undefined
553
- && (userBindingNames.get(name) !== original.userGlobalEntryId
554
- || userBindingEntries.get(original.userGlobalEntryId)?.descriptors.get(name) !== original.attachment)) {
555
- delete context[name]
556
- } else if (original.descriptor === undefined) delete context[name]
557
- else Object.defineProperty(context, name, original.descriptor)
659
+ const installedNames = setValuesArray(installedGlobals)
660
+ for (let index = 0; index < installedNames.length; index += 1) {
661
+ const name = installedNames[index]
662
+ const original = mapGet(installedGlobalOriginals, name)
663
+ const attachedEntry = original.userGlobalEntryId === undefined
664
+ ? undefined : mapGet(userBindingEntries, original.userGlobalEntryId)
665
+ const attachment = attachedEntry === undefined ? undefined : mapGet(attachedEntry.descriptors, name)
666
+ if (mapHas(originalDynamicNamespaceGlobals, name)) {
667
+ installNamespaceGlobal(name)
668
+ } else if (original.userGlobalEntryId !== undefined
669
+ && (mapGet(userBindingNames, name) !== original.userGlobalEntryId
670
+ || attachment !== original.attachment)) {
671
+ deleteWorkerRealmProperty(WORKER_REALM_MUTATION.RESTORE, undefined, context, name)
672
+ } else if (original.descriptor === undefined) {
673
+ deleteWorkerRealmProperty(WORKER_REALM_MUTATION.RESTORE, undefined, context, name)
674
+ } else {
675
+ defineWorkerRealmProperty(WORKER_REALM_MUTATION.RESTORE, undefined,
676
+ context, name, original.descriptor)
677
+ }
558
678
  }
559
- installedGlobals.clear()
560
- installedGlobalOriginals.clear()
561
- dynamicNamespaces.clear()
679
+ setClear(installedGlobals)
680
+ mapClear(installedGlobalOriginals)
681
+ mapClear(dynamicNamespaces)
682
+ let namespaceError
562
683
 
563
- for (const namespace of message.namespaces) {
684
+ for (let namespaceIndex = 0; namespaceIndex < message.namespaces.length; namespaceIndex += 1) {
685
+ const namespace = message.namespaces[namespaceIndex]
564
686
  const descriptor = namespace.errorClass
565
687
  // The wrapper owns the constructor, so a pending call keeps its submitted
566
688
  // error identity even after the cell shadows the context binding.
@@ -573,42 +695,56 @@ function installBindings(message) {
573
695
  }
574
696
  }
575
697
  const view = Object.create(null)
576
- const emptyObjectMembers = new Set(namespace.emptyObjectMembers ?? [])
577
- dynamicNamespaces.set(namespace.global, {
578
- members: new Set(namespace.members),
698
+ const emptyObjectMembers = setFromArray(namespace.emptyObjectMembers ?? [])
699
+ mapSet(dynamicNamespaces, namespace.global, {
700
+ members: setFromArray(namespace.members),
579
701
  shadowable: namespace.shadowable === true,
580
702
  emptyObjectMembers,
581
703
  BoundError,
704
+ cellView: view,
582
705
  })
583
- for (const member of namespace.members) {
706
+ for (let memberIndex = 0; memberIndex < namespace.members.length; memberIndex += 1) {
707
+ const member = namespace.members[memberIndex]
584
708
  Object.defineProperty(view, member, {
585
709
  enumerable: true,
586
710
  value: (...args) => callHost(
587
711
  message.id,
588
712
  namespace.global,
589
713
  member,
590
- args.length === 0 && emptyObjectMembers.has(member) ? {} : args[0],
714
+ args.length === 0 && setHas(emptyObjectMembers, member) ? {} : args[0],
591
715
  BoundError,
592
716
  ),
593
717
  })
594
718
  }
595
719
  Object.freeze(view)
596
- installedGlobalOriginals.set(namespace.global, capturedGlobalDescriptor(namespace.global))
597
- Object.defineProperty(context, namespace.global, { configurable: true, value: view })
598
- installedGlobals.add(namespace.global)
720
+ const original = capturedGlobalDescriptor(namespace.global)
721
+ mapSet(installedGlobalOriginals, namespace.global, original)
722
+ if (setHas(workerBaselineGlobals, namespace.global)) {
723
+ namespaceError ??= new Error(
724
+ `program namespace ${jsonStringify(namespace.global)} cannot be bridged into the worker realm because the global already exists`,
725
+ )
726
+ } else {
727
+ installNamespaceGlobal(namespace.global)
728
+ setAdd(installedGlobals, namespace.global)
729
+ }
599
730
 
600
731
  if (descriptor !== undefined) {
601
- installedGlobalOriginals.set(descriptor.name, capturedGlobalDescriptor(descriptor.name))
602
- Object.defineProperty(context, descriptor.name, { configurable: true, value: BoundError })
603
- installedGlobals.add(descriptor.name)
732
+ mapSet(installedGlobalOriginals, descriptor.name, capturedGlobalDescriptor(descriptor.name))
733
+ defineWorkerRealmProperty(WORKER_REALM_MUTATION.PROGRAM, undefined,
734
+ context, descriptor.name, { configurable: true, value: BoundError })
735
+ setAdd(installedGlobals, descriptor.name)
604
736
  }
605
737
  }
738
+ return namespaceError
606
739
  }
607
740
 
608
741
  function capturedGlobalDescriptor(name) {
609
- const descriptor = Object.getOwnPropertyDescriptor(context, name)
610
- const userGlobalEntryId = userBindingNames.get(name)
611
- const installed = userBindingEntries.get(userGlobalEntryId)?.descriptors.get(name)
742
+ const descriptor = mapHas(originalDynamicNamespaceGlobals, name)
743
+ ? mapGet(originalDynamicNamespaceGlobals, name)
744
+ : Object.getOwnPropertyDescriptor(context, name)
745
+ const userGlobalEntryId = mapGet(userBindingNames, name)
746
+ const entry = mapGet(userBindingEntries, userGlobalEntryId)
747
+ const installed = entry === undefined ? undefined : mapGet(entry.descriptors, name)
612
748
  return {
613
749
  descriptor,
614
750
  ...(userGlobalEntryId !== undefined && descriptorsEqual(descriptor, installed)
@@ -618,8 +754,8 @@ function capturedGlobalDescriptor(name) {
618
754
  }
619
755
 
620
756
  function underlyingUserBindingDescriptor(name) {
621
- return installedGlobals.has(name)
622
- ? installedGlobalOriginals.get(name).descriptor
757
+ return setHas(installedGlobals, name)
758
+ ? mapGet(installedGlobalOriginals, name).descriptor
623
759
  : Object.getOwnPropertyDescriptor(context, name)
624
760
  }
625
761
 
@@ -646,7 +782,7 @@ function rootBindingStorage(name, nativeLexical = false) {
646
782
  try {
647
783
  // Both an own data property and an initialized lexical can be read
648
784
  // without executing an accessor. Either proves an available local name.
649
- runInContext(name, context, { displayErrors: false })
785
+ runInWorkerReplRealm(name, { displayErrors: false })
650
786
  return nativeLexical ? 'lexical' : 'local'
651
787
  } catch {
652
788
  return 'unknown'
@@ -654,14 +790,18 @@ function rootBindingStorage(name, nativeLexical = false) {
654
790
  }
655
791
  let propertyRead = false
656
792
  try {
657
- Object.defineProperty(context, name, { configurable: true, get() { propertyRead = true } })
658
- runInContext(name, context, { displayErrors: false })
793
+ defineWorkerRealmProperty(WORKER_REALM_MUTATION.TEMPORARY, undefined,
794
+ context, name, { configurable: true, get() { propertyRead = true } })
795
+ runInWorkerReplRealm(name, { displayErrors: false })
659
796
  return propertyRead ? 'property' : 'lexical'
660
797
  } catch {
661
798
  return 'unknown'
662
799
  } finally {
663
- if (original === undefined) delete context[name]
664
- else Object.defineProperty(context, name, original)
800
+ if (original === undefined) {
801
+ deleteWorkerRealmProperty(WORKER_REALM_MUTATION.RESTORE, undefined, context, name)
802
+ } else {
803
+ defineWorkerRealmProperty(WORKER_REALM_MUTATION.RESTORE, undefined, context, name, original)
804
+ }
665
805
  }
666
806
  }
667
807
 
@@ -673,7 +813,8 @@ function writableRootProperty(name) {
673
813
 
674
814
  function userBindingRootStorage(name) {
675
815
  if (statefulRoots.has(name)) return 'local'
676
- const namespace = activeExecution.importBindingNamespaces?.get(name)
816
+ const namespaces = activeExecution.importBindingNamespaces
817
+ const namespace = namespaces === undefined ? undefined : mapGet(namespaces, name)
677
818
  if (namespace === undefined) return rootBindingStorage(name)
678
819
  // Compiler-validated imports use native namespace slots, not public alias
679
820
  // properties. Probe only the slot; reading an export could invoke user code.
@@ -681,145 +822,181 @@ function userBindingRootStorage(name) {
681
822
  }
682
823
 
683
824
  function reconcileUserBindingNames() {
684
- for (const name of userBindingSources.keys()) {
825
+ const names = mapKeysArray(userBindingSources)
826
+ for (let index = 0; index < names.length; index += 1) {
827
+ const name = names[index]
685
828
  const storage = userBindingRootStorage(name)
686
829
  const descriptor = underlyingUserBindingDescriptor(name)
687
- const id = userBindingNames.get(name)
688
- const installed = userBindingEntries.get(id)?.descriptors.get(name)
830
+ const id = mapGet(userBindingNames, name)
831
+ const entry = mapGet(userBindingEntries, id)
832
+ const installed = entry === undefined ? undefined : mapGet(entry.descriptors, name)
689
833
  const state = storage === 'lexical' || storage === 'local' ? 'local'
690
834
  : storage === 'unknown' ? 'unknown'
691
835
  : descriptor === undefined ? 'absent'
692
836
  : installed !== undefined && descriptorsEqual(descriptor, installed) ? 'provider' : 'local'
693
- userBindingSources.set(name, { state, ...(state === 'provider' ? { entryId: id } : {}) })
694
- if (state !== 'provider') userBindingNames.delete(name)
837
+ mapSet(userBindingSources, name, { state, ...(state === 'provider' ? { entryId: id } : {}) })
838
+ if (state !== 'provider') mapDelete(userBindingNames, name)
695
839
  }
696
840
  }
697
841
 
698
842
  function removePerNameUserBindingEntry(id) {
699
- const entry = userBindingEntries.get(id)
700
- for (const name of entry.names) {
701
- if (userBindingNames.get(name) !== id) continue
702
- const installed = entry.descriptors.get(name)
703
- userBindingNames.delete(name)
704
- if (installedGlobals.has(name)) {
705
- const original = installedGlobalOriginals.get(name)
843
+ const entry = mapGet(userBindingEntries, id)
844
+ for (let index = 0; index < entry.names.length; index += 1) {
845
+ const name = entry.names[index]
846
+ if (mapGet(userBindingNames, name) !== id) continue
847
+ const installed = mapGet(entry.descriptors, name)
848
+ mapDelete(userBindingNames, name)
849
+ if (setHas(installedGlobals, name)) {
850
+ const original = mapGet(installedGlobalOriginals, name)
706
851
  if (original.attachment === installed) original.descriptor = undefined
707
- } else if (descriptorsEqual(Object.getOwnPropertyDescriptor(context, name), installed)) delete context[name]
852
+ } else if (descriptorsEqual(Object.getOwnPropertyDescriptor(context, name), installed)) {
853
+ deleteWorkerRealmProperty(WORKER_REALM_MUTATION.RESTORE, undefined, context, name)
854
+ }
708
855
  // Lifecycle removal permits reattachment; explicit deletion retains absent.
709
- userBindingSources.delete(name)
856
+ mapDelete(userBindingSources, name)
710
857
  }
711
- userBindingEntries.delete(id)
858
+ mapDelete(userBindingEntries, id)
712
859
  }
713
860
 
714
861
  async function activatePerNameUserBindings(snapshot, shadowedNames, cwd, reusePolicy, initialFailures = []) {
715
862
  // Import preceding legacy attachments into the new source owner once.
716
- for (const [name, entryId] of userBindingNames) {
717
- if (userBindingSources.has(name)) continue
718
- userBindingSources.set(name, { state: 'provider', entryId })
863
+ const existingNames = mapEntriesArray(userBindingNames)
864
+ for (let index = 0; index < existingNames.length; index += 1) {
865
+ const name = existingNames[index][0]
866
+ const entryId = existingNames[index][1]
867
+ if (mapHas(userBindingSources, name)) continue
868
+ mapSet(userBindingSources, name, { state: 'provider', entryId })
719
869
  // The legacy catalog includes actual setter writes as well as descriptor
720
870
  // changes. Do not reattach a restored local accessor by descriptor equality.
721
- if (shadowedNames.has(name) && !installedGlobals.has(name)) userBindingNames.delete(name)
871
+ if (setHas(shadowedNames, name) && !setHas(installedGlobals, name)) mapDelete(userBindingNames, name)
722
872
  }
723
873
  reconcileUserBindingNames()
724
- const blockedIds = new Set(initialFailures.map(failure => failure.id))
725
- const desired = new Map((snapshot?.entries ?? []).filter(entry => !blockedIds.has(entry.id)).map(entry => [entry.id, entry]))
874
+ const blockedIds = setFromArray(mapArray(initialFailures, failure => failure.id))
875
+ const desired = mapFromArray(filterArray(snapshot?.entries ?? [], entry => !setHas(blockedIds, entry.id)), entry => [entry.id, entry])
726
876
  try {
727
- if (desired.size > 0 || retainedUserBindingRuntime) installDynamicNamespaceGlobals()
877
+ if (mapSize(desired) > 0 || retainedLegacyUserBindingRuntime) installDynamicNamespaceGlobals()
728
878
  } catch (error) {
729
- for (const id of userBindingEntries.keys()) removePerNameUserBindingEntry(id)
730
- return { activated: [], failures: [...initialFailures, ...[...desired.keys()].map(id => ({ id, error: messageOf(error) }))], error }
879
+ const ids = mapKeysArray(userBindingEntries)
880
+ for (let index = 0; index < ids.length; index += 1) removePerNameUserBindingEntry(ids[index])
881
+ const failures = copyArray(initialFailures)
882
+ const desiredFailures = mapArray(mapKeysArray(desired), id => ({ id, error: messageOf(error) }))
883
+ for (let index = 0; index < desiredFailures.length; index += 1) appendArray(failures, desiredFailures[index])
884
+ return { activated: [], failures, error }
731
885
  }
732
- for (const [id, current] of userBindingEntries) {
733
- const next = desired.get(id)
886
+ const currentEntries = mapEntriesArray(userBindingEntries)
887
+ for (let index = 0; index < currentEntries.length; index += 1) {
888
+ const id = currentEntries[index][0]
889
+ const current = currentEntries[index][1]
890
+ const next = mapGet(desired, id)
734
891
  if (next === undefined || current.transform !== snapshot.transform || !(reusePolicy === LEGACY_USER_BINDINGS_REUSE_POLICY
735
892
  ? next.fingerprint === current.entry.fingerprint : userBindingImplementationMatches(next, current.entry))) {
736
893
  removePerNameUserBindingEntry(id)
737
894
  }
738
895
  }
739
896
  const activated = []
740
- const failures = [...initialFailures]
741
- for (const entry of desired.values()) {
742
- const current = userBindingEntries.get(entry.id)
897
+ const failures = copyArray(initialFailures)
898
+ const desiredEntries = mapValuesArray(desired)
899
+ for (let entryIndex = 0; entryIndex < desiredEntries.length; entryIndex += 1) {
900
+ const entry = desiredEntries[entryIndex]
901
+ const current = mapGet(userBindingEntries, entry.id)
743
902
  if (current !== undefined) {
744
903
  current.entry = entry
745
- activated.push(entry.id)
904
+ appendArray(activated, entry.id)
746
905
  continue
747
906
  }
748
907
  const names = entry.scope === 'namespace' ? [entry.name] : entry.symbols
749
- const previousSources = new Map(names.map(name => [name, userBindingSources.get(name)]))
908
+ const previousSources = mapFromArray(names, name => [name, mapGet(userBindingSources, name)])
750
909
  const installedNames = []
751
910
  let evaluated
752
911
  const activation = { id: entry.id, called: false, failed: false }
753
912
  try {
754
- if (entry.durability === 'volatile') markVolatile(`user binding ${JSON.stringify(entry.id)}: ${entry.volatileReason ?? 'non-replayable source'}`)
913
+ if (entry.durability === 'volatile') markVolatile(`user binding ${jsonStringify(entry.id)}: ${entry.volatileReason ?? 'non-replayable source'}`)
755
914
  evaluated = await userBindingActivationScope.run(activation, () => evaluateUserBinding(entry, cwd, snapshot.transform))
756
915
  const namespace = evaluated.namespace
757
- for (const symbol of entry.symbols) {
758
- if (!Object.hasOwn(namespace, symbol)) throw new Error(`named export ${JSON.stringify(symbol)} is unavailable after evaluation`)
916
+ for (let symbolIndex = 0; symbolIndex < entry.symbols.length; symbolIndex += 1) {
917
+ const symbol = entry.symbols[symbolIndex]
918
+ if (!Object.hasOwn(namespace, symbol)) throw new Error(`named export ${jsonStringify(symbol)} is unavailable after evaluation`)
759
919
  }
760
920
  const view = Object.create(null)
761
- for (const symbol of entry.symbols) Object.defineProperty(view, symbol, { enumerable: true, get: () => namespace[symbol] })
921
+ for (let symbolIndex = 0; symbolIndex < entry.symbols.length; symbolIndex += 1) {
922
+ const symbol = entry.symbols[symbolIndex]
923
+ Object.defineProperty(view, symbol, { enumerable: true, get: () => namespace[symbol] })
924
+ }
762
925
  Object.freeze(view)
763
926
  const descriptors = new Map()
764
- for (const name of names) {
927
+ for (let nameIndex = 0; nameIndex < names.length; nameIndex += 1) {
928
+ const name = names[nameIndex]
765
929
  const storage = userBindingRootStorage(name)
766
- const prior = userBindingSources.get(name)
930
+ const prior = mapGet(userBindingSources, name)
767
931
  if (storage === 'lexical' || storage === 'unknown') {
768
- userBindingSources.set(name, { state: storage === 'lexical' ? 'local' : 'unknown' })
769
- } else if (prior === undefined && shadowedNames.has(name)) {
932
+ mapSet(userBindingSources, name, { state: storage === 'lexical' ? 'local' : 'unknown' })
933
+ } else if (prior === undefined && setHas(shadowedNames, name)) {
770
934
  const descriptor = underlyingUserBindingDescriptor(name)
771
- userBindingSources.set(name, { state: descriptor === undefined ? 'unknown' : 'local' })
935
+ mapSet(userBindingSources, name, { state: descriptor === undefined ? 'unknown' : 'local' })
772
936
  }
773
- const source = userBindingSources.get(name)
937
+ const source = mapGet(userBindingSources, name)
774
938
  if (source !== undefined && source.state !== 'provider') continue
775
939
  const descriptor = Object.getOwnPropertyDescriptor(context, name)
776
- Object.defineProperty(context, name, {
940
+ defineWorkerRealmProperty(WORKER_REALM_MUTATION.USER, undefined, context, name, {
777
941
  configurable: true,
778
942
  enumerable: true,
779
943
  get: () => entry.scope === 'namespace' ? view : namespace[name],
780
944
  set(value) {
781
945
  const receiver = this === contextGlobal ? context : this
782
- Object.defineProperty(receiver, name, { configurable: true, enumerable: true, writable: true, value })
946
+ defineWorkerRealmProperty(WORKER_REALM_MUTATION.USER, undefined,
947
+ receiver, name, { configurable: true, enumerable: true, writable: true, value })
783
948
  if (receiver === context) recordUserBindingAssignment(name)
784
949
  },
785
950
  })
786
- installedNames.push({ name, descriptor })
787
- descriptors.set(name, Object.getOwnPropertyDescriptor(context, name))
788
- userBindingNames.set(name, entry.id)
789
- userBindingSources.set(name, { state: 'provider', entryId: entry.id })
951
+ appendArray(installedNames, { name, descriptor })
952
+ mapSet(descriptors, name, Object.getOwnPropertyDescriptor(context, name))
953
+ mapSet(userBindingNames, name, entry.id)
954
+ mapSet(userBindingSources, name, { state: 'provider', entryId: entry.id })
790
955
  }
791
- userBindingEntries.set(entry.id, { entry, transform: snapshot.transform, moduleUrl: evaluated.moduleUrl, names, descriptors })
792
- retainedUserBindingRuntime = true
793
- activated.push(entry.id)
956
+ mapSet(userBindingEntries, entry.id, { entry, transform: snapshot.transform, moduleUrl: evaluated.moduleUrl, names, descriptors })
957
+ if (snapshot.transform !== USER_BINDING_TRANSFORM) retainedLegacyUserBindingRuntime = true
958
+ appendArray(activated, entry.id)
794
959
  } catch (error) {
795
960
  activation.failed = true
796
- if (activation.called) markVolatile(`failed user binding ${JSON.stringify(entry.id)} issued a host call`)
797
- for (const installed of installedNames.reverse()) {
798
- userBindingNames.delete(installed.name)
799
- if (installed.descriptor === undefined) delete context[installed.name]
800
- else Object.defineProperty(context, installed.name, installed.descriptor)
961
+ if (activation.called) markVolatile(`failed user binding ${jsonStringify(entry.id)} issued a host call`)
962
+ for (let index = installedNames.length - 1; index >= 0; index--) {
963
+ const installed = installedNames[index]
964
+ mapDelete(userBindingNames, installed.name)
965
+ if (installed.descriptor === undefined) {
966
+ deleteWorkerRealmProperty(WORKER_REALM_MUTATION.RESTORE, undefined,
967
+ context, installed.name)
968
+ } else {
969
+ defineWorkerRealmProperty(WORKER_REALM_MUTATION.RESTORE, undefined,
970
+ context, installed.name, installed.descriptor)
971
+ }
801
972
  }
802
- for (const [name, prior] of previousSources) {
803
- if (prior === undefined) userBindingSources.delete(name)
804
- else userBindingSources.set(name, prior)
973
+ const previousEntries = mapEntriesArray(previousSources)
974
+ for (let previousIndex = 0; previousIndex < previousEntries.length; previousIndex += 1) {
975
+ const name = previousEntries[previousIndex][0]
976
+ const prior = previousEntries[previousIndex][1]
977
+ if (prior === undefined) mapDelete(userBindingSources, name)
978
+ else mapSet(userBindingSources, name, prior)
805
979
  }
806
- if (evaluated !== undefined) userBindingModuleParents.delete(evaluated.moduleUrl)
807
- failures.push({ id: entry.id, error: messageOf(error) })
980
+ if (evaluated !== undefined) mapDelete(userBindingModuleParents, evaluated.moduleUrl)
981
+ appendArray(failures, { id: entry.id, error: messageOf(error) })
808
982
  }
809
983
  }
810
- if (userBindingEntries.size === 0 && !retainedUserBindingRuntime) restoreDynamicNamespaceGlobals()
984
+ if (mapSize(userBindingEntries) === 0 && !retainedLegacyUserBindingRuntime) restoreDynamicNamespaceGlobals()
811
985
  return { activated, failures }
812
986
  }
813
987
 
814
988
  function removeUserBindingEntry(id, shadowedNames) {
815
- const previous = userBindingEntries.get(id)
989
+ const previous = mapGet(userBindingEntries, id)
816
990
  if (previous === undefined) return
817
- for (const name of previous.names) {
818
- if (userBindingNames.get(name) !== id) continue
819
- userBindingNames.delete(name)
820
- if (!shadowedNames.has(name)) delete context[name]
991
+ for (let index = 0; index < previous.names.length; index += 1) {
992
+ const name = previous.names[index]
993
+ if (mapGet(userBindingNames, name) !== id) continue
994
+ mapDelete(userBindingNames, name)
995
+ if (!setHas(shadowedNames, name)) {
996
+ deleteWorkerRealmProperty(WORKER_REALM_MUTATION.RESTORE, undefined, context, name)
997
+ }
821
998
  }
822
- userBindingEntries.delete(id)
999
+ mapDelete(userBindingEntries, id)
823
1000
  }
824
1001
 
825
1002
  function descriptorsEqual(left, right) {
@@ -833,12 +1010,16 @@ function descriptorsEqual(left, right) {
833
1010
  }
834
1011
 
835
1012
  function reconcileUserBindingShadows(shadowedNames) {
836
- for (const [id, entry] of userBindingEntries) {
837
- for (const name of entry.names) {
838
- if (userBindingNames.get(name) !== id) continue
839
- const installed = entry.descriptors.get(name)
1013
+ const entries = mapEntriesArray(userBindingEntries)
1014
+ for (let entryIndex = 0; entryIndex < entries.length; entryIndex += 1) {
1015
+ const id = entries[entryIndex][0]
1016
+ const entry = entries[entryIndex][1]
1017
+ for (let nameIndex = 0; nameIndex < entry.names.length; nameIndex += 1) {
1018
+ const name = entry.names[nameIndex]
1019
+ if (mapGet(userBindingNames, name) !== id) continue
1020
+ const installed = mapGet(entry.descriptors, name)
840
1021
  const current = Object.getOwnPropertyDescriptor(context, name)
841
- if (!descriptorsEqual(current, installed)) shadowedNames.add(name)
1022
+ if (!descriptorsEqual(current, installed)) setAdd(shadowedNames, name)
842
1023
  }
843
1024
  }
844
1025
  }
@@ -847,15 +1028,18 @@ async function evaluateUserBinding(entry, cwd, transform) {
847
1028
  if (typeof cwd !== 'string' || !isAbsolute(cwd)) {
848
1029
  throw new Error('user binding activation requires an absolute storage directory')
849
1030
  }
850
- const prepared = compileStatefulModule(entry.source, { transform })
1031
+ const prepared = compileStatefulModule(entry.source, {
1032
+ transform,
1033
+ programBindings: transform === USER_BINDING_TRANSFORM,
1034
+ })
851
1035
  const url = `data:text/javascript,${encodeURIComponent(prepared.code)}#ptc-plus-${entry.fingerprint}-${++nextUserBindingModuleId}`
852
- userBindingModuleParents.set(url, pathToFileURL(resolve(cwd, 'bindings.json')).href)
1036
+ mapSet(userBindingModuleParents, url, pathToFileURL(resolve(cwd, 'bindings.json')).href)
853
1037
  userModuleCompilation.mark(url, { transform, compiled: true, moduleInterface: prepared.moduleInterface, sourceRegions: prepared.sourceRegions })
854
1038
  try {
855
1039
  return { namespace: transform === LEGACY_USER_BINDING_TRANSFORM ? await import(url)
856
1040
  : await managedModuleImport(url, url), moduleUrl: url }
857
1041
  } catch (error) {
858
- userBindingModuleParents.delete(url)
1042
+ mapDelete(userBindingModuleParents, url)
859
1043
  throw error
860
1044
  }
861
1045
  }
@@ -865,22 +1049,31 @@ function userBindingImplementationMatches(left, right) {
865
1049
  && left.scope === right.scope
866
1050
  && (left.scope !== 'namespace' || left.name === right.name)
867
1051
  && left.symbols.length === right.symbols.length
868
- && left.symbols.every((symbol, index) => symbol === right.symbols[index])
1052
+ && everyArray(left.symbols, (symbol, index) => symbol === right.symbols[index])
869
1053
  }
870
1054
 
871
1055
  async function activateUserBindings(snapshot, shadowedNames, cwd, reusePolicy, initialFailures = []) {
872
1056
  reconcileUserBindingShadows(shadowedNames)
873
- const blockedIds = new Set(initialFailures.map(failure => failure.id))
874
- const desired = new Map((snapshot?.entries ?? [])
875
- .filter((entry) => {
1057
+ const blockedIds = setFromArray(mapArray(initialFailures, failure => failure.id))
1058
+ const desired = mapFromArray(filterArray(snapshot?.entries ?? [], (entry) => {
876
1059
  const names = entry.scope === 'namespace' ? [entry.name] : entry.symbols
877
- return !blockedIds.has(entry.id) && !names.some(name => shadowedNames.has(name))
878
- })
879
- .map(entry => [entry.id, entry]))
880
- if (desired.size > 0 || retainedUserBindingRuntime) installDynamicNamespaceGlobals()
881
- for (const id of [...userBindingEntries.keys()]) {
882
- const current = userBindingEntries.get(id)
883
- const next = desired.get(id)
1060
+ return !setHas(blockedIds, entry.id) && !someArray(names, name => setHas(shadowedNames, name))
1061
+ }), entry => [entry.id, entry])
1062
+ try {
1063
+ if (mapSize(desired) > 0 || retainedLegacyUserBindingRuntime) installDynamicNamespaceGlobals()
1064
+ } catch (error) {
1065
+ const ids = mapKeysArray(userBindingEntries)
1066
+ for (let index = 0; index < ids.length; index += 1) removeUserBindingEntry(ids[index], shadowedNames)
1067
+ const failures = copyArray(initialFailures)
1068
+ const desiredFailures = mapArray(mapKeysArray(desired), id => ({ id, error: messageOf(error) }))
1069
+ for (let index = 0; index < desiredFailures.length; index += 1) appendArray(failures, desiredFailures[index])
1070
+ return { activated: [], failures, error }
1071
+ }
1072
+ const existingIds = mapKeysArray(userBindingEntries)
1073
+ for (let index = 0; index < existingIds.length; index += 1) {
1074
+ const id = existingIds[index]
1075
+ const current = mapGet(userBindingEntries, id)
1076
+ const next = mapGet(desired, id)
884
1077
  // Historical cells retain fingerprint-based resets, including presentation edits.
885
1078
  const reusable = next !== undefined && current.transform === snapshot.transform && (reusePolicy === LEGACY_USER_BINDINGS_REUSE_POLICY
886
1079
  ? next.fingerprint === current.entry.fingerprint
@@ -890,12 +1083,14 @@ async function activateUserBindings(snapshot, shadowedNames, cwd, reusePolicy, i
890
1083
  }
891
1084
  }
892
1085
  const activated = []
893
- const failures = [...initialFailures]
894
- for (const entry of desired.values()) {
895
- const current = userBindingEntries.get(entry.id)
1086
+ const failures = copyArray(initialFailures)
1087
+ const desiredEntries = mapValuesArray(desired)
1088
+ for (let entryIndex = 0; entryIndex < desiredEntries.length; entryIndex += 1) {
1089
+ const entry = desiredEntries[entryIndex]
1090
+ const current = mapGet(userBindingEntries, entry.id)
896
1091
  if (current !== undefined) {
897
1092
  current.entry = entry
898
- activated.push(entry.id)
1093
+ appendArray(activated, entry.id)
899
1094
  continue
900
1095
  }
901
1096
  let evaluated
@@ -903,22 +1098,24 @@ async function activateUserBindings(snapshot, shadowedNames, cwd, reusePolicy, i
903
1098
  const activation = { id: entry.id, called: false, failed: false }
904
1099
  try {
905
1100
  if (entry.durability === 'volatile') {
906
- markVolatile(`user binding ${JSON.stringify(entry.id)}: ${entry.volatileReason ?? 'non-replayable source'}`)
1101
+ markVolatile(`user binding ${jsonStringify(entry.id)}: ${entry.volatileReason ?? 'non-replayable source'}`)
907
1102
  }
908
1103
  evaluated = await userBindingActivationScope.run(
909
1104
  activation,
910
1105
  () => evaluateUserBinding(entry, cwd, snapshot.transform),
911
1106
  )
912
1107
  const namespace = evaluated.namespace
913
- for (const symbol of entry.symbols) {
1108
+ for (let symbolIndex = 0; symbolIndex < entry.symbols.length; symbolIndex += 1) {
1109
+ const symbol = entry.symbols[symbolIndex]
914
1110
  if (!Object.hasOwn(namespace, symbol)) {
915
- throw new Error(`named export ${JSON.stringify(symbol)} is unavailable after evaluation`)
1111
+ throw new Error(`named export ${jsonStringify(symbol)} is unavailable after evaluation`)
916
1112
  }
917
1113
  }
918
1114
  const names = []
919
1115
  if (entry.scope === 'namespace') {
920
1116
  const view = Object.create(null)
921
- for (const symbol of entry.symbols) {
1117
+ for (let symbolIndex = 0; symbolIndex < entry.symbols.length; symbolIndex += 1) {
1118
+ const symbol = entry.symbols[symbolIndex]
922
1119
  Object.defineProperty(view, symbol, {
923
1120
  enumerable: true,
924
1121
  get: () => namespace[symbol],
@@ -926,13 +1123,13 @@ async function activateUserBindings(snapshot, shadowedNames, cwd, reusePolicy, i
926
1123
  }
927
1124
  Object.freeze(view)
928
1125
  const descriptor = Object.getOwnPropertyDescriptor(context, entry.name)
929
- Object.defineProperty(context, entry.name, {
1126
+ defineWorkerRealmProperty(WORKER_REALM_MUTATION.USER, undefined, context, entry.name, {
930
1127
  configurable: true,
931
1128
  enumerable: true,
932
1129
  get: () => view,
933
1130
  set(value) {
934
1131
  recordUserBindingAssignment(entry.name, true)
935
- Object.defineProperty(context, entry.name, {
1132
+ defineWorkerRealmProperty(WORKER_REALM_MUTATION.USER, undefined, context, entry.name, {
936
1133
  configurable: true,
937
1134
  enumerable: true,
938
1135
  writable: true,
@@ -940,22 +1137,23 @@ async function activateUserBindings(snapshot, shadowedNames, cwd, reusePolicy, i
940
1137
  })
941
1138
  },
942
1139
  })
943
- installedNames.push({
1140
+ appendArray(installedNames, {
944
1141
  name: entry.name,
945
1142
  descriptor,
946
1143
  })
947
- userBindingNames.set(entry.name, entry.id)
948
- names.push(entry.name)
1144
+ mapSet(userBindingNames, entry.name, entry.id)
1145
+ appendArray(names, entry.name)
949
1146
  } else {
950
- for (const symbol of entry.symbols) {
1147
+ for (let symbolIndex = 0; symbolIndex < entry.symbols.length; symbolIndex += 1) {
1148
+ const symbol = entry.symbols[symbolIndex]
951
1149
  const descriptor = Object.getOwnPropertyDescriptor(context, symbol)
952
- Object.defineProperty(context, symbol, {
1150
+ defineWorkerRealmProperty(WORKER_REALM_MUTATION.USER, undefined, context, symbol, {
953
1151
  configurable: true,
954
1152
  enumerable: true,
955
1153
  get: () => namespace[symbol],
956
1154
  set(value) {
957
1155
  recordUserBindingAssignment(symbol, true)
958
- Object.defineProperty(context, symbol, {
1156
+ defineWorkerRealmProperty(WORKER_REALM_MUTATION.USER, undefined, context, symbol, {
959
1157
  configurable: true,
960
1158
  enumerable: true,
961
1159
  writable: true,
@@ -963,48 +1161,55 @@ async function activateUserBindings(snapshot, shadowedNames, cwd, reusePolicy, i
963
1161
  })
964
1162
  },
965
1163
  })
966
- installedNames.push({ name: symbol, descriptor })
967
- userBindingNames.set(symbol, entry.id)
968
- names.push(symbol)
1164
+ appendArray(installedNames, { name: symbol, descriptor })
1165
+ mapSet(userBindingNames, symbol, entry.id)
1166
+ appendArray(names, symbol)
969
1167
  }
970
1168
  }
971
- userBindingEntries.set(entry.id, {
1169
+ mapSet(userBindingEntries, entry.id, {
972
1170
  entry,
973
1171
  transform: snapshot.transform,
974
1172
  moduleUrl: evaluated.moduleUrl,
975
1173
  names,
976
- descriptors: new Map(names.map(name => [name, Object.getOwnPropertyDescriptor(context, name)])),
1174
+ descriptors: mapFromArray(names, name => [name, Object.getOwnPropertyDescriptor(context, name)]),
977
1175
  })
978
- retainedUserBindingRuntime = true
979
- activated.push(entry.id)
1176
+ if (snapshot.transform !== USER_BINDING_TRANSFORM) retainedLegacyUserBindingRuntime = true
1177
+ appendArray(activated, entry.id)
980
1178
  } catch (error) {
981
1179
  activation.failed = true
982
1180
  if (activation.called) {
983
- markVolatile(`failed user binding ${JSON.stringify(entry.id)} issued a host call`)
1181
+ markVolatile(`failed user binding ${jsonStringify(entry.id)} issued a host call`)
984
1182
  }
985
- for (const installed of installedNames.reverse()) {
986
- userBindingNames.delete(installed.name)
987
- if (installed.descriptor === undefined) delete context[installed.name]
988
- else Object.defineProperty(context, installed.name, installed.descriptor)
1183
+ for (let index = installedNames.length - 1; index >= 0; index--) {
1184
+ const installed = installedNames[index]
1185
+ mapDelete(userBindingNames, installed.name)
1186
+ if (installed.descriptor === undefined) {
1187
+ deleteWorkerRealmProperty(WORKER_REALM_MUTATION.RESTORE, undefined,
1188
+ context, installed.name)
1189
+ } else {
1190
+ defineWorkerRealmProperty(WORKER_REALM_MUTATION.RESTORE, undefined,
1191
+ context, installed.name, installed.descriptor)
1192
+ }
989
1193
  }
990
- if (evaluated !== undefined) userBindingModuleParents.delete(evaluated.moduleUrl)
1194
+ if (evaluated !== undefined) mapDelete(userBindingModuleParents, evaluated.moduleUrl)
991
1195
  removeUserBindingEntry(entry.id, shadowedNames)
992
- failures.push({
1196
+ appendArray(failures, {
993
1197
  id: entry.id,
994
1198
  error: messageOf(error),
995
1199
  })
996
1200
  }
997
1201
  }
998
- if (userBindingEntries.size === 0 && !retainedUserBindingRuntime) restoreDynamicNamespaceGlobals()
1202
+ if (mapSize(userBindingEntries) === 0 && !retainedLegacyUserBindingRuntime) restoreDynamicNamespaceGlobals()
999
1203
  return { activated, failures }
1000
1204
  }
1001
1205
 
1002
1206
  async function closeExecution(execution) {
1003
1207
  activeRun = undefined
1004
- execution.open = false
1005
- await Promise.all([...pending.values()]
1006
- .filter(call => call.runId === execution.id)
1007
- .map(call => call.settled))
1208
+ const settling = []
1209
+ mapForEach(pending, call => {
1210
+ if (call.runId === execution.id) appendArray(settling, call.settled)
1211
+ })
1212
+ for (let index = 0; index < settling.length; index++) await settling[index]
1008
1213
  }
1009
1214
 
1010
1215
  function failureOutcome(error, phase, program) {
@@ -1031,7 +1236,7 @@ function failureOutcome(error, phase, program) {
1031
1236
 
1032
1237
  function observeBindings(names) {
1033
1238
  const observation = valueObserver.observe(names)
1034
- return { ...observation, entries: observation.entries.map(entry => {
1239
+ return { ...observation, entries: mapArray(observation.entries, entry => {
1035
1240
  const local = statefulRoots.localValue(entry.name)
1036
1241
  return local === undefined ? entry : { name: entry.name, ...previewBindingValue(local.value) }
1037
1242
  }) }
@@ -1051,27 +1256,44 @@ function refreshLegacyPublication(name) {
1051
1256
  }
1052
1257
  }
1053
1258
 
1054
- function sendCompletion(message, execution, userBindings, committedRedeclarations, outcome) {
1055
- for (const name of execution.nativePublications?.keys() ?? []) refreshLegacyPublication(name)
1259
+ function writeOutputFence(write, marker) {
1260
+ return new ControlPromise((resolve, reject) => {
1261
+ write(marker, error => error === null || error === undefined ? resolve() : reject(error))
1262
+ })
1263
+ }
1264
+
1265
+ async function sendCompletion(message, execution, userBindings, committedRedeclarations, outcome) {
1266
+ const publicationNames = execution.nativePublications === undefined
1267
+ ? [] : mapKeysArray(execution.nativePublications)
1268
+ for (let index = 0; index < publicationNames.length; index += 1) refreshLegacyPublication(publicationNames[index])
1056
1269
  const shadowedNames = execution.legacyAssignedUserBindingNames
1057
1270
  const perName = message.userBindingsShadowPolicy === LIVE_USER_BINDINGS_SHADOW_POLICY
1058
1271
  if (perName) reconcileUserBindingNames()
1059
1272
  else reconcileUserBindingShadows(shadowedNames)
1060
1273
  const names = message.observeNames ?? []
1274
+ await writeOutputFence(nativeStdoutWrite, outputFenceMarker(message.outputFence, 'stdout', 'end'))
1275
+ await writeOutputFence(nativeStderrWrite, outputFenceMarker(message.outputFence, 'stderr', 'end'))
1276
+ // Program-call settlement and value encoding belong to this physical run, so
1277
+ // structured output from a drained continuation keeps its cell ownership
1278
+ // until this completion is posted. Output from the same scheduling execution
1279
+ // after this point is background output and carries the attribution
1280
+ // diagnostic instead of being attached to a later cell.
1281
+ execution.open = false
1061
1282
  channel.postMessage({
1062
1283
  type: 'done',
1063
1284
  id: message.id,
1285
+ outputFence: message.outputFence,
1064
1286
  logs: execution.logs,
1065
1287
  ...completionDurability(execution),
1066
- committedRedeclarations: [...committedRedeclarations],
1288
+ committedRedeclarations: setValuesArray(committedRedeclarations),
1067
1289
  ...(message.rootBindings === undefined ? {} : { rootBindingFacts: statefulRoots.facts() }),
1068
- ...(perName ? { userBindingNames: normalizeUserBindingNames([...userBindingSources].map(([name, source]) => ({
1069
- name, state: source.state, ...(source.state === 'provider' ? { entryId: source.entryId } : {}),
1290
+ ...(perName ? { userBindingNames: normalizeUserBindingNames(mapArray(mapEntriesArray(userBindingSources), entry => ({
1291
+ name: entry[0], state: entry[1].state, ...(entry[1].state === 'provider' ? { entryId: entry[1].entryId } : {}),
1070
1292
  }))) } : {}),
1071
1293
  ...(message.userBindings === undefined ? {} : {
1072
1294
  activatedUserBindings: userBindings.activated,
1073
1295
  userBindingFailures: userBindings.failures,
1074
- ...(perName ? {} : { shadowedUserBindings: [...shadowedNames] }),
1296
+ ...(perName ? {} : { shadowedUserBindings: setValuesArray(shadowedNames) }),
1075
1297
  }),
1076
1298
  ...outcome,
1077
1299
  observing: names.length > 0,
@@ -1084,12 +1306,18 @@ function sendCompletion(message, execution, userBindings, committedRedeclaration
1084
1306
 
1085
1307
  async function runCell(message) {
1086
1308
  if (activeExecution !== undefined) throw new Error('kernel received overlapping cells')
1309
+ await writeOutputFence(nativeStdoutWrite, outputFenceMarker(message.outputFence, 'stdout', 'start'))
1310
+ await writeOutputFence(nativeStderrWrite, outputFenceMarker(message.outputFence, 'stderr', 'start'))
1311
+ await new ControlPromise(resolve => {
1312
+ mapSet(pendingOutputStarts, message.id, { outputFence: message.outputFence, resolve })
1313
+ channel.postMessage({ type: 'output-start', id: message.id, outputFence: message.outputFence })
1314
+ })
1087
1315
  activeRun = message.id
1088
- installBindings(message)
1089
1316
  const execution = {
1090
1317
  id: message.id,
1091
1318
  exceptionOrigins: createExceptionOriginScope(),
1092
1319
  languageSemantics: message.languageSemantics,
1320
+ moduleTransform: message.moduleTransform,
1093
1321
  userBindingsShadowPolicy: message.userBindingsShadowPolicy,
1094
1322
  importBindingNamespaces: message.importBindingNamespaces,
1095
1323
  legacyAssignedUserBindingNames: new Set(),
@@ -1099,10 +1327,9 @@ async function runCell(message) {
1099
1327
  logBytes: 2,
1100
1328
  maxOutputBytes: message.maxOutputBytes,
1101
1329
  valueLimits: message.valueLimits,
1102
- durability: message.durability === 'volatile' || pendingVolatileReason !== undefined ? 'volatile' : 'durable',
1103
- volatileReason: pendingVolatileReason,
1330
+ durability: message.durability === 'volatile' ? 'volatile' : 'durable',
1331
+ volatileReason: message.volatileReason,
1104
1332
  }
1105
- pendingVolatileReason = undefined
1106
1333
  activeExecution = execution
1107
1334
  const cellGlobals = []
1108
1335
  const committedRedeclarations = new Set()
@@ -1113,56 +1340,62 @@ async function runCell(message) {
1113
1340
  let userBindings = { activated: [], failures: [] }
1114
1341
  try {
1115
1342
  completion = await logScope.run(execution, async () => {
1116
- Object.defineProperty(context, message.returnSignal, {
1343
+ const namespaceError = installBindings(message)
1344
+ defineWorkerRealmProperty(WORKER_REALM_MUTATION.TEMPORARY, undefined, context, message.returnSignal, {
1117
1345
  configurable: true,
1118
1346
  value: CellReturn,
1119
1347
  })
1120
- cellGlobals.push(message.returnSignal)
1121
- Object.defineProperty(context, message.commitSignal, {
1348
+ appendArray(cellGlobals, message.returnSignal)
1349
+ defineWorkerRealmProperty(WORKER_REALM_MUTATION.TEMPORARY, undefined, context, message.commitSignal, {
1122
1350
  configurable: true,
1123
1351
  value(name) {
1124
1352
  setAdd(committedRedeclarations, name)
1125
1353
  },
1126
1354
  })
1127
- cellGlobals.push(message.commitSignal)
1355
+ appendArray(cellGlobals, message.commitSignal)
1128
1356
  if (message.rootRuntimeName !== undefined) {
1129
- const nativeLexicals = new Set(message.rootBindings.legacyNativeLexicals)
1130
- const legacyStorage = new Map(message.rootBindings.legacyLexicals
1131
- .filter(name => !statefulRoots.has(name)).map(name => [name, rootBindingStorage(name, nativeLexicals.has(name))]))
1132
- const legacyLexicals = message.rootBindings.legacyLexicals.filter(name => legacyStorage.get(name) === 'lexical')
1133
- const legacyNative = new Set(message.rootBindings.legacyNative)
1134
- const legacyWritable = message.rootBindings.legacyWritable.filter(name => {
1135
- const storage = legacyStorage.get(name)
1136
- return storage === 'lexical' || legacyNative.has(name)
1357
+ const nativeLexicals = setFromArray(message.rootBindings.legacyNativeLexicals)
1358
+ const legacyStorage = mapFromArray(filterArray(message.rootBindings.legacyLexicals,
1359
+ name => !statefulRoots.has(name)), name => [name, rootBindingStorage(name, setHas(nativeLexicals, name))])
1360
+ const legacyLexicals = filterArray(message.rootBindings.legacyLexicals, name => mapGet(legacyStorage, name) === 'lexical')
1361
+ const legacyNative = setFromArray(message.rootBindings.legacyNative)
1362
+ const legacyWritable = filterArray(message.rootBindings.legacyWritable, name => {
1363
+ const storage = mapGet(legacyStorage, name)
1364
+ return storage === 'lexical' || setHas(legacyNative, name)
1137
1365
  && (storage === 'local' || storage === 'property') && writableRootProperty(name)
1138
1366
  })
1139
- Object.defineProperty(context, message.rootRuntimeName, {
1367
+ defineWorkerRealmProperty(WORKER_REALM_MUTATION.TEMPORARY, undefined,
1368
+ context, message.rootRuntimeName, {
1140
1369
  configurable: true,
1141
1370
  value: statefulRoots.begin({ ...message.rootBindings,
1142
1371
  legacyLexicals,
1143
1372
  legacyWritable,
1144
- legacyObjects: legacyWritable.filter(name => legacyStorage.get(name) !== 'lexical'),
1373
+ legacyObjects: filterArray(legacyWritable, name => mapGet(legacyStorage, name) !== 'lexical'),
1145
1374
  committed(name) {
1146
1375
  setDelete(committedRedeclarations, name)
1147
1376
  setAdd(committedRedeclarations, name)
1148
1377
  } }),
1149
1378
  })
1150
- cellGlobals.push(message.rootRuntimeName)
1379
+ appendArray(cellGlobals, message.rootRuntimeName)
1151
1380
  }
1152
1381
  const activate = message.userBindingsShadowPolicy === LIVE_USER_BINDINGS_SHADOW_POLICY
1153
1382
  ? activatePerNameUserBindings : activateUserBindings
1154
1383
  userBindings = await activate(
1155
1384
  message.userBindings,
1156
- new Set(message.shadowedUserBindingNames ?? []),
1385
+ setFromArray(message.shadowedUserBindingNames ?? []),
1157
1386
  message.userBindingsCwd,
1158
1387
  message.userBindingsReusePolicy,
1159
1388
  message.userBindingFailures,
1160
1389
  )
1161
1390
  if (userBindings.error !== undefined) throw userBindings.error
1162
- for (const failure of userBindings.failures) {
1163
- appendText(execution, `Global binding ${JSON.stringify(failure.id)} was not activated: ${failure.error}`)
1391
+ if (namespaceError !== undefined) throw namespaceError
1392
+ for (let failureIndex = 0; failureIndex < userBindings.failures.length; failureIndex += 1) {
1393
+ const failure = userBindings.failures[failureIndex]
1394
+ appendText(execution, `Global binding ${jsonStringify(failure.id)} was not activated: ${failure.error}`)
1164
1395
  }
1165
- for (const load of message.moduleLoads ?? []) {
1396
+ const moduleLoads = message.moduleLoads ?? []
1397
+ for (let loadIndex = 0; loadIndex < moduleLoads.length; loadIndex += 1) {
1398
+ const load = moduleLoads[loadIndex]
1166
1399
  let namespace
1167
1400
  try {
1168
1401
  namespace = (await loadStaticModule(load)).namespace
@@ -1170,18 +1403,25 @@ async function runCell(message) {
1170
1403
  throw new StaticImportFailure(error, load.position)
1171
1404
  }
1172
1405
  if (load.global !== undefined) {
1173
- Object.defineProperty(context, load.global, {
1406
+ defineWorkerRealmProperty(WORKER_REALM_MUTATION.TEMPORARY, undefined, context, load.global, {
1174
1407
  configurable: true,
1175
1408
  value: namespace,
1176
1409
  })
1177
- cellGlobals.push(load.global)
1410
+ appendArray(cellGlobals, load.global)
1178
1411
  }
1179
1412
  }
1180
1413
  // Preload failure leaves prior aliases authoritative. Once evaluation
1181
1414
  // starts, new slots must prove their own initialization, including TDZ.
1182
1415
  execution.importBindingNamespaces = message.preparedImportBindingNamespaces
1183
- execution.nativePublications = new Map((message.moduleLoads ?? []).flatMap(load =>
1184
- (load.nativePublications ?? []).map(publication => [publication.name, publication])))
1416
+ execution.nativePublications = new Map()
1417
+ for (let loadIndex = 0; loadIndex < moduleLoads.length; loadIndex += 1) {
1418
+ const load = moduleLoads[loadIndex]
1419
+ const publications = load.nativePublications ?? []
1420
+ for (let publicationIndex = 0; publicationIndex < publications.length; publicationIndex += 1) {
1421
+ const publication = publications[publicationIndex]
1422
+ mapSet(execution.nativePublications, publication.name, publication)
1423
+ }
1424
+ }
1185
1425
  return execution.exceptionOrigins.run(() => evaluate(message.program, message.returnSignal, message.asyncCompletion))
1186
1426
  })
1187
1427
  } catch (error) {
@@ -1203,9 +1443,12 @@ async function runCell(message) {
1203
1443
  outcome = failureOutcome(error, 'encode', message.program)
1204
1444
  }
1205
1445
  }
1206
- sendCompletion(message, execution, userBindings, committedRedeclarations, outcome)
1446
+ await sendCompletion(message, execution, userBindings, committedRedeclarations, outcome)
1207
1447
  } finally {
1208
- for (const name of cellGlobals) delete context[name]
1448
+ for (let index = 0; index < cellGlobals.length; index += 1) {
1449
+ deleteWorkerRealmProperty(WORKER_REALM_MUTATION.RESTORE, undefined,
1450
+ context, cellGlobals[index])
1451
+ }
1209
1452
  activeRun = undefined
1210
1453
  activeExecution = undefined
1211
1454
  execution.exceptionOrigins.close()
@@ -1221,6 +1464,7 @@ parentPort.on('message', (message) => {
1221
1464
  input.destroy()
1222
1465
  output.destroy()
1223
1466
  errorDomain.removeAllListeners('error')
1467
+ process.removeListener('uncaughtException', handleUncaughtException)
1224
1468
  channel.close()
1225
1469
  parentPort.postMessage({ type: WORKER_SHUTDOWN_ACKNOWLEDGEMENT })
1226
1470
  parentPort.close()
@@ -1238,9 +1482,9 @@ channel.on('message', (message) => {
1238
1482
  return
1239
1483
  }
1240
1484
  if (message?.type === 'reply') {
1241
- const call = pending.get(message.id)
1485
+ const call = mapGet(pending, message.id)
1242
1486
  if (call === undefined || call.runId !== message.runId) return
1243
- pending.delete(message.id)
1487
+ mapDelete(pending, message.id)
1244
1488
  try {
1245
1489
  if (message.ok) call.resolve(decodeValue(message.value, call.valueLimits))
1246
1490
  else if (call.BoundError === undefined) {
@@ -1253,6 +1497,14 @@ channel.on('message', (message) => {
1253
1497
  }
1254
1498
  return
1255
1499
  }
1500
+ if (message?.type === 'output-start-ack') {
1501
+ const pendingStart = mapGet(pendingOutputStarts, message.id)
1502
+ if (pendingStart !== undefined && pendingStart.outputFence === message.outputFence) {
1503
+ mapDelete(pendingOutputStarts, message.id)
1504
+ pendingStart.resolve()
1505
+ }
1506
+ return
1507
+ }
1256
1508
  if (message?.type === 'run') void runCell(message)
1257
1509
  })
1258
1510