@dougongjs/core 0.4.0 → 0.5.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.
Files changed (59) hide show
  1. package/dist/change-set.d.ts +6 -0
  2. package/dist/change-set.d.ts.map +1 -1
  3. package/dist/configuration.d.ts +12 -1
  4. package/dist/configuration.d.ts.map +1 -1
  5. package/dist/contract-registry.d.ts +2 -2
  6. package/dist/contract-registry.d.ts.map +1 -1
  7. package/dist/contracts.d.ts +27 -0
  8. package/dist/contracts.d.ts.map +1 -1
  9. package/dist/contribution-store.d.ts +20 -1
  10. package/dist/contribution-store.d.ts.map +1 -1
  11. package/dist/diagnostics.d.ts +21 -3
  12. package/dist/diagnostics.d.ts.map +1 -1
  13. package/dist/engine.d.ts +28 -1
  14. package/dist/engine.d.ts.map +1 -1
  15. package/dist/errors.d.ts +44 -7
  16. package/dist/errors.d.ts.map +1 -1
  17. package/dist/event-hub.d.ts +12 -1
  18. package/dist/event-hub.d.ts.map +1 -1
  19. package/dist/group-configuration.d.ts +20 -0
  20. package/dist/group-configuration.d.ts.map +1 -0
  21. package/dist/group-coordinator.d.ts +20 -2
  22. package/dist/group-coordinator.d.ts.map +1 -1
  23. package/dist/group-lifecycle.d.ts +13 -0
  24. package/dist/group-lifecycle.d.ts.map +1 -1
  25. package/dist/group.d.ts +5 -11
  26. package/dist/group.d.ts.map +1 -1
  27. package/dist/host-api.d.ts +33 -0
  28. package/dist/host-api.d.ts.map +1 -1
  29. package/dist/host.d.ts.map +1 -1
  30. package/dist/index.d.ts +1 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +905 -590
  33. package/dist/installation-graph.d.ts +8 -1
  34. package/dist/installation-graph.d.ts.map +1 -1
  35. package/dist/installation-registry.d.ts +6 -0
  36. package/dist/installation-registry.d.ts.map +1 -1
  37. package/dist/installation.d.ts +21 -3
  38. package/dist/installation.d.ts.map +1 -1
  39. package/dist/instance-coordinator.d.ts +32 -3
  40. package/dist/instance-coordinator.d.ts.map +1 -1
  41. package/dist/lifecycle-status.d.ts.map +1 -1
  42. package/dist/lifetime-diagnostics.d.ts +7 -1
  43. package/dist/lifetime-diagnostics.d.ts.map +1 -1
  44. package/dist/lifetime.d.ts +37 -1
  45. package/dist/lifetime.d.ts.map +1 -1
  46. package/dist/plugin.d.ts +28 -2
  47. package/dist/plugin.d.ts.map +1 -1
  48. package/dist/readonly-map.d.ts +8 -1
  49. package/dist/readonly-map.d.ts.map +1 -1
  50. package/dist/record.d.ts +16 -1
  51. package/dist/record.d.ts.map +1 -1
  52. package/dist/resource.d.ts +6 -0
  53. package/dist/resource.d.ts.map +1 -1
  54. package/dist/serial-queue.d.ts +7 -1
  55. package/dist/serial-queue.d.ts.map +1 -1
  56. package/dist/snapshot-view.d.ts +2 -0
  57. package/dist/snapshot-view.d.ts.map +1 -1
  58. package/dist/sync-result.d.ts.map +1 -1
  59. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,42 +1,42 @@
1
- function at(r, t) {
1
+ function Ot(r, t) {
2
2
  if (typeof t != "string" || !t.trim())
3
3
  throw new TypeError(`${r} id must be a non-empty string`);
4
4
  if (t !== t.trim())
5
5
  throw new TypeError(`${r} id cannot start or end with whitespace`);
6
6
  }
7
- function V(r, t) {
8
- return at("Contract", t), Object.freeze({ id: t, kind: r });
7
+ function Q(r, t) {
8
+ return Ot("Contract", t), Object.freeze({ id: t, kind: r });
9
9
  }
10
- function v(r, t) {
10
+ function E(r, t) {
11
11
  if (!r || typeof r != "object") return !1;
12
12
  const e = r;
13
13
  return typeof e.id == "string" && e.id.length > 0 && e.id === e.id.trim() && (e.kind === "service" || e.kind === "extensionPoint" || e.kind === "event") && (t === void 0 || e.kind === t);
14
14
  }
15
- function m(r, t) {
16
- if (!v(r, t))
17
- throw new TypeError(t ? `Expected ${ct(t)}` : "Invalid contract");
15
+ function A(r, t) {
16
+ if (!E(r, t))
17
+ throw new TypeError(t ? `Expected ${At(t)}` : "Invalid contract");
18
18
  }
19
- function ct(r) {
19
+ function At(r) {
20
20
  return r === "extensionPoint" ? "an ExtensionPoint" : r === "event" ? "an Event" : "a Service";
21
21
  }
22
- function fe(r) {
23
- return V("service", r);
22
+ function It(r) {
23
+ return Q("service", r);
24
24
  }
25
- function pe(r) {
26
- return V("event", r);
25
+ function Le(r) {
26
+ return Q("event", r);
27
27
  }
28
- function ge(r) {
29
- return V("extensionPoint", r);
28
+ function ze(r) {
29
+ return Q("extensionPoint", r);
30
30
  }
31
- function we(r) {
32
- if (!v(r, "service"))
31
+ function Re(r) {
32
+ if (!E(r, "service"))
33
33
  throw new TypeError("optional() expects a Service");
34
- return Object.freeze({ kind: "optional", service: r });
34
+ return Object.freeze({ kind: "optional", service: It(r.id) });
35
35
  }
36
- function ht(r) {
36
+ function jt(r) {
37
37
  if (!r || typeof r != "object") return !1;
38
38
  const t = r;
39
- return t.kind === "optional" && v(t.service, "service");
39
+ return t.kind === "optional" && E(t.service, "service");
40
40
  }
41
41
  class u extends Error {
42
42
  name = "DougongError";
@@ -48,62 +48,89 @@ class u extends Error {
48
48
  super(e, i), this.code = t;
49
49
  }
50
50
  }
51
- class dt {
52
- #t;
51
+ class J extends Error {
52
+ name = "RecordedFailure";
53
+ code;
54
+ snapshot;
53
55
  constructor(t) {
54
- if (!(t instanceof Error)) throw new TypeError("ErrorSummary expects an Error");
55
- const e = F(t, "name", "Error"), i = F(t, "message", ""), s = t instanceof u ? lt(t) : void 0;
56
- this.#t = Object.freeze(
57
- s === void 0 ? {
58
- category: t instanceof TypeError ? "typeError" : "error",
59
- name: e,
60
- message: i
61
- } : {
62
- category: "coded",
63
- name: e,
64
- message: i,
65
- code: s
66
- }
67
- ), Object.freeze(this);
68
- }
69
- restore(t = (e, i) => new u(e, i)) {
70
- const e = this.#t, i = e.category === "coded" ? t(e.code, e.message) : e.category === "typeError" ? new TypeError(e.message) : new Error(e.message);
71
- if (!(i instanceof Error))
72
- throw new TypeError("ErrorSummary coded error factory must return an Error");
73
- return i.name = e.name, i;
56
+ if (!(t instanceof Error)) throw new TypeError("RecordedFailure expects an Error");
57
+ const e = t instanceof J ? t.snapshot : kt(t);
58
+ super(e.message), this.code = e.code, this.snapshot = e, e.stack !== void 0 && (this.stack = e.stack), Object.freeze(this);
74
59
  }
75
60
  }
76
- function F(r, t, e) {
77
- try {
78
- const i = r[t];
79
- return typeof i == "string" ? i : e;
80
- } catch {
81
- return e;
82
- }
61
+ function kt(r) {
62
+ const t = /* @__PURE__ */ new Set();
63
+ let e = 32;
64
+ const i = (s, n) => {
65
+ if (n > 4 || e-- <= 0 || t.has(s))
66
+ return Object.freeze({
67
+ name: "TruncatedError",
68
+ message: "Error chain truncated",
69
+ truncated: !0
70
+ });
71
+ if (!(s instanceof Error)) {
72
+ const p = s === null || ["string", "number", "boolean", "undefined", "bigint", "symbol"].includes(typeof s) ? String(s).slice(0, 4096) : "Non-Error object omitted";
73
+ return Object.freeze({ name: "NonError", message: p });
74
+ }
75
+ t.add(s);
76
+ let o = !1;
77
+ const a = (p, w) => (p.length > w && (o = !0), p.slice(0, w)), h = (p, w) => {
78
+ const b = m(s, p);
79
+ return typeof b == "string" ? a(b, w) : void 0;
80
+ }, c = (p) => {
81
+ const w = m(s, p);
82
+ if (Array.isArray(w))
83
+ return w.length > 8 && (o = !0), Array.from(
84
+ { length: Math.min(w.length, 8) },
85
+ (b, j) => m(w, j)
86
+ );
87
+ }, d = h("name", 256) ?? "Error", l = h("message", 4096) ?? "", f = h("stack", 16384), g = h("code", 256), I = m(s, "cause"), et = c("errors"), it = h("manifestName", 256), st = c("denied")?.filter((p) => typeof p == "string").map((p) => a(p, 256)), rt = c("issues")?.map((p) => {
88
+ const w = m(p, "message"), b = m(p, "path"), j = Array.isArray(b) ? Array.from({ length: Math.min(b.length, 16) }, (Pe, St) => {
89
+ const k = m(b, St), S = k && typeof k == "object" ? m(k, "key") : k;
90
+ return typeof S == "number" ? S : typeof S == "string" || typeof S == "symbol" ? a(String(S), 256) : "[omitted]";
91
+ }) : void 0;
92
+ return Array.isArray(b) && b.length > 16 && (o = !0), Object.freeze({
93
+ message: typeof w == "string" ? a(w, 4096) : "",
94
+ ...j ? { path: Object.freeze(j) } : {}
95
+ });
96
+ });
97
+ return Object.freeze({
98
+ name: d,
99
+ message: l,
100
+ ...g === void 0 ? {} : { code: g },
101
+ ...f === void 0 ? {} : { stack: f },
102
+ ...I === void 0 ? {} : { cause: i(I, n + 1) },
103
+ ...et === void 0 ? {} : { errors: Object.freeze(et.map((p) => i(p, n + 1))) },
104
+ ...it === void 0 ? {} : { manifestName: it },
105
+ ...st === void 0 ? {} : { denied: Object.freeze(st) },
106
+ ...rt === void 0 ? {} : { issues: Object.freeze(rt) },
107
+ ...o ? { truncated: !0 } : {}
108
+ });
109
+ };
110
+ return i(r, 0);
83
111
  }
84
- function lt(r) {
112
+ function m(r, t) {
85
113
  try {
86
- const t = r.code;
87
- return typeof t == "string" && t.trim() === t && t.length > 0 ? t : void 0;
114
+ return r && typeof r == "object" ? r[t] : void 0;
88
115
  } catch {
89
116
  return;
90
117
  }
91
118
  }
92
- class z extends u {
119
+ class G extends u {
93
120
  }
94
- function $(r, t, e) {
95
- return r instanceof z ? r.code === t ? r : new z(t, e, { cause: r.cause }) : r instanceof Error ? r : new z(t, e, { cause: r });
121
+ function V(r, t, e) {
122
+ return r instanceof G ? r.code === t ? r : new G(t, e, { cause: r.cause }) : r instanceof Error ? r : new G(t, e, { cause: r });
96
123
  }
97
- function tt(r, t) {
124
+ function wt(r, t) {
98
125
  if (!r || typeof r != "object" || typeof r.aborted != "boolean" || typeof r.addEventListener != "function" || typeof r.removeEventListener != "function" || typeof r.throwIfAborted != "function")
99
126
  throw new TypeError("Cancellation classifier expects an AbortSignal");
100
127
  return r.aborted ? Object.is(t, r.reason) ? !0 : t instanceof Error && t.name === "AbortError" : !1;
101
128
  }
102
- class ut extends u {
129
+ class $t extends u {
103
130
  name = "ConfigValidationError";
104
131
  issues;
105
132
  constructor(t) {
106
- const e = ft(t);
133
+ const e = Tt(t);
107
134
  super(
108
135
  "CONFIG_INVALID",
109
136
  `Invalid Plugin config:
@@ -112,7 +139,7 @@ ${e.map((i) => ` - ${i.message}`).join(`
112
139
  ), this.issues = e;
113
140
  }
114
141
  }
115
- function ft(r) {
142
+ function Tt(r) {
116
143
  if (!Array.isArray(r))
117
144
  throw new TypeError("Config validation issues must be an array");
118
145
  return Object.freeze(
@@ -124,23 +151,23 @@ function ft(r) {
124
151
  throw new TypeError(`Config validation issue at index ${e} message must be a string`);
125
152
  return Object.freeze({
126
153
  message: i.message,
127
- ...i.path === void 0 ? {} : { path: pt(i.path, e) }
154
+ ...i.path === void 0 ? {} : { path: Pt(i.path, e) }
128
155
  });
129
156
  })
130
157
  );
131
158
  }
132
- function pt(r, t) {
159
+ function Pt(r, t) {
133
160
  if (!Array.isArray(r))
134
161
  throw new TypeError(`Config validation issue at index ${t} path must be an array`);
135
162
  return Object.freeze(
136
163
  Array.from(r, (e, i) => {
137
- if (G(e)) return e;
164
+ if (nt(e)) return e;
138
165
  if (!e || typeof e != "object")
139
166
  throw new TypeError(
140
167
  `Config validation issue at index ${t} path segment ${i} must be a property key`
141
168
  );
142
169
  const s = e.key;
143
- if (!G(s))
170
+ if (!nt(s))
144
171
  throw new TypeError(
145
172
  `Config validation issue at index ${t} path segment ${i} must contain a property key`
146
173
  );
@@ -148,10 +175,10 @@ function pt(r, t) {
148
175
  })
149
176
  );
150
177
  }
151
- function G(r) {
178
+ function nt(r) {
152
179
  return typeof r == "string" || typeof r == "number" || typeof r == "symbol";
153
180
  }
154
- async function et(r, t, e) {
181
+ async function Lt(r, t, e) {
155
182
  if (!r) return t;
156
183
  const i = await r["~standard"].validate(t);
157
184
  if (!i || typeof i != "object")
@@ -160,7 +187,7 @@ async function et(r, t, e) {
160
187
  );
161
188
  const s = Object.hasOwn(i, "issues") ? i.issues : void 0;
162
189
  if (s !== void 0)
163
- throw Array.isArray(s) ? new ut(s) : new TypeError(
190
+ throw Array.isArray(s) ? new $t(s) : new TypeError(
164
191
  `Installation '${e}' config validator returned non-array issues`
165
192
  );
166
193
  if (!Object.hasOwn(i, "value"))
@@ -169,29 +196,29 @@ async function et(r, t, e) {
169
196
  );
170
197
  return i.value;
171
198
  }
172
- class gt {
199
+ class zt {
173
200
  #t = /* @__PURE__ */ new Map();
174
201
  get kinds() {
175
202
  return this.#t;
176
203
  }
177
204
  assertCompatible(t) {
178
- it(this.#t, t);
205
+ bt(this.#t, t);
179
206
  }
180
207
  remember(t) {
181
- j(this.#t, t);
208
+ q(this.#t, t);
182
209
  }
183
- draft(t) {
184
- return new wt(this, t);
210
+ writer(t) {
211
+ return new Rt(this, t);
185
212
  }
186
213
  commit(t) {
187
214
  for (const [e, i] of t) this.assertCompatible({ id: e, kind: i });
188
215
  for (const [e, i] of t) this.#t.set(e, i);
189
216
  }
190
217
  }
191
- class wt {
218
+ class Rt {
192
219
  #t;
193
220
  constructor(t, e) {
194
- this.#t = { phase: "open", registry: t, pending: /* @__PURE__ */ new Map() };
221
+ this.#t = { phase: "staged", registry: t, pending: /* @__PURE__ */ new Map() };
195
222
  for (const [i, s] of e) this.remember({ id: i, kind: s });
196
223
  }
197
224
  remember(t) {
@@ -201,21 +228,21 @@ class wt {
201
228
  return;
202
229
  }
203
230
  if (e.phase === "discarded")
204
- throw new Error("Contract registry draft has been discarded");
205
- e.registry.assertCompatible(t), !e.registry.kinds.has(t.id) && j(e.pending, t);
231
+ throw new Error("Contract registry writer has been discarded");
232
+ e.registry.assertCompatible(t), !e.registry.kinds.has(t.id) && q(e.pending, t);
206
233
  }
207
234
  commit() {
208
235
  const t = this.#t;
209
- t.phase === "open" && (t.registry.commit(t.pending), this.#t = { phase: "committed", registry: t.registry });
236
+ t.phase === "staged" && (t.registry.commit(t.pending), this.#t = { phase: "committed", registry: t.registry });
210
237
  }
211
238
  discard() {
212
- this.#t.phase === "open" && (this.#t = { phase: "discarded" });
239
+ this.#t.phase === "staged" && (this.#t = { phase: "discarded" });
213
240
  }
214
241
  }
215
- function j(r, t) {
216
- it(r, t), r.set(t.id, t.kind);
242
+ function q(r, t) {
243
+ bt(r, t), r.set(t.id, t.kind);
217
244
  }
218
- function it(r, t) {
245
+ function bt(r, t) {
219
246
  const e = r.get(t.id);
220
247
  if (e && e !== t.kind)
221
248
  throw new u(
@@ -223,9 +250,9 @@ function it(r, t) {
223
250
  `Contract '${t.id}' is used as both '${e}' and '${t.kind}'`
224
251
  );
225
252
  }
226
- class M {
227
- constructor(t, e, i, s, n, o) {
228
- this.order = t, this.layers = e, this.providers = i, this.dependents = s, this.contractKinds = n, this.#t = o;
253
+ class X {
254
+ constructor(t, e, i, s, n, o, a) {
255
+ this.order = e, this.layers = i, this.providers = s, this.dependents = n, this.contractKinds = o, this.#t = t, this.#e = a;
229
256
  }
230
257
  order;
231
258
  layers;
@@ -233,13 +260,15 @@ class M {
233
260
  dependents;
234
261
  contractKinds;
235
262
  #t;
263
+ #e;
236
264
  static build(t, e) {
237
- const i = [...t].sort((c, d) => c.index - d.index), s = new Map(e), n = vt(i, s), o = bt(i, n, s), { order: a, layers: h } = mt(
265
+ const i = [...t].sort((c, d) => c.index - d.index), s = new Map(e), n = Nt(i, s), o = xt(i, n, s), { order: a, layers: h } = Mt(
238
266
  i,
239
267
  o.dependents,
240
268
  o.indegree
241
269
  );
242
- return new M(
270
+ return new X(
271
+ new Map(i.map((c) => [c, c.declaration])),
243
272
  Object.freeze(a),
244
273
  Object.freeze(h.map((c) => Object.freeze(c))),
245
274
  n,
@@ -248,17 +277,28 @@ class M {
248
277
  o.resolvedProviders
249
278
  );
250
279
  }
280
+ declarationFor(t) {
281
+ const e = this.#t.get(t);
282
+ if (!e) throw new Error(`Installation '${t.id}' is not in this plan`);
283
+ return e;
284
+ }
251
285
  providerFor(t, e) {
252
- return this.#t.get(t)?.get(e);
286
+ return this.#e.get(t)?.get(e);
253
287
  }
254
288
  provider(t) {
255
289
  return this.providers.get(t);
256
290
  }
291
+ /**
292
+ * Transitive dependents in *both* plans. A Service being replaced has old
293
+ * consumers that must stop and new ones that must start, and only the union
294
+ * covers both — expanding in the current plan alone would leave an Instance
295
+ * running against a dependency that no longer exists.
296
+ */
257
297
  affectedByTransitionTo(t, e) {
258
298
  const i = /* @__PURE__ */ new Set();
259
- return this.#e(e, i), t.#e(e, i), i;
299
+ return this.#i(e, i), t.#i(e, i), i;
260
300
  }
261
- #e(t, e) {
301
+ #i(t, e) {
262
302
  const i = [...t], s = /* @__PURE__ */ new Set();
263
303
  for (let n = 0; n < i.length; n++) {
264
304
  const o = i[n];
@@ -269,11 +309,11 @@ class M {
269
309
  }
270
310
  }
271
311
  }
272
- function vt(r, t) {
312
+ function Nt(r, t) {
273
313
  const e = /* @__PURE__ */ new Map();
274
314
  for (const i of r)
275
- for (const s of Object.values(i.declaration.plugin.provides ?? {})) {
276
- j(t, s);
315
+ for (const s of Object.values(i.declaration.plugin.provides)) {
316
+ q(t, s);
277
317
  const n = e.get(s.id);
278
318
  if (n)
279
319
  throw new u(
@@ -284,12 +324,12 @@ function vt(r, t) {
284
324
  }
285
325
  return e;
286
326
  }
287
- function bt(r, t, e) {
327
+ function xt(r, t, e) {
288
328
  const i = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Map(), n = new Map(r.map((o) => [o, 0]));
289
329
  for (const o of r)
290
- for (const a of Object.values(o.declaration.plugin.requires ?? {})) {
330
+ for (const a of Object.values(o.declaration.plugin.requires)) {
291
331
  const h = a.kind === "optional" ? a.service : a;
292
- if (j(e, h), h.kind === "extensionPoint") continue;
332
+ if (q(e, h), h.kind === "extensionPoint") continue;
293
333
  const c = t.get(h.id);
294
334
  if (!c) {
295
335
  if (a.kind === "optional") continue;
@@ -310,7 +350,7 @@ function bt(r, t, e) {
310
350
  }
311
351
  return { resolvedProviders: i, dependents: s, indegree: n };
312
352
  }
313
- function mt(r, t, e) {
353
+ function Mt(r, t, e) {
314
354
  let i = r.filter((o) => e.get(o) === 0);
315
355
  const s = [], n = [];
316
356
  for (; i.length; ) {
@@ -324,7 +364,7 @@ function mt(r, t, e) {
324
364
  }
325
365
  }
326
366
  if (s.length !== r.length) {
327
- const o = yt(r, t);
367
+ const o = Dt(r, t);
328
368
  throw new u(
329
369
  "SERVICE_CYCLE",
330
370
  `Installation dependency cycle: ${o.map((a) => a.id).join(" -> ")}`
@@ -332,7 +372,7 @@ function mt(r, t, e) {
332
372
  }
333
373
  return { order: s, layers: n };
334
374
  }
335
- function yt(r, t) {
375
+ function Dt(r, t) {
336
376
  const e = /* @__PURE__ */ new Set(), i = /* @__PURE__ */ new Set(), s = [], n = (o) => {
337
377
  i.add(o), s.push(o);
338
378
  for (const a of t.get(o) ?? []) {
@@ -353,7 +393,7 @@ function yt(r, t) {
353
393
  }
354
394
  throw new Error("Dependency graph is cyclic but no cycle path was found");
355
395
  }
356
- class O {
396
+ class z {
357
397
  #t;
358
398
  constructor(t = []) {
359
399
  if (!t || typeof t != "object" && typeof t != "function" || typeof t[Symbol.iterator] != "function")
@@ -385,31 +425,65 @@ class O {
385
425
  return this.entries();
386
426
  }
387
427
  }
388
- function Et(r) {
428
+ function Vt(r) {
389
429
  if (typeof r != "function")
390
430
  throw new Error("Unsupported JavaScript runtime: Promise.withResolvers is required");
391
431
  }
392
- function st(r, t) {
432
+ function mt(r, t) {
393
433
  return r ?? Symbol.for(t);
394
434
  }
395
- Et(Promise.withResolvers);
396
- const g = st(
435
+ Vt(Promise.withResolvers);
436
+ const v = mt(
397
437
  Symbol.dispose,
398
438
  "Symbol.dispose"
399
- ), k = st(
439
+ ), F = mt(
400
440
  Symbol.asyncDispose,
401
441
  "Symbol.asyncDispose"
402
442
  );
403
- function P(r, t) {
404
- if (Ct(r))
443
+ function R(r, t) {
444
+ if (qt(r))
405
445
  throw Promise.resolve(r).catch(() => {
406
446
  }), new TypeError(t);
407
447
  }
408
- function Ct(r) {
448
+ function qt(r) {
409
449
  return r === null || typeof r != "object" && typeof r != "function" ? !1 : typeof r.then == "function";
410
450
  }
411
- const y = /* @__PURE__ */ new WeakMap();
412
- class q {
451
+ const N = /* @__PURE__ */ new WeakMap(), C = /* @__PURE__ */ new Set();
452
+ let T = 0, U = !1;
453
+ function Ft(r) {
454
+ T++;
455
+ const t = [];
456
+ try {
457
+ r();
458
+ } catch (e) {
459
+ t.push(e);
460
+ } finally {
461
+ if (T--, !T)
462
+ try {
463
+ vt();
464
+ } catch (e) {
465
+ t.push(e);
466
+ }
467
+ }
468
+ if (t.length === 1) throw t[0];
469
+ if (t.length > 1) throw new AggregateError(t, "Snapshot batch failed");
470
+ }
471
+ function vt() {
472
+ if (U) return;
473
+ U = !0;
474
+ const r = [];
475
+ try {
476
+ for (; C.size; ) {
477
+ const t = C.values().next().value;
478
+ C.delete(t), Gt(t, r);
479
+ }
480
+ } finally {
481
+ U = !1, C.clear();
482
+ }
483
+ if (r.length)
484
+ throw new AggregateError(r, "Snapshot error reporting failed");
485
+ }
486
+ class Z {
413
487
  #t = /* @__PURE__ */ new Set();
414
488
  #e;
415
489
  #i;
@@ -425,24 +499,9 @@ class q {
425
499
  subscribe: (t) => this.#a(t)
426
500
  });
427
501
  invalidate() {
428
- const { report: t } = this.#o();
429
- this.#s = !0;
430
- const e = [];
431
- for (const i of [...this.#t])
432
- try {
433
- Ot(i);
434
- } catch (s) {
435
- try {
436
- P(
437
- t(s),
438
- "Snapshot error reporters must be synchronous"
439
- );
440
- } catch (n) {
441
- e.push(s, n);
442
- }
443
- }
444
- if (e.length)
445
- throw new AggregateError(e, "Snapshot error reporting failed");
502
+ this.#o(), this.#s = !0;
503
+ for (const t of this.#t) C.add(t);
504
+ T || vt();
446
505
  }
447
506
  dispose() {
448
507
  const t = this.#e;
@@ -454,11 +513,11 @@ class q {
454
513
  this.#s = !1;
455
514
  const e = [...this.#t];
456
515
  this.#t.clear();
457
- for (const i of e) It(i);
516
+ for (const i of e) yt(i);
458
517
  }
459
518
  }
460
519
  }
461
- [g]() {
520
+ [v]() {
462
521
  this.dispose();
463
522
  }
464
523
  #n() {
@@ -467,11 +526,10 @@ class q {
467
526
  }
468
527
  #a(t) {
469
528
  if (typeof t != "function") throw new TypeError("Subscriber must be a function");
470
- this.#o();
471
- const e = new St(t, (i) => {
472
- this.#t.delete(i);
529
+ const { report: e } = this.#o(), i = new Bt(t, e, (s) => {
530
+ this.#t.delete(s);
473
531
  });
474
- return this.#t.add(e), e;
532
+ return this.#t.add(i), i;
475
533
  }
476
534
  #r(t) {
477
535
  this.#s && (this.#i = t(), this.#s = !1);
@@ -482,26 +540,38 @@ class q {
482
540
  return t;
483
541
  }
484
542
  }
485
- class St {
486
- constructor(t, e) {
487
- y.set(this, { listener: t, detach: e }), Object.freeze(this);
543
+ class Bt {
544
+ constructor(t, e, i) {
545
+ N.set(this, { listener: t, report: e, detach: i }), Object.freeze(this);
488
546
  }
489
547
  dispose() {
490
- const t = y.get(this);
491
- t && (y.delete(this), t.detach(this));
548
+ const t = N.get(this);
549
+ t && (yt(this), t.detach(this));
492
550
  }
493
- [g]() {
551
+ [v]() {
494
552
  this.dispose();
495
553
  }
496
554
  }
497
- function Ot(r) {
498
- const t = y.get(r);
499
- t && P(t.listener(), "Snapshot subscribers must be synchronous");
555
+ function Gt(r, t) {
556
+ const e = N.get(r);
557
+ if (e)
558
+ try {
559
+ R(e.listener(), "Snapshot subscribers must be synchronous");
560
+ } catch (i) {
561
+ try {
562
+ R(
563
+ e.report(i),
564
+ "Snapshot error reporters must be synchronous"
565
+ );
566
+ } catch (s) {
567
+ t.push(i, s);
568
+ }
569
+ }
500
570
  }
501
- function It(r) {
502
- y.delete(r);
571
+ function yt(r) {
572
+ C.delete(r), N.delete(r);
503
573
  }
504
- class At {
574
+ class Ut {
505
575
  #t;
506
576
  get;
507
577
  subscribe;
@@ -523,7 +593,7 @@ class At {
523
593
  return t;
524
594
  }
525
595
  }
526
- class Tt {
596
+ class _t {
527
597
  #t;
528
598
  constructor(t) {
529
599
  this.#t = t, Object.freeze(this);
@@ -534,16 +604,16 @@ class Tt {
534
604
  dispose() {
535
605
  this.#t.dispose();
536
606
  }
537
- [g]() {
607
+ [v]() {
538
608
  this.dispose();
539
609
  }
540
610
  }
541
- class $t {
611
+ class Ht {
542
612
  #t;
543
613
  #e;
544
614
  handle;
545
615
  constructor(t, e, i, s) {
546
- this.#e = e, this.#t = { phase: "staged", store: t, value: i, detachFromOwner: s }, this.handle = new Tt(this);
616
+ this.#e = e, this.#t = { phase: "staged", store: t, value: i, detachFromOwner: s }, this.handle = new _t(this);
547
617
  }
548
618
  publish() {
549
619
  const t = this.#t;
@@ -572,55 +642,62 @@ class $t {
572
642
  }
573
643
  }
574
644
  }
575
- [g]() {
645
+ [v]() {
576
646
  this.dispose();
577
647
  }
578
648
  }
579
- class jt {
649
+ class Kt {
580
650
  #t;
581
651
  #e;
582
652
  #i = /* @__PURE__ */ new Map();
583
653
  #s = /* @__PURE__ */ new Map();
584
654
  #n;
585
- #a = new O();
655
+ #a = new z();
586
656
  #r = 0;
587
657
  #o = 0;
588
- #d = !1;
589
- #f = !1;
658
+ #h = !1;
659
+ #p = !1;
590
660
  constructor(t, e, i) {
591
- this.#t = t, this.#e = i, this.#n = new q(() => this.#a, e);
661
+ this.#t = t, this.#e = i, this.#n = new Z(() => this.#a, e);
592
662
  }
593
663
  stage(t, e, i, s) {
594
- zt(e);
595
- const n = Rt(t, e);
664
+ Jt(e);
665
+ const n = Xt(t, e);
596
666
  if (this.#i.has(n))
597
667
  throw new TypeError(`Duplicate contribution '${n}'`);
598
- const o = new $t(this, n, i, s);
668
+ const o = new Ht(this, n, i, s);
599
669
  return this.#i.set(n, o), o;
600
670
  }
601
671
  view(t) {
602
672
  this.#r++;
603
673
  const e = { binding: { store: this, own: t } };
604
674
  let i;
605
- const s = new kt(e, () => {
675
+ const s = new Wt(e, () => {
606
676
  const n = i;
607
677
  i = void 0;
608
678
  try {
609
679
  n?.();
610
680
  } finally {
611
- this.#r--, this.#h();
681
+ this.#r--, this.#c();
612
682
  }
613
683
  });
614
684
  try {
615
685
  i = t(s, "view");
616
686
  } catch (n) {
617
- throw e.binding = void 0, this.#r--, this.#h(), n;
687
+ throw e.binding = void 0, this.#r--, this.#c(), n;
618
688
  }
619
- return new At(e);
620
- }
621
- /** A stable view explicitly owned by the Host for graph-external consumers. */
689
+ return new Ut(e);
690
+ }
691
+ /**
692
+ * A stable view explicitly owned by the Host for graph-external consumers.
693
+ *
694
+ * Application code has no Lifetime to lease a view from, so this one is
695
+ * retained by the Host instead — which also pins the store open for as long as
696
+ * the Host lives. That is the trade: `host.contributions()` gives a handle that
697
+ * never goes stale, at the cost of a store that is no longer self-releasing.
698
+ */
622
699
  hostView() {
623
- return this.#d = !0, this.#n.view;
700
+ return this.#h = !0, this.#n.view;
624
701
  }
625
702
  snapshot() {
626
703
  return this.#n.view.get();
@@ -631,13 +708,13 @@ class jt {
631
708
  const i = this.#n.view.subscribe(t);
632
709
  this.#o++;
633
710
  let s;
634
- const n = new Pt(i, () => {
711
+ const n = new Yt(i, () => {
635
712
  const o = s;
636
713
  s = void 0;
637
714
  try {
638
715
  o?.();
639
716
  } finally {
640
- this.#o--, this.#h();
717
+ this.#o--, this.#c();
641
718
  }
642
719
  });
643
720
  try {
@@ -648,39 +725,50 @@ class jt {
648
725
  return n;
649
726
  }
650
727
  insert(t, e, i) {
651
- if (this.#l(t, e), this.#s.has(t))
728
+ if (this.#u(t, e), this.#s.has(t))
652
729
  throw new Error(`Contribution '${t}' is already published`);
653
730
  e.commitPublication(), this.#s.set(t, { contribution: e, value: i }), this.#t(this);
654
731
  }
655
732
  update(t, e, i) {
656
- const s = this.#u(t, e);
733
+ const s = this.#f(t, e);
657
734
  s.value = i, this.#t(this);
658
735
  }
659
736
  removeContribution(t, e, i) {
660
- this.#l(t, e), i === "published" && this.#u(t, e);
737
+ this.#u(t, e), i === "published" && this.#f(t, e);
661
738
  try {
662
739
  this.#i.delete(t), i === "published" && (this.#s.delete(t), this.#t(this));
663
740
  } finally {
664
- this.#h();
741
+ this.#c();
665
742
  }
666
743
  }
744
+ /**
745
+ * Rebuilds the published snapshot, and does nothing when the result would be
746
+ * equal by identity per key. A change that adds and removes the same entry —
747
+ * an Instance restarting with an identical contribution — therefore notifies
748
+ * nobody, instead of waking every observer to hand them what they already had.
749
+ */
667
750
  publishSnapshot() {
668
751
  const t = [...this.#s].map(([i, s]) => [i, s.value]);
669
- t.length === this.#a.size && t.every(([i, s]) => Object.is(this.#a.get(i), s)) || (this.#a = new O(t), this.#n.invalidate());
752
+ t.length === this.#a.size && t.every(
753
+ ([i, s]) => this.#a.has(i) && Object.is(this.#a.get(i), s)
754
+ ) || (this.#a = new z(t), this.#n.invalidate());
670
755
  }
671
- #l(t, e) {
756
+ #u(t, e) {
672
757
  if (this.#i.get(t) !== e)
673
758
  throw new Error(`Contribution '${t}' is not the current claim`);
674
759
  }
675
- #u(t, e) {
760
+ #f(t, e) {
676
761
  const i = this.#s.get(t);
677
762
  if (i?.contribution !== e)
678
763
  throw new Error(`Contribution '${t}' is not the published entry`);
679
764
  return i;
680
765
  }
681
- #h() {
682
- if (!(this.#f || this.#d || this.#i.size || this.#s.size || this.#o || this.#r)) {
683
- this.#f = !0;
766
+ // The self-release check. Every counter has to be at zero and the Host must
767
+ // not be holding a view; `#released` makes it run once, since disposing the
768
+ // publisher can re-enter through a subscription's own teardown.
769
+ #c() {
770
+ if (!(this.#p || this.#h || this.#i.size || this.#s.size || this.#o || this.#r)) {
771
+ this.#p = !0;
684
772
  try {
685
773
  this.#n.dispose();
686
774
  } finally {
@@ -689,7 +777,7 @@ class jt {
689
777
  }
690
778
  }
691
779
  }
692
- class kt {
780
+ class Wt {
693
781
  #t;
694
782
  constructor(t, e) {
695
783
  this.#t = { state: t, release: e }, Object.freeze(this);
@@ -698,11 +786,11 @@ class kt {
698
786
  const t = this.#t;
699
787
  t && (this.#t = void 0, t.state.binding = void 0, t.release());
700
788
  }
701
- [g]() {
789
+ [v]() {
702
790
  this.dispose();
703
791
  }
704
792
  }
705
- class Pt {
793
+ class Yt {
706
794
  #t;
707
795
  constructor(t, e) {
708
796
  this.#t = { subscription: t, release: e }, Object.freeze(this);
@@ -718,11 +806,11 @@ class Pt {
718
806
  }
719
807
  }
720
808
  }
721
- [g]() {
809
+ [v]() {
722
810
  this.dispose();
723
811
  }
724
812
  }
725
- class Lt {
813
+ class Qt {
726
814
  #t = /* @__PURE__ */ new Map();
727
815
  #e = /* @__PURE__ */ new Set();
728
816
  #i;
@@ -733,7 +821,7 @@ class Lt {
733
821
  get(t) {
734
822
  const e = this.#t.get(t.id);
735
823
  if (e) return e;
736
- const i = new jt(
824
+ const i = new Kt(
737
825
  (s) => this.#n(s),
738
826
  this.#i,
739
827
  (s) => {
@@ -748,39 +836,44 @@ class Lt {
748
836
  beginBatch() {
749
837
  this.#s++;
750
838
  }
839
+ // Depth-counted, so nested Host operations collapse into the outermost batch
840
+ // and publication happens exactly once. Every store's snapshot is attempted
841
+ // even if one of them throws, then the failures are aggregated — a single bad
842
+ // subscriber must not leave the remaining stores unpublished.
751
843
  endBatch() {
752
844
  if (!this.#s) throw new Error("Contribution batch is not active");
753
845
  if (this.#s--, this.#s) return;
754
846
  const t = [...this.#e];
755
- this.#e.clear();
756
- const e = [];
757
- for (const i of t)
758
- try {
759
- i.publishSnapshot();
760
- } catch (s) {
761
- e.push(s);
762
- }
763
- if (e.length === 1) throw e[0];
764
- if (e.length > 1)
765
- throw new AggregateError(e, "Contribution batch publication failed");
847
+ this.#e.clear(), Ft(() => {
848
+ const e = [];
849
+ for (const i of t)
850
+ try {
851
+ i.publishSnapshot();
852
+ } catch (s) {
853
+ e.push(s);
854
+ }
855
+ if (e.length === 1) throw e[0];
856
+ if (e.length > 1)
857
+ throw new AggregateError(e, "Contribution batch publication failed");
858
+ });
766
859
  }
767
860
  #n(t) {
768
861
  this.#e.add(t), !this.#s && (this.#e.delete(t), t.publishSnapshot());
769
862
  }
770
863
  }
771
- function zt(r) {
864
+ function Jt(r) {
772
865
  if (typeof r != "string" || !r.trim())
773
866
  throw new TypeError("Contribution key must be a non-empty string");
774
867
  if (r !== r.trim())
775
868
  throw new TypeError("Contribution key cannot start or end with whitespace");
776
869
  }
777
- function Rt(r, t) {
778
- return `${U(r)}/${U(t)}`;
870
+ function Xt(r, t) {
871
+ return `${ot(r)}/${ot(t)}`;
779
872
  }
780
- function U(r) {
873
+ function ot(r) {
781
874
  return r.replaceAll("%", "%25").replaceAll("/", "%2F");
782
875
  }
783
- class Nt {
876
+ class Zt {
784
877
  #t;
785
878
  constructor(t) {
786
879
  this.#t = t, Object.freeze(this);
@@ -789,16 +882,16 @@ class Nt {
789
882
  const t = this.#t;
790
883
  this.#t = void 0, t?.dispose();
791
884
  }
792
- [g]() {
885
+ [v]() {
793
886
  this.dispose();
794
887
  }
795
888
  }
796
- class xt {
889
+ class te {
797
890
  #t;
798
891
  #e;
799
892
  handle;
800
893
  constructor(t, e, i, s) {
801
- this.#e = e, this.#t = { phase: "staged", hub: t, slot: { listener: i }, release: s }, this.handle = new Nt(this);
894
+ this.#e = e, this.#t = { phase: "staged", hub: t, slot: { listener: i }, release: s }, this.handle = new Zt(this);
802
895
  }
803
896
  publish() {
804
897
  const t = this.#t;
@@ -807,7 +900,7 @@ class xt {
807
900
  dispose() {
808
901
  const t = this.#t;
809
902
  if (t.phase !== "removed") {
810
- this.#t = { phase: "removed" };
903
+ this.#t = { phase: "removed" }, t.slot.listener = void 0;
811
904
  try {
812
905
  t.phase === "published" && t.hub.delete(this.#e, t.slot);
813
906
  } finally {
@@ -815,15 +908,15 @@ class xt {
815
908
  }
816
909
  }
817
910
  }
818
- [g]() {
911
+ [v]() {
819
912
  this.dispose();
820
913
  }
821
914
  }
822
- class Dt {
915
+ class ee {
823
916
  #t = /* @__PURE__ */ new Map();
824
917
  stage(t, e, i) {
825
918
  if (typeof e != "function") throw new TypeError("Event listener must be a function");
826
- return new xt(this, t, e, i);
919
+ return new te(this, t, e, i);
827
920
  }
828
921
  add(t, e) {
829
922
  const i = this.#t.get(t) ?? /* @__PURE__ */ new Set();
@@ -833,25 +926,34 @@ class Dt {
833
926
  const i = this.#t.get(t);
834
927
  i && (i.delete(e), i.size || this.#t.delete(t));
835
928
  }
929
+ /**
930
+ * Every still-subscribed listener runs, then failures are aggregated. Three details, all
931
+ * deliberate:
932
+ *
933
+ * - the listener set is copied first; a new subscription does not join this
934
+ * emission. A disposed slot is skipped if its callback has not started;
935
+ * - each call is wrapped in a resolved promise, so a listener that throws
936
+ * synchronously is collected like one that rejects;
937
+ * - `allSettled`, so one broken listener cannot stop the others from being
938
+ * told. The emitter still learns about it — every error comes back.
939
+ */
836
940
  async emit(t, e) {
837
- const i = [...this.#t.get(t) ?? []].map(
838
- (o) => o.listener
839
- ), n = (await Promise.allSettled(
840
- i.map((o) => Promise.resolve().then(() => o(e)))
941
+ const i = [...this.#t.get(t) ?? []], n = (await Promise.allSettled(
942
+ i.map((o) => Promise.resolve().then(() => o.listener?.(e)))
841
943
  )).filter((o) => o.status === "rejected").map((o) => o.reason);
842
944
  if (n.length)
843
945
  throw new AggregateError(n, `Event '${t}' listeners failed`);
844
946
  }
845
947
  }
846
- class Vt {
948
+ class ie {
847
949
  #t;
848
950
  root;
849
951
  view;
850
952
  constructor(t, e) {
851
- this.root = _(t), this.#t = new q(() => rt(this.root), e), this.view = this.#t.view;
953
+ this.root = at(t), this.#t = new Z(() => Et(this.root), e), this.view = this.#t.view;
852
954
  }
853
955
  createNode(t) {
854
- return _(t);
956
+ return at(t);
855
957
  }
856
958
  attach(t, e) {
857
959
  if (t.children.has(e)) throw new Error("Lifetime diagnostic node is attached");
@@ -872,7 +974,7 @@ class Vt {
872
974
  this.root.phase !== "disposed" && (this.root.phase = "disposed", this.#t.invalidate(), this.#t.dispose());
873
975
  }
874
976
  }
875
- function _(r) {
977
+ function at(r) {
876
978
  return {
877
979
  label: r,
878
980
  phase: "active",
@@ -887,8 +989,8 @@ function _(r) {
887
989
  children: /* @__PURE__ */ new Set()
888
990
  };
889
991
  }
890
- function rt(r) {
891
- const t = Object.freeze([...r.children].map(rt));
992
+ function Et(r) {
993
+ const t = Object.freeze([...r.children].map(Et));
892
994
  return Object.freeze({
893
995
  label: r.label,
894
996
  phase: r.phase,
@@ -896,7 +998,7 @@ function rt(r) {
896
998
  children: t
897
999
  });
898
1000
  }
899
- function Mt(r) {
1001
+ function se(r) {
900
1002
  if (!r || typeof r != "object" && typeof r != "function") return !1;
901
1003
  try {
902
1004
  const t = r;
@@ -907,8 +1009,8 @@ function Mt(r) {
907
1009
  return !1;
908
1010
  }
909
1011
  }
910
- const x = Object.freeze(new DOMException("Resource disposed", "AbortError"));
911
- class w {
1012
+ const W = Object.freeze(new DOMException("Resource disposed", "AbortError"));
1013
+ class y {
912
1014
  #t = /* @__PURE__ */ new Set();
913
1015
  #e;
914
1016
  constructor(t) {
@@ -928,6 +1030,12 @@ class w {
928
1030
  const e = this.#e;
929
1031
  return e && e.diagnostics.change(e.node, e.kind, -1), !0;
930
1032
  };
1033
+ /**
1034
+ * Disposes in reverse creation order, collecting failures rather than
1035
+ * throwing. Later resources may depend on earlier ones, and a resource that
1036
+ * refuses to shut down cleanly must not strand its siblings. The caller
1037
+ * aggregates the collected errors.
1038
+ */
931
1039
  async dispose(t) {
932
1040
  try {
933
1041
  for (const e of [...this.#t].reverse())
@@ -942,11 +1050,26 @@ class w {
942
1050
  this.#e = void 0;
943
1051
  }
944
1052
  }
1053
+ /** Capability withdrawal must finish without yielding to callbacks or tasks. */
1054
+ disposeSynchronously(t) {
1055
+ try {
1056
+ for (const e of [...this.#t].reverse())
1057
+ try {
1058
+ e.dispose();
1059
+ } catch (i) {
1060
+ t.push(i);
1061
+ } finally {
1062
+ this.release(e);
1063
+ }
1064
+ } finally {
1065
+ this.#e = void 0;
1066
+ }
1067
+ }
945
1068
  [Symbol.iterator]() {
946
1069
  return this.#t[Symbol.iterator]();
947
1070
  }
948
1071
  }
949
- class qt {
1072
+ class re {
950
1073
  #t;
951
1074
  constructor(t, e) {
952
1075
  this.#t = { phase: "active", cleanup: t, detachFromParent: e }, Object.freeze(this);
@@ -966,11 +1089,11 @@ class qt {
966
1089
  });
967
1090
  return this.#t = { phase: "disposing", completion: e }, e;
968
1091
  }
969
- [k]() {
1092
+ [F]() {
970
1093
  return this.dispose();
971
1094
  }
972
1095
  }
973
- class Bt {
1096
+ class ne {
974
1097
  #t;
975
1098
  #e;
976
1099
  #i;
@@ -983,7 +1106,7 @@ class Bt {
983
1106
  () => this.#s(),
984
1107
  (a) => {
985
1108
  try {
986
- tt(n.signal, a) || i(a);
1109
+ wt(n.signal, a) || i(a);
987
1110
  } finally {
988
1111
  this.#s();
989
1112
  }
@@ -1001,9 +1124,9 @@ class Bt {
1001
1124
  () => {
1002
1125
  }
1003
1126
  );
1004
- return this.#i = { phase: "disposing", completion: e }, t.controller.abort(x), this.#n(), e;
1127
+ return this.#i = { phase: "disposing", completion: e }, t.controller.abort(W), this.#n(), e;
1005
1128
  }
1006
- [k]() {
1129
+ [F]() {
1007
1130
  return this.dispose();
1008
1131
  }
1009
1132
  #s() {
@@ -1017,7 +1140,7 @@ class Bt {
1017
1140
  this.#t = void 0, t?.();
1018
1141
  }
1019
1142
  }
1020
- class B {
1143
+ class tt {
1021
1144
  #t;
1022
1145
  #e;
1023
1146
  #i;
@@ -1026,45 +1149,45 @@ class B {
1026
1149
  #a;
1027
1150
  #r;
1028
1151
  #o;
1029
- #d;
1030
- #f;
1031
- #l;
1152
+ #h;
1153
+ #p;
1032
1154
  #u;
1155
+ #f;
1033
1156
  handle;
1034
- #h;
1157
+ #c;
1035
1158
  constructor(t, e, i = {}) {
1036
- this.#e = e, this.handle = new Ft(this);
1159
+ this.#e = e, this.handle = new oe(this);
1037
1160
  const s = new AbortController();
1038
- this.#h = {
1161
+ this.#c = {
1039
1162
  phase: "active",
1040
1163
  controller: s,
1041
1164
  declarations: i.declarations ?? "staged"
1042
1165
  };
1043
1166
  const n = i.parent;
1044
- this.#u = n?.detach, this.#f = n ? "child" : "root";
1045
- const o = n?.diagnostics ?? new Vt(e, (l) => t.report(l)), a = n?.diagnosticNode ?? o.root;
1167
+ this.#f = n?.detach, this.#p = n ? "child" : "root";
1168
+ const o = n?.diagnostics ?? new ie(e, (l) => t.report(l)), a = n?.diagnosticNode ?? o.root;
1046
1169
  this.#t = { port: t, diagnostics: o, diagnosticNode: a };
1047
1170
  const h = (l) => ({
1048
1171
  diagnostics: o,
1049
1172
  node: a,
1050
1173
  kind: l
1051
1174
  });
1052
- this.#i = new w(h("listeners")), this.#s = new w(h("contributions")), this.#n = new w(h("contributionViews")), this.#a = new w(h("subscriptions")), this.#r = new w(h("tasks")), this.#o = new w(), this.#d = new w(h("cleanups"));
1175
+ this.#i = new y(h("listeners")), this.#s = new y(h("contributions")), this.#n = new y(h("contributionViews")), this.#a = new y(h("subscriptions")), this.#r = new y(h("tasks")), this.#o = new y(), this.#h = new y(h("cleanups"));
1053
1176
  const c = i.parentSignal;
1054
1177
  if (!c) return;
1055
1178
  const d = () => s.abort(c.reason);
1056
- c.addEventListener("abort", d, { once: !0 }), this.#l = () => c.removeEventListener("abort", d), c.aborted && d();
1179
+ c.addEventListener("abort", d, { once: !0 }), this.#u = () => c.removeEventListener("abort", d), c.aborted && d();
1057
1180
  }
1058
1181
  get signal() {
1059
- const t = this.#h;
1182
+ const t = this.#c;
1060
1183
  return t.phase === "disposed" ? t.signal : t.controller.signal;
1061
1184
  }
1062
1185
  get diagnostics() {
1063
- return this.#c().diagnostics.view;
1186
+ return this.#g().diagnostics.view;
1064
1187
  }
1065
1188
  /** A facade whose only InstanceCoordinator edge disappears when this Lifetime terminates. */
1066
1189
  contextLogger(t) {
1067
- const e = (i) => (s, ...n) => this.#p().port.writeLog(i, s, t, n);
1190
+ const e = (i) => (s, ...n) => this.#g().port.writeLog(i, s, t, n);
1068
1191
  return Object.freeze({
1069
1192
  debug: e("debug"),
1070
1193
  info: e("info"),
@@ -1073,16 +1196,25 @@ class B {
1073
1196
  });
1074
1197
  }
1075
1198
  cleanup(t) {
1076
- if (this.#c(), typeof t != "function") throw new TypeError("Cleanup must be a function");
1077
- const e = new qt(t, this.#d.release);
1078
- return this.#d.add(e), e;
1079
- }
1199
+ if (this.#d(), typeof t != "function") throw new TypeError("Cleanup must be a function");
1200
+ const e = new re(t, this.#h.release);
1201
+ return this.#h.add(e), e;
1202
+ }
1203
+ /**
1204
+ * A child scope. It inherits this Lifetime's signal, so cancellation flows
1205
+ * down, and its declaration phase, so a child created during `setup()` also
1206
+ * stages rather than publishing immediately.
1207
+ *
1208
+ * Disposing a child detaches it from both the resource set and the diagnostics
1209
+ * tree. A Plugin can therefore use child lifetimes as a unit of work — one per
1210
+ * open document, per connection — without the parent growing.
1211
+ */
1080
1212
  lifetime(t) {
1081
- const { port: e, diagnostics: i, diagnosticNode: s } = this.#c();
1082
- Gt(t);
1083
- const n = i.createNode(t), o = new B(e, this.#e, {
1213
+ const { port: e, diagnostics: i, diagnosticNode: s } = this.#d();
1214
+ ae(t);
1215
+ const n = i.createNode(t), o = new tt(e, this.#e, {
1084
1216
  parentSignal: this.signal,
1085
- declarations: this.#g(),
1217
+ declarations: this.#w(),
1086
1218
  parent: {
1087
1219
  diagnostics: i,
1088
1220
  diagnosticNode: n,
@@ -1094,9 +1226,9 @@ class B {
1094
1226
  return this.#o.add(o), i.attach(s, n), o.handle;
1095
1227
  }
1096
1228
  spawn(t) {
1097
- const { port: e } = this.#c();
1229
+ const { port: e } = this.#d();
1098
1230
  if (typeof t != "function") throw new TypeError("Background task must be a function");
1099
- const i = new Bt(
1231
+ const i = new ne(
1100
1232
  this.signal,
1101
1233
  t,
1102
1234
  (s) => e.report(s),
@@ -1105,89 +1237,137 @@ class B {
1105
1237
  return this.#r.add(i), i;
1106
1238
  }
1107
1239
  on(t, e) {
1108
- const { port: i } = this.#c(), s = i.stageOn(
1240
+ const { port: i } = this.#d(), s = i.stageOn(
1109
1241
  this.#e,
1110
1242
  t,
1111
1243
  e,
1112
1244
  this.#i.release
1113
1245
  );
1114
- return this.#i.add(s), this.#g() === "published" && s.publish(), s.handle;
1246
+ return this.#i.add(s), this.#w() === "published" && s.publish(), s.handle;
1115
1247
  }
1116
1248
  async emit(t, ...e) {
1117
- const { port: i } = this.#c();
1249
+ const { port: i } = this.#d();
1118
1250
  await i.emit(this.#e, t, e[0]);
1119
1251
  }
1120
1252
  contribute(t, e, i) {
1121
- const { port: s } = this.#c(), n = s.stageContribution(
1253
+ const { port: s } = this.#d(), n = s.stageContribution(
1122
1254
  this.#e,
1123
1255
  t,
1124
1256
  e,
1125
1257
  i,
1126
1258
  this.#s.release
1127
1259
  );
1128
- return this.#s.add(n), this.#g() === "published" && n.publish(), n.handle;
1260
+ return this.#s.add(n), this.#w() === "published" && n.publish(), n.handle;
1129
1261
  }
1130
1262
  /** Owns an internal live capability without exposing a second Context API. */
1131
1263
  ownLease(t, e) {
1132
- return this.#c(), (e === "view" ? this.#n : this.#a).own(t);
1264
+ return this.#d(), (e === "view" ? this.#n : this.#a).own(t);
1133
1265
  }
1134
1266
  /** Releases a temporary startup cancellation edge after its layer commits. */
1135
1267
  detachStartupSignal() {
1136
- const t = this.#l;
1137
- this.#l = void 0, t?.();
1138
- }
1139
- /** Atomically makes all declarations staged during setup visible. */
1268
+ const t = this.#u;
1269
+ this.#u = void 0, t?.();
1270
+ }
1271
+ /**
1272
+ * Atomically makes all declarations staged during setup visible.
1273
+ *
1274
+ * Children publish too, recursively, so a Lifetime created inside `setup()`
1275
+ * becomes visible with its parent rather than being stranded staged. Called by
1276
+ * the InstanceCoordinator once the whole activation layer has succeeded —
1277
+ * which is what makes "no half-built Instance is observable" true.
1278
+ */
1140
1279
  publish() {
1141
- const t = this.#h;
1142
- if (t.phase !== "active") throw E();
1143
- if (this.#c(), t.declarations !== "published") {
1280
+ const t = this.#c;
1281
+ if (t.phase !== "active") throw $();
1282
+ if (this.#d(), t.declarations !== "published") {
1144
1283
  for (const e of this.#i) e.publish();
1145
1284
  for (const e of this.#s) e.publish();
1146
1285
  for (const e of this.#o) e.publish();
1147
- this.#h = { ...t, declarations: "published" };
1286
+ this.#c = { ...t, declarations: "published" };
1148
1287
  }
1149
1288
  }
1289
+ /**
1290
+ * Disposal is idempotent and joinable: concurrent callers share one completion
1291
+ * promise, so a Lifetime disposed by both its owner and a `using` block runs
1292
+ * its teardown once.
1293
+ *
1294
+ * The three-stage order is the module header's; see it for why. Errors from
1295
+ * every stage accumulate and are aggregated at the end, so one failing cleanup
1296
+ * neither hides the others nor prevents them from running.
1297
+ */
1150
1298
  dispose() {
1151
- const t = this.#h;
1299
+ const t = this.#c;
1152
1300
  if (t.phase === "disposing") return t.completion;
1153
1301
  if (t.phase === "disposed") return Promise.resolve();
1154
- const e = this.#p(), i = Promise.resolve().then(async () => {
1155
- const s = [];
1156
- if (await this.#i.dispose(s), await this.#s.dispose(s), await this.#a.dispose(s), await this.#n.dispose(s), t.controller.abort(x), this.detachStartupSignal(), await this.#r.dispose(s), await this.#o.dispose(s), await this.#d.dispose(s), s.length === 1) throw s[0];
1157
- if (s.length > 1) throw new AggregateError(s, "Lifetime cleanup failed");
1158
- }).finally(() => {
1159
- this.#h = { phase: "disposed", signal: AbortSignal.abort(x) };
1160
- const s = this.#u;
1161
- this.#u = void 0;
1162
- try {
1163
- s?.(this), this.#f === "root" && e.diagnostics.finishRoot();
1164
- } finally {
1165
- this.#t = void 0;
1166
- }
1167
- });
1168
- return this.#h = { phase: "disposing", controller: t.controller, completion: i }, e.diagnostics.beginDisposing(e.diagnosticNode), i;
1302
+ const e = [], i = this.#l(e);
1303
+ for (const { lifetime: s, errors: n } of e)
1304
+ s.#i.disposeSynchronously(n), s.#a.disposeSynchronously(n), s.#n.disposeSynchronously(n);
1305
+ for (const { lifetime: s, errors: n } of e)
1306
+ s.#s.disposeSynchronously(n);
1307
+ for (const { lifetime: s, binding: n, controller: o } of e)
1308
+ n.diagnostics.beginDisposing(n.diagnosticNode), o.abort(W), s.detachStartupSignal();
1309
+ for (const s of e) s.finish();
1310
+ return i;
1169
1311
  }
1170
- [k]() {
1312
+ /** Stages joinable completions without running any externally observable work. */
1313
+ #l(t) {
1314
+ const e = this.#c;
1315
+ if (e.phase === "disposing") return e.completion;
1316
+ if (e.phase === "disposed") return Promise.resolve();
1317
+ const i = this.#g(), s = Promise.withResolvers(), n = [];
1318
+ this.#c = {
1319
+ phase: "disposing",
1320
+ controller: e.controller,
1321
+ completion: s.promise
1322
+ };
1323
+ const o = Promise.allSettled(
1324
+ [...this.#o].reverse().map((h) => h.#l(t))
1325
+ ), a = () => {
1326
+ (async () => {
1327
+ try {
1328
+ await this.#r.dispose(n);
1329
+ for (const h of await o)
1330
+ h.status === "rejected" && n.push(h.reason);
1331
+ if (await this.#h.dispose(n), n.length === 1) throw n[0];
1332
+ if (n.length > 1) throw new AggregateError(n, "Lifetime cleanup failed");
1333
+ } finally {
1334
+ this.#c = { phase: "disposed", signal: AbortSignal.abort(W) };
1335
+ const h = this.#f;
1336
+ this.#f = void 0;
1337
+ try {
1338
+ h?.(this), this.#p === "root" && i.diagnostics.finishRoot();
1339
+ } finally {
1340
+ this.#t = void 0;
1341
+ }
1342
+ }
1343
+ })().then(s.resolve, s.reject);
1344
+ };
1345
+ return t.push({ lifetime: this, errors: n, binding: i, controller: e.controller, finish: a }), s.promise;
1346
+ }
1347
+ [F]() {
1171
1348
  return this.dispose();
1172
1349
  }
1173
- #c() {
1350
+ // The gate on every operation that creates a resource. `signal.aborted` is
1351
+ // checked as well as the phase, because a parent's disposal aborts this signal
1352
+ // during startup cancellation as well as disposal. Both boundaries close work.
1353
+ #d() {
1174
1354
  const t = this.#t;
1175
- if (this.#h.phase !== "active" || this.signal.aborted || !t)
1176
- throw E();
1355
+ if (this.#c.phase !== "active" || this.signal.aborted || !t)
1356
+ throw $();
1177
1357
  return t;
1178
1358
  }
1179
- #p() {
1359
+ #g() {
1180
1360
  const t = this.#t;
1181
- if (!t) throw E();
1361
+ if (!t) throw $();
1182
1362
  return t;
1183
1363
  }
1184
- #g() {
1185
- const t = this.#h;
1186
- if (t.phase !== "active") throw E();
1364
+ #w() {
1365
+ const t = this.#c;
1366
+ if (t.phase !== "active") throw $();
1187
1367
  return t.declarations;
1188
1368
  }
1189
1369
  }
1190
- class Ft {
1370
+ class oe {
1191
1371
  #t;
1192
1372
  constructor(t) {
1193
1373
  this.#t = t, Object.freeze(this);
@@ -1216,32 +1396,32 @@ class Ft {
1216
1396
  dispose() {
1217
1397
  return this.#t.dispose();
1218
1398
  }
1219
- [k]() {
1399
+ [F]() {
1220
1400
  return this.dispose();
1221
1401
  }
1222
1402
  }
1223
- function Gt(r) {
1403
+ function ae(r) {
1224
1404
  if (typeof r != "string" || !r.trim())
1225
1405
  throw new TypeError("Lifetime label must be a non-empty string");
1226
1406
  if (r !== r.trim())
1227
1407
  throw new TypeError("Lifetime label cannot start or end with whitespace");
1228
1408
  }
1229
- function E() {
1409
+ function $() {
1230
1410
  return new u("LIFETIME_DISPOSED", "Lifetime is disposing or has been disposed");
1231
1411
  }
1232
- class D extends AggregateError {
1412
+ class Y extends AggregateError {
1233
1413
  }
1234
- class Ut {
1414
+ class ce {
1235
1415
  #t;
1236
1416
  #e;
1237
1417
  #i;
1238
1418
  #s;
1239
1419
  #n = /* @__PURE__ */ new Map();
1240
- #a = new Dt();
1420
+ #a = new ee();
1241
1421
  #r;
1242
1422
  #o = [];
1243
1423
  constructor(t) {
1244
- this.#t = t.hostName, this.#e = t.logger, this.#i = t.isInstalled, this.#s = t.report, this.#r = new Lt(t.report);
1424
+ this.#t = t.hostName, this.#e = t.logger, this.#i = t.isInstalled, this.#s = t.report, this.#r = new Qt(t.report);
1245
1425
  }
1246
1426
  readService(t, e) {
1247
1427
  const i = this.#n.get(t);
@@ -1268,7 +1448,7 @@ class Ut {
1268
1448
  return this.deactivate(new Set(this.#o));
1269
1449
  }
1270
1450
  async activate(t, e, i, s) {
1271
- const n = this.#c(s);
1451
+ const n = this.#l(s);
1272
1452
  for (const o of t.layers) {
1273
1453
  const a = o.filter(
1274
1454
  (f) => e.has(f) && !f.instance
@@ -1277,35 +1457,39 @@ class Ut {
1277
1457
  const h = new AbortController(), c = await Promise.allSettled(
1278
1458
  a.map(async (f) => {
1279
1459
  try {
1280
- const p = i.has(f) ? i.get(f) : await et(
1281
- f.declaration.plugin.config,
1282
- f.declaration.config,
1283
- f.id
1284
- );
1285
- return await this.#d(
1460
+ if (!i.has(f))
1461
+ throw new Error(`Installation '${f.id}' has no prepared config`);
1462
+ const g = i.get(f);
1463
+ return await this.#h(
1286
1464
  t,
1287
1465
  f,
1288
- p,
1466
+ g,
1289
1467
  h.signal,
1290
1468
  n
1291
1469
  );
1292
- } catch (p) {
1293
- throw h.abort(p), p;
1470
+ } catch (g) {
1471
+ throw h.abort(g), g;
1294
1472
  }
1295
1473
  })
1296
- ), d = _t(c, h.signal), l = c.filter(
1474
+ ), d = he(c, h.signal), l = c.filter(
1297
1475
  (f) => f.status === "fulfilled"
1298
1476
  ).map((f) => f.value);
1299
1477
  if (d.length) {
1300
- const f = await this.#l(l), p = d.length === 1 ? d[0] : new AggregateError(d, "Installation startup layer failed");
1301
- throw f.length ? new D(
1302
- [p, ...f],
1478
+ const f = await this.#u(l), g = d.length === 1 ? d[0] : new AggregateError(d, "Installation startup layer failed");
1479
+ throw f.length ? new Y(
1480
+ [g, ...f],
1303
1481
  "Installation startup layer failed and could not be cleanly disposed"
1304
- ) : p;
1482
+ ) : g;
1305
1483
  }
1306
- this.#f(l);
1484
+ this.#p(l);
1307
1485
  }
1308
1486
  }
1487
+ /**
1488
+ * Stops in reverse activation order, sequentially, and collects every failure
1489
+ * instead of stopping at the first. A dependent must be gone before the
1490
+ * Service it consumed, and one Instance that refuses to shut down cleanly must
1491
+ * not leave the rest running — its error is returned, not thrown.
1492
+ */
1309
1493
  async deactivate(t) {
1310
1494
  const e = [], i = this.#o.filter((s) => t.has(s)).reverse();
1311
1495
  this.#o = this.#o.filter(
@@ -1326,6 +1510,17 @@ class Ut {
1326
1510
  }
1327
1511
  return e;
1328
1512
  }
1513
+ /**
1514
+ * Runs one operation with contribution publication deferred to its end, so a
1515
+ * change that withdraws and adds contributions is observed once rather than as
1516
+ * an intermediate state.
1517
+ *
1518
+ * Publication happens even when the operation failed, because contributions
1519
+ * withdrawn by Instances that did stop must not stay visible. If both the
1520
+ * operation and the publication fail, both errors are reported — the
1521
+ * publication failure must not hide the reason the change failed, and the
1522
+ * operation failure must not hide a store left inconsistent.
1523
+ */
1329
1524
  async withContributionBatch(t) {
1330
1525
  this.#r.beginBatch();
1331
1526
  let e;
@@ -1349,23 +1544,23 @@ class Ut {
1349
1544
  if (!i.ok) throw i.error;
1350
1545
  return e.value;
1351
1546
  }
1352
- async #d(t, e, i, s, n) {
1547
+ async #h(t, e, i, s, n) {
1353
1548
  e.deactivate();
1354
- const o = e.declaration.plugin, a = new B(n, e.id, { parentSignal: s });
1549
+ const o = t.declarationFor(e).plugin, a = new tt(n, e.id, { parentSignal: s });
1355
1550
  try {
1356
- const h = this.#u(t, e, o, a), c = Object.freeze({
1551
+ const h = this.#f(t, e, o, a), c = Object.freeze({
1357
1552
  hostName: this.#t,
1358
1553
  pluginName: o.name,
1359
1554
  installationId: e.id,
1360
1555
  groupId: e.groupId
1361
- }), d = this.#h(a, c, h), l = await o.setup(d, i), f = /* @__PURE__ */ new Map();
1362
- for (const [p, ot] of Object.entries(o.provides ?? {})) {
1363
- if (typeof l != "object" || l === null || !Object.hasOwn(l, p))
1556
+ }), d = this.#c(a, c, h), l = await o.setup(d, i), f = /* @__PURE__ */ new Map();
1557
+ for (const [g, I] of Object.entries(o.provides)) {
1558
+ if (typeof l != "object" || l === null || !Object.hasOwn(l, g))
1364
1559
  throw new u(
1365
1560
  "SERVICE_NOT_RETURNED",
1366
- `Installation '${e.id}' did not return provided Service '${p}'`
1561
+ `Installation '${e.id}' did not return provided Service '${g}'`
1367
1562
  );
1368
- f.set(ot.id, l[p]);
1563
+ f.set(I.id, l[g]);
1369
1564
  }
1370
1565
  return Object.freeze({
1371
1566
  installation: e,
@@ -1377,7 +1572,7 @@ class Ut {
1377
1572
  try {
1378
1573
  await a.dispose();
1379
1574
  } catch (d) {
1380
- throw new D(
1575
+ throw new Y(
1381
1576
  [c, d],
1382
1577
  `Installation '${e.id}' failed to start and could not be cleanly disposed`
1383
1578
  );
@@ -1385,13 +1580,13 @@ class Ut {
1385
1580
  throw c;
1386
1581
  }
1387
1582
  }
1388
- #f(t) {
1583
+ #p(t) {
1389
1584
  for (const { installation: e, instance: i, services: s } of t)
1390
1585
  this.#n.set(e, s), e.activate(i), this.#o.push(e);
1391
1586
  for (const { instance: e } of t)
1392
1587
  e.lifetime.publish(), e.lifetime.detachStartupSignal();
1393
1588
  }
1394
- async #l(t) {
1589
+ async #u(t) {
1395
1590
  const e = [];
1396
1591
  for (const i of [...t].reverse())
1397
1592
  try {
@@ -1401,9 +1596,9 @@ class Ut {
1401
1596
  }
1402
1597
  return e;
1403
1598
  }
1404
- #u(t, e, i, s) {
1599
+ #f(t, e, i, s) {
1405
1600
  const n = /* @__PURE__ */ Object.create(null);
1406
- for (const [o, a] of Object.entries(i.requires ?? {}))
1601
+ for (const [o, a] of Object.entries(i.requires))
1407
1602
  if (a.kind === "optional") {
1408
1603
  const h = t.providerFor(e, a.service.id);
1409
1604
  if (!h) {
@@ -1426,10 +1621,10 @@ class Ut {
1426
1621
  );
1427
1622
  n[o] = c.get(a.id);
1428
1623
  } else
1429
- n[o] = this.#b(a, s);
1624
+ n[o] = this.#m(a, s);
1430
1625
  return n;
1431
1626
  }
1432
- #h(t, e, i) {
1627
+ #c(t, e, i) {
1433
1628
  return Object.freeze({
1434
1629
  ...i,
1435
1630
  get signal() {
@@ -1445,13 +1640,13 @@ class Ut {
1445
1640
  contribute: t.contribute.bind(t)
1446
1641
  });
1447
1642
  }
1448
- #c(t) {
1643
+ #l(t) {
1449
1644
  return {
1450
- stageOn: (e, i, s, n) => this.#p(e, i, s, n, t),
1645
+ stageOn: (e, i, s, n) => this.#d(e, i, s, n, t),
1451
1646
  emit: (e, i, s) => this.#g(e, i, s, t),
1452
- stageContribution: (e, i, s, n, o) => this.#v(e, i, s, n, o, t),
1647
+ stageContribution: (e, i, s, n, o) => this.#w(e, i, s, n, o, t),
1453
1648
  writeLog: (e, i, s, n) => {
1454
- P(
1649
+ R(
1455
1650
  this.#e[e](i, s, ...n),
1456
1651
  "Logger methods must be synchronous"
1457
1652
  );
@@ -1459,43 +1654,43 @@ class Ut {
1459
1654
  report: this.#s
1460
1655
  };
1461
1656
  }
1462
- #p(t, e, i, s, n) {
1463
- return this.#w(t), m(e, "event"), n.remember(e), this.#a.stage(e.id, i, s);
1657
+ #d(t, e, i, s, n) {
1658
+ return this.#b(t), A(e, "event"), n.remember(e), this.#a.stage(e.id, i, s);
1464
1659
  }
1465
1660
  #g(t, e, i, s) {
1466
- return this.#w(t), m(e, "event"), s.remember(e), this.#a.emit(e.id, i);
1661
+ return this.#b(t), A(e, "event"), s.remember(e), this.#a.emit(e.id, i);
1467
1662
  }
1468
- #v(t, e, i, s, n, o) {
1469
- return this.#w(t), m(e, "extensionPoint"), o.remember(e), this.#r.get(e).stage(t, i, s, n);
1663
+ #w(t, e, i, s, n, o) {
1664
+ return this.#b(t), A(e, "extensionPoint"), o.remember(e), this.#r.get(e).stage(t, i, s, n);
1470
1665
  }
1471
- #b(t, e) {
1666
+ #m(t, e) {
1472
1667
  return this.#r.get(t).view((i, s) => e.ownLease(i, s));
1473
1668
  }
1474
- #w(t) {
1669
+ #b(t) {
1475
1670
  if (!this.#i(t))
1476
1671
  throw new Error(`Installation '${t}' is not installed`);
1477
1672
  }
1478
1673
  }
1479
- function _t(r, t) {
1674
+ function he(r, t) {
1480
1675
  const e = [];
1481
1676
  let i = !1;
1482
1677
  for (const s of r)
1483
- s.status !== "fulfilled" && (Object.is(s.reason, t.reason) ? (i || e.push(s.reason), i = !0) : tt(t, s.reason) || e.push(s.reason));
1678
+ s.status !== "fulfilled" && (Object.is(s.reason, t.reason) ? (i || e.push(s.reason), i = !0) : wt(t, s.reason) || e.push(s.reason));
1484
1679
  return e;
1485
1680
  }
1486
- class Ht {
1487
- #t = new gt();
1681
+ class de {
1682
+ #t = new zt();
1488
1683
  #e;
1489
1684
  #i;
1490
1685
  constructor(t) {
1491
- this.#e = new Ut(t);
1686
+ this.#e = new ce(t);
1492
1687
  }
1493
1688
  get hasCommittedPlan() {
1494
1689
  return this.#i !== void 0;
1495
1690
  }
1496
1691
  get(t, e) {
1497
- const i = ht(t), s = i ? t.service : t;
1498
- if (m(s, "service"), this.#t.assertCompatible(s), e === "unavailable") throw H();
1692
+ const i = jt(t), s = i ? t.service : t;
1693
+ if (A(s, "service"), this.#t.assertCompatible(s), e === "unavailable") throw ct();
1499
1694
  const n = this.#o().provider(s.id);
1500
1695
  if (!n) {
1501
1696
  if (i) return;
@@ -1509,13 +1704,13 @@ class Ht {
1509
1704
  return o.value;
1510
1705
  }
1511
1706
  contributions(t) {
1512
- return m(t, "extensionPoint"), this.#t.remember(t), this.#e.contributions(t);
1707
+ return A(t, "extensionPoint"), this.#t.remember(t), this.#e.contributions(t);
1513
1708
  }
1514
1709
  buildPlan(t) {
1515
- return M.build(t, this.#t.kinds);
1710
+ return X.build(t, this.#t.kinds);
1516
1711
  }
1517
1712
  async start(t) {
1518
- const e = this.#t.draft(t.contractKinds);
1713
+ const e = this.#t.writer(t.contractKinds);
1519
1714
  try {
1520
1715
  await this.#e.withContributionBatch(() => this.#s(t, e)), this.#i = t;
1521
1716
  } catch (i) {
@@ -1528,14 +1723,29 @@ class Ht {
1528
1723
  );
1529
1724
  return this.#i = void 0, t;
1530
1725
  }
1726
+ /**
1727
+ * The whole transaction, in the order the steps have to happen.
1728
+ *
1729
+ * Configs resolve and Contract identities draft *before* anything stops,
1730
+ * because those are the failures that can still be taken back for free — a
1731
+ * schema rejection at this point costs nothing. Once the affected Instances
1732
+ * are down, every remaining failure has a price, and the only question left is
1733
+ * whether the previous state can be restored.
1734
+ *
1735
+ * The whole body runs inside one contribution batch, so contributions
1736
+ * withdrawn by stopping Instances and added by starting ones become visible in
1737
+ * a single step. Without it, an ExtensionPoint observer would see the set
1738
+ * briefly empty in the middle of a successful change.
1739
+ */
1531
1740
  async transition(t, e, i) {
1532
1741
  return this.#e.withContributionBatch(async () => {
1533
1742
  const s = this.#o(), n = s.affectedByTransitionTo(t, e);
1534
1743
  let o, a;
1535
1744
  try {
1536
1745
  o = await this.#r(
1746
+ t,
1537
1747
  t.order.filter((d) => n.has(d))
1538
- ), a = this.#t.draft(t.contractKinds);
1748
+ ), a = this.#t.writer(t.contractKinds);
1539
1749
  } catch (d) {
1540
1750
  throw i(), d;
1541
1751
  }
@@ -1550,7 +1760,7 @@ class Ht {
1550
1760
  return await this.#e.activate(t, n, o, a), a.commit(), this.#e.commitActivationOrder(t.order), this.#i = t, Object.freeze({ kind: "committed", affected: n });
1551
1761
  } catch (d) {
1552
1762
  const l = await this.#e.deactivate(n);
1553
- return a.discard(), d instanceof D || l.length ? this.#n(
1763
+ return a.discard(), d instanceof Y || l.length ? this.#n(
1554
1764
  i,
1555
1765
  [d, ...l],
1556
1766
  "Installation change failed and its partial activation could not be cleanly disposed"
@@ -1562,7 +1772,7 @@ class Ht {
1562
1772
  });
1563
1773
  }
1564
1774
  async #s(t, e) {
1565
- const i = new Set(t.order), s = await this.#r(t.order);
1775
+ const i = new Set(t.order), s = await this.#r(t, t.order);
1566
1776
  this.#e.resetActivationState();
1567
1777
  try {
1568
1778
  await this.#e.activate(t, i, s, e), e.commit(), this.#e.commitActivationOrder(t.order);
@@ -1571,14 +1781,30 @@ class Ht {
1571
1781
  throw o.length ? new AggregateError([n, ...o], "Host startup failed") : n;
1572
1782
  }
1573
1783
  }
1784
+ /**
1785
+ * The last level: no consistent graph could be reached, so none is presented.
1786
+ *
1787
+ * Everything stops and `#plan` is cleared, which drops the Host to `idle` and
1788
+ * closes reads. The AggregateError carries every cause — the original failure
1789
+ * first, then whatever shutdown itself hit. Nothing is swallowed here; the
1790
+ * point of failing closed is that the report is complete.
1791
+ */
1574
1792
  async #n(t, e, i) {
1575
1793
  t();
1576
1794
  const s = await this.#e.deactivateAll();
1577
1795
  throw this.#i = void 0, new AggregateError([...e, ...s], i);
1578
1796
  }
1797
+ /**
1798
+ * Restores the previous plan using the configs captured before the change, not
1799
+ * mutable records. Declaration versions belong to the plan; re-validating
1800
+ * configs could fail for a second,
1801
+ * unrelated reason while trying to recover from the first.
1802
+ *
1803
+ * A rollback that itself fails escalates to fail-closed by throwing.
1804
+ */
1579
1805
  async #a(t, e, i, s, n) {
1580
1806
  t();
1581
- const o = this.#t.draft(e.contractKinds);
1807
+ const o = this.#t.writer(e.contractKinds);
1582
1808
  try {
1583
1809
  await this.#e.activate(e, i, s, o), o.commit(), this.#e.commitActivationOrder(e.order), this.#i = e;
1584
1810
  } catch (h) {
@@ -1591,48 +1817,47 @@ class Ht {
1591
1817
  const a = n.length === 1 ? n[0] : new AggregateError(n, "Installation change failed");
1592
1818
  return Object.freeze({ kind: "rolled-back", affected: i, error: a });
1593
1819
  }
1594
- async #r(t) {
1595
- const e = /* @__PURE__ */ new Map();
1596
- for (const i of t)
1820
+ async #r(t, e) {
1821
+ const i = /* @__PURE__ */ new Map();
1822
+ for (const s of e)
1597
1823
  try {
1598
- e.set(
1599
- i,
1600
- await et(
1601
- i.declaration.plugin.config,
1602
- i.declaration.config,
1603
- i.id
1604
- )
1605
- );
1606
- } catch (s) {
1607
- throw $(
1824
+ const n = t.declarationFor(s);
1825
+ i.set(
1608
1826
  s,
1827
+ await Lt(n.plugin.config, n.config, s.id)
1828
+ );
1829
+ } catch (n) {
1830
+ throw V(
1831
+ n,
1609
1832
  "INSTALLATION_UNAVAILABLE",
1610
- `Installation '${i.id}' failed with a non-Error value`
1833
+ `Installation '${s.id}' failed with a non-Error value`
1611
1834
  );
1612
1835
  }
1613
- return e;
1836
+ return i;
1614
1837
  }
1615
1838
  #o() {
1616
- if (!this.#i) throw H();
1839
+ if (!this.#i) throw ct();
1617
1840
  return this.#i;
1618
1841
  }
1619
1842
  }
1620
- function H() {
1843
+ function ct() {
1621
1844
  return new u("SERVICE_UNAVAILABLE", "Host services are not active");
1622
1845
  }
1623
- class Kt {
1846
+ class le {
1624
1847
  #t;
1625
1848
  #e;
1626
- #i;
1627
- #s = 0;
1849
+ #i = 0;
1628
1850
  view;
1629
1851
  constructor(t, e, i) {
1630
- this.#t = t, this.#i = this.#n("idle", [], e), this.#e = new q(() => this.#i, i), this.view = this.#e.view;
1852
+ this.#t = t, this.#e = new Z(() => {
1853
+ const { status: s, installations: n, groups: o } = e();
1854
+ return this.#s(s, n, o);
1855
+ }, i), this.view = this.#e.view;
1631
1856
  }
1632
- publish(t, e, i) {
1633
- this.#s++, this.#i = this.#n(t, e, i), this.#e.invalidate();
1857
+ publish() {
1858
+ this.#i++, this.#e.invalidate();
1634
1859
  }
1635
- #n(t, e, i) {
1860
+ #s(t, e, i) {
1636
1861
  const s = /* @__PURE__ */ new Map();
1637
1862
  for (const o of e) {
1638
1863
  const a = {
@@ -1641,10 +1866,10 @@ class Kt {
1641
1866
  groupId: o.groupId,
1642
1867
  status: o.status,
1643
1868
  requires: Object.freeze(
1644
- Object.values(o.declaration.plugin.requires ?? {}).map((c) => c.kind === "optional" ? c.service.id : c.id)
1869
+ Object.values(o.declaration.plugin.requires).map((c) => c.kind === "optional" ? c.service.id : c.id)
1645
1870
  ),
1646
1871
  provides: Object.freeze(
1647
- Object.values(o.declaration.plugin.provides ?? {}).map((c) => c.id)
1872
+ Object.values(o.declaration.plugin.provides).map((c) => c.id)
1648
1873
  ),
1649
1874
  ...o.instance ? { lifetime: o.instance.lifetime.diagnostics } : {}
1650
1875
  }, h = o.error;
@@ -1664,32 +1889,32 @@ class Kt {
1664
1889
  return Object.freeze({
1665
1890
  name: this.#t,
1666
1891
  status: t,
1667
- revision: this.#s,
1668
- installations: new O(s),
1669
- groups: new O(n)
1892
+ revision: this.#i,
1893
+ installations: new z(s),
1894
+ groups: new z(n)
1670
1895
  });
1671
1896
  }
1672
1897
  }
1673
- function L(r, t, e = {}) {
1898
+ function B(r, t, e = {}) {
1674
1899
  if (!r || typeof r != "object" || Array.isArray(r))
1675
- throw b(e, `${t} must be a plain record`);
1900
+ throw O(e, `${t} must be a plain record`);
1676
1901
  const i = Object.getPrototypeOf(r);
1677
1902
  if (i !== Object.prototype && i !== null)
1678
- throw b(e, `${t} must be a plain record`);
1903
+ throw O(e, `${t} must be a plain record`);
1679
1904
  for (const s of Reflect.ownKeys(r)) {
1680
1905
  const n = Reflect.getOwnPropertyDescriptor(r, s);
1681
1906
  if (typeof s != "string" || !n?.enumerable)
1682
- throw b(e, `${t} keys must be enumerable strings`);
1907
+ throw O(e, `${t} keys must be enumerable strings`);
1683
1908
  if (!("value" in n))
1684
- throw b(e, `${t} field '${s}' must be a data property`);
1909
+ throw O(e, `${t} field '${s}' must be a data property`);
1685
1910
  if (e.fields && !e.fields.has(s))
1686
- throw b(e, `${t}: unknown field '${s}'`);
1911
+ throw O(e, `${t}: unknown field '${s}'`);
1687
1912
  }
1688
1913
  }
1689
- function b(r, t) {
1914
+ function O(r, t) {
1690
1915
  return r.createError?.(t) ?? new TypeError(t);
1691
1916
  }
1692
- const Wt = /* @__PURE__ */ new Set([
1917
+ const ue = /* @__PURE__ */ new Set([
1693
1918
  "signal",
1694
1919
  "meta",
1695
1920
  "log",
@@ -1699,12 +1924,12 @@ const Wt = /* @__PURE__ */ new Set([
1699
1924
  "on",
1700
1925
  "emit",
1701
1926
  "contribute"
1702
- ]), Yt = /* @__PURE__ */ new Set(["name", "config", "requires", "provides", "setup"]);
1703
- function ve(r) {
1704
- return nt(r);
1927
+ ]), fe = /* @__PURE__ */ new Set(["name", "config", "requires", "provides", "setup"]);
1928
+ function Ne(r) {
1929
+ return Ct(r);
1705
1930
  }
1706
- function nt(r) {
1707
- Qt(r);
1931
+ function Ct(r) {
1932
+ pe(r);
1708
1933
  const t = Object.hasOwn(r, "name") ? r.name : void 0, e = Object.hasOwn(r, "config") ? r.config : void 0, i = Object.hasOwn(r, "requires") ? r.requires : void 0, s = Object.hasOwn(r, "provides") ? r.provides : void 0, n = Object.hasOwn(r, "setup") ? r.setup : void 0;
1709
1934
  if (typeof t != "string" || !t.trim())
1710
1935
  throw new TypeError("Plugin name must be a non-empty string");
@@ -1712,24 +1937,27 @@ function nt(r) {
1712
1937
  throw new TypeError("Plugin name cannot start or end with whitespace");
1713
1938
  if (typeof n != "function")
1714
1939
  throw new TypeError(`Plugin '${t}' must define setup()`);
1715
- if (e !== void 0 && !Jt(e))
1940
+ if (e !== void 0 && !ge(e))
1716
1941
  throw new TypeError(`Plugin '${t}' config must implement Standard Schema V1`);
1717
- K(i, t, "requires"), K(s, t, "provides");
1718
- const o = i ? Object.freeze({ ...i }) : void 0, a = s ? Object.freeze({ ...s }) : void 0, h = /* @__PURE__ */ new Map();
1719
- for (const [c, d] of Object.entries(o ?? {})) {
1942
+ ht(i, t, "requires"), ht(s, t, "provides");
1943
+ const o = /* @__PURE__ */ Object.create(null), a = /* @__PURE__ */ Object.create(null), h = /* @__PURE__ */ new Map();
1944
+ for (const [c, d] of Object.entries(i ?? {})) {
1720
1945
  if (!c.trim()) throw new TypeError("Plugin requirement alias cannot be empty");
1721
1946
  if (c !== c.trim())
1722
1947
  throw new TypeError("Plugin requirement alias cannot start or end with whitespace");
1723
- if (Wt.has(c))
1948
+ if (ue.has(c))
1724
1949
  throw new TypeError(`Plugin requirement '${c}' conflicts with the context API`);
1725
1950
  if (!d || typeof d != "object")
1726
1951
  throw new TypeError(`Plugin requirement '${c}' is not a contract`);
1727
1952
  if (d.kind === "optional") {
1728
- if (!v(d.service, "service"))
1953
+ if (!E(d.service, "service"))
1729
1954
  throw new TypeError(`Optional requirement '${c}' must wrap a Service`);
1730
- } else if (!v(d, "service") && !v(d, "extensionPoint"))
1955
+ } else if (!E(d, "service") && !E(d, "extensionPoint"))
1731
1956
  throw new TypeError(`Plugin requirement '${c}' must be a Service or ExtensionPoint`);
1732
- W(
1957
+ o[c] = d.kind === "optional" ? Object.freeze({
1958
+ kind: "optional",
1959
+ service: _(d.service)
1960
+ }) : _(d), dt(
1733
1961
  t,
1734
1962
  h,
1735
1963
  "requirement",
@@ -1737,36 +1965,41 @@ function nt(r) {
1737
1965
  d.kind === "optional" ? d.service : d
1738
1966
  );
1739
1967
  }
1740
- for (const [c, d] of Object.entries(a ?? {})) {
1968
+ for (const [c, d] of Object.entries(s ?? {})) {
1741
1969
  if (!c.trim()) throw new TypeError("Plugin provision alias cannot be empty");
1742
1970
  if (c !== c.trim())
1743
1971
  throw new TypeError("Plugin provision alias cannot start or end with whitespace");
1744
- if (!v(d, "service"))
1972
+ if (c === "then")
1973
+ throw new TypeError("Plugin provision alias 'then' conflicts with the Promise protocol");
1974
+ if (!E(d, "service"))
1745
1975
  throw new TypeError(`Plugin provision '${c}' must be a Service`);
1746
- W(t, h, "provision", c, d);
1976
+ a[c] = _(d), dt(t, h, "provision", c, a[c]);
1747
1977
  }
1748
1978
  return Object.freeze({
1749
1979
  name: t,
1750
1980
  ...e === void 0 ? {} : { config: e },
1751
- ...o ? { requires: o } : {},
1752
- ...a ? { provides: a } : {},
1981
+ requires: Object.freeze(o),
1982
+ provides: Object.freeze(a),
1753
1983
  setup: n
1754
1984
  });
1755
1985
  }
1756
- function Qt(r) {
1757
- L(r, "Plugin declaration", { fields: Yt });
1986
+ function _(r) {
1987
+ return Object.freeze({ id: r.id, kind: r.kind });
1758
1988
  }
1759
- function Jt(r) {
1989
+ function pe(r) {
1990
+ B(r, "Plugin declaration", { fields: fe });
1991
+ }
1992
+ function ge(r) {
1760
1993
  if (!r || typeof r != "object" && typeof r != "function") return !1;
1761
1994
  const t = r["~standard"];
1762
1995
  if (!t || typeof t != "object") return !1;
1763
1996
  const e = t;
1764
1997
  return e.version === 1 && typeof e.vendor == "string" && typeof e.validate == "function";
1765
1998
  }
1766
- function K(r, t, e) {
1767
- r !== void 0 && L(r, `Plugin '${t}' ${e}`);
1999
+ function ht(r, t, e) {
2000
+ r !== void 0 && B(r, `Plugin '${t}' ${e}`);
1768
2001
  }
1769
- function W(r, t, e, i, s) {
2002
+ function dt(r, t, e, i, s) {
1770
2003
  const n = t.get(s.id);
1771
2004
  if (!n) {
1772
2005
  t.set(s.id, { alias: i, kind: s.kind, role: e });
@@ -1774,35 +2007,35 @@ function W(r, t, e, i, s) {
1774
2007
  }
1775
2008
  if (n.kind !== s.kind)
1776
2009
  throw new TypeError(
1777
- `Plugin '${r}' uses Contract '${s.id}' as both ${R(n.kind)} and ${R(s.kind)}`
2010
+ `Plugin '${r}' uses Contract '${s.id}' as both ${H(n.kind)} and ${H(s.kind)}`
1778
2011
  );
1779
- const o = R(s.kind);
2012
+ const o = H(s.kind);
1780
2013
  throw n.role !== e ? new TypeError(
1781
2014
  `Plugin '${r}' cannot both require and provide ${o} '${s.id}'`
1782
2015
  ) : new TypeError(
1783
2016
  `Plugin '${r}' ${e} aliases '${n.alias}' and '${i}' reference the same ${o} '${s.id}'`
1784
2017
  );
1785
2018
  }
1786
- function R(r) {
2019
+ function H(r) {
1787
2020
  return r === "extensionPoint" ? "ExtensionPoint" : r === "event" ? "Event" : "Service";
1788
2021
  }
1789
- function Y(r) {
1790
- return nt(r);
2022
+ function lt(r) {
2023
+ return Ct(r);
1791
2024
  }
1792
- const Xt = /* @__PURE__ */ new Set(["plugin", "config"]), I = /* @__PURE__ */ new WeakMap();
1793
- function Zt(r, t) {
1794
- I.get(r)?.discard(t);
2025
+ const we = /* @__PURE__ */ new Set(["plugin", "config"]), x = /* @__PURE__ */ new WeakMap();
2026
+ function be(r, t) {
2027
+ x.get(r)?.discard(t);
1795
2028
  }
1796
- function te(r, t, e, i) {
1797
- const s = I.get(r);
2029
+ function me(r, t, e, i) {
2030
+ const s = x.get(r);
1798
2031
  if (!s) throw new Error("ChangeSet draft control has been released");
1799
2032
  return s.install(t, e, i);
1800
2033
  }
1801
- class ee {
2034
+ class ve {
1802
2035
  #t = /* @__PURE__ */ new Map();
1803
2036
  #e;
1804
2037
  constructor(t) {
1805
- this.#e = { phase: "open", port: t }, I.set(this, {
2038
+ this.#e = { phase: "open", port: t }, x.set(this, {
1806
2039
  discard: (e) => this.#s(e),
1807
2040
  install: (e, i, s) => this.#i(e, i, s)
1808
2041
  }), Object.freeze(this);
@@ -1811,12 +2044,12 @@ class ee {
1811
2044
  return this.#i(t, e[0]);
1812
2045
  }
1813
2046
  #i(t, e, i) {
1814
- const s = this.#a(), n = Y(t), o = (i ?? s.create)(n, e);
2047
+ const s = this.#a(), n = lt(t), o = (i ?? s.create)(n, e);
1815
2048
  return this.#n({ kind: "install", installation: o.record }), o.facade;
1816
2049
  }
1817
2050
  update(t, e) {
1818
2051
  const i = this.#a();
1819
- L(e, "Installation update", { fields: Xt });
2052
+ B(e, "Installation update", { fields: we });
1820
2053
  const s = Object.hasOwn(e, "plugin"), n = Object.hasOwn(e, "config");
1821
2054
  if (!s && !n)
1822
2055
  throw new TypeError("Installation update must include 'plugin' or 'config'");
@@ -1825,7 +2058,7 @@ class ee {
1825
2058
  if (s) {
1826
2059
  const d = e.plugin;
1827
2060
  if (!d) throw new TypeError("Installation update 'plugin' must be a Plugin");
1828
- a = Y(d);
2061
+ a = lt(d);
1829
2062
  }
1830
2063
  let h;
1831
2064
  a && n ? h = { kind: "plugin-and-config", plugin: a, config: e.config } : a ? h = { kind: "plugin", plugin: a } : h = { kind: "config", config: e.config };
@@ -1836,6 +2069,12 @@ class ee {
1836
2069
  const e = this.#a();
1837
2070
  this.#n({ kind: "remove", installation: e.resolve(t) });
1838
2071
  }
2072
+ /**
2073
+ * Commit is idempotent by returning the same promise, so two callers awaiting
2074
+ * one draft get one transaction rather than two. `committing` exists to catch
2075
+ * the reentrant case — an `attach` hook that calls `commit()` again — which
2076
+ * would otherwise submit the operation list twice.
2077
+ */
1839
2078
  commit() {
1840
2079
  const t = this.#e;
1841
2080
  if (t.phase === "submitted") return t.promise;
@@ -1870,6 +2109,10 @@ class ee {
1870
2109
  for (const s of i)
1871
2110
  s.kind === "install" && e.port.discard(s.installation, t);
1872
2111
  }
2112
+ // One operation per Installation per ChangeSet. Allowing two would make the
2113
+ // outcome depend on staging order — `update` then `remove` versus the reverse —
2114
+ // and there is no reading of "both" that is obviously right. The caller has to
2115
+ // say which one it meant.
1873
2116
  #n(t) {
1874
2117
  if (this.#t.has(t.installation))
1875
2118
  throw new TypeError(
@@ -1887,55 +2130,10 @@ class ee {
1887
2130
  return this.#e = { phase: "submitted", promise: t }, this.#o(), t;
1888
2131
  }
1889
2132
  #o() {
1890
- this.#t.clear(), I.delete(this);
2133
+ this.#t.clear(), x.delete(this);
1891
2134
  }
1892
2135
  }
1893
- class ie {
1894
- #t;
1895
- constructor(t, e, i) {
1896
- this.#t = { phase: "open", draft: t, discard: e, normalize: i };
1897
- }
1898
- get failure() {
1899
- return this.#t.phase === "failed" ? this.#t.error : void 0;
1900
- }
1901
- requireDraft() {
1902
- const t = this.#t;
1903
- if (t.phase === "failed") throw t.error;
1904
- if (t.phase === "sealed") throw N();
1905
- return t.draft;
1906
- }
1907
- assertOpen() {
1908
- this.requireDraft();
1909
- }
1910
- fail(t) {
1911
- const e = this.#t;
1912
- if (e.phase === "failed") return e.error;
1913
- if (e.phase === "sealed") throw N();
1914
- const i = e.normalize(t);
1915
- return this.#t = {
1916
- phase: "failed",
1917
- draft: e.draft,
1918
- discard: e.discard,
1919
- error: i
1920
- }, i;
1921
- }
1922
- seal() {
1923
- const t = this.#t;
1924
- if (t.phase === "failed") throw t.error;
1925
- if (t.phase === "sealed") throw N();
1926
- return this.#t = { phase: "sealed" }, t.draft;
1927
- }
1928
- discard(t) {
1929
- const e = this.#t;
1930
- if (e.phase === "sealed") return;
1931
- const i = e.phase === "failed" ? e.error : e.normalize(t);
1932
- this.#t = { phase: "sealed" }, e.discard(e.draft, i);
1933
- }
1934
- }
1935
- function N() {
1936
- return new Error("Group configuration has been sealed");
1937
- }
1938
- class A {
2136
+ class M {
1939
2137
  constructor(t, e, i) {
1940
2138
  this.id = t, this.name = e, this.#e = { phase: "attached", parent: i };
1941
2139
  }
@@ -1944,7 +2142,7 @@ class A {
1944
2142
  #t = /* @__PURE__ */ new Map();
1945
2143
  #e;
1946
2144
  static root(t) {
1947
- return new A("/", t, void 0);
2145
+ return new M("/", t, void 0);
1948
2146
  }
1949
2147
  get attached() {
1950
2148
  return this.#e.phase !== "detached";
@@ -1957,21 +2155,24 @@ class A {
1957
2155
  return t.phase === "detached" ? void 0 : t.parent;
1958
2156
  }
1959
2157
  create(t) {
1960
- if (this.assertAttached(), se(t), this.#t.has(t))
2158
+ if (this.assertAttached(), ye(t), this.#t.has(t))
1961
2159
  throw new TypeError(`Group '${this.#i(t)}' already exists`);
1962
- const e = new A(this.#i(t), t, this);
2160
+ const e = new M(this.#i(t), t, this);
1963
2161
  return this.#t.set(t, e), e;
1964
2162
  }
1965
2163
  contains(t) {
1966
- if (t === this) return !0;
1967
- for (const e of this.#t.values())
1968
- if (e.contains(t)) return !0;
2164
+ for (let e = t; e; e = e.parent)
2165
+ if (e === this) return !0;
1969
2166
  return !1;
1970
2167
  }
1971
2168
  assertAttached() {
1972
2169
  if (this.#e.phase === "detached")
1973
2170
  throw new TypeError(`Group '${this.id}' has been removed`);
1974
2171
  }
2172
+ // Depth-first, and children are detached before this node unlinks itself from
2173
+ // its parent. A node that unlinked first would leave its subtree unreachable
2174
+ // but still attached, so those groups would report themselves as live with no
2175
+ // path back to the root.
1975
2176
  detach() {
1976
2177
  const t = this.#e;
1977
2178
  if (t.phase !== "detached") {
@@ -1991,14 +2192,59 @@ class A {
1991
2192
  return this.id === "/" ? `/${t}` : `${this.id}/${t}`;
1992
2193
  }
1993
2194
  }
1994
- function se(r) {
2195
+ function ye(r) {
1995
2196
  if (typeof r != "string" || !r.trim())
1996
2197
  throw new TypeError("Group name must be a non-empty string");
1997
2198
  if (r !== r.trim())
1998
2199
  throw new TypeError("Group name cannot start or end with whitespace");
1999
2200
  if (r.includes("/")) throw new TypeError("Group name cannot contain '/'");
2000
2201
  }
2001
- class Q {
2202
+ class Ee {
2203
+ #t;
2204
+ constructor(t, e, i) {
2205
+ this.#t = { phase: "open", draft: t, discard: e, normalize: i };
2206
+ }
2207
+ get failure() {
2208
+ return this.#t.phase === "failed" ? this.#t.error : void 0;
2209
+ }
2210
+ requireDraft() {
2211
+ const t = this.#t;
2212
+ if (t.phase === "failed") throw t.error;
2213
+ if (t.phase === "sealed") throw K();
2214
+ return t.draft;
2215
+ }
2216
+ assertOpen() {
2217
+ this.requireDraft();
2218
+ }
2219
+ fail(t) {
2220
+ const e = this.#t;
2221
+ if (e.phase === "failed") return e.error;
2222
+ if (e.phase === "sealed") throw K();
2223
+ const i = e.normalize(t);
2224
+ return this.#t = {
2225
+ phase: "failed",
2226
+ draft: e.draft,
2227
+ discard: e.discard,
2228
+ error: i
2229
+ }, i;
2230
+ }
2231
+ seal() {
2232
+ const t = this.#t;
2233
+ if (t.phase === "failed") throw t.error;
2234
+ if (t.phase === "sealed") throw K();
2235
+ return this.#t = { phase: "sealed" }, t.draft;
2236
+ }
2237
+ discard(t) {
2238
+ const e = this.#t;
2239
+ if (e.phase === "sealed") return;
2240
+ const i = e.phase === "failed" ? e.error : e.normalize(t);
2241
+ this.#t = { phase: "sealed" }, e.discard(e.draft, i);
2242
+ }
2243
+ }
2244
+ function K() {
2245
+ return new Error("Group configuration has been sealed");
2246
+ }
2247
+ class ut {
2002
2248
  constructor(t, e, i) {
2003
2249
  this.node = t, this.#t = { phase: e }, this.#e = i;
2004
2250
  }
@@ -2018,6 +2264,19 @@ class Q {
2018
2264
  if (i.phase === "failed") throw i.error;
2019
2265
  await t();
2020
2266
  }
2267
+ /**
2268
+ * `preserveCommittedState` is the whole point of this class.
2269
+ *
2270
+ * A Group that has already committed something is established, and a later
2271
+ * failed change does not un-establish it — the transaction rolled back, so the
2272
+ * state the Group describes is still the one that was committed. Such a
2273
+ * failure leaves the phase at `established` and does not rethrow, because the
2274
+ * caller of the failed change already has the error.
2275
+ *
2276
+ * A Group whose *first* change fails never had a committed state, so it does
2277
+ * become `failed` and the failure propagates: there is nothing to report but
2278
+ * the failure.
2279
+ */
2021
2280
  track(t) {
2022
2281
  this.node.assertAttached();
2023
2282
  const e = this.#t, i = e.phase === "established" || e.phase === "pending" && e.baseline === "established", s = {}, n = t.then(
@@ -2025,7 +2284,7 @@ class Q {
2025
2284
  this.#i(s) && (this.#t = { phase: "established" }, this.#e?.());
2026
2285
  },
2027
2286
  (o) => {
2028
- const a = $(
2287
+ const a = V(
2029
2288
  o,
2030
2289
  "GROUP_UNAVAILABLE",
2031
2290
  `Group '${this.node.id}' operation failed with a non-Error value`
@@ -2049,15 +2308,15 @@ class Q {
2049
2308
  return e.phase === "pending" && e.attempt === t;
2050
2309
  }
2051
2310
  }
2052
- function T(r) {
2311
+ function D(r) {
2053
2312
  return new u("GROUP_REMOVED", `Group '${r.id}' has been removed`);
2054
2313
  }
2055
- const C = /* @__PURE__ */ new WeakMap();
2056
- class re {
2314
+ const P = /* @__PURE__ */ new WeakMap();
2315
+ class Ce {
2057
2316
  #t;
2058
2317
  #e;
2059
2318
  constructor(t, e, i) {
2060
- this.#t = e, this.#e = i ? { phase: "configuring", coordinator: t, configuration: i } : { phase: "attached", coordinator: t }, C.set(this, {
2319
+ this.#t = e, this.#e = i ? { phase: "configuring", coordinator: t, configuration: i } : { phase: "attached", coordinator: t }, P.set(this, {
2061
2320
  finishConfiguration: () => {
2062
2321
  const s = this.#e;
2063
2322
  s.phase === "configuring" && (this.#e = { phase: "attached", coordinator: s.coordinator });
@@ -2079,7 +2338,7 @@ class re {
2079
2338
  }
2080
2339
  ready() {
2081
2340
  const t = this.#e;
2082
- return t.phase === "revoked" ? Promise.reject(T(this.#t)) : t.coordinator.ready(this.#t);
2341
+ return t.phase === "revoked" ? Promise.reject(D(this.#t)) : t.coordinator.ready(this.#t);
2083
2342
  }
2084
2343
  install(t, ...e) {
2085
2344
  const i = this.#e;
@@ -2112,19 +2371,19 @@ class re {
2112
2371
  }
2113
2372
  #i() {
2114
2373
  const t = this.#e;
2115
- if (t.phase === "revoked") throw T(this.#t);
2374
+ if (t.phase === "revoked") throw D(this.#t);
2116
2375
  return t.coordinator;
2117
2376
  }
2118
2377
  }
2119
- class ne {
2378
+ class Se {
2120
2379
  root;
2121
2380
  #t;
2122
2381
  #e = /* @__PURE__ */ new WeakMap();
2123
2382
  #i = /* @__PURE__ */ new WeakMap();
2124
2383
  constructor(t, e) {
2125
- this.root = A.root(t), this.#t = e, this.#i.set(
2384
+ this.root = M.root(t), this.#t = e, this.#i.set(
2126
2385
  this.root,
2127
- new Q(this.root, "established", () => e.notifyChanged())
2386
+ new ut(this.root, "established", () => e.notifyChanged())
2128
2387
  );
2129
2388
  }
2130
2389
  nodes() {
@@ -2132,23 +2391,29 @@ class ne {
2132
2391
  }
2133
2392
  install(t, e, ...i) {
2134
2393
  const s = this.change(t), n = s.install(e, ...i);
2135
- return J(s.commit()), n;
2394
+ return ft(s.commit()), n;
2136
2395
  }
2137
2396
  stageConfigurationInstall(t, e, i, ...s) {
2138
- return this.#r(t), te(
2397
+ return this.#r(t), me(
2139
2398
  e.requireDraft(),
2140
2399
  i,
2141
2400
  s[0],
2142
2401
  (n, o) => (this.#r(t), this.#t.createDraft(t, n, o))
2143
2402
  );
2144
2403
  }
2404
+ /**
2405
+ * `deferred` tracking exists for `create()`. A Group being configured must not
2406
+ * mark itself pending on each nested install, because the whole configuration
2407
+ * commits once; the coordinator attaches that single operation to every node in
2408
+ * the finished subtree instead.
2409
+ */
2145
2410
  change(t, e = "immediate") {
2146
- return this.#r(t), new ee({
2411
+ return this.#r(t), new ve({
2147
2412
  create: (i, s) => (this.#r(t), this.#t.createDraft(t, i, s)),
2148
2413
  resolve: (i) => {
2149
2414
  this.#r(t);
2150
2415
  const s = this.#t.resolveInstallation(i);
2151
- if (!s.attached) throw s.unavailableError();
2416
+ if (!s.hasAuthority) throw s.unavailableError();
2152
2417
  if (!t.contains(s.group))
2153
2418
  throw new TypeError(`Installation '${s.id}' is outside Group '${t.id}'`);
2154
2419
  return s;
@@ -2168,20 +2433,20 @@ class ne {
2168
2433
  create(t, e, i, s) {
2169
2434
  if (typeof i != "function") throw new TypeError("Group configure must be a function");
2170
2435
  const n = t.create(e);
2171
- this.#i.set(n, new Q(n, "new", () => this.#t.notifyChanged()));
2172
- const o = s === void 0, a = s ?? new ie(
2436
+ this.#i.set(n, new ut(n, "new", () => this.#t.notifyChanged()));
2437
+ const o = s === void 0, a = s ?? new Ee(
2173
2438
  this.change(n, "deferred"),
2174
- Zt,
2175
- (c) => $(
2439
+ be,
2440
+ (c) => V(
2176
2441
  c,
2177
2442
  "GROUP_UNAVAILABLE",
2178
2443
  `Group '${n.id}' configuration failed with a non-Error value`
2179
2444
  )
2180
- ), h = new re(this, n, a);
2445
+ ), h = new Ce(this, n, a);
2181
2446
  this.#e.set(n, h);
2182
2447
  try {
2183
2448
  const c = i(h);
2184
- P(c, "Group configure must be synchronous");
2449
+ R(c, "Group configure must be synchronous");
2185
2450
  const d = a.failure;
2186
2451
  if (d) throw d;
2187
2452
  } catch (c) {
@@ -2192,9 +2457,9 @@ class ne {
2192
2457
  const c = a.seal().commit();
2193
2458
  for (const d of n.walk()) {
2194
2459
  const l = this.#e.get(d);
2195
- l && C.get(l)?.finishConfiguration(), this.#a(d, c);
2460
+ l && P.get(l)?.finishConfiguration(), this.#a(d, c);
2196
2461
  }
2197
- J(c);
2462
+ ft(c);
2198
2463
  }
2199
2464
  return this.#t.notifyChanged(), h;
2200
2465
  }
@@ -2225,6 +2490,10 @@ class ne {
2225
2490
  (e) => t.contains(e.group)
2226
2491
  );
2227
2492
  }
2493
+ // A Group has no status of its own — it reports what its contents say, with
2494
+ // the worst news winning. The last line is the one that needs stating: an
2495
+ // empty Group is `active`, because a subtree that owns nothing has nothing
2496
+ // left to wait for.
2228
2497
  #n(t) {
2229
2498
  const e = this.#s(t);
2230
2499
  return e.some((i) => i.status === "failed") ? "failed" : e.some((i) => i.status === "stopping") ? "stopping" : e.length && e.every((i) => i.status === "active") ? "active" : e.length ? "pending" : "active";
@@ -2234,25 +2503,25 @@ class ne {
2234
2503
  }
2235
2504
  #r(t) {
2236
2505
  const e = this.#i.get(t);
2237
- if (!t.attached || !e) throw T(t);
2506
+ if (!t.attached || !e) throw D(t);
2238
2507
  return e;
2239
2508
  }
2240
2509
  #o(t) {
2241
2510
  for (const e of t) {
2242
2511
  this.#i.get(e)?.release(), this.#i.delete(e);
2243
2512
  const i = this.#e.get(e);
2244
- i && (C.get(i)?.revoke(), C.delete(i)), this.#e.delete(e);
2513
+ i && (P.get(i)?.revoke(), P.delete(i)), this.#e.delete(e);
2245
2514
  }
2246
2515
  }
2247
2516
  }
2248
- function J(r) {
2517
+ function ft(r) {
2249
2518
  Promise.resolve(r).catch(() => {
2250
2519
  });
2251
2520
  }
2252
- function X(r, t) {
2521
+ function pt(r, t) {
2253
2522
  return Object.freeze({ plugin: r, config: t });
2254
2523
  }
2255
- class oe {
2524
+ class Oe {
2256
2525
  constructor(t, e, i, s) {
2257
2526
  this.id = t, this.index = e, this.groupId = i.id, this.#i = { declaration: s, group: i, notifyChanged: void 0 };
2258
2527
  }
@@ -2264,7 +2533,7 @@ class oe {
2264
2533
  groupId;
2265
2534
  #s = /* @__PURE__ */ new Set();
2266
2535
  attach(t) {
2267
- const e = this.#d();
2536
+ const e = this.#h();
2268
2537
  if (e.notifyChanged) throw new Error(`Installation '${this.id}' is already attached`);
2269
2538
  e.notifyChanged = t;
2270
2539
  }
@@ -2272,7 +2541,7 @@ class oe {
2272
2541
  return this.#t.phase;
2273
2542
  }
2274
2543
  /** Whether the owning ChangeSet has granted this Installation Host authority. */
2275
- get attached() {
2544
+ get hasAuthority() {
2276
2545
  return this.#i?.notifyChanged !== void 0;
2277
2546
  }
2278
2547
  get instance() {
@@ -2282,18 +2551,18 @@ class oe {
2282
2551
  get error() {
2283
2552
  const t = this.#t;
2284
2553
  if (t.phase === "failed")
2285
- return t.failure.retention === "live" ? t.failure.error : t.failure.summary.restore();
2554
+ return t.error;
2286
2555
  if (t.phase === "removed")
2287
2556
  return new u("INSTALLATION_REMOVED", `Installation '${this.id}' has been removed`);
2288
2557
  }
2289
2558
  get group() {
2290
- return this.#d().group;
2559
+ return this.#h().group;
2291
2560
  }
2292
2561
  get declaration() {
2293
- return this.#d().declaration;
2562
+ return this.#h().declaration;
2294
2563
  }
2295
2564
  replaceDeclaration(t) {
2296
- this.#d().declaration = t;
2565
+ this.#h().declaration = t;
2297
2566
  }
2298
2567
  ready() {
2299
2568
  const t = this.#e;
@@ -2305,18 +2574,34 @@ class oe {
2305
2574
  `Installation '${this.id}' has not been committed`
2306
2575
  );
2307
2576
  }
2577
+ /**
2578
+ * Binds `ready()` to the change currently in flight.
2579
+ *
2580
+ * The `attempt` token is the point: a second change can start before the
2581
+ * first one's promise settles, and only the latest attempt is allowed to clear
2582
+ * the barrier. Without it, a stale resolution would report readiness for an
2583
+ * attempt that has already been superseded.
2584
+ *
2585
+ * A rejection is rethrown only when this Installation did not end up active.
2586
+ * A change that failed elsewhere in the batch but left this one running is not
2587
+ * this Installation's failure to report.
2588
+ */
2308
2589
  trackReadiness(t) {
2309
2590
  const e = {}, i = t.then(
2310
2591
  () => {
2311
2592
  this.#e?.attempt === e && (this.#e = void 0);
2312
2593
  },
2313
2594
  (s) => {
2314
- if (this.#e?.attempt === e && (this.#e = void 0), this.#t.phase !== "active") throw s;
2595
+ if (this.#e?.attempt === e && (this.#e = void 0), this.#t.phase !== "active") throw this.error ?? s;
2315
2596
  }
2316
2597
  );
2317
2598
  this.#e = { attempt: e, barrier: i }, i.catch(() => {
2318
2599
  });
2319
2600
  }
2601
+ // A terminal state only answers `ready()` once its readiness has been settled.
2602
+ // Before that the outcome is still being decided by the change in flight, so
2603
+ // the caller joins the waiter set instead of being told a result that the
2604
+ // transaction might still roll back.
2320
2605
  #n() {
2321
2606
  const t = this.#t;
2322
2607
  if ((t.phase === "active" || t.phase === "failed" || t.phase === "removed") && t.readiness === "settled") {
@@ -2357,11 +2642,17 @@ class oe {
2357
2642
  fail(t) {
2358
2643
  return this.#a(t);
2359
2644
  }
2645
+ /**
2646
+ * For an Installation that never made it into the graph. Unlike `fail()`, this
2647
+ * is terminal: readiness settles immediately, the attachment is dropped, and
2648
+ * the reason is kept as a RecordedFailure rather than the live Error. The handle the
2649
+ * caller already holds stays usable and reports why it is dead.
2650
+ */
2360
2651
  discard(t) {
2361
- const e = this.#o(t);
2652
+ const e = new J(this.#o(t));
2362
2653
  this.#r({
2363
2654
  phase: "failed",
2364
- failure: { retention: "summary", summary: new dt(e) },
2655
+ error: e,
2365
2656
  readiness: "settled"
2366
2657
  });
2367
2658
  for (const i of this.#s) i.reject(e);
@@ -2371,7 +2662,7 @@ class oe {
2371
2662
  const e = this.#o(t);
2372
2663
  return this.#r({
2373
2664
  phase: "failed",
2374
- failure: { retention: "live", error: e },
2665
+ error: e,
2375
2666
  readiness: "unsettled"
2376
2667
  }), e;
2377
2668
  }
@@ -2382,24 +2673,24 @@ class oe {
2382
2673
  this.#t = t, this.#i?.notifyChanged?.();
2383
2674
  }
2384
2675
  #o(t) {
2385
- return $(
2676
+ return V(
2386
2677
  t,
2387
2678
  "INSTALLATION_UNAVAILABLE",
2388
2679
  `Installation '${this.id}' failed with a non-Error value`
2389
2680
  );
2390
2681
  }
2391
- #d() {
2682
+ #h() {
2392
2683
  const t = this.#i;
2393
2684
  if (!t) throw new Error(`Installation '${this.id}' is no longer installed`);
2394
2685
  return t;
2395
2686
  }
2396
2687
  }
2397
- const S = /* @__PURE__ */ new WeakMap();
2398
- class ae {
2688
+ const L = /* @__PURE__ */ new WeakMap();
2689
+ class Ae {
2399
2690
  #t;
2400
2691
  #e = { phase: "draft" };
2401
2692
  constructor(t) {
2402
- this.#t = t, S.set(this, {
2693
+ this.#t = t, L.set(this, {
2403
2694
  attach: (e, i) => {
2404
2695
  if (this.#e.phase !== "draft")
2405
2696
  throw new Error(`Installation '${this.#t.id}' control is already sealed`);
@@ -2444,7 +2735,7 @@ class ae {
2444
2735
  );
2445
2736
  }
2446
2737
  }
2447
- class ce {
2738
+ class Ie {
2448
2739
  #t = /* @__PURE__ */ new Map();
2449
2740
  #e = /* @__PURE__ */ new WeakMap();
2450
2741
  #i = /* @__PURE__ */ new WeakMap();
@@ -2464,12 +2755,12 @@ class ce {
2464
2755
  }
2465
2756
  create(t, e, i) {
2466
2757
  t.assertAttached();
2467
- const s = ++this.#n, n = new oe(
2758
+ const s = ++this.#n, n = new Oe(
2468
2759
  `${e.name}:${s}`,
2469
2760
  s,
2470
2761
  t,
2471
- X(e, i)
2472
- ), o = new ae(n);
2762
+ pt(e, i)
2763
+ ), o = new Ae(n);
2473
2764
  return this.#e.set(o, n), this.#i.set(n, o), { record: n, facade: o };
2474
2765
  }
2475
2766
  resolve(t) {
@@ -2480,13 +2771,19 @@ class ce {
2480
2771
  attach(t) {
2481
2772
  const e = this.#i.get(t);
2482
2773
  if (!e) throw new Error(`Installation '${t.id}' has no public facade`);
2483
- const i = S.get(e);
2774
+ const i = L.get(e);
2484
2775
  if (!i) throw new Error(`Installation '${t.id}' has no draft control`);
2485
2776
  t.attach(this.#s.notifyChanged), i.attach(
2486
2777
  (s) => this.#s.update(t, e, s),
2487
2778
  () => this.#s.remove(t, e)
2488
2779
  );
2489
2780
  }
2781
+ /**
2782
+ * Validates the whole batch, then applies the whole batch. The two loops are
2783
+ * not a style choice: a rejected operation halfway through a single loop would
2784
+ * leave the earlier ones already written, and this method has no way to undo
2785
+ * them. Checking first means `apply` either changes everything or nothing.
2786
+ */
2490
2787
  apply(t) {
2491
2788
  for (const e of t) {
2492
2789
  if (e.kind === "install") {
@@ -2507,7 +2804,7 @@ class ce {
2507
2804
  this.#t.set(e.installation.id, e.installation);
2508
2805
  else if (e.kind === "update") {
2509
2806
  const i = e.installation.declaration, s = e.declaration.kind === "config" ? i.plugin : e.declaration.plugin, n = e.declaration.kind === "plugin" ? i.config : e.declaration.config;
2510
- e.installation.replaceDeclaration(X(s, n));
2807
+ e.installation.replaceDeclaration(pt(s, n));
2511
2808
  } else this.contains(e.installation) && this.#t.delete(e.installation.id);
2512
2809
  }
2513
2810
  settleChanges(t, e) {
@@ -2520,6 +2817,9 @@ class ce {
2520
2817
  discard(t, e) {
2521
2818
  t.discard(e), this.#a(t);
2522
2819
  }
2820
+ // Captures declarations only, since that is all a rollback restores. Instances
2821
+ // belong to the InstanceCoordinator and are rebuilt from the previous plan
2822
+ // rather than snapshotted — a live Instance is not a value that can be copied.
2523
2823
  capture() {
2524
2824
  return [...this.#t.values()].map((t) => ({
2525
2825
  installation: t,
@@ -2533,10 +2833,10 @@ class ce {
2533
2833
  }
2534
2834
  #a(t) {
2535
2835
  const e = this.#i.get(t);
2536
- e && (S.get(e)?.revoke(), S.delete(e)), this.#i.delete(t);
2836
+ e && (L.get(e)?.revoke(), L.delete(e)), this.#i.delete(t);
2537
2837
  }
2538
2838
  }
2539
- class he {
2839
+ class je {
2540
2840
  #t = Promise.resolve();
2541
2841
  get settled() {
2542
2842
  return this.#t;
@@ -2553,8 +2853,8 @@ class he {
2553
2853
  ), e;
2554
2854
  }
2555
2855
  }
2556
- const de = console, le = /* @__PURE__ */ new Set(["name", "logger", "onError"]);
2557
- class ue {
2856
+ const ke = console, $e = /* @__PURE__ */ new Set(["name", "logger", "onError"]);
2857
+ class Te {
2558
2858
  name;
2559
2859
  diagnostics;
2560
2860
  #t;
@@ -2564,9 +2864,9 @@ class ue {
2564
2864
  #n;
2565
2865
  #a;
2566
2866
  #r = "idle";
2567
- #o = new he();
2867
+ #o = new je();
2568
2868
  constructor(t = {}) {
2569
- L(t, "Host options", { fields: le });
2869
+ B(t, "Host options", { fields: $e });
2570
2870
  const e = Object.hasOwn(t, "name") ? t.name : void 0, i = Object.hasOwn(t, "logger") ? t.logger : void 0, s = Object.hasOwn(t, "onError") ? t.onError : void 0, n = e ?? "host";
2571
2871
  if (typeof n != "string" || !n.trim())
2572
2872
  throw new TypeError("Host name must be a non-empty string");
@@ -2574,10 +2874,10 @@ class ue {
2574
2874
  throw new TypeError("Host name cannot start or end with whitespace");
2575
2875
  if (s !== void 0 && typeof s != "function")
2576
2876
  throw new TypeError("Host onError must be a function");
2577
- if (i !== void 0 && !Mt(i))
2877
+ if (i !== void 0 && !se(i))
2578
2878
  throw new TypeError("Host logger must implement debug/info/warn/error");
2579
- this.name = n, this.#i = i ?? de, this.#n = s ?? ((o) => this.#i.error(o)), this.#t = new ce({
2580
- notifyChanged: () => this.#p(),
2879
+ this.name = n, this.#i = i ?? ke, this.#n = s ?? ((o) => this.#i.error(o)), this.#t = new Ie({
2880
+ notifyChanged: () => this.#d(),
2581
2881
  update: (o, a, h) => {
2582
2882
  const c = this.#s.change(o.group);
2583
2883
  return c.update(a, h), c.commit();
@@ -2586,25 +2886,29 @@ class ue {
2586
2886
  const h = this.#s.change(o.group);
2587
2887
  return h.remove(a), h.commit();
2588
2888
  }
2589
- }), this.#a = new Ht({
2889
+ }), this.#a = new de({
2590
2890
  hostName: n,
2591
2891
  logger: this.#i,
2592
2892
  isInstalled: (o) => this.#t.has(o),
2593
- report: (o) => this.#h(o)
2594
- }), this.#s = new ne(n, {
2893
+ report: (o) => this.#c(o)
2894
+ }), this.#s = new Se(n, {
2595
2895
  installations: () => this.#t.values(),
2596
2896
  createDraft: (o, a, h) => this.#t.create(o, a, h),
2597
2897
  resolveInstallation: (o) => this.#t.resolve(o),
2598
- executeChanges: (o, a) => this.#d(o, a),
2898
+ executeChanges: (o, a) => this.#h(o, a),
2599
2899
  attachInstallation: (o) => this.#t.attach(o),
2600
2900
  discardInstallation: (o, a) => this.#t.discard(o, a),
2601
2901
  runExclusive: (o) => this.#o.run(o),
2602
- removeInstallations: (o) => this.#f(o),
2603
- notifyChanged: () => this.#p()
2604
- }), this.#e = new Kt(
2902
+ removeInstallations: (o) => this.#p(o),
2903
+ notifyChanged: () => this.#d()
2904
+ }), this.#e = new le(
2605
2905
  n,
2606
- this.#s.nodes(),
2607
- (o) => this.#h(o)
2906
+ () => ({
2907
+ status: this.#r,
2908
+ installations: this.#t.values(),
2909
+ groups: this.#s.nodes()
2910
+ }),
2911
+ (o) => this.#c(o)
2608
2912
  ), this.diagnostics = this.#e.view, Object.freeze(this);
2609
2913
  }
2610
2914
  get status() {
@@ -2629,12 +2933,12 @@ class ue {
2629
2933
  start() {
2630
2934
  return this.#o.run(async () => {
2631
2935
  if (this.#r !== "active") {
2632
- this.#c("starting");
2936
+ this.#l("starting");
2633
2937
  try {
2634
2938
  const t = this.#a.buildPlan(this.#t.values());
2635
- await this.#a.start(t), this.#c("active"), this.#t.settleReadiness(t.order);
2939
+ await this.#a.start(t), this.#l("active"), this.#t.settleReadiness(t.order);
2636
2940
  } catch (t) {
2637
- this.#c("idle");
2941
+ this.#l("idle");
2638
2942
  for (const e of this.#t.values())
2639
2943
  e.status !== "active" && e.fail(t);
2640
2944
  throw this.#t.settleReadiness(this.#t.values()), t;
@@ -2645,19 +2949,19 @@ class ue {
2645
2949
  stop() {
2646
2950
  return this.#o.run(async () => {
2647
2951
  if (this.#r === "idle") return;
2648
- this.#c("stopping");
2952
+ this.#l("stopping");
2649
2953
  const t = await this.#a.stop();
2650
- if (this.#c("idle"), t.length === 1) throw t[0];
2954
+ if (this.#l("idle"), t.length === 1) throw t[0];
2651
2955
  if (t.length > 1) throw new AggregateError(t, "Host shutdown failed");
2652
2956
  });
2653
2957
  }
2654
- #d(t, e) {
2958
+ #h(t, e) {
2655
2959
  const i = e.filter((s) => s.kind === "install").map((s) => s.installation);
2656
2960
  return this.#o.run(async () => {
2657
2961
  try {
2658
- if (!t.attached) throw T(t);
2962
+ if (!t.attached) throw D(t);
2659
2963
  if (!e.length) return;
2660
- this.#r === "active" ? await this.#l(e) : (this.#t.apply(e), this.#t.settleChanges(e, !1)), this.#p();
2964
+ this.#r === "active" ? await this.#u(e) : (this.#t.apply(e), this.#t.settleChanges(e, !1)), this.#d();
2661
2965
  } catch (s) {
2662
2966
  for (const n of i)
2663
2967
  this.#t.contains(n) || this.#t.discard(n, s);
@@ -2665,22 +2969,33 @@ class ue {
2665
2969
  }
2666
2970
  });
2667
2971
  }
2668
- async #f(t) {
2669
- t.length && this.#r === "active" ? await this.#l(t) : (this.#t.apply(t), this.#t.settleChanges(t, !1));
2972
+ async #p(t) {
2973
+ t.length && this.#r === "active" ? await this.#u(t) : (this.#t.apply(t), this.#t.settleChanges(t, !1));
2670
2974
  }
2671
- async #l(t) {
2672
- const e = await this.#u(t);
2975
+ async #u(t) {
2976
+ const e = await this.#f(t);
2673
2977
  if (this.#t.settleReadiness(e.affected), e.kind === "rolled-back") throw e.error;
2674
2978
  this.#t.settleChanges(t, !0);
2675
2979
  }
2676
- async #u(t) {
2980
+ // Three failure levels, in the order they are attempted:
2981
+ //
2982
+ // 1. the new plan does not even build restore declarations, stay active
2983
+ // 2. the plan builds but activation fails Engine rolls back to the previous
2984
+ // Instances and reports the cause
2985
+ // 3. rollback itself cannot complete fail closed: everything stops and
2986
+ // the Host reports `idle`
2987
+ //
2988
+ // Level 3 is the one worth stating out loud. A Host that cannot restore its
2989
+ // previous state is not healthy, so it refuses to present itself as active —
2990
+ // `hasCommittedPlan` is what distinguishes the two outcomes here.
2991
+ async #f(t) {
2677
2992
  const e = this.#t.capture(), i = new Set(t.map((n) => n.installation));
2678
- this.#c("changing");
2993
+ this.#l("changing");
2679
2994
  let s;
2680
2995
  try {
2681
2996
  this.#t.apply(t), s = this.#a.buildPlan(this.#t.values());
2682
2997
  } catch (n) {
2683
- throw this.#t.restore(e), this.#c("active"), n;
2998
+ throw this.#t.restore(e), this.#l("active"), n;
2684
2999
  }
2685
3000
  try {
2686
3001
  const n = await this.#a.transition(
@@ -2688,33 +3003,33 @@ class ue {
2688
3003
  i,
2689
3004
  () => this.#t.restore(e)
2690
3005
  );
2691
- return this.#c("active"), n;
3006
+ return this.#l("active"), n;
2692
3007
  } catch (n) {
2693
- throw this.#c(this.#a.hasCommittedPlan ? "active" : "idle"), n;
3008
+ throw this.#l(this.#a.hasCommittedPlan ? "active" : "idle"), n;
2694
3009
  }
2695
3010
  }
2696
- #h(t) {
3011
+ #c(t) {
2697
3012
  const e = this.#i;
2698
3013
  try {
2699
3014
  const i = this.#n(t);
2700
3015
  Promise.resolve(i).catch((s) => {
2701
- Z(e, t, s);
3016
+ gt(e, t, s);
2702
3017
  });
2703
3018
  } catch (i) {
2704
- Z(e, t, i);
3019
+ gt(e, t, i);
2705
3020
  }
2706
3021
  }
2707
- #c(t) {
2708
- this.#r = t, this.#p();
3022
+ #l(t) {
3023
+ this.#r = t, this.#d();
2709
3024
  }
2710
- #p() {
2711
- this.#e.publish(this.#r, this.#t.values(), this.#s.nodes());
3025
+ #d() {
3026
+ this.#e.publish();
2712
3027
  }
2713
3028
  }
2714
- function be(r) {
2715
- return new ue(r);
3029
+ function xe(r) {
3030
+ return new Te(r);
2716
3031
  }
2717
- function Z(r, t, e) {
3032
+ function gt(r, t, e) {
2718
3033
  try {
2719
3034
  const i = r.error(
2720
3035
  new AggregateError([t, e], "Host error reporter failed")
@@ -2725,21 +3040,21 @@ function Z(r, t, e) {
2725
3040
  }
2726
3041
  }
2727
3042
  export {
2728
- ut as ConfigValidationError,
3043
+ $t as ConfigValidationError,
2729
3044
  u as DougongError,
2730
- dt as ErrorSummary,
2731
- O as ReadonlyMapSnapshot,
2732
- he as SerialQueue,
2733
- q as SnapshotPublisher,
2734
- L as assertPlainRecord,
2735
- k as asyncDisposeSymbol,
2736
- be as createHost,
2737
- ve as definePlugin,
2738
- g as disposeSymbol,
2739
- pe as event,
2740
- ge as extensionPoint,
2741
- tt as isCancellationReason,
2742
- Mt as isLogger,
2743
- we as optional,
2744
- fe as service
3045
+ z as ReadonlyMapSnapshot,
3046
+ J as RecordedFailure,
3047
+ je as SerialQueue,
3048
+ Z as SnapshotPublisher,
3049
+ B as assertPlainRecord,
3050
+ F as asyncDisposeSymbol,
3051
+ xe as createHost,
3052
+ Ne as definePlugin,
3053
+ v as disposeSymbol,
3054
+ Le as event,
3055
+ ze as extensionPoint,
3056
+ wt as isCancellationReason,
3057
+ se as isLogger,
3058
+ Re as optional,
3059
+ It as service
2745
3060
  };