tutuca 0.9.93 → 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.
@@ -13933,36 +13933,81 @@ class Transactor {
13933
13933
  this.transactions = [];
13934
13934
  this.state = new State2(rootValue);
13935
13935
  this.onTransactionPushed = () => {};
13936
+ this._inflight = new Set;
13936
13937
  }
13937
13938
  pushTransaction(t) {
13938
13939
  this.transactions.push(t);
13939
13940
  this.onTransactionPushed(t);
13940
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
+ }
13941
13949
  pushSend(path, name, args = [], opts = {}, parent = null) {
13942
- 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);
13943
13958
  }
13944
13959
  pushBubble(path, name, args = [], opts = {}, parent = null, targetPath = null) {
13945
13960
  const newOpts = opts.skipSelf ? { ...opts, skipSelf: false } : opts;
13946
- this.pushTransaction(new BubbleEvent(path, this, name, args, parent, newOpts, targetPath));
13947
- }
13948
- async pushRequest(path, name, args = [], opts = {}, parent = null) {
13949
- const curRoot = this.state.val;
13950
- const txnPath = path.toTransactionPath();
13951
- const curLeaf = txnPath.lookup(curRoot);
13952
- const handler = this.comps.getRequestFor(curLeaf, name) ?? mkReq404(name);
13953
- const reqCtx = new RequestContext(path, this, parent, curRoot);
13954
- const resHandlerName = opts?.onResName ?? name;
13955
- const resPath = opts?.livePath ? null : txnPath.pinKeys(curRoot);
13956
- const push = (specificName, baseName, singleArg, result, error) => {
13957
- const resArgs = specificName ? [singleArg] : [result, error];
13958
- const t = new ResponseEvent(path, this, specificName ?? baseName, resArgs, parent, resPath);
13959
- 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
+ }
13960
13987
  };
13961
13988
  try {
13962
- const result = await handler.fn.apply(null, [...args, reqCtx]);
13963
- push(opts?.onOkName, resHandlerName, result, result, null);
13964
- } catch (error) {
13965
- 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();
13966
14011
  }
13967
14012
  }
13968
14013
  get hasPendingTransactions() {
@@ -13973,13 +14018,18 @@ class Transactor {
13973
14018
  this.transact(this.transactions.shift());
13974
14019
  }
13975
14020
  transact(transaction) {
13976
- const curState = this.state.val;
13977
- const newState = transaction.run(curState, this.comps);
13978
- if (newState !== undefined) {
13979
- this.state.set(newState, { transaction });
13980
- transaction.afterTransaction();
13981
- } else
13982
- 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
+ }
13983
14033
  }
13984
14034
  transactInputNow(path, event, eventHandler, dragInfo) {
13985
14035
  this.transact(new InputEvent(path, event, eventHandler, this, dragInfo));
@@ -14000,18 +14050,17 @@ class Transaction {
14000
14050
  this.path = path;
14001
14051
  this.transactor = transactor;
14002
14052
  this.parentTransaction = parentTransaction;
14003
- this._task = null;
14053
+ this._completion = null;
14004
14054
  }
14005
- get task() {
14006
- this._task ??= new Task;
14007
- return this._task;
14055
+ get completion() {
14056
+ this._completion ??= new Completion;
14057
+ return this._completion;
14008
14058
  }
14009
- getCompletionPromise() {
14010
- return this.task.promise;
14059
+ whenSettled() {
14060
+ return this.completion.whenSettled();
14011
14061
  }
14012
- setParent(parentTransaction) {
14013
- this.parentTransaction = parentTransaction;
14014
- parentTransaction.task.addDep(this.task);
14062
+ whenSubtreeSettled() {
14063
+ return this.completion.whenSubtreeSettled();
14015
14064
  }
14016
14065
  run(rootValue, comps) {
14017
14066
  return this.updateRootValue(rootValue, comps);
@@ -14037,7 +14086,7 @@ class Transaction {
14037
14086
  const txnPath = this.getTransactionPath();
14038
14087
  const curLeaf = txnPath.lookup(curRoot);
14039
14088
  const newLeaf = this.callHandler(curRoot, curLeaf, comps);
14040
- this._task?.complete?.({ value: newLeaf, old: curLeaf });
14089
+ this._completion?.markSelfSettled({ value: newLeaf, old: curLeaf });
14041
14090
  return curLeaf !== newLeaf ? txnPath.setValue(curRoot, newLeaf) : curRoot;
14042
14091
  }
14043
14092
  lookupName(_name) {
@@ -14048,29 +14097,65 @@ function getValue(e) {
14048
14097
  return e.target.type === "checkbox" ? e.target.checked : (e instanceof CustomEvent ? e.detail : e.target.value) ?? null;
14049
14098
  }
14050
14099
 
14051
- class Task {
14100
+ class Completion {
14052
14101
  constructor() {
14053
- this.deps = [];
14054
- this.val = this.resolve = this.reject = null;
14055
- this.promise = new Promise((res, rej) => {
14056
- this.resolve = res;
14057
- 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;
14058
14117
  });
14059
- this.isCompleted = false;
14118
+ return this._selfPromise;
14060
14119
  }
14061
- addDep(task) {
14062
- console.assert(!this.isCompleted, "addDep for completed task", this, task);
14063
- this.deps.push(task);
14064
- 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;
14065
14127
  }
14066
- complete(val) {
14128
+ markSelfSettled(val) {
14129
+ if (this.selfSettled)
14130
+ return;
14131
+ this.selfSettled = true;
14067
14132
  this.val = val;
14068
- this._check();
14133
+ this._selfResolve?.(val);
14134
+ }
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
+ };
14069
14148
  }
14070
- _check() {
14071
- if (this.deps.every((task) => task.isCompleted)) {
14072
- this.isCompleted = true;
14073
- this.resolve(this);
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);
14074
14159
  }
14075
14160
  }
14076
14161
  }
@@ -14109,11 +14194,17 @@ class Dispatcher {
14109
14194
  requestAtPath(path, name, args, opts) {
14110
14195
  return this.transactor.pushRequest(path, name, args, opts, this.parent);
14111
14196
  }
14197
+ inputAtPath(path, name, args, opts) {
14198
+ return this.transactor.pushInput(path, name, args, opts, this.parent);
14199
+ }
14112
14200
  lookupTypeFor(name, inst) {
14113
14201
  return this.transactor.comps.getCompFor(inst).scope.lookupComponent(name);
14114
14202
  }
14115
14203
  }
14116
- 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;
14117
14208
  var init_transactor = __esm(() => {
14118
14209
  init_path();
14119
14210
  init_stack();
@@ -14238,6 +14329,9 @@ var init_transactor = __esm(() => {
14238
14329
  this.opts.bubbles = false;
14239
14330
  }
14240
14331
  };
14332
+ InputDispatchEvent = class InputDispatchEvent extends NameArgsTransaction {
14333
+ handlerProp = "input";
14334
+ };
14241
14335
  EventContext = class EventContext extends Dispatcher {
14242
14336
  get name() {
14243
14337
  return this.parent?.name ?? null;
@@ -14623,6 +14717,49 @@ var init_render2 = __esm(() => {
14623
14717
  init_render();
14624
14718
  });
14625
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
+
14626
14763
  // tools/core/tests.js
14627
14764
  class Describe {
14628
14765
  constructor({ title, componentName = null, parent = null }) {
@@ -14762,7 +14899,9 @@ async function runTests({
14762
14899
  expect: expect2,
14763
14900
  name = null,
14764
14901
  grep = null,
14765
- bail = false
14902
+ bail = false,
14903
+ requestHandlers = null,
14904
+ macros = null
14766
14905
  } = {}) {
14767
14906
  const counts = { pass: 0, fail: 0, skip: 0, total: 0 };
14768
14907
  if (typeof getTests !== "function") {
@@ -14774,7 +14913,30 @@ async function runTests({
14774
14913
  throw new Error("runTests: expect must be provided (e.g. chai's expect)");
14775
14914
  }
14776
14915
  const { describe, test: test2, moduleTests } = makeCollector({ path, components });
14777
- 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
+ }
14778
14940
  let bailed = false;
14779
14941
  async function visit(node) {
14780
14942
  if (node instanceof Test) {
@@ -14842,7 +15004,12 @@ async function runTests({
14842
15004
  modules: [new ModuleTestReport({ path, suites: suiteResults, counts })]
14843
15005
  });
14844
15006
  }
14845
- var init_test = () => {};
15007
+ var init_test = __esm(() => {
15008
+ init_components();
15009
+ init_on();
15010
+ init_path();
15011
+ init_transactor();
15012
+ });
14846
15013
 
14847
15014
  // tools/cli/commands/_registry.js
14848
15015
  var exports__registry = {};
@@ -14930,7 +15097,9 @@ var init__registry = __esm(() => {
14930
15097
  expect,
14931
15098
  name: positionals[0] ?? null,
14932
15099
  grep: values.grep ?? null,
14933
- bail: values.bail ?? false
15100
+ bail: values.bail ?? false,
15101
+ requestHandlers: normalized.requestHandlers,
15102
+ macros: normalized.macros
14934
15103
  }),
14935
15104
  exitOn: (result) => result.hasFailures ? 4 : 0
14936
15105
  }
@@ -15951,7 +16120,9 @@ async function runDevTests(projectDir, devModuleUrls) {
15951
16120
  getTests: mod.getTests,
15952
16121
  components: normalized.components,
15953
16122
  path: abs,
15954
- expect
16123
+ expect,
16124
+ requestHandlers: normalized.requestHandlers,
16125
+ macros: normalized.macros
15955
16126
  });
15956
16127
  const m = report.modules[0];
15957
16128
  totalTests += m.counts.total;