@bananapus/nana-sdk-core 6.1.0 → 6.3.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.
Files changed (149) hide show
  1. package/dist/cjs/review/confirming.d.ts +8 -0
  2. package/dist/cjs/review/confirming.d.ts.map +1 -0
  3. package/dist/cjs/review/confirming.js +11 -0
  4. package/dist/cjs/review/confirming.js.map +1 -0
  5. package/dist/cjs/review/heldWrite.d.ts +6 -1
  6. package/dist/cjs/review/heldWrite.d.ts.map +1 -1
  7. package/dist/cjs/review/heldWrite.js +13 -6
  8. package/dist/cjs/review/heldWrite.js.map +1 -1
  9. package/dist/cjs/review/locks.d.ts +141 -2
  10. package/dist/cjs/review/locks.d.ts.map +1 -1
  11. package/dist/cjs/review/locks.js +406 -58
  12. package/dist/cjs/review/locks.js.map +1 -1
  13. package/dist/cjs/review/messages.d.ts +16 -0
  14. package/dist/cjs/review/messages.d.ts.map +1 -1
  15. package/dist/cjs/review/messages.js +17 -1
  16. package/dist/cjs/review/messages.js.map +1 -1
  17. package/dist/cjs/review/react/ownership.d.ts +30 -0
  18. package/dist/cjs/review/react/ownership.d.ts.map +1 -0
  19. package/dist/cjs/review/react/ownership.js +27 -0
  20. package/dist/cjs/review/react/ownership.js.map +1 -0
  21. package/dist/cjs/review/react/useReviewedRun.d.ts +17 -3
  22. package/dist/cjs/review/react/useReviewedRun.d.ts.map +1 -1
  23. package/dist/cjs/review/react/useReviewedRun.js +528 -68
  24. package/dist/cjs/review/react/useReviewedRun.js.map +1 -1
  25. package/dist/cjs/review/react/useSavedAttempt.d.ts.map +1 -1
  26. package/dist/cjs/review/react/useSavedAttempt.js +12 -0
  27. package/dist/cjs/review/react/useSavedAttempt.js.map +1 -1
  28. package/dist/cjs/review/relayrSend.d.ts +20 -0
  29. package/dist/cjs/review/relayrSend.d.ts.map +1 -1
  30. package/dist/cjs/review/relayrSend.js +150 -105
  31. package/dist/cjs/review/relayrSend.js.map +1 -1
  32. package/dist/cjs/review/runStatus.d.ts +6 -2
  33. package/dist/cjs/review/runStatus.d.ts.map +1 -1
  34. package/dist/cjs/review/runStatus.js +3 -1
  35. package/dist/cjs/review/runStatus.js.map +1 -1
  36. package/dist/cjs/review/safeRelayr.d.ts +12 -2
  37. package/dist/cjs/review/safeRelayr.d.ts.map +1 -1
  38. package/dist/cjs/review/safeRelayr.js +226 -156
  39. package/dist/cjs/review/safeRelayr.js.map +1 -1
  40. package/dist/cjs/review/savedAttempt.d.ts.map +1 -1
  41. package/dist/cjs/review/savedAttempt.js +7 -2
  42. package/dist/cjs/review/savedAttempt.js.map +1 -1
  43. package/dist/cjs/review/send.d.ts +46 -5
  44. package/dist/cjs/review/send.d.ts.map +1 -1
  45. package/dist/cjs/review/send.js +251 -58
  46. package/dist/cjs/review/send.js.map +1 -1
  47. package/dist/cjs/review/sequence.d.ts +25 -0
  48. package/dist/cjs/review/sequence.d.ts.map +1 -1
  49. package/dist/cjs/review/sequence.js +45 -7
  50. package/dist/cjs/review/sequence.js.map +1 -1
  51. package/dist/cjs/review/sign.d.ts +6 -1
  52. package/dist/cjs/review/sign.d.ts.map +1 -1
  53. package/dist/cjs/review/sign.js +10 -4
  54. package/dist/cjs/review/sign.js.map +1 -1
  55. package/dist/cjs/review/tabState.d.ts +4 -0
  56. package/dist/cjs/review/tabState.d.ts.map +1 -1
  57. package/dist/cjs/review/tabState.js +4 -0
  58. package/dist/cjs/review/tabState.js.map +1 -1
  59. package/dist/cjs/review/wallet.d.ts +16 -4
  60. package/dist/cjs/review/wallet.d.ts.map +1 -1
  61. package/dist/cjs/review/wallet.js +62 -17
  62. package/dist/cjs/review/wallet.js.map +1 -1
  63. package/dist/cjs/review/writeHold.d.ts +92 -14
  64. package/dist/cjs/review/writeHold.d.ts.map +1 -1
  65. package/dist/cjs/review/writeHold.js +102 -26
  66. package/dist/cjs/review/writeHold.js.map +1 -1
  67. package/dist/cjs/review/writeRecovery.d.ts +42 -7
  68. package/dist/cjs/review/writeRecovery.d.ts.map +1 -1
  69. package/dist/cjs/review/writeRecovery.js +183 -21
  70. package/dist/cjs/review/writeRecovery.js.map +1 -1
  71. package/dist/cjs/testing.d.ts +13 -0
  72. package/dist/cjs/testing.d.ts.map +1 -1
  73. package/dist/cjs/testing.js +21 -0
  74. package/dist/cjs/testing.js.map +1 -1
  75. package/dist/esm/review/confirming.d.ts +8 -0
  76. package/dist/esm/review/confirming.d.ts.map +1 -0
  77. package/dist/esm/review/confirming.js +8 -0
  78. package/dist/esm/review/confirming.js.map +1 -0
  79. package/dist/esm/review/heldWrite.d.ts +6 -1
  80. package/dist/esm/review/heldWrite.d.ts.map +1 -1
  81. package/dist/esm/review/heldWrite.js +13 -6
  82. package/dist/esm/review/heldWrite.js.map +1 -1
  83. package/dist/esm/review/locks.d.ts +141 -2
  84. package/dist/esm/review/locks.d.ts.map +1 -1
  85. package/dist/esm/review/locks.js +396 -58
  86. package/dist/esm/review/locks.js.map +1 -1
  87. package/dist/esm/review/messages.d.ts +16 -0
  88. package/dist/esm/review/messages.d.ts.map +1 -1
  89. package/dist/esm/review/messages.js +16 -0
  90. package/dist/esm/review/messages.js.map +1 -1
  91. package/dist/esm/review/react/ownership.d.ts +30 -0
  92. package/dist/esm/review/react/ownership.d.ts.map +1 -0
  93. package/dist/esm/review/react/ownership.js +24 -0
  94. package/dist/esm/review/react/ownership.js.map +1 -0
  95. package/dist/esm/review/react/useReviewedRun.d.ts +17 -3
  96. package/dist/esm/review/react/useReviewedRun.d.ts.map +1 -1
  97. package/dist/esm/review/react/useReviewedRun.js +529 -69
  98. package/dist/esm/review/react/useReviewedRun.js.map +1 -1
  99. package/dist/esm/review/react/useSavedAttempt.d.ts.map +1 -1
  100. package/dist/esm/review/react/useSavedAttempt.js +12 -0
  101. package/dist/esm/review/react/useSavedAttempt.js.map +1 -1
  102. package/dist/esm/review/relayrSend.d.ts +20 -0
  103. package/dist/esm/review/relayrSend.d.ts.map +1 -1
  104. package/dist/esm/review/relayrSend.js +154 -109
  105. package/dist/esm/review/relayrSend.js.map +1 -1
  106. package/dist/esm/review/runStatus.d.ts +6 -2
  107. package/dist/esm/review/runStatus.d.ts.map +1 -1
  108. package/dist/esm/review/runStatus.js +3 -1
  109. package/dist/esm/review/runStatus.js.map +1 -1
  110. package/dist/esm/review/safeRelayr.d.ts +12 -2
  111. package/dist/esm/review/safeRelayr.d.ts.map +1 -1
  112. package/dist/esm/review/safeRelayr.js +226 -156
  113. package/dist/esm/review/safeRelayr.js.map +1 -1
  114. package/dist/esm/review/savedAttempt.d.ts.map +1 -1
  115. package/dist/esm/review/savedAttempt.js +7 -2
  116. package/dist/esm/review/savedAttempt.js.map +1 -1
  117. package/dist/esm/review/send.d.ts +46 -5
  118. package/dist/esm/review/send.d.ts.map +1 -1
  119. package/dist/esm/review/send.js +252 -59
  120. package/dist/esm/review/send.js.map +1 -1
  121. package/dist/esm/review/sequence.d.ts +25 -0
  122. package/dist/esm/review/sequence.d.ts.map +1 -1
  123. package/dist/esm/review/sequence.js +43 -7
  124. package/dist/esm/review/sequence.js.map +1 -1
  125. package/dist/esm/review/sign.d.ts +6 -1
  126. package/dist/esm/review/sign.d.ts.map +1 -1
  127. package/dist/esm/review/sign.js +10 -4
  128. package/dist/esm/review/sign.js.map +1 -1
  129. package/dist/esm/review/tabState.d.ts +4 -0
  130. package/dist/esm/review/tabState.d.ts.map +1 -1
  131. package/dist/esm/review/tabState.js +4 -0
  132. package/dist/esm/review/tabState.js.map +1 -1
  133. package/dist/esm/review/wallet.d.ts +16 -4
  134. package/dist/esm/review/wallet.d.ts.map +1 -1
  135. package/dist/esm/review/wallet.js +62 -17
  136. package/dist/esm/review/wallet.js.map +1 -1
  137. package/dist/esm/review/writeHold.d.ts +92 -14
  138. package/dist/esm/review/writeHold.d.ts.map +1 -1
  139. package/dist/esm/review/writeHold.js +102 -26
  140. package/dist/esm/review/writeHold.js.map +1 -1
  141. package/dist/esm/review/writeRecovery.d.ts +42 -7
  142. package/dist/esm/review/writeRecovery.d.ts.map +1 -1
  143. package/dist/esm/review/writeRecovery.js +180 -22
  144. package/dist/esm/review/writeRecovery.js.map +1 -1
  145. package/dist/esm/testing.d.ts +13 -0
  146. package/dist/esm/testing.d.ts.map +1 -1
  147. package/dist/esm/testing.js +20 -0
  148. package/dist/esm/testing.js.map +1 -1
  149. package/package.json +1 -1
@@ -5,6 +5,8 @@ exports.reviewPlan = reviewPlan;
5
5
  exports.sendReviewed = sendReviewed;
6
6
  exports.resumeReviewed = resumeReviewed;
7
7
  exports.completeReviewedStep = completeReviewedStep;
8
+ const locks_js_1 = require("./locks.js");
9
+ const confirming_js_1 = require("./confirming.js");
8
10
  const lineError_js_1 = require("./lineError.js");
9
11
  const viem_1 = require("viem");
10
12
  const confirm_js_1 = require("./confirm.js");
@@ -389,16 +391,38 @@ async function resumeReviewed(record, action, deps) {
389
391
  const { settled } = state;
390
392
  // Read before anything advances: a failed read leaves the step to resume again.
391
393
  const receipt = await confirmedReceipt(settled, deps.clientFor(call.chainId));
392
- // This sequence's own step: it is recorded, told, and only then
393
- // acknowledged, so it is never offered again.
394
- const kept = await recordSending(saved, settled, storage, expected);
395
- await tellConfirmed(action, handle, call, step, settled, receipt);
394
+ // This sequence's own step: told, recorded, and only then acknowledged,
395
+ // so it is never offered again. Its notice not delivered yet, it stays
396
+ // held at its receipt, acknowledged by none, for a completion to tell.
397
+ let kept = expected;
398
+ const outcome = outcomeOf(handle, reservation, {
399
+ account: saved.account,
400
+ step,
401
+ call,
402
+ prior: [...saved.completed],
403
+ ...(saved.signatures ? { signatures: saved.signatures } : {}),
404
+ });
405
+ const ended = await endStep(action, handle, outcome, settled, receipt, async () => {
406
+ kept = await recordSending(saved, settled, storage, expected);
407
+ });
408
+ if (!ended)
409
+ throw new lineError_js_1.ReviewedReceiptUnverifiedError(step, settled.hash, undefined, {
410
+ call: outcome.call,
411
+ reservation,
412
+ hashes: [...outcome.prior],
413
+ ...signedBy(outcome),
414
+ });
396
415
  if (settled?.result === "success")
397
416
  await handle.acknowledge(settled);
398
417
  if (kept === null) {
399
418
  const total = saved.calls.length;
400
419
  (0, receipt_js_1.tell)(deps.onProgress, { step: total - 1, total, phase: "done" });
401
- return { status: "done", hashes: saved.completed, receipts: [] };
420
+ return {
421
+ status: "done",
422
+ hashes: hashesThrough(outcome, settled),
423
+ receipts: [],
424
+ ...signedBy(outcome),
425
+ };
402
426
  }
403
427
  expected = kept;
404
428
  }
@@ -437,14 +461,87 @@ async function confirmedReceipt(settled, client, given) {
437
461
  /**
438
462
  * Tells `onConfirmed` of a step's verified success, once per settlement:
439
463
  * only the completion (a run, `completeReviewedStep`, `resumeReviewed`, in
440
- * any tab) that claims its notice on the hold tells it.
464
+ * any tab) that claims its notice on the hold tells it. The claim is made
465
+ * for good once told, and given back if the site never gets it, so a later
466
+ * completion tells it.
441
467
  */
442
468
  async function tellConfirmed(action, handle, call, step, settled, receipt) {
443
- if (settled?.result === "success" &&
444
- receipt &&
445
- (await handle.claimConfirmed(settled)))
446
- await (0, receipt_js_1.tell)(action.onConfirmed, call, step, receipt);
469
+ if (settled?.result !== "success" || !receipt)
470
+ return "delivered";
471
+ const claimant = globalThis.crypto.randomUUID();
472
+ if (!(await handle.claimConfirmed(settled, claimant)))
473
+ // Another completion's: delivered, or not yet.
474
+ return (await handle.confirmation(settled)) === "delivered"
475
+ ? "delivered"
476
+ : "pending";
477
+ // Renewed while this delivery runs (kept for a lock too), so only a claim
478
+ // whose tab closed or crashed lapses; ended only by this claimant.
479
+ const renewal = setInterval(() => void handle.claimConfirmed(settled, claimant).catch(() => undefined), confirming_js_1.CONFIRMING_RENEW_MS);
480
+ let delivery = "pending";
481
+ const end = async (delivered) => {
482
+ clearInterval(renewal);
483
+ await handle.settleConfirmed(settled, delivered, claimant);
484
+ };
485
+ try {
486
+ const told = await tellKept(action, () => action.onConfirmed?.(call, step, receipt), async (applied) => {
487
+ delivery = applied ? "delivered" : "undelivered";
488
+ await end(applied);
489
+ });
490
+ if (!told)
491
+ return delivery;
492
+ await end(true);
493
+ return "delivered";
494
+ }
495
+ catch (error) {
496
+ clearInterval(renewal);
497
+ throw error;
498
+ }
447
499
  }
500
+ /**
501
+ * Ends a settled step, the one way every completion does (a run, a resume,
502
+ * `completeReviewedStep`): its `onConfirmed` told with its outcome's call
503
+ * and step, and only once delivered is it recorded done (`record`, its
504
+ * reservation leaving the sequence's journal). Until then the step stays
505
+ * held there, so a later completion or resume tells it before any step
506
+ * after it is sent. False while undelivered.
507
+ */
508
+ async function endStep(action, handle, outcome, settled, receipt, record) {
509
+ if ((await tellConfirmed(action, handle, outcome.call, outcome.step, settled, receipt)) !== "delivered")
510
+ return false;
511
+ await record();
512
+ return true;
513
+ }
514
+ /**
515
+ * A sent step's outcome as its run saved it on the hold with `reservation`
516
+ * ({@link ReviewedOutcome}), else `handed` (what its caller has: a hold
517
+ * that saved none, or an older version's).
518
+ */
519
+ const outcomeOf = (handle, reservation, handed) => (reservation === undefined
520
+ ? undefined
521
+ : (0, sequence_js_1.decodeOutcome)(handle.outcomeOf(reservation))) ?? handed;
522
+ /** The action's hashes through `outcome`'s step, as `settled` ended it. */
523
+ const hashesThrough = (outcome, settled) => settled?.result === "success" && "hash" in settled
524
+ ? [...outcome.prior, settled.hash]
525
+ : [...outcome.prior];
526
+ /** The outcome's signatures, as a result spreads them. */
527
+ const signedBy = ({ signatures }) => signatures ? { signatures: [...signatures] } : {};
528
+ /**
529
+ * Tells a site what the wallet answered, under its `locks`: refused for a
530
+ * lost one, kept for a fresh lock ({@link keepOutcome}, `settled` told
531
+ * whether it was applied in the end), or dropped when it can't be kept
532
+ * (`settled` told false). Any other failure is logged and never changes
533
+ * what was sent. False when the outcome was only kept, not told.
534
+ */
535
+ const tellKept = async ({ locks, attemptScoped }, run, settled) => {
536
+ let kept = false;
537
+ await (0, receipt_js_1.tell)(() => (0, locks_js_1.keepOutcome)(locks, run, attemptScoped ? run : undefined, settled).then((applied) => void (kept = !applied), async (error) => {
538
+ if (error?.code !== "LOCK_LOST")
539
+ throw error;
540
+ kept = true;
541
+ await settled?.(false);
542
+ }));
543
+ return !kept;
544
+ };
448
545
  /**
449
546
  * A sent step's settlement by its own reservation, and the receipt its hold
450
547
  * verified ({@link heldReceipt}), read again until it reads. Null on `follow`'s
@@ -463,7 +560,10 @@ async function followSent(handle, client, reserved, follow) {
463
560
  const read = (work) => Promise.race([work, ended]);
464
561
  try {
465
562
  while (!signal.aborted) {
466
- const state = await read((0, heldWrite_js_1.followHeldWrite)(handle, client, { signal }));
563
+ const state = await read((0, heldWrite_js_1.followHeldWrite)(handle, client, {
564
+ signal,
565
+ ...(reserved ? { reservation: reserved } : {}),
566
+ }));
467
567
  if (signal.aborted)
468
568
  return null;
469
569
  if (state?.status !== "clear" ||
@@ -496,33 +596,72 @@ async function followSent(handle, client, reserved, follow) {
496
596
  * one acknowledgement of a followed step. Returns the settlement and that
497
597
  * checked receipt (a failure's too); null until then. A read that fails or disagrees with the hold throws with
498
598
  * nothing advanced: call it again. Call it once per settlement:
499
- * `onConfirmed` is told each time.
599
+ * `onConfirmed` is told each time. Its reservation's own saved settlement
600
+ * completes it after a later reservation of the call replaced it. Once
601
+ * `signal` aborts (the run that showed it is gone) it returns null, leaving
602
+ * the journal and `onConfirmed` to the run that resumes it. With
603
+ * `acknowledge` false it acknowledges nothing: the caller acknowledges the
604
+ * success once it has shown it (`handle.acknowledge`), so a success it
605
+ * never showed still warns the next run. `undelivered` says `onConfirmed`
606
+ * was kept for a lost lock and dropped: nothing is acknowledged, and a later
607
+ * completion (under the same or a fresh lock) tells it once.
500
608
  */
501
- async function completeReviewedStep(unverified, action, deps, receipt) {
609
+ async function completeReviewedStep(unverified, action, deps, receipt, signal, acknowledge = true) {
502
610
  const { step, held } = unverified;
503
- if (!held)
611
+ if (!held || signal?.aborted)
504
612
  return null;
505
613
  const { call, reservation } = held;
506
614
  const client = deps.clientFor(call.chainId);
507
615
  const handle = (0, writeHold_js_1.openStepHold)(deps.hold, action.account, false, call);
508
616
  const state = await handle.state(client);
509
- if (state.status !== "clear" ||
510
- state.reservation !== reservation ||
511
- !state.settled)
617
+ const settled = (state.status === "clear" && state.reservation === reservation
618
+ ? state.settled
619
+ : undefined) ??
620
+ (reservation === undefined ? undefined : handle.settlementOf(reservation));
621
+ if (!settled)
512
622
  return null;
513
- const { settled } = state;
514
623
  const confirmed = await confirmedReceipt(settled, client, receipt);
515
- if (action.scope !== undefined) {
624
+ if (signal?.aborted)
625
+ return null;
626
+ const outcome = outcomeOf(handle, reservation, {
627
+ account: action.account,
628
+ step,
629
+ call,
630
+ prior: [...(held.hashes ?? [])],
631
+ ...(held.signatures ? { signatures: held.signatures } : {}),
632
+ });
633
+ const ended = await endStep(action, handle, outcome, settled, confirmed, async () => {
634
+ if (action.scope === undefined)
635
+ return;
516
636
  const storage = deps.storage ?? (0, writeRecovery_js_1.createHeldWriteStorage)();
517
- const { raw, record } = (0, sequence_js_1.readReviewedSequence)(action.scope, action.account, storage);
518
- if (record?.sending?.step === step &&
519
- record.sending.reservation === reservation)
520
- await recordSending(record, settled, storage, raw);
521
- }
522
- await tellConfirmed(action, handle, call, step, settled, confirmed);
523
- if (settled.result === "success")
637
+ const scope = action.scope;
638
+ // Its journal entry by the reservation it saved (by the step its
639
+ // outcome saved, without one), never by the index of the action that
640
+ // completes it: a refreshed action's calls may be fewer.
641
+ const sending = () => {
642
+ const read = (0, sequence_js_1.readReviewedSequence)(scope, action.account, storage);
643
+ const saved = read.record?.sending;
644
+ return saved &&
645
+ saved.reservation === reservation &&
646
+ (reservation !== undefined || saved.step === outcome.step)
647
+ ? read
648
+ : null;
649
+ };
650
+ const read = sending();
651
+ if (!read)
652
+ return;
653
+ // Another completion recording it first leaves nothing to record.
654
+ await recordSending(read.record, settled, storage, read.raw).catch((error) => {
655
+ if (sending())
656
+ throw error;
657
+ });
658
+ });
659
+ const done = { settled, receipt: confirmed, ...signedBy(outcome) };
660
+ if (!ended)
661
+ return { ...done, undelivered: true, hashes: [] };
662
+ if (acknowledge && settled.result === "success")
524
663
  await handle.acknowledge(settled);
525
- return { settled, receipt: confirmed };
664
+ return { ...done, hashes: hashesThrough(outcome, settled) };
526
665
  }
527
666
  /**
528
667
  * The hash a wallet returned, even when the site's record of it failed
@@ -545,8 +684,21 @@ async function send(action, deps, resuming) {
545
684
  const sent = snapshots.map(({ sent }) => sent);
546
685
  /** Each step's simulation block, read once; a resumed run's prerequisites are long confirmed. */
547
686
  const blocks = calls.map((call) => resuming ? undefined : call.simulationBlockNumber);
548
- /** A site's release callback never stops core's own release. */
549
- const tellUnsent = (call, step) => (0, receipt_js_1.tell)(action.onUnsent, call, step);
687
+ /**
688
+ * Tell the site its steps from `from` were not sent, each only while every
689
+ * one of its locks is still this tab's, read fresh right before that
690
+ * step's callback (an earlier callback's await may have outlived the
691
+ * lease): once one was lost, the lock's new owner owns the site's journal,
692
+ * whatever ended the attempt. A site's release callback never stops core's
693
+ * own release.
694
+ */
695
+ const tellUnsent = async (steps, from) => {
696
+ for (const [index, call] of steps.entries())
697
+ await tellKept(action, async () => {
698
+ await (0, locks_js_1.assertLocksHeld)(action.locks);
699
+ await action.onUnsent?.(call, from + index);
700
+ });
701
+ };
550
702
  const connected = adapter.account();
551
703
  if (!connected)
552
704
  throw new lineError_js_1.ReviewLineError("Connect a wallet first.");
@@ -581,32 +733,34 @@ async function send(action, deps, resuming) {
581
733
  try {
582
734
  for (const [step, call] of calls.entries()) {
583
735
  began += 1;
736
+ // The site's own record before the wallet: never under a lost lock.
737
+ await (0, locks_js_1.assertLocksHeld)(action.locks);
584
738
  await action.beforeWallet?.(call, step);
585
739
  }
740
+ await (0, locks_js_1.assertLocksHeld)(action.locks);
586
741
  // The site's hooks may have awaited: the gates once more, right before the wallet.
587
742
  action.guard?.();
588
743
  action.assertWriteState?.();
589
744
  if (adapter.account()?.toLowerCase() !== account.toLowerCase())
590
745
  throw new lineError_js_1.ReviewLineError(ACCOUNT_CHANGED);
591
746
  submitting = true;
592
- hash = await adapter.submitToReviewer({
593
- account,
594
- calls: sent.map((call) => ({ ...call })),
595
- });
747
+ hash = await adapter.submitToReviewer({ account, calls: sent.map((call) => ({ ...call })) }, { fence: () => (0, locks_js_1.assertLocksHeld)(action.locks) });
596
748
  }
597
749
  catch (error) {
598
- // Only the site's own refusal or an explicit rejection proves nothing was sent.
599
- if (!submitting || (0, contractWrite_js_1.isDefiniteWalletRejection)(error))
600
- for (const [step, call] of calls.slice(0, began).entries())
601
- await tellUnsent(call, step);
750
+ // Only the site's own refusal, the adapter's or an explicit rejection
751
+ // proves nothing was sent.
752
+ if (!submitting ||
753
+ error instanceof lineError_js_1.WalletNotInvokedError ||
754
+ (0, contractWrite_js_1.isDefiniteWalletRejection)(error))
755
+ await tellUnsent(calls.slice(0, began), 0);
602
756
  throw error;
603
757
  }
604
758
  // Every step is told; a site's failure to record it never changes what was sent.
605
759
  for (const [step, call] of calls.entries())
606
- await (0, receipt_js_1.tell)(action.onSubmitted, call, step, hash, {
760
+ await tellKept(action, () => action.onSubmitted?.(call, step, hash, {
607
761
  safe: false,
608
762
  adopted: false,
609
- });
763
+ }));
610
764
  return { status: "done", hashes: [hash], receipts: [] };
611
765
  }
612
766
  const { scope } = action;
@@ -707,7 +861,7 @@ async function send(action, deps, resuming) {
707
861
  });
708
862
  };
709
863
  signed = {
710
- signatures: await (0, sign_js_1.signPrepared)(prepared, account, adapter, stillReviewed),
864
+ signatures: await (0, sign_js_1.signPrepared)(prepared, account, adapter, stillReviewed, () => (0, locks_js_1.assertLocksHeld)(action.locks), () => (0, locks_js_1.assertLocksHeld)(action.locks)),
711
865
  };
712
866
  }
713
867
  if (batched) {
@@ -738,9 +892,10 @@ async function send(action, deps, resuming) {
738
892
  }
739
893
  const released = async () => {
740
894
  gate.onRejected();
741
- for (const [step, call] of calls.slice(0, began).entries())
742
- await tellUnsent(call, step);
895
+ await tellUnsent(calls.slice(0, began), 0);
743
896
  };
897
+ /** Every lock is still this tab's: the hold's and the site's. */
898
+ const fence = () => (0, locks_js_1.assertLocksHeld)([gate, ...(action.locks ?? [])]);
744
899
  return (0, contractWrite_js_1.submitReviewedContractWrite)({
745
900
  request: batched,
746
901
  expectedAccount: account,
@@ -768,12 +923,23 @@ async function send(action, deps, resuming) {
768
923
  return sending;
769
924
  },
770
925
  beforeWrite: async () => {
771
- await gate.beforeSend();
926
+ await gate.beforeSend(undefined, (0, sequence_js_1.encodeOutcome)({
927
+ account,
928
+ step: calls.length - 1,
929
+ call: batched,
930
+ prior: [],
931
+ ...signed,
932
+ }));
772
933
  try {
773
934
  for (const [step, call] of calls.entries()) {
774
935
  began = step + 1;
936
+ // The site's own record before the wallet: never under a lost lock.
937
+ await (0, locks_js_1.assertLocksHeld)(action.locks);
775
938
  await action.beforeWallet?.(call, step);
776
939
  }
940
+ // Core's own check before the adapter, which fences again as
941
+ // the last await before the wallet.
942
+ await fence();
777
943
  }
778
944
  catch (error) {
779
945
  await released();
@@ -796,13 +962,13 @@ async function send(action, deps, resuming) {
796
962
  gate.afterSubmit(submitted);
797
963
  // Every step went: each is told, whatever an earlier callback does.
798
964
  for (const [step, call] of calls.entries())
799
- await (0, receipt_js_1.tell)(action.onSubmitted, call, step, submitted, {
965
+ await tellKept(action, () => action.onSubmitted?.(call, step, submitted, {
800
966
  safe: true,
801
967
  adopted: false,
802
- });
968
+ }));
803
969
  },
804
970
  write: async () => {
805
- const id = await adapter.sendCalls(batched.chainId, sent.map(({ to, data, value }) => ({ to, data, value })));
971
+ const id = await adapter.sendCalls(batched.chainId, sent.map(({ to, data, value }) => ({ to, data, value })), { fence });
806
972
  // A Safe app answers with the proposal's safeTxHash; anything else can't be followed.
807
973
  if (typeof id !== "string" || !/^0x[0-9a-fA-F]{64}$/.test(id))
808
974
  throw new lineError_js_1.ReviewLineError(exports.SAFE_BATCH_UNFOLLOWABLE_LINE);
@@ -835,6 +1001,7 @@ async function send(action, deps, resuming) {
835
1001
  ...snapshots[step].sent,
836
1002
  })),
837
1003
  completed: [],
1004
+ ...signed,
838
1005
  };
839
1006
  // Refused when another window changed it meanwhile.
840
1007
  expected = await (0, sequence_js_1.saveReviewedSequence)(journal, storage, expected);
@@ -855,6 +1022,11 @@ async function send(action, deps, resuming) {
855
1022
  }
856
1023
  };
857
1024
  const hashes = [];
1025
+ // A resumed run shows the signatures its first run signed.
1026
+ if (journal?.signatures)
1027
+ signed = { signatures: journal.signatures };
1028
+ /** The last step's outcome, as its hold keeps it, and its hashes: the done result's source. */
1029
+ let last;
858
1030
  const receipts = [];
859
1031
  const total = offset + calls.length;
860
1032
  for (const [step, call] of calls.entries()) {
@@ -916,6 +1088,14 @@ async function send(action, deps, resuming) {
916
1088
  const runEnd = hashes.length ? action.follow?.signal : undefined;
917
1089
  /** The site's failed record of this step's hash: the step was sent, and is held unverified. */
918
1090
  let unrecorded;
1091
+ /** What this step's run shows, saved with its reservation. */
1092
+ const outcome = {
1093
+ account,
1094
+ step: at,
1095
+ call,
1096
+ prior: journal ? [...journal.completed] : [...hashes],
1097
+ ...signed,
1098
+ };
919
1099
  const hash = await handles[step]
920
1100
  .send(client, (gate) => {
921
1101
  // Under the hold's own check of the call, before anything reserves.
@@ -929,8 +1109,10 @@ async function send(action, deps, resuming) {
929
1109
  delete journal.sending;
930
1110
  await keep();
931
1111
  }
932
- await tellUnsent(call, at);
1112
+ await tellUnsent([call], at);
933
1113
  };
1114
+ /** Every lock is still this tab's: the hold's and the site's. */
1115
+ const fence = () => (0, locks_js_1.assertLocksHeld)([gate, ...(action.locks ?? [])]);
934
1116
  return (0, contractWrite_js_1.submitReviewedContractWrite)({
935
1117
  request: reviewed,
936
1118
  expectedAccount: account,
@@ -983,7 +1165,7 @@ async function send(action, deps, resuming) {
983
1165
  },
984
1166
  beforeWrite: async () => {
985
1167
  // An adopted proposal is held as the call it runs.
986
- await gate.beforeSend(queued?.call);
1168
+ await gate.beforeSend(queued?.call, (0, sequence_js_1.encodeOutcome)(outcome));
987
1169
  reserved = handles[step].peek().reservation;
988
1170
  if (journal)
989
1171
  try {
@@ -1000,7 +1182,12 @@ async function send(action, deps, resuming) {
1000
1182
  throw error;
1001
1183
  }
1002
1184
  try {
1185
+ // The site's own record before the wallet: never under a lost lock.
1186
+ await (0, locks_js_1.assertLocksHeld)(action.locks);
1003
1187
  await action.beforeWallet?.(call, at);
1188
+ // Core's own check before the adapter, which fences again as
1189
+ // the last await before the wallet.
1190
+ await fence();
1004
1191
  }
1005
1192
  catch (error) {
1006
1193
  await released();
@@ -1030,10 +1217,10 @@ async function send(action, deps, resuming) {
1030
1217
  // An adopted proposal's nonce lets the hold prove its execution onchain.
1031
1218
  onWriteSubmitted: async (submitted) => {
1032
1219
  gate.afterSubmit(submitted, queued?.tx);
1033
- await (0, receipt_js_1.tell)(action.onSubmitted, call, at, submitted, {
1220
+ await tellKept(action, () => action.onSubmitted?.(call, at, submitted, {
1034
1221
  safe,
1035
1222
  adopted: !!queued,
1036
- });
1223
+ }));
1037
1224
  },
1038
1225
  write: async ({ chainId, to, data, value, gas }) => queued?.proposalHash ??
1039
1226
  adapter.sendTransaction({
@@ -1045,6 +1232,7 @@ async function send(action, deps, resuming) {
1045
1232
  // After a step was sent, the run's end refuses this one, unsent,
1046
1233
  // as late as the wallet's own last check.
1047
1234
  ...(runEnd ? { signal: runEnd } : {}),
1235
+ fence,
1048
1236
  }),
1049
1237
  onPhase: (phase) => progress(phase),
1050
1238
  guard: action.guard,
@@ -1079,14 +1267,14 @@ async function send(action, deps, resuming) {
1079
1267
  }
1080
1268
  // From here the step was sent: it ends done, failed as its hold
1081
1269
  // verified, or unverified with what resumes it, never any other way.
1270
+ /** `completed`: this step's hash already recorded as completed. */
1082
1271
  const unverified = (cause) => new lineError_js_1.ReviewedReceiptUnverifiedError(at, hash, (0, safeService_js_1.isSafeExecTransaction)(reviewed.data)
1083
1272
  ? messages_js_1.WRITE_HOLD_SAFE_UNCONFIRMED_LINE
1084
1273
  : undefined, {
1085
1274
  call,
1086
1275
  reservation: reserved,
1087
- // This run's sends, its own last: only the steps before it completed.
1088
- hashes: journal ? [...journal.completed] : hashes.slice(0, -1),
1089
- ...signed,
1276
+ hashes: [...outcome.prior],
1277
+ ...signedBy(outcome),
1090
1278
  }, cause === undefined ? {} : { cause });
1091
1279
  // The site could not record the hash: held as sent, for it to resume.
1092
1280
  if (unrecorded !== undefined)
@@ -1145,19 +1333,24 @@ async function send(action, deps, resuming) {
1145
1333
  throw new lineError_js_1.ReviewedCallRevertedError(at, mined, receipt);
1146
1334
  }
1147
1335
  receipts.push(receipt);
1148
- if (journal) {
1336
+ // Its notice not delivered yet: held at its receipt for its completion.
1337
+ const ended = await endStep(action, handles[step], outcome, settled, receipt, async () => {
1338
+ if (!journal)
1339
+ return;
1149
1340
  delete journal.sending;
1150
1341
  journal.completed.push(mined);
1151
1342
  await keep();
1152
- }
1153
- await tellConfirmed(action, handles[step], call, at, settled, receipt);
1343
+ });
1344
+ if (!ended)
1345
+ throw unverified();
1346
+ last = { outcome, hashes: hashesThrough(outcome, settled) };
1154
1347
  }
1155
1348
  onProgress({ step: total - 1, total, phase: "done" });
1156
1349
  return {
1157
1350
  status: "done",
1158
- hashes: journal ? [...journal.completed] : hashes,
1351
+ hashes: last.hashes,
1159
1352
  receipts,
1160
- ...signed,
1353
+ ...signedBy(last.outcome),
1161
1354
  };
1162
1355
  }
1163
1356
  //# sourceMappingURL=send.js.map