dsh-ptc-plus 0.4.1 → 0.4.2

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 (53) hide show
  1. package/README.en.md +6 -10
  2. package/README.md +6 -10
  3. package/client.js +262 -31
  4. package/compiler-core.cjs +132 -130
  5. package/compiler-core.cjs.map +3 -3
  6. package/docs/README.md +1 -0
  7. package/docs/adr/0023-global-user-bindings.md +6 -6
  8. package/docs/adr/0025-use-versioned-logical-binding-identities.md +3 -3
  9. package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +5 -5
  10. package/docs/architecture.md +9 -5
  11. package/docs/client-ui.md +1 -1
  12. package/docs/durability-design.md +6 -3
  13. package/docs/installation.md +4 -4
  14. package/docs/runtime-reference.md +5 -1
  15. package/docs/stateful-binding-updates-proposal.md +8 -4
  16. package/docs/user-bindings.en.md +5 -3
  17. package/docs/user-bindings.md +4 -2
  18. package/docs/verification-optimization-work.md +41 -0
  19. package/docs/verification.md +15 -1
  20. package/internal/async-local-storage-intrinsics.js +38 -0
  21. package/internal/binding-declaration-contract.js +8 -0
  22. package/internal/binding-source-analysis.js +381 -64
  23. package/internal/binding-update-policy.js +7 -1
  24. package/internal/cell-analysis.js +21 -8
  25. package/internal/compiler-intrinsics.js +9 -43
  26. package/internal/compiler-module-links.js +7 -4
  27. package/internal/dynamic-environment-integration.js +8 -4
  28. package/internal/dynamic-environment-runtime.js +24 -3
  29. package/internal/dynamic-native-calls.js +4 -1
  30. package/internal/dynamic-native-runtime.js +15 -1
  31. package/internal/failure-reporting.js +38 -30
  32. package/internal/isolated-worker.js +17 -10
  33. package/internal/kernel-worker.js +493 -331
  34. package/internal/module-compilation.js +5 -4
  35. package/internal/module-transform-contract.js +32 -2
  36. package/internal/record-utils.js +24 -15
  37. package/internal/repl-value-observer.js +6 -3
  38. package/internal/runtime-intrinsics.js +101 -0
  39. package/internal/session-cell-executor.js +2 -1
  40. package/internal/session-journal-schema.js +30 -13
  41. package/internal/session-journal.js +19 -4
  42. package/internal/session-runtime.js +1 -0
  43. package/internal/source-position-map.js +19 -0
  44. package/internal/stateful-root-compiler.js +3 -1
  45. package/internal/typescript-transform.js +4 -2
  46. package/internal/user-binding-console-worker.js +101 -41
  47. package/internal/user-bindings.js +10 -2
  48. package/internal/value-wire.js +101 -71
  49. package/internal/worker-cwd-virtualization.js +80 -53
  50. package/internal/worker-environment.js +20 -0
  51. package/internal/worker-repl-realm.js +134 -0
  52. package/package.json +2 -1
  53. package/src/client-activity.js +8 -1
@@ -1,20 +1,19 @@
1
- import { AsyncLocalStorage } from 'node:async_hooks'
2
1
  import { create as createDomain } from 'node:domain'
3
2
  import { createRequire, registerHooks } from 'node:module'
4
3
  import { managedModuleImport, managedRequire, readModuleImport, statefulModuleLink } from './stateful-module-runtime.js'
5
4
  import { staticModuleLinkReference } from './compiler-module-links.js'
6
5
  import { compileStatefulModule, createUserModuleCompilationHooks } from './stateful-module-compiler.js'
7
- import { USER_BINDING_TRANSFORM, LEGACY_USER_BINDING_TRANSFORM, moduleTransformForLanguage } from './module-transform-contract.js'
6
+ import { USER_BINDING_TRANSFORM, LEGACY_USER_BINDING_TRANSFORM } from './module-transform-contract.js'
8
7
  import { isAbsolute, resolve } from 'node:path'
9
8
  import repl from 'node:repl'
10
9
  import { PassThrough } from 'node:stream'
11
10
  import { pathToFileURL } from 'node:url'
12
11
  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'
@@ -25,9 +24,77 @@ import { moduleRuntimeIntrinsics } from './compiler-intrinsics.js'
25
24
  import { compilerDescriptors } from './compiler-descriptors.js'
26
25
  import { createCellCompletionObserver } from './cell-completion.js'
27
26
  import { WORKER_SHUTDOWN_ACKNOWLEDGEMENT, WORKER_SHUTDOWN_REQUEST } from './worker-shutdown.js'
27
+ import { WORKER_REPL_OPTIONS, createWorkerControlPromise, createWorkerReplErrorHandler,
28
+ createWorkerUncaughtExceptionHandler,
29
+ disableWorkerReplDomain,
30
+ captureWorkerReplGlobals, protectWorkerReplAsyncContext, restoreWorkerReplGlobals, runInWorkerReplRealm,
31
+ workerReplContext } from './worker-repl-realm.js'
32
+ import { installProgramAmbientResolver } from './dynamic-environment-runtime.js'
28
33
 
29
- const { Object, mapGet, mapSet, mapHas, mapDelete, setHas, setAdd, setDelete } = moduleRuntimeIntrinsics
30
- const hasProperty = Reflect.has
34
+ const { Object, Map, Set, Proxy, Error,
35
+ mapGet, mapSet, mapHas, mapDelete, mapClear, mapSize, mapForEach,
36
+ setHas, setAdd, setDelete, setClear, setForEach,
37
+ everyArray, someArray, appendArray, copyArray, join, Reflect: privateReflect, promiseResolve, promiseReject } = moduleRuntimeIntrinsics
38
+ const PublicPromise = globalThis.Promise
39
+ const jsonStringify = globalThis.JSON.stringify
40
+ const bufferByteLength = globalThis.Buffer.byteLength
41
+ const String = globalThis.String
42
+ const process = globalThis.process
43
+ const ControlPromise = createWorkerControlPromise()
44
+ const hasProperty = privateReflect.has
45
+
46
+ function mapArray(values, visit) {
47
+ const result = []
48
+ for (let index = 0; index < values.length; index++) appendArray(result, visit(values[index], index))
49
+ return result
50
+ }
51
+
52
+ function filterArray(values, select) {
53
+ const result = []
54
+ for (let index = 0; index < values.length; index++) {
55
+ if (select(values[index], index)) appendArray(result, values[index])
56
+ }
57
+ return result
58
+ }
59
+
60
+ function mapFromArray(values, visit) {
61
+ const result = new Map()
62
+ for (let index = 0; index < values.length; index++) {
63
+ const entry = visit(values[index], index)
64
+ mapSet(result, entry[0], entry[1])
65
+ }
66
+ return result
67
+ }
68
+
69
+ function setFromArray(values) {
70
+ const result = new Set()
71
+ for (let index = 0; index < values.length; index++) setAdd(result, values[index])
72
+ return result
73
+ }
74
+
75
+ function mapKeysArray(value) {
76
+ const result = []
77
+ mapForEach(value, (_item, key) => appendArray(result, key))
78
+ return result
79
+ }
80
+
81
+ function mapEntriesArray(value) {
82
+ const result = []
83
+ mapForEach(value, (item, key) => appendArray(result, [key, item]))
84
+ return result
85
+ }
86
+
87
+ function mapValuesArray(value) {
88
+ const result = []
89
+ mapForEach(value, item => appendArray(result, item))
90
+ return result
91
+ }
92
+
93
+ function setValuesArray(value) {
94
+ const result = []
95
+ setForEach(value, item => appendArray(result, item))
96
+ return result
97
+ }
31
98
 
32
99
  if (parentPort === null) throw new Error('ptc-plus kernel worker started without a parent port')
33
100
  const { port1, port2: channel } = new MessageChannel()
@@ -35,42 +102,50 @@ const { port1, port2: channel } = new MessageChannel()
35
102
  const input = new PassThrough()
36
103
  const output = new PassThrough()
37
104
  output.resume()
38
- const evaluationScope = new AsyncLocalStorage()
105
+ const evaluationScope = createPrivateAsyncLocalStorage()
39
106
  const sessionCwd = typeof workerData?.cwd === 'string' ? workerData.cwd : undefined
40
107
  if (sessionCwd !== undefined && !isAbsolute(sessionCwd)) {
41
- throw new Error(`ptc-plus session cwd must be absolute, got ${JSON.stringify(sessionCwd)}`)
108
+ throw new Error(`ptc-plus session cwd must be absolute, got ${jsonStringify(sessionCwd)}`)
42
109
  }
110
+ const workerGlobalBaseline = captureWorkerReplGlobals()
43
111
  const server = repl.start({
44
112
  input,
45
113
  output,
46
114
  terminal: false,
47
115
  prompt: '',
48
- useGlobal: false,
116
+ ...WORKER_REPL_OPTIONS,
117
+ handleError: createWorkerReplErrorHandler(evaluationScope, (finish, error) => finish(true, error)),
49
118
  ignoreUndefined: true,
50
119
  })
120
+ protectWorkerReplAsyncContext(server)
121
+ const originalRequire = server.context.require
51
122
  // domain.bind exposes its owner on a bound evaluator in older Node REPLs.
52
123
  // Newer REPLs honor an explicitly entered domain. Capture before REPL error
53
124
  // formatting can evaluate user-defined stack/name getters.
54
- const errorDomain = server.eval.domain ?? createDomain()
125
+ const errorDomain = disableWorkerReplDomain(server.eval.domain ?? createDomain())
55
126
  errorDomain.removeAllListeners('error')
56
127
  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))
128
+ const handleUncaughtException = createWorkerUncaughtExceptionHandler(evaluationScope, process)
129
+ process.on('uncaughtException', handleUncaughtException)
130
+ const context = workerReplContext(server)
131
+ restoreWorkerReplGlobals(workerGlobalBaseline, context)
132
+ const workerBaselineGlobals = setFromArray(mapKeysArray(workerGlobalBaseline))
133
+ const contextGlobal = runInWorkerReplRealm('globalThis')
134
+ const completionObserver = createCellCompletionObserver(runInWorkerReplRealm('Function'))
60
135
  let replModuleRealm
61
136
  const statefulRoots = createStatefulRootRuntime({
62
137
  refresh: name => refreshLegacyPublication(name),
63
138
  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),
139
+ errors: runInWorkerReplRealm('({ ReferenceError, TypeError })'),
140
+ dynamicIntrinsics: runInWorkerReplRealm('({intrinsicEval:eval,realmFunction:Function,globalObject:globalThis,errors:{ReferenceError,TypeError,SyntaxError},reflect:Reflect,object:Object})'),
141
+ readAmbient: name => runInWorkerReplRealm(name),
142
+ typeofAmbient: name => runInWorkerReplRealm(`typeof ${name}`),
68
143
  canWriteAmbient: name => writableRootProperty(name),
69
144
  writeAmbient(name, value, strict) {
70
145
  const argument = name === '__ptc_value' ? '__ptc_other_value' : '__ptc_value'
71
- runInContext(`${strict ? '"use strict";' : ''}(${argument}) => (${name} = ${argument})`, context)(value)
146
+ runInWorkerReplRealm(`${strict ? '"use strict";' : ''}(${argument}) => (${name} = ${argument})`)(value)
72
147
  },
73
- deleteAmbient: name => runInContext(`delete ${name}`, context),
148
+ deleteAmbient: name => runInWorkerReplRealm(`delete ${name}`),
74
149
  hasAmbient: name => setHas(installedGlobals, name)
75
150
  ? mapGet(installedGlobalOriginals, name).descriptor !== undefined : hasProperty(context, name),
76
151
  // Request namespaces stay on globalThis; source declarations own separate
@@ -83,15 +158,15 @@ const statefulRoots = createStatefulRootRuntime({
83
158
  })
84
159
  const nativeRootDynamic = createNativeRootDynamic({
85
160
  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),
161
+ intrinsics: runInWorkerReplRealm('({intrinsicEval:eval,realmFunction:Function,globalObject:globalThis,errors:{ReferenceError,TypeError,SyntaxError},reflect:Reflect,object:Object})'),
87
162
  importModule: (source, options) => managedModuleImport(replParent, source, options, replModuleRealm),
88
- read: name => runInContext(name, context),
89
- typeOf: name => runInContext(`typeof ${name}`, context),
163
+ read: name => runInWorkerReplRealm(name),
164
+ typeOf: name => runInWorkerReplRealm(`typeof ${name}`),
90
165
  write(name, value, strict) {
91
166
  const argument = name === '__ptc_value' ? '__ptc_other_value' : '__ptc_value'
92
- runInContext(`${strict ? '"use strict";' : ''}(${argument}) => (${name} = ${argument})`, context)(value)
167
+ runInWorkerReplRealm(`${strict ? '"use strict";' : ''}(${argument}) => (${name} = ${argument})`)(value)
93
168
  },
94
- remove: name => runInContext(`delete ${name}`, context),
169
+ remove: name => runInWorkerReplRealm(`delete ${name}`),
95
170
  })
96
171
  let valueObserver
97
172
  const REPL_IMPORT_CANARY = 'data:text/javascript,export default 1'
@@ -101,7 +176,7 @@ const staticAdapterParents = new Set()
101
176
  const userBindingModuleParents = new Map()
102
177
  const userModuleCompilation = createUserModuleCompilationHooks({
103
178
  transformForParent: parent => (parent === replParent || parent === sessionReplParent) && activeExecution !== undefined
104
- ? moduleTransformForLanguage(activeExecution.languageSemantics) : undefined,
179
+ ? activeExecution.moduleTransform : undefined,
105
180
  })
106
181
  registerHooks({
107
182
  resolve(specifier, context, nextResolve) {
@@ -117,12 +192,12 @@ registerHooks({
117
192
  },
118
193
  load: userModuleCompilation.load,
119
194
  })
120
- const logScope = new AsyncLocalStorage()
121
- const userBindingActivationScope = new AsyncLocalStorage()
195
+ const logScope = createPrivateAsyncLocalStorage()
196
+ const userBindingActivationScope = createPrivateAsyncLocalStorage()
122
197
  const pending = new Map()
123
198
  const installedGlobals = new Set()
124
199
  const installedGlobalOriginals = new Map()
125
- const PROCESS_CONTROLS = new Set(['exit', 'abort', 'kill', 'chdir'])
200
+ const PROCESS_CONTROLS = setFromArray(['exit', 'abort', 'kill', 'chdir'])
126
201
  const CELL_FRAME_SUFFIX = '\n;'
127
202
  let filenameSequence = 0
128
203
  let activeFilename = 'ptc-plus-repl'
@@ -131,12 +206,12 @@ const CONFORMANCE_CELL = `"use strict";
131
206
  if (this !== globalThis) throw new Error('invalid REPL global receiver semantics')
132
207
  const __ptc_canary = await Promise.resolve(1)
133
208
  if (__ptc_canary !== 1) throw new Error('invalid REPL await semantics')
134
- const __ptc_import_canary = await import(${JSON.stringify(REPL_IMPORT_CANARY)})
209
+ const __ptc_import_canary = await import(${jsonStringify(REPL_IMPORT_CANARY)})
135
210
  if (__ptc_import_canary.default !== 1) throw new Error('invalid REPL import semantics')
136
211
  }`
137
212
  let activeRun
138
213
  let activeExecution
139
- let pendingVolatileReason
214
+ let suppressReplCwdObservation = false
140
215
  let nextCallId = 0
141
216
  let nextStaticAdapterId = 0
142
217
  let nextUserBindingModuleId = 0
@@ -145,7 +220,20 @@ const userBindingNames = new Map()
145
220
  const userBindingSources = new Map()
146
221
  const dynamicNamespaces = new Map()
147
222
  const originalDynamicNamespaceGlobals = new Map()
148
- let retainedUserBindingRuntime = false
223
+ const dynamicNamespaceViews = new Map()
224
+ const dynamicNamespaceGlobalGetters = new Map()
225
+ let retainedLegacyUserBindingRuntime = false
226
+
227
+ installProgramAmbientResolver(name => {
228
+ if (!mapHas(dynamicNamespaces, name) && !mapHas(originalDynamicNamespaceGlobals, name)
229
+ && !mapHas(dynamicNamespaceViews, name)) return undefined
230
+ return {
231
+ get: () => dynamicNamespaceView(name),
232
+ set() { throw new TypeError(`${name} cannot be overwritten because reserved program bindings are not shadowable`) },
233
+ typeof: () => 'object',
234
+ delete: () => false,
235
+ }
236
+ })
149
237
 
150
238
  class StaticImportFailure {
151
239
  constructor(error, position) {
@@ -172,14 +260,14 @@ function appendLog(...values) {
172
260
 
173
261
  function appendText(current, text) {
174
262
  if (current.open !== true || current.outputLimited) return
175
- const bytes = Buffer.byteLength(JSON.stringify(text), 'utf8') + (current.logs.length === 0 ? 0 : 1)
263
+ const bytes = bufferByteLength(jsonStringify(text), 'utf8') + (current.logs.length === 0 ? 0 : 1)
176
264
  if (current.logBytes + bytes > current.maxOutputBytes) {
177
265
  current.outputLimited = true
178
266
  channel.postMessage({ type: 'output-limit', id: current.id, logs: current.logs })
179
267
  return
180
268
  }
181
269
  current.logBytes += bytes
182
- current.logs.push(text)
270
+ appendArray(current.logs, text)
183
271
  }
184
272
 
185
273
  const consoleView = Object.freeze({
@@ -195,7 +283,8 @@ Object.defineProperty(context, 'console', { configurable: true, value: consoleVi
195
283
  function captureWrite(chunk, ...rest) {
196
284
  const current = logScope.getStore()
197
285
  if (current?.open === true) appendText(current, typeof chunk === 'string' ? chunk : String(chunk))
198
- const callback = [rest[0], rest[1]].find(value => typeof value === 'function')
286
+ const callback = typeof rest[0] === 'function' ? rest[0]
287
+ : typeof rest[1] === 'function' ? rest[1] : undefined
199
288
  if (callback !== undefined) queueMicrotask(() => callback(null))
200
289
  return true
201
290
  }
@@ -204,10 +293,7 @@ process.stderr.write = captureWrite
204
293
 
205
294
  function markVolatile(reason) {
206
295
  const current = activeExecution
207
- if (current === undefined) {
208
- pendingVolatileReason ??= reason
209
- return
210
- }
296
+ if (current === undefined) return
211
297
  if (current.durability === 'volatile') return
212
298
  current.durability = 'volatile'
213
299
  current.volatileReason ??= reason
@@ -221,10 +307,10 @@ function completionDurability(execution) {
221
307
  }
222
308
  }
223
309
 
224
- const originalRequire = context.require
225
-
226
310
  function guardProcessControls() {
227
- for (const property of PROCESS_CONTROLS) {
311
+ const properties = setValuesArray(PROCESS_CONTROLS)
312
+ for (let index = 0; index < properties.length; index += 1) {
313
+ const property = properties[index]
228
314
  const descriptor = Object.getOwnPropertyDescriptor(process, property)
229
315
  Object.defineProperty(process, property, {
230
316
  configurable: false,
@@ -238,84 +324,31 @@ function guardProcessControls() {
238
324
  }
239
325
 
240
326
  guardProcessControls()
241
- installWorkerCwdVirtualization(sessionCwd, originalRequire)
327
+ installWorkerCwdVirtualization(sessionCwd, originalRequire, reason => {
328
+ if (suppressReplCwdObservation) {
329
+ suppressReplCwdObservation = false
330
+ return
331
+ }
332
+ markVolatile(reason)
333
+ })
242
334
  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
335
  const providedRequire = sessionReplParent === undefined ? originalRequire : createRequire(sessionReplParent)
248
336
  function selectProvidedRequire(args) {
249
337
  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)})`)
338
+ if (setHas(FORBIDDEN_IMPORTS, specifier)) throw new Error(`module ${specifier} is forbidden because it exposes kernel control`)
339
+ if (!setHas(DURABLE_IMPORTS, specifier)) markVolatile(`require(${jsonStringify(specifier)})`)
252
340
  return managedRequire(activeExecution?.languageSemantics === 'legacy-v1' ? replParent : sessionReplParent ?? replParent,
253
341
  activeExecution?.languageSemantics === 'legacy-v1' ? originalRequire : providedRequire)
254
342
  }
255
343
  Object.defineProperty(context, 'require', {
256
344
  configurable: true,
257
345
  value: new Proxy(providedRequire, {
258
- apply: (_, receiver, args) => Reflect.apply(selectProvidedRequire(args), receiver, args),
259
- construct: (_, args, newTarget) => Reflect.construct(selectProvidedRequire(args), args, newTarget),
346
+ apply: (_, receiver, args) => privateReflect.apply(selectProvidedRequire(args), receiver, args),
347
+ construct: (_, args, newTarget) => privateReflect.construct(selectProvidedRequire(args), args, newTarget),
260
348
  }),
261
349
  })
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
350
  function evaluate(program, completionSignal, asyncCompletion = false) {
318
- return new Promise((resolve, reject) => {
351
+ return new ControlPromise((resolve, reject) => {
319
352
  let settled = false
320
353
  const finish = (failed, value) => {
321
354
  if (settled) return
@@ -334,6 +367,7 @@ function evaluate(program, completionSignal, asyncCompletion = false) {
334
367
  }
335
368
  }
336
369
  activeFilename = `ptc-plus-repl-${++filenameSequence}`
370
+ suppressReplCwdObservation = true
337
371
  evaluationScope.run(finish, () => errorDomain.run(() => {
338
372
  // Older REPL callbacks conflate null/undefined await rejection with a
339
373
  // successful empty result. A reached EOF distinguishes those outcomes.
@@ -341,7 +375,7 @@ function evaluate(program, completionSignal, asyncCompletion = false) {
341
375
  // preceding non-await expression value without adding a session binding.
342
376
  // Keep the final semicolon so REPL cannot guess a block is an object literal.
343
377
  const suffix = completionSignal === undefined || asyncCompletion ? CELL_FRAME_SUFFIX
344
- : `${CELL_FRAME_SUFFIX}{ let completed = this[${JSON.stringify(completionSignal)}].complete(); }${CELL_FRAME_SUFFIX}`
378
+ : `${CELL_FRAME_SUFFIX}{ let completed = this[${jsonStringify(completionSignal)}].complete(); }${CELL_FRAME_SUFFIX}`
345
379
  server.eval(program + suffix, context, activeFilename, (error, value) => {
346
380
  if (asyncCompletion && (error === null || error === undefined)) {
347
381
  completionObserver.observe(value, value => finish(false, value), error => finish(true, error))
@@ -351,6 +385,7 @@ function evaluate(program, completionSignal, asyncCompletion = false) {
351
385
  finish(failed, failed ? error : value)
352
386
  })
353
387
  }))
388
+ suppressReplCwdObservation = false
354
389
  })
355
390
  }
356
391
 
@@ -361,9 +396,13 @@ async function verifyEvaluation() {
361
396
  context.__ptc_settlement_probe__ = marker
362
397
  context.__ptc_completion_probe__ = CellReturn
363
398
  try {
364
- for (const value of [marker, null, undefined, false, 0]) {
399
+ const values = [marker, null, undefined, false, 0]
400
+ for (let valueIndex = 0; valueIndex < values.length; valueIndex += 1) {
401
+ const value = values[valueIndex]
365
402
  context.__ptc_settlement_probe__ = value
366
- for (const source of ['throw __ptc_settlement_probe__', 'await Promise.reject(__ptc_settlement_probe__)']) {
403
+ const sources = ['throw __ptc_settlement_probe__', 'await Promise.reject(__ptc_settlement_probe__)']
404
+ for (let sourceIndex = 0; sourceIndex < sources.length; sourceIndex += 1) {
405
+ const source = sources[sourceIndex]
367
406
  let caught = false
368
407
  try { await evaluate(source, '__ptc_completion_probe__') } catch (error) {
369
408
  if (error !== value) throw new Error('REPL did not preserve the original thrown value')
@@ -375,7 +414,7 @@ async function verifyEvaluation() {
375
414
  let syntaxRejected = false
376
415
  try { await evaluate('const =') } catch (error) { syntaxRejected = error?.name === 'SyntaxError' }
377
416
  if (!syntaxRejected) throw new Error('REPL did not preserve syntax failure')
378
- context.__ptc_settlement_probe__ = new CellReturn(Promise.resolve(marker))
417
+ context.__ptc_settlement_probe__ = new CellReturn(promiseResolve(marker))
379
418
  const returned = await evaluate('throw __ptc_settlement_probe__')
380
419
  if (!returned.hasValue || returned.value !== marker) throw new Error('REPL did not preserve cell return')
381
420
  await evaluate(CONFORMANCE_CELL)
@@ -390,47 +429,45 @@ async function verifyEvaluation() {
390
429
 
391
430
  function staticImportClause(options) {
392
431
  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(', ')} }` }
432
+ const option = Object.entries(options)[0]
433
+ const keyword = option[0]
434
+ const attributes = option[1]
435
+ const entries = mapArray(Object.entries(attributes), entry => `${jsonStringify(entry[0])}: ${jsonStringify(entry[1])}`)
436
+ return { attributes, clause: ` ${keyword} { ${join(entries, ', ')} }` }
397
437
  }
398
438
 
399
439
  function staticAdapterSource(load) {
400
440
  const { attributes, clause } = staticImportClause(load.options)
401
- const source = JSON.stringify(statefulModuleLink(load.source, staticModuleLinkReference(load.source, attributes)))
441
+ const source = jsonStringify(statefulModuleLink(load.source, staticModuleLinkReference(load.source, attributes)))
402
442
  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)
443
+ const requirements = load.requiredExports === undefined ? [] : mapArray(load.requiredExports, (name, index) => {
444
+ const imported = name === 'default' ? 'default' : jsonStringify(name)
405
445
  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')
446
+ })
447
+ const lines = [`import * as namespace from ${source}${clause};`]
448
+ if (requirements.length > 0) appendArray(lines, `export { ${join(requirements, ', ')} } from ${source}${clause};`)
449
+ appendArray(lines, 'export { namespace };')
450
+ return join(lines, '\n')
414
451
  }
415
452
 
416
453
  async function loadStaticModule(load) {
417
454
  if (load.operation === 'native-dynamic') return { namespace: nativeRootDynamic.environment(load.callableSources,
418
- load.awaitRoot === true, load.logicalRoots.filter(name => statefulRoots.has(name)
455
+ load.awaitRoot === true, filterArray(load.logicalRoots, name => statefulRoots.has(name)
419
456
  && rootBindingStorage(name, moduleRuntimeIntrinsics.includes(load.nativeLexicals, name)) !== 'lexical'), load.writableRoots) }
420
457
  if (load.operation === 'import') {
421
458
  return { namespace: (source, options) => managedModuleImport(replParent, source, options, replModuleRealm) }
422
459
  }
423
460
  const adapter = `data:text/javascript,${encodeURIComponent(staticAdapterSource(load))}#${++nextStaticAdapterId}`
424
- staticAdapterParents.add(adapter)
461
+ setAdd(staticAdapterParents, adapter)
425
462
  userModuleCompilation.mark(adapter, { compiled: true,
426
- transform: moduleTransformForLanguage(activeExecution?.languageSemantics) })
463
+ transform: activeExecution?.moduleTransform })
427
464
  try {
428
- const completion = await evaluate(`import(${JSON.stringify(adapter)})`)
465
+ const completion = await evaluate(`import(${jsonStringify(adapter)})`)
429
466
  if (load.global === undefined) return { namespace: undefined }
430
467
  const namespace = readModuleImport(adapter, load.source, null, () => completion.value.namespace, load.options?.with)
431
468
  return { namespace }
432
469
  } finally {
433
- staticAdapterParents.delete(adapter)
470
+ setDelete(staticAdapterParents, adapter)
434
471
  }
435
472
  }
436
473
 
@@ -443,28 +480,30 @@ function hasExecutionLease(runId) {
443
480
  // call is pending, and the call must keep its submitted error identity.
444
481
  function callHost(runId, global, member, args, BoundError) {
445
482
  if (!hasExecutionLease(runId)) {
446
- return Promise.reject(programBindingError('lease', 'PTC execution lease expired'))
483
+ return promiseReject(programBindingError('lease', 'PTC execution lease expired'))
447
484
  }
448
485
  const activation = userBindingActivationScope.getStore()
449
486
  if (activation !== undefined) {
450
487
  activation.called = true
451
488
  if (activation.failed) {
452
- markVolatile(`failed user binding ${JSON.stringify(activation.id)} issued a host call`)
489
+ markVolatile(`failed user binding ${jsonStringify(activation.id)} issued a host call`)
453
490
  }
454
491
  }
455
492
  const id = ++nextCallId
456
493
  const valueLimits = activeExecution.valueLimits
457
494
  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 })
495
+ const result = new PublicPromise((resolve, reject) => { settle = { resolve, reject } })
496
+ const settled = new ControlPromise(resolve => {
497
+ moduleRuntimeIntrinsics.Reflect.apply(moduleRuntimeIntrinsics.promiseThen, result, [resolve, resolve])
498
+ })
499
+ mapSet(pending, id, { ...settle, runId, valueLimits, settled, member, BoundError })
461
500
  try {
462
501
  channel.postMessage({
463
502
  type: 'call', runId, id, global, member,
464
503
  args: encodeValue(args, valueLimits),
465
504
  })
466
505
  } catch (error) {
467
- pending.delete(id)
506
+ mapDelete(pending, id)
468
507
  settle.reject(error)
469
508
  }
470
509
  return result
@@ -480,36 +519,39 @@ function dynamicNamespace(name) {
480
519
  get(_target, property) {
481
520
  if (typeof property !== 'string') return undefined
482
521
  assertLease()
483
- const descriptor = dynamicNamespaces.get(name)
484
- if (descriptor === undefined || !descriptor.members.has(property)) return undefined
522
+ const descriptor = mapGet(dynamicNamespaces, name)
523
+ if (descriptor === undefined || !setHas(descriptor.members, property)) return undefined
485
524
  return (...args) => {
486
525
  if (!hasExecutionLease(logScope.getStore()?.id)) {
487
- return Promise.reject(programBindingError('lease', 'PTC execution lease expired'))
526
+ return promiseReject(programBindingError('lease', 'PTC execution lease expired'))
488
527
  }
489
- const current = dynamicNamespaces.get(name)
490
- if (current === undefined || !current.members.has(property)) {
491
- return Promise.reject(programBindingError('capability', `unknown binding ${name}.${property}`))
528
+ const current = mapGet(dynamicNamespaces, name)
529
+ if (current === undefined || !setHas(current.members, property)) {
530
+ return promiseReject(programBindingError('capability', `unknown binding ${name}.${property}`))
492
531
  }
493
532
  return callHost(
494
533
  logScope.getStore()?.id,
495
534
  name,
496
535
  property,
497
- args.length === 0 && current.emptyObjectMembers.has(property) ? {} : args[0],
536
+ args.length === 0 && setHas(current.emptyObjectMembers, property) ? {} : args[0],
498
537
  current.BoundError,
499
538
  )
500
539
  }
501
540
  },
502
541
  has(_target, property) {
503
542
  assertLease()
504
- return typeof property === 'string' && dynamicNamespaces.get(name)?.members.has(property) === true
543
+ const descriptor = mapGet(dynamicNamespaces, name)
544
+ return typeof property === 'string' && descriptor !== undefined && setHas(descriptor.members, property)
505
545
  },
506
546
  ownKeys() {
507
547
  assertLease()
508
- return [...dynamicNamespaces.get(name)?.members ?? []]
548
+ const descriptor = mapGet(dynamicNamespaces, name)
549
+ return descriptor === undefined ? [] : setValuesArray(descriptor.members)
509
550
  },
510
551
  getOwnPropertyDescriptor(_target, property) {
511
552
  assertLease()
512
- return typeof property === 'string' && dynamicNamespaces.get(name)?.members.has(property) === true
553
+ const descriptor = mapGet(dynamicNamespaces, name)
554
+ return typeof property === 'string' && descriptor !== undefined && setHas(descriptor.members, property)
513
555
  ? compilerDescriptors.descriptor({ configurable: true, enumerable: true })
514
556
  : undefined
515
557
  },
@@ -517,50 +559,92 @@ function dynamicNamespace(name) {
517
559
  })
518
560
  }
519
561
 
562
+ function dynamicNamespaceView(name) {
563
+ let view = mapGet(dynamicNamespaceViews, name)
564
+ if (view === undefined) {
565
+ view = dynamicNamespace(name)
566
+ mapSet(dynamicNamespaceViews, name, view)
567
+ }
568
+ return view
569
+ }
570
+
571
+ function namespaceGlobalGetter(name) {
572
+ let getter = mapGet(dynamicNamespaceGlobalGetters, name)
573
+ if (getter === undefined) {
574
+ getter = () => mapGet(dynamicNamespaces, name)?.cellView ?? dynamicNamespaceView(name)
575
+ mapSet(dynamicNamespaceGlobalGetters, name, getter)
576
+ }
577
+ return getter
578
+ }
579
+
580
+ function installNamespaceGlobal(name) {
581
+ Object.defineProperty(globalThis, name, {
582
+ configurable: true,
583
+ get: namespaceGlobalGetter(name),
584
+ })
585
+ }
586
+
520
587
  function installDynamicNamespaceGlobals() {
521
588
  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) {
589
+ const names = mapKeysArray(dynamicNamespaces)
590
+ for (let index = 0; index < names.length; index += 1) {
591
+ const name = names[index]
592
+ if (mapHas(originalDynamicNamespaceGlobals, name)) {
593
+ installNamespaceGlobal(name)
594
+ continue
595
+ }
596
+ const original = setHas(installedGlobals, name)
597
+ ? mapGet(installedGlobalOriginals, name)
598
+ : capturedGlobalDescriptor(name)
599
+ if (setHas(workerBaselineGlobals, name)) {
526
600
  throw new Error(
527
- `program namespace ${JSON.stringify(name)} cannot be bridged into Global User Binding modules because the worker global already exists`,
601
+ `program namespace ${jsonStringify(name)} cannot be bridged into Global User Binding modules because the worker global already exists`,
528
602
  )
529
603
  }
530
- pendingGlobals.push(name)
604
+ appendArray(pendingGlobals, [name, original.descriptor])
531
605
  }
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
- })
606
+ for (let index = 0; index < pendingGlobals.length; index += 1) {
607
+ const entry = pendingGlobals[index]
608
+ const name = entry[0]
609
+ const descriptor = entry[1]
610
+ mapSet(originalDynamicNamespaceGlobals, name, descriptor)
611
+ if (!setHas(installedGlobals, name)) installNamespaceGlobal(name)
539
612
  }
540
613
  }
541
614
 
542
615
  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()
616
+ const names = mapKeysArray(originalDynamicNamespaceGlobals)
617
+ for (let index = 0; index < names.length; index += 1) {
618
+ const name = names[index]
619
+ setHas(installedGlobals, name) ? installNamespaceGlobal(name) : privateReflect.deleteProperty(globalThis, name)
620
+ }
621
+ mapClear(originalDynamicNamespaceGlobals)
547
622
  }
548
623
 
549
624
  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)) {
625
+ const installedNames = setValuesArray(installedGlobals)
626
+ for (let index = 0; index < installedNames.length; index += 1) {
627
+ const name = installedNames[index]
628
+ const original = mapGet(installedGlobalOriginals, name)
629
+ const attachedEntry = original.userGlobalEntryId === undefined
630
+ ? undefined : mapGet(userBindingEntries, original.userGlobalEntryId)
631
+ const attachment = attachedEntry === undefined ? undefined : mapGet(attachedEntry.descriptors, name)
632
+ if (mapHas(originalDynamicNamespaceGlobals, name)) {
633
+ installNamespaceGlobal(name)
634
+ } else if (original.userGlobalEntryId !== undefined
635
+ && (mapGet(userBindingNames, name) !== original.userGlobalEntryId
636
+ || attachment !== original.attachment)) {
555
637
  delete context[name]
556
638
  } else if (original.descriptor === undefined) delete context[name]
557
639
  else Object.defineProperty(context, name, original.descriptor)
558
640
  }
559
- installedGlobals.clear()
560
- installedGlobalOriginals.clear()
561
- dynamicNamespaces.clear()
641
+ setClear(installedGlobals)
642
+ mapClear(installedGlobalOriginals)
643
+ mapClear(dynamicNamespaces)
644
+ let namespaceError
562
645
 
563
- for (const namespace of message.namespaces) {
646
+ for (let namespaceIndex = 0; namespaceIndex < message.namespaces.length; namespaceIndex += 1) {
647
+ const namespace = message.namespaces[namespaceIndex]
564
648
  const descriptor = namespace.errorClass
565
649
  // The wrapper owns the constructor, so a pending call keeps its submitted
566
650
  // error identity even after the cell shadows the context binding.
@@ -573,42 +657,55 @@ function installBindings(message) {
573
657
  }
574
658
  }
575
659
  const view = Object.create(null)
576
- const emptyObjectMembers = new Set(namespace.emptyObjectMembers ?? [])
577
- dynamicNamespaces.set(namespace.global, {
578
- members: new Set(namespace.members),
660
+ const emptyObjectMembers = setFromArray(namespace.emptyObjectMembers ?? [])
661
+ mapSet(dynamicNamespaces, namespace.global, {
662
+ members: setFromArray(namespace.members),
579
663
  shadowable: namespace.shadowable === true,
580
664
  emptyObjectMembers,
581
665
  BoundError,
666
+ cellView: view,
582
667
  })
583
- for (const member of namespace.members) {
668
+ for (let memberIndex = 0; memberIndex < namespace.members.length; memberIndex += 1) {
669
+ const member = namespace.members[memberIndex]
584
670
  Object.defineProperty(view, member, {
585
671
  enumerable: true,
586
672
  value: (...args) => callHost(
587
673
  message.id,
588
674
  namespace.global,
589
675
  member,
590
- args.length === 0 && emptyObjectMembers.has(member) ? {} : args[0],
676
+ args.length === 0 && setHas(emptyObjectMembers, member) ? {} : args[0],
591
677
  BoundError,
592
678
  ),
593
679
  })
594
680
  }
595
681
  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)
682
+ const original = capturedGlobalDescriptor(namespace.global)
683
+ mapSet(installedGlobalOriginals, namespace.global, original)
684
+ if (setHas(workerBaselineGlobals, namespace.global)) {
685
+ namespaceError ??= new Error(
686
+ `program namespace ${jsonStringify(namespace.global)} cannot be bridged into the worker realm because the global already exists`,
687
+ )
688
+ } else {
689
+ installNamespaceGlobal(namespace.global)
690
+ setAdd(installedGlobals, namespace.global)
691
+ }
599
692
 
600
693
  if (descriptor !== undefined) {
601
- installedGlobalOriginals.set(descriptor.name, capturedGlobalDescriptor(descriptor.name))
694
+ mapSet(installedGlobalOriginals, descriptor.name, capturedGlobalDescriptor(descriptor.name))
602
695
  Object.defineProperty(context, descriptor.name, { configurable: true, value: BoundError })
603
- installedGlobals.add(descriptor.name)
696
+ setAdd(installedGlobals, descriptor.name)
604
697
  }
605
698
  }
699
+ return namespaceError
606
700
  }
607
701
 
608
702
  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)
703
+ const descriptor = mapHas(originalDynamicNamespaceGlobals, name)
704
+ ? mapGet(originalDynamicNamespaceGlobals, name)
705
+ : Object.getOwnPropertyDescriptor(context, name)
706
+ const userGlobalEntryId = mapGet(userBindingNames, name)
707
+ const entry = mapGet(userBindingEntries, userGlobalEntryId)
708
+ const installed = entry === undefined ? undefined : mapGet(entry.descriptors, name)
612
709
  return {
613
710
  descriptor,
614
711
  ...(userGlobalEntryId !== undefined && descriptorsEqual(descriptor, installed)
@@ -618,8 +715,8 @@ function capturedGlobalDescriptor(name) {
618
715
  }
619
716
 
620
717
  function underlyingUserBindingDescriptor(name) {
621
- return installedGlobals.has(name)
622
- ? installedGlobalOriginals.get(name).descriptor
718
+ return setHas(installedGlobals, name)
719
+ ? mapGet(installedGlobalOriginals, name).descriptor
623
720
  : Object.getOwnPropertyDescriptor(context, name)
624
721
  }
625
722
 
@@ -646,7 +743,7 @@ function rootBindingStorage(name, nativeLexical = false) {
646
743
  try {
647
744
  // Both an own data property and an initialized lexical can be read
648
745
  // without executing an accessor. Either proves an available local name.
649
- runInContext(name, context, { displayErrors: false })
746
+ runInWorkerReplRealm(name, { displayErrors: false })
650
747
  return nativeLexical ? 'lexical' : 'local'
651
748
  } catch {
652
749
  return 'unknown'
@@ -655,7 +752,7 @@ function rootBindingStorage(name, nativeLexical = false) {
655
752
  let propertyRead = false
656
753
  try {
657
754
  Object.defineProperty(context, name, { configurable: true, get() { propertyRead = true } })
658
- runInContext(name, context, { displayErrors: false })
755
+ runInWorkerReplRealm(name, { displayErrors: false })
659
756
  return propertyRead ? 'property' : 'lexical'
660
757
  } catch {
661
758
  return 'unknown'
@@ -673,7 +770,8 @@ function writableRootProperty(name) {
673
770
 
674
771
  function userBindingRootStorage(name) {
675
772
  if (statefulRoots.has(name)) return 'local'
676
- const namespace = activeExecution.importBindingNamespaces?.get(name)
773
+ const namespaces = activeExecution.importBindingNamespaces
774
+ const namespace = namespaces === undefined ? undefined : mapGet(namespaces, name)
677
775
  if (namespace === undefined) return rootBindingStorage(name)
678
776
  // Compiler-validated imports use native namespace slots, not public alias
679
777
  // properties. Probe only the slot; reading an export could invoke user code.
@@ -681,96 +779,117 @@ function userBindingRootStorage(name) {
681
779
  }
682
780
 
683
781
  function reconcileUserBindingNames() {
684
- for (const name of userBindingSources.keys()) {
782
+ const names = mapKeysArray(userBindingSources)
783
+ for (let index = 0; index < names.length; index += 1) {
784
+ const name = names[index]
685
785
  const storage = userBindingRootStorage(name)
686
786
  const descriptor = underlyingUserBindingDescriptor(name)
687
- const id = userBindingNames.get(name)
688
- const installed = userBindingEntries.get(id)?.descriptors.get(name)
787
+ const id = mapGet(userBindingNames, name)
788
+ const entry = mapGet(userBindingEntries, id)
789
+ const installed = entry === undefined ? undefined : mapGet(entry.descriptors, name)
689
790
  const state = storage === 'lexical' || storage === 'local' ? 'local'
690
791
  : storage === 'unknown' ? 'unknown'
691
792
  : descriptor === undefined ? 'absent'
692
793
  : installed !== undefined && descriptorsEqual(descriptor, installed) ? 'provider' : 'local'
693
- userBindingSources.set(name, { state, ...(state === 'provider' ? { entryId: id } : {}) })
694
- if (state !== 'provider') userBindingNames.delete(name)
794
+ mapSet(userBindingSources, name, { state, ...(state === 'provider' ? { entryId: id } : {}) })
795
+ if (state !== 'provider') mapDelete(userBindingNames, name)
695
796
  }
696
797
  }
697
798
 
698
799
  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)
800
+ const entry = mapGet(userBindingEntries, id)
801
+ for (let index = 0; index < entry.names.length; index += 1) {
802
+ const name = entry.names[index]
803
+ if (mapGet(userBindingNames, name) !== id) continue
804
+ const installed = mapGet(entry.descriptors, name)
805
+ mapDelete(userBindingNames, name)
806
+ if (setHas(installedGlobals, name)) {
807
+ const original = mapGet(installedGlobalOriginals, name)
706
808
  if (original.attachment === installed) original.descriptor = undefined
707
809
  } else if (descriptorsEqual(Object.getOwnPropertyDescriptor(context, name), installed)) delete context[name]
708
810
  // Lifecycle removal permits reattachment; explicit deletion retains absent.
709
- userBindingSources.delete(name)
811
+ mapDelete(userBindingSources, name)
710
812
  }
711
- userBindingEntries.delete(id)
813
+ mapDelete(userBindingEntries, id)
712
814
  }
713
815
 
714
816
  async function activatePerNameUserBindings(snapshot, shadowedNames, cwd, reusePolicy, initialFailures = []) {
715
817
  // 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 })
818
+ const existingNames = mapEntriesArray(userBindingNames)
819
+ for (let index = 0; index < existingNames.length; index += 1) {
820
+ const name = existingNames[index][0]
821
+ const entryId = existingNames[index][1]
822
+ if (mapHas(userBindingSources, name)) continue
823
+ mapSet(userBindingSources, name, { state: 'provider', entryId })
719
824
  // The legacy catalog includes actual setter writes as well as descriptor
720
825
  // changes. Do not reattach a restored local accessor by descriptor equality.
721
- if (shadowedNames.has(name) && !installedGlobals.has(name)) userBindingNames.delete(name)
826
+ if (setHas(shadowedNames, name) && !setHas(installedGlobals, name)) mapDelete(userBindingNames, name)
722
827
  }
723
828
  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]))
829
+ const blockedIds = setFromArray(mapArray(initialFailures, failure => failure.id))
830
+ const desired = mapFromArray(filterArray(snapshot?.entries ?? [], entry => !setHas(blockedIds, entry.id)), entry => [entry.id, entry])
726
831
  try {
727
- if (desired.size > 0 || retainedUserBindingRuntime) installDynamicNamespaceGlobals()
832
+ if (mapSize(desired) > 0 || retainedLegacyUserBindingRuntime) installDynamicNamespaceGlobals()
728
833
  } 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 }
834
+ const ids = mapKeysArray(userBindingEntries)
835
+ for (let index = 0; index < ids.length; index += 1) removePerNameUserBindingEntry(ids[index])
836
+ const failures = copyArray(initialFailures)
837
+ const desiredFailures = mapArray(mapKeysArray(desired), id => ({ id, error: messageOf(error) }))
838
+ for (let index = 0; index < desiredFailures.length; index += 1) appendArray(failures, desiredFailures[index])
839
+ return { activated: [], failures, error }
731
840
  }
732
- for (const [id, current] of userBindingEntries) {
733
- const next = desired.get(id)
841
+ const currentEntries = mapEntriesArray(userBindingEntries)
842
+ for (let index = 0; index < currentEntries.length; index += 1) {
843
+ const id = currentEntries[index][0]
844
+ const current = currentEntries[index][1]
845
+ const next = mapGet(desired, id)
734
846
  if (next === undefined || current.transform !== snapshot.transform || !(reusePolicy === LEGACY_USER_BINDINGS_REUSE_POLICY
735
847
  ? next.fingerprint === current.entry.fingerprint : userBindingImplementationMatches(next, current.entry))) {
736
848
  removePerNameUserBindingEntry(id)
737
849
  }
738
850
  }
739
851
  const activated = []
740
- const failures = [...initialFailures]
741
- for (const entry of desired.values()) {
742
- const current = userBindingEntries.get(entry.id)
852
+ const failures = copyArray(initialFailures)
853
+ const desiredEntries = mapValuesArray(desired)
854
+ for (let entryIndex = 0; entryIndex < desiredEntries.length; entryIndex += 1) {
855
+ const entry = desiredEntries[entryIndex]
856
+ const current = mapGet(userBindingEntries, entry.id)
743
857
  if (current !== undefined) {
744
858
  current.entry = entry
745
- activated.push(entry.id)
859
+ appendArray(activated, entry.id)
746
860
  continue
747
861
  }
748
862
  const names = entry.scope === 'namespace' ? [entry.name] : entry.symbols
749
- const previousSources = new Map(names.map(name => [name, userBindingSources.get(name)]))
863
+ const previousSources = mapFromArray(names, name => [name, mapGet(userBindingSources, name)])
750
864
  const installedNames = []
751
865
  let evaluated
752
866
  const activation = { id: entry.id, called: false, failed: false }
753
867
  try {
754
- if (entry.durability === 'volatile') markVolatile(`user binding ${JSON.stringify(entry.id)}: ${entry.volatileReason ?? 'non-replayable source'}`)
868
+ if (entry.durability === 'volatile') markVolatile(`user binding ${jsonStringify(entry.id)}: ${entry.volatileReason ?? 'non-replayable source'}`)
755
869
  evaluated = await userBindingActivationScope.run(activation, () => evaluateUserBinding(entry, cwd, snapshot.transform))
756
870
  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`)
871
+ for (let symbolIndex = 0; symbolIndex < entry.symbols.length; symbolIndex += 1) {
872
+ const symbol = entry.symbols[symbolIndex]
873
+ if (!Object.hasOwn(namespace, symbol)) throw new Error(`named export ${jsonStringify(symbol)} is unavailable after evaluation`)
759
874
  }
760
875
  const view = Object.create(null)
761
- for (const symbol of entry.symbols) Object.defineProperty(view, symbol, { enumerable: true, get: () => namespace[symbol] })
876
+ for (let symbolIndex = 0; symbolIndex < entry.symbols.length; symbolIndex += 1) {
877
+ const symbol = entry.symbols[symbolIndex]
878
+ Object.defineProperty(view, symbol, { enumerable: true, get: () => namespace[symbol] })
879
+ }
762
880
  Object.freeze(view)
763
881
  const descriptors = new Map()
764
- for (const name of names) {
882
+ for (let nameIndex = 0; nameIndex < names.length; nameIndex += 1) {
883
+ const name = names[nameIndex]
765
884
  const storage = userBindingRootStorage(name)
766
- const prior = userBindingSources.get(name)
885
+ const prior = mapGet(userBindingSources, name)
767
886
  if (storage === 'lexical' || storage === 'unknown') {
768
- userBindingSources.set(name, { state: storage === 'lexical' ? 'local' : 'unknown' })
769
- } else if (prior === undefined && shadowedNames.has(name)) {
887
+ mapSet(userBindingSources, name, { state: storage === 'lexical' ? 'local' : 'unknown' })
888
+ } else if (prior === undefined && setHas(shadowedNames, name)) {
770
889
  const descriptor = underlyingUserBindingDescriptor(name)
771
- userBindingSources.set(name, { state: descriptor === undefined ? 'unknown' : 'local' })
890
+ mapSet(userBindingSources, name, { state: descriptor === undefined ? 'unknown' : 'local' })
772
891
  }
773
- const source = userBindingSources.get(name)
892
+ const source = mapGet(userBindingSources, name)
774
893
  if (source !== undefined && source.state !== 'provider') continue
775
894
  const descriptor = Object.getOwnPropertyDescriptor(context, name)
776
895
  Object.defineProperty(context, name, {
@@ -783,43 +902,48 @@ async function activatePerNameUserBindings(snapshot, shadowedNames, cwd, reusePo
783
902
  if (receiver === context) recordUserBindingAssignment(name)
784
903
  },
785
904
  })
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 })
905
+ appendArray(installedNames, { name, descriptor })
906
+ mapSet(descriptors, name, Object.getOwnPropertyDescriptor(context, name))
907
+ mapSet(userBindingNames, name, entry.id)
908
+ mapSet(userBindingSources, name, { state: 'provider', entryId: entry.id })
790
909
  }
791
- userBindingEntries.set(entry.id, { entry, transform: snapshot.transform, moduleUrl: evaluated.moduleUrl, names, descriptors })
792
- retainedUserBindingRuntime = true
793
- activated.push(entry.id)
910
+ mapSet(userBindingEntries, entry.id, { entry, transform: snapshot.transform, moduleUrl: evaluated.moduleUrl, names, descriptors })
911
+ if (snapshot.transform !== USER_BINDING_TRANSFORM) retainedLegacyUserBindingRuntime = true
912
+ appendArray(activated, entry.id)
794
913
  } catch (error) {
795
914
  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)
915
+ if (activation.called) markVolatile(`failed user binding ${jsonStringify(entry.id)} issued a host call`)
916
+ for (let index = installedNames.length - 1; index >= 0; index--) {
917
+ const installed = installedNames[index]
918
+ mapDelete(userBindingNames, installed.name)
799
919
  if (installed.descriptor === undefined) delete context[installed.name]
800
920
  else Object.defineProperty(context, installed.name, installed.descriptor)
801
921
  }
802
- for (const [name, prior] of previousSources) {
803
- if (prior === undefined) userBindingSources.delete(name)
804
- else userBindingSources.set(name, prior)
922
+ const previousEntries = mapEntriesArray(previousSources)
923
+ for (let previousIndex = 0; previousIndex < previousEntries.length; previousIndex += 1) {
924
+ const name = previousEntries[previousIndex][0]
925
+ const prior = previousEntries[previousIndex][1]
926
+ if (prior === undefined) mapDelete(userBindingSources, name)
927
+ else mapSet(userBindingSources, name, prior)
805
928
  }
806
- if (evaluated !== undefined) userBindingModuleParents.delete(evaluated.moduleUrl)
807
- failures.push({ id: entry.id, error: messageOf(error) })
929
+ if (evaluated !== undefined) mapDelete(userBindingModuleParents, evaluated.moduleUrl)
930
+ appendArray(failures, { id: entry.id, error: messageOf(error) })
808
931
  }
809
932
  }
810
- if (userBindingEntries.size === 0 && !retainedUserBindingRuntime) restoreDynamicNamespaceGlobals()
933
+ if (mapSize(userBindingEntries) === 0 && !retainedLegacyUserBindingRuntime) restoreDynamicNamespaceGlobals()
811
934
  return { activated, failures }
812
935
  }
813
936
 
814
937
  function removeUserBindingEntry(id, shadowedNames) {
815
- const previous = userBindingEntries.get(id)
938
+ const previous = mapGet(userBindingEntries, id)
816
939
  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]
940
+ for (let index = 0; index < previous.names.length; index += 1) {
941
+ const name = previous.names[index]
942
+ if (mapGet(userBindingNames, name) !== id) continue
943
+ mapDelete(userBindingNames, name)
944
+ if (!setHas(shadowedNames, name)) delete context[name]
821
945
  }
822
- userBindingEntries.delete(id)
946
+ mapDelete(userBindingEntries, id)
823
947
  }
824
948
 
825
949
  function descriptorsEqual(left, right) {
@@ -833,12 +957,16 @@ function descriptorsEqual(left, right) {
833
957
  }
834
958
 
835
959
  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)
960
+ const entries = mapEntriesArray(userBindingEntries)
961
+ for (let entryIndex = 0; entryIndex < entries.length; entryIndex += 1) {
962
+ const id = entries[entryIndex][0]
963
+ const entry = entries[entryIndex][1]
964
+ for (let nameIndex = 0; nameIndex < entry.names.length; nameIndex += 1) {
965
+ const name = entry.names[nameIndex]
966
+ if (mapGet(userBindingNames, name) !== id) continue
967
+ const installed = mapGet(entry.descriptors, name)
840
968
  const current = Object.getOwnPropertyDescriptor(context, name)
841
- if (!descriptorsEqual(current, installed)) shadowedNames.add(name)
969
+ if (!descriptorsEqual(current, installed)) setAdd(shadowedNames, name)
842
970
  }
843
971
  }
844
972
  }
@@ -847,15 +975,18 @@ async function evaluateUserBinding(entry, cwd, transform) {
847
975
  if (typeof cwd !== 'string' || !isAbsolute(cwd)) {
848
976
  throw new Error('user binding activation requires an absolute storage directory')
849
977
  }
850
- const prepared = compileStatefulModule(entry.source, { transform })
978
+ const prepared = compileStatefulModule(entry.source, {
979
+ transform,
980
+ programBindings: transform === USER_BINDING_TRANSFORM,
981
+ })
851
982
  const url = `data:text/javascript,${encodeURIComponent(prepared.code)}#ptc-plus-${entry.fingerprint}-${++nextUserBindingModuleId}`
852
- userBindingModuleParents.set(url, pathToFileURL(resolve(cwd, 'bindings.json')).href)
983
+ mapSet(userBindingModuleParents, url, pathToFileURL(resolve(cwd, 'bindings.json')).href)
853
984
  userModuleCompilation.mark(url, { transform, compiled: true, moduleInterface: prepared.moduleInterface, sourceRegions: prepared.sourceRegions })
854
985
  try {
855
986
  return { namespace: transform === LEGACY_USER_BINDING_TRANSFORM ? await import(url)
856
987
  : await managedModuleImport(url, url), moduleUrl: url }
857
988
  } catch (error) {
858
- userBindingModuleParents.delete(url)
989
+ mapDelete(userBindingModuleParents, url)
859
990
  throw error
860
991
  }
861
992
  }
@@ -865,22 +996,31 @@ function userBindingImplementationMatches(left, right) {
865
996
  && left.scope === right.scope
866
997
  && (left.scope !== 'namespace' || left.name === right.name)
867
998
  && left.symbols.length === right.symbols.length
868
- && left.symbols.every((symbol, index) => symbol === right.symbols[index])
999
+ && everyArray(left.symbols, (symbol, index) => symbol === right.symbols[index])
869
1000
  }
870
1001
 
871
1002
  async function activateUserBindings(snapshot, shadowedNames, cwd, reusePolicy, initialFailures = []) {
872
1003
  reconcileUserBindingShadows(shadowedNames)
873
- const blockedIds = new Set(initialFailures.map(failure => failure.id))
874
- const desired = new Map((snapshot?.entries ?? [])
875
- .filter((entry) => {
1004
+ const blockedIds = setFromArray(mapArray(initialFailures, failure => failure.id))
1005
+ const desired = mapFromArray(filterArray(snapshot?.entries ?? [], (entry) => {
876
1006
  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)
1007
+ return !setHas(blockedIds, entry.id) && !someArray(names, name => setHas(shadowedNames, name))
1008
+ }), entry => [entry.id, entry])
1009
+ try {
1010
+ if (mapSize(desired) > 0 || retainedLegacyUserBindingRuntime) installDynamicNamespaceGlobals()
1011
+ } catch (error) {
1012
+ const ids = mapKeysArray(userBindingEntries)
1013
+ for (let index = 0; index < ids.length; index += 1) removeUserBindingEntry(ids[index], shadowedNames)
1014
+ const failures = copyArray(initialFailures)
1015
+ const desiredFailures = mapArray(mapKeysArray(desired), id => ({ id, error: messageOf(error) }))
1016
+ for (let index = 0; index < desiredFailures.length; index += 1) appendArray(failures, desiredFailures[index])
1017
+ return { activated: [], failures, error }
1018
+ }
1019
+ const existingIds = mapKeysArray(userBindingEntries)
1020
+ for (let index = 0; index < existingIds.length; index += 1) {
1021
+ const id = existingIds[index]
1022
+ const current = mapGet(userBindingEntries, id)
1023
+ const next = mapGet(desired, id)
884
1024
  // Historical cells retain fingerprint-based resets, including presentation edits.
885
1025
  const reusable = next !== undefined && current.transform === snapshot.transform && (reusePolicy === LEGACY_USER_BINDINGS_REUSE_POLICY
886
1026
  ? next.fingerprint === current.entry.fingerprint
@@ -890,12 +1030,14 @@ async function activateUserBindings(snapshot, shadowedNames, cwd, reusePolicy, i
890
1030
  }
891
1031
  }
892
1032
  const activated = []
893
- const failures = [...initialFailures]
894
- for (const entry of desired.values()) {
895
- const current = userBindingEntries.get(entry.id)
1033
+ const failures = copyArray(initialFailures)
1034
+ const desiredEntries = mapValuesArray(desired)
1035
+ for (let entryIndex = 0; entryIndex < desiredEntries.length; entryIndex += 1) {
1036
+ const entry = desiredEntries[entryIndex]
1037
+ const current = mapGet(userBindingEntries, entry.id)
896
1038
  if (current !== undefined) {
897
1039
  current.entry = entry
898
- activated.push(entry.id)
1040
+ appendArray(activated, entry.id)
899
1041
  continue
900
1042
  }
901
1043
  let evaluated
@@ -903,22 +1045,24 @@ async function activateUserBindings(snapshot, shadowedNames, cwd, reusePolicy, i
903
1045
  const activation = { id: entry.id, called: false, failed: false }
904
1046
  try {
905
1047
  if (entry.durability === 'volatile') {
906
- markVolatile(`user binding ${JSON.stringify(entry.id)}: ${entry.volatileReason ?? 'non-replayable source'}`)
1048
+ markVolatile(`user binding ${jsonStringify(entry.id)}: ${entry.volatileReason ?? 'non-replayable source'}`)
907
1049
  }
908
1050
  evaluated = await userBindingActivationScope.run(
909
1051
  activation,
910
1052
  () => evaluateUserBinding(entry, cwd, snapshot.transform),
911
1053
  )
912
1054
  const namespace = evaluated.namespace
913
- for (const symbol of entry.symbols) {
1055
+ for (let symbolIndex = 0; symbolIndex < entry.symbols.length; symbolIndex += 1) {
1056
+ const symbol = entry.symbols[symbolIndex]
914
1057
  if (!Object.hasOwn(namespace, symbol)) {
915
- throw new Error(`named export ${JSON.stringify(symbol)} is unavailable after evaluation`)
1058
+ throw new Error(`named export ${jsonStringify(symbol)} is unavailable after evaluation`)
916
1059
  }
917
1060
  }
918
1061
  const names = []
919
1062
  if (entry.scope === 'namespace') {
920
1063
  const view = Object.create(null)
921
- for (const symbol of entry.symbols) {
1064
+ for (let symbolIndex = 0; symbolIndex < entry.symbols.length; symbolIndex += 1) {
1065
+ const symbol = entry.symbols[symbolIndex]
922
1066
  Object.defineProperty(view, symbol, {
923
1067
  enumerable: true,
924
1068
  get: () => namespace[symbol],
@@ -940,14 +1084,15 @@ async function activateUserBindings(snapshot, shadowedNames, cwd, reusePolicy, i
940
1084
  })
941
1085
  },
942
1086
  })
943
- installedNames.push({
1087
+ appendArray(installedNames, {
944
1088
  name: entry.name,
945
1089
  descriptor,
946
1090
  })
947
- userBindingNames.set(entry.name, entry.id)
948
- names.push(entry.name)
1091
+ mapSet(userBindingNames, entry.name, entry.id)
1092
+ appendArray(names, entry.name)
949
1093
  } else {
950
- for (const symbol of entry.symbols) {
1094
+ for (let symbolIndex = 0; symbolIndex < entry.symbols.length; symbolIndex += 1) {
1095
+ const symbol = entry.symbols[symbolIndex]
951
1096
  const descriptor = Object.getOwnPropertyDescriptor(context, symbol)
952
1097
  Object.defineProperty(context, symbol, {
953
1098
  configurable: true,
@@ -963,48 +1108,51 @@ async function activateUserBindings(snapshot, shadowedNames, cwd, reusePolicy, i
963
1108
  })
964
1109
  },
965
1110
  })
966
- installedNames.push({ name: symbol, descriptor })
967
- userBindingNames.set(symbol, entry.id)
968
- names.push(symbol)
1111
+ appendArray(installedNames, { name: symbol, descriptor })
1112
+ mapSet(userBindingNames, symbol, entry.id)
1113
+ appendArray(names, symbol)
969
1114
  }
970
1115
  }
971
- userBindingEntries.set(entry.id, {
1116
+ mapSet(userBindingEntries, entry.id, {
972
1117
  entry,
973
1118
  transform: snapshot.transform,
974
1119
  moduleUrl: evaluated.moduleUrl,
975
1120
  names,
976
- descriptors: new Map(names.map(name => [name, Object.getOwnPropertyDescriptor(context, name)])),
1121
+ descriptors: mapFromArray(names, name => [name, Object.getOwnPropertyDescriptor(context, name)]),
977
1122
  })
978
- retainedUserBindingRuntime = true
979
- activated.push(entry.id)
1123
+ if (snapshot.transform !== USER_BINDING_TRANSFORM) retainedLegacyUserBindingRuntime = true
1124
+ appendArray(activated, entry.id)
980
1125
  } catch (error) {
981
1126
  activation.failed = true
982
1127
  if (activation.called) {
983
- markVolatile(`failed user binding ${JSON.stringify(entry.id)} issued a host call`)
1128
+ markVolatile(`failed user binding ${jsonStringify(entry.id)} issued a host call`)
984
1129
  }
985
- for (const installed of installedNames.reverse()) {
986
- userBindingNames.delete(installed.name)
1130
+ for (let index = installedNames.length - 1; index >= 0; index--) {
1131
+ const installed = installedNames[index]
1132
+ mapDelete(userBindingNames, installed.name)
987
1133
  if (installed.descriptor === undefined) delete context[installed.name]
988
1134
  else Object.defineProperty(context, installed.name, installed.descriptor)
989
1135
  }
990
- if (evaluated !== undefined) userBindingModuleParents.delete(evaluated.moduleUrl)
1136
+ if (evaluated !== undefined) mapDelete(userBindingModuleParents, evaluated.moduleUrl)
991
1137
  removeUserBindingEntry(entry.id, shadowedNames)
992
- failures.push({
1138
+ appendArray(failures, {
993
1139
  id: entry.id,
994
1140
  error: messageOf(error),
995
1141
  })
996
1142
  }
997
1143
  }
998
- if (userBindingEntries.size === 0 && !retainedUserBindingRuntime) restoreDynamicNamespaceGlobals()
1144
+ if (mapSize(userBindingEntries) === 0 && !retainedLegacyUserBindingRuntime) restoreDynamicNamespaceGlobals()
999
1145
  return { activated, failures }
1000
1146
  }
1001
1147
 
1002
1148
  async function closeExecution(execution) {
1003
1149
  activeRun = undefined
1004
1150
  execution.open = false
1005
- await Promise.all([...pending.values()]
1006
- .filter(call => call.runId === execution.id)
1007
- .map(call => call.settled))
1151
+ const settling = []
1152
+ mapForEach(pending, call => {
1153
+ if (call.runId === execution.id) appendArray(settling, call.settled)
1154
+ })
1155
+ for (let index = 0; index < settling.length; index++) await settling[index]
1008
1156
  }
1009
1157
 
1010
1158
  function failureOutcome(error, phase, program) {
@@ -1031,7 +1179,7 @@ function failureOutcome(error, phase, program) {
1031
1179
 
1032
1180
  function observeBindings(names) {
1033
1181
  const observation = valueObserver.observe(names)
1034
- return { ...observation, entries: observation.entries.map(entry => {
1182
+ return { ...observation, entries: mapArray(observation.entries, entry => {
1035
1183
  const local = statefulRoots.localValue(entry.name)
1036
1184
  return local === undefined ? entry : { name: entry.name, ...previewBindingValue(local.value) }
1037
1185
  }) }
@@ -1052,7 +1200,9 @@ function refreshLegacyPublication(name) {
1052
1200
  }
1053
1201
 
1054
1202
  function sendCompletion(message, execution, userBindings, committedRedeclarations, outcome) {
1055
- for (const name of execution.nativePublications?.keys() ?? []) refreshLegacyPublication(name)
1203
+ const publicationNames = execution.nativePublications === undefined
1204
+ ? [] : mapKeysArray(execution.nativePublications)
1205
+ for (let index = 0; index < publicationNames.length; index += 1) refreshLegacyPublication(publicationNames[index])
1056
1206
  const shadowedNames = execution.legacyAssignedUserBindingNames
1057
1207
  const perName = message.userBindingsShadowPolicy === LIVE_USER_BINDINGS_SHADOW_POLICY
1058
1208
  if (perName) reconcileUserBindingNames()
@@ -1063,15 +1213,15 @@ function sendCompletion(message, execution, userBindings, committedRedeclaration
1063
1213
  id: message.id,
1064
1214
  logs: execution.logs,
1065
1215
  ...completionDurability(execution),
1066
- committedRedeclarations: [...committedRedeclarations],
1216
+ committedRedeclarations: setValuesArray(committedRedeclarations),
1067
1217
  ...(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 } : {}),
1218
+ ...(perName ? { userBindingNames: normalizeUserBindingNames(mapArray(mapEntriesArray(userBindingSources), entry => ({
1219
+ name: entry[0], state: entry[1].state, ...(entry[1].state === 'provider' ? { entryId: entry[1].entryId } : {}),
1070
1220
  }))) } : {}),
1071
1221
  ...(message.userBindings === undefined ? {} : {
1072
1222
  activatedUserBindings: userBindings.activated,
1073
1223
  userBindingFailures: userBindings.failures,
1074
- ...(perName ? {} : { shadowedUserBindings: [...shadowedNames] }),
1224
+ ...(perName ? {} : { shadowedUserBindings: setValuesArray(shadowedNames) }),
1075
1225
  }),
1076
1226
  ...outcome,
1077
1227
  observing: names.length > 0,
@@ -1085,11 +1235,11 @@ function sendCompletion(message, execution, userBindings, committedRedeclaration
1085
1235
  async function runCell(message) {
1086
1236
  if (activeExecution !== undefined) throw new Error('kernel received overlapping cells')
1087
1237
  activeRun = message.id
1088
- installBindings(message)
1089
1238
  const execution = {
1090
1239
  id: message.id,
1091
1240
  exceptionOrigins: createExceptionOriginScope(),
1092
1241
  languageSemantics: message.languageSemantics,
1242
+ moduleTransform: message.moduleTransform,
1093
1243
  userBindingsShadowPolicy: message.userBindingsShadowPolicy,
1094
1244
  importBindingNamespaces: message.importBindingNamespaces,
1095
1245
  legacyAssignedUserBindingNames: new Set(),
@@ -1099,10 +1249,9 @@ async function runCell(message) {
1099
1249
  logBytes: 2,
1100
1250
  maxOutputBytes: message.maxOutputBytes,
1101
1251
  valueLimits: message.valueLimits,
1102
- durability: message.durability === 'volatile' || pendingVolatileReason !== undefined ? 'volatile' : 'durable',
1103
- volatileReason: pendingVolatileReason,
1252
+ durability: message.durability === 'volatile' ? 'volatile' : 'durable',
1253
+ volatileReason: message.volatileReason,
1104
1254
  }
1105
- pendingVolatileReason = undefined
1106
1255
  activeExecution = execution
1107
1256
  const cellGlobals = []
1108
1257
  const committedRedeclarations = new Set()
@@ -1113,27 +1262,28 @@ async function runCell(message) {
1113
1262
  let userBindings = { activated: [], failures: [] }
1114
1263
  try {
1115
1264
  completion = await logScope.run(execution, async () => {
1265
+ const namespaceError = installBindings(message)
1116
1266
  Object.defineProperty(context, message.returnSignal, {
1117
1267
  configurable: true,
1118
1268
  value: CellReturn,
1119
1269
  })
1120
- cellGlobals.push(message.returnSignal)
1270
+ appendArray(cellGlobals, message.returnSignal)
1121
1271
  Object.defineProperty(context, message.commitSignal, {
1122
1272
  configurable: true,
1123
1273
  value(name) {
1124
1274
  setAdd(committedRedeclarations, name)
1125
1275
  },
1126
1276
  })
1127
- cellGlobals.push(message.commitSignal)
1277
+ appendArray(cellGlobals, message.commitSignal)
1128
1278
  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)
1279
+ const nativeLexicals = setFromArray(message.rootBindings.legacyNativeLexicals)
1280
+ const legacyStorage = mapFromArray(filterArray(message.rootBindings.legacyLexicals,
1281
+ name => !statefulRoots.has(name)), name => [name, rootBindingStorage(name, setHas(nativeLexicals, name))])
1282
+ const legacyLexicals = filterArray(message.rootBindings.legacyLexicals, name => mapGet(legacyStorage, name) === 'lexical')
1283
+ const legacyNative = setFromArray(message.rootBindings.legacyNative)
1284
+ const legacyWritable = filterArray(message.rootBindings.legacyWritable, name => {
1285
+ const storage = mapGet(legacyStorage, name)
1286
+ return storage === 'lexical' || setHas(legacyNative, name)
1137
1287
  && (storage === 'local' || storage === 'property') && writableRootProperty(name)
1138
1288
  })
1139
1289
  Object.defineProperty(context, message.rootRuntimeName, {
@@ -1141,28 +1291,32 @@ async function runCell(message) {
1141
1291
  value: statefulRoots.begin({ ...message.rootBindings,
1142
1292
  legacyLexicals,
1143
1293
  legacyWritable,
1144
- legacyObjects: legacyWritable.filter(name => legacyStorage.get(name) !== 'lexical'),
1294
+ legacyObjects: filterArray(legacyWritable, name => mapGet(legacyStorage, name) !== 'lexical'),
1145
1295
  committed(name) {
1146
1296
  setDelete(committedRedeclarations, name)
1147
1297
  setAdd(committedRedeclarations, name)
1148
1298
  } }),
1149
1299
  })
1150
- cellGlobals.push(message.rootRuntimeName)
1300
+ appendArray(cellGlobals, message.rootRuntimeName)
1151
1301
  }
1152
1302
  const activate = message.userBindingsShadowPolicy === LIVE_USER_BINDINGS_SHADOW_POLICY
1153
1303
  ? activatePerNameUserBindings : activateUserBindings
1154
1304
  userBindings = await activate(
1155
1305
  message.userBindings,
1156
- new Set(message.shadowedUserBindingNames ?? []),
1306
+ setFromArray(message.shadowedUserBindingNames ?? []),
1157
1307
  message.userBindingsCwd,
1158
1308
  message.userBindingsReusePolicy,
1159
1309
  message.userBindingFailures,
1160
1310
  )
1161
1311
  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}`)
1312
+ if (namespaceError !== undefined) throw namespaceError
1313
+ for (let failureIndex = 0; failureIndex < userBindings.failures.length; failureIndex += 1) {
1314
+ const failure = userBindings.failures[failureIndex]
1315
+ appendText(execution, `Global binding ${jsonStringify(failure.id)} was not activated: ${failure.error}`)
1164
1316
  }
1165
- for (const load of message.moduleLoads ?? []) {
1317
+ const moduleLoads = message.moduleLoads ?? []
1318
+ for (let loadIndex = 0; loadIndex < moduleLoads.length; loadIndex += 1) {
1319
+ const load = moduleLoads[loadIndex]
1166
1320
  let namespace
1167
1321
  try {
1168
1322
  namespace = (await loadStaticModule(load)).namespace
@@ -1174,14 +1328,21 @@ async function runCell(message) {
1174
1328
  configurable: true,
1175
1329
  value: namespace,
1176
1330
  })
1177
- cellGlobals.push(load.global)
1331
+ appendArray(cellGlobals, load.global)
1178
1332
  }
1179
1333
  }
1180
1334
  // Preload failure leaves prior aliases authoritative. Once evaluation
1181
1335
  // starts, new slots must prove their own initialization, including TDZ.
1182
1336
  execution.importBindingNamespaces = message.preparedImportBindingNamespaces
1183
- execution.nativePublications = new Map((message.moduleLoads ?? []).flatMap(load =>
1184
- (load.nativePublications ?? []).map(publication => [publication.name, publication])))
1337
+ execution.nativePublications = new Map()
1338
+ for (let loadIndex = 0; loadIndex < moduleLoads.length; loadIndex += 1) {
1339
+ const load = moduleLoads[loadIndex]
1340
+ const publications = load.nativePublications ?? []
1341
+ for (let publicationIndex = 0; publicationIndex < publications.length; publicationIndex += 1) {
1342
+ const publication = publications[publicationIndex]
1343
+ mapSet(execution.nativePublications, publication.name, publication)
1344
+ }
1345
+ }
1185
1346
  return execution.exceptionOrigins.run(() => evaluate(message.program, message.returnSignal, message.asyncCompletion))
1186
1347
  })
1187
1348
  } catch (error) {
@@ -1205,7 +1366,7 @@ async function runCell(message) {
1205
1366
  }
1206
1367
  sendCompletion(message, execution, userBindings, committedRedeclarations, outcome)
1207
1368
  } finally {
1208
- for (const name of cellGlobals) delete context[name]
1369
+ for (let index = 0; index < cellGlobals.length; index += 1) delete context[cellGlobals[index]]
1209
1370
  activeRun = undefined
1210
1371
  activeExecution = undefined
1211
1372
  execution.exceptionOrigins.close()
@@ -1221,6 +1382,7 @@ parentPort.on('message', (message) => {
1221
1382
  input.destroy()
1222
1383
  output.destroy()
1223
1384
  errorDomain.removeAllListeners('error')
1385
+ process.removeListener('uncaughtException', handleUncaughtException)
1224
1386
  channel.close()
1225
1387
  parentPort.postMessage({ type: WORKER_SHUTDOWN_ACKNOWLEDGEMENT })
1226
1388
  parentPort.close()
@@ -1238,9 +1400,9 @@ channel.on('message', (message) => {
1238
1400
  return
1239
1401
  }
1240
1402
  if (message?.type === 'reply') {
1241
- const call = pending.get(message.id)
1403
+ const call = mapGet(pending, message.id)
1242
1404
  if (call === undefined || call.runId !== message.runId) return
1243
- pending.delete(message.id)
1405
+ mapDelete(pending, message.id)
1244
1406
  try {
1245
1407
  if (message.ok) call.resolve(decodeValue(message.value, call.valueLimits))
1246
1408
  else if (call.BoundError === undefined) {