@inhuman.tools/plexus-expectations 0.0.0-stage → 0.2.0
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/LICENSE.md +7 -0
- package/README.md +144 -2
- package/dist/driver/ExpectationActor.d.ts +56 -0
- package/dist/driver/ExpectationActor.d.ts.map +1 -0
- package/dist/driver/ExpectationActor.js +210 -0
- package/dist/driver/ExpectationActor.js.map +1 -0
- package/dist/driver/PEWDriver.d.ts +144 -0
- package/dist/driver/PEWDriver.d.ts.map +1 -0
- package/dist/driver/PEWDriver.js +303 -0
- package/dist/driver/PEWDriver.js.map +1 -0
- package/dist/driver/actor-machine.d.ts +28 -0
- package/dist/driver/actor-machine.d.ts.map +1 -0
- package/dist/driver/actor-machine.js +50 -0
- package/dist/driver/actor-machine.js.map +1 -0
- package/dist/driver/connection-miss.d.ts +9 -0
- package/dist/driver/connection-miss.d.ts.map +1 -0
- package/dist/driver/connection-miss.js +13 -0
- package/dist/driver/connection-miss.js.map +1 -0
- package/dist/driver/index.d.ts +6 -0
- package/dist/driver/index.d.ts.map +1 -0
- package/dist/driver/index.js +5 -0
- package/dist/driver/index.js.map +1 -0
- package/dist/executor/index.d.ts +4 -0
- package/dist/executor/index.d.ts.map +1 -0
- package/dist/executor/index.js +4 -0
- package/dist/executor/index.js.map +1 -0
- package/dist/executor/kernel-intern.d.ts +16 -0
- package/dist/executor/kernel-intern.d.ts.map +1 -0
- package/dist/executor/kernel-intern.js +98 -0
- package/dist/executor/kernel-intern.js.map +1 -0
- package/dist/executor/orchestrator.d.ts +97 -0
- package/dist/executor/orchestrator.d.ts.map +1 -0
- package/dist/executor/orchestrator.js +465 -0
- package/dist/executor/orchestrator.js.map +1 -0
- package/dist/executor/types.d.ts +8 -0
- package/dist/executor/types.d.ts.map +1 -0
- package/dist/executor/types.js +2 -0
- package/dist/executor/types.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/connection.d.ts +4 -0
- package/dist/schemas/connection.d.ts.map +1 -0
- package/dist/schemas/connection.js +4 -0
- package/dist/schemas/connection.js.map +1 -0
- package/dist/schemas/index.d.ts +2 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +2 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/shared/control.d.ts +4 -0
- package/dist/shared/control.d.ts.map +1 -0
- package/dist/shared/control.js +2 -0
- package/dist/shared/control.js.map +1 -0
- package/dist/shared/index.d.ts +7 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +7 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/shared/map-field-awareness.d.ts +19 -0
- package/dist/shared/map-field-awareness.d.ts.map +1 -0
- package/dist/shared/map-field-awareness.js +58 -0
- package/dist/shared/map-field-awareness.js.map +1 -0
- package/dist/shared/models/ConnectionSpec.d.ts +44 -0
- package/dist/shared/models/ConnectionSpec.d.ts.map +1 -0
- package/dist/shared/models/ConnectionSpec.js +180 -0
- package/dist/shared/models/ConnectionSpec.js.map +1 -0
- package/dist/shared/models/DriverSpec.d.ts +45 -0
- package/dist/shared/models/DriverSpec.d.ts.map +1 -0
- package/dist/shared/models/DriverSpec.js +221 -0
- package/dist/shared/models/DriverSpec.js.map +1 -0
- package/dist/shared/models/Job.d.ts +85 -0
- package/dist/shared/models/Job.d.ts.map +1 -0
- package/dist/shared/models/Job.js +261 -0
- package/dist/shared/models/Job.js.map +1 -0
- package/dist/shared/models/WorkContainer.d.ts +36 -0
- package/dist/shared/models/WorkContainer.d.ts.map +1 -0
- package/dist/shared/models/WorkContainer.js +140 -0
- package/dist/shared/models/WorkContainer.js.map +1 -0
- package/dist/shared/models/WorkExpectation.d.ts +63 -0
- package/dist/shared/models/WorkExpectation.d.ts.map +1 -0
- package/dist/shared/models/WorkExpectation.js +290 -0
- package/dist/shared/models/WorkExpectation.js.map +1 -0
- package/dist/shared/models/index.d.ts +7 -0
- package/dist/shared/models/index.d.ts.map +1 -0
- package/dist/shared/models/index.js +6 -0
- package/dist/shared/models/index.js.map +1 -0
- package/dist/shared/models/jsonRecord.d.ts +14 -0
- package/dist/shared/models/jsonRecord.d.ts.map +1 -0
- package/dist/shared/models/jsonRecord.js +46 -0
- package/dist/shared/models/jsonRecord.js.map +1 -0
- package/dist/shared/revalidation-demand.d.ts +27 -0
- package/dist/shared/revalidation-demand.d.ts.map +1 -0
- package/dist/shared/revalidation-demand.js +116 -0
- package/dist/shared/revalidation-demand.js.map +1 -0
- package/dist/shared/role.d.ts +12 -0
- package/dist/shared/role.d.ts.map +1 -0
- package/dist/shared/role.js +21 -0
- package/dist/shared/role.js.map +1 -0
- package/dist/shared/types.d.ts +43 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +12 -0
- package/dist/shared/types.js.map +1 -0
- package/package.json +79 -3
- package/src/driver/ExpectationActor.ts +244 -0
- package/src/driver/PEWDriver.ts +302 -0
- package/src/driver/actor-machine.ts +54 -0
- package/src/driver/connection-miss.ts +13 -0
- package/src/driver/index.ts +5 -0
- package/src/executor/index.ts +3 -0
- package/src/executor/kernel-intern.ts +121 -0
- package/src/executor/orchestrator.ts +463 -0
- package/src/executor/types.ts +3 -0
- package/src/index.ts +1 -0
- package/src/schemas/connection.ts +4 -0
- package/src/schemas/index.ts +1 -0
- package/src/shared/control.ts +4 -0
- package/src/shared/index.ts +6 -0
- package/src/shared/map-field-awareness.ts +65 -0
- package/src/shared/models/ConnectionSpec.ts +84 -0
- package/src/shared/models/DriverSpec.ts +102 -0
- package/src/shared/models/Job.ts +192 -0
- package/src/shared/models/WorkContainer.ts +90 -0
- package/src/shared/models/WorkExpectation.ts +229 -0
- package/src/shared/models/index.ts +6 -0
- package/src/shared/models/jsonRecord.ts +49 -0
- package/src/shared/revalidation-demand.ts +121 -0
- package/src/shared/role.ts +32 -0
- package/src/shared/types.ts +57 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
2
|
+
var useValue = arguments.length > 2;
|
|
3
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
4
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
5
|
+
}
|
|
6
|
+
return useValue ? value : void 0;
|
|
7
|
+
};
|
|
8
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
9
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
10
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
11
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
12
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
13
|
+
var _, done = false;
|
|
14
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
15
|
+
var context = {};
|
|
16
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
17
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
18
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
19
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
20
|
+
if (kind === "accessor") {
|
|
21
|
+
if (result === void 0) continue;
|
|
22
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
23
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
24
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
25
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
26
|
+
}
|
|
27
|
+
else if (_ = accept(result)) {
|
|
28
|
+
if (kind === "field") initializers.unshift(_);
|
|
29
|
+
else descriptor[key] = _;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
33
|
+
done = true;
|
|
34
|
+
};
|
|
35
|
+
import { DefaultedWeakMap } from "@here.build/collections";
|
|
36
|
+
import { action, computed, observable, toJS } from "mobx";
|
|
37
|
+
import { pewConfigurationSchema, pewSecretsSchema } from "../schemas/connection.js";
|
|
38
|
+
import { revalidationDemandOf } from "../shared/revalidation-demand.js";
|
|
39
|
+
const EMPTY_SECRETS = { get: () => undefined };
|
|
40
|
+
/** Module-graph duplication breaks instanceof; the elevation gate checks this brand too. */
|
|
41
|
+
// eslint-disable-next-line no-secrets/no-secrets -- package identity tag, not a secret
|
|
42
|
+
export const PEW_DRIVER_BRAND = "@inhuman.tools/plexus-expectations:PEWDriver@1";
|
|
43
|
+
/**
|
|
44
|
+
* Driver extras the orchestrator mirrors onto `spec.live`.
|
|
45
|
+
* Core process facts live on `readyState` / `error` / `offers`.
|
|
46
|
+
*/
|
|
47
|
+
export function extraLiveFromState(state) {
|
|
48
|
+
const extra = {};
|
|
49
|
+
for (const [key, value] of Object.entries(state)) {
|
|
50
|
+
if (value === undefined)
|
|
51
|
+
continue;
|
|
52
|
+
extra[key] = toJS(value);
|
|
53
|
+
}
|
|
54
|
+
return extra;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The CLASS is the driver (one loaded module per driver name).
|
|
58
|
+
* The INSTANCE is a live connection (one per connection id).
|
|
59
|
+
*
|
|
60
|
+
* CLAIM DOES NOT LIVE HERE. Which WorkExpectation classes this driver handles
|
|
61
|
+
* lives on the advertised driver record (`can`): dynamically loaded code does
|
|
62
|
+
* not get to declare its own authority.
|
|
63
|
+
*
|
|
64
|
+
* Live state is report only — no machinery branches on it. Instance bootstrap
|
|
65
|
+
* truth is `bootstrapDone` / `bootstrapPending`. Drivers read {@link attention}
|
|
66
|
+
* as the reduced token, never clientIds.
|
|
67
|
+
*
|
|
68
|
+
* YAML doors ({@link configurationSchema} / {@link secretsSchema}) parse
|
|
69
|
+
* without `new`. Secrets stay a live view — the host may replace it (reauth).
|
|
70
|
+
*/
|
|
71
|
+
let PEWDriver = (() => {
|
|
72
|
+
var _a, _b;
|
|
73
|
+
let _instanceExtraInitializers = [];
|
|
74
|
+
let _readyState_decorators;
|
|
75
|
+
let _readyState_initializers = [];
|
|
76
|
+
let _readyState_extraInitializers = [];
|
|
77
|
+
let _error_decorators;
|
|
78
|
+
let _error_initializers = [];
|
|
79
|
+
let _error_extraInitializers = [];
|
|
80
|
+
let _state_decorators;
|
|
81
|
+
let _state_initializers = [];
|
|
82
|
+
let _state_extraInitializers = [];
|
|
83
|
+
let _awareness_decorators;
|
|
84
|
+
let _awareness_initializers = [];
|
|
85
|
+
let _awareness_extraInitializers = [];
|
|
86
|
+
let _get_revalidationDemand_decorators;
|
|
87
|
+
let _ensureBootstrapStarted_decorators;
|
|
88
|
+
let _failReady_decorators;
|
|
89
|
+
return class PEWDriver {
|
|
90
|
+
static {
|
|
91
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
92
|
+
__esDecorate(this, null, _readyState_decorators, { kind: "accessor", name: "readyState", static: false, private: false, access: { has: obj => "readyState" in obj, get: obj => obj.readyState, set: (obj, value) => { obj.readyState = value; } }, metadata: _metadata }, _readyState_initializers, _readyState_extraInitializers);
|
|
93
|
+
__esDecorate(this, null, _error_decorators, { kind: "accessor", name: "error", static: false, private: false, access: { has: obj => "error" in obj, get: obj => obj.error, set: (obj, value) => { obj.error = value; } }, metadata: _metadata }, _error_initializers, _error_extraInitializers);
|
|
94
|
+
__esDecorate(this, null, _state_decorators, { kind: "accessor", name: "state", static: false, private: false, access: { has: obj => "state" in obj, get: obj => obj.state, set: (obj, value) => { obj.state = value; } }, metadata: _metadata }, _state_initializers, _state_extraInitializers);
|
|
95
|
+
__esDecorate(this, null, _awareness_decorators, { kind: "accessor", name: "awareness", static: false, private: false, access: { has: obj => "awareness" in obj, get: obj => obj.awareness, set: (obj, value) => { obj.awareness = value; } }, metadata: _metadata }, _awareness_initializers, _awareness_extraInitializers);
|
|
96
|
+
__esDecorate(this, null, _get_revalidationDemand_decorators, { kind: "getter", name: "revalidationDemand", static: false, private: false, access: { has: obj => "revalidationDemand" in obj, get: obj => obj.revalidationDemand }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
97
|
+
__esDecorate(this, null, _ensureBootstrapStarted_decorators, { kind: "method", name: "ensureBootstrapStarted", static: false, private: false, access: { has: obj => "ensureBootstrapStarted" in obj, get: obj => obj.ensureBootstrapStarted }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
98
|
+
__esDecorate(this, null, _failReady_decorators, { kind: "method", name: "failReady", static: false, private: false, access: { has: obj => "failReady" in obj, get: obj => obj.failReady }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
99
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
100
|
+
}
|
|
101
|
+
connectionId = __runInitializers(this, _instanceExtraInitializers);
|
|
102
|
+
// ── driver side (the CLASS is the driver) ─────────────────────────────
|
|
103
|
+
static pewBrand = PEW_DRIVER_BRAND;
|
|
104
|
+
/**
|
|
105
|
+
* YAML settings shape. Default is a passthrough record — subclasses replace
|
|
106
|
+
* this with a real object schema (transforms included). First issue message
|
|
107
|
+
* is what construct / parse throw.
|
|
108
|
+
*/
|
|
109
|
+
static configurationSchema = pewConfigurationSchema;
|
|
110
|
+
static configurationLabel = "";
|
|
111
|
+
static configurationError(message) {
|
|
112
|
+
const label = this.configurationLabel.trim();
|
|
113
|
+
return label === "" ? `connection config invalid: ${message}` : `${label} connection config invalid: ${message}`;
|
|
114
|
+
}
|
|
115
|
+
/** YAML settings door. Does not construct an instance. */
|
|
116
|
+
static parseConfiguration(settings = {}) {
|
|
117
|
+
const result = this.configurationSchema.safeParse(settings ?? {});
|
|
118
|
+
if (result.success)
|
|
119
|
+
return result.data;
|
|
120
|
+
const message = result.error.issues[0]?.message ?? "invalid";
|
|
121
|
+
throw new Error(this.configurationError(message));
|
|
122
|
+
}
|
|
123
|
+
static secretsSchema = pewSecretsSchema;
|
|
124
|
+
static secretsError(message) {
|
|
125
|
+
const label = this.configurationLabel.trim();
|
|
126
|
+
return label === "" ? `connection secrets invalid: ${message}` : `${label} connection secrets invalid: ${message}`;
|
|
127
|
+
}
|
|
128
|
+
/** YAML secrets door. Does not construct an instance. */
|
|
129
|
+
static parseSecrets(secrets = {}) {
|
|
130
|
+
const result = this.secretsSchema.safeParse(secrets ?? {});
|
|
131
|
+
if (result.success)
|
|
132
|
+
return result.data;
|
|
133
|
+
const message = result.error.issues[0]?.message ?? "invalid";
|
|
134
|
+
throw new Error(this.secretsError(message));
|
|
135
|
+
}
|
|
136
|
+
/** Shared on the prototype chain — own extras per subclass. */
|
|
137
|
+
static state = {};
|
|
138
|
+
/**
|
|
139
|
+
* A plain list, or a getter over observable state when the driver keeps its
|
|
140
|
+
* offers fresh (local detection). The orchestrator mirrors it onto
|
|
141
|
+
* `DriverSpec.offers` with a reaction, which is also what observes it.
|
|
142
|
+
*/
|
|
143
|
+
static offers = [];
|
|
144
|
+
/** Kernel-plane occupancy hub. Orchestrator assignment is own-per-class. */
|
|
145
|
+
static awareness;
|
|
146
|
+
static driverName = "";
|
|
147
|
+
/**
|
|
148
|
+
* A getter, not `@computed` — MobX: the decorator's initializer pins to the
|
|
149
|
+
* declaring class, so subclasses would share one computed.
|
|
150
|
+
*/
|
|
151
|
+
static get revalidationDemand() {
|
|
152
|
+
const hub = this.awareness;
|
|
153
|
+
const name = this.driverName;
|
|
154
|
+
if (hub === undefined || name === "")
|
|
155
|
+
return "none";
|
|
156
|
+
return revalidationDemandOf(hub, { kind: "driver", name });
|
|
157
|
+
}
|
|
158
|
+
#readyState_accessor_storage = __runInitializers(this, _readyState_initializers, null);
|
|
159
|
+
// ── connection side (the INSTANCE is the connection) ──────────────────
|
|
160
|
+
get readyState() { return this.#readyState_accessor_storage; }
|
|
161
|
+
set readyState(value) { this.#readyState_accessor_storage = value; }
|
|
162
|
+
#error_accessor_storage = (__runInitializers(this, _readyState_extraInitializers), __runInitializers(this, _error_initializers, ""));
|
|
163
|
+
get error() { return this.#error_accessor_storage; }
|
|
164
|
+
set error(value) { this.#error_accessor_storage = value; }
|
|
165
|
+
#state_accessor_storage = (__runInitializers(this, _error_extraInitializers), __runInitializers(this, _state_initializers, {}));
|
|
166
|
+
/**
|
|
167
|
+
* MobX: shallow — extras (models, tools, account) are replaced wholesale.
|
|
168
|
+
* Override the accessor with a wider literal to add driver facts.
|
|
169
|
+
*/
|
|
170
|
+
get state() { return this.#state_accessor_storage; }
|
|
171
|
+
set state(value) { this.#state_accessor_storage = value; }
|
|
172
|
+
#awareness_accessor_storage = (__runInitializers(this, _state_extraInitializers), __runInitializers(this, _awareness_initializers, undefined));
|
|
173
|
+
get awareness() { return this.#awareness_accessor_storage; }
|
|
174
|
+
set awareness(value) { this.#awareness_accessor_storage = value; }
|
|
175
|
+
get revalidationDemand() {
|
|
176
|
+
const hub = this.awareness;
|
|
177
|
+
if (hub === undefined)
|
|
178
|
+
return "none";
|
|
179
|
+
return revalidationDemandOf(hub, { kind: "connection", id: this.connectionId });
|
|
180
|
+
}
|
|
181
|
+
constructor(connectionId, settings = {}, secrets = EMPTY_SECRETS) {
|
|
182
|
+
this.connectionId = connectionId;
|
|
183
|
+
const cls = new.target;
|
|
184
|
+
this.settings = cls.parseConfiguration(settings);
|
|
185
|
+
const view = typeof secrets.get === "function"
|
|
186
|
+
? secrets
|
|
187
|
+
: {
|
|
188
|
+
get: (field) => secrets[field],
|
|
189
|
+
all: () => secrets,
|
|
190
|
+
};
|
|
191
|
+
cls.parseSecrets(view.all?.() ?? {});
|
|
192
|
+
this.secrets = view;
|
|
193
|
+
}
|
|
194
|
+
settings = __runInitializers(this, _awareness_extraInitializers);
|
|
195
|
+
/** Live view. The host may replace it (reauth). */
|
|
196
|
+
secrets;
|
|
197
|
+
/**
|
|
198
|
+
* Host binds the disk writer for this connection's settings bag.
|
|
199
|
+
* Parsed settings stay readonly; a driver that learns a value overrides this.
|
|
200
|
+
*/
|
|
201
|
+
bindSettingsWriter(_write) { }
|
|
202
|
+
bootstrapDone = false;
|
|
203
|
+
bootstrapPending = null;
|
|
204
|
+
/** `resolve()` threw — the connection is broken, not the work. */
|
|
205
|
+
broken = false;
|
|
206
|
+
/** Concurrent callers share one promise. Async settlement is its own MobX action. */
|
|
207
|
+
ensureBootstrapStarted() {
|
|
208
|
+
if (this.bootstrapDone)
|
|
209
|
+
return Promise.resolve();
|
|
210
|
+
if (this.bootstrapPending)
|
|
211
|
+
return this.bootstrapPending;
|
|
212
|
+
this.readyState = "bootstrapping";
|
|
213
|
+
this.error = "";
|
|
214
|
+
this.bootstrapPending = this.bootstrap().then(action(() => {
|
|
215
|
+
this.bootstrapDone = true;
|
|
216
|
+
this.readyState = "ready";
|
|
217
|
+
this.error = "";
|
|
218
|
+
}), action((error) => {
|
|
219
|
+
this.readyState = "failed";
|
|
220
|
+
this.error = error instanceof Error ? error.message : String(error);
|
|
221
|
+
throw error;
|
|
222
|
+
}));
|
|
223
|
+
return this.bootstrapPending;
|
|
224
|
+
}
|
|
225
|
+
failReady(error) {
|
|
226
|
+
this.broken = true;
|
|
227
|
+
this.readyState = "failed";
|
|
228
|
+
this.error = error instanceof Error ? error.message : String(error);
|
|
229
|
+
}
|
|
230
|
+
/** Nothing in PEW calls this; hosts or tests may. */
|
|
231
|
+
async [(_readyState_decorators = [observable], _error_decorators = [observable], _state_decorators = [(_a = observable).shallow.bind(_a)], _awareness_decorators = [(_b = observable).ref.bind(_b)], _get_revalidationDemand_decorators = [computed], _ensureBootstrapStarted_decorators = [action], _failReady_decorators = [action], Symbol.asyncDispose)]() { }
|
|
232
|
+
actors = new DefaultedWeakMap((expectation) => {
|
|
233
|
+
const actor = this.resolve(expectation);
|
|
234
|
+
this.liveActors.add(actor);
|
|
235
|
+
return actor;
|
|
236
|
+
});
|
|
237
|
+
liveActors = new Set();
|
|
238
|
+
has(expectation) {
|
|
239
|
+
return this.actors.has(expectation);
|
|
240
|
+
}
|
|
241
|
+
resolveExpectation(expectation) {
|
|
242
|
+
return this.actors.get(expectation);
|
|
243
|
+
}
|
|
244
|
+
release(expectation) {
|
|
245
|
+
if (!this.actors.has(expectation))
|
|
246
|
+
return;
|
|
247
|
+
const actor = this.actors.get(expectation);
|
|
248
|
+
this.actors.delete(expectation);
|
|
249
|
+
this.liveActors.delete(actor);
|
|
250
|
+
actor.leave();
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Logout/drop aborts interned work. Not park. Subclasses abort `#inflight`.
|
|
254
|
+
*/
|
|
255
|
+
abortOnDrop(reason) {
|
|
256
|
+
for (const actor of this.liveActors)
|
|
257
|
+
actor.abort(reason);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Honor one demand.
|
|
261
|
+
* `resolve()` throwing is a broken connection, not a failed expectation.
|
|
262
|
+
*/
|
|
263
|
+
handle(expectation) {
|
|
264
|
+
if (this.broken)
|
|
265
|
+
return;
|
|
266
|
+
if (expectation.isDone)
|
|
267
|
+
return;
|
|
268
|
+
if (this.has(expectation)) {
|
|
269
|
+
const existing = this.resolveExpectation(expectation);
|
|
270
|
+
if (!existing.started)
|
|
271
|
+
existing.start();
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
let actor;
|
|
275
|
+
try {
|
|
276
|
+
actor = this.resolveExpectation(expectation);
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
this.failReady(error);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
// Awareness / host policy may have sealed the work while resolve() ran.
|
|
283
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- resolve() can seal via spawn hooks
|
|
284
|
+
if (expectation.isDone) {
|
|
285
|
+
actor.leave();
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
if (!actor.started) {
|
|
289
|
+
actor.start();
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
})();
|
|
294
|
+
export { PEWDriver };
|
|
295
|
+
export function isDriverConnectionClass(value) {
|
|
296
|
+
if (typeof value !== "function")
|
|
297
|
+
return false;
|
|
298
|
+
if (value.pewBrand === PEW_DRIVER_BRAND)
|
|
299
|
+
return true;
|
|
300
|
+
return (typeof value.prototype === "object" &&
|
|
301
|
+
value.prototype instanceof PEWDriver);
|
|
302
|
+
}
|
|
303
|
+
//# sourceMappingURL=PEWDriver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PEWDriver.js","sourceRoot":"","sources":["../../src/driver/PEWDriver.ts"],"names":[],"mappings":"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","sourcesContent":["import { DefaultedWeakMap } from \"@here.build/collections\";\nimport type { PlexusAwareness } from \"@here.build/plexus\";\nimport { action, computed, observable, toJS } from \"mobx\";\n\nimport type { ExpectationActor } from \"./ExpectationActor.js\";\nimport { pewConfigurationSchema, pewSecretsSchema } from \"../schemas/connection.js\";\nimport type { AnyWorkExpectation, WorkExpectation } from \"../shared/models/WorkExpectation.js\";\nimport { revalidationDemandOf, type RevalidationDemand } from \"../shared/revalidation-demand.js\";\nimport type { ReadyState } from \"../shared/types.js\";\n\n/** Duck-typed — drivers may ship a different Zod major than this package. */\nexport type ConfigurationSchema = {\n safeParse(\n data: unknown,\n ):\n | { readonly success: true; readonly data: unknown }\n | { readonly success: false; readonly error: { readonly issues: ReadonlyArray<{ readonly message?: string }> } };\n};\n\n/** Process-local. Secrets are never a Plexus field. */\nexport type ConnectionSecretsView = {\n readonly revision?: number;\n get(field: string): unknown | undefined;\n /** MCP env_* enumerates deposited fields. */\n all?(): Readonly<Record<string, unknown>> | undefined;\n /** Hosts that persist a refreshed session (reauth) implement this. */\n replace?(secrets: Readonly<Record<string, string>>): void;\n};\n\nconst EMPTY_SECRETS: ConnectionSecretsView = { get: () => undefined };\n\n/** Module-graph duplication breaks instanceof; the elevation gate checks this brand too. */\n// eslint-disable-next-line no-secrets/no-secrets -- package identity tag, not a secret\nexport const PEW_DRIVER_BRAND = \"@inhuman.tools/plexus-expectations:PEWDriver@1\";\n\n/** A connection construction the driver already knows will work. */\nexport type DriverOffer = {\n readonly name: string;\n readonly description: string;\n readonly live: boolean;\n readonly adoptable: boolean;\n readonly settings: Readonly<Record<string, unknown>>;\n};\n\n/**\n * Driver extras the orchestrator mirrors onto `spec.live`.\n * Core process facts live on `readyState` / `error` / `offers`.\n */\nexport function extraLiveFromState(state: object): Record<string, unknown> {\n const extra: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(state)) {\n if (value === undefined) continue;\n extra[key] = toJS(value);\n }\n return extra;\n}\n\n/**\n * The CLASS is the driver (one loaded module per driver name).\n * The INSTANCE is a live connection (one per connection id).\n *\n * CLAIM DOES NOT LIVE HERE. Which WorkExpectation classes this driver handles\n * lives on the advertised driver record (`can`): dynamically loaded code does\n * not get to declare its own authority.\n *\n * Live state is report only — no machinery branches on it. Instance bootstrap\n * truth is `bootstrapDone` / `bootstrapPending`. Drivers read {@link attention}\n * as the reduced token, never clientIds.\n *\n * YAML doors ({@link configurationSchema} / {@link secretsSchema}) parse\n * without `new`. Secrets stay a live view — the host may replace it (reauth).\n */\nexport abstract class PEWDriver<\n E extends AnyWorkExpectation = AnyWorkExpectation,\n C = Readonly<Record<string, unknown>>,\n> implements AsyncDisposable {\n // ── driver side (the CLASS is the driver) ─────────────────────────────\n static readonly pewBrand: string = PEW_DRIVER_BRAND;\n\n /**\n * YAML settings shape. Default is a passthrough record — subclasses replace\n * this with a real object schema (transforms included). First issue message\n * is what construct / parse throw.\n */\n static readonly configurationSchema: ConfigurationSchema = pewConfigurationSchema;\n\n static readonly configurationLabel: string = \"\";\n\n static configurationError(message: string): string {\n const label = this.configurationLabel.trim();\n return label === \"\" ? `connection config invalid: ${message}` : `${label} connection config invalid: ${message}`;\n }\n\n /** YAML settings door. Does not construct an instance. */\n static parseConfiguration(settings: unknown = {}): unknown {\n const result = this.configurationSchema.safeParse(settings ?? {});\n if (result.success) return result.data;\n const message = result.error.issues[0]?.message ?? \"invalid\";\n throw new Error(this.configurationError(message));\n }\n\n static readonly secretsSchema: ConfigurationSchema = pewSecretsSchema;\n\n static secretsError(message: string): string {\n const label = this.configurationLabel.trim();\n return label === \"\" ? `connection secrets invalid: ${message}` : `${label} connection secrets invalid: ${message}`;\n }\n\n /** YAML secrets door. Does not construct an instance. */\n static parseSecrets(secrets: unknown = {}): unknown {\n const result = this.secretsSchema.safeParse(secrets ?? {});\n if (result.success) return result.data;\n const message = result.error.issues[0]?.message ?? \"invalid\";\n throw new Error(this.secretsError(message));\n }\n\n /** Shared on the prototype chain — own extras per subclass. */\n static readonly state: Record<string, unknown> = {};\n /**\n * A plain list, or a getter over observable state when the driver keeps its\n * offers fresh (local detection). The orchestrator mirrors it onto\n * `DriverSpec.offers` with a reaction, which is also what observes it.\n */\n static readonly offers: readonly DriverOffer[] = [];\n\n /** Kernel-plane occupancy hub. Orchestrator assignment is own-per-class. */\n static awareness: PlexusAwareness | undefined;\n static driverName = \"\";\n\n /**\n * A getter, not `@computed` — MobX: the decorator's initializer pins to the\n * declaring class, so subclasses would share one computed.\n */\n static get revalidationDemand(): RevalidationDemand {\n const hub = this.awareness;\n const name = this.driverName;\n if (hub === undefined || name === \"\") return \"none\";\n return revalidationDemandOf(hub, { kind: \"driver\", name });\n }\n\n // ── connection side (the INSTANCE is the connection) ──────────────────\n @observable accessor readyState: ReadyState = null;\n @observable accessor error: string = \"\";\n /**\n * MobX: shallow — extras (models, tools, account) are replaced wholesale.\n * Override the accessor with a wider literal to add driver facts.\n */\n @observable.shallow accessor state: Record<string, unknown> = {};\n\n @observable.ref accessor awareness: PlexusAwareness | undefined = undefined;\n\n @computed\n get revalidationDemand(): RevalidationDemand {\n const hub = this.awareness;\n if (hub === undefined) return \"none\";\n return revalidationDemandOf(hub, { kind: \"connection\", id: this.connectionId });\n }\n\n constructor(\n public readonly connectionId: string,\n settings: unknown = {},\n secrets: ConnectionSecretsView | Readonly<Record<string, unknown>> = EMPTY_SECRETS,\n ) {\n const cls = new.target as typeof PEWDriver;\n this.settings = cls.parseConfiguration(settings) as C;\n const view: ConnectionSecretsView =\n typeof (secrets as ConnectionSecretsView).get === \"function\"\n ? (secrets as ConnectionSecretsView)\n : {\n get: (field) => (secrets as Readonly<Record<string, unknown>>)[field],\n all: () => secrets as Readonly<Record<string, unknown>>,\n };\n cls.parseSecrets(view.all?.() ?? {});\n this.secrets = view;\n }\n readonly settings: C;\n /** Live view. The host may replace it (reauth). */\n secrets: ConnectionSecretsView;\n\n /**\n * Host binds the disk writer for this connection's settings bag.\n * Parsed settings stay readonly; a driver that learns a value overrides this.\n */\n bindSettingsWriter(_write: (patch: Readonly<Record<string, unknown>>) => void): void {}\n\n abstract bootstrap(): Promise<void>;\n\n bootstrapDone = false;\n bootstrapPending: Promise<void> | null = null;\n /** `resolve()` threw — the connection is broken, not the work. */\n broken = false;\n /** Concurrent callers share one promise. Async settlement is its own MobX action. */\n @action\n ensureBootstrapStarted(): Promise<void> {\n if (this.bootstrapDone) return Promise.resolve();\n if (this.bootstrapPending) return this.bootstrapPending;\n this.readyState = \"bootstrapping\";\n this.error = \"\";\n this.bootstrapPending = this.bootstrap().then(\n action(() => {\n this.bootstrapDone = true;\n this.readyState = \"ready\";\n this.error = \"\";\n }),\n action((error: unknown) => {\n this.readyState = \"failed\";\n this.error = error instanceof Error ? error.message : String(error);\n throw error;\n }),\n );\n return this.bootstrapPending;\n }\n\n @action\n failReady(error: unknown): void {\n this.broken = true;\n this.readyState = \"failed\";\n this.error = error instanceof Error ? error.message : String(error);\n }\n\n protected abstract resolve(expectation: E): ExpectationActor<E>;\n /** Nothing in PEW calls this; hosts or tests may. */\n async [Symbol.asyncDispose](): Promise<void> {}\n\n private readonly actors = new DefaultedWeakMap<WorkExpectation, ExpectationActor<any>>((expectation: WorkExpectation): ExpectationActor<any> => {\n const actor = this.resolve(expectation as E);\n this.liveActors.add(actor);\n return actor;\n});\n private readonly liveActors = new Set<ExpectationActor<any>>();\n\n has(expectation: WorkExpectation): boolean {\n return this.actors.has(expectation);\n }\n resolveExpectation<T extends AnyWorkExpectation>(expectation: T): ExpectationActor<T> {\n return this.actors.get(expectation);\n }\n release(expectation: WorkExpectation): void {\n if (!this.actors.has(expectation)) return;\n const actor = this.actors.get(expectation);\n this.actors.delete(expectation);\n this.liveActors.delete(actor);\n actor.leave();\n }\n\n /**\n * Logout/drop aborts interned work. Not park. Subclasses abort `#inflight`.\n */\n abortOnDrop(reason: unknown): void {\n for (const actor of this.liveActors) actor.abort(reason);\n }\n\n /**\n * Honor one demand.\n * `resolve()` throwing is a broken connection, not a failed expectation.\n */\n handle(expectation: WorkExpectation): void {\n if (this.broken) return;\n if (expectation.isDone) return;\n if (this.has(expectation)) {\n const existing = this.resolveExpectation(expectation);\n if (!existing.started) existing.start();\n return;\n }\n\n let actor: ExpectationActor<any>;\n try {\n actor = this.resolveExpectation(expectation);\n } catch (error) {\n this.failReady(error);\n return;\n }\n\n // Awareness / host policy may have sealed the work while resolve() ran.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- resolve() can seal via spawn hooks\n if (expectation.isDone) {\n actor.leave();\n return;\n }\n\n if (!actor.started) {\n actor.start();\n }\n }\n}\n\n/**\n * A concrete `PEWDriver` subclass — constructable, not abstract.\n * `loadDriverModule` returns `unknown`; {@link isDriverConnectionClass} is the gate.\n */\nexport type DriverConnectionClass = {\n [K in keyof typeof PEWDriver]: (typeof PEWDriver)[K];\n} & (new (...args: ConstructorParameters<typeof PEWDriver>) => PEWDriver);\n\nexport function isDriverConnectionClass(value: unknown): value is DriverConnectionClass {\n if (typeof value !== \"function\") return false;\n if ((value as { pewBrand?: unknown }).pewBrand === PEW_DRIVER_BRAND) return true;\n return (\n typeof (value as { prototype?: unknown }).prototype === \"object\" &&\n (value as { prototype: object }).prototype instanceof PEWDriver\n );\n}\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type EventFromLogic, type StateValueFrom } from "xstate";
|
|
2
|
+
/**
|
|
3
|
+
* Process-local actor phase. Distinct from the durable WorkExpectation lifecycle.
|
|
4
|
+
*
|
|
5
|
+
* minted ──START──► running ──SETTLE|CRASH──► resolved (final)
|
|
6
|
+
* │ │
|
|
7
|
+
* ├──ABORT──► aborted ──SETTLE|CRASH──────────┘
|
|
8
|
+
* └──RELEASE────────┴──RELEASE──────────► released (final)
|
|
9
|
+
*
|
|
10
|
+
* ENDINGS ARE OBSERVED. RELEASE fires from the actor's done reaction on the
|
|
11
|
+
* durable entity — never from a control-flow wrapper. `resolved` is
|
|
12
|
+
* settlement/crash; `released` is abort without a settlement.
|
|
13
|
+
*/
|
|
14
|
+
export declare const expectationActorMachine: import("xstate").StateMachine<import("xstate").MachineContext, {
|
|
15
|
+
type: "START" | "SETTLE" | "CRASH" | "ABORT" | "RELEASE";
|
|
16
|
+
}, {}, never, never, never, never, "minted" | "running" | "aborted" | "released" | "resolved", string, import("xstate").NonReducibleUnknown, import("xstate").NonReducibleUnknown, import("xstate").EventObject, import("xstate").MetaObject, {
|
|
17
|
+
id: "pew-expectation-actor";
|
|
18
|
+
states: {
|
|
19
|
+
readonly minted: {};
|
|
20
|
+
readonly running: {};
|
|
21
|
+
readonly resolved: {};
|
|
22
|
+
readonly aborted: {};
|
|
23
|
+
readonly released: {};
|
|
24
|
+
};
|
|
25
|
+
}>;
|
|
26
|
+
export type ActorPhase = StateValueFrom<typeof expectationActorMachine>;
|
|
27
|
+
export type ActorPhaseEvent = EventFromLogic<typeof expectationActorMachine>;
|
|
28
|
+
//# sourceMappingURL=actor-machine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actor-machine.d.ts","sourceRoot":"","sources":["../../src/driver/actor-machine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,QAAQ,CAAC;AAEzE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,uBAAuB;UAEV,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS;;;;;;;;;;EAiC1E,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,cAAc,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,MAAM,MAAM,eAAe,GAAG,cAAc,CAAC,OAAO,uBAAuB,CAAC,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { setup } from "xstate";
|
|
2
|
+
/**
|
|
3
|
+
* Process-local actor phase. Distinct from the durable WorkExpectation lifecycle.
|
|
4
|
+
*
|
|
5
|
+
* minted ──START──► running ──SETTLE|CRASH──► resolved (final)
|
|
6
|
+
* │ │
|
|
7
|
+
* ├──ABORT──► aborted ──SETTLE|CRASH──────────┘
|
|
8
|
+
* └──RELEASE────────┴──RELEASE──────────► released (final)
|
|
9
|
+
*
|
|
10
|
+
* ENDINGS ARE OBSERVED. RELEASE fires from the actor's done reaction on the
|
|
11
|
+
* durable entity — never from a control-flow wrapper. `resolved` is
|
|
12
|
+
* settlement/crash; `released` is abort without a settlement.
|
|
13
|
+
*/
|
|
14
|
+
export const expectationActorMachine = setup({
|
|
15
|
+
types: {
|
|
16
|
+
events: {},
|
|
17
|
+
},
|
|
18
|
+
}).createMachine({
|
|
19
|
+
id: "pew-expectation-actor",
|
|
20
|
+
initial: "minted",
|
|
21
|
+
states: {
|
|
22
|
+
minted: {
|
|
23
|
+
tags: ["live"],
|
|
24
|
+
on: {
|
|
25
|
+
START: "running",
|
|
26
|
+
ABORT: "aborted",
|
|
27
|
+
RELEASE: "released",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
running: {
|
|
31
|
+
tags: ["live"],
|
|
32
|
+
on: {
|
|
33
|
+
SETTLE: "resolved",
|
|
34
|
+
CRASH: "resolved",
|
|
35
|
+
ABORT: "aborted",
|
|
36
|
+
RELEASE: "released",
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
resolved: { type: "final" },
|
|
40
|
+
aborted: {
|
|
41
|
+
on: {
|
|
42
|
+
SETTLE: "resolved",
|
|
43
|
+
CRASH: "resolved",
|
|
44
|
+
RELEASE: "released",
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
released: { type: "final" },
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
//# sourceMappingURL=actor-machine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actor-machine.js","sourceRoot":"","sources":["../../src/driver/actor-machine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAA4C,MAAM,QAAQ,CAAC;AAEzE;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,KAAK,CAAC;IAC3C,KAAK,EAAE;QACL,MAAM,EAAE,EAAkE;KAC3E;CACF,CAAC,CAAC,aAAa,CAAC;IACf,EAAE,EAAE,uBAAuB;IAC3B,OAAO,EAAE,QAAQ;IACjB,MAAM,EAAE;QACN,MAAM,EAAE;YACN,IAAI,EAAE,CAAC,MAAM,CAAC;YACd,EAAE,EAAE;gBACF,KAAK,EAAE,SAAS;gBAChB,KAAK,EAAE,SAAS;gBAChB,OAAO,EAAE,UAAU;aACpB;SACF;QACD,OAAO,EAAE;YACP,IAAI,EAAE,CAAC,MAAM,CAAC;YACd,EAAE,EAAE;gBACF,MAAM,EAAE,UAAU;gBAClB,KAAK,EAAE,UAAU;gBACjB,KAAK,EAAE,SAAS;gBAChB,OAAO,EAAE,UAAU;aACpB;SACF;QACD,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE;QAC3B,OAAO,EAAE;YACP,EAAE,EAAE;gBACF,MAAM,EAAE,UAAU;gBAClB,KAAK,EAAE,UAAU;gBACjB,OAAO,EAAE,UAAU;aACpB;SACF;QACD,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE;KAC5B;CACF,CAAC,CAAC","sourcesContent":["import { setup, type EventFromLogic, type StateValueFrom } from \"xstate\";\n\n/**\n * Process-local actor phase. Distinct from the durable WorkExpectation lifecycle.\n *\n * minted ──START──► running ──SETTLE|CRASH──► resolved (final)\n * │ │\n * ├──ABORT──► aborted ──SETTLE|CRASH──────────┘\n * └──RELEASE────────┴──RELEASE──────────► released (final)\n *\n * ENDINGS ARE OBSERVED. RELEASE fires from the actor's done reaction on the\n * durable entity — never from a control-flow wrapper. `resolved` is\n * settlement/crash; `released` is abort without a settlement.\n */\nexport const expectationActorMachine = setup({\n types: {\n events: {} as { type: \"START\" | \"SETTLE\" | \"CRASH\" | \"ABORT\" | \"RELEASE\" },\n },\n}).createMachine({\n id: \"pew-expectation-actor\",\n initial: \"minted\",\n states: {\n minted: {\n tags: [\"live\"],\n on: {\n START: \"running\",\n ABORT: \"aborted\",\n RELEASE: \"released\",\n },\n },\n running: {\n tags: [\"live\"],\n on: {\n SETTLE: \"resolved\",\n CRASH: \"resolved\",\n ABORT: \"aborted\",\n RELEASE: \"released\",\n },\n },\n resolved: { type: \"final\" },\n aborted: {\n on: {\n SETTLE: \"resolved\",\n CRASH: \"resolved\",\n RELEASE: \"released\",\n },\n },\n released: { type: \"final\" },\n },\n});\n\nexport type ActorPhase = StateValueFrom<typeof expectationActorMachine>;\n\nexport type ActorPhaseEvent = EventFromLogic<typeof expectationActorMachine>;\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intern / send-ready miss. Remainder maps `noconnect` → writeFail("no model connection").
|
|
3
|
+
* Honor fallback (one release) catches only this class.
|
|
4
|
+
*/
|
|
5
|
+
export declare class ConnectionMiss extends Error {
|
|
6
|
+
readonly code: "noconnect" | "not-ready" | "needs-auth" | "connection-dropped";
|
|
7
|
+
constructor(code: ConnectionMiss["code"], message?: string);
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=connection-miss.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection-miss.d.ts","sourceRoot":"","sources":["../../src/driver/connection-miss.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,WAAW,GAAG,YAAY,GAAG,oBAAoB,CAAC;gBAEnE,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM;CAK3D"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intern / send-ready miss. Remainder maps `noconnect` → writeFail("no model connection").
|
|
3
|
+
* Honor fallback (one release) catches only this class.
|
|
4
|
+
*/
|
|
5
|
+
export class ConnectionMiss extends Error {
|
|
6
|
+
code;
|
|
7
|
+
constructor(code, message) {
|
|
8
|
+
super(message ?? code);
|
|
9
|
+
this.name = "ConnectionMiss";
|
|
10
|
+
this.code = code;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=connection-miss.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection-miss.js","sourceRoot":"","sources":["../../src/driver/connection-miss.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,OAAO,cAAe,SAAQ,KAAK;IAC9B,IAAI,CAAkE;IAE/E,YAAY,IAA4B,EAAE,OAAgB;QACxD,KAAK,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF","sourcesContent":["/**\n * Intern / send-ready miss. Remainder maps `noconnect` → writeFail(\"no model connection\").\n * Honor fallback (one release) catches only this class.\n */\nexport class ConnectionMiss extends Error {\n readonly code: \"noconnect\" | \"not-ready\" | \"needs-auth\" | \"connection-dropped\";\n\n constructor(code: ConnectionMiss[\"code\"], message?: string) {\n super(message ?? code);\n this.name = \"ConnectionMiss\";\n this.code = code;\n }\n}\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from "./PEWDriver.js";
|
|
2
|
+
export { type RevalidationDemand as RevalidationDemand } from "../shared/index.js";
|
|
3
|
+
export * from "./ExpectationActor.js";
|
|
4
|
+
export * from "./actor-machine.js";
|
|
5
|
+
export * from "./connection-miss.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/driver/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EAAE,KAAK,kBAAkB,IAAI,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACnF,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/driver/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAE/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC","sourcesContent":["export * from \"./PEWDriver.js\";\nexport { type RevalidationDemand as RevalidationDemand } from \"../shared/index.js\";\nexport * from \"./ExpectationActor.js\";\nexport * from \"./actor-machine.js\";\nexport * from \"./connection-miss.js\";\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/executor/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/executor/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC","sourcesContent":["export * from \"./orchestrator.js\";\nexport * from \"./kernel-intern.js\";\nexport * from \"./types.js\";\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type PEWDriver } from "../driver/PEWDriver.js";
|
|
2
|
+
import type { ConnectionSpec } from "../shared/models/ConnectionSpec.js";
|
|
3
|
+
type KernelRoot = {
|
|
4
|
+
readonly connections: Record<string, ConnectionSpec | undefined>;
|
|
5
|
+
};
|
|
6
|
+
export declare function acquireConnection(kernel: KernelRoot, owner: object, connectionId: string, construct: () => PEWDriver | Promise<PEWDriver>): Promise<PEWDriver>;
|
|
7
|
+
export declare function releaseConnection(kernel: object, owner: object, connectionId: string): void;
|
|
8
|
+
/**
|
|
9
|
+
* Remint door. Logout/drop — not park, not last-ref.
|
|
10
|
+
* Pending lease still `asyncDispose`s when it fulfills. Next `ensure` remints.
|
|
11
|
+
*/
|
|
12
|
+
export declare function dropConnection(kernel: KernelRoot, connectionId: string): Promise<void>;
|
|
13
|
+
/** Kernel teardown only. Session park is a document wrap, not a connection lifetime. */
|
|
14
|
+
export declare function disposeKernelConnections(kernel: object): void;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=kernel-intern.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kernel-intern.d.ts","sourceRoot":"","sources":["../../src/executor/kernel-intern.ts"],"names":[],"mappings":"AAWA,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAC5E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AASzE,KAAK,UAAU,GAAG;IAAE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,SAAS,CAAC,CAAA;CAAE,CAAC;AAUvF,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,GAC9C,OAAO,CAAC,SAAS,CAAC,CAsCpB;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAG3F;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB5F;AAED,wFAAwF;AACxF,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAa7D"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-local connection intern table keyed by the kernel root object.
|
|
3
|
+
*
|
|
4
|
+
* Session wrap is Yjs load. Park drops an owner ref and must not dispose the
|
|
5
|
+
* instance — {@link disposeKernelConnections} is kernel teardown only.
|
|
6
|
+
* Rejected: last-ref dispose. Switching seats reminted every driver.
|
|
7
|
+
*/
|
|
8
|
+
import { DefaultedWeakMap } from "@here.build/collections";
|
|
9
|
+
import { comparer, reaction } from "mobx";
|
|
10
|
+
import { ConnectionMiss } from "../driver/connection-miss.js";
|
|
11
|
+
import { extraLiveFromState } from "../driver/PEWDriver.js";
|
|
12
|
+
import { asReadyState } from "../shared/types.js";
|
|
13
|
+
function internOwnerOf(owner) {
|
|
14
|
+
return typeof owner.forgetConnection === "function" ? owner : undefined;
|
|
15
|
+
}
|
|
16
|
+
const internByKernel = new DefaultedWeakMap(() => ({ connections: new Map() }));
|
|
17
|
+
export function acquireConnection(kernel, owner, connectionId, construct) {
|
|
18
|
+
const bag = internByKernel.get(kernel);
|
|
19
|
+
const existing = bag.connections.get(connectionId);
|
|
20
|
+
if (existing !== undefined) {
|
|
21
|
+
existing.refs.add(owner);
|
|
22
|
+
return existing.pending;
|
|
23
|
+
}
|
|
24
|
+
const lease = {
|
|
25
|
+
refs: new Set([owner]),
|
|
26
|
+
pending: Promise.resolve().then(construct).then((instance) => {
|
|
27
|
+
lease.mirror = reaction(() => {
|
|
28
|
+
const spec = kernel.connections[connectionId];
|
|
29
|
+
if (spec === undefined)
|
|
30
|
+
return null;
|
|
31
|
+
const ready = asReadyState(instance.readyState);
|
|
32
|
+
// Bootstrapping remint must not wipe a ready catalog.
|
|
33
|
+
if (ready !== "ready" && ready !== "failed" && spec.readyState === "ready")
|
|
34
|
+
return null;
|
|
35
|
+
return { ready, error: instance.error, live: extraLiveFromState(instance.state) };
|
|
36
|
+
}, (snap) => {
|
|
37
|
+
if (snap == null)
|
|
38
|
+
return;
|
|
39
|
+
kernel.connections[connectionId]?.stampProcess({
|
|
40
|
+
readyState: snap.ready,
|
|
41
|
+
error: snap.error,
|
|
42
|
+
live: snap.live,
|
|
43
|
+
});
|
|
44
|
+
}, { fireImmediately: true, equals: comparer.structural });
|
|
45
|
+
return instance;
|
|
46
|
+
}),
|
|
47
|
+
mirror: undefined,
|
|
48
|
+
};
|
|
49
|
+
bag.connections.set(connectionId, lease);
|
|
50
|
+
void lease.pending.then(undefined, () => {
|
|
51
|
+
if (lease.refs.size === 0)
|
|
52
|
+
bag.connections.delete(connectionId);
|
|
53
|
+
});
|
|
54
|
+
return lease.pending;
|
|
55
|
+
}
|
|
56
|
+
export function releaseConnection(kernel, owner, connectionId) {
|
|
57
|
+
if (!internByKernel.has(kernel))
|
|
58
|
+
return;
|
|
59
|
+
internByKernel.get(kernel).connections.get(connectionId)?.refs.delete(owner);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Remint door. Logout/drop — not park, not last-ref.
|
|
63
|
+
* Pending lease still `asyncDispose`s when it fulfills. Next `ensure` remints.
|
|
64
|
+
*/
|
|
65
|
+
export async function dropConnection(kernel, connectionId) {
|
|
66
|
+
if (!internByKernel.has(kernel))
|
|
67
|
+
return;
|
|
68
|
+
const bag = internByKernel.get(kernel);
|
|
69
|
+
const lease = bag.connections.get(connectionId);
|
|
70
|
+
if (lease === undefined)
|
|
71
|
+
return;
|
|
72
|
+
bag.connections.delete(connectionId);
|
|
73
|
+
lease.mirror?.();
|
|
74
|
+
const owners = [...lease.refs];
|
|
75
|
+
lease.refs.clear();
|
|
76
|
+
const miss = new ConnectionMiss("connection-dropped", `connection ${connectionId} dropped`);
|
|
77
|
+
const disposing = lease.pending.then((instance) => {
|
|
78
|
+
instance.abortOnDrop(miss);
|
|
79
|
+
return instance[Symbol.asyncDispose]();
|
|
80
|
+
}, () => { });
|
|
81
|
+
for (const owner of owners)
|
|
82
|
+
internOwnerOf(owner)?.forgetConnection(connectionId);
|
|
83
|
+
await disposing;
|
|
84
|
+
}
|
|
85
|
+
/** Kernel teardown only. Session park is a document wrap, not a connection lifetime. */
|
|
86
|
+
export function disposeKernelConnections(kernel) {
|
|
87
|
+
if (!internByKernel.has(kernel))
|
|
88
|
+
return;
|
|
89
|
+
const bag = internByKernel.get(kernel);
|
|
90
|
+
internByKernel.delete(kernel);
|
|
91
|
+
for (const lease of bag.connections.values()) {
|
|
92
|
+
lease.refs.clear();
|
|
93
|
+
lease.mirror?.();
|
|
94
|
+
void lease.pending.then((instance) => instance[Symbol.asyncDispose](), () => { });
|
|
95
|
+
}
|
|
96
|
+
bag.connections.clear();
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=kernel-intern.js.map
|