tutuca 0.9.92 → 0.9.94

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.
@@ -12963,6 +12963,7 @@ function checkComponent(Comp, lx = new LintContext, { wellKnownExtras = EMPTY_SE
12963
12963
  checkProvidesAreAddressable(lx, Comp);
12964
12964
  checkLookupShapes(lx, Comp);
12965
12965
  checkHandlersNotAsync(lx, Comp);
12966
+ checkScopedStyleTopLevel(lx, Comp);
12966
12967
  const referencedAlters = new Set;
12967
12968
  const referencedInputs = new Set;
12968
12969
  const referencedDynamics = new Set;
@@ -13376,6 +13377,64 @@ function checkHandlersNotAsync(lx, Comp) {
13376
13377
  }
13377
13378
  }
13378
13379
  }
13380
+ function blankCssNoise(css) {
13381
+ return css.replace(/\/\*[\s\S]*?\*\//g, blankRun).replace(/"(?:[^"\\\n]|\\.)*"/g, blankRun).replace(/'(?:[^'\\\n]|\\.)*'/g, blankRun);
13382
+ }
13383
+ function offsetToLineCol2(str, index) {
13384
+ let line = 1;
13385
+ let lineStart = 0;
13386
+ for (let i = 0;i < index; i++) {
13387
+ if (str[i] === `
13388
+ `) {
13389
+ line++;
13390
+ lineStart = i + 1;
13391
+ }
13392
+ }
13393
+ return { line, column: index - lineStart + 1 };
13394
+ }
13395
+ function suppressedLines(css) {
13396
+ const lines = new Set;
13397
+ IGNORE_DIRECTIVE.lastIndex = 0;
13398
+ for (let m = IGNORE_DIRECTIVE.exec(css);m !== null; m = IGNORE_DIRECTIVE.exec(css)) {
13399
+ lines.add(offsetToLineCol2(css, m.index).line);
13400
+ }
13401
+ return lines;
13402
+ }
13403
+ function scanScopedCss(lx, css, key) {
13404
+ if (!css)
13405
+ return;
13406
+ const ignore = suppressedLines(css);
13407
+ const clean = blankCssNoise(css);
13408
+ const report = (id, info) => {
13409
+ if (ignore.has(info.location.line))
13410
+ return;
13411
+ lx.error(id, { key, ...info }, { kind: "rephrase", text: STYLE_TO_GLOBAL_HELP });
13412
+ };
13413
+ NON_NESTABLE_AT_RULE.lastIndex = 0;
13414
+ for (let m = NON_NESTABLE_AT_RULE.exec(clean);m !== null; m = NON_NESTABLE_AT_RULE.exec(clean)) {
13415
+ const at = m.index + m[0].indexOf("@");
13416
+ report(TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE, {
13417
+ atRule: m[1].toLowerCase(),
13418
+ location: offsetToLineCol2(clean, at)
13419
+ });
13420
+ }
13421
+ GLOBAL_LEADING_SELECTOR.lastIndex = 0;
13422
+ for (let m = GLOBAL_LEADING_SELECTOR.exec(clean);m !== null; m = GLOBAL_LEADING_SELECTOR.exec(clean)) {
13423
+ const at = m.index + m[0].indexOf(m[1]);
13424
+ report(GLOBAL_SELECTOR_IN_SCOPED_STYLE, {
13425
+ selector: m[1].toLowerCase(),
13426
+ location: offsetToLineCol2(clean, at)
13427
+ });
13428
+ }
13429
+ }
13430
+ function checkScopedStyleTopLevel(lx, Comp) {
13431
+ scanScopedCss(lx, Comp.commonStyle, "commonStyle");
13432
+ for (const name in Comp.views) {
13433
+ const { style } = Comp.views[name];
13434
+ if (style)
13435
+ lx.push({ viewName: name }, () => scanScopedCss(lx, style, "style"));
13436
+ }
13437
+ }
13379
13438
  function checkProvidesAreAddressable(lx, Comp) {
13380
13439
  for (const name in Comp._rawProvide) {
13381
13440
  if (Comp.provide[name] === undefined) {
@@ -13452,7 +13511,7 @@ class LintContext {
13452
13511
  this.reports.push({ id, info, level, context: { ...this.frame }, suggestion });
13453
13512
  }
13454
13513
  }
13455
- var KNOWN_COMPONENT_SPEC_KEYS, EMPTY_SET2, FRAMEWORK_WELL_KNOWN_EXTRAS, KNOWN_DIRECTIVE_NAMES, ALT_HANDLER_NOT_DEFINED = "ALT_HANDLER_NOT_DEFINED", ALT_HANDLER_NOT_REFERENCED = "ALT_HANDLER_NOT_REFERENCED", DYN_VAL_NOT_DEFINED = "DYN_VAL_NOT_DEFINED", DYN_ALIAS_NOT_REFERENCED = "DYN_ALIAS_NOT_REFERENCED", PROVIDE_NOT_ADDRESSABLE = "PROVIDE_NOT_ADDRESSABLE", LOOKUP_BAD_SHAPE = "LOOKUP_BAD_SHAPE", LOOKUP_TARGET_MALFORMED = "LOOKUP_TARGET_MALFORMED", RENDER_IT_OUTSIDE_OF_LOOP = "RENDER_IT_OUTSIDE_OF_LOOP", UNKNOWN_EVENT_MODIFIER = "UNKNOWN_EVENT_MODIFIER", UNKNOWN_HANDLER_ARG_NAME = "UNKNOWN_HANDLER_ARG_NAME", INPUT_HANDLER_NOT_IMPLEMENTED = "INPUT_HANDLER_NOT_IMPLEMENTED", INPUT_HANDLER_NOT_REFERENCED = "INPUT_HANDLER_NOT_REFERENCED", INPUT_HANDLER_METHOD_NOT_IMPLEMENTED = "INPUT_HANDLER_METHOD_NOT_IMPLEMENTED", INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD = "INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD", INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER = "INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER", FIELD_VAL_NOT_DEFINED = "FIELD_VAL_NOT_DEFINED", FIELD_VAL_IS_METHOD = "FIELD_VAL_IS_METHOD", METHOD_VAL_NOT_DEFINED = "METHOD_VAL_NOT_DEFINED", METHOD_VAL_IS_FIELD = "METHOD_VAL_IS_FIELD", DUPLICATE_ATTR_DEFINITION = "DUPLICATE_ATTR_DEFINITION", IF_NO_BRANCH_SET = "IF_NO_BRANCH_SET", UNKNOWN_REQUEST_NAME = "UNKNOWN_REQUEST_NAME", UNKNOWN_COMPONENT_NAME = "UNKNOWN_COMPONENT_NAME", UNKNOWN_MACRO_ARG = "UNKNOWN_MACRO_ARG", UNKNOWN_DIRECTIVE = "UNKNOWN_DIRECTIVE", UNKNOWN_X_OP = "UNKNOWN_X_OP", UNKNOWN_X_ATTR = "UNKNOWN_X_ATTR", MAYBE_DROP_AT_PREFIX = "MAYBE_DROP_AT_PREFIX", MAYBE_ADD_AT_PREFIX = "MAYBE_ADD_AT_PREFIX", BAD_VALUE = "BAD_VALUE", UNSUPPORTED_EXPR_SYNTAX = "UNSUPPORTED_EXPR_SYNTAX", REDUNDANT_TEMPLATE_STRING = "REDUNDANT_TEMPLATE_STRING", PLACEHOLDERLESS_TEMPLATE_STRING = "PLACEHOLDERLESS_TEMPLATE_STRING", UNKNOWN_COMPONENT_SPEC_KEY = "UNKNOWN_COMPONENT_SPEC_KEY", COMP_FIELD_BAD_SHAPE = "COMP_FIELD_BAD_SHAPE", ASYNC_HANDLER = "ASYNC_HANDLER", X_KNOWN_OP_NAMES, X_KNOWN_ATTR_NAMES, HOST_DIRECTIVE_ONLY_NAMES, LEVEL_WARN2 = "warn", LEVEL_ERROR2 = "error", LEVEL_HINT = "hint", PARSE_ISSUES, UNSUPPORTED_EXPR_GUIDANCE, HTML_LINT_OPTS, NO_WRAPPERS, KNOWN_HANDLER_NAMES, fixTo = (from, to) => ({ kind: "rewrite", from, to }), ATTR_VAL_CHECKERS, NODE_KIND_TO_CTX, HANDLER_CHANNELS, ASYNC_HANDLER_HELP, KNOWN_LOOKUP_KEYS, LintParseContext;
13514
+ var KNOWN_COMPONENT_SPEC_KEYS, EMPTY_SET2, FRAMEWORK_WELL_KNOWN_EXTRAS, KNOWN_DIRECTIVE_NAMES, ALT_HANDLER_NOT_DEFINED = "ALT_HANDLER_NOT_DEFINED", ALT_HANDLER_NOT_REFERENCED = "ALT_HANDLER_NOT_REFERENCED", DYN_VAL_NOT_DEFINED = "DYN_VAL_NOT_DEFINED", DYN_ALIAS_NOT_REFERENCED = "DYN_ALIAS_NOT_REFERENCED", PROVIDE_NOT_ADDRESSABLE = "PROVIDE_NOT_ADDRESSABLE", LOOKUP_BAD_SHAPE = "LOOKUP_BAD_SHAPE", LOOKUP_TARGET_MALFORMED = "LOOKUP_TARGET_MALFORMED", RENDER_IT_OUTSIDE_OF_LOOP = "RENDER_IT_OUTSIDE_OF_LOOP", UNKNOWN_EVENT_MODIFIER = "UNKNOWN_EVENT_MODIFIER", UNKNOWN_HANDLER_ARG_NAME = "UNKNOWN_HANDLER_ARG_NAME", INPUT_HANDLER_NOT_IMPLEMENTED = "INPUT_HANDLER_NOT_IMPLEMENTED", INPUT_HANDLER_NOT_REFERENCED = "INPUT_HANDLER_NOT_REFERENCED", INPUT_HANDLER_METHOD_NOT_IMPLEMENTED = "INPUT_HANDLER_METHOD_NOT_IMPLEMENTED", INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD = "INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD", INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER = "INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER", FIELD_VAL_NOT_DEFINED = "FIELD_VAL_NOT_DEFINED", FIELD_VAL_IS_METHOD = "FIELD_VAL_IS_METHOD", METHOD_VAL_NOT_DEFINED = "METHOD_VAL_NOT_DEFINED", METHOD_VAL_IS_FIELD = "METHOD_VAL_IS_FIELD", DUPLICATE_ATTR_DEFINITION = "DUPLICATE_ATTR_DEFINITION", IF_NO_BRANCH_SET = "IF_NO_BRANCH_SET", UNKNOWN_REQUEST_NAME = "UNKNOWN_REQUEST_NAME", UNKNOWN_COMPONENT_NAME = "UNKNOWN_COMPONENT_NAME", UNKNOWN_MACRO_ARG = "UNKNOWN_MACRO_ARG", UNKNOWN_DIRECTIVE = "UNKNOWN_DIRECTIVE", UNKNOWN_X_OP = "UNKNOWN_X_OP", UNKNOWN_X_ATTR = "UNKNOWN_X_ATTR", MAYBE_DROP_AT_PREFIX = "MAYBE_DROP_AT_PREFIX", MAYBE_ADD_AT_PREFIX = "MAYBE_ADD_AT_PREFIX", BAD_VALUE = "BAD_VALUE", UNSUPPORTED_EXPR_SYNTAX = "UNSUPPORTED_EXPR_SYNTAX", REDUNDANT_TEMPLATE_STRING = "REDUNDANT_TEMPLATE_STRING", PLACEHOLDERLESS_TEMPLATE_STRING = "PLACEHOLDERLESS_TEMPLATE_STRING", UNKNOWN_COMPONENT_SPEC_KEY = "UNKNOWN_COMPONENT_SPEC_KEY", COMP_FIELD_BAD_SHAPE = "COMP_FIELD_BAD_SHAPE", ASYNC_HANDLER = "ASYNC_HANDLER", TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE = "TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE", GLOBAL_SELECTOR_IN_SCOPED_STYLE = "GLOBAL_SELECTOR_IN_SCOPED_STYLE", X_KNOWN_OP_NAMES, X_KNOWN_ATTR_NAMES, HOST_DIRECTIVE_ONLY_NAMES, LEVEL_WARN2 = "warn", LEVEL_ERROR2 = "error", LEVEL_HINT = "hint", PARSE_ISSUES, UNSUPPORTED_EXPR_GUIDANCE, HTML_LINT_OPTS, NO_WRAPPERS, KNOWN_HANDLER_NAMES, fixTo = (from, to) => ({ kind: "rewrite", from, to }), ATTR_VAL_CHECKERS, NODE_KIND_TO_CTX, HANDLER_CHANNELS, ASYNC_HANDLER_HELP, NON_NESTABLE_AT_RULE, GLOBAL_LEADING_SELECTOR, IGNORE_DIRECTIVE, blankRun = (m) => m.replace(/[^\n]/g, " "), STYLE_TO_GLOBAL_HELP, KNOWN_LOOKUP_KEYS, LintParseContext;
13456
13515
  var init_lint_check = __esm(() => {
13457
13516
  init_anode();
13458
13517
  init_htmllinter();
@@ -13603,6 +13662,10 @@ var init_lint_check = __esm(() => {
13603
13662
  };
13604
13663
  HANDLER_CHANNELS = ["input", "receive", "bubble", "response", "alter"];
13605
13664
  ASYNC_HANDLER_HELP = "Move the async work into a request handler and trigger it with " + "ctx.request('name', args), then handle the result in a synchronous response " + "handler. To coordinate other components, keep the handler synchronous and use " + "ctx.send to deliver a message or ctx.bubble to raise an event.";
13665
+ NON_NESTABLE_AT_RULE = /(?:^|[\s;{}])@(?:-[a-z]+-)?(charset|import|namespace|font-face|keyframes|page|property|counter-style|font-feature-values|font-palette-values|view-transition)\b/gi;
13666
+ GLOBAL_LEADING_SELECTOR = /(?:^|[{}])\s*(html|body|:root)\b[^{};]*\{/gi;
13667
+ IGNORE_DIRECTIVE = /\/\*\s*tutuca-lint-ignore\s*\*\//g;
13668
+ STYLE_TO_GLOBAL_HELP = "Move it to the component's 'globalStyle' key, which is injected without the " + "component-scoping wrapper. To suppress a false positive, put a " + "/* tutuca-lint-ignore */ comment on the same line.";
13606
13669
  KNOWN_LOOKUP_KEYS = new Set(["for", "default"]);
13607
13670
  LintParseContext = class LintParseContext extends ParseContext {
13608
13671
  constructor(document2, Text, Comment) {
@@ -13870,36 +13933,81 @@ class Transactor {
13870
13933
  this.transactions = [];
13871
13934
  this.state = new State2(rootValue);
13872
13935
  this.onTransactionPushed = () => {};
13936
+ this._inflight = new Set;
13873
13937
  }
13874
13938
  pushTransaction(t) {
13875
13939
  this.transactions.push(t);
13876
13940
  this.onTransactionPushed(t);
13877
13941
  }
13942
+ _link(child, parent) {
13943
+ if (parent) {
13944
+ const release = parent.completion.track();
13945
+ child.completion.whenSubtreeSettled().then(release);
13946
+ }
13947
+ return child;
13948
+ }
13878
13949
  pushSend(path, name, args = [], opts = {}, parent = null) {
13879
- this.pushTransaction(new SendEvent(path, this, name, args, parent, opts));
13950
+ const t = new SendEvent(path, this, name, args, parent, opts);
13951
+ this.pushTransaction(t);
13952
+ return this._link(t, parent);
13953
+ }
13954
+ pushInput(path, name, args = [], opts = {}, parent = null) {
13955
+ const t = new InputDispatchEvent(path, this, name, args, parent, opts);
13956
+ this.pushTransaction(t);
13957
+ return this._link(t, parent);
13880
13958
  }
13881
13959
  pushBubble(path, name, args = [], opts = {}, parent = null, targetPath = null) {
13882
13960
  const newOpts = opts.skipSelf ? { ...opts, skipSelf: false } : opts;
13883
- this.pushTransaction(new BubbleEvent(path, this, name, args, parent, newOpts, targetPath));
13884
- }
13885
- async pushRequest(path, name, args = [], opts = {}, parent = null) {
13886
- const curRoot = this.state.val;
13887
- const txnPath = path.toTransactionPath();
13888
- const curLeaf = txnPath.lookup(curRoot);
13889
- const handler = this.comps.getRequestFor(curLeaf, name) ?? mkReq404(name);
13890
- const reqCtx = new RequestContext(path, this, parent, curRoot);
13891
- const resHandlerName = opts?.onResName ?? name;
13892
- const resPath = opts?.livePath ? null : txnPath.pinKeys(curRoot);
13893
- const push = (specificName, baseName, singleArg, result, error) => {
13894
- const resArgs = specificName ? [singleArg] : [result, error];
13895
- const t = new ResponseEvent(path, this, specificName ?? baseName, resArgs, parent, resPath);
13896
- this.pushTransaction(t);
13961
+ const t = new BubbleEvent(path, this, name, args, parent, newOpts, targetPath);
13962
+ this.pushTransaction(t);
13963
+ return this._link(t, parent);
13964
+ }
13965
+ pushRequest(path, name, args = [], opts = {}, parent = null) {
13966
+ const release = parent ? parent.completion.track() : null;
13967
+ const p = this._runRequest(path, name, args, opts, parent, release);
13968
+ this._inflight.add(p);
13969
+ p.finally(() => this._inflight.delete(p));
13970
+ return p;
13971
+ }
13972
+ async settle(maxTurns = 1e4) {
13973
+ while ((this.hasPendingTransactions || this._inflight.size) && maxTurns-- > 0) {
13974
+ while (this.hasPendingTransactions)
13975
+ this.transactNext();
13976
+ if (this._inflight.size)
13977
+ await Promise.allSettled([...this._inflight]);
13978
+ }
13979
+ }
13980
+ async _runRequest(path, name, args = [], opts = {}, parent = null, release = null) {
13981
+ let released = false;
13982
+ const transfer = (t) => {
13983
+ if (release) {
13984
+ released = true;
13985
+ t.completion.whenSubtreeSettled().then(release);
13986
+ }
13897
13987
  };
13898
13988
  try {
13899
- const result = await handler.fn.apply(null, [...args, reqCtx]);
13900
- push(opts?.onOkName, resHandlerName, result, result, null);
13901
- } catch (error) {
13902
- push(opts?.onErrorName, resHandlerName, error, null, error);
13989
+ const curRoot = this.state.val;
13990
+ const txnPath = path.toTransactionPath();
13991
+ const curLeaf = txnPath.lookup(curRoot);
13992
+ const handler = this.comps.getRequestFor(curLeaf, name) ?? mkReq404(name);
13993
+ const reqCtx = new RequestContext(path, this, parent, curRoot);
13994
+ const resHandlerName = opts?.onResName ?? name;
13995
+ const resPath = opts?.livePath ? null : txnPath.pinKeys(curRoot);
13996
+ const push = (specificName, baseName, singleArg, result, error) => {
13997
+ const resArgs = specificName ? [singleArg] : [result, error];
13998
+ const t = new ResponseEvent(path, this, specificName ?? baseName, resArgs, parent, resPath);
13999
+ transfer(t);
14000
+ this.pushTransaction(t);
14001
+ };
14002
+ try {
14003
+ const result = await handler.fn.apply(null, [...args, reqCtx]);
14004
+ push(opts?.onOkName, resHandlerName, result, result, null);
14005
+ } catch (error) {
14006
+ push(opts?.onErrorName, resHandlerName, error, null, error);
14007
+ }
14008
+ } finally {
14009
+ if (release && !released)
14010
+ release();
13903
14011
  }
13904
14012
  }
13905
14013
  get hasPendingTransactions() {
@@ -13910,13 +14018,18 @@ class Transactor {
13910
14018
  this.transact(this.transactions.shift());
13911
14019
  }
13912
14020
  transact(transaction) {
13913
- const curState = this.state.val;
13914
- const newState = transaction.run(curState, this.comps);
13915
- if (newState !== undefined) {
13916
- this.state.set(newState, { transaction });
13917
- transaction.afterTransaction();
13918
- } else
13919
- console.warn("undefined new state", { curState, transaction });
14021
+ try {
14022
+ const curState = this.state.val;
14023
+ const newState = transaction.run(curState, this.comps);
14024
+ if (newState !== undefined) {
14025
+ this.state.set(newState, { transaction });
14026
+ transaction.afterTransaction();
14027
+ } else
14028
+ console.warn("undefined new state", { curState, transaction });
14029
+ } finally {
14030
+ transaction._completion?.ensureSelfSettled();
14031
+ transaction._completion?.releaseSelf();
14032
+ }
13920
14033
  }
13921
14034
  transactInputNow(path, event, eventHandler, dragInfo) {
13922
14035
  this.transact(new InputEvent(path, event, eventHandler, this, dragInfo));
@@ -13937,18 +14050,17 @@ class Transaction {
13937
14050
  this.path = path;
13938
14051
  this.transactor = transactor;
13939
14052
  this.parentTransaction = parentTransaction;
13940
- this._task = null;
14053
+ this._completion = null;
13941
14054
  }
13942
- get task() {
13943
- this._task ??= new Task;
13944
- return this._task;
14055
+ get completion() {
14056
+ this._completion ??= new Completion;
14057
+ return this._completion;
13945
14058
  }
13946
- getCompletionPromise() {
13947
- return this.task.promise;
14059
+ whenSettled() {
14060
+ return this.completion.whenSettled();
13948
14061
  }
13949
- setParent(parentTransaction) {
13950
- this.parentTransaction = parentTransaction;
13951
- parentTransaction.task.addDep(this.task);
14062
+ whenSubtreeSettled() {
14063
+ return this.completion.whenSubtreeSettled();
13952
14064
  }
13953
14065
  run(rootValue, comps) {
13954
14066
  return this.updateRootValue(rootValue, comps);
@@ -13974,7 +14086,7 @@ class Transaction {
13974
14086
  const txnPath = this.getTransactionPath();
13975
14087
  const curLeaf = txnPath.lookup(curRoot);
13976
14088
  const newLeaf = this.callHandler(curRoot, curLeaf, comps);
13977
- this._task?.complete?.({ value: newLeaf, old: curLeaf });
14089
+ this._completion?.markSelfSettled({ value: newLeaf, old: curLeaf });
13978
14090
  return curLeaf !== newLeaf ? txnPath.setValue(curRoot, newLeaf) : curRoot;
13979
14091
  }
13980
14092
  lookupName(_name) {
@@ -13985,29 +14097,65 @@ function getValue(e) {
13985
14097
  return e.target.type === "checkbox" ? e.target.checked : (e instanceof CustomEvent ? e.detail : e.target.value) ?? null;
13986
14098
  }
13987
14099
 
13988
- class Task {
14100
+ class Completion {
13989
14101
  constructor() {
13990
- this.deps = [];
13991
- this.val = this.resolve = this.reject = null;
13992
- this.promise = new Promise((res, rej) => {
13993
- this.resolve = res;
13994
- this.reject = rej;
14102
+ this.val = undefined;
14103
+ this.selfSettled = false;
14104
+ this.subtreeSettled = false;
14105
+ this.pending = 1;
14106
+ this._selfResolve = null;
14107
+ this._selfPromise = null;
14108
+ this._subtreeResolve = null;
14109
+ this._subtreePromise = null;
14110
+ this._selfReleased = false;
14111
+ }
14112
+ whenSettled() {
14113
+ if (this.selfSettled)
14114
+ return Promise.resolve(this.val);
14115
+ this._selfPromise ??= new Promise((res) => {
14116
+ this._selfResolve = res;
13995
14117
  });
13996
- this.isCompleted = false;
14118
+ return this._selfPromise;
13997
14119
  }
13998
- addDep(task) {
13999
- console.assert(!this.isCompleted, "addDep for completed task", this, task);
14000
- this.deps.push(task);
14001
- task.promise.then((_) => this._check());
14120
+ whenSubtreeSettled() {
14121
+ if (this.subtreeSettled)
14122
+ return Promise.resolve(this.val);
14123
+ this._subtreePromise ??= new Promise((res) => {
14124
+ this._subtreeResolve = res;
14125
+ });
14126
+ return this._subtreePromise;
14002
14127
  }
14003
- complete(val) {
14128
+ markSelfSettled(val) {
14129
+ if (this.selfSettled)
14130
+ return;
14131
+ this.selfSettled = true;
14004
14132
  this.val = val;
14005
- this._check();
14133
+ this._selfResolve?.(val);
14006
14134
  }
14007
- _check() {
14008
- if (this.deps.every((task) => task.isCompleted)) {
14009
- this.isCompleted = true;
14010
- this.resolve(this);
14135
+ ensureSelfSettled() {
14136
+ if (!this.selfSettled)
14137
+ this.markSelfSettled(this.val);
14138
+ }
14139
+ track() {
14140
+ this.pending++;
14141
+ let done = false;
14142
+ return () => {
14143
+ if (done)
14144
+ return;
14145
+ done = true;
14146
+ this._release();
14147
+ };
14148
+ }
14149
+ releaseSelf() {
14150
+ if (this._selfReleased)
14151
+ return;
14152
+ this._selfReleased = true;
14153
+ this._release();
14154
+ }
14155
+ _release() {
14156
+ if (--this.pending === 0) {
14157
+ this.subtreeSettled = true;
14158
+ this._subtreeResolve?.(this.val);
14011
14159
  }
14012
14160
  }
14013
14161
  }
@@ -14046,11 +14194,17 @@ class Dispatcher {
14046
14194
  requestAtPath(path, name, args, opts) {
14047
14195
  return this.transactor.pushRequest(path, name, args, opts, this.parent);
14048
14196
  }
14197
+ inputAtPath(path, name, args, opts) {
14198
+ return this.transactor.pushInput(path, name, args, opts, this.parent);
14199
+ }
14049
14200
  lookupTypeFor(name, inst) {
14050
14201
  return this.transactor.comps.getCompFor(inst).scope.lookupComponent(name);
14051
14202
  }
14052
14203
  }
14053
- var toNullIfNaN = (v) => Number.isNaN(v) ? null : v, InputEvent, NameArgsTransaction, ResponseEvent, SendEvent, BubbleEvent, EventContext, RequestContext, PathChanges;
14204
+ function rootDispatcher(transactor) {
14205
+ return new Dispatcher(new Path([]), transactor, null);
14206
+ }
14207
+ var toNullIfNaN = (v) => Number.isNaN(v) ? null : v, InputEvent, NameArgsTransaction, ResponseEvent, SendEvent, BubbleEvent, InputDispatchEvent, EventContext, RequestContext, PathChanges;
14054
14208
  var init_transactor = __esm(() => {
14055
14209
  init_path();
14056
14210
  init_stack();
@@ -14175,6 +14329,9 @@ var init_transactor = __esm(() => {
14175
14329
  this.opts.bubbles = false;
14176
14330
  }
14177
14331
  };
14332
+ InputDispatchEvent = class InputDispatchEvent extends NameArgsTransaction {
14333
+ handlerProp = "input";
14334
+ };
14178
14335
  EventContext = class EventContext extends Dispatcher {
14179
14336
  get name() {
14180
14337
  return this.parent?.name ?? null;
@@ -14560,6 +14717,49 @@ var init_render2 = __esm(() => {
14560
14717
  init_render();
14561
14718
  });
14562
14719
 
14720
+ // src/on.js
14721
+ function phaseOps(phase) {
14722
+ const ops = [];
14723
+ for (const type3 of OP_KINDS)
14724
+ for (const a of phase[type3] ?? [])
14725
+ ops.push({ type: type3, ...a });
14726
+ for (const a of phase.do ?? [])
14727
+ ops.push(a);
14728
+ return ops;
14729
+ }
14730
+ function resolveArgs(args, self) {
14731
+ return typeof args === "function" ? args(self) ?? [] : args ?? [];
14732
+ }
14733
+ function dispatchPhase(dispatcher, targetPath, phase, self) {
14734
+ if (!phase)
14735
+ return;
14736
+ for (const op of phaseOps(phase)) {
14737
+ const args = resolveArgs(op.args, self);
14738
+ switch (op.type) {
14739
+ case "send":
14740
+ dispatcher.sendAtPath(targetPath, op.name, args, op.opts);
14741
+ break;
14742
+ case "bubble":
14743
+ dispatcher.sendAtPath(targetPath, op.name, args, {
14744
+ skipSelf: true,
14745
+ bubbles: true,
14746
+ ...op.opts
14747
+ });
14748
+ break;
14749
+ case "request":
14750
+ dispatcher.requestAtPath(targetPath, op.name, args, op.opts);
14751
+ break;
14752
+ case "input":
14753
+ dispatcher.inputAtPath(targetPath, op.name, args, op.opts);
14754
+ break;
14755
+ }
14756
+ }
14757
+ }
14758
+ var OP_KINDS;
14759
+ var init_on = __esm(() => {
14760
+ OP_KINDS = ["send", "bubble", "request", "input"];
14761
+ });
14762
+
14563
14763
  // tools/core/tests.js
14564
14764
  class Describe {
14565
14765
  constructor({ title, componentName = null, parent = null }) {
@@ -14699,7 +14899,9 @@ async function runTests({
14699
14899
  expect: expect2,
14700
14900
  name = null,
14701
14901
  grep = null,
14702
- bail = false
14902
+ bail = false,
14903
+ requestHandlers = null,
14904
+ macros = null
14703
14905
  } = {}) {
14704
14906
  const counts = { pass: 0, fail: 0, skip: 0, total: 0 };
14705
14907
  if (typeof getTests !== "function") {
@@ -14711,7 +14913,30 @@ async function runTests({
14711
14913
  throw new Error("runTests: expect must be provided (e.g. chai's expect)");
14712
14914
  }
14713
14915
  const { describe, test: test2, moduleTests } = makeCollector({ path, components });
14714
- await getTests({ describe, test: test2, expect: expect2 });
14916
+ await getTests({ describe, test: test2, expect: expect2, drive });
14917
+ let _stack = null;
14918
+ function getStack() {
14919
+ if (_stack)
14920
+ return _stack;
14921
+ _stack = new ComponentStack;
14922
+ _stack.registerComponents(components);
14923
+ if (macros)
14924
+ _stack.registerMacros(macros);
14925
+ if (requestHandlers)
14926
+ _stack.registerRequestHandlers(requestHandlers);
14927
+ return _stack;
14928
+ }
14929
+ async function drive(value, phase, opts = {}) {
14930
+ const transactor = new Transactor(getStack().comps, value);
14931
+ if (opts.onMessage)
14932
+ transactor.state.onChange(({ val, old, info }) => {
14933
+ const t = info?.transaction;
14934
+ opts.onMessage({ kind: t?.handlerProp ?? "input", name: t?.name, args: t?.args, path: t?.path }, old, val);
14935
+ });
14936
+ dispatchPhase(rootDispatcher(transactor), new Path([]), phase, value);
14937
+ await transactor.settle();
14938
+ return transactor.state.val;
14939
+ }
14715
14940
  let bailed = false;
14716
14941
  async function visit(node) {
14717
14942
  if (node instanceof Test) {
@@ -14779,7 +15004,12 @@ async function runTests({
14779
15004
  modules: [new ModuleTestReport({ path, suites: suiteResults, counts })]
14780
15005
  });
14781
15006
  }
14782
- var init_test = () => {};
15007
+ var init_test = __esm(() => {
15008
+ init_components();
15009
+ init_on();
15010
+ init_path();
15011
+ init_transactor();
15012
+ });
14783
15013
 
14784
15014
  // tools/cli/commands/_registry.js
14785
15015
  var exports__registry = {};
@@ -14867,7 +15097,9 @@ var init__registry = __esm(() => {
14867
15097
  expect,
14868
15098
  name: positionals[0] ?? null,
14869
15099
  grep: values.grep ?? null,
14870
- bail: values.bail ?? false
15100
+ bail: values.bail ?? false,
15101
+ requestHandlers: normalized.requestHandlers,
15102
+ macros: normalized.macros
14871
15103
  }),
14872
15104
  exitOn: (result) => result.hasFailures ? 4 : 0
14873
15105
  }
@@ -15103,6 +15335,18 @@ var init_lint_rules = __esm(() => {
15103
15335
  group: "Handler effects",
15104
15336
  summary: "A handler in `input`/`receive`/`bubble`/`response`/`alter` is an async " + "function — handlers must be synchronous; use a request handler, `ctx.send`, " + "or `ctx.bubble` instead."
15105
15337
  },
15338
+ {
15339
+ code: TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE,
15340
+ level: "error",
15341
+ group: "Component styles",
15342
+ summary: "A top-level-only at-rule (`@import`, `@keyframes`, `@font-face`, …) appears " + "in `style`/`commonStyle`, which is wrapped in a component-scoped selector " + "where it is invalid and dropped — move it to `globalStyle`."
15343
+ },
15344
+ {
15345
+ code: GLOBAL_SELECTOR_IN_SCOPED_STYLE,
15346
+ level: "error",
15347
+ group: "Component styles",
15348
+ summary: "A rule whose leading selector is `html`/`body`/`:root` appears in " + "`style`/`commonStyle`; once scoped it becomes a descendant selector that " + "never matches — move it to `globalStyle`."
15349
+ },
15106
15350
  {
15107
15351
  code: COMP_FIELD_BAD_SHAPE,
15108
15352
  level: "error",
@@ -15298,6 +15542,10 @@ function lintIdToMessage(id, info) {
15298
15542
  return `MathML attribute '${info.raw}' will be rewritten to '${info.canonical}'${fmtLocationSuffix(info)}`;
15299
15543
  case "ASYNC_HANDLER":
15300
15544
  return `Handler '${info.name}' in '${info.channel}' is an async function — handlers must be synchronous and return the updated state (an async function returns a Promise the framework won't await)`;
15545
+ case "TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE":
15546
+ return `'@${info.atRule}' is a top-level-only at-rule, but '${info.key}' is wrapped in a component-scoped selector ([data-cid=…]{…}) where it is invalid and silently dropped — move it to 'globalStyle'${fmtLocationSuffix(info)}`;
15547
+ case "GLOBAL_SELECTOR_IN_SCOPED_STYLE":
15548
+ return `Selector '${info.selector}' targets the document root, but '${info.key}' is wrapped in a component-scoped selector, so it becomes a descendant selector that never matches — move it to 'globalStyle'${fmtLocationSuffix(info)}`;
15301
15549
  case "LINT_ERROR":
15302
15550
  return info.message;
15303
15551
  default:
@@ -15872,7 +16120,9 @@ async function runDevTests(projectDir, devModuleUrls) {
15872
16120
  getTests: mod.getTests,
15873
16121
  components: normalized.components,
15874
16122
  path: abs,
15875
- expect
16123
+ expect,
16124
+ requestHandlers: normalized.requestHandlers,
16125
+ macros: normalized.macros
15876
16126
  });
15877
16127
  const m = report.modules[0];
15878
16128
  totalTests += m.counts.total;