dsh-ptc-plus 0.4.1 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +164 -78
- package/README.md +165 -79
- package/assets/dsh-ptc-plus-banner-en.webp +0 -0
- package/assets/dsh-ptc-plus-banner-zh.webp +0 -0
- package/assets/ptc-plus-icon.svg +5 -0
- package/client.js +854 -336
- package/compiler-core.cjs +161 -157
- package/compiler-core.cjs.map +3 -3
- package/docs/README.md +4 -1
- package/docs/adr/0005-temporary-rejected-cell-edit-transport.md +8 -7
- package/docs/adr/0008-define-agent-operating-contract.md +2 -2
- package/docs/adr/0010-session-log-derived-recovery-tips.md +7 -0
- package/docs/adr/0016-archive-resolved-review-findings.md +3 -3
- package/docs/adr/0019-plugin-settings-and-kill-switch.md +3 -3
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -0
- package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +14 -3
- package/docs/adr/0023-global-user-bindings.md +7 -7
- package/docs/adr/0025-use-versioned-logical-binding-identities.md +3 -3
- package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +23 -7
- package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +9 -5
- package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +26 -6
- package/docs/adr/0031-close-semantic-obligations-by-owner.md +29 -0
- package/docs/adr/README.md +4 -4
- package/docs/architecture.md +15 -10
- package/docs/binding-review-placement-design.md +1 -1
- package/docs/client-ui.md +9 -5
- package/docs/durability-design.md +12 -9
- package/docs/installation.md +27 -9
- package/docs/runtime-reference.md +23 -12
- package/docs/semantic-validation.md +7 -3
- package/docs/stateful-binding-updates-proposal.md +8 -4
- package/docs/user-bindings.en.md +10 -7
- package/docs/user-bindings.md +9 -6
- package/docs/value-wire.md +1 -1
- package/docs/verification-optimization-work.md +41 -0
- package/docs/verification.md +53 -2
- package/index.js +39 -34
- package/internal/async-local-storage-intrinsics.js +43 -0
- package/internal/binding-declaration-contract.js +8 -0
- package/internal/binding-source-analysis.js +381 -64
- package/internal/binding-update-policy.js +7 -1
- package/internal/callable-source-facts.js +37 -1
- package/internal/cell-analysis.js +85 -17
- package/internal/compiler-intrinsics.js +9 -43
- package/internal/compiler-module-links.js +7 -4
- package/internal/config-spec.js +1 -1
- package/internal/cordis-tools-owner.js +20 -10
- package/internal/dynamic-environment-integration.js +8 -4
- package/internal/dynamic-environment-runtime.js +24 -3
- package/internal/dynamic-native-calls.js +4 -1
- package/internal/dynamic-native-runtime.js +15 -1
- package/internal/edit-transport-owner.js +80 -20
- package/internal/execution-seam-compat.js +192 -30
- package/internal/failure-reporting.js +38 -30
- package/internal/isolated-worker.js +17 -10
- package/internal/kernel-worker.js +645 -393
- package/internal/message-sources.js +14 -0
- package/internal/module-compilation.js +7 -5
- package/internal/module-transform-contract.js +32 -2
- package/internal/record-utils.js +24 -15
- package/internal/repl-memory-projection.js +10 -0
- package/internal/repl-scope-normalizer.js +72 -35
- package/internal/repl-value-observer.js +145 -46
- package/internal/runtime-config.js +62 -2
- package/internal/runtime-intrinsics.js +104 -0
- package/internal/runtime-messages.js +6 -4
- package/internal/session-cell-executor.js +26 -2
- package/internal/session-journal-recovery.js +267 -67
- package/internal/session-journal-schema.js +30 -13
- package/internal/session-journal.js +485 -44
- package/internal/session-log-view.js +5 -4
- package/internal/session-runtime.js +46 -21
- package/internal/session-state.js +7 -29
- package/internal/settings-compat.js +66 -1
- package/internal/source-position-map.js +38 -0
- package/internal/stateful-root-compiler.js +3 -1
- package/internal/typescript-transform.js +4 -2
- package/internal/user-binding-console-worker.js +115 -41
- package/internal/user-binding-draft-projection.js +3 -2
- package/internal/user-bindings-owner.js +2 -1
- package/internal/user-bindings.js +16 -3
- package/internal/validated-parse-repair.js +6 -2
- package/internal/value-wire.js +189 -94
- package/internal/worker-client.js +99 -8
- package/internal/worker-cwd-virtualization.js +82 -54
- package/internal/worker-environment.js +20 -0
- package/internal/worker-output-capture.js +187 -0
- package/internal/worker-output-fence.js +26 -0
- package/internal/worker-realm-surfaces.js +197 -0
- package/internal/worker-repl-realm.js +140 -0
- package/package.json +37 -4
- package/src/client-activity.js +12 -2
- package/src/client-authoring-view.js +121 -62
- package/src/client-console.js +4 -2
- package/src/client-copy.js +4 -2
- package/src/client-feature-gates.js +7 -9
- package/src/client-host-compat.js +104 -27
- package/src/client-repl-view.js +3 -1
- package/src/client-styles.js +12 -1
- package/src/client-tool-view.js +3 -1
- package/src/client-transport.js +2 -2
- package/src/client-workbench.js +4 -3
- package/src/client.js +56 -17
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Producer identity shared by Host messages and Client history projections. */
|
|
2
|
+
export const PTC_MESSAGE_SOURCE_KIND = 'plugin:ptc-plus'
|
|
3
|
+
|
|
4
|
+
/** Read the current producer identity and its historical pre-migration wrapper. */
|
|
5
|
+
export function isPtcMessageSource(source) {
|
|
6
|
+
return source?.kind === PTC_MESSAGE_SOURCE_KIND
|
|
7
|
+
|| (source?.kind === 'plugin' && source.plugin === 'ptc-plus')
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** DSH owns aggregate runtime-context messages independently of PTC messages. */
|
|
11
|
+
export function isHostRuntimeContextSource(source) {
|
|
12
|
+
return source?.kind === 'runtime-context'
|
|
13
|
+
|| (source?.kind === 'plugin' && source.plugin === '@deepseek-ai/dsh-system-prompt')
|
|
14
|
+
}
|
|
@@ -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')
|
|
@@ -187,18 +187,19 @@ export function compileStatefulModule(source, {
|
|
|
187
187
|
callableSources = marked.callableSources
|
|
188
188
|
nativeJavaScript = marked.nativeJavaScript
|
|
189
189
|
mapped = marked
|
|
190
|
-
if (!nativeJavaScript) mapped = normalizeTypeScriptValues(mapped.code, mapped.sourceMap, target)
|
|
191
|
-
if (target !== 'commonjs' && (transform
|
|
190
|
+
if (!nativeJavaScript) mapped = normalizeTypeScriptValues(mapped.code, mapped.sourceMap, target, callableSources)
|
|
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
|
|
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,
|
|
202
|
+
callableSources,
|
|
202
203
|
mode: transform === PROTECTED_MODULE_TRANSFORM ? 'protected-v1' : 'stateful-v1' })
|
|
203
204
|
// Protected entries retain native declaration policy; adapters still need
|
|
204
205
|
// bounded source regions after TypeScript and decorator lowering.
|
|
@@ -222,6 +223,7 @@ export function compileStatefulModule(source, {
|
|
|
222
223
|
sourceRegions: normalized.sourceRegions,
|
|
223
224
|
callableCollector: createRegionCallableCollector(callableSources),
|
|
224
225
|
module: true, compactOutput: true, importOperation: normalized.importOperation, sourceType, originalSource: source,
|
|
226
|
+
logicalRoots: programBindings,
|
|
225
227
|
bindings: normalized.dynamicBindings, privateBindings: normalized.privateBindings,
|
|
226
228
|
internalBindings: normalized.internalBindings, parserPlugins: CELL_PARSER_PLUGINS,
|
|
227
229
|
}) }
|
|
@@ -1,12 +1,42 @@
|
|
|
1
1
|
export const LEGACY_USER_BINDING_TRANSFORM = 'amaro@1.1.11'
|
|
2
|
-
export const
|
|
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
|
|
41
|
+
return isStatefulUserBindingTransform(transform) || transform === LEGACY_USER_BINDING_TRANSFORM
|
|
12
42
|
}
|
package/internal/record-utils.js
CHANGED
|
@@ -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' && !
|
|
10
|
+
return value !== null && typeof value === 'object' && !isArray(value)
|
|
5
11
|
}
|
|
6
12
|
|
|
7
13
|
export function assertOwnFields(value, allowed, label) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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 (
|
|
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
|
|
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
|
|
41
|
-
if (seen
|
|
42
|
-
seen
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
|
56
|
+
if (child !== null && typeof child === 'object') appendArray(pending, child)
|
|
48
57
|
}
|
|
49
58
|
}
|
|
50
|
-
|
|
59
|
+
objectFreeze(current)
|
|
51
60
|
}
|
|
52
61
|
return value
|
|
53
62
|
}
|
|
@@ -26,6 +26,16 @@ const PROJECTION_STATE_FIELDS = new Set(['generation', 'memory', 'pendingReplCal
|
|
|
26
26
|
const PENDING_CALL_FIELDS = new Set(['callId', 'seq'])
|
|
27
27
|
const REPL_MEMORY_META_VERSION = 5
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Bound one definition source to the presentation budget. Producers apply this
|
|
31
|
+
* before a catalog entry can carry more than the validated metadata shape.
|
|
32
|
+
*/
|
|
33
|
+
export function boundedDefinitionSource(source) {
|
|
34
|
+
return source.length <= MAX_DEFINITION_SOURCE_LENGTH
|
|
35
|
+
? source
|
|
36
|
+
: `${source.slice(0, MAX_DEFINITION_SOURCE_LENGTH - 3)}...`
|
|
37
|
+
}
|
|
38
|
+
|
|
29
39
|
function isReuseCount(value) {
|
|
30
40
|
return Number.isSafeInteger(value) && value >= 0
|
|
31
41
|
}
|
|
@@ -23,7 +23,7 @@ import { supportsNativeUsing, transformTypeScriptSource } from './typescript-tra
|
|
|
23
23
|
import { createDynamicScopeAnalysis, varInitializerTarget } from './dynamic-scope-analysis.js'
|
|
24
24
|
import { visitSource } from './compiler-source-regions.js'
|
|
25
25
|
import { analyzeSourceDeclarations, resolveSourceBinding, COMMONJS_PARAMETERS } from './compiler-scope-facts.js'
|
|
26
|
-
import { callableMarkerPosition } from './callable-source-facts.js'
|
|
26
|
+
import { callableMarkerPosition, restoreMappedCallableSourceMarks } from './callable-source-facts.js'
|
|
27
27
|
import { createSourcePieces } from './compiler-source-pieces.js'
|
|
28
28
|
import { indexSourceRegions, validateRegionSource, sourceFeatures } from './compiler-region-output.js'
|
|
29
29
|
|
|
@@ -424,7 +424,7 @@ export function lowerStatefulDecorators(result, target, intrinsicContext) {
|
|
|
424
424
|
|
|
425
425
|
/** Erase type-only declarations and lower runtime TypeScript values before
|
|
426
426
|
* logical ownership and module export/link plans consume the source. */
|
|
427
|
-
export function normalizeTypeScriptValues(code, sourceMap, target) {
|
|
427
|
+
export function normalizeTypeScriptValues(code, sourceMap, target, callableSources) {
|
|
428
428
|
const tree=parse(code,{...parserOptionsForTarget(target),errorRecovery:true})
|
|
429
429
|
let parameterProperties = false
|
|
430
430
|
let tokens
|
|
@@ -521,20 +521,15 @@ export function normalizeTypeScriptValues(code, sourceMap, target) {
|
|
|
521
521
|
if(enumEdits.length>0) source=applySourceEdits(source,identitySourceMap(source.length),enumEdits).code
|
|
522
522
|
const transformed=transformTypeScriptSource(source,{module:target!=='commonjs',sourceMap:true,
|
|
523
523
|
transform:{tsEnumIsMutable:true,noEmptyExport:true}})
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
originalEnd:Math.min(owner.end,path.node.start+start+1)})
|
|
534
|
-
if(transformed.code[index]==='\n'){line++;column=1}else column++
|
|
535
|
-
}
|
|
536
|
-
const exported=owner!==path.node&&transformed.code.trim()!==''?`\nexport {${path.node.id.name}};`:''
|
|
537
|
-
edits.push({start:owner.start,end:owner.end,text:transformed.code+exported,mappings})
|
|
524
|
+
let mapped=mappedSourceTransform(source,identitySourceMap(source.length),transformed)
|
|
525
|
+
mapped=restoreMappedCallableSourceMarks(mapped,source,transformed,callableSources,parserOptionsForTarget(target))
|
|
526
|
+
const mappings=[...mapped.sourceMap].map(mapping=>({
|
|
527
|
+
...mapping,
|
|
528
|
+
originalStart:path.node.start+mapping.originalStart,
|
|
529
|
+
originalEnd:Math.min(owner.end,path.node.start+mapping.originalEnd),
|
|
530
|
+
}))
|
|
531
|
+
const exported=owner!==path.node&&mapped.code.trim()!==''?`\nexport {${path.node.id.name}};`:''
|
|
532
|
+
edits.push({start:owner.start,end:owner.end,text:mapped.code+exported,mappings})
|
|
538
533
|
path.skip()
|
|
539
534
|
},
|
|
540
535
|
})
|
|
@@ -1130,23 +1125,46 @@ export function preserveStatementBoundaries(code, sourceMap = identitySourceMap(
|
|
|
1130
1125
|
* The root compiler owns Program bindings and calls this pass after lowering
|
|
1131
1126
|
* those declarations. protected-v1 keeps native local binding policy.
|
|
1132
1127
|
*/
|
|
1128
|
+
const normalizationFailureInputs = new WeakMap()
|
|
1129
|
+
|
|
1130
|
+
function retainNormalizationFailureInput(error, input) {
|
|
1131
|
+
if ((typeof error === 'object' && error !== null) || typeof error === 'function') {
|
|
1132
|
+
if (!normalizationFailureInputs.has(error)) normalizationFailureInputs.set(error, input)
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
/** Latest mapped normalization stage established before an error was thrown. */
|
|
1137
|
+
export function normalizationFailureInput(error) {
|
|
1138
|
+
return (typeof error === 'object' && error !== null) || typeof error === 'function'
|
|
1139
|
+
? normalizationFailureInputs.get(error) : undefined
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1133
1142
|
export function normalizeStatefulScopes(code, sourceMap = identitySourceMap(code.length), options = {}) {
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1143
|
+
let diagnosticInput = { code, sourceMap }
|
|
1144
|
+
try {
|
|
1145
|
+
const { outputPlan, ...metadata } = normalizeStatefulScopeInput(code, sourceMap, options)
|
|
1146
|
+
let result = metadata
|
|
1147
|
+
if (outputPlan) {
|
|
1148
|
+
// Release declaration ASTs and scope ancestry before materializing the
|
|
1149
|
+
// expanded output. This phase retains only numeric pieces and source facts.
|
|
1150
|
+
const { pieces, root, code, sourceMap, regionFacts } = outputPlan
|
|
1151
|
+
diagnosticInput = { code, sourceMap }
|
|
1152
|
+
options.onPhase?.('mapping')
|
|
1153
|
+
const output = pieces.emit(root)
|
|
1154
|
+
result = { ...metadata, ...applySourceEdits(code, sourceMap,
|
|
1155
|
+
[{ start: 0, end: code.length, ...output }]),
|
|
1156
|
+
sourceRegions: { ...regionFacts, regions: output.regions } }
|
|
1157
|
+
}
|
|
1158
|
+
diagnosticInput = result
|
|
1159
|
+
options.onPhase?.('validation')
|
|
1160
|
+
if (options.target === 'module' || options.target === 'commonjs') validateRegionSource(result, parserOptionsForTarget(options.target))
|
|
1161
|
+
result = options.deferDecorators ? result : lowerStatefulDecorators(result, options.target, options.intrinsicContext)
|
|
1162
|
+
diagnosticInput = result
|
|
1163
|
+
return options.deferResources ? result : lowerStatefulResources(result, options)
|
|
1164
|
+
} catch (error) {
|
|
1165
|
+
retainNormalizationFailureInput(error, diagnosticInput)
|
|
1166
|
+
throw error
|
|
1145
1167
|
}
|
|
1146
|
-
options.onPhase?.('validation')
|
|
1147
|
-
if (options.target === 'module' || options.target === 'commonjs') validateRegionSource(result, parserOptionsForTarget(options.target))
|
|
1148
|
-
result = options.deferDecorators ? result : lowerStatefulDecorators(result, options.target, options.intrinsicContext)
|
|
1149
|
-
return options.deferResources ? result : lowerStatefulResources(result, options)
|
|
1150
1168
|
}
|
|
1151
1169
|
|
|
1152
1170
|
/** Resource lifetime is lowered only after declaration owners have consumed
|
|
@@ -1227,15 +1245,33 @@ export function hasModuleResources(code) {
|
|
|
1227
1245
|
node.type === 'VariableDeclaration' && (node.kind === 'using' || node.kind === 'await using'))
|
|
1228
1246
|
}
|
|
1229
1247
|
|
|
1230
|
-
function normalizeStatefulScopeInput(code, sourceMap, {
|
|
1231
|
-
|
|
1248
|
+
function normalizeStatefulScopeInput(code, sourceMap, options) {
|
|
1249
|
+
let diagnosticInput = { code, sourceMap }
|
|
1250
|
+
try {
|
|
1251
|
+
return normalizeStatefulScopeInputUnchecked(code, sourceMap, options, input => { diagnosticInput = input })
|
|
1252
|
+
} catch (error) {
|
|
1253
|
+
retainNormalizationFailureInput(error, diagnosticInput)
|
|
1254
|
+
throw error
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
function normalizeStatefulScopeInputUnchecked(code, sourceMap, { mode = 'stateful-v1', target, moduleImport, intrinsicContext, onPhase,
|
|
1259
|
+
callableSources, nativeJavaScript = false, nativeUsing = supportsNativeUsing() } = {}, updateDiagnosticInput) {
|
|
1232
1260
|
if (mode !== 'stateful-v1' && mode !== 'protected-v1') throw new TypeError('ptc-plus: unsupported scope semantics')
|
|
1233
1261
|
onPhase?.('typescript')
|
|
1234
|
-
|
|
1235
|
-
|
|
1262
|
+
let typescript
|
|
1263
|
+
if (nativeJavaScript) {
|
|
1264
|
+
typescript = { code, sourceMap }
|
|
1265
|
+
} else {
|
|
1266
|
+
const normalizedTypescript = normalizeTypeScriptValues(code,sourceMap,target,callableSources)
|
|
1267
|
+
updateDiagnosticInput(normalizedTypescript)
|
|
1268
|
+
typescript = lowerLegacyParameterDecorators(normalizedTypescript,target,intrinsicContext)
|
|
1269
|
+
}
|
|
1270
|
+
updateDiagnosticInput(typescript)
|
|
1236
1271
|
const separated = preserveStatementBoundaries(typescript.code, typescript.sourceMap, parserOptionsForTarget(target))
|
|
1237
1272
|
code=separated.code
|
|
1238
1273
|
sourceMap=separated.sourceMap
|
|
1274
|
+
updateDiagnosticInput(separated)
|
|
1239
1275
|
const finish = result => {
|
|
1240
1276
|
result = { ...result, deferredHelpers: typescript.deferredHelpers,
|
|
1241
1277
|
internalBindings: new Set([...typescript.internalBindings ?? [], ...result.internalBindings ?? []]) }
|
|
@@ -1269,6 +1305,7 @@ function normalizeStatefulScopeInput(code, sourceMap, { mode = 'stateful-v1', ta
|
|
|
1269
1305
|
const annex = introduceAnnexBVars(code, sourceMap, target, onPhase)
|
|
1270
1306
|
code = annex.code
|
|
1271
1307
|
sourceMap = annex.sourceMap
|
|
1308
|
+
updateDiagnosticInput(annex)
|
|
1272
1309
|
onPhase?.('facts')
|
|
1273
1310
|
const facts = analyzeLogicalScopes(code, { target, compact: true, compactNames: target === 'module' || target === 'commonjs', onPhase })
|
|
1274
1311
|
onPhase?.('planning')
|
|
@@ -1,113 +1,212 @@
|
|
|
1
1
|
import { types } from 'node:util'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { readFileSync } from 'node:fs'
|
|
3
|
+
import { createRequire } from 'node:module'
|
|
4
|
+
import { createContext, isContext, runInContext, runInThisContext } from 'node:vm'
|
|
4
5
|
import { randomUUID } from 'node:crypto'
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
6
|
+
import { runtimeIntrinsics as internal } from './runtime-intrinsics.js'
|
|
7
|
+
import { deleteWorkerRealmProperty, WORKER_REALM_MUTATION } from './worker-realm-surfaces.js'
|
|
7
8
|
|
|
8
9
|
const MAX_ENTRIES = 128
|
|
9
10
|
const MAX_TEXT = 512
|
|
10
11
|
const MAX_PROPERTIES = 5
|
|
11
|
-
const ownDescriptor = Object.getOwnPropertyDescriptor
|
|
12
|
-
const isArray = Array.isArray
|
|
13
12
|
const isProxy = types.isProxy
|
|
14
|
-
const stringify = JSON.stringify
|
|
15
13
|
const now = Date.now
|
|
16
|
-
const bigintText = BigInt.prototype.toString
|
|
17
14
|
const BIGINT_LIMIT = 10n ** 128n
|
|
15
|
+
const IDENTIFIER = /^[$_\p{ID_Start}][$\u200c\u200d\p{ID_Continue}]*$/u
|
|
16
|
+
const AST_METADATA_KEYS = new internal.Set(['start', 'end', 'loc', 'range'])
|
|
17
|
+
const {
|
|
18
|
+
Set: PrivateSet,
|
|
19
|
+
appendArray,
|
|
20
|
+
bigintToString,
|
|
21
|
+
isArray,
|
|
22
|
+
jsonStringify,
|
|
23
|
+
min,
|
|
24
|
+
objectCreate,
|
|
25
|
+
objectGetOwnPropertyDescriptor: ownDescriptor,
|
|
26
|
+
objectHasOwn,
|
|
27
|
+
objectIs,
|
|
28
|
+
objectKeys,
|
|
29
|
+
regexpTest,
|
|
30
|
+
replaceAllString,
|
|
31
|
+
setAdd,
|
|
32
|
+
setHas,
|
|
33
|
+
sliceString,
|
|
34
|
+
toString,
|
|
35
|
+
} = internal
|
|
18
36
|
const unreadable = () => ({ status: 'unreadable', text: '', truncated: false })
|
|
37
|
+
const runInRealm = (source, context, options) => isContext(context)
|
|
38
|
+
? runInContext(source, context, options)
|
|
39
|
+
: runInThisContext(source, options)
|
|
40
|
+
|
|
41
|
+
function createPrivateParser() {
|
|
42
|
+
const require = createRequire(import.meta.url)
|
|
43
|
+
const exports = objectCreate(null)
|
|
44
|
+
const module = objectCreate(null)
|
|
45
|
+
module.exports = exports
|
|
46
|
+
const context = createContext({ exports, module })
|
|
47
|
+
runInContext(readFileSync(require.resolve('acorn'), 'utf8'), context,
|
|
48
|
+
{ filename: require.resolve('acorn') })
|
|
49
|
+
return module.exports.parse
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const parse = createPrivateParser()
|
|
53
|
+
|
|
54
|
+
function parseProgram(program) {
|
|
55
|
+
const options = objectCreate(null)
|
|
56
|
+
options.ecmaVersion = 'latest'
|
|
57
|
+
options.allowAwaitOutsideFunction = true
|
|
58
|
+
return parse(program, options)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function containsAwait(node) {
|
|
62
|
+
if (node === null || typeof node !== 'object') return false
|
|
63
|
+
if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression'
|
|
64
|
+
|| node.type === 'ArrowFunctionExpression') return false
|
|
65
|
+
if (node.type === 'AwaitExpression' || node.await === true) return true
|
|
66
|
+
const keys = objectKeys(node)
|
|
67
|
+
for (let index = 0; index < keys.length; index += 1) {
|
|
68
|
+
const key = keys[index]
|
|
69
|
+
if (setHas(AST_METADATA_KEYS, key)) continue
|
|
70
|
+
const value = node[key]
|
|
71
|
+
if (isArray(value)) {
|
|
72
|
+
for (let child = 0; child < value.length; child += 1) {
|
|
73
|
+
if (containsAwait(value[child])) return true
|
|
74
|
+
}
|
|
75
|
+
} else if (containsAwait(value)) return true
|
|
76
|
+
}
|
|
77
|
+
return false
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function recordBindingNames(pattern, names) {
|
|
81
|
+
if (pattern === null || typeof pattern !== 'object') return
|
|
82
|
+
if (pattern.type === 'Identifier') {
|
|
83
|
+
setAdd(names, pattern.name)
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
if (pattern.type === 'RestElement') return recordBindingNames(pattern.argument, names)
|
|
87
|
+
if (pattern.type === 'AssignmentPattern') return recordBindingNames(pattern.left, names)
|
|
88
|
+
if (pattern.type === 'ArrayPattern') {
|
|
89
|
+
for (let index = 0; index < pattern.elements.length; index += 1) {
|
|
90
|
+
recordBindingNames(pattern.elements[index], names)
|
|
91
|
+
}
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
if (pattern.type === 'ObjectPattern') {
|
|
95
|
+
for (let index = 0; index < pattern.properties.length; index += 1) {
|
|
96
|
+
const property = pattern.properties[index]
|
|
97
|
+
recordBindingNames(property.type === 'RestElement' ? property.argument : property.value, names)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
19
101
|
|
|
20
102
|
function primitiveText(value) {
|
|
21
|
-
if (typeof value === 'string') return
|
|
103
|
+
if (typeof value === 'string') return jsonStringify(sliceString(value, 0, MAX_TEXT))
|
|
22
104
|
if (typeof value === 'bigint') return value > -BIGINT_LIMIT && value < BIGINT_LIMIT
|
|
23
|
-
? `${
|
|
105
|
+
? `${bigintToString(value)}n` : '[bigint: more than 128 digits]'
|
|
24
106
|
if (typeof value === 'symbol' || typeof value === 'function') return `[${typeof value}: unreadable]`
|
|
25
107
|
if (value === null) return 'null'
|
|
26
108
|
if (typeof value === 'object') return isProxy(value) ? '[proxy: unreadable]' : '[object]'
|
|
27
|
-
return
|
|
109
|
+
return objectIs(value, -0) ? '-0' : toString(value)
|
|
28
110
|
}
|
|
29
111
|
|
|
30
112
|
/** Inspect own descriptors only, rejecting proxies before any reflective operation. */
|
|
31
113
|
export function previewBindingValue(value) {
|
|
32
|
-
if (
|
|
114
|
+
if (typeof value === 'function' || typeof value === 'symbol') return unreadable()
|
|
33
115
|
if (value === null || typeof value !== 'object') {
|
|
34
116
|
const text = primitiveText(value)
|
|
35
|
-
return { status: 'readable', text: text
|
|
117
|
+
return { status: 'readable', text: sliceString(text, 0, MAX_TEXT), truncated: text.length > MAX_TEXT
|
|
36
118
|
|| (typeof value === 'bigint' && (value <= -BIGINT_LIMIT || value >= BIGINT_LIMIT)) }
|
|
37
119
|
}
|
|
38
120
|
// Only arrays provide a finite slot range without enumerating the whole object.
|
|
39
121
|
if (isProxy(value) || !isArray(value)) return unreadable()
|
|
40
|
-
const keys =
|
|
41
|
-
const
|
|
122
|
+
const keys = []
|
|
123
|
+
const length = min(ownDescriptor(value, 'length').value, MAX_PROPERTIES)
|
|
124
|
+
for (let index = 0; index < length; index += 1) appendArray(keys, toString(index))
|
|
125
|
+
const properties = []
|
|
126
|
+
for (let index = 0; index < keys.length; index += 1) {
|
|
127
|
+
const key = keys[index]
|
|
42
128
|
const descriptor = ownDescriptor(value, key)
|
|
43
129
|
const text = descriptor === undefined ? '[empty]'
|
|
44
|
-
:
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
|
|
130
|
+
: objectHasOwn(descriptor, 'value') ? primitiveText(descriptor.value) : '[accessor: unreadable]'
|
|
131
|
+
appendArray(properties, `${jsonStringify(key)}: ${text}`)
|
|
132
|
+
}
|
|
133
|
+
let text = 'array { '
|
|
134
|
+
for (let index = 0; index < properties.length; index += 1) {
|
|
135
|
+
if (index > 0) text += ', '
|
|
136
|
+
text += properties[index]
|
|
137
|
+
}
|
|
138
|
+
text += ' }'
|
|
48
139
|
// Uninspected own properties remain unknown even when all indexed slots fit.
|
|
49
|
-
return { status: 'readable', text: text
|
|
140
|
+
return { status: 'readable', text: sliceString(text, 0, MAX_TEXT), truncated: true }
|
|
50
141
|
}
|
|
51
142
|
|
|
52
143
|
/** Probe the actual evaluator without consulting a Node version or any session value. */
|
|
53
144
|
export async function supportsAwaitLexicals(context, evaluate) {
|
|
54
|
-
const prefix = `__ptc_observation_${randomUUID()
|
|
145
|
+
const prefix = `__ptc_observation_${replaceAllString(randomUUID(), '-', '')}`
|
|
55
146
|
const names = [`${prefix}_let`, `${prefix}_const`]
|
|
56
147
|
try {
|
|
57
148
|
await evaluate(`let ${names[0]} = await Promise.resolve(41); const ${names[1]} = 42;`)
|
|
58
|
-
|
|
59
|
-
|
|
149
|
+
for (let index = 0; index < names.length; index += 1) {
|
|
150
|
+
if (ownDescriptor(context, names[index]) !== undefined
|
|
151
|
+
|| runInRealm(names[index], context, { timeout: 25 }) !== index + 41) return false
|
|
152
|
+
}
|
|
153
|
+
return true
|
|
60
154
|
} catch {
|
|
61
155
|
return false
|
|
62
156
|
} finally {
|
|
63
157
|
// Lexical probe names are private and never enter the binding inventory.
|
|
64
|
-
for (
|
|
158
|
+
for (let index = 0; index < names.length; index += 1) {
|
|
159
|
+
deleteWorkerRealmProperty(
|
|
160
|
+
WORKER_REALM_MUTATION.RESTORE,
|
|
161
|
+
'await lexical probe',
|
|
162
|
+
context,
|
|
163
|
+
names[index],
|
|
164
|
+
)
|
|
165
|
+
}
|
|
65
166
|
}
|
|
66
167
|
}
|
|
67
168
|
|
|
68
169
|
/** Successful programs prove storage; await lexical reads additionally require a runtime probe. */
|
|
69
170
|
export function createReplValueObserver(context, { awaitLexicals = false } = {}) {
|
|
70
|
-
const lexicals = new
|
|
71
|
-
const globals = new
|
|
171
|
+
const lexicals = new PrivateSet()
|
|
172
|
+
const globals = new PrivateSet()
|
|
72
173
|
return {
|
|
73
174
|
record(program) {
|
|
74
175
|
try {
|
|
75
|
-
const tree =
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (node.type === 'AwaitExpression' || node.await === true) awaits = true
|
|
80
|
-
})
|
|
81
|
-
if (awaits && !awaitLexicals) return
|
|
82
|
-
for (const statement of tree.body) {
|
|
176
|
+
const tree = parseProgram(program)
|
|
177
|
+
if (containsAwait(tree) && !awaitLexicals) return
|
|
178
|
+
for (let statementIndex = 0; statementIndex < tree.body.length; statementIndex += 1) {
|
|
179
|
+
const statement = tree.body[statementIndex]
|
|
83
180
|
if (statement.type !== 'VariableDeclaration') continue
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
181
|
+
const names = statement.kind === 'var' ? globals : lexicals
|
|
182
|
+
for (let index = 0; index < statement.declarations.length; index += 1) {
|
|
183
|
+
recordBindingNames(statement.declarations[index].id, names)
|
|
88
184
|
}
|
|
89
185
|
}
|
|
90
186
|
} catch {}
|
|
91
187
|
},
|
|
92
188
|
observe(names) {
|
|
93
189
|
const at = now()
|
|
94
|
-
const entries =
|
|
190
|
+
const entries = []
|
|
191
|
+
const length = min(names.length, MAX_ENTRIES)
|
|
192
|
+
for (let index = 0; index < length; index += 1) {
|
|
193
|
+
const name = names[index]
|
|
95
194
|
let preview = unreadable()
|
|
96
195
|
try {
|
|
97
|
-
if (lexicals
|
|
196
|
+
if (setHas(lexicals, name)) {
|
|
98
197
|
// Lexical storage shadows global properties, even while uninitialized.
|
|
99
|
-
if (
|
|
100
|
-
preview = previewBindingValue(
|
|
198
|
+
if (regexpTest(IDENTIFIER, name) && now() - at < 100) {
|
|
199
|
+
preview = previewBindingValue(runInRealm(name, context, { timeout: 25, displayErrors: false }))
|
|
101
200
|
}
|
|
102
|
-
} else if (globals
|
|
201
|
+
} else if (setHas(globals, name)) {
|
|
103
202
|
const descriptor = ownDescriptor(context, name)
|
|
104
|
-
if (descriptor !== undefined &&
|
|
203
|
+
if (descriptor !== undefined && objectHasOwn(descriptor, 'value')) {
|
|
105
204
|
preview = previewBindingValue(descriptor.value)
|
|
106
205
|
}
|
|
107
206
|
}
|
|
108
207
|
} catch {}
|
|
109
|
-
|
|
110
|
-
}
|
|
208
|
+
appendArray(entries, { name, ...preview })
|
|
209
|
+
}
|
|
111
210
|
return { at, entries }
|
|
112
211
|
},
|
|
113
212
|
}
|