tutuca 0.9.106 → 0.9.107

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.
@@ -4248,6 +4248,9 @@ class Step {
4248
4248
  pinKey(_v) {
4249
4249
  return this;
4250
4250
  }
4251
+ toKey() {
4252
+ return null;
4253
+ }
4251
4254
  }
4252
4255
  function warnRawDynStep(op, step) {
4253
4256
  console.warn(`Path.${op} reached a DynStep: call toTransactionPath() first`, step);
@@ -4332,6 +4335,15 @@ class Path {
4332
4335
  }
4333
4336
  return out;
4334
4337
  }
4338
+ toKeys() {
4339
+ const out = [];
4340
+ for (const step of this.steps) {
4341
+ const k = step.toKey();
4342
+ if (k !== null)
4343
+ out.push(k);
4344
+ }
4345
+ return out;
4346
+ }
4335
4347
  setValue(root, v) {
4336
4348
  const intermediates = new Array(this.steps.length);
4337
4349
  let curVal = root;
@@ -4589,6 +4601,9 @@ var init_path = __esm(() => {
4589
4601
  withKey(k) {
4590
4602
  return new SeqStep(this.field, k);
4591
4603
  }
4604
+ toKey() {
4605
+ return { field: this.field };
4606
+ }
4592
4607
  };
4593
4608
  SeqStep = class SeqStep extends Step {
4594
4609
  constructor(field, key) {
@@ -4607,6 +4622,9 @@ var init_path = __esm(() => {
4607
4622
  enterFrame(stack, _prev, next) {
4608
4623
  return stack.enter(next, { key: this.key }, true);
4609
4624
  }
4625
+ toKey() {
4626
+ return { field: this.field, key: this.key };
4627
+ }
4610
4628
  };
4611
4629
  SeqAccessStep = class SeqAccessStep extends Step {
4612
4630
  constructor(seqField, keyField) {
@@ -4628,6 +4646,9 @@ var init_path = __esm(() => {
4628
4646
  const key = v?.get(this.keyField, NONE);
4629
4647
  return key === NONE ? this : new SeqStep(this.seqField, key);
4630
4648
  }
4649
+ toKey() {
4650
+ return { field: this.seqField };
4651
+ }
4631
4652
  };
4632
4653
  EachBindStep = class EachBindStep extends Step {
4633
4654
  constructor(iterInfo, key) {
@@ -15154,12 +15175,47 @@ class Transactor {
15154
15175
  this.transactions = [];
15155
15176
  this.state = new State2(rootValue);
15156
15177
  this.onTransactionPushed = () => {};
15178
+ this._observers = [];
15157
15179
  this._inflight = new Set;
15158
15180
  }
15159
15181
  pushTransaction(t) {
15160
15182
  this.transactions.push(t);
15161
15183
  this.onTransactionPushed(t);
15162
15184
  }
15185
+ observe(cb) {
15186
+ this._observers.push(cb);
15187
+ return () => {
15188
+ const i = this._observers.indexOf(cb);
15189
+ if (i !== -1)
15190
+ this._observers.splice(i, 1);
15191
+ };
15192
+ }
15193
+ _emit(record) {
15194
+ for (const cb of this._observers)
15195
+ cb(record);
15196
+ }
15197
+ _emitTransaction(transaction, root) {
15198
+ if (this._observers.length === 0)
15199
+ return;
15200
+ if (transaction._resolvedHandler === undefined)
15201
+ return;
15202
+ const path = transaction.getTransactionPath().pinKeys(root);
15203
+ this._emit({
15204
+ kind: transaction.observeKind,
15205
+ name: transaction.observeName,
15206
+ args: transaction.args ?? null,
15207
+ path,
15208
+ pathKeys: path.toKeys(),
15209
+ targetPath: transaction.targetPath ?? transaction.path,
15210
+ handler: transaction._resolvedHandler ?? null,
15211
+ handlerName: transaction._resolvedHandler?.name || null,
15212
+ matched: transaction._matched ?? null,
15213
+ before: transaction._before,
15214
+ after: transaction._after,
15215
+ parent: transaction.parentTransaction,
15216
+ timestamp: Date.now()
15217
+ });
15218
+ }
15163
15219
  _link(child, parent) {
15164
15220
  if (parent) {
15165
15221
  const release = parent.completion.track();
@@ -15210,7 +15266,26 @@ class Transactor {
15210
15266
  const curRoot = this.state.val;
15211
15267
  const txnPath = path.toTransactionPath();
15212
15268
  const curLeaf = txnPath.lookup(curRoot);
15213
- const handler = this.comps.getRequestFor(curLeaf, name) ?? mkReq404(name);
15269
+ const found = this.comps.getRequestFor(curLeaf, name);
15270
+ const handler = found ?? mkReq404(name);
15271
+ if (this._observers.length > 0) {
15272
+ const reqPath = txnPath.pinKeys(curRoot);
15273
+ this._emit({
15274
+ kind: "request",
15275
+ name,
15276
+ args,
15277
+ path: reqPath,
15278
+ pathKeys: reqPath.toKeys(),
15279
+ targetPath: path,
15280
+ handler: found?.fn ?? null,
15281
+ handlerName: found?.fn?.name || name,
15282
+ matched: found ? "exact" : "none",
15283
+ before: curLeaf,
15284
+ after: undefined,
15285
+ parent,
15286
+ timestamp: Date.now()
15287
+ });
15288
+ }
15214
15289
  const reqCtx = new RequestContext(path, this, parent, curRoot);
15215
15290
  const resHandlerName = opts?.onResName ?? name;
15216
15291
  const resPath = opts?.livePath ? null : txnPath.pinKeys(curRoot);
@@ -15245,6 +15320,7 @@ class Transactor {
15245
15320
  if (newState !== undefined) {
15246
15321
  this.state.set(newState, { transaction });
15247
15322
  transaction.afterTransaction();
15323
+ this._emitTransaction(transaction, curState);
15248
15324
  } else
15249
15325
  console.warn("undefined new state", { curState, transaction });
15250
15326
  } finally {
@@ -15293,8 +15369,15 @@ class Transaction {
15293
15369
  buildStack(root, comps) {
15294
15370
  return this.path.toTransactionPath().buildStack(this.buildRootStack(root, comps));
15295
15371
  }
15372
+ get observeKind() {
15373
+ return null;
15374
+ }
15375
+ get observeName() {
15376
+ return null;
15377
+ }
15296
15378
  callHandler(root, instance, comps) {
15297
15379
  const [handler, args] = this.getHandlerAndArgs(root, instance, comps);
15380
+ this._resolvedHandler = handler;
15298
15381
  return handler.apply(instance, args);
15299
15382
  }
15300
15383
  getHandlerAndArgs(_root, _instance, _comps) {
@@ -15307,6 +15390,8 @@ class Transaction {
15307
15390
  const txnPath = this.getTransactionPath();
15308
15391
  const curLeaf = txnPath.lookup(curRoot);
15309
15392
  const newLeaf = this.callHandler(curRoot, curLeaf, comps);
15393
+ this._before = curLeaf;
15394
+ this._after = newLeaf;
15310
15395
  this._completion?.markSelfSettled({ value: newLeaf, old: curLeaf });
15311
15396
  return curLeaf !== newLeaf ? txnPath.setValue(curRoot, newLeaf) : curRoot;
15312
15397
  }
@@ -15442,6 +15527,12 @@ var init_transactor = __esm(() => {
15442
15527
  this._dispatchPath ??= this.path.compact();
15443
15528
  return this._dispatchPath;
15444
15529
  }
15530
+ get observeKind() {
15531
+ return "input";
15532
+ }
15533
+ get observeName() {
15534
+ return this.e?.type ?? null;
15535
+ }
15445
15536
  buildRootStack(root, comps) {
15446
15537
  return Stack2.root(comps, root, this);
15447
15538
  }
@@ -15510,9 +15601,26 @@ var init_transactor = __esm(() => {
15510
15601
  this.targetPath = path;
15511
15602
  }
15512
15603
  handlerProp = null;
15604
+ get observeKind() {
15605
+ return this.handlerProp;
15606
+ }
15607
+ get observeName() {
15608
+ return this.name;
15609
+ }
15513
15610
  getHandlerForName(comp) {
15514
15611
  const handlers = comp?.[this.handlerProp];
15515
- return handlers?.[this.name] ?? handlers?.$unknown ?? nullHandler;
15612
+ const exact = handlers?.[this.name];
15613
+ if (exact) {
15614
+ this._matched = "exact";
15615
+ return exact;
15616
+ }
15617
+ const unknown = handlers?.$unknown;
15618
+ if (unknown) {
15619
+ this._matched = "unknown";
15620
+ return unknown;
15621
+ }
15622
+ this._matched = "none";
15623
+ return nullHandler;
15516
15624
  }
15517
15625
  getHandlerAndArgs(_root, instance, comps) {
15518
15626
  const handler = this.getHandlerForName(comps.getCompFor(instance));
@@ -15722,6 +15830,9 @@ class App {
15722
15830
  onChange(callback) {
15723
15831
  this.transactor.state.onChange(callback);
15724
15832
  }
15833
+ observe(callback) {
15834
+ return this.transactor.observe(callback);
15835
+ }
15725
15836
  compile() {
15726
15837
  for (const Comp of this.comps.byId.values()) {
15727
15838
  Comp.compile(this.ParseContext);
@@ -1402,6 +1402,12 @@ function getComponents4() {
1402
1402
  ];
1403
1403
  }
1404
1404
 
1405
+ // src/components/tutuca/activity-inspector.js
1406
+ import { component as component9, html as html9 } from "tutuca";
1407
+
1408
+ // src/components/tutuca/instance-inspector.js
1409
+ import { component as component8, html as html8, isRecord } from "tutuca";
1410
+
1405
1411
  // src/components/tutuca/component-inspector.js
1406
1412
  import { component as component5, html as html5 } from "tutuca";
1407
1413
  function introspectComponent(comp) {
@@ -1579,9 +1585,6 @@ function getComponents5() {
1579
1585
  return [ComponentInspector, CompSection, CompField, CompName, CompView, ...getComponents2()];
1580
1586
  }
1581
1587
 
1582
- // src/components/tutuca/instance-inspector.js
1583
- import { component as component8, html as html8, isRecord } from "tutuca";
1584
-
1585
1588
  // src/components/tutuca/lint-inspector.js
1586
1589
  import { component as component6, html as html6 } from "tutuca";
1587
1590
  var tagDisplay = (tag) => tag ? String(tag).toLowerCase() : "";
@@ -2235,6 +2238,120 @@ function getComponents8() {
2235
2238
  });
2236
2239
  }
2237
2240
 
2241
+ // src/components/tutuca/activity-inspector.js
2242
+ var ACTIVITY_CAP = 50;
2243
+ var ActivityEntry = component9({
2244
+ name: "ActivityEntry",
2245
+ fields: {
2246
+ kind: "",
2247
+ name: "",
2248
+ handlerName: "",
2249
+ matched: "",
2250
+ pathLabel: "",
2251
+ time: "",
2252
+ before: null,
2253
+ after: null,
2254
+ hasAfter: true
2255
+ },
2256
+ view: html9`<div
2257
+ class="rounded border border-base-content/15 p-2 text-sm flex flex-col gap-1"
2258
+ >
2259
+ <div class="flex items-center gap-2 flex-wrap">
2260
+ <span
2261
+ title="How this was dispatched: receive (send), bubble, response, input, or request"
2262
+ class="badge badge-sm badge-neutral"
2263
+ @text=".kind"
2264
+ ></span>
2265
+ <span
2266
+ title="Message name dispatched — for a DOM input, the event type (click, input, change, keydown, …)"
2267
+ class="font-mono font-semibold"
2268
+ @text=".name"
2269
+ ></span>
2270
+ <span
2271
+ title="Actual handler that ran — shown only when it differs from the name (e.g. a $unknown fallback)"
2272
+ class="text-xs opacity-60 font-mono"
2273
+ @show="truthy? .handlerName"
2274
+ @text=".handlerName"
2275
+ ></span>
2276
+ <span
2277
+ title="Path to the target component"
2278
+ class="text-xs opacity-40 font-mono"
2279
+ @text=".pathLabel"
2280
+ ></span>
2281
+ <span
2282
+ title="Handler resolution — exact: matched by name · unknown: $unknown fallback · none: no handler (no-op)"
2283
+ class="badge badge-xs badge-ghost"
2284
+ @show="truthy? .matched"
2285
+ @text=".matched"
2286
+ ></span>
2287
+ <span
2288
+ title="Time this event ran (HH:MM:SS.mmm)"
2289
+ class="ml-auto text-xs opacity-40 font-mono tabular-nums"
2290
+ @text=".time"
2291
+ ></span>
2292
+ </div>
2293
+ <div class="flex gap-3 items-start font-mono text-xs">
2294
+ <div class="flex-1 min-w-0">
2295
+ <div class="opacity-50" title="Target state before the handler ran">before</div>
2296
+ <x render=".before"></x>
2297
+ </div>
2298
+ <div class="flex-1 min-w-0" @show=".hasAfter">
2299
+ <div class="opacity-50" title="Target state after the handler ran">after</div>
2300
+ <x render=".after"></x>
2301
+ </div>
2302
+ </div>
2303
+ </div>`
2304
+ });
2305
+ var ActivityLog = component9({
2306
+ name: "ActivityLog",
2307
+ fields: { events: [], hasEvents: false },
2308
+ methods: {
2309
+ appendEntry(entry) {
2310
+ return this.setEvents(this.events.unshift(entry).slice(0, ACTIVITY_CAP)).setHasEvents(true);
2311
+ }
2312
+ },
2313
+ view: html9`<div class="p-3 flex flex-col gap-2 max-h-[60vh] overflow-y-auto">
2314
+ <div class="text-xs opacity-40" @hide=".hasEvents">
2315
+ No activity yet — interact with the component.
2316
+ </div>
2317
+ <x render-each=".events"></x>
2318
+ </div>`
2319
+ });
2320
+ function fmtActivityTime(ts) {
2321
+ const d = new Date(ts);
2322
+ const p = (n, w = 2) => String(n).padStart(w, "0");
2323
+ return `${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}.${p(d.getMilliseconds(), 3)}`;
2324
+ }
2325
+ function pathKeysLabel(pathKeys) {
2326
+ return pathKeys.map((k) => k.key === undefined ? k.field : `${k.field}[${k.key}]`).join(".");
2327
+ }
2328
+ function makeInspect(app) {
2329
+ return (value) => {
2330
+ const comp = isComponentInstance(value) ? app.comps.getCompFor(value) : null;
2331
+ return InstanceInspector.Class.fromData(value, comp);
2332
+ };
2333
+ }
2334
+ function recordToEntry(record, { inspect, pathLabel = pathKeysLabel } = {}) {
2335
+ const hasAfter = record.after !== undefined;
2336
+ const primary = record.name ?? record.handlerName ?? "?";
2337
+ const handler = record.handlerName ?? "";
2338
+ const handlerLabel = handler && handler !== primary ? `→ ${handler}` : "";
2339
+ return ActivityEntry.make({
2340
+ kind: record.kind ?? "",
2341
+ name: primary,
2342
+ handlerName: handlerLabel,
2343
+ matched: record.matched ?? "",
2344
+ pathLabel: pathLabel(record.pathKeys),
2345
+ time: fmtActivityTime(record.timestamp),
2346
+ before: inspect(record.before),
2347
+ after: hasAfter ? inspect(record.after) : null,
2348
+ hasAfter
2349
+ });
2350
+ }
2351
+ function getComponents9() {
2352
+ return [ActivityLog, ActivityEntry];
2353
+ }
2354
+
2238
2355
  // src/components/build-views.js
2239
2356
  async function buildInspectorViews(value, scope, { getTests = null, components = [], dev = null, name } = {}) {
2240
2357
  const comp = isComponentInstance(value) ? scope.getCompFor(value) : null;
@@ -2273,8 +2390,9 @@ async function buildInspectorViews(value, scope, { getTests = null, components =
2273
2390
  }
2274
2391
 
2275
2392
  // src/components/index.js
2276
- function getComponents9() {
2393
+ function getComponents10() {
2277
2394
  const all = [
2395
+ ...getComponents9(),
2278
2396
  ...getComponents5(),
2279
2397
  ...getComponents8(),
2280
2398
  ...getComponents6(),
@@ -2294,13 +2412,17 @@ function getComponents9() {
2294
2412
  }
2295
2413
  export {
2296
2414
  valueWrapperMethods,
2415
+ recordToEntry,
2416
+ pathKeysLabel,
2297
2417
  makeValueInspector,
2418
+ makeInspect,
2298
2419
  makeCompositeView,
2299
2420
  lintMessage,
2300
2421
  isComponentInstance,
2301
2422
  introspectComponent,
2302
- getComponents9 as getComponents,
2423
+ getComponents10 as getComponents,
2303
2424
  fmtAnyKey,
2425
+ fmtActivityTime,
2304
2426
  compositeView,
2305
2427
  compositeMethods,
2306
2428
  compositeFields,
@@ -2369,5 +2491,7 @@ export {
2369
2491
  CompView,
2370
2492
  CompSection,
2371
2493
  CompName,
2372
- CompField
2494
+ CompField,
2495
+ ActivityLog,
2496
+ ActivityEntry
2373
2497
  };
@@ -117,6 +117,9 @@ class Step {
117
117
  pinKey(_v) {
118
118
  return this;
119
119
  }
120
+ toKey() {
121
+ return null;
122
+ }
120
123
  }
121
124
 
122
125
  class BindStep extends Step {
@@ -178,6 +181,9 @@ class FieldStep extends Step {
178
181
  withKey(k) {
179
182
  return new SeqStep(this.field, k);
180
183
  }
184
+ toKey() {
185
+ return { field: this.field };
186
+ }
181
187
  }
182
188
 
183
189
  class SeqStep extends Step {
@@ -197,6 +203,9 @@ class SeqStep extends Step {
197
203
  enterFrame(stack, _prev, next) {
198
204
  return stack.enter(next, { key: this.key }, true);
199
205
  }
206
+ toKey() {
207
+ return { field: this.field, key: this.key };
208
+ }
200
209
  }
201
210
 
202
211
  class SeqAccessStep extends Step {
@@ -219,6 +228,9 @@ class SeqAccessStep extends Step {
219
228
  const key = v?.get(this.keyField, NONE);
220
229
  return key === NONE ? this : new SeqStep(this.seqField, key);
221
230
  }
231
+ toKey() {
232
+ return { field: this.seqField };
233
+ }
222
234
  }
223
235
 
224
236
  class EachBindStep extends Step {
@@ -374,6 +386,15 @@ class Path {
374
386
  }
375
387
  return out;
376
388
  }
389
+ toKeys() {
390
+ const out = [];
391
+ for (const step of this.steps) {
392
+ const k = step.toKey();
393
+ if (k !== null)
394
+ out.push(k);
395
+ }
396
+ return out;
397
+ }
377
398
  setValue(root, v) {
378
399
  const intermediates = new Array(this.steps.length);
379
400
  let curVal = root;
@@ -6285,12 +6306,47 @@ class Transactor {
6285
6306
  this.transactions = [];
6286
6307
  this.state = new State2(rootValue);
6287
6308
  this.onTransactionPushed = () => {};
6309
+ this._observers = [];
6288
6310
  this._inflight = new Set;
6289
6311
  }
6290
6312
  pushTransaction(t) {
6291
6313
  this.transactions.push(t);
6292
6314
  this.onTransactionPushed(t);
6293
6315
  }
6316
+ observe(cb) {
6317
+ this._observers.push(cb);
6318
+ return () => {
6319
+ const i = this._observers.indexOf(cb);
6320
+ if (i !== -1)
6321
+ this._observers.splice(i, 1);
6322
+ };
6323
+ }
6324
+ _emit(record) {
6325
+ for (const cb of this._observers)
6326
+ cb(record);
6327
+ }
6328
+ _emitTransaction(transaction, root) {
6329
+ if (this._observers.length === 0)
6330
+ return;
6331
+ if (transaction._resolvedHandler === undefined)
6332
+ return;
6333
+ const path = transaction.getTransactionPath().pinKeys(root);
6334
+ this._emit({
6335
+ kind: transaction.observeKind,
6336
+ name: transaction.observeName,
6337
+ args: transaction.args ?? null,
6338
+ path,
6339
+ pathKeys: path.toKeys(),
6340
+ targetPath: transaction.targetPath ?? transaction.path,
6341
+ handler: transaction._resolvedHandler ?? null,
6342
+ handlerName: transaction._resolvedHandler?.name || null,
6343
+ matched: transaction._matched ?? null,
6344
+ before: transaction._before,
6345
+ after: transaction._after,
6346
+ parent: transaction.parentTransaction,
6347
+ timestamp: Date.now()
6348
+ });
6349
+ }
6294
6350
  _link(child, parent) {
6295
6351
  if (parent) {
6296
6352
  const release = parent.completion.track();
@@ -6341,7 +6397,26 @@ class Transactor {
6341
6397
  const curRoot = this.state.val;
6342
6398
  const txnPath = path.toTransactionPath();
6343
6399
  const curLeaf = txnPath.lookup(curRoot);
6344
- const handler = this.comps.getRequestFor(curLeaf, name) ?? mkReq404(name);
6400
+ const found = this.comps.getRequestFor(curLeaf, name);
6401
+ const handler = found ?? mkReq404(name);
6402
+ if (this._observers.length > 0) {
6403
+ const reqPath = txnPath.pinKeys(curRoot);
6404
+ this._emit({
6405
+ kind: "request",
6406
+ name,
6407
+ args,
6408
+ path: reqPath,
6409
+ pathKeys: reqPath.toKeys(),
6410
+ targetPath: path,
6411
+ handler: found?.fn ?? null,
6412
+ handlerName: found?.fn?.name || name,
6413
+ matched: found ? "exact" : "none",
6414
+ before: curLeaf,
6415
+ after: undefined,
6416
+ parent,
6417
+ timestamp: Date.now()
6418
+ });
6419
+ }
6345
6420
  const reqCtx = new RequestContext(path, this, parent, curRoot);
6346
6421
  const resHandlerName = opts?.onResName ?? name;
6347
6422
  const resPath = opts?.livePath ? null : txnPath.pinKeys(curRoot);
@@ -6376,6 +6451,7 @@ class Transactor {
6376
6451
  if (newState !== undefined) {
6377
6452
  this.state.set(newState, { transaction });
6378
6453
  transaction.afterTransaction();
6454
+ this._emitTransaction(transaction, curState);
6379
6455
  } else
6380
6456
  console.warn("undefined new state", { curState, transaction });
6381
6457
  } finally {
@@ -6424,8 +6500,15 @@ class Transaction {
6424
6500
  buildStack(root, comps) {
6425
6501
  return this.path.toTransactionPath().buildStack(this.buildRootStack(root, comps));
6426
6502
  }
6503
+ get observeKind() {
6504
+ return null;
6505
+ }
6506
+ get observeName() {
6507
+ return null;
6508
+ }
6427
6509
  callHandler(root, instance, comps) {
6428
6510
  const [handler, args] = this.getHandlerAndArgs(root, instance, comps);
6511
+ this._resolvedHandler = handler;
6429
6512
  return handler.apply(instance, args);
6430
6513
  }
6431
6514
  getHandlerAndArgs(_root, _instance, _comps) {
@@ -6438,6 +6521,8 @@ class Transaction {
6438
6521
  const txnPath = this.getTransactionPath();
6439
6522
  const curLeaf = txnPath.lookup(curRoot);
6440
6523
  const newLeaf = this.callHandler(curRoot, curLeaf, comps);
6524
+ this._before = curLeaf;
6525
+ this._after = newLeaf;
6441
6526
  this._completion?.markSelfSettled({ value: newLeaf, old: curLeaf });
6442
6527
  return curLeaf !== newLeaf ? txnPath.setValue(curRoot, newLeaf) : curRoot;
6443
6528
  }
@@ -6462,6 +6547,12 @@ class InputEvent extends Transaction {
6462
6547
  this._dispatchPath ??= this.path.compact();
6463
6548
  return this._dispatchPath;
6464
6549
  }
6550
+ get observeKind() {
6551
+ return "input";
6552
+ }
6553
+ get observeName() {
6554
+ return this.e?.type ?? null;
6555
+ }
6465
6556
  buildRootStack(root, comps) {
6466
6557
  return Stack.root(comps, root, this);
6467
6558
  }
@@ -6531,9 +6622,26 @@ class NameArgsTransaction extends Transaction {
6531
6622
  this.targetPath = path;
6532
6623
  }
6533
6624
  handlerProp = null;
6625
+ get observeKind() {
6626
+ return this.handlerProp;
6627
+ }
6628
+ get observeName() {
6629
+ return this.name;
6630
+ }
6534
6631
  getHandlerForName(comp) {
6535
6632
  const handlers = comp?.[this.handlerProp];
6536
- return handlers?.[this.name] ?? handlers?.$unknown ?? nullHandler;
6633
+ const exact = handlers?.[this.name];
6634
+ if (exact) {
6635
+ this._matched = "exact";
6636
+ return exact;
6637
+ }
6638
+ const unknown = handlers?.$unknown;
6639
+ if (unknown) {
6640
+ this._matched = "unknown";
6641
+ return unknown;
6642
+ }
6643
+ this._matched = "none";
6644
+ return nullHandler;
6537
6645
  }
6538
6646
  getHandlerAndArgs(_root, instance, comps) {
6539
6647
  const handler = this.getHandlerForName(comps.getCompFor(instance));
@@ -7564,6 +7672,9 @@ class App {
7564
7672
  onChange(callback) {
7565
7673
  this.transactor.state.onChange(callback);
7566
7674
  }
7675
+ observe(callback) {
7676
+ return this.transactor.observe(callback);
7677
+ }
7567
7678
  compile() {
7568
7679
  for (const Comp of this.comps.byId.values()) {
7569
7680
  Comp.compile(this.ParseContext);
@@ -8467,6 +8578,7 @@ export {
8467
8578
  setIn,
8468
8579
  set,
8469
8580
  runTests,
8581
+ rootDispatcher,
8470
8582
  resolveArgs,
8471
8583
  reportTestReportToConsole,
8472
8584
  removeIn,