dsh-ptc-plus 0.3.2 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +65 -78
- package/README.md +65 -78
- package/assets/ptc-plus-bindings-en.png +0 -0
- package/assets/ptc-plus-bindings-zh.png +0 -0
- package/assets/ptc-plus-repair-en.png +0 -0
- package/assets/ptc-plus-repair-zh.png +0 -0
- package/assets/ptc-plus-repl-workspace-en.png +0 -0
- package/assets/ptc-plus-repl-workspace-zh.png +0 -0
- package/assets/ptc-plus-settings-en.png +0 -0
- package/assets/ptc-plus-settings-zh.png +0 -0
- package/client.js +30596 -742
- package/compiler-amaro.cjs +5 -0
- package/compiler-amaro.cjs.map +6 -0
- package/compiler-asset-124f80c1bba92ff5fe79cfec6e8ad518f476de4a42514cae3919538887f510e8.txt +1 -0
- package/compiler-assets.json +3 -0
- package/compiler-core.cjs +554 -0
- package/compiler-core.cjs.map +6 -0
- package/compiler-platform.cjs +2 -0
- package/compiler-platform.cjs.map +6 -0
- package/compiler-typescript.cjs +388 -0
- package/compiler-typescript.cjs.map +6 -0
- package/docs/README.md +6 -0
- package/docs/adr/0004-declare-program-bindings-at-the-host.md +1 -1
- package/docs/adr/0006-rewrite-module-syntax-with-an-ast.md +5 -3
- package/docs/adr/0007-separate-worker-transport-from-session-semantics.md +1 -1
- package/docs/adr/0009-bound-expensive-acceptance-default.md +2 -0
- package/docs/adr/0010-session-log-derived-recovery-tips.md +59 -5
- package/docs/adr/0011-isolate-the-local-recovery-tip-provider.md +1 -1
- package/docs/adr/0012-preserve-mixed-destructuring-initialization.md +3 -1
- package/docs/adr/0013-isolate-repl-redeclaration-convenience.md +3 -1
- package/docs/adr/0014-persist-cell-rewrite-policy.md +6 -2
- package/docs/adr/0015-preserve-source-positions-through-rewrites.md +1 -1
- package/docs/adr/0016-archive-resolved-review-findings.md +1 -1
- package/docs/adr/0017-track-the-latest-dsh-public-surface.md +49 -0
- package/docs/adr/0018-use-versioned-fixture-for-ab-benchmark.md +3 -3
- package/docs/adr/0019-plugin-settings-and-kill-switch.md +5 -3
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
- package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +12 -0
- package/docs/adr/0023-global-user-bindings.md +104 -0
- package/docs/adr/0024-repl-console-observation.md +33 -0
- package/docs/adr/0025-use-versioned-logical-binding-identities.md +119 -0
- package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +21 -0
- package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +37 -0
- package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +103 -0
- package/docs/adr/0029-keep-plugin-unavailability-out-of-the-host-agent-lifecycle.md +27 -0
- package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +27 -0
- package/docs/architecture.md +73 -21
- package/docs/binding-review-placement-design.md +200 -0
- package/docs/capability-projection.md +1 -1
- package/docs/client-ui.md +95 -19
- package/docs/durability-design.md +25 -10
- package/docs/evaluation.md +78 -6
- package/docs/installation.md +41 -6
- package/docs/publishing.md +5 -3
- package/docs/runtime-reference.md +72 -25
- package/docs/semantic-validation.md +29 -0
- package/docs/stateful-binding-updates-proposal.md +755 -0
- package/docs/user-bindings.en.md +64 -0
- package/docs/user-bindings.md +64 -0
- package/docs/value-wire.md +3 -3
- package/docs/verification.md +57 -0
- package/index.js +369 -92
- package/internal/ast-traversal.js +5 -2
- package/internal/binding-descriptors.js +1 -0
- package/internal/binding-pattern-traversal.js +25 -0
- package/internal/binding-pattern.js +26 -29
- package/internal/binding-source-analysis.js +391 -0
- package/internal/binding-update-policy.js +66 -0
- package/internal/bounded-worker-round.js +56 -0
- package/internal/callable-definition-environment.js +42 -0
- package/internal/callable-source-catalog.js +129 -0
- package/internal/callable-source-encoding.js +47 -0
- package/internal/callable-source-facts.js +459 -0
- package/internal/callable-source-format.js +2 -0
- package/internal/callable-source-lowering.js +123 -0
- package/internal/cell-analysis-contract.js +25 -0
- package/internal/cell-analysis.js +240 -290
- package/internal/cell-completion.js +15 -0
- package/internal/cell-error.js +7 -0
- package/internal/cell-parser.js +66 -0
- package/internal/cell-rewriter.js +3 -677
- package/internal/commonjs-export-evidence.js +63 -0
- package/internal/commonjs-source-evidence.js +72 -0
- package/internal/compiler-amaro.cjs +2 -0
- package/internal/compiler-compression.js +12 -0
- package/internal/compiler-data.js +58 -0
- package/internal/compiler-descriptors.js +54 -0
- package/internal/compiler-dynamic-cache.js +48 -0
- package/internal/compiler-entry.js +35 -0
- package/internal/compiler-generated-names.js +17 -0
- package/internal/compiler-intrinsics.js +84 -0
- package/internal/compiler-module-links.js +34 -0
- package/internal/compiler-operations.js +61 -0
- package/internal/compiler-platform-factory.js +84 -0
- package/internal/compiler-platform.js +136 -0
- package/internal/compiler-record-roles.js +53 -0
- package/internal/compiler-region-output.js +259 -0
- package/internal/compiler-resource-lowering.js +103 -0
- package/internal/compiler-scope-facts.js +115 -0
- package/internal/compiler-service.js +109 -0
- package/internal/compiler-source-cache.js +32 -0
- package/internal/compiler-source-pieces.js +93 -0
- package/internal/compiler-source-regions.js +132 -0
- package/internal/compiler-storage-source.js +54 -0
- package/internal/compiler-text.js +15 -0
- package/internal/compiler-typescript.cjs +2 -0
- package/internal/config-spec.js +83 -27
- package/internal/cordis-tools-owner.js +130 -21
- package/internal/diagnostic.js +27 -1
- package/internal/direct-surface-owner.js +76 -23
- package/internal/dynamic-binding-evidence.js +16 -0
- package/internal/dynamic-environment-compiler.js +779 -0
- package/internal/dynamic-environment-integration.js +87 -0
- package/internal/dynamic-environment-runtime.js +362 -0
- package/internal/dynamic-native-calls.js +150 -0
- package/internal/dynamic-native-runtime.js +248 -0
- package/internal/dynamic-scope-analysis.js +90 -0
- package/internal/edit-transport-owner.js +27 -2
- package/internal/execution-seam-compat.js +193 -0
- package/internal/failure-reporting.js +118 -18
- package/internal/host-rpc.js +66 -0
- package/internal/isolated-worker.js +548 -0
- package/internal/kernel-child.js +139 -0
- package/internal/kernel-worker.js +904 -120
- package/internal/language-semantics.js +14 -0
- package/internal/legacy-binding-catalog.js +48 -0
- package/internal/legacy-cell-analysis.js +600 -0
- package/internal/legacy-cell-parser.js +50 -0
- package/internal/legacy-cell-rewriter.js +631 -0
- package/internal/legacy-repl-convenience.js +291 -0
- package/internal/managed-module-operations.js +170 -0
- package/internal/module-compilation.js +321 -0
- package/internal/module-runtime-platform.js +19 -0
- package/internal/module-transform-contract.js +12 -0
- package/internal/native-await-declarations.js +68 -0
- package/internal/native-root-compilation.js +54 -0
- package/internal/native-root-dynamic.js +67 -0
- package/internal/recovery-tips.js +27 -15
- package/internal/rejected-cell-editor.js +13 -8
- package/internal/repl-convenience.js +2 -289
- package/internal/repl-memory-projection.js +81 -12
- package/internal/repl-observation-interest.js +70 -0
- package/internal/repl-rewrite-contract.js +6 -0
- package/internal/repl-scope-normalizer.js +2091 -0
- package/internal/repl-value-observer.js +114 -0
- package/internal/rpc-contract.js +52 -0
- package/internal/runtime-bridge-owner.js +172 -28
- package/internal/runtime-config.js +48 -52
- package/internal/runtime-contexts.js +82 -10
- package/internal/runtime-messages.js +111 -0
- package/internal/session-cell-executor.js +401 -87
- package/internal/session-journal-recovery.js +684 -0
- package/internal/session-journal-schema.js +106 -0
- package/internal/session-journal.js +184 -654
- package/internal/session-log-view.js +42 -9
- package/internal/session-runtime.js +256 -95
- package/internal/session-state.js +247 -71
- package/internal/source-position-map.js +211 -71
- package/internal/stateful-module-compiler.js +154 -0
- package/internal/stateful-module-runtime.js +303 -0
- package/internal/stateful-root-compiler.js +598 -0
- package/internal/stateful-root-runtime.js +340 -0
- package/internal/typert-codec-compat.js +28 -0
- package/internal/typescript-transform.js +29 -0
- package/internal/user-binding-authoring.js +70 -0
- package/internal/user-binding-console-worker.js +184 -0
- package/internal/user-binding-console.js +148 -0
- package/internal/user-binding-draft-projection.js +335 -0
- package/internal/user-binding-model-context.js +31 -0
- package/internal/user-binding-runner.js +56 -0
- package/internal/user-bindings-owner.js +1065 -0
- package/internal/user-bindings-store.js +293 -0
- package/internal/user-bindings.js +353 -0
- package/internal/value-wire-schema.js +28 -0
- package/internal/value-wire.js +29 -20
- package/internal/worker-client.js +57 -67
- package/internal/worker-environment.js +45 -0
- package/internal/worker-output-relay.js +29 -0
- package/internal/worker-shutdown.js +14 -0
- package/package.json +75 -7
- package/src/client-activity.js +88 -53
- package/src/client-authoring-view.js +580 -0
- package/src/client-binding-review.js +230 -0
- package/src/client-bindings-data.js +29 -0
- package/src/client-catalog.js +127 -0
- package/src/client-code-editor.js +84 -0
- package/src/client-console.js +112 -0
- package/src/client-copy.js +370 -0
- package/src/client-feature-gates.js +62 -0
- package/src/client-host-compat.js +150 -0
- package/src/client-indicator-view.js +267 -0
- package/src/client-repl-view.js +292 -0
- package/src/client-rpc.js +20 -0
- package/src/client-settings-view.js +221 -0
- package/src/client-styles.js +106 -0
- package/src/client-tool-view.js +108 -0
- package/src/client-transport.js +159 -0
- package/src/client-workbench.js +521 -0
- package/src/client.js +233 -615
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks'
|
|
2
|
+
import { createContext, runInContext } from 'node:vm'
|
|
3
|
+
import { recordExceptionOrigin } from './failure-reporting.js'
|
|
4
|
+
import { createCallableSourceRegistry } from './callable-source-catalog.js'
|
|
5
|
+
import { captureCompilerIntrinsics, moduleRuntimeIntrinsics as internal } from './compiler-intrinsics.js'
|
|
6
|
+
|
|
7
|
+
const realmStates = new internal.WeakMap()
|
|
8
|
+
const exposedIntrinsics = new internal.WeakMap()
|
|
9
|
+
// Source facts describe canonical values and emitted source, independent of
|
|
10
|
+
// which owned realm's toString observes a callable crossing a module boundary.
|
|
11
|
+
const sources = createCallableSourceRegistry()
|
|
12
|
+
const callableSources = new internal.WeakMap()
|
|
13
|
+
const callableSourceTargets = new internal.WeakMap()
|
|
14
|
+
const sourceTarget = origin => typeof origin === 'string' ? origin : origin?.target
|
|
15
|
+
const copyArguments = (source, start = 0, result = []) => internal.copyArray(source, start, source.length, result)
|
|
16
|
+
const invocationScopes = new AsyncLocalStorage()
|
|
17
|
+
const getScope = AsyncLocalStorage.prototype.getStore
|
|
18
|
+
const enterScope = AsyncLocalStorage.prototype.enterWith
|
|
19
|
+
const currentScope = () => internal.Reflect.apply(getScope, invocationScopes, [])
|
|
20
|
+
const selectScope = value => internal.Reflect.apply(enterScope, invocationScopes, [value])
|
|
21
|
+
// Node 22 installs promise hooks lazily through a host array iterator. Complete
|
|
22
|
+
// that platform initialization before source can mutate the module realm.
|
|
23
|
+
selectScope(undefined)
|
|
24
|
+
// Native eval has no callable activation in the source caller chain. Its
|
|
25
|
+
// private realm gives each invocation a native try/finally lifetime without
|
|
26
|
+
// moving source parameters or declarations, or changing source realm globals.
|
|
27
|
+
let pendingInvocation
|
|
28
|
+
const invocationRealm = createContext({
|
|
29
|
+
take() {
|
|
30
|
+
const invocation = pendingInvocation
|
|
31
|
+
pendingInvocation = undefined
|
|
32
|
+
return invocation
|
|
33
|
+
},
|
|
34
|
+
enter(invocation) {
|
|
35
|
+
const previous = currentScope()
|
|
36
|
+
selectScope({ invoke: invocation.invoke, origin: invocation.origin,
|
|
37
|
+
interface: invocation.interface, parent: previous })
|
|
38
|
+
return previous
|
|
39
|
+
},
|
|
40
|
+
failed(invocation, error) { recordExceptionOrigin(error, sourceTarget(invocation.origin)) },
|
|
41
|
+
leave(previous) { selectScope(previous) },
|
|
42
|
+
})
|
|
43
|
+
// A distinct source identity keeps V8 eval-origin notes out of user frames.
|
|
44
|
+
const nativeContinuation = runInContext(`eval.bind(undefined, ${JSON.stringify(`
|
|
45
|
+
const invocation = take();
|
|
46
|
+
const previous = enter(invocation);
|
|
47
|
+
try {
|
|
48
|
+
while (invocation.advance(invocation)) invocation.args = invocation.collect(invocation.arrayLike);
|
|
49
|
+
invocation.complete(invocation, invocation.dispatch())
|
|
50
|
+
}
|
|
51
|
+
catch (error) { failed(invocation, error); throw error }
|
|
52
|
+
finally { leave(previous) }
|
|
53
|
+
//# sourceURL=ptc-plus:native-invocation
|
|
54
|
+
`)})`, invocationRealm)
|
|
55
|
+
|
|
56
|
+
/** Native callable identities are classified once for their owning realm. */
|
|
57
|
+
export function createNativeCallAdapter({ realmFunction, intrinsicEval, reflect, errors, evaluate, rootEnvironment, interfaceOwner }) {
|
|
58
|
+
reflect = { apply: reflect.apply, construct: reflect.construct }
|
|
59
|
+
const { getPrototypeOf, setPrototypeOf } = captureCompilerIntrinsics(realmFunction).Object
|
|
60
|
+
let state = internal.weakMapGet(realmStates, realmFunction)
|
|
61
|
+
if (state === undefined) {
|
|
62
|
+
const intrinsics = realmFunction('return {constructors:[Function,(async function(){}).constructor,(function*(){}).constructor,(async function*(){}).constructor],call:Function.prototype.call,apply:Function.prototype.apply,bind:Function.prototype.bind,toString:Function.prototype.toString,reflectApply:Reflect.apply,reflectConstruct:Reflect.construct}')()
|
|
63
|
+
const constructors = new internal.Set()
|
|
64
|
+
for (let index = 0; index < intrinsics.constructors.length; index++) internal.setAdd(constructors, intrinsics.constructors[index])
|
|
65
|
+
state = { ...intrinsics, realmFunction, intrinsicEval, constructors,
|
|
66
|
+
bound: new internal.WeakMap(), prepared: new internal.WeakMap(), sources, callableSources, callableSourceTargets,
|
|
67
|
+
exposed: new internal.Map(), interfaces: new internal.WeakMap(),
|
|
68
|
+
nativeApply: reflect.apply(intrinsics.bind, intrinsics.reflectApply, [undefined]),
|
|
69
|
+
nativeConstruct: reflect.apply(intrinsics.bind, intrinsics.reflectConstruct, [undefined]),
|
|
70
|
+
templateArguments: realmFunction('return Array.of')() }
|
|
71
|
+
internal.weakMapSet(realmStates, realmFunction, state)
|
|
72
|
+
}
|
|
73
|
+
let selected = interfaceOwner === undefined ? state : internal.weakMapGet(state.interfaces, interfaceOwner)
|
|
74
|
+
if (selected === undefined) {
|
|
75
|
+
selected = { exposed: new internal.Map() }
|
|
76
|
+
internal.weakMapSet(state.interfaces, interfaceOwner, selected)
|
|
77
|
+
}
|
|
78
|
+
// Realm owners share intrinsic classification and source facts. An explicit
|
|
79
|
+
// generation owner retains its own root through opaque and asynchronous calls.
|
|
80
|
+
const install = () => {
|
|
81
|
+
if (selected.installed) return
|
|
82
|
+
selected.installed = true
|
|
83
|
+
const expose = (target, constructable = false) => {
|
|
84
|
+
const dispatch = (receiver, args, newTarget) => {
|
|
85
|
+
const active = currentScope()
|
|
86
|
+
const owner = active?.interface === selected ? active : undefined
|
|
87
|
+
return (owner?.invoke ?? invoke)(target, receiver, args, owner?.origin, newTarget)
|
|
88
|
+
}
|
|
89
|
+
const wrapper = new internal.Proxy(target, {
|
|
90
|
+
apply: (_, receiver, args) => dispatch(receiver, args),
|
|
91
|
+
...(constructable ? { construct: (_, args, newTarget) => dispatch(undefined, args, newTarget) } : {}),
|
|
92
|
+
})
|
|
93
|
+
internal.mapSet(selected.exposed, target, wrapper)
|
|
94
|
+
if (interfaceOwner === undefined) internal.weakMapSet(exposedIntrinsics, target, wrapper)
|
|
95
|
+
internal.weakMapSet(state.callableSources, wrapper, reflect.apply(state.toString, target, []))
|
|
96
|
+
return wrapper
|
|
97
|
+
}
|
|
98
|
+
// Source reads select interfaces without replacing native global or
|
|
99
|
+
// prototype properties, including in escaped asynchronous continuations.
|
|
100
|
+
expose(state.intrinsicEval)
|
|
101
|
+
expose(state.toString)
|
|
102
|
+
internal.setForEach(state.constructors, constructor => expose(constructor, true))
|
|
103
|
+
const exposedFunction = internal.mapGet(selected.exposed, state.realmFunction)
|
|
104
|
+
internal.weakMapSet(realmStates, exposedFunction, state)
|
|
105
|
+
}
|
|
106
|
+
const isNativeConstructor = target => {
|
|
107
|
+
while (internal.weakMapHas(state.bound, target)) target = internal.weakMapGet(state.bound, target).callee
|
|
108
|
+
return internal.setHas(state.constructors, target)
|
|
109
|
+
}
|
|
110
|
+
const adaptConstructed = (native, origin) => {
|
|
111
|
+
const source = reflect.apply(state.toString, native, [])
|
|
112
|
+
const compiled = evaluate(state.intrinsicEval, undefined, [`(${source})`], rootEnvironment(sourceTarget(origin)), { functionConstructor: true })
|
|
113
|
+
setPrototypeOf(compiled, getPrototypeOf(native))
|
|
114
|
+
internal.weakMapSet(state.callableSources, compiled, source)
|
|
115
|
+
return compiled
|
|
116
|
+
}
|
|
117
|
+
const exposedValue = value => internal.mapGet(selected.exposed, value) ?? internal.weakMapGet(exposedIntrinsics, value) ?? value
|
|
118
|
+
const propagate = (error, origin) => {
|
|
119
|
+
recordExceptionOrigin(error, sourceTarget(origin), { reset: true })
|
|
120
|
+
for (let scope = currentScope(); scope !== undefined; scope = scope.parent) {
|
|
121
|
+
recordExceptionOrigin(error, sourceTarget(scope.origin))
|
|
122
|
+
}
|
|
123
|
+
return error
|
|
124
|
+
}
|
|
125
|
+
const invoke = (callee, receiver, args, origin, newTarget) => {
|
|
126
|
+
// Exposed intrinsic proxies already selected their owning interface.
|
|
127
|
+
const plan = invocation(callee, receiver, origin, false, newTarget !== undefined, false)
|
|
128
|
+
plan.newTarget = newTarget
|
|
129
|
+
return beginInvocation(plan, args)()
|
|
130
|
+
}
|
|
131
|
+
const prepareCall = (callee, receiver, origin, optional = false) => {
|
|
132
|
+
const plan = invocation(callee, receiver, origin, optional)
|
|
133
|
+
return plan === undefined ? undefined : prepareTagInvocation(plan)
|
|
134
|
+
}
|
|
135
|
+
const invocation = (callee, receiver, origin, optional = false, construct = false, selectInterface = true) => {
|
|
136
|
+
if (optional && callee == null) return undefined
|
|
137
|
+
const reference = internal.weakMapGet(state.prepared, callee)
|
|
138
|
+
if (reference !== undefined && !construct) ({ callee, receiver } = reference)
|
|
139
|
+
return { callee: selectInterface ? exposedValue(callee) : callee, receiver, origin, construct }
|
|
140
|
+
}
|
|
141
|
+
const validateInvocation = ({ callee, origin, construct }) => {
|
|
142
|
+
if (!construct && typeof callee !== 'function' && typeof origin?.callee === 'string') {
|
|
143
|
+
const error = new errors.TypeError(`${origin.callee} is not a function`)
|
|
144
|
+
recordExceptionOrigin(error, origin.target, { sourceFailure: true })
|
|
145
|
+
throw error
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const collectArguments = reflect.apply(state.bind, state.reflectApply, [undefined, state.templateArguments, undefined])
|
|
149
|
+
const intrinsicTarget = callee => {
|
|
150
|
+
while (internal.weakMapHas(state.bound, callee)) callee = internal.weakMapGet(state.bound, callee).callee
|
|
151
|
+
return internal.setHas(state.constructors, callee) || callee === state.intrinsicEval || callee === state.toString
|
|
152
|
+
}
|
|
153
|
+
// Native array-like conversion runs in the continuation, after target
|
|
154
|
+
// validation and before dispatch. User getters therefore retain their source
|
|
155
|
+
// caller, and nested call/apply aliases consume each argument list once.
|
|
156
|
+
const advanceInvocation = plan => {
|
|
157
|
+
while (true) {
|
|
158
|
+
const { callee, receiver, args, construct } = plan
|
|
159
|
+
const bound = internal.weakMapGet(state.bound, callee)
|
|
160
|
+
if (bound !== undefined && intrinsicTarget(callee)) {
|
|
161
|
+
plan.callee = bound.callee
|
|
162
|
+
if (!construct) plan.receiver = bound.receiver
|
|
163
|
+
if (plan.newTarget === callee) plan.newTarget = bound.callee
|
|
164
|
+
plan.args = copyArguments(args, 0, copyArguments(bound.args))
|
|
165
|
+
continue
|
|
166
|
+
}
|
|
167
|
+
if (!construct && callee === state.call) {
|
|
168
|
+
plan.callee = receiver
|
|
169
|
+
plan.receiver = args[0]
|
|
170
|
+
plan.args = copyArguments(args, 1)
|
|
171
|
+
continue
|
|
172
|
+
}
|
|
173
|
+
if (!construct && (callee === state.apply && typeof receiver === 'function'
|
|
174
|
+
|| callee === state.reflectApply && typeof args[0] === 'function')) {
|
|
175
|
+
plan.callee = callee === state.apply ? receiver : args[0]
|
|
176
|
+
plan.receiver = callee === state.apply ? args[0] : args[1]
|
|
177
|
+
plan.arrayLike = callee === state.apply ? args[1] : args[2]
|
|
178
|
+
if (callee === state.apply && plan.arrayLike == null) { plan.args = []; continue }
|
|
179
|
+
return true
|
|
180
|
+
}
|
|
181
|
+
plan.adaptConstructed = internal.setHas(state.constructors, callee)
|
|
182
|
+
|| !construct && callee === state.reflectConstruct && isNativeConstructor(args[0])
|
|
183
|
+
plan.dispatch = !construct && callee === state.intrinsicEval
|
|
184
|
+
? () => evaluate(callee, receiver, args, rootEnvironment(sourceTarget(plan.origin)))
|
|
185
|
+
: reflect.apply(state.bind, construct ? state.nativeConstruct : state.nativeApply,
|
|
186
|
+
construct ? [undefined, callee, args, plan.newTarget] : [undefined, callee, receiver, args])
|
|
187
|
+
return false
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const completeInvocation = (plan, value) => {
|
|
191
|
+
if (plan.adaptConstructed) return adaptConstructed(value, plan.origin)
|
|
192
|
+
if (!plan.construct && plan.callee === state.bind) internal.weakMapSet(state.bound, value,
|
|
193
|
+
{ callee: plan.receiver, receiver: plan.args[0], args: copyArguments(plan.args, 1) })
|
|
194
|
+
if (!plan.construct && plan.callee === state.toString) {
|
|
195
|
+
let receiver = plan.receiver
|
|
196
|
+
while (!internal.weakMapHas(state.callableSources, receiver) && internal.weakMapHas(state.callableSourceTargets, receiver)) {
|
|
197
|
+
receiver = internal.weakMapGet(state.callableSourceTargets, receiver)
|
|
198
|
+
value = reflect.apply(state.toString, receiver, [])
|
|
199
|
+
}
|
|
200
|
+
return internal.weakMapGet(state.callableSources, receiver) ?? state.sources.get(value) ?? value
|
|
201
|
+
}
|
|
202
|
+
return exposedValue(value)
|
|
203
|
+
}
|
|
204
|
+
const beginInvocation = (plan, args) => {
|
|
205
|
+
validateInvocation(plan)
|
|
206
|
+
pendingInvocation = {
|
|
207
|
+
...plan, args, invoke, interface: selected, complete: completeInvocation,
|
|
208
|
+
newTarget: plan.construct && plan.newTarget === undefined ? plan.callee : plan.newTarget,
|
|
209
|
+
advance: advanceInvocation, collect: collectArguments,
|
|
210
|
+
}
|
|
211
|
+
return nativeContinuation
|
|
212
|
+
}
|
|
213
|
+
const prepareTagInvocation = plan => {
|
|
214
|
+
const result = (...args) => invoke(plan.callee, plan.receiver, args, plan.origin)
|
|
215
|
+
internal.weakMapSet(state.prepared, result, plan)
|
|
216
|
+
return result
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
install,
|
|
220
|
+
exposedValue,
|
|
221
|
+
isEval: callee => callee === state.intrinsicEval || internal.mapHas(selected.exposed, state.intrinsicEval) && callee === internal.mapGet(selected.exposed, state.intrinsicEval),
|
|
222
|
+
evaluate(compiled, environment) {
|
|
223
|
+
// Bootstrap originals never leave this owner. Direct eval must use the
|
|
224
|
+
// actual intrinsic identity inside its private explicit lexical frame.
|
|
225
|
+
const run = state.realmFunction(compiled.environmentName, 'eval', 'source', 'return eval(source)')
|
|
226
|
+
return reflect.apply(run, undefined, [environment, state.intrinsicEval, compiled.code])
|
|
227
|
+
},
|
|
228
|
+
declareGlobals(names) { if (names.length > 0) reflect.apply(state.intrinsicEval, undefined, [`var ${internal.join(names, ',')};`]) },
|
|
229
|
+
propagate,
|
|
230
|
+
prepareCall,
|
|
231
|
+
prepareInvocation: invocation,
|
|
232
|
+
releaseMemberReceiver(receiver) { return receiver },
|
|
233
|
+
prepareMemberInvocation(receiver, callee, origin, optional) { return invocation(callee, receiver, origin, optional) },
|
|
234
|
+
beginInvocation,
|
|
235
|
+
prepareTagInvocation,
|
|
236
|
+
templateArguments: state.templateArguments,
|
|
237
|
+
referenceCall(callee, receiver) {
|
|
238
|
+
if (callee == null) return callee
|
|
239
|
+
callee = exposedValue(callee)
|
|
240
|
+
const result = (...args) => invoke(callee, receiver, args)
|
|
241
|
+
internal.weakMapSet(state.prepared, result, { callee, receiver })
|
|
242
|
+
return result
|
|
243
|
+
},
|
|
244
|
+
registerCallableSourceTarget(adapter, target) { internal.weakMapSet(state.callableSourceTargets, adapter, target); return adapter },
|
|
245
|
+
registerSources(sources) { state.sources.register(sources) },
|
|
246
|
+
originalSource(source) { return state.sources.get(source) },
|
|
247
|
+
}
|
|
248
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { visitSource } from './compiler-source-regions.js'
|
|
2
|
+
|
|
3
|
+
/** A switch creates its case environment after evaluating the discriminant.
|
|
4
|
+
* Babel parents nested discriminant scopes to the switch, so filter that
|
|
5
|
+
* synthetic edge using the reference's actual source ancestry. */
|
|
6
|
+
export function* sourceScopePath(path, origin = path) {
|
|
7
|
+
const excluded = new Set()
|
|
8
|
+
for (let child = origin; child.parentPath; child = child.parentPath) {
|
|
9
|
+
if (child.key === 'discriminant' && child.parentPath.isSwitchStatement()) excluded.add(child.parentPath.scope)
|
|
10
|
+
}
|
|
11
|
+
for (let scope = path.scope; scope; scope = scope.parent) {
|
|
12
|
+
if (!excluded.has(scope)) yield scope
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function sourceBinding(path, name) {
|
|
17
|
+
for (const scope of sourceScopePath(path)) {
|
|
18
|
+
const binding = scope.getOwnBinding(name)
|
|
19
|
+
if (binding !== undefined && sourceBindingVisible(path, binding)) return binding
|
|
20
|
+
}
|
|
21
|
+
return undefined
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Lookup and dynamic descriptor capture share the same visibility test. */
|
|
25
|
+
export function sourceBindingVisible(path, binding) {
|
|
26
|
+
return !sourceInParameters(path, binding.scope) || binding.kind === 'param'
|
|
27
|
+
|| binding.kind === 'local' && binding.path.node === binding.scope.block
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** A parameter expression (including a closure inside it) cannot see its
|
|
31
|
+
* function's body declarations. Source ancestry owns this visibility edge. */
|
|
32
|
+
export function sourceInParameters(path, scope) {
|
|
33
|
+
for (let child = path; child.parentPath; child = child.parentPath) {
|
|
34
|
+
if (child.parentPath.node === scope.block) return child.listKey === 'params'
|
|
35
|
+
}
|
|
36
|
+
return false
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Source environments that can introduce a binding between a reference and
|
|
40
|
+
* its static owner. Strict closures still inherit outer sloppy activations. */
|
|
41
|
+
export function createDynamicScopeAnalysis(tree) {
|
|
42
|
+
const evalActivations = new WeakSet()
|
|
43
|
+
const parameterEvalActivations = new WeakSet()
|
|
44
|
+
const directEvalSubtrees = new WeakSet()
|
|
45
|
+
visitSource(tree, { CallExpression(path) {
|
|
46
|
+
if (path.node.callee.type !== 'Identifier' || path.node.callee.name !== 'eval') return
|
|
47
|
+
// Parameter plans need syntactic eval containment, including strict and
|
|
48
|
+
// nested callables; shadow introduction separately follows real owners.
|
|
49
|
+
for (let current = path; current && !directEvalSubtrees.has(current.node); current = current.parentPath) {
|
|
50
|
+
directEvalSubtrees.add(current.node)
|
|
51
|
+
}
|
|
52
|
+
if (path.isInStrictMode()) return
|
|
53
|
+
let child = path
|
|
54
|
+
for (let parent = path.parentPath; parent; child = parent, parent = parent.parentPath) {
|
|
55
|
+
if (parent.isFunction()) {
|
|
56
|
+
if (child.key === 'body') { evalActivations.add(parent.node); return }
|
|
57
|
+
if (child.listKey === 'params') { parameterEvalActivations.add(parent.node); return }
|
|
58
|
+
}
|
|
59
|
+
if (parent.isProgram()) evalActivations.add(parent.node)
|
|
60
|
+
}
|
|
61
|
+
} })
|
|
62
|
+
return {
|
|
63
|
+
containsDirectEval(node) { return directEvalSubtrees.has(node) },
|
|
64
|
+
mayEvalShadow(path, ownerNode, self = false) {
|
|
65
|
+
let child = path
|
|
66
|
+
for (let parent = path.parentPath; parent; child = parent, parent = parent.parentPath) {
|
|
67
|
+
if (parent.node === ownerNode && !self) return false
|
|
68
|
+
if (evalActivations.has(parent.node) && (parent.isProgram() || child.key === 'body')) return true
|
|
69
|
+
if (parameterEvalActivations.has(parent.node) && (child.key === 'body' || child.listKey === 'params')) return true
|
|
70
|
+
if (parent.node === ownerNode) return false
|
|
71
|
+
}
|
|
72
|
+
return false
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Var storage belongs to its variable scope, while initialization can select
|
|
78
|
+
* a simple catch binding or an intervening with object. Ordinary block lexical
|
|
79
|
+
* declarations retain their independent stateful identity. */
|
|
80
|
+
export function varInitializerTarget(path, ownerNode, name, sourceName = node => node.name) {
|
|
81
|
+
const withNodes = []
|
|
82
|
+
let child = path
|
|
83
|
+
for (let parent = path.parentPath; parent && parent.node !== ownerNode;
|
|
84
|
+
child = parent, parent = parent.parentPath) {
|
|
85
|
+
if (parent.isWithStatement() && child.key === 'body') withNodes.push(parent.node)
|
|
86
|
+
if (parent.isCatchClause() && child.key === 'body' && parent.node.param?.type === 'Identifier'
|
|
87
|
+
&& sourceName(parent.node.param) === name) return { catchNode: parent.node, withNodes }
|
|
88
|
+
}
|
|
89
|
+
return { withNodes }
|
|
90
|
+
}
|
|
@@ -20,6 +20,14 @@ import {
|
|
|
20
20
|
import { editTargetForCall, projectSessionLog } from './session-log-view.js'
|
|
21
21
|
import { RUN_CODE } from './runtime-bridge-owner.js'
|
|
22
22
|
import { isRecord } from './record-utils.js'
|
|
23
|
+
import {
|
|
24
|
+
userBindingDraftCapabilityFromMeta,
|
|
25
|
+
withUserBindingDraftCapability,
|
|
26
|
+
} from './user-binding-draft-projection.js'
|
|
27
|
+
import {
|
|
28
|
+
userBindingsSnapshotFromMeta,
|
|
29
|
+
withUserBindingsSnapshot,
|
|
30
|
+
} from './user-bindings.js'
|
|
23
31
|
import {
|
|
24
32
|
validatedReplMemorySnapshot,
|
|
25
33
|
withReplMemorySnapshot,
|
|
@@ -96,9 +104,19 @@ export function createEditTransportOwner(ctx, {
|
|
|
96
104
|
if (derived.recoveryBoundaries !== undefined) {
|
|
97
105
|
meta[RECOVERY_BOUNDARY_KEY] = normalizeRecoveryBoundaries(derived.recoveryBoundaries)
|
|
98
106
|
}
|
|
99
|
-
|
|
107
|
+
const withBindings = derived.userBindings === undefined
|
|
100
108
|
? meta
|
|
101
|
-
:
|
|
109
|
+
: withUserBindingsSnapshot(meta, derived.userBindings)
|
|
110
|
+
const withDraft = derived.draftCapability === undefined
|
|
111
|
+
? withBindings
|
|
112
|
+
: withUserBindingDraftCapability(
|
|
113
|
+
withBindings,
|
|
114
|
+
derived.draftCapability,
|
|
115
|
+
presentationGeneration,
|
|
116
|
+
)
|
|
117
|
+
return derived.replMemory === undefined
|
|
118
|
+
? withDraft
|
|
119
|
+
: withReplMemorySnapshot(withDraft, derived.replMemory, presentationGeneration)
|
|
102
120
|
}
|
|
103
121
|
definition.output = {
|
|
104
122
|
schema: {
|
|
@@ -186,6 +204,11 @@ export function createEditTransportOwner(ctx, {
|
|
|
186
204
|
? normalizeRecoveryBoundaries(inner.meta[RECOVERY_BOUNDARY_KEY])
|
|
187
205
|
: undefined
|
|
188
206
|
const replMemory = validatedReplMemorySnapshot(inner?.meta, presentationGeneration)
|
|
207
|
+
const userBindings = userBindingsSnapshotFromMeta(inner?.meta)
|
|
208
|
+
const draftCapability = userBindingDraftCapabilityFromMeta(
|
|
209
|
+
inner?.meta,
|
|
210
|
+
presentationGeneration,
|
|
211
|
+
)
|
|
189
212
|
const value = derivedEditResult(inner)
|
|
190
213
|
const derived = {
|
|
191
214
|
targetCallSeq: target.callSeq,
|
|
@@ -193,6 +216,8 @@ export function createEditTransportOwner(ctx, {
|
|
|
193
216
|
rewrites,
|
|
194
217
|
recoveryBoundaries,
|
|
195
218
|
replMemory,
|
|
219
|
+
userBindings,
|
|
220
|
+
draftCapability,
|
|
196
221
|
code: edited.code,
|
|
197
222
|
description: edited.description,
|
|
198
223
|
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Own how PTC Plus attaches to the host's program-execution seam.
|
|
3
|
+
*
|
|
4
|
+
* DSH registers one execution seam per generation. The current release
|
|
5
|
+
* registers `ctx.ptcRuntime` (`@deepseek-ai/dsh-ptc-runtime`); the preceding one
|
|
6
|
+
* registered `ctx.codeRuntime` (`@deepseek-ai/dsh-code-runtime`). Both run one
|
|
7
|
+
* model-written program against host async bindings. They differ in the service
|
|
8
|
+
* name, in the call shape (`resolve(request)` then `run(spec)` against a single
|
|
9
|
+
* `run(request)`), and in the capability descriptors a provider publishes.
|
|
10
|
+
* Which seam is live is decided by the registered service, never by a version
|
|
11
|
+
* string, so a deployment on either generation obtains the same behavior.
|
|
12
|
+
*
|
|
13
|
+
* PTC Plus replaces the execution of session cells with its own persistent
|
|
14
|
+
* REPL. The object DSH describes to the model is therefore the plugin's
|
|
15
|
+
* execution, not the wrapped provider's, so `takeOver()` answers the
|
|
16
|
+
* descriptors the plugin can honor and withholds the ones it cannot. The REPL
|
|
17
|
+
* runs every cell in one long-lived worker rather than a fresh process per
|
|
18
|
+
* call, applies the session's configured budgets rather than a per-call host
|
|
19
|
+
* deadline, and performs no file confinement. DSH gates `timeoutMs` on
|
|
20
|
+
* `timeout` and resolves or escalates file policy on `sandboxMode`, so
|
|
21
|
+
* withholding those descriptors keeps DSH's authority decisions with DSH
|
|
22
|
+
* instead of having this plugin accept an input it would then ignore.
|
|
23
|
+
*
|
|
24
|
+
* A seam-neutral request carries exactly `program`, `bindings`, and `signal`:
|
|
25
|
+
* the fields PTC Plus consumes. A resolved spec's directory, deadline, and
|
|
26
|
+
* authority describe the provider whose execution was replaced, and are neither
|
|
27
|
+
* forwarded to the session kernel nor re-derived here.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/** Cordis service name registered by the current DSH generation. */
|
|
31
|
+
export const EXECUTION_SEAM_SERVICE = 'ptcRuntime'
|
|
32
|
+
|
|
33
|
+
/** Cordis service name registered by the preceding DSH generation. */
|
|
34
|
+
export const LEGACY_EXECUTION_SEAM_SERVICE = 'codeRuntime'
|
|
35
|
+
|
|
36
|
+
const SEAM_SERVICES = Object.freeze([EXECUTION_SEAM_SERVICE, LEGACY_EXECUTION_SEAM_SERVICE])
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Descriptors the plugin replaces with the capabilities of its own execution.
|
|
40
|
+
* `executionInstructions` describes the wrapped provider's program model, which
|
|
41
|
+
* no longer applies; `timeout` and `sandboxMode` gate host inputs the plugin
|
|
42
|
+
* cannot honor. Withdrawal is unconditional because the rule is a property of
|
|
43
|
+
* the plugin's execution, not of a generation: the preceding generation's
|
|
44
|
+
* contract defines none of these members and nothing in its install closure
|
|
45
|
+
* reads them, so withdrawing there is inert, while a provider that published
|
|
46
|
+
* them stays unable to outlive the takeover.
|
|
47
|
+
*/
|
|
48
|
+
const WITHHELD_DESCRIPTORS = Object.freeze(['executionInstructions', 'sandboxMode', 'timeout'])
|
|
49
|
+
|
|
50
|
+
/** Read a registered service without declaring an injection requirement. */
|
|
51
|
+
function readService(ctx, name) {
|
|
52
|
+
if (typeof ctx?.get === 'function') return ctx.get(name)
|
|
53
|
+
return ctx?.[name]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function descriptorsMatch(left, right) {
|
|
57
|
+
if (left === undefined || right === undefined) return left === right
|
|
58
|
+
return left.value === right.value
|
|
59
|
+
&& left.get === right.get
|
|
60
|
+
&& left.set === right.set
|
|
61
|
+
&& left.writable === right.writable
|
|
62
|
+
&& left.enumerable === right.enumerable
|
|
63
|
+
&& left.configurable === right.configurable
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Build the seam handle for one live execution service.
|
|
68
|
+
* @param {object} service - the registered `ctx.ptcRuntime` or `ctx.codeRuntime`.
|
|
69
|
+
* @param {string} serviceName - the service name it was registered under.
|
|
70
|
+
*/
|
|
71
|
+
export function createExecutionSeam(service, serviceName) {
|
|
72
|
+
if (service === null || typeof service !== 'object') {
|
|
73
|
+
throw new TypeError(`ptc-plus: ${serviceName} must be an execution service object`)
|
|
74
|
+
}
|
|
75
|
+
const current = serviceName === EXECUTION_SEAM_SERVICE
|
|
76
|
+
const upstreamRun = service.run
|
|
77
|
+
if (typeof upstreamRun !== 'function') {
|
|
78
|
+
throw new TypeError(`ptc-plus: ${serviceName}.run must be a function`)
|
|
79
|
+
}
|
|
80
|
+
const upstreamResolve = service.resolve
|
|
81
|
+
if (current && typeof upstreamResolve !== 'function') {
|
|
82
|
+
throw new TypeError('ptc-plus: ptcRuntime.resolve must be a function')
|
|
83
|
+
}
|
|
84
|
+
let installed
|
|
85
|
+
return Object.freeze({
|
|
86
|
+
serviceName,
|
|
87
|
+
language: service.language,
|
|
88
|
+
/**
|
|
89
|
+
* Run one program through the provider this plugin wraps. The current
|
|
90
|
+
* generation resolves the request before running it, exactly as its own
|
|
91
|
+
* consumer does; the preceding generation accepts the request directly.
|
|
92
|
+
*/
|
|
93
|
+
invokeUpstream(request) {
|
|
94
|
+
return current
|
|
95
|
+
? upstreamRun.call(service, upstreamResolve.call(service, request))
|
|
96
|
+
: upstreamRun.call(service, request)
|
|
97
|
+
},
|
|
98
|
+
/**
|
|
99
|
+
* Install the plugin's execution entry, and its capability descriptors, on
|
|
100
|
+
* the live service. Only one takeover is live at a time.
|
|
101
|
+
* @param {(request: object) => Promise<object>} execute - the plugin's entry.
|
|
102
|
+
* @returns {() => void} restores every descriptor this call replaced.
|
|
103
|
+
*/
|
|
104
|
+
takeOver(execute) {
|
|
105
|
+
if (installed !== undefined) throw new Error('ptc-plus: execution seam already taken over')
|
|
106
|
+
const saved = new Map()
|
|
107
|
+
const written = new Map()
|
|
108
|
+
const define = (name, descriptor) => {
|
|
109
|
+
const previous = Object.getOwnPropertyDescriptor(service, name)
|
|
110
|
+
if (!saved.has(name)) saved.set(name, previous)
|
|
111
|
+
// Preserve the shadowed property's visibility so a consumer that
|
|
112
|
+
// enumerates the provider sees the same member set.
|
|
113
|
+
Object.defineProperty(service, name, { enumerable: previous?.enumerable ?? false, ...descriptor })
|
|
114
|
+
written.set(name, Object.getOwnPropertyDescriptor(service, name))
|
|
115
|
+
}
|
|
116
|
+
define('run', {
|
|
117
|
+
configurable: true,
|
|
118
|
+
writable: true,
|
|
119
|
+
value: current
|
|
120
|
+
? spec => execute({
|
|
121
|
+
program: spec.program,
|
|
122
|
+
bindings: spec.bindings,
|
|
123
|
+
signal: spec.signal,
|
|
124
|
+
})
|
|
125
|
+
: request => execute(request),
|
|
126
|
+
})
|
|
127
|
+
for (const name of WITHHELD_DESCRIPTORS) {
|
|
128
|
+
define(name, { configurable: true, value: name === 'executionInstructions' ? '' : undefined })
|
|
129
|
+
}
|
|
130
|
+
const restore = () => {
|
|
131
|
+
if (installed !== restore) return
|
|
132
|
+
installed = undefined
|
|
133
|
+
for (const [name, descriptor] of saved) {
|
|
134
|
+
if (!descriptorsMatch(Object.getOwnPropertyDescriptor(service, name), written.get(name))) continue
|
|
135
|
+
if (descriptor === undefined) delete service[name]
|
|
136
|
+
else Object.defineProperty(service, name, descriptor)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
installed = restore
|
|
140
|
+
return restore
|
|
141
|
+
},
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Wait for the host's execution seam and attach the plugin to the live one.
|
|
147
|
+
*
|
|
148
|
+
* A deployment registers one generation, so the current service is preferred
|
|
149
|
+
* whenever both are live; the preceding generation attaches only when the
|
|
150
|
+
* current one is absent. The injected scope requires the plugin's own core
|
|
151
|
+
* services as well, so the callback receives a context the plugin can work in,
|
|
152
|
+
* and everything the plugin registers there belongs to the seam the host
|
|
153
|
+
* registered: unloading that service unloads the plugin, and registering it
|
|
154
|
+
* again attaches once more, which is how a host that reloads its runtime
|
|
155
|
+
* provider reloads this dependent.
|
|
156
|
+
*
|
|
157
|
+
* @param {object} ctx - the plugin context.
|
|
158
|
+
* @param {object} options - `services` are the plugin's own required services,
|
|
159
|
+
* and `attach` is invoked with the injected scope and the seam handle.
|
|
160
|
+
* @returns {Promise<unknown>} settles with the attach result.
|
|
161
|
+
*/
|
|
162
|
+
export function installExecutionSeam(ctx, { services = [], attach }) {
|
|
163
|
+
let live
|
|
164
|
+
const select = (name, scope) => {
|
|
165
|
+
if (live !== undefined) return live.attached
|
|
166
|
+
if (name === LEGACY_EXECUTION_SEAM_SERVICE && readService(ctx, EXECUTION_SEAM_SERVICE) !== undefined) {
|
|
167
|
+
return undefined
|
|
168
|
+
}
|
|
169
|
+
const entry = { name }
|
|
170
|
+
live = entry
|
|
171
|
+
try {
|
|
172
|
+
entry.attached = attach(scope, createExecutionSeam(scope?.[name] ?? readService(ctx, name), name))
|
|
173
|
+
} catch (error) {
|
|
174
|
+
live = undefined
|
|
175
|
+
throw error
|
|
176
|
+
}
|
|
177
|
+
scope.effect?.(() => () => {
|
|
178
|
+
if (live === entry) live = undefined
|
|
179
|
+
}, 'ptc-plus execution seam lifecycle')
|
|
180
|
+
return entry.attached
|
|
181
|
+
}
|
|
182
|
+
const pending = SEAM_SERVICES.map(name => new Promise((resolve, reject) => {
|
|
183
|
+
ctx.inject([...services, name], scope => {
|
|
184
|
+
try {
|
|
185
|
+
resolve(select(name, scope))
|
|
186
|
+
} catch (error) {
|
|
187
|
+
reject(error)
|
|
188
|
+
throw error
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
}))
|
|
192
|
+
return Promise.race(pending)
|
|
193
|
+
}
|