dsh-ptc-plus 0.3.1 → 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 +66 -73
- package/README.md +66 -73
- 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 +30610 -722
- 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 +6 -4
- package/docs/adr/0007-separate-worker-transport-from-session-semantics.md +15 -3
- 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 +9 -3
- package/docs/adr/0014-persist-cell-rewrite-policy.md +11 -5
- 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 +8 -6
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
- package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +17 -3
- 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 +82 -22
- package/docs/binding-review-placement-design.md +200 -0
- package/docs/capability-projection.md +1 -1
- package/docs/client-ui.md +96 -20
- package/docs/durability-design.md +61 -24
- package/docs/evaluation.md +78 -6
- package/docs/installation.md +41 -6
- package/docs/program-data-plane.md +6 -0
- package/docs/publishing.md +5 -3
- package/docs/runtime-reference.md +79 -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 +374 -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 +252 -242
- 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 -553
- 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 +91 -25
- 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 +28 -3
- 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 +911 -116
- 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 -201
- package/internal/repl-memory-projection.js +81 -12
- package/internal/repl-observation-interest.js +70 -0
- package/internal/repl-rewrite-contract.js +21 -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 -51
- package/internal/runtime-contexts.js +82 -10
- package/internal/runtime-messages.js +111 -0
- package/internal/session-cell-executor.js +455 -91
- package/internal/session-journal-recovery.js +684 -0
- package/internal/session-journal-schema.js +106 -0
- package/internal/session-journal.js +237 -440
- package/internal/session-log-view.js +42 -9
- package/internal/session-runtime.js +325 -80
- package/internal/session-state.js +249 -40
- 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 +81 -11
- package/src/client-activity.js +109 -45
- 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
|
@@ -1,27 +1,103 @@
|
|
|
1
1
|
import { diagnostic } from './diagnostic.js'
|
|
2
|
+
import { AsyncLocalStorage } from 'node:async_hooks'
|
|
3
|
+
import { createHash } from 'node:crypto'
|
|
4
|
+
import { moduleRuntimeIntrinsics as internal } from './compiler-intrinsics.js'
|
|
2
5
|
|
|
3
6
|
export const MAX_ERROR_LOG_BYTES = 4 * 1024
|
|
4
7
|
export const FAILURE_HINT_THRESHOLD = 3
|
|
5
8
|
export const LONG_CELL_CODE_UNITS = 2_000
|
|
6
9
|
const BINDING_FAILURE = Symbol('binding failure')
|
|
10
|
+
const PROGRAM_FAILURES = new internal.WeakMap()
|
|
11
|
+
const MAX_CAUSE_CODE_UNITS = 2048
|
|
12
|
+
const exceptionOrigins = new AsyncLocalStorage()
|
|
13
|
+
const runExceptionScope = exceptionOrigins.run.bind(exceptionOrigins)
|
|
14
|
+
const currentExceptionScope = exceptionOrigins.getStore.bind(exceptionOrigins)
|
|
7
15
|
|
|
8
|
-
|
|
9
|
-
|
|
16
|
+
/** Propagation facts belong to one execution, never to a user-thrown value. */
|
|
17
|
+
export function createExceptionOriginScope() {
|
|
18
|
+
const state = { origins: new internal.Map(), sourceFailures: new internal.Set(), open: true }
|
|
19
|
+
return {
|
|
20
|
+
run: callback => runExceptionScope(state, callback),
|
|
21
|
+
origins: error => internal.mapGet(state.origins, error),
|
|
22
|
+
sourceFailure: error => internal.setHas(state.sourceFailures, error),
|
|
23
|
+
close() { state.open = false; internal.mapClear(state.origins); internal.setClear(state.sourceFailures) },
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function recordExceptionOrigin(error, origin, { reset = false, sourceFailure = false } = {}) {
|
|
28
|
+
const state = currentExceptionScope()
|
|
29
|
+
if (state?.open !== true) return
|
|
30
|
+
if (reset) { internal.mapDelete(state.origins, error); internal.setDelete(state.sourceFailures, error) }
|
|
31
|
+
if (sourceFailure) internal.setAdd(state.sourceFailures, error)
|
|
32
|
+
if (typeof origin !== 'string') return
|
|
33
|
+
const origins = internal.mapGet(state.origins, error) ?? []
|
|
34
|
+
if (!internal.includes(origins, origin)) internal.appendArray(origins, origin)
|
|
35
|
+
internal.mapSet(state.origins, error, origins)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const SOURCE_LINE_BREAK = /\r\n|[\n\r\u2028\u2029]/
|
|
39
|
+
const sourceLines = source => source.split(SOURCE_LINE_BREAK)
|
|
40
|
+
|
|
41
|
+
/** A cell position is a 1-based line and column inside its source. The column
|
|
42
|
+
* may address the position just after the last character of a line. */
|
|
43
|
+
function boundedPosition(position, lines) {
|
|
44
|
+
if (position === null || typeof position !== 'object'
|
|
45
|
+
|| !Number.isSafeInteger(position.line) || position.line < 1
|
|
46
|
+
|| !Number.isSafeInteger(position.column) || position.column < 1
|
|
47
|
+
|| position.line > lines.length || position.column > lines[position.line - 1].length + 1) return undefined
|
|
48
|
+
return { line: position.line, column: position.column }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Validate a candidate against the source that owns the reported cell. */
|
|
52
|
+
export function cellPosition(position, source) {
|
|
53
|
+
return boundedPosition(position, sourceLines(source))
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Only a fact belonging to the current original source can bypass its map. */
|
|
57
|
+
export function exceptionOriginPosition(origins, source) {
|
|
58
|
+
if (!Array.isArray(origins)) return undefined
|
|
59
|
+
const prefix = `eval:${createHash('sha256').update(source).digest('hex')}:`
|
|
60
|
+
const lines = sourceLines(source)
|
|
61
|
+
for (const origin of origins) {
|
|
62
|
+
if (typeof origin !== 'string' || !origin.startsWith(prefix)) continue
|
|
63
|
+
const match = /^(\d+):(\d+)$/.exec(origin.slice(prefix.length))
|
|
64
|
+
if (match === null) continue
|
|
65
|
+
const position = boundedPosition({ line: Number(match[1]), column: Number(match[2]) }, lines)
|
|
66
|
+
if (position !== undefined) return position
|
|
67
|
+
}
|
|
68
|
+
return undefined
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function markBindingFailure(error, kind = 'lexical') {
|
|
72
|
+
Object.defineProperty(error, BINDING_FAILURE, { value: kind })
|
|
10
73
|
return error
|
|
11
74
|
}
|
|
12
75
|
|
|
13
|
-
|
|
14
|
-
|
|
76
|
+
/** Preserve worker-owned failure identity without trusting user exception text. */
|
|
77
|
+
export function programBindingError(kind, message) {
|
|
78
|
+
const error = new internal.Error(message)
|
|
79
|
+
internal.weakMapSet(PROGRAM_FAILURES, error, kind)
|
|
80
|
+
return error
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function repeatedFailureDiagnostic(kind, streak, stateEffect) {
|
|
84
|
+
if (kind === 'lexical' || kind === 'capability') {
|
|
85
|
+
const capability = kind === 'capability'
|
|
15
86
|
return diagnostic({
|
|
16
87
|
code: 'PTC-W001',
|
|
17
88
|
severity: 'warning',
|
|
18
89
|
phase: 'execute',
|
|
19
|
-
message: `this cell failed ${streak} times with the same
|
|
20
|
-
stateEffect
|
|
21
|
-
|
|
90
|
+
message: `this cell failed ${streak} times with the same ${capability ? 'capability' : 'lexical binding'} error; resolve the reported name before continuing`,
|
|
91
|
+
stateEffect,
|
|
92
|
+
cause: { code: capability ? 'PTC-CAPABILITY' : 'PTC-LOCAL', message: capability ? 'current program capability lookup failed' : 'local name or declaration conflict' },
|
|
93
|
+
help: capability ? [
|
|
22
94
|
'inspect live bindings with capabilities.tree(), capabilities.find(), or capabilities.inspect()',
|
|
23
95
|
'call available typed members through tools.*',
|
|
24
96
|
'do not repeat the same unresolved binding expression',
|
|
97
|
+
] : [
|
|
98
|
+
'inspect the visible cell source and results for the local declaration, scope, or initialization failure',
|
|
99
|
+
'correct the name or initialization; use a fresh name or block for a declaration conflict',
|
|
100
|
+
'capabilities describes program APIs, not session-local variables; discovery cannot prove local state',
|
|
25
101
|
],
|
|
26
102
|
})
|
|
27
103
|
}
|
|
@@ -30,7 +106,7 @@ function repeatedFailureDiagnostic(kind, streak) {
|
|
|
30
106
|
severity: 'warning',
|
|
31
107
|
phase: 'execute',
|
|
32
108
|
message: `this cell failed ${streak} times with the same error; inspect the reported cause and change the approach before retrying`,
|
|
33
|
-
stateEffect
|
|
109
|
+
stateEffect,
|
|
34
110
|
help: ['inspect the reported cause', 'change the inputs or approach before retrying'],
|
|
35
111
|
})
|
|
36
112
|
}
|
|
@@ -54,6 +130,13 @@ export function messageOf(error, fallback = 'Unprintable error') {
|
|
|
54
130
|
}
|
|
55
131
|
}
|
|
56
132
|
|
|
133
|
+
/** Preserve the child-process exit facts in every helper failure diagnostic. */
|
|
134
|
+
export function processExitDescription(code, signal) {
|
|
135
|
+
return signal === null || signal === undefined
|
|
136
|
+
? `code ${code}`
|
|
137
|
+
: `code ${code}, signal ${signal}`
|
|
138
|
+
}
|
|
139
|
+
|
|
57
140
|
export function firstLine(value, fallback = undefined) {
|
|
58
141
|
if (typeof value !== 'string') return fallback
|
|
59
142
|
const line = value.split(/[\r\n]/, 1)[0]
|
|
@@ -92,18 +175,26 @@ export function limitLogs(logs) {
|
|
|
92
175
|
return tail
|
|
93
176
|
}
|
|
94
177
|
|
|
95
|
-
|
|
178
|
+
/** A frame is only useful when its 1-based line and column are safe integers. */
|
|
179
|
+
function structuralPosition(position) {
|
|
180
|
+
return Number.isSafeInteger(position.line) && position.line >= 1
|
|
181
|
+
&& Number.isSafeInteger(position.column) && position.column >= 1 ? position : undefined
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** Extract the first frame for the active cell. The frame is reported in the
|
|
185
|
+
* executed text, so it is only a cell position when it lies inside the source
|
|
186
|
+
* that produced that text. */
|
|
187
|
+
export function errorPosition(error, filename, source) {
|
|
96
188
|
const stack = safeProperty(error, 'stack')
|
|
97
189
|
if (typeof stack !== 'string') return undefined
|
|
98
190
|
const escaped = filename.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
99
191
|
const match = new RegExp(`${escaped}:(\\d+):(\\d+)`).exec(stack)
|
|
100
192
|
if (match === null) return undefined
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
return line >= 1 && column >= 1 ? { line, column } : undefined
|
|
193
|
+
const position = { line: Number(match[1]), column: Number(match[2]) }
|
|
194
|
+
return source === undefined ? structuralPosition(position) : cellPosition(position, source)
|
|
104
195
|
}
|
|
105
196
|
|
|
106
|
-
export function errorDetails(error, filename) {
|
|
197
|
+
export function errorDetails(error, filename, source) {
|
|
107
198
|
const message = messageOf(error, 'Unprintable thrown value')
|
|
108
199
|
const rawName = safeProperty(error, 'name')
|
|
109
200
|
const name = typeof rawName === 'string' && rawName.length > 0 ? rawName : 'Error'
|
|
@@ -114,19 +205,28 @@ export function errorDetails(error, filename) {
|
|
|
114
205
|
const candidate = safeProperty(error, 'ptcCause')
|
|
115
206
|
const causeMessage = firstLine(safeProperty(candidate, 'message'))
|
|
116
207
|
const causeCode = firstLine(safeProperty(candidate, 'code'))
|
|
208
|
+
const stderr = safeProperty(error, 'stderr')
|
|
209
|
+
const stderrText = typeof stderr === 'string' ? stderr.slice(0, MAX_CAUSE_CODE_UNITS)
|
|
210
|
+
: Buffer.isBuffer(stderr) ? stderr.subarray(0, MAX_CAUSE_CODE_UNITS).toString('utf8') : ''
|
|
211
|
+
const boundedMessage = message.slice(0, MAX_CAUSE_CODE_UNITS)
|
|
212
|
+
const continuation = boundedMessage.search(/[\r\n]/)
|
|
213
|
+
const detail = [continuation < 0 ? '' : boundedMessage.slice(continuation + 1), stderrText]
|
|
214
|
+
.join(' ').slice(0, MAX_CAUSE_CODE_UNITS).replace(/\s+/g, ' ').trim()
|
|
117
215
|
const cause = causeMessage === undefined
|
|
118
|
-
? undefined
|
|
216
|
+
? detail.length === 0 ? undefined : { message: detail }
|
|
119
217
|
: {
|
|
120
218
|
...(causeCode === undefined ? {} : { code: causeCode }),
|
|
121
219
|
message: causeMessage,
|
|
122
220
|
}
|
|
123
|
-
const position = errorPosition(error, filename)
|
|
221
|
+
const position = errorPosition(error, filename, source)
|
|
222
|
+
const failureOrigin = internal.weakMapGet(PROGRAM_FAILURES, error)
|
|
124
223
|
return {
|
|
125
224
|
name,
|
|
126
225
|
message,
|
|
127
226
|
...(toolName === undefined ? {} : { toolName }),
|
|
128
227
|
...(position === undefined ? {} : { position }),
|
|
129
228
|
...(cause === undefined ? {} : { cause }),
|
|
229
|
+
...(failureOrigin === undefined ? {} : { failureOrigin }),
|
|
130
230
|
}
|
|
131
231
|
}
|
|
132
232
|
|
|
@@ -140,16 +240,16 @@ export function createFailureTracker() {
|
|
|
140
240
|
kind = undefined
|
|
141
241
|
streak = 0
|
|
142
242
|
},
|
|
143
|
-
hint(error) {
|
|
243
|
+
hint(error, stateEffect = 'unknown') {
|
|
144
244
|
const nextFingerprint = `${error.kind}\u0000${error.message}`
|
|
145
245
|
if (fingerprint === nextFingerprint) streak += 1
|
|
146
246
|
else {
|
|
147
247
|
fingerprint = nextFingerprint
|
|
148
|
-
kind = error[BINDING_FAILURE]
|
|
248
|
+
kind = error[BINDING_FAILURE] ?? 'generic'
|
|
149
249
|
streak = 1
|
|
150
250
|
}
|
|
151
251
|
if (streak !== FAILURE_HINT_THRESHOLD) return undefined
|
|
152
|
-
return repeatedFailureDiagnostic(kind, streak)
|
|
252
|
+
return repeatedFailureDiagnostic(kind, streak, stateEffect)
|
|
153
253
|
},
|
|
154
254
|
})
|
|
155
255
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { bindTypertRemote } from '@deepseek-ai/dsh-typert-protocol'
|
|
2
|
+
import { rpcDescriptor } from './rpc-contract.js'
|
|
3
|
+
|
|
4
|
+
/** Own Remote services and the package's Host invocation contribution. */
|
|
5
|
+
export function createHostRpc(ctx) {
|
|
6
|
+
const handlers = new Map()
|
|
7
|
+
let provider
|
|
8
|
+
let definition
|
|
9
|
+
let epoch
|
|
10
|
+
let disposed = false
|
|
11
|
+
const publish = () => {
|
|
12
|
+
// Registry withdrawal is synchronous, so replacement has no overlapping endpoints.
|
|
13
|
+
definition?.()
|
|
14
|
+
definition = undefined
|
|
15
|
+
if (provider === undefined || disposed || handlers.size === 0) return
|
|
16
|
+
definition = provider.effect(() => provider.typert.register({
|
|
17
|
+
package: 'dsh-ptc-plus', face: 'host', schemas: [],
|
|
18
|
+
model: { services: [], events: [], objects: [] },
|
|
19
|
+
invocations: [...handlers.keys()].map(rpcDescriptor),
|
|
20
|
+
}), 'ptc-plus: Remote definitions')
|
|
21
|
+
}
|
|
22
|
+
const port = {
|
|
23
|
+
register(contract, handler) {
|
|
24
|
+
if (disposed) throw new Error('PTC Plus RPC owner is disposed')
|
|
25
|
+
if (handlers.has(contract)) throw new Error(`PTC Plus RPC service ${contract.service} is already registered`)
|
|
26
|
+
const lifetime = new AbortController()
|
|
27
|
+
const service = {
|
|
28
|
+
invoke(operation, payload, signal) {
|
|
29
|
+
lifetime.signal.throwIfAborted()
|
|
30
|
+
if (epoch === undefined) throw new Error('PTC Plus RPC provider is unavailable')
|
|
31
|
+
return handler(operation, payload, AbortSignal.any([signal, lifetime.signal, epoch.signal]))
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
service.typertRemote = bindTypertRemote(service, contract.service)
|
|
35
|
+
const release = ctx.effect(function* () {
|
|
36
|
+
yield ctx.provide(contract.service, service)
|
|
37
|
+
handlers.set(contract, { release: () => release() })
|
|
38
|
+
yield () => {
|
|
39
|
+
lifetime.abort()
|
|
40
|
+
handlers.delete(contract)
|
|
41
|
+
publish()
|
|
42
|
+
}
|
|
43
|
+
publish()
|
|
44
|
+
}, 'ptc-plus: Remote service')
|
|
45
|
+
return release
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
const injection = ctx.inject?.(['typert'], scope => {
|
|
49
|
+
provider = scope
|
|
50
|
+
epoch = new AbortController()
|
|
51
|
+
publish()
|
|
52
|
+
scope.effect(() => scope.provide('ptcPlusRpc', port), 'ptc-plus: RPC registration port')
|
|
53
|
+
scope.effect(() => () => {
|
|
54
|
+
epoch.abort()
|
|
55
|
+
epoch = undefined
|
|
56
|
+
provider = undefined
|
|
57
|
+
definition = undefined
|
|
58
|
+
}, 'ptc-plus: RPC provider lifetime')
|
|
59
|
+
})
|
|
60
|
+
return { async dispose() {
|
|
61
|
+
disposed = true
|
|
62
|
+
for (const entry of [...handlers.values()]) await entry.release()
|
|
63
|
+
if (typeof injection === 'function') await injection()
|
|
64
|
+
else await injection?.dispose()
|
|
65
|
+
} }
|
|
66
|
+
}
|