tutuca 0.13.3 → 0.15.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.
@@ -128,8 +128,8 @@ function jestMatchers(chai2, utils) {
128
128
  expected
129
129
  );
130
130
  });
131
- m("toHaveProperty", function(path, ...rest) {
132
- const keys = Array.isArray(path) ? path : String(path).split(".");
131
+ m("toHaveProperty", function(path2, ...rest) {
132
+ const keys = Array.isArray(path2) ? path2 : String(path2).split(".");
133
133
  let cur = this._obj;
134
134
  let found = true;
135
135
  for (const k of keys) {
@@ -144,7 +144,7 @@ function jestMatchers(chai2, utils) {
144
144
  pass,
145
145
  "expected #{this} to have property #{exp}",
146
146
  "expected #{this} not to have property #{exp}",
147
- path
147
+ path2
148
148
  );
149
149
  });
150
150
  m("toBeInstanceOf", function(ctor) {
@@ -209,12 +209,6 @@ enableMapSet();
209
209
 
210
210
  // src/path.js
211
211
  var NONE = /* @__PURE__ */ Symbol("NONE");
212
- var readKey = (value, key, dval = null) => {
213
- if (value == null) return dval;
214
- if (value instanceof Map) return value.has(key) ? value.get(key) : dval;
215
- if (value instanceof Set) return value.has(key) ? key : dval;
216
- return Object.hasOwn(value, key) ? value[key] : dval;
217
- };
218
212
  var writeKey = (value, key, next) => {
219
213
  if (value instanceof Map) value.set(key, next);
220
214
  else if (value instanceof Set) {
@@ -222,20 +216,15 @@ var writeKey = (value, key, next) => {
222
216
  value.add(next);
223
217
  } else value[key] = next;
224
218
  };
225
- var writeSeqKey = (value, key, next) => {
226
- if (value instanceof Map || value instanceof Set || Array.isArray(value) || Object.hasOwn(value, key))
227
- writeKey(value, key, next);
228
- else if (typeof value?.set === "function") value.set(key, next);
229
- else writeKey(value, key, next);
230
- };
219
+ var writeSeqKey = (value, key, next) => !(value instanceof Map || value instanceof Set || Array.isArray(value)) && !Object.hasOwn(value, key) && typeof value?.set === "function" ? value.set(key, next) : writeKey(value, key, next);
231
220
  var Step = class {
232
- lookup(_v, dval = null) {
233
- return dval;
234
- }
235
221
  setDraftValue(_root, _v) {
236
222
  }
237
- enterFrame(stack, next) {
238
- return stack.enter(next, {}, true);
223
+ // Re-enter this step while rebuilding a stack. `renderPath` is the dispatch
224
+ // position AFTER this step: a rebuilt frame must carry the same render path
225
+ // the renderer had there, or the provides it publishes would be located wrong.
226
+ enterFrame(stack, next, renderPath) {
227
+ return stack.enter(next, {}, true, renderPath);
239
228
  }
240
229
  toAbstractPathStep() {
241
230
  return this;
@@ -252,41 +241,29 @@ var Step = class {
252
241
  return null;
253
242
  }
254
243
  };
255
- var BindStep = class _BindStep extends Step {
244
+ var BindStep = class extends Step {
256
245
  constructor(binds) {
257
246
  super();
258
247
  this.binds = binds;
259
248
  }
260
- lookup(v, _dval) {
249
+ lookup(v) {
261
250
  return v;
262
251
  }
263
- enterFrame(stack, next) {
264
- return stack.enter(next, { ...this.binds }, false);
265
- }
266
- withIndex(i) {
267
- return new _BindStep({ ...this.binds, key: i });
268
- }
269
- withKey(key) {
270
- return new _BindStep({ ...this.binds, key });
252
+ enterFrame(stack, next, renderPath) {
253
+ return stack.enter(next, { ...this.binds }, false, renderPath);
271
254
  }
272
255
  toAbstractPathStep() {
273
256
  return null;
274
257
  }
275
258
  };
276
- var ScopeBindStep = class _ScopeBindStep extends BindStep {
259
+ var ScopeBindStep = class extends BindStep {
277
260
  constructor(val, binds = {}) {
278
261
  super(binds);
279
262
  this.val = val;
280
263
  }
281
- enterFrame(stack, next) {
264
+ enterFrame(stack, next, renderPath) {
282
265
  const dyn = this.val.evalAsHandler(stack)?.call(stack.it) ?? {};
283
- return stack.enter(next, { ...this.binds, ...dyn }, false);
284
- }
285
- withIndex(i) {
286
- return new _ScopeBindStep(this.val, { ...this.binds, key: i });
287
- }
288
- withKey(key) {
289
- return new _ScopeBindStep(this.val, { ...this.binds, key });
266
+ return stack.enter(next, { ...this.binds, ...dyn }, false, renderPath);
290
267
  }
291
268
  };
292
269
  var FieldStep = class extends Step {
@@ -294,18 +271,12 @@ var FieldStep = class extends Step {
294
271
  super();
295
272
  this.field = field;
296
273
  }
297
- lookup(v, dval = null) {
298
- return readKey(v, this.field, dval);
274
+ lookup(v) {
275
+ return seqGet(v, this.field, NONE);
299
276
  }
300
277
  setDraftValue(root, v) {
301
278
  writeKey(root, this.field, v);
302
279
  }
303
- withIndex(i) {
304
- return new SeqStep(this.field, i);
305
- }
306
- withKey(k) {
307
- return new SeqStep(this.field, k);
308
- }
309
280
  toKey() {
310
281
  return { field: this.field };
311
282
  }
@@ -316,15 +287,15 @@ var SeqStep = class extends Step {
316
287
  this.field = field;
317
288
  this.key = key;
318
289
  }
319
- lookup(v, dval = null) {
320
- return seqGet(readKey(v, this.field, null), this.key, dval);
290
+ lookup(v) {
291
+ return seqGet(seqGet(v, this.field, null), this.key, NONE);
321
292
  }
322
293
  setDraftValue(root, v) {
323
- const seq = readKey(root, this.field, null);
294
+ const seq = seqGet(root, this.field, null);
324
295
  if (seq != null) writeSeqKey(seq, this.key, v);
325
296
  }
326
- enterFrame(stack, next) {
327
- return stack.enter(next, { key: this.key }, true);
297
+ enterFrame(stack, next, renderPath) {
298
+ return stack.enter(next, { key: this.key }, true, renderPath);
328
299
  }
329
300
  toKey() {
330
301
  return { field: this.field, key: this.key };
@@ -336,20 +307,20 @@ var SeqAccessStep = class extends Step {
336
307
  this.seqField = seqField;
337
308
  this.keyField = keyField;
338
309
  }
339
- lookup(v, dval = null) {
340
- const seq = readKey(v, this.seqField, NONE);
341
- const key = readKey(v, this.keyField, NONE);
342
- return key !== NONE && seq !== NONE ? seqGet(seq, key, dval) : dval;
310
+ lookup(v) {
311
+ const seq = seqGet(v, this.seqField, NONE);
312
+ const key = seqGet(v, this.keyField, NONE);
313
+ return key !== NONE && seq !== NONE ? seqGet(seq, key, NONE) : NONE;
343
314
  }
344
315
  setDraftValue(root, v) {
345
- const seq = readKey(root, this.seqField, NONE);
346
- const key = readKey(root, this.keyField, NONE);
316
+ const seq = seqGet(root, this.seqField, NONE);
317
+ const key = seqGet(root, this.keyField, NONE);
347
318
  if (seq !== NONE && key !== NONE) writeSeqKey(seq, key, v);
348
319
  }
349
320
  // Resolve `keyField` against `v` now and freeze it as a literal-key `SeqStep`, so a
350
321
  // later lookup/setValue lands on this same item even if `keyField` changes meanwhile.
351
322
  pinKey(v) {
352
- const key = readKey(v, this.keyField, NONE);
323
+ const key = seqGet(v, this.keyField, NONE);
353
324
  return key === NONE ? this : new SeqStep(this.seqField, key);
354
325
  }
355
326
  // The key is a *field reference* resolved live, so it is unknown without a value;
@@ -360,70 +331,62 @@ var SeqAccessStep = class extends Step {
360
331
  }
361
332
  };
362
333
  var EachBindStep = class extends Step {
363
- constructor(iterInfo, key) {
334
+ constructor(eachNode, key) {
364
335
  super();
365
- this.iterInfo = iterInfo;
336
+ this.eachNode = eachNode;
366
337
  this.key = key;
367
338
  }
368
- lookup(v, _dval) {
339
+ lookup(v) {
369
340
  return v;
370
341
  }
371
342
  // Replay the renderer's per-item binds (key, value + any @enrich-with binds)
372
343
  // so a rebuilt stack matches the one @each rendered with.
373
- enterFrame(stack, next) {
374
- return stack.enter(next, this.iterInfo.enrichBinds(stack, this.key), false);
344
+ enterFrame(stack, next, renderPath) {
345
+ return stack.enter(next, this.eachNode.enrichBinds(stack, this.key), false, renderPath);
375
346
  }
376
347
  toAbstractPathStep() {
377
348
  return null;
378
349
  }
379
350
  };
380
351
  var EachRenderItStep = class extends SeqStep {
381
- enterFrame(stack, next) {
382
- return stack.enter(next, { key: this.key, value: next }, false).enter(next, {}, true);
352
+ enterFrame(stack, next, renderPath) {
353
+ return stack.enter(next, { key: this.key, value: next }, false, renderPath).enter(next, {}, true, renderPath);
383
354
  }
384
355
  toAbstractPathStep() {
385
356
  return new SeqStep(this.field, this.key);
386
357
  }
387
358
  };
388
- function warnRawDynStep(op, step) {
389
- console.warn(`Path.${op} reached a DynStep: call toTransactionPath() first`, step);
359
+ function stepToJson(step) {
360
+ if (step instanceof SeqStep) return { f: step.field, k: step.key };
361
+ if (step instanceof FieldStep) return { f: step.field };
362
+ if (step instanceof SeqAccessStep) return { f: step.seqField, a: step.keyField };
363
+ return null;
390
364
  }
391
- var DynStep = class extends Step {
392
- constructor(producerCompId, producerSteps) {
393
- super();
394
- this.producerCompId = producerCompId;
395
- this.producerSteps = producerSteps;
396
- this.interiorCids = /* @__PURE__ */ new Set();
397
- }
398
- // Steps spliced into the transaction path in place of this marker.
399
- teleportSteps() {
400
- return this.producerSteps;
401
- }
402
- lookup(_v, dval = null) {
403
- warnRawDynStep("lookup", this);
404
- return dval;
405
- }
406
- enterFrame(stack, _next) {
407
- warnRawDynStep("enterFrame", this);
408
- return stack;
409
- }
410
- };
411
- var DynEachStep = class extends DynStep {
412
- constructor(producerCompId, producerSteps, key) {
413
- super(producerCompId, producerSteps);
414
- this.key = key;
365
+ function stepFromJson(j) {
366
+ if (j == null || typeof j.f !== "string") return null;
367
+ if (j.k !== void 0) return new SeqStep(j.f, j.k);
368
+ if (j.a !== void 0) return new SeqAccessStep(j.f, j.a);
369
+ return new FieldStep(j.f);
370
+ }
371
+ function pathToJson(path2) {
372
+ const out = [];
373
+ for (const step of path2.steps) {
374
+ const j = stepToJson(step);
375
+ if (j === null) return null;
376
+ out.push(j);
415
377
  }
416
- teleportSteps() {
417
- const { producerSteps, key } = this;
418
- if (producerSteps.length === 0) return producerSteps;
419
- const last = producerSteps[producerSteps.length - 1];
420
- if (!(last instanceof FieldStep)) {
421
- console.warn("DynEachStep: seq-access dynamic cannot be iterated", this);
422
- return producerSteps;
423
- }
424
- return producerSteps.slice(0, -1).concat(new SeqStep(last.field, key));
378
+ return out;
379
+ }
380
+ function pathFromJson(j) {
381
+ if (!Array.isArray(j)) return null;
382
+ const steps = [];
383
+ for (const item of j) {
384
+ const step = stepFromJson(item);
385
+ if (step === null) return null;
386
+ steps.push(step);
425
387
  }
426
- };
388
+ return new Path(steps);
389
+ }
427
390
  var Path = class _Path {
428
391
  constructor(steps = []) {
429
392
  this.steps = steps;
@@ -431,43 +394,13 @@ var Path = class _Path {
431
394
  concat(steps) {
432
395
  return new _Path(this.steps.concat(steps));
433
396
  }
434
- popStep() {
435
- return new _Path(this.steps.slice(0, -1));
436
- }
437
- // The dispatch path: frame-only steps removed, one step per crossed component
438
- // (DynStep included). `popStep` over it bubbles through every component.
397
+ // Frame-only steps removed, one step per crossed component: `popStep` over the
398
+ // result bubbles through every component.
439
399
  compact() {
440
400
  const out = [];
441
401
  for (const step of this.steps) {
442
402
  const s = step.toAbstractPathStep();
443
- if (s !== null) {
444
- if (s !== step) s._originCid = step._originCid;
445
- out.push(s);
446
- }
447
- }
448
- return new _Path(out);
449
- }
450
- // The abstract path used to apply a transaction: every DynStep is teleported —
451
- // the steps interior to its producer..consumer span are dropped and the
452
- // producer's own path spliced in — so a mutation lands on the data's real
453
- // location. A path with no DynStep is returned unchanged.
454
- toTransactionPath() {
455
- let hasDyn = false;
456
- for (const step of this.steps)
457
- if (step instanceof DynStep) {
458
- hasDyn = true;
459
- break;
460
- }
461
- if (!hasDyn) return this;
462
- const out = [];
463
- for (const step of this.steps) {
464
- if (step instanceof DynStep) {
465
- while (out.length > 0 && step.interiorCids.has(out[out.length - 1]._originCid)) out.pop();
466
- for (const ts of step.teleportSteps()) {
467
- ts._originCid = step.producerCompId;
468
- out.push(ts);
469
- }
470
- } else out.push(step);
403
+ if (s !== null) out.push(s);
471
404
  }
472
405
  return new _Path(out);
473
406
  }
@@ -475,7 +408,6 @@ var Path = class _Path {
475
408
  // key as it is *now* so a later lookup/setValue lands on the same item even if the
476
409
  // keyField changed meanwhile (e.g. the selected tab moved while an intent was in
477
410
  // flight). Returns a new Path with those steps replaced; `this` if nothing pinned.
478
- // Must be called on a transaction path (no DynSteps — call toTransactionPath first).
479
411
  pinKeys(root) {
480
412
  let curVal = root;
481
413
  let out = null;
@@ -483,7 +415,7 @@ var Path = class _Path {
483
415
  const step = this.steps[i];
484
416
  const pinned = step.pinKey(curVal);
485
417
  if (pinned !== step) (out ??= this.steps.slice())[i] = pinned;
486
- curVal = step.lookup(curVal, NONE);
418
+ curVal = step.lookup(curVal);
487
419
  if (curVal === NONE) break;
488
420
  }
489
421
  return out ? new _Path(out) : this;
@@ -491,20 +423,20 @@ var Path = class _Path {
491
423
  lookup(v, dval = null) {
492
424
  let curVal = v;
493
425
  for (const step of this.steps) {
494
- curVal = step.lookup(curVal, NONE);
426
+ curVal = step.lookup(curVal);
495
427
  if (curVal === NONE) return dval;
496
428
  }
497
429
  return curVal;
498
430
  }
499
431
  // The values entered along the path, root→leaf (root included): index 0 is `root`,
500
432
  // the last entry is the leaf this path resolves to. Stops early at the first
501
- // unresolvable step. Call on a transaction path (no DynSteps). Used to walk the
502
- // component instances on a dispatch path (filter via Components.getCompFor).
433
+ // unresolvable step. Used to walk the component instances on a dispatch path
434
+ // (filter via Components.getCompFor).
503
435
  resolveChain(root) {
504
436
  const out = [root];
505
437
  let curVal = root;
506
438
  for (const step of this.steps) {
507
- curVal = step.lookup(curVal, NONE);
439
+ curVal = step.lookup(curVal);
508
440
  if (curVal === NONE) break;
509
441
  out.push(curVal);
510
442
  }
@@ -513,7 +445,7 @@ var Path = class _Path {
513
445
  // A flat `[{ field, key? }]` list of the addressing steps, skipping frame-only
514
446
  // steps (binds). Generic path introspection so tooling (e.g. the storybook
515
447
  // activity log) can identify which subtree a transaction touched without
516
- // depending on Step internals. Call on a transaction path (no DynSteps).
448
+ // depending on Step internals.
517
449
  toKeys() {
518
450
  const out = [];
519
451
  for (const step of this.steps) {
@@ -527,69 +459,146 @@ var Path = class _Path {
527
459
  return (0, immer_exports.produce)(root, (draft) => {
528
460
  let parent = draft;
529
461
  for (let i = 0; i < this.steps.length - 1; i++) {
530
- parent = this.steps[i].lookup(parent, NONE);
462
+ parent = this.steps[i].lookup(parent);
531
463
  if (parent === NONE) return;
532
464
  }
533
465
  this.steps.at(-1).setDraftValue(parent, v);
534
466
  });
535
467
  }
468
+ };
469
+ function walkItems(stack, items, renderPath, path2) {
470
+ let prev = stack.it;
471
+ for (const step of items) {
472
+ const next = step.lookup(prev);
473
+ if (next === NONE) {
474
+ console.warn("bad PathItem", { root: stack.it, step, path: path2 });
475
+ return null;
476
+ }
477
+ renderPath = renderPath.pushItem(step);
478
+ stack = step.enterFrame(stack, next, renderPath);
479
+ prev = next;
480
+ }
481
+ return [stack, renderPath];
482
+ }
483
+ var EMPTY_PATH = new Path([]);
484
+ var DispatchPath = class _DispatchPath {
485
+ constructor(frames = [{ base: EMPTY_PATH, items: [] }]) {
486
+ this.frames = frames;
487
+ }
488
+ // Plain addressing steps in one frame based at the root: what a caller means
489
+ // by "this position" when it has no continuation of its own.
490
+ static ofSteps(steps) {
491
+ return new _DispatchPath([{ base: EMPTY_PATH, items: steps.slice() }]);
492
+ }
493
+ get top() {
494
+ return this.frames[this.frames.length - 1];
495
+ }
496
+ _withTopItems(items) {
497
+ const frames = this.frames.slice();
498
+ frames[frames.length - 1] = { base: this.top.base, items };
499
+ return new _DispatchPath(frames);
500
+ }
501
+ concat(steps) {
502
+ return this._withTopItems(this.top.items.concat(steps));
503
+ }
504
+ pushItem(step) {
505
+ return this.concat([step]);
506
+ }
507
+ pushFrame(base) {
508
+ return new _DispatchPath(this.frames.concat({ base, items: [] }));
509
+ }
510
+ // Whether bubbling has anywhere left to go: another step in this frame, or a
511
+ // visual caller underneath it. (A path always has at least one frame.)
512
+ canPop() {
513
+ return this.frames.length > 1 || this.frames[0].items.length > 0;
514
+ }
515
+ // One component closer to the root. At the top of a frame that is popping back
516
+ // to the visual caller, not to the producer's own parent — the caller is where
517
+ // the `*name` was written, and where an unhandled message should keep going.
518
+ popStep() {
519
+ const { top } = this;
520
+ if (top.items.length > 0) return this._withTopItems(top.items.slice(0, -1));
521
+ if (this.frames.length > 1) return new _DispatchPath(this.frames.slice(0, -1));
522
+ return this;
523
+ }
524
+ // Drop frame-only steps inside every frame independently; a frame's base is
525
+ // already addressing-only.
526
+ compact() {
527
+ return new _DispatchPath(
528
+ this.frames.map(({ base, items }) => ({ base, items: new Path(items).compact().steps }))
529
+ );
530
+ }
531
+ // A stable string for the ADDRESS this path denotes, for the render cache. The
532
+ // same immutable value can sit at two places in the tree, and a subtree rendered
533
+ // at one of them bakes that address in — the provides it publishes are located
534
+ // there. Frame-only steps address nothing, so they are compacted out: two sites
535
+ // that differ only in binds do render the same subtree.
536
+ get addressKey() {
537
+ this._addressKey ??= renderPathText(this.toTransactionPath().compact());
538
+ return this._addressKey;
539
+ }
540
+ // The active transaction address: the top frame's absolute base followed by
541
+ // its ordinary descendant steps. This is where a mutation lands.
542
+ toTransactionPath() {
543
+ const { base, items } = this.top;
544
+ return base.concat(items);
545
+ }
546
+ // Rebuild the render stack this path was dispatched from. A frame with a base
547
+ // re-enters at that absolute value first — replaying the resume a `*name`
548
+ // render performed — and then walks its ordinary items.
536
549
  buildStack(stack) {
537
- let prev = stack.it;
538
- for (const step of this.steps) {
539
- const next = step.lookup(prev, NONE);
540
- if (next === NONE) {
541
- console.warn("bad PathItem", { root: stack.it, step, path: this });
542
- return null;
550
+ let renderPath = new _DispatchPath();
551
+ for (let i = 0; i < this.frames.length; i++) {
552
+ const { base, items } = this.frames[i];
553
+ if (i > 0 || base.steps.length > 0) {
554
+ const baseValue = base.lookup(stack.root, NONE);
555
+ if (baseValue === NONE) {
556
+ console.warn("bad frame base", { base, path: this });
557
+ return null;
558
+ }
559
+ renderPath = renderPath.pushFrame(base);
560
+ stack = stack.enter(baseValue, {}, true, renderPath);
543
561
  }
544
- stack = step.enterFrame(stack, next);
545
- prev = next;
562
+ const walked = walkItems(stack, items, renderPath, this);
563
+ if (walked === null) return null;
564
+ [stack, renderPath] = walked;
546
565
  }
547
566
  return stack;
548
567
  }
549
- static fromNodeAndEventName(node, eventName, rootNode, maxDepth, comps, stopOnNoEvent = true) {
550
- const pathSteps = [];
551
- const pendingDyns = [];
568
+ // Reconstruct the dispatch position of a DOM event from the `data-*` stamps and
569
+ // `§…§` meta comments the renderer left on the way down. `stopOnNoEvent` is off for
570
+ // drag events, whose path is needed even where no handler is registered.
571
+ static fromNodeAndEventName(node, eventName, rootNode, comps, stopOnNoEvent = true) {
572
+ const parts = [];
552
573
  const bubbles = BUBBLING_EVENTS.has(eventName);
553
- let depth = 0;
554
574
  let eventIds = [];
555
575
  let handlers = null;
556
- let nodeIds = [];
576
+ let nodeRefs = [];
557
577
  let isLeafComponent = true;
558
578
  const crossComponent = (cidNum, vid) => {
559
579
  const comp = comps.getComponentForId(cidNum);
560
- let pushStep = true;
580
+ let pushPart = true;
561
581
  if (handlers === null && (isLeafComponent || bubbles)) {
562
582
  handlers = findHandlers(comp, eventIds, vid, eventName);
563
583
  if (handlers === null) {
564
584
  if (isLeafComponent && stopOnNoEvent && !bubbles) return false;
565
585
  } else if (!isLeafComponent) {
566
- pathSteps.length = 0;
567
- pendingDyns.length = 0;
568
- pushStep = false;
586
+ parts.length = 0;
587
+ pushPart = false;
569
588
  }
570
589
  }
571
590
  isLeafComponent = false;
572
- for (const dyn of pendingDyns) dyn.interiorCids.add(cidNum);
573
- if (pushStep) {
574
- const step = resolvePathStep(comp, nodeIds, vid);
575
- if (step) {
576
- step._originCid = cidNum;
577
- pathSteps.push(step);
578
- if (step instanceof DynStep) {
579
- step.interiorCids.add(cidNum);
580
- pendingDyns.push(step);
581
- }
582
- }
591
+ if (pushPart) {
592
+ const part = resolvePathPart(comp, nodeRefs, vid);
593
+ if (part) parts.push(part);
583
594
  }
584
- for (let i = pendingDyns.length - 1; i >= 0; i--)
585
- if (pendingDyns[i].producerCompId === cidNum) pendingDyns.splice(i, 1);
586
595
  eventIds = [];
587
- nodeIds = [];
596
+ nodeRefs = [];
588
597
  return true;
589
598
  };
590
- while (node && node !== rootNode && depth < maxDepth) {
599
+ while (node && node !== rootNode) {
591
600
  if (node?.dataset) {
592
- const { eid, cid, vid } = node.dataset;
601
+ const { eid, cid, vid, rp } = node.dataset;
593
602
  if (eid !== void 0) eventIds.push(eid);
594
603
  const metas = metaChain(node.previousSibling);
595
604
  let sawComp = false;
@@ -597,21 +606,46 @@ var Path = class _Path {
597
606
  if (m.$ === "Comp") {
598
607
  sawComp = true;
599
608
  if (!crossComponent(m.cid, m.vid)) return NO_EVENT_INFO;
600
- nodeIds.push({ nid: m.nid });
609
+ nodeRefs.push({ nid: m.nid, base: pathFromJson(m.base) });
601
610
  } else {
602
- nodeIds.push({ nid: m.nid, si: m.si, sk: m.sk });
611
+ nodeRefs.push({ nid: m.nid, si: m.si, sk: m.sk });
612
+ }
613
+ }
614
+ if (!sawComp && cid !== void 0) {
615
+ if (!crossComponent(+cid, vid)) return NO_EVENT_INFO;
616
+ if (rp !== void 0) {
617
+ const base = parseRenderPath(rp);
618
+ if (base !== null) nodeRefs.push({ base });
603
619
  }
604
620
  }
605
- if (!sawComp && cid !== void 0 && !crossComponent(+cid, vid)) return NO_EVENT_INFO;
606
621
  }
607
- depth += 1;
608
622
  node = node.parentNode;
609
623
  }
610
- if (pendingDyns.length > 0)
611
- console.warn("event reconstruction: dynamic-var producer not found", pendingDyns);
612
- return [new _Path(pathSteps.reverse()), handlers];
624
+ parts.reverse();
625
+ let path2 = new _DispatchPath();
626
+ for (const part of parts)
627
+ path2 = part.base !== void 0 ? path2.pushFrame(part.base) : path2.pushItem(part.step);
628
+ return [path2, handlers];
613
629
  }
614
630
  };
631
+ function keyedPath(path2, key) {
632
+ const steps = path2.steps;
633
+ const last = steps[steps.length - 1];
634
+ if (last instanceof SeqStep || last instanceof FieldStep)
635
+ return new Path(steps.slice(0, -1).concat(new SeqStep(last.field, key)));
636
+ return null;
637
+ }
638
+ function renderPathText(path2) {
639
+ return JSON.stringify(pathToJson(path2));
640
+ }
641
+ function parseRenderPath(text) {
642
+ try {
643
+ return pathFromJson(JSON.parse(text));
644
+ } catch (err) {
645
+ console.warn("bad render path", err, text);
646
+ return null;
647
+ }
648
+ }
615
649
  function metaChain(n) {
616
650
  const out = [];
617
651
  while (n?.nodeType === 8 && n.textContent[0] === "§") {
@@ -656,28 +690,28 @@ var StepCtx = class _StepCtx {
656
690
  resolveNode() {
657
691
  return this.comp.getNodeForId(+this.meta.nid, this.vid);
658
692
  }
659
- applyKey(pi) {
660
- if (pi === null) return null;
661
- const m = this.meta;
662
- if (m.si !== void 0) return pi.withIndex(+m.si);
663
- if (m.sk !== void 0) return pi.withKey(m.sk);
664
- return pi;
665
- }
666
693
  };
667
- function resolvePathStep(comp, nodeIds, vid) {
668
- for (let i = 0; i < nodeIds.length; i++) {
669
- const ctx = new StepCtx(comp, nodeIds, i, vid);
670
- const step = ctx.resolveNode().toPathStep(ctx);
671
- if (step !== null) return step;
694
+ function resolvePathPart(comp, nodeRefs, vid) {
695
+ for (let i = 0; i < nodeRefs.length; i++) {
696
+ const ref = nodeRefs[i];
697
+ if (ref.base != null) return { base: ref.base };
698
+ if (ref.nid === void 0 || ref.nid === null) continue;
699
+ const ctx = new StepCtx(comp, nodeRefs, i, vid);
700
+ const step = ctx.resolveNode()?.toPathStep(ctx) ?? null;
701
+ if (step !== null) return { step };
672
702
  }
673
703
  return null;
674
704
  }
675
705
  var NO_EVENT_INFO = [null, null];
676
706
  var BUBBLING_EVENTS = /* @__PURE__ */ new Set(["drop"]);
707
+ var path = () => new PathBuilder();
677
708
  var PathBuilder = class {
678
709
  constructor() {
679
710
  this.pathChanges = [];
680
711
  }
712
+ toPath() {
713
+ return new Path(this.pathChanges);
714
+ }
681
715
  add(pathChange) {
682
716
  this.pathChanges.push(pathChange);
683
717
  return this;
@@ -693,10 +727,8 @@ var PathBuilder = class {
693
727
  }
694
728
  };
695
729
 
696
- // src/util/env.js
697
- var isMac = (globalThis.navigator?.userAgent ?? "").toLowerCase().includes("mac");
698
-
699
730
  // src/value.js
731
+ var isMac = (globalThis.navigator?.userAgent ?? "").toLowerCase().includes("mac");
700
732
  var VALID_VAL_ID_RE = /^[a-zA-Z][a-zA-Z0-9_]*\??$/;
701
733
  var isValidValId = (name) => VALID_VAL_ID_RE.test(name);
702
734
  var VALID_FLOAT_RE = /^-?[0-9]+(\.[0-9]+)?$/;
@@ -878,22 +910,10 @@ function _parsePredicate(s, tokens, px) {
878
910
  }
879
911
  return new PredicateVal(pred, args);
880
912
  }
881
- function kindOf(val) {
882
- if (val === null) return 0;
883
- if (val instanceof ConstVal) return val.kind;
884
- if (val instanceof StrTplVal) return val.kind;
885
- if (val instanceof SeqAccessVal) return K_SEQ;
886
- if (val instanceof FieldVal) return K_FIELD;
887
- if (val instanceof MethodVal) return K_METHOD;
888
- if (val instanceof BindVal) return K_BIND;
889
- if (val instanceof DynVal) return K_DYN;
890
- if (val instanceof NameVal) return K_NAME;
891
- if (val instanceof EventMemberVal) return K_EVENT;
892
- return 0;
893
- }
913
+ var kindOf = (val) => val?.kind ?? 0;
894
914
  var BaseVal = class {
895
- render(_stack, _rx) {
896
- }
915
+ // No kind: never accepted by a group (PredicateVal, which only parseBool builds).
916
+ kind = 0;
897
917
  eval(_stack) {
898
918
  }
899
919
  toPathItem() {
@@ -913,9 +933,6 @@ var ConstVal = class extends BaseVal {
913
933
  this.val = val;
914
934
  this.kind = kind;
915
935
  }
916
- render(_stack, _rx) {
917
- return this.val;
918
- }
919
936
  eval(_stack) {
920
937
  return this.val;
921
938
  }
@@ -941,9 +958,7 @@ var PredicateVal = class extends BaseVal {
941
958
  return `${this.pred.name} ${this.args.map(String).join(" ")}`;
942
959
  }
943
960
  };
944
- var VarVal = class extends BaseVal {
945
- };
946
- var StrTplVal = class _StrTplVal extends VarVal {
961
+ var StrTplVal = class _StrTplVal extends BaseVal {
947
962
  constructor(vals) {
948
963
  super();
949
964
  this.vals = vals;
@@ -956,9 +971,6 @@ var StrTplVal = class _StrTplVal extends VarVal {
956
971
  for (const v of this.vals) if (!(v instanceof ConstVal) || v.fromMacroVar) return false;
957
972
  return true;
958
973
  }
959
- render(stack, _rx) {
960
- return this.eval(stack);
961
- }
962
974
  eval(stack) {
963
975
  const strs = new Array(this.vals.length);
964
976
  for (let i = 0; i < this.vals.length; i++) strs[i] = this.vals[i]?.eval(stack, "");
@@ -995,7 +1007,8 @@ var StrTplVal = class _StrTplVal extends VarVal {
995
1007
  return new _StrTplVal(lo === 0 && hi === vals.length ? vals : vals.slice(lo, hi));
996
1008
  }
997
1009
  };
998
- var NameVal = class extends VarVal {
1010
+ var NameVal = class extends BaseVal {
1011
+ kind = K_NAME;
999
1012
  constructor(name) {
1000
1013
  super();
1001
1014
  this.name = name;
@@ -1045,6 +1058,7 @@ var EVENT_CONVENIENCES = {
1045
1058
  dragKey: nullSafe((info) => info.lookupBind("key"))
1046
1059
  };
1047
1060
  var EventMemberVal = class extends BaseVal {
1061
+ kind = K_EVENT;
1048
1062
  constructor(members) {
1049
1063
  super();
1050
1064
  this.members = members;
@@ -1067,24 +1081,21 @@ var EventMemberVal = class extends BaseVal {
1067
1081
  return `e.${this.members.join(".")}`;
1068
1082
  }
1069
1083
  };
1070
- var RenderVal = class extends BaseVal {
1071
- render(stack, _rx) {
1072
- return this.eval(stack);
1073
- }
1074
- };
1075
- var RenderNameVal = class extends RenderVal {
1084
+ var SigilNameVal = class extends BaseVal {
1076
1085
  constructor(name) {
1077
1086
  super();
1078
1087
  this.name = name;
1079
1088
  }
1089
+ toString() {
1090
+ return this.sigil + this.name;
1091
+ }
1080
1092
  };
1081
- var BindVal = class extends RenderNameVal {
1093
+ var BindVal = class extends SigilNameVal {
1094
+ kind = K_BIND;
1095
+ sigil = "@";
1082
1096
  eval(stack) {
1083
1097
  return stack.lookupBind(this.name);
1084
1098
  }
1085
- toString() {
1086
- return `@${this.name}`;
1087
- }
1088
1099
  };
1089
1100
  var BindMemberVal = class extends BindVal {
1090
1101
  constructor(name, member) {
@@ -1099,37 +1110,35 @@ var BindMemberVal = class extends BindVal {
1099
1110
  return `@${this.name}.${this.member}`;
1100
1111
  }
1101
1112
  };
1102
- var DynVal = class extends RenderNameVal {
1113
+ var DynVal = class extends SigilNameVal {
1114
+ kind = K_DYN;
1115
+ sigil = "*";
1103
1116
  eval(stack) {
1104
1117
  return stack.lookupDynamic(this.name);
1105
1118
  }
1106
- toString() {
1107
- return `*${this.name}`;
1108
- }
1109
1119
  };
1110
- var FieldVal = class extends RenderNameVal {
1120
+ var FieldVal = class extends SigilNameVal {
1121
+ kind = K_FIELD;
1122
+ sigil = ".";
1111
1123
  eval(stack) {
1112
1124
  return stack.lookupFieldRaw(this.name);
1113
1125
  }
1114
1126
  toPathItem() {
1115
1127
  return new FieldStep(this.name);
1116
1128
  }
1117
- toString() {
1118
- return `.${this.name}`;
1119
- }
1120
1129
  };
1121
- var MethodVal = class extends RenderNameVal {
1130
+ var MethodVal = class extends SigilNameVal {
1131
+ kind = K_METHOD;
1132
+ sigil = "$";
1122
1133
  eval(stack) {
1123
1134
  return stack.lookupMethod(this.name);
1124
1135
  }
1125
1136
  evalAsHandler(stack) {
1126
1137
  return stack.lookupFieldRaw(this.name);
1127
1138
  }
1128
- toString() {
1129
- return `$${this.name}`;
1130
- }
1131
1139
  };
1132
- var SeqAccessVal = class extends RenderVal {
1140
+ var SeqAccessVal = class extends BaseVal {
1141
+ kind = K_SEQ;
1133
1142
  constructor(seqVal, keyVal) {
1134
1143
  super();
1135
1144
  this.seqVal = seqVal;
@@ -1152,9 +1161,6 @@ var Attributes = class {
1152
1161
  constructor(items) {
1153
1162
  this.items = items;
1154
1163
  }
1155
- static parse(attributes, px, parseAll = false) {
1156
- return new AttrParser(px).parse(attributes, parseAll);
1157
- }
1158
1164
  isConstant() {
1159
1165
  return false;
1160
1166
  }
@@ -1172,10 +1178,16 @@ function parseDirectiveValue(px, directiveName, source, parser) {
1172
1178
  }
1173
1179
  return val;
1174
1180
  }
1181
+ var ITER_DIRECTIVES = {
1182
+ when: "whenVal",
1183
+ "loop-with": "loopWithVal",
1184
+ "enrich-with": "enrichWithVal"
1185
+ };
1186
+ var parseIterDirective = (px, name, s) => parseDirectiveValue(px, name, s, parseAlterHandler);
1175
1187
  function parseIterationDirectives(attributes, px) {
1176
1188
  const parseNamed = (name) => {
1177
1189
  const attr = attributes.getNamedItem(`@${name}`);
1178
- return attr ? parseDirectiveValue(px, name, attr.value, parseAlterHandler) : null;
1190
+ return attr ? parseIterDirective(px, name, attr.value) : null;
1179
1191
  };
1180
1192
  return { whenVal: parseNamed("when"), loopWithVal: parseNamed("loop-with") };
1181
1193
  }
@@ -1216,11 +1228,22 @@ var AttrParser = class {
1216
1228
  this.px.onParseIssue("bad-value", info);
1217
1229
  }
1218
1230
  }
1219
- parseThen(s) {
1220
- if (this.ifAttr) this.ifAttr.thenVal = parseText(s, this.px) ?? NOT_SET_VAL;
1231
+ // `@then` / `@else` fill the branches of the `@if.<attr>` on the same element.
1232
+ parseBranch(slot, name, s) {
1233
+ if (this.ifAttr === null) return this._orphan(name, s, "if");
1234
+ this.ifAttr[slot] = parseText(s, this.px) ?? NOT_SET_VAL;
1221
1235
  }
1222
- parseElse(value) {
1223
- if (this.ifAttr) this.ifAttr.elseVal = parseText(value, this.px) ?? NOT_SET_VAL;
1236
+ // `@when` / `@loop-with` / `@enrich-with` amend the `@each` on the same element. A
1237
+ // loop-less `@enrich-with` is a scope of its own; the other two need the loop.
1238
+ parseIter(name, s) {
1239
+ const val = parseIterDirective(this.px, name, s);
1240
+ if (this.eachAttr !== null) this.eachAttr[ITER_DIRECTIVES[name]] = val;
1241
+ else if (name === "enrich-with") this.pushWrapper("scope", s, val);
1242
+ else this._orphan(name, s, "each");
1243
+ }
1244
+ // A directive that only means something next to another one, written alone.
1245
+ _orphan(name, value, needs) {
1246
+ this.px.onParseIssue("orphan-directive", { name, value, needs });
1224
1247
  }
1225
1248
  parseEvent(directiveName, value) {
1226
1249
  const [eventName, ...modifiers] = directiveName.slice(3).split("+");
@@ -1234,77 +1257,51 @@ var AttrParser = class {
1234
1257
  this.events.add(eventName, handler, modifiers);
1235
1258
  }
1236
1259
  }
1237
- _parseDirectiveValue(directiveName, s, parserFn) {
1238
- return parseDirectiveValue(this.px, directiveName, s, parserFn);
1239
- }
1240
1260
  parseDirective(s, directiveName) {
1241
1261
  switch (directiveName) {
1242
1262
  case "dangerouslysetinnerhtml":
1243
1263
  this.attrs ??= [];
1244
- this.attrs.push(new RawHtmlAttr(this._parseDirectiveValue(directiveName, s, parseText)));
1264
+ this.attrs.push(new RawHtmlAttr(parseDirectiveValue(this.px, directiveName, s, parseText)));
1245
1265
  this.hasDynamic = true;
1246
1266
  return;
1247
1267
  case "push-view":
1248
- this.pushWrapper("push-view", s, this._parseDirectiveValue(directiveName, s, parseText));
1268
+ this.pushWrapper("push-view", s, parseDirectiveValue(this.px, directiveName, s, parseText));
1249
1269
  return;
1250
1270
  case "text":
1251
- this.textChild = this._parseDirectiveValue(directiveName, s, parseText);
1271
+ this.textChild = parseDirectiveValue(this.px, directiveName, s, parseText);
1252
1272
  return;
1253
1273
  case "show":
1254
- this.pushWrapper("show", s, this._parseDirectiveValue(directiveName, s, parseBool));
1274
+ this.pushWrapper("show", s, parseDirectiveValue(this.px, directiveName, s, parseBool));
1255
1275
  return;
1256
1276
  case "hide":
1257
- this.pushWrapper("hide", s, this._parseDirectiveValue(directiveName, s, parseBool));
1277
+ this.pushWrapper("hide", s, parseDirectiveValue(this.px, directiveName, s, parseBool));
1258
1278
  return;
1259
1279
  case "each": {
1260
- const val = this._parseDirectiveValue(directiveName, s, parseSequence);
1280
+ const val = parseDirectiveValue(this.px, directiveName, s, parseSequence);
1261
1281
  this.eachAttr = this.pushWrapper("each", s, val);
1262
1282
  return;
1263
1283
  }
1264
- case "enrich-with":
1265
- if (this.eachAttr !== null)
1266
- this.eachAttr.enrichWithVal = this._parseDirectiveValue(
1267
- directiveName,
1268
- s,
1269
- parseAlterHandler
1270
- );
1271
- else
1272
- this.pushWrapper(
1273
- "scope",
1274
- s,
1275
- this._parseDirectiveValue(directiveName, s, parseAlterHandler)
1276
- );
1277
- return;
1278
1284
  case "when":
1279
- this._parseWhen(s);
1280
- return;
1281
1285
  case "loop-with":
1282
- this._parseLoopWith(s);
1286
+ case "enrich-with":
1287
+ this.parseIter(directiveName, s);
1283
1288
  return;
1284
1289
  case "then":
1285
- this.parseThen(s);
1290
+ this.parseBranch("thenVal", directiveName, s);
1286
1291
  return;
1287
1292
  case "else":
1288
- this.parseElse(s);
1293
+ this.parseBranch("elseVal", directiveName, s);
1289
1294
  return;
1290
1295
  }
1291
1296
  if (directiveName.startsWith("on.")) this.parseEvent(directiveName, s);
1292
1297
  else if (directiveName.startsWith("if.")) this.parseIf(directiveName, s);
1293
- else if (directiveName.startsWith("then.")) this.parseThen(s);
1294
- else if (directiveName.startsWith("else.")) this.parseElse(s);
1298
+ else if (directiveName.startsWith("then.")) this.parseBranch("thenVal", directiveName, s);
1299
+ else if (directiveName.startsWith("else.")) this.parseBranch("elseVal", directiveName, s);
1295
1300
  else {
1296
1301
  const info = { name: directiveName, value: s };
1297
1302
  this.px.onParseIssue("unknown-directive", info);
1298
1303
  }
1299
1304
  }
1300
- _parseWhen(s) {
1301
- if (this.eachAttr !== null)
1302
- this.eachAttr.whenVal = this._parseDirectiveValue("when", s, parseAlterHandler);
1303
- }
1304
- _parseLoopWith(s) {
1305
- if (this.eachAttr !== null)
1306
- this.eachAttr.loopWithVal = this._parseDirectiveValue("loop-with", s, parseAlterHandler);
1307
- }
1308
1305
  parse(attributes, parseAll = false) {
1309
1306
  for (const { name, value } of attributes) {
1310
1307
  const charCode = name.charCodeAt(0);
@@ -1657,19 +1654,21 @@ var VNode = class _VNode extends VBase {
1657
1654
  const createOpts = attrs.is != null ? { is: attrs.is } : void 0;
1658
1655
  const node = ns === null ? doc.createElement(tag, createOpts) : doc.createElementNS(ns, tag, createOpts);
1659
1656
  const cOpts = childOpts(this, ns, opts);
1660
- if ("value" in attrs || "checked" in attrs) {
1661
- const { value, checked, ...rest } = attrs;
1662
- applyProperties(node, rest);
1663
- appendChildNodes(node, this.childs, cOpts);
1664
- if (value !== void 0) applyValueLast(node, value);
1665
- if (checked !== void 0) setProp(node, "checked", checked, false);
1666
- } else {
1667
- applyProperties(node, attrs);
1668
- appendChildNodes(node, this.childs, cOpts);
1669
- }
1657
+ applyAttrs(node, attrs, () => appendChildNodes(node, this.childs, cOpts));
1670
1658
  return node;
1671
1659
  }
1672
1660
  };
1661
+ function applyAttrs(node, attrs, placeChildren) {
1662
+ const hasValue = "value" in attrs;
1663
+ const hasChecked = "checked" in attrs;
1664
+ if (hasValue || hasChecked) {
1665
+ const { value: _v, checked: _c, ...rest } = attrs;
1666
+ applyProperties(node, rest);
1667
+ } else applyProperties(node, attrs);
1668
+ placeChildren();
1669
+ if (hasValue) applyValueLast(node, attrs.value);
1670
+ if (hasChecked) setProp(node, "checked", attrs.checked, isNamespaced(node));
1671
+ }
1673
1672
  function diffProps(a, b) {
1674
1673
  if (a === b) return null;
1675
1674
  let diff = null;
@@ -1700,22 +1699,16 @@ function morphNode(domNode, source, target, opts) {
1700
1699
  }
1701
1700
  if (type === 1 && source.isSameKind(target)) {
1702
1701
  const propsDiff = diffProps(source.attrs, target.attrs);
1703
- const hasValue = propsDiff != null && "value" in propsDiff;
1704
- const hasChecked = propsDiff != null && "checked" in propsDiff;
1705
- if (propsDiff) {
1706
- if (hasValue || hasChecked) {
1707
- const { value: _v, checked: _c, ...rest } = propsDiff;
1708
- applyProperties(domNode, rest);
1709
- } else applyProperties(domNode, propsDiff);
1710
- }
1711
- if (!target.attrs.dangerouslySetInnerHTML) {
1702
+ const placeChildren = () => {
1703
+ if (target.attrs.dangerouslySetInnerHTML) return;
1712
1704
  const ns = effectiveNs(target, opts);
1713
1705
  morphChildren(domNode, source.childs, target.childs, childOpts(target, ns, opts));
1706
+ };
1707
+ if (propsDiff) applyAttrs(domNode, propsDiff, placeChildren);
1708
+ else placeChildren();
1709
+ if (!(propsDiff && "value" in propsDiff) && source.tag === "SELECT") {
1710
+ if (target.attrs.value !== void 0) applyValueLast(domNode, target.attrs.value);
1714
1711
  }
1715
- if (hasValue) applyValueLast(domNode, propsDiff.value);
1716
- else if (source.tag === "SELECT" && target.attrs.value !== void 0)
1717
- applyValueLast(domNode, target.attrs.value);
1718
- if (hasChecked) setProp(domNode, "checked", propsDiff.checked, false);
1719
1712
  return domNode;
1720
1713
  }
1721
1714
  if (type === 11) {
@@ -1834,20 +1827,26 @@ function h(tagName, properties, children, namespace) {
1834
1827
  }
1835
1828
 
1836
1829
  // src/anode.js
1837
- function resolveDynProducer(comp, name) {
1838
- const own = comp?.provide?.[name];
1839
- const producerComp = own != null ? comp : comp?.scope?.lookupProvider(name) ?? null;
1840
- const producerProvide = own ?? producerComp?.provide?.[name];
1841
- if (producerComp == null || producerProvide == null) return null;
1842
- const pi = producerProvide.val?.toPathItem?.() ?? null;
1843
- return { producerCompId: producerComp.id, producerSteps: pi ? [pi] : [] };
1830
+ function renderTarget(val, stack) {
1831
+ if (val instanceof DynVal) {
1832
+ const loc = stack.lookupDynamicLocated(val.name);
1833
+ if (loc?.path == null) return [loc?.value ?? null, stack.renderPath, null];
1834
+ return [loc.value, stack.renderPath.pushFrame(loc.path), loc.path];
1835
+ }
1836
+ const step = val.toPathItem?.() ?? null;
1837
+ if (step === null) return [val.eval(stack), stack.renderPath, null];
1838
+ const path2 = stack.renderPath.pushItem(step);
1839
+ return [val.eval(stack), path2, stack.pendingFrame ? path2.toTransactionPath() : null];
1844
1840
  }
1845
1841
  var BaseNode = class {
1846
1842
  render(_stack, _rx) {
1847
1843
  return null;
1848
1844
  }
1849
- setDataAttr(key, val) {
1850
- console.warn("setDataAttr not implemented for", this, { key, val });
1845
+ // Stamp a `data-*` attribute on the element(s) this node renders. A no-op for
1846
+ // nodes that produce no element of their own (text, `<x render*>`): a view whose
1847
+ // root is one of those records its component boundary in the `§Comp§` meta
1848
+ // comment instead (see Renderer._rValComp).
1849
+ setDataAttr(_key, _val) {
1851
1850
  }
1852
1851
  isConstant() {
1853
1852
  return false;
@@ -1886,21 +1885,12 @@ var TextNode = class extends BaseNode {
1886
1885
  isConstant() {
1887
1886
  return true;
1888
1887
  }
1889
- setDataAttr(_key, _val) {
1890
- }
1891
1888
  };
1892
1889
  var CommentNode = class extends TextNode {
1893
1890
  render(_stack, rx) {
1894
1891
  return rx.renderComment(this.val);
1895
1892
  }
1896
1893
  };
1897
- function optimizeChilds(childs) {
1898
- for (let i = 0; i < childs.length; i++) {
1899
- const child = childs[i];
1900
- if (child.isConstant()) childs[i] = new RenderOnceNode(child);
1901
- else child.optimize();
1902
- }
1903
- }
1904
1894
  function optimizeNode(node) {
1905
1895
  if (node.isConstant()) return new RenderOnceNode(node);
1906
1896
  node.optimize();
@@ -1915,7 +1905,8 @@ var ChildsNode = class extends BaseNode {
1915
1905
  return this.childs.every((v) => v.isConstant());
1916
1906
  }
1917
1907
  optimize() {
1918
- optimizeChilds(this.childs);
1908
+ const { childs } = this;
1909
+ for (let i = 0; i < childs.length; i++) childs[i] = optimizeNode(childs[i]);
1919
1910
  }
1920
1911
  };
1921
1912
  var DomNode = class extends ChildsNode {
@@ -1927,8 +1918,7 @@ var DomNode = class extends ChildsNode {
1927
1918
  }
1928
1919
  render(stack, rx) {
1929
1920
  const childNodes = new Array(this.childs.length);
1930
- for (let i = 0; i < childNodes.length; i++)
1931
- childNodes[i] = this.childs[i]?.render?.(stack, rx) ?? null;
1921
+ for (let i = 0; i < childNodes.length; i++) childNodes[i] = this.childs[i].render(stack, rx);
1932
1922
  return rx.renderTag(this.tagName, this.attrs.eval(stack), childNodes, this.namespace);
1933
1923
  }
1934
1924
  setDataAttr(key, val) {
@@ -1940,7 +1930,7 @@ var DomNode = class extends ChildsNode {
1940
1930
  };
1941
1931
  var FragmentNode = class extends ChildsNode {
1942
1932
  render(stack, rx) {
1943
- return rx.renderFragment(this.childs.map((c) => c?.render(stack, rx)));
1933
+ return rx.renderFragment(this.childs.map((c) => c.render(stack, rx)));
1944
1934
  }
1945
1935
  setDataAttr(key, val) {
1946
1936
  for (const child of this.childs) child.setDataAttr(key, val);
@@ -1954,8 +1944,10 @@ var ANode = class _ANode extends BaseNode {
1954
1944
  this.nodeId = nodeId;
1955
1945
  this.val = val;
1956
1946
  }
1957
- toPathStep(ctx) {
1958
- return ctx.applyKey(this.val?.toPathItem?.() ?? null);
1947
+ // The addressing step this node contributes to an event's dispatch path, given
1948
+ // the `StepCtx` of the meta that named it; null when it addresses nothing.
1949
+ toPathStep(_ctx) {
1950
+ return this.val?.toPathItem?.() ?? null;
1959
1951
  }
1960
1952
  static parse(html2, px) {
1961
1953
  const nodes = px.parseHTML(html2);
@@ -1971,8 +1963,8 @@ var ANode = class _ANode extends BaseNode {
1971
1963
  return maybeFragment(trimmed);
1972
1964
  }
1973
1965
  static fromDOM(node, px) {
1974
- if (node instanceof px.Text) return new TextNode(node.textContent);
1975
- else if (node instanceof px.Comment) return new CommentNode(node.textContent);
1966
+ if (node.nodeType === 3) return new TextNode(node.textContent);
1967
+ if (node.nodeType === 8) return new CommentNode(node.textContent);
1976
1968
  const { childNodes, attributes: attrs, tagName: tag } = node;
1977
1969
  const childs = [];
1978
1970
  for (let i = 0; i < childNodes.length; i++) {
@@ -1990,11 +1982,11 @@ var ANode = class _ANode extends BaseNode {
1990
1982
  const slotName = attrs.getNamedItem("name")?.value ?? "_";
1991
1983
  return px.frame.macroSlots[slotName] ?? maybeFragment(childs);
1992
1984
  }
1993
- const [nAttrs, wrappers] = Attributes.parse(attrs, px, true);
1985
+ const [nAttrs, wrappers] = new AttrParser(px).parse(attrs, true);
1994
1986
  px.onAttributes(nAttrs, wrappers, null, true, tag);
1995
1987
  return wrap(px.newMacroNode(macroName, nAttrs.toMacroVars(), childs), px, wrappers);
1996
1988
  } else if (VALID_NODE_RE.test(tag)) {
1997
- const [nAttrs, wrappers, textChild] = Attributes.parse(attrs, px);
1989
+ const [nAttrs, wrappers, textChild] = new AttrParser(px).parse(attrs);
1998
1990
  px.onAttributes(nAttrs, wrappers, textChild, false, tag);
1999
1991
  if (textChild) childs.unshift(new RenderTextNode(null, textChild));
2000
1992
  const domChilds = tag !== "PRE" ? condenseChildsWhites(childs) : childs;
@@ -2156,39 +2148,32 @@ var RenderViewId = class extends ANode {
2156
2148
  evalViewName(stack) {
2157
2149
  return this.viewVal ? this.viewVal.eval(stack) : null;
2158
2150
  }
2159
- // A `<x render*>` produces no DOM element of its own to carry `data-cid`;
2160
- // when it is a view's root the component boundary is recorded in the `Comp`
2161
- // meta comment instead (see Renderer._rValComp), so this is a no-op.
2162
- setDataAttr(_key, _val) {
2163
- }
2164
2151
  };
2165
- function dynRenderStep(comp, name, key) {
2166
- const p = resolveDynProducer(comp, name);
2167
- if (!p) return null;
2168
- return key === void 0 ? new DynStep(p.producerCompId, p.producerSteps) : new DynEachStep(p.producerCompId, p.producerSteps, key);
2169
- }
2170
2152
  var RenderNode = class extends RenderViewId {
2171
2153
  render(stack, rx) {
2172
- const newStack = stack.enter(this.val.eval(stack), {}, true);
2173
- return rx.renderIt(newStack, this, "", this.evalViewName(stack));
2154
+ const [value, renderPath, base] = renderTarget(this.val, stack);
2155
+ const newStack = stack.enter(value, {}, true, renderPath, false);
2156
+ return rx.renderIt(newStack, this, this.evalViewName(stack), base);
2174
2157
  }
2158
+ // A `*name` target contributes no step: the site recorded the absolute base it
2159
+ // resumed at, and event reconstruction turns that into a continuation frame.
2175
2160
  toPathStep(ctx) {
2176
- if (this.val instanceof DynVal) return dynRenderStep(ctx.comp, this.val.name);
2161
+ if (this.val instanceof DynVal) return null;
2177
2162
  return super.toPathStep(ctx);
2178
2163
  }
2179
2164
  };
2180
2165
  var RenderItNode = class extends RenderViewId {
2181
2166
  render(stack, rx) {
2182
- const newStack = stack.enter(stack.it, {}, true);
2183
- return rx.renderIt(newStack, this, "", this.evalViewName(stack));
2167
+ const base = stack.pendingFrame ? stack.renderPath.toTransactionPath() : null;
2168
+ const newStack = stack.enter(stack.it, {}, true, stack.renderPath, false);
2169
+ return rx.renderIt(newStack, this, this.evalViewName(stack), base);
2184
2170
  }
2185
2171
  toPathStep(ctx) {
2186
2172
  const next = ctx.next();
2187
2173
  if (next === null) return null;
2188
2174
  const nextNode = next.resolveNode();
2189
2175
  if (nextNode instanceof EachNode && next.hasKey) {
2190
- if (nextNode.val instanceof DynVal)
2191
- return dynRenderStep(ctx.comp, nextNode.val.name, next.key);
2176
+ if (nextNode.val instanceof DynVal) return null;
2192
2177
  return new EachRenderItStep(nextNode.val.name, next.key);
2193
2178
  }
2194
2179
  return null;
@@ -2210,9 +2195,6 @@ var RenderTextNode = class extends ANode {
2210
2195
  render(stack, _rx) {
2211
2196
  return this.val.eval(stack);
2212
2197
  }
2213
- // Renders to a text node, which can't carry `data-cid`.
2214
- setDataAttr(_key, _val) {
2215
- }
2216
2198
  };
2217
2199
  var RenderOnceNode = class extends BaseNode {
2218
2200
  constructor(node) {
@@ -2287,35 +2269,45 @@ var ScopeNode = class extends WrapperNode {
2287
2269
  var EachNode = class extends WrapperNode {
2288
2270
  constructor(nodeId, val) {
2289
2271
  super(nodeId, val);
2290
- this.iterInfo = new IterInfo(val, null, null, null);
2272
+ this.iterInfo = new IterInfo();
2291
2273
  }
2292
2274
  render(stack, rx) {
2293
- return rx.renderEachWhen(stack, this.iterInfo, this.node, this.nodeId);
2275
+ return rx.renderEachWhen(stack, this);
2276
+ }
2277
+ // The sequence and the three loop handlers, resolved against `stack`.
2278
+ evalIter(stack) {
2279
+ const { whenVal, loopWithVal, enrichWithVal } = this.iterInfo;
2280
+ return {
2281
+ seq: this.val.eval(stack) ?? [],
2282
+ filter: whenVal?.evalAsHandler(stack) ?? filterAlwaysTrue,
2283
+ loopWith: loopWithVal?.evalAsHandler(stack) ?? nullLoopWith,
2284
+ enricher: enrichWithVal?.evalAsHandler(stack) ?? null
2285
+ };
2286
+ }
2287
+ // Rebuild the per-item binds for `key` (see iteration.js bindsForKey).
2288
+ enrichBinds(stack, key) {
2289
+ const { seq, filter, loopWith, enricher } = this.evalIter(stack);
2290
+ const ctx = makeLoopCtx(stack, filter);
2291
+ return bindsForKey({ seq, it: stack.it, loopWith, enricher, ctx }, key);
2294
2292
  }
2295
2293
  toPathStep(ctx) {
2296
- return ctx.hasKey ? new EachBindStep(this.iterInfo, ctx.key) : null;
2294
+ return ctx.hasKey ? new EachBindStep(this, ctx.key) : null;
2295
+ }
2296
+ // Where one item lives, for the render path. `@each` re-binds `it` to the item
2297
+ // whether or not the body is a component, so the position moves either way and
2298
+ // the step has to address it — `.rows` iterated at `key` IS `rows[key]`.
2299
+ // Null for a dynamic sequence: `*rows` carries its own absolute path and enters
2300
+ // a continuation frame instead (see Renderer.renderEachWhen).
2301
+ itemStep(key) {
2302
+ return this.val instanceof FieldVal ? new SeqStep(this.val.name, key) : null;
2297
2303
  }
2298
2304
  static register = true;
2299
2305
  };
2300
2306
  var IterInfo = class {
2301
- constructor(val, whenVal, loopWithVal, enrichWithVal) {
2302
- this.val = val;
2303
- this.whenVal = whenVal;
2304
- this.loopWithVal = loopWithVal;
2305
- this.enrichWithVal = enrichWithVal;
2306
- }
2307
- eval(stack) {
2308
- const seq = this.val.eval(stack) ?? [];
2309
- const filter = this.whenVal?.evalAsHandler(stack) ?? filterAlwaysTrue;
2310
- const loopWith = this.loopWithVal?.evalAsHandler(stack) ?? nullLoopWith;
2311
- const enricher = this.enrichWithVal?.evalAsHandler(stack) ?? null;
2312
- return { seq, filter, loopWith, enricher };
2313
- }
2314
- // Rebuild the per-item binds for `key` (see iteration.js bindsForKey).
2315
- enrichBinds(stack, key) {
2316
- const { seq, filter, loopWith, enricher } = this.eval(stack);
2317
- const ctx = makeLoopCtx(stack, filter);
2318
- return bindsForKey({ seq, it: stack.it, loopWith, enricher, ctx }, key);
2307
+ constructor() {
2308
+ this.whenVal = null;
2309
+ this.loopWithVal = null;
2310
+ this.enrichWithVal = null;
2319
2311
  }
2320
2312
  };
2321
2313
  function xOp(consumed = [], { wrappable = false, wrapper = null, ignoresChildren = false } = {}) {
@@ -2343,26 +2335,31 @@ var WRAPPER_NODES = {
2343
2335
  scope: ScopeNode,
2344
2336
  "push-view": PushViewNameNode
2345
2337
  };
2346
- var ParseContext = class _ParseContext {
2347
- constructor(document2, Text, Comment, nodes, events, macroNodes, frame, parent) {
2348
- this.nodes = nodes ?? [];
2349
- this.events = events ?? [];
2350
- this.macroNodes = macroNodes ?? [];
2351
- this.parent = parent ?? null;
2352
- this.frame = frame ?? {};
2353
- this.document = document2 ?? globalThis.document;
2354
- this.Text = Text ?? globalThis.Text;
2355
- this.Comment = Comment ?? globalThis.Comment;
2356
- this.cacheConstNodes = true;
2338
+ var ParseContext = class {
2339
+ constructor(document2 = globalThis.document) {
2340
+ this.document = document2;
2341
+ this.nodes = [];
2342
+ this.events = [];
2343
+ this.macroNodes = [];
2344
+ this.parent = null;
2345
+ this.frame = {};
2357
2346
  this.currentTag = null;
2358
2347
  }
2359
2348
  isInsideMacro(name) {
2360
2349
  return this.frame.macroName === name || this.parent?.isInsideMacro(name);
2361
2350
  }
2351
+ // A macro body parses in a child context that shares every accumulator with
2352
+ // its parent (nodes, events, macroNodes, and whatever a subclass collects) and
2353
+ // differs only in its frame. Cloned rather than constructed so a subclass
2354
+ // keeps collecting inside macro bodies without re-implementing this — the
2355
+ // lint and class-set contexts used to lose (or had to copy) exactly that.
2362
2356
  enterMacro(macroName, macroVars, macroSlots) {
2363
- const { document: document2, Text, Comment, nodes, events, macroNodes } = this;
2364
- const frame = { macroName, macroVars, macroSlots };
2365
- return new _ParseContext(document2, Text, Comment, nodes, events, macroNodes, frame, this);
2357
+ const child = Object.create(Object.getPrototypeOf(this));
2358
+ return Object.assign(child, this, {
2359
+ frame: { macroName, macroVars, macroSlots },
2360
+ parent: this,
2361
+ currentTag: null
2362
+ });
2366
2363
  }
2367
2364
  parseHTML(html2) {
2368
2365
  const t = this.document.createElement("template");
@@ -2427,10 +2424,10 @@ var isBlockDomNode = (n) => {
2427
2424
  return node instanceof DomNode && HTML_BLOCK_TAGS.has(node.tagName);
2428
2425
  };
2429
2426
  var isEmptyText = (c) => c instanceof TextNode && c.val === "";
2430
- var isIgnorableXChild = (c) => c instanceof CommentNode || (c.isWhiteSpace?.() ?? false);
2427
+ var isIgnorableXChild = (c) => c instanceof CommentNode || c.isWhiteSpace();
2431
2428
  var hasMeaningfulChilds = (childs) => childs.some((c) => !isIgnorableXChild(c));
2432
2429
  function trimEdgeWhite(node) {
2433
- if (!node.isWhiteSpace?.()) return false;
2430
+ if (!node.isWhiteSpace()) return false;
2434
2431
  node.condenseWhiteSpace();
2435
2432
  return true;
2436
2433
  }
@@ -2441,7 +2438,7 @@ function condenseChildsWhites(childs) {
2441
2438
  if (last > 0 && trimEdgeWhite(childs[last])) emptied = true;
2442
2439
  for (let i = 1; i < last; i++) {
2443
2440
  const cur = childs[i];
2444
- if (!(cur.isWhiteSpace?.() && cur.hasNewLine())) continue;
2441
+ if (!(cur.isWhiteSpace() && cur.hasNewLine())) continue;
2445
2442
  const bothBlock = isBlockDomNode(childs[i - 1]) && isBlockDomNode(childs[i + 1]);
2446
2443
  cur.condenseWhiteSpace(bothBlock ? "" : " ");
2447
2444
  if (bothBlock) emptied = true;
@@ -2449,12 +2446,12 @@ function condenseChildsWhites(childs) {
2449
2446
  return emptied ? childs.filter((c) => !isEmptyText(c)) : childs;
2450
2447
  }
2451
2448
  var View = class {
2452
- constructor(name, rawView = "No View Defined", style = "", anode = null, ctx = null) {
2449
+ constructor(name, rawView = "No View Defined", style = "") {
2453
2450
  this.name = name;
2454
- this.anode = anode;
2455
- this.style = style;
2456
- this.ctx = ctx;
2457
2451
  this.rawView = rawView;
2452
+ this.style = style;
2453
+ this.anode = null;
2454
+ this.ctx = null;
2458
2455
  }
2459
2456
  compile(ctx, scope, cid) {
2460
2457
  this.ctx = ctx;
@@ -2462,7 +2459,7 @@ var View = class {
2462
2459
  this.anode.setDataAttr("data-cid", cid);
2463
2460
  this.anode.setDataAttr("data-vid", this.name);
2464
2461
  this.ctx.compile(scope);
2465
- if (ctx.cacheConstNodes) this.anode = optimizeNode(this.anode);
2462
+ this.anode = optimizeNode(this.anode);
2466
2463
  }
2467
2464
  render(stack, rx) {
2468
2465
  if (this.anode === null) {
@@ -2487,7 +2484,7 @@ var NodeEvents = class {
2487
2484
  getHandlersFor(eventName) {
2488
2485
  let r = null;
2489
2486
  for (const handler of this.handlers)
2490
- if (handler.handlesEventName(eventName)) {
2487
+ if (handler.name === eventName) {
2491
2488
  r ??= [];
2492
2489
  r.push(handler);
2493
2490
  }
@@ -2501,9 +2498,6 @@ var NodeEvent = class {
2501
2498
  this.modifierWrapper = compileModifiers(name, modifiers);
2502
2499
  this.modifiers = modifiers;
2503
2500
  }
2504
- handlesEventName(name) {
2505
- return this.name === name;
2506
- }
2507
2501
  getHandlerAndArgs(stack, event) {
2508
2502
  const r = this.handlerCall.getHandlerAndArgs(stack, event);
2509
2503
  r[0] = this.modifierWrapper(r[0], event);
@@ -2555,92 +2549,209 @@ function compileModifiers(eventName, names) {
2555
2549
  };
2556
2550
  }
2557
2551
 
2558
- // src/util/parsectx.js
2559
- var ParseCtxClassSetCollector = class _ParseCtxClassSetCollector extends ParseContext {
2560
- constructor(...args) {
2561
- super(...args);
2562
- this.classes = /* @__PURE__ */ new Set();
2563
- }
2564
- _addClasses(s) {
2565
- for (const v of s.split(/\s+/)) {
2566
- this.classes.add(v);
2567
- }
2552
+ // src/stack.js
2553
+ var STOP = /* @__PURE__ */ Symbol("STOP");
2554
+ var NEXT = /* @__PURE__ */ Symbol("NEXT");
2555
+ var DEFAULT_ROUTE = ["dyn", "lex"];
2556
+ var isTypeName = (s) => {
2557
+ const c = s.charCodeAt(0);
2558
+ return c >= 65 && c <= 90;
2559
+ };
2560
+ function lookup(chain, name, dv = null) {
2561
+ let n = chain;
2562
+ while (n !== null) {
2563
+ const r = n[0].lookup(name);
2564
+ if (r === STOP) return dv;
2565
+ if (r !== NEXT) return r;
2566
+ n = n[1];
2568
2567
  }
2569
- enterMacro(macroName, macroVars, macroSlots) {
2570
- const { document: document2, Text, Comment, nodes, events, macroNodes } = this;
2571
- const frame = { macroName, macroVars, macroSlots };
2572
- const v = new _ParseCtxClassSetCollector(
2573
- document2,
2574
- Text,
2575
- Comment,
2576
- nodes,
2577
- events,
2578
- macroNodes,
2579
- frame,
2580
- this
2581
- );
2582
- v.classes = this.classes;
2583
- return v;
2568
+ return dv;
2569
+ }
2570
+ var BindFrame = class {
2571
+ constructor(it, binds, isFrame) {
2572
+ this.it = it;
2573
+ this.binds = binds;
2574
+ this.isFrame = isFrame;
2584
2575
  }
2585
- onAttributes(attrs, _wrapperAttrs, _textChild, _isMacroCall, _tag) {
2586
- if (Array.isArray(attrs.items)) {
2587
- for (const attr of attrs.items) {
2588
- if (attr.name !== "class") {
2589
- continue;
2590
- }
2591
- const { val, thenVal, elseVal } = attr;
2592
- if (thenVal !== void 0) {
2593
- this._maybeAddVal(thenVal);
2594
- this._maybeAddVal(elseVal);
2595
- } else {
2596
- this._maybeAddVal(val);
2597
- }
2598
- }
2599
- } else {
2600
- const attr = attrs.items.class;
2601
- if (attr) {
2602
- this._addClasses(attr);
2603
- }
2604
- }
2576
+ lookup(name) {
2577
+ const v = this.binds[name];
2578
+ return v === void 0 ? this.isFrame ? STOP : NEXT : v;
2605
2579
  }
2606
- _maybeAddVal(value) {
2607
- if (!this._maybeAddStrTpl(value) && typeof value?.val === "string") {
2608
- this._addClasses(value.val);
2609
- }
2580
+ };
2581
+ var DynFrame = class {
2582
+ constructor(binds, types) {
2583
+ this.binds = binds;
2584
+ this.types = types;
2610
2585
  }
2611
- _maybeAddStrTpl(value) {
2612
- if (value?.vals !== void 0) {
2613
- for (const val of value.vals) {
2614
- if (val instanceof ConstVal && val.val !== "") {
2615
- this._addClasses(val.val);
2616
- }
2617
- }
2618
- return true;
2619
- }
2620
- return false;
2586
+ lookup(name) {
2587
+ const v = (isTypeName(name) ? this.types : this.binds)[name];
2588
+ return v === void 0 ? NEXT : v;
2621
2589
  }
2622
2590
  };
2623
- function collectAppClassesInSet(app) {
2624
- const classes = /* @__PURE__ */ new Set();
2625
- for (const Comp of app.comps.byId.values()) {
2626
- for (const key in Comp.views) {
2627
- const view = Comp.views[key];
2628
- for (const name of view.ctx.classes) {
2629
- classes.add(name);
2630
- }
2631
- }
2591
+ function computeViewsId(views) {
2592
+ let s = "";
2593
+ let n = views;
2594
+ while (n !== null) {
2595
+ s += n[0];
2596
+ n = n[1];
2632
2597
  }
2633
- return classes;
2598
+ return s === "main" ? "" : s;
2634
2599
  }
2635
-
2636
- // src/components.js
2637
- var COMPONENT = /* @__PURE__ */ Symbol.for("tutuca.component");
2638
- var Components = class {
2639
- constructor() {
2640
- this.byId = /* @__PURE__ */ new Map();
2600
+ var Stack = class _Stack {
2601
+ constructor(fields) {
2602
+ Object.assign(this, fields);
2603
+ }
2604
+ _with(patch) {
2605
+ return new _Stack({ ...this, ...patch });
2606
+ }
2607
+ // Evaluate every provide the entered component publishes and push them as one
2608
+ // dynBinds frame, keyed by NAME. A value is published together with the absolute
2609
+ // path it lives at: the same declaration is read as `*name` AND resumed at by
2610
+ // `<x render="*name">`, so a consumer needs both halves. Types go in the same
2611
+ // frame's other map. No-op with no provides.
2612
+ _pushProvides() {
2613
+ const comp = this.comps.getCompFor(this.it);
2614
+ if (comp == null) return this;
2615
+ const { provide, provideType } = comp;
2616
+ const binds = {};
2617
+ const types = {};
2618
+ let has = false;
2619
+ const base = this._publishBase();
2620
+ for (const k in provide) {
2621
+ const step = provide[k].toPathItem?.() ?? null;
2622
+ if (step === null) continue;
2623
+ const value = provide[k].eval(this);
2624
+ binds[k] = { value, path: base === null ? null : base.concat([step]) };
2625
+ has = true;
2626
+ }
2627
+ for (const k in provideType) {
2628
+ types[k] = provideType[k];
2629
+ has = true;
2630
+ }
2631
+ if (!has) return this;
2632
+ return this._with({ dynBinds: [new DynFrame(binds, types), this.dynBinds] });
2633
+ }
2634
+ // The absolute address of the component being entered, or null when this
2635
+ // position cannot be written down as one. Frame-only steps carry bindings and
2636
+ // address nothing, so they are compacted away first; what is left is checked
2637
+ // against the value actually being rendered, because a scope CAN move the
2638
+ // render position without contributing an addressing step (a plain `@each`
2639
+ // body re-binds `it` to the item while its rebuild step is an identity).
2640
+ //
2641
+ // A null base still publishes the VALUE — `*name` reads it fine — but there is
2642
+ // nowhere to resume, so `<x render="*name">` renders nothing rather than
2643
+ // editing whatever happens to live at the address we guessed.
2644
+ _publishBase() {
2645
+ const base = this.renderPath.toTransactionPath().compact();
2646
+ return base.lookup(this.root, NOT_FOUND) === this.it ? base : null;
2647
+ }
2648
+ static root(comps, it, ctx = null) {
2649
+ return new _Stack({
2650
+ comps,
2651
+ root: it,
2652
+ it,
2653
+ binds: [new BindFrame(it, {}, true), null],
2654
+ dynBinds: [new DynFrame({}, {}), null],
2655
+ views: ["main", null],
2656
+ viewsId: "",
2657
+ renderPath: new DispatchPath(),
2658
+ pendingFrame: false,
2659
+ ctx
2660
+ })._pushProvides();
2661
+ }
2662
+ // `renderPath` defaults to this stack's own: an ordinary scope does not move.
2663
+ // `pendingFrame` clears on a component frame (which emits the base) and is
2664
+ // inherited by transparent scopes, which have to carry it to the next boundary.
2665
+ enter(it, bindings = {}, isFrame = true, renderPath = this.renderPath, pendingFrame = null) {
2666
+ const stack = this._with({
2667
+ it,
2668
+ binds: [new BindFrame(it, bindings, isFrame), this.binds],
2669
+ renderPath,
2670
+ pendingFrame: pendingFrame ?? (isFrame ? false : this.pendingFrame)
2671
+ });
2672
+ return isFrame ? stack._pushProvides() : stack;
2673
+ }
2674
+ pushViewName(name) {
2675
+ const views = [name, this.views];
2676
+ return this._with({ views, viewsId: computeViewsId(views) });
2677
+ }
2678
+ // Published types are stable per scope and would only churn the render cache, so
2679
+ // the cache key covers values alone.
2680
+ _pushDynBindValuesToArray(arr, comp) {
2681
+ for (const k in comp.provide) arr.push(this.lookupDynamic(k));
2682
+ for (const k in comp.lookup) arr.push(this.lookupDynamic(k));
2683
+ }
2684
+ // `*name`: the nearest binding above (including this component's own provides,
2685
+ // pushed on entering it), else a path registered in the component's lexical
2686
+ // scope, else this component's declared default, else null.
2687
+ //
2688
+ // One chain walk and no producer resolution: a lookup names what it WANTS, so the
2689
+ // frame it wants is keyed by that name. The default belongs to the CONSUMER's
2690
+ // declaration and is evaluated against the consumer's stack, which is why it is
2691
+ // consulted only after the whole chain has missed.
2692
+ lookupDynamicLocated(name) {
2693
+ if (isTypeName(name)) return null;
2694
+ const v = lookup(this.dynBinds, name);
2695
+ if (v != null) return v;
2696
+ const comp = this.comps.getCompFor(this.it);
2697
+ if (comp == null) return null;
2698
+ const path2 = comp.scope?.lookupPath?.(name) ?? null;
2699
+ if (path2 !== null) {
2700
+ const value2 = path2.lookup(this.root, NOT_FOUND);
2701
+ if (value2 !== NOT_FOUND) return { value: value2, path: path2 };
2702
+ }
2703
+ const dval = comp.lookup[name] ?? null;
2704
+ if (dval === null) return null;
2705
+ const step = dval.toPathItem?.() ?? null;
2706
+ const value = dval.eval(this);
2707
+ return step === null ? { value, path: null } : { value, path: this.renderPath.toTransactionPath().concat([step]) };
2708
+ }
2709
+ lookupDynamic(name) {
2710
+ if (isTypeName(name)) return lookup(this.dynBinds, name);
2711
+ return this.lookupDynamicLocated(name)?.value ?? null;
2712
+ }
2713
+ lookupBind(name) {
2714
+ return lookup(this.binds, name);
2715
+ }
2716
+ lookupFieldRaw(name) {
2717
+ return this.it[name] ?? null;
2718
+ }
2719
+ lookupMethod(name) {
2720
+ const fn = this.it[name];
2721
+ return fn instanceof Function ? fn.call(this.it) : null;
2722
+ }
2723
+ // The dispatched DOM event / drag info, read only by EventMemberVal's
2724
+ // `e.<member>` handler args. Null outside a live event transaction (`ctx` is
2725
+ // then a send/intent transaction, which carries no `e`).
2726
+ lookupEvent() {
2727
+ return this.ctx?.e ?? null;
2728
+ }
2729
+ lookupDragInfo() {
2730
+ return this.ctx?.dragInfo ?? null;
2731
+ }
2732
+ getHandlerFor(name, key) {
2733
+ return this.comps.getHandlerFor(this.it, name, key);
2734
+ }
2735
+ lookupBestView(views, defaultViewName) {
2736
+ let n = this.views;
2737
+ while (n !== null) {
2738
+ const view = views[n[0]];
2739
+ if (view !== void 0) return view;
2740
+ n = n[1];
2741
+ }
2742
+ return views[defaultViewName];
2743
+ }
2744
+ };
2745
+ var NOT_FOUND = /* @__PURE__ */ Symbol("NOT_FOUND");
2746
+
2747
+ // src/components.js
2748
+ var COMPONENT = /* @__PURE__ */ Symbol.for("tutuca.component");
2749
+ var Components = class {
2750
+ constructor() {
2751
+ this.byId = /* @__PURE__ */ new Map();
2641
2752
  }
2642
2753
  registerComponent(Comp) {
2643
- this.byId.set(Comp[COMPONENT].id, Comp);
2754
+ this.byId.set(Comp.id, Comp);
2644
2755
  }
2645
2756
  getComponentForId(id) {
2646
2757
  return this.byId.get(id) ?? null;
@@ -2660,7 +2771,7 @@ var Components = class {
2660
2771
  }
2661
2772
  compileStyles() {
2662
2773
  const styles = [];
2663
- for (const Comp of this.byId.values()) styles.push(Comp[COMPONENT].compileStyle());
2774
+ for (const Comp of this.byId.values()) styles.push(Comp.compileStyle());
2664
2775
  return styles.join("\n");
2665
2776
  }
2666
2777
  };
@@ -2671,15 +2782,17 @@ var ComponentStack = class _ComponentStack {
2671
2782
  this.byName = {};
2672
2783
  this.intentsByName = {};
2673
2784
  this.macros = {};
2785
+ this.paths = {};
2674
2786
  }
2675
2787
  enter() {
2676
2788
  return new _ComponentStack(this.comps, this);
2677
2789
  }
2678
2790
  registerComponents(comps, opts) {
2679
- const { aliases = {} } = opts ?? {};
2791
+ const { aliases = {}, paths } = opts ?? {};
2792
+ if (paths) this.registerPaths(paths);
2680
2793
  for (let i = 0; i < comps.length; i++) {
2681
2794
  const Comp = comps[i];
2682
- Comp[COMPONENT].scope = this.enter();
2795
+ Comp.scope = this.enter();
2683
2796
  this.comps.registerComponent(Comp);
2684
2797
  this.byName[Comp.name] = Comp;
2685
2798
  }
@@ -2690,6 +2803,27 @@ var ComponentStack = class _ComponentStack {
2690
2803
  else console.warn("alias", alias, "to inexistent component", aliases[alias]);
2691
2804
  }
2692
2805
  }
2806
+ // Register lowercase names as absolute paths from the app state root. A
2807
+ // descendant that declares one in its `lookup` reads and renders `*name` without
2808
+ // anything above it publishing one — which is what makes a session, a theme or a
2809
+ // host-owned value available in its natural registration scope, instead of forcing
2810
+ // an application root whose only job is to `provide` it. Register on a nested
2811
+ // scope to narrow a name; nearest registration wins.
2812
+ //
2813
+ // Uppercase names are ignored: a component TYPE is what `lookupComponent` already
2814
+ // answers, and a type has no path.
2815
+ registerPaths(paths) {
2816
+ for (const name in paths) {
2817
+ if (isTypeName(name)) {
2818
+ console.warn("registerPaths: a type name has no path", name);
2819
+ continue;
2820
+ }
2821
+ this.paths[name] = paths[name].toPath();
2822
+ }
2823
+ }
2824
+ lookupPath(name) {
2825
+ return this.paths[name] ?? this.parent?.lookupPath(name) ?? null;
2826
+ }
2693
2827
  registerMacros(macros) {
2694
2828
  for (const key in macros) {
2695
2829
  const lower = key.toLowerCase();
@@ -2721,97 +2855,82 @@ var ComponentStack = class _ComponentStack {
2721
2855
  lookupComponent(name) {
2722
2856
  return this.byName[name] ?? this.parent?.lookupComponent(name) ?? null;
2723
2857
  }
2724
- // The component in this scope chain that provides `name`, innermost first. A lookup
2725
- // names a value without naming its producer, so the render-target teleport
2726
- // (resolveDynProducer) recovers the producer here instead. Sound because
2727
- // PROVIDE_NAME_COLLISION keeps a provide name to one producer per chain; with two,
2728
- // this returns the innermost and the linter has already reported the ambiguity.
2729
- lookupProvider(name) {
2858
+ // Whether anything in this scope chain provides `name`. Existence only: a lookup
2859
+ // names what it WANTS and takes whoever is nearest above it at render time, so
2860
+ // there is no producer to identify — several components may publish one name and
2861
+ // the live render ancestry decides. Used by the linter to tell a lookup that can
2862
+ // be satisfied from one that never will be.
2863
+ hasProvider(name) {
2730
2864
  for (const compName in this.byName) {
2731
- const Comp = this.byName[compName];
2732
- if (Comp.provide?.[name] !== void 0) return Comp;
2865
+ if (this.byName[compName].provide?.[name] !== void 0) return true;
2733
2866
  }
2734
- return this.parent?.lookupProvider(name) ?? null;
2867
+ return this.parent?.hasProvider(name) ?? false;
2735
2868
  }
2736
2869
  lookupMacro(name) {
2737
2870
  return this.macros[name] ?? this.parent?.lookupMacro(name) ?? null;
2738
2871
  }
2739
2872
  };
2740
- var ProvideInfo = class {
2741
- constructor(val) {
2742
- this.val = val;
2743
- }
2744
- };
2745
- var LookupInfo = class {
2746
- constructor(val) {
2747
- this.val = val;
2748
- }
2749
- };
2750
2873
  var isString = (v) => typeof v === "string";
2751
- var isTypeName = (s) => {
2752
- const c = s.charCodeAt(0);
2753
- return c >= 65 && c <= 90;
2754
- };
2755
2874
  var _rawSpecKeys = "name view style commonStyle globalStyle receive intent alter views provide lookup fields methods statics";
2756
2875
  var KNOWN_SPEC_KEYS = new Set(_rawSpecKeys.split(" "));
2757
2876
  var _compId = 0;
2758
- var Component = class {
2759
- constructor(Class, o) {
2760
- this.id = _compId++;
2761
- this.name = o.name ?? "UnkComp";
2762
- this.Class = Class;
2763
- this.views = { main: new View("main", o.view, o.style) };
2764
- this.commonStyle = o.commonStyle ?? "";
2765
- this.globalStyle = o.globalStyle ?? "";
2766
- this.receive = o.receive ?? {};
2767
- this.intent = o.intent ?? {};
2768
- this.alter = o.alter ?? {};
2769
- for (const name in o.views ?? {}) {
2770
- const v = o.views[name];
2771
- const { view, style } = isString(v) ? { view: v } : v;
2772
- this.views[name] = new View(name, view, style);
2773
- }
2774
- this._rawProvide = o.provide ?? {};
2775
- this._rawLookup = o.lookup ?? [];
2776
- this.provide = {};
2777
- this.provideType = {};
2778
- this.lookup = {};
2779
- this.scope = null;
2780
- this.spec = o;
2781
- this.extra = {};
2782
- for (const key of Object.keys(o)) if (!KNOWN_SPEC_KEYS.has(key)) this.extra[key] = o[key];
2783
- }
2877
+ function initComponent(Class, o) {
2878
+ Class[COMPONENT] = Class;
2879
+ Class.id = _compId++;
2880
+ Class.views = { main: new View("main", o.view, o.style) };
2881
+ Class.commonStyle = o.commonStyle ?? "";
2882
+ Class.globalStyle = o.globalStyle ?? "";
2883
+ Class.receive = o.receive ?? {};
2884
+ Class.intent = o.intent ?? {};
2885
+ Class.alter = o.alter ?? {};
2886
+ for (const name in o.views ?? {}) {
2887
+ const v = o.views[name];
2888
+ const { view, style } = isString(v) ? { view: v } : v;
2889
+ Class.views[name] = new View(name, view, style);
2890
+ }
2891
+ Class._rawProvide = o.provide ?? {};
2892
+ Class._rawLookup = o.lookup ?? [];
2893
+ Class.provide = {};
2894
+ Class.provideType = {};
2895
+ Class.lookup = {};
2896
+ Class.scope = null;
2897
+ Class.spec = o;
2898
+ Class.extra = {};
2899
+ for (const key of Object.keys(o)) if (!KNOWN_SPEC_KEYS.has(key)) Class.extra[key] = o[key];
2900
+ return Object.assign(Class, COMPONENT_METHODS);
2901
+ }
2902
+ var COMPONENT_METHODS = {
2784
2903
  compile(ParseContext2) {
2785
2904
  for (const name in this.views)
2786
2905
  this.views[name].compile(new ParseContext2(), this.scope, this.id);
2787
2906
  const ctx = this.views.main.ctx;
2788
2907
  for (const key in this._rawProvide) {
2789
2908
  if (isTypeName(key)) {
2790
- if (this._rawProvide[key] === "self") this.provideType[key] = this.Class;
2909
+ if (this._rawProvide[key] === "self") this.provideType[key] = this;
2791
2910
  continue;
2792
2911
  }
2793
2912
  const val = parseProvide(this._rawProvide[key], ctx);
2794
- if (val) this.provide[key] = new ProvideInfo(val);
2913
+ if (val) this.provide[key] = val;
2795
2914
  }
2796
2915
  for (const entry of this._rawLookup) {
2797
2916
  const name = isString(entry) ? entry : isString(entry?.name) ? entry.name : null;
2798
2917
  if (name === null) continue;
2799
2918
  const defStr = isString(entry?.default) ? entry.default : null;
2800
- this.lookup[name] = new LookupInfo(defStr === null ? null : parseField(defStr, ctx));
2919
+ this.lookup[name] = defStr === null ? null : parseField(defStr, ctx);
2801
2920
  }
2802
2921
  for (const key in this.lookup)
2803
2922
  if (this.provide[key] !== void 0)
2804
2923
  console.warn("name declared in both provide and lookup", this.name, key);
2805
- }
2924
+ },
2806
2925
  getView(name) {
2807
2926
  return this.views[name] ?? this.views.main;
2808
- }
2809
- getEventForId(id, name = "main") {
2810
- return this.getView(name).ctx.getEventForId(id);
2811
- }
2812
- getNodeForId(id, name = "main") {
2813
- return this.getView(name).ctx.getNodeForId(id);
2814
- }
2927
+ },
2928
+ getEventForId(id, viewName) {
2929
+ return this.getView(viewName).ctx.getEventForId(id);
2930
+ },
2931
+ getNodeForId(id, viewName) {
2932
+ return this.getView(viewName).ctx.getNodeForId(id);
2933
+ },
2815
2934
  compileStyle() {
2816
2935
  const { id, commonStyle, globalStyle, views } = this;
2817
2936
  const styles = commonStyle ? [`[data-cid="${id}"]{${commonStyle}}`] : [];
@@ -2824,6 +2943,68 @@ var Component = class {
2824
2943
  }
2825
2944
  };
2826
2945
 
2946
+ // src/util/parsectx.js
2947
+ var ParseCtxClassSetCollector = class extends ParseContext {
2948
+ constructor(...args) {
2949
+ super(...args);
2950
+ this.classes = /* @__PURE__ */ new Set();
2951
+ }
2952
+ _addClasses(s) {
2953
+ for (const v of s.split(/\s+/)) {
2954
+ this.classes.add(v);
2955
+ }
2956
+ }
2957
+ onAttributes(attrs, _wrapperAttrs, _textChild, _isMacroCall, _tag) {
2958
+ if (Array.isArray(attrs.items)) {
2959
+ for (const attr of attrs.items) {
2960
+ if (attr.name !== "class") {
2961
+ continue;
2962
+ }
2963
+ const { val, thenVal, elseVal } = attr;
2964
+ if (thenVal !== void 0) {
2965
+ this._maybeAddVal(thenVal);
2966
+ this._maybeAddVal(elseVal);
2967
+ } else {
2968
+ this._maybeAddVal(val);
2969
+ }
2970
+ }
2971
+ } else {
2972
+ const attr = attrs.items.class;
2973
+ if (attr) {
2974
+ this._addClasses(attr);
2975
+ }
2976
+ }
2977
+ }
2978
+ _maybeAddVal(value) {
2979
+ if (!this._maybeAddStrTpl(value) && typeof value?.val === "string") {
2980
+ this._addClasses(value.val);
2981
+ }
2982
+ }
2983
+ _maybeAddStrTpl(value) {
2984
+ if (value?.vals !== void 0) {
2985
+ for (const val of value.vals) {
2986
+ if (val instanceof ConstVal && val.val !== "") {
2987
+ this._addClasses(val.val);
2988
+ }
2989
+ }
2990
+ return true;
2991
+ }
2992
+ return false;
2993
+ }
2994
+ };
2995
+ function collectAppClassesInSet(app) {
2996
+ const classes = /* @__PURE__ */ new Set();
2997
+ for (const Comp of app.comps.byId.values()) {
2998
+ for (const key in Comp.views) {
2999
+ const view = Comp.views[key];
3000
+ for (const name of view.ctx.classes) {
3001
+ classes.add(name);
3002
+ }
3003
+ }
3004
+ }
3005
+ return classes;
3006
+ }
3007
+
2827
3008
  // tools/core/html-tokenizer.js
2828
3009
  var CharCodes = {
2829
3010
  Tab: 9,
@@ -5115,7 +5296,6 @@ var DYN_VAL_NOT_DEFINED = "DYN_VAL_NOT_DEFINED";
5115
5296
  var DYN_ALIAS_NOT_REFERENCED = "DYN_ALIAS_NOT_REFERENCED";
5116
5297
  var PROVIDE_NOT_ADDRESSABLE = "PROVIDE_NOT_ADDRESSABLE";
5117
5298
  var PROVIDE_TYPE_BAD_SHAPE = "PROVIDE_TYPE_BAD_SHAPE";
5118
- var PROVIDE_NAME_COLLISION = "PROVIDE_NAME_COLLISION";
5119
5299
  var LOOKUP_BAD_SHAPE = "LOOKUP_BAD_SHAPE";
5120
5300
  var LOOKUP_NO_PROVIDER = "LOOKUP_NO_PROVIDER";
5121
5301
  var RENDER_IT_OUTSIDE_OF_LOOP = "RENDER_IT_OUTSIDE_OF_LOOP";
@@ -5132,6 +5312,7 @@ var IF_NO_BRANCH_SET = "IF_NO_BRANCH_SET";
5132
5312
  var UNKNOWN_COMPONENT_NAME = "UNKNOWN_COMPONENT_NAME";
5133
5313
  var UNKNOWN_MACRO_ARG = "UNKNOWN_MACRO_ARG";
5134
5314
  var UNKNOWN_DIRECTIVE = "UNKNOWN_DIRECTIVE";
5315
+ var ORPHAN_DIRECTIVE = "ORPHAN_DIRECTIVE";
5135
5316
  var UNKNOWN_X_OP = "UNKNOWN_X_OP";
5136
5317
  var UNKNOWN_X_ATTR = "UNKNOWN_X_ATTR";
5137
5318
  var X_OP_IGNORES_CHILDREN = "X_OP_IGNORES_CHILDREN";
@@ -5166,6 +5347,7 @@ var LEVEL_ERROR2 = "error";
5166
5347
  var LEVEL_HINT = "hint";
5167
5348
  var PARSE_ISSUES = {
5168
5349
  "unknown-directive": { id: UNKNOWN_DIRECTIVE, candidates: KNOWN_DIRECTIVE_NAMES },
5350
+ "orphan-directive": { id: ORPHAN_DIRECTIVE },
5169
5351
  "unknown-x-op": { id: UNKNOWN_X_OP, candidates: X_KNOWN_OP_NAMES, atPrefix: X_KNOWN_OP_NAMES },
5170
5352
  "unknown-x-attr": {
5171
5353
  id: UNKNOWN_X_ATTR,
@@ -5238,7 +5420,6 @@ function checkComponent(Comp, lx = new LintContext(), { wellKnownExtras = EMPTY_
5238
5420
  checkFieldMethodNameCollisions(lx, Comp);
5239
5421
  checkProvidesAreAddressable(lx, Comp);
5240
5422
  checkProvidedTypes(lx, Comp);
5241
- checkProvideNameCollisions(lx, Comp);
5242
5423
  checkLookupShapes(lx, Comp);
5243
5424
  checkLookupTypesResolve(lx, Comp);
5244
5425
  checkLookupsHaveProviders(lx, Comp);
@@ -5922,19 +6103,6 @@ function checkProvidedTypes(lx, Comp) {
5922
6103
  if (raw !== "self") lx.error(PROVIDE_TYPE_BAD_SHAPE, { name, value: raw });
5923
6104
  }
5924
6105
  }
5925
- function checkProvideNameCollisions(lx, Comp) {
5926
- const scope = Comp.scope;
5927
- if (!scope) return;
5928
- for (const name in Comp.provide) {
5929
- for (let s = scope; s; s = s.parent) {
5930
- for (const otherName in s.byName) {
5931
- const Other = s.byName[otherName];
5932
- if (Other !== Comp && Other.provide?.[name] !== void 0)
5933
- lx.error(PROVIDE_NAME_COLLISION, { name, other: Other.name });
5934
- }
5935
- }
5936
- }
5937
- }
5938
6106
  var KNOWN_LOOKUP_KEYS = /* @__PURE__ */ new Set(["name", "default"]);
5939
6107
  function checkLookupShapes(lx, Comp) {
5940
6108
  const raw = Comp._rawLookup;
@@ -5976,8 +6144,8 @@ function checkLookupsHaveProviders(lx, Comp) {
5976
6144
  if (!scope) return;
5977
6145
  for (const name in Comp.lookup) {
5978
6146
  if (isTypeName2(name)) continue;
5979
- if (scope.lookupProvider?.(name)) continue;
5980
- const info = { name, hasDefault: Comp.lookup[name].val != null };
6147
+ if (scope.hasProvider?.(name) || scope.lookupPath?.(name)) continue;
6148
+ const info = { name, hasDefault: Comp.lookup[name] != null };
5981
6149
  if (info.hasDefault) lx.hint(LOOKUP_NO_PROVIDER, info);
5982
6150
  else lx.error(LOOKUP_NO_PROVIDER, info);
5983
6151
  }
@@ -6025,13 +6193,14 @@ var LintContext = class {
6025
6193
  this.reports.push({ id, info, level, context: { ...this.frame }, suggestion });
6026
6194
  }
6027
6195
  };
6028
- var LintParseContext = class extends ParseContext {
6029
- constructor(document2, Text, Comment) {
6030
- super(document2, Text, Comment);
6196
+ var LintParseContext = class extends ParseCtxClassSetCollector {
6197
+ constructor(document2) {
6198
+ super(document2);
6031
6199
  this.attrs = [];
6032
6200
  this.parseIssues = [];
6033
6201
  }
6034
6202
  onAttributes(attrs, wrapperAttrs, textChild, isMacroCall = false, tag = null) {
6203
+ super.onAttributes(attrs, wrapperAttrs, textChild, isMacroCall, tag);
6035
6204
  this.attrs.push({ attrs, wrapperAttrs, textChild, isMacroCall, tag });
6036
6205
  }
6037
6206
  onParseIssue(kind, info) {
@@ -6068,14 +6237,36 @@ function dispatchPhase(dispatcher, targetPath, phase, self) {
6068
6237
 
6069
6238
  // src/oo.js
6070
6239
  var BAD_VALUE2 = /* @__PURE__ */ Symbol("BadValue");
6071
- var nullCoercer = (v) => v;
6240
+ var COERCE_NONE = (_v) => null;
6241
+ var COERCE_MAP = (v) => {
6242
+ if (v instanceof Map) return new Map(v);
6243
+ if (Array.isArray(v) || isPlainObject(v))
6244
+ return new Map(Array.isArray(v) ? v : Object.entries(v));
6245
+ return null;
6246
+ };
6247
+ var FIELD_TYPES = {
6248
+ any: [(_v) => true, COERCE_NONE, () => null],
6249
+ text: [(v) => typeof v === "string", (v) => v?.toString?.() ?? "", () => ""],
6250
+ int: [Number.isInteger, (v) => Number.isFinite(v) ? Math.trunc(v) : null, () => 0],
6251
+ float: [Number.isFinite, COERCE_NONE, () => 0],
6252
+ bool: [(v) => typeof v === "boolean", (v) => !!v, () => false],
6253
+ list: [Array.isArray, (v) => Array.isArray(v) ? [...v] : null, () => []],
6254
+ object: [isPlainObject, (v) => isPlainObject(v) ? { ...v } : null, () => ({})],
6255
+ map: [(v) => v instanceof Map, COERCE_MAP, () => /* @__PURE__ */ new Map()],
6256
+ set: [
6257
+ (v) => v instanceof Set,
6258
+ (v) => v instanceof Set || Array.isArray(v) ? new Set(v) : null,
6259
+ () => /* @__PURE__ */ new Set()
6260
+ ]
6261
+ };
6072
6262
  var Field = class {
6073
- constructor(type, name, typeCheck, coercer, defaultValue = null) {
6263
+ // `type` names an entry of FIELD_TYPES; `defaultValue` falls back to the type's own.
6264
+ constructor(type, name, defaultValue, [typeCheck, coercer, makeDefault] = FIELD_TYPES[type]) {
6074
6265
  this.type = type;
6075
6266
  this.name = name;
6076
6267
  this.typeCheck = typeCheck;
6077
6268
  this.coercer = coercer;
6078
- this.defaultValue = defaultValue;
6269
+ this.defaultValue = defaultValue === void 0 ? makeDefault() : defaultValue;
6079
6270
  }
6080
6271
  isValid(v) {
6081
6272
  return this.typeCheck(v);
@@ -6089,81 +6280,14 @@ var Field = class {
6089
6280
  return this.coerceOr(v, this.defaultValue);
6090
6281
  }
6091
6282
  };
6092
- var CHECK_TYPE_ANY = (_v) => true;
6093
- var CHECK_TYPE_INT = Number.isInteger;
6094
- var CHECK_TYPE_FLOAT = Number.isFinite;
6095
- var CHECK_TYPE_BOOL = (v) => typeof v === "boolean";
6096
- var CHECK_TYPE_STRING = (v) => typeof v === "string";
6097
- var CHECK_TYPE_LIST = Array.isArray;
6098
- var CHECK_TYPE_OBJECT = isPlainObject;
6099
- var CHECK_TYPE_MAP = (v) => v instanceof Map;
6100
- var CHECK_TYPE_SET = (v) => v instanceof Set;
6101
- var COERCE_NONE = (_v) => null;
6102
- var COERCE_BOOL = (v) => !!v;
6103
- var COERCE_STRING = (v) => v?.toString?.() ?? "";
6104
- var COERCE_INT = (v) => Number.isFinite(v) ? Math.trunc(v) : null;
6105
- var COERCE_LIST = (v) => Array.isArray(v) ? [...v] : null;
6106
- var COERCE_OBJECT = (v) => isPlainObject(v) ? { ...v } : null;
6107
- var COERCE_MAP = (v) => {
6108
- if (v instanceof Map) return new Map(v);
6109
- if (Array.isArray(v) || isPlainObject(v))
6110
- return new Map(Array.isArray(v) ? v : Object.entries(v));
6111
- return null;
6112
- };
6113
- var COERCE_SET = (v) => v instanceof Set || Array.isArray(v) ? new Set(v) : null;
6114
- var FieldBool = class extends Field {
6115
- constructor(name, defaultValue = false) {
6116
- super("bool", name, CHECK_TYPE_BOOL, COERCE_BOOL, defaultValue);
6117
- }
6118
- };
6119
- var FieldAny = class extends Field {
6120
- constructor(name, defaultValue = null) {
6121
- super("any", name, CHECK_TYPE_ANY, nullCoercer, defaultValue);
6122
- }
6123
- };
6124
- var FieldString = class extends Field {
6125
- constructor(name, defaultValue = "") {
6126
- super("text", name, CHECK_TYPE_STRING, COERCE_STRING, defaultValue);
6127
- }
6128
- };
6129
- var FieldInt = class extends Field {
6130
- constructor(name, defaultValue = 0) {
6131
- super("int", name, CHECK_TYPE_INT, COERCE_INT, defaultValue);
6132
- }
6133
- };
6134
- var FieldFloat = class extends Field {
6135
- constructor(name, defaultValue = 0) {
6136
- super("float", name, CHECK_TYPE_FLOAT, COERCE_NONE, defaultValue);
6137
- }
6138
- };
6139
- var metaOf = (v) => v?.constructor?.[COMPONENT] ?? v?.constructor?.getMetaClass?.();
6283
+ var metaOf = (v) => v?.constructor?.getMetaClass?.();
6140
6284
  var getTypeName = (v) => metaOf(v)?.name ?? null;
6141
6285
  var FieldComp = class extends Field {
6142
6286
  constructor(type, name, args) {
6143
- super(type, name, (v) => getTypeName(v) === type, nullCoercer, null);
6287
+ super(type, name, null, [(v) => getTypeName(v) === type, COERCE_NONE, () => null]);
6144
6288
  this.args = args;
6145
6289
  }
6146
6290
  };
6147
- var FieldList = class extends Field {
6148
- constructor(name, defaultValue = []) {
6149
- super("list", name, CHECK_TYPE_LIST, COERCE_LIST, defaultValue);
6150
- }
6151
- };
6152
- var FieldObject = class extends Field {
6153
- constructor(name, defaultValue = {}) {
6154
- super("object", name, CHECK_TYPE_OBJECT, COERCE_OBJECT, defaultValue);
6155
- }
6156
- };
6157
- var FieldMap = class extends Field {
6158
- constructor(name, defaultValue = /* @__PURE__ */ new Map()) {
6159
- super("map", name, CHECK_TYPE_MAP, COERCE_MAP, defaultValue);
6160
- }
6161
- };
6162
- var FieldSet = class extends Field {
6163
- constructor(name, defaultValue = /* @__PURE__ */ new Set()) {
6164
- super("set", name, CHECK_TYPE_SET, COERCE_SET, defaultValue);
6165
- }
6166
- };
6167
6291
  function mkCompField(field, scope, args) {
6168
6292
  const Comp = scope?.lookupComponent(field.type) ?? null;
6169
6293
  if (Comp === null)
@@ -6172,112 +6296,66 @@ function mkCompField(field, scope, args) {
6172
6296
  );
6173
6297
  return Comp?.make({ ...field.args, ...args }, { scope }) ?? null;
6174
6298
  }
6175
- var ClassBuilder = class {
6176
- constructor(name) {
6177
- this.name = name;
6178
- this.fields = {};
6179
- this.compFields = /* @__PURE__ */ new Set();
6180
- this._methods = {};
6181
- this._statics = {};
6182
- }
6183
- build() {
6184
- const { name, fields, compFields, _methods } = this;
6185
- const defaults = Object.fromEntries(
6186
- Object.entries(fields).map(([fieldName, field]) => [fieldName, field.defaultValue])
6187
- );
6188
- const Class = {
6189
- [name]: class {
6190
- constructor(values = {}) {
6191
- Object.assign(this, defaults, values);
6192
- }
6299
+ function fieldFromSpec(name, value) {
6300
+ const type = typeof value;
6301
+ if (type === "string") return new Field("text", name, value);
6302
+ if (type === "number") return new Field("float", name, value);
6303
+ if (type === "boolean") return new Field("bool", name, value);
6304
+ if (Array.isArray(value)) return new Field("list", name, [...value]);
6305
+ if (value instanceof Set) return new Field("set", name, new Set(value));
6306
+ if (value instanceof Map) return new Field("map", name, new Map(value));
6307
+ if (value?.type && Object.hasOwn(value, "defaultValue")) {
6308
+ const t = value.type in FIELD_TYPES ? value.type : "any";
6309
+ const probe = new Field(t, name);
6310
+ return new Field(t, name, probe.coerceOr(value.defaultValue, probe.defaultValue));
6311
+ }
6312
+ if (value?.component && value?.args !== void 0)
6313
+ return new FieldComp(value.component, name, value.args);
6314
+ if (isPlainObject(value)) return new Field("object", name, { ...value });
6315
+ return new Field("any", name, value);
6316
+ }
6317
+ function classFromData(name, { fields: spec = {}, methods = {}, statics = {} }) {
6318
+ const fields = {};
6319
+ for (const key in spec) fields[key] = fieldFromSpec(key, spec[key]);
6320
+ const defaults = Object.fromEntries(
6321
+ Object.entries(fields).map(([key, field]) => [key, field.defaultValue])
6322
+ );
6323
+ const Class = {
6324
+ [name]: class {
6325
+ constructor(values = {}) {
6326
+ Object.assign(this, defaults, values);
6193
6327
  }
6194
- }[name];
6195
- Class[immer_exports.immerable] = true;
6196
- Object.assign(Class.prototype, _methods);
6197
- const metaClass = { fields, name, methods: _methods };
6198
- Object.assign(
6199
- Class,
6200
- {
6201
- getMetaClass: () => metaClass,
6202
- make(inArgs = {}, opts = {}) {
6203
- const args = {};
6204
- const scope = opts.scope ?? this[COMPONENT]?.scope ?? this.scope;
6205
- for (const key in inArgs) {
6206
- const field = fields[key];
6207
- if (compFields.has(key)) args[key] = mkCompField(field, scope, inArgs[key]);
6208
- else if (field === void 0)
6209
- console.warn("extra argument to constructor:", name, key, inArgs);
6210
- else args[key] = field.coerceOrDefault(inArgs[key]);
6211
- }
6212
- for (const key of compFields)
6213
- if (args[key] === void 0) args[key] = mkCompField(fields[key], scope, inArgs[key]);
6214
- return (0, immer_exports.freeze)(new this(args), true);
6215
- }
6216
- },
6217
- this._statics
6218
- );
6219
- return Class;
6220
- }
6221
- methods(proto) {
6222
- for (const k in proto) this._methods[k] = proto[k];
6223
- }
6224
- statics(proto) {
6225
- for (const k in proto) this._statics[k] = proto[k];
6226
- }
6227
- addField(name, dval, FieldCls) {
6228
- const field = new FieldCls(name, dval);
6229
- this.fields[name] = field;
6230
- return field;
6231
- }
6232
- addCompField(name, type, args) {
6233
- const field = new FieldComp(type, name, args);
6234
- this.compFields.add(name);
6235
- this.fields[name] = field;
6236
- return field;
6237
- }
6238
- };
6239
- var FIELD_CLASS = /* @__PURE__ */ Symbol.for("tutuca.fieldClass");
6240
- var fieldsByTypeName = {
6241
- text: FieldString,
6242
- int: FieldInt,
6243
- float: FieldFloat,
6244
- bool: FieldBool,
6245
- list: FieldList,
6246
- object: FieldObject,
6247
- map: FieldMap,
6248
- set: FieldSet,
6249
- any: FieldAny
6250
- };
6251
- function fieldFromDescriptor(name, value) {
6252
- const FieldCls = fieldsByTypeName[value.type] ?? FieldAny;
6253
- const probe = new FieldCls(name);
6254
- return [FieldCls, probe.coerceOr(value.defaultValue, probe.defaultValue)];
6255
- }
6256
- function classFromData(name, { fields = {}, methods, statics }) {
6257
- const b = new ClassBuilder(name);
6258
- for (const field in fields) {
6259
- const value = fields[field];
6260
- const type = typeof value;
6261
- if (type === "string") b.addField(field, value, FieldString);
6262
- else if (type === "number") b.addField(field, value, FieldFloat);
6263
- else if (type === "boolean") b.addField(field, value, FieldBool);
6264
- else if (Array.isArray(value)) b.addField(field, [...value], FieldList);
6265
- else if (value instanceof Set) b.addField(field, new Set(value), FieldSet);
6266
- else if (value instanceof Map) b.addField(field, new Map(value), FieldMap);
6267
- else if (value?.type && Object.hasOwn(value, "defaultValue")) {
6268
- const [FieldCls, dval] = fieldFromDescriptor(field, value);
6269
- b.addField(field, dval, FieldCls);
6270
- } else if (value?.component && value?.args !== void 0)
6271
- b.addCompField(field, value.component, value.args);
6272
- else if (isPlainObject(value)) b.addField(field, { ...value }, FieldObject);
6273
- else {
6274
- const FieldCls = value?.[FIELD_CLASS] ?? FieldAny;
6275
- b.addField(field, value, FieldCls);
6276
6328
  }
6277
- }
6278
- if (methods) b.methods(methods);
6279
- if (statics) b.statics(statics);
6280
- return b.build();
6329
+ }[name];
6330
+ Class[immer_exports.immerable] = true;
6331
+ Object.assign(Class.prototype, methods);
6332
+ Object.assign(
6333
+ Class,
6334
+ {
6335
+ fields,
6336
+ methods,
6337
+ // The documented way for instance code to reach its class's fields
6338
+ // (`this.constructor.getMetaClass().fields.x.defaultValue`).
6339
+ getMetaClass: () => Class,
6340
+ make(inArgs = {}, opts = {}) {
6341
+ const args = {};
6342
+ const scope = opts.scope ?? this.scope;
6343
+ for (const key in inArgs) {
6344
+ const field = fields[key];
6345
+ if (field === void 0)
6346
+ console.warn("extra argument to constructor:", name, key, inArgs);
6347
+ else if (field instanceof FieldComp) args[key] = mkCompField(field, scope, inArgs[key]);
6348
+ else args[key] = field.coerceOrDefault(inArgs[key]);
6349
+ }
6350
+ for (const key in fields)
6351
+ if (fields[key] instanceof FieldComp && args[key] === void 0)
6352
+ args[key] = mkCompField(fields[key], scope, void 0);
6353
+ return (0, immer_exports.freeze)(new this(args), true);
6354
+ }
6355
+ },
6356
+ statics
6357
+ );
6358
+ return Class;
6281
6359
  }
6282
6360
  function validateDraftFields(current, draft) {
6283
6361
  const meta = metaOf(current);
@@ -6293,14 +6371,11 @@ function validateDraftFields(current, draft) {
6293
6371
  }
6294
6372
  }
6295
6373
  }
6296
- var META_KEYS = "name id fields methods views receive intent alter provide provideType lookup spec extra commonStyle globalStyle scope _rawProvide _rawLookup".split(
6297
- " "
6298
- );
6299
6374
  var RESERVED_COMPONENT_STATICS = /* @__PURE__ */ new Set([
6300
- ...META_KEYS,
6301
- "make",
6302
- "getMetaClass",
6303
- ...Object.getOwnPropertyNames(Component.prototype)
6375
+ ..."name id fields methods views receive intent alter provide provideType lookup spec extra commonStyle globalStyle scope _rawProvide _rawLookup make getMetaClass".split(
6376
+ " "
6377
+ ),
6378
+ ...Object.keys(COMPONENT_METHODS)
6304
6379
  ]);
6305
6380
  function assertNoReservedComponentStatics(statics = {}) {
6306
6381
  for (const name in statics) {
@@ -6309,186 +6384,10 @@ function assertNoReservedComponentStatics(statics = {}) {
6309
6384
  }
6310
6385
  }
6311
6386
  }
6312
- Component.fromSpec = (opts) => {
6387
+ function component(opts) {
6313
6388
  assertNoReservedComponentStatics(opts.statics);
6314
- const Class = classFromData(opts.name, opts);
6315
- const comp = new Component(Class, opts);
6316
- const metaClass = Class.getMetaClass();
6317
- comp.fields = metaClass.fields;
6318
- comp.methods = metaClass.methods;
6319
- Class.getMetaClass = () => comp;
6320
- Class[COMPONENT] = comp;
6321
- for (const key of META_KEYS)
6322
- if (!Object.hasOwn(Class, key))
6323
- Object.defineProperty(Class, key, {
6324
- get() {
6325
- return comp[key];
6326
- },
6327
- set(v) {
6328
- comp[key] = v;
6329
- },
6330
- configurable: true
6331
- });
6332
- for (const key of Object.getOwnPropertyNames(Component.prototype))
6333
- if (key !== "constructor" && !Object.hasOwn(Class, key))
6334
- Class[key] = (...args) => comp[key](...args);
6335
- return Class;
6336
- };
6337
- var component = (opts) => Component.fromSpec(opts);
6338
-
6339
- // src/stack.js
6340
- var STOP = /* @__PURE__ */ Symbol("STOP");
6341
- var NEXT = /* @__PURE__ */ Symbol("NEXT");
6342
- var DEFAULT_ROUTE = ["dyn", "lex"];
6343
- function routeLookup(route, lex, dyn) {
6344
- for (let i = 0; i < route.length; i++) {
6345
- const leg = route[i];
6346
- if (leg === "dyn") {
6347
- const v = dyn();
6348
- if (v != null) return v;
6349
- } else if (leg === "lex") {
6350
- const v = lex();
6351
- if (v != null) return v;
6352
- } else {
6353
- console.warn("unknown lookup route leg", leg, '- expected "dyn" or "lex"');
6354
- }
6355
- }
6356
- return null;
6357
- }
6358
- function lookup(chain, name, dv = null) {
6359
- let n = chain;
6360
- while (n !== null) {
6361
- const r = n[0].lookup(name);
6362
- if (r === STOP) return dv;
6363
- if (r !== NEXT) return r;
6364
- n = n[1];
6365
- }
6366
- return dv;
6367
- }
6368
- var BindFrame = class {
6369
- constructor(it, binds, isFrame) {
6370
- this.it = it;
6371
- this.binds = binds;
6372
- this.isFrame = isFrame;
6373
- }
6374
- lookup(name) {
6375
- const v = this.binds[name];
6376
- return v === void 0 ? this.isFrame ? STOP : NEXT : v;
6377
- }
6378
- };
6379
- var ObjectFrame = class {
6380
- constructor(binds) {
6381
- this.binds = binds;
6382
- }
6383
- lookup(key) {
6384
- const v = this.binds[key];
6385
- return v === void 0 ? NEXT : v;
6386
- }
6387
- };
6388
- function computeViewsId(views) {
6389
- let s = "";
6390
- let n = views;
6391
- while (n !== null) {
6392
- s += n[0];
6393
- n = n[1];
6394
- }
6395
- return s === "main" ? "" : s;
6389
+ return initComponent(classFromData(opts.name ?? "UnkComp", opts), opts);
6396
6390
  }
6397
- var Stack = class _Stack {
6398
- constructor(comps, it, binds, dynBinds, views, viewsId, ctx = null) {
6399
- this.comps = comps;
6400
- this.it = it;
6401
- this.binds = binds;
6402
- this.dynBinds = dynBinds;
6403
- this.views = views;
6404
- this.viewsId = viewsId;
6405
- this.ctx = ctx;
6406
- }
6407
- // Evaluate every provide the entered component publishes and push them as one
6408
- // dynBinds frame, keyed by NAME. Published types go in the same frame: a type name
6409
- // starts A-Z and a value name does not, so the two namespaces cannot collide and
6410
- // nearest-ancestor-wins falls out of frame order for both. No-op with no provides.
6411
- _pushProvides() {
6412
- const comp = this.comps.getCompFor(this.it);
6413
- if (comp == null) return this;
6414
- const { provide, provideType } = comp;
6415
- const dynObj = {};
6416
- let has = false;
6417
- for (const k in provide) {
6418
- dynObj[k] = provide[k].val.eval(this);
6419
- has = true;
6420
- }
6421
- for (const k in provideType) {
6422
- dynObj[k] = provideType[k];
6423
- has = true;
6424
- }
6425
- if (!has) return this;
6426
- const newDynBinds = [new ObjectFrame(dynObj), this.dynBinds];
6427
- const { comps, it, binds, views, viewsId, ctx } = this;
6428
- return new _Stack(comps, it, binds, newDynBinds, views, viewsId, ctx);
6429
- }
6430
- static root(comps, it, ctx) {
6431
- const binds = [new BindFrame(it, {}, true), null];
6432
- const dynBinds = [new ObjectFrame({}), null];
6433
- const views = ["main", null];
6434
- return new _Stack(comps, it, binds, dynBinds, views, "", ctx)._pushProvides();
6435
- }
6436
- enter(it, bindings = {}, isFrame = true) {
6437
- const { comps, binds, dynBinds, views, viewsId, ctx } = this;
6438
- const newBinds = [new BindFrame(it, bindings, isFrame), binds];
6439
- const stack = new _Stack(comps, it, newBinds, dynBinds, views, viewsId, ctx);
6440
- return isFrame ? stack._pushProvides() : stack;
6441
- }
6442
- pushViewName(name) {
6443
- const { comps, it, binds, dynBinds, views, ctx } = this;
6444
- const newViews = [name, views];
6445
- return new _Stack(comps, it, binds, dynBinds, newViews, computeViewsId(newViews), ctx);
6446
- }
6447
- // Published types are stable per scope and would only churn the render cache, so
6448
- // the cache key covers values alone.
6449
- _pushDynBindValuesToArray(arr, comp) {
6450
- for (const k in comp.provide) arr.push(this.lookupDynamic(k));
6451
- for (const k in comp.lookup) arr.push(this.lookupDynamic(k));
6452
- }
6453
- // `*name`: the nearest binding above (including this component's own provides,
6454
- // pushed on entering it), else this component's declared default, else null.
6455
- lookupDynamic(name) {
6456
- const v = lookup(this.dynBinds, name);
6457
- if (v != null) return v;
6458
- const comp = this.comps.getCompFor(this.it);
6459
- return comp?.lookup[name]?.val?.eval(this) ?? null;
6460
- }
6461
- lookupBind(name) {
6462
- return lookup(this.binds, name);
6463
- }
6464
- lookupFieldRaw(name) {
6465
- return this.it[name] ?? null;
6466
- }
6467
- lookupMethod(name) {
6468
- const fn = this.it[name];
6469
- return fn instanceof Function ? fn.call(this.it) : null;
6470
- }
6471
- // The dispatched DOM event / drag info, read only by EventMemberVal's
6472
- // `e.<member>` handler args. Null outside a live event transaction.
6473
- lookupEvent() {
6474
- return this.ctx?.event ?? null;
6475
- }
6476
- lookupDragInfo() {
6477
- return this.ctx?.dragInfo ?? null;
6478
- }
6479
- getHandlerFor(name, key) {
6480
- return this.comps.getHandlerFor(this.it, name, key);
6481
- }
6482
- lookupBestView(views, defaultViewName) {
6483
- let n = this.views;
6484
- while (n !== null) {
6485
- const view = views[n[0]];
6486
- if (view !== void 0) return view;
6487
- n = n[1];
6488
- }
6489
- return views[defaultViewName];
6490
- }
6491
- };
6492
6391
 
6493
6392
  // src/transactor.js
6494
6393
  var State2 = class {
@@ -6529,14 +6428,7 @@ var Transactor = class {
6529
6428
  // carry: kind, name, args, path, pathKeys, targetPath, handler, handlerName,
6530
6429
  // matched, before, after, parent, timestamp. Purely observational.
6531
6430
  observe(cb) {
6532
- this._observers.push(cb);
6533
- return () => {
6534
- const i = this._observers.indexOf(cb);
6535
- if (i !== -1) this._observers.splice(i, 1);
6536
- };
6537
- }
6538
- _emit(record) {
6539
- for (const cb of this._observers) cb(record);
6431
+ return subscribe(this._observers, cb);
6540
6432
  }
6541
6433
  // Record a refusal: a dispatch the runtime could not carry out. Kinds are
6542
6434
  // open strings; today the runtime raises NO_HANDLER (a receive name with no
@@ -6555,20 +6447,16 @@ var Transactor = class {
6555
6447
  // Subscribe to refusal records as they happen. Returns an unsubscribe fn,
6556
6448
  // like observe().
6557
6449
  observeRefusals(cb) {
6558
- this._refusalObservers.push(cb);
6559
- return () => {
6560
- const i = this._refusalObservers.indexOf(cb);
6561
- if (i !== -1) this._refusalObservers.splice(i, 1);
6562
- };
6450
+ return subscribe(this._refusalObservers, cb);
6563
6451
  }
6564
6452
  // Build and dispatch one observer record. Pins field-resolved keys (e.g. a
6565
6453
  // `.a[.selId]` render target reconstructed from a DOM event) against the root the
6566
6454
  // handler read, so pathKeys carries concrete keys instead of dropping the dynamic
6567
6455
  // step. No-op when nobody is observing.
6568
- _emitRecord(root, { kind, name, args, path, targetPath, handler, handlerName, matched, before, after, parent }) {
6456
+ _emitRecord(root, { kind, name, args, path: path2, targetPath, handler, handlerName, matched, before, after, parent }) {
6569
6457
  if (this._observers.length === 0) return;
6570
- const pinned = path.pinKeys(root);
6571
- this._emit({
6458
+ const pinned = path2.pinKeys(root);
6459
+ const record = {
6572
6460
  kind,
6573
6461
  name,
6574
6462
  args: args ?? null,
@@ -6582,14 +6470,14 @@ var Transactor = class {
6582
6470
  after,
6583
6471
  parent,
6584
6472
  timestamp: Date.now()
6585
- });
6473
+ };
6474
+ for (const cb of this._observers) cb(record);
6586
6475
  }
6587
6476
  // The observer record for a settled transaction. The resolved handler
6588
6477
  // (`_resolvedHandler`/`_matched`) and per-leaf before/after (`_before`/`_after`)
6589
6478
  // were captured while the handler ran (see callHandler / Transaction.run).
6590
6479
  _emitTransaction(transaction, root) {
6591
6480
  if (this._observers.length === 0) return;
6592
- if (transaction._resolvedHandler === void 0) return;
6593
6481
  this._emitRecord(root, {
6594
6482
  kind: transaction.observeKind,
6595
6483
  name: transaction.observeName,
@@ -6603,33 +6491,29 @@ var Transactor = class {
6603
6491
  parent: transaction.parentTransaction
6604
6492
  });
6605
6493
  }
6606
- // Make `child` a tracked unit of `parent`'s subtree: the parent's completion stays open
6607
- // until the child's *whole* subtree settles. Tracking happens at dispatch time — during
6608
- // the parent's handler or afterTransaction — while the parent's self-unit is still held,
6609
- // so the parent counter can't reach zero before the child is registered. Returns `child`.
6610
- _link(child, parent) {
6611
- if (parent) {
6612
- const release = parent.completion.track();
6613
- child.completion.whenSubtreeSettled().then(release);
6614
- }
6615
- return child;
6616
- }
6617
6494
  // `origin` is where the message came FROM, pinned now for the same reason an
6618
6495
  // intent's answerPath is: a reply must reach the sender that asked even if a key
6619
6496
  // moved while the message was in flight. Null when nobody is waiting — a host
6620
6497
  // sendAtRoot or a view's own `@on.*` — and ctx.sendReply refuses on that.
6621
- pushSend(path, name, args = [], parent = null, origin = null) {
6622
- const t = new SendEvent(path, this, name, args, parent);
6623
- t.origin = origin === null ? null : origin.toTransactionPath().pinKeys(this.state.val);
6498
+ //
6499
+ // The parent's subtree stays open until this message's whole subtree settles. The
6500
+ // unit is taken now, at dispatch — during the parent's handler or afterTransaction,
6501
+ // while the parent's self-unit is still held — so its counter can't reach zero first.
6502
+ pushSend(path2, name, args = [], parent = null, origin = null, txnPath = null) {
6503
+ const t = new SendEvent(path2, this, name, args, parent);
6504
+ t.txnPath = txnPath;
6505
+ t.origin = origin;
6506
+ t.originPinned = origin === null ? null : origin.toTransactionPath().pinKeys(this.state.val);
6624
6507
  this.pushTransaction(t);
6625
- return this._link(t, parent);
6508
+ if (parent) t.completion.carry(parent.completion.track());
6509
+ return t;
6626
6510
  }
6627
6511
  // Raise an intent: a job the sender did not address, walked along a route until
6628
6512
  // something answers. `opts.route` is a list of legs (see DEFAULT_ROUTE) and
6629
6513
  // `opts.livePath` opts out of pinning the answer's destination keys.
6630
- pushIntent(path, name, args = [], opts = {}, parent = null) {
6514
+ pushIntent(path2, name, args = [], opts = {}, parent = null) {
6631
6515
  const release = parent ? parent.completion.track() : null;
6632
- const walk = new IntentWalk(this, path, name, args, opts, parent, release);
6516
+ const walk = new IntentWalk(this, path2, name, args, opts, parent, release);
6633
6517
  walk.advance();
6634
6518
  return walk;
6635
6519
  }
@@ -6646,7 +6530,7 @@ var Transactor = class {
6646
6530
  return this.transactions.length > 0;
6647
6531
  }
6648
6532
  transactNext() {
6649
- if (this.hasPendingTransactions) this.transact(this.transactions.shift());
6533
+ this.transact(this.transactions.shift());
6650
6534
  }
6651
6535
  transact(transaction) {
6652
6536
  try {
@@ -6659,20 +6543,31 @@ var Transactor = class {
6659
6543
  } else console.warn("undefined new state", { curState, transaction });
6660
6544
  } finally {
6661
6545
  transaction.ensureWalkAdvanced?.();
6662
- transaction._completion?.ensureSelfSettled();
6663
- transaction._completion?.releaseSelf();
6546
+ transaction._completion?.finish();
6664
6547
  }
6665
6548
  }
6666
- transactInputNow(path, event, eventHandler, dragInfo) {
6667
- this.transact(new InputEvent(path, event, eventHandler, this, dragInfo));
6549
+ transactInputNow(path2, event, eventHandler, dragInfo) {
6550
+ this.transact(new InputEvent(path2, event, eventHandler, this, dragInfo));
6668
6551
  }
6669
6552
  };
6670
6553
  function nullHandler() {
6671
6554
  return this;
6672
6555
  }
6556
+ function subscribe(list, cb) {
6557
+ list.push(cb);
6558
+ return () => {
6559
+ const i = list.indexOf(cb);
6560
+ if (i !== -1) list.splice(i, 1);
6561
+ };
6562
+ }
6563
+ function warnUnknownLeg(what, leg) {
6564
+ console.warn(`unknown ${what} route leg`, leg, '- expected "dyn" or "lex"');
6565
+ return null;
6566
+ }
6673
6567
  var Transaction = class {
6674
- constructor(path, transactor, parentTransaction = null) {
6675
- this.path = path;
6568
+ constructor(path2, transactor, parentTransaction = null) {
6569
+ this.path = path2;
6570
+ this.txnPath = null;
6676
6571
  this.transactor = transactor;
6677
6572
  this.parentTransaction = parentTransaction;
6678
6573
  this._completion = null;
@@ -6692,39 +6587,41 @@ var Transaction = class {
6692
6587
  whenSubtreeSettled() {
6693
6588
  return this.completion.whenSubtreeSettled();
6694
6589
  }
6695
- afterTransaction() {
6696
- }
6697
- // Ending a walk and forwarding belong to the two dispatch buckets; the base warns
6698
- // rather than throwing, so a stray call from the wrong place is a message and not a
6699
- // crash. `walk` is undefined here, which is how ctx.reply/ctx.fail tell the two apart.
6700
- stop() {
6701
- warnNotIntent("stop");
6702
- }
6703
- forward(_opts) {
6704
- console.warn('ctx.forward() needs a "receive" or "intent" handler - ignored');
6705
- }
6706
- // The kind reported to observers (see Transactor.observe); null on the base.
6707
- get observeKind() {
6708
- return null;
6709
- }
6710
- // The name reported to observers; null on the base. Overridden by NameArgs (the
6711
- // dispatched message name) and InputEvent (the DOM event type). Kept separate from
6712
- // `name`/`ctx.name` so it can't change handler-visible behavior.
6713
- get observeName() {
6714
- return null;
6715
- }
6590
+ // Every transaction kind defines `observeKind` / `observeName` (what observers see;
6591
+ // kept apart from `name`/`ctx.name` so they can't change handler-visible behavior),
6592
+ // `getHandlerAndArgs(root, instance, comps)`, and the three `_forward*` hooks below.
6716
6593
  callHandler(root, instance, draft, comps) {
6717
6594
  const [handler, args] = this.getHandlerAndArgs(root, instance, comps);
6718
6595
  this._resolvedHandler = handler;
6719
6596
  return handler.apply(instance, [draft, ...args]);
6720
6597
  }
6721
- getHandlerAndArgs(_root, _instance, _comps) {
6722
- return null;
6598
+ // `forward` is where a name can LEAVE the component: the message being handled is
6599
+ // raised again as an INTENT under the same name (`_forwardName()`) with the same
6600
+ // arguments unless `opts.args` says otherwise, from the position `_forwardPath()`
6601
+ // names. Deferred to afterTransaction so the state the handler produced is in place
6602
+ // before the walk starts. (`IntentEvent` overrides both: in an intent body, forward
6603
+ // amends the walk that is already running.)
6604
+ forward(opts) {
6605
+ this._forward = opts ?? {};
6606
+ }
6607
+ afterTransaction() {
6608
+ const f = this._forward;
6609
+ if (f === void 0) return;
6610
+ this._forward = void 0;
6611
+ const name = this._forwardName();
6612
+ if (name === void 0) {
6613
+ this.transactor.refuse("FORWARD_NO_NAME", {});
6614
+ return;
6615
+ }
6616
+ const { args = this._forwardArgs(), ...rest } = f;
6617
+ this.transactor.pushIntent(this._forwardPath(), name, args, rest, this);
6723
6618
  }
6724
- // The path used to apply the mutation. Teleports dynamic-var renders so it lands on
6725
- // the data's real location (the dispatch `this.path` keeps intermediates).
6619
+ // The path used to apply the mutation: the ACTIVE frame of the dispatch path, so
6620
+ // an event inside a `<x render="*name">` subtree updates the value where it
6621
+ // really lives (the dispatch `this.path` keeps the visual callers, for bubbling).
6622
+ // `txnPath`, when pinned by the sender, wins.
6726
6623
  getTransactionPath() {
6727
- return this.path.toTransactionPath().compact();
6624
+ return this.txnPath ?? this.path.toTransactionPath();
6728
6625
  }
6729
6626
  run(curRoot, comps) {
6730
6627
  const txnPath = this.getTransactionPath();
@@ -6741,19 +6638,27 @@ var Transaction = class {
6741
6638
  }
6742
6639
  };
6743
6640
  var InputEvent = class extends Transaction {
6744
- constructor(path, e, handler, transactor, dragInfo) {
6745
- super(path, transactor);
6641
+ constructor(path2, e, handler, transactor, dragInfo) {
6642
+ super(path2, transactor);
6746
6643
  this.e = e;
6747
6644
  this.handler = handler;
6748
6645
  this.dragInfo = dragInfo;
6749
6646
  this._dispatchPath = null;
6750
6647
  }
6751
- // Frame steps removed, DynStep + one step per crossed component kept: bubbling
6752
- // it visits every component (including intermediates of a dynamic-var render).
6648
+ // Frame-only steps removed, one step per crossed component kept — inside every
6649
+ // continuation frame independently, so bubbling it visits every component and
6650
+ // then returns to the caller that wrote the `*name`.
6753
6651
  get dispatchPath() {
6754
6652
  this._dispatchPath ??= this.path.compact();
6755
6653
  return this._dispatchPath;
6756
6654
  }
6655
+ // Only a DOM event's path carries frame-only bind steps (they replay the handler's
6656
+ // arguments, but address no state); the compacted path is where the mutation lands,
6657
+ // so a handler inside @each still updates the component that owns the view. Every
6658
+ // other transaction is dispatched at an already-compacted position.
6659
+ getTransactionPath() {
6660
+ return this.dispatchPath.toTransactionPath();
6661
+ }
6757
6662
  // A DOM event is an ADDRESSED message like any other — it reaches the component that
6758
6663
  // owns the view and stops. It keeps its own class only because it resolves its handler
6759
6664
  // from the compiled view and runs synchronously, not because it is a different channel.
@@ -6763,47 +6668,36 @@ var InputEvent = class extends Transaction {
6763
6668
  get observeName() {
6764
6669
  return this.e?.type ?? null;
6765
6670
  }
6766
- // A view's name is a message. `forward` is where that name can LEAVE the component:
6767
- // it becomes an intent with the same name and the same arguments, so a view that says
6768
- // `@on.click="saveDraft .text"` never has to change when an ancestor takes the job over.
6769
- // (A `$method` handler has a name too, so forwarding one raises an intent under it.)
6770
- forward(opts) {
6771
- this._forward = opts ?? {};
6772
- }
6773
- afterTransaction() {
6774
- const f = this._forward;
6775
- if (f === void 0) return;
6776
- this._forward = void 0;
6777
- const { args = this._handlerArgs ?? [], ...rest } = f;
6671
+ // A view's name is a message, and forwarding it means a view that says
6672
+ // `@on.click="saveDraft .text"` never has to change when an ancestor takes the job
6673
+ // over. (A `$method` handler has a name too, so forwarding one raises an intent under
6674
+ // it.) `handler` is a NodeEvent, whose own `name` is the DOM event type; the MESSAGE
6675
+ // name is the one the view wrote, on the handler call it wraps.
6676
+ _forwardName() {
6778
6677
  const hv = this.handler?.handlerCall?.handlerVal ?? this.handler?.handlerVal;
6779
- const name = hv?.name;
6780
- if (name === void 0) {
6781
- this.transactor.refuse("FORWARD_NO_NAME", {});
6782
- return;
6783
- }
6784
- this.transactor.pushIntent(this.dispatchPath, name, args, rest, this);
6678
+ return hv?.name;
6679
+ }
6680
+ _forwardArgs() {
6681
+ return this._handlerArgs ?? [];
6682
+ }
6683
+ _forwardPath() {
6684
+ return this.dispatchPath;
6785
6685
  }
6786
6686
  getHandlerAndArgs(root, _instance, comps) {
6787
- const stack = this.path.toTransactionPath().buildStack(Stack.root(comps, root, this));
6687
+ const stack = this.path.buildStack(Stack.root(comps, root, this));
6788
6688
  const [handler, args] = this.handler.getHandlerAndArgs(stack, this);
6789
6689
  this._handlerArgs = [...args];
6790
- const path = this.dispatchPath;
6791
- args.push(new EventContext(path, this.transactor, this));
6690
+ const path2 = this.dispatchPath;
6691
+ args.push(new EventContext(path2, this.transactor, this));
6792
6692
  return [handler, args];
6793
6693
  }
6794
- // The dispatched DOM event, read only through `e.<member>` handler args
6795
- // (src/value.js EventMemberVal via Stack.lookupEvent). There is no bare
6796
- // implicit-name vocabulary anymore: every arg carries a sigil.
6797
- get event() {
6798
- return this.e;
6799
- }
6800
6694
  };
6801
6695
  var NameArgsTransaction = class extends Transaction {
6802
- constructor(path, transactor, name, args, parentTransaction) {
6803
- super(path, transactor, parentTransaction);
6696
+ constructor(path2, transactor, name, args, parentTransaction) {
6697
+ super(path2, transactor, parentTransaction);
6804
6698
  this.name = name;
6805
6699
  this.args = args;
6806
- this.targetPath = path;
6700
+ this.targetPath = path2;
6807
6701
  }
6808
6702
  handlerProp = null;
6809
6703
  // NameArgs verbs map their handler bucket straight to the observed kind:
@@ -6814,6 +6708,17 @@ var NameArgsTransaction = class extends Transaction {
6814
6708
  get observeName() {
6815
6709
  return this.name;
6816
6710
  }
6711
+ // `forward` in a RECEIVE body starts a walk: the message that arrived becomes an
6712
+ // intent, keeping its name and payload.
6713
+ _forwardName() {
6714
+ return this.name;
6715
+ }
6716
+ _forwardArgs() {
6717
+ return this.args;
6718
+ }
6719
+ _forwardPath() {
6720
+ return this.path;
6721
+ }
6817
6722
  getHandlerForName(comp) {
6818
6723
  const handlers = comp?.[this.handlerProp];
6819
6724
  const exact = handlers?.[this.name];
@@ -6844,24 +6749,11 @@ var SendEvent = class extends NameArgsTransaction {
6844
6749
  get observeKind() {
6845
6750
  return this._isAnswer ? "answer" : "receive";
6846
6751
  }
6847
- // `forward` in a RECEIVE body starts a walk: the message that arrived becomes an
6848
- // intent, keeping its name and payload. This is what lets a view's name leave the
6849
- // component without the view changing.
6850
- forward(opts) {
6851
- this._forward = opts ?? {};
6852
- }
6853
- afterTransaction() {
6854
- const f = this._forward;
6855
- if (f === void 0) return;
6856
- this._forward = void 0;
6857
- const { args = this.args, ...rest } = f;
6858
- this.transactor.pushIntent(this.path, this.name, args, rest, this);
6859
- }
6860
6752
  };
6861
6753
  var IntentEvent = class extends NameArgsTransaction {
6862
6754
  handlerProp = "intent";
6863
- constructor(path, transactor, walk) {
6864
- super(path, transactor, walk.name, walk.args, walk.parent);
6755
+ constructor(path2, transactor, walk) {
6756
+ super(path2, transactor, walk.name, walk.args, walk.parent);
6865
6757
  this.walk = walk;
6866
6758
  this.targetPath = walk.origin;
6867
6759
  }
@@ -6870,9 +6762,6 @@ var IntentEvent = class extends NameArgsTransaction {
6870
6762
  forward(opts) {
6871
6763
  this.walk.amend(opts, this.path);
6872
6764
  }
6873
- stop() {
6874
- this.walk.finish(null, null);
6875
- }
6876
6765
  afterTransaction() {
6877
6766
  this.ensureWalkAdvanced();
6878
6767
  }
@@ -6888,17 +6777,17 @@ var PASS = /* @__PURE__ */ Symbol.for("tutuca.intent.pass");
6888
6777
  var REFUSAL_RING_CAP = 200;
6889
6778
  var INTENT_DEPTH = 64;
6890
6779
  var IntentWalk = class {
6891
- constructor(transactor, path, name, args, opts, parent, release) {
6780
+ constructor(transactor, path2, name, args, opts, parent, release) {
6892
6781
  this.transactor = transactor;
6893
6782
  this.name = name;
6894
6783
  this.args = args;
6895
6784
  this.route = opts?.route ?? DEFAULT_ROUTE;
6896
6785
  this.parent = parent;
6897
6786
  this.release = release;
6898
- this.origin = path;
6899
- this.answerPath = opts?.livePath ? null : path.toTransactionPath().pinKeys(transactor.state.val);
6787
+ this.origin = path2;
6788
+ this.answerPath = opts?.livePath ? null : path2.toTransactionPath().pinKeys(transactor.state.val);
6900
6789
  this.legIndex = 0;
6901
- this.dynAt = path;
6790
+ this.dynAt = path2;
6902
6791
  this.hops = 0;
6903
6792
  this.ended = false;
6904
6793
  }
@@ -6910,7 +6799,7 @@ var IntentWalk = class {
6910
6799
  while (this.legIndex < this.route.length) {
6911
6800
  const leg = this.route[this.legIndex];
6912
6801
  if (leg === "dyn") {
6913
- if (this.dynAt.steps.length === 0) {
6802
+ if (!this.dynAt.canPop()) {
6914
6803
  this.legIndex++;
6915
6804
  continue;
6916
6805
  }
@@ -6922,7 +6811,7 @@ var IntentWalk = class {
6922
6811
  this.legIndex++;
6923
6812
  return this._tryLex();
6924
6813
  }
6925
- console.warn("unknown intent route leg", leg, '- expected "dyn" or "lex"');
6814
+ warnUnknownLeg("intent", leg);
6926
6815
  this.legIndex++;
6927
6816
  }
6928
6817
  this.exhaust("noHandler");
@@ -7007,11 +6896,11 @@ var IntentWalk = class {
7007
6896
  if (this.ended) return;
7008
6897
  this.ended = true;
7009
6898
  if (name === null) return this.release?.();
7010
- const path = this.answerPath ?? this.origin;
7011
- const t = new SendEvent(path, this.transactor, name, args, this.parent);
6899
+ const t = new SendEvent(this.origin, this.transactor, name, args, this.parent);
6900
+ t.txnPath = this.answerPath;
7012
6901
  t._isAnswer = true;
7013
6902
  this.transactor.pushTransaction(t);
7014
- if (this.release) t.completion.whenSubtreeSettled().then(this.release);
6903
+ if (this.release) t.completion.carry(this.release);
7015
6904
  }
7016
6905
  };
7017
6906
  var Completion = class {
@@ -7019,12 +6908,12 @@ var Completion = class {
7019
6908
  this.val = void 0;
7020
6909
  this.selfSettled = false;
7021
6910
  this.subtreeSettled = false;
7022
- this.pending = 1;
6911
+ this.pending = 0;
7023
6912
  this._selfResolve = null;
7024
6913
  this._selfPromise = null;
7025
6914
  this._subtreeResolve = null;
7026
6915
  this._subtreePromise = null;
7027
- this._selfReleased = false;
6916
+ this._releaseSelf = this.track();
7028
6917
  }
7029
6918
  whenSettled() {
7030
6919
  if (this.selfSettled) return Promise.resolve(this.val);
@@ -7047,38 +6936,38 @@ var Completion = class {
7047
6936
  this.val = val;
7048
6937
  this._selfResolve?.(val);
7049
6938
  }
7050
- // Settle self even when no handler produced a value (undefined-state / throw paths).
7051
- ensureSelfSettled() {
7052
- if (!this.selfSettled) this.markSelfSettled(this.val);
6939
+ // The transaction is done processing: self is settled (with the value the handler
6940
+ // produced, or none on the undefined-state / throw paths) and the self-unit goes.
6941
+ finish() {
6942
+ this.markSelfSettled(this.val);
6943
+ this._releaseSelf();
7053
6944
  }
7054
- // Register an outstanding unit; returns a one-shot release.
6945
+ // Register an outstanding unit; returns its one-shot release. The subtree settles
6946
+ // when the last unit is released.
7055
6947
  track() {
7056
6948
  this.pending++;
7057
6949
  let done = false;
7058
6950
  return () => {
7059
6951
  if (done) return;
7060
6952
  done = true;
7061
- this._release();
6953
+ if (--this.pending === 0) {
6954
+ this.subtreeSettled = true;
6955
+ this._subtreeResolve?.(this.val);
6956
+ }
7062
6957
  };
7063
6958
  }
7064
- releaseSelf() {
7065
- if (this._selfReleased) return;
7066
- this._selfReleased = true;
7067
- this._release();
7068
- }
7069
- _release() {
7070
- if (--this.pending === 0) {
7071
- this.subtreeSettled = true;
7072
- this._subtreeResolve?.(this.val);
7073
- }
6959
+ // Hand a unit held elsewhere to this subtree: `release` runs once this transaction
6960
+ // and everything it spawned have settled.
6961
+ carry(release) {
6962
+ this.whenSubtreeSettled().then(release);
7074
6963
  }
7075
6964
  };
7076
6965
  var Dispatcher = class {
7077
- constructor(path, transactor, parentTransaction, root = transactor.state.val) {
7078
- this.path = path;
6966
+ constructor(path2, transactor, parentTransaction) {
6967
+ this.path = path2;
7079
6968
  this.transactor = transactor;
7080
6969
  this.parent = parentTransaction;
7081
- this.root = root;
6970
+ this.root = transactor.state.val;
7082
6971
  }
7083
6972
  // Walk the component instances on this ctx's path, leaf→root, calling
7084
6973
  // callback(Component, instance). Return false from the callback to stop early.
@@ -7103,28 +6992,37 @@ var Dispatcher = class {
7103
6992
  // replayed; provides are pushed on every component frame either way, so a provide
7104
6993
  // that reads a loop binding is the one case this cannot reproduce faithfully.
7105
6994
  _stack() {
7106
- this._stackMemo ??= this.path.toTransactionPath().buildStack(Stack.root(this.transactor.comps, this.root, this.parent));
6995
+ this._stackMemo ??= this.path.buildStack(
6996
+ Stack.root(this.transactor.comps, this.root, this.parent)
6997
+ );
7107
6998
  return this._stackMemo;
7108
6999
  }
7109
7000
  // Resolve a name the way the renderer would. `opts.route` takes the same legs in
7110
7001
  // the same spelling as `ctx.intent` — `["dyn"]` the render ancestry, `["lex"]` the
7111
7002
  // registration scope, default both.
7003
+ // Legs are tried in route order, like the intent walker's; an unknown leg warns and
7004
+ // is skipped, and an empty route resolves to null.
7112
7005
  lookup(name, opts) {
7113
- return routeLookup(
7114
- opts?.route ?? DEFAULT_ROUTE,
7115
- () => this._lookupLex(name),
7116
- () => this._stack()?.lookupDynamic(name) ?? null
7117
- );
7006
+ for (const leg of opts?.route ?? DEFAULT_ROUTE) {
7007
+ const v = leg === "dyn" ? this._stack()?.lookupDynamic(name) ?? null : leg === "lex" ? this._lookupLex(name) : warnUnknownLeg("lookup", leg);
7008
+ if (v != null) return v;
7009
+ }
7010
+ return null;
7118
7011
  }
7119
7012
  // The `lex` leg without a Stack: the scope of the component whose handler is
7120
- // running, which is the leaf of this ctx's path.
7013
+ // running, which is the leaf of this ctx's path. A type name resolves to the
7014
+ // component registered under it; a value name to a path registered under it
7015
+ // (see ComponentStack.registerPaths), read against the current root.
7121
7016
  _lookupLex(name) {
7122
7017
  let Comp = null;
7123
7018
  this.walkPath((c) => {
7124
7019
  Comp = c;
7125
7020
  return false;
7126
7021
  });
7127
- return Comp?.scope?.lookupComponent(name) ?? null;
7022
+ const scope = Comp?.scope;
7023
+ if (scope == null) return null;
7024
+ if (isTypeName(name)) return scope.lookupComponent(name) ?? null;
7025
+ return scope.lookupPath(name)?.lookup(this.root) ?? null;
7128
7026
  }
7129
7027
  // A component lookup is a value lookup constrained to a component. The `lex` leg
7130
7028
  // can only ever answer with one; the `dyn` leg reads a binding an ancestor
@@ -7146,8 +7044,8 @@ var Dispatcher = class {
7146
7044
  send(name, args) {
7147
7045
  return this.sendAtPath(this.path, name, args);
7148
7046
  }
7149
- sendAtPath(path, name, args) {
7150
- return this.transactor.pushSend(path, name, args, this.parent, this.path);
7047
+ sendAtPath(path2, name, args) {
7048
+ return this.transactor.pushSend(path2, name, args, this.parent, this.path);
7151
7049
  }
7152
7050
  // An intent is ROUTED: it names a job and walks until something answers. The verb
7153
7051
  // does not decide which scope answers — `opts.route` does, written here at the call
@@ -7155,8 +7053,8 @@ var Dispatcher = class {
7155
7053
  intent(name, args, opts) {
7156
7054
  return this.intentAtPath(this.path, name, args, opts);
7157
7055
  }
7158
- intentAtPath(path, name, args, opts) {
7159
- return this.transactor.pushIntent(path, name, args, opts, this.parent);
7056
+ intentAtPath(path2, name, args, opts) {
7057
+ return this.transactor.pushIntent(path2, name, args, opts, this.parent);
7160
7058
  }
7161
7059
  };
7162
7060
  var EventContext = class extends Dispatcher {
@@ -7185,11 +7083,19 @@ var EventContext = class extends Dispatcher {
7185
7083
  this.transactor.refuse("NO_SENDER", { name });
7186
7084
  return null;
7187
7085
  }
7188
- return this.sendAtPath(origin, name, args);
7086
+ return this.transactor.pushSend(
7087
+ origin,
7088
+ name,
7089
+ args,
7090
+ this.parent,
7091
+ this.path,
7092
+ this.parent.originPinned
7093
+ );
7189
7094
  }
7190
- // End the walk answering nothing — "served, and no answer".
7095
+ // End the walk answering nothing — "served, and no answer". Like reply/fail,
7096
+ // legal only in an `intent` handler.
7191
7097
  stop() {
7192
- return this.parent.stop();
7098
+ return this.parent.walk === void 0 ? warnNotIntent("stop") : this.parent.walk.finish(null, null);
7193
7099
  }
7194
7100
  // From an `intent` body: hand the intent to the next hop, optionally amending it.
7195
7101
  // From a `receive` body: turn the message that arrived into an intent. One word from
@@ -7217,13 +7123,13 @@ var PathChanges = class extends PathBuilder {
7217
7123
  }
7218
7124
  };
7219
7125
  function rootDispatcher(transactor) {
7220
- return new Dispatcher(new Path([]), transactor, null);
7126
+ return new Dispatcher(new DispatchPath(), transactor, null);
7221
7127
  }
7222
7128
 
7223
7129
  // tools/core/results.js
7224
7130
  var ModuleInfo = class {
7225
- constructor({ path = null, present = /* @__PURE__ */ new Set(), counts = {}, warnings = [] }) {
7226
- this.path = path;
7131
+ constructor({ path: path2 = null, present = /* @__PURE__ */ new Set(), counts = {}, warnings = [] }) {
7132
+ this.path = path2;
7227
7133
  this.present = present;
7228
7134
  this.counts = counts;
7229
7135
  this.warnings = warnings;
@@ -7333,8 +7239,8 @@ var DescribeResult = class {
7333
7239
  }
7334
7240
  };
7335
7241
  var ModuleTestReport = class {
7336
- constructor({ path = null, suites = [], counts = { pass: 0, fail: 0, skip: 0, total: 0 } }) {
7337
- this.path = path;
7242
+ constructor({ path: path2 = null, suites = [], counts = { pass: 0, fail: 0, skip: 0, total: 0 } }) {
7243
+ this.path = path2;
7338
7244
  this.suites = suites;
7339
7245
  this.counts = counts;
7340
7246
  }
@@ -7377,8 +7283,8 @@ var Test = class {
7377
7283
  }
7378
7284
  };
7379
7285
  var ModuleTests = class {
7380
- constructor({ path = null, suites = [] } = {}) {
7381
- this.path = path;
7286
+ constructor({ path: path2 = null, suites = [] } = {}) {
7287
+ this.path = path2;
7382
7288
  this.suites = suites;
7383
7289
  }
7384
7290
  };
@@ -7400,8 +7306,8 @@ function titleFromArg(arg) {
7400
7306
  if (typeof arg === "function") return arg.name || "(anonymous)";
7401
7307
  return String(arg);
7402
7308
  }
7403
- function makeCollector({ path = null, components = [] } = {}) {
7404
- const moduleTests = new ModuleTests({ path, suites: [] });
7309
+ function makeCollector({ path: path2 = null, components = [] } = {}) {
7310
+ const moduleTests = new ModuleTests({ path: path2, suites: [] });
7405
7311
  const stack = [];
7406
7312
  function describe(...args) {
7407
7313
  let head;
@@ -7502,7 +7408,7 @@ async function driveStack(stack, value, phase, opts = {}) {
7502
7408
  val
7503
7409
  );
7504
7410
  });
7505
- dispatchPhase(rootDispatcher(transactor), new Path([]), phase, value);
7411
+ dispatchPhase(rootDispatcher(transactor), new DispatchPath(), phase, value);
7506
7412
  await transactor.settle();
7507
7413
  return transactor.state.val;
7508
7414
  }
@@ -7512,7 +7418,7 @@ async function drive(cfg, value, phase, opts = {}) {
7512
7418
  async function runTests({
7513
7419
  getTests,
7514
7420
  components = [],
7515
- path = null,
7421
+ path: path2 = null,
7516
7422
  expect: expect3,
7517
7423
  name = null,
7518
7424
  grep = null,
@@ -7523,13 +7429,13 @@ async function runTests({
7523
7429
  const counts = { pass: 0, fail: 0, skip: 0, total: 0 };
7524
7430
  if (typeof getTests !== "function") {
7525
7431
  return new TestReport({
7526
- modules: [new ModuleTestReport({ path, suites: [], counts })]
7432
+ modules: [new ModuleTestReport({ path: path2, suites: [], counts })]
7527
7433
  });
7528
7434
  }
7529
7435
  if (typeof expect3 !== "function") {
7530
7436
  throw new Error("runTests: expect must be provided (e.g. chai's expect)");
7531
7437
  }
7532
- const { describe, test: test2, moduleTests } = makeCollector({ path, components });
7438
+ const { describe, test: test2, moduleTests } = makeCollector({ path: path2, components });
7533
7439
  let _stack = null;
7534
7440
  const getStack = () => _stack ??= buildStack({ components, macros, intentHandlers });
7535
7441
  const driveLocal = (value, phase, opts = {}) => driveStack(getStack(), value, phase, opts);
@@ -7592,7 +7498,7 @@ async function runTests({
7592
7498
  if (r !== null) suiteResults.push(r);
7593
7499
  }
7594
7500
  return new TestReport({
7595
- modules: [new ModuleTestReport({ path, suites: suiteResults, counts })]
7501
+ modules: [new ModuleTestReport({ path: path2, suites: suiteResults, counts })]
7596
7502
  });
7597
7503
  }
7598
7504
 
@@ -7761,19 +7667,19 @@ function lintIdToMessage(id, info) {
7761
7667
  case "DYN_ALIAS_NOT_REFERENCED":
7762
7668
  return `Lookup '${info.name}' is defined but never used — remove it or reference it as '*${info.name}' in a view`;
7763
7669
  case "PROVIDE_NOT_ADDRESSABLE":
7764
- return `Provide '${info.name}' value '${info.value}' must be a field ('.f') or seq-access ('.s[.k]') — a method/constant can't be a render target`;
7670
+ return `Provide '${info.name}' value '${info.value}' must be a field ('.f') or seq-access ('.s[.k]') — a provide doubles as the path '<x render="*${info.name}">' resumes at, so this one is dropped and '*${info.name}' resolves to nothing`;
7765
7671
  case "LOOKUP_BAD_SHAPE":
7766
7672
  return `Lookup '${info.name}' has an invalid shape: ${info.problem}`;
7767
7673
  case "LOOKUP_NO_PROVIDER":
7768
- return info.hasDefault ? `Lookup '${info.name}' is provided by no component in scope, so it always resolves to its default` : `Lookup '${info.name}' is provided by no component in scope — add a 'provide' for it, or give this lookup a default`;
7674
+ return info.hasDefault ? `Lookup '${info.name}' is provided by no component in scope and matches no registered path, so it always resolves to its default` : `Lookup '${info.name}' is provided by no component in scope and matches no registered path — add a 'provide' for it, register a path under that name, or give this lookup a default`;
7769
7675
  case "PROVIDE_TYPE_BAD_SHAPE":
7770
7676
  return `Provide '${info.name}' starts uppercase, so it publishes a component type — its value must be 'self', not '${info.value}'`;
7771
- case "PROVIDE_NAME_COLLISION":
7772
- return `Provide '${info.name}' is also provided by '${info.other}' in the same scope — one name, one provider, so a lookup can find it`;
7773
7677
  case "UNKNOWN_MACRO_ARG":
7774
7678
  return `Argument '${info.name}' is not declared in macro '${info.macroName}'`;
7775
7679
  case "UNKNOWN_DIRECTIVE":
7776
7680
  return `Unknown directive '@${info.name}=${JSON.stringify(info.value)}'${fmtTagSuffix(info)}`;
7681
+ case "ORPHAN_DIRECTIVE":
7682
+ return `'@${info.name}=${JSON.stringify(info.value)}' needs an '@${info.needs}' on the same element — it was ignored${fmtTagSuffix(info)}`;
7777
7683
  case "UNKNOWN_X_OP":
7778
7684
  return `Unknown <x> op '${info.name}=${JSON.stringify(info.value)}'${fmtTagSuffix(info)}`;
7779
7685
  case "UNKNOWN_X_ATTR":
@@ -7907,7 +7813,6 @@ var App = class {
7907
7813
  this.transactor = new Transactor(comps, null);
7908
7814
  this.ParseContext = ParseContext2;
7909
7815
  this.renderer = renderer;
7910
- this.maxEventNodeDepth = Infinity;
7911
7816
  this._transactNextBatchId = this._evictCacheId = null;
7912
7817
  this._eventNames = new Set(_evs);
7913
7818
  this.dragInfo = this.curDragOver = null;
@@ -7934,18 +7839,17 @@ var App = class {
7934
7839
  _dispatchEvent(e) {
7935
7840
  const { type } = e;
7936
7841
  const isDrag = type === "dragover" || type === "dragstart" || type === "dragend" || type === "drop";
7937
- const { rootNode: root, maxEventNodeDepth: maxDepth, comps, transactor } = this;
7938
- const [path, handlers] = Path.fromNodeAndEventName(
7842
+ const { rootNode, comps, transactor } = this;
7843
+ const [path2, handlers] = DispatchPath.fromNodeAndEventName(
7939
7844
  e.target,
7940
7845
  type,
7941
- root,
7942
- maxDepth,
7846
+ rootNode,
7943
7847
  comps,
7944
7848
  !isDrag
7945
7849
  );
7946
- if (isDrag) this._handleDragEvent(e, type, path);
7947
- if (path !== null && handlers !== null)
7948
- for (const handler of handlers) transactor.transactInputNow(path, e, handler, this.dragInfo);
7850
+ if (isDrag) this._handleDragEvent(e, type, path2);
7851
+ if (path2 !== null && handlers !== null)
7852
+ for (const handler of handlers) transactor.transactInputNow(path2, e, handler, this.dragInfo);
7949
7853
  }
7950
7854
  _handleTouchEvent(e) {
7951
7855
  const { type } = e;
@@ -7988,7 +7892,7 @@ var App = class {
7988
7892
  this._touch = null;
7989
7893
  }
7990
7894
  }
7991
- _handleDragEvent(e, type, path) {
7895
+ _handleDragEvent(e, type, path2) {
7992
7896
  if (type === "dragover") {
7993
7897
  const dropTarget = getClosestDropTarget(e.target, this.rootNode, Infinity);
7994
7898
  if (dropTarget !== null) {
@@ -8000,10 +7904,10 @@ var App = class {
8000
7904
  } else if (type === "dragstart") {
8001
7905
  e.target.dataset.dragging = 1;
8002
7906
  const rootValue = this.state.val;
8003
- const txnPath = path.compact().toTransactionPath();
7907
+ const txnPath = path2.compact().toTransactionPath();
8004
7908
  const value = txnPath.lookup(rootValue);
8005
7909
  const dragType = e.target.dataset.dragtype ?? "?";
8006
- const stack = path.toTransactionPath().buildStack(this.makeStack(rootValue));
7910
+ const stack = path2.buildStack(this.makeStack(rootValue));
8007
7911
  this.dragInfo = new DragInfo(stack, value, dragType, e.target);
8008
7912
  } else if (type === "drop") {
8009
7913
  e.preventDefault();
@@ -8048,10 +7952,9 @@ var App = class {
8048
7952
  }
8049
7953
  compile() {
8050
7954
  for (const Comp of this.comps.byId.values()) {
8051
- const meta = Comp[COMPONENT];
8052
- meta.compile(this.ParseContext);
8053
- for (const key in meta.views)
8054
- for (const name of meta.views[key].ctx.genEventNames()) this._eventNames.add(name);
7955
+ Comp.compile(this.ParseContext);
7956
+ for (const key in Comp.views)
7957
+ for (const name of Comp.views[key].ctx.genEventNames()) this._eventNames.add(name);
8055
7958
  }
8056
7959
  this._compiled = true;
8057
7960
  }
@@ -8074,11 +7977,13 @@ var App = class {
8074
7977
  }
8075
7978
  stop() {
8076
7979
  this.stopCacheEvictionInterval();
7980
+ clearTimeout(this._transactNextBatchId);
7981
+ this._transactNextBatchId = null;
8077
7982
  for (const name of this._eventNames)
8078
7983
  this.rootNode.removeEventListener(name, this, listenerOpts(name));
8079
7984
  }
8080
7985
  sendAtRoot(name, args) {
8081
- this.transactor.pushSend(new Path([]), name, args);
7986
+ this.transactor.pushSend(new DispatchPath(), name, args);
8082
7987
  }
8083
7988
  registerComponents(comps, opts) {
8084
7989
  const scope = this.compStack.enter();
@@ -8160,28 +8065,21 @@ var NullDomCache = class {
8160
8065
  set(_keys, _cacheKey, _v) {
8161
8066
  }
8162
8067
  evict() {
8163
- return { hit: 0, miss: 0, badKey: 0 };
8164
8068
  }
8165
8069
  };
8166
8070
  var WeakMapDomCache = class {
8167
8071
  constructor() {
8168
- this.hit = this.miss = this.badKey = 0;
8169
8072
  this.keysByLen = /* @__PURE__ */ new Map();
8170
8073
  }
8171
- _returnValue(r) {
8172
- if (r === void 0) this.miss += 1;
8173
- else this.hit += 1;
8174
- return r;
8175
- }
8176
8074
  get(keys, cacheKey) {
8177
8075
  const len = keys.length;
8178
8076
  let cur = this.keysByLen.get(len);
8179
- if (!cur) return this._returnValue(void 0);
8077
+ if (!cur) return void 0;
8180
8078
  for (let i = 0; i < len - 1; i++) {
8181
8079
  cur = cur.get(keys[i]);
8182
- if (!cur) return this._returnValue(void 0);
8080
+ if (!cur) return void 0;
8183
8081
  }
8184
- return this._returnValue(cur.get(keys[len - 1])?.[cacheKey]);
8082
+ return cur.get(keys[len - 1])?.[cacheKey];
8185
8083
  }
8186
8084
  set(keys, cacheKey, v) {
8187
8085
  const len = keys.length;
@@ -8194,10 +8092,7 @@ var WeakMapDomCache = class {
8194
8092
  const key = keys[i];
8195
8093
  let next = cur.get(key);
8196
8094
  if (!next) {
8197
- if (!isWeakKey(key)) {
8198
- this.badKey += 1;
8199
- return;
8200
- }
8095
+ if (!isWeakKey(key)) return;
8201
8096
  next = /* @__PURE__ */ new WeakMap();
8202
8097
  cur.set(key, next);
8203
8098
  }
@@ -8207,23 +8102,38 @@ var WeakMapDomCache = class {
8207
8102
  const leaf = cur.get(lastKey);
8208
8103
  if (leaf) leaf[cacheKey] = v;
8209
8104
  else if (isWeakKey(lastKey)) cur.set(lastKey, { [cacheKey]: v });
8210
- else this.badKey += 1;
8211
8105
  }
8212
8106
  evict() {
8213
- const { hit, miss, badKey } = this;
8214
- this.hit = this.miss = this.badKey = 0;
8215
8107
  this.keysByLen = /* @__PURE__ */ new Map();
8216
- return { hit, miss, badKey };
8217
8108
  }
8218
8109
  };
8219
8110
 
8220
8111
  // src/renderer.js
8221
- var DATASET_ATTRS = ["nid", "cid", "eid", "vid", "si", "sk"];
8112
+ function stampRenderBase(vdom, text) {
8113
+ if (vdom instanceof VNode)
8114
+ return new VNode(
8115
+ vdom.tag,
8116
+ { ...vdom.attrs, "data-rp": text },
8117
+ vdom.childs,
8118
+ vdom.key,
8119
+ vdom.namespace
8120
+ );
8121
+ if (vdom instanceof VFragment)
8122
+ return new VFragment(vdom.childs.map((c) => stampRenderBase(c, text)));
8123
+ return vdom;
8124
+ }
8222
8125
  var Renderer = class {
8223
8126
  constructor(comps) {
8224
8127
  this.comps = comps;
8225
8128
  this.cache = new WeakMapDomCache();
8226
- this.renderTag = h;
8129
+ }
8130
+ // Parse nodes build their VDOM through the renderer, never by importing vdom.js
8131
+ // themselves: a component may have been compiled by ANOTHER copy of the library
8132
+ // (a docs example imports the dist bundle while the host app runs from src), and
8133
+ // `render()` recognizes VNodes by `instanceof` against ITS copy's classes. Going
8134
+ // through `rx` guarantees the rendering copy makes them.
8135
+ renderTag(tag, attrs, childs, namespace) {
8136
+ return h(tag, attrs, childs, namespace);
8227
8137
  }
8228
8138
  renderFragment(childs) {
8229
8139
  return new VFragment(childs);
@@ -8234,38 +8144,24 @@ var Renderer = class {
8234
8144
  setNullCache() {
8235
8145
  this.cache = new NullDomCache();
8236
8146
  }
8237
- // Library utilities for consumers embedding tutuca: render a value to a detached
8238
- // DOM node / an HTML string. Not called by the framework itself.
8239
- renderToDOM(stack, val) {
8240
- const rootNode = document.createElement("div");
8241
- const rOpts = { document };
8242
- render(h("DIV", null, [this.renderRoot(stack, val)]), rootNode, rOpts);
8243
- return rootNode.childNodes[0];
8244
- }
8245
- renderToString(stack, val, cleanAttrs = true) {
8246
- const dom = this.renderToDOM(stack, val);
8247
- if (cleanAttrs) {
8248
- const nodes = dom.querySelectorAll("[data-nid],[data-cid],[data-eid]");
8249
- for (const { dataset } of nodes) for (const name of DATASET_ATTRS) delete dataset[name];
8250
- }
8251
- return dom.innerHTML;
8252
- }
8253
8147
  renderRoot(stack, val, viewName = null) {
8254
8148
  const comp = this.comps.getCompFor(val);
8255
8149
  if (comp === null) return null;
8256
8150
  return this._rValComp(stack, val, comp, comp.getView(viewName).anode, "ROOT", viewName);
8257
8151
  }
8258
- renderIt(stack, node, key, viewName) {
8152
+ // Render `stack.it` at a `<x render*>` site (`node`). `base` is the absolute
8153
+ // path the site resumed at, when it rendered a `*name` (see _rValComp).
8154
+ renderIt(stack, node, viewName, base = null) {
8259
8155
  const comp = this.comps.getCompFor(stack.it);
8260
- return comp ? this._rValComp(stack, stack.it, comp, node, key, viewName) : null;
8156
+ return comp ? this._rValComp(stack, stack.it, comp, node, "", viewName, base) : null;
8261
8157
  }
8262
8158
  // `node` is the parse node of the render site (`<x render>` / `render-it` /
8263
8159
  // `render-each`, or the view's root anode for the app root). It keys the
8264
8160
  // cache as a globally-unique object: node ids alone are unique only within a
8265
8161
  // single view, so the same value rendered by two components (e.g. through a
8266
8162
  // shared dynamic-var sequence) would otherwise collide in the cache.
8267
- _rValComp(stack, val, comp, node, key, viewName) {
8268
- const cacheKey = `${viewName ?? ""}${stack.viewsId ?? ""}${key}`;
8163
+ _rValComp(stack, val, comp, node, key, viewName, base = null) {
8164
+ const cacheKey = `${viewName ?? ""}${stack.viewsId ?? ""}${key}${stack.renderPath.addressKey}`;
8269
8165
  const cachePath = [node, val];
8270
8166
  stack._pushDynBindValuesToArray(cachePath, comp);
8271
8167
  const cachedNode = this.cache.get(cachePath, cacheKey);
@@ -8273,30 +8169,43 @@ var Renderer = class {
8273
8169
  const view = viewName ? comp.getView(viewName) : stack.lookupBestView(comp.views, "main");
8274
8170
  const body = this.renderView(view, stack);
8275
8171
  if (body == null) return null;
8172
+ const baseJson = base === null ? null : pathToJson(base);
8276
8173
  const meta = this._renderMetadata({
8277
8174
  $: "Comp",
8278
8175
  nid: node?.nodeId ?? null,
8279
8176
  cid: comp.id,
8280
- vid: view.name
8177
+ vid: view.name,
8178
+ ...baseJson === null ? null : { base: baseJson }
8281
8179
  });
8282
- const dom = new VFragment([meta, body]);
8180
+ const dom = new VFragment([
8181
+ meta,
8182
+ baseJson === null ? body : stampRenderBase(body, JSON.stringify(baseJson))
8183
+ ]);
8283
8184
  this.cache.set(cachePath, cacheKey, dom);
8284
8185
  return dom;
8285
8186
  }
8286
8187
  pushEachEntry(r, nid, attrName, key, dom) {
8287
8188
  r.push(this._renderMetadata({ $: "Each", nid, [attrName]: key }), dom);
8288
8189
  }
8289
- renderEachWhen(stack, iterInfo, view, nid) {
8290
- const { seq, filter, loopWith, enricher } = iterInfo.eval(stack);
8190
+ renderEachWhen(stack, each) {
8191
+ const { val: seqVal, node: view, nodeId: nid } = each;
8192
+ const { seq, filter, loopWith, enricher } = each.evalIter(stack);
8193
+ const seqPath = seqVal instanceof DynVal ? stack.lookupDynamicLocated(seqVal.name)?.path ?? null : null;
8291
8194
  const r = [];
8292
8195
  const it = stack.it;
8293
8196
  const renderOne = (key, value, attrName, binds) => {
8197
+ const itemBase = seqPath === null ? null : keyedPath(seqPath, key);
8198
+ const itemStep = itemBase === null ? each.itemStep(key) : null;
8199
+ const itemPath = itemBase !== null ? stack.renderPath.pushFrame(itemBase) : itemStep !== null ? stack.renderPath.pushItem(itemStep) : stack.renderPath;
8294
8200
  const cachePath = enricher ? [view, it, value] : [view, value];
8295
- const cacheKey = `${stack.viewsId ?? ""}${nid}${key}`;
8201
+ const cacheKey = `${stack.viewsId ?? ""}${nid}${key}${itemPath.addressKey}`;
8296
8202
  const cachedNode = this.cache.get(cachePath, cacheKey);
8297
8203
  if (cachedNode) this.pushEachEntry(r, nid, attrName, key, cachedNode);
8298
8204
  else {
8299
- const dom = this.renderView(view, stack.enter(value, binds, false));
8205
+ const dom = this.renderView(
8206
+ view,
8207
+ stack.enter(value, binds, false, itemPath, itemBase !== null)
8208
+ );
8300
8209
  if (dom != null) this.pushEachEntry(r, nid, attrName, key, dom);
8301
8210
  this.cache.set(cachePath, cacheKey, dom);
8302
8211
  }
@@ -8445,12 +8354,7 @@ function shadowCheckComponent(Comp) {
8445
8354
  shadowViews[name] = { name, ctx, rawView };
8446
8355
  }
8447
8356
  const shadowComp = Object.create(Comp);
8448
- Object.defineProperty(shadowComp, "views", {
8449
- value: shadowViews,
8450
- writable: true,
8451
- enumerable: true,
8452
- configurable: true
8453
- });
8357
+ shadowComp.views = shadowViews;
8454
8358
  return checkComponent(shadowComp).reports;
8455
8359
  }
8456
8360
  function check(app) {
@@ -8476,39 +8380,6 @@ function check(app) {
8476
8380
  );
8477
8381
  return counts;
8478
8382
  }
8479
- var LintClassCollectorCtx = class _LintClassCollectorCtx extends ParseCtxClassSetCollector {
8480
- constructor(...args) {
8481
- super(...args);
8482
- this.attrs = [];
8483
- this.parseIssues = [];
8484
- }
8485
- enterMacro(macroName, macroVars, macroSlots) {
8486
- const { document: document2, Text, Comment, nodes, events, macroNodes } = this;
8487
- const frame = { macroName, macroVars, macroSlots };
8488
- const v = new _LintClassCollectorCtx(
8489
- document2,
8490
- Text,
8491
- Comment,
8492
- nodes,
8493
- events,
8494
- macroNodes,
8495
- frame,
8496
- this
8497
- );
8498
- v.classes = this.classes;
8499
- v.attrs = this.attrs;
8500
- v.parseIssues = this.parseIssues;
8501
- return v;
8502
- }
8503
- onAttributes(attrs, wrapperAttrs, textChild, isMacroCall = false, tag = null) {
8504
- super.onAttributes(attrs, wrapperAttrs, textChild, isMacroCall, tag);
8505
- this.attrs.push({ attrs, wrapperAttrs, textChild, isMacroCall, tag });
8506
- }
8507
- onParseIssue(kind, info) {
8508
- const tag = this.currentTag;
8509
- this.parseIssues.push({ kind, info: tag && info.tag === void 0 ? { ...info, tag } : info });
8510
- }
8511
- };
8512
8383
  var export_expect = chai_exports.expect;
8513
8384
  export {
8514
8385
  ALT_HANDLER_NOT_DEFINED,
@@ -8529,7 +8400,6 @@ export {
8529
8400
  DescribeResult,
8530
8401
  EVENT_HANDLER_METHOD_NOT_ALLOWED,
8531
8402
  ExampleIndex,
8532
- FIELD_CLASS,
8533
8403
  FIELD_METHOD_NAME_COLLISION,
8534
8404
  FIELD_VAL_IS_METHOD,
8535
8405
  FIELD_VAL_NOT_DEFINED,
@@ -8538,7 +8408,6 @@ export {
8538
8408
  IF_NO_BRANCH_SET,
8539
8409
  LOOKUP_BAD_SHAPE,
8540
8410
  LOOKUP_NO_PROVIDER,
8541
- LintClassCollectorCtx,
8542
8411
  LintComponentResult,
8543
8412
  LintContext,
8544
8413
  LintFinding,
@@ -8551,9 +8420,9 @@ export {
8551
8420
  ModuleInfo,
8552
8421
  ModuleTestReport,
8553
8422
  ModuleTests,
8423
+ ORPHAN_DIRECTIVE,
8554
8424
  PASS,
8555
8425
  PLACEHOLDERLESS_TEMPLATE_STRING,
8556
- PROVIDE_NAME_COLLISION,
8557
8426
  PROVIDE_NOT_ADDRESSABLE,
8558
8427
  PROVIDE_TYPE_BAD_SHAPE,
8559
8428
  ParseContext,
@@ -8594,6 +8463,7 @@ export {
8594
8463
  lintIdToMessage,
8595
8464
  macro,
8596
8465
  makeCollector,
8466
+ path,
8597
8467
  phaseOps,
8598
8468
  reportTestReportToConsole,
8599
8469
  resolveArgs,