dsh-ptc-plus 0.3.2 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +63 -80
- package/README.md +63 -80
- 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 +30827 -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 +556 -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 +7 -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 +77 -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 +28 -10
- package/docs/evaluation.md +78 -6
- package/docs/installation.md +41 -6
- package/docs/publishing.md +5 -3
- package/docs/runtime-reference.md +76 -25
- package/docs/semantic-validation.md +29 -0
- package/docs/stateful-binding-updates-proposal.md +759 -0
- package/docs/user-bindings.en.md +66 -0
- package/docs/user-bindings.md +66 -0
- package/docs/value-wire.md +3 -3
- package/docs/verification-optimization-work.md +41 -0
- package/docs/verification.md +71 -0
- package/index.js +369 -92
- package/internal/ast-traversal.js +5 -2
- package/internal/async-local-storage-intrinsics.js +38 -0
- package/internal/binding-declaration-contract.js +8 -0
- 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 +708 -0
- package/internal/binding-update-policy.js +72 -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 +253 -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 +50 -0
- package/internal/compiler-module-links.js +37 -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 +91 -0
- package/internal/dynamic-environment-runtime.js +383 -0
- package/internal/dynamic-native-calls.js +153 -0
- package/internal/dynamic-native-runtime.js +262 -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 +136 -28
- package/internal/host-rpc.js +66 -0
- package/internal/isolated-worker.js +555 -0
- package/internal/kernel-child.js +139 -0
- package/internal/kernel-worker.js +1179 -233
- 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 +322 -0
- package/internal/module-runtime-platform.js +19 -0
- package/internal/module-transform-contract.js +42 -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/record-utils.js +24 -15
- 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 +117 -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-intrinsics.js +101 -0
- package/internal/runtime-messages.js +111 -0
- package/internal/session-cell-executor.js +402 -87
- package/internal/session-journal-recovery.js +684 -0
- package/internal/session-journal-schema.js +123 -0
- package/internal/session-journal.js +199 -654
- package/internal/session-log-view.js +42 -9
- package/internal/session-runtime.js +257 -95
- package/internal/session-state.js +247 -71
- package/internal/source-position-map.js +230 -71
- package/internal/stateful-module-compiler.js +154 -0
- package/internal/stateful-module-runtime.js +303 -0
- package/internal/stateful-root-compiler.js +600 -0
- package/internal/stateful-root-runtime.js +340 -0
- package/internal/typert-codec-compat.js +28 -0
- package/internal/typescript-transform.js +31 -0
- package/internal/user-binding-authoring.js +70 -0
- package/internal/user-binding-console-worker.js +244 -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 +361 -0
- package/internal/value-wire-schema.js +28 -0
- package/internal/value-wire.js +130 -91
- package/internal/worker-client.js +57 -67
- package/internal/worker-cwd-virtualization.js +80 -53
- package/internal/worker-environment.js +65 -0
- package/internal/worker-output-relay.js +29 -0
- package/internal/worker-repl-realm.js +134 -0
- package/internal/worker-shutdown.js +14 -0
- package/package.json +76 -7
- package/src/client-activity.js +95 -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
package/internal/value-wire.js
CHANGED
|
@@ -1,28 +1,36 @@
|
|
|
1
1
|
import { assertFields, isRecord } from './record-utils.js'
|
|
2
|
+
import { runtimeIntrinsics as internal } from './runtime-intrinsics.js'
|
|
3
|
+
import {
|
|
4
|
+
VALUE_CODEC,
|
|
5
|
+
DEFAULT_VALUE_LIMITS,
|
|
6
|
+
VALUE_ENVELOPE_FIELDS as ENVELOPE_FIELDS,
|
|
7
|
+
VALUE_OBJECT_FIELDS as OBJECT_NODE_FIELDS,
|
|
8
|
+
VALUE_ARRAY_FIELDS as ARRAY_NODE_FIELDS,
|
|
9
|
+
VALUE_UNDEFINED_FIELDS as UNDEFINED_FIELDS,
|
|
10
|
+
VALUE_NUMBER_FIELDS as NUMBER_FIELDS,
|
|
11
|
+
VALUE_BIGINT_FIELDS as BIGINT_FIELDS,
|
|
12
|
+
VALUE_REFERENCE_FIELDS as REFERENCE_FIELDS,
|
|
13
|
+
} from './value-wire-schema.js'
|
|
2
14
|
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
maxBigIntDigits: 100_000,
|
|
10
|
-
maxStringBytes: 64 * 1024 * 1024,
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
const ENVELOPE_FIELDS = new Set(['codec', 'root', 'nodes'])
|
|
14
|
-
const OBJECT_NODE_FIELDS = new Set(['type', 'prototype', 'entries'])
|
|
15
|
-
const ARRAY_NODE_FIELDS = new Set(['type', 'length', 'entries'])
|
|
16
|
-
const UNDEFINED_FIELDS = new Set(['tag'])
|
|
17
|
-
const NUMBER_FIELDS = new Set(['tag', 'value'])
|
|
18
|
-
const BIGINT_FIELDS = new Set(['tag', 'value'])
|
|
19
|
-
const REFERENCE_FIELDS = new Set(['tag', 'index'])
|
|
15
|
+
export { VALUE_CODEC, DEFAULT_VALUE_LIMITS } from './value-wire-schema.js'
|
|
16
|
+
const { Object, Reflect, Array, Map, Set, WeakMap, WeakSet, TypeError,
|
|
17
|
+
isArray, numberIsFinite, numberIsNaN, numberIsSafeInteger, toBigInt,
|
|
18
|
+
toNumber, toString, regexpTest, mapGet, mapHas, mapSet, setHas, setAdd,
|
|
19
|
+
setDelete, setSize, weakMapGet, weakMapSet, weakSetHas, weakSetAdd,
|
|
20
|
+
appendArray, popArray, join } = internal
|
|
20
21
|
const IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/
|
|
22
|
+
const intrinsicFunctionToString = Function.prototype.toString
|
|
23
|
+
const reflectApply = Reflect.apply
|
|
24
|
+
const bufferByteLength = internal.bufferByteLength
|
|
25
|
+
const jsonStringify = JSON.stringify
|
|
21
26
|
|
|
22
27
|
function limitsOf(options = {}) {
|
|
23
28
|
const limits = { ...DEFAULT_VALUE_LIMITS, ...options }
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
const entries = Object.entries(limits)
|
|
30
|
+
for (let index = 0; index < entries.length; index += 1) {
|
|
31
|
+
const name = entries[index][0]
|
|
32
|
+
const value = entries[index][1]
|
|
33
|
+
if (!numberIsSafeInteger(value) || value < 1) throw new TypeError(`invalid PTC value limit ${name}`)
|
|
26
34
|
}
|
|
27
35
|
return limits
|
|
28
36
|
}
|
|
@@ -33,7 +41,7 @@ function hasIntrinsicConstructor(prototype, name) {
|
|
|
33
41
|
try {
|
|
34
42
|
return constructor.name === name
|
|
35
43
|
&& constructor.prototype === prototype
|
|
36
|
-
&&
|
|
44
|
+
&& reflectApply(intrinsicFunctionToString, constructor, []) === `function ${name}() { [native code] }`
|
|
37
45
|
} catch {
|
|
38
46
|
return false
|
|
39
47
|
}
|
|
@@ -52,7 +60,7 @@ function plainObjectPrototype(value) {
|
|
|
52
60
|
|
|
53
61
|
function hasPlainArrayPrototype(value) {
|
|
54
62
|
const prototype = Object.getPrototypeOf(value)
|
|
55
|
-
if (!
|
|
63
|
+
if (!isArray(prototype) || !hasIntrinsicConstructor(prototype, 'Array')) return false
|
|
56
64
|
const objectPrototype = Object.getPrototypeOf(prototype)
|
|
57
65
|
return objectPrototype !== null && typeof objectPrototype === 'object'
|
|
58
66
|
&& isIntrinsicObjectPrototype(objectPrototype)
|
|
@@ -63,7 +71,7 @@ function invalid(path, detail) {
|
|
|
63
71
|
}
|
|
64
72
|
|
|
65
73
|
function stringBytes(value) {
|
|
66
|
-
return
|
|
74
|
+
return bufferByteLength(value, 'utf8')
|
|
67
75
|
}
|
|
68
76
|
|
|
69
77
|
/** Encode one supported JavaScript graph into the project-owned JSON-safe wire. */
|
|
@@ -87,7 +95,7 @@ export function encodeValue(value, options = {}) {
|
|
|
87
95
|
return current
|
|
88
96
|
}
|
|
89
97
|
if (typeof current === 'number') {
|
|
90
|
-
if (
|
|
98
|
+
if (numberIsNaN(current)) return { tag: 'number', value: 'nan' }
|
|
91
99
|
if (current === Infinity) return { tag: 'number', value: 'infinity' }
|
|
92
100
|
if (current === -Infinity) return { tag: 'number', value: '-infinity' }
|
|
93
101
|
if (Object.is(current, -0)) return { tag: 'number', value: '-0' }
|
|
@@ -95,20 +103,20 @@ export function encodeValue(value, options = {}) {
|
|
|
95
103
|
}
|
|
96
104
|
if (current === undefined) return { tag: 'undefined' }
|
|
97
105
|
if (typeof current === 'bigint') {
|
|
98
|
-
const text =
|
|
106
|
+
const text = toString(current)
|
|
99
107
|
const digits = text[0] === '-' ? text.length - 1 : text.length
|
|
100
108
|
if (digits > limits.maxBigIntDigits) invalid(path, `BigInt exceeds ${limits.maxBigIntDigits} digits`)
|
|
101
109
|
accountText(text, path)
|
|
102
110
|
return { tag: 'bigint', value: text }
|
|
103
111
|
}
|
|
104
112
|
if (typeof current !== 'object') invalid(path, typeof current)
|
|
105
|
-
const prior = seen
|
|
113
|
+
const prior = weakMapGet(seen, current)
|
|
106
114
|
if (prior !== undefined) return { tag: 'reference', index: prior }
|
|
107
115
|
if (nodes.length >= limits.maxNodes) invalid(path, `node budget exceeds ${limits.maxNodes}`)
|
|
108
116
|
const index = nodes.length
|
|
109
|
-
seen
|
|
110
|
-
nodes
|
|
111
|
-
pending
|
|
117
|
+
weakMapSet(seen, current, index)
|
|
118
|
+
appendArray(nodes, undefined)
|
|
119
|
+
appendArray(pending, { value: current, index, path })
|
|
112
120
|
return { tag: 'reference', index }
|
|
113
121
|
}
|
|
114
122
|
|
|
@@ -116,24 +124,26 @@ export function encodeValue(value, options = {}) {
|
|
|
116
124
|
for (let cursor = 0; cursor < pending.length; cursor += 1) {
|
|
117
125
|
const item = pending[cursor]
|
|
118
126
|
const current = item.value
|
|
119
|
-
if (
|
|
127
|
+
if (isArray(current)) {
|
|
120
128
|
if (!hasPlainArrayPrototype(current)) invalid(item.path, 'non-plain array')
|
|
121
129
|
if (current.length > limits.maxArrayLength) invalid(item.path, `array length exceeds ${limits.maxArrayLength}`)
|
|
122
130
|
const entries = []
|
|
123
|
-
|
|
131
|
+
const keys = Reflect.ownKeys(current)
|
|
132
|
+
for (let keyIndex = 0; keyIndex < keys.length; keyIndex += 1) {
|
|
133
|
+
const key = keys[keyIndex]
|
|
124
134
|
if (key === 'length') continue
|
|
125
|
-
if (typeof key !== 'string' ||
|
|
135
|
+
if (typeof key !== 'string' || !regexpTest(/^(0|[1-9][0-9]*)$/, key)) {
|
|
126
136
|
invalid(item.path, 'array has a symbol or non-index property')
|
|
127
137
|
}
|
|
128
|
-
const index =
|
|
129
|
-
if (!
|
|
138
|
+
const index = toNumber(key)
|
|
139
|
+
if (!numberIsSafeInteger(index) || index >= current.length) invalid(`${item.path}[${key}]`, 'invalid array index')
|
|
130
140
|
const descriptor = Object.getOwnPropertyDescriptor(current, key)
|
|
131
141
|
if (descriptor === undefined || !Object.hasOwn(descriptor, 'value') || descriptor.enumerable !== true) {
|
|
132
142
|
invalid(`${item.path}[${key}]`, 'array index must be an enumerable data property')
|
|
133
143
|
}
|
|
134
144
|
edgeCount += 1
|
|
135
145
|
if (edgeCount > limits.maxEdges) invalid(item.path, `edge budget exceeds ${limits.maxEdges}`)
|
|
136
|
-
entries
|
|
146
|
+
appendArray(entries, [index, atom(descriptor.value, `${item.path}[${key}]`)])
|
|
137
147
|
}
|
|
138
148
|
nodes[item.index] = { type: 'array', length: current.length, entries }
|
|
139
149
|
continue
|
|
@@ -142,7 +152,9 @@ export function encodeValue(value, options = {}) {
|
|
|
142
152
|
const prototype = plainObjectPrototype(current)
|
|
143
153
|
if (prototype === undefined) invalid(item.path, 'non-plain object')
|
|
144
154
|
const entries = []
|
|
145
|
-
|
|
155
|
+
const keys = Reflect.ownKeys(current)
|
|
156
|
+
for (let keyIndex = 0; keyIndex < keys.length; keyIndex += 1) {
|
|
157
|
+
const key = keys[keyIndex]
|
|
146
158
|
if (typeof key !== 'string') invalid(item.path, 'object has a symbol key')
|
|
147
159
|
const descriptor = Object.getOwnPropertyDescriptor(current, key)
|
|
148
160
|
if (descriptor === undefined || !Object.hasOwn(descriptor, 'value') || descriptor.enumerable !== true) {
|
|
@@ -151,7 +163,7 @@ export function encodeValue(value, options = {}) {
|
|
|
151
163
|
accountText(key, `${item.path}.${key}`)
|
|
152
164
|
edgeCount += 1
|
|
153
165
|
if (edgeCount > limits.maxEdges) invalid(item.path, `edge budget exceeds ${limits.maxEdges}`)
|
|
154
|
-
entries
|
|
166
|
+
appendArray(entries, [key, atom(descriptor.value, `${item.path}.${key}`)])
|
|
155
167
|
}
|
|
156
168
|
nodes[item.index] = { type: 'object', prototype, entries }
|
|
157
169
|
}
|
|
@@ -166,7 +178,7 @@ function decodeAtom(atom, nodes, limits, budget, path) {
|
|
|
166
178
|
return atom
|
|
167
179
|
}
|
|
168
180
|
if (typeof atom === 'number') {
|
|
169
|
-
if (!
|
|
181
|
+
if (!numberIsFinite(atom) || Object.is(atom, -0)) throw new TypeError(`invalid PTC value number at ${path}`)
|
|
170
182
|
return atom
|
|
171
183
|
}
|
|
172
184
|
if (!isRecord(atom) || typeof atom.tag !== 'string') throw new TypeError(`invalid PTC value atom at ${path}`)
|
|
@@ -184,31 +196,31 @@ function decodeAtom(atom, nodes, limits, budget, path) {
|
|
|
184
196
|
}
|
|
185
197
|
if (atom.tag === 'bigint') {
|
|
186
198
|
assertFields(atom, BIGINT_FIELDS, 'PTC BigInt atom')
|
|
187
|
-
if (typeof atom.value !== 'string' ||
|
|
199
|
+
if (typeof atom.value !== 'string' || !regexpTest(/^(0|-[1-9][0-9]*|[1-9][0-9]*)$/, atom.value)) {
|
|
188
200
|
throw new TypeError('invalid PTC BigInt')
|
|
189
201
|
}
|
|
190
202
|
const digits = atom.value[0] === '-' ? atom.value.length - 1 : atom.value.length
|
|
191
203
|
if (digits > limits.maxBigIntDigits) throw new TypeError('PTC BigInt digit budget exceeded')
|
|
192
204
|
budget.textBytes += stringBytes(atom.value)
|
|
193
205
|
if (budget.textBytes > limits.maxStringBytes) throw new TypeError('PTC value string budget exceeded')
|
|
194
|
-
return
|
|
206
|
+
return toBigInt(atom.value)
|
|
195
207
|
}
|
|
196
208
|
if (atom.tag === 'reference') {
|
|
197
209
|
assertFields(atom, REFERENCE_FIELDS, 'PTC reference atom')
|
|
198
|
-
if (!
|
|
210
|
+
if (!numberIsSafeInteger(atom.index) || atom.index < 0 || atom.index >= nodes.length) {
|
|
199
211
|
throw new TypeError('dangling PTC value reference')
|
|
200
212
|
}
|
|
201
|
-
budget.reachable
|
|
213
|
+
setAdd(budget.reachable, atom.index)
|
|
202
214
|
return nodes[atom.index]
|
|
203
215
|
}
|
|
204
|
-
throw new TypeError(`unknown PTC value atom tag ${
|
|
216
|
+
throw new TypeError(`unknown PTC value atom tag ${jsonStringify(atom.tag)}`)
|
|
205
217
|
}
|
|
206
218
|
|
|
207
219
|
/** Validate, hydrate, and own one canonical PTC value envelope without recursive stack growth. */
|
|
208
220
|
function decodeCanonicalValue(wire, options = {}) {
|
|
209
221
|
const limits = limitsOf(options)
|
|
210
222
|
assertFields(wire, ENVELOPE_FIELDS, 'PTC value envelope')
|
|
211
|
-
if (wire.codec !== VALUE_CODEC || !
|
|
223
|
+
if (wire.codec !== VALUE_CODEC || !isArray(wire.nodes)) throw new TypeError('invalid PTC value codec')
|
|
212
224
|
if (wire.nodes.length > limits.maxNodes) throw new TypeError('PTC value node budget exceeded')
|
|
213
225
|
const targets = new Array(wire.nodes.length)
|
|
214
226
|
for (let index = 0; index < wire.nodes.length; index += 1) {
|
|
@@ -216,14 +228,14 @@ function decodeCanonicalValue(wire, options = {}) {
|
|
|
216
228
|
if (!isRecord(node)) throw new TypeError(`invalid PTC value node ${index}`)
|
|
217
229
|
if (node.type === 'array') {
|
|
218
230
|
assertFields(node, ARRAY_NODE_FIELDS, `PTC array node ${index}`)
|
|
219
|
-
if (!
|
|
231
|
+
if (!numberIsSafeInteger(node.length) || node.length < 0 || node.length > limits.maxArrayLength) {
|
|
220
232
|
throw new TypeError(`invalid PTC array length at node ${index}`)
|
|
221
233
|
}
|
|
222
|
-
if (!
|
|
234
|
+
if (!isArray(node.entries)) throw new TypeError(`invalid PTC array entries at node ${index}`)
|
|
223
235
|
targets[index] = new Array(node.length)
|
|
224
236
|
} else if (node.type === 'object') {
|
|
225
237
|
assertFields(node, OBJECT_NODE_FIELDS, `PTC object node ${index}`)
|
|
226
|
-
if (
|
|
238
|
+
if ((node.prototype !== 'object' && node.prototype !== 'null') || !isArray(node.entries)) {
|
|
227
239
|
throw new TypeError(`invalid PTC object node ${index}`)
|
|
228
240
|
}
|
|
229
241
|
targets[index] = node.prototype === 'null' ? Object.create(null) : {}
|
|
@@ -239,8 +251,9 @@ function decodeCanonicalValue(wire, options = {}) {
|
|
|
239
251
|
const target = targets[nodeIndex]
|
|
240
252
|
if (node.type === 'array') {
|
|
241
253
|
let previous = -1
|
|
242
|
-
for (
|
|
243
|
-
|
|
254
|
+
for (let entryIndex = 0; entryIndex < node.entries.length; entryIndex += 1) {
|
|
255
|
+
const entry = node.entries[entryIndex]
|
|
256
|
+
if (!isArray(entry) || entry.length !== 2 || !numberIsSafeInteger(entry[0])
|
|
244
257
|
|| entry[0] <= previous || entry[0] < 0 || entry[0] >= node.length) {
|
|
245
258
|
throw new TypeError(`invalid PTC array entry at node ${nodeIndex}`)
|
|
246
259
|
}
|
|
@@ -255,11 +268,12 @@ function decodeCanonicalValue(wire, options = {}) {
|
|
|
255
268
|
continue
|
|
256
269
|
}
|
|
257
270
|
const keys = new Set()
|
|
258
|
-
for (
|
|
259
|
-
|
|
271
|
+
for (let entryIndex = 0; entryIndex < node.entries.length; entryIndex += 1) {
|
|
272
|
+
const entry = node.entries[entryIndex]
|
|
273
|
+
if (!isArray(entry) || entry.length !== 2 || typeof entry[0] !== 'string' || setHas(keys, entry[0])) {
|
|
260
274
|
throw new TypeError(`invalid PTC object entry at node ${nodeIndex}`)
|
|
261
275
|
}
|
|
262
|
-
keys
|
|
276
|
+
setAdd(keys, entry[0])
|
|
263
277
|
budget.textBytes += stringBytes(entry[0])
|
|
264
278
|
if (budget.textBytes > limits.maxStringBytes) throw new TypeError('PTC value string budget exceeded')
|
|
265
279
|
budget.edges += 1
|
|
@@ -270,10 +284,10 @@ function decodeCanonicalValue(wire, options = {}) {
|
|
|
270
284
|
})
|
|
271
285
|
}
|
|
272
286
|
}
|
|
273
|
-
if (budget.reachable
|
|
287
|
+
if (setSize(budget.reachable) !== wire.nodes.length) throw new TypeError('PTC value envelope contains unreachable nodes')
|
|
274
288
|
const canonical = encodeValue(root, limits)
|
|
275
|
-
const serialization =
|
|
276
|
-
if (serialization !==
|
|
289
|
+
const serialization = jsonStringify(canonical)
|
|
290
|
+
if (serialization !== jsonStringify(wire)) throw new TypeError('non-canonical PTC value envelope')
|
|
277
291
|
return { value: root, wire: canonical, serialization }
|
|
278
292
|
}
|
|
279
293
|
|
|
@@ -300,31 +314,32 @@ export function isPlainJsonTree(value) {
|
|
|
300
314
|
const pending = [value]
|
|
301
315
|
const seen = new WeakSet()
|
|
302
316
|
while (pending.length > 0) {
|
|
303
|
-
const current = pending
|
|
317
|
+
const current = popArray(pending)
|
|
304
318
|
if (current === null || typeof current === 'string' || typeof current === 'boolean') continue
|
|
305
|
-
if (typeof current === 'number' &&
|
|
319
|
+
if (typeof current === 'number' && numberIsFinite(current) && !Object.is(current, -0)) continue
|
|
306
320
|
if (typeof current !== 'object') return false
|
|
307
|
-
if (seen
|
|
308
|
-
seen
|
|
309
|
-
if (
|
|
321
|
+
if (weakSetHas(seen, current)) return false
|
|
322
|
+
weakSetAdd(seen, current)
|
|
323
|
+
if (isArray(current)) {
|
|
310
324
|
for (let index = 0; index < current.length; index += 1) {
|
|
311
325
|
if (!Object.hasOwn(current, index)) return false
|
|
312
|
-
pending
|
|
326
|
+
appendArray(pending, current[index])
|
|
313
327
|
}
|
|
314
328
|
} else {
|
|
315
|
-
|
|
329
|
+
const keys = Object.keys(current)
|
|
330
|
+
for (let index = 0; index < keys.length; index += 1) appendArray(pending, current[keys[index]])
|
|
316
331
|
}
|
|
317
332
|
}
|
|
318
333
|
return true
|
|
319
334
|
}
|
|
320
335
|
|
|
321
336
|
function renderKey(key) {
|
|
322
|
-
return IDENTIFIER
|
|
337
|
+
return regexpTest(IDENTIFIER, key) ? key : jsonStringify(key)
|
|
323
338
|
}
|
|
324
339
|
|
|
325
340
|
function atomText(atom) {
|
|
326
|
-
if (atom === null || typeof atom === 'boolean' || typeof atom === 'number') return
|
|
327
|
-
if (typeof atom === 'string') return
|
|
341
|
+
if (atom === null || typeof atom === 'boolean' || typeof atom === 'number') return toString(atom)
|
|
342
|
+
if (typeof atom === 'string') return jsonStringify(atom)
|
|
328
343
|
if (atom.tag === 'undefined') return 'undefined'
|
|
329
344
|
if (atom.tag === 'number') {
|
|
330
345
|
return atom.value === 'nan' ? 'NaN'
|
|
@@ -338,17 +353,24 @@ function atomText(atom) {
|
|
|
338
353
|
/** Deterministic bounded TS-like presentation. It is never a decode format. */
|
|
339
354
|
export function renderValueWire(wire, options = {}) {
|
|
340
355
|
const limits = limitsOf(options)
|
|
341
|
-
|
|
342
|
-
|
|
356
|
+
return renderCanonicalValue(decodeCanonicalValue(wire, limits).wire, limits)
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function renderCanonicalValue(normalized, limits) {
|
|
360
|
+
const references = new Array(normalized.nodes.length)
|
|
361
|
+
for (let index = 0; index < references.length; index += 1) references[index] = 0
|
|
343
362
|
const count = (atom) => {
|
|
344
363
|
if (isRecord(atom) && atom.tag === 'reference') references[atom.index] += 1
|
|
345
364
|
}
|
|
346
365
|
count(normalized.root)
|
|
347
|
-
for (
|
|
366
|
+
for (let nodeIndex = 0; nodeIndex < normalized.nodes.length; nodeIndex += 1) {
|
|
367
|
+
const entries = normalized.nodes[nodeIndex].entries
|
|
368
|
+
for (let entryIndex = 0; entryIndex < entries.length; entryIndex += 1) count(entries[entryIndex][1])
|
|
369
|
+
}
|
|
348
370
|
const labels = new Map()
|
|
349
371
|
let nextLabel = 0
|
|
350
372
|
for (let index = 0; index < references.length; index += 1) {
|
|
351
|
-
if (references[index] > 1) labels
|
|
373
|
+
if (references[index] > 1) mapSet(labels, index, ++nextLabel)
|
|
352
374
|
}
|
|
353
375
|
|
|
354
376
|
const chunks = []
|
|
@@ -356,19 +378,19 @@ export function renderValueWire(wire, options = {}) {
|
|
|
356
378
|
const append = (text) => {
|
|
357
379
|
bytes += stringBytes(text)
|
|
358
380
|
if (bytes > limits.maxStringBytes) throw new TypeError(`rendered PTC value exceeds ${limits.maxStringBytes} bytes`)
|
|
359
|
-
chunks
|
|
381
|
+
appendArray(chunks, text)
|
|
360
382
|
}
|
|
361
383
|
const emitted = new Set()
|
|
362
384
|
const active = new Set()
|
|
363
385
|
const tasks = [{ kind: 'atom', atom: normalized.root }]
|
|
364
386
|
while (tasks.length > 0) {
|
|
365
|
-
const task = tasks
|
|
387
|
+
const task = popArray(tasks)
|
|
366
388
|
if (task.kind === 'text') {
|
|
367
389
|
append(task.text)
|
|
368
390
|
continue
|
|
369
391
|
}
|
|
370
392
|
if (task.kind === 'leave') {
|
|
371
|
-
active
|
|
393
|
+
setDelete(active, task.index)
|
|
372
394
|
continue
|
|
373
395
|
}
|
|
374
396
|
const primitive = atomText(task.atom)
|
|
@@ -377,38 +399,44 @@ export function renderValueWire(wire, options = {}) {
|
|
|
377
399
|
continue
|
|
378
400
|
}
|
|
379
401
|
const index = task.atom.index
|
|
380
|
-
const label = labels
|
|
381
|
-
if (emitted
|
|
382
|
-
append(active
|
|
402
|
+
const label = mapGet(labels, index)
|
|
403
|
+
if (setHas(emitted, index)) {
|
|
404
|
+
append(setHas(active, index) ? `[Circular *${label}]` : `[Reference *${label}]`)
|
|
383
405
|
continue
|
|
384
406
|
}
|
|
385
|
-
emitted
|
|
386
|
-
active
|
|
407
|
+
setAdd(emitted, index)
|
|
408
|
+
setAdd(active, index)
|
|
387
409
|
if (label !== undefined) append(`<ref *${label}> `)
|
|
388
410
|
const node = normalized.nodes[index]
|
|
389
|
-
tasks
|
|
411
|
+
appendArray(tasks, { kind: 'leave', index })
|
|
390
412
|
if (node.type === 'array') {
|
|
391
413
|
append('[')
|
|
392
|
-
tasks
|
|
393
|
-
const entries = new Map(
|
|
394
|
-
|
|
414
|
+
appendArray(tasks, { kind: 'text', text: ']' })
|
|
415
|
+
const entries = new Map()
|
|
416
|
+
for (let entryIndex = 0; entryIndex < node.entries.length; entryIndex += 1) {
|
|
417
|
+
const entry = node.entries[entryIndex]
|
|
418
|
+
mapSet(entries, entry[0], entry[1])
|
|
419
|
+
}
|
|
420
|
+
if (node.length > 0 && !mapHas(entries, node.length - 1)) appendArray(tasks, { kind: 'text', text: ',' })
|
|
395
421
|
for (let item = node.length - 1; item >= 0; item -= 1) {
|
|
396
|
-
if (item < node.length - 1) tasks
|
|
397
|
-
const value = entries
|
|
398
|
-
if (value !== undefined) tasks
|
|
422
|
+
if (item < node.length - 1) appendArray(tasks, { kind: 'text', text: ', ' })
|
|
423
|
+
const value = mapGet(entries, item)
|
|
424
|
+
if (value !== undefined) appendArray(tasks, { kind: 'atom', atom: value })
|
|
399
425
|
}
|
|
400
426
|
continue
|
|
401
427
|
}
|
|
402
428
|
append('{')
|
|
403
|
-
tasks
|
|
429
|
+
appendArray(tasks, { kind: 'text', text: '}' })
|
|
404
430
|
for (let entryIndex = node.entries.length - 1; entryIndex >= 0; entryIndex -= 1) {
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
431
|
+
const entry = node.entries[entryIndex]
|
|
432
|
+
const key = entry[0]
|
|
433
|
+
const value = entry[1]
|
|
434
|
+
if (entryIndex < node.entries.length - 1) appendArray(tasks, { kind: 'text', text: ', ' })
|
|
435
|
+
appendArray(tasks, { kind: 'atom', atom: value })
|
|
436
|
+
appendArray(tasks, { kind: 'text', text: `${renderKey(key)}: ` })
|
|
409
437
|
}
|
|
410
438
|
}
|
|
411
|
-
return
|
|
439
|
+
return join(chunks, '')
|
|
412
440
|
}
|
|
413
441
|
|
|
414
442
|
/**
|
|
@@ -416,6 +444,17 @@ export function renderValueWire(wire, options = {}) {
|
|
|
416
444
|
* text. The untagged ranges overlap because a plain JSON root may itself be a string.
|
|
417
445
|
*/
|
|
418
446
|
export function projectValueWire(wire, options = {}) {
|
|
419
|
-
|
|
420
|
-
|
|
447
|
+
return prepareValueWire(wire, options).projectedValue
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/** Validate once when canonical storage and outer presentation are both required. */
|
|
451
|
+
export function prepareValueWire(wire, options = {}) {
|
|
452
|
+
const limits = limitsOf(options)
|
|
453
|
+
const canonical = decodeCanonicalValue(wire, limits)
|
|
454
|
+
return {
|
|
455
|
+
wire: canonical.wire,
|
|
456
|
+
projectedValue: isPlainJsonTree(canonical.value)
|
|
457
|
+
? canonical.value
|
|
458
|
+
: renderCanonicalValue(canonical.wire, limits),
|
|
459
|
+
}
|
|
421
460
|
}
|
|
@@ -1,65 +1,32 @@
|
|
|
1
1
|
import { mkdtemp, rm } from 'node:fs/promises'
|
|
2
2
|
import { tmpdir } from 'node:os'
|
|
3
3
|
import { isAbsolute, join } from 'node:path'
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { messageOf, processExitDescription } from './failure-reporting.js'
|
|
5
|
+
import { IsolatedOwner } from './isolated-worker.js'
|
|
6
|
+
import { normalizeWorkerEnvironment } from './worker-environment.js'
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
['appdata', 'APPDATA'],
|
|
9
|
-
['comspec', 'ComSpec'],
|
|
10
|
-
['home', 'HOME'],
|
|
11
|
-
['homedrive', 'HOMEDRIVE'],
|
|
12
|
-
['homepath', 'HOMEPATH'],
|
|
13
|
-
['localappdata', 'LOCALAPPDATA'],
|
|
14
|
-
['path', 'PATH'],
|
|
15
|
-
['pathext', 'PATHEXT'],
|
|
16
|
-
['programdata', 'ProgramData'],
|
|
17
|
-
['programfiles', 'ProgramFiles'],
|
|
18
|
-
['programfiles(x86)', 'ProgramFiles(x86)'],
|
|
19
|
-
['systemdrive', 'SystemDrive'],
|
|
20
|
-
['systemroot', 'SystemRoot'],
|
|
21
|
-
['temp', 'TEMP'],
|
|
22
|
-
['tmp', 'TMP'],
|
|
23
|
-
['userprofile', 'USERPROFILE'],
|
|
24
|
-
['windir', 'windir'],
|
|
25
|
-
])
|
|
26
|
-
const HOST_ONLY_ENVIRONMENT_NAMES = new Set(['node_test_context', 'node_v8_coverage'])
|
|
8
|
+
export { normalizeWorkerEnvironment }
|
|
27
9
|
|
|
28
|
-
|
|
29
|
-
export function normalizeWorkerEnvironment(source, platform = process.platform) {
|
|
30
|
-
if (platform !== 'win32') {
|
|
31
|
-
return Object.fromEntries(Object.entries(source).filter(([name, value]) => (
|
|
32
|
-
value !== undefined && !HOST_ONLY_ENVIRONMENT_NAMES.has(name.toLowerCase())
|
|
33
|
-
)))
|
|
34
|
-
}
|
|
35
|
-
const normalized = new Map()
|
|
36
|
-
for (const [name, value] of Object.entries(source)) {
|
|
37
|
-
if (value === undefined) continue
|
|
38
|
-
const key = name.toLowerCase()
|
|
39
|
-
if (HOST_ONLY_ENVIRONMENT_NAMES.has(key)) continue
|
|
40
|
-
const canonicalName = WINDOWS_ENVIRONMENT_NAMES.get(key) ?? name
|
|
41
|
-
const current = normalized.get(key)
|
|
42
|
-
if (current === undefined || name === canonicalName) {
|
|
43
|
-
normalized.set(key, [canonicalName, value])
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return Object.fromEntries(normalized.values())
|
|
47
|
-
}
|
|
10
|
+
const KERNEL_HELPER = new URL('./kernel-child.js', import.meta.url)
|
|
48
11
|
|
|
49
12
|
/** Owns one session kernel's worker process, private port, and scratch directory. */
|
|
50
13
|
export class WorkerClient {
|
|
51
|
-
constructor({ workerUrl, cwd, onMessage, onFailure }) {
|
|
14
|
+
constructor({ workerUrl, cwd, onMessage, onFailure, compilerCache }) {
|
|
52
15
|
this.workerUrl = workerUrl
|
|
53
16
|
this.cwd = cwd
|
|
54
17
|
this.onMessage = onMessage
|
|
55
18
|
this.onFailure = onFailure
|
|
19
|
+
this.compilerCache = compilerCache
|
|
56
20
|
this.worker = undefined
|
|
57
21
|
this.workerLimit = undefined
|
|
58
22
|
this.workerReady = undefined
|
|
59
23
|
this.port = undefined
|
|
60
24
|
this.scratchReady = undefined
|
|
61
25
|
this.stderrTails = new WeakMap()
|
|
62
|
-
|
|
26
|
+
// One owner keeps every started kernel transport, including startup failures
|
|
27
|
+
// and closes that were refused, until its real exit is observed.
|
|
28
|
+
this.owner = new IsolatedOwner()
|
|
29
|
+
this.hostIds = new WeakMap()
|
|
63
30
|
this.disposed = false
|
|
64
31
|
}
|
|
65
32
|
|
|
@@ -101,19 +68,20 @@ export class WorkerClient {
|
|
|
101
68
|
const environment = normalizeWorkerEnvironment(process.env)
|
|
102
69
|
let worker
|
|
103
70
|
try {
|
|
104
|
-
|
|
71
|
+
const started = this.owner.start({
|
|
72
|
+
helper: KERNEL_HELPER,
|
|
73
|
+
entry: this.workerUrl,
|
|
74
|
+
workerData: { cwd: this.cwd, compilerCache: this.compilerCache?.() },
|
|
75
|
+
resourceLimits: { maxOldGenerationSizeMb: this.workerLimit },
|
|
105
76
|
env: {
|
|
106
77
|
...environment,
|
|
107
78
|
TEMP: scratchDirectory,
|
|
108
79
|
TMP: scratchDirectory,
|
|
109
80
|
TMPDIR: scratchDirectory,
|
|
110
81
|
},
|
|
111
|
-
execArgv: [],
|
|
112
|
-
workerData: this.cwd === undefined ? {} : { cwd: this.cwd },
|
|
113
|
-
resourceLimits: { maxOldGenerationSizeMb: this.workerLimit },
|
|
114
|
-
stdout: true,
|
|
115
|
-
stderr: true,
|
|
116
82
|
})
|
|
83
|
+
worker = started.transport
|
|
84
|
+
this.hostIds.set(worker, started.id)
|
|
117
85
|
} catch (error) {
|
|
118
86
|
this.workerLimit = undefined
|
|
119
87
|
throw error
|
|
@@ -129,15 +97,23 @@ export class WorkerClient {
|
|
|
129
97
|
})
|
|
130
98
|
this.stderrTails.set(worker, stderrTail)
|
|
131
99
|
worker.on('error', error => this.fail(worker, `worker error: ${messageOf(error)}`))
|
|
132
|
-
worker.on('exit', code
|
|
100
|
+
worker.on('exit', (code, signal) => {
|
|
101
|
+
this.fail(worker, `worker exited with ${processExitDescription(code, signal)}`)
|
|
102
|
+
})
|
|
133
103
|
this.worker = worker
|
|
134
104
|
this.workerReady = new Promise((resolve, reject) => {
|
|
135
|
-
const onError = error =>
|
|
136
|
-
const onExit = (code) => {
|
|
105
|
+
const onError = error => {
|
|
137
106
|
const detail = this.stderrDetail(worker)
|
|
107
|
+
reject(detail === undefined
|
|
108
|
+
? error
|
|
109
|
+
: new Error(`${messageOf(error)}; last stderr: ${detail}`, { cause: error }))
|
|
110
|
+
}
|
|
111
|
+
const onExit = (code, signal) => {
|
|
112
|
+
const detail = this.stderrDetail(worker)
|
|
113
|
+
const exit = processExitDescription(code, signal)
|
|
138
114
|
reject(new Error(detail === undefined
|
|
139
|
-
? `worker exited with
|
|
140
|
-
: `worker exited with
|
|
115
|
+
? `worker exited with ${exit} before opening its private channel`
|
|
116
|
+
: `worker exited with ${exit} before opening its private channel; last stderr: ${detail}`))
|
|
141
117
|
}
|
|
142
118
|
worker.once('error', onError)
|
|
143
119
|
worker.once('exit', onExit)
|
|
@@ -146,12 +122,16 @@ export class WorkerClient {
|
|
|
146
122
|
worker.removeListener('exit', onExit)
|
|
147
123
|
if (message?.type === 'startup-error' && typeof message.error === 'string') {
|
|
148
124
|
reject(new Error(message.error))
|
|
149
|
-
|
|
125
|
+
// The owner keeps a refused reclamation, so this must not surface as an
|
|
126
|
+
// unhandled rejection after the caller already handled the startup error.
|
|
127
|
+
void this.reset(worker).catch(() => {})
|
|
150
128
|
return
|
|
151
129
|
}
|
|
152
130
|
if (message?.type !== 'ready' || typeof message.port?.postMessage !== 'function') {
|
|
153
131
|
reject(new Error('kernel worker returned an invalid private channel'))
|
|
154
|
-
|
|
132
|
+
// The owner keeps a refused reclamation, so this must not surface as an
|
|
133
|
+
// unhandled rejection after the caller already handled the startup error.
|
|
134
|
+
void this.reset(worker).catch(() => {})
|
|
155
135
|
return
|
|
156
136
|
}
|
|
157
137
|
this.port = message.port
|
|
@@ -185,27 +165,37 @@ export class WorkerClient {
|
|
|
185
165
|
}
|
|
186
166
|
|
|
187
167
|
async reset(worker) {
|
|
168
|
+
const port = this.worker === worker ? this.port : undefined
|
|
188
169
|
if (this.worker === worker) {
|
|
189
170
|
this.worker = undefined
|
|
190
171
|
this.workerLimit = undefined
|
|
191
172
|
this.workerReady = undefined
|
|
192
|
-
this.port?.close()
|
|
193
173
|
this.port = undefined
|
|
194
174
|
}
|
|
195
|
-
|
|
196
|
-
this.
|
|
197
|
-
|
|
198
|
-
await termination
|
|
199
|
-
} finally {
|
|
200
|
-
this.terminations.delete(termination)
|
|
201
|
-
}
|
|
175
|
+
await this.owner.stop(this.hostIds.get(worker))
|
|
176
|
+
this.hostIds.delete(worker)
|
|
177
|
+
port?.close()
|
|
202
178
|
}
|
|
203
179
|
|
|
204
180
|
async dispose() {
|
|
205
181
|
this.disposed = true
|
|
206
182
|
const worker = this.worker
|
|
207
|
-
|
|
208
|
-
|
|
183
|
+
const port = this.port
|
|
184
|
+
this.worker = undefined
|
|
185
|
+
this.workerLimit = undefined
|
|
186
|
+
this.workerReady = undefined
|
|
187
|
+
this.port = undefined
|
|
188
|
+
if (worker !== undefined) this.hostIds.delete(worker)
|
|
189
|
+
const failures = []
|
|
190
|
+
try {
|
|
191
|
+
port?.close()
|
|
192
|
+
} catch (error) {
|
|
193
|
+
failures.push(error)
|
|
194
|
+
}
|
|
195
|
+
failures.push(...await this.owner.dispose())
|
|
196
|
+
if (failures.length > 0) {
|
|
197
|
+
throw new AggregateError(failures, 'ptc-plus kernel worker disposal failed')
|
|
198
|
+
}
|
|
209
199
|
if (this.scratchReady !== undefined) {
|
|
210
200
|
try {
|
|
211
201
|
const scratchDirectory = await this.scratchReady
|