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
@@ -14,7 +14,7 @@ export { commonJsExportEvidence, linkCommonJsEvidenceSource } from './commonjs-s
14
14
  import { COMMONJS_PARAMETERS } from './compiler-scope-facts.js'
15
15
  import {
16
16
  LEGACY_USER_BINDING_TRANSFORM, USER_BINDING_TRANSFORM, PROTECTED_MODULE_TRANSFORM,
17
- supportedUserBindingTransform, transformTypeScriptSource,
17
+ isStatefulUserBindingTransform, supportedUserBindingTransform, transformTypeScriptSource,
18
18
  } from './typescript-transform.js'
19
19
 
20
20
  const topLevelRegion = region => region.kind === 'program' || region.top === true
@@ -162,7 +162,7 @@ function commonJsBodyRange(source) {
162
162
 
163
163
  /** Module activations share lexical updates while retaining the native linker. */
164
164
  export function compileStatefulModule(source, {
165
- transform = USER_BINDING_TRANSFORM, target = 'module', url, nativeUsing,
165
+ transform = USER_BINDING_TRANSFORM, target = 'module', url, nativeUsing, programBindings = false,
166
166
  } = {}) {
167
167
  if (transform !== PROTECTED_MODULE_TRANSFORM && !supportedUserBindingTransform(transform)) {
168
168
  throw new TypeError('user binding snapshot cannot prove its historical TypeScript transform')
@@ -188,14 +188,14 @@ export function compileStatefulModule(source, {
188
188
  nativeJavaScript = marked.nativeJavaScript
189
189
  mapped = marked
190
190
  if (!nativeJavaScript) mapped = normalizeTypeScriptValues(mapped.code, mapped.sourceMap, target)
191
- if (target !== 'commonjs' && (transform === USER_BINDING_TRANSFORM
191
+ if (target !== 'commonjs' && (isStatefulUserBindingTransform(transform)
192
192
  || transform === PROTECTED_MODULE_TRANSFORM && hasModuleResources(mapped.code))) {
193
193
  mapped = normalizeModuleExports(mapped.code, mapped.sourceMap)
194
194
  }
195
195
  }
196
196
  const commonJsSource = target === 'commonjs'
197
197
  ? commonJsExportEvidence(mapped.code, { legacy: transform === LEGACY_USER_BINDING_TRANSFORM }) : undefined
198
- const links = transform === USER_BINDING_TRANSFORM && target === 'module'
198
+ const links = isStatefulUserBindingTransform(transform) && target === 'module'
199
199
  ? moduleLinkPlan(mapped.code) : undefined
200
200
  let normalized = transform === LEGACY_USER_BINDING_TRANSFORM ? mapped
201
201
  : normalizeStatefulScopes(mapped.code, mapped.sourceMap, { target, moduleImport: links?.reader, nativeJavaScript, nativeUsing,
@@ -222,6 +222,7 @@ export function compileStatefulModule(source, {
222
222
  sourceRegions: normalized.sourceRegions,
223
223
  callableCollector: createRegionCallableCollector(callableSources),
224
224
  module: true, compactOutput: true, importOperation: normalized.importOperation, sourceType, originalSource: source,
225
+ logicalRoots: programBindings,
225
226
  bindings: normalized.dynamicBindings, privateBindings: normalized.privateBindings,
226
227
  internalBindings: normalized.internalBindings, parserPlugins: CELL_PARSER_PLUGINS,
227
228
  }) }
@@ -1,12 +1,42 @@
1
1
  export const LEGACY_USER_BINDING_TRANSFORM = 'amaro@1.1.11'
2
- export const USER_BINDING_TRANSFORM = 'stateful-module-v1+amaro@1.1.11'
2
+ export const PREVIOUS_USER_BINDING_TRANSFORM = 'stateful-module-v1+amaro@1.1.11'
3
+ export const USER_BINDING_TRANSFORM = 'stateful-module-v2+amaro@1.1.11'
3
4
  export const PROTECTED_MODULE_TRANSFORM = 'protected-module-v1+amaro@1.1.11'
5
+ export const MODULE_TRANSFORMS = new Set([
6
+ LEGACY_USER_BINDING_TRANSFORM,
7
+ PREVIOUS_USER_BINDING_TRANSFORM,
8
+ USER_BINDING_TRANSFORM,
9
+ PROTECTED_MODULE_TRANSFORM,
10
+ ])
11
+
12
+ export function isStatefulUserBindingTransform(transform) {
13
+ return transform === USER_BINDING_TRANSFORM || transform === PREVIOUS_USER_BINDING_TRANSFORM
14
+ }
4
15
 
5
16
  export function moduleTransformForLanguage(languageSemantics) {
6
17
  return languageSemantics === 'stateful-v1' ? USER_BINDING_TRANSFORM
7
18
  : languageSemantics === 'protected-v1' ? PROTECTED_MODULE_TRANSFORM : LEGACY_USER_BINDING_TRANSFORM
8
19
  }
9
20
 
21
+ export function historicalModuleTransformForLanguage(languageSemantics) {
22
+ return languageSemantics === 'stateful-v1' ? PREVIOUS_USER_BINDING_TRANSFORM
23
+ : languageSemantics === 'protected-v1' ? PROTECTED_MODULE_TRANSFORM : LEGACY_USER_BINDING_TRANSFORM
24
+ }
25
+
26
+ export function moduleTransformMatchesLanguage(transform, languageSemantics) {
27
+ return languageSemantics === 'stateful-v1' ? isStatefulUserBindingTransform(transform)
28
+ : languageSemantics === 'protected-v1' ? transform === PROTECTED_MODULE_TRANSFORM
29
+ : languageSemantics === 'legacy-v1' ? transform === LEGACY_USER_BINDING_TRANSFORM : false
30
+ }
31
+
32
+ export function normalizeModuleTransform(transform, languageSemantics) {
33
+ if (!MODULE_TRANSFORMS.has(transform)) throw new TypeError('invalid dsh-ptc-plus journal module transform')
34
+ if (!moduleTransformMatchesLanguage(transform, languageSemantics)) {
35
+ throw new TypeError('dsh-ptc-plus journal module transform does not match its language semantics')
36
+ }
37
+ return transform
38
+ }
39
+
10
40
  export function supportedUserBindingTransform(transform) {
11
- return transform === USER_BINDING_TRANSFORM || transform === LEGACY_USER_BINDING_TRANSFORM
41
+ return isStatefulUserBindingTransform(transform) || transform === LEGACY_USER_BINDING_TRANSFORM
12
42
  }
@@ -1,14 +1,22 @@
1
1
  /** Shared structural guards for closed PTC metadata boundaries. */
2
2
 
3
+ import { runtimeIntrinsics as internal } from './runtime-intrinsics.js'
4
+
5
+ const { TypeError, isArray, reflectOwnKeys, objectGetOwnPropertyDescriptor,
6
+ objectHasOwn, objectPropertyIsEnumerable, objectFreeze, trimString, toString,
7
+ Set, setHas, setAdd, setSize, appendArray, popArray } = internal
8
+
3
9
  export function isRecord(value) {
4
- return value !== null && typeof value === 'object' && !Array.isArray(value)
10
+ return value !== null && typeof value === 'object' && !isArray(value)
5
11
  }
6
12
 
7
13
  export function assertOwnFields(value, allowed, label) {
8
- for (const key of Reflect.ownKeys(value)) {
9
- if (typeof key !== 'string' || !allowed.has(key)
10
- || !Object.prototype.propertyIsEnumerable.call(value, key)) {
11
- throw new TypeError(`invalid ${label} field ${String(key)}`)
14
+ const keys = reflectOwnKeys(value)
15
+ for (let index = 0; index < keys.length; index += 1) {
16
+ const key = keys[index]
17
+ if (typeof key !== 'string' || !setHas(allowed, key)
18
+ || !objectPropertyIsEnumerable(value, key)) {
19
+ throw new TypeError(`invalid ${label} field ${toString(key)}`)
12
20
  }
13
21
  }
14
22
  }
@@ -16,7 +24,7 @@ export function assertOwnFields(value, allowed, label) {
16
24
  export function assertFields(value, allowed, label) {
17
25
  if (!isRecord(value)) throw new TypeError(`invalid ${label}`)
18
26
  assertOwnFields(value, allowed, label)
19
- if (Reflect.ownKeys(value).length !== allowed.size) throw new TypeError(`invalid ${label} fields`)
27
+ if (reflectOwnKeys(value).length !== setSize(allowed)) throw new TypeError(`invalid ${label} fields`)
20
28
  }
21
29
 
22
30
  export function record(value, label) {
@@ -25,7 +33,7 @@ export function record(value, label) {
25
33
  }
26
34
 
27
35
  export function text(value, label) {
28
- if (typeof value !== 'string' || value.trim().length === 0) {
36
+ if (typeof value !== 'string' || trimString(value).length === 0) {
29
37
  throw new TypeError(`ptc-plus: ${label} must be a non-empty string`)
30
38
  }
31
39
  return value
@@ -37,17 +45,18 @@ export function deepFreeze(value) {
37
45
  const pending = [value]
38
46
  const seen = new Set()
39
47
  while (pending.length > 0) {
40
- const current = pending.pop()
41
- if (seen.has(current)) continue
42
- seen.add(current)
43
- for (const key of Reflect.ownKeys(current)) {
44
- const descriptor = Object.getOwnPropertyDescriptor(current, key)
45
- if (descriptor !== undefined && Object.hasOwn(descriptor, 'value')) {
48
+ const current = popArray(pending)
49
+ if (setHas(seen, current)) continue
50
+ setAdd(seen, current)
51
+ const keys = reflectOwnKeys(current)
52
+ for (let index = 0; index < keys.length; index += 1) {
53
+ const descriptor = objectGetOwnPropertyDescriptor(current, keys[index])
54
+ if (descriptor !== undefined && objectHasOwn(descriptor, 'value')) {
46
55
  const child = descriptor.value
47
- if (child !== null && typeof child === 'object') pending.push(child)
56
+ if (child !== null && typeof child === 'object') appendArray(pending, child)
48
57
  }
49
58
  }
50
- Object.freeze(current)
59
+ objectFreeze(current)
51
60
  }
52
61
  return value
53
62
  }
@@ -1,5 +1,5 @@
1
1
  import { types } from 'node:util'
2
- import { runInContext } from 'node:vm'
2
+ import { isContext, runInContext, runInThisContext } from 'node:vm'
3
3
  import { parse } from 'acorn'
4
4
  import { randomUUID } from 'node:crypto'
5
5
  import { walkAst, SKIP_AST_CHILDREN } from './ast-traversal.js'
@@ -16,6 +16,9 @@ const now = Date.now
16
16
  const bigintText = BigInt.prototype.toString
17
17
  const BIGINT_LIMIT = 10n ** 128n
18
18
  const unreadable = () => ({ status: 'unreadable', text: '', truncated: false })
19
+ const runInRealm = (source, context, options) => isContext(context)
20
+ ? runInContext(source, context, options)
21
+ : runInThisContext(source, options)
19
22
 
20
23
  function primitiveText(value) {
21
24
  if (typeof value === 'string') return stringify(value.slice(0, MAX_TEXT))
@@ -56,7 +59,7 @@ export async function supportsAwaitLexicals(context, evaluate) {
56
59
  try {
57
60
  await evaluate(`let ${names[0]} = await Promise.resolve(41); const ${names[1]} = 42;`)
58
61
  return names.every((name, index) => ownDescriptor(context, name) === undefined
59
- && runInContext(name, context, { timeout: 25 }) === index + 41)
62
+ && runInRealm(name, context, { timeout: 25 }) === index + 41)
60
63
  } catch {
61
64
  return false
62
65
  } finally {
@@ -97,7 +100,7 @@ export function createReplValueObserver(context, { awaitLexicals = false } = {})
97
100
  if (lexicals.has(name)) {
98
101
  // Lexical storage shadows global properties, even while uninitialized.
99
102
  if (/^[$_\p{ID_Start}][$\u200c\u200d\p{ID_Continue}]*$/u.test(name) && now() - at < 100) {
100
- preview = previewBindingValue(runInContext(name, context, { timeout: 25, displayErrors: false }))
103
+ preview = previewBindingValue(runInRealm(name, context, { timeout: 25, displayErrors: false }))
101
104
  }
102
105
  } else if (globals.has(name)) {
103
106
  const descriptor = ownDescriptor(context, name)
@@ -0,0 +1,101 @@
1
+ import { compilerDescriptors } from './compiler-descriptors.js'
2
+
3
+ const uncurry = Function.prototype.bind.bind(Function.prototype.call)
4
+ const mapKeys = uncurry(Map.prototype.keys)
5
+ const mapIteratorNext = uncurry(Object.getPrototypeOf(new Map().keys()).next)
6
+ const weakMapGet = uncurry(WeakMap.prototype.get)
7
+ const weakMapSet = uncurry(WeakMap.prototype.set)
8
+
9
+ function appendArray(target, value) {
10
+ compilerDescriptors.defineProperty(target, target.length,
11
+ { value, writable: true, enumerable: true, configurable: true })
12
+ return target
13
+ }
14
+
15
+ function copyArray(source, start = 0, end = source.length, target = []) {
16
+ for (let index = start; index < end; index++) appendArray(target, source[index])
17
+ return target
18
+ }
19
+
20
+ /** Host-private operations captured before any owned realm executes user code. */
21
+ export const runtimeIntrinsics = {
22
+ Object: compilerDescriptors.Object,
23
+ Reflect: {
24
+ apply: Reflect.apply,
25
+ construct: Reflect.construct,
26
+ deleteProperty: Reflect.deleteProperty,
27
+ defineProperty: compilerDescriptors.reflectDefineProperty,
28
+ has: Reflect.has,
29
+ ownKeys: Reflect.ownKeys,
30
+ },
31
+ Error, TypeError, Array, Map, Set, WeakMap, WeakSet, RegExp, Proxy,
32
+ objectCreate: Object.create,
33
+ objectDefineProperty: Object.defineProperty,
34
+ objectEntries: Object.entries,
35
+ objectFreeze: Object.freeze,
36
+ objectGetOwnPropertyDescriptor: Object.getOwnPropertyDescriptor,
37
+ objectGetPrototypeOf: Object.getPrototypeOf,
38
+ objectHasOwn: Object.hasOwn,
39
+ objectIs: Object.is,
40
+ objectKeys: Object.keys,
41
+ objectPropertyIsEnumerable: uncurry(Object.prototype.propertyIsEnumerable),
42
+ reflectOwnKeys: Reflect.ownKeys,
43
+ numberIsFinite: Number.isFinite,
44
+ numberIsNaN: Number.isNaN,
45
+ numberIsSafeInteger: Number.isSafeInteger,
46
+ toBigInt: BigInt,
47
+ toNumber: Number,
48
+ toString: String,
49
+ mapGet: uncurry(Map.prototype.get),
50
+ mapHas: uncurry(Map.prototype.has),
51
+ mapSet: uncurry(Map.prototype.set),
52
+ mapDelete: uncurry(Map.prototype.delete),
53
+ mapClear: uncurry(Map.prototype.clear),
54
+ mapSize: uncurry(Object.getOwnPropertyDescriptor(Map.prototype, 'size').get),
55
+ mapFirstKey: value => mapIteratorNext(mapKeys(value)).value,
56
+ mapForEach: uncurry(Map.prototype.forEach),
57
+ setHas: uncurry(Set.prototype.has),
58
+ setAdd: uncurry(Set.prototype.add),
59
+ setDelete: uncurry(Set.prototype.delete),
60
+ setClear: uncurry(Set.prototype.clear),
61
+ setSize: uncurry(Object.getOwnPropertyDescriptor(Set.prototype, 'size').get),
62
+ setForEach: uncurry(Set.prototype.forEach),
63
+ weakMapHas: uncurry(WeakMap.prototype.has),
64
+ weakMapGet,
65
+ weakMapSet,
66
+ weakSetHas: uncurry(WeakSet.prototype.has),
67
+ weakSetAdd: uncurry(WeakSet.prototype.add),
68
+ isArray: Array.isArray,
69
+ everyArray: uncurry(Array.prototype.every),
70
+ includes: uncurry(Array.prototype.includes),
71
+ join: uncurry(Array.prototype.join),
72
+ popArray: uncurry(Array.prototype.pop),
73
+ prependArray: uncurry(Array.prototype.unshift),
74
+ someArray: uncurry(Array.prototype.some),
75
+ sliceString: uncurry(String.prototype.slice),
76
+ splitString: uncurry(String.prototype.split),
77
+ searchString: uncurry(String.prototype.search),
78
+ replaceString: uncurry(String.prototype.replace),
79
+ trimString: uncurry(String.prototype.trim),
80
+ replaceAllString: uncurry(String.prototype.replaceAll),
81
+ includesString: uncurry(String.prototype.includes),
82
+ startsWith: uncurry(String.prototype.startsWith),
83
+ endsWith: uncurry(String.prototype.endsWith),
84
+ regexpExec: uncurry(RegExp.prototype.exec),
85
+ regexpTest: uncurry(RegExp.prototype.test),
86
+ floor: Math.floor,
87
+ min: Math.min,
88
+ max: Math.max,
89
+ appendArray,
90
+ copyArray,
91
+ sort: uncurry(Array.prototype.sort),
92
+ compare: uncurry(String.prototype.localeCompare),
93
+ }
94
+
95
+ if (typeof Buffer === 'function') {
96
+ runtimeIntrinsics.bufferByteLength = Buffer.byteLength
97
+ runtimeIntrinsics.bufferConcat = Buffer.concat
98
+ runtimeIntrinsics.bufferIsBuffer = Buffer.isBuffer
99
+ runtimeIntrinsics.bufferSubarray = uncurry(Buffer.prototype.subarray)
100
+ runtimeIntrinsics.bufferToString = uncurry(Buffer.prototype.toString)
101
+ }
@@ -414,7 +414,7 @@ export class SessionCellExecutor {
414
414
  }
415
415
  const priorBindingCatalog = userBindingPlan.catalog
416
416
  const catalog = priorBindingCatalog.inputs()
417
- const { bindingPolicy, rewritesEnabled, moduleSemantics, languageSemantics } = executionPolicies(config, replayRecord)
417
+ const { bindingPolicy, rewritesEnabled, moduleSemantics, languageSemantics, moduleTransform } = executionPolicies(config, replayRecord)
418
418
  const prepareCell = program => prepareProgram(program, {
419
419
  knownBindings: catalog.knownBindings,
420
420
  bindingPolicy,
@@ -576,6 +576,7 @@ export class SessionCellExecutor {
576
576
  type: 'run', id, program: prepared.code, namespaces: bindings.workerDescriptors,
577
577
  moduleLoads: prepared.moduleLoads,
578
578
  languageSemantics,
579
+ moduleTransform,
579
580
  rootRuntimeName: prepared.rootRuntimeName,
580
581
  rootBindings: prepared.rootBindings,
581
582
  importBindingNamespaces: new Map([...catalog.importBindings].map(([name, binding]) => [name, binding.namespace])),
@@ -13,7 +13,8 @@ export const RECOVERY_BOUNDARY_KEY = 'dshPtcPlusRecoveryBoundaries'
13
13
  export const IMPORT_BOUNDARY_JOURNAL_VERSION = 7
14
14
  export const PER_NAME_USER_BINDINGS_JOURNAL_VERSION = 8
15
15
  export const LANGUAGE_SEMANTICS_JOURNAL_VERSION = 9
16
- export const JOURNAL_VERSION = LANGUAGE_SEMANTICS_JOURNAL_VERSION
16
+ export const MODULE_TRANSFORM_JOURNAL_VERSION = 10
17
+ export const JOURNAL_VERSION = MODULE_TRANSFORM_JOURNAL_VERSION
17
18
  export const LIVE_USER_BINDINGS_SHADOW_POLICY = 'per-name'
18
19
  export const LEGACY_USER_BINDINGS_SHADOW_POLICY = 'whole-entry'
19
20
  export const LIVE_USER_BINDINGS_REUSE_POLICY = 'implementation-v1'
@@ -30,7 +31,8 @@ export const STATUSES = new Set(['durable', 'volatile', 'discarded', 'noop'])
30
31
  export const BINDING_MODES = new Set(['loose', 'strict'])
31
32
  export const WHOLE_ENTRY_JOURNAL_FIELDS = new Set(['version', 'bindingPolicy', 'rewritePolicy', 'moduleSemantics', 'userBindingsFingerprint', 'userBindingsReusePolicy', 'status', 'calls', 'operations', 'confirms', 'diagnostics', 'completion', 'volatileReason'])
32
33
  export const PER_NAME_JOURNAL_FIELDS = new Set([...WHOLE_ENTRY_JOURNAL_FIELDS, 'userBindingsShadowPolicy', 'userBindingNames'])
33
- export const JOURNAL_FIELDS = new Set([...PER_NAME_JOURNAL_FIELDS, 'languageSemantics'])
34
+ export const LANGUAGE_SEMANTICS_JOURNAL_FIELDS = new Set([...PER_NAME_JOURNAL_FIELDS, 'languageSemantics'])
35
+ export const JOURNAL_FIELDS = new Set([...LANGUAGE_SEMANTICS_JOURNAL_FIELDS, 'moduleTransform'])
34
36
  export const FINGERPRINT_REUSE_JOURNAL_FIELDS = new Set([...WHOLE_ENTRY_JOURNAL_FIELDS].filter(field => field !== 'userBindingsReusePolicy'))
35
37
  export const RELATIONLESS_JOURNAL_FIELDS = new Set([...FINGERPRINT_REUSE_JOURNAL_FIELDS].filter(field => field !== 'userBindingsFingerprint'))
36
38
  export const PREDECESSOR_JOURNAL_FIELDS = new Set(['version', 'bindingMode', 'rewritePolicy', 'status', 'calls', 'operations', 'confirms', 'diagnostics', 'completion', 'volatileReason'])
@@ -56,32 +58,47 @@ export const ERROR_FIELDS = new Set(['kind', 'message'])
56
58
  export const EDIT_TARGET_FIELDS = new Set(['targetCallSeq'])
57
59
  export const DERIVED_RUN_FIELDS = new Set(['code', 'description'])
58
60
 
59
- export const JOURNAL_VERSIONS = new Set([LEGACY_JOURNAL_VERSION, INTERMEDIATE_JOURNAL_VERSION, PREVIOUS_JOURNAL_VERSION, USER_BINDING_RELATIONLESS_JOURNAL_VERSION, FINGERPRINT_REUSE_JOURNAL_VERSION, VERSIONED_BINDING_REUSE_JOURNAL_VERSION, IMPORT_BOUNDARY_JOURNAL_VERSION, PER_NAME_USER_BINDINGS_JOURNAL_VERSION, JOURNAL_VERSION])
61
+ export const JOURNAL_VERSIONS = new Set([LEGACY_JOURNAL_VERSION, INTERMEDIATE_JOURNAL_VERSION, PREVIOUS_JOURNAL_VERSION, USER_BINDING_RELATIONLESS_JOURNAL_VERSION, FINGERPRINT_REUSE_JOURNAL_VERSION, VERSIONED_BINDING_REUSE_JOURNAL_VERSION, IMPORT_BOUNDARY_JOURNAL_VERSION, PER_NAME_USER_BINDINGS_JOURNAL_VERSION, LANGUAGE_SEMANTICS_JOURNAL_VERSION, JOURNAL_VERSION])
60
62
  export const USER_BINDINGS_REUSE_POLICIES = new Set([LEGACY_USER_BINDINGS_REUSE_POLICY, LIVE_USER_BINDINGS_REUSE_POLICY])
61
63
  export const USER_BINDINGS_SHADOW_POLICIES = new Set([LEGACY_USER_BINDINGS_SHADOW_POLICY, LIVE_USER_BINDINGS_SHADOW_POLICY])
62
64
  const USER_BINDING_NAME_STATES = new Set(['provider', 'local', 'absent', 'unknown'])
65
+ const isArray = Array.isArray
66
+ const freeze = Object.freeze
67
+ const ownKeys = Reflect.ownKeys
68
+ const defineProperty = Object.defineProperty
69
+ const uncurry = Function.prototype.bind.bind(Function.prototype.call)
70
+ const setHas = uncurry(Set.prototype.has)
71
+ const setAdd = uncurry(Set.prototype.add)
72
+ const sort = uncurry(Array.prototype.sort)
73
+ const enumerable = uncurry(Object.prototype.propertyIsEnumerable)
63
74
 
64
75
  /** Closed, value-free evidence shared by worker settlement, persistence and Client. */
65
76
  export function normalizeUserBindingNames(value) {
66
- if (!Array.isArray(value)) throw new TypeError('invalid user binding name evidence')
77
+ if (!isArray(value)) throw new TypeError('invalid user binding name evidence')
67
78
  const names = new Set()
68
- return Object.freeze(value.map(fact => {
79
+ const normalized = []
80
+ for (let index = 0; index < value.length; index++) {
81
+ const fact = value[index]
69
82
  if (fact === null || typeof fact !== 'object'
70
- || !USER_BINDING_NAME_STATES.has(fact.state)
83
+ || !setHas(USER_BINDING_NAME_STATES, fact.state)
71
84
  || typeof fact.name !== 'string' || fact.name.length > 128
72
85
  || !/^[$_\p{ID_Start}][$\u200c\u200d\p{ID_Continue}]*$/u.test(fact.name)
73
- || names.has(fact.name)) throw new TypeError('invalid user binding name evidence')
86
+ || setHas(names, fact.name)) throw new TypeError('invalid user binding name evidence')
74
87
  const fields = fact.state === 'provider' ? ['name', 'state', 'entryId'] : ['name', 'state']
75
- if (Reflect.ownKeys(fact).length !== fields.length
76
- || !fields.every(key => Object.prototype.propertyIsEnumerable.call(fact, key))
88
+ let validFields = ownKeys(fact).length === fields.length
89
+ for (let field = 0; validFields && field < fields.length; field++) validFields = enumerable(fact, fields[field])
90
+ if (!validFields
77
91
  || (fact.state === 'provider' && (typeof fact.entryId !== 'string'
78
92
  || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(fact.entryId)))) {
79
93
  throw new TypeError('invalid user binding name evidence')
80
94
  }
81
- names.add(fact.name)
82
- return Object.freeze({ name: fact.name, state: fact.state,
83
- ...(fact.state === 'provider' ? { entryId: fact.entryId } : {}) })
84
- }).sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0))
95
+ setAdd(names, fact.name)
96
+ defineProperty(normalized, normalized.length, { configurable: true, enumerable: true, writable: true,
97
+ value: freeze({ name: fact.name, state: fact.state,
98
+ ...(fact.state === 'provider' ? { entryId: fact.entryId } : {}) }) })
99
+ }
100
+ sort(normalized, (left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0)
101
+ return freeze(normalized)
85
102
  }
86
103
 
87
104
  export function normalizeJournalUserBindingNames(journal) {
@@ -3,6 +3,8 @@ import { normalizeDiagnostic } from './diagnostic.js'
3
3
  import { assertOwnFields, isRecord } from './record-utils.js'
4
4
  import { sessionEvents } from './session-events.js'
5
5
  import { LEGACY_LANGUAGE_SEMANTICS, normalizeLanguageSemantics } from './language-semantics.js'
6
+ import { LEGACY_USER_BINDING_TRANSFORM, historicalModuleTransformForLanguage,
7
+ moduleTransformForLanguage, normalizeModuleTransform } from './module-transform-contract.js'
6
8
  import {
7
9
  LEGACY_DEFAULT_EXPORT_BINDING,
8
10
  LEGACY_IMPORT_EXPRESSION_BOUNDARY,
@@ -16,6 +18,8 @@ import {
16
18
  REWRITES_KEY,
17
19
  RECOVERY_BOUNDARY_KEY,
18
20
  JOURNAL_VERSION,
21
+ LANGUAGE_SEMANTICS_JOURNAL_VERSION,
22
+ LANGUAGE_SEMANTICS_JOURNAL_FIELDS,
19
23
  IMPORT_BOUNDARY_JOURNAL_VERSION,
20
24
  PER_NAME_USER_BINDINGS_JOURNAL_VERSION,
21
25
  PER_NAME_JOURNAL_FIELDS,
@@ -251,12 +255,18 @@ function normalizeUserBindingsFingerprint(value) {
251
255
 
252
256
  function migrateJournal(value, resolveLegacyConfirm) {
253
257
  if (value.version === JOURNAL_VERSION) return value
258
+ if (value.version === LANGUAGE_SEMANTICS_JOURNAL_VERSION) {
259
+ assertOwnFields(value, LANGUAGE_SEMANTICS_JOURNAL_FIELDS, 'dsh-ptc-plus journal')
260
+ const languageSemantics = normalizeLanguageSemantics(value.languageSemantics)
261
+ return { ...value, version: JOURNAL_VERSION, moduleTransform: historicalModuleTransformForLanguage(languageSemantics) }
262
+ }
254
263
  if (value.version === PER_NAME_USER_BINDINGS_JOURNAL_VERSION) {
255
264
  assertOwnFields(value, PER_NAME_JOURNAL_FIELDS, 'dsh-ptc-plus journal')
256
- return { ...value, version: JOURNAL_VERSION, languageSemantics: LEGACY_LANGUAGE_SEMANTICS }
265
+ return { ...value, version: JOURNAL_VERSION, languageSemantics: LEGACY_LANGUAGE_SEMANTICS,
266
+ moduleTransform: LEGACY_USER_BINDING_TRANSFORM }
257
267
  }
258
268
  const shadow = { userBindingsShadowPolicy: LEGACY_USER_BINDINGS_SHADOW_POLICY, userBindingNames: null,
259
- languageSemantics: LEGACY_LANGUAGE_SEMANTICS }
269
+ languageSemantics: LEGACY_LANGUAGE_SEMANTICS, moduleTransform: LEGACY_USER_BINDING_TRANSFORM }
260
270
  if (value.version === IMPORT_BOUNDARY_JOURNAL_VERSION) {
261
271
  assertOwnFields(value, WHOLE_ENTRY_JOURNAL_FIELDS, 'dsh-ptc-plus journal')
262
272
  return { ...value, version: JOURNAL_VERSION, ...shadow }
@@ -325,6 +335,7 @@ export function normalizeJournal(value, options = {}) {
325
335
  const migrated = migrateJournal(value, options.resolveLegacyConfirm)
326
336
  assertOwnFields(migrated, JOURNAL_FIELDS, 'dsh-ptc-plus journal')
327
337
  const languageSemantics = normalizeLanguageSemantics(migrated.languageSemantics)
338
+ const moduleTransform = normalizeModuleTransform(migrated.moduleTransform, languageSemantics)
328
339
  const bindingPolicy = normalizeBindingPolicy(migrated.bindingPolicy)
329
340
  const rewritePolicy = normalizeRewritePolicy(migrated.rewritePolicy)
330
341
  const moduleSemantics = normalizeModuleSemantics(migrated.moduleSemantics)
@@ -355,6 +366,7 @@ export function normalizeJournal(value, options = {}) {
355
366
  return Object.freeze({
356
367
  version: JOURNAL_VERSION,
357
368
  languageSemantics,
369
+ moduleTransform,
358
370
  bindingPolicy,
359
371
  rewritePolicy,
360
372
  moduleSemantics,
@@ -583,7 +595,8 @@ export function liveToolCallSeq(session, callId, toolName) {
583
595
  }
584
596
 
585
597
  /** Start a mutable journal for one live cell. */
586
- export function createJournal(confirms = [], bindingPolicy, rewritePolicy, languageSemantics = LEGACY_LANGUAGE_SEMANTICS) {
598
+ export function createJournal(confirms = [], bindingPolicy, rewritePolicy, languageSemantics = LEGACY_LANGUAGE_SEMANTICS,
599
+ moduleTransform = moduleTransformForLanguage(languageSemantics)) {
587
600
  if (typeof bindingPolicy === 'string') {
588
601
  if (!BINDING_MODES.has(bindingPolicy)) throw new TypeError('invalid dsh-ptc-plus journal binding mode')
589
602
  bindingPolicy = {
@@ -591,9 +604,11 @@ export function createJournal(confirms = [], bindingPolicy, rewritePolicy, langu
591
604
  functionClassRedeclarations: false,
592
605
  }
593
606
  }
607
+ languageSemantics = normalizeLanguageSemantics(languageSemantics)
594
608
  return {
595
609
  version: JOURNAL_VERSION,
596
- languageSemantics: normalizeLanguageSemantics(languageSemantics),
610
+ languageSemantics,
611
+ moduleTransform: normalizeModuleTransform(moduleTransform, languageSemantics),
597
612
  bindingPolicy: normalizeBindingPolicy(bindingPolicy),
598
613
  rewritePolicy: normalizeRewritePolicy(rewritePolicy),
599
614
  moduleSemantics: normalizeModuleSemantics(LIVE_MODULE_SEMANTICS),
@@ -713,6 +713,7 @@ export class SessionRuntime {
713
713
  policies.bindingPolicy,
714
714
  policies.rewritesEnabled,
715
715
  policies.languageSemantics,
716
+ policies.moduleTransform,
716
717
  )
717
718
  const workerReservation = kernel.reserveWorkerConfiguration(cellConfig)
718
719
  let result
@@ -117,6 +117,25 @@ export function sourceOffsetAt(sourceMap, generatedOffset) {
117
117
  return segmentOffsetAt(segment, generatedOffset)
118
118
  }
119
119
 
120
+ /** Whether a complete generated interval still contains its exact original text. */
121
+ export function sourceRangeHasOriginalText(sourceMap, generatedSource, originalSource, generatedStart, generatedEnd) {
122
+ if (typeof generatedSource !== 'string' || typeof originalSource !== 'string'
123
+ || generatedEnd <= generatedStart || generatedStart < 0 || generatedEnd > generatedSource.length) return false
124
+ let cursor = generatedStart
125
+ for (let index = segmentIndexAt(sourceMap, generatedStart); index < sourceMap.length && cursor < generatedEnd; index += 1) {
126
+ const segment = sourceMap.at(index)
127
+ if (segment.generatedStart > cursor || segment.generatedEnd <= cursor) break
128
+ const end = Math.min(generatedEnd, segment.generatedEnd)
129
+ const length = end - cursor
130
+ const originalStart = segmentOffsetAt(segment, cursor)
131
+ if (segment.originalEnd <= segment.originalStart
132
+ || originalStart + length > originalSource.length
133
+ || generatedSource.slice(cursor, end) !== originalSource.slice(originalStart, originalStart + length)) return false
134
+ cursor = end
135
+ }
136
+ return cursor === generatedEnd
137
+ }
138
+
120
139
  function segmentOffsetAt(segment, generatedOffset) {
121
140
  const generatedLength = segment.generatedEnd - segment.generatedStart
122
141
  const originalLength = segment.originalEnd - segment.originalStart
@@ -191,8 +191,10 @@ function rewriteRootReferences(code, sourceMap, runtime, candidateNames, candida
191
191
  }
192
192
  handled.add(path.node.start)
193
193
  } })
194
+ const classificationSource = applySourceEdits(code, sourceMap, classificationEdits)
194
195
  return { ...applySourceEdits(code, sourceMap, edits),
195
- classificationCode: applySourceEdits(code, sourceMap, classificationEdits).code,
196
+ classificationCode: classificationSource.code,
197
+ classificationSourceMap: classificationSource.sourceMap,
196
198
  implicitDeclarations: [...implicitDeclarations.values()],
197
199
  readNames: [...readNames] }
198
200
  }
@@ -1,7 +1,9 @@
1
1
  import loadAmaro from './compiler-amaro.cjs'
2
2
 
3
- export { LEGACY_USER_BINDING_TRANSFORM, USER_BINDING_TRANSFORM, PROTECTED_MODULE_TRANSFORM,
4
- moduleTransformForLanguage, supportedUserBindingTransform } from './module-transform-contract.js'
3
+ export { LEGACY_USER_BINDING_TRANSFORM, PREVIOUS_USER_BINDING_TRANSFORM, USER_BINDING_TRANSFORM,
4
+ PROTECTED_MODULE_TRANSFORM, isStatefulUserBindingTransform,
5
+ historicalModuleTransformForLanguage, moduleTransformForLanguage, moduleTransformMatchesLanguage,
6
+ normalizeModuleTransform, supportedUserBindingTransform } from './module-transform-contract.js'
5
7
 
6
8
  /** Keep binding-module lowering independent of the Host's experimental transform API. */
7
9
  export function transformTypeScriptModule(source) {