@jsenv/navi 0.30.25 → 0.30.26

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.
@@ -3535,6 +3535,7 @@ const updateActions = ({
3535
3535
 
3536
3536
  const actionToRunPrivateProperties =
3537
3537
  getActionPrivateProperties(actionToPrerunOrRun);
3538
+ actionToRunPrivateProperties.answeredAsync = false;
3538
3539
  let performRunResult;
3539
3540
  try {
3540
3541
  performRunResult = actionToRunPrivateProperties.performRun({
@@ -3559,6 +3560,7 @@ const updateActions = ({
3559
3560
  activationWeakSet.add(actionToPrerunOrRun);
3560
3561
 
3561
3562
  if (performRunResult && typeof performRunResult.then === "function") {
3563
+ actionToRunPrivateProperties.answeredAsync = true;
3562
3564
  actionPromiseMap.set(actionToPrerunOrRun, performRunResult);
3563
3565
  allThenableArray.push(performRunResult);
3564
3566
  hasAsync = true;
@@ -3588,7 +3590,9 @@ const updateActions = ({
3588
3590
 
3589
3591
  // Execute promotions (prerun -> run requested)
3590
3592
  for (const actionToPromote of willPromoteSet) {
3591
- actionToPromote.isPrerunSignal.value = false;
3593
+ writeActionState(() => {
3594
+ actionToPromote.isPrerunSignal.value = false;
3595
+ });
3592
3596
  }
3593
3597
  }
3594
3598
 
@@ -3597,13 +3601,21 @@ const updateActions = ({
3597
3601
  // that IS carrying it. `await action.run()` then means the same thing to
3598
3602
  // every caller: the second one waits for the first one's request and reads
3599
3603
  // the same data, instead of being handed `undefined` before the network has
3600
- // said anything. See the run() JSDoc.
3604
+ // said anything. It answers in the tense of that run: the answer of a run
3605
+ // that answered with a promise comes back as one, so `run().catch()` holds
3606
+ // whatever state the instance is in. See the run() JSDoc.
3601
3607
  {
3602
3608
  for (const actionToJoin of willJoinSet) {
3603
3609
  const runningPromise = actionPromiseMap.get(actionToJoin);
3604
3610
  if (runningPromise) {
3605
3611
  hasAsync = true;
3606
3612
  resultArray.push({ type: "async", promise: runningPromise });
3613
+ } else if (getActionPrivateProperties(actionToJoin).answeredAsync) {
3614
+ hasAsync = true;
3615
+ resultArray.push({
3616
+ type: "async",
3617
+ promise: Promise.resolve(actionToJoin.dataSignal.peek()),
3618
+ });
3607
3619
  } else {
3608
3620
  resultArray.push({
3609
3621
  type: "sync",
@@ -3809,6 +3821,10 @@ const createAction = (callback, rootOptions = {}) => {
3809
3821
  * the one that asked first or the one that arrived while the request was
3810
3822
  * out. Use `rerun()` to force a fresh run ("refresh", "check now", any
3811
3823
  * explicit user intent to go back to the network).
3824
+ *
3825
+ * An asynchronous callback answers with a promise, joined or not:
3826
+ * `ACTION.run().catch(…)` holds in every state. A synchronous one answers
3827
+ * synchronously, and throws its failure.
3812
3828
  */
3813
3829
  const run = (options) => {
3814
3830
  action.debug(`${action}.run(${stringifyForDisplay(options)})`);
@@ -4267,7 +4283,9 @@ const createAction = (callback, rootOptions = {}) => {
4267
4283
  // another run's, or idle.
4268
4284
  return;
4269
4285
  }
4270
- runningStateSignal.value = ABORTED;
4286
+ writeActionState(() => {
4287
+ runningStateSignal.value = ABORTED;
4288
+ });
4271
4289
  internalAbortController.abort(abortReason);
4272
4290
  actionAbortMap.delete(action);
4273
4291
  if (isPrerun && (globalAbortSignal.aborted || abortSignal.aborted)) {
@@ -4291,7 +4309,7 @@ const createAction = (callback, rootOptions = {}) => {
4291
4309
 
4292
4310
  actionAbortMap.set(action, abort);
4293
4311
 
4294
- batch(() => {
4312
+ writeActionState(() => {
4295
4313
  if (valueSignal.peek() === undefined) {
4296
4314
  // An action saying nothing of its own may still have a value the
4297
4315
  // page kept from the previous document (see kept_values.js).
@@ -4370,7 +4388,7 @@ const createAction = (callback, rootOptions = {}) => {
4370
4388
  * before the UI attempts to render the now-missing resource.
4371
4389
  */
4372
4390
 
4373
- batch(() => {
4391
+ writeActionState(() => {
4374
4392
  const value = resultToValue
4375
4393
  ? resultToValue(runResult, action)
4376
4394
  : runResult;
@@ -4399,7 +4417,9 @@ const createAction = (callback, rootOptions = {}) => {
4399
4417
  error === internalAbortSignal.reason) ||
4400
4418
  error.name === "AbortError";
4401
4419
  if (isAbort) {
4402
- runningStateSignal.value = ABORTED;
4420
+ writeActionState(() => {
4421
+ runningStateSignal.value = ABORTED;
4422
+ });
4403
4423
  if (isPrerun && abortSignal.aborted) {
4404
4424
  prerunProtectionRegistry.unprotect(action);
4405
4425
  }
@@ -4410,7 +4430,7 @@ const createAction = (callback, rootOptions = {}) => {
4410
4430
  if (isPrerun) {
4411
4431
  markErrorAsPrerunFailure(error);
4412
4432
  }
4413
- batch(() => {
4433
+ writeActionState(() => {
4414
4434
  errorSignal.value = error;
4415
4435
  runningStateSignal.value = FAILED;
4416
4436
  onError?.(error, { event, action, args });
@@ -4502,7 +4522,7 @@ const createAction = (callback, rootOptions = {}) => {
4502
4522
  }
4503
4523
 
4504
4524
  actionPromiseMap.delete(action);
4505
- batch(() => {
4525
+ writeActionState(() => {
4506
4526
  if (!willRunOrPrerun) {
4507
4527
  errorSignal.value = undefined;
4508
4528
  valueSignal.value = valueInitial;
@@ -4520,6 +4540,9 @@ const createAction = (callback, rootOptions = {}) => {
4520
4540
  provisionalValue,
4521
4541
  inheritData,
4522
4542
  keepsAnswers: keptAnswerRegistry !== null,
4543
+ // Whether the last run answered with a promise, written by
4544
+ // updateActions: the tense a run joining this instance answers in.
4545
+ answeredAsync: false,
4523
4546
 
4524
4547
  performRun,
4525
4548
  performReset,
@@ -5003,6 +5026,35 @@ const isPlainObject$2 = (obj) => {
5003
5026
  );
5004
5027
  };
5005
5028
 
5029
+ // Every state write of a run (start, end, failure, abort, reset), batched. At
5030
+ // the end of the batch preact runs the effects those writes woke up, anywhere
5031
+ // in the app, and rethrows the first one that threw: from there it would fail
5032
+ // this run, or leave it RUNNING, or stop the update half way. That throw is
5033
+ // the effect's, so it is reported on its own, as action_error_report.js
5034
+ // reports an error (thrown from a timer), and the action carries on. What
5035
+ // `write` throws (resultToValue, onComplete…) is the run's, and goes on.
5036
+ const writeActionState = (write) => {
5037
+ let writeThrew = false;
5038
+ let writeError;
5039
+ try {
5040
+ batch(() => {
5041
+ try {
5042
+ write();
5043
+ } catch (e) {
5044
+ writeThrew = true;
5045
+ writeError = e;
5046
+ }
5047
+ });
5048
+ } catch (effectError) {
5049
+ setTimeout(() => {
5050
+ throw effectError;
5051
+ });
5052
+ }
5053
+ if (writeThrew) {
5054
+ throw writeError;
5055
+ }
5056
+ };
5057
+
5006
5058
  /**
5007
5059
  * Reactively runs an action whenever the params derived from signals change.
5008
5060
  *