@expressive/mvc 0.87.0 → 0.88.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/state.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { Context } from "./context.js";
2
2
  import { event } from "./observable.js";
3
- import { Caught } from "./caught.js";
4
3
 
5
4
  //#region src/state.d.ts
6
5
  /** Internal state assigned to states. */
@@ -9,7 +8,16 @@ declare const STORE: WeakMap<State, Record<string | number | symbol, unknown>>;
9
8
  * Also carries work to run at the deadline, which `def` uses to bound its registry. */
10
9
  declare const PENDING: Set<State | (() => void)>;
11
10
  /** External lifecycle listeners for any given State class. */
12
- type Handler = State.Init<any> | State.On<any>;
11
+ type Handler = State.On<any>;
12
+ declare const SETUP: WeakMap<State.Extends<State>, Set<Handler>>;
13
+ /** Reference bound instance methods, or their getter, to real one. */
14
+ declare const UNBIND: WeakMap<any, any>;
15
+ /** List of methods defined by a given type. */
16
+ declare const METHODS: WeakMap<Function, Map<string, (value: any) => void>>;
17
+ /** List of reactive getters defined by a given type. */
18
+ declare const GETTERS: WeakMap<Function, Map<string, () => unknown>>;
19
+ /** Replacement for a getter captured by live computed properties, for hot patching. */
20
+ declare const LATEST: WeakMap<Function, Function>;
13
21
  declare namespace State {
14
22
  /** Any type of State, using own class constructor as its identifier. */
15
23
  type Extends<T extends State = State> = (abstract new (...args: any[]) => T) & typeof State;
@@ -32,10 +40,7 @@ declare namespace State {
32
40
  * will observe). Returned function will run when state is destroyed.
33
41
  */
34
42
  type Init<T extends State = State> = (this: T, thisArg: T) => Promise<void> | (() => void) | Args<T> | Assign<T> | void;
35
- /**
36
- * Lifecycle handlers for `State.on`, keyed by when they run. A bare `Init`
37
- * function passed to `on` is sugar for `{ before }`.
38
- */
43
+ /** Lifecycle handlers for `State.on`, keyed by when they run. */
39
44
  interface On<T extends State = State> {
40
45
  /**
41
46
  * Per-class setup, run once when the class is first bootstrapped - before
@@ -45,24 +50,32 @@ declare namespace State {
45
50
  */
46
51
  type?(type: State.Extends<T>): void;
47
52
  /**
48
- * Per-instance setup, run in the `prepare` phase before `observe` and the
49
- * `new()` hook. Equivalent to passing a bare function to `on`. May return a
50
- * cleanup, constructor args, or an assign overlay.
53
+ * Per-instance, once constructed - before own values are observed and
54
+ * before constructor args and `new()`. May return a cleanup, constructor
55
+ * args, or an assign overlay.
51
56
  */
52
- before?(this: T, thisArg: T): void | (() => void) | Promise<void> | Args<T> | Assign<T>;
57
+ setup?(this: T, self: T): void | (() => void) | Promise<void> | Args<T> | Assign<T>;
53
58
  /**
54
- * Per-instance setup, run at the `new()` slot - after own values are
55
- * observed and constructor args applied. May return a cleanup function.
59
+ * Per-instance, once ready - after own values are observed, constructor
60
+ * args applied and `new()` has run. May return a cleanup function.
56
61
  */
57
- after?(this: T, self: T): void | (() => void);
62
+ ready?(this: T, self: T): void | (() => void);
58
63
  /**
59
- * Receives a `Caught` mvc reports for this State or a subclass - most-derived
60
- * class first, last registered first. Return it (or a replacement) to pass it
61
- * on; return nothing to handle it; throw to let it escape uncaught. Passed off
62
- * the end, a warning logs and anything else is thrown - to the writer for a
63
- * destroyed write, else uncaught.
64
+ * Runs each time a method is bound to an instance - first access,
65
+ * reassignment, and the rebind after a hot patch - with the key, the bound
66
+ * function and the instance. Tooling and development use.
64
67
  */
65
- catch?(this: T, error: Caught): Caught | void;
68
+ method?(this: T, key: string, fn: Function, self: T): void;
69
+ /**
70
+ * Receives what an effect, refreshing getter or async initializer of this State
71
+ * or a subclass threw, a destroyed write, or an instance never activated - with
72
+ * its `kind` (`effect`, `getter`, `setup`, `dead`, `unused`) and `key` where
73
+ * one applies. Most-derived class first, last registered first, each once. Return the error
74
+ * (or another) to pass it on; return nothing to handle it; throw to let it escape.
75
+ * Passed off the end, a destroyed write outputs nothing, `unused` warns,
76
+ * anything else escapes uncaught.
77
+ */
78
+ catch?(this: T, error: unknown, kind: 'effect' | 'getter' | 'setup' | 'dead' | 'unused', key?: string): unknown;
66
79
  }
67
80
  /** Object overlay to override values and methods on a state. */
68
81
  type Assign<T> = Record<string, unknown> & { [K in Property<T>]?: T[K] extends ((...args: infer A) => infer R) ? (this: T, ...args: A) => R : T[K] };
@@ -210,6 +223,18 @@ declare abstract class State {
210
223
  * @returns Function to cancel listener.
211
224
  */
212
225
  get<T extends State>(type: State.Type<T>, callback: Context.Expect<T>, downstream?: boolean): () => void;
226
+ /** Owner of this State. Throws if it has none. */
227
+ get<T extends typeof State>(type: T & (typeof State extends T ? unknown : never), required?: true): State;
228
+ /** Owner of this State. Undefined if it has none. */
229
+ get<T extends typeof State>(type: T & (typeof State extends T ? unknown : never), required: boolean): State | undefined;
230
+ /**
231
+ * Run a callback for each State this one owns - those owned now, then each
232
+ * one as it activates. Callback may return a function, called when that State
233
+ * is destroyed.
234
+ *
235
+ * @returns Function to stop watching.
236
+ */
237
+ get<T extends typeof State>(type: T & (typeof State extends T ? unknown : never), callback: (child: State) => void | (() => void), downstream: true): () => void;
213
238
  /**
214
239
  * Get update in progress.
215
240
  *
@@ -221,7 +246,7 @@ declare abstract class State {
221
246
  * Properties which are not managed by this state will be ignored.
222
247
  *
223
248
  * @param assign - Object with properties to update.
224
- * @param silent - If true, listeners will not be notified. If state is destroyed, drops the update without reporting it.
249
+ * @param silent - If true, listeners will not be notified. If state is destroyed, stores the update without reporting it.
225
250
  * @returns Array of keys updated, syncronously contains keys updated immediately and may be resolved (to itself) when all updates are settled.
226
251
  */
227
252
  set(assign?: State.Assign<this>, silent?: boolean): State.Updated<this>;
@@ -255,7 +280,7 @@ declare abstract class State {
255
280
  */
256
281
  set(key: State.Event<this>): State.Updated<this>;
257
282
  /**
258
- * Declare an end to updates. This event is final and will freeze state.
283
+ * Declare an end to updates. This event is final - later writes are stored but never dispatched.
259
284
  * This event can be watched for as well, to run cleanup logic and internally will remove all listeners.
260
285
  *
261
286
  * @param status - `null` to end updates.
@@ -304,16 +329,18 @@ declare abstract class State {
304
329
  /**
305
330
  * Register a lifecycle handler for this State and its subclasses.
306
331
  *
307
- * A bare function is per-instance setup run in the `prepare` phase (sugar for
308
- * `{ before }`); if it returns a function, that runs when the instance is
309
- * destroyed. Pass a {@link State.On} object to hook by cadence - `type`
310
- * (per-class, at bootstrap), `before` (per-instance, before `new()`), and
311
- * `after` (per-instance, at the `new()` slot).
332
+ * Hooks by cadence - `type` (per-class, at bootstrap), `setup` (per-instance,
333
+ * before values are observed), `ready` (per-instance, after `new()`), and
334
+ * `method` (per method binding). A function alone is a `setup` handler. A
335
+ * function returned from `setup` or `ready` runs when the instance is destroyed.
312
336
  *
313
337
  * @returns Function to remove the handler.
314
338
  */
315
- static on<T extends State>(this: State.Extends<T>, handler: State.Init<T> | State.On<T>): () => boolean;
339
+ static on<T extends State>(this: State.Extends<T>, setup: NonNullable<State.On<T>['setup']>): () => boolean;
340
+ static on<T extends State>(this: State.Extends<T>, handler: State.On<T>): () => boolean;
316
341
  }
342
+ /** Bind own configurable methods of a type and collect its reactive getters. */
343
+ declare function classify(type: State.Extends, keys: Map<string, (value: any) => void>, getters: Map<string, () => unknown>): void;
317
344
  /**
318
345
  * Install a reactive computed property on a state instance, derived from a prototype
319
346
  * getter or an arity-bearing `set` factory.
@@ -344,26 +371,27 @@ declare function trailing(state: State): State[];
344
371
  * claimed later. Returns a callback to stop watching.
345
372
  */
346
373
  declare function children(state: State, callback: (child: State) => void): () => boolean;
374
+ /**
375
+ * Report States owned by this one - those active now, then each one as it
376
+ * activates. A function returned by `callback` runs when that State is destroyed.
377
+ */
378
+ declare function owned(state: State, callback: (child: State) => void | (() => void)): () => undefined;
347
379
  declare function access(state: State, property: string, required?: boolean): any;
348
380
  /**
349
381
  * Update a property on a state instance and notify listeners.
350
382
  *
351
383
  * This is used internally to update properties, but can also be used to update properties which are not managed by state, or to update values without triggering setters.
352
384
  *
353
- * A destroyed state reports the write - thrown to the writer unless a `catch` handler takes it - and returns `false`; `silent` skips both, and dispatch.
385
+ * A destroyed state stores the write without dispatch, reports it unless `silent`, and returns `false`.
354
386
  */
355
387
  declare function update<T>(state: State, key: State.Event<T>, value: T, silent?: boolean, own?: boolean): boolean;
356
- /** Error thrown by a handler replaying in dispatch, attributed to the State which queued it. */
357
- declare function fault(err: unknown, owner?: object): void;
358
- /** Begin tracking live instances, so a hot patch can reach them. */
359
- declare function track(): void;
360
388
  /**
361
- * Move the members of `next` onto `prev`, which keeps its identity. Returns the
362
- * live instances of `prev`, each needing a refresh.
389
+ * Hand a value stored by an owning writer to that property's adopter,
390
+ * creating one the first time a State is stored there. A property which has
391
+ * held a State keeps its adopter, so a later non-State value releases the
392
+ * previous child.
363
393
  */
364
- declare function patch(prev: State.Extends, next: State.Extends, own: Handler[]): State[];
365
- /** Lifecycle handlers registered on a class itself. */
366
- declare function handlers(type: State.Extends): Handler[];
394
+ declare function adopt(state: State, key: unknown, value: unknown): void;
367
395
  /** Random alphanumberic of length 6; always starts with a letter. */
368
396
  declare function uid(): string;
369
397
  declare function unbind<T extends Function>(fn: T): T;
@@ -377,5 +405,5 @@ declare function unbind<T extends Function>(fn?: T): T | undefined;
377
405
  declare function parent(child: object): State | null | undefined;
378
406
  declare function parent(child: object, value: State | null): boolean;
379
407
  //#endregion
380
- export { PENDING, STORE, State, access, apply, children, compute, event, fault, handlers, parent, patch, track, trailing, uid, unbind, update };
408
+ export { GETTERS, type Handler, LATEST, METHODS, PENDING, SETUP, STORE, State, UNBIND, access, adopt, apply, children, classify, compute, event, owned, parent, trailing, uid, unbind, update };
381
409
  //# sourceMappingURL=state.d.ts.map
package/dist/state.js CHANGED
@@ -1,6 +1,6 @@
1
- import { Caught } from "./caught.js";
1
+ import { REPORT } from "./dispatch.js";
2
2
  import { capture, event, listener, observer, queued, touch, watch } from "./observable.js";
3
- import { Context, join } from "./context.js";
3
+ import { Context, host, join } from "./context.js";
4
4
 
5
5
  //#region src/state.ts
6
6
  const define = Object.defineProperty;
@@ -22,12 +22,6 @@ const METHODS = /* @__PURE__ */ new WeakMap();
22
22
  const GETTERS = /* @__PURE__ */ new WeakMap();
23
23
  /** Replacement for a getter captured by live computed properties, for hot patching. */
24
24
  const LATEST = /* @__PURE__ */ new WeakMap();
25
- /** Method implementations replaced by a hot patch. */
26
- const PAST = /* @__PURE__ */ new WeakSet();
27
- /** Live instances, tracked once hot patching is enabled. */
28
- let LIVE;
29
- /** Bootstrapped subclasses of each class, tracked once hot patching is enabled. */
30
- const SUBCLASSES = /* @__PURE__ */ new WeakMap();
31
25
  /** Event every observer treats as watched, forcing a refresh after a hot patch. */
32
26
  const REFRESH = Symbol.for("@expressive/mvc.refresh");
33
27
  /** Stale flags for compute closures awaiting refresh on next access. */
@@ -40,6 +34,8 @@ let ANNOUNCED = false;
40
34
  /** Adopters for managed properties which have held a child State. */
41
35
  const ADOPT = /* @__PURE__ */ new WeakMap();
42
36
  const CHILDREN = /* @__PURE__ */ new WeakMap();
37
+ const OWNS = /* @__PURE__ */ new WeakMap();
38
+ const OWNED = /* @__PURE__ */ new WeakMap();
43
39
  var State = class State {
44
40
  /**
45
41
  * Whether an instance activated with no enclosing context registers itself
@@ -60,7 +56,8 @@ var State = class State {
60
56
  get(arg1, arg2, arg3) {
61
57
  const self = this.is;
62
58
  if (arg1 === void 0) return values(self);
63
- if (State.is(arg1)) return Context.get(self).get(arg1, arg2, arg3, self);
59
+ if (arg1 === State) return typeof arg2 == "function" ? owned(self, arg2) : observed(this, arg1, owner(self, arg2));
60
+ if (State.is(arg1)) return observed(this, arg1, Context.get(self).get(arg1, arg2, arg3, self));
64
61
  if (typeof arg1 == "function") return watch(self, unbind(arg1));
65
62
  if (typeof arg2 == "function") return callback(self, arg2, arg1);
66
63
  if (arg1 === null) return observer(self) === null;
@@ -105,18 +102,8 @@ var State = class State {
105
102
  static is(maybe) {
106
103
  return maybe === this || typeof maybe == "function" && maybe.prototype instanceof this;
107
104
  }
108
- /**
109
- * Register a lifecycle handler for this State and its subclasses.
110
- *
111
- * A bare function is per-instance setup run in the `prepare` phase (sugar for
112
- * `{ before }`); if it returns a function, that runs when the instance is
113
- * destroyed. Pass a {@link State.On} object to hook by cadence - `type`
114
- * (per-class, at bootstrap), `before` (per-instance, before `new()`), and
115
- * `after` (per-instance, at the `new()` slot).
116
- *
117
- * @returns Function to remove the handler.
118
- */
119
105
  static on(handler) {
106
+ if (typeof handler == "function") handler = { setup: handler };
120
107
  let setup = SETUP.get(this);
121
108
  if (!setup) SETUP.set(this, setup = /* @__PURE__ */ new Set());
122
109
  setup.add(handler);
@@ -129,6 +116,9 @@ define(State.prototype, "toString", { value() {
129
116
  define(State, "toString", { value() {
130
117
  return this.name;
131
118
  } });
119
+ function observed(from, key, value) {
120
+ return value instanceof State ? touch(from, key, value) : value;
121
+ }
132
122
  /** Register a user OnEvent callback, preserving `this` and `source`. */
133
123
  function callback(self, cb, select) {
134
124
  return listener(self, (key) => cb.call(self, key, self), select);
@@ -147,7 +137,6 @@ function init(state, ...args) {
147
137
  const { before, after } = bootstrap(T);
148
138
  ID.set(state, `${T}-${uid()}`);
149
139
  STORE.set(state, {});
150
- LIVE?.add(new WeakRef(state));
151
140
  function observe() {
152
141
  for (const key in state) {
153
142
  const desc = Object.getOwnPropertyDescriptor(state, key) || {};
@@ -162,13 +151,20 @@ function init(state, ...args) {
162
151
  const g = type.global;
163
152
  if (!(typeof g == "function" ? g(state) : g)) return;
164
153
  if (!Object.prototype.hasOwnProperty.call(type, "global")) throw new Error(`${state} would register as a global by inheritance alone - re-declare \`static global\` on ${type.name} (\`true\` to keep it, \`false\` to opt out).`);
154
+ for (let T = type; T !== State; T = Object.getPrototypeOf(T)) {
155
+ const found = Context.root.get(T, false);
156
+ if (found && (T === type || found.constructor === T)) throw new Error(`Cannot register ${state} as a global - ${found} already exists in root. Destroy the existing instance first, or provide additional ones via explicit context.`);
157
+ }
165
158
  return Context.root.add(state);
166
159
  }
167
160
  listener(state, (key) => {
168
161
  const rest = trailing(state);
169
162
  PENDING.delete(state);
170
163
  if (key === null) return null;
171
- parent(state, null);
164
+ if (!PARENT.has(state)) parent(state, host(state) || null);
165
+ const above = PARENT.get(state);
166
+ const owns = above && (OWNS.get(above) || OWNS.set(above, /* @__PURE__ */ new Set()).get(above));
167
+ if (owns) listener(state, () => void owns.delete(state), null);
172
168
  const queue = [
173
169
  ...before,
174
170
  observe,
@@ -181,12 +177,16 @@ function init(state, ...args) {
181
177
  for (let i = 0; i < queue.length; i++) {
182
178
  const arg = queue[i];
183
179
  const out = typeof arg == "function" ? arg.call(state, state) : arg;
184
- if (out instanceof Promise) out.catch((err) => report(new Caught.Init(state, err)));
180
+ if (out instanceof Promise) out.catch((err) => report("setup", state, err));
185
181
  else if (Array.isArray(out)) queue.splice(i + 1, 0, ...out);
186
182
  else if (typeof out == "function") listener(state, out, null);
187
183
  else if (typeof out == "object") assign(state, out, true);
188
184
  }
189
185
  });
186
+ if (owns) {
187
+ owns.add(state);
188
+ OWNED.get(above)?.forEach((cb) => cb(state));
189
+ }
190
190
  let end = rest.length;
191
191
  while (end-- > 0 && PENDING.has(rest[end]));
192
192
  for (let i = 0; i < end; i++) PENDING.delete(rest[i]);
@@ -197,7 +197,7 @@ function init(state, ...args) {
197
197
  queueMicrotask(() => {
198
198
  QUEUED = false;
199
199
  for (const item of PENDING) if (typeof item == "function") item();
200
- else report(new Caught.Inactive(item));
200
+ else report("unused", item, /* @__PURE__ */ new Error(`${item} was constructed but never activated.`));
201
201
  PENDING.clear();
202
202
  });
203
203
  }
@@ -232,10 +232,9 @@ function bootstrap(T) {
232
232
  T = Object.getPrototypeOf(T);
233
233
  }
234
234
  for (const type of chain) {
235
- for (const handler of SETUP.get(type) || []) if (typeof handler == "function") before.add(handler);
236
- else {
237
- if (handler.before) before.add(handler.before);
238
- if (handler.after) after.add(handler.after);
235
+ for (const handler of SETUP.get(type) || []) {
236
+ if (handler.setup) before.add(handler.setup);
237
+ if (handler.ready) after.add(handler.ready);
239
238
  if (handler.type) onType.add(handler.type);
240
239
  }
241
240
  if (type === State) continue;
@@ -245,12 +244,6 @@ function bootstrap(T) {
245
244
  continue;
246
245
  }
247
246
  for (const setupType of onType) setupType(type);
248
- if (LIVE) {
249
- const parent = Object.getPrototypeOf(type);
250
- let children = SUBCLASSES.get(parent);
251
- if (!children) SUBCLASSES.set(parent, children = /* @__PURE__ */ new Set());
252
- children.add(type);
253
- }
254
247
  METHODS.set(type, keys = new Map(keys));
255
248
  GETTERS.set(type, getters = new Map(getters));
256
249
  classify(type, keys, getters);
@@ -295,6 +288,7 @@ function classify(type, keys, getters) {
295
288
  writable: true,
296
289
  configurable: true
297
290
  });
291
+ for (const handler of stages(is.constructor, "method")) handler.call(is, key, bound, is);
298
292
  return bound;
299
293
  }
300
294
  UNBIND.set(bind, value);
@@ -305,6 +299,12 @@ function classify(type, keys, getters) {
305
299
  });
306
300
  }
307
301
  }
302
+ /** Handlers of one stage along the class chain - ancestor first, in registration order, each once. */
303
+ function stages(T, key) {
304
+ const found = T === State ? /* @__PURE__ */ new Set() : stages(Object.getPrototypeOf(T), key);
305
+ for (const handler of SETUP.get(T) || []) if (handler[key]) found.add(handler[key]);
306
+ return found;
307
+ }
308
308
  /**
309
309
  * Install a reactive computed property on a state instance, derived from a prototype
310
310
  * getter or an arity-bearing `set` factory.
@@ -339,7 +339,7 @@ function compute(getter, key) {
339
339
  console.warn(`An exception was thrown while initializing ${this}.${key}.`);
340
340
  throw err;
341
341
  }
342
- if (!(err instanceof Promise)) report(new Caught.Getter(this, key, err));
342
+ if (!(err instanceof Promise)) report("getter", this, err, key);
343
343
  }
344
344
  update(this, key, next, !isAsync);
345
345
  };
@@ -461,6 +461,26 @@ function children(state, callback) {
461
461
  set.add(callback);
462
462
  return () => set.delete(callback);
463
463
  }
464
+ function owner(state, required) {
465
+ const found = PARENT.get(state);
466
+ if (found || required === false) return found || void 0;
467
+ throw new Error(`${state} has no owner.`);
468
+ }
469
+ /**
470
+ * Report States owned by this one - those active now, then each one as it
471
+ * activates. A function returned by `callback` runs when that State is destroyed.
472
+ */
473
+ function owned(state, callback) {
474
+ function each(child) {
475
+ const done = callback(child);
476
+ if (typeof done == "function") listener(child, () => void done(), null);
477
+ }
478
+ OWNS.get(state)?.forEach(each);
479
+ let set = OWNED.get(state);
480
+ if (!set) OWNED.set(state, set = /* @__PURE__ */ new Set());
481
+ set.add(each);
482
+ return () => void set.delete(each);
483
+ }
464
484
  /** Currently accumulating export. Stores real values of placeholder properties such as ref() or child states. */
465
485
  let EXPORT;
466
486
  function values(state) {
@@ -524,15 +544,16 @@ function assign(state, data, silent) {
524
544
  *
525
545
  * This is used internally to update properties, but can also be used to update properties which are not managed by state, or to update values without triggering setters.
526
546
  *
527
- * A destroyed state reports the write - thrown to the writer unless a `catch` handler takes it - and returns `false`; `silent` skips both, and dispatch.
547
+ * A destroyed state stores the write without dispatch, reports it unless `silent`, and returns `false`.
528
548
  */
529
549
  function update(state, key, value, silent, own) {
550
+ const store = STORE.get(state);
551
+ if (value instanceof State) value = value.is;
530
552
  if (observer(state) === null) {
531
- if (!silent) report(new Caught.Destroyed(state, String(key)), true);
553
+ if (!silent) report("dead", state, /* @__PURE__ */ new Error(`Tried to update ${state}.${String(key)} but state is destroyed.`), String(key));
554
+ store[key] = value;
532
555
  return false;
533
556
  }
534
- const store = STORE.get(state);
535
- if (value instanceof State) value = value.is;
536
557
  if (key in store && value === store[key]) return false;
537
558
  store[key] = value;
538
559
  if (!silent) event(state, key);
@@ -540,40 +561,23 @@ function update(state, key, value, silent, own) {
540
561
  return true;
541
562
  }
542
563
  /**
543
- * Pass a caught error along `catch` handlers - most-derived class first, last registered
544
- * first - until one returns nothing. Passed off the end, a warning logs and anything
545
- * else is thrown - to the caller when `sync`, else uncaught. So is a handler's throw,
546
- * and one wrapping an error a handler already threw escapes that error unreported.
564
+ * Pass an error along `catch` handlers - most-derived class first, last registered
565
+ * first, one on several classes once at the outermost - until one returns nothing. Passed off
566
+ * the end, a destroyed write outputs nothing, `unused` warns, anything else escapes uncaught;
567
+ * so does a handler's throw.
547
568
  */
548
- function report(caught, sync) {
549
- const handlers = /* @__PURE__ */ new Set();
550
- let error = caught;
551
- if (caught.cause instanceof Caught) return escape(caught.cause);
552
- for (let T = caught.state.constructor;; T = Object.getPrototypeOf(T)) {
553
- for (const handler of [...SETUP.get(T) || []].reverse()) if (typeof handler == "object" && handler.catch) handlers.add(handler.catch);
554
- if (T === State) break;
555
- }
569
+ function report(kind, owner, error, key) {
570
+ const state = owner instanceof State ? owner : owner && PARENT.get(owner);
571
+ if (!state) return REPORT.error(error);
556
572
  try {
557
- for (const handler of handlers) if (!(error = handler.call(caught.state, error))) return;
573
+ for (const handler of [...stages(state.constructor, "catch")].reverse()) if ((error = handler.call(state, error, kind, key)) === void 0) return;
574
+ if (kind == "unused") console.warn(error);
575
+ else if (kind != "dead") REPORT.error(error);
558
576
  } catch (err) {
559
- if (sync) throw err;
560
- return escape(err);
577
+ REPORT.error(err);
561
578
  }
562
- if (error.warning) console.warn(error);
563
- else if (sync) throw error;
564
- else escape(error);
565
- }
566
- function escape(err) {
567
- queueMicrotask(() => {
568
- throw err;
569
- });
570
- }
571
- /** Error thrown by a handler replaying in dispatch, attributed to the State which queued it. */
572
- function fault(err, owner) {
573
- const state = owner instanceof State ? owner : owner && PARENT.get(owner);
574
- if (state) report(new Caught.Effect(state, err));
575
- else escape(err);
576
579
  }
580
+ REPORT.as = report;
577
581
  /**
578
582
  * Hand a value stored by an owning writer to that property's adopter,
579
583
  * creating one the first time a State is stored there. A property which has
@@ -598,101 +602,6 @@ function latest(getter) {
598
602
  for (let next; next = LATEST.get(getter);) getter = next;
599
603
  return getter;
600
604
  }
601
- /** Begin tracking live instances, so a hot patch can reach them. */
602
- function track() {
603
- LIVE ||= /* @__PURE__ */ new Set();
604
- }
605
- /**
606
- * Move the members of `next` onto `prev`, which keeps its identity. Returns the
607
- * live instances of `prev`, each needing a refresh.
608
- */
609
- function patch(prev, next, own) {
610
- const proto = prev.prototype;
611
- const incoming = Object.getOwnPropertyDescriptors(next.prototype);
612
- const keys = METHODS.get(prev);
613
- const getters = GETTERS.get(prev);
614
- const methods = /* @__PURE__ */ new Map();
615
- const knownKeys = new Set(keys?.keys());
616
- const knownGetters = new Set(getters?.keys());
617
- let setup = SETUP.get(prev);
618
- for (const handler of own) setup.delete(handler);
619
- for (const handler of handlers(next)) {
620
- if (!setup) SETUP.set(prev, setup = /* @__PURE__ */ new Set());
621
- setup.add(handler);
622
- }
623
- for (const key of Reflect.ownKeys(next)) {
624
- if (key == "prototype" || key == "length" || key == "name") continue;
625
- if (Object.getOwnPropertyDescriptor(prev, key)?.configurable !== false) define(prev, key, Object.getOwnPropertyDescriptor(next, key));
626
- }
627
- const removed = /* @__PURE__ */ new Set();
628
- for (const key of Object.getOwnPropertyNames(proto)) if (key != "constructor" && !(key in incoming)) {
629
- const bind = keys?.get(key);
630
- if (bind && Object.getOwnPropertyDescriptor(proto, key).get === bind) {
631
- PAST.add(UNBIND.get(bind));
632
- keys.delete(key);
633
- }
634
- removed.add(key);
635
- Reflect.deleteProperty(proto, key);
636
- getters?.delete(key);
637
- }
638
- for (const [key, desc] of Object.entries(incoming)) {
639
- if (key == "constructor") continue;
640
- const current = Object.getOwnPropertyDescriptor(proto, key);
641
- if (typeof desc.value == "function") methods.set(key, desc.value);
642
- if (current?.configurable === false) {
643
- if (current.writable) proto[key] = desc.value;
644
- continue;
645
- }
646
- if (current?.get && keys?.get(key) === current.get) PAST.add(UNBIND.get(current.get));
647
- const getter = getters?.get(key);
648
- if (getter) LATEST.set(getter, desc.get);
649
- define(proto, key, {
650
- ...desc,
651
- configurable: true
652
- });
653
- }
654
- if (keys) {
655
- for (let T = prev;; T = Object.getPrototypeOf(T)) {
656
- for (const handler of SETUP.get(T) || []) if (typeof handler == "object" && handler.type) handler.type(prev);
657
- if (T === State) break;
658
- }
659
- classify(prev, keys, getters);
660
- }
661
- const live = [];
662
- for (const ref of LIVE) {
663
- const state = ref.deref();
664
- if (!state) LIVE.delete(ref);
665
- else if (state instanceof prev) live.push(state);
666
- }
667
- const descendants = new Set([prev, ...live.map((state) => state.constructor)]);
668
- for (const type of descendants) for (const child of SUBCLASSES.get(type) || []) descendants.add(child);
669
- descendants.delete(prev);
670
- for (const type of descendants) {
671
- const inherit = METHODS.get(type);
672
- const computed = GETTERS.get(type);
673
- for (const [key, bind] of keys) if (!knownKeys.has(key) && !inherit.has(key)) inherit.set(key, bind);
674
- for (const [key, get] of getters) if (!knownGetters.has(key) && !computed.has(key)) computed.set(key, get);
675
- for (const key of removed) if (!(key in type.prototype)) {
676
- inherit.delete(key);
677
- computed.delete(key);
678
- }
679
- }
680
- for (const state of live) {
681
- for (const [key, value] of methods) {
682
- const desc = Object.getOwnPropertyDescriptor(state, key);
683
- if (!desc) continue;
684
- if (desc.set) desc.set.call(state, value);
685
- else if (PAST.has(UNBIND.get(desc.value))) delete state[key];
686
- }
687
- for (const key of removed) if (PAST.has(UNBIND.get(Object.getOwnPropertyDescriptor(state, key)?.value))) delete state[key];
688
- for (const [key, get] of GETTERS.get(state.constructor)) if (!knownGetters.has(key) && get === getters.get(key)) compute.call(state, get, key);
689
- }
690
- return live;
691
- }
692
- /** Lifecycle handlers registered on a class itself. */
693
- function handlers(type) {
694
- return [...SETUP.get(type) || []];
695
- }
696
605
  /** Random alphanumberic of length 6; always starts with a letter. */
697
606
  function uid() {
698
607
  return (.278 + Math.random() * .722).toString(36).substring(2, 8).toUpperCase();
@@ -708,5 +617,5 @@ function parent(child, value) {
708
617
  }
709
618
 
710
619
  //#endregion
711
- export { PENDING, STORE, State, access, apply, children, compute, event, fault, handlers, parent, patch, track, trailing, uid, unbind, update };
620
+ export { GETTERS, LATEST, METHODS, PENDING, SETUP, STORE, State, UNBIND, access, adopt, apply, children, classify, compute, event, owned, parent, trailing, uid, unbind, update };
712
621
  //# sourceMappingURL=state.js.map